From ce62b17898fcdfe8804f4f9f0a89c2065d8e35e4 Mon Sep 17 00:00:00 2001 From: Tyler Date: Wed, 1 Jul 2026 20:04:41 -0600 Subject: [PATCH] feat(water-log): surface irrigator name on log form + success screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makes the currently-signed-in irrigator visible while data is being entered and confirmed, so the operator can verify the reading is attributed to them at a glance. - LogForm nav bar: title + initials avatar + name as a subtitle, sign-out button moves from the form footer into the nav bar (more discoverable, frees vertical space for the Submit button) - SuccessScreen recap: 'Logged by {name}' row with avatar below the timestamp, matches the log form styling - MobileWaterApp threads irrigatorName through to both screens The headgate list still shows 'Signed in as {name}' as a small caption — left untouched to avoid duplicate UI. Validation: - tsc --noEmit clean - eslint clean on all modified files --- src/components/water/mobile/LogForm.tsx | 72 +++++++++++++------ .../water/mobile/MobileWaterApp.tsx | 2 + src/components/water/mobile/SuccessScreen.tsx | 29 +++++++- 3 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/components/water/mobile/LogForm.tsx b/src/components/water/mobile/LogForm.tsx index 6509051..fc07917 100644 --- a/src/components/water/mobile/LogForm.tsx +++ b/src/components/water/mobile/LogForm.tsx @@ -34,7 +34,7 @@ import { import { SegmentedControl } from "./SegmentedControl"; import type { FieldHeadgate, MobileUnit } from "./types"; import { MOBILE_UNITS } from "./types"; -import { ChevronLeft, Clock, Gauge, Pencil, Pin, Spinner } from "./icons"; +import { ChevronLeft, Clock, Gauge, Logout, Pencil, Pin, Spinner } from "./icons"; import { formatLongTimestamp, formatMeasurement, @@ -43,6 +43,7 @@ import { type Props = { headgate: FieldHeadgate; + irrigatorName?: string; onSubmit: (input: { measurement: number; unit: string; notes: string }) => Promise; onChangeHeadgate: () => void; onLogout: () => void; @@ -54,6 +55,7 @@ const NOTES_MAX = 500; export function MobileWaterLogForm({ headgate, + irrigatorName, onSubmit, onChangeHeadgate, onLogout, @@ -137,10 +139,9 @@ export function MobileWaterLogForm({ > {/* ── Top nav ────────────────────────────────────────────── */}
-

+

+ Log Reading +

+ {irrigatorName ? ( +
+ + {initials(irrigatorName)} + + {irrigatorName} +
+ ) : null} + + +
{/* ── Scrollable form body ───────────────────────────────── */} @@ -352,15 +375,6 @@ export function MobileWaterLogForm({ )} -
- -
@@ -377,4 +391,16 @@ function SectionHeader({ children }: { children: React.ReactNode }) { ); } +/** + * Two-letter initials for the avatar chip in the nav bar. "Tyler + * Smith" → "TS", "Maria" → "MA". Falls back to "?" when there's no + * usable name. + */ +function initials(name: string): string { + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) return "?"; + if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase(); + return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); +} + export default MobileWaterLogForm; \ No newline at end of file diff --git a/src/components/water/mobile/MobileWaterApp.tsx b/src/components/water/mobile/MobileWaterApp.tsx index 2ccc3a5..d2eafed 100644 --- a/src/components/water/mobile/MobileWaterApp.tsx +++ b/src/components/water/mobile/MobileWaterApp.tsx @@ -213,6 +213,7 @@ export function MobileWaterApp() { diff --git a/src/components/water/mobile/SuccessScreen.tsx b/src/components/water/mobile/SuccessScreen.tsx index 55e7073..ee10e3d 100644 --- a/src/components/water/mobile/SuccessScreen.tsx +++ b/src/components/water/mobile/SuccessScreen.tsx @@ -26,6 +26,9 @@ type Props = { measurement: number; unit: string; timestamp: Date; + /** Name of the irrigator who logged the entry — shown in the recap + * so the operator can confirm the reading is attributed to them. */ + irrigatorName?: string; onLogAnother: () => void; onBackToHeadgates: () => void; }; @@ -35,6 +38,7 @@ export function MobileWaterSuccessScreen({ measurement, unit, timestamp, + irrigatorName, onLogAnother, onBackToHeadgates, }: Props) { @@ -197,6 +201,17 @@ export function MobileWaterSuccessScreen({
{formatLongTimestamp(timestamp)}
+ {irrigatorName ? ( +
+ + {initials(irrigatorName)} + + Logged by {irrigatorName} +
+ ) : null} {/* Action buttons */} @@ -247,4 +262,16 @@ export function MobileWaterSuccessScreen({ ); } -export default MobileWaterSuccessScreen; \ No newline at end of file +export default MobileWaterSuccessScreen; + +/** + * Two-letter initials for the avatar chip in the recap. + * Mirrors the same helper used in the log form header so the avatar + * stays consistent across the two screens. + */ +function initials(name: string): string { + const parts = name.trim().split(/\s+/).filter(Boolean); + if (parts.length === 0) return "?"; + if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase(); + return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); +} \ No newline at end of file