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:
@@ -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 }),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user