diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index 2629a63..0484a51 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -28,6 +28,9 @@ export default tseslint.config( }, { files: ['src/components/reui/**/*.{ts,tsx}'], + linterOptions: { + reportUnusedDisableDirectives: 'off', + }, rules: { '@typescript-eslint/no-unused-vars': 'off', 'react-hooks/exhaustive-deps': 'off', diff --git a/apps/web/package.json b/apps/web/package.json index e2dea18..b9961b7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -16,6 +16,7 @@ }, "dependencies": { "@base-ui/react": "^1.0.0", + "@date-fns/tz": "^1.5.0", "@dnd-kit/core": "^6.3.1", "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", @@ -26,8 +27,8 @@ "@tanstack/react-query-devtools": "^5.90.2", "@tanstack/react-router": "^1.130.2", "@tanstack/react-router-devtools": "^1.130.2", - "@tanstack/react-table": "^8.21.3", - "@tanstack/react-virtual": "^3.14.4", + "@tanstack/react-table": "^9.1.2", + "@tanstack/react-virtual": "^3.14.10", "class-variance-authority": "^0.7.1", "cmdk": "^1.1.1", "date-fns": "^4.4.0", diff --git a/apps/web/src/components/access/access-api-keys-card.tsx b/apps/web/src/components/access/access-api-keys-card.tsx index ba15238..b71bc91 100644 --- a/apps/web/src/components/access/access-api-keys-card.tsx +++ b/apps/web/src/components/access/access-api-keys-card.tsx @@ -2,9 +2,7 @@ import { useState } from 'react' import { Plus, RefreshCw } from 'lucide-react' import { Button } from '@evobgp/ui/components/button' - import { AccessApiKeysGrid } from '@/components/access/access-api-keys-grid' -import { FrameDataGrid } from '@/components/reui-kit' import { ApiKeyCreateDialog } from '@/components/access/api-key-create-dialog' import { ApiKeyTokenDialog } from '@/components/access/api-key-token-dialog' import { QueryState } from '@/components/query-state' @@ -47,46 +45,38 @@ export function AccessApiKeysCard({ return ( <> - - - - - } + } + onRetry={onRetry} > - } - onRetry={onRetry} - > - {(data) => ( - revoke.mutate(id)} - rotatePending={rotate.isPending} - revokePending={revoke.isPending} - /> - )} - - + {(data) => ( + revoke.mutate(id)} + rotatePending={rotate.isPending} + revokePending={revoke.isPending} + actions={ + <> + + + + } + /> + )} + void rotatePending?: boolean revokePending?: boolean + actions?: ReactNode }) { - const columns = useMemo[]>( + const [filterQuery, setFilterQuery] = useState(() => + createTextFilterQuery('search'), + ) + + const columns = useMemo[]>( () => [ { accessorKey: 'name', header: ({ column }) => , cell: ({ row }) => ( - ), @@ -135,24 +147,24 @@ export function AccessApiKeysGrid({ [onRevoke, onRotate, revokePending, rotatePending], ) - const { table, globalFilter, setGlobalFilter, filteredCount } = useClientDataGrid({ - data: items, - columns, - getSearchText: (row) => - `${row.name} ${row.role} ${row.prefix} ${row.revoked_at ? 'отозван' : 'активен'}`, - getRowId: (row) => row.id, - }) - return ( - setFilterQuery(createTextFilterQuery('search'))} + getFilterFieldValue={(row) => + `${row.name} ${row.role} ${row.prefix} ${row.revoked_at ? 'отозван' : 'активен'}` + } + columns={columns} + data={items} + getRowId={(row) => row.id} isLoading={isLoading} - emptyMessage="Нет ключей" - tableLayout={DATA_GRID_DENSE_LAYOUT} - searchValue={globalFilter} - onSearchChange={setGlobalFilter} - searchPlaceholder="Поиск API-ключей…" + primaryAction={actions} + pinLastColumn + emptyState={{ title: 'Нет ключей' }} /> ) } diff --git a/apps/web/src/components/analytics/analytics-card-shell.tsx b/apps/web/src/components/analytics/analytics-card-shell.tsx index 3c414c7..3870071 100644 --- a/apps/web/src/components/analytics/analytics-card-shell.tsx +++ b/apps/web/src/components/analytics/analytics-card-shell.tsx @@ -10,6 +10,10 @@ import { import { PanelCard } from '@/components/panel-card' +/** Chart Frame shell — chart-1 spacing, not Card. + * @see https://reui.io/preview/base/chart-1 + * @see https://reui.io/docs/components/base/frame + */ export function AnalyticsCardShell({ title, description, diff --git a/apps/web/src/components/blocks/data-grid-base-7/components/columns.tsx b/apps/web/src/components/blocks/data-grid-base-7/components/columns.tsx new file mode 100644 index 0000000..0cee6d9 --- /dev/null +++ b/apps/web/src/components/blocks/data-grid-base-7/components/columns.tsx @@ -0,0 +1,324 @@ +import { type ComponentProps } from "react" +import { Badge } from "@/components/reui/badge" +import { type DataGridFeatures } from "@/components/reui/data-grid/data-grid" +import { DataGridColumnHeader } from "@/components/reui/data-grid/data-grid-column-header" +import { type ColumnDef } from "@tanstack/react-table" + +import { cn } from "@evobgp/ui/lib/utils" +import { Button } from "@evobgp/ui/components/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@evobgp/ui/components/dropdown-menu" +import { type ModuleRecord, type ModuleStatus } from "./data" +import { CalendarDaysIcon, FileTextIcon, StarIcon, MoreHorizontalIcon, CopyIcon, ArchiveIcon } from "lucide-react" + +export type ModuleRowAction = "open" | "favorite" | "duplicate" | "archive" + +const moduleStatusVariant: Record< + ModuleStatus, + ComponentProps["variant"] +> = { + Planned: "info-outline", + Backlog: "outline", + "In Progress": "warning-outline", +} + +const moduleStatusDotClass: Record = { + Planned: "bg-sky-500 dark:bg-sky-400", + Backlog: "bg-muted-foreground/50", + "In Progress": "bg-amber-500 dark:bg-amber-400", +} + +function DotSeparator() { + return ( +