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:
@@ -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',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user