Files
2026-07-11 08:54:43 +02:00

132 lines
3.3 KiB
TypeScript

import { Table, Text, Card, Stack, Group, Box } from '@pikku/mantine/core'
import { useMediaQuery } from '@mantine/hooks'
import { type ReactNode } from 'react'
import { asI18n, type I18nNode } from '@pikku/react'
export interface Column<T> {
key: string
label: I18nNode
render: (row: T) => ReactNode
}
interface DataTableProps<T> {
data: T[]
columns: Column<T>[]
emptyMessage?: string
rowKey: (row: T) => string
}
export function DataTable<T>({
data,
columns,
emptyMessage = 'No data found.',
rowKey,
}: DataTableProps<T>) {
const isMobile = useMediaQuery('(max-width: 768px)')
if (data.length === 0) {
return (
<Card
padding="xl"
radius="md"
style={{
backgroundColor: '#ffffff',
boxShadow:
'0 2px 12px rgba(61, 43, 31, 0.08), 0 1px 4px rgba(61, 43, 31, 0.04)',
}}
>
<Text size="sm" c="dimmed" ta="center">
{asI18n(emptyMessage)}
</Text>
</Card>
)
}
if (isMobile) {
return (
<Stack gap="sm">
{data.map((row) => (
<Card
key={rowKey(row)}
padding="md"
radius="md"
style={{
backgroundColor: '#ffffff',
boxShadow:
'0 2px 12px rgba(61, 43, 31, 0.08), 0 1px 4px rgba(61, 43, 31, 0.04)',
}}
>
<Stack gap="xs">
{columns.map((col) => (
<Group key={col.key} justify="space-between" wrap="nowrap" gap="sm">
<Text size="xs" c="dimmed" fw={600} style={{ minWidth: 80 }}>
{col.label}
</Text>
<Box style={{ textAlign: 'right' }}>{col.render(row)}</Box>
</Group>
))}
</Stack>
</Card>
))}
</Stack>
)
}
return (
<Card
padding={0}
radius="md"
style={{
backgroundColor: '#ffffff',
boxShadow:
'0 2px 12px rgba(61, 43, 31, 0.08), 0 1px 4px rgba(61, 43, 31, 0.04)',
overflow: 'hidden',
}}
>
<Table
highlightOnHover
horizontalSpacing="lg"
verticalSpacing="sm"
styles={{
thead: {
backgroundColor: '#f8f6f2',
borderBottom: '2px solid rgba(61, 43, 31, 0.1)',
},
th: {
padding: '12px 16px',
fontSize: '11px',
fontWeight: 700,
letterSpacing: '0.05em',
textTransform: 'uppercase' as const,
color: '#8b7d6b',
},
td: {
padding: '14px 16px',
borderBottom: '1px solid rgba(61, 43, 31, 0.06)',
},
tr: {
transition: 'background-color 0.15s ease',
},
}}
>
<Table.Thead>
<Table.Tr>
{columns.map((col) => (
<Table.Th key={col.key}>{col.label}</Table.Th>
))}
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{data.map((row) => (
<Table.Tr key={rowKey(row)}>
{columns.map((col) => (
<Table.Td key={col.key}>{col.render(row)}</Table.Td>
))}
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Card>
)
}