feat(water-log): remember last unit per headgate (cross-device)
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:
Tyler
2026-07-02 11:32:18 -06:00
parent 9ecb496a7c
commit d34fc2434a
3 changed files with 50 additions and 4 deletions
+9 -3
View File
@@ -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);
+7
View File
@@ -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.