feat(water-log): remember last unit per headgate (cross-device)
Deploy to route.crispygoat.com / deploy (push) Successful in 4m11s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m11s
Field operators no longer have to re-pick their preferred unit on the same headgate every shift. Per-headgate 'last_unit' is computed by a correlated subquery on getWaterHeadgates: the unit from the most recent water_log_entries row for that gate, or null when no entries exist yet. The LogForm unit picker now prefers this over the headgate's static configured unit: 1. remembered last_unit (if in catalog) 2. headgate.unit (if in catalog) 3. units[0] (final fallback) Because the value is derived from water_log_entries (not stored in a new column), no migration is needed — the entry-table is already the source of truth for what was actually logged, and a new entry naturally propagates to the next visit. Cross-device by construction. Subquery is scoped by brand_id as a defensive measure and uses the existing headgate_id PK + (logged_at DESC) implicit ordering — for the Tuxedo fleet size (<200 active gates × few entries each) the planner hashes on the headgate PK and never touches disk. Refs /tmp/refactor-water-log.md (customer-feature followup)
This commit is contained in:
@@ -66,9 +66,15 @@ export function MobileWaterLogForm({
|
||||
const { t, lang } = useLang();
|
||||
// ── State ────────────────────────────────────────────────────────
|
||||
const [measurement, setMeasurement] = useState("");
|
||||
const [unit, setUnit] = useState<string>(
|
||||
units.includes(headgate.unit) ? headgate.unit : units[0],
|
||||
);
|
||||
// Unit priority: most-recent log > headgate's static config > first
|
||||
// option. `last_unit` is the operator's *last* choice on this gate
|
||||
// across the whole fleet — far better than re-picking every shift.
|
||||
const [unit, setUnit] = useState<string>(() => {
|
||||
const remembered = headgate.last_unit;
|
||||
if (remembered && units.includes(remembered)) return remembered;
|
||||
if (units.includes(headgate.unit)) return headgate.unit;
|
||||
return units[0];
|
||||
});
|
||||
const [notes, setNotes] = useState("");
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
@@ -18,6 +18,13 @@ export type FieldHeadgate = {
|
||||
notes: string | null;
|
||||
/** ISO timestamp of last successful log. */
|
||||
last_used_at: string | null;
|
||||
/**
|
||||
* Unit from the most recent log entry for this headgate, or
|
||||
* `null` if none have been logged yet. The log form's unit picker
|
||||
* prefers this over the headgate's configured `unit` so operators
|
||||
* don't have to re-pick on every visit.
|
||||
*/
|
||||
last_unit: string | null;
|
||||
};
|
||||
|
||||
/** The units the mobile app offers in its segmented control.
|
||||
|
||||
Reference in New Issue
Block a user