fist commit ftc staff app clone
This commit is contained in:
321
lib/view/custom_widgets/rota/custom_calendar_widget.dart
Normal file
321
lib/view/custom_widgets/rota/custom_calendar_widget.dart
Normal file
@@ -0,0 +1,321 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_calendar_carousel/classes/event.dart';
|
||||
import 'package:flutter_calendar_carousel/flutter_calendar_carousel.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../ftc_mobile_app.dart';
|
||||
|
||||
// class SetMonthAndYearWidget extends StatelessWidget {
|
||||
// const SetMonthAndYearWidget({
|
||||
// super.key,
|
||||
// required this.currentMonthName,
|
||||
// required this.previousMonthTap,
|
||||
// required this.nextMonthTap,
|
||||
// });
|
||||
//
|
||||
// final String currentMonthName;
|
||||
// final VoidCallback previousMonthTap;
|
||||
// final VoidCallback nextMonthTap;
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.only(left: 20.0, right: 20.0),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// GestureDetector(
|
||||
// onTap: previousMonthTap,
|
||||
// child: Transform.rotate(
|
||||
// angle: 3.1415 * 2.5,
|
||||
// child: const Icon(Icons.arrow_drop_down),
|
||||
// ),
|
||||
// ),
|
||||
// CustomTextWidget(
|
||||
// isExpanded: false,
|
||||
// text: currentMonthName,
|
||||
// textAlign: TextAlign.center,
|
||||
// fontColor: CustomAppColors.kBlackColor,
|
||||
// fontSize: 13,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// GestureDetector(
|
||||
// onTap: nextMonthTap,
|
||||
// child: Transform.rotate(
|
||||
// angle: 3.1415 * 3.5,
|
||||
// child: const Icon(Icons.arrow_drop_down),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
typedef OnRangeSelect = Function(
|
||||
DateTime rangeStart, DateTime? rangeEnd, List<String> selectedDates);
|
||||
|
||||
class CalendarWidget extends StatefulWidget {
|
||||
static final dateFormat = DateFormat("dd-MM-yyyy");
|
||||
|
||||
const CalendarWidget({
|
||||
super.key,
|
||||
this.minDate,
|
||||
this.maxDate,
|
||||
this.onEventTap,
|
||||
required this.targetDateTime,
|
||||
required this.markedDatesMap,
|
||||
this.onDayTap,
|
||||
this.rangeStart,
|
||||
this.rangeEnd,
|
||||
this.canSelectRange = false,
|
||||
this.onRangeSelect,
|
||||
});
|
||||
|
||||
final DateTime? minDate;
|
||||
final DateTime? maxDate;
|
||||
final DateTime targetDateTime;
|
||||
final EventList<Event> markedDatesMap;
|
||||
final ValueChanged<List<Event>>? onEventTap;
|
||||
final Function(DateTime, List<Event>)? onDayTap;
|
||||
final bool canSelectRange;
|
||||
|
||||
///[selectedDates] will be in format dd-MM-yyyy
|
||||
final OnRangeSelect? onRangeSelect;
|
||||
final DateTime? rangeStart;
|
||||
final DateTime? rangeEnd;
|
||||
|
||||
@override
|
||||
State<CalendarWidget> createState() => _CalendarWidgetState();
|
||||
|
||||
///This method checks if [start] and [end] dates are at proper position i.e.,
|
||||
///[start] date should be before [end] date. If not, this method corrects the two dates and
|
||||
///return proper [start] [end] dates
|
||||
static ({DateTime start, DateTime end}) rectifyStartEndDates(
|
||||
DateTime start, DateTime end) {
|
||||
final first = start.isBefore(end) ? start : end;
|
||||
final last = end.isAfter(start) ? end : start;
|
||||
return (start: first, end: last);
|
||||
}
|
||||
|
||||
static List<String> getDatesFromStartEndRange(DateTime start, DateTime end) {
|
||||
final List<String> dates = [];
|
||||
// final first = start.isBefore(end) ? start : end;
|
||||
// final last = end.isAfter(start) ? end : start;
|
||||
|
||||
final record = rectifyStartEndDates(start, end);
|
||||
for (int i = 0; i <= record.end.difference(record.start).inDays; i++) {
|
||||
dates.add(dateFormat.format(record.start.add(i.days)));
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
static Widget shiftIcon(String day) => CircleAvatar(
|
||||
backgroundColor: CustomAppColors.kSecondaryColor,
|
||||
child: Text(
|
||||
day,
|
||||
style:
|
||||
const TextStyle(color: CustomAppColors.kWhiteColor, fontSize: 13),
|
||||
),
|
||||
);
|
||||
|
||||
static Widget underlineIcon() => Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
width: 3.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _CalendarWidgetState extends State<CalendarWidget> {
|
||||
//Selected dates will be in format dd-MM-yyyy
|
||||
final selectedDates = RxList<String>();
|
||||
|
||||
DateTime? start, end;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.canSelectRange &&
|
||||
widget.rangeStart != null &&
|
||||
widget.rangeEnd != null) {
|
||||
start = widget.rangeStart;
|
||||
end = widget.rangeEnd;
|
||||
|
||||
selectedDates.value =
|
||||
CalendarWidget.getDatesFromStartEndRange(start!, end!);
|
||||
} else {
|
||||
selectedDates.value = [
|
||||
CalendarWidget.dateFormat.format(widget.targetDateTime)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double width = MediaQuery.of(context).size.width;
|
||||
double height = MediaQuery.of(context).size.height;
|
||||
double textScaleFactor = MediaQuery.of(context).textScaleFactor;
|
||||
double itemHeight = (height * textScaleFactor - kToolbarHeight - 24) / 4;
|
||||
double calHeight = (height * textScaleFactor) / 2.9;
|
||||
double itemWidth = width * 0.7;
|
||||
|
||||
return Container(
|
||||
height: calHeight,
|
||||
margin: const EdgeInsets.only(top: 10, bottom: 3),
|
||||
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
|
||||
child: CalendarCarousel<Event>(
|
||||
targetDateTime: widget.targetDateTime,
|
||||
firstDayOfWeek: 0,
|
||||
dayPadding: 1.0,
|
||||
minSelectedDate: widget.minDate,
|
||||
maxSelectedDate: widget.maxDate,
|
||||
todayTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlackColor,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
weekdayTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlueColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
weekendTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlackColor,
|
||||
fontSize: 13.0,
|
||||
),
|
||||
customDayBuilder: (bool isSelectable,
|
||||
int index,
|
||||
bool isSelectedDay,
|
||||
bool isToday,
|
||||
bool isPrevMonthDay,
|
||||
TextStyle textStyle,
|
||||
bool isNextMonthDay,
|
||||
bool isThisMonthDay,
|
||||
DateTime day) {
|
||||
if (selectedDates.contains(CalendarWidget.dateFormat.format(day))) {
|
||||
return Container(
|
||||
width: double.maxFinite,
|
||||
height: double.maxFinite,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
day.day.toString(),
|
||||
style: textStyle.copyWith(color: Colors.white),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onDayPressed: (DateTime date, List<Event> events) {
|
||||
if (widget.onEventTap != null) {
|
||||
widget.onEventTap!(events);
|
||||
}
|
||||
if (widget.onDayTap != null) {
|
||||
widget.onDayTap!(date, events);
|
||||
}
|
||||
if (widget.canSelectRange) {
|
||||
rangeSelectionHandler(date);
|
||||
}
|
||||
},
|
||||
childAspectRatio: textScaleFactor > 1.5
|
||||
? ((itemWidth / itemHeight) * 1.1)
|
||||
: textScaleFactor > 1
|
||||
? ((itemWidth / itemHeight) * 1.05)
|
||||
: (itemWidth / itemHeight),
|
||||
todayButtonColor: Colors.transparent,
|
||||
todayBorderColor: Colors.transparent,
|
||||
daysHaveCircularBorder: true,
|
||||
isScrollable: true,
|
||||
shouldShowTransform: false,
|
||||
pageSnapping: true,
|
||||
// selectedDateTime: DateTime(2024, 5, 25),
|
||||
thisMonthDayBorderColor: Colors.transparent,
|
||||
daysTextStyle: const TextStyle(
|
||||
fontSize: 13.0,
|
||||
color: CustomAppColors.kBlackColor,
|
||||
),
|
||||
weekFormat: false,
|
||||
showOnlyCurrentMonthDate: true,
|
||||
width: width,
|
||||
onHeaderTitlePressed: () {},
|
||||
customGridViewPhysics: const NeverScrollableScrollPhysics(),
|
||||
markedDatesMap: widget.markedDatesMap,
|
||||
//marked up dates
|
||||
markedDateIconBuilder: (event) => event.icon,
|
||||
markedDateCustomTextStyle: TextStyle(
|
||||
fontSize: 13.sp,
|
||||
color: CustomAppColors.kWhiteColor,
|
||||
),
|
||||
markedDateShowIcon: true,
|
||||
markedDateIconMaxShown: 1,
|
||||
markedDateMoreShowTotal: null,
|
||||
showHeader: true,
|
||||
leftButtonIcon: const Icon(
|
||||
Icons.arrow_left,
|
||||
color: Colors.black,
|
||||
),
|
||||
rightButtonIcon: const Icon(
|
||||
Icons.arrow_right,
|
||||
color: Colors.black,
|
||||
),
|
||||
headerTextStyle: const TextStyle(
|
||||
color: CustomAppColors.kBlackColor,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold),
|
||||
onCalendarChanged: (DateTime date) {},
|
||||
onDayLongPressed: (DateTime date) {},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
rangeSelectionHandler(DateTime selectedDay) {
|
||||
final d = CalendarWidget.dateFormat.format(selectedDay);
|
||||
|
||||
if (selectedDates.contains(d)) {
|
||||
selectedDates.remove(d);
|
||||
} else {
|
||||
selectedDates.add(d);
|
||||
}
|
||||
|
||||
//meaning either no range selected or range already been selected
|
||||
if (start == null || (start != null && end != null)) {
|
||||
start = selectedDay;
|
||||
end = null;
|
||||
selectedDates
|
||||
..clear()
|
||||
..add(CalendarWidget.dateFormat.format(selectedDay));
|
||||
|
||||
if (widget.onRangeSelect != null) {
|
||||
widget.onRangeSelect!(start!, null, selectedDates());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Already selected start date now selecting range end date
|
||||
if (end == null) {
|
||||
final record = CalendarWidget.rectifyStartEndDates(start!, selectedDay);
|
||||
|
||||
start = record.start;
|
||||
end = record.end;
|
||||
|
||||
selectedDates.clear();
|
||||
selectedDates.value =
|
||||
CalendarWidget.getDatesFromStartEndRange(start!, end!);
|
||||
}
|
||||
print("start: ${start.toString()}\nEnd:${end.toString()}");
|
||||
|
||||
if (widget.onRangeSelect != null) {
|
||||
widget.onRangeSelect!(start!, end, selectedDates());
|
||||
}
|
||||
}
|
||||
}
|
||||
3
lib/view/custom_widgets/rota/export_rota.dart
Normal file
3
lib/view/custom_widgets/rota/export_rota.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
export 'custom_calendar_widget.dart';
|
||||
export "package:ftc_mobile_app/dialogs/widgets/shift_dialog.dart";
|
||||
export 'rota_list_item.dart';
|
||||
130
lib/view/custom_widgets/rota/rota_list_item.dart
Normal file
130
lib/view/custom_widgets/rota/rota_list_item.dart
Normal file
@@ -0,0 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:ftc_mobile_app/models/rota/WeekArrayData.dart';
|
||||
import 'package:ftc_mobile_app/utilities/extensions/custom_extensions.dart';
|
||||
import '../../../ftc_mobile_app.dart';
|
||||
|
||||
class RotaWidget extends StatelessWidget {
|
||||
const RotaWidget(
|
||||
{super.key, required this.data, required this.onViewShiftTap});
|
||||
|
||||
final DaysArrayData data;
|
||||
final Function() onViewShiftTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
// height: 150,
|
||||
alignment: Alignment.center,
|
||||
padding: EdgeInsets.all(10.sp),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
width: 6.r,
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
top: const BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
right: const BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
bottom: const BorderSide(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
),
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(2.0.r)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
text: data.locationId?.shiftLocationName ?? "",
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
children: [
|
||||
const TextSpan(text: ' ('),
|
||||
TextSpan(
|
||||
text: ((data.serviceUserId?.displayName ??
|
||||
"Unassigned"))
|
||||
.trim(),
|
||||
style: TextStyle(
|
||||
color: data.serviceUserId?.displayName
|
||||
.isNotNullOrEmpty() ==
|
||||
true
|
||||
? Colors.black
|
||||
: Colors.red)),
|
||||
const TextSpan(text: ')'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
CustomTextWidget(
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 10.sp,
|
||||
text: DateFormatter().getRotaNewDate(
|
||||
shiftDate: data.shiftDate ?? 0,
|
||||
startShiftTime: data.shiftStartTime ?? '0',
|
||||
endShiftTime: data.shiftEndTime ?? '0',
|
||||
),
|
||||
isExpanded: false),
|
||||
6.verticalSpace,
|
||||
CustomTextWidget(
|
||||
text: '${int.parse(data.workHrs?.toString() ?? "0")}hrs',
|
||||
fontSize: 14.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
isExpanded: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
10.verticalSpace,
|
||||
ShiftButtonWidget(onTap: onViewShiftTap)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ShiftButtonWidget extends StatelessWidget {
|
||||
final VoidCallback onTap;
|
||||
|
||||
const ShiftButtonWidget({
|
||||
super.key,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: 26.h,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: CustomAppColors.kSecondaryColor,
|
||||
borderRadius: BorderRadius.circular(2.r),
|
||||
),
|
||||
child: const Text(
|
||||
"View Shift",
|
||||
style: TextStyle(color: CustomAppColors.kPrimaryColor),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user