feat: Implement attire item filtering and refactor attire capture flow and repository logic

This commit is contained in:
Achintha Isuru
2026-02-25 22:18:25 -05:00
parent c7c505f743
commit 083744cd34
6 changed files with 38 additions and 25 deletions

View File

@@ -196,7 +196,7 @@ extension StaffNavigator on IModularNavigator {
///
/// Record sizing and appearance information for uniform allocation.
void toAttire() {
pushNamed(StaffPaths.attire);
navigate(StaffPaths.attire);
}
/// Pushes the attire capture page.

View File

@@ -36,6 +36,10 @@ class AttireRepositoryImpl implements AttireRepository {
@override
Future<AttireItem> uploadPhoto(String itemId, String filePath) async {
// 6. Return updated AttireItem by re-fetching to get the PENDING/SUCCESS status
final List<AttireItem> finalOptions = await _connector.getAttireOptions();
return finalOptions.firstWhere((AttireItem e) => e.id == itemId);
// 1. Upload file to Core API
final FileUploadService uploadService = Modular.get<FileUploadService>();
final FileUploadResponse uploadRes = await uploadService.uploadFile(
@@ -104,8 +108,8 @@ class AttireRepositoryImpl implements AttireRepository {
);
// 6. Return updated AttireItem by re-fetching to get the PENDING/SUCCESS status
final List<AttireItem> finalOptions = await _connector.getAttireOptions();
return finalOptions.firstWhere((AttireItem e) => e.id == itemId);
// final List<AttireItem> finalOptions = await _connector.getAttireOptions();
// return finalOptions.firstWhere((AttireItem e) => e.id == itemId);
}
AttireVerificationStatus _mapToAttireStatus(VerificationStatus status) {

View File

@@ -64,6 +64,10 @@ class AttireCubit extends Cubit<AttireState>
emit(state.copyWith(selectedIds: currentSelection));
}
void updateFilter(String filter) {
emit(state.copyWith(filter: filter));
}
void syncCapturedPhoto(AttireItem item) {
// Update the options list with the new item data
final List<AttireItem> updatedOptions = state.options

View File

@@ -9,12 +9,14 @@ class AttireState extends Equatable {
this.options = const <AttireItem>[],
this.selectedIds = const <String>[],
this.photoUrls = const <String, String>{},
this.filter = 'All',
this.errorMessage,
});
final AttireStatus status;
final List<AttireItem> options;
final List<String> selectedIds;
final Map<String, String> photoUrls;
final String filter;
final String? errorMessage;
/// Helper to check if item is mandatory
@@ -44,11 +46,20 @@ class AttireState extends Equatable {
bool get canSave => allMandatorySelected && allMandatoryHavePhotos;
List<AttireItem> get filteredOptions {
return options.where((AttireItem item) {
if (filter == 'Required') return item.isMandatory;
if (filter == 'Non-Essential') return !item.isMandatory;
return true;
}).toList();
}
AttireState copyWith({
AttireStatus? status,
List<AttireItem>? options,
List<String>? selectedIds,
Map<String, String>? photoUrls,
String? filter,
String? errorMessage,
}) {
return AttireState(
@@ -56,6 +67,7 @@ class AttireState extends Equatable {
options: options ?? this.options,
selectedIds: selectedIds ?? this.selectedIds,
photoUrls: photoUrls ?? this.photoUrls,
filter: filter ?? this.filter,
errorMessage: errorMessage,
);
}
@@ -66,6 +78,7 @@ class AttireState extends Equatable {
options,
selectedIds,
photoUrls,
filter,
errorMessage,
];
}

View File

@@ -203,6 +203,15 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
type: UiSnackbarType.error,
);
}
if (state.status == AttireCaptureStatus.success) {
UiSnackbar.show(
context,
message: 'Attire image submitted for verification',
type: UiSnackbarType.success,
);
Modular.to.toAttire();
}
},
builder: (BuildContext context, AttireCaptureState state) {
final String? currentPhotoUrl =

View File

@@ -12,16 +12,9 @@ import '../widgets/attire_filter_chips.dart';
import '../widgets/attire_info_card.dart';
import '../widgets/attire_item_card.dart';
class AttirePage extends StatefulWidget {
class AttirePage extends StatelessWidget {
const AttirePage({super.key});
@override
State<AttirePage> createState() => _AttirePageState();
}
class _AttirePageState extends State<AttirePage> {
String _filter = 'All';
@override
Widget build(BuildContext context) {
final AttireCubit cubit = Modular.get<AttireCubit>();
@@ -30,6 +23,7 @@ class _AttirePageState extends State<AttirePage> {
appBar: UiAppBar(
title: t.staff_profile_attire.title,
showBackButton: true,
onLeadingPressed: () => Modular.to.toProfile(),
),
body: BlocProvider<AttireCubit>.value(
value: cubit,
@@ -48,14 +42,7 @@ class _AttirePageState extends State<AttirePage> {
return const Center(child: CircularProgressIndicator());
}
final List<AttireItem> options = state.options;
final List<AttireItem> filteredOptions = options.where((
AttireItem item,
) {
if (_filter == 'Required') return item.isMandatory;
if (_filter == 'Non-Essential') return !item.isMandatory;
return true;
}).toList();
final List<AttireItem> filteredOptions = state.filteredOptions;
return Column(
children: <Widget>[
@@ -70,12 +57,8 @@ class _AttirePageState extends State<AttirePage> {
// Filter Chips
AttireFilterChips(
selectedFilter: _filter,
onFilterChanged: (String value) {
setState(() {
_filter = value;
});
},
selectedFilter: state.filter,
onFilterChanged: cubit.updateFilter,
),
const SizedBox(height: UiConstants.space6),