"use client"; import React, { FC, useMemo, useState } from "react"; import { closestCorners, defaultDropAnimation, DndContext, DragOverEvent, DragOverlay, DragStartEvent, KeyboardSensor, PointerSensor, useSensor, useSensors, } from "@dnd-kit/core"; import { sortableKeyboardCoordinates } from "@dnd-kit/sortable"; import { LexoRank } from "lexorank"; import { throttle } from "lodash"; import { Group } from "@mantine/core"; import DealCard from "@/app/deals/components/DealCard/DealCard"; import StatusColumn from "@/app/deals/components/StatusColumn/StatusColumn"; import { useStatusesContext } from "@/app/deals/contexts/StatusesContext"; import { DealSchema } from "@/types/DealSchema"; import { getNewLexorank, sortByLexorank } from "@/utils/lexorank"; type Props = { onDealDragEnd: ( dealId: number, statusId: number, lexorank?: string ) => void; }; const StatusColumnsDnd: FC = props => { const { statuses, deals, setDeals } = useStatusesContext(); const [activeDeal, setActiveDeal] = useState(null); const throttledSetDeals = useMemo( () => throttle(setDeals, 200), [setDeals] ); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }) ); const handleDragStart = ({ active }: DragStartEvent) => { setActiveDeal( deals.find(deal => deal.id === (active.id as number)) ?? null ); }; const getStatusByDealId = (dealId: number) => { const deal = deals.find(deal => deal.id === dealId); if (!deal) return; return statuses.find(status => status.id === deal.statusId); }; const handleDragOver = ({ active, over }: DragOverEvent) => { if (!over?.id) return; const activeDealId = Number(active.id); const activeStatusId = getStatusByDealId(activeDealId)?.id; if (!activeStatusId) return; const { overStatusId, newLexorank } = getDropTarget( over.id, activeDealId, activeStatusId ); if (!overStatusId) return; throttledSetDeals(deals => deals.map(deal => deal.id === activeDealId ? { ...deal, statusId: overStatusId, rank: newLexorank || deal.rank, } : deal ) ); }; const getDropTarget = ( overId: string | number, activeDealId: number, activeStatusId: number ) => { if (typeof overId === "string") { return { overStatusId: Number(overId.replace(/-status$/, "")), newLexorank: undefined, }; } const overDealId = Number(overId); const overStatusId = getStatusByDealId(overDealId)?.id; if (!overStatusId || activeDealId === overDealId) { return { overStatusId: undefined, newLexorank: undefined }; } const statusDeals = sortByLexorank( deals.filter(deal => deal.statusId === overStatusId) ); const overDealIndex = statusDeals.findIndex( deal => deal.id === overDealId ); let newLexorank; if (activeStatusId === overStatusId) { newLexorank = getNewRankForSameStatus( statusDeals, overDealIndex, activeDealId ); } else { newLexorank = getNewRankForAnotherStatus( statusDeals, overDealIndex ); } return { overStatusId, newLexorank }; }; const getNewRankForSameStatus = ( statusDeals: DealSchema[], overDealIndex: number, activeDealId: number ) => { const activeDealIndex = statusDeals.findIndex( deal => deal.id === activeDealId ); const [leftIndex, rightIndex] = overDealIndex < activeDealIndex ? [overDealIndex - 1, overDealIndex] : [overDealIndex, overDealIndex + 1]; const leftLexorank = leftIndex >= 0 ? LexoRank.parse(deals[leftIndex].rank) : null; const rightLexorank = rightIndex < deals.length ? LexoRank.parse(deals[rightIndex].rank) : null; return getNewLexorank(leftLexorank, rightLexorank).toString(); }; const getNewRankForAnotherStatus = ( statusDeals: DealSchema[], overDealIndex: number ) => { const leftLexorank = overDealIndex > 0 ? LexoRank.parse(statusDeals[overDealIndex - 1].rank) : null; const rightLexorank = LexoRank.parse(statusDeals[overDealIndex].rank); return getNewLexorank(leftLexorank, rightLexorank).toString(); }; const handleDragEnd = ({ active, over }: DragOverEvent) => { setActiveDeal(null); if (!over?.id) return; const activeDealId = Number(active.id); const activeStatusId = getStatusByDealId(activeDealId)?.id; if (!activeStatusId) return; const { overStatusId, newLexorank } = getDropTarget( over.id, activeDealId, activeStatusId ); if (!overStatusId) return; props.onDealDragEnd(activeDealId, overStatusId, newLexorank); }; return ( {statuses.map(status => ( deal.statusId === status.id )} /> ))} {activeDeal ? : null} ); }; export default StatusColumnsDnd;