Files
MikrotikManager/components/data-grids/data-collection-scheduler-data-grid.tsx
T
Denozordec d3a2d38b37
Docker images / prepare-release (push) Successful in 6s
Docker images / backend-image (push) Successful in 1m37s
Docker images / frontend-image (push) Successful in 1m50s
Docker images / updater-image (push) Successful in 44s
Docker images / notify-webhook (push) Has been skipped
Docker images / publish-release (push) Successful in 7s
fix(ui): заменить таблицы на карточки данных и улучшить функциональность поиска
2026-06-30 22:22:51 +07:00

220 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import { useMemo } from "react"
import {
type ColumnDef,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table"
import type { SchedulerJobStatusDto } from "@/lib/scheduler-settings"
import { FormToggle } from "@/components/form-kit"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell"
import {
DATA_GRID_CELL_PAD,
DATA_GRID_CELL_PAD_FIRST,
DATA_GRID_CELL_PAD_LAST,
} from "@/components/data-grids/shared/data-grid-layout"
import { cn } from "@/lib/utils"
import { RefreshCwIcon } from "lucide-react"
export interface SchedulerJobGridRow {
id: string
jobKey: string
label: string
description: string
fixedSchedule: boolean
enabled: boolean
intervalValue: string
intervalReadOnly: boolean
intervalDisabled: boolean
defaultInterval: number
job?: SchedulerJobStatusDto
onEnabledChange?: (enabled: boolean) => void
onIntervalChange: (value: string) => void
onRunNow: () => void
runNowLoading: boolean
saveBusy: boolean
}
interface DataCollectionSchedulerDataGridProps {
rows: SchedulerJobGridRow[]
}
function DataCollectionSchedulerDataGrid({ rows }: DataCollectionSchedulerDataGridProps) {
const columns = useMemo<ColumnDef<SchedulerJobGridRow>[]>(
() => [
{
id: "task",
accessorKey: "label",
header: () => <span className="text-xs font-medium text-muted-foreground">Задача</span>,
enableSorting: false,
cell: ({ row }) => (
<div className="align-top">
<span className="font-medium text-sm">{row.original.label}</span>
<p className="text-[11px] text-muted-foreground mt-0.5 leading-snug">
{row.original.description}
</p>
<p className="text-[11px] text-muted-foreground font-mono mt-1">{row.original.jobKey}</p>
</div>
),
meta: {
headerClassName: DATA_GRID_CELL_PAD_FIRST,
cellClassName: cn(DATA_GRID_CELL_PAD_FIRST, "align-top"),
},
},
{
id: "enabled",
accessorKey: "enabled",
header: () => (
<span className="text-xs font-medium text-muted-foreground text-center block">Вкл</span>
),
enableSorting: false,
cell: ({ row }) => {
const { fixedSchedule, enabled, saveBusy, onEnabledChange } = row.original
return (
<div className="text-center align-top">
<span className={fixedSchedule ? "inline-flex pointer-events-none opacity-50" : "inline-flex"}>
<FormToggle
checked={enabled}
disabled={fixedSchedule || saveBusy}
onChange={(v) => {
if (fixedSchedule || saveBusy) return
onEnabledChange?.(v)
}}
/>
</span>
</div>
)
},
size: 56,
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "align-top") },
},
{
id: "interval",
accessorKey: "intervalValue",
header: () => <span className="text-xs font-medium text-muted-foreground">Интервал (с)</span>,
enableSorting: false,
cell: ({ row }) => {
const r = row.original
return (
<div className="w-28 align-top">
<Input
value={r.intervalValue}
onChange={(e) => r.onIntervalChange(e.target.value)}
className="h-8 text-sm tabular-nums"
inputMode="numeric"
readOnly={r.intervalReadOnly}
disabled={r.intervalDisabled}
placeholder={String(r.defaultInterval)}
/>
</div>
)
},
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "align-top") },
},
{
id: "lastRun",
accessorFn: (row) => row.job?.lastFinishedAt ?? "",
header: () => <span className="text-xs font-medium text-muted-foreground">Последний прогон</span>,
enableSorting: false,
cell: ({ row }) => {
const j = row.original.job
return (
<div className="text-xs text-muted-foreground align-top">
{j?.lastFinishedAt ? new Date(j.lastFinishedAt).toLocaleString("ru-RU") : "—"}
{j?.lastDurationMs != null && (
<span className="block text-[11px]">{j.lastDurationMs} мс</span>
)}
</div>
)
},
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "align-top") },
},
{
id: "status",
accessorFn: (row) => row.job?.lastStatus ?? "",
header: () => <span className="text-xs font-medium text-muted-foreground">Статус</span>,
enableSorting: false,
cell: ({ row }) => {
const j = row.original.job
return (
<div className="flex flex-wrap items-center gap-1.5 align-top">
{j?.running ? (
<Badge variant="secondary" className="text-[10px]">
выполняется
</Badge>
) : null}
{j?.lastStatus ? (
<Badge
variant="outline"
className={cn(
"text-[10px]",
j.lastStatus === "ok" && "border-emerald-500/40 text-emerald-700 dark:text-emerald-400",
j.lastStatus === "error" && "border-destructive/50 text-destructive",
)}
>
{j.lastStatus}
</Badge>
) : null}
{j?.lastError ? (
<span className="text-[10px] text-destructive max-w-[200px] truncate block" title={j.lastError}>
{j.lastError}
</span>
) : null}
</div>
)
},
meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "align-top") },
},
{
id: "runNow",
header: () => (
<span className="text-xs font-medium text-muted-foreground text-right block">Сейчас</span>
),
enableSorting: false,
cell: ({ row }) => {
const r = row.original
return (
<div className="text-right align-top">
<Button
size="sm"
variant="outline"
className="h-8"
disabled={r.job?.running || r.runNowLoading}
onClick={r.onRunNow}
>
<RefreshCwIcon className={cn("size-3.5", r.runNowLoading && "animate-spin")} />
</Button>
</div>
)
},
meta: {
headerClassName: DATA_GRID_CELL_PAD_LAST,
cellClassName: cn(DATA_GRID_CELL_PAD_LAST, "align-top"),
},
},
],
[],
)
const table = useReactTable({
data: rows,
columns,
getCoreRowModel: getCoreRowModel(),
getRowId: (row) => row.id,
})
return (
<DataGridShell
table={table}
recordCount={rows.length}
tableClassNames={{ bodyRow: "group/row hover:bg-muted/40 text-sm" }}
/>
)
}
export { DataCollectionSchedulerDataGrid, type DataCollectionSchedulerDataGridProps }