Polish pass: removed lastTouched dead code, selective useWatch perf, token sweep, required-field messages, scroll-to-first-error, file prefill display, matrix sticky shadow

This commit is contained in:
Joel Brock
2026-05-09 22:49:25 -07:00
parent e452fbb15f
commit 0d84b9654b
4 changed files with 180 additions and 89 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useId, useMemo, useState } from "react";
import { useEffect, useId, useMemo, useState } from "react";
import type { StageSectionConfig, SelectOption } from "@/types/form";
import type { UseFormRegister, FieldValues, FieldErrors } from "react-hook-form";
import { FieldRenderer } from "./fields/FieldRenderer";
@@ -20,8 +20,6 @@ interface StageSectionProps {
defaultOpen: boolean;
/** Option groups fetched from CiviCRM, keyed by option_group_id. */
options: Record<number, SelectOption[]>;
/** Optional last-checked-in timestamp for this stage (ISO date). */
lastTouched?: string;
}
/**
@@ -43,12 +41,22 @@ export function StageSection({
isCurrent,
defaultOpen,
options,
lastTouched,
}: StageSectionProps) {
const [open, setOpen] = useState(defaultOpen);
const headingId = useId();
const panelId = useId();
// Listen for programmatic expand requests (fired by EngagementForm when a
// submit fails on a required field inside a collapsed section).
useEffect(() => {
const handler = (e: Event) => {
const detail = (e as CustomEvent<{ sectionId: string }>).detail;
if (detail?.sectionId === section.id) setOpen(true);
};
document.addEventListener("coop-checkin:expand-section", handler);
return () => document.removeEventListener("coop-checkin:expand-section", handler);
}, [section.id]);
const matrixFieldNames = useMemo(() => {
const names = new Set<string>();
for (const m of section.matrixGroups ?? []) {
@@ -104,12 +112,6 @@ export function StageSection({
<span className="tabular-nums">
{fieldCount} {fieldCount === 1 ? "field" : "fields"}
</span>
{lastTouched && (
<>
<span aria-hidden>·</span>
<span>Last touched {formatRelative(lastTouched)}</span>
</>
)}
</span>
</span>
<Chevron open={open} />
@@ -210,13 +212,3 @@ function Chevron({ open }: { open: boolean }) {
);
}
function formatRelative(iso: string): string {
const then = new Date(iso).getTime();
if (Number.isNaN(then)) return iso;
const days = Math.round((Date.now() - then) / (1000 * 60 * 60 * 24));
if (days <= 0) return "today";
if (days === 1) return "yesterday";
if (days < 30) return `${days} days ago`;
if (days < 365) return `${Math.round(days / 30)} months ago`;
return `${Math.round(days / 365)} years ago`;
}