feat: implement attire section toggles for required and non-essential items in AttirePage

This commit is contained in:
Achintha Isuru
2026-03-07 02:47:55 -05:00
parent 720bf247b3
commit c9a46a1a71

View File

@@ -8,13 +8,20 @@ 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_cubit.dart';
import 'package:staff_attire/src/presentation/blocs/attire/attire_state.dart'; import 'package:staff_attire/src/presentation/blocs/attire/attire_state.dart';
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 StatelessWidget { class AttirePage extends StatefulWidget {
const AttirePage({super.key}); const AttirePage({super.key});
@override
State<AttirePage> createState() => _AttirePageState();
}
class _AttirePageState extends State<AttirePage> {
bool _showRequired = true;
bool _showNonEssential = true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final AttireCubit cubit = Modular.get<AttireCubit>(); final AttireCubit cubit = Modular.get<AttireCubit>();
@@ -42,7 +49,12 @@ class AttirePage extends StatelessWidget {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
final List<AttireItem> filteredOptions = state.filteredOptions; final List<AttireItem> requiredItems = state.options
.where((AttireItem item) => item.isMandatory)
.toList();
final List<AttireItem> nonEssentialItems = state.options
.where((AttireItem item) => !item.isMandatory)
.toList();
return Column( return Column(
children: <Widget>[ children: <Widget>[
@@ -55,55 +67,110 @@ class AttirePage extends StatelessWidget {
const AttireInfoCard(), const AttireInfoCard(),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
// Filter Chips // Section toggle chips
AttireFilterChips( Row(
selectedFilter: state.filter, children: <Widget>[
onFilterChanged: cubit.updateFilter, _SectionTab(
label: 'Required',
isSelected: _showRequired,
onTap: () => setState(
() => _showRequired = !_showRequired,
),
),
const SizedBox(width: UiConstants.space3),
_SectionTab(
label: 'Non-Essential',
isSelected: _showNonEssential,
onTap: () => setState(
() => _showNonEssential = !_showNonEssential,
),
),
],
), ),
const SizedBox(height: UiConstants.space6), const SizedBox(height: UiConstants.space6),
// Item List // Required section
if (filteredOptions.isEmpty) if (_showRequired) ...<Widget>[
Padding( _SectionHeader(
padding: const EdgeInsets.symmetric( title: 'Required',
vertical: UiConstants.space10, count: requiredItems.length,
), ),
child: Center( const SizedBox(height: UiConstants.space3),
child: Column( if (requiredItems.isEmpty)
children: <Widget>[ _EmptySection(
const Icon( message: context
UiIcons.shirt, .t
size: 48, .staff_profile_attire
color: UiColors.iconInactive, .capture
), .no_items_filter,
const SizedBox(height: UiConstants.space4), )
Text( else
context.t.staff_profile_attire.capture.no_items_filter, ...requiredItems.map((AttireItem item) {
style: UiTypography.body1m.textSecondary, return Padding(
), padding: const EdgeInsets.only(
], bottom: UiConstants.space3,
), ),
child: AttireItemCard(
item: item,
isUploading: false,
uploadedPhotoUrl: state.photoUrls[item.id],
onTap: () {
Modular.to.toAttireCapture(
item: item,
initialPhotoUrl: state.photoUrls[item.id],
);
},
),
);
}),
],
// Divider between sections
if (_showRequired && _showNonEssential)
const Padding(
padding: EdgeInsets.symmetric(
vertical: UiConstants.space8,
), ),
child: Divider(),
) )
else else
...filteredOptions.map((AttireItem item) { const SizedBox(height: UiConstants.space6),
return Padding(
padding: const EdgeInsets.only( // Non-Essential section
bottom: UiConstants.space3, if (_showNonEssential) ...<Widget>[
), _SectionHeader(
child: AttireItemCard( title: 'Non-Essential',
item: item, count: nonEssentialItems.length,
isUploading: false, ),
uploadedPhotoUrl: state.photoUrls[item.id], const SizedBox(height: UiConstants.space3),
onTap: () { if (nonEssentialItems.isEmpty)
Modular.to.toAttireCapture( _EmptySection(
item: item, message: context
initialPhotoUrl: state.photoUrls[item.id], .t
); .staff_profile_attire
}, .capture
), .no_items_filter,
); )
}), else
...nonEssentialItems.map((AttireItem item) {
return Padding(
padding: const EdgeInsets.only(
bottom: UiConstants.space3,
),
child: AttireItemCard(
item: item,
isUploading: false,
uploadedPhotoUrl: state.photoUrls[item.id],
onTap: () {
Modular.to.toAttireCapture(
item: item,
initialPhotoUrl: state.photoUrls[item.id],
);
},
),
);
}),
],
const SizedBox(height: UiConstants.space20), const SizedBox(height: UiConstants.space20),
], ],
), ),
@@ -117,3 +184,81 @@ class AttirePage extends StatelessWidget {
); );
} }
} }
class _SectionTab extends StatelessWidget {
const _SectionTab({
required this.label,
required this.isSelected,
required this.onTap,
});
final String label;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
decoration: BoxDecoration(
color: isSelected ? UiColors.primary : UiColors.white,
borderRadius: UiConstants.radiusFull,
border: Border.all(
color: isSelected ? UiColors.primary : UiColors.border,
),
),
child: Text(
label,
style: isSelected
? UiTypography.footnote2m.white
: UiTypography.footnote2m.textSecondary,
),
),
);
}
}
class _SectionHeader extends StatelessWidget {
const _SectionHeader({required this.title, required this.count});
final String title;
final int count;
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Text(title, style: UiTypography.headline4b),
const SizedBox(width: UiConstants.space2),
Text('($count)', style: UiTypography.body1m.textSecondary),
],
);
}
}
class _EmptySection extends StatelessWidget {
const _EmptySection({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: UiConstants.space6),
child: Center(
child: Column(
children: <Widget>[
const Icon(UiIcons.shirt, size: 48, color: UiColors.iconInactive),
const SizedBox(height: UiConstants.space4),
Text(message, style: UiTypography.body1m.textSecondary),
],
),
),
);
}
}