diff --git a/ordinis-admin-ui/src/components/layout/Sidebar.tsx b/ordinis-admin-ui/src/components/layout/Sidebar.tsx index 9557dbc..d74b22a 100644 --- a/ordinis-admin-ui/src/components/layout/Sidebar.tsx +++ b/ordinis-admin-ui/src/components/layout/Sidebar.tsx @@ -10,7 +10,6 @@ import { Webhook, ScrollText, Search, - GitBranch, type LucideIcon, } from 'lucide-react' import { useAuth } from 'react-oidc-context' @@ -63,6 +62,9 @@ export function Sidebar() { // sidebar'а — overkill. Skip badge. const reviewsCount = 0 + // Flat list per redesign/compact.html — no section labels, /graph removed + // (now reachable via catalog toolbar "Граф ⇄" button). Order: Главная → + // Справочники → Мои черновики → На ревью → Outbox → Webhooks → Аудит → Поиск. const sections: NavSection[] = [ { items: [ @@ -73,14 +75,6 @@ export function Sidebar() { icon: Database, badge: dictsCount > 0 ? dictsCount : undefined, }, - { to: '/search', label: t('nav.search'), icon: Search }, - // Graph view — Stage 3.3 placeholder. Когда d3-force готов. - { to: '/graph', label: t('nav.graph'), icon: GitBranch }, - ], - }, - { - label: t('nav.section.workflow'), - items: [ { to: '/my-drafts', label: t('nav.myDrafts'), @@ -95,14 +89,10 @@ export function Sidebar() { badge: reviewsCount > 0 ? reviewsCount : undefined, authRequired: true, }, - ], - }, - { - label: t('nav.section.admin'), - items: [ - { to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true }, { to: '/outbox', label: t('nav.outbox'), icon: Inbox, authRequired: true }, { to: '/webhooks', label: t('nav.webhooks'), icon: Webhook, authRequired: true }, + { to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true }, + { to: '/search', label: t('nav.search'), icon: Search }, ], }, ] @@ -309,6 +299,7 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () => const myDraftsCount = myDrafts.data?.totalElements ?? 0 const reviewsCount = 0 + // Flat list per redesign — same as desktop Sidebar above. const sections: NavSection[] = [ { items: [ @@ -319,13 +310,6 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () => icon: Database, badge: dictsCount > 0 ? dictsCount : undefined, }, - { to: '/search', label: t('nav.search'), icon: Search }, - { to: '/graph', label: t('nav.graph'), icon: GitBranch }, - ], - }, - { - label: t('nav.section.workflow'), - items: [ { to: '/my-drafts', label: t('nav.myDrafts'), @@ -340,14 +324,10 @@ export function MobileSidebar({ open, onClose }: { open: boolean; onClose: () => badge: reviewsCount > 0 ? reviewsCount : undefined, authRequired: true, }, - ], - }, - { - label: t('nav.section.admin'), - items: [ - { to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true }, { to: '/outbox', label: t('nav.outbox'), icon: Inbox, authRequired: true }, { to: '/webhooks', label: t('nav.webhooks'), icon: Webhook, authRequired: true }, + { to: '/audit', label: t('nav.audit'), icon: ScrollText, authRequired: true }, + { to: '/search', label: t('nav.search'), icon: Search }, ], }, ] diff --git a/ordinis-admin-ui/src/routes/dictionaries.index.tsx b/ordinis-admin-ui/src/routes/dictionaries.index.tsx index 04f3521..b121eb9 100644 --- a/ordinis-admin-ui/src/routes/dictionaries.index.tsx +++ b/ordinis-admin-ui/src/routes/dictionaries.index.tsx @@ -423,6 +423,8 @@ function DictionaryListTable({ rows }: { rows: DictionaryDefinition[] }) { {t('dict.col.updated', { defaultValue: 'изменён' })} + {/* Chevron col — affordance что row кликабелен (открывает editor) */} + @@ -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 }) { {incomingCount > 0 ? incomingCount : '—'} - {/* updated */} + {/* updated — relative time */} {updatedLabel} + {/* Chevron — кликабельность row affordance */} + + › + ) }