Added liked tenders desigm and data from api
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/view_models/liked_tenders_view_model.dart';
|
||||
import 'package:tm_app/views/liked_tenders/widgets/liked_tenders_list_item.dart';
|
||||
|
||||
import '../../../core/routes/app_routes.dart';
|
||||
import '../../shared/desktop_navigation_widget.dart';
|
||||
|
||||
class LikedTendersDesktopPage extends StatefulWidget {
|
||||
const LikedTendersDesktopPage({super.key});
|
||||
|
||||
@override
|
||||
State<LikedTendersDesktopPage> createState() =>
|
||||
_LikedTendersDesktopPageState();
|
||||
}
|
||||
|
||||
class _LikedTendersDesktopPageState extends State<LikedTendersDesktopPage> {
|
||||
late ScrollController _scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = ScrollController()..addListener(_onScroll);
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (!_scrollController.hasClients) {
|
||||
return;
|
||||
}
|
||||
final max = _scrollController.position.maxScrollExtent;
|
||||
final pos = _scrollController.position.pixels;
|
||||
const threshold = 200.0;
|
||||
if (max - pos <= threshold) {
|
||||
final vm = Provider.of<LikedTendersViewModel>(context, listen: false);
|
||||
vm.loadMoreIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.removeListener(_onScroll);
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
|
||||
body: Column(
|
||||
children: [
|
||||
DesktopNavigationWidget(
|
||||
currentIndex: 1,
|
||||
onTabChanged: (index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
Router.neglect(context, () => HomeRouteData().go(context));
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
Router.neglect(context, () => ProfileRouteData().go(context));
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: 740,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Consumer<LikedTendersViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
if (viewModel.isLoading &&
|
||||
(viewModel.data?.data?.feedback ?? []).isEmpty) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.secondary50,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (viewModel.errorMessage != null) {
|
||||
return Center(child: Text(viewModel.errorMessage!));
|
||||
}
|
||||
|
||||
final feedback = viewModel.data?.data?.feedback ?? [];
|
||||
|
||||
if (feedback.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('چیزی برای نمایش نیست'),
|
||||
);
|
||||
}
|
||||
|
||||
final itemCount =
|
||||
feedback.length + (viewModel.isLoadingMore ? 1 : 0);
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await viewModel.getLikedTenders(reset: true);
|
||||
},
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.0.w(),
|
||||
vertical: 15.0.h(),
|
||||
),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < feedback.length) {
|
||||
final item = feedback[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 20.0.h()),
|
||||
child: Dismissible(
|
||||
key: ValueKey(item.id ?? index),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20.0.w(),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
AssetsManager.trash,
|
||||
),
|
||||
),
|
||||
onDismissed: (_) {
|
||||
if (item.id != null) {
|
||||
viewModel.removeTenderById(item.id!);
|
||||
}
|
||||
},
|
||||
child: LikedListItem(tender: item.tender!),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.secondary50,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/view_models/liked_tenders_view_model.dart';
|
||||
import 'package:tm_app/views/liked_tenders/strings/liked_tenders_strings.dart';
|
||||
import 'package:tm_app/views/liked_tenders/widgets/liked_tenders_list_item.dart';
|
||||
import 'package:tm_app/views/shared/tender_app_bar.dart';
|
||||
|
||||
class LikedTendersMobilePage extends StatefulWidget {
|
||||
const LikedTendersMobilePage({super.key});
|
||||
|
||||
@override
|
||||
State<LikedTendersMobilePage> createState() => _LikedTendersMobilePageState();
|
||||
}
|
||||
|
||||
class _LikedTendersMobilePageState extends State<LikedTendersMobilePage> {
|
||||
late final ScrollController _scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = ScrollController()..addListener(_onScroll);
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (!_scrollController.hasClients) {
|
||||
return;
|
||||
}
|
||||
final max = _scrollController.position.maxScrollExtent;
|
||||
final pos = _scrollController.position.pixels;
|
||||
const threshold = 200.0;
|
||||
if (max - pos <= threshold) {
|
||||
final vm = Provider.of<LikedTendersViewModel>(context, listen: false);
|
||||
vm.loadMoreIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.removeListener(_onScroll);
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
appBar: appBar(context: context, title: LikedTendersStrings.likedTenders),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Consumer<LikedTendersViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
if (viewModel.isLoading &&
|
||||
(viewModel.data?.data?.feedback ?? []).isEmpty) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.secondary50,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (viewModel.errorMessage != null) {
|
||||
return Center(child: Text(viewModel.errorMessage!));
|
||||
}
|
||||
|
||||
final feedback = viewModel.data?.data?.feedback ?? [];
|
||||
|
||||
if (feedback.isEmpty) {
|
||||
return const Center(child: Text('چیزی برای نمایش نیست'));
|
||||
}
|
||||
|
||||
final itemCount = feedback.length + (viewModel.isLoadingMore ? 1 : 0);
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await viewModel.getLikedTenders(reset: true);
|
||||
},
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.0.w(),
|
||||
vertical: 15.0.h(),
|
||||
),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < feedback.length) {
|
||||
final item = feedback[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 20.0.h()),
|
||||
child: Dismissible(
|
||||
key: ValueKey(item.id ?? index),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20.0.w()),
|
||||
child: SvgPicture.asset(AssetsManager.trash),
|
||||
),
|
||||
onDismissed: (_) {
|
||||
if (item.id != null) {
|
||||
viewModel.removeTenderById(item.id!);
|
||||
}
|
||||
},
|
||||
child: LikedListItem(tender: item.tender!),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.secondary50,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/views/shared/responsive_builder.dart';
|
||||
import 'package:tm_app/views/liked_tenders/pages/liked_tenders_desktop_page.dart';
|
||||
import 'package:tm_app/views/liked_tenders/pages/liked_tenders_mobile_page.dart';
|
||||
import 'package:tm_app/views/liked_tenders/pages/liked_tenders_tablet_page.dart';
|
||||
|
||||
class LikedTendersScreen extends StatelessWidget {
|
||||
const LikedTendersScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SizeConfig.init(context);
|
||||
return ResponsiveBuilder(
|
||||
mobile: LikedTendersMobilePage(),
|
||||
tablet: LikedTendersTabletPage(),
|
||||
desktop: LikedTendersDesktopPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/routes/app_routes.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/view_models/liked_tenders_view_model.dart';
|
||||
import 'package:tm_app/views/liked_tenders/strings/liked_tenders_strings.dart';
|
||||
import 'package:tm_app/views/liked_tenders/widgets/liked_tenders_list_item.dart';
|
||||
|
||||
import '../../shared/tablet_navigation_widget.dart';
|
||||
import '../../shared/tender_app_bar.dart';
|
||||
|
||||
class LikedTendersTabletPage extends StatefulWidget {
|
||||
const LikedTendersTabletPage({super.key});
|
||||
|
||||
@override
|
||||
State<LikedTendersTabletPage> createState() => _LikedTendersTabletPageState();
|
||||
}
|
||||
|
||||
class _LikedTendersTabletPageState extends State<LikedTendersTabletPage> {
|
||||
late final ScrollController _scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = ScrollController()..addListener(_onScroll);
|
||||
}
|
||||
|
||||
void _onScroll() {
|
||||
if (!_scrollController.hasClients) {
|
||||
return;
|
||||
}
|
||||
final max = _scrollController.position.maxScrollExtent;
|
||||
final pos = _scrollController.position.pixels;
|
||||
const threshold = 200.0;
|
||||
if (max - pos <= threshold) {
|
||||
final vm = Provider.of<LikedTendersViewModel>(context, listen: false);
|
||||
vm.loadMoreIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.removeListener(_onScroll);
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final GlobalKey<ScaffoldState> key = GlobalKey();
|
||||
return Scaffold(
|
||||
key: key,
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
appBar: tabletAppBar(title: LikedTendersStrings.likedTenders, key: key),
|
||||
drawer: TabletNavigationWidget(
|
||||
currentIndex: 1,
|
||||
onTabChanged: (index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
Router.neglect(context, () => HomeRouteData().go(context));
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
context.pop();
|
||||
Router.neglect(context, () => ProfileRouteData().go(context));
|
||||
break;
|
||||
}
|
||||
},
|
||||
),
|
||||
body: Center(
|
||||
child: SizedBox(
|
||||
width: 720,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Consumer<LikedTendersViewModel>(
|
||||
builder: (context, viewModel, child) {
|
||||
if (viewModel.isLoading &&
|
||||
(viewModel.data?.data?.feedback ?? []).isEmpty) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.secondary50,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (viewModel.errorMessage != null) {
|
||||
return Center(child: Text(viewModel.errorMessage!));
|
||||
}
|
||||
|
||||
final feedback = viewModel.data?.data?.feedback ?? [];
|
||||
|
||||
if (feedback.isEmpty) {
|
||||
return const Center(child: Text('چیزی برای نمایش نیست'));
|
||||
}
|
||||
|
||||
final itemCount =
|
||||
feedback.length + (viewModel.isLoadingMore ? 1 : 0);
|
||||
return RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await viewModel.getLikedTenders(reset: true);
|
||||
},
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 24.0.w(),
|
||||
vertical: 15.0.h(),
|
||||
),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < feedback.length) {
|
||||
final item = feedback[index];
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 20.0.h()),
|
||||
child: Dismissible(
|
||||
key: ValueKey(item.id ?? index),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
alignment: Alignment.centerRight,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 20.0.w(),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
AssetsManager.trash,
|
||||
),
|
||||
),
|
||||
onDismissed: (_) {
|
||||
if (item.id != null) {
|
||||
viewModel.removeTenderById(item.id!);
|
||||
}
|
||||
},
|
||||
child: LikedListItem(tender: item.tender!),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 20),
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.secondary50,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
); },
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class LikedTendersStrings {
|
||||
LikedTendersStrings._();
|
||||
|
||||
static const likedTenders = 'Liked tenders';
|
||||
static const filter = 'Filter';
|
||||
static const selectDateRange = 'Select date range';
|
||||
static const status = 'Status';
|
||||
static const all = 'All';
|
||||
static const dislikeTenders = 'Dislike tenders';
|
||||
static const tendersSubmitted = 'Tenders submitted';
|
||||
static const confirm = 'Confirm';
|
||||
static const startDate = 'YYYY/MM/DD';
|
||||
static const endDate = 'YYYY/MM/DD';
|
||||
static const approvedTenders = 'Approved tenders';
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import 'package:country_flags/country_flags.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:tm_app/core/constants/assets.dart';
|
||||
import 'package:tm_app/core/theme/colors.dart';
|
||||
import 'package:tm_app/core/utils/size_config.dart';
|
||||
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
|
||||
import 'package:tm_app/views/home/strings/home_strings.dart';
|
||||
|
||||
class LikedListItem extends StatelessWidget {
|
||||
const LikedListItem({required this.tender, super.key});
|
||||
final TenderData tender;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.cardBackground,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(color: AppColors.grey30),
|
||||
),
|
||||
margin: EdgeInsetsDirectional.only(end: 16.0.w()),
|
||||
width: double.infinity,
|
||||
height: 250.0.h(),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16.0.w(), vertical: 16.0.h()),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 10.0.h()),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10.0.w(),
|
||||
vertical: 5.0.h(),
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary30,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'${HomeStrings.tenderDeadline} :',
|
||||
style: TextStyle(
|
||||
fontSize: 15.0.sp(),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.textBlue,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
timeConvertor(tender.tenderDeadline!),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.0.h()),
|
||||
Text(
|
||||
tender.title!,
|
||||
style: TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontSize: 16.0.sp(),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8.0.h()),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text(
|
||||
tender.description!,
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 4,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey70,
|
||||
),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(AssetsManager.location),
|
||||
SizedBox(width: 1.0.w()),
|
||||
Text(
|
||||
tender.countryCode!,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0.sp(),
|
||||
fontWeight: FontWeight.w400,
|
||||
color: AppColors.grey80,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.0.w()),
|
||||
SizedBox(
|
||||
width: 32.0.w(),
|
||||
height: 21.0.h(),
|
||||
child: CountryFlag.fromCountryCode(
|
||||
tender.countryCode!,
|
||||
shape: RoundedRectangle(4),
|
||||
),
|
||||
),
|
||||
Spacer(),
|
||||
Container(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: AppColors.grey60, width: 1.5.w()),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(Icons.close, color: AppColors.grey60, size: 16),
|
||||
),
|
||||
SizedBox(width: 30.0.w()),
|
||||
Container(
|
||||
width: 32.0.w(),
|
||||
height: 32.0.w(),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: AppColors.grey60, width: 1.5.w()),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(Icons.check, color: AppColors.grey60, size: 16),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String timeConvertor(int time) {
|
||||
DateTime date = DateTime.fromMillisecondsSinceEpoch(time);
|
||||
|
||||
String formattedDate =
|
||||
"${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}";
|
||||
|
||||
return formattedDate;
|
||||
}
|
||||
Reference in New Issue
Block a user