diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart index 2b42b3de..2d2a9f8c 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_bloc.dart @@ -32,7 +32,7 @@ class ShiftsBloc extends Bloc required this.getCancelledShifts, required this.getHistoryShifts, required this.getProfileCompletion, - }) : super(ShiftsInitial()) { + }) : super(const ShiftsState()) { on(_onLoadShifts); on(_onLoadHistoryShifts); on(_onLoadAvailableShifts); @@ -46,8 +46,8 @@ class ShiftsBloc extends Bloc LoadShiftsEvent event, Emitter emit, ) async { - if (state is! ShiftsLoaded) { - emit(ShiftsLoading()); + if (state.status != ShiftsStatus.loaded) { + emit(state.copyWith(status: ShiftsStatus.loading)); } await handleError( @@ -58,22 +58,26 @@ class ShiftsBloc extends Bloc GetMyShiftsArguments(start: days.first, end: days.last), ); - emit(ShiftsLoaded( - myShifts: myShiftsResult, - pendingShifts: const [], - cancelledShifts: const [], - availableShifts: const [], - historyShifts: const [], - availableLoading: false, - availableLoaded: false, - historyLoading: false, - historyLoaded: false, - myShiftsLoaded: true, - searchQuery: '', - jobType: 'all', - )); + emit( + state.copyWith( + status: ShiftsStatus.loaded, + myShifts: myShiftsResult, + pendingShifts: const [], + cancelledShifts: const [], + availableShifts: const [], + historyShifts: const [], + availableLoading: false, + availableLoaded: false, + historyLoading: false, + historyLoaded: false, + myShiftsLoaded: true, + searchQuery: '', + jobType: 'all', + ), + ); }, - onError: (String errorKey) => ShiftsError(errorKey), + onError: (String errorKey) => + state.copyWith(status: ShiftsStatus.error, errorMessage: errorKey), ); } @@ -81,27 +85,29 @@ class ShiftsBloc extends Bloc LoadHistoryShiftsEvent event, Emitter emit, ) async { - final currentState = state; - if (currentState is! ShiftsLoaded) return; - if (currentState.historyLoading || currentState.historyLoaded) return; + if (state.status != ShiftsStatus.loaded) return; + if (state.historyLoading || state.historyLoaded) return; - emit(currentState.copyWith(historyLoading: true)); + emit(state.copyWith(historyLoading: true)); await handleError( emit: emit.call, action: () async { final historyResult = await getHistoryShifts(); - emit(currentState.copyWith( - myShiftsLoaded: true, - historyShifts: historyResult, - historyLoading: false, - historyLoaded: true, - )); + emit( + state.copyWith( + myShiftsLoaded: true, + historyShifts: historyResult, + historyLoading: false, + historyLoaded: true, + ), + ); }, onError: (String errorKey) { - if (state is ShiftsLoaded) { - return (state as ShiftsLoaded).copyWith(historyLoading: false); - } - return ShiftsError(errorKey); + return state.copyWith( + historyLoading: false, + status: ShiftsStatus.error, + errorMessage: errorKey, + ); }, ); } @@ -110,33 +116,32 @@ class ShiftsBloc extends Bloc LoadAvailableShiftsEvent event, Emitter emit, ) async { - final currentState = state; - if (currentState is! ShiftsLoaded) return; - if (!event.force && - (currentState.availableLoading || currentState.availableLoaded)) { + if (state.status != ShiftsStatus.loaded) return; + if (!event.force && (state.availableLoading || state.availableLoaded)) { return; } - emit(currentState.copyWith( - availableLoading: true, - availableLoaded: false, - )); + emit(state.copyWith(availableLoading: true, availableLoaded: false)); await handleError( emit: emit.call, action: () async { - final availableResult = - await getAvailableShifts(const GetAvailableShiftsArguments()); - emit(currentState.copyWith( - availableShifts: _filterPastShifts(availableResult), - availableLoading: false, - availableLoaded: true, - )); + final availableResult = await getAvailableShifts( + const GetAvailableShiftsArguments(), + ); + emit( + state.copyWith( + availableShifts: _filterPastShifts(availableResult), + availableLoading: false, + availableLoaded: true, + ), + ); }, onError: (String errorKey) { - if (state is ShiftsLoaded) { - return (state as ShiftsLoaded).copyWith(availableLoading: false); - } - return ShiftsError(errorKey); + return state.copyWith( + availableLoading: false, + status: ShiftsStatus.error, + errorMessage: errorKey, + ); }, ); } @@ -145,62 +150,51 @@ class ShiftsBloc extends Bloc LoadFindFirstEvent event, Emitter emit, ) async { - if (state is! ShiftsLoaded) { - emit(const ShiftsLoaded( - myShifts: [], - pendingShifts: [], - cancelledShifts: [], - availableShifts: [], - historyShifts: [], - availableLoading: false, - availableLoaded: false, - historyLoading: false, - historyLoaded: false, - myShiftsLoaded: false, - searchQuery: '', - jobType: 'all', - )); + if (state.status != ShiftsStatus.loaded) { + emit( + state.copyWith( + status: ShiftsStatus.loading, + myShifts: const [], + pendingShifts: const [], + cancelledShifts: const [], + availableShifts: const [], + historyShifts: const [], + availableLoading: false, + availableLoaded: false, + historyLoading: false, + historyLoaded: false, + myShiftsLoaded: false, + searchQuery: '', + jobType: 'all', + ), + ); } - final currentState = state is ShiftsLoaded ? state as ShiftsLoaded : null; - if (currentState != null && currentState.availableLoaded) return; + if (state.availableLoaded) return; - if (currentState != null) { - emit(currentState.copyWith(availableLoading: true)); - } + emit(state.copyWith(availableLoading: true)); await handleError( emit: emit.call, action: () async { - final availableResult = - await getAvailableShifts(const GetAvailableShiftsArguments()); - final loadedState = state is ShiftsLoaded - ? state as ShiftsLoaded - : const ShiftsLoaded( - myShifts: [], - pendingShifts: [], - cancelledShifts: [], - availableShifts: [], - historyShifts: [], - availableLoading: true, - availableLoaded: false, - historyLoading: false, - historyLoaded: false, - myShiftsLoaded: false, - searchQuery: '', - jobType: 'all', - ); - emit(loadedState.copyWith( - availableShifts: _filterPastShifts(availableResult), - availableLoading: false, - availableLoaded: true, - )); + final availableResult = await getAvailableShifts( + const GetAvailableShiftsArguments(), + ); + emit( + state.copyWith( + status: ShiftsStatus.loaded, + availableShifts: _filterPastShifts(availableResult), + availableLoading: false, + availableLoaded: true, + ), + ); }, onError: (String errorKey) { - if (state is ShiftsLoaded) { - return (state as ShiftsLoaded).copyWith(availableLoading: false); - } - return ShiftsError(errorKey); + return state.copyWith( + availableLoading: false, + status: ShiftsStatus.error, + errorMessage: errorKey, + ); }, ); } @@ -216,31 +210,16 @@ class ShiftsBloc extends Bloc GetMyShiftsArguments(start: event.start, end: event.end), ); - if (state is ShiftsLoaded) { - final currentState = state as ShiftsLoaded; - emit(currentState.copyWith( + emit( + state.copyWith( + status: ShiftsStatus.loaded, myShifts: myShiftsResult, myShiftsLoaded: true, - )); - return; - } - - emit(ShiftsLoaded( - myShifts: myShiftsResult, - pendingShifts: const [], - cancelledShifts: const [], - availableShifts: const [], - historyShifts: const [], - availableLoading: false, - availableLoaded: false, - historyLoading: false, - historyLoaded: false, - myShiftsLoaded: true, - searchQuery: '', - jobType: 'all', - )); + ), + ); }, - onError: (String errorKey) => ShiftsError(errorKey), + onError: (String errorKey) => + state.copyWith(status: ShiftsStatus.error, errorMessage: errorKey), ); } @@ -248,9 +227,8 @@ class ShiftsBloc extends Bloc FilterAvailableShiftsEvent event, Emitter emit, ) async { - final currentState = state; - if (currentState is ShiftsLoaded) { - if (!currentState.availableLoaded && !currentState.availableLoading) { + if (state.status == ShiftsStatus.loaded) { + if (!state.availableLoaded && !state.availableLoading) { add(LoadAvailableShiftsEvent()); return; } @@ -258,21 +236,26 @@ class ShiftsBloc extends Bloc await handleError( emit: emit.call, action: () async { - final result = await getAvailableShifts(GetAvailableShiftsArguments( - query: event.query ?? currentState.searchQuery, - type: event.jobType ?? currentState.jobType, - )); + final result = await getAvailableShifts( + GetAvailableShiftsArguments( + query: event.query ?? state.searchQuery, + type: event.jobType ?? state.jobType, + ), + ); - emit(currentState.copyWith( - availableShifts: _filterPastShifts(result), - searchQuery: event.query ?? currentState.searchQuery, - jobType: event.jobType ?? currentState.jobType, - )); + emit( + state.copyWith( + availableShifts: _filterPastShifts(result), + searchQuery: event.query ?? state.searchQuery, + jobType: event.jobType ?? state.jobType, + ), + ); }, onError: (String errorKey) { - // Stay on current state for filtering errors, maybe show a snackbar? - // For now just logging is enough via handleError mixin. - return currentState; + return state.copyWith( + status: ShiftsStatus.error, + errorMessage: errorKey, + ); }, ); } @@ -282,17 +265,14 @@ class ShiftsBloc extends Bloc CheckProfileCompletionEvent event, Emitter emit, ) async { - final currentState = state; - if (currentState is! ShiftsLoaded) return; - await handleError( emit: emit.call, action: () async { final bool isComplete = await getProfileCompletion(); - emit(currentState.copyWith(profileComplete: isComplete)); + emit(state.copyWith(profileComplete: isComplete)); }, onError: (String errorKey) { - return currentState.copyWith(profileComplete: false); + return state.copyWith(profileComplete: false); }, ); } diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart index 48e2eefe..f9e108d5 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/blocs/shifts/shifts_state.dart @@ -1,18 +1,9 @@ part of 'shifts_bloc.dart'; -@immutable -sealed class ShiftsState extends Equatable { - const ShiftsState(); - - @override - List get props => []; -} +enum ShiftsStatus { initial, loading, loaded, error } -class ShiftsInitial extends ShiftsState {} - -class ShiftsLoading extends ShiftsState {} - -class ShiftsLoaded extends ShiftsState { +class ShiftsState extends Equatable { + final ShiftsStatus status; final List myShifts; final List pendingShifts; final List cancelledShifts; @@ -26,24 +17,28 @@ class ShiftsLoaded extends ShiftsState { final String searchQuery; final String jobType; final bool? profileComplete; + final String? errorMessage; - const ShiftsLoaded({ - required this.myShifts, - required this.pendingShifts, - required this.cancelledShifts, - required this.availableShifts, - required this.historyShifts, - required this.availableLoading, - required this.availableLoaded, - required this.historyLoading, - required this.historyLoaded, - required this.myShiftsLoaded, - required this.searchQuery, - required this.jobType, + const ShiftsState({ + this.status = ShiftsStatus.initial, + this.myShifts = const [], + this.pendingShifts = const [], + this.cancelledShifts = const [], + this.availableShifts = const [], + this.historyShifts = const [], + this.availableLoading = false, + this.availableLoaded = false, + this.historyLoading = false, + this.historyLoaded = false, + this.myShiftsLoaded = false, + this.searchQuery = '', + this.jobType = 'all', this.profileComplete, + this.errorMessage, }); - ShiftsLoaded copyWith({ + ShiftsState copyWith({ + ShiftsStatus? status, List? myShifts, List? pendingShifts, List? cancelledShifts, @@ -57,8 +52,10 @@ class ShiftsLoaded extends ShiftsState { String? searchQuery, String? jobType, bool? profileComplete, + String? errorMessage, }) { - return ShiftsLoaded( + return ShiftsState( + status: status ?? this.status, myShifts: myShifts ?? this.myShifts, pendingShifts: pendingShifts ?? this.pendingShifts, cancelledShifts: cancelledShifts ?? this.cancelledShifts, @@ -72,32 +69,26 @@ class ShiftsLoaded extends ShiftsState { searchQuery: searchQuery ?? this.searchQuery, jobType: jobType ?? this.jobType, profileComplete: profileComplete ?? this.profileComplete, + errorMessage: errorMessage ?? this.errorMessage, ); } @override - List get props => [ - myShifts, - pendingShifts, - cancelledShifts, - availableShifts, - historyShifts, - availableLoading, - availableLoaded, - historyLoading, - historyLoaded, - myShiftsLoaded, - searchQuery, - jobType, - profileComplete ?? '', - ]; -} - -class ShiftsError extends ShiftsState { - final String message; - - const ShiftsError(this.message); - - @override - List get props => [message]; + List get props => [ + status, + myShifts, + pendingShifts, + cancelledShifts, + availableShifts, + historyShifts, + availableLoading, + availableLoaded, + historyLoading, + historyLoaded, + myShiftsLoaded, + searchQuery, + jobType, + profileComplete, + errorMessage, + ]; } diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart index ceda2b68..d803a199 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/pages/shifts_page.dart @@ -5,12 +5,13 @@ import 'package:design_system/design_system.dart'; import 'package:core_localization/core_localization.dart'; import 'package:krow_domain/krow_domain.dart'; import '../blocs/shifts/shifts_bloc.dart'; +import '../utils/shift_tab_type.dart'; import '../widgets/tabs/my_shifts_tab.dart'; import '../widgets/tabs/find_shifts_tab.dart'; import '../widgets/tabs/history_shifts_tab.dart'; class ShiftsPage extends StatefulWidget { - final String? initialTab; + final ShiftTabType? initialTab; final DateTime? selectedDate; final bool refreshAvailable; const ShiftsPage({ @@ -25,7 +26,7 @@ class ShiftsPage extends StatefulWidget { } class _ShiftsPageState extends State { - late String _activeTab; + late ShiftTabType _activeTab; DateTime? _selectedDate; bool _prioritizeFind = false; bool _refreshAvailable = false; @@ -35,9 +36,9 @@ class _ShiftsPageState extends State { @override void initState() { super.initState(); - _activeTab = widget.initialTab ?? 'myshifts'; + _activeTab = widget.initialTab ?? ShiftTabType.find; _selectedDate = widget.selectedDate; - _prioritizeFind = widget.initialTab == 'find'; + _prioritizeFind = _activeTab == ShiftTabType.find; _refreshAvailable = widget.refreshAvailable; _pendingAvailableRefresh = widget.refreshAvailable; if (_prioritizeFind) { @@ -45,16 +46,15 @@ class _ShiftsPageState extends State { } else { _bloc.add(LoadShiftsEvent()); } - if (_activeTab == 'history') { + if (_activeTab == ShiftTabType.history) { _bloc.add(LoadHistoryShiftsEvent()); } - if (_activeTab == 'find') { + if (_activeTab == ShiftTabType.find) { if (!_prioritizeFind) { - _bloc.add( - LoadAvailableShiftsEvent(force: _refreshAvailable), - ); + _bloc.add(LoadAvailableShiftsEvent(force: _refreshAvailable)); } } + // Check profile completion _bloc.add(const CheckProfileCompletionEvent()); } @@ -65,7 +65,7 @@ class _ShiftsPageState extends State { if (widget.initialTab != null && widget.initialTab != _activeTab) { setState(() { _activeTab = widget.initialTab!; - _prioritizeFind = widget.initialTab == 'find'; + _prioritizeFind = _activeTab == ShiftTabType.find; }); } if (widget.selectedDate != null && widget.selectedDate != _selectedDate) { @@ -86,50 +86,31 @@ class _ShiftsPageState extends State { value: _bloc, child: BlocConsumer( listener: (context, state) { - if (state is ShiftsError) { + if (state.status == ShiftsStatus.error && + state.errorMessage != null) { UiSnackbar.show( context, - message: translateErrorKey(state.message), + message: translateErrorKey(state.errorMessage!), type: UiSnackbarType.error, ); } }, builder: (context, state) { - if (_pendingAvailableRefresh && state is ShiftsLoaded) { + if (_pendingAvailableRefresh && state.status == ShiftsStatus.loaded) { _pendingAvailableRefresh = false; _bloc.add(const LoadAvailableShiftsEvent(force: true)); } - final bool baseLoaded = state is ShiftsLoaded; - final List myShifts = (state is ShiftsLoaded) - ? state.myShifts - : []; - final List availableJobs = (state is ShiftsLoaded) - ? state.availableShifts - : []; - final bool availableLoading = (state is ShiftsLoaded) - ? state.availableLoading - : false; - final bool availableLoaded = (state is ShiftsLoaded) - ? state.availableLoaded - : false; - final List pendingAssignments = (state is ShiftsLoaded) - ? state.pendingShifts - : []; - final List cancelledShifts = (state is ShiftsLoaded) - ? state.cancelledShifts - : []; - final List historyShifts = (state is ShiftsLoaded) - ? state.historyShifts - : []; - final bool historyLoading = (state is ShiftsLoaded) - ? state.historyLoading - : false; - final bool historyLoaded = (state is ShiftsLoaded) - ? state.historyLoaded - : false; - final bool myShiftsLoaded = (state is ShiftsLoaded) - ? state.myShiftsLoaded - : false; + final bool baseLoaded = state.status == ShiftsStatus.loaded; + final List myShifts = state.myShifts; + final List availableJobs = state.availableShifts; + final bool availableLoading = state.availableLoading; + final bool availableLoaded = state.availableLoaded; + final List pendingAssignments = state.pendingShifts; + final List cancelledShifts = state.cancelledShifts; + final List historyShifts = state.historyShifts; + final bool historyLoading = state.historyLoading; + final bool historyLoaded = state.historyLoaded; + final bool myShiftsLoaded = state.myShiftsLoaded; final bool blockTabsForFind = _prioritizeFind && !availableLoaded; // Note: "filteredJobs" logic moved to FindShiftsTab @@ -160,44 +141,47 @@ class _ShiftsPageState extends State { // Tabs Row( children: [ - if (state is ShiftsLoaded && state.profileComplete != false) + if (state.profileComplete != false) Expanded( child: _buildTab( - "myshifts", + ShiftTabType.myShifts, t.staff_shifts.tabs.my_shifts, UiIcons.calendar, myShifts.length, showCount: myShiftsLoaded, - enabled: !blockTabsForFind && (state.profileComplete ?? false), + enabled: + !blockTabsForFind && + (state.profileComplete ?? false), ), ) else const SizedBox.shrink(), - if (state is ShiftsLoaded && state.profileComplete != false) + if (state.profileComplete != false) const SizedBox(width: UiConstants.space2) else const SizedBox.shrink(), _buildTab( - "find", + ShiftTabType.find, t.staff_shifts.tabs.find_work, UiIcons.search, availableJobs.length, showCount: availableLoaded, enabled: baseLoaded, ), - if (state is ShiftsLoaded && state.profileComplete != false) + if (state.profileComplete != false) const SizedBox(width: UiConstants.space2) else const SizedBox.shrink(), - if (state is ShiftsLoaded && state.profileComplete != false) + if (state.profileComplete != false) Expanded( child: _buildTab( - "history", + ShiftTabType.history, t.staff_shifts.tabs.history, UiIcons.clock, historyShifts.length, showCount: historyLoaded, - enabled: !blockTabsForFind && + enabled: + !blockTabsForFind && baseLoaded && (state.profileComplete ?? false), ), @@ -212,9 +196,9 @@ class _ShiftsPageState extends State { // Body Content Expanded( - child: state is ShiftsLoading + child: state.status == ShiftsStatus.loading ? const Center(child: CircularProgressIndicator()) - : state is ShiftsError + : state.status == ShiftsStatus.error ? Center( child: Padding( padding: const EdgeInsets.all(UiConstants.space5), @@ -222,7 +206,7 @@ class _ShiftsPageState extends State { mainAxisSize: MainAxisSize.min, children: [ Text( - translateErrorKey(state.message), + translateErrorKey(state.errorMessage ?? ''), style: UiTypography.body2r.textSecondary, textAlign: TextAlign.center, ), @@ -258,47 +242,45 @@ class _ShiftsPageState extends State { bool historyLoading, ) { switch (_activeTab) { - case 'myshifts': + case ShiftTabType.myShifts: return MyShiftsTab( myShifts: myShifts, pendingAssignments: pendingAssignments, cancelledShifts: cancelledShifts, initialDate: _selectedDate, ); - case 'find': + case ShiftTabType.find: if (availableLoading) { return const Center(child: CircularProgressIndicator()); } return FindShiftsTab(availableJobs: availableJobs); - case 'history': + case ShiftTabType.history: if (historyLoading) { return const Center(child: CircularProgressIndicator()); } return HistoryShiftsTab(historyShifts: historyShifts); - default: - return const SizedBox.shrink(); } } Widget _buildTab( - String id, + ShiftTabType type, String label, IconData icon, int count, { bool showCount = true, bool enabled = true, }) { - final isActive = _activeTab == id; + final isActive = _activeTab == type; return Expanded( child: GestureDetector( onTap: !enabled ? null : () { - setState(() => _activeTab = id); - if (id == 'history') { + setState(() => _activeTab = type); + if (type == ShiftTabType.history) { _bloc.add(LoadHistoryShiftsEvent()); } - if (id == 'find') { + if (type == ShiftTabType.find) { _bloc.add(LoadAvailableShiftsEvent()); } }, diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/utils/shift_tab_type.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/utils/shift_tab_type.dart new file mode 100644 index 00000000..16576408 --- /dev/null +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/utils/shift_tab_type.dart @@ -0,0 +1,30 @@ +enum ShiftTabType { + myShifts, + find, + history; + + static ShiftTabType fromString(String? value) { + if (value == null) return ShiftTabType.find; + switch (value.toLowerCase()) { + case 'myshifts': + return ShiftTabType.myShifts; + case 'find': + return ShiftTabType.find; + case 'history': + return ShiftTabType.history; + default: + return ShiftTabType.find; + } + } + + String get id { + switch (this) { + case ShiftTabType.myShifts: + return 'myshifts'; + case ShiftTabType.find: + return 'find'; + case ShiftTabType.history: + return 'history'; + } + } +} diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart index 03f20b49..37373e51 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/presentation/widgets/my_shift_card.dart @@ -27,7 +27,6 @@ class MyShiftCard extends StatefulWidget { } class _MyShiftCardState extends State { - String _formatTime(String time) { if (time.isEmpty) return ''; try { @@ -77,22 +76,23 @@ class _MyShiftCardState extends State { String _getShiftType() { // Handling potential localization key availability try { - final String orderType = (widget.shift.orderType ?? '').toUpperCase(); - if (orderType == 'PERMANENT') { - return t.staff_shifts.filter.long_term; - } - if (orderType == 'RECURRING') { - return t.staff_shifts.filter.multi_day; - } - if (widget.shift.durationDays != null && widget.shift.durationDays! > 30) { - return t.staff_shifts.filter.long_term; - } - if (widget.shift.durationDays != null && widget.shift.durationDays! > 1) { - return t.staff_shifts.filter.multi_day; - } - return t.staff_shifts.filter.one_day; + final String orderType = (widget.shift.orderType ?? '').toUpperCase(); + if (orderType == 'PERMANENT') { + return t.staff_shifts.filter.long_term; + } + if (orderType == 'RECURRING') { + return t.staff_shifts.filter.multi_day; + } + if (widget.shift.durationDays != null && + widget.shift.durationDays! > 30) { + return t.staff_shifts.filter.long_term; + } + if (widget.shift.durationDays != null && widget.shift.durationDays! > 1) { + return t.staff_shifts.filter.multi_day; + } + return t.staff_shifts.filter.one_day; } catch (_) { - return "One Day"; + return "One Day"; } } @@ -110,34 +110,34 @@ class _MyShiftCardState extends State { // Fallback localization if keys missing try { - if (status == 'confirmed') { - statusText = t.staff_shifts.status.confirmed; - statusColor = UiColors.textLink; - statusBg = UiColors.primary; - } else if (status == 'checked_in') { - statusText = 'Checked in'; - statusColor = UiColors.textSuccess; - statusBg = UiColors.iconSuccess; - } else if (status == 'pending' || status == 'open') { - statusText = t.staff_shifts.status.act_now; - statusColor = UiColors.destructive; - statusBg = UiColors.destructive; - } else if (status == 'swap') { - statusText = t.staff_shifts.status.swap_requested; - statusColor = UiColors.textWarning; - statusBg = UiColors.textWarning; - statusIcon = UiIcons.swap; - } else if (status == 'completed') { - statusText = t.staff_shifts.status.completed; - statusColor = UiColors.textSuccess; - statusBg = UiColors.iconSuccess; - } else if (status == 'no_show') { - statusText = t.staff_shifts.status.no_show; - statusColor = UiColors.destructive; - statusBg = UiColors.destructive; - } + if (status == 'confirmed') { + statusText = t.staff_shifts.status.confirmed; + statusColor = UiColors.textLink; + statusBg = UiColors.primary; + } else if (status == 'checked_in') { + statusText = 'Checked in'; + statusColor = UiColors.textSuccess; + statusBg = UiColors.iconSuccess; + } else if (status == 'pending' || status == 'open') { + statusText = t.staff_shifts.status.act_now; + statusColor = UiColors.destructive; + statusBg = UiColors.destructive; + } else if (status == 'swap') { + statusText = t.staff_shifts.status.swap_requested; + statusColor = UiColors.textWarning; + statusBg = UiColors.textWarning; + statusIcon = UiIcons.swap; + } else if (status == 'completed') { + statusText = t.staff_shifts.status.completed; + statusColor = UiColors.textSuccess; + statusBg = UiColors.iconSuccess; + } else if (status == 'no_show') { + statusText = t.staff_shifts.status.no_show; + statusColor = UiColors.destructive; + statusBg = UiColors.destructive; + } } catch (_) { - statusText = status?.toUpperCase() ?? ""; + statusText = status?.toUpperCase() ?? ""; } final schedules = widget.shift.schedules ?? []; @@ -145,8 +145,9 @@ class _MyShiftCardState extends State { final List visibleSchedules = schedules.length <= 5 ? schedules : schedules.take(3).toList(); - final int remainingSchedules = - schedules.length <= 5 ? 0 : schedules.length - 3; + final int remainingSchedules = schedules.length <= 5 + ? 0 + : schedules.length - 3; final String scheduleRange = hasSchedules ? () { final first = schedules.first.date; @@ -192,7 +193,9 @@ class _MyShiftCardState extends State { children: [ if (statusIcon != null) Padding( - padding: const EdgeInsets.only(right: UiConstants.space2), + padding: const EdgeInsets.only( + right: UiConstants.space2, + ), child: Icon( statusIcon, size: UiConstants.iconXs, @@ -203,7 +206,9 @@ class _MyShiftCardState extends State { Container( width: 8, height: 8, - margin: const EdgeInsets.only(right: UiConstants.space2), + margin: const EdgeInsets.only( + right: UiConstants.space2, + ), decoration: BoxDecoration( color: statusBg, shape: BoxShape.circle, @@ -257,14 +262,18 @@ class _MyShiftCardState extends State { begin: Alignment.topLeft, end: Alignment.bottomRight, ), - borderRadius: BorderRadius.circular(UiConstants.radiusBase), + borderRadius: BorderRadius.circular( + UiConstants.radiusBase, + ), border: Border.all( color: UiColors.primary.withValues(alpha: 0.09), ), ), child: widget.shift.logoUrl != null ? ClipRRect( - borderRadius: BorderRadius.circular(UiConstants.radiusBase), + borderRadius: BorderRadius.circular( + UiConstants.radiusBase, + ), child: Image.network( widget.shift.logoUrl!, fit: BoxFit.contain, @@ -290,8 +299,7 @@ class _MyShiftCardState extends State { children: [ Expanded( child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.shift.title, @@ -347,11 +355,13 @@ class _MyShiftCardState extends State { ), const SizedBox(height: UiConstants.space1), Padding( - padding: const EdgeInsets.only(bottom: 2), - child: Text( - scheduleRange, - style: UiTypography.footnote2r.copyWith(color: UiColors.primary), + padding: const EdgeInsets.only(bottom: 2), + child: Text( + scheduleRange, + style: UiTypography.footnote2r.copyWith( + color: UiColors.primary, ), + ), ), ...visibleSchedules.map( (schedule) => Padding( @@ -368,7 +378,9 @@ class _MyShiftCardState extends State { Text( '+$remainingSchedules more schedules', style: UiTypography.footnote2r.copyWith( - color: UiColors.primary.withOpacity(0.7), + color: UiColors.primary.withValues( + alpha: 0.7, + ), ), ), ], @@ -410,10 +422,11 @@ class _MyShiftCardState extends State { Text( '... +${widget.shift.durationDays! - 1} more days', style: UiTypography.footnote2r.copyWith( - color: - UiColors.primary.withOpacity(0.7), + color: UiColors.primary.withValues( + alpha: 0.7, + ), ), - ) + ), ], ), ] else ...[ diff --git a/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart b/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart index d9429238..5934588f 100644 --- a/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart +++ b/apps/mobile/packages/features/staff/shifts/lib/src/staff_shifts_module.dart @@ -13,6 +13,7 @@ import 'domain/usecases/apply_for_shift_usecase.dart'; import 'domain/usecases/get_shift_details_usecase.dart'; import 'presentation/blocs/shifts/shifts_bloc.dart'; import 'presentation/blocs/shift_details/shift_details_bloc.dart'; +import 'presentation/utils/shift_tab_type.dart'; import 'presentation/pages/shifts_page.dart'; class StaffShiftsModule extends Module { @@ -45,14 +46,16 @@ class StaffShiftsModule extends Module { i.add(GetShiftDetailsUseCase.new); // Bloc - i.add(() => ShiftsBloc( - getMyShifts: i.get(), - getAvailableShifts: i.get(), - getPendingAssignments: i.get(), - getCancelledShifts: i.get(), - getHistoryShifts: i.get(), - getProfileCompletion: i.get(), - )); + i.add( + () => ShiftsBloc( + getMyShifts: i.get(), + getAvailableShifts: i.get(), + getPendingAssignments: i.get(), + getCancelledShifts: i.get(), + getHistoryShifts: i.get(), + getProfileCompletion: i.get(), + ), + ); i.add(ShiftDetailsBloc.new); } @@ -63,8 +66,9 @@ class StaffShiftsModule extends Module { child: (_) { final args = r.args.data as Map?; final queryParams = r.args.queryParams; + final initialTabStr = queryParams['tab'] ?? args?['initialTab']; return ShiftsPage( - initialTab: queryParams['tab'] ?? args?['initialTab'], + initialTab: ShiftTabType.fromString(initialTabStr), selectedDate: args?['selectedDate'], refreshAvailable: args?['refreshAvailable'] == true, );