From 9bc4778cc1eade0606c1f418340e03be525c1857 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Tue, 24 Feb 2026 16:19:59 -0500 Subject: [PATCH] feat: Extract attire photo capture logic into `AttireCaptureCubit` and reorganize existing attire BLoC into a dedicated subdirectory. --- .../attire/lib/src/attire_module.dart | 6 +- .../blocs/attire/attire_cubit.dart | 103 ++++++++++ .../blocs/{ => attire}/attire_state.dart | 42 ++-- .../attire_capture/attire_capture_cubit.dart | 39 ++++ .../attire_capture/attire_capture_state.dart | 39 ++++ .../src/presentation/blocs/attire_cubit.dart | 160 --------------- .../pages/attire_capture_page.dart | 190 ++++++++++-------- .../src/presentation/pages/attire_page.dart | 32 +-- 8 files changed, 325 insertions(+), 286 deletions(-) create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart rename apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/{ => attire}/attire_state.dart (59%) create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_cubit.dart create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_state.dart delete mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_cubit.dart diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/attire_module.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/attire_module.dart index 7937e0c1..eb32cf88 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/attire_module.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/attire_module.dart @@ -1,12 +1,13 @@ import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/core.dart'; +import 'package:staff_attire/src/presentation/blocs/attire/attire_cubit.dart'; +import 'package:staff_attire/src/presentation/blocs/attire_capture/attire_capture_cubit.dart'; import 'data/repositories_impl/attire_repository_impl.dart'; import 'domain/repositories/attire_repository.dart'; import 'domain/usecases/get_attire_options_usecase.dart'; import 'domain/usecases/save_attire_usecase.dart'; import 'domain/usecases/upload_attire_photo_usecase.dart'; -import 'presentation/blocs/attire_cubit.dart'; import 'presentation/pages/attire_page.dart'; class StaffAttireModule extends Module { @@ -19,9 +20,10 @@ class StaffAttireModule extends Module { i.addLazySingleton(GetAttireOptionsUseCase.new); i.addLazySingleton(SaveAttireUseCase.new); i.addLazySingleton(UploadAttirePhotoUseCase.new); - + // BLoC i.addLazySingleton(AttireCubit.new); + i.add(AttireCaptureCubit.new); } @override diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart new file mode 100644 index 00000000..f8b6df22 --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_cubit.dart @@ -0,0 +1,103 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:krow_core/core.dart'; +import 'package:krow_domain/krow_domain.dart'; +import 'package:staff_attire/src/domain/arguments/save_attire_arguments.dart'; +import 'package:staff_attire/src/domain/usecases/get_attire_options_usecase.dart'; +import 'package:staff_attire/src/domain/usecases/save_attire_usecase.dart'; + +import 'attire_state.dart'; + +class AttireCubit extends Cubit + with BlocErrorHandler { + AttireCubit(this._getAttireOptionsUseCase, this._saveAttireUseCase) + : super(const AttireState()) { + loadOptions(); + } + final GetAttireOptionsUseCase _getAttireOptionsUseCase; + final SaveAttireUseCase _saveAttireUseCase; + + Future loadOptions() async { + emit(state.copyWith(status: AttireStatus.loading)); + await handleError( + emit: emit, + action: () async { + final List options = await _getAttireOptionsUseCase(); + + // Auto-select mandatory items initially as per prototype + final List mandatoryIds = options + .where((AttireItem e) => e.isMandatory) + .map((AttireItem e) => e.id) + .toList(); + + final List initialSelection = List.from( + state.selectedIds, + ); + for (final String id in mandatoryIds) { + if (!initialSelection.contains(id)) { + initialSelection.add(id); + } + } + + emit( + state.copyWith( + status: AttireStatus.success, + options: options, + selectedIds: initialSelection, + ), + ); + }, + onError: (String errorKey) => + state.copyWith(status: AttireStatus.failure, errorMessage: errorKey), + ); + } + + void toggleSelection(String id) { + // Prevent unselecting mandatory items + if (state.isMandatory(id)) return; + + final List currentSelection = List.from(state.selectedIds); + if (currentSelection.contains(id)) { + currentSelection.remove(id); + } else { + currentSelection.add(id); + } + emit(state.copyWith(selectedIds: currentSelection)); + } + + void syncCapturedPhoto(String itemId, String url) { + final Map currentPhotos = Map.from( + state.photoUrls, + ); + currentPhotos[itemId] = url; + + // Auto-select item on upload success if not selected + final List currentSelection = List.from(state.selectedIds); + if (!currentSelection.contains(itemId)) { + currentSelection.add(itemId); + } + + emit( + state.copyWith(photoUrls: currentPhotos, selectedIds: currentSelection), + ); + } + + Future save() async { + if (!state.canSave) return; + + emit(state.copyWith(status: AttireStatus.saving)); + await handleError( + emit: emit, + action: () async { + await _saveAttireUseCase( + SaveAttireArguments( + selectedItemIds: state.selectedIds, + photoUrls: state.photoUrls, + ), + ); + emit(state.copyWith(status: AttireStatus.saved)); + }, + onError: (String errorKey) => + state.copyWith(status: AttireStatus.failure, errorMessage: errorKey), + ); + } +} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_state.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_state.dart similarity index 59% rename from apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_state.dart rename to apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_state.dart index 179ff3f0..3d882c07 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_state.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire/attire_state.dart @@ -4,51 +4,51 @@ import 'package:krow_domain/krow_domain.dart'; enum AttireStatus { initial, loading, success, failure, saving, saved } class AttireState extends Equatable { - const AttireState({ this.status = AttireStatus.initial, this.options = const [], this.selectedIds = const [], this.photoUrls = const {}, - this.uploadingStatus = const {}, - this.attestationChecked = false, this.errorMessage, }); final AttireStatus status; final List options; final List selectedIds; final Map photoUrls; - final Map uploadingStatus; - final bool attestationChecked; final String? errorMessage; - bool get uploading => uploadingStatus.values.any((bool u) => u); - /// Helper to check if item is mandatory bool isMandatory(String id) { - return options.firstWhere((AttireItem e) => e.id == id, orElse: () => const AttireItem(id: '', label: '')).isMandatory; + return options + .firstWhere( + (AttireItem e) => e.id == id, + orElse: () => const AttireItem(id: '', label: ''), + ) + .isMandatory; } /// Validation logic bool get allMandatorySelected { - final Iterable mandatoryIds = options.where((AttireItem e) => e.isMandatory).map((AttireItem e) => e.id); + final Iterable mandatoryIds = options + .where((AttireItem e) => e.isMandatory) + .map((AttireItem e) => e.id); return mandatoryIds.every((String id) => selectedIds.contains(id)); } bool get allMandatoryHavePhotos { - final Iterable mandatoryIds = options.where((AttireItem e) => e.isMandatory).map((AttireItem e) => e.id); + final Iterable mandatoryIds = options + .where((AttireItem e) => e.isMandatory) + .map((AttireItem e) => e.id); return mandatoryIds.every((String id) => photoUrls.containsKey(id)); } - bool get canSave => allMandatorySelected && allMandatoryHavePhotos && attestationChecked && !uploading; + bool get canSave => allMandatorySelected && allMandatoryHavePhotos; AttireState copyWith({ AttireStatus? status, List? options, List? selectedIds, Map? photoUrls, - Map? uploadingStatus, - bool? attestationChecked, String? errorMessage, }) { return AttireState( @@ -56,20 +56,16 @@ class AttireState extends Equatable { options: options ?? this.options, selectedIds: selectedIds ?? this.selectedIds, photoUrls: photoUrls ?? this.photoUrls, - uploadingStatus: uploadingStatus ?? this.uploadingStatus, - attestationChecked: attestationChecked ?? this.attestationChecked, errorMessage: errorMessage, ); } @override List get props => [ - status, - options, - selectedIds, - photoUrls, - uploadingStatus, - attestationChecked, - errorMessage - ]; + status, + options, + selectedIds, + photoUrls, + errorMessage, + ]; } diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_cubit.dart new file mode 100644 index 00000000..884abb37 --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_cubit.dart @@ -0,0 +1,39 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:krow_core/core.dart'; +import 'package:staff_attire/src/domain/arguments/upload_attire_photo_arguments.dart'; +import 'package:staff_attire/src/domain/usecases/upload_attire_photo_usecase.dart'; + +import 'attire_capture_state.dart'; + +class AttireCaptureCubit extends Cubit + with BlocErrorHandler { + AttireCaptureCubit(this._uploadAttirePhotoUseCase) + : super(const AttireCaptureState()); + + final UploadAttirePhotoUseCase _uploadAttirePhotoUseCase; + + void toggleAttestation(bool value) { + emit(state.copyWith(isAttested: value)); + } + + Future uploadPhoto(String itemId) async { + emit(state.copyWith(status: AttireCaptureStatus.uploading)); + + await handleError( + emit: emit, + action: () async { + final String url = await _uploadAttirePhotoUseCase( + UploadAttirePhotoArguments(itemId: itemId), + ); + + emit( + state.copyWith(status: AttireCaptureStatus.success, photoUrl: url), + ); + }, + onError: (String errorKey) => state.copyWith( + status: AttireCaptureStatus.failure, + errorMessage: errorKey, + ), + ); + } +} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_state.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_state.dart new file mode 100644 index 00000000..6b776816 --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_capture/attire_capture_state.dart @@ -0,0 +1,39 @@ +import 'package:equatable/equatable.dart'; + +enum AttireCaptureStatus { initial, uploading, success, failure } + +class AttireCaptureState extends Equatable { + const AttireCaptureState({ + this.status = AttireCaptureStatus.initial, + this.isAttested = false, + this.photoUrl, + this.errorMessage, + }); + + final AttireCaptureStatus status; + final bool isAttested; + final String? photoUrl; + final String? errorMessage; + + AttireCaptureState copyWith({ + AttireCaptureStatus? status, + bool? isAttested, + String? photoUrl, + String? errorMessage, + }) { + return AttireCaptureState( + status: status ?? this.status, + isAttested: isAttested ?? this.isAttested, + photoUrl: photoUrl ?? this.photoUrl, + errorMessage: errorMessage, + ); + } + + @override + List get props => [ + status, + isAttested, + photoUrl, + errorMessage, + ]; +} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_cubit.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_cubit.dart deleted file mode 100644 index a184ea56..00000000 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/blocs/attire_cubit.dart +++ /dev/null @@ -1,160 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:krow_core/core.dart'; -import 'package:krow_domain/krow_domain.dart'; - -import '../../domain/arguments/save_attire_arguments.dart'; -import '../../domain/arguments/upload_attire_photo_arguments.dart'; -import '../../domain/usecases/get_attire_options_usecase.dart'; -import '../../domain/usecases/save_attire_usecase.dart'; -import '../../domain/usecases/upload_attire_photo_usecase.dart'; -import 'attire_state.dart'; - -class AttireCubit extends Cubit - with BlocErrorHandler { - - AttireCubit( - this._getAttireOptionsUseCase, - this._saveAttireUseCase, - this._uploadAttirePhotoUseCase, - ) : super(const AttireState()) { - loadOptions(); - } - final GetAttireOptionsUseCase _getAttireOptionsUseCase; - final SaveAttireUseCase _saveAttireUseCase; - final UploadAttirePhotoUseCase _uploadAttirePhotoUseCase; - - Future loadOptions() async { - emit(state.copyWith(status: AttireStatus.loading)); - await handleError( - emit: emit, - action: () async { - final List options = await _getAttireOptionsUseCase(); - - // Auto-select mandatory items initially as per prototype - final List mandatoryIds = - options - .where((AttireItem e) => e.isMandatory) - .map((AttireItem e) => e.id) - .toList(); - - final List initialSelection = List.from( - state.selectedIds, - ); - for (final String id in mandatoryIds) { - if (!initialSelection.contains(id)) { - initialSelection.add(id); - } - } - - emit( - state.copyWith( - status: AttireStatus.success, - options: options, - selectedIds: initialSelection, - ), - ); - }, - onError: - (String errorKey) => state.copyWith( - status: AttireStatus.failure, - errorMessage: errorKey, - ), - ); - } - - void toggleSelection(String id) { - // Prevent unselecting mandatory items - if (state.isMandatory(id)) return; - - final List currentSelection = List.from(state.selectedIds); - if (currentSelection.contains(id)) { - currentSelection.remove(id); - } else { - currentSelection.add(id); - } - emit(state.copyWith(selectedIds: currentSelection)); - } - - void toggleAttestation(bool value) { - emit(state.copyWith(attestationChecked: value)); - } - - Future uploadPhoto(String itemId) async { - final Map currentUploading = Map.from( - state.uploadingStatus, - ); - currentUploading[itemId] = true; - emit(state.copyWith(uploadingStatus: currentUploading)); - - await handleError( - emit: emit, - action: () async { - final String url = await _uploadAttirePhotoUseCase( - UploadAttirePhotoArguments(itemId: itemId), - ); - - final Map currentPhotos = Map.from( - state.photoUrls, - ); - currentPhotos[itemId] = url; - - // Auto-select item on upload success if not selected - final List currentSelection = List.from( - state.selectedIds, - ); - if (!currentSelection.contains(itemId)) { - currentSelection.add(itemId); - } - - final Map updatedUploading = Map.from( - state.uploadingStatus, - ); - updatedUploading[itemId] = false; - - emit( - state.copyWith( - uploadingStatus: updatedUploading, - photoUrls: currentPhotos, - selectedIds: currentSelection, - ), - ); - }, - onError: (String errorKey) { - final Map updatedUploading = Map.from( - state.uploadingStatus, - ); - updatedUploading[itemId] = false; - // Could handle error specifically via snackbar event - // For now, attaching the error message but keeping state generally usable - return state.copyWith( - uploadingStatus: updatedUploading, - errorMessage: errorKey, - ); - }, - ); - } - - Future save() async { - if (!state.canSave) return; - - emit(state.copyWith(status: AttireStatus.saving)); - await handleError( - emit: emit, - action: () async { - await _saveAttireUseCase( - SaveAttireArguments( - selectedItemIds: state.selectedIds, - photoUrls: state.photoUrls, - ), - ); - emit(state.copyWith(status: AttireStatus.saved)); - }, - onError: - (String errorKey) => state.copyWith( - status: AttireStatus.failure, - errorMessage: errorKey, - ), - ); - } -} - diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart index d314b6d0..5f227ca9 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_capture_page.dart @@ -1,31 +1,37 @@ +import 'package:core_localization/core_localization.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:krow_domain/krow_domain.dart'; import 'package:flutter_modular/flutter_modular.dart'; -import 'package:core_localization/core_localization.dart'; +import 'package:krow_domain/krow_domain.dart'; +import 'package:staff_attire/src/presentation/blocs/attire_capture/attire_capture_cubit.dart'; +import 'package:staff_attire/src/presentation/blocs/attire_capture/attire_capture_state.dart'; -import '../blocs/attire_cubit.dart'; -import '../blocs/attire_state.dart'; import '../widgets/attestation_checkbox.dart'; import '../widgets/attire_capture_page/attire_image_preview.dart'; import '../widgets/attire_capture_page/attire_upload_buttons.dart'; import '../widgets/attire_capture_page/attire_verification_status_card.dart'; class AttireCapturePage extends StatefulWidget { - const AttireCapturePage({super.key, required this.item}); + const AttireCapturePage({ + super.key, + required this.item, + this.initialPhotoUrl, + }); final AttireItem item; + final String? initialPhotoUrl; @override State createState() => _AttireCapturePageState(); } class _AttireCapturePageState extends State { - bool _isAttested = false; - void _onUpload(BuildContext context) { - if (!_isAttested) { + final AttireCaptureCubit cubit = BlocProvider.of( + context, + ); + if (!cubit.state.isAttested) { UiSnackbar.show( context, message: 'Please attest that you own this item.', @@ -35,100 +41,106 @@ class _AttireCapturePageState extends State { return; } // Call the upload via cubit - final AttireCubit cubit = Modular.get(); cubit.uploadPhoto(widget.item.id); } @override Widget build(BuildContext context) { - final AttireCubit cubit = Modular.get(); + return BlocProvider( + create: (_) => Modular.get(), + child: Builder( + builder: (BuildContext context) { + final AttireCaptureCubit cubit = BlocProvider.of( + context, + ); - return Scaffold( - backgroundColor: UiColors.background, - appBar: UiAppBar(title: widget.item.label, showBackButton: true), - body: BlocConsumer( - bloc: cubit, - listener: (BuildContext context, AttireState state) { - if (state.status == AttireStatus.failure) { - UiSnackbar.show( - context, - message: translateErrorKey(state.errorMessage ?? 'Error'), - type: UiSnackbarType.error, - ); - } - }, - builder: (BuildContext context, AttireState state) { - final bool isUploading = - state.uploadingStatus[widget.item.id] ?? false; - final bool hasPhoto = state.photoUrls.containsKey(widget.item.id); - final String statusText = hasPhoto - ? 'Pending Verification' - : 'Not Uploaded'; - final Color statusColor = hasPhoto - ? UiColors.textWarning - : UiColors.textInactive; + return Scaffold( + backgroundColor: UiColors.background, + appBar: UiAppBar(title: widget.item.label, showBackButton: true), + body: BlocConsumer( + bloc: cubit, + listener: (BuildContext context, AttireCaptureState state) { + if (state.status == AttireCaptureStatus.failure) { + UiSnackbar.show( + context, + message: translateErrorKey(state.errorMessage ?? 'Error'), + type: UiSnackbarType.error, + ); + } + }, + builder: (BuildContext context, AttireCaptureState state) { + final bool isUploading = + state.status == AttireCaptureStatus.uploading; + final bool hasPhoto = + state.photoUrl != null || widget.initialPhotoUrl != null; + final String statusText = hasPhoto + ? 'Pending Verification' + : 'Not Uploaded'; + final Color statusColor = hasPhoto + ? UiColors.textWarning + : UiColors.textInactive; - return Column( - children: [ - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(UiConstants.space5), - child: Column( - children: [ - // Image Preview - // Image Preview - AttireImagePreview(imageUrl: widget.item.imageUrl), - const SizedBox(height: UiConstants.space6), + return Column( + children: [ + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(UiConstants.space5), + child: Column( + children: [ + // Image Preview + AttireImagePreview(imageUrl: widget.item.imageUrl), + const SizedBox(height: UiConstants.space6), - Text( - widget.item.description ?? '', - style: UiTypography.body1r.textSecondary, - textAlign: TextAlign.center, - ), - const SizedBox(height: UiConstants.space6), + Text( + widget.item.description ?? '', + style: UiTypography.body1r.textSecondary, + textAlign: TextAlign.center, + ), + const SizedBox(height: UiConstants.space6), - // Verification info - AttireVerificationStatusCard( - statusText: statusText, - statusColor: statusColor, - ), - const SizedBox(height: UiConstants.space6), + // Verification info + AttireVerificationStatusCard( + statusText: statusText, + statusColor: statusColor, + ), + const SizedBox(height: UiConstants.space6), - AttestationCheckbox( - isChecked: _isAttested, - onChanged: (bool? val) { - setState(() { - _isAttested = val ?? false; - }); - }, - ), - const SizedBox(height: UiConstants.space6), + AttestationCheckbox( + isChecked: state.isAttested, + onChanged: (bool? val) { + cubit.toggleAttestation(val ?? false); + }, + ), + const SizedBox(height: UiConstants.space6), - if (isUploading) - const Center(child: CircularProgressIndicator()) - else if (!hasPhoto || - true) // Show options even if has photo (allows re-upload) - AttireUploadButtons(onUpload: _onUpload), - ], - ), - ), - ), - if (hasPhoto) - SafeArea( - child: Padding( - padding: const EdgeInsets.all(UiConstants.space5), - child: SizedBox( - width: double.infinity, - child: UiButton.primary( - text: 'Submit Image', - onPressed: () { - Modular.to.pop(); - }, + if (isUploading) + const Center(child: CircularProgressIndicator()) + else if (!hasPhoto || + true) // Show options even if has photo (allows re-upload) + AttireUploadButtons(onUpload: _onUpload), + ], + ), ), ), - ), - ), - ], + if (hasPhoto) + SafeArea( + child: Padding( + padding: const EdgeInsets.all(UiConstants.space5), + child: SizedBox( + width: double.infinity, + child: UiButton.primary( + text: 'Submit Image', + onPressed: () { + Modular.to.pop(state.photoUrl); + }, + ), + ), + ), + ), + ], + ); + }, + ), ); }, ), diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart index 7d3aaa34..9f3d62c8 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/pages/attire_page.dart @@ -4,9 +4,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_domain/krow_domain.dart'; +import 'package:staff_attire/src/presentation/blocs/attire/attire_cubit.dart'; +import 'package:staff_attire/src/presentation/blocs/attire/attire_state.dart'; -import '../blocs/attire_cubit.dart'; -import '../blocs/attire_state.dart'; import '../widgets/attire_filter_chips.dart'; import '../widgets/attire_info_card.dart'; import '../widgets/attire_item_card.dart'; @@ -87,17 +87,25 @@ class _AttirePageState extends State { ), child: AttireItemCard( item: item, - isUploading: - state.uploadingStatus[item.id] ?? false, + isUploading: false, uploadedPhotoUrl: state.photoUrls[item.id], - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (BuildContext ctx) => - AttireCapturePage(item: item), - ), - ); + onTap: () async { + final String? resultUrl = + await Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext ctx) => + AttireCapturePage( + item: item, + initialPhotoUrl: + state.photoUrls[item.id], + ), + ), + ); + + if (resultUrl != null && mounted) { + cubit.syncCapturedPhoto(item.id, resultUrl); + } }, ), );