miler map
This commit is contained in:
@@ -8,6 +8,7 @@ import 'package:miler/data/service_profile.dart';
|
||||
import 'package:miler/data/stop_area.dart';
|
||||
import 'package:miler/views/Dashboard/home/stop_card.dart' show LiveMark;
|
||||
import 'package:miler/views/Dashboard/home/trip.dart';
|
||||
import 'package:miler/views/Dashboard/pickups/stop_type.dart';
|
||||
import 'package:miler/views/Dashboard/pickups/route_metrics.dart';
|
||||
import 'package:miler/views/helpers/constants/Colorconstants.dart';
|
||||
import 'package:miler/views/helpers/constants/design_constants.dart';
|
||||
@@ -209,9 +210,7 @@ class RouteGroup {
|
||||
if (allStopStates.length != allStops.length) {
|
||||
return allStops.isNotEmpty ? allStops.length : stops.length;
|
||||
}
|
||||
final carried = allStopStates
|
||||
.where((s) => s == StopState.collected)
|
||||
.length;
|
||||
final carried = allStopStates.where((s) => s == StopState.collected).length;
|
||||
return stops.length + carried;
|
||||
}
|
||||
|
||||
@@ -495,14 +494,32 @@ class _RouteTimelineState extends State<RouteTimeline> {
|
||||
}
|
||||
|
||||
/// Every order on the run the rider may tick right now, across the flat
|
||||
/// groups — a parcel route, where each stop is its own place.
|
||||
/// groups — the stops that are their own place.
|
||||
///
|
||||
/// One rung only, and only when there are at least two: with one stop its own
|
||||
/// row already is the select-all.
|
||||
///
|
||||
/// ── Why this is not "every group is flat" ──
|
||||
///
|
||||
/// It was, and the guard was right while a route was one shape or the other:
|
||||
/// a parcel run of single-stop nodes got a run-level select-all, a kitchen
|
||||
/// run got one per counter, and neither had the other's.
|
||||
///
|
||||
/// A logistics route is now **both** — its drops gather under the hub they
|
||||
/// were loaded at, its collections stay their own doors — and `every` made
|
||||
/// the whole control vanish the moment one group was not flat. The rider lost
|
||||
/// the only way to take his collections in one go, on exactly the routes that
|
||||
/// have the most of them.
|
||||
///
|
||||
/// So it gathers from the flat groups and ignores the rest. A grouped place
|
||||
/// already carries its own select-all *inside* it, over its own orders, which
|
||||
/// is the control that belongs to it; this one is for the stops that have no
|
||||
/// place above them to hold it. A run with no flat groups at all — a milk
|
||||
/// round — still gets nothing here, exactly as before.
|
||||
List<String> get _flatSelectIds {
|
||||
if (!widget.groups.every((g) => g.flat)) return const [];
|
||||
final ids = <String>[];
|
||||
for (final g in widget.groups) {
|
||||
if (!g.flat) continue;
|
||||
if (g.state != StopState.pending) continue;
|
||||
for (final (i, s) in g.stops.indexed) {
|
||||
if (g.stopStates[i] != StopState.pending) continue;
|
||||
@@ -783,7 +800,7 @@ class SelectAllAction extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
textAlign: scoped ? TextAlign.right : TextAlign.left,
|
||||
style: MilerType.label.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: accent,
|
||||
),
|
||||
),
|
||||
@@ -1006,7 +1023,7 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: MilerType.label.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: ColorConstants.primary,
|
||||
),
|
||||
),
|
||||
@@ -1488,7 +1505,7 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
? MilerType.cardTitle
|
||||
: MilerType.body)
|
||||
.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: group.isDone || rejected
|
||||
? ColorConstants.secondaryText
|
||||
: ColorConstants.slateText,
|
||||
@@ -1571,7 +1588,7 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// pale grey reserved for things nobody has
|
||||
// to act on.
|
||||
style: MilerType.label.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: chosen > 0
|
||||
? ColorConstants.primary
|
||||
: ColorConstants.onSurfaceVariant,
|
||||
@@ -1608,7 +1625,18 @@ class PickupTimelineGroup extends StatelessWidget {
|
||||
// the distances line up on one right edge, so "which is
|
||||
// nearest" is answered by looking down a column rather
|
||||
// than reading five blocks.
|
||||
// ── And on a flat stop, which has no rail under it ──
|
||||
//
|
||||
// A place with one order draws no list, so there is no
|
||||
// rung for [_KindTag] to sit on — and a logistics route
|
||||
// is mostly flat stops, which is exactly where "am I
|
||||
// collecting or dropping here" is asked. The tag goes on
|
||||
// the header instead, in the same corner and at the same
|
||||
// size, so the answer is in one place whichever shape the
|
||||
// route happens to take.
|
||||
if (group.flat) ...[
|
||||
if (group.stops.isNotEmpty)
|
||||
_KindTag(kind: stopKindOf(group.stops.first)),
|
||||
SizedBox(width: 4.w),
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
@@ -2151,8 +2179,8 @@ class PickupTimelineStep extends StatelessWidget {
|
||||
style: MilerType.body.copyWith(
|
||||
fontSize: 15.sp,
|
||||
fontWeight: selected || accepted
|
||||
? FontWeight.w600
|
||||
: FontWeight.w500,
|
||||
? FontWeight.w700
|
||||
: FontWeight.w600,
|
||||
color: rejected
|
||||
? ColorConstants.secondaryText
|
||||
: selected || accepted
|
||||
@@ -2190,6 +2218,11 @@ class PickupTimelineStep extends StatelessWidget {
|
||||
// [OrderManifest.labelFor].
|
||||
label: rejected ? 'Rejected' : line.label,
|
||||
orderId: line.orderId,
|
||||
// What the rider is doing at this door — see
|
||||
// [_KindTag]. Read from the stop itself, so a route
|
||||
// that mixes collections and drops says which is which
|
||||
// on every rung.
|
||||
kind: stopKindOf(line.stop),
|
||||
selected: selected,
|
||||
onTap: canOpen ? onOpen : null,
|
||||
),
|
||||
@@ -2228,12 +2261,17 @@ class _OrderOpenBlock extends StatelessWidget {
|
||||
final String label;
|
||||
|
||||
final String orderId;
|
||||
|
||||
/// Collection, drop, or both — stamped beside the chevron.
|
||||
final StopKind kind;
|
||||
|
||||
final bool selected;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const _OrderOpenBlock({
|
||||
required this.label,
|
||||
required this.orderId,
|
||||
required this.kind,
|
||||
required this.selected,
|
||||
required this.onTap,
|
||||
});
|
||||
@@ -2310,8 +2348,8 @@ class _OrderOpenBlock extends StatelessWidget {
|
||||
style: MilerType.label.copyWith(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: selected
|
||||
? FontWeight.w700
|
||||
: FontWeight.w600,
|
||||
? FontWeight.w800
|
||||
: FontWeight.w700,
|
||||
color: selected
|
||||
? ColorConstants.primary
|
||||
: ColorConstants.secondaryText,
|
||||
@@ -2327,12 +2365,22 @@ class _OrderOpenBlock extends StatelessWidget {
|
||||
// the list. The id is on the detail sheet this block opens, and
|
||||
// on the Activity record — both places the rare question is
|
||||
// actually asked.
|
||||
if (onTap != null)
|
||||
// ── What he is doing here, in one or two characters ──
|
||||
//
|
||||
// A mixed route runs collections and drops down the same rail,
|
||||
// and until now the only way to tell one rung from another was
|
||||
// to open it. The tag sits inside the block the chevron closes,
|
||||
// so the row reads `name · area · P ›` — identity, place, job,
|
||||
// and the way in.
|
||||
_KindTag(kind: kind),
|
||||
if (onTap != null) ...[
|
||||
SizedBox(width: 4.w),
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
size: 18.sp,
|
||||
color: ColorConstants.borderStrong,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -2342,6 +2390,60 @@ class _OrderOpenBlock extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
/// `P`, `D` or `P&D` on a rail rung.
|
||||
///
|
||||
/// ── A mark, not a word ──
|
||||
///
|
||||
/// The full names are [StopKindUi.badgeLabel], and they are what the stop's own
|
||||
/// card and sheet carry — there is room there, and the rider is reading one
|
||||
/// stop. The rail is a *column*: he runs down it deciding which doors are
|
||||
/// collections and which are drops, and `PICKUP & DELIVERY` at a size that fits
|
||||
/// beside a chevron is a word he has to read on every rung. One or two
|
||||
/// characters is a mark he recognises. See [StopKindUi.shortLabel].
|
||||
///
|
||||
/// ── And it is grey ──
|
||||
///
|
||||
/// The obvious version tints it with the kind's own accent — collections one
|
||||
/// colour, drops another. `stop_card_neutral_test.dart` forbids exactly that,
|
||||
/// and the reason survives this tag: the route carries **one** accent, the
|
||||
/// brand, on the live node and the journey action, and a per-stop hue down the
|
||||
/// rail competes with the one mark that says *where you are*. Three hues on
|
||||
/// every rung is a second colour system on the screen that most needs one.
|
||||
///
|
||||
/// What was asked for here is the *information* — which of these is a
|
||||
/// collection and which is a drop — and a letter carries that on its own. So
|
||||
/// the tag is the same quiet grey as the metric tiles, and the accents stay
|
||||
/// where that test put them: on the stop's own sheet, one tap away, where the
|
||||
/// rider is reading one stop instead of scanning forty.
|
||||
class _KindTag extends StatelessWidget {
|
||||
final StopKind kind;
|
||||
|
||||
const _KindTag({required this.kind});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(left: 6.w),
|
||||
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorConstants.tint(ColorConstants.slateText, 0.055),
|
||||
borderRadius: BorderRadius.circular(DesignConstants.radiusFull),
|
||||
),
|
||||
child: Text(
|
||||
kind.shortLabel,
|
||||
maxLines: 1,
|
||||
style: MilerType.label.copyWith(
|
||||
fontSize: 10.5.sp,
|
||||
fontWeight: FontWeight.w900,
|
||||
letterSpacing: 0.2,
|
||||
height: 1.1,
|
||||
color: ColorConstants.secondaryText,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The order id, as every screen reads it.
|
||||
String stopIdOf(Map<String, dynamic> stop) =>
|
||||
(stop['orderid'] ?? '').toString();
|
||||
@@ -2893,7 +2995,7 @@ class _JourneyColumn extends StatelessWidget {
|
||||
// it: same column, one step down in size and weight.
|
||||
style: MilerType.label.copyWith(
|
||||
fontSize: prominent ? 13.sp : 12.5.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorConstants.secondaryText,
|
||||
),
|
||||
),
|
||||
@@ -2981,7 +3083,7 @@ class _StateWord extends StatelessWidget {
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: MilerType.label.copyWith(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
@@ -3182,7 +3284,7 @@ class _TextAction extends StatelessWidget {
|
||||
Text(
|
||||
label,
|
||||
style: MilerType.label.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorConstants.primary,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user