You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
car_provider_app/lib/views/appoinments/appointment_page.dart

289 lines
11 KiB
Dart

import 'dart:async';
import 'package:car_provider_app/views/dashboard/widget/general_appointment_widget.dart';
import 'package:mc_common_app/config/routes.dart';
import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/generated/locale_keys.g.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
import 'package:mc_common_app/theme/colors.dart';
import 'package:mc_common_app/utils/date_helper.dart';
import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/widgets/common_widgets/filters_list.dart';
import 'package:mc_common_app/widgets/empty_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:flutter/material.dart';
import 'package:mc_common_app/classes/consts.dart';
import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:percent_indicator/percent_indicator.dart';
import 'package:provider/provider.dart';
import 'package:easy_localization/easy_localization.dart';
class AppointmentPage extends StatefulWidget {
final BranchDetailModel branch;
const AppointmentPage({required this.branch, super.key});
@override
State<AppointmentPage> createState() => _AppointmentPageState();
}
class _AppointmentPageState extends State<AppointmentPage> {
late AppointmentsVM appointmentsVM;
GlobalKey<RefreshIndicatorState> refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
Future<void> _pullRefresh(BuildContext context) async {
var futures = <Future<void>>[
appointmentsVM.getAppointmentSlotsInfo(branchID: widget.branch.id!),
appointmentsVM.applyFilterOnAppointmentsVMForProviders(
appointmentStatusEnum: AppointmentStatusEnum.allAppointments,
isNeedCustomerFilter: true,
),
];
// Wait for both futures to complete
await Future.wait(futures);
}
@override
void initState() {
super.initState();
appointmentsVM = context.read<AppointmentsVM>();
appointmentsVM.selectedBranchIdForAppointments = widget.branch.id!;
scheduleMicrotask(() {
appointmentsVM.selectedDateForAppointments = DateHelper.formatAsYearMonthDay(DateTime.now());
appointmentsVM.populateAppointmentsFilterList();
_pullRefresh(context);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(
//profileImageUrl: MyAssets.carBanner,
title: widget.branch.branchName,
actions: [
Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return Padding(
padding: EdgeInsets.only(top: appointmentsVM.appointmentFiltersCounter > 0 ? 20 : 0, right: 21),
child: Badge(
isLabelVisible: appointmentsVM.appointmentFiltersCounter > 0,
largeSize: 15,
smallSize: 20,
backgroundColor: MyColors.darkPrimaryColor,
label: Text('${appointmentsVM.appointmentFiltersCounter}'),
child: MyAssets.searchIcon.buildSvg(),
),
).onPress(() {
navigateWithName(context, AppRoutes.appoinmentSearchFilterPage);
});
}),
],
),
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: Consumer(builder: (BuildContext context, AppointmentsVM appointmentsVM, Widget? child) {
return RefreshIndicator(
onRefresh: () => _pullRefresh(context),
key: refreshIndicatorKey,
child: SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
slotDetailsWidget(context, appointmentsVM),
FiltersList(
filterList: appointmentsVM.appointmentsFilterOptions,
padding: const EdgeInsets.symmetric(horizontal: 18),
onFilterTapped: (index, selectedFilterId) {
if (appointmentsVM.isAppointmentLoading) {
return;
}
appointmentsVM.applyFilterOnAppointmentsVMForProviders(
appointmentStatusEnum: selectedFilterId.toAppointmentStatusEnum(),
isNeedCustomerFilter: true,
);
},
),
if (appointmentsVM.isAppointmentLoading) ...[
const Column(
children: [
SizedBox(height: 50),
Center(child: CircularProgressIndicator()),
],
),
] else
...[
if (appointmentsVM.myFilteredAppointmentsForProvider.isEmpty)
EmptyWidget(
spacerWidget: const SizedBox(height: 25),
text: LocaleKeys.noAppointmentFound.tr(),
)
else
...[
ListView.separated(
itemBuilder: (context, index) {
return GeneralAppointmentWidget(
isNeedToShowItems: true,
appointmentListModel: appointmentsVM.myFilteredAppointmentsForProvider[index],
isNeedTotalPayment: false,
onTap: () {
appointmentsVM.selectedAppointmentIndex = index;
navigateWithName(
context,
AppRoutes.appointmentDetailList,
);
},
);
},
separatorBuilder: (context, snapchat) => 21.height,
itemCount: appointmentsVM.myFilteredAppointmentsForProvider.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: const EdgeInsets.all(21),
),
],
],
],
),
),
);
}),
),
);
}
Widget slotDetailsWidget(BuildContext context, AppointmentsVM appointmentsVM) {
if (appointmentsVM.isSlotsLoading) {
return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator()));
}
double percent = 0.0;
if (appointmentsVM.appointmentSlots != null) {
percent = appointmentsVM.appointmentSlots!.occupiedSlots / appointmentsVM.appointmentSlots!.totalSlots;
}
return Column(
children: [
Row(
children: [
Expanded(
child: LocaleKeys.slotsOverview.tr().toText(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
Row(
children: [
appointmentsVM.selectedDateForAppointments.toText(
fontWeight: FontWeight.bold,
),
const Icon(
Icons.keyboard_arrow_down_outlined,
size: 16,
),
],
)
.toContainer(
backgroundColor: MyColors.lightGreyEAColor,
borderRadius: 100,
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
)
.onPress(
() async {
String date = await Utils.pickDateFromDatePicker(context, firstDate: DateTime(2023));
appointmentsVM.selectedDateForAppointments = date;
await appointmentsVM.getAppointmentSlotsInfo(branchID: widget.branch.id!);
},
),
],
),
24.height,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 14,
height: 14,
color: MyColors.lightGreyEAColor,
),
4.width,
("${LocaleKeys.empty.tr()}:").toText(
fontSize: 8,
color: Colors.white,
),
(appointmentsVM.appointmentSlots?.emptySlots ?? 0).toString().toText(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.white,
),
],
).toContainer(
backgroundColor: MyColors.darkIconColor,
),
8.height,
Row(
children: [
Container(
width: 14,
height: 14,
color: MyColors.darkPrimaryColor,
),
4.width,
("${LocaleKeys.occupied.tr()}:").toText(
fontSize: 8,
color: Colors.white,
),
(appointmentsVM.appointmentSlots?.occupiedSlots ?? 0).toString().toText(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Colors.white,
),
],
).toContainer(
backgroundColor: MyColors.darkIconColor,
),
],
),
CircularPercentIndicator(
radius: 60.0,
lineWidth: 12.0,
percent: percent,
circularStrokeCap: CircularStrokeCap.round,
center: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
LocaleKeys.totalSlots.tr().toText(
fontSize: 13,
fontWeight: FontWeight.bold,
),
(appointmentsVM.appointmentSlots?.totalSlots ?? 0).toString().toText(
fontSize: 24,
fontWeight: FontWeight.bold,
),
],
),
backgroundColor: MyColors.lightGreyEAColor,
progressColor: MyColors.darkPrimaryColor,
),
],
)
],
).toWhiteContainer(
width: double.infinity,
pading: const EdgeInsets.all(12),
margin: const EdgeInsets.all(21),
);
}
}