feat: Implement attire item filtering and refactor attire capture flow and repository logic
This commit is contained in:
@@ -196,7 +196,7 @@ extension StaffNavigator on IModularNavigator {
|
|||||||
///
|
///
|
||||||
/// Record sizing and appearance information for uniform allocation.
|
/// Record sizing and appearance information for uniform allocation.
|
||||||
void toAttire() {
|
void toAttire() {
|
||||||
pushNamed(StaffPaths.attire);
|
navigate(StaffPaths.attire);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the attire capture page.
|
/// Pushes the attire capture page.
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ class AttireRepositoryImpl implements AttireRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<AttireItem> uploadPhoto(String itemId, String filePath) async {
|
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
|
// 1. Upload file to Core API
|
||||||
final FileUploadService uploadService = Modular.get<FileUploadService>();
|
final FileUploadService uploadService = Modular.get<FileUploadService>();
|
||||||
final FileUploadResponse uploadRes = await uploadService.uploadFile(
|
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
|
// 6. Return updated AttireItem by re-fetching to get the PENDING/SUCCESS status
|
||||||
final List<AttireItem> finalOptions = await _connector.getAttireOptions();
|
// final List<AttireItem> finalOptions = await _connector.getAttireOptions();
|
||||||
return finalOptions.firstWhere((AttireItem e) => e.id == itemId);
|
// return finalOptions.firstWhere((AttireItem e) => e.id == itemId);
|
||||||
}
|
}
|
||||||
|
|
||||||
AttireVerificationStatus _mapToAttireStatus(VerificationStatus status) {
|
AttireVerificationStatus _mapToAttireStatus(VerificationStatus status) {
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ class AttireCubit extends Cubit<AttireState>
|
|||||||
emit(state.copyWith(selectedIds: currentSelection));
|
emit(state.copyWith(selectedIds: currentSelection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateFilter(String filter) {
|
||||||
|
emit(state.copyWith(filter: filter));
|
||||||
|
}
|
||||||
|
|
||||||
void syncCapturedPhoto(AttireItem item) {
|
void syncCapturedPhoto(AttireItem item) {
|
||||||
// Update the options list with the new item data
|
// Update the options list with the new item data
|
||||||
final List<AttireItem> updatedOptions = state.options
|
final List<AttireItem> updatedOptions = state.options
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ class AttireState extends Equatable {
|
|||||||
this.options = const <AttireItem>[],
|
this.options = const <AttireItem>[],
|
||||||
this.selectedIds = const <String>[],
|
this.selectedIds = const <String>[],
|
||||||
this.photoUrls = const <String, String>{},
|
this.photoUrls = const <String, String>{},
|
||||||
|
this.filter = 'All',
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
final AttireStatus status;
|
final AttireStatus status;
|
||||||
final List<AttireItem> options;
|
final List<AttireItem> options;
|
||||||
final List<String> selectedIds;
|
final List<String> selectedIds;
|
||||||
final Map<String, String> photoUrls;
|
final Map<String, String> photoUrls;
|
||||||
|
final String filter;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
/// Helper to check if item is mandatory
|
/// Helper to check if item is mandatory
|
||||||
@@ -44,11 +46,20 @@ class AttireState extends Equatable {
|
|||||||
|
|
||||||
bool get canSave => allMandatorySelected && allMandatoryHavePhotos;
|
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({
|
AttireState copyWith({
|
||||||
AttireStatus? status,
|
AttireStatus? status,
|
||||||
List<AttireItem>? options,
|
List<AttireItem>? options,
|
||||||
List<String>? selectedIds,
|
List<String>? selectedIds,
|
||||||
Map<String, String>? photoUrls,
|
Map<String, String>? photoUrls,
|
||||||
|
String? filter,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) {
|
||||||
return AttireState(
|
return AttireState(
|
||||||
@@ -56,6 +67,7 @@ class AttireState extends Equatable {
|
|||||||
options: options ?? this.options,
|
options: options ?? this.options,
|
||||||
selectedIds: selectedIds ?? this.selectedIds,
|
selectedIds: selectedIds ?? this.selectedIds,
|
||||||
photoUrls: photoUrls ?? this.photoUrls,
|
photoUrls: photoUrls ?? this.photoUrls,
|
||||||
|
filter: filter ?? this.filter,
|
||||||
errorMessage: errorMessage,
|
errorMessage: errorMessage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -66,6 +78,7 @@ class AttireState extends Equatable {
|
|||||||
options,
|
options,
|
||||||
selectedIds,
|
selectedIds,
|
||||||
photoUrls,
|
photoUrls,
|
||||||
|
filter,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,15 @@ class _AttireCapturePageState extends State<AttireCapturePage> {
|
|||||||
type: UiSnackbarType.error,
|
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) {
|
builder: (BuildContext context, AttireCaptureState state) {
|
||||||
final String? currentPhotoUrl =
|
final String? currentPhotoUrl =
|
||||||
|
|||||||
@@ -12,16 +12,9 @@ import '../widgets/attire_filter_chips.dart';
|
|||||||
import '../widgets/attire_info_card.dart';
|
import '../widgets/attire_info_card.dart';
|
||||||
import '../widgets/attire_item_card.dart';
|
import '../widgets/attire_item_card.dart';
|
||||||
|
|
||||||
class AttirePage extends StatefulWidget {
|
class AttirePage extends StatelessWidget {
|
||||||
const AttirePage({super.key});
|
const AttirePage({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
State<AttirePage> createState() => _AttirePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AttirePageState extends State<AttirePage> {
|
|
||||||
String _filter = 'All';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final AttireCubit cubit = Modular.get<AttireCubit>();
|
final AttireCubit cubit = Modular.get<AttireCubit>();
|
||||||
@@ -30,6 +23,7 @@ class _AttirePageState extends State<AttirePage> {
|
|||||||
appBar: UiAppBar(
|
appBar: UiAppBar(
|
||||||
title: t.staff_profile_attire.title,
|
title: t.staff_profile_attire.title,
|
||||||
showBackButton: true,
|
showBackButton: true,
|
||||||
|
onLeadingPressed: () => Modular.to.toProfile(),
|
||||||
),
|
),
|
||||||
body: BlocProvider<AttireCubit>.value(
|
body: BlocProvider<AttireCubit>.value(
|
||||||
value: cubit,
|
value: cubit,
|
||||||
@@ -48,14 +42,7 @@ class _AttirePageState extends State<AttirePage> {
|
|||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<AttireItem> options = state.options;
|
final List<AttireItem> filteredOptions = state.filteredOptions;
|
||||||
final List<AttireItem> filteredOptions = options.where((
|
|
||||||
AttireItem item,
|
|
||||||
) {
|
|
||||||
if (_filter == 'Required') return item.isMandatory;
|
|
||||||
if (_filter == 'Non-Essential') return !item.isMandatory;
|
|
||||||
return true;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -70,12 +57,8 @@ class _AttirePageState extends State<AttirePage> {
|
|||||||
|
|
||||||
// Filter Chips
|
// Filter Chips
|
||||||
AttireFilterChips(
|
AttireFilterChips(
|
||||||
selectedFilter: _filter,
|
selectedFilter: state.filter,
|
||||||
onFilterChanged: (String value) {
|
onFilterChanged: cubit.updateFilter,
|
||||||
setState(() {
|
|
||||||
_filter = value;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user