Merge branch 'fix/catalog-toolbar-bar-topbar-count' into 'main'
fix(catalog): title→TopBar + toolbar→tinted bar + table white + font fix See merge request 2-6/2-6-4/terravault/ordinis!86
This commit is contained in:
@@ -99,7 +99,9 @@ export function EditorInfoPanel({ detail, recordCount, actions }: EditorInfoPane
|
|||||||
>
|
>
|
||||||
{outgoingNames.length > 0 && (
|
{outgoingNames.length > 0 && (
|
||||||
<div className="space-y-1.5 mb-3">
|
<div className="space-y-1.5 mb-3">
|
||||||
<div className="text-cap text-mute">
|
{/* Per prototype: section subhead "→ ссылается" — plain body
|
||||||
|
* font Inter mute, lowercase (НЕ Tektur uppercase cap). */}
|
||||||
|
<div className="text-body text-mute">
|
||||||
→ {t('editor.info.outgoing', { defaultValue: 'ссылается' })}
|
→ {t('editor.info.outgoing', { defaultValue: 'ссылается' })}
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-0.5">
|
<ul className="space-y-0.5">
|
||||||
@@ -119,7 +121,7 @@ export function EditorInfoPanel({ detail, recordCount, actions }: EditorInfoPane
|
|||||||
)}
|
)}
|
||||||
{incomingDeps.length > 0 && (
|
{incomingDeps.length > 0 && (
|
||||||
<div className="space-y-1.5">
|
<div className="space-y-1.5">
|
||||||
<div className="text-cap text-mute">
|
<div className="text-body text-mute">
|
||||||
← {t('editor.info.incoming', { defaultValue: 'используют' })}
|
← {t('editor.info.incoming', { defaultValue: 'используют' })}
|
||||||
</div>
|
</div>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { Link, useLocation, useNavigate } from '@tanstack/react-router'
|
import { Link, useLocation, useNavigate } from '@tanstack/react-router'
|
||||||
import { useDictionaryDetail } from '@/api/queries'
|
import { useDictionaries, useDictionaryDetail } from '@/api/queries'
|
||||||
import { LanguageSwitch } from '@/ui'
|
import { LanguageSwitch } from '@/ui'
|
||||||
import { AuthBadge } from '@/auth/AuthBadge'
|
import { AuthBadge } from '@/auth/AuthBadge'
|
||||||
import { ThemeSwitch } from '@/components/layout/ThemeSwitch'
|
import { ThemeSwitch } from '@/components/layout/ThemeSwitch'
|
||||||
@@ -105,6 +105,10 @@ export function TopBar({ onMenuClick }: { onMenuClick?: () => void }) {
|
|||||||
{isLast && crumb.subtitle && (
|
{isLast && crumb.subtitle && (
|
||||||
<span className="text-mono text-cell text-mute shrink-0 ml-1">{crumb.subtitle}</span>
|
<span className="text-mono text-cell text-mute shrink-0 ml-1">{crumb.subtitle}</span>
|
||||||
)}
|
)}
|
||||||
|
{/* Catalog route: inline cap "N шт." count badge after title */}
|
||||||
|
{isLast && crumb.caption && (
|
||||||
|
<span className="text-cap text-mute shrink-0 ml-1">{crumb.caption}</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
@@ -150,7 +154,14 @@ export function TopBar({ onMenuClick }: { onMenuClick?: () => void }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Crumb = { label: string; to?: string; subtitle?: string }
|
type Crumb = {
|
||||||
|
label: string
|
||||||
|
to?: string
|
||||||
|
/** Mono-styled secondary text (editor route: "name · v1.0.0"). */
|
||||||
|
subtitle?: string
|
||||||
|
/** Cap-styled count badge (catalog route: "40 шт."). */
|
||||||
|
caption?: string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Breadcrumb из current route. Для editor route ('/dictionaries/$name')
|
* Breadcrumb из current route. Для editor route ('/dictionaries/$name')
|
||||||
@@ -166,6 +177,12 @@ function useBreadcrumb(): Crumb[] {
|
|||||||
const editorName = editorMatch ? decodeURIComponent(editorMatch[1]) : undefined
|
const editorName = editorMatch ? decodeURIComponent(editorMatch[1]) : undefined
|
||||||
const editorDict = useDictionaryDetail(editorName)
|
const editorDict = useDictionaryDetail(editorName)
|
||||||
|
|
||||||
|
// Catalog route: total dict count для inline "N шт." subtitle на breadcrumb.
|
||||||
|
// Запрос cached глобально (useDictionaries) — не вызывает extra fetch.
|
||||||
|
const isCatalog = path === '/dictionaries'
|
||||||
|
const dictsQuery = useDictionaries()
|
||||||
|
const dictsCount = dictsQuery.data?.length
|
||||||
|
|
||||||
if (path === '/' || path === '') {
|
if (path === '/' || path === '') {
|
||||||
return [{ label: t('nav.home') }]
|
return [{ label: t('nav.home') }]
|
||||||
}
|
}
|
||||||
@@ -185,6 +202,23 @@ function useBreadcrumb(): Crumb[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const rootLabel = ROOT_LABELS[first] ?? first
|
const rootLabel = ROOT_LABELS[first] ?? first
|
||||||
|
|
||||||
|
// Catalog: title "Справочники" + inline caption "N шт." per user feedback.
|
||||||
|
if (isCatalog) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: rootLabel,
|
||||||
|
caption:
|
||||||
|
typeof dictsCount === 'number'
|
||||||
|
? t('dict.list.countShort', {
|
||||||
|
count: dictsCount,
|
||||||
|
defaultValue: `${dictsCount} шт.`,
|
||||||
|
})
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
const crumbs: Crumb[] = [
|
const crumbs: Crumb[] = [
|
||||||
{ label: rootLabel, to: segments.length === 1 ? undefined : `/${first}` },
|
{ label: rootLabel, to: segments.length === 1 ? undefined : `/${first}` },
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -218,19 +218,15 @@ function DictionariesPage() {
|
|||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
aria-label={t('dict.list.heading')}
|
aria-label={t('dict.list.heading')}
|
||||||
className="space-y-4"
|
className="space-y-3"
|
||||||
>
|
>
|
||||||
{/* PageHeader per redesign: title + inline "N шт." count, без description.
|
{/* Title block убран — "Справочники N шт." переехал в TopBar breadcrumb
|
||||||
Actions удалены — + Создать переехала в toolbar справа от Граф ⇄. */}
|
per user feedback ("эта надпись в топбаре"). Page начинается прямо с
|
||||||
<div className="flex items-baseline gap-3">
|
tinted toolbar bar, потом white table. */}
|
||||||
<h1 className="text-title-lg text-ink">{t('nav.dictionaries')}</h1>
|
|
||||||
<span className="text-cap text-mute">
|
|
||||||
{data.length} {t('dict.list.countSuffix', { defaultValue: 'шт.' })}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Toolbar per redesign prototype: [scope pills] | [bundle pills] [...] N/M [Граф ⇄] [+ Создать] */}
|
{/* Toolbar in tinted bar per user feedback ("бар цветом").
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
surface-2 (warm cream) визуально отделяет controls от table chrome. */}
|
||||||
|
<div className="flex flex-wrap items-center gap-3 rounded-lg bg-surface-2 border border-line px-3 py-2">
|
||||||
{/* Scope filter: PUBLIC / INTERNAL / RESTRICTED — pill chips с border */}
|
{/* Scope filter: PUBLIC / INTERNAL / RESTRICTED — pill chips с border */}
|
||||||
<div
|
<div
|
||||||
role="group"
|
role="group"
|
||||||
@@ -387,9 +383,11 @@ export const uniqueRefBy = (deps: SchemaDependent[]): SchemaDependent[] => {
|
|||||||
function DictionaryListTable({ rows }: { rows: DictionaryDefinition[] }) {
|
function DictionaryListTable({ rows }: { rows: DictionaryDefinition[] }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-line overflow-hidden">
|
<div className="rounded-lg border border-line overflow-hidden bg-surface">
|
||||||
<table className="w-full">
|
<table className="w-full">
|
||||||
<thead className="bg-surface-2 border-b border-line">
|
{/* White header per user feedback ("заголовки таблицы белый фон").
|
||||||
|
Прошлое bg-surface-2 сливалось с toolbar bar. */}
|
||||||
|
<thead className="bg-surface border-b border-line">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="text-cap text-mute text-left px-3 py-2">
|
<th scope="col" className="text-cap text-mute text-left px-3 py-2">
|
||||||
{t('dict.col.title', { defaultValue: 'Название' })}
|
{t('dict.col.title', { defaultValue: 'Название' })}
|
||||||
|
|||||||
Reference in New Issue
Block a user