refactor: extract shared utilities and standardize page patterns

- Add fmtBytes() and csvToArr() helpers to hoover/helpers.js
- Replace inline async patterns with ActionButton/ConfirmDelete in wireguard.js
- Convert addDomain/editDomain to QuickModal + apiSubmit in proxy.js
- Convert settingsModal handlers to formAction in certs.js
- Remove redundant synced handling from dhcp.js apply button
- Add onComplete callback to ConfirmDelete (fixes users.js onRefresh bug)
- Fix passkeys.js ActionCell/Table usage (invalid component API)
- Remove duplicate fmtBytes from dashboard.js
This commit is contained in:
2026-07-28 17:32:51 +00:00
parent 244576b8eb
commit 8bb3619ddc
10 changed files with 156 additions and 220 deletions
+24 -41
View File
@@ -18,9 +18,8 @@ import {
PageHeader,
Empty,
Table,
esc,
ActionCell,
Badge,
esc,
startRegistration,
webauthnSupported,
isModalProcessing,
@@ -224,60 +223,44 @@ function CredentialsPage() {
PageHeader({
title: 'Passkeys',
subtitle: 'Manage your passkey credentials for passwordless authentication',
actions: html`<button class="btn btn-sm btn-primary" onClick=${() => webauthnSupported() && addCredentialModal()}>
Add passkey
</button>`,
actions: webauthnSupported()
? html`<button class="btn btn-sm btn-primary" onClick=${() => addCredentialModal()}>Add passkey</button>`
: html`<span class="text-sm text-muted">WebAuthn not supported</span>`,
}),
html`<div class="card" key="empty">
<div class="text-muted text-sm">No passkeys registered</div>
<button class="btn btn-sm btn-primary"
onClick=${() => webauthnSupported() && addCredentialModal()}>
Add passkey
</button>
${webauthnSupported()
? html`<button class="btn btn-sm btn-primary" onClick=${() => addCredentialModal()}>Add passkey</button>`
: ''}
</div>`,
];
}
const cols = [
{ key: 'name', label: 'Name' },
{ key: 'transports', label: 'Transports' },
{ key: 'signCount', label: 'Uses' },
{ key: 'id', label: 'ID' },
{ key: '_action', label: '' },
];
const rows = state.credentials.map(c => ({
name: esc(c.name || 'Unnamed'),
transports: (c.transports || ['internal']).map(t =>
html`<Badge>${esc(t)}</Badge>`
),
signCount: c.sign_count ?? 0,
id: esc(c.id.slice(0, 12) + '...'),
_action: ActionCell({
actions: [
{
label: 'Remove',
cls: 'btn-danger',
icon: 'Delete',
onClick: () => confirmRemove(c.id, c.name),
},
],
}),
}));
const rows = state.credentials.map(c =>
html`<tr key=${c.id}>
<td><strong>${esc(c.name || 'Unnamed')}</strong></td>
<td>${(c.transports || ['internal']).map(t => html`<${Badge} text=${esc(t)} />`)}</td>
<td>${c.sign_count ?? 0}</td>
<td class="text-sm">${esc(c.id.slice(0, 12) + '...')}</td>
<td><button class="btn btn-sm btn-outline" onClick=${() => confirmRemove(c.id, c.name)}>Remove</button></td>
</tr>`
);
const actions = webauthnSupported()
? html`<button class="btn btn-sm btn-primary" onClick=${() => addCredentialModal()}>
Add passkey
</button>`
: html`<span class="text-sm text-muted">WebAuthn not supported in this browser</span>`;
? html`<button class="btn btn-sm btn-primary" onClick=${() => addCredentialModal()}>Add passkey</button>`
: html`<span class="text-sm text-muted">WebAuthn not supported</span>`;
return [
PageHeader({
title: 'Passkeys',
subtitle: 'Manage your passkey credentials for passwordless authentication',
actions: actions,
actions,
}),
Table({
columns: ['Name', 'Transports', 'Uses', 'ID', 'Actions'],
rows,
emptyText: 'No passkeys found',
}),
Table({ columns: cols, rows }),
];
}