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

75
test/device_id_test.dart Normal file
View File

@@ -0,0 +1,75 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:miler/utils/device.dart';
/// ─────────────────────────────────────────────────────────────────────────
/// THE SIGN-IN MUST NOT DIE ON A FIELD IT DOES NOT SEND
///
/// `ensureDeviceId` threw on every unhappy path, and it is awaited on the line
/// before the PIN check posts — inside the sign-in's outer `try`. So the throw
/// was caught as "Could not reach the server" before a single byte left the
/// phone, and the MPIN screen then reported it as **"Incorrect MPIN"**. For
/// every phone number, on every attempt.
///
/// It threw for a guaranteed reason, not a rare one: it asked for
/// `androidInfo` unconditionally, which raises on iOS.
///
/// There are no platform channels in a widget test, so `device_info_plus`
/// raises `MissingPluginException` here — which makes this file an exact
/// reproduction of the iOS path. Under the old code every test below throws.
/// ─────────────────────────────────────────────────────────────────────────
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() => SharedPreferences.setMockInitialValues({}));
test('hands back an id even when the platform cannot supply one', () async {
final prefs = await SharedPreferences.getInstance();
final id = await DeviceUtils.ensureDeviceId(prefs);
expect(id, isNotEmpty, reason: 'a sign-in cannot wait for a device id');
expect(
id,
startsWith(DeviceUtils.unknownDeviceId),
reason: 'a generated id says so, so a log can be read honestly',
);
});
test('the id is stable across calls — that is the whole job', () async {
final prefs = await SharedPreferences.getInstance();
final first = await DeviceUtils.ensureDeviceId(prefs);
final second = await DeviceUtils.ensureDeviceId(prefs);
expect(second, first);
expect(prefs.getString('deviceId'), first);
});
test('a stored hardware id is never replaced by a generated one', () async {
SharedPreferences.setMockInitialValues({'deviceId': 'real-hardware-id'});
final prefs = await SharedPreferences.getInstance();
expect(await DeviceUtils.ensureDeviceId(prefs), 'real-hardware-id');
});
test('two installs do not collide', () async {
final prefs = await SharedPreferences.getInstance();
final a = await DeviceUtils.ensureDeviceId(prefs);
SharedPreferences.setMockInitialValues({});
final b = await DeviceUtils.ensureDeviceId(
await SharedPreferences.getInstance(),
);
expect(a, isNot(b));
});
test('the push token degrades to empty rather than throwing', () async {
// Same contract, for the same reason: a rider who declined notifications
// still has a shift to start.
final prefs = await SharedPreferences.getInstance();
expect(await DeviceUtils.ensureFcmToken(prefs), '');
});
}