Read profile date inputs from the DOM at save (Safari-proof)

Safari doesn't reliably fire onChange for native date pickers, so read dob/
start/agreement dates directly from the input refs when saving.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 20:01:55 -04:00
co-authored by Claude Opus 4.8
parent e95171a0d4
commit 872eacefc5
+10 -2
View File
@@ -11,7 +11,7 @@ import { Checkbox } from "@/components/ui/checkbox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
import { ArrowLeft, Trash2, Plus, FileText, Loader2, ImageUp, User, Pencil, Check, UserPlus, Copy, Link2, Mail } from "lucide-react"; import { ArrowLeft, Trash2, Plus, FileText, Loader2, ImageUp, User, Pencil, Check, UserPlus, Copy, Link2, Mail } from "lucide-react";
import { useState } from "react"; import { useState, useRef } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { createUserFn } from "@/lib/user-admin.functions"; import { createUserFn } from "@/lib/user-admin.functions";
import { createIntakeToken } from "@/lib/intake.functions"; import { createIntakeToken } from "@/lib/intake.functions";
@@ -167,6 +167,9 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
const startEdit = () => { setForm({ ...(s as Record<string, unknown>) }); setEditing(true); }; const startEdit = () => { setForm({ ...(s as Record<string, unknown>) }); setEditing(true); };
const c = (editing ? form : s) as Record<string, unknown> | null; const c = (editing ? form : s) as Record<string, unknown> | null;
const upd = (k: string, v: unknown) => setForm((f) => ({ ...f, [k]: v })); const upd = (k: string, v: unknown) => setForm((f) => ({ ...f, [k]: v }));
// Read date inputs straight from the DOM at save time (Safari doesn't reliably
// fire onChange for native date pickers, so state can be stale/empty).
const dateRefs = useRef<Record<string, HTMLInputElement | null>>({});
const dismissal = (c?.dismissal_methods as string[]) ?? []; const dismissal = (c?.dismissal_methods as string[]) ?? [];
const toggleDismissal = (v: string) => upd("dismissal_methods", dismissal.includes(v) ? dismissal.filter((x) => x !== v) : [...dismissal, v]); const toggleDismissal = (v: string) => upd("dismissal_methods", dismissal.includes(v) ? dismissal.filter((x) => x !== v) : [...dismissal, v]);
@@ -182,6 +185,11 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
for (const k of keys) payload[k] = (c[k] as string) || null; for (const k of keys) payload[k] = (c[k] as string) || null;
payload.first_name = (c.first_name as string) || ""; payload.first_name = (c.first_name as string) || "";
payload.last_name = (c.last_name as string) || ""; payload.last_name = (c.last_name as string) || "";
// Dates: read the live DOM value so a Safari pick can't be lost.
for (const k of ["dob", "preferred_start_date", "agreement_signed_date"]) {
const el = dateRefs.current[k];
if (el) payload[k] = el.value || null;
}
const { error } = await supabase.from("students").update(payload as never).eq("id", studentId); const { error } = await supabase.from("students").update(payload as never).eq("id", studentId);
if (error) throw error; if (error) throw error;
}, },
@@ -235,7 +243,7 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
// ── Edit mode ── // ── Edit mode ──
const T = (k: string, label: string) => <Field label={label}><Input value={val(k)} onChange={(e) => upd(k, e.target.value)} /></Field>; const T = (k: string, label: string) => <Field label={label}><Input value={val(k)} onChange={(e) => upd(k, e.target.value)} /></Field>;
// Uncontrolled (defaultValue) so Safari's native date picker doesn't get reset by React's controlled value. // Uncontrolled (defaultValue) so Safari's native date picker doesn't get reset by React's controlled value.
const D = (k: string, label: string) => <Field label={label}><Input type="date" defaultValue={val(k)} onChange={(e) => upd(k, e.target.value)} /></Field>; const D = (k: string, label: string) => <Field label={label}><Input type="date" defaultValue={val(k)} ref={(el) => { dateRefs.current[k] = el; }} onChange={(e) => upd(k, e.target.value)} /></Field>;
const A = (k: string, label: string, rows = 2) => <Field label={label}><Textarea rows={rows} value={val(k)} onChange={(e) => upd(k, e.target.value)} /></Field>; const A = (k: string, label: string, rows = 2) => <Field label={label}><Textarea rows={rows} value={val(k)} onChange={(e) => upd(k, e.target.value)} /></Field>;
return ( return (
<div className="bg-card border rounded-lg p-6 space-y-6 mt-4"> <div className="bg-card border rounded-lg p-6 space-y-6 mt-4">