fix(ui): выровнять confirms и токены под ReUI PRO
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m31s
Docker images / frontend-image (push) Successful in 2m11s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 40s
Docker images / publish-release (push) Successful in 10s
Docker images / prepare-release (push) Successful in 8s
Docker images / backend-image (push) Successful in 1m31s
Docker images / frontend-image (push) Successful in 2m11s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 40s
Docker images / publish-release (push) Successful in 10s
Добавить ui-design-contract, AlertDialog вместо hand-roll, CodeExportSheet для preview фильтров, ReUI Badge и gap вместо space-y. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -4,7 +4,7 @@ import Link from "next/link"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { PageHeader } from "@/components/page-header"
|
||||
import { FormToggle } from "@/components/form-kit"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Badge } from "@/components/reui/badge"
|
||||
import { Button, buttonVariants } from "@/components/ui/button"
|
||||
import { Frame, FramePanel } from "@/components/reui/frame"
|
||||
import { IconTile } from "@/components/reui/icon-tile"
|
||||
@@ -104,7 +104,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "traffic") {
|
||||
const t = snap as TrafficRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
{t.skipped ? (
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">Прогон пропущен: предыдущий сбор трафика ещё выполнялся.</p>
|
||||
) : null}
|
||||
@@ -126,7 +126,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "uptime_resources") {
|
||||
const u = snap as ResourcesRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
{u.skipped ? (
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">Прогон пропущен: сбор ресурсов уже выполняется.</p>
|
||||
) : null}
|
||||
@@ -148,7 +148,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "servers_rest_ping") {
|
||||
const s = snap as ServersRestPingRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
{s.skipped ? (
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">Прогон пропущен: предыдущая проверка API ещё выполнялась.</p>
|
||||
) : null}
|
||||
@@ -171,7 +171,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "uptime_ping") {
|
||||
const p = snap as PingRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
{p.skipped ? (
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">Прогон пропущен: сбор ping уже выполняется.</p>
|
||||
) : null}
|
||||
@@ -196,7 +196,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "uptime_speed") {
|
||||
const s = snap as SpeedScheduledRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Прогоны speed на <span className="font-mono tabular-nums">{new Date(s.sampledAt).toLocaleString("ru-RU")}</span> — по очереди для каждой включённой пробы
|
||||
</p>
|
||||
@@ -209,7 +209,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "gre_bgp") {
|
||||
const g = snap as GreBgpSnapshotRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
{g.skipped ? (
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">Прогон пропущен: предыдущий сбор GRE/BGP ещё выполнялся.</p>
|
||||
) : null}
|
||||
@@ -235,7 +235,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
</div>
|
||||
</dl>
|
||||
{g.errors?.length ? (
|
||||
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200 space-y-1">
|
||||
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200 flex flex-col gap-1">
|
||||
{g.errors.map((e, i) => (
|
||||
<p key={i} className="break-words">
|
||||
{e}
|
||||
@@ -249,7 +249,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "internet_path") {
|
||||
const p = snap as InternetPathRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Снимок internet-path на{" "}
|
||||
<span className="font-mono tabular-nums">{new Date(p.sampledAt).toLocaleString("ru-RU")}</span>
|
||||
@@ -276,7 +276,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "certificates_renew") {
|
||||
const c = snap as CertificatesRenewRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
{c.skipped ? (
|
||||
<p className="text-xs text-amber-600 dark:text-amber-400">Прогон пропущен: предыдущая проверка ещё выполнялась или задача отключена.</p>
|
||||
) : null}
|
||||
@@ -299,7 +299,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
</div>
|
||||
</dl>
|
||||
{c.errors.length ? (
|
||||
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200 space-y-1">
|
||||
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200 flex flex-col gap-1">
|
||||
{c.errors.map((e, i) => (
|
||||
<p key={i} className="break-words">{e}</p>
|
||||
))}
|
||||
@@ -311,7 +311,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "backups") {
|
||||
const b = snap as BackupsRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<dl className="grid grid-cols-2 gap-3 text-xs sm:grid-cols-4">
|
||||
<div>
|
||||
<dt className="text-muted-foreground">Слот расписания</dt>
|
||||
@@ -331,7 +331,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
</div>
|
||||
</dl>
|
||||
{b.errors?.length ? (
|
||||
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200 space-y-1">
|
||||
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2 text-xs text-amber-800 dark:text-amber-200 flex flex-col gap-1">
|
||||
{b.errors.map((e, i) => (
|
||||
<p key={i} className="break-words">{e}</p>
|
||||
))}
|
||||
@@ -343,7 +343,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
if (snap.job === "alert_engine") {
|
||||
const a = snap as AlertEngineRunSnapshot
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Снимок на{" "}
|
||||
<span className="font-mono tabular-nums">{new Date(a.sampledAt).toLocaleString("ru-RU")}</span>
|
||||
@@ -370,7 +370,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
{a.errors?.length ? (
|
||||
<Alert variant="destructive" className="py-2">
|
||||
<AlertCircleIcon />
|
||||
<AlertDescription className="space-y-1 text-xs">
|
||||
<AlertDescription className="flex flex-col gap-1 text-xs">
|
||||
{a.errors.map((e, i) => (
|
||||
<p key={i} className="break-words">
|
||||
{e}
|
||||
@@ -380,7 +380,7 @@ function SnapshotTables({ snap }: { snap: SchedulerRunSnapshot }) {
|
||||
</Alert>
|
||||
) : null}
|
||||
{a.ruleDiag && a.ruleDiag.length > 0 ? (
|
||||
<div className="rounded-md border border-border bg-muted/20 px-3 py-2 space-y-2">
|
||||
<div className="rounded-md border border-border bg-muted/20 px-3 py-2 flex flex-col gap-2">
|
||||
<p className="text-[11px] font-medium text-muted-foreground">По правилам (почему не ушло в Telegram)</p>
|
||||
<DataPageCard className="border-0 shadow-none bg-transparent">
|
||||
<AlertEngineRuleDiagGrid ruleDiag={a.ruleDiag} />
|
||||
@@ -398,39 +398,39 @@ function RunRowDetail({ r }: { r: SchedulerRunRowDto }) {
|
||||
const jobDesc = SCHEDULER_JOB_DESCRIPTIONS[r.jobKey] ?? "—"
|
||||
const snapshot = useMemo(() => parseSchedulerRunSnapshot(r.resultJson ?? null), [r.resultJson])
|
||||
return (
|
||||
<div className="space-y-4 text-sm">
|
||||
<div className="flex flex-col gap-4 text-sm">
|
||||
<dl className="grid grid-cols-1 sm:grid-cols-2 gap-x-8 gap-y-3">
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">ID записи</dt>
|
||||
<dd className="font-mono text-xs break-all bg-muted/60 rounded-md px-2 py-1.5 border border-border">{r.id}</dd>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Ключ задачи</dt>
|
||||
<dd className="font-mono text-xs">{r.jobKey}</dd>
|
||||
</div>
|
||||
<div className="sm:col-span-2 space-y-1">
|
||||
<div className="sm:col-span-2 flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Название и назначение</dt>
|
||||
<dd>
|
||||
<span className="font-medium">{jobTitle}</span>
|
||||
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">{jobDesc}</p>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Старт</dt>
|
||||
<dd className="tabular-nums text-xs">{new Date(r.startedAt).toLocaleString("ru-RU")}</dd>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Завершение</dt>
|
||||
<dd className="tabular-nums text-xs">{new Date(r.finishedAt).toLocaleString("ru-RU")}</dd>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Длительность</dt>
|
||||
<dd className="tabular-nums">
|
||||
<span className="font-mono">{r.durationMs}</span> мс
|
||||
<span className="text-muted-foreground text-xs ml-2">({fmtMs(r.durationMs)})</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col gap-1">
|
||||
<dt className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Результат</dt>
|
||||
<dd>
|
||||
<Badge
|
||||
@@ -447,7 +447,7 @@ function RunRowDetail({ r }: { r: SchedulerRunRowDto }) {
|
||||
</div>
|
||||
</dl>
|
||||
{r.error ? (
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<p className="text-xs font-medium text-destructive">Текст ошибки</p>
|
||||
<pre
|
||||
className="text-xs font-mono whitespace-pre-wrap break-words rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 max-h-48 overflow-y-auto"
|
||||
@@ -461,7 +461,7 @@ function RunRowDetail({ r }: { r: SchedulerRunRowDto }) {
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Результаты измерений</p>
|
||||
{snapshot ? (
|
||||
<SnapshotTables snap={snapshot} />
|
||||
@@ -1090,7 +1090,7 @@ export default function DataCollectionPage() {
|
||||
</div>
|
||||
<DataCollectionSchedulerDataGrid rows={schedulerGridRows} />
|
||||
<Separator />
|
||||
<div className="space-y-3 px-5 py-4">
|
||||
<div className="flex flex-col gap-3 px-5 py-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-1.5">Хранение сэмплов трафика (дней)</p>
|
||||
@@ -1142,7 +1142,7 @@ export default function DataCollectionPage() {
|
||||
{schedulerSaveBusy ? <LoaderCircleIcon className="size-4 animate-spin mr-2" /> : null}
|
||||
Сохранить настройки планировщика
|
||||
</Button>
|
||||
<div className="text-xs text-muted-foreground space-y-0.5 pt-1 border-t border-border/60">
|
||||
<div className="text-xs text-muted-foreground flex flex-col gap-0.5 pt-1 border-t border-border/60">
|
||||
<p>
|
||||
Трафик — последний сбор:{" "}
|
||||
{trafficCollector?.lastCollectedAt
|
||||
|
||||
Reference in New Issue
Block a user