**Source:** codebase audit, 2026-06-11 (audit finding F7, Tier 1) **Severity:** Low **Category:** DRY ## Problem The same mutation-wrapper pattern is duplicated: ```tsx const run = (action: () => Promise<unknown>, onSuccess: () => void): void => { setError(null); setNotice(null); setBusy(true); action().then(onSuccess).catch(err => setError(errorMessage(err))).finally(() => setBusy(false)); }; ``` Sites: - [UsersView.tsx:114](frontend/src/admin/UsersView.tsx#L114) - [DbCredentialsView.tsx:102](frontend/src/admin/DbCredentialsView.tsx#L102) Only two sites today, so it's not worth its own hook in isolation — but it folds naturally into the cluster with #55 / #57 / #58. ## Suggested approach Either: 1. **If** doing the #57 `useAsyncData` refactor: pair it with a sibling `useMutation(action, { onSuccess, refresh? })` hook in the same `frontend/src/hooks/` module. 2. **Else**: defer — the duplication is small and isolated. Test alongside whichever hook lands. ## Part of a cluster Same PR as #55, #56, #57, #58.
Source: codebase audit, 2026-06-11 (audit finding F7, Tier 1)
Severity: Low
Category: DRY
Problem
The same mutation-wrapper pattern is duplicated:
Sites:
Only two sites today, so it's not worth its own hook in isolation — but it folds naturally into the cluster with #55 / #57 / #58.
Suggested approach
Either:
useAsyncDatarefactor: pair it with a siblinguseMutation(action, { onSuccess, refresh? })hook in the samefrontend/src/hooks/module.Test alongside whichever hook lands.
Part of a cluster
Same PR as #55, #56, #57, #58.