init
This commit is contained in:
182
frontend/src/components/IdeasTable/IdeasTable.tsx
Normal file
182
frontend/src/components/IdeasTable/IdeasTable.tsx
Normal file
@ -0,0 +1,182 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
useReactTable,
|
||||
getCoreRowModel,
|
||||
flexRender,
|
||||
} from '@tanstack/react-table';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Paper,
|
||||
TableSortLabel,
|
||||
Skeleton,
|
||||
Box,
|
||||
Typography,
|
||||
TablePagination,
|
||||
} from '@mui/material';
|
||||
import { Inbox } from '@mui/icons-material';
|
||||
import { useIdeasQuery, useDeleteIdea } from '../../hooks/useIdeas';
|
||||
import { useIdeasStore } from '../../store/ideas';
|
||||
import { createColumns } from './columns';
|
||||
|
||||
const SKELETON_COLUMNS_COUNT = 7;
|
||||
|
||||
export function IdeasTable() {
|
||||
const { data, isLoading, isError } = useIdeasQuery();
|
||||
const deleteIdea = useDeleteIdea();
|
||||
const { sorting, setSorting, pagination, setPage, setLimit } = useIdeasStore();
|
||||
|
||||
const columns = useMemo(
|
||||
() => createColumns((id) => deleteIdea.mutate(id)),
|
||||
[deleteIdea]
|
||||
);
|
||||
|
||||
const table = useReactTable({
|
||||
data: data?.data ?? [],
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
manualSorting: true,
|
||||
manualPagination: true,
|
||||
});
|
||||
|
||||
const handleSort = (columnId: string) => {
|
||||
setSorting(columnId);
|
||||
};
|
||||
|
||||
const handleChangePage = (_: unknown, newPage: number) => {
|
||||
setPage(newPage + 1);
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setLimit(parseInt(event.target.value, 10));
|
||||
};
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<Box sx={{ p: 4, textAlign: 'center' }}>
|
||||
<Typography color="error">Failed to load ideas</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
|
||||
<TableContainer>
|
||||
<Table stickyHeader size="small">
|
||||
<TableHead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableCell
|
||||
key={header.id}
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
backgroundColor: 'grey.100',
|
||||
width: header.getSize(),
|
||||
}}
|
||||
>
|
||||
{header.column.getCanSort() ? (
|
||||
<TableSortLabel
|
||||
active={sorting.sortBy === header.id}
|
||||
direction={
|
||||
sorting.sortBy === header.id
|
||||
? (sorting.sortOrder.toLowerCase() as 'asc' | 'desc')
|
||||
: 'asc'
|
||||
}
|
||||
onClick={() => handleSort(header.id)}
|
||||
>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableSortLabel>
|
||||
) : (
|
||||
flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
Array.from({ length: 5 }).map((_, index) => (
|
||||
<TableRow key={index}>
|
||||
{Array.from({ length: SKELETON_COLUMNS_COUNT }).map((_, colIndex) => (
|
||||
<TableCell key={colIndex}>
|
||||
<Skeleton variant="text" />
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : table.getRowModel().rows.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={SKELETON_COLUMNS_COUNT}>
|
||||
<Box
|
||||
sx={{
|
||||
py: 8,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
color: 'text.secondary',
|
||||
}}
|
||||
>
|
||||
<Inbox sx={{ fontSize: 48, mb: 2, opacity: 0.5 }} />
|
||||
<Typography variant="h6">No ideas yet</Typography>
|
||||
<Typography variant="body2">
|
||||
Create your first idea to get started
|
||||
</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow
|
||||
key={row.id}
|
||||
hover
|
||||
sx={{
|
||||
backgroundColor: row.original.color
|
||||
? `${row.original.color}15`
|
||||
: undefined,
|
||||
'&:hover': {
|
||||
backgroundColor: row.original.color
|
||||
? `${row.original.color}25`
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{data && (
|
||||
<TablePagination
|
||||
component="div"
|
||||
count={data.meta.total}
|
||||
page={pagination.page - 1}
|
||||
rowsPerPage={pagination.limit}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
rowsPerPageOptions={[10, 20, 50, 100]}
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user