Miler rider app: surface system, visible design language, backend lifecycle
Design system - MilerSurface ladder (canvas → working → raised → floating) with MilerPanel as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1. - Visible vocabulary applied across Home, Deliveries, Activity, Account and the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x), canvas wells for anything that opens, small filled tags for shelf labels, demoted placeholders. Recorded in DESIGN_SYSTEM.md §6. - One icon family: 222 Material glyphs migrated to Lucide; none left outside lib/xpress. - Colour semantics corrected: amber only for what is genuinely owed, brand red reserved for the live stop, disabled primaries go neutral rather than pale. Data and lifecycle - lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart makes admin sequence the single ordering authority; service_day.dart, and stop_area.dart rewritten against live Coimbatore addresses (digit-token stripping, city stoplist, street suffixes, stammer collapse). - countLabel states the load once, in bags. Testing - 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity, sheets and verify, with test/failures/ now gitignored (diff debris). - New pins: home_gutter_test, stop_area_test, plus updated structural bounds. Note: this commit also carries pre-existing working-tree deletions that were present before this work (API_SPEC.md, README.md, demo test fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -349,30 +349,57 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
'$_remainingCount ${_remainingCount == 1 ? 'stop' : 'stops'} left',
|
||||
];
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Icon(
|
||||
isCancelled ? Icons.cancel_rounded : Icons.check_circle_rounded,
|
||||
size: 16.sp,
|
||||
color: isCancelled
|
||||
? ColorConstants.errorRed
|
||||
: ColorConstants.acceptGreen,
|
||||
// ── It still has to answer "did that register?" ──
|
||||
//
|
||||
// The reasoning above is right that this is completed information and must
|
||||
// not out-rank the next stop. It went one step too far: a 16sp tick beside
|
||||
// 12.5sp grey text is the same treatment as every other caption on the
|
||||
// screen, so the one line confirming the act the rider just performed
|
||||
// looked like metadata about it.
|
||||
//
|
||||
// A tinted capsule — the app's own state vocabulary, the same one the route
|
||||
// chips and the sheet's secondary actions use — gives it an edge and its
|
||||
// semantic colour back without giving it size. It is still shorter and
|
||||
// quieter than the next-stop hero underneath, which is the ranking that
|
||||
// matters.
|
||||
final Color tone = isCancelled
|
||||
? ColorConstants.errorRed
|
||||
: ColorConstants.acceptGreen;
|
||||
|
||||
return Align(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 7.h),
|
||||
decoration: BoxDecoration(
|
||||
color: tone.withValues(alpha: 0.10),
|
||||
borderRadius: BorderRadius.circular(DesignConstants.radiusFull),
|
||||
),
|
||||
SizedBox(width: 7.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
parts.join(' · '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 12.5.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorConstants.secondaryText,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
isCancelled ? LucideIcons.circleX : LucideIcons.circleCheck,
|
||||
size: 18.sp,
|
||||
color: tone,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Flexible(
|
||||
child: Text(
|
||||
parts.join(' · '),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 13.5.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.2,
|
||||
color: tone,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -389,7 +416,8 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
/// asks what he will be doing when he arrives. A saturated chip at the top
|
||||
/// asked that question first and in the loudest voice on the screen.
|
||||
Widget _nextStopHero(Map<String, dynamic> stop) {
|
||||
final kind = stopKindOf(stop);
|
||||
// The leg, not the payload — see [MilkRun.workingKind].
|
||||
final kind = MilkRun.workingKind(stop);
|
||||
final name = (stop['pickupcustomer'] ?? 'Stop').toString();
|
||||
final address = (stop['pickupaddress'] ?? 'Address not available')
|
||||
.toString();
|
||||
@@ -408,7 +436,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 28.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.9,
|
||||
height: 1.08,
|
||||
color: ColorConstants.slateText,
|
||||
@@ -450,7 +478,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 12.5.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: kind.accent,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -478,11 +506,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.replay_rounded,
|
||||
size: 15.sp,
|
||||
color: ColorConstants.warning,
|
||||
),
|
||||
Icon(LucideIcons.rotateCcw, size: 15.sp, color: ColorConstants.warning),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Text(
|
||||
@@ -490,7 +514,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
style: TextStyle(
|
||||
fontSize: 12.5.sp,
|
||||
height: 1.35,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: ColorConstants.warning,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -541,7 +565,8 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
}
|
||||
|
||||
Widget _upNextRow(Map<String, dynamic> stop, int position) {
|
||||
final kind = stopKindOf(stop);
|
||||
// The leg, not the payload — see [MilkRun.workingKind].
|
||||
final kind = MilkRun.workingKind(stop);
|
||||
final name = (stop['pickupcustomer'] ?? 'Stop').toString();
|
||||
final address = (stop['pickupaddress'] ?? '').toString();
|
||||
|
||||
@@ -562,8 +587,8 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
child: Text(
|
||||
'$position',
|
||||
style: TextStyle(
|
||||
fontSize: 11.5.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: kind.accent,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -580,7 +605,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 15.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.2,
|
||||
color: ColorConstants.slateText,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
@@ -605,7 +630,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
LucideIcons.chevronRight,
|
||||
size: 20.sp,
|
||||
color: ColorConstants.secondaryText,
|
||||
),
|
||||
@@ -636,7 +661,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
'Finding your next stop…',
|
||||
style: TextStyle(
|
||||
fontSize: 13.5.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorConstants.secondaryText,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -666,7 +691,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.warehouse_rounded,
|
||||
LucideIcons.warehouse,
|
||||
size: 30.sp,
|
||||
color: ColorConstants.acceptGreen,
|
||||
),
|
||||
@@ -677,7 +702,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.6,
|
||||
color: ColorConstants.slateText,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
@@ -725,8 +750,16 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
? 'Loading…'
|
||||
: hasNext
|
||||
? 'Move to next stop'
|
||||
: 'Return to hub',
|
||||
icon: hasNext ? Icons.arrow_forward_rounded : Icons.warehouse_rounded,
|
||||
// A milk run ends at the last door, not at a depot — see
|
||||
// [ServiceProfile.endsAtHub]. Capability, not screen position.
|
||||
: (ServiceProfile.active.endsAtHub
|
||||
? 'Return to hub'
|
||||
: 'Finish my round'),
|
||||
icon: hasNext
|
||||
? LucideIcons.arrowRight
|
||||
: (ServiceProfile.active.endsAtHub
|
||||
? LucideIcons.warehouse
|
||||
: LucideIcons.house),
|
||||
color: hasNext ? ColorConstants.acceptGreen : ColorConstants.primary,
|
||||
loading: _loadingNextOrders,
|
||||
onPressed: _loadingNextOrders ? null : () => _goToNextStop(),
|
||||
@@ -738,16 +771,19 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
/// Section labels. Small, letterspaced, grey — the quietest way to name a
|
||||
/// group, and the reason none of the groups needs an outline.
|
||||
TextStyle get _eyebrow => TextStyle(
|
||||
fontSize: 11.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
letterSpacing: 1.3,
|
||||
// 11 -> 12.5, and less tracking. A letterspaced 11sp all-caps label is the
|
||||
// least readable combination in the app, on the screen a rider glances at
|
||||
// between two stops.
|
||||
fontSize: 12.5.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 0.9,
|
||||
color: ColorConstants.secondaryText,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
);
|
||||
|
||||
TextStyle get _metaStyle => TextStyle(
|
||||
fontSize: 12.5.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 13.5.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: ColorConstants.secondaryText,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
);
|
||||
@@ -779,11 +815,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
color: ColorConstants.rewardAmber,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.card_giftcard_rounded,
|
||||
size: 30.sp,
|
||||
color: Colors.white,
|
||||
),
|
||||
child: Icon(LucideIcons.gift, size: 30.sp, color: Colors.white),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -796,7 +828,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
'You Won a Bonus!',
|
||||
style: TextStyle(
|
||||
fontSize: 24.sp,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: ColorConstants.onSurface,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -869,7 +901,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.monetization_on_rounded,
|
||||
LucideIcons.circleDollarSign,
|
||||
size: 64.sp,
|
||||
color: const Color(0xFFFFB800),
|
||||
),
|
||||
@@ -878,7 +910,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
'${widget.bonusPoints}',
|
||||
style: TextStyle(
|
||||
fontSize: 52.sp,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: ColorConstants.onSurface,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
letterSpacing: -2,
|
||||
@@ -888,7 +920,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
'Bonus Points',
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorConstants.onSurfaceVariant,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -909,7 +941,7 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.touch_app_rounded,
|
||||
LucideIcons.pointer,
|
||||
color: ColorConstants.onSurfaceVariant,
|
||||
size: 16.sp,
|
||||
),
|
||||
@@ -945,8 +977,16 @@ class _PickupsDoneState extends State<PickupsDone>
|
||||
Widget _buildDoneButton({required bool isEnabled}) {
|
||||
final bool hasNext = _nextOrders.isNotEmpty;
|
||||
return MilerButton(
|
||||
label: hasNext ? 'Move to next stop' : 'Return to hub',
|
||||
icon: hasNext ? Icons.arrow_forward_rounded : Icons.warehouse_rounded,
|
||||
label: hasNext
|
||||
? 'Move to next stop'
|
||||
: (ServiceProfile.active.endsAtHub
|
||||
? 'Return to hub'
|
||||
: 'Finish my round'),
|
||||
icon: hasNext
|
||||
? LucideIcons.arrowRight
|
||||
: (ServiceProfile.active.endsAtHub
|
||||
? LucideIcons.warehouse
|
||||
: LucideIcons.house),
|
||||
color: hasNext ? ColorConstants.acceptGreen : ColorConstants.primary,
|
||||
onPressed: isEnabled ? () => _goToNextStop() : null,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user