feat(web): enhance certificate management and UI components
Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m0s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m17s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m0s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m17s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
- Updated the CertificatesPage to include a new CertKpiCards component for improved KPI visualization. - Refactored filter handling and state management for better user experience. - Introduced new status options and enhanced filtering capabilities in certificates columns. - Updated the OpsDashboard to utilize KpiStatGrid for displaying key metrics. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { ShieldCheckIcon } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
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 { ResourcePage } from '@/components/reui-kit'
|
||||
import { CertKpiCards } from '@/components/cert-kpi-cards'
|
||||
import {
|
||||
CERT_TABS,
|
||||
certFilterFieldValue,
|
||||
@@ -16,14 +16,6 @@ import {
|
||||
useCertificateColumns,
|
||||
useCertFilterFields,
|
||||
} from '@/components/columns/certificates-columns'
|
||||
import { StatusBadge } from '@/components/status-badge'
|
||||
import {
|
||||
Frame,
|
||||
FrameDescription,
|
||||
FrameHeader,
|
||||
FramePanel,
|
||||
FrameTitle,
|
||||
} from '@/components/reui/frame'
|
||||
import { LoadingButton } from '@/components/loading-button'
|
||||
|
||||
export const Route = createFileRoute('/_auth/certificates')({
|
||||
@@ -37,6 +29,7 @@ export const Route = createFileRoute('/_auth/certificates')({
|
||||
|
||||
function CertificatesPage() {
|
||||
const [filters, setFilters] = useState<Filter[]>(createDefaultCertFilters)
|
||||
const [activeTab, setActiveTab] = useState('all')
|
||||
const queryClient = useQueryClient()
|
||||
const {
|
||||
data: certs,
|
||||
@@ -45,7 +38,7 @@ function CertificatesPage() {
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery(certificatesQueryOptions())
|
||||
const { data: summary } = useQuery(certSummaryQueryOptions())
|
||||
const { data: summary, isLoading: isSummaryLoading } = useQuery(certSummaryQueryOptions())
|
||||
|
||||
const checkMutation = useMutation({
|
||||
mutationFn: () => api.post('/api/v1/certificates/check'),
|
||||
@@ -59,40 +52,13 @@ function CertificatesPage() {
|
||||
},
|
||||
})
|
||||
|
||||
const handleSelectTab = useCallback((tabId: string) => {
|
||||
setActiveTab(tabId)
|
||||
}, [])
|
||||
|
||||
const filterFields = useCertFilterFields()
|
||||
const columns = useCertificateColumns()
|
||||
|
||||
const kpiStrip = useMemo(() => {
|
||||
const chartData = summary?.map(([status, count]) => ({ status, count })) ?? []
|
||||
return (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{chartData.length === 0 ? (
|
||||
<Frame dense spacing="sm" className="sm:col-span-2 lg:col-span-4">
|
||||
<FramePanel className="flex items-center gap-3 p-4">
|
||||
<ShieldCheckIcon className="text-muted-foreground size-5" />
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Нет данных — запустите проверку сертификатов
|
||||
</p>
|
||||
</FramePanel>
|
||||
</Frame>
|
||||
) : (
|
||||
chartData.map((entry) => (
|
||||
<Frame key={entry.status} dense spacing="sm">
|
||||
<FrameHeader className="px-3! py-2!">
|
||||
<FrameTitle className="text-sm font-medium">
|
||||
<StatusBadge status={entry.status} />
|
||||
</FrameTitle>
|
||||
<FrameDescription className="text-2xl font-semibold tabular-nums">
|
||||
{entry.count}
|
||||
</FrameDescription>
|
||||
</FrameHeader>
|
||||
</Frame>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}, [summary])
|
||||
|
||||
const primaryAction = (
|
||||
<LoadingButton
|
||||
onClick={() => checkMutation.mutate()}
|
||||
@@ -105,11 +71,19 @@ function CertificatesPage() {
|
||||
|
||||
return (
|
||||
<PageShell className="gap-6">
|
||||
{kpiStrip}
|
||||
<CertKpiCards
|
||||
summary={summary}
|
||||
total={certs?.length}
|
||||
activeTab={activeTab}
|
||||
isLoading={isSummaryLoading && !summary}
|
||||
onSelectTab={handleSelectTab}
|
||||
/>
|
||||
<ResourcePage
|
||||
title="Сертификаты"
|
||||
description="Мониторинг SSL: хосты с активными сервисами или режимом «Обязательно»"
|
||||
tabs={CERT_TABS.map((tab) => ({ ...tab }))}
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
tabFilter={certTabFilter}
|
||||
filterFields={filterFields}
|
||||
filters={filters}
|
||||
|
||||
Reference in New Issue
Block a user