Fix student profile dates not saving
Native date pickers blur/refocus the window, which triggered a React Query refetch mid-edit and wiped the in-progress date. Disable refetchOnWindowFocus, and make profile/academics edits work on a snapshot with functional state updates so a background refetch can't clobber input. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -3,7 +3,14 @@ import { createRouter } from "@tanstack/react-router";
|
|||||||
import { routeTree } from "./routeTree.gen";
|
import { routeTree } from "./routeTree.gen";
|
||||||
|
|
||||||
export const getRouter = () => {
|
export const getRouter = () => {
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
// Don't refetch when the window regains focus — opening a native date
|
||||||
|
// picker blurs/refocuses the window, which would refetch mid-edit and
|
||||||
|
// wipe in-progress form input.
|
||||||
|
queries: { refetchOnWindowFocus: false },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
routeTree,
|
routeTree,
|
||||||
|
|||||||
@@ -159,9 +159,13 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
|
|||||||
queryKey: ["classes"],
|
queryKey: ["classes"],
|
||||||
queryFn: async () => (await supabase.from("classes").select("id, name").order("name")).data ?? [],
|
queryFn: async () => (await supabase.from("classes").select("id, name").order("name")).data ?? [],
|
||||||
});
|
});
|
||||||
const [form, setForm] = useState<Record<string, unknown> | null>(null);
|
// In edit mode we work on a snapshot (`form`) taken when Edit is pressed, so a
|
||||||
const c = (form ?? s) as Record<string, unknown> | null;
|
// background refetch can never clobber in-progress input. Functional updates
|
||||||
const upd = (k: string, v: unknown) => setForm({ ...(c as Record<string, unknown>), [k]: v });
|
// avoid stale-closure races.
|
||||||
|
const [form, setForm] = useState<Record<string, unknown>>({});
|
||||||
|
const startEdit = () => { setForm({ ...(s as Record<string, unknown>) }); setEditing(true); };
|
||||||
|
const c = (editing ? form : s) as Record<string, unknown> | null;
|
||||||
|
const upd = (k: string, v: unknown) => setForm((f) => ({ ...f, [k]: v }));
|
||||||
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]);
|
||||||
|
|
||||||
@@ -180,7 +184,7 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
|
|||||||
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;
|
||||||
},
|
},
|
||||||
onSuccess: () => { toast.success("Profile saved"); qc.invalidateQueries({ queryKey: ["student", studentId] }); setForm(null); setEditing(false); },
|
onSuccess: () => { toast.success("Profile saved"); qc.invalidateQueries({ queryKey: ["student", studentId] }); setEditing(false); },
|
||||||
onError: (e: Error) => toast.error(e.message),
|
onError: (e: Error) => toast.error(e.message),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,7 +196,7 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
|
|||||||
if (!editing) {
|
if (!editing) {
|
||||||
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">
|
||||||
<div className="flex justify-end"><EditToggle editing={editing} setEditing={setEditing} canEdit={canEdit} /></div>
|
<div className="flex justify-end">{canEdit && <Button size="sm" variant="outline" onClick={startEdit}><Pencil className="h-4 w-4 mr-1" /> Edit</Button>}</div>
|
||||||
<Section title="Student">
|
<Section title="Student">
|
||||||
<ViewRow label="First name" value={val("first_name")} />
|
<ViewRow label="First name" value={val("first_name")} />
|
||||||
<ViewRow label="Last name" value={val("last_name")} />
|
<ViewRow label="Last name" value={val("last_name")} />
|
||||||
@@ -281,7 +285,7 @@ function ProfileTab({ studentId, canEdit, isAdmin }: { studentId: string; canEdi
|
|||||||
</Section>
|
</Section>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button onClick={() => save.mutate()} disabled={save.isPending}>{save.isPending ? "Saving…" : "Save profile"}</Button>
|
<Button onClick={() => save.mutate()} disabled={save.isPending}>{save.isPending ? "Saving…" : "Save profile"}</Button>
|
||||||
<Button variant="ghost" onClick={() => { setForm(null); setEditing(false); }}>Cancel</Button>
|
<Button variant="ghost" onClick={() => setEditing(false)}>Cancel</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -513,9 +517,10 @@ function AcademicsTab({ studentId, canEdit }: { studentId: string; canEdit: bool
|
|||||||
queryKey: ["student", studentId],
|
queryKey: ["student", studentId],
|
||||||
queryFn: async () => (await supabase.from("students").select("*").eq("id", studentId).single()).data,
|
queryFn: async () => (await supabase.from("students").select("*").eq("id", studentId).single()).data,
|
||||||
});
|
});
|
||||||
const [form, setForm] = useState<Record<string, unknown> | null>(null);
|
const [form, setForm] = useState<Record<string, unknown>>({});
|
||||||
const c = (form ?? s) as Record<string, unknown> | null;
|
const startEdit = () => { setForm({ ...(s as Record<string, unknown>) }); setEditing(true); };
|
||||||
const upd = (k: string, v: unknown) => setForm({ ...(c as Record<string, unknown>), [k]: v });
|
const c = (editing ? form : s) as Record<string, unknown> | null;
|
||||||
|
const upd = (k: string, v: unknown) => setForm((f) => ({ ...f, [k]: v }));
|
||||||
const val = (k: string) => (c?.[k] as string) ?? "";
|
const val = (k: string) => (c?.[k] as string) ?? "";
|
||||||
const { data: logins } = useQuery({
|
const { data: logins } = useQuery({
|
||||||
queryKey: ["curriculum", studentId],
|
queryKey: ["curriculum", studentId],
|
||||||
@@ -530,7 +535,7 @@ function AcademicsTab({ studentId, canEdit }: { studentId: string; canEdit: bool
|
|||||||
}).eq("id", studentId);
|
}).eq("id", studentId);
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
},
|
},
|
||||||
onSuccess: () => { toast.success("Saved"); qc.invalidateQueries({ queryKey: ["student", studentId] }); setForm(null); },
|
onSuccess: () => { toast.success("Saved"); qc.invalidateQueries({ queryKey: ["student", studentId] }); },
|
||||||
onError: (e: Error) => toast.error(e.message),
|
onError: (e: Error) => toast.error(e.message),
|
||||||
});
|
});
|
||||||
const addLogin = useMutation({
|
const addLogin = useMutation({
|
||||||
@@ -543,7 +548,7 @@ function AcademicsTab({ studentId, canEdit }: { studentId: string; canEdit: bool
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 mt-4">
|
<div className="space-y-6 mt-4">
|
||||||
<div className="flex justify-end"><EditToggle editing={editing} setEditing={setEditing} canEdit={canEdit} /></div>
|
<div className="flex justify-end"><EditToggle editing={editing} setEditing={(v) => (v ? startEdit() : setEditing(false))} canEdit={canEdit} /></div>
|
||||||
|
|
||||||
<div className="bg-card border rounded-lg p-6 space-y-4">
|
<div className="bg-card border rounded-lg p-6 space-y-4">
|
||||||
{!editing ? (
|
{!editing ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user