UI : badge de statut texte (À JOUR, ÉCARTS, BLOQUANT) sur les cartes à la place du liseré coloré
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
4980cd4c7f
commit
dc1a7ff40e
2 files changed
+15
-16
No files matched your search
+8
-9
@@ -35,7 +35,7 @@
|
|||||||
allKinds: 'Tous les types',
|
allKinds: 'Tous les types',
|
||||||
st: { same: 'Conforme', create: 'À créer', update: 'À modifier', delete: 'À supprimer', extra: 'Hors primaire', error: 'Erreur' },
|
st: { same: 'Conforme', create: 'À créer', update: 'À modifier', delete: 'À supprimer', extra: 'Hors primaire', error: 'Erreur' },
|
||||||
filterDiff: 'Écarts et erreurs', filterAll: 'Tous les objets', filterSame: 'Conformes', filterError: 'Erreurs',
|
filterDiff: 'Écarts et erreurs', filterAll: 'Tous les objets', filterSame: 'Conformes', filterError: 'Erreurs',
|
||||||
reference: 'PRIMAIRE', unreadable: 'illisible', inLine: 'conforme', nodes: '{n} nœud(s)',
|
reference: 'PRIMAIRE', cst: { ok: 'À JOUR', diff: 'ÉCARTS', error: 'BLOQUANT' }, unreadable: 'illisible', inLine: 'conforme', nodes: '{n} nœud(s)',
|
||||||
readAt: 'lu à {t} ({ms} ms)', dryClient: 'Dry-run de la cible', syncClient: 'Répliquer vers la cible',
|
readAt: 'lu à {t} ({ms} ms)', dryClient: 'Dry-run de la cible', syncClient: 'Répliquer vers la cible',
|
||||||
dryObj: 'Dry-run de cet objet', syncObj: 'Répliquer cet objet',
|
dryObj: 'Dry-run de cet objet', syncObj: 'Répliquer cet objet',
|
||||||
refError: 'Primaire illisible', clientError: 'Cible illisible', planError: 'Bloquant', warning: 'Avertissement',
|
refError: 'Primaire illisible', clientError: 'Cible illisible', planError: 'Bloquant', warning: 'Avertissement',
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
allKinds: 'All kinds',
|
allKinds: 'All kinds',
|
||||||
st: { same: 'In sync', create: 'Create', update: 'Update', delete: 'Delete', extra: 'Not on primary', error: 'Error' },
|
st: { same: 'In sync', create: 'Create', update: 'Update', delete: 'Delete', extra: 'Not on primary', error: 'Error' },
|
||||||
filterDiff: 'Differences and errors', filterAll: 'All objects', filterSame: 'In sync', filterError: 'Errors',
|
filterDiff: 'Differences and errors', filterAll: 'All objects', filterSame: 'In sync', filterError: 'Errors',
|
||||||
reference: 'PRIMARY', unreadable: 'unreadable', inLine: 'in sync', nodes: '{n} node(s)',
|
reference: 'PRIMARY', cst: { ok: 'IN SYNC', diff: 'DIFFS', error: 'BLOCKED' }, unreadable: 'unreadable', inLine: 'in sync', nodes: '{n} node(s)',
|
||||||
readAt: 'read at {t} ({ms} ms)', dryClient: 'Target dry-run', syncClient: 'Replicate to target',
|
readAt: 'read at {t} ({ms} ms)', dryClient: 'Target dry-run', syncClient: 'Replicate to target',
|
||||||
dryObj: 'Dry-run this object', syncObj: 'Replicate this object',
|
dryObj: 'Dry-run this object', syncObj: 'Replicate this object',
|
||||||
refError: 'Primary unreadable', clientError: 'Target unreadable', planError: 'Blocking', warning: 'Warning',
|
refError: 'Primary unreadable', clientError: 'Target unreadable', planError: 'Blocking', warning: 'Warning',
|
||||||
@@ -571,8 +571,9 @@
|
|||||||
}
|
}
|
||||||
// reference card
|
// reference card
|
||||||
const r = st.reference;
|
const r = st.reference;
|
||||||
strip.appendChild(h('div', { class: 'ccard ref' + (r.error ? ' st-error' : '') },
|
strip.appendChild(h('div', { class: 'ccard ref' },
|
||||||
h('div', { class: 'c-head' }, h('span', { class: 'c-role', text: t('reference') }), h('span', { class: 'c-name', text: r.name }),
|
h('div', { class: 'c-head' }, h('span', { class: 'c-role', text: t('reference') }),
|
||||||
|
r.error ? h('span', { class: 'c-role st st-error', text: t('cst.error') }) : null, h('span', { class: 'c-name', text: r.name }),
|
||||||
h('span', { class: 'c-ver', text: r.version ? 'PVE ' + r.version : '' })),
|
h('span', { class: 'c-ver', text: r.version ? 'PVE ' + r.version : '' })),
|
||||||
h('div', { class: 'c-url', text: r.url }),
|
h('div', { class: 'c-url', text: r.url }),
|
||||||
r.error ? h('div', { class: 'c-err', text: tm(r.error), title: r.error })
|
r.error ? h('div', { class: 'c-err', text: tm(r.error), title: r.error })
|
||||||
@@ -585,17 +586,15 @@
|
|||||||
counts.appendChild(h('span', { class: 'cnt error', text: t('unreadable') }));
|
counts.appendChild(h('span', { class: 'cnt error', text: t('unreadable') }));
|
||||||
} else if (!r.error) {
|
} else if (!r.error) {
|
||||||
const n = c.counts || {};
|
const n = c.counts || {};
|
||||||
let any = false;
|
|
||||||
for (const [k, sym] of [['create', '+'], ['update', '~'], ['delete', '−'], ['error', '!'], ['extra', '?']]) {
|
for (const [k, sym] of [['create', '+'], ['update', '~'], ['delete', '−'], ['error', '!'], ['extra', '?']]) {
|
||||||
if (n[k]) { any = true; counts.appendChild(h('span', { class: 'cnt ' + k, title: t('st.' + k), text: sym + n[k] })); }
|
if (n[k]) { counts.appendChild(h('span', { class: 'cnt ' + k, title: t('st.' + k), text: sym + n[k] })); }
|
||||||
}
|
}
|
||||||
if (!any) counts.appendChild(h('span', { class: 'cnt ok', text: '✓ ' + t('inLine') }));
|
|
||||||
}
|
}
|
||||||
strip.appendChild(h('button', {
|
strip.appendChild(h('button', {
|
||||||
type: 'button', role: 'tab', class: 'ccard st-' + cs, 'aria-selected': String(c.name === S.client),
|
type: 'button', role: 'tab', class: 'ccard', 'aria-selected': String(c.name === S.client),
|
||||||
onclick: () => { S.client = c.name; S.open.clear(); writeHash(); renderDiff(); },
|
onclick: () => { S.client = c.name; S.open.clear(); writeHash(); renderDiff(); },
|
||||||
},
|
},
|
||||||
h('div', { class: 'c-head' }, h('span', { class: 'c-name', text: c.name }), h('span', { class: 'c-ver', text: c.version ? 'PVE ' + c.version : '' })),
|
h('div', { class: 'c-head' }, h('span', { class: 'c-role st st-' + cs, text: t('cst.' + cs) }), h('span', { class: 'c-name', text: c.name }), h('span', { class: 'c-ver', text: c.version ? 'PVE ' + c.version : '' })),
|
||||||
h('div', { class: 'c-url', text: c.url }),
|
h('div', { class: 'c-url', text: c.url }),
|
||||||
counts,
|
counts,
|
||||||
c.error ? h('div', { class: 'c-err', text: tm(c.error), title: c.error }) : null));
|
c.error ? h('div', { class: 'c-err', text: tm(c.error), title: c.error }) : null));
|
||||||
|
|||||||
+7
-7
@@ -254,21 +254,21 @@ body.busy .progress::after {
|
|||||||
position: relative; display: flex; flex-direction: column; gap: 4px; min-width: 0;
|
position: relative; display: flex; flex-direction: column; gap: 4px; min-width: 0;
|
||||||
padding: 10px 12px 10px 14px; text-align: left;
|
padding: 10px 12px 10px 14px; text-align: left;
|
||||||
background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
|
background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
|
||||||
/* status edge drawn inside the border: follows the rounded corners */
|
|
||||||
box-shadow: inset 3px 0 0 var(--st, var(--border));
|
|
||||||
}
|
}
|
||||||
button.ccard:hover { background: var(--row-hover); }
|
button.ccard:hover { background: var(--row-hover); }
|
||||||
.ccard[aria-selected="true"] { border-color: var(--accent); box-shadow: inset 3px 0 0 var(--st, var(--accent)), 0 0 0 1px var(--accent); }
|
.ccard[aria-selected="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
||||||
.ccard.ref { --st: var(--accent); background: color-mix(in srgb, var(--accent) 5%, var(--panel)); cursor: default; }
|
.ccard.ref { background: color-mix(in srgb, var(--accent) 5%, var(--panel)); cursor: default; }
|
||||||
.ccard.st-ok { --st: var(--ok); }
|
|
||||||
.ccard.st-diff { --st: var(--warn); }
|
|
||||||
.ccard.st-error { --st: var(--err); }
|
|
||||||
.ccard .c-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
.ccard .c-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||||
.ccard .c-name { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.ccard .c-name { font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.ccard .c-role {
|
.ccard .c-role {
|
||||||
flex: none; padding: 0 5px; border-radius: 5px; font-size: 10px; font-weight: 700; letter-spacing: .04em;
|
flex: none; padding: 0 5px; border-radius: 5px; font-size: 10px; font-weight: 700; letter-spacing: .04em;
|
||||||
color: var(--accent); background: var(--accent-soft);
|
color: var(--accent); background: var(--accent-soft);
|
||||||
}
|
}
|
||||||
|
/* status badge, same shape as the role badge */
|
||||||
|
.ccard .c-role.st { --c: var(--muted); color: var(--c); background: color-mix(in srgb, var(--c) 14%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) 40%, transparent); }
|
||||||
|
.ccard .c-role.st-ok { --c: var(--ok); }
|
||||||
|
.ccard .c-role.st-diff { --c: var(--warn); }
|
||||||
|
.ccard .c-role.st-error { --c: var(--err); }
|
||||||
.ccard .c-ver { margin-left: auto; flex: none; font: 11.5px var(--mono); color: var(--muted); }
|
.ccard .c-ver { margin-left: auto; flex: none; font: 11.5px var(--mono); color: var(--muted); }
|
||||||
.ccard .c-url { font: 11.5px var(--mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.ccard .c-url { font: 11.5px var(--mono); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.ccard .c-counts { display: flex; flex-wrap: wrap; gap: 4px; min-height: 20px; align-items: center; }
|
.ccard .c-counts { display: flex; flex-wrap: wrap; gap: 4px; min-height: 20px; align-items: center; }
|
||||||
|
|||||||
Reference in new issue
Block a user