fix(ux): resolve tickets #55 #56 — resource modal stability and success feedback

#55: Add SuccessToast after new resource is created. ResourceModal gains an
optional onSuccess(displayName) prop; ResourcesClient wires it to a toast
that auto-dismisses after 2.5 s.

#56: Fix useFocusTrap stale-closure bug. Focusable elements are now queried
dynamically inside handleKeyDown (not captured once at mount), so Tab
navigation stays correct as the form re-renders. Initial focus is deferred
via requestAnimationFrame so the browser layout is stable before focus() fires.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-04-03 15:43:12 +02:00
parent 0d0707264d
commit 65db330a4d
3 changed files with 39 additions and 10 deletions
@@ -161,6 +161,7 @@ interface ResourceModalProps {
mode: "create" | "edit";
resource?: Resource;
onClose: () => void;
onSuccess?: (displayName: string) => void;
}
const INPUT_CLASS =
@@ -189,7 +190,7 @@ function Spinner() {
);
}
export function ResourceModal({ mode, resource, onClose }: ResourceModalProps) {
export function ResourceModal({ mode, resource, onClose, onSuccess }: ResourceModalProps) {
const [form, setForm] = useState<FormState>(() =>
resource ? resourceToFormState(resource) : defaultFormState(),
);
@@ -324,7 +325,12 @@ export function ResourceModal({ mode, resource, onClose }: ResourceModalProps) {
try {
if (mode === "create") {
await createMutation.mutateAsync(payload);
const created = await createMutation.mutateAsync(payload);
void utils.resource.directory.invalidate();
void utils.resource.listStaff.invalidate();
onSuccess?.(created.displayName);
onClose();
return;
} else if (resource) {
await updateMutation.mutateAsync({ id: resource.id, data: payload });
}