From 4e8373b2e587428bc43b0203bdb53599085555f1 Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Fri, 23 Jan 2026 12:23:01 -0500 Subject: [PATCH] Update view_order_card.dart --- .../presentation/widgets/view_order_card.dart | 1345 ++++++++--------- 1 file changed, 671 insertions(+), 674 deletions(-) diff --git a/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart b/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart index b8704f21..7c6beb78 100644 --- a/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart +++ b/apps/mobile/packages/features/client/view_orders/lib/src/presentation/widgets/view_order_card.dart @@ -122,23 +122,20 @@ class _ViewOrderCardState extends State { return Container( decoration: BoxDecoration( color: UiColors.white, - borderRadius: BorderRadius.circular(UiConstants.radiusBase), - border: Border.all( - color: UiColors.primary.withValues(alpha: 0.12), - width: 1.5, - ), + borderRadius: UiConstants.radiusLg, + border: Border.all(color: UiColors.border), boxShadow: [ BoxShadow( - color: UiColors.primary.withValues(alpha: 0.08), - blurRadius: 3, - offset: const Offset(0, 1), + color: UiColors.black.withValues(alpha: 0.04), + blurRadius: 8, + offset: const Offset(0, 2), ), ], ), child: Column( children: [ Padding( - padding: const EdgeInsets.all(UiConstants.space4), + padding: const EdgeInsets.all(UiConstants.space5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -150,34 +147,43 @@ class _ViewOrderCardState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Status Dot & Label - Row( - children: [ - Container( - width: 6, - height: 6, - decoration: BoxDecoration( - color: statusColor, - shape: BoxShape.circle, + // Status Badge + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + decoration: BoxDecoration( + color: statusColor.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(4), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 6, + height: 6, + decoration: BoxDecoration( + color: statusColor, + shape: BoxShape.circle, + ), ), - ), - const SizedBox(width: UiConstants.space2), - Text( - statusLabel, - style: UiTypography.footnote2b.copyWith( - color: statusColor, - letterSpacing: 0.5, + const SizedBox(width: 6), + Text( + statusLabel.toUpperCase(), + style: UiTypography.footnote2b.copyWith( + color: statusColor, + letterSpacing: 0.5, + ), ), - ), - ], + ], + ), ), - const SizedBox(height: UiConstants.space1), + const SizedBox(height: UiConstants.space3), // Title Text( order.title, - style: UiTypography.body1b.copyWith( - color: UiColors.textPrimary, - ), + style: UiTypography.headline4m.textPrimary, ), const SizedBox(height: UiConstants.space1), // Client & Date @@ -185,84 +191,54 @@ class _ViewOrderCardState extends State { children: [ Text( order.clientName, - style: UiTypography.body3r.copyWith( - color: UiColors.textSecondary, - ), + style: UiTypography.body3r.textSecondary, ), Padding( padding: const EdgeInsets.symmetric( - horizontal: UiConstants.space1, + horizontal: 6, ), child: Text( '•', - style: UiTypography.body3r.copyWith( - color: UiColors.textInactive, - ), + style: UiTypography.body3r.textInactive, ), ), Text( _formatDate(dateStr: order.date), - style: UiTypography.body3r.copyWith( - fontWeight: FontWeight.w500, - color: UiColors.textSecondary, - ), + style: UiTypography.body3m.textSecondary, ), ], ), - const SizedBox(height: UiConstants.space1), + const SizedBox(height: UiConstants.space2), // Address Row( children: [ const Icon( UiIcons.mapPin, - size: 12, + size: 14, color: UiColors.iconSecondary, ), - const SizedBox(width: UiConstants.space1), + const SizedBox(width: 4), Expanded( child: Text( order.locationAddress, - style: UiTypography.footnote2r.copyWith( - color: UiColors.textSecondary, - ), + style: UiTypography.footnote2r.textSecondary, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), - const SizedBox(width: UiConstants.space1), - GestureDetector( - onTap: () { - // TODO: Handle location - }, - child: Row( - children: [ - const Icon( - UiIcons.navigation, - size: 12, - color: UiColors.primary, - ), - const SizedBox(width: 2), - Text( - t.client_view_orders.card.get_direction, - style: UiTypography.footnote2m.copyWith( - color: UiColors.primary, - ), - ), - ], - ), - ), ], ), ], ), ), + const SizedBox(width: UiConstants.space3), // Actions Row( children: [ _buildHeaderIconButton( icon: UiIcons.edit, color: UiColors.primary, - bgColor: UiColors.tagInProgress, + bgColor: UiColors.primary.withValues(alpha: 0.08), onTap: () => _openEditSheet(order: order), ), const SizedBox(width: UiConstants.space2), @@ -279,70 +255,57 @@ class _ViewOrderCardState extends State { ], ), - const SizedBox(height: UiConstants.space3), - const Divider(height: 1, color: UiColors.separatorSecondary), - const SizedBox(height: UiConstants.space3), + const SizedBox(height: UiConstants.space4), + const Divider(height: 1, color: UiColors.border), + const SizedBox(height: UiConstants.space4), // Stats Row Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Expanded( - child: _buildStatItem( - icon: UiIcons.dollar, - value: '\$${cost.round()}', - label: t.client_view_orders.card.total, - ), + _buildStatItem( + icon: UiIcons.dollar, + value: '\$${cost.round()}', + label: 'Total', ), - Container( - width: 1, - height: 32, - color: UiColors.separatorSecondary, + _buildStatDivider(), + _buildStatItem( + icon: UiIcons.clock, + value: hours.toStringAsFixed(1), + label: 'Hrs', ), - Expanded( - child: _buildStatItem( - icon: UiIcons.clock, - value: hours.toStringAsFixed(1), - label: t.client_view_orders.card.hrs, - ), - ), - Container( - width: 1, - height: 32, - color: UiColors.separatorSecondary, - ), - Expanded( - child: _buildStatItem( - icon: UiIcons.users, - value: - '${order.filled > 0 ? order.filled : order.workersNeeded}', - label: t.client_view_orders.card.workers, - ), + _buildStatDivider(), + _buildStatItem( + icon: UiIcons.users, + value: + '${order.filled > 0 ? order.filled : order.workersNeeded}', + label: 'Workers', ), ], ), - const SizedBox(height: UiConstants.space4), + const SizedBox(height: UiConstants.space5), - // Clock In/Out Boxes + // Times Section Row( children: [ Expanded( - child: _buildTimeBox( - label: t.client_view_orders.card.clock_in, + child: _buildTimeDisplay( + label: 'Clock In', time: _formatTime(timeStr: order.startTime), ), ), const SizedBox(width: UiConstants.space3), Expanded( - child: _buildTimeBox( - label: t.client_view_orders.card.clock_out, + child: _buildTimeDisplay( + label: 'Clock Out', time: _formatTime(timeStr: order.endTime), ), ), ], ), - const SizedBox(height: UiConstants.space3), + const SizedBox(height: UiConstants.space4), // Coverage Section if (order.status != 'completed') ...[ @@ -356,15 +319,10 @@ class _ViewOrderCardState extends State { size: 16, color: UiColors.textSuccess, ), - const SizedBox(width: 6), + const SizedBox(width: 8), Text( - t.client_view_orders.card.workers_label( - filled: order.filled, - needed: order.workersNeeded, - ), - style: UiTypography.body2m.copyWith( - color: UiColors.textPrimary, - ), + '${order.filled}/${order.workersNeeded} Workers Filled', + style: UiTypography.body2m.textPrimary, ), ], ), @@ -376,12 +334,12 @@ class _ViewOrderCardState extends State { ), ], ), - const SizedBox(height: 8), + const SizedBox(height: 10), ClipRRect( - borderRadius: BorderRadius.circular(4), + borderRadius: BorderRadius.circular(100), child: LinearProgressIndicator( value: coveragePercent / 100, - backgroundColor: UiColors.separatorSecondary, + backgroundColor: UiColors.bgSecondary, valueColor: const AlwaysStoppedAnimation( UiColors.primary, ), @@ -391,18 +349,16 @@ class _ViewOrderCardState extends State { // Avatar Stack Preview (if not expanded) if (!_expanded && order.confirmedApps.isNotEmpty) ...[ - const SizedBox(height: UiConstants.space3), + const SizedBox(height: UiConstants.space4), Row( children: [ _buildAvatarStack(order.confirmedApps), if (order.confirmedApps.length > 3) Padding( - padding: const EdgeInsets.only(left: 8), + padding: const EdgeInsets.only(left: 12), child: Text( '+${order.confirmedApps.length - 3} more', - style: UiTypography.footnote2r.copyWith( - color: UiColors.textSecondary, - ), + style: UiTypography.footnote2r.textSecondary, ), ), ], @@ -418,14 +374,12 @@ class _ViewOrderCardState extends State { Container( decoration: const BoxDecoration( color: UiColors.bgSecondary, - border: Border( - top: BorderSide(color: UiColors.separatorSecondary), - ), + border: Border(top: BorderSide(color: UiColors.border)), borderRadius: BorderRadius.vertical( - bottom: Radius.circular(UiConstants.radiusBase), + bottom: Radius.circular(12), ), ), - padding: const EdgeInsets.all(UiConstants.space4), + padding: const EdgeInsets.all(UiConstants.space5), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -433,37 +387,35 @@ class _ViewOrderCardState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - t.client_view_orders.card.confirmed_workers, - style: UiTypography.footnote2b.copyWith( - color: UiColors.textSecondary, - ), + 'CONFIRMED WORKERS', + style: UiTypography.footnote2b.textSecondary, ), - UiButton.primary( - text: 'Message All', - leadingIcon: UiIcons.messageCircle, - size: UiButtonSize.small, - // style: ElevatedButton.styleFrom( - // minimumSize: const Size(0, 32), - // maximumSize: const Size(0, 32), - // ), - onPressed: () { - // TODO: Message all workers - }, + GestureDetector( + onTap: () {}, + child: Text( + 'Message All', + style: UiTypography.footnote2b.copyWith( + color: UiColors.primary, + ), + ), ), ], ), - const SizedBox(height: UiConstants.space3), + const SizedBox(height: UiConstants.space4), ...order.confirmedApps .take(5) .map((Map app) => _buildWorkerRow(app)), if (order.confirmedApps.length > 5) - Center( - child: TextButton( - onPressed: () => setState(() => _expanded = !_expanded), - child: Text( - 'Show ${order.confirmedApps.length - 5} more workers', - style: UiTypography.body3m.copyWith( - color: UiColors.primary, + Padding( + padding: const EdgeInsets.only(top: 8), + child: Center( + child: TextButton( + onPressed: () {}, + child: Text( + 'Show ${order.confirmedApps.length - 5} more workers', + style: UiTypography.body2m.copyWith( + color: UiColors.primary, + ), ), ), ), @@ -477,10 +429,35 @@ class _ViewOrderCardState extends State { ); } + Widget _buildStatDivider() { + return Container(width: 1, height: 24, color: UiColors.border); + } + + Widget _buildTimeDisplay({required String label, required String time}) { + return Container( + padding: const EdgeInsets.all(UiConstants.space3), + decoration: BoxDecoration( + color: UiColors.bgSecondary, + borderRadius: UiConstants.radiusMd, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + label.toUpperCase(), + style: UiTypography.titleUppercase4m.textSecondary, + ), + const SizedBox(height: 4), + Text(time, style: UiTypography.body1b.textPrimary), + ], + ), + ); + } + /// Builds a stacked avatar UI for a list of applications. Widget _buildAvatarStack(List> apps) { - const double size = 28.0; - const double overlap = 20.0; + const double size = 32.0; + const double overlap = 22.0; final int count = apps.length > 3 ? 3 : apps.length; return SizedBox( @@ -497,7 +474,7 @@ class _ViewOrderCardState extends State { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: UiColors.white, width: 2), - color: UiColors.tagInProgress, + color: UiColors.primary.withValues(alpha: 0.1), ), child: Center( child: Text( @@ -516,104 +493,67 @@ class _ViewOrderCardState extends State { /// Builds a detailed row for a worker. Widget _buildWorkerRow(Map app) { - return Padding( - padding: const EdgeInsets.only(bottom: 8), - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: UiColors.white, - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( + return Container( + margin: const EdgeInsets.only(bottom: 12), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: UiColors.white, + borderRadius: UiConstants.radiusMd, + border: Border.all(color: UiColors.border), + ), + child: Row( + children: [ + CircleAvatar( + radius: 20, + backgroundColor: UiColors.primary.withValues(alpha: 0.1), + child: Text( + (app['worker_name'] as String)[0], + style: UiTypography.body1b.copyWith(color: UiColors.primary), + ), + ), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Avatar - CircleAvatar( - radius: 18, - backgroundColor: UiColors.tagInProgress, - child: Text( - (app['worker_name'] as String)[0], - style: UiTypography.body2b.copyWith( - color: UiColors.primary, - ), - ), + Text( + app['worker_name'] as String, + style: UiTypography.body2m.textPrimary, ), - const SizedBox(width: UiConstants.space3), - Column( - crossAxisAlignment: CrossAxisAlignment.start, + const SizedBox(height: 2), + Row( children: [ - Text( - app['worker_name'] as String, - style: UiTypography.body2m.copyWith( - color: UiColors.textPrimary, - ), - ), - const SizedBox(height: 2), - Row( - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 1, - ), - decoration: BoxDecoration( - border: Border.all( - color: UiColors.separatorPrimary, - ), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - '⭐ 4.8', - style: UiTypography.titleUppercase4m.copyWith( - color: UiColors.textSecondary, - ), + const Icon(UiIcons.star, size: 10, color: UiColors.accent), + const SizedBox(width: 2), + Text('4.8', style: UiTypography.footnote2r.textSecondary), + if (app['check_in_time'] != null) ...[ + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 4, + vertical: 1, + ), + decoration: BoxDecoration( + color: UiColors.textSuccess.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + 'Checked In', + style: UiTypography.titleUppercase4m.copyWith( + color: UiColors.textSuccess, ), ), - if (app['check_in_time'] != null) ...[ - const SizedBox(width: 4), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 1, - ), - decoration: BoxDecoration( - color: UiColors.tagSuccess, - borderRadius: BorderRadius.circular(4), - ), - child: Text( - 'Checked In', - style: UiTypography.titleUppercase4m.copyWith( - color: UiColors.textSuccess, - ), - ), - ), - ], - ], - ), + ), + ], ], ), ], ), - Row( - children: [ - _buildActionIconButton(icon: UiIcons.phone, onTap: () {}), - const SizedBox(width: 8), - _buildActionIconButton( - icon: UiIcons.messageCircle, - onTap: () {}, - ), - const SizedBox(width: 8), - const Icon( - UiIcons.success, - size: 20, - color: UiColors.textSuccess, - ), - ], - ), - ], - ), + ), + _buildActionIconButton(icon: UiIcons.phone, onTap: () {}), + const SizedBox(width: 8), + _buildActionIconButton(icon: UiIcons.messageCircle, onTap: () {}), + ], ), ); } @@ -626,9 +566,12 @@ class _ViewOrderCardState extends State { return GestureDetector( onTap: onTap, child: Container( - width: 32, - height: 32, - decoration: const BoxDecoration(color: UiColors.transparent), + width: 36, + height: 36, + decoration: BoxDecoration( + color: UiColors.bgSecondary, + borderRadius: BorderRadius.circular(8), + ), child: Icon(icon, size: 16, color: UiColors.primary), ), ); @@ -644,12 +587,12 @@ class _ViewOrderCardState extends State { return GestureDetector( onTap: onTap, child: Container( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: bgColor, - borderRadius: BorderRadius.circular(10), + borderRadius: UiConstants.radiusSm, ), - child: Icon(icon, size: 14, color: color), + child: Icon(icon, size: 16, color: color), ), ); } @@ -665,58 +608,23 @@ class _ViewOrderCardState extends State { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(icon, size: 10, color: UiColors.iconSecondary), - const SizedBox(width: UiConstants.space1), - Text( - value, - style: UiTypography.body2b.copyWith(color: UiColors.textPrimary), - ), + Icon(icon, size: 14, color: UiColors.iconSecondary), + const SizedBox(width: 6), + Text(value, style: UiTypography.body1b.textPrimary), ], ), + const SizedBox(height: 2), Text( label.toUpperCase(), - style: UiTypography.titleUppercase4m.copyWith( - color: UiColors.textInactive, - fontWeight: FontWeight.bold, - ), + style: UiTypography.titleUppercase4m.textInactive, ), ], ); } - - /// Builds a box displaying a label and a time value. - Widget _buildTimeBox({required String label, required String time}) { - return Container( - padding: const EdgeInsets.symmetric(vertical: 8), - decoration: BoxDecoration( - color: UiColors.bgSecondary, - borderRadius: BorderRadius.circular(8), - ), - child: Column( - children: [ - Text( - label, - style: UiTypography.titleUppercase4m.copyWith( - color: UiColors.textSecondary, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 2), - Text( - time, - style: UiTypography.body2m.copyWith( - color: UiColors.foreground, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ); - } } /// A sophisticated bottom sheet for editing an existing order, -/// following the Unified Order Flow prototype. +/// following the Unified Order Flow prototype and matching OneTimeOrderView. class _OrderEditSheet extends StatefulWidget { const _OrderEditSheet({required this.order}); @@ -733,7 +641,6 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { late TextEditingController _dateController; late TextEditingController _globalLocationController; - // Local state for positions (starts with the single position from OrderItem) late List> _positions; @override @@ -750,7 +657,8 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { 'count': widget.order.workersNeeded, 'start_time': widget.order.startTime, 'end_time': widget.order.endTime, - 'location': '', // Specific location if different from global + 'lunch_break': 0, + 'location': null, }, ]; } @@ -769,7 +677,8 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { 'count': 1, 'start_time': '09:00', 'end_time': '17:00', - 'location': '', + 'lunch_break': 0, + 'location': null, }); }); } @@ -787,7 +696,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { double _calculateTotalCost() { double total = 0; for (final Map pos in _positions) { - double hours = 8; // Default fallback + double hours = 8.0; try { final List startParts = pos['start_time'].toString().split(':'); final List endParts = pos['end_time'].toString().split(':'); @@ -816,142 +725,459 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { return Container( height: MediaQuery.of(context).size.height * 0.95, decoration: const BoxDecoration( - color: UiColors.bgSecondary, - borderRadius: BorderRadius.vertical( - top: Radius.circular(UiConstants.radiusBase * 2), - ), + color: UiColors.bgPrimary, + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), child: Column( children: [ - // Header - Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), - decoration: const BoxDecoration( - color: UiColors.primary, - borderRadius: BorderRadius.vertical( - top: Radius.circular(UiConstants.radiusBase * 2), - ), - ), - child: Row( + _buildHeader(), + Expanded( + child: ListView( + padding: const EdgeInsets.all(UiConstants.space5), children: [ - GestureDetector( - onTap: () => Navigator.pop(context), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: UiColors.white.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(12), - ), - child: const Center( - child: Icon( - UiIcons.chevronLeft, - color: UiColors.white, - size: 24, - ), - ), - ), + Text( + 'Edit Your Order', + style: UiTypography.headline3m.textPrimary, ), - const SizedBox(width: UiConstants.space3), - Column( - crossAxisAlignment: CrossAxisAlignment.start, + const SizedBox(height: UiConstants.space4), + + _buildSectionHeader('DATE'), + UiTextField( + controller: _dateController, + hintText: 'mm/dd/yyyy', + prefixIcon: UiIcons.calendar, + readOnly: true, + onTap: () {}, + ), + const SizedBox(height: UiConstants.space4), + + _buildSectionHeader('LOCATION'), + UiTextField( + controller: _globalLocationController, + hintText: 'Business address', + prefixIcon: UiIcons.mapPin, + ), + const SizedBox(height: UiConstants.space6), + + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Edit Order', - style: UiTypography.title1m.copyWith( - color: UiColors.white, - ), + 'POSITIONS', + style: UiTypography.headline4m.textPrimary, ), - Text( - 'Refine your staffing needs', - style: UiTypography.body3r.copyWith( - color: UiColors.white.withValues(alpha: 0.7), + TextButton( + onPressed: _addPosition, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(UiIcons.add, size: 16, color: UiColors.primary), + SizedBox(width: UiConstants.space2), + Text( + 'Add Position', + style: TextStyle( + color: UiColors.primary, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ], ), ), ], ), + const SizedBox(height: UiConstants.space3), + + ..._positions.asMap().entries.map(( + MapEntry> entry, + ) { + return _buildPositionCard(entry.key, entry.value); + }), + + const SizedBox(height: 40), ], ), ), - - // Content - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(20), - child: Column( - children: [ - _buildSectionLabel('Date *'), - UiTextField( - controller: _dateController, - hintText: 'mm/dd/yyyy', - prefixIcon: UiIcons.calendar, - readOnly: true, - onTap: () { - // TODO: Date picker - }, - ), - const SizedBox(height: 16), - - _buildSectionLabel('Location *'), - UiTextField( - controller: _globalLocationController, - hintText: 'Business address', - prefixIcon: UiIcons.mapPin, - ), - const SizedBox(height: 24), - - // Positions Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Positions', - style: UiTypography.title1m.copyWith( - color: UiColors.textPrimary, - ), - ), - UiButton.text( - leadingIcon: UiIcons.add, - text: 'Add Position', - onPressed: _addPosition, - ), - ], - ), - const SizedBox(height: 8), - - ..._positions.asMap().entries.map(( - MapEntry> entry, - ) { - return _buildPositionCard(entry.key, entry.value); - }), - - const SizedBox(height: 40), - ], - ), - ), - ), - - // Footer - Container( - padding: const EdgeInsets.all(20), - decoration: const BoxDecoration( - color: UiColors.white, - border: Border(top: BorderSide(color: UiColors.separatorPrimary)), - ), - child: SafeArea( - top: false, - child: UiButton.primary( - text: 'Review ${_positions.length} Positions', - fullWidth: true, - onPressed: () => setState(() => _showReview = true), - ), - ), + _buildBottomAction( + label: 'Review ${_positions.length} Positions', + onPressed: () => setState(() => _showReview = true), ), ], ), ); } + Widget _buildHeader() { + return Container( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 20), + decoration: const BoxDecoration( + color: UiColors.primary, + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), + ), + child: Row( + children: [ + GestureDetector( + onTap: () => Navigator.pop(context), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: UiColors.white.withValues(alpha: 0.2), + borderRadius: UiConstants.radiusMd, + ), + child: const Icon( + UiIcons.chevronLeft, + color: UiColors.white, + size: 24, + ), + ), + ), + const SizedBox(width: UiConstants.space3), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'One-Time Order', + style: UiTypography.headline3m.copyWith(color: UiColors.white), + ), + Text( + 'Refine your staffing needs', + style: UiTypography.footnote2r.copyWith( + color: UiColors.white.withValues(alpha: 0.8), + ), + ), + ], + ), + ], + ), + ); + } + + Widget _buildSectionHeader(String title) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Text(title, style: UiTypography.footnote2r.textSecondary), + ); + } + + Widget _buildPositionCard(int index, Map pos) { + return Container( + margin: const EdgeInsets.only(bottom: UiConstants.space3), + padding: const EdgeInsets.all(UiConstants.space4), + decoration: BoxDecoration( + color: UiColors.white, + borderRadius: UiConstants.radiusLg, + border: Border.all(color: UiColors.border), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'POSITION #${index + 1}', + style: UiTypography.footnote1m.textSecondary, + ), + if (_positions.length > 1) + GestureDetector( + onTap: () => _removePosition(index), + child: Text( + 'Remove', + style: UiTypography.footnote1m.copyWith( + color: UiColors.destructive, + ), + ), + ), + ], + ), + const SizedBox(height: UiConstants.space3), + + _buildDropdownField( + hint: 'Select role', + value: pos['role'], + items: [ + 'Server', + 'Bartender', + 'Cook', + 'Busser', + 'Host', + 'Barista', + 'Dishwasher', + 'Event Staff', + if (pos['role'] != null && + pos['role'].toString().isNotEmpty && + ![ + 'Server', + 'Bartender', + 'Cook', + 'Busser', + 'Host', + 'Barista', + 'Dishwasher', + 'Event Staff', + ].contains(pos['role'])) + pos['role'].toString(), + ], + onChanged: (dynamic val) => _updatePosition(index, 'role', val), + ), + + const SizedBox(height: UiConstants.space3), + + Row( + children: [ + Expanded( + child: _buildInlineTimeInput( + label: 'Start', + value: pos['start_time'], + onTap: () {}, + ), + ), + const SizedBox(width: UiConstants.space2), + Expanded( + child: _buildInlineTimeInput( + label: 'End', + value: pos['end_time'], + onTap: () {}, + ), + ), + const SizedBox(width: UiConstants.space2), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Workers', + style: UiTypography.footnote2r.textSecondary, + ), + const SizedBox(height: UiConstants.space1), + Container( + height: 40, + decoration: BoxDecoration( + color: UiColors.bgSecondary, + borderRadius: UiConstants.radiusSm, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + if ((pos['count'] as int) > 1) { + _updatePosition( + index, + 'count', + (pos['count'] as int) - 1, + ); + } + }, + child: const Icon(UiIcons.minus, size: 12), + ), + Text( + '${pos['count']}', + style: UiTypography.body2b.textPrimary, + ), + GestureDetector( + onTap: () => _updatePosition( + index, + 'count', + (pos['count'] as int) + 1, + ), + child: const Icon(UiIcons.add, size: 12), + ), + ], + ), + ), + ], + ), + ), + ], + ), + const SizedBox(height: UiConstants.space4), + + if (pos['location'] == null) + GestureDetector( + onTap: () => _updatePosition(index, 'location', ''), + child: Row( + children: [ + const Icon(UiIcons.mapPin, size: 14, color: UiColors.primary), + const SizedBox(width: UiConstants.space1), + Text( + 'Use different location for this position', + style: UiTypography.footnote1m.copyWith( + color: UiColors.primary, + ), + ), + ], + ), + ) + else + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + const Icon( + UiIcons.mapPin, + size: 14, + color: UiColors.iconSecondary, + ), + const SizedBox(width: UiConstants.space1), + Text( + 'Different Location', + style: UiTypography.footnote1m.textSecondary, + ), + ], + ), + GestureDetector( + onTap: () => _updatePosition(index, 'location', null), + child: const Icon( + UiIcons.close, + size: 14, + color: UiColors.destructive, + ), + ), + ], + ), + const SizedBox(height: UiConstants.space2), + UiTextField( + controller: TextEditingController(text: pos['location']), + hintText: 'Enter different address', + onChanged: (String val) => + _updatePosition(index, 'location', val), + ), + ], + ), + + const SizedBox(height: UiConstants.space3), + + _buildSectionHeader('LUNCH BREAK'), + _buildDropdownField( + hint: 'No break', + value: pos['lunch_break'], + items: [0, 15, 30, 45, 60], + itemBuilder: (dynamic val) { + if (val == 0) return 'No break'; + return '$val min'; + }, + onChanged: (dynamic val) => + _updatePosition(index, 'lunch_break', val), + ), + ], + ), + ); + } + + Widget _buildDropdownField({ + required String hint, + required dynamic value, + required List items, + String Function(dynamic)? itemBuilder, + required ValueChanged onChanged, + }) { + return Container( + height: 44, + padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3), + decoration: BoxDecoration( + borderRadius: UiConstants.radiusMd, + border: Border.all(color: UiColors.border), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: true, + hint: Text(hint, style: UiTypography.body2r.textPlaceholder), + value: value == '' || value == null ? null : value, + icon: const Icon( + UiIcons.chevronDown, + size: 18, + color: UiColors.iconSecondary, + ), + onChanged: onChanged, + items: items.toSet().map((dynamic item) { + String label = item.toString(); + if (itemBuilder != null) label = itemBuilder(item); + return DropdownMenuItem( + value: item, + child: Text(label, style: UiTypography.body2r.textPrimary), + ); + }).toList(), + ), + ), + ); + } + + Widget _buildInlineTimeInput({ + required String label, + required String value, + required VoidCallback onTap, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: UiTypography.footnote2r.textSecondary), + const SizedBox(height: UiConstants.space1), + GestureDetector( + onTap: onTap, + child: Container( + height: 40, + padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3), + decoration: BoxDecoration( + borderRadius: UiConstants.radiusSm, + border: Border.all(color: UiColors.border), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + value.isEmpty ? '--:--' : value, + style: UiTypography.body2r.textPrimary, + ), + const Icon( + UiIcons.clock, + size: 14, + color: UiColors.iconSecondary, + ), + ], + ), + ), + ), + ], + ); + } + + Widget _buildBottomAction({ + required String label, + required VoidCallback onPressed, + }) { + return Container( + padding: EdgeInsets.fromLTRB( + UiConstants.space5, + UiConstants.space5, + UiConstants.space5, + MediaQuery.of(context).padding.bottom + UiConstants.space5, + ), + decoration: const BoxDecoration( + color: UiColors.white, + border: Border(top: BorderSide(color: UiColors.border)), + ), + child: SizedBox( + width: double.infinity, + child: UiButton.primary( + text: label, + onPressed: onPressed, + size: UiButtonSize.large, + ), + ), + ); + } + Widget _buildReviewView() { final int totalWorkers = _positions.fold( 0, @@ -963,64 +1189,11 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { height: MediaQuery.of(context).size.height * 0.95, decoration: const BoxDecoration( color: UiColors.bgSecondary, - borderRadius: BorderRadius.vertical( - top: Radius.circular(UiConstants.radiusBase * 2), - ), + borderRadius: BorderRadius.vertical(top: Radius.circular(24)), ), child: Column( children: [ - // Header - Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 16), - decoration: const BoxDecoration( - color: UiColors.primary, - borderRadius: BorderRadius.vertical( - top: Radius.circular(UiConstants.radiusBase * 2), - ), - ), - child: Row( - children: [ - GestureDetector( - onTap: () => setState(() => _showReview = false), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: UiColors.white.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(12), - ), - child: const Center( - child: Icon( - UiIcons.chevronLeft, - color: UiColors.white, - size: 24, - ), - ), - ), - ), - const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Review Order', - style: UiTypography.title1m.copyWith( - color: UiColors.white, - ), - ), - Text( - 'Confirm details before saving', - style: UiTypography.body3r.copyWith( - color: UiColors.white.withValues(alpha: 0.7), - ), - ), - ], - ), - ], - ), - ), - - // Content + _buildHeader(), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(20), @@ -1078,9 +1251,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { const SizedBox(width: 8), Text( _dateController.text, - style: UiTypography.body2m.copyWith( - color: UiColors.textPrimary, - ), + style: UiTypography.body2m.textPrimary, ), ], ), @@ -1099,9 +1270,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { Expanded( child: Text( _globalLocationController.text, - style: UiTypography.body2r.copyWith( - color: UiColors.textPrimary, - ), + style: UiTypography.body2r.textPrimary, overflow: TextOverflow.ellipsis, ), ), @@ -1115,9 +1284,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { Text( 'Positions Breakdown', - style: UiTypography.body2b.copyWith( - color: UiColors.textPrimary, - ), + style: UiTypography.body2b.textPrimary, ), const SizedBox(height: 12), @@ -1133,36 +1300,36 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { // Footer Container( - padding: const EdgeInsets.all(20), + padding: EdgeInsets.fromLTRB( + UiConstants.space5, + UiConstants.space5, + UiConstants.space5, + MediaQuery.of(context).padding.bottom + UiConstants.space5, + ), decoration: const BoxDecoration( color: UiColors.white, - border: Border(top: BorderSide(color: UiColors.separatorPrimary)), + border: Border(top: BorderSide(color: UiColors.border)), ), - child: SafeArea( - top: false, - child: Row( - children: [ - Expanded( - child: UiButton.secondary( - text: 'Edit', - onPressed: () => setState(() => _showReview = false), - ), + child: Row( + children: [ + Expanded( + child: UiButton.secondary( + text: 'Edit', + onPressed: () => setState(() => _showReview = false), ), - const SizedBox(width: 12), - Expanded( - child: UiButton.primary( - text: 'Confirm & Save', - onPressed: () async { - setState(() => _isLoading = true); - await Future.delayed(const Duration(seconds: 1)); - if (mounted) { - // TODO: Implement actual save logic - } - }, - ), + ), + const SizedBox(width: 12), + Expanded( + child: UiButton.primary( + text: 'Confirm & Save', + onPressed: () async { + setState(() => _isLoading = true); + await Future.delayed(const Duration(seconds: 1)); + if (mounted) Navigator.pop(context); + }, ), - ], - ), + ), + ], ), ), ], @@ -1190,141 +1357,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { ); } - Widget _buildPositionCard(int index, Map pos) { - return Container( - margin: const EdgeInsets.only(bottom: 16), - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: UiColors.white, - borderRadius: BorderRadius.circular(16), - border: Border.all(color: UiColors.separatorSecondary, width: 2), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Container( - width: 24, - height: 24, - decoration: const BoxDecoration( - color: UiColors.primary, - shape: BoxShape.circle, - ), - child: Center( - child: Text( - '${index + 1}', - style: UiTypography.footnote2b.copyWith( - color: UiColors.white, - ), - ), - ), - ), - const SizedBox(width: 8), - Text( - 'Position ${index + 1}', - style: UiTypography.footnote2m.copyWith( - color: UiColors.textSecondary, - ), - ), - ], - ), - if (_positions.length > 1) - GestureDetector( - onTap: () => _removePosition(index), - child: Container( - padding: const EdgeInsets.all(4), - decoration: const BoxDecoration( - color: Color(0xFFFEF2F2), - shape: BoxShape.circle, - ), - child: const Icon( - UiIcons.close, - size: 14, - color: UiColors.destructive, - ), - ), - ), - ], - ), - const SizedBox(height: 16), - - _buildSectionLabel('Position Title *'), - UiTextField( - controller: TextEditingController(text: pos['role']), - hintText: 'e.g. Server, Bartender', - prefixIcon: UiIcons.briefcase, - onChanged: (String val) => _updatePosition(index, 'role', val), - ), - const SizedBox(height: 12), - - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildSectionLabel('Start Time *'), - UiTextField( - controller: TextEditingController( - text: pos['start_time'], - ), - prefixIcon: UiIcons.clock, - onTap: () {}, // Time picker - ), - ], - ), - ), - const SizedBox(width: 8), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildSectionLabel('End Time *'), - UiTextField( - controller: TextEditingController(text: pos['end_time']), - prefixIcon: UiIcons.clock, - onTap: () {}, // Time picker - ), - ], - ), - ), - ], - ), - const SizedBox(height: 12), - - _buildSectionLabel('Workers Needed'), - Row( - children: [ - _buildCounterBtn( - icon: UiIcons.minus, - onTap: () { - if ((pos['count'] as int) > 1) { - _updatePosition(index, 'count', (pos['count'] as int) - 1); - } - }, - ), - const SizedBox(width: 16), - Text('${pos['count']}', style: UiTypography.body1b), - const SizedBox(width: 16), - _buildCounterBtn( - icon: UiIcons.add, - onTap: () { - _updatePosition(index, 'count', (pos['count'] as int) + 1); - }, - ), - ], - ), - ], - ), - ); - } - Widget _buildReviewPositionCard(Map pos) { - // Simplified cost calculation return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), @@ -1345,15 +1378,11 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { pos['role'].toString().isEmpty ? 'Position' : pos['role'].toString(), - style: UiTypography.body2b.copyWith( - color: UiColors.textPrimary, - ), + style: UiTypography.body2b.textPrimary, ), Text( '${pos['count']} worker${pos['count'] > 1 ? 's' : ''}', - style: UiTypography.footnote2r.copyWith( - color: UiColors.textSecondary, - ), + style: UiTypography.footnote2r.textSecondary, ), ], ), @@ -1374,9 +1403,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { const SizedBox(width: 6), Text( '${pos['start_time']} - ${pos['end_time']}', - style: UiTypography.footnote2r.copyWith( - color: UiColors.textSecondary, - ), + style: UiTypography.footnote2r.textSecondary, ), ], ), @@ -1385,23 +1412,6 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { ); } - Widget _buildCounterBtn({ - required IconData icon, - required VoidCallback onTap, - }) { - return GestureDetector( - onTap: onTap, - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: UiColors.bgSecondary, - borderRadius: BorderRadius.circular(8), - ), - child: Icon(icon, size: 16, color: UiColors.primary), - ), - ); - } - Widget _buildSuccessView() { return Container( width: double.infinity, @@ -1461,17 +1471,4 @@ class _OrderEditSheetState extends State<_OrderEditSheet> { ), ); } - - Widget _buildSectionLabel(String label) { - return Padding( - padding: const EdgeInsets.only(bottom: 8), - child: Text( - label.toUpperCase(), - style: UiTypography.titleUppercase4m.copyWith( - color: UiColors.textSecondary, - fontWeight: FontWeight.bold, - ), - ), - ); - } }