From dc1a7ff40e19690c389c4185cc53e631ca345405 Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 23 Oct 2026 19:49:58 +0200 Subject: [PATCH] =?UTF-8?q?UI=20:=20badge=20de=20statut=20texte=20(=C3=80?= =?UTF-8?q?=20JOUR,=20=C3=89CARTS,=20BLOQUANT)=20sur=20les=20cartes=20?= =?UTF-8?q?=C3=A0=20la=20place=20du=20liser=C3=A9=20color=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 (1M context) --- web/app.js | 17 ++++++++--------- web/style.css | 14 +++++++------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/web/app.js b/web/app.js index 0622403..9b793a3 100644 --- a/web/app.js +++ b/web/app.js @@ -35,7 +35,7 @@ allKinds: 'Tous les types', 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', - 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', dryObj: 'Dry-run de cet objet', syncObj: 'Répliquer cet objet', refError: 'Primaire illisible', clientError: 'Cible illisible', planError: 'Bloquant', warning: 'Avertissement', @@ -137,7 +137,7 @@ allKinds: 'All kinds', 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', - 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', dryObj: 'Dry-run this object', syncObj: 'Replicate this object', refError: 'Primary unreadable', clientError: 'Target unreadable', planError: 'Blocking', warning: 'Warning', @@ -571,8 +571,9 @@ } // reference card const r = st.reference; - strip.appendChild(h('div', { class: 'ccard ref' + (r.error ? ' st-error' : '') }, - h('div', { class: 'c-head' }, h('span', { class: 'c-role', text: t('reference') }), h('span', { class: 'c-name', text: r.name }), + strip.appendChild(h('div', { class: 'ccard ref' }, + 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('div', { class: 'c-url', text: r.url }), 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') })); } else if (!r.error) { const n = c.counts || {}; - let any = false; 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', { - 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(); }, }, - 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 }), counts, c.error ? h('div', { class: 'c-err', text: tm(c.error), title: c.error }) : null)); diff --git a/web/style.css b/web/style.css index 43a87d6..4d0bddd 100644 --- a/web/style.css +++ b/web/style.css @@ -254,21 +254,21 @@ body.busy .progress::after { position: relative; display: flex; flex-direction: column; gap: 4px; min-width: 0; padding: 10px 12px 10px 14px; text-align: left; 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); } -.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.ref { --st: var(--accent); 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[aria-selected="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); } +.ccard.ref { background: color-mix(in srgb, var(--accent) 5%, var(--panel)); cursor: default; } .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-role { flex: none; padding: 0 5px; border-radius: 5px; font-size: 10px; font-weight: 700; letter-spacing: .04em; 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-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; }