feat(layout): catalog relative time + chevron + flat sidebar per redesign (4)

Per /Users/zimin/Downloads/Ordinis (4)/redesign/compact.html (бэкграунд
прототипа уже #faf9f5 — match'ит наш neutral default).

Catalog table:
- ИЗМЕНЁН column показывает relative time: Xс / Xмин / Xч / Xд / Xмес / Xг.
  Прошлая абсолютная дата (05/06/2026) затирала scale ("5 минут vs 3 месяца —
  визуально одинаково далеко"). Прототип использует 2ч, 3д, 1мес.
- Chevron `›` column добавлен в конец каждой строки — affordance что row
  кликабелен (открывает editor).

Sidebar:
- Flatten: убраны section labels (workflow / admin). Все nav items в одном
  списке per redesign: Главная → Справочники → Мои черновики → На ревью →
  Outbox → Webhooks → Аудит → Поиск.
- /graph удалён из sidebar — теперь reachable только через catalog toolbar
  "Граф ⇄" button (single entry point, no duplication).
- Применено в обоих Sidebar (desktop) и MobileSidebar (drawer).

Cleanup: убран unused GitBranch icon import.
This commit is contained in:
Zimin A.N.
2026-05-11 19:26:56 +03:00
parent cbe21dbc52
commit 7e33456d70
2 changed files with 31 additions and 36 deletions
@@ -423,6 +423,8 @@ function DictionaryListTable({ rows }: { rows: DictionaryDefinition[] }) {
<th scope="col" className="text-cap text-mute text-left px-3 py-2 hidden xl:table-cell">
{t('dict.col.updated', { defaultValue: 'изменён' })}
</th>
{/* Chevron col — affordance что row кликабелен (открывает editor) */}
<th scope="col" aria-hidden className="w-8" />
</tr>
</thead>
<tbody>
@@ -441,15 +443,24 @@ function DictRow({ d, t }: { d: DictionaryDefinition; t: TFunc }) {
const refBy = useMemo(() => uniqueRefBy(refByRaw ?? []), [refByRaw])
const incomingCount = refBy.length
// Relative time per redesign prototype: 12мин / 2ч / 3д / 1мес.
// Stale absolute date (YYYY-MM-DD) скрывал scale (5 минут vs 3 месяца —
// в формате 05/06/2026 это одинаково "далёкая дата").
const updatedLabel = useMemo(() => {
const date = new Date(d.updatedAt)
return isNaN(date.getTime())
? '—'
: date.toLocaleDateString(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
if (isNaN(date.getTime())) return '—'
const diffSec = Math.max(0, Math.floor((Date.now() - date.getTime()) / 1000))
if (diffSec < 60) return `${diffSec}с`
const diffMin = Math.floor(diffSec / 60)
if (diffMin < 60) return `${diffMin}мин`
const diffH = Math.floor(diffMin / 60)
if (diffH < 24) return `${diffH}ч`
const diffD = Math.floor(diffH / 24)
if (diffD < 30) return `${diffD}д`
const diffMo = Math.floor(diffD / 30)
if (diffMo < 12) return `${diffMo}мес`
const diffY = Math.floor(diffMo / 12)
return `${diffY}г`
}, [d.updatedAt])
const handleClick = () => {
@@ -509,10 +520,14 @@ function DictRow({ d, t }: { d: DictionaryDefinition; t: TFunc }) {
<td className="px-3 py-2 text-right text-mono text-mute hidden lg:table-cell">
{incomingCount > 0 ? incomingCount : '—'}
</td>
{/* updated */}
{/* updated — relative time */}
<td className="px-3 py-2 text-mono text-mute tabular-nums hidden xl:table-cell whitespace-nowrap">
{updatedLabel}
</td>
{/* Chevron — кликабельность row affordance */}
<td aria-hidden className="px-2 py-2 text-mute text-mono text-right">
</td>
</tr>
)
}