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:
Achintha Isuru
2026-03-18 15:26:53 -04:00
parent b9d64bd53b
commit 77f8b8511c
7 changed files with 44 additions and 6 deletions

View File

@@ -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"

View File

@@ -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"

View File

@@ -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,
);
}
}

View File

@@ -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,
),
],

View File

@@ -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
? context.t.staff_shifts.my_shift_card.submitted
: context.t.staff_shifts.my_shift_card.ready_to_submit,
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,

View File

@@ -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),

View File

@@ -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(