From c936d5f2ab13083fd5e431e79f503c52a3fe11fe Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Sat, 7 Mar 2026 02:51:07 -0500 Subject: [PATCH] feat: add attire section components for improved UI organization --- .../src/presentation/pages/attire_page.dart | 92 ++----------------- .../widgets/attire_empty_section.dart | 24 +++++ .../widgets/attire_section_header.dart | 24 +++++ .../widgets/attire_section_tab.dart | 41 +++++++++ 4 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_empty_section.dart create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_header.dart create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_tab.dart 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 fd8702a8..afcc60f4 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 @@ -8,8 +8,11 @@ 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 '../widgets/attire_empty_section.dart'; import '../widgets/attire_info_card.dart'; import '../widgets/attire_item_card.dart'; +import '../widgets/attire_section_header.dart'; +import '../widgets/attire_section_tab.dart'; class AttirePage extends StatefulWidget { const AttirePage({super.key}); @@ -70,7 +73,7 @@ class _AttirePageState extends State { // Section toggle chips Row( children: [ - _SectionTab( + AttireSectionTab( label: 'Required', isSelected: _showRequired, onTap: () => setState( @@ -78,7 +81,7 @@ class _AttirePageState extends State { ), ), const SizedBox(width: UiConstants.space3), - _SectionTab( + AttireSectionTab( label: 'Non-Essential', isSelected: _showNonEssential, onTap: () => setState( @@ -91,13 +94,13 @@ class _AttirePageState extends State { // Required section if (_showRequired) ...[ - _SectionHeader( + AttireSectionHeader( title: 'Required', count: requiredItems.length, ), const SizedBox(height: UiConstants.space3), if (requiredItems.isEmpty) - _EmptySection( + AttireEmptySection( message: context .t .staff_profile_attire @@ -138,13 +141,13 @@ class _AttirePageState extends State { // Non-Essential section if (_showNonEssential) ...[ - _SectionHeader( + AttireSectionHeader( title: 'Non-Essential', count: nonEssentialItems.length, ), const SizedBox(height: UiConstants.space3), if (nonEssentialItems.isEmpty) - _EmptySection( + AttireEmptySection( message: context .t .staff_profile_attire @@ -185,80 +188,3 @@ class _AttirePageState extends State { } } -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: [ - 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: [ - const Icon(UiIcons.shirt, size: 48, color: UiColors.iconInactive), - const SizedBox(height: UiConstants.space4), - Text(message, style: UiTypography.body1m.textSecondary), - ], - ), - ), - ); - } -} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_empty_section.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_empty_section.dart new file mode 100644 index 00000000..07afd35f --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_empty_section.dart @@ -0,0 +1,24 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class AttireEmptySection extends StatelessWidget { + const AttireEmptySection({super.key, 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: [ + const Icon(UiIcons.shirt, size: 48, color: UiColors.iconInactive), + const SizedBox(height: UiConstants.space4), + Text(message, style: UiTypography.body1m.textSecondary), + ], + ), + ), + ); + } +} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_header.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_header.dart new file mode 100644 index 00000000..b39ef5bb --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_header.dart @@ -0,0 +1,24 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class AttireSectionHeader extends StatelessWidget { + const AttireSectionHeader({ + super.key, + required this.title, + required this.count, + }); + + final String title; + final int count; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Text(title, style: UiTypography.headline4b), + const SizedBox(width: UiConstants.space2), + Text('($count)', style: UiTypography.body1m.textSecondary), + ], + ); + } +} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_tab.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_tab.dart new file mode 100644 index 00000000..365b80b4 --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/attire/lib/src/presentation/widgets/attire_section_tab.dart @@ -0,0 +1,41 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; + +class AttireSectionTab extends StatelessWidget { + const AttireSectionTab({ + super.key, + 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, + ), + ), + ); + } +}