chore: perauset customer project
This commit is contained in:
131
packages/components/src/data-table.tsx
Normal file
131
packages/components/src/data-table.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user