Enhance frontend documentation and UI components; update frontend-shadcn.mdc to include UI patterns reference; improve navigation structure in AppSidebar by categorizing items; refactor DataTableCard to support empty states with EmptyState component; implement ConfirmDialog for delete actions in DnsRecordsTable and GroupsPage; add search functionality in CertificatesPage and GroupsPage for better user experience; update ServiceEditSheet to utilize tabs for organization.
Build, Test, and Push CFDM Docker Image / test (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / build-and-push (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / create-release (push) Has been cancelled
Build, Test, and Push CFDM Docker Image / update-wiki (push) Has been cancelled

This commit is contained in:
Denozordec committed 2026-06-19 12:15:29 +07:00
1 parent d11414666f
commit 4a70cf5854
32 files changed
+1546 -435

No files matched your search

+38 -10
View File
@@ -1,4 +1,5 @@
import { Link, useRouterState } from '@tanstack/react-router'
import { Link, useMatches, useRouterState } from '@tanstack/react-router'
import { useMemo } from 'react'
import {
Breadcrumb,
BreadcrumbItem,
@@ -10,31 +11,39 @@ import {
import { Separator } from '@cfdm/ui/components/separator'
import { SidebarTrigger } from '@cfdm/ui/components/sidebar'
export interface RouteBreadcrumbLoaderData {
breadcrumb?: string
}
const routeTitles: Record<string, string> = {
'/': 'Панель управления',
'/domains': 'Домены',
'/groups': 'Группы',
'/groups': 'Группы доменов',
'/services': 'Сервисы',
'/certificates': 'Сертификаты',
}
function getBreadcrumbs(pathname: string) {
function getBreadcrumbs(
pathname: string,
dynamicLabels: Record<string, string>,
) {
if (pathname === '/') {
return [{ label: 'Панель управления', href: '/' }]
}
if (pathname.match(/^\/groups\/\d+$/)) {
return [
{ label: 'Группы', href: '/groups' },
{ label: 'Группа', href: pathname },
{ label: 'Группы доменов', href: '/groups' },
{ label: dynamicLabels[pathname] ?? 'Группа', href: pathname },
]
}
if (pathname.match(/^\/domains\/\d+\/dns$/)) {
const domainId = pathname.split('/')[2]
const domainPath = `/domains/${domainId}`
return [
{ label: 'Домены', href: '/domains' },
{ label: 'Домен', href: `/domains/${domainId}` },
{ label: dynamicLabels[domainPath] ?? 'Домен', href: domainPath },
{ label: 'DNS', href: pathname },
]
}
@@ -42,7 +51,7 @@ function getBreadcrumbs(pathname: string) {
if (pathname.match(/^\/domains\/\d+$/)) {
return [
{ label: 'Домены', href: '/domains' },
{ label: 'Обзор домена', href: pathname },
{ label: dynamicLabels[pathname] ?? 'Обзор домена', href: pathname },
]
}
@@ -54,9 +63,24 @@ function getBreadcrumbs(pathname: string) {
return [{ label: 'Панель управления', href: '/' }]
}
function useDynamicBreadcrumbLabels() {
const matches = useMatches()
return useMemo(() => {
const labels: Record<string, string> = {}
for (const match of matches) {
const data = match.loaderData as RouteBreadcrumbLoaderData | undefined
if (data?.breadcrumb && match.pathname) {
labels[match.pathname] = data.breadcrumb
}
}
return labels
}, [matches])
}
export function SiteHeader() {
const pathname = useRouterState({ select: (s) => s.location.pathname })
const crumbs = getBreadcrumbs(pathname)
const dynamicLabels = useDynamicBreadcrumbLabels()
const crumbs = getBreadcrumbs(pathname, dynamicLabels)
return (
<header className="flex h-16 shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
@@ -72,8 +96,12 @@ export function SiteHeader() {
const isLast = index === crumbs.length - 1
return (
<span key={crumb.href} className="contents">
{index > 0 && <BreadcrumbSeparator className="hidden md:block" />}
<BreadcrumbItem className={index === 0 ? 'hidden md:block' : undefined}>
{index > 0 && (
<BreadcrumbSeparator className="hidden md:block" />
)}
<BreadcrumbItem
className={index === 0 ? 'hidden md:block' : undefined}
>
{isLast ? (
<BreadcrumbPage>{crumb.label}</BreadcrumbPage>
) : (