also looking at this
refactor(dashboard): one confirm bar instead of five copies
#131Part of #125.
partials/op-confirm.stx now takes three optional params — label (default 'Run'), danger (default false), output (default true) — so the five pages that hand-rolled their own bar can use the shared one. The defaults keep the 13 existing callers byte-identical.
Migrated: firewall, team, ssh-keys, secrets, alarms — each renames its confirm signals to the contract the partial binds to (pending / typed / confirmTok / confirmVerb / canRun / runOp / cancelOp) and drops ~6 lines of duplicated markup.
Divergences this fixes
The copies had quietly drifted apart:
firewall's confirm button said "Run" under a "Remove" trigger- the shared bar rendered a non-danger button even for
backup:restore - neither the partial nor any copy set
type="button"— they defaulted tosubmit - the confirm input had only
placeholder="confirm", no accessible name
What this does not do
The ~15-line JS block stays duplicated per page. That was the other half of #125 and it isn't currently possible — I tried three ways and verified each against a working control:
| Approach | Result |
|---|---|
import { useOpConfirm } from '../../src/use-op-confirm.ts' | bundles, page 200, bar never appears |
same logic moved into the partial as <script client> | inlines into the page, same failure |
import … from '@composables' | transforms to window.__composables, but stx has no auto-registration |
stx bundles an importing client script into module scope, so directives (@click="askOp(…)", @show="pending()…") can no longer resolve those names. It fails closed — the click is a silent no-op, no console error — which is exactly why this would have been nasty to ship blind. Follow-up filed upstream.
The two free-form command runners keep their own bars: those pages already bind the partial to their operations, so a second include would collide on names.
Verification
Ran every confirm bar in a headless browser against the live dev server — clicking the trigger, checking the token/label/gating, typing the token, and confirming the request only fires afterwards:
PASS firewall (migrated) tok="6379" btn="Remove" gated sentEarly=0 sentAfter=1
PASS team (migrated) tok="ada" btn="Remove" gated sentEarly=0 sentAfter=1
PASS secrets (migrated) tok="acme/production/app-key" btn="Delete" gated sentEarly=0 sentAfter=1
PASS alarms (migrated) tok="acme-production-http-errors" btn="Delete" gated sentEarly=0 sentAfter=1
PASS ssh-keys (migrated) tok="glenn-laptop" btn="Remove" gated sentEarly=0 sentAfter=1
PASS backups (untouched) tok="backup" btn="Run" gated sentEarly=0 sentAfter=1
PASS services (untouched) tok="nginx" btn="Run" gated sentEarly=0 sentAfter=1The two untouched pages confirm the defaults cause no regression. Lint clean.
Unrelated but worth knowing: packages/ui's dev/build scripts pass --pages, which stx removed (it now auto-detects pages/). bun run dev fails outright until that's updated — likely why this UI hasn't been exercised locally.
0 changed files on the files tab, with 0 review threads.