Expand activity journal color cues
Build and Deploy Gateway / build-and-push (push) Successful in 37s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
Dmitriy Petrov
2026-08-25 16:42:05 +03:00
parent f451c65b2f
commit 4a566e082a
5 changed files with 46 additions and 40 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({ export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.30.5', macClient: '0.30.6',
gatewayClient: '0.31.5', gatewayClient: '0.31.6',
gatewayBackend: '0.31.0', gatewayBackend: '0.31.0',
}); });
@@ -156,15 +156,13 @@ export function ActivityJournalPanel({ feature, loadPage }: {
<ol>{group.items.map((item) => { <ol>{group.items.map((item) => {
const { event } = item; const { event } = item;
const [title, details] = refreshStreakCopy(item) || eventCopy(event); const [title, details] = refreshStreakCopy(item) || eventCopy(event);
return <li key={event.id} className={event.severity === 'error' return <li
? 'client-journal-event is-error' key={event.id}
: event.severity === 'warning' className="client-journal-event"
? 'client-journal-event is-warning' data-event-type={event.type}
: event.type === 'failover.switched' && event.data.toRole === 'primary' data-severity={event.severity}
? 'client-journal-event is-switch-primary' data-target-role={event.type === 'failover.switched' ? event.data.toRole : undefined}
: event.type === 'failover.switched' && event.data.toRole === 'reserve' >
? 'client-journal-event is-switch-reserve'
: 'client-journal-event'}>
<div className="client-journal-time"><time dateTime={event.occurredAt}>{new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }).format(new Date(event.occurredAt))}</time><span>{event.source}</span></div> <div className="client-journal-time"><time dateTime={event.occurredAt}>{new Intl.DateTimeFormat('ru-RU', { hour: '2-digit', minute: '2-digit' }).format(new Date(event.occurredAt))}</time><span>{event.source}</span></div>
<div><strong>{title}</strong>{details && <span>{details}</span>}{event.severity !== 'info' && <em>{event.severity === 'error' ? 'Ошибка' : 'Внимание'}</em>}</div> <div><strong>{title}</strong>{details && <span>{details}</span>}{event.severity !== 'info' && <em>{event.severity === 'error' ? 'Ошибка' : 'Внимание'}</em>}</div>
</li>; </li>;
+16 -11
View File
@@ -16,17 +16,22 @@
.client-journal-time span, .client-journal-groups li span { color: var(--client-muted); font: var(--type-label); letter-spacing: var(--type-label-tracking); text-transform: var(--type-label-transform); overflow-wrap: anywhere; } .client-journal-time span, .client-journal-groups li span { color: var(--client-muted); font: var(--type-label); letter-spacing: var(--type-label-tracking); text-transform: var(--type-label-transform); overflow-wrap: anywhere; }
.client-journal-time span { text-transform: var(--type-label-transform); } .client-journal-time span { text-transform: var(--type-label-transform); }
.client-journal-groups li strong { font: var(--type-body); letter-spacing: var(--type-body-tracking); text-transform: var(--type-body-transform); } .client-journal-groups li strong { font: var(--type-body); letter-spacing: var(--type-body-tracking); text-transform: var(--type-body-transform); }
.client-journal-groups li em { width: fit-content; color: oklch(0.68 0.15 28); font: var(--type-label); font-style: normal; letter-spacing: var(--type-label-tracking); text-transform: var(--type-label-transform); } .client-journal-groups li em { width: fit-content; color: var(--client-journal-tone); font: var(--type-label); font-style: normal; letter-spacing: var(--type-label-tracking); text-transform: var(--type-label-transform); }
.client-journal-event.is-switch-primary::before, .client-journal-event { --client-journal-tone: var(--client-muted); }
.client-journal-event.is-switch-reserve::before, .client-journal-event::before { content: ''; position: absolute; inset: 9px auto 9px -8px; width: 3px; border-radius: 999px; background: var(--client-journal-tone); }
.client-journal-event.is-warning::before, .client-journal-event .client-journal-time span { color: var(--client-journal-tone); }
.client-journal-event.is-error::before { content: ''; position: absolute; inset: 9px auto 9px -8px; width: 3px; border-radius: 999px; } .client-journal-event[data-event-type='connection.started'],
.client-journal-event.is-switch-primary::before { background: var(--harbor-connect); } .client-journal-event[data-event-type='subscription.added'],
.client-journal-event.is-switch-reserve::before { background: var(--harbor-gateway); } .client-journal-event[data-event-type='subscription.refreshed'],
.client-journal-event.is-warning::before { background: oklch(0.68 0.14 72); } .client-journal-event[data-event-type='failover.enabled'],
.client-journal-event.is-error::before { background: oklch(0.68 0.15 28); } .client-journal-event[data-event-type='failover.resumed'],
.client-journal-event.is-warning em { color: oklch(0.68 0.14 72); } .client-journal-event[data-event-type='failover.recovered'],
.client-journal-event.is-error em { color: oklch(0.68 0.15 28); } .client-journal-event[data-event-type='failover.switched'][data-target-role='primary'] { --client-journal-tone: var(--harbor-connect); }
.client-journal-event[data-event-type='failover.switched'][data-target-role='reserve'] { --client-journal-tone: var(--harbor-gateway); }
.client-journal-event[data-event-type='failover.waiting_for_idle'],
.client-journal-event[data-event-type='failover.paused'],
.client-journal-event[data-severity='warning'] { --client-journal-tone: oklch(0.68 0.14 72); }
.client-journal-event[data-severity='error'] { --client-journal-tone: oklch(0.68 0.15 28); }
.client-journal-error, .client-journal-empty { min-height: 80px; margin: 0 8px; color: var(--client-muted); font: var(--type-body); letter-spacing: var(--type-body-tracking); text-transform: var(--type-body-transform); } .client-journal-error, .client-journal-empty { min-height: 80px; margin: 0 8px; color: var(--client-muted); font: var(--type-body); letter-spacing: var(--type-body-tracking); text-transform: var(--type-body-transform); }
.client-journal-error button, .client-journal-footer button { border: 0; background: transparent; color: var(--client-accent); font: var(--type-control); letter-spacing: var(--type-control-tracking); text-transform: var(--type-control-transform); cursor: pointer; } .client-journal-error button, .client-journal-footer button { border: 0; background: transparent; color: var(--client-accent); font: var(--type-control); letter-spacing: var(--type-control-tracking); text-transform: var(--type-control-transform); cursor: pointer; }
.client-journal-skeleton { display: grid; gap: 14px; margin: 0 8px; } .client-journal-skeleton { display: grid; gap: 14px; margin: 0 8px; }
@@ -27,8 +27,11 @@ test('journal presents 30-day grouped history with refresh, pagination and stabl
assert.match(styles, /@media \(prefers-reduced-motion: reduce\)/); assert.match(styles, /@media \(prefers-reduced-motion: reduce\)/);
assert.match(feature, /compactActivityJournalEvents\(events\)/); assert.match(feature, /compactActivityJournalEvents\(events\)/);
assert.match(model, /Подписки обновлялись без ошибок/); assert.match(model, /Подписки обновлялись без ошибок/);
assert.match(styles, /\.client-journal-event\.is-switch-primary::before \{ background: var\(--harbor-connect\); \}/); assert.match(feature, /data-event-type=\{event\.type\}[\s\S]*data-severity=\{event\.severity\}[\s\S]*data-target-role=/);
assert.match(styles, /\.client-journal-event\.is-switch-reserve::before \{ background: var\(--harbor-gateway\); \}/); assert.match(styles, /\.client-journal-event::before \{[^}]*background: var\(--client-journal-tone\); \}/);
assert.match(styles, /\.client-journal-event\.is-warning::before \{ background: oklch\(0\.68 0\.14 72\); \}/); assert.match(styles, /\.client-journal-event \.client-journal-time span \{ color: var\(--client-journal-tone\); \}/);
assert.match(styles, /\.client-journal-event\.is-error::before \{ background: oklch\(0\.68 0\.15 28\); \}/); assert.match(styles, /\[data-event-type='subscription\.refreshed'\][\s\S]*\[data-event-type='failover\.recovered'\][\s\S]*--client-journal-tone: var\(--harbor-connect\)/);
assert.match(styles, /\[data-target-role='reserve'\] \{ --client-journal-tone: var\(--harbor-gateway\); \}/);
assert.match(styles, /\[data-event-type='failover\.waiting_for_idle'\][\s\S]*\[data-severity='warning'\] \{ --client-journal-tone: oklch\(0\.68 0\.14 72\); \}/);
assert.match(styles, /\[data-severity='error'\] \{ --client-journal-tone: oklch\(0\.68 0\.15 28\); \}/);
}); });
+14 -14
View File
@@ -39,26 +39,26 @@ const expectedImports = [
const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex'); const sha256 = (value) => crypto.createHash('sha256').update(value).digest('hex');
const acceptedLedger = { const acceptedLedger = {
counts: { counts: {
cascadeEdges: 1098, cascadeEdges: 1054,
customProperties: 106, customProperties: 111,
declarations: 4122, declarations: 4123,
important: 0, important: 0,
keyframes: 50, keyframes: 50,
media: 17, media: 17,
rules: 1113, rules: 1113,
variableReferences: 1051, variableReferences: 1055,
}, },
hashes: { hashes: {
cascadeEdges: '9814b59202515f6993f2836d529b7bb9e25e632b0803c832a363ac264e5b60a3', cascadeEdges: 'c3b2ebc3f49891437122b19af5e9a42e7db11a311ecb4c2c76446c1541fe1023',
customProperties: 'fd6f16069f9fe3526f09d4d574431ddae67150d8e7a8a40e04c9fd2d179ec7ac', customProperties: '14fb162f1c6d754e69dd355fefb1a3a192a191324468f85d7a5d811665ce35b4',
declarations: 'dcbf716667469136ad6c25412fb24741c3b66749ceefb799d5785a6668dbf9de', declarations: '37b2f9f3ace795a826ad5eca6e6c4ba98ab6bb893aac87938bdeef826c383d63',
duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945', duplicateKeyframes: '4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945',
duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848', duplicateSelectors: '8982145dba05b33bf1c93b2d54cfbe76305b276ff3c657aa020144016ada5848',
keyframes: 'af4b9ae18d070fd2462a9b050293f3821bf5017c6e7cc53bbe18dc826f0fe3b9', keyframes: 'af4b9ae18d070fd2462a9b050293f3821bf5017c6e7cc53bbe18dc826f0fe3b9',
ruleDeclarationSequences: '55167d84597ea8e8a4c4898f8fc8431593e920dc2e089728cc18c52f31d8c2cb', ruleDeclarationSequences: '24377c2c29079886b62124688c2f858142913247426728c9b81f4b0a79e559e6',
selectors: '2ab1759842aadbf1e733e9fc97946f48578903bb47d192b0b373329262982299', selectors: '0b7624b5064d514a267e9bb45c40c445ce10f3e6998a34af7e1b604e4c0445b9',
variableReferences: '2065fa19b325d995769988999b4e5dc51b54e8b011af39397f980584c3fffbe4', variableReferences: '56dbfff5943b1ddd09d31b9d6699ff6680d07e5e562285f5f8268cc7566dca59',
witnesses: '2ade0023244f8fc6a2fed0b2e1c3a99c4055c32abd6d8bfcd0d5f117a48f05bb', witnesses: 'e9f8c6e640c19375635847dc064edd6752a8216c6acd3c2aad6ffd18c1a4aaa2',
}, },
}; };
@@ -408,8 +408,8 @@ test('main owns one public stylesheet and the regrouped production CSS is determ
assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1); assert.equal((main.match(/import ['"][^'"]+\.css['"]/g) || []).length, 1);
const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css')); const assets = fs.readdirSync(path.join(root, 'dist/assets')).filter((file) => file.endsWith('.css'));
assert.deepEqual(assets, ['index-BH_msVtT.css']); assert.deepEqual(assets, ['index-Dng8HPUW.css']);
const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0])); const built = fs.readFileSync(path.join(root, 'dist/assets', assets[0]));
assert.equal(built.byteLength, 156918); assert.equal(built.byteLength, 157518);
assert.equal(sha256(built), 'a1eafbf8c9c89a57478ba5c78d8b2a2fa1d44bcc8dc7bb747cba2889096509c5'); assert.equal(sha256(built), '4fbc6c3e73a497101cacc456b580c6c4b8a58a44870856689719f2ada5b92bd7');
}); });