device infos

This commit is contained in:
2026-08-28 15:07:30 +05:30
parent 5723d373b2
commit 69f4f3e909
84 changed files with 4337 additions and 2339 deletions

View File

@@ -66,10 +66,10 @@ class StopCard extends StatelessWidget {
/// Time at the door, shown in the card's header.
final Duration service;
/// Which bag this order travels in — `Bag 3`, or the label the kitchen
/// printed. One order, one bag; the count is never a separate figure. See
/// [BagManifest].
final String bag;
/// Which label this order travels in — `Bag 3`, or the label the kitchen
/// printed. One order, one label; the count is never a separate figure. See
/// [OrderManifest].
final String label;
/// True when this card sits under a heading that already names its pickup
/// location.
@@ -122,7 +122,7 @@ class StopCard extends StatelessWidget {
required this.state,
required this.address,
required this.service,
this.bag = '',
this.label = '',
this.groupedUnderSource = false,
this.selected = false,
this.busy = false,
@@ -256,7 +256,7 @@ class StopCard extends StatelessWidget {
SizedBox(height: 12.h),
_factsRow(
kind,
bagShownAbove: twoLeg && !grouped && bag.isNotEmpty,
labelShownAbove: twoLeg && !grouped && label.isNotEmpty,
),
if (action != null) ...[SizedBox(height: 16.h), action],
],
@@ -318,7 +318,7 @@ class StopCard extends StatelessWidget {
/// `→ Joe · Bag 1` — the far end, in one line.
///
/// Everything the second leg used to spend a whole block on: who it is for,
/// and which bag it is. The address is one tap away in the detail sheet,
/// and which label it is. The address is one tap away in the detail sheet,
/// where a rider going to a *kitchen* has no use for it yet.
Widget _dropLine(String customer) {
return Row(
@@ -343,7 +343,10 @@ class StopCard extends StatelessWidget {
),
),
),
if (bag.isNotEmpty) ...[SizedBox(width: 10.w), _BagChip(label: bag)],
if (label.isNotEmpty) ...[
SizedBox(width: 10.w),
_LabelChip(label: label),
],
],
);
}
@@ -352,7 +355,7 @@ class StopCard extends StatelessWidget {
///
/// This is the line a rider re-reads at a red light, so nothing on it is
/// under 14sp and none of it is in a box.
Widget _factsRow(StopKind kind, {required bool bagShownAbove}) {
Widget _factsRow(StopKind kind, {required bool labelShownAbove}) {
final due = DateTime.tryParse(
(stop['expected_pickup_time'] ?? stop['eta'] ?? '').toString().trim(),
);
@@ -393,33 +396,33 @@ class StopCard extends StatelessWidget {
),
],
),
// What is in it, and which bag it is. The bag appears here whenever the
// drop line above is not already carrying it — a card must never be
// silent about which box this order is, and it must never say it twice.
// What is in it, and the counter's own label where there is one. It
// appears here whenever the drop line above is not already carrying it:
// a card must never say the same thing twice.
Builder(
builder: (_) {
final label = bag.isNotEmpty ? bag : stopBagLabel(stop);
final printed = label.isNotEmpty ? label : stopPrintedLabel(stop);
final meta = _MetaLine(
deliver: kind.hasDelivery ? _qty(deliveryParcelCount(stop)) : 0,
collect: kind.hasPickup ? _qty(pickupParcelCount(stop)) : 0,
cash: cash,
muted: _rejected || state == StopState.done,
// On a meal run the parcel counts are the one-bag rule restated
// On a meal run the parcel counts are the one-label rule restated
// as an item count, which is exactly the second number this app
// does not allow. Only the money survives.
countsHidden: ServiceProfile.active.deliversToCustomer,
);
final showBag = !bagShownAbove && label.isNotEmpty;
if (!showBag && meta.isEmpty) return const SizedBox.shrink();
final showLabel = !labelShownAbove && printed.isNotEmpty;
if (!showLabel && meta.isEmpty) return const SizedBox.shrink();
return Padding(
padding: EdgeInsets.only(top: 8.h),
child: Row(
children: [
if (!meta.isEmpty) Flexible(child: meta),
if (showBag) ...[
if (showLabel) ...[
if (!meta.isEmpty) SizedBox(width: 10.w),
_BagChip(label: label),
_LabelChip(label: printed),
],
],
),
@@ -441,7 +444,7 @@ class StopCard extends StatelessWidget {
/// journey and the bar offers the claim, and neither duplicates the other.
///
/// A stop that is already *arrived* is the one exception: the single-order
/// case ("one bag, one counter") would otherwise make him tick a card he is
/// case ("one label, one counter") would otherwise make him tick a card he is
/// looking at to reach a control that talks about "the selection". That
/// button opens the same slide sheet the bar does, over the same batch.
Widget? _primaryAction() {
@@ -598,16 +601,19 @@ class _IconAction extends StatelessWidget {
/// the card has exactly one colour to spend. It is a chip rather than plain
/// text because it is read *against an object* — a bordered word is findable on
/// a card at arm's length in a way a run of grey text is not.
class _BagChip extends StatelessWidget {
class _LabelChip extends StatelessWidget {
final String label;
const _BagChip({required this.label});
const _LabelChip({required this.label});
/// Backend labels are codes — `DG-1042`, `BAG-1` — and derived ones already
/// read `Bag 3`. The word belongs on both, and the test for "already has it"
/// is the *word*, not the prefix: `BAG-1` starts with those three letters and
/// is still a code, so it becomes `Bag BAG-1` exactly as it always read.
String get _text =>
label.toLowerCase().startsWith('bag ') ? label : 'Bag $label';
/// ── The word is gone; the code is not ──
///
/// This prefixed everything with `Bag ` — so a counter's own code rendered
/// as `Bag DG-1042`, and a number the app had derived rendered as `Bag 3`
/// and read like something stencilled on an object nobody had stencilled.
///
/// What a counter prints is printed on it already. It is shown exactly as it
/// was sent, and nothing is shown at all where nothing was sent.
String get _text => label;
@override
Widget build(BuildContext context) {