feat(ui): единый PageHeader h1 на всех разделах
Build, Test, and Push CFDM Docker Image / test (push) Successful in 3m4s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 1m45s
Build, Test, and Push CFDM Docker Image / create-release (push) Skipped
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s

Заголовок раздела над KPI/контентом; FrameTitle text-sm больше не заменяет page-level h1.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-17 14:29:44 +07:00
co-authored by Cursor
parent 6c379f339b
commit 704624cbe7
8 changed files with 136 additions and 57 deletions
+7 -1
View File
@@ -6,6 +6,7 @@ import type { Filter } from '@/components/reui/filters'
import { certificatesQueryOptions, certKeys, certSummaryQueryOptions } from '@/queries'
import { api } from '@/lib/api-client'
import { PageShell } from '@/components/page-shell'
import { PageHeader } from '@/components/page-header'
import { ResourcePage } from '@/components/reui-kit'
import { CertKpiCards } from '@/components/cert-kpi-cards'
import {
@@ -71,6 +72,11 @@ function CertificatesPage() {
return (
<PageShell>
<PageHeader
title="Сертификаты"
description="Мониторинг SSL: хосты с активными сервисами или режимом «Обязательно»"
actions={primaryAction}
/>
<CertKpiCards
summary={summary}
total={certs?.length}
@@ -81,6 +87,7 @@ function CertificatesPage() {
<ResourcePage
title="Сертификаты"
description="Мониторинг SSL: хосты с активными сервисами или режимом «Обязательно»"
hideHeader
tabs={CERT_TABS.map((tab) => ({ ...tab }))}
activeTab={activeTab}
onTabChange={setActiveTab}
@@ -97,7 +104,6 @@ function CertificatesPage() {
isError={isError}
error={error}
onRetry={refetch}
primaryAction={primaryAction}
emptyState={{
title: 'Сертификаты не найдены',
description: 'Сертификаты появятся после проверки доменов',
+7 -1
View File
@@ -15,6 +15,7 @@ import {
serviceBindingKeys,
} from '@/queries'
import { PageShell } from '@/components/page-shell'
import { PageHeader } from '@/components/page-header'
import { DomainKpiCards } from '@/components/domain-kpi-cards'
import { ResourcePage } from '@/components/reui-kit'
import {
@@ -149,6 +150,11 @@ function DomainsPage() {
return (
<PageShell>
<PageHeader
title="Домены"
description="Импортированные зоны Cloudflare"
actions={primaryAction}
/>
<DomainKpiCards
domains={domains}
activeTab={activeTab}
@@ -158,6 +164,7 @@ function DomainsPage() {
<ResourcePage
title="Домены"
description="Импортированные зоны Cloudflare"
hideHeader
tabs={DOMAIN_TABS.map((tab) => ({ ...tab }))}
activeTab={activeTab}
onTabChange={setActiveTab}
@@ -174,7 +181,6 @@ function DomainsPage() {
isError={isError}
error={error}
onRetry={refetch}
primaryAction={primaryAction}
enableRowSelection
selectionToolbar={({ selectedIds, clearSelection }) => (
<DomainsBulkToolbar
+28 -12
View File
@@ -14,6 +14,7 @@ import {
import { api } from '@/lib/api-client'
import type { CreateGroupInput, Group } from '@/lib/schemas'
import { PageShell } from '@/components/page-shell'
import { PageHeader } from '@/components/page-header'
import { GroupKpiCards } from '@/components/group-kpi-cards'
import { EmptyState } from '@/components/empty-state'
import { ConfirmDialog } from '@/components/confirm-dialog'
@@ -202,15 +203,31 @@ function GroupsPage() {
</Button>
)
const headerActions = (
<>
{createButton}
<CatalogBoardToggle
view={view}
onViewChange={setView}
/>
</>
)
if (isLoading) {
return (
<PageShell>
<PageHeader
title="Группы"
description="Каталог групп доменов"
actions={headerActions}
/>
{view === 'board' ? (
<KanbanBoardSkeleton columns={3} />
) : (
<ResourcePage
title="Группы"
description="Каталог групп доменов"
hideHeader
isLoading
filterFields={[]}
filters={[]}
@@ -228,6 +245,7 @@ function GroupsPage() {
if (isError) {
return (
<PageShell>
<PageHeader title="Группы" description="Каталог групп доменов" />
<EmptyState
title="Не удалось загрузить группы"
description={error?.message}
@@ -243,6 +261,15 @@ function GroupsPage() {
return (
<PageShell>
<PageHeader
title="Группы"
description={
view === 'board'
? 'Перетащите домен в группу или создайте новую группу'
: 'Каталог групп доменов'
}
actions={headerActions}
/>
{view === 'board' ? (
(groups?.length ?? 0) === 0 && (domains?.length ?? 0) === 0 ? (
<EmptyState
@@ -263,12 +290,6 @@ function GroupsPage() {
columns={columnConfigs}
value={kanbanValue}
onValueChange={handleKanbanValueChange}
toolbarActions={
<>
{createButton}
<CatalogBoardToggle view="board" onViewChange={setView} />
</>
}
renderCard={(domain, ctx) => (
<DomainKanbanCard
domain={domain}
@@ -289,6 +310,7 @@ function GroupsPage() {
<ResourcePage
title="Группы"
description="Каталог групп доменов"
hideHeader
tabs={GROUP_TABS.map((tab) => ({ ...tab }))}
activeTab={activeTab}
onTabChange={setActiveTab}
@@ -301,12 +323,6 @@ function GroupsPage() {
columns={columns}
data={catalogRows}
getRowId={(row) => String(row.id)}
primaryAction={
<>
{createButton}
<CatalogBoardToggle view="catalog" onViewChange={setView} />
</>
}
emptyState={{
title: 'Группы не найдены',
description: 'Создайте группу для организации доменов.',
+25 -13
View File
@@ -21,6 +21,7 @@ import type {
UpdateServiceConfigInput,
} from '@/lib/schemas'
import { PageShell } from '@/components/page-shell'
import { PageHeader } from '@/components/page-header'
import { ServiceKpiCards } from '@/components/service-kpi-cards'
import { EmptyState } from '@/components/empty-state'
import { ConfirmDialog } from '@/components/confirm-dialog'
@@ -437,6 +438,17 @@ function ServicesPage() {
/>
)
const headerActions = (
<>
{addMenu}
<CatalogBoardToggle view={view} onViewChange={setView} />
</>
)
const pageDescription = filteredDomain
? `Сервисы с привязками к домену ${filteredDomain.zone_name}`
: 'Группы, FQDN и доступность сервисов'
const sheets = (
<>
<ServiceEditSheet
@@ -528,12 +540,18 @@ function ServicesPage() {
if (isLoading) {
return (
<PageShell>
<PageHeader
title="Сервисы"
description={pageDescription}
actions={headerActions}
/>
{view === 'board' ? (
<KanbanBoardSkeleton columns={3} />
) : (
<ServicesGroupedCatalog
data={{ groups: [], ungrouped: [] }}
isLoading
hideHeader
togglingId={null}
onEditService={() => {}}
onDeleteService={() => {}}
@@ -551,6 +569,7 @@ function ServicesPage() {
if (isError) {
return (
<PageShell>
<PageHeader title="Сервисы" description={pageDescription} />
<EmptyState
title="Не удалось загрузить сервисы"
description={error?.message}
@@ -567,6 +586,11 @@ function ServicesPage() {
return (
<PageShell>
<PageHeader
title="Сервисы"
description={pageDescription}
actions={headerActions}
/>
{view === 'board' ? (
<>
<ServicesBulkToolbar
@@ -588,7 +612,6 @@ function ServicesPage() {
action={
<div className="flex flex-wrap justify-center gap-2">
{addMenu}
<CatalogBoardToggle view="board" onViewChange={setView} />
</div>
}
/>
@@ -603,12 +626,6 @@ function ServicesPage() {
columns={columnConfigs}
value={kanbanValue}
onValueChange={dragDisabled ? () => {} : handleKanbanValueChange}
toolbarActions={
<>
{addMenu}
<CatalogBoardToggle view="board" onViewChange={setView} />
</>
}
renderCard={(service, ctx) => (
<ServiceKanbanCard
service={service}
@@ -638,18 +655,13 @@ function ServicesPage() {
togglingId={togglingServiceId}
activeTab={activeTab}
onTabChange={setActiveTab}
hideHeader
onEditService={setEditingService}
onDeleteService={setDeletingService}
onToggleService={handleServiceToggle}
onEditGroup={setEditingGroup}
onDeleteGroup={setDeletingGroup}
onAddServiceToGroup={handleOpenCreateService}
primaryAction={
<>
{addMenu}
<CatalogBoardToggle view="catalog" onViewChange={setView} />
</>
}
emptyAction={
<div className="flex flex-wrap justify-center gap-2">
{addMenu}