diff --git a/ordinis-admin-ui/src/components/layout/ThemeSwitch.tsx b/ordinis-admin-ui/src/components/layout/ThemeSwitch.tsx
index da8465d..65158cf 100644
--- a/ordinis-admin-ui/src/components/layout/ThemeSwitch.tsx
+++ b/ordinis-admin-ui/src/components/layout/ThemeSwitch.tsx
@@ -1,42 +1,37 @@
import { useTranslation } from 'react-i18next'
+import { Monitor, Moon, Sun } from 'lucide-react'
import { useTheme, type ThemePreference } from '@/stores/ThemeProvider'
import { cn } from '@/lib/utils'
/**
- * Theme switch per redesign prototype — 2-button segmented control с text
- * labels "Earth | Dark". Filled accent-bg на active.
+ * Tri-state theme switch: Light / Dark / System.
*
- *
Прошлый tri-state (Light/Dark/System icons) был noise — "system" preference
- * редко используется явно, мало кто понимает Monitor icon. Простой 2-button
- * `Earth / Dark` toggle — точно повторяет prototype и улучшает discoverability.
+ *
Segmented control с тремя иконками. Активный — `--color-accent`
+ * background + `--color-on-accent` foreground. Click меняет `preference`
+ * в {@link ThemeProvider}, который пишет в localStorage и переключает
+ * `data-theme` на html.
*
- *
Internally "Earth" maps на `light` preference (Earthy palette default).
- * "System" preference больше не выставляется через UI; localStorage value
- * остаётся валидным (ThemeProvider может read), но новые users получают
- * explicit light/dark choice.
+ *
System icon показывает «follow OS» — auto-update при OS theme change.
*/
export function ThemeSwitch() {
const { t } = useTranslation()
const { preference, setPreference } = useTheme()
- // Treat 'system' как 'light' для UI — переключение всё равно flatten'ит
- // preference на explicit choice.
- const activeId: Exclude =
- preference === 'dark' ? 'dark' : 'light'
-
- const items: { id: Exclude; label: string }[] = [
- { id: 'light', label: t('theme.earth', { defaultValue: 'Earth' }) },
- { id: 'dark', label: t('theme.dark', { defaultValue: 'Dark' }) },
+ const items: { id: ThemePreference; label: string; icon: typeof Sun }[] = [
+ { id: 'light', label: t('theme.light'), icon: Sun },
+ { id: 'dark', label: t('theme.dark'), icon: Moon },
+ { id: 'system', label: t('theme.system'), icon: Monitor },
]
return (
{items.map((item) => {
- const active = activeId === item.id
+ const Icon = item.icon
+ const active = preference === item.id
return (
setPreference(item.id)}
className={cn(
- 'h-8 px-3 text-cap font-semibold tracking-[0.14em] transition-colors',
+ 'inline-flex h-6 w-6 items-center justify-center rounded transition-colors',
active
? 'bg-accent text-on-accent'
- : 'text-ink-2 hover:text-ink hover:bg-surface-2',
+ : 'text-mute hover:text-ink hover:bg-surface-2',
)}
>
- {item.label}
+
)
})}
diff --git a/ordinis-admin-ui/src/components/layout/TopBar.tsx b/ordinis-admin-ui/src/components/layout/TopBar.tsx
index 7a2f3ef..4bfabb2 100644
--- a/ordinis-admin-ui/src/components/layout/TopBar.tsx
+++ b/ordinis-admin-ui/src/components/layout/TopBar.tsx
@@ -1,12 +1,19 @@
import { useTranslation } from 'react-i18next'
import { Link, useLocation, useNavigate } from '@tanstack/react-router'
import { useDictionaryDetail } from '@/api/queries'
+import { LanguageSwitch } from '@/ui'
import { AuthBadge } from '@/auth/AuthBadge'
import { ThemeSwitch } from '@/components/layout/ThemeSwitch'
+import { VersionBadge } from '@/components/version/VersionBadge'
import { SearchInput } from '@/ui/components/search-input'
import { Menu } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
+const LANG_OPTIONS = [
+ { id: 'ru-RU', label: 'RU' },
+ { id: 'en-US', label: 'EN' },
+]
+
/**
* TopBar — sticky header (h=56) per handoff design.
*
@@ -17,7 +24,7 @@ import { useEffect, useRef, useState } from 'react'
* - Right: VersionBadge · ThemeSwitch · LanguageSwitch · Docs · AuthBadge
*/
export function TopBar({ onMenuClick }: { onMenuClick?: () => void }) {
- const { t } = useTranslation()
+ const { t, i18n } = useTranslation()
const navigate = useNavigate()
const [searchValue, setSearchValue] = useState('')
const inputRef = useRef
(null)
@@ -115,42 +122,34 @@ export function TopBar({ onMenuClick }: { onMenuClick?: () => void }) {
/>
- {/* Right cluster — minimal per redesign prototype:
- * - Lang toggle (single label, click flips)
- * - Theme switch (Earth / Dark)
- * - AuthBadge (sign-in / user avatar)
- * Docs link и VersionBadge перенесены в sidebar footer — diagnostic info,
- * не critical UX. */}
+ {/* Right cluster — user prefers full set: Docs link + VersionBadge +
+ * ThemeSwitch + LanguageSwitch + AuthBadge (logged-in avatar/sign-in).
+ * User explicitly: "Руководство · dev · {commit} · theme · RU EN ·
+ * zimin.an · logout это оставь". */}
)
}
-/**
- * LanguageToggle — single-button toggle per redesign prototype (показывает
- * текущий язык как label, click переключает на следующий). Замена tri/dual
- * radio LanguageSwitch который занимал ~80px горизонтально.
- */
-function LanguageToggle() {
- const { i18n } = useTranslation()
- const cur = i18n.language?.toLowerCase().startsWith('en') ? 'en' : 'ru'
- const next = cur === 'ru' ? 'en-US' : 'ru-RU'
- return (
- i18n.changeLanguage(next)}
- aria-label={`Language: ${cur.toUpperCase()}, click to switch`}
- className="h-8 px-3 rounded-md border border-line bg-surface text-cap font-semibold tracking-[0.14em] text-ink-2 hover:bg-surface-2 hover:text-ink transition-colors"
- >
- {cur.toUpperCase()}
-
- )
-}
-
type Crumb = { label: string; to?: string; subtitle?: string }
/**
diff --git a/ordinis-admin-ui/src/i18n.ts b/ordinis-admin-ui/src/i18n.ts
index f0d83f9..38ba474 100644
--- a/ordinis-admin-ui/src/i18n.ts
+++ b/ordinis-admin-ui/src/i18n.ts
@@ -338,7 +338,7 @@ i18n
'scope.PUBLIC': 'PUBLIC — публичный',
'scope.INTERNAL': 'INTERNAL — внутренний',
'scope.RESTRICTED': 'RESTRICTED — ограниченный',
- 'schema.action.create': 'Создать справочник',
+ 'schema.action.create': 'Создать',
'schema.action.edit': 'Изменить справочник',
'schema.action.editSchema': 'Схема',
'schema.tabs.metadata': 'Метаданные',
@@ -844,7 +844,7 @@ i18n
'scope.PUBLIC': 'PUBLIC',
'scope.INTERNAL': 'INTERNAL',
'scope.RESTRICTED': 'RESTRICTED',
- 'schema.action.create': 'Create dictionary',
+ 'schema.action.create': 'Create',
'schema.action.edit': 'Edit dictionary',
'schema.action.editSchema': 'Schema',
'schema.tabs.metadata': 'Metadata',