feat(admin): Control Center redesign, Explore dual-view и полный RU/EN i18n
Refs EventHub/EventHubFrontAdmin#32
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
SortingState,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ArrowDown, ArrowUp, ArrowUpDown } from 'lucide-react';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { TablePaginationBar } from '@/components/explore/TablePaginationBar';
|
||||
import { DEFAULT_TABLE_PAGE_SIZE } from '@/utils/tablePagination';
|
||||
|
||||
export interface ServerTableParams {
|
||||
offset?: number;
|
||||
limit?: number;
|
||||
sort?: string;
|
||||
order?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
interface DataTableProps<TData, TParams extends ServerTableParams> {
|
||||
columns: ColumnDef<TData, unknown>[];
|
||||
data: TData[];
|
||||
total?: number;
|
||||
isLoading?: boolean;
|
||||
tableKey: string;
|
||||
params: TParams;
|
||||
onParamsChange: (params: TParams) => void;
|
||||
emptyMessage?: string;
|
||||
}
|
||||
|
||||
export function DataTable<TData, TParams extends ServerTableParams>({
|
||||
columns,
|
||||
data,
|
||||
total,
|
||||
isLoading,
|
||||
tableKey,
|
||||
params,
|
||||
onParamsChange,
|
||||
emptyMessage,
|
||||
}: DataTableProps<TData, TParams>) {
|
||||
const { t } = useTranslation();
|
||||
const empty = emptyMessage ?? t('common.noData');
|
||||
const sorting: SortingState =
|
||||
params.sort && params.order ? [{ id: params.sort, desc: params.order === 'desc' }] : [];
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
pageCount: total != null ? Math.ceil(total / (params.limit || DEFAULT_TABLE_PAGE_SIZE)) : -1,
|
||||
state: {
|
||||
sorting,
|
||||
pagination: {
|
||||
pageIndex: Math.floor((params.offset || 0) / (params.limit || DEFAULT_TABLE_PAGE_SIZE)),
|
||||
pageSize: params.limit || DEFAULT_TABLE_PAGE_SIZE,
|
||||
},
|
||||
},
|
||||
manualPagination: true,
|
||||
manualSorting: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onSortingChange: (updater) => {
|
||||
const next = typeof updater === 'function' ? updater(sorting) : updater;
|
||||
const first = next[0];
|
||||
onParamsChange({
|
||||
...params,
|
||||
sort: first?.id ?? params.sort,
|
||||
order: first ? (first.desc ? 'desc' : 'asc') : params.order,
|
||||
offset: 0,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
const canSort = header.column.getCanSort();
|
||||
const sorted = header.column.getIsSorted();
|
||||
return (
|
||||
<TableHead key={header.id} style={{ width: header.getSize() || undefined }}>
|
||||
{header.isPlaceholder ? null : canSort ? (
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center gap-1 font-medium hover:text-foreground"
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{sorted === 'asc' ? (
|
||||
<ArrowUp className="h-3.5 w-3.5" />
|
||||
) : sorted === 'desc' ? (
|
||||
<ArrowDown className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<ArrowUpDown className="h-3.5 w-3.5 opacity-40" />
|
||||
)}
|
||||
</button>
|
||||
) : (
|
||||
flexRender(header.column.columnDef.header, header.getContext())
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
Array.from({ length: 5 }).map((_, i) => (
|
||||
<TableRow key={`sk-${i}`}>
|
||||
{columns.map((_, j) => (
|
||||
<TableCell key={j}>
|
||||
<Skeleton className="h-4 w-full" />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center text-muted-foreground">
|
||||
{empty}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<TablePaginationBar
|
||||
tableKey={tableKey}
|
||||
params={params}
|
||||
total={total}
|
||||
dataLength={data.length}
|
||||
onParamsChange={onParamsChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user