File fields: surface a prior-attachment indicator from RHF prefill
The earlier 'Currently on file' indicator hung off readonlyValue, which is sourced from evalState (only carries current_stage). For file fields the prefill lives in RHF state, not in evalState, so the indicator never fired. Replaced with a FilePriorIndicator subcomponent that subscribes to the field's RHF value via useWatch and renders a small leaf-50 banner with a paperclip glyph when a previous attachment is present. Falls silent the moment the user picks a new file (RHF value becomes a FileList). Filename is derived from whatever shape Civi returned — bare string, object with file_name/name/label/filename, or numeric file id (generic message in that case).
This commit is contained in:
@@ -203,16 +203,10 @@ export function FieldRenderer({
|
||||
|
||||
// ── File ────────────────────────────────────────────────────────────────
|
||||
if (field.type === "file") {
|
||||
const priorName = typeof readonlyValue === "string" ? readonlyValue : "";
|
||||
const hasPrior = priorName.length > 0;
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="space-y-1.5">
|
||||
<Label id={id} field={field} />
|
||||
{hasPrior && (
|
||||
<p className="text-xs text-ink-soft" aria-live="polite">
|
||||
Currently on file: <span className="font-medium text-ink">{priorName}</span>
|
||||
</p>
|
||||
)}
|
||||
<FilePriorIndicator control={control} name={field.name} />
|
||||
<input
|
||||
id={id}
|
||||
type="file"
|
||||
@@ -363,6 +357,68 @@ function CurrencyPreview({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* For file fields: detects whether a previously-uploaded attachment is
|
||||
* carried in this field's prefill value (RHF state) and surfaces a small
|
||||
* banner with a paperclip glyph. Falls silent once the user picks a new
|
||||
* file (RHF value becomes a FileList) so it doesn't contradict their
|
||||
* fresh upload. Filename is derived from whatever shape Civi returned —
|
||||
* a bare string filename, an object with `file_name`/`name`/`label`, or
|
||||
* a numeric file id (in which case we render a generic message).
|
||||
*/
|
||||
function FilePriorIndicator({
|
||||
control,
|
||||
name,
|
||||
}: {
|
||||
control: Control<FieldValues>;
|
||||
name: string;
|
||||
}) {
|
||||
const value = useWatch({ control, name });
|
||||
if (value === null || value === undefined || value === "") return null;
|
||||
// A FileList means the user has just picked a new file — they don't
|
||||
// need a reminder about what *used* to be on file.
|
||||
if (typeof FileList !== "undefined" && value instanceof FileList) return null;
|
||||
|
||||
let filename: string | null = null;
|
||||
if (typeof value === "string") {
|
||||
filename = value;
|
||||
} else if (typeof value === "object" && value !== null) {
|
||||
const o = value as Record<string, unknown>;
|
||||
const cand = o.file_name ?? o.name ?? o.label ?? o.filename;
|
||||
if (typeof cand === "string") filename = cand;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
className="flex items-start gap-2 rounded-md border border-rule-soft bg-leaf-50/60 px-3 py-2 text-xs"
|
||||
>
|
||||
<svg
|
||||
aria-hidden
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="mt-[1px] h-3.5 w-3.5 flex-shrink-0 text-leaf-700"
|
||||
>
|
||||
<path d="M11.5 4.5 L6 10 a2 2 0 1 0 2.83 2.83 L13.5 7.5 a3.5 3.5 0 0 0 -4.95 -4.95 L3.5 7.5" />
|
||||
</svg>
|
||||
<span className="text-ink-soft leading-snug">
|
||||
Attachment on file
|
||||
{filename ? (
|
||||
<>
|
||||
: <span className="font-medium text-ink break-all">{filename}</span>
|
||||
</>
|
||||
) : null}
|
||||
. Choose a new file below to replace it, or leave blank to keep it.
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Label({ id, field }: { id: string; field: FieldConfig }) {
|
||||
return (
|
||||
<label htmlFor={id} className="block text-sm font-medium text-ink">
|
||||
|
||||
Reference in New Issue
Block a user