advanced table fixes

This commit is contained in:
estifanos
2026-07-27 08:52:13 +00:00
parent af85fca02a
commit b5d09d2aea
2 changed files with 75 additions and 50 deletions

View File

@@ -9,8 +9,10 @@ import {
Pagination,
Loader,
Center,
Paper,
Badge,
} from '@mantine/core';
import { IconRefresh, IconEye } from '@tabler/icons-react';
import { IconRefresh, IconEye, IconInbox } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
export interface AdvancedColumn<T> {
@@ -64,84 +66,106 @@ export function AdvancedTable<T extends { id?: string | number }>({
const { t } = useTranslation();
const [visible, setVisible] = useState<boolean[]>(columns.map((c) => c.enabled ?? true));
const toggleColumn = (i: number) =>
setVisible((prev) => prev.map((v, idx) => (idx === i ? !v : v)));
setVisible((prev) => {
if (prev[i] && prev.filter(Boolean).length === 1) return prev; // keep at least one column visible
return prev.map((v, idx) => (idx === i ? !v : v));
});
const shownColumns = columns.filter((_, i) => visible[i] ?? true);
return (
<div>
<Group justify="space-between" mb="sm">
<Text fw={600}>{tableName}</Text>
<Paper withBorder radius="md" p="md">
<Group justify="space-between" mb="md">
<Group gap="xs">
<Menu closeOnItemClick={false} shadow="md" position="bottom-end">
<Text fw={600}>{tableName}</Text>
<Badge variant="light" size="sm" radius="sm">
{itemCount}
</Badge>
</Group>
<Group gap="xs">
<Menu closeOnItemClick={false} shadow="md" position="bottom-end" width={220}>
<Menu.Target>
<Button variant="subtle" size="sm" leftSection={<IconEye size={16} />}>
<Button variant="default" size="sm" leftSection={<IconEye size={16} />}>
{t('common.view', 'View')}
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>{t('common.toggleColumns', 'Toggle columns')}</Menu.Label>
{columns.map((col, i) => (
<Menu.Item key={i} onClick={() => toggleColumn(i)}>
<Checkbox
label={col.header}
checked={visible[i] ?? true}
onChange={() => toggleColumn(i)}
disabled={visible.filter(Boolean).length === 1 && (visible[i] ?? true)}
readOnly
tabIndex={-1}
styles={{ input: { cursor: 'pointer' }, label: { cursor: 'pointer' } }}
/>
</Menu.Item>
))}
</Menu.Dropdown>
</Menu>
{refresh && (
<Button variant="subtle" size="sm" rightSection={<IconRefresh size={16} />} onClick={refresh}>
<Button
variant="default"
size="sm"
leftSection={<IconRefresh size={16} />}
onClick={refresh}
loading={isLoading}
>
{t('common.refresh', 'Refresh')}
</Button>
)}
</Group>
</Group>
<Table striped highlightOnHover>
<Table.Thead>
<Table.Tr>
{shownColumns.map((col, i) => (
<Table.Th key={i} style={{ width: col.size, textAlign: col.align }}>
{col.header}
</Table.Th>
))}
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{isLoading ? (
<Table.ScrollContainer minWidth={480}>
<Table striped highlightOnHover withTableBorder withColumnBorders verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Td colSpan={shownColumns.length}>
<Center py="xl">
<Loader size="sm" />
</Center>
</Table.Td>
{shownColumns.map((col, i) => (
<Table.Th key={i} style={{ width: col.size, textAlign: col.align }}>
{col.header}
</Table.Th>
))}
</Table.Tr>
) : data.length === 0 ? (
<Table.Tr>
<Table.Td colSpan={shownColumns.length}>
<Text c="dimmed" ta="center" py="xl">
{emptyText ?? t('common.noResult', 'No results')}
</Text>
</Table.Td>
</Table.Tr>
) : (
data.map((row, rowIndex) => (
<Table.Tr key={row.id ?? rowIndex}>
{shownColumns.map((col, i) => {
const value = getByPath(row, col.accessorKey);
return (
<Table.Td key={i} style={{ textAlign: col.align }}>
{col.cell ? col.cell({ row: { original: row }, value }) : (value as ReactNode) ?? '-'}
</Table.Td>
);
})}
</Table.Thead>
<Table.Tbody>
{isLoading ? (
<Table.Tr>
<Table.Td colSpan={shownColumns.length}>
<Center py="xl">
<Loader size="sm" />
</Center>
</Table.Td>
</Table.Tr>
))
)}
</Table.Tbody>
</Table>
) : data.length === 0 ? (
<Table.Tr>
<Table.Td colSpan={shownColumns.length}>
<Center py="xl">
<Group gap="xs" c="dimmed">
<IconInbox size={18} />
<Text c="dimmed">{emptyText ?? t('common.noResult', 'No results')}</Text>
</Group>
</Center>
</Table.Td>
</Table.Tr>
) : (
data.map((row, rowIndex) => (
<Table.Tr key={row.id ?? rowIndex}>
{shownColumns.map((col, i) => {
const value = getByPath(row, col.accessorKey);
return (
<Table.Td key={i} style={{ textAlign: col.align }}>
{col.cell ? col.cell({ row: { original: row }, value }) : (value as ReactNode) ?? '-'}
</Table.Td>
);
})}
</Table.Tr>
))
)}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
{itemCount > pageSize && (
<Group justify="flex-end" mt="md">
@@ -153,6 +177,6 @@ export function AdvancedTable<T extends { id?: string | number }>({
/>
</Group>
)}
</div>
</Paper>
);
}