This commit is contained in:
2025-12-29 16:58:56 +03:00
commit 524f3ebf23
62 changed files with 30925 additions and 0 deletions

View 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>
);
}