Add submitting state to shift approval UI
Add a submitting state to the shift approval flow: include new "submitting" strings in English and Spanish localization files; thread submittingShiftId from ShiftsPage into MyShiftsTab and HistoryShiftsTab and compute per-shift isSubmitting; add isSubmitting prop to ShiftCard and ShiftCardApprovalFooter. When a shift is being submitted the footer shows "SUBMITTING..." and a small CircularProgressIndicator instead of the submit button.
This commit is contained in:
@@ -1360,7 +1360,8 @@
|
||||
"timesheet_submitted": "Timesheet submitted for client approval",
|
||||
"checked_in": "Checked in",
|
||||
"submitted": "SUBMITTED",
|
||||
"ready_to_submit": "READY TO SUBMIT"
|
||||
"ready_to_submit": "READY TO SUBMIT",
|
||||
"submitting": "SUBMITTING..."
|
||||
},
|
||||
"shift_location": {
|
||||
"could_not_open_maps": "Could not open maps"
|
||||
|
||||
@@ -1355,7 +1355,8 @@
|
||||
"timesheet_submitted": "Hoja de tiempo enviada para aprobación del cliente",
|
||||
"checked_in": "Registrado",
|
||||
"submitted": "ENVIADO",
|
||||
"ready_to_submit": "LISTO PARA ENVIAR"
|
||||
"ready_to_submit": "LISTO PARA ENVIAR",
|
||||
"submitting": "ENVIANDO..."
|
||||
},
|
||||
"shift_location": {
|
||||
"could_not_open_maps": "No se pudo abrir mapas"
|
||||
|
||||
@@ -266,6 +266,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
cancelledShifts: cancelledShifts,
|
||||
initialDate: _selectedDate,
|
||||
submittedShiftIds: state.submittedShiftIds,
|
||||
submittingShiftId: state.submittingShiftId,
|
||||
);
|
||||
case ShiftTabType.find:
|
||||
if (availableLoading) {
|
||||
@@ -282,6 +283,7 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
||||
return HistoryShiftsTab(
|
||||
historyShifts: historyShifts,
|
||||
submittedShiftIds: state.submittedShiftIds,
|
||||
submittingShiftId: state.submittingShiftId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ class ShiftCard extends StatelessWidget {
|
||||
this.onSubmitForApproval,
|
||||
this.showApprovalAction = false,
|
||||
this.isSubmitted = false,
|
||||
this.isSubmitting = false,
|
||||
this.onAccept,
|
||||
this.onDecline,
|
||||
this.isAccepting = false,
|
||||
@@ -41,6 +42,9 @@ class ShiftCard extends StatelessWidget {
|
||||
/// Whether the timesheet has already been submitted.
|
||||
final bool isSubmitted;
|
||||
|
||||
/// Whether the timesheet submission is currently in progress.
|
||||
final bool isSubmitting;
|
||||
|
||||
/// Callback when the accept action is pressed (pending assignments only).
|
||||
final VoidCallback? onAccept;
|
||||
|
||||
@@ -91,6 +95,7 @@ class ShiftCard extends StatelessWidget {
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
ShiftCardApprovalFooter(
|
||||
isSubmitted: isSubmitted,
|
||||
isSubmitting: isSubmitting,
|
||||
onSubmit: onSubmitForApproval,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,12 +8,16 @@ class ShiftCardApprovalFooter extends StatelessWidget {
|
||||
const ShiftCardApprovalFooter({
|
||||
super.key,
|
||||
required this.isSubmitted,
|
||||
this.isSubmitting = false,
|
||||
this.onSubmit,
|
||||
});
|
||||
|
||||
/// Whether the timesheet has already been submitted.
|
||||
final bool isSubmitted;
|
||||
|
||||
/// Whether the submission is currently in progress.
|
||||
final bool isSubmitting;
|
||||
|
||||
/// Callback when the submit button is pressed.
|
||||
final VoidCallback? onSubmit;
|
||||
|
||||
@@ -23,14 +27,25 @@ class ShiftCardApprovalFooter extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
isSubmitted
|
||||
isSubmitting
|
||||
? context.t.staff_shifts.my_shift_card.submitting
|
||||
: isSubmitted
|
||||
? context.t.staff_shifts.my_shift_card.submitted
|
||||
: context.t.staff_shifts.my_shift_card.ready_to_submit,
|
||||
style: UiTypography.footnote2b.copyWith(
|
||||
color: isSubmitted ? UiColors.textSuccess : UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
if (!isSubmitted)
|
||||
if (isSubmitting)
|
||||
const SizedBox(
|
||||
height: UiConstants.space4,
|
||||
width: UiConstants.space4,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: UiColors.primary,
|
||||
),
|
||||
)
|
||||
else if (!isSubmitted)
|
||||
UiButton.secondary(
|
||||
text: context.t.staff_shifts.my_shift_card.submit_for_approval,
|
||||
size: UiButtonSize.small,
|
||||
|
||||
@@ -17,6 +17,7 @@ class HistoryShiftsTab extends StatelessWidget {
|
||||
super.key,
|
||||
required this.historyShifts,
|
||||
this.submittedShiftIds = const <String>{},
|
||||
this.submittingShiftId,
|
||||
});
|
||||
|
||||
/// Completed shifts.
|
||||
@@ -25,6 +26,9 @@ class HistoryShiftsTab extends StatelessWidget {
|
||||
/// Set of shift IDs that have been successfully submitted for approval.
|
||||
final Set<String> submittedShiftIds;
|
||||
|
||||
/// The shift ID currently being submitted (null when idle).
|
||||
final String? submittingShiftId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (historyShifts.isEmpty) {
|
||||
@@ -44,6 +48,8 @@ class HistoryShiftsTab extends StatelessWidget {
|
||||
(CompletedShift shift) {
|
||||
final bool isSubmitted =
|
||||
submittedShiftIds.contains(shift.shiftId);
|
||||
final bool isSubmitting =
|
||||
submittingShiftId == shift.shiftId;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||
child: ShiftCard(
|
||||
@@ -52,6 +58,7 @@ class HistoryShiftsTab extends StatelessWidget {
|
||||
Modular.to.toShiftDetailsById(shift.shiftId),
|
||||
showApprovalAction: !isSubmitted,
|
||||
isSubmitted: isSubmitted,
|
||||
isSubmitting: isSubmitting,
|
||||
onSubmitForApproval: () {
|
||||
ReadContext(context).read<ShiftsBloc>().add(
|
||||
SubmitForApprovalEvent(shiftId: shift.shiftId),
|
||||
|
||||
@@ -22,6 +22,7 @@ class MyShiftsTab extends StatefulWidget {
|
||||
required this.cancelledShifts,
|
||||
this.initialDate,
|
||||
this.submittedShiftIds = const <String>{},
|
||||
this.submittingShiftId,
|
||||
});
|
||||
|
||||
/// Assigned shifts for the current week.
|
||||
@@ -39,6 +40,9 @@ class MyShiftsTab extends StatefulWidget {
|
||||
/// Set of shift IDs that have been successfully submitted for approval.
|
||||
final Set<String> submittedShiftIds;
|
||||
|
||||
/// The shift ID currently being submitted (null when idle).
|
||||
final String? submittingShiftId;
|
||||
|
||||
@override
|
||||
State<MyShiftsTab> createState() => _MyShiftsTabState();
|
||||
}
|
||||
@@ -392,6 +396,8 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
||||
shift.status == AssignmentStatus.completed;
|
||||
final bool isSubmitted =
|
||||
widget.submittedShiftIds.contains(shift.shiftId);
|
||||
final bool isSubmitting =
|
||||
widget.submittingShiftId == shift.shiftId;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
@@ -403,6 +409,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
||||
.toShiftDetailsById(shift.shiftId),
|
||||
showApprovalAction: isCompleted,
|
||||
isSubmitted: isSubmitted,
|
||||
isSubmitting: isSubmitting,
|
||||
onSubmitForApproval: () {
|
||||
ReadContext(context).read<ShiftsBloc>().add(
|
||||
SubmitForApprovalEvent(
|
||||
|
||||
Reference in New Issue
Block a user