add auth
This commit is contained in:
@ -1,9 +1,25 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import {
|
||||
useReactTable,
|
||||
getCoreRowModel,
|
||||
flexRender,
|
||||
} from '@tanstack/react-table';
|
||||
import {
|
||||
DndContext,
|
||||
closestCenter,
|
||||
KeyboardSensor,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragOverlay,
|
||||
} from '@dnd-kit/core';
|
||||
import type { DragEndEvent, DragStartEvent } from '@dnd-kit/core';
|
||||
import {
|
||||
SortableContext,
|
||||
sortableKeyboardCoordinates,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@ -19,18 +35,27 @@ import {
|
||||
TablePagination,
|
||||
} from '@mui/material';
|
||||
import { Inbox } from '@mui/icons-material';
|
||||
import { useIdeasQuery, useDeleteIdea } from '../../hooks/useIdeas';
|
||||
import {
|
||||
useIdeasQuery,
|
||||
useDeleteIdea,
|
||||
useReorderIdeas,
|
||||
} from '../../hooks/useIdeas';
|
||||
import { useIdeasStore } from '../../store/ideas';
|
||||
import { createColumns } from './columns';
|
||||
import { DraggableRow } from './DraggableRow';
|
||||
|
||||
const SKELETON_COLUMNS_COUNT = 7;
|
||||
const SKELETON_COLUMNS_COUNT = 8;
|
||||
|
||||
export function IdeasTable() {
|
||||
const { data, isLoading, isError } = useIdeasQuery();
|
||||
const deleteIdea = useDeleteIdea();
|
||||
const reorderIdeas = useReorderIdeas();
|
||||
const { sorting, setSorting, pagination, setPage, setLimit } =
|
||||
useIdeasStore();
|
||||
|
||||
// ID активно перетаскиваемого элемента
|
||||
const [activeId, setActiveId] = useState<string | null>(null);
|
||||
|
||||
const columns = useMemo(
|
||||
() => createColumns((id) => deleteIdea.mutate(id)),
|
||||
[deleteIdea],
|
||||
@ -43,8 +68,49 @@ export function IdeasTable() {
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
manualSorting: true,
|
||||
manualPagination: true,
|
||||
getRowId: (row) => row.id,
|
||||
});
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
distance: 8,
|
||||
},
|
||||
}),
|
||||
useSensor(KeyboardSensor, {
|
||||
coordinateGetter: sortableKeyboardCoordinates,
|
||||
}),
|
||||
);
|
||||
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
setActiveId(event.active.id as string);
|
||||
};
|
||||
|
||||
const handleDragEnd = (event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
setActiveId(null);
|
||||
|
||||
if (over && active.id !== over.id && data?.data) {
|
||||
const oldIndex = data.data.findIndex((item) => item.id === active.id);
|
||||
const newIndex = data.data.findIndex((item) => item.id === over.id);
|
||||
|
||||
if (oldIndex !== -1 && newIndex !== -1) {
|
||||
// Создаём новый порядок
|
||||
const items = [...data.data];
|
||||
const [movedItem] = items.splice(oldIndex, 1);
|
||||
items.splice(newIndex, 0, movedItem);
|
||||
|
||||
// Отправляем на сервер новый порядок
|
||||
const reorderItems = items.map((item, index) => ({
|
||||
id: item.id,
|
||||
order: index,
|
||||
}));
|
||||
|
||||
reorderIdeas.mutate(reorderItems);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSort = (columnId: string) => {
|
||||
setSorting(columnId);
|
||||
};
|
||||
@ -67,101 +133,131 @@ export function IdeasTable() {
|
||||
);
|
||||
}
|
||||
|
||||
const rows = table.getRowModel().rows;
|
||||
const rowIds = rows.map((row) => row.original.id);
|
||||
const activeRow = activeId
|
||||
? rows.find((row) => row.original.id === activeId)
|
||||
: null;
|
||||
|
||||
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(
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
modifiers={[restrictToVerticalAxis]}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<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(),
|
||||
)}
|
||||
</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>
|
||||
),
|
||||
)}
|
||||
)
|
||||
)}
|
||||
</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">Идей пока нет</Typography>
|
||||
<Typography variant="body2">
|
||||
Создайте первую идею, чтобы начать
|
||||
</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,
|
||||
},
|
||||
}}
|
||||
))}
|
||||
</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>
|
||||
))
|
||||
) : 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">Идей пока нет</Typography>
|
||||
<Typography variant="body2">
|
||||
Создайте первую идею, чтобы начать
|
||||
</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
<SortableContext
|
||||
items={rowIds}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{rows.map((row) => (
|
||||
<DraggableRow key={row.id} row={row} />
|
||||
))}
|
||||
</SortableContext>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<DragOverlay dropAnimation={null}>
|
||||
{activeRow ? (
|
||||
<Table
|
||||
size="small"
|
||||
sx={{
|
||||
backgroundColor: 'background.paper',
|
||||
boxShadow: 6,
|
||||
borderRadius: 1,
|
||||
'& td': {
|
||||
backgroundColor: activeRow.original.color
|
||||
? `${activeRow.original.color}30`
|
||||
: 'action.selected',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
{activeRow.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
sx={{ width: cell.column.getSize() }}
|
||||
>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
@ -169,11 +265,11 @@ export function IdeasTable() {
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
</DndContext>
|
||||
{data && (
|
||||
<TablePagination
|
||||
component="div"
|
||||
|
||||
Reference in New Issue
Block a user