appointments ux optimization
This commit is contained in:
parent
11dbb32a1a
commit
b605346c3c
|
|
@ -19,7 +19,6 @@ extension Code on Languages {
|
|||
class AppColors {
|
||||
static Color sentMessageColor = const Color(0xff7986ca);
|
||||
static Color primeColor = const Color(0xff7986ca);
|
||||
static Color tailAuthColor = const Color(0xffFEE64B);
|
||||
static Color textButtonColor = const Color(0xff484848);
|
||||
static Color textColor = Colors.black;
|
||||
static Color borderColor = const Color(0xffD9D9D9);
|
||||
|
|
@ -28,6 +27,7 @@ class AppColors {
|
|||
static Color textMessageColor = const Color(0xff4E5D78);
|
||||
static Color timeMessageColor = const Color(0xff9FA6B5);
|
||||
static Color secondaryColor = const Color(0xff44bee8);
|
||||
static Color tailAuthColor = secondaryColor;
|
||||
static Color emailColor = const Color(0xff76BAD0);
|
||||
static Color callColor = const Color(0xff76D095);
|
||||
static Color messageColor = const Color(0xff4E5D78);
|
||||
|
|
|
|||
|
|
@ -23,8 +23,9 @@ class HeaderScreen extends StatelessWidget {
|
|||
return Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
child: Obx(() => (!homeController.isArabic.value)
|
||||
width: 32,
|
||||
child: Obx(
|
||||
() => (!homeController.isArabic.value)
|
||||
? SvgPicture.asset(
|
||||
"assets/icons/arrow-left.svg",
|
||||
width: Responsive.isTablet() ? 25 : null,
|
||||
|
|
@ -38,9 +39,12 @@ class HeaderScreen extends StatelessWidget {
|
|||
colorFilter: (iconColor != null)
|
||||
? ColorFilter.mode(iconColor!, BlendMode.srcIn)
|
||||
: null,
|
||||
)).onTap(() {
|
||||
),
|
||||
),
|
||||
).onTap(
|
||||
() {
|
||||
RoutingManager.back();
|
||||
}),
|
||||
},
|
||||
),
|
||||
BoldTextWidget(
|
||||
title,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,14 @@ class PagesTranslations implements Translations {
|
|||
@override
|
||||
Map<String, Map<String, String>> get keys => {
|
||||
'en': {
|
||||
'you_have_an_appointment_on': 'your appointment on',
|
||||
'appointment': 'Appointment',
|
||||
'you_have_an_appointment_on': 'You have An Appointment on',
|
||||
'appointment_scheduled_successfully.':
|
||||
'Appointment scheduled successfully.',
|
||||
'am': 'AM',
|
||||
'pm': 'PM',
|
||||
'available_times': 'Available Times',
|
||||
'you_have_an_appointment': 'Details',
|
||||
'schedule_an_appointment': 'Appointment',
|
||||
'There are no notifications to display at this time.':
|
||||
'There are no notifications to display at this time.',
|
||||
|
|
@ -184,7 +191,13 @@ class PagesTranslations implements Translations {
|
|||
'Enter your email to reset your password please \n We will send verification code to your Email.',
|
||||
},
|
||||
'ar': {
|
||||
'appointment': 'موعد',
|
||||
'you_have_an_appointment_on': 'لديك موعد بتاريخ',
|
||||
'appointment_scheduled_successfully.': 'تم حجز الموعد بنجاح',
|
||||
'am': 'صباحاً',
|
||||
'pm': 'مساءً',
|
||||
'available_times': 'الأوقات المتاحة',
|
||||
'you_have_an_appointment': 'التفاصيل',
|
||||
'schedule_an_appointment': 'حجز موعد',
|
||||
'There are no notifications to display at this time.':
|
||||
'لا توجد إشعارات لعرضها في الوقت الحالي.',
|
||||
|
|
|
|||
|
|
@ -59,6 +59,29 @@ class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
static DateTime generateRandomDateTime() {
|
||||
final random = Random();
|
||||
DateTime currentDate = DateTime.now();
|
||||
|
||||
final year = currentDate.year;
|
||||
|
||||
final month = currentDate.month;
|
||||
|
||||
final maxDaysInMonth = 30 - currentDate.day;
|
||||
|
||||
final day = currentDate.day + 1 + random.nextInt(maxDaysInMonth) + 1;
|
||||
|
||||
final hour = 12 + random.nextInt(5) + 1;
|
||||
|
||||
final minute = random.nextInt(60);
|
||||
|
||||
final second = random.nextInt(60);
|
||||
|
||||
final millisecond = random.nextInt(1000);
|
||||
|
||||
return DateTime(year, month, day, hour, minute, second, millisecond);
|
||||
}
|
||||
|
||||
static Future<List<AssetEntity>?> pickImage(BuildContext context) async {
|
||||
return await AssetPicker.pickAssets(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -238,4 +238,12 @@ class CardController extends GetxController {
|
|||
onError?.call(e);
|
||||
}, onSuccess: onSuccess);
|
||||
}
|
||||
|
||||
/// ------------ card appointments ---------///
|
||||
RxFuture<void> availableAppointmentsState = RxFuture(null);
|
||||
void getAvailableAppointments() async {
|
||||
return availableAppointmentsState.observe((value) async {
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:taafee_mobile/common/extensions/widget_extension.dart';
|
|||
import 'package:taafee_mobile/common/widgets/button.dart';
|
||||
import 'package:taafee_mobile/common/widgets/text.dart';
|
||||
import 'package:taafee_mobile/core/routing/routing_manager.dart';
|
||||
import 'package:taafee_mobile/core/utils/utils.dart';
|
||||
import 'package:taafee_mobile/features/card/business_logic_layer/card_controller.dart';
|
||||
import 'package:taafee_mobile/features/card/data_layer/model/appointment.dart';
|
||||
import 'package:taafee_mobile/features/card/presentation_layer/widgets/card_details.dart';
|
||||
|
|
@ -16,6 +17,8 @@ import '../../../../common/widgets/toast.dart';
|
|||
import '../../../../core/url launcher/url_launcher_service.dart';
|
||||
import '../../../home/business_logic_layer/home_controller.dart';
|
||||
import '../../data_layer/model/card_model.dart';
|
||||
import '../widgets/appointment_details.dart';
|
||||
import '../widgets/appointment_widget.dart';
|
||||
import '../widgets/card_email.dart';
|
||||
import '../widgets/card_header.dart';
|
||||
import '../widgets/card_image.dart';
|
||||
|
|
@ -30,6 +33,93 @@ class CardDetailsScreen extends StatelessWidget {
|
|||
final ChatController chatController = Get.find<ChatController>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
void appointmentSchedulingDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
actionsPadding: EdgeInsets.zero,
|
||||
title: RegularTextWidget(
|
||||
"available_times".tr,
|
||||
fontSize: 14,
|
||||
),
|
||||
content: Obx(() {
|
||||
if (cardController.availableAppointmentsState.loading ||
|
||||
homeController.appointmentSchedulingState.loading) {
|
||||
return SizedBox(
|
||||
width: 320,
|
||||
height: 320,
|
||||
child: const CircularProgressIndicator().center());
|
||||
}
|
||||
return SizedBox(
|
||||
height: 320,
|
||||
width: 320,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
SizedBox(
|
||||
height: 280,
|
||||
child: ListView.separated(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: 12,
|
||||
itemBuilder: (context, index) {
|
||||
DateTime dateTime = Utils.generateRandomDateTime();
|
||||
return AppointmentWidget(dateTime: dateTime)
|
||||
.onTap(() {
|
||||
homeController.scheduleAnAppointment(
|
||||
Appointment(
|
||||
cardId: cardModel.id,
|
||||
dateTime: dateTime,
|
||||
user: homeController.user.value!),
|
||||
onSuccess: () {
|
||||
Toast.showToast(
|
||||
'appointment_scheduled_successfully.'.tr);
|
||||
RoutingManager.back();
|
||||
});
|
||||
});
|
||||
},
|
||||
separatorBuilder: (context, index) {
|
||||
return const Divider();
|
||||
},
|
||||
).paddingOnly(left: 8, right: 8, top: 8, bottom: 8),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void appointmentDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20)),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
actionsPadding: EdgeInsets.zero,
|
||||
title: RegularTextWidget(
|
||||
"appointment".tr,
|
||||
textAlign: TextAlign.center,
|
||||
fontSize: 14,
|
||||
),
|
||||
content: AppointmentDetails(
|
||||
dateTime:
|
||||
homeController.currentCardAppointment.value!.dateTime),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
cardController.updateCardNetworkImageUrls(cardModel.cardImages);
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backGroundColor,
|
||||
|
|
@ -262,18 +352,32 @@ class CardDetailsScreen extends StatelessWidget {
|
|||
}),
|
||||
),
|
||||
if (Responsive.isTablet())
|
||||
Visibility(
|
||||
Obx(() {
|
||||
return Visibility(
|
||||
visible: homeController.user.value!.id != cardModel.user.id,
|
||||
child: ButtonWidget(
|
||||
child: (homeController.currentCardAppointment.value != null)
|
||||
? ButtonWidget(
|
||||
haveIcon: true,
|
||||
onTap: () {
|
||||
homeController.scheduleAnAppointment(
|
||||
Appointment(
|
||||
cardId: cardModel.id,
|
||||
dateTime: DateTime.now(),
|
||||
user: homeController.user.value!),
|
||||
);
|
||||
appointmentDialog(context);
|
||||
},
|
||||
fontSize: 12,
|
||||
color: AppColors.secondaryColor,
|
||||
title: 'you_have_an_appointment'.tr,
|
||||
child: const Icon(
|
||||
Icons.schedule,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
.paddingSymmetric(horizontal: 4)
|
||||
.expanded(Responsive.isTablet() ? 1 : 5)
|
||||
: ButtonWidget(
|
||||
onTap: () {
|
||||
cardController.getAvailableAppointments();
|
||||
appointmentSchedulingDialog(context);
|
||||
},
|
||||
haveIcon: true,
|
||||
fontSize: 12,
|
||||
title: 'schedule_an_appointment'.tr,
|
||||
color: AppColors.secondaryColor,
|
||||
child: const Icon(
|
||||
|
|
@ -281,9 +385,10 @@ class CardDetailsScreen extends StatelessWidget {
|
|||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
.paddingSymmetric(horizontal: 10)
|
||||
.paddingSymmetric(horizontal: 4)
|
||||
.expanded(Responsive.isTablet() ? 1 : 5),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
).paddingSymmetric(
|
||||
vertical: Responsive.isTablet() ? 20 : 4,
|
||||
|
|
@ -299,22 +404,24 @@ class CardDetailsScreen extends StatelessWidget {
|
|||
child:
|
||||
(homeController.currentCardAppointment.value != null)
|
||||
? ButtonWidget(
|
||||
onTap: () {},
|
||||
haveIcon: true,
|
||||
onTap: () {
|
||||
appointmentDialog(context);
|
||||
},
|
||||
fontSize: 12,
|
||||
color: AppColors.secondaryColor,
|
||||
title:
|
||||
'${'you_have_an_appointment_on'.tr} ${homeController.currentCardAppointment.value!.dateTime.toString().substring(0, 11)}',
|
||||
title: 'you_have_an_appointment'.tr,
|
||||
child: const Icon(
|
||||
Icons.schedule,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
.paddingSymmetric(horizontal: 4)
|
||||
.expanded(Responsive.isTablet() ? 1 : 5)
|
||||
: ButtonWidget(
|
||||
onTap: () {
|
||||
homeController.scheduleAnAppointment(
|
||||
Appointment(
|
||||
cardId: cardModel.id,
|
||||
dateTime: DateTime.now(),
|
||||
user: homeController.user.value!),
|
||||
);
|
||||
cardController.getAvailableAppointments();
|
||||
appointmentSchedulingDialog(context);
|
||||
},
|
||||
haveIcon: true,
|
||||
fontSize: 12,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../../common/const/const.dart';
|
||||
import '../../../../common/widgets/text.dart';
|
||||
import '../../../home/business_logic_layer/home_controller.dart';
|
||||
|
||||
class AppointmentDetails extends StatelessWidget {
|
||||
AppointmentDetails({
|
||||
super.key,
|
||||
required this.dateTime,
|
||||
});
|
||||
|
||||
final DateTime dateTime;
|
||||
final HomeController homeController = Get.find<HomeController>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 320,
|
||||
height: 240,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
RegularTextWidget(
|
||||
'you_have_an_appointment_on'.tr,
|
||||
fontSize: 15,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: AppColors.secondaryColor.withOpacity(0.7),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
RegularTextWidget(
|
||||
DateFormat.EEEE().format(dateTime).tr,
|
||||
fontSize: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.calendar_month,
|
||||
color: Colors.white,
|
||||
),
|
||||
RegularTextWidget(
|
||||
' ${dateTime.day} / ${dateTime.month}',
|
||||
fontSize: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.schedule,
|
||||
color: Colors.white,
|
||||
),
|
||||
if (!homeController.isArabic.value)
|
||||
RegularTextWidget(
|
||||
' ${dateTime.hour % 12} : ${dateTime.minute >= 10 ? dateTime.minute : '0${dateTime.minute}'} ${(dateTime.hour <= 12) ? 'am'.tr : 'pm'.tr}',
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
),
|
||||
if (homeController.isArabic.value)
|
||||
RegularTextWidget(
|
||||
' ${dateTime.minute >= 10 ? dateTime.minute : '0${dateTime.minute}'} : ${dateTime.hour % 12} ${(dateTime.hour <= 12) ? 'am'.tr : 'pm'.tr}',
|
||||
color: Colors.white,
|
||||
fontSize: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
).paddingSymmetric(horizontal: 16)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:taafee_mobile/features/home/business_logic_layer/home_controller.dart';
|
||||
|
||||
import '../../../../common/const/const.dart';
|
||||
import '../../../../common/widgets/text.dart';
|
||||
|
||||
class AppointmentWidget extends StatelessWidget {
|
||||
AppointmentWidget({
|
||||
super.key,
|
||||
required this.dateTime,
|
||||
});
|
||||
final DateTime dateTime;
|
||||
final HomeController homeController = Get.find<HomeController>();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: AppColors.tailAuthColor.withOpacity(0.5),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 100,
|
||||
height: 40,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.date_range,
|
||||
color: Colors.black87,
|
||||
),
|
||||
RegularTextWidget(
|
||||
DateFormat.EEEE().format(dateTime).tr,
|
||||
color: Colors.black87,
|
||||
),
|
||||
RegularTextWidget(
|
||||
'${dateTime.day} / ${dateTime.month}',
|
||||
color: Colors.black87,
|
||||
),
|
||||
if (!homeController.isArabic.value)
|
||||
RegularTextWidget(
|
||||
'${dateTime.hour % 12} : ${dateTime.minute >= 10 ? dateTime.minute : '0${dateTime.minute}'} ${(dateTime.hour <= 12) ? 'am'.tr : 'pm'.tr}',
|
||||
color: Colors.black87,
|
||||
),
|
||||
if (homeController.isArabic.value)
|
||||
RegularTextWidget(
|
||||
'${dateTime.minute >= 10 ? dateTime.minute : '0${dateTime.minute}'} : ${dateTime.hour % 12} ${(dateTime.hour <= 12) ? 'am'.tr : 'pm'.tr}',
|
||||
color: Colors.black87,
|
||||
),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -182,7 +182,14 @@ class HomeController extends GetxController {
|
|||
user.refresh();
|
||||
}
|
||||
|
||||
void scheduleAnAppointment(Appointment appointment) async {
|
||||
RxFuture<void> appointmentSchedulingState = RxFuture(null);
|
||||
void scheduleAnAppointment(Appointment appointment,
|
||||
{void Function()? onSuccess}) async {
|
||||
appointmentSchedulingState.observe(
|
||||
(value) async {
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
},
|
||||
onSuccess: (response) async {
|
||||
user.update((val) {
|
||||
if (val!.appointments != null) {
|
||||
val.appointments!.add(appointment);
|
||||
|
|
@ -197,6 +204,9 @@ class HomeController extends GetxController {
|
|||
currentCardAppointment = appointment.obs;
|
||||
currentCardAppointment.refresh();
|
||||
user.refresh();
|
||||
onSuccess?.call();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Appointment? getCardAppointment(int cardId) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user