refactor: straightened logic, replaces throttle with mantine debounced
This commit is contained in:
221
src/app/deals/hooks/useDealsAndStatusesDnd.ts
Normal file
221
src/app/deals/hooks/useDealsAndStatusesDnd.ts
Normal file
@ -0,0 +1,221 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { DragOverEvent, DragStartEvent, Over } from "@dnd-kit/core";
|
||||
import { useDebouncedCallback } from "@mantine/hooks";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import useGetNewRank from "@/app/deals/hooks/useGetNewRank";
|
||||
import { getStatusId, isStatusId } from "@/app/deals/utils/statusId";
|
||||
import { DealSchema, StatusSchema } from "@/client";
|
||||
import { sortByLexorank } from "@/utils/lexorank";
|
||||
|
||||
type Props = {
|
||||
onDealDragEnd: (
|
||||
dealId: number,
|
||||
statusId: number,
|
||||
lexorank?: string
|
||||
) => void;
|
||||
onStatusDragEnd: (statusId: number, lexorank: string) => void;
|
||||
};
|
||||
|
||||
const useDealsAndStatusesDnd = (props: Props) => {
|
||||
const [activeDeal, setActiveDeal] = useState<DealSchema | null>(null);
|
||||
const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null);
|
||||
const { statuses, deals, setDeals, setStatuses } = useStatusesContext();
|
||||
const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]);
|
||||
|
||||
const {
|
||||
getNewRankForSameStatus,
|
||||
getNewRankForAnotherStatus,
|
||||
getNewStatusRank,
|
||||
} = useGetNewRank();
|
||||
|
||||
const debouncedSetStatuses = useDebouncedCallback(setStatuses, 200);
|
||||
const debouncedSetDeals = useDebouncedCallback(setDeals, 200);
|
||||
|
||||
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) return;
|
||||
const activeId = active.id as string | number;
|
||||
|
||||
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||
handleColumnDragOver(activeId, over);
|
||||
return;
|
||||
}
|
||||
handleDealDragOver(activeId, over);
|
||||
};
|
||||
|
||||
const handleDealDragOver = (activeId: string | number, over: Over) => {
|
||||
const activeDealId = Number(activeId);
|
||||
const activeStatusId = getStatusByDealId(activeDealId)?.id;
|
||||
if (!activeStatusId) return;
|
||||
|
||||
const { overStatusId, newLexorank } = getDropTarget(
|
||||
over.id,
|
||||
activeDealId,
|
||||
activeStatusId
|
||||
);
|
||||
if (!overStatusId) return;
|
||||
|
||||
debouncedSetDeals(deals =>
|
||||
deals.map(deal =>
|
||||
deal.id === activeDealId
|
||||
? {
|
||||
...deal,
|
||||
statusId: overStatusId,
|
||||
lexorank: newLexorank || deal.lexorank,
|
||||
}
|
||||
: deal
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleColumnDragOver = (activeId: string, over: Over) => {
|
||||
const activeStatusId = getStatusId(activeId);
|
||||
let overStatusId: number;
|
||||
|
||||
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||
overStatusId = getStatusId(over.id);
|
||||
} else {
|
||||
const deal = deals.find(deal => deal.id === over.id);
|
||||
if (!deal) return;
|
||||
overStatusId = deal.statusId;
|
||||
}
|
||||
|
||||
if (!overStatusId || activeStatusId === overStatusId) return;
|
||||
|
||||
const newRank = getNewStatusRank(activeStatusId, overStatusId);
|
||||
if (!newRank) return;
|
||||
|
||||
debouncedSetStatuses(statuses =>
|
||||
statuses.map(status =>
|
||||
status.id === activeStatusId
|
||||
? { ...status, lexorank: newRank }
|
||||
: status
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const getDropTarget = (
|
||||
overId: string | number,
|
||||
activeDealId: number,
|
||||
activeStatusId: number,
|
||||
isOnDragEnd: boolean = false
|
||||
) => {
|
||||
if (typeof overId === "string") {
|
||||
return {
|
||||
overStatusId: getStatusId(overId),
|
||||
newLexorank: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const overDealId = Number(overId);
|
||||
const overStatusId = getStatusByDealId(overDealId)?.id;
|
||||
|
||||
if (!overStatusId || (!isOnDragEnd && activeDealId === overDealId)) {
|
||||
return { overStatusId: undefined, newLexorank: undefined };
|
||||
}
|
||||
|
||||
const statusDeals = sortByLexorank(
|
||||
deals.filter(deal => deal.statusId === overStatusId)
|
||||
);
|
||||
const overDealIndex = statusDeals.findIndex(
|
||||
deal => deal.id === overDealId
|
||||
);
|
||||
|
||||
if (activeStatusId === overStatusId) {
|
||||
const newLexorank = getNewRankForSameStatus(
|
||||
statusDeals,
|
||||
overDealIndex,
|
||||
activeDealId
|
||||
);
|
||||
return { overStatusId, newLexorank };
|
||||
}
|
||||
|
||||
const newLexorank = getNewRankForAnotherStatus(
|
||||
statusDeals,
|
||||
overDealIndex
|
||||
);
|
||||
return { overStatusId, newLexorank };
|
||||
};
|
||||
|
||||
const handleDragEnd = ({ active, over }: DragOverEvent) => {
|
||||
setActiveDeal(null);
|
||||
setActiveStatus(null);
|
||||
if (!over) return;
|
||||
|
||||
const activeId: string | number = active.id;
|
||||
|
||||
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||
handleStatusColumnDragEnd(activeId, over);
|
||||
return;
|
||||
}
|
||||
handleDealDragEnd(activeId, over);
|
||||
};
|
||||
|
||||
const handleStatusColumnDragEnd = (activeId: string, over: Over) => {
|
||||
const activeStatusId = getStatusId(activeId);
|
||||
let overStatusId: number;
|
||||
|
||||
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||
overStatusId = getStatusId(over.id);
|
||||
} else {
|
||||
const deal = deals.find(deal => deal.statusId === over.id);
|
||||
if (!deal) return;
|
||||
overStatusId = deal.statusId;
|
||||
}
|
||||
|
||||
if (!overStatusId) return;
|
||||
|
||||
const newRank = getNewStatusRank(activeStatusId, overStatusId);
|
||||
if (!newRank) return;
|
||||
|
||||
props.onStatusDragEnd?.(activeStatusId, newRank);
|
||||
};
|
||||
|
||||
const handleDealDragEnd = (activeId: number | string, over: Over) => {
|
||||
const activeDealId = Number(activeId);
|
||||
const activeStatusId = getStatusByDealId(activeDealId)?.id;
|
||||
if (!activeStatusId) return;
|
||||
|
||||
const { overStatusId, newLexorank } = getDropTarget(
|
||||
over.id,
|
||||
activeDealId,
|
||||
activeStatusId,
|
||||
true
|
||||
);
|
||||
if (!overStatusId) return;
|
||||
|
||||
props.onDealDragEnd(activeDealId, overStatusId, newLexorank);
|
||||
};
|
||||
|
||||
const handleDragStart = ({ active }: DragStartEvent) => {
|
||||
const activeId = active.id as string | number;
|
||||
|
||||
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||
const statusId = getStatusId(activeId);
|
||||
setActiveStatus(
|
||||
statuses.find(status => status.id === statusId) ?? null
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setActiveDeal(
|
||||
deals.find(deal => deal.id === (activeId as number)) ?? null
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
sortedStatuses,
|
||||
handleDragStart,
|
||||
handleDragOver,
|
||||
handleDragEnd,
|
||||
activeStatus,
|
||||
activeDeal,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDealsAndStatusesDnd;
|
||||
Reference in New Issue
Block a user