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:
2026-08-22 05:40:35 +05:30
parent c8350563d9
commit d7348e253f
387 changed files with 80693 additions and 12272 deletions

View File

@@ -76,21 +76,27 @@ void main() {
/// Gap between the bottom of the visible card and the bottom of the screen.
double gapBelowCard(WidgetTester tester) {
final screen = tester.view.physicalSize.height / tester.view.devicePixelRatio;
final screen =
tester.view.physicalSize.height / tester.view.devicePixelRatio;
return screen - tester.getRect(find.byType(Material).last).bottom;
}
testWidgets('clears the floating nav bar while the bar is showing',
(tester) async {
testWidgets('clears the floating nav bar while the bar is showing', (
tester,
) async {
await pump(tester);
final context = tester.element(find.byType(ActivePickupBanner));
final required = BottomPage.bottomInset(context);
expect(gapBelowCard(tester), greaterThanOrEqualTo(required),
reason: 'the card must sit above the nav bar, which claims '
'${required.toStringAsFixed(1)}pt at the foot of the page. It was '
'pinned to bottom: 0 and the bar covered it.');
expect(
gapBelowCard(tester),
greaterThanOrEqualTo(required),
reason:
'the card must sit above the nav bar, which claims '
'${required.toStringAsFixed(1)}pt at the foot of the page. It was '
'pinned to bottom: 0 and the bar covered it.',
);
});
testWidgets('sits at one height rather than chasing the bar', (tester) async {
@@ -106,13 +112,18 @@ void main() {
expect(gapBelowCard(tester), closeTo(resting, 0.5));
final context = tester.element(find.byType(ActivePickupBanner));
expect(resting, greaterThanOrEqualTo(BottomPage.bottomInset(context)),
reason: 'it must clear the bar at its resting height, which is the '
'height the bar returns to the moment he scrolls back up');
expect(
resting,
greaterThanOrEqualTo(BottomPage.bottomInset(context)),
reason:
'it must clear the bar at its resting height, which is the '
'height the bar returns to the moment he scrolls back up',
);
});
testWidgets('lines up with the nav bar rather than running full width',
(tester) async {
testWidgets('lines up with the nav bar rather than running full width', (
tester,
) async {
await pump(tester);
final card = tester.getRect(find.byType(Material).last);
// The nav bar sits on a 16 gutter; the banner stacks directly under it.
@@ -131,11 +142,15 @@ void main() {
width: width,
textScale: scale,
count: 3,
address: '19/4B, Thadagam Main Road, Venkitapuram, Coimbatore, '
address:
'19/4B, Thadagam Main Road, Venkitapuram, Coimbatore, '
'Tamil Nadu 641025',
);
expect(tester.takeException(), isNull,
reason: 'the banner overflowed at ${width.toInt()}pt / ${scale}x');
expect(
tester.takeException(),
isNull,
reason: 'the banner overflowed at ${width.toInt()}pt / ${scale}x',
);
});
}
}
@@ -150,10 +165,14 @@ void main() {
testWidgets('names the count and the stop status', (tester) async {
await pump(tester, count: 3);
expect(find.text('3 Active Pickups'), findsOneWidget);
expect(find.textContaining('At the stop'), findsOneWidget,
reason: 'the rider should see how far into the stop he already is. The '
'wording comes from StopStatusX.label, the one place the app names '
'its statuses — and it is "At the stop" rather than the banner\'s '
'old private "At pickup", which was wrong on a delivery');
expect(
find.textContaining('At the stop'),
findsOneWidget,
reason:
'the rider should see how far into the stop he already is. The '
'wording comes from StopStatusX.label, the one place the app names '
'its statuses — and it is "At the stop" rather than the banner\'s '
'old private "At pickup", which was wrong on a delivery',
);
});
}