Admin UI: diagnostics in a panel attached below the dialog — no more layout shift; normal text color instead of dimmed
This commit is contained in:
@@ -95,8 +95,7 @@ const lockoutWarning = computed(() => {
|
||||
return pageOriginAllowed(d.origins || [], rpId) ? null : window.location.host
|
||||
})
|
||||
|
||||
// Whether any origin diagnostic is present (guards the list against
|
||||
// occupying dialog spacing when empty)
|
||||
// Whether any origin diagnostic is present
|
||||
const hasOriginDiagnostics = computed(() => {
|
||||
const d = props.dialog?.data
|
||||
if (props.dialog?.type !== 'domain-edit' || !d) return false
|
||||
@@ -104,6 +103,11 @@ const hasOriginDiagnostics = computed(() => {
|
||||
return relatedEntries.value.length > 5 || !!lockoutWarning.value
|
||||
})
|
||||
|
||||
// Any runtime diagnostic to show in the dialog's attached feedback panel
|
||||
const hasDiagnostics = computed(
|
||||
() => hasOriginDiagnostics.value || !!props.dialog?.data?.wellKnownCheck
|
||||
)
|
||||
|
||||
function pageOriginAllowed(rows, rpId) {
|
||||
const toUrl = key => (isWildcardEntry(key) || key.includes('://')) ? key : 'https://' + key
|
||||
const inDomain = []
|
||||
@@ -448,6 +452,21 @@ function onRemoveOrigin(i) {
|
||||
|
||||
<template>
|
||||
<Modal v-if="dialog.type" @close="$emit('closeDialog')">
|
||||
<template #attached>
|
||||
<div v-if="dialog?.type === 'domain-edit' && hasDiagnostics" class="diag-panel" @click.stop>
|
||||
<ul class="diag-list">
|
||||
<li v-if="dialog.data.originValidation.some(v => v === 'invalid')" class="small error">Some entries are invalid — check for typos in the hostname; a bare '*' or '**' is not allowed, and wildcards only within the domain.</li>
|
||||
<li v-if="dialog.data.originValidation.some(v => v === 'unreachable')" class="small">Some sites are unreachable — make sure they are routed to this instance.</li>
|
||||
<li v-else-if="dialog.data.originValidation.some(v => v === 'mismatch')" class="small">Some sites are reachable but do not serve this domain.</li>
|
||||
<li v-if="relatedEntries.length > 5" class="small error">At most 5 related origins are allowed ({{ relatedEntries.length }} listed) — the save is rejected.</li>
|
||||
<li v-if="lockoutWarning" class="small error">Saving would lock you out: {{ lockoutWarning }} could no longer run sign-in ceremonies for this domain. Keep it listed, or mark an auth host.</li>
|
||||
<li v-if="dialog.data.wellKnownCheck === 'validating'" class="small">Checking the published document…</li>
|
||||
<li v-else-if="dialog.data.wellKnownCheck === 'valid'" class="small">✓ The published document lists all related origins.</li>
|
||||
<li v-else-if="dialog.data.wellKnownCheck === 'missing'" class="small error">The published document does not list: {{ (dialog.data.wellKnownMissing || []).join(', ') }}</li>
|
||||
<li v-else-if="dialog.data.wellKnownCheck === 'unreachable'" class="small">Could not fetch the published document to verify it.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<h3 class="modal-title">
|
||||
<template v-if="dialog.type==='org-create'">Create Organization</template>
|
||||
<template v-else-if="dialog.type==='org-update'">Rename Organization</template>
|
||||
@@ -556,13 +575,6 @@ function onRemoveOrigin(i) {
|
||||
<p class="small muted">
|
||||
Only the listed sites may sign in with {{ dialog.data.rp_id }} passkeys. Wildcards may be used: <strong>**.{{ dialog.data.rp_id }}</strong> allows the whole domain, <strong>*.{{ dialog.data.rp_id }}</strong> only a single subdomain level.<template v-if="relatedEntries.length"> 🔗 means related host requiring WebAuthn ROR setup.</template><template v-if="dialog.data.auth_host"> 🔑 is the dedicated Paskia host for all account management.</template>
|
||||
</p>
|
||||
<ul v-if="hasOriginDiagnostics" class="diag-list">
|
||||
<li v-if="dialog.data.originValidation.some(v => v === 'invalid')" class="small error">Some entries are invalid — check for typos in the hostname; a bare '*' or '**' is not allowed, and wildcards only within the domain.</li>
|
||||
<li v-if="dialog.data.originValidation.some(v => v === 'unreachable')" class="small muted">Some sites are unreachable — make sure they are routed to this instance.</li>
|
||||
<li v-else-if="dialog.data.originValidation.some(v => v === 'mismatch')" class="small muted">Some sites are reachable but do not serve this domain.</li>
|
||||
<li v-if="relatedEntries.length > 5" class="small error">At most 5 related origins are allowed ({{ relatedEntries.length }} listed) — the save is rejected.</li>
|
||||
<li v-if="lockoutWarning" class="small error">Saving would lock you out: {{ lockoutWarning }} could no longer run sign-in ceremonies for this domain. Keep it listed, or mark an auth host.</li>
|
||||
</ul>
|
||||
|
||||
<template v-if="relatedEntries.length">
|
||||
<p class="small muted">
|
||||
@@ -574,12 +586,6 @@ function onRemoveOrigin(i) {
|
||||
<pre>{{ wellKnownJson }}</pre>
|
||||
<button type="button" class="icon-btn" @click="copyText(wellKnownJson, 'Well-known document')" aria-label="Copy well-known document" title="Copy">📋</button>
|
||||
</div>
|
||||
<ul v-if="dialog.data.wellKnownCheck" class="diag-list">
|
||||
<li v-if="dialog.data.wellKnownCheck === 'validating'" class="small muted">Checking the published document…</li>
|
||||
<li v-else-if="dialog.data.wellKnownCheck === 'valid'" class="small muted">✓ The published document lists all related origins.</li>
|
||||
<li v-else-if="dialog.data.wellKnownCheck === 'missing'" class="small error">The published document does not list: {{ (dialog.data.wellKnownMissing || []).join(', ') }}</li>
|
||||
<li v-else-if="dialog.data.wellKnownCheck === 'unreachable'" class="small muted">Could not fetch the published document to verify it.</li>
|
||||
</ul>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="dialog.type==='confirm'">
|
||||
|
||||
@@ -490,6 +490,22 @@ th {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* The diagnostics panel attaches below the dialog, so feedback appearing
|
||||
or disappearing never shifts the dialog's layout or position */
|
||||
.diag-panel {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.5rem);
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--color-dialog);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-xl);
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
max-height: 30vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
@@ -599,6 +615,12 @@ th {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
/* Positions attachments (e.g. the diagnostics panel) relative to the
|
||||
dialog; shrink-wraps the panel in the overlay's flex layout */
|
||||
.modal-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.device-dialog,
|
||||
.modal {
|
||||
background: var(--color-dialog);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<template>
|
||||
<div class="dialog-overlay" @click="$emit('close')">
|
||||
<div class="modal-wrap">
|
||||
<div ref="dialog" :class="['modal-panel', panelClass]" @keydown="handleDialogKeydown" @click.stop>
|
||||
<slot />
|
||||
</div>
|
||||
<slot name="attached" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user