fix(ui): улучшить отображение таблицы сертификатов и добавить классы для сетки
This commit is contained in:
1 parent
d62d212c86
commit
6c8ca17590
2 files changed
+53
-14
No files matched your search
@@ -78,6 +78,9 @@ const STATUS_CONFIG: Record<
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CERT_TABLE_GRID_CLASS =
|
||||||
|
"grid grid-cols-[1.25rem_minmax(0,1.35fr)_minmax(0,0.85fr)_minmax(0,1fr)_9.5rem_minmax(0,8.5rem)_6rem] gap-3"
|
||||||
|
|
||||||
function daysLeftColor(days: number): string {
|
function daysLeftColor(days: number): string {
|
||||||
if (days < 0) return "text-red-500"
|
if (days < 0) return "text-red-500"
|
||||||
if (days <= 7) return "text-red-500"
|
if (days <= 7) return "text-red-500"
|
||||||
@@ -197,7 +200,10 @@ function CertListRow({
|
|||||||
return (
|
return (
|
||||||
<div className={cn("border-b last:border-b-0", cfg.row)}>
|
<div className={cn("border-b last:border-b-0", cfg.row)}>
|
||||||
<div
|
<div
|
||||||
className="grid grid-cols-[20px_1fr_1fr_1fr_160px_auto_auto] gap-3 px-4 py-3 items-center hover:bg-muted/30 transition-colors cursor-pointer"
|
className={cn(
|
||||||
|
CERT_TABLE_GRID_CLASS,
|
||||||
|
"px-4 py-3 items-center hover:bg-muted/30 transition-colors cursor-pointer",
|
||||||
|
)}
|
||||||
onClick={onToggle}
|
onClick={onToggle}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
@@ -211,11 +217,15 @@ function CertListRow({
|
|||||||
{expanded ? <ChevronDownIcon className="size-3.5" /> : <ChevronRightIcon className="size-3.5" />}
|
{expanded ? <ChevronDownIcon className="size-3.5" /> : <ChevronRightIcon className="size-3.5" />}
|
||||||
</button>
|
</button>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2 min-w-0">
|
||||||
{cfg.icon}
|
<span className="shrink-0">{cfg.icon}</span>
|
||||||
<span className="font-medium text-sm truncate">{cert.name}</span>
|
<span className="font-medium text-sm truncate" title={cert.name}>
|
||||||
|
{cert.name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground font-mono mt-0.5 truncate">{cert.commonName}</p>
|
<p className="text-xs text-muted-foreground font-mono mt-0.5 truncate" title={cert.commonName}>
|
||||||
|
{cert.commonName}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground min-w-0">
|
<div className="flex items-center gap-1.5 text-xs text-muted-foreground min-w-0">
|
||||||
{server
|
{server
|
||||||
@@ -232,11 +242,13 @@ function CertListRow({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground truncate">{cert.issuedBy}</p>
|
<p className="text-xs text-muted-foreground truncate min-w-0" title={cert.issuedBy}>
|
||||||
<div>
|
{cert.issuedBy}
|
||||||
|
</p>
|
||||||
|
<div className="min-w-0">
|
||||||
<CertPartDays cert={cert} pct={pct} />
|
<CertPartDays cert={cert} pct={pct} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1 min-w-0 overflow-hidden">
|
||||||
{cert.usage.map((u) => (
|
{cert.usage.map((u) => (
|
||||||
<span
|
<span
|
||||||
key={u}
|
key={u}
|
||||||
@@ -246,7 +258,12 @@ function CertListRow({
|
|||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<span className={cn("text-[11px] font-mono px-2 py-0.5 rounded border whitespace-nowrap", cfg.badge)}>
|
<span
|
||||||
|
className={cn(
|
||||||
|
"text-[11px] font-mono px-2 py-0.5 rounded border whitespace-nowrap shrink-0 justify-self-end",
|
||||||
|
cfg.badge,
|
||||||
|
)}
|
||||||
|
>
|
||||||
{cfg.label}
|
{cfg.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -400,14 +417,25 @@ function CertPartAcmeSettings({
|
|||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Публичные Let's Encrypt для зон в Cloudflare выпускаются на backend и импортируются на RouterOS 7.22+.
|
Публичные Let's Encrypt для зон в Cloudflare выпускаются на backend и импортируются на RouterOS 7.22+.
|
||||||
</p>
|
</p>
|
||||||
<div className="grid gap-3 md:grid-cols-2">
|
<form
|
||||||
|
className="grid gap-3 md:grid-cols-2"
|
||||||
|
autoComplete="off"
|
||||||
|
onSubmit={(event) => event.preventDefault()}
|
||||||
|
>
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<label className="text-sm font-medium">ACME directory URL</label>
|
<label className="text-sm font-medium">ACME directory URL</label>
|
||||||
<Input value={acmeDirectoryUrl} onChange={(e) => setAcmeDirectoryUrl(e.target.value)} />
|
<Input
|
||||||
|
autoComplete="off"
|
||||||
|
name="acme-directory-url"
|
||||||
|
value={acmeDirectoryUrl}
|
||||||
|
onChange={(e) => setAcmeDirectoryUrl(e.target.value)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<label className="text-sm font-medium">Cloudflare zone id (опционально)</label>
|
<label className="text-sm font-medium">Cloudflare zone id (опционально)</label>
|
||||||
<Input
|
<Input
|
||||||
|
autoComplete="off"
|
||||||
|
name="cloudflare-zone-id"
|
||||||
value={acmeZoneId}
|
value={acmeZoneId}
|
||||||
onChange={(e) => setAcmeZoneId(e.target.value)}
|
onChange={(e) => setAcmeZoneId(e.target.value)}
|
||||||
placeholder="Авто по домену"
|
placeholder="Авто по домену"
|
||||||
@@ -417,6 +445,8 @@ function CertPartAcmeSettings({
|
|||||||
<label className="text-sm font-medium">Cloudflare API token</label>
|
<label className="text-sm font-medium">Cloudflare API token</label>
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="new-password"
|
||||||
|
name="cloudflare-api-token"
|
||||||
value={acmeTokenDraft}
|
value={acmeTokenDraft}
|
||||||
onChange={(e) => setAcmeTokenDraft(e.target.value)}
|
onChange={(e) => setAcmeTokenDraft(e.target.value)}
|
||||||
placeholder={
|
placeholder={
|
||||||
@@ -426,7 +456,7 @@ function CertPartAcmeSettings({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
<Button variant="outline" size="sm" disabled={acmeSaveBusy} onClick={onTest}>
|
<Button variant="outline" size="sm" disabled={acmeSaveBusy} onClick={onTest}>
|
||||||
Проверить Cloudflare
|
Проверить Cloudflare
|
||||||
@@ -512,7 +542,12 @@ function CertPartTableHeaderUsage() {
|
|||||||
|
|
||||||
function CertPartTableHeader() {
|
function CertPartTableHeader() {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-[20px_1fr_1fr_1fr_160px_auto_auto] gap-3 px-4 py-2 border-b text-[10px] font-semibold uppercase tracking-widest text-muted-foreground bg-muted/20">
|
<div
|
||||||
|
className={cn(
|
||||||
|
CERT_TABLE_GRID_CLASS,
|
||||||
|
"px-4 py-2 border-b text-[10px] font-semibold uppercase tracking-widest text-muted-foreground bg-muted/20",
|
||||||
|
)}
|
||||||
|
>
|
||||||
<span />
|
<span />
|
||||||
<span>Имя / CN</span>
|
<span>Имя / CN</span>
|
||||||
<span>Сервер</span>
|
<span>Сервер</span>
|
||||||
@@ -962,6 +997,8 @@ export default function CertificatesPage() {
|
|||||||
setStatusFilter={setStatusFilter}
|
setStatusFilter={setStatusFilter}
|
||||||
filteredCount={filtered.length}
|
filteredCount={filtered.length}
|
||||||
/>
|
/>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<div className="min-w-[48rem]">
|
||||||
<CertPartTableHeader />
|
<CertPartTableHeader />
|
||||||
{prefsHydrated && isLive && loadState === "loading" && displayCerts.length === 0 ? (
|
{prefsHydrated && isLive && loadState === "loading" && displayCerts.length === 0 ? (
|
||||||
<div className="py-16 text-center text-sm text-muted-foreground">Загрузка сертификатов…</div>
|
<div className="py-16 text-center text-sm text-muted-foreground">Загрузка сертификатов…</div>
|
||||||
@@ -981,6 +1018,8 @@ export default function CertificatesPage() {
|
|||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<CertPartReference />
|
<CertPartReference />
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatiLine truncated
|
{"fileNames":["./node_modules/typescript/lib/lib.es5.d.ts","./node_modules/typescript/lib/lib.es2015.d.ts","./node_modules/typescript/lib/lib.es2016.d.ts","./node_modules/typescript/lib/lib.es2017.d.ts","./node_modules/typescript/lib/lib.es2018.d.ts","./node_modules/typescript/lib/lib.es2019.d.ts","./node_modules/typescript/lib/lib.es2020.d.ts","./node_modules/typescript/lib/lib.es2021.d.ts","./node_modules/typescript/lib/lib.es2022.d.ts","./node_modules/typescript/lib/lib.es2023.d.ts","./node_modules/typescript/lib/lib.es2024.d.ts","./node_modules/typescript/lib/lib.esnext.d.ts","./node_modules/typescript/lib/lib.dom.d.ts","./node_modules/typescript/lib/lib.dom.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.core.d.ts","./node_modules/typescript/lib/lib.es2015.collection.d.ts","./node_modules/typescript/lib/lib.es2015.generator.d.ts","./node_modules/typescript/lib/lib.es2015.iterable.d.ts","./node_modules/typescript/lib/lib.es2015.promise.d.ts","./node_modules/typescript/lib/lib.es2015.proxy.d.ts","./node_modules/typescript/lib/lib.es2015.reflect.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.d.ts","./node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2016.array.include.d.ts","./node_modules/typescript/lib/lib.es2016.intl.d.ts","./node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2017.date.d.ts","./node_modules/typescript/lib/lib.es2017.object.d.ts","./node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2017.string.d.ts","./node_modules/typescript/lib/lib.es2017.intl.d.ts","./node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","./node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","./node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","./node_modules/typescript/lib/lib.es2018.intl.d.ts","./node_modules/typescript/lib/lib.es2018.promise.d.ts","./node_modules/typescript/lib/lib.es2018.regexp.d.ts","./node_modules/typescript/lib/lib.es2019.array.d.ts","./node_modules/typescript/lib/lib.es2019.object.d.ts","./node_modules/typescript/lib/lib.es2019.string.d.ts","./node_modules/typescript/lib/lib.es2019.symbol.d.ts","./node_modules/typescript/lib/lib.es2019.intl.d.ts","./node_modules/typescript/lib/lib.es2020.bigint.d.ts","./node_modules/typescript/lib/lib.es2020.date.d.ts","./node_modules/typescript/lib/lib.es2020.promise.d.ts","./node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2020.string.d.ts","./node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","./node_modules/typescript/lib/lib.es2020.intl.d.ts","./node_modules/typescript/lib/lib.es2020.number.d.ts","./node_modules/typescript/lib/lib.es2021.promise.d.ts","./node_modules/typescript/lib/lib.es2021.string.d.ts","./node_modules/typescript/lib/lib.es2021.weakref.d.ts","./node_modules/typescript/lib/lib.es2021.intl.d.ts","./node_modules/typescript/lib/lib.es2022.array.d.ts","./node_modules/typescript/lib/lib.es2022.error.d.ts","./node_modules/typescript/lib/lib.es2022.intl.d.ts","./node_modules/typescript/lib/lib.es2022.object.d.ts","./node_modules/typescript/lib/lib.es2022.string.d.ts","./node_modules/typescript/lib/lib.es2022.regexp.d.ts","./node_modules/typescript/lib/lib.es2023.array.d.ts","./node_modules/typescript/lib/lib.es2023.collection.d.ts","./node_modules/typescript/lib/lib.es2023.intl.d.ts","./node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts","./node_modules/typescript/lib/lib.es2024.collection.d.ts","./node_modules/typescript/lib/lib.es2024.object.d.ts","./node_modules/typescript/lib/lib.es2024.promise.d.ts","./node_modules/typescript/lib/lib.es2024.regexp.d.ts","./node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts","./node_modules/typescript/lib/lib.es2024.string.d.ts","./node_modules/typescript/lib/lib.esnext.array.d.ts","./node_modules/typescript/lib/lib.esnext.collection.d.ts","./node_modules/typescript/lib/lib.esnext.intl.d.ts","./node_modules/typescript/lib/lib.esnext.disposable.d.ts","./node_modules/typescript/lib/lib.esnext.promise.d.ts","./node_modules/typescript/lib/lib.esnext.decorators.d.ts","./node_modules/typescript/lib/lib.esnext.iterator.d.ts","./node_modules/typescript/lib/lib.esnext.float16.d.ts","./node_modules/typescript/lib/lib.esnext.error.d.ts","./node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts","./node_modules/typescript/lib/lib.decorators.d.ts","./node_modules/typescript/lib/lib.decorators.legacy.d.ts","./node_modules/@types/react/global.d.ts","./node_modules/csstype/index.d.ts","./node_modules/@types/react/index.d.ts","./node_modules/next/dist/styled-jsx/types/css.d.ts","./node_modules/next/dist/styled-jsx/types/macro.d.ts","./node_modules/next/dist/styled-jsx/types/style.d.ts","./node_modules/next/dist/styled-jsx/types/global.d.ts","./node_modules/next/dist/styled-jsx/types/index.d.ts","./node_modules/next/dist/server/get-page-files.d.ts","./node_modules/@types/node/compatiLine truncated
|
||||||
Reference in new issue
Block a user