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>
81 lines
2.9 KiB
Dart
81 lines
2.9 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:miler/views/Dashboard/home/trip.dart';
|
|
|
|
/// Bookings shows one flat list — no trip tabs, deliberately — so the only
|
|
/// thing telling the rider which of the day's runs he is on is the label above
|
|
/// it. The number has to mean what the hub means by it, and that is the part
|
|
/// worth pinning down: the numbering is against the WHOLE day, not against the
|
|
/// subset of it the rider happened to accept.
|
|
///
|
|
/// This exercises the same `groupIntoTrips` + positional numbering the screen
|
|
/// uses, which is the piece that can silently go wrong.
|
|
void main() {
|
|
Map<String, dynamic> stop(String id, String slotFrom, String slotTo) =>
|
|
<String, dynamic>{
|
|
'orderid': id,
|
|
'pickupcustomer': 'Shop $id',
|
|
'slotstarttime': slotFrom,
|
|
'slotendtime': slotTo,
|
|
};
|
|
|
|
/// Order id → 1-based trip number, the way `_numberTrips` builds it.
|
|
Map<String, int> numberTrips(List<Map<String, dynamic>> allToday) {
|
|
final byId = <String, Map<String, dynamic>>{};
|
|
for (final s in allToday) {
|
|
final id = (s['orderid'] ?? '').toString();
|
|
if (id.isEmpty) continue;
|
|
byId.putIfAbsent(id, () => s);
|
|
}
|
|
final trips = Trip.groupIntoTrips(byId.values.toList());
|
|
return <String, int>{
|
|
for (var i = 0; i < trips.length; i++)
|
|
for (final id in trips[i].orderIds) id: i + 1,
|
|
};
|
|
}
|
|
|
|
final morning = [
|
|
stop('A', '2026-08-06T09:00:00', '2026-08-06T12:00:00'),
|
|
stop('B', '2026-08-06T09:00:00', '2026-08-06T12:00:00'),
|
|
];
|
|
final afternoon = [stop('C', '2026-08-06T14:00:00', '2026-08-06T17:00:00')];
|
|
|
|
test('trips are numbered by their slot, earliest first', () {
|
|
final numbers = numberTrips([...afternoon, ...morning]);
|
|
|
|
expect(numbers['A'], 1);
|
|
expect(numbers['B'], 1);
|
|
expect(numbers['C'], 2);
|
|
});
|
|
|
|
test('a rider who took only the afternoon is still on Trip 2', () {
|
|
// ── The reason this is computed from the day and not from the queue ──
|
|
//
|
|
// Numbering only the accepted stops would call this run "Trip 1", while the
|
|
// hub, Home and the rider's own supervisor all call it Trip 2. The label is
|
|
// a shared name or it is worse than nothing.
|
|
final dayNumbers = numberTrips([...morning, ...afternoon]);
|
|
expect(dayNumbers['C'], 2);
|
|
|
|
final queueOnly = numberTrips(afternoon);
|
|
expect(
|
|
queueOnly['C'],
|
|
1,
|
|
reason: 'this is exactly the wrong answer the screen must not give',
|
|
);
|
|
});
|
|
|
|
test('a stop appearing twice in one fetch does not split its trip', () {
|
|
// The same stop arrives from up to three sources per fetch — v1, v3 and the
|
|
// local accepted store — and each copy carries the same slot.
|
|
final numbers = numberTrips([...morning, ...morning, ...afternoon]);
|
|
|
|
expect(numbers['A'], 1);
|
|
expect(numbers['C'], 2);
|
|
});
|
|
|
|
test('a day with one slot is Trip 1, not "your route"', () {
|
|
expect(numberTrips(morning)['A'], 1);
|
|
});
|
|
}
|