feat: displaying and sorting groups of deals
This commit is contained in:
@ -1,11 +1,13 @@
|
||||
.create-button {
|
||||
cursor: pointer;
|
||||
min-height: max-content;
|
||||
|
||||
border: 1px dashed;
|
||||
@mixin light {
|
||||
background-color: var(--color-light-white-blue);
|
||||
border-color: lightblue;
|
||||
}
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-7);
|
||||
border-color: var(--mantine-color-dark-5);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,27 @@
|
||||
|
||||
.container {
|
||||
padding: 0;
|
||||
border: 1px dashed;
|
||||
@mixin light {
|
||||
background-color: var(--color-light-white-blue);
|
||||
border-color: lightblue;
|
||||
}
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-7);
|
||||
border-color: var(--mantine-color-dark-5);
|
||||
}
|
||||
}
|
||||
|
||||
.container-in-group {
|
||||
padding: 0;
|
||||
border: 1px dashed;
|
||||
@mixin light {
|
||||
background-color: var(--color-light-aqua);
|
||||
border-color: lightblue;
|
||||
}
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-6);
|
||||
border-color: var(--mantine-color-dark-5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,9 +8,10 @@ import styles from "./DealCard.module.css";
|
||||
|
||||
type Props = {
|
||||
deal: DealSchema;
|
||||
isInGroup?: boolean;
|
||||
};
|
||||
|
||||
const DealCard = ({ deal }: Props) => {
|
||||
const DealCard = ({ deal, isInGroup = false }: Props) => {
|
||||
const { selectedProject, modulesSet } = useProjectsContext();
|
||||
const { dealsCrud, refetchDeals } = useDealsContext();
|
||||
const { openDrawer } = useDrawersContext();
|
||||
@ -31,7 +32,9 @@ const DealCard = ({ deal }: Props) => {
|
||||
return (
|
||||
<Card
|
||||
onClick={onClick}
|
||||
className={styles.container}>
|
||||
className={
|
||||
isInGroup ? styles["container-in-group"] : styles.container
|
||||
}>
|
||||
<Group
|
||||
justify={"space-between"}
|
||||
wrap={"nowrap"}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import React, { FC, useMemo } from "react";
|
||||
import { Box } from "@mantine/core";
|
||||
import DealCard from "@/app/deals/components/shared/DealCard/DealCard";
|
||||
import SortableItem from "@/components/dnd/SortableItem";
|
||||
import { DealSchema } from "@/lib/client";
|
||||
|
||||
type Props = {
|
||||
deal: DealSchema;
|
||||
};
|
||||
|
||||
const DealContainer: FC<Props> = ({ deal }) => {
|
||||
const dealBody = useMemo(() => <DealCard deal={deal} />, [deal]);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<SortableItem
|
||||
dragHandleStyle={{ cursor: "pointer" }}
|
||||
id={deal.id}
|
||||
renderItem={() => dealBody}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default DealContainer;
|
||||
@ -0,0 +1,12 @@
|
||||
|
||||
.group-container {
|
||||
border: 1px dashed;
|
||||
@mixin light {
|
||||
background-color: var(--color-light-white-blue);
|
||||
border-color: lightblue;
|
||||
}
|
||||
@mixin dark {
|
||||
background-color: var(--mantine-color-dark-7);
|
||||
border-color: var(--mantine-color-dark-5);
|
||||
}
|
||||
}
|
||||
30
src/app/deals/components/shared/DealsGroup/DealsGroup.tsx
Normal file
30
src/app/deals/components/shared/DealsGroup/DealsGroup.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { FC } from "react";
|
||||
import { Stack, Text } from "@mantine/core";
|
||||
import DealCard from "@/app/deals/components/shared/DealCard/DealCard";
|
||||
import GroupWithDealsSchema from "@/types/GroupWithDealsSchema";
|
||||
import styles from "./DealsGroup.module.css";
|
||||
|
||||
type Props = {
|
||||
group: GroupWithDealsSchema;
|
||||
};
|
||||
|
||||
const DealsGroup: FC<Props> = ({ group }) => {
|
||||
return (
|
||||
<Stack
|
||||
className={styles["group-container"]}
|
||||
gap={"xs"}
|
||||
bdrs={"lg"}
|
||||
p={"xs"}>
|
||||
<Text mx={"xs"}>{group.name}</Text>
|
||||
{group.items.map(deal => (
|
||||
<DealCard
|
||||
deal={deal}
|
||||
isInGroup
|
||||
key={deal.id}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default DealsGroup;
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import React, { FC, ReactNode } from "react";
|
||||
import DealCard from "@/app/deals/components/shared/DealCard/DealCard";
|
||||
import DealContainer from "@/app/deals/components/shared/DealContainer/DealContainer";
|
||||
import DealsGroup from "@/app/deals/components/shared/DealsGroup/DealsGroup";
|
||||
import StatusColumnHeader from "@/app/deals/components/shared/StatusColumnHeader/StatusColumnHeader";
|
||||
import StatusColumnWrapper from "@/app/deals/components/shared/StatusColumnWrapper/StatusColumnWrapper";
|
||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||
@ -11,37 +11,36 @@ import useDealsAndStatusesDnd from "@/app/deals/hooks/useDealsAndStatusesDnd";
|
||||
import FunnelDnd from "@/components/dnd/FunnelDnd/FunnelDnd";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
import { DealSchema, StatusSchema } from "@/lib/client";
|
||||
|
||||
import GroupWithDealsSchema from "@/types/GroupWithDealsSchema";
|
||||
import { sortByLexorank } from "@/utils/lexorank/sort";
|
||||
|
||||
const Funnel: FC = () => {
|
||||
const { selectedBoard } = useBoardsContext();
|
||||
const { deals } = useDealsContext();
|
||||
const { dealsWithoutGroup, groupsWithDeals } = useDealsContext();
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
const {
|
||||
sortedStatuses,
|
||||
handleDragStart,
|
||||
handleDragOver,
|
||||
handleDragEnd,
|
||||
activeStatus,
|
||||
activeDeal,
|
||||
swiperRef,
|
||||
} = useDealsAndStatusesDnd();
|
||||
const { sortedStatuses, handleDragOver, handleDragEnd, swiperRef } =
|
||||
useDealsAndStatusesDnd();
|
||||
|
||||
return (
|
||||
<FunnelDnd
|
||||
<FunnelDnd<StatusSchema, DealSchema, GroupWithDealsSchema>
|
||||
containers={sortedStatuses}
|
||||
items={deals}
|
||||
onDragStart={handleDragStart}
|
||||
itemsAndGroups={sortByLexorank([
|
||||
...dealsWithoutGroup,
|
||||
...groupsWithDeals,
|
||||
])}
|
||||
onDragOver={handleDragOver}
|
||||
onDragEnd={handleDragEnd}
|
||||
swiperRef={swiperRef}
|
||||
getContainerId={(status: StatusSchema) => `${status.id}-status`}
|
||||
getItemsByContainer={(status: StatusSchema, items: DealSchema[]) =>
|
||||
sortByLexorank(
|
||||
items.filter(deal => deal.status.id === status.id)
|
||||
)
|
||||
getItemsByContainer={(status: StatusSchema) =>
|
||||
sortByLexorank([
|
||||
...dealsWithoutGroup.filter(
|
||||
deal => deal.status.id === status.id
|
||||
),
|
||||
...groupsWithDeals.filter(
|
||||
group => group.items[0].status.id === status.id
|
||||
),
|
||||
])
|
||||
}
|
||||
renderContainer={(
|
||||
status: StatusSchema,
|
||||
@ -59,25 +58,28 @@ const Funnel: FC = () => {
|
||||
renderContainerHeader={status => (
|
||||
<StatusColumnHeader
|
||||
status={status}
|
||||
isDragging={activeStatus?.id === status.id}
|
||||
isDragging={false}
|
||||
/>
|
||||
)}
|
||||
renderItem={(deal: DealSchema) => (
|
||||
<DealContainer
|
||||
<DealCard
|
||||
key={deal.id}
|
||||
deal={deal}
|
||||
/>
|
||||
)}
|
||||
activeContainer={activeStatus}
|
||||
activeItem={activeDeal}
|
||||
renderItemOverlay={(deal: DealSchema) => <DealCard deal={deal} />}
|
||||
renderGroup={(group: GroupWithDealsSchema) => (
|
||||
<DealsGroup
|
||||
key={`${group.id}group`}
|
||||
group={group}
|
||||
/>
|
||||
)}
|
||||
renderContainerOverlay={(status: StatusSchema, children) => (
|
||||
<StatusColumnWrapper
|
||||
status={status}
|
||||
renderHeader={() => (
|
||||
<StatusColumnHeader
|
||||
status={status}
|
||||
isDragging={activeStatus?.id === status.id}
|
||||
isDragging
|
||||
/>
|
||||
)}>
|
||||
{children}
|
||||
|
||||
@ -3,18 +3,24 @@
|
||||
import React from "react";
|
||||
import { UseFormReturnType } from "@mantine/form";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import useDealsAndGroups from "@/app/deals/hooks/useDealsAndGroups";
|
||||
import { DealsFiltersForm } from "@/app/deals/hooks/useDealsFilters";
|
||||
import useDealGroupCrud, { GroupsCrud } from "@/hooks/cruds/useDealGroupCrud";
|
||||
import { DealsCrud, useDealsCrud } from "@/hooks/cruds/useDealsCrud";
|
||||
import useDealsList from "@/hooks/lists/useDealsList";
|
||||
import { SortingForm } from "@/hooks/utils/useSorting";
|
||||
import { DealSchema, PaginationInfoSchema } from "@/lib/client";
|
||||
import makeContext from "@/lib/contextFactory/contextFactory";
|
||||
import GroupWithDealsSchema from "@/types/GroupWithDealsSchema";
|
||||
|
||||
type DealsContextState = {
|
||||
deals: DealSchema[];
|
||||
setDeals: (deals: DealSchema[]) => void;
|
||||
dealsWithoutGroup: DealSchema[];
|
||||
groupsWithDeals: GroupWithDealsSchema[];
|
||||
refetchDeals: () => void;
|
||||
dealsCrud: DealsCrud;
|
||||
groupsCrud: GroupsCrud;
|
||||
paginationInfo?: PaginationInfoSchema;
|
||||
page: number;
|
||||
setPage: React.Dispatch<React.SetStateAction<number>>;
|
||||
@ -48,9 +54,17 @@ const useDealsContextState = ({
|
||||
statuses,
|
||||
});
|
||||
|
||||
const groupsCrud = useDealGroupCrud();
|
||||
|
||||
const { dealsWithoutGroup, groupsWithDeals } =
|
||||
useDealsAndGroups(dealsListObjects);
|
||||
|
||||
return {
|
||||
...dealsListObjects,
|
||||
dealsCrud,
|
||||
groupsCrud,
|
||||
dealsWithoutGroup,
|
||||
groupsWithDeals,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
44
src/app/deals/hooks/useDealsAndGroups.ts
Normal file
44
src/app/deals/hooks/useDealsAndGroups.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { useMemo } from "react";
|
||||
import { isNull } from "lodash";
|
||||
import { DealSchema } from "@/lib/client";
|
||||
import GroupWithDealsSchema from "@/types/GroupWithDealsSchema";
|
||||
import { sortByLexorank } from "@/utils/lexorank/sort";
|
||||
|
||||
type Props = {
|
||||
deals: DealSchema[];
|
||||
};
|
||||
|
||||
const useDealsAndGroups = ({ deals }: Props) => {
|
||||
const dealsWithoutGroup: DealSchema[] = useMemo(
|
||||
() => deals.filter(d => isNull(d.group)),
|
||||
[deals]
|
||||
);
|
||||
|
||||
const groupsWithDeals: GroupWithDealsSchema[] = useMemo(() => {
|
||||
const groupsWithDealMap = new Map<number, GroupWithDealsSchema>();
|
||||
|
||||
for (const deal of deals) {
|
||||
if (isNull(deal.group)) continue;
|
||||
|
||||
const groupData = groupsWithDealMap.get(deal.group.id);
|
||||
if (groupData) {
|
||||
groupData.items.push(deal);
|
||||
groupsWithDealMap.set(deal.group.id, groupData);
|
||||
} else {
|
||||
groupsWithDealMap.set(deal.group.id, {
|
||||
...deal.group,
|
||||
items: [deal],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return sortByLexorank(groupsWithDealMap.values().toArray());
|
||||
}, [deals]);
|
||||
|
||||
return {
|
||||
dealsWithoutGroup,
|
||||
groupsWithDeals,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDealsAndGroups;
|
||||
@ -1,32 +1,41 @@
|
||||
import { RefObject, useMemo, useRef, useState } from "react";
|
||||
import { DragOverEvent, DragStartEvent, Over } from "@dnd-kit/core";
|
||||
import { RefObject, useMemo, useRef } from "react";
|
||||
import { DragOverEvent, Over } from "@dnd-kit/core";
|
||||
import { SwiperRef } from "swiper/swiper-react";
|
||||
import { useDebouncedCallback } from "@mantine/hooks";
|
||||
import { useDealsContext } from "@/app/deals/contexts/DealsContext";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import useGetNewRank from "@/app/deals/hooks/useGetNewRank";
|
||||
import { getStatusId, isStatusId } from "@/app/deals/utils/statusId";
|
||||
import isItemGroup from "@/app/deals/utils/isItemGroup";
|
||||
import {
|
||||
getContainerId,
|
||||
isContainerId,
|
||||
} from "@/components/dnd/FunnelDnd/utils/columnId";
|
||||
import {
|
||||
getGroupId,
|
||||
isGroupId,
|
||||
} from "@/components/dnd/FunnelDnd/utils/groupId";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
import { DealSchema, StatusSchema } from "@/lib/client";
|
||||
|
||||
import { StatusSchema } from "@/lib/client";
|
||||
import { sortByLexorank } from "@/utils/lexorank/sort";
|
||||
|
||||
type ReturnType = {
|
||||
sortedStatuses: StatusSchema[];
|
||||
handleDragStart: ({ active }: DragStartEvent) => void;
|
||||
handleDragOver: ({ active, over }: DragOverEvent) => void;
|
||||
handleDragEnd: ({ active, over }: DragOverEvent) => void;
|
||||
activeStatus: StatusSchema | null;
|
||||
activeDeal: DealSchema | null;
|
||||
swiperRef: RefObject<SwiperRef | null>;
|
||||
};
|
||||
|
||||
const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
const swiperRef = useRef<SwiperRef>(null);
|
||||
const [activeDeal, setActiveDeal] = useState<DealSchema | null>(null);
|
||||
const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null);
|
||||
const { statuses, setStatuses, statusesCrud } = useStatusesContext();
|
||||
const { deals, setDeals, dealsCrud } = useDealsContext();
|
||||
const {
|
||||
deals,
|
||||
dealsWithoutGroup,
|
||||
groupsWithDeals,
|
||||
setDeals,
|
||||
dealsCrud,
|
||||
groupsCrud,
|
||||
} = useDealsContext();
|
||||
const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]);
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
@ -40,15 +49,29 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
const debouncedSetDeals = useDebouncedCallback(setDeals, 200);
|
||||
|
||||
const getStatusByDealId = (dealId: number) => {
|
||||
const deal = deals.find(deal => deal.id === dealId);
|
||||
const deal = dealsWithoutGroup.find(deal => deal.id === dealId);
|
||||
if (!deal) return;
|
||||
return statuses.find(status => status.id === deal.status.id);
|
||||
};
|
||||
|
||||
const getStatusByGroupId = (groupId: number) => {
|
||||
const group = groupsWithDeals.find(group => group.id === groupId);
|
||||
if (!group || group.items.length === 0) return;
|
||||
return statuses.find(status => status.id === group.items[0].status.id);
|
||||
};
|
||||
|
||||
const getStatusById = (statusId: number) => {
|
||||
return statuses.find(status => status.id === statusId);
|
||||
};
|
||||
|
||||
const getStatusDealsAndGroups = (statusId: number) =>
|
||||
sortByLexorank([
|
||||
...dealsWithoutGroup.filter(d => d.status.id === statusId),
|
||||
...groupsWithDeals.filter(
|
||||
g => g.items.length > 0 && g.items[0].status.id === statusId
|
||||
),
|
||||
]);
|
||||
|
||||
const swipeSliderDuringDrag = (activeId: number, over: Over) => {
|
||||
const activeStatus = getStatusByDealId(activeId);
|
||||
const swiperActiveStatus =
|
||||
@ -58,8 +81,8 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
const activeStatusLexorank = activeStatus?.lexorank;
|
||||
let overStatusLexorank: string | undefined;
|
||||
|
||||
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||
const overStatusId = getStatusId(over.id);
|
||||
if (typeof over.id === "string" && isContainerId(over.id)) {
|
||||
const overStatusId = getContainerId(over.id);
|
||||
overStatusLexorank = statuses.find(
|
||||
s => s.id === overStatusId
|
||||
)?.lexorank;
|
||||
@ -98,11 +121,16 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
swipeSliderDuringDrag(activeId, over);
|
||||
}
|
||||
|
||||
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||
if (typeof activeId !== "string") {
|
||||
handleDealDragOver(activeId, over);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isContainerId(activeId)) {
|
||||
handleColumnDragOver(activeId, over);
|
||||
return;
|
||||
}
|
||||
handleDealDragOver(activeId, over);
|
||||
handleGroupDragOver(activeId, over);
|
||||
};
|
||||
|
||||
const handleDealDragOver = (activeId: string | number, over: Over) => {
|
||||
@ -113,6 +141,7 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
const { overStatus, newLexorank } = getDropTarget(
|
||||
over.id,
|
||||
activeDealId,
|
||||
undefined,
|
||||
activeStatusId
|
||||
);
|
||||
if (!overStatus) return;
|
||||
@ -130,14 +159,49 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
);
|
||||
};
|
||||
|
||||
const handleGroupDragOver = (activeId: string, over: Over) => {
|
||||
const activeGroupId = getGroupId(activeId);
|
||||
const activeStatusId = getStatusByGroupId(activeGroupId)?.id;
|
||||
if (!activeStatusId) return;
|
||||
|
||||
const { overStatus, newLexorank } = getDropTarget(
|
||||
over.id,
|
||||
undefined,
|
||||
activeGroupId,
|
||||
activeStatusId
|
||||
);
|
||||
if (!overStatus) return;
|
||||
|
||||
debouncedSetDeals(
|
||||
deals.map(deal =>
|
||||
deal.group && deal.group.id === activeGroupId
|
||||
? {
|
||||
...deal,
|
||||
status: overStatus,
|
||||
group: {
|
||||
...deal.group,
|
||||
lexorank: newLexorank || deal.group.lexorank,
|
||||
},
|
||||
}
|
||||
: deal
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const handleColumnDragOver = (activeId: string, over: Over) => {
|
||||
const activeStatusId = getStatusId(activeId);
|
||||
const activeStatusId = getContainerId(activeId);
|
||||
let overStatusId: number;
|
||||
|
||||
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||
overStatusId = getStatusId(over.id);
|
||||
if (typeof over.id === "string") {
|
||||
if (isContainerId(over.id)) {
|
||||
overStatusId = getContainerId(over.id);
|
||||
} else {
|
||||
const deal = deals.find(deal => deal.id === over.id);
|
||||
const status = getStatusByGroupId(getGroupId(over.id));
|
||||
if (!status) return;
|
||||
overStatusId = status.id;
|
||||
}
|
||||
} else {
|
||||
const deal = dealsWithoutGroup.find(deal => deal.id === over.id);
|
||||
if (!deal) return;
|
||||
overStatusId = deal.status.id;
|
||||
}
|
||||
@ -158,17 +222,44 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
|
||||
const getDropTarget = (
|
||||
overId: string | number,
|
||||
activeDealId: number,
|
||||
activeDealId: number | undefined,
|
||||
activeGroupId: number | undefined,
|
||||
activeStatusId: number,
|
||||
isOnDragEnd: boolean = false
|
||||
): { overStatus?: StatusSchema; newLexorank?: string } => {
|
||||
if (typeof overId === "string") {
|
||||
return {
|
||||
overStatus: getStatusById(getStatusId(overId)),
|
||||
newLexorank: undefined,
|
||||
};
|
||||
if (isContainerId(overId)) {
|
||||
return getStatusDropTarget(overId);
|
||||
}
|
||||
if (isGroupId(overId)) {
|
||||
return getGroupDropTarget(
|
||||
overId,
|
||||
activeGroupId,
|
||||
activeStatusId,
|
||||
isOnDragEnd
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return getDealDropTarget(
|
||||
Number(overId),
|
||||
activeDealId,
|
||||
activeStatusId,
|
||||
isOnDragEnd
|
||||
);
|
||||
};
|
||||
|
||||
const getStatusDropTarget = (overId: string) => ({
|
||||
overStatus: getStatusById(getContainerId(overId)),
|
||||
newLexorank: undefined,
|
||||
});
|
||||
|
||||
const getDealDropTarget = (
|
||||
overId: number,
|
||||
activeDealId: number | undefined,
|
||||
activeStatusId: number,
|
||||
isOnDragEnd: boolean = false
|
||||
) => {
|
||||
const overDealId = Number(overId);
|
||||
const overStatus = getStatusByDealId(overDealId);
|
||||
|
||||
@ -176,51 +267,93 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
return { overStatus: undefined, newLexorank: undefined };
|
||||
}
|
||||
|
||||
const statusDeals = sortByLexorank(
|
||||
deals.filter(deal => deal.status.id === overStatus.id)
|
||||
const statusItems = getStatusDealsAndGroups(overStatus.id);
|
||||
const overDealIndex = statusItems.findIndex(
|
||||
deal => !isItemGroup(deal) && deal.id === overDealId
|
||||
);
|
||||
const overDealIndex = statusDeals.findIndex(
|
||||
deal => deal.id === overDealId
|
||||
const activeDealIndex = statusItems.findIndex(
|
||||
deal => !isItemGroup(deal) && deal.id === activeDealId
|
||||
);
|
||||
|
||||
if (activeStatusId === overStatus.id) {
|
||||
const newLexorank = getNewRankForSameStatus(
|
||||
statusDeals,
|
||||
statusItems,
|
||||
overDealIndex,
|
||||
activeDealId
|
||||
activeDealIndex
|
||||
);
|
||||
return { overStatus, newLexorank };
|
||||
}
|
||||
|
||||
const newLexorank = getNewRankForAnotherStatus(
|
||||
statusDeals,
|
||||
statusItems,
|
||||
overDealIndex
|
||||
);
|
||||
return { overStatus, newLexorank };
|
||||
};
|
||||
|
||||
const getGroupDropTarget = (
|
||||
overId: string,
|
||||
activeGroupId: number | undefined,
|
||||
activeStatusId: number,
|
||||
isOnDragEnd: boolean = false
|
||||
) => {
|
||||
const overGroupId = getGroupId(overId);
|
||||
const overStatus = getStatusByGroupId(overGroupId);
|
||||
|
||||
if (!overStatus || (!isOnDragEnd && activeGroupId === overGroupId)) {
|
||||
return { overStatus: undefined, newLexorank: undefined };
|
||||
}
|
||||
|
||||
const statusItems = getStatusDealsAndGroups(overStatus.id);
|
||||
const overGroupIndex = statusItems.findIndex(
|
||||
group => isItemGroup(group) && group.id === overGroupId
|
||||
);
|
||||
const activeGroupIndex = statusItems.findIndex(
|
||||
group => isItemGroup(group) && group.id === activeGroupId
|
||||
);
|
||||
|
||||
if (activeStatusId === overStatus.id) {
|
||||
const newLexorank = getNewRankForSameStatus(
|
||||
statusItems,
|
||||
overGroupIndex,
|
||||
activeGroupIndex
|
||||
);
|
||||
return { overStatus, newLexorank };
|
||||
}
|
||||
|
||||
const newLexorank = getNewRankForAnotherStatus(
|
||||
statusItems,
|
||||
overGroupIndex
|
||||
);
|
||||
return { overStatus, 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)) {
|
||||
if (typeof activeId !== "string") {
|
||||
handleDealDragEnd(activeId, over);
|
||||
return;
|
||||
}
|
||||
if (isContainerId(activeId)) {
|
||||
handleStatusColumnDragEnd(activeId, over);
|
||||
return;
|
||||
}
|
||||
handleDealDragEnd(activeId, over);
|
||||
handleGroupDragEnd(activeId, over);
|
||||
};
|
||||
|
||||
const handleStatusColumnDragEnd = (activeId: string, over: Over) => {
|
||||
const activeStatusId = getStatusId(activeId);
|
||||
const activeStatusId = getContainerId(activeId);
|
||||
let overStatusId: number;
|
||||
|
||||
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||
overStatusId = getStatusId(over.id);
|
||||
if (typeof over.id === "string" && isContainerId(over.id)) {
|
||||
overStatusId = getContainerId(over.id);
|
||||
} else {
|
||||
const deal = deals.find(deal => deal.status.id === over.id);
|
||||
const deal = dealsWithoutGroup.find(
|
||||
deal => deal.status.id === over.id
|
||||
);
|
||||
if (!deal) return;
|
||||
overStatusId = deal.status.id;
|
||||
}
|
||||
@ -245,6 +378,7 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
const { overStatus, newLexorank } = getDropTarget(
|
||||
over.id,
|
||||
activeDealId,
|
||||
undefined,
|
||||
activeStatusId,
|
||||
true
|
||||
);
|
||||
@ -261,30 +395,36 @@ const useDealsAndStatusesDnd = (): ReturnType => {
|
||||
dealsCrud.onUpdate(dealId, { statusId, lexorank, name: null });
|
||||
};
|
||||
|
||||
const handleDragStart = ({ active }: DragStartEvent) => {
|
||||
const activeId = active.id as string | number;
|
||||
const handleGroupDragEnd = (activeId: string, over: Over) => {
|
||||
const activeGroupId = getGroupId(activeId);
|
||||
const activeStatusId = getStatusByGroupId(activeGroupId)?.id;
|
||||
if (!activeStatusId) return;
|
||||
|
||||
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||
const statusId = getStatusId(activeId);
|
||||
setActiveStatus(
|
||||
statuses.find(status => status.id === statusId) ?? null
|
||||
const { overStatus, newLexorank } = getDropTarget(
|
||||
over.id,
|
||||
undefined,
|
||||
activeGroupId,
|
||||
activeStatusId,
|
||||
true
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!overStatus) return;
|
||||
|
||||
setActiveDeal(
|
||||
deals.find(deal => deal.id === (activeId as number)) ?? null
|
||||
);
|
||||
onGroupDragEnd(activeGroupId, overStatus.id, newLexorank);
|
||||
};
|
||||
|
||||
const onGroupDragEnd = (
|
||||
groupId: number,
|
||||
statusId: number,
|
||||
lexorank?: string
|
||||
) => {
|
||||
groupsCrud.onUpdate(groupId, { statusId, lexorank, name: null });
|
||||
};
|
||||
|
||||
return {
|
||||
swiperRef,
|
||||
sortedStatuses,
|
||||
handleDragStart,
|
||||
handleDragOver,
|
||||
handleDragEnd,
|
||||
activeStatus,
|
||||
activeDeal,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,18 +1,24 @@
|
||||
import { LexoRank } from "lexorank";
|
||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||
import { DealSchema } from "@/lib/client";
|
||||
import { sortByLexorank } from "@/utils/lexorank/sort";
|
||||
import {
|
||||
BaseDraggable,
|
||||
BaseGroupDraggable,
|
||||
} from "@/components/dnd/types/types";
|
||||
import { getNewLexorank } from "@/utils/lexorank/generation";
|
||||
import { sortByLexorank } from "@/utils/lexorank/sort";
|
||||
|
||||
type NewRankGetters = {
|
||||
type NewRankGetters<
|
||||
TItem extends BaseDraggable,
|
||||
TGroup extends BaseGroupDraggable<TItem>,
|
||||
> = {
|
||||
getNewRankForSameStatus: (
|
||||
statusDeals: DealSchema[],
|
||||
overDealIndex: number,
|
||||
activeDealId: number
|
||||
statusItemsAndGroups: (TItem | TGroup)[],
|
||||
overItemOrGroupIndex: number,
|
||||
activeItemOrGroupIndex: number
|
||||
) => string;
|
||||
getNewRankForAnotherStatus: (
|
||||
statusDeals: DealSchema[],
|
||||
overDealIndex: number
|
||||
statusItemsAndGroups: (TItem | TGroup)[],
|
||||
overItemOrGroupIndex: number
|
||||
) => string;
|
||||
getNewStatusRank: (
|
||||
activeStatusId: number,
|
||||
@ -20,44 +26,46 @@ type NewRankGetters = {
|
||||
) => string | null;
|
||||
};
|
||||
|
||||
const useGetNewRank = (): NewRankGetters => {
|
||||
const useGetNewRank = <
|
||||
TItem extends BaseDraggable,
|
||||
TGroup extends BaseGroupDraggable<TItem>,
|
||||
>(): NewRankGetters<TItem, TGroup> => {
|
||||
const { statuses } = useStatusesContext();
|
||||
|
||||
const getNewRankForSameStatus = (
|
||||
statusDeals: DealSchema[],
|
||||
overDealIndex: number,
|
||||
activeDealId: number
|
||||
statusItemsAndGroups: (TItem | TGroup)[],
|
||||
overItemOrGroupIndex: number,
|
||||
activeItemOrGroupIndex: number
|
||||
): string => {
|
||||
const activeDealIndex = statusDeals.findIndex(
|
||||
deal => deal.id === activeDealId
|
||||
);
|
||||
const [leftIndex, rightIndex] =
|
||||
overDealIndex < activeDealIndex
|
||||
? [overDealIndex - 1, overDealIndex]
|
||||
: [overDealIndex, overDealIndex + 1];
|
||||
overItemOrGroupIndex < activeItemOrGroupIndex
|
||||
? [overItemOrGroupIndex - 1, overItemOrGroupIndex]
|
||||
: [overItemOrGroupIndex, overItemOrGroupIndex + 1];
|
||||
|
||||
const leftLexorank =
|
||||
leftIndex >= 0
|
||||
? LexoRank.parse(statusDeals[leftIndex].lexorank)
|
||||
? LexoRank.parse(statusItemsAndGroups[leftIndex].lexorank)
|
||||
: null;
|
||||
const rightLexorank =
|
||||
rightIndex < statusDeals.length
|
||||
? LexoRank.parse(statusDeals[rightIndex].lexorank)
|
||||
rightIndex < statusItemsAndGroups.length
|
||||
? LexoRank.parse(statusItemsAndGroups[rightIndex].lexorank)
|
||||
: null;
|
||||
|
||||
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||
};
|
||||
|
||||
const getNewRankForAnotherStatus = (
|
||||
statusDeals: DealSchema[],
|
||||
overDealIndex: number
|
||||
statusItemsAndGroups: (TItem | TGroup)[],
|
||||
overItemOrGroupIndex: number
|
||||
): string => {
|
||||
const leftLexorank =
|
||||
overDealIndex > 0
|
||||
? LexoRank.parse(statusDeals[overDealIndex - 1].lexorank)
|
||||
overItemOrGroupIndex > 0
|
||||
? LexoRank.parse(
|
||||
statusItemsAndGroups[overItemOrGroupIndex - 1].lexorank
|
||||
)
|
||||
: null;
|
||||
const rightLexorank = LexoRank.parse(
|
||||
statusDeals[overDealIndex].lexorank
|
||||
statusItemsAndGroups[overItemOrGroupIndex].lexorank
|
||||
);
|
||||
|
||||
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||
|
||||
15
src/app/deals/utils/isItemGroup.ts
Normal file
15
src/app/deals/utils/isItemGroup.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import {
|
||||
BaseDraggable,
|
||||
BaseGroupDraggable,
|
||||
} from "@/components/dnd/types/types";
|
||||
|
||||
const isItemGroup = <
|
||||
TItem extends BaseDraggable,
|
||||
TGroup extends BaseGroupDraggable<TItem>,
|
||||
>(
|
||||
item: TItem | TGroup
|
||||
): boolean => {
|
||||
return "items" in item;
|
||||
};
|
||||
|
||||
export default isItemGroup;
|
||||
@ -1,6 +0,0 @@
|
||||
const STATUS_POSTFIX = "-status";
|
||||
|
||||
export const isStatusId = (rawId: string) => rawId.endsWith(STATUS_POSTFIX);
|
||||
|
||||
export const getStatusId = (rawId: string) =>
|
||||
Number(rawId.replace(STATUS_POSTFIX, ""));
|
||||
@ -1,42 +0,0 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { useDroppable } from "@dnd-kit/core";
|
||||
import {
|
||||
SortableContext,
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { Stack } from "@mantine/core";
|
||||
import { BaseDraggable } from "@/components/dnd/types/types";
|
||||
|
||||
type Props<TItem> = {
|
||||
id: string;
|
||||
items: TItem[];
|
||||
renderItem: (item: TItem) => ReactNode;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const FunnelColumn = <TItem extends BaseDraggable>({
|
||||
id,
|
||||
items,
|
||||
renderItem,
|
||||
children,
|
||||
}: Props<TItem>) => {
|
||||
const { setNodeRef } = useDroppable({ id });
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<SortableContext
|
||||
id={id}
|
||||
items={items}
|
||||
strategy={verticalListSortingStrategy}>
|
||||
<Stack
|
||||
gap="xs"
|
||||
ref={setNodeRef}>
|
||||
{items.map(renderItem)}
|
||||
</Stack>
|
||||
</SortableContext>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FunnelColumn;
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { ReactNode, RefObject, useMemo } from "react";
|
||||
import React, { ReactNode, RefObject, useMemo, useState } from "react";
|
||||
import {
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
@ -16,17 +16,28 @@ import { Swiper, SwiperRef, SwiperSlide } from "swiper/react";
|
||||
import { Box } from "@mantine/core";
|
||||
import CreateStatusButton from "@/app/deals/components/shared/CreateStatusButton/CreateStatusButton";
|
||||
import useDndSensors from "@/app/deals/hooks/useSensors";
|
||||
import FunnelColumn from "@/components/dnd/FunnelDnd/FunnelColumn";
|
||||
import FunnelOverlay from "@/components/dnd/FunnelDnd/FunnelOverlay";
|
||||
import { BaseDraggable } from "@/components/dnd/types/types";
|
||||
import FunnelColumn from "@/components/dnd/FunnelDnd/components/FunnelColumn";
|
||||
import FunnelOverlay from "@/components/dnd/FunnelDnd/components/FunnelOverlay";
|
||||
import {
|
||||
getContainerId,
|
||||
getDndContainerId,
|
||||
isContainerId,
|
||||
} from "@/components/dnd/FunnelDnd/utils/columnId";
|
||||
import {
|
||||
getGroupId,
|
||||
isGroupId,
|
||||
} from "@/components/dnd/FunnelDnd/utils/groupId";
|
||||
import {
|
||||
BaseDraggable,
|
||||
BaseGroupDraggable,
|
||||
} from "@/components/dnd/types/types";
|
||||
import useIsMobile from "@/hooks/utils/useIsMobile";
|
||||
import SortableItem from "../SortableItem";
|
||||
import classes from "./FunnelDnd.module.css";
|
||||
|
||||
type Props<TContainer, TItem> = {
|
||||
type Props<TContainer, TItem, TGroup> = {
|
||||
containers: TContainer[];
|
||||
items: TItem[];
|
||||
onDragStart: (event: DragStartEvent) => void;
|
||||
itemsAndGroups: (TItem | TGroup)[];
|
||||
onDragOver: (event: DragOverEvent) => void;
|
||||
onDragEnd: (event: DragEndEvent) => void;
|
||||
swiperRef: RefObject<SwiperRef | null>;
|
||||
@ -42,11 +53,8 @@ type Props<TContainer, TItem> = {
|
||||
children: ReactNode
|
||||
) => ReactNode;
|
||||
renderItem: (item: TItem) => ReactNode;
|
||||
renderItemOverlay: (item: TItem) => ReactNode;
|
||||
getContainerId: (container: TContainer) => string;
|
||||
getItemsByContainer: (container: TContainer, items: TItem[]) => TItem[];
|
||||
activeContainer: TContainer | null;
|
||||
activeItem: TItem | null;
|
||||
renderGroup: (group: TGroup) => ReactNode;
|
||||
getItemsByContainer: (container: TContainer) => (TItem | TGroup)[];
|
||||
isCreatingContainerEnabled?: boolean;
|
||||
disabledColumns?: boolean;
|
||||
};
|
||||
@ -54,10 +62,10 @@ type Props<TContainer, TItem> = {
|
||||
const FunnelDnd = <
|
||||
TContainer extends BaseDraggable,
|
||||
TItem extends BaseDraggable,
|
||||
TGroup extends BaseGroupDraggable<TItem>,
|
||||
>({
|
||||
containers,
|
||||
items,
|
||||
onDragStart,
|
||||
itemsAndGroups,
|
||||
onDragOver,
|
||||
onDragEnd,
|
||||
swiperRef,
|
||||
@ -65,22 +73,25 @@ const FunnelDnd = <
|
||||
renderContainerHeader,
|
||||
renderContainerOverlay,
|
||||
renderItem,
|
||||
renderItemOverlay,
|
||||
getContainerId,
|
||||
renderGroup,
|
||||
getItemsByContainer,
|
||||
activeContainer,
|
||||
activeItem,
|
||||
isCreatingContainerEnabled = true,
|
||||
disabledColumns = false,
|
||||
}: Props<TContainer, TItem>) => {
|
||||
}: Props<TContainer, TItem, TGroup>) => {
|
||||
const sensors = useDndSensors();
|
||||
const isMobile = useIsMobile();
|
||||
const frequency = useMemo(() => (isMobile ? 1 : undefined), [isMobile]);
|
||||
|
||||
const [activeItem, setActiveItem] = useState<TItem | null>(null);
|
||||
const [activeContainer, setActiveContainer] = useState<TContainer | null>(
|
||||
null
|
||||
);
|
||||
const [activeGroup, setActiveGroup] = useState<TGroup | null>(null);
|
||||
|
||||
const renderContainers = () =>
|
||||
containers.map((container, index) => {
|
||||
const containerItems = getItemsByContainer(container, items);
|
||||
const containerId = getContainerId(container);
|
||||
const containerItems = getItemsByContainer(container);
|
||||
const containerId = getDndContainerId(container.id);
|
||||
return (
|
||||
<SwiperSlide
|
||||
style={{ width: 250 }}
|
||||
@ -94,8 +105,9 @@ const FunnelDnd = <
|
||||
container,
|
||||
<FunnelColumn
|
||||
id={containerId}
|
||||
items={containerItems}
|
||||
itemsAndGroups={containerItems}
|
||||
renderItem={renderItem}
|
||||
renderGroup={renderGroup}
|
||||
/>,
|
||||
renderDraggable!,
|
||||
index
|
||||
@ -156,6 +168,34 @@ const FunnelDnd = <
|
||||
);
|
||||
};
|
||||
|
||||
const onDragStart = ({ active }: DragStartEvent) => {
|
||||
const activeId = active.id as string | number;
|
||||
|
||||
if (typeof activeId !== "string") {
|
||||
const item = (itemsAndGroups.find(
|
||||
item => !("items" in item) && item.id === activeId
|
||||
) ?? null) as TItem | null;
|
||||
setActiveItem(item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isContainerId(activeId)) {
|
||||
const contId = getContainerId(activeId);
|
||||
setActiveContainer(
|
||||
containers.find(container => container.id === contId) ?? null
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isGroupId(activeId)) {
|
||||
const groupId = getGroupId(activeId);
|
||||
const group = (itemsAndGroups.find(
|
||||
group => "items" in group && group.id === groupId
|
||||
) ?? null) as TGroup | null;
|
||||
setActiveGroup(group);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
@ -166,30 +206,37 @@ const FunnelDnd = <
|
||||
}}
|
||||
onDragStart={onDragStart}
|
||||
onDragOver={onDragOver}
|
||||
onDragEnd={onDragEnd}>
|
||||
onDragEnd={state => {
|
||||
setActiveContainer(null);
|
||||
setActiveItem(null);
|
||||
setActiveGroup(null);
|
||||
onDragEnd(state);
|
||||
}}>
|
||||
<SortableContext
|
||||
items={containers.map(getContainerId)}
|
||||
items={containers.map(container =>
|
||||
getDndContainerId(container.id)
|
||||
)}
|
||||
strategy={horizontalListSortingStrategy}>
|
||||
{renderBody()}
|
||||
<FunnelOverlay
|
||||
activeContainer={activeContainer}
|
||||
activeItem={activeItem}
|
||||
activeGroup={activeGroup}
|
||||
renderContainer={container => {
|
||||
const containerItems = getItemsByContainer(
|
||||
container,
|
||||
items
|
||||
);
|
||||
const containerId = getContainerId(container);
|
||||
const containerItems = getItemsByContainer(container);
|
||||
const containerId = getDndContainerId(container.id);
|
||||
return renderContainerOverlay(
|
||||
container,
|
||||
<FunnelColumn
|
||||
id={containerId}
|
||||
items={containerItems}
|
||||
itemsAndGroups={containerItems}
|
||||
renderItem={renderItem}
|
||||
renderGroup={renderGroup}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
renderItem={renderItemOverlay}
|
||||
renderItem={renderItem}
|
||||
renderGroup={renderGroup}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
|
||||
76
src/components/dnd/FunnelDnd/components/FunnelColumn.tsx
Normal file
76
src/components/dnd/FunnelDnd/components/FunnelColumn.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { useDroppable } from "@dnd-kit/core";
|
||||
import {
|
||||
SortableContext,
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { Stack } from "@mantine/core";
|
||||
import { getDndGroupId } from "@/components/dnd/FunnelDnd/utils/groupId";
|
||||
import SortableItem from "@/components/dnd/SortableItem";
|
||||
import {
|
||||
BaseDraggable,
|
||||
BaseGroupDraggable,
|
||||
} from "@/components/dnd/types/types";
|
||||
import isItemGroup from "@/app/deals/utils/isItemGroup";
|
||||
|
||||
type Props<
|
||||
TItem extends BaseDraggable,
|
||||
TGroup extends BaseGroupDraggable<TItem>,
|
||||
> = {
|
||||
id: string;
|
||||
itemsAndGroups: (TItem | TGroup)[];
|
||||
renderItem: (item: TItem) => ReactNode;
|
||||
renderGroup: (group: TGroup) => ReactNode;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const FunnelColumn = <
|
||||
TItem extends BaseDraggable,
|
||||
TGroup extends BaseGroupDraggable<TItem>,
|
||||
>({
|
||||
id,
|
||||
itemsAndGroups,
|
||||
renderItem,
|
||||
renderGroup,
|
||||
children,
|
||||
}: Props<TItem, TGroup>) => {
|
||||
const { setNodeRef } = useDroppable({ id });
|
||||
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<SortableContext
|
||||
id={id}
|
||||
items={itemsAndGroups.map(itemOrGroup =>
|
||||
isItemGroup(itemOrGroup)
|
||||
? getDndGroupId(itemOrGroup.id)
|
||||
: itemOrGroup.id
|
||||
)}
|
||||
strategy={verticalListSortingStrategy}>
|
||||
<Stack
|
||||
gap="xs"
|
||||
ref={setNodeRef}>
|
||||
{itemsAndGroups.map(itemOrGroup =>
|
||||
"items" in itemOrGroup ? (
|
||||
<SortableItem
|
||||
key={`${itemOrGroup.id.toString()}g`}
|
||||
dragHandleStyle={{ cursor: "pointer" }}
|
||||
id={getDndGroupId(itemOrGroup.id)}
|
||||
renderItem={() => renderGroup(itemOrGroup)}
|
||||
/>
|
||||
) : (
|
||||
<SortableItem
|
||||
key={itemOrGroup.id}
|
||||
dragHandleStyle={{ cursor: "pointer" }}
|
||||
id={itemOrGroup.id}
|
||||
renderItem={() => renderItem(itemOrGroup)}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</Stack>
|
||||
</SortableContext>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default FunnelColumn;
|
||||
@ -2,28 +2,32 @@ import React, { ReactNode } from "react";
|
||||
import { defaultDropAnimation, DragOverlay } from "@dnd-kit/core";
|
||||
import styles from "@/components/dnd/FunnelDnd/FunnelDnd.module.css";
|
||||
|
||||
type Props<TContainer, TItem> = {
|
||||
type Props<TContainer, TItem, TGroup> = {
|
||||
activeContainer: TContainer | null;
|
||||
activeItem: TItem | null;
|
||||
activeGroup: TGroup | null;
|
||||
renderContainer: (container: TContainer) => ReactNode;
|
||||
renderItem: (item: TItem) => ReactNode;
|
||||
renderGroup: (group: TGroup) => ReactNode;
|
||||
};
|
||||
|
||||
const FunnelOverlay = <TContainer, TItem>({
|
||||
const FunnelOverlay = <TContainer, TItem, TGroup>({
|
||||
activeContainer,
|
||||
activeItem,
|
||||
activeGroup,
|
||||
renderContainer,
|
||||
renderItem,
|
||||
}: Props<TContainer, TItem>) => {
|
||||
renderGroup,
|
||||
}: Props<TContainer, TItem, TGroup>) => {
|
||||
const renderOverlay = () => {
|
||||
if (activeItem) return renderItem(activeItem);
|
||||
if (activeContainer) return renderContainer(activeContainer);
|
||||
if (activeGroup) return renderGroup(activeGroup);
|
||||
};
|
||||
|
||||
return (
|
||||
<DragOverlay dropAnimation={defaultDropAnimation}>
|
||||
<div className={styles.overlay}>
|
||||
{activeItem
|
||||
? renderItem(activeItem)
|
||||
: activeContainer
|
||||
? renderContainer(activeContainer)
|
||||
: null}
|
||||
</div>
|
||||
<div className={styles.overlay}>{renderOverlay()}</div>
|
||||
</DragOverlay>
|
||||
);
|
||||
};
|
||||
9
src/components/dnd/FunnelDnd/utils/columnId.ts
Normal file
9
src/components/dnd/FunnelDnd/utils/columnId.ts
Normal file
@ -0,0 +1,9 @@
|
||||
const CONTAINER_POSTFIX = "-con";
|
||||
|
||||
export const isContainerId = (rawId: string) => rawId.endsWith(CONTAINER_POSTFIX);
|
||||
|
||||
export const getContainerId = (rawId: string) =>
|
||||
Number(rawId.replace(CONTAINER_POSTFIX, ""));
|
||||
|
||||
export const getDndContainerId = (id: number) =>
|
||||
`${id}${CONTAINER_POSTFIX}`;
|
||||
8
src/components/dnd/FunnelDnd/utils/groupId.ts
Normal file
8
src/components/dnd/FunnelDnd/utils/groupId.ts
Normal file
@ -0,0 +1,8 @@
|
||||
const GROUP_POSTFIX = "-gr";
|
||||
|
||||
export const isGroupId = (rawId: string) => rawId.endsWith(GROUP_POSTFIX);
|
||||
|
||||
export const getGroupId = (rawId: string) =>
|
||||
Number(rawId.replace(GROUP_POSTFIX, ""));
|
||||
|
||||
export const getDndGroupId = (id: number) => `${id}${GROUP_POSTFIX}`;
|
||||
@ -1,3 +1,8 @@
|
||||
export type BaseDraggable = {
|
||||
id: number;
|
||||
lexorank: string;
|
||||
};
|
||||
|
||||
export type BaseGroupDraggable<TItem extends BaseDraggable> = BaseDraggable & {
|
||||
items: TItem[];
|
||||
};
|
||||
|
||||
69
src/hooks/cruds/useDealGroupCrud.tsx
Normal file
69
src/hooks/cruds/useDealGroupCrud.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { UpdateDealGroupSchema } from "@/lib/client";
|
||||
import {
|
||||
addDealMutation,
|
||||
createDealGroupMutation,
|
||||
removeDealMutation,
|
||||
updateDealGroupMutation,
|
||||
} from "@/lib/client/@tanstack/react-query.gen";
|
||||
|
||||
export type GroupsCrud = {
|
||||
onUpdate: (groupId: number, group: UpdateDealGroupSchema) => void;
|
||||
// onDelete: (group: DealGroupSchema, onSuccess?: () => void) => void;
|
||||
};
|
||||
|
||||
const useDealGroupCrud = (): GroupsCrud => {
|
||||
const updateMutation = useMutation(updateDealGroupMutation());
|
||||
|
||||
const onUpdate = (groupId: number, entity: UpdateDealGroupSchema) => {
|
||||
updateMutation.mutate({
|
||||
path: {
|
||||
pk: groupId,
|
||||
},
|
||||
body: {
|
||||
entity,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const createMutation = useMutation(createDealGroupMutation());
|
||||
|
||||
const onCreate = (draggingDealId: number, hoveredDealId: number) => {
|
||||
createMutation.mutate({
|
||||
body: {
|
||||
draggingDealId,
|
||||
hoveredDealId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const addDealToGroupMutation = useMutation(addDealMutation());
|
||||
|
||||
const onAddDeal = (dealId: number, groupId: number) => {
|
||||
addDealToGroupMutation.mutate({
|
||||
body: {
|
||||
dealId,
|
||||
groupId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const removeDealFromGroupMutation = useMutation(removeDealMutation());
|
||||
|
||||
const onRemoveDeal = (dealId: number) => {
|
||||
removeDealFromGroupMutation.mutate({
|
||||
body: {
|
||||
dealId,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
onUpdate,
|
||||
// onCreate,
|
||||
// onAddDeal,
|
||||
// onRemoveDeal,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDealGroupCrud;
|
||||
@ -9,12 +9,14 @@ import {
|
||||
import type { AxiosError } from "axios";
|
||||
import { client as _heyApiClient } from "../client.gen";
|
||||
import {
|
||||
addDeal,
|
||||
addKitToDeal,
|
||||
addKitToDealProduct,
|
||||
createBarcodeTemplate,
|
||||
createBoard,
|
||||
createClient,
|
||||
createDeal,
|
||||
createDealGroup,
|
||||
createDealProduct,
|
||||
createDealProductService,
|
||||
createDealService,
|
||||
@ -59,10 +61,12 @@ import {
|
||||
getServicesKits,
|
||||
getStatuses,
|
||||
getStatusHistory,
|
||||
removeDeal,
|
||||
updateBarcodeTemplate,
|
||||
updateBoard,
|
||||
updateClient,
|
||||
updateDeal,
|
||||
updateDealGroup,
|
||||
updateDealProduct,
|
||||
updateDealProductService,
|
||||
updateDealService,
|
||||
@ -76,6 +80,9 @@ import {
|
||||
type Options,
|
||||
} from "../sdk.gen";
|
||||
import type {
|
||||
AddDealData,
|
||||
AddDealError,
|
||||
AddDealResponse,
|
||||
AddKitToDealData,
|
||||
AddKitToDealError,
|
||||
AddKitToDealProductData,
|
||||
@ -93,6 +100,9 @@ import type {
|
||||
CreateClientResponse2,
|
||||
CreateDealData,
|
||||
CreateDealError,
|
||||
CreateDealGroupData,
|
||||
CreateDealGroupError,
|
||||
CreateDealGroupResponse2,
|
||||
CreateDealProductData,
|
||||
CreateDealProductError,
|
||||
CreateDealProductResponse2,
|
||||
@ -194,6 +204,9 @@ import type {
|
||||
GetServicesKitsData,
|
||||
GetStatusesData,
|
||||
GetStatusHistoryData,
|
||||
RemoveDealData,
|
||||
RemoveDealError,
|
||||
RemoveDealResponse,
|
||||
UpdateBarcodeTemplateData,
|
||||
UpdateBarcodeTemplateError,
|
||||
UpdateBarcodeTemplateResponse2,
|
||||
@ -205,6 +218,9 @@ import type {
|
||||
UpdateClientResponse2,
|
||||
UpdateDealData,
|
||||
UpdateDealError,
|
||||
UpdateDealGroupData,
|
||||
UpdateDealGroupError,
|
||||
UpdateDealGroupResponse2,
|
||||
UpdateDealProductData,
|
||||
UpdateDealProductError,
|
||||
UpdateDealProductResponse2,
|
||||
@ -605,6 +621,159 @@ export const updateDealMutation = (
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Group
|
||||
*/
|
||||
export const updateDealGroupMutation = (
|
||||
options?: Partial<Options<UpdateDealGroupData>>
|
||||
): UseMutationOptions<
|
||||
UpdateDealGroupResponse2,
|
||||
AxiosError<UpdateDealGroupError>,
|
||||
Options<UpdateDealGroupData>
|
||||
> => {
|
||||
const mutationOptions: UseMutationOptions<
|
||||
UpdateDealGroupResponse2,
|
||||
AxiosError<UpdateDealGroupError>,
|
||||
Options<UpdateDealGroupData>
|
||||
> = {
|
||||
mutationFn: async localOptions => {
|
||||
const { data } = await updateDealGroup({
|
||||
...options,
|
||||
...localOptions,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const createDealGroupQueryKey = (
|
||||
options: Options<CreateDealGroupData>
|
||||
) => createQueryKey("createDealGroup", options);
|
||||
|
||||
/**
|
||||
* Create Group
|
||||
*/
|
||||
export const createDealGroupOptions = (
|
||||
options: Options<CreateDealGroupData>
|
||||
) => {
|
||||
return queryOptions({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await createDealGroup({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: createDealGroupQueryKey(options),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create Group
|
||||
*/
|
||||
export const createDealGroupMutation = (
|
||||
options?: Partial<Options<CreateDealGroupData>>
|
||||
): UseMutationOptions<
|
||||
CreateDealGroupResponse2,
|
||||
AxiosError<CreateDealGroupError>,
|
||||
Options<CreateDealGroupData>
|
||||
> => {
|
||||
const mutationOptions: UseMutationOptions<
|
||||
CreateDealGroupResponse2,
|
||||
AxiosError<CreateDealGroupError>,
|
||||
Options<CreateDealGroupData>
|
||||
> = {
|
||||
mutationFn: async localOptions => {
|
||||
const { data } = await createDealGroup({
|
||||
...options,
|
||||
...localOptions,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove Deal
|
||||
*/
|
||||
export const removeDealMutation = (
|
||||
options?: Partial<Options<RemoveDealData>>
|
||||
): UseMutationOptions<
|
||||
RemoveDealResponse,
|
||||
AxiosError<RemoveDealError>,
|
||||
Options<RemoveDealData>
|
||||
> => {
|
||||
const mutationOptions: UseMutationOptions<
|
||||
RemoveDealResponse,
|
||||
AxiosError<RemoveDealError>,
|
||||
Options<RemoveDealData>
|
||||
> = {
|
||||
mutationFn: async localOptions => {
|
||||
const { data } = await removeDeal({
|
||||
...options,
|
||||
...localOptions,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const addDealQueryKey = (options: Options<AddDealData>) =>
|
||||
createQueryKey("addDeal", options);
|
||||
|
||||
/**
|
||||
* Add Deal
|
||||
*/
|
||||
export const addDealOptions = (options: Options<AddDealData>) => {
|
||||
return queryOptions({
|
||||
queryFn: async ({ queryKey, signal }) => {
|
||||
const { data } = await addDeal({
|
||||
...options,
|
||||
...queryKey[0],
|
||||
signal,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
queryKey: addDealQueryKey(options),
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add Deal
|
||||
*/
|
||||
export const addDealMutation = (
|
||||
options?: Partial<Options<AddDealData>>
|
||||
): UseMutationOptions<
|
||||
AddDealResponse,
|
||||
AxiosError<AddDealError>,
|
||||
Options<AddDealData>
|
||||
> => {
|
||||
const mutationOptions: UseMutationOptions<
|
||||
AddDealResponse,
|
||||
AxiosError<AddDealError>,
|
||||
Options<AddDealData>
|
||||
> = {
|
||||
mutationFn: async localOptions => {
|
||||
const { data } = await addDeal({
|
||||
...options,
|
||||
...localOptions,
|
||||
throwOnError: true,
|
||||
});
|
||||
return data;
|
||||
},
|
||||
};
|
||||
return mutationOptions;
|
||||
};
|
||||
|
||||
export const getBuiltInModulesQueryKey = (
|
||||
options?: Options<GetBuiltInModulesData>
|
||||
) => createQueryKey("getBuiltInModules", options);
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
import type { Client, Options as ClientOptions, TDataShape } from "./client";
|
||||
import { client as _heyApiClient } from "./client.gen";
|
||||
import type {
|
||||
AddDealData,
|
||||
AddDealErrors,
|
||||
AddDealResponses,
|
||||
AddKitToDealData,
|
||||
AddKitToDealErrors,
|
||||
AddKitToDealProductData,
|
||||
@ -20,6 +23,9 @@ import type {
|
||||
CreateClientResponses,
|
||||
CreateDealData,
|
||||
CreateDealErrors,
|
||||
CreateDealGroupData,
|
||||
CreateDealGroupErrors,
|
||||
CreateDealGroupResponses,
|
||||
CreateDealProductData,
|
||||
CreateDealProductErrors,
|
||||
CreateDealProductResponses,
|
||||
@ -144,6 +150,9 @@ import type {
|
||||
GetStatusHistoryData,
|
||||
GetStatusHistoryErrors,
|
||||
GetStatusHistoryResponses,
|
||||
RemoveDealData,
|
||||
RemoveDealErrors,
|
||||
RemoveDealResponses,
|
||||
UpdateBarcodeTemplateData,
|
||||
UpdateBarcodeTemplateErrors,
|
||||
UpdateBarcodeTemplateResponses,
|
||||
@ -155,6 +164,9 @@ import type {
|
||||
UpdateClientResponses,
|
||||
UpdateDealData,
|
||||
UpdateDealErrors,
|
||||
UpdateDealGroupData,
|
||||
UpdateDealGroupErrors,
|
||||
UpdateDealGroupResponses,
|
||||
UpdateDealProductData,
|
||||
UpdateDealProductErrors,
|
||||
UpdateDealProductResponses,
|
||||
@ -188,6 +200,8 @@ import type {
|
||||
UpdateStatusResponses,
|
||||
} from "./types.gen";
|
||||
import {
|
||||
zAddDealData,
|
||||
zAddDealResponse,
|
||||
zAddKitToDealData,
|
||||
zAddKitToDealProductData,
|
||||
zAddKitToDealProductResponse,
|
||||
@ -199,6 +213,8 @@ import {
|
||||
zCreateClientData,
|
||||
zCreateClientResponse2,
|
||||
zCreateDealData,
|
||||
zCreateDealGroupData,
|
||||
zCreateDealGroupResponse2,
|
||||
zCreateDealProductData,
|
||||
zCreateDealProductResponse2,
|
||||
zCreateDealProductServiceData,
|
||||
@ -288,6 +304,8 @@ import {
|
||||
zGetStatusesResponse2,
|
||||
zGetStatusHistoryData,
|
||||
zGetStatusHistoryResponse2,
|
||||
zRemoveDealData,
|
||||
zRemoveDealResponse,
|
||||
zUpdateBarcodeTemplateData,
|
||||
zUpdateBarcodeTemplateResponse2,
|
||||
zUpdateBoardData,
|
||||
@ -295,6 +313,8 @@ import {
|
||||
zUpdateClientData,
|
||||
zUpdateClientResponse2,
|
||||
zUpdateDealData,
|
||||
zUpdateDealGroupData,
|
||||
zUpdateDealGroupResponse2,
|
||||
zUpdateDealProductData,
|
||||
zUpdateDealProductResponse2,
|
||||
zUpdateDealProductServiceData,
|
||||
@ -535,6 +555,114 @@ export const updateDeal = <ThrowOnError extends boolean = false>(
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Update Group
|
||||
*/
|
||||
export const updateDealGroup = <ThrowOnError extends boolean = false>(
|
||||
options: Options<UpdateDealGroupData, ThrowOnError>
|
||||
) => {
|
||||
return (options.client ?? _heyApiClient).patch<
|
||||
UpdateDealGroupResponses,
|
||||
UpdateDealGroupErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
requestValidator: async data => {
|
||||
return await zUpdateDealGroupData.parseAsync(data);
|
||||
},
|
||||
responseType: "json",
|
||||
responseValidator: async data => {
|
||||
return await zUpdateDealGroupResponse2.parseAsync(data);
|
||||
},
|
||||
url: "/deal-group/{pk}",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create Group
|
||||
*/
|
||||
export const createDealGroup = <ThrowOnError extends boolean = false>(
|
||||
options: Options<CreateDealGroupData, ThrowOnError>
|
||||
) => {
|
||||
return (options.client ?? _heyApiClient).post<
|
||||
CreateDealGroupResponses,
|
||||
CreateDealGroupErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
requestValidator: async data => {
|
||||
return await zCreateDealGroupData.parseAsync(data);
|
||||
},
|
||||
responseType: "json",
|
||||
responseValidator: async data => {
|
||||
return await zCreateDealGroupResponse2.parseAsync(data);
|
||||
},
|
||||
url: "/deal-group/",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove Deal
|
||||
*/
|
||||
export const removeDeal = <ThrowOnError extends boolean = false>(
|
||||
options: Options<RemoveDealData, ThrowOnError>
|
||||
) => {
|
||||
return (options.client ?? _heyApiClient).delete<
|
||||
RemoveDealResponses,
|
||||
RemoveDealErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
requestValidator: async data => {
|
||||
return await zRemoveDealData.parseAsync(data);
|
||||
},
|
||||
responseType: "json",
|
||||
responseValidator: async data => {
|
||||
return await zRemoveDealResponse.parseAsync(data);
|
||||
},
|
||||
url: "/deal-group/deal",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Add Deal
|
||||
*/
|
||||
export const addDeal = <ThrowOnError extends boolean = false>(
|
||||
options: Options<AddDealData, ThrowOnError>
|
||||
) => {
|
||||
return (options.client ?? _heyApiClient).post<
|
||||
AddDealResponses,
|
||||
AddDealErrors,
|
||||
ThrowOnError
|
||||
>({
|
||||
requestValidator: async data => {
|
||||
return await zAddDealData.parseAsync(data);
|
||||
},
|
||||
responseType: "json",
|
||||
responseValidator: async data => {
|
||||
return await zAddDealResponse.parseAsync(data);
|
||||
},
|
||||
url: "/deal-group/deal",
|
||||
...options,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options.headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get Built In Modules
|
||||
*/
|
||||
|
||||
@ -1,5 +1,29 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
/**
|
||||
* AddDealToGroupRequest
|
||||
*/
|
||||
export type AddDealToGroupRequest = {
|
||||
/**
|
||||
* Dealid
|
||||
*/
|
||||
dealId: number;
|
||||
/**
|
||||
* Groupid
|
||||
*/
|
||||
groupId: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* AddDealToGroupResponse
|
||||
*/
|
||||
export type AddDealToGroupResponse = {
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* BarcodeTemplateAttributeSchema
|
||||
*/
|
||||
@ -347,6 +371,27 @@ export type CreateClientSchema = {
|
||||
details: ClientDetailsSchema;
|
||||
};
|
||||
|
||||
/**
|
||||
* CreateDealGroupRequest
|
||||
*/
|
||||
export type CreateDealGroupRequest = {
|
||||
/**
|
||||
* Draggingdealid
|
||||
*/
|
||||
draggingDealId: number;
|
||||
/**
|
||||
* Hovereddealid
|
||||
*/
|
||||
hoveredDealId: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* CreateDealGroupResponse
|
||||
*/
|
||||
export type CreateDealGroupResponse = {
|
||||
entity: DealGroupSchema;
|
||||
};
|
||||
|
||||
/**
|
||||
* CreateDealProductRequest
|
||||
*/
|
||||
@ -832,6 +877,24 @@ export type DealAddKitResponse = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* DealGroupSchema
|
||||
*/
|
||||
export type DealGroupSchema = {
|
||||
/**
|
||||
* Id
|
||||
*/
|
||||
id: number;
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name?: string | null;
|
||||
/**
|
||||
* Lexorank
|
||||
*/
|
||||
lexorank: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* DealProductAddKitRequest
|
||||
*/
|
||||
@ -887,6 +950,26 @@ export type DealProductSchema = {
|
||||
productServices: Array<ProductServiceSchema>;
|
||||
};
|
||||
|
||||
/**
|
||||
* DealRemoveFromGroupRequest
|
||||
*/
|
||||
export type DealRemoveFromGroupRequest = {
|
||||
/**
|
||||
* Dealid
|
||||
*/
|
||||
dealId: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* DealRemoveFromGroupResponse
|
||||
*/
|
||||
export type DealRemoveFromGroupResponse = {
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* DealSchema
|
||||
*/
|
||||
@ -909,6 +992,7 @@ export type DealSchema = {
|
||||
* Createdat
|
||||
*/
|
||||
createdAt: string;
|
||||
group: DealGroupSchema | null;
|
||||
/**
|
||||
* Productsquantity
|
||||
*/
|
||||
@ -1753,6 +1837,41 @@ export type UpdateClientSchema = {
|
||||
details?: ClientDetailsSchema | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* UpdateDealGroupRequest
|
||||
*/
|
||||
export type UpdateDealGroupRequest = {
|
||||
entity: UpdateDealGroupSchema;
|
||||
};
|
||||
|
||||
/**
|
||||
* UpdateDealGroupResponse
|
||||
*/
|
||||
export type UpdateDealGroupResponse = {
|
||||
/**
|
||||
* Message
|
||||
*/
|
||||
message: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* UpdateDealGroupSchema
|
||||
*/
|
||||
export type UpdateDealGroupSchema = {
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name?: string | null;
|
||||
/**
|
||||
* Lexorank
|
||||
*/
|
||||
lexorank?: string | null;
|
||||
/**
|
||||
* Statusid
|
||||
*/
|
||||
statusId?: number | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* UpdateDealProductRequest
|
||||
*/
|
||||
@ -2466,6 +2585,115 @@ export type UpdateDealResponses = {
|
||||
export type UpdateDealResponse2 =
|
||||
UpdateDealResponses[keyof UpdateDealResponses];
|
||||
|
||||
export type UpdateDealGroupData = {
|
||||
body: UpdateDealGroupRequest;
|
||||
path: {
|
||||
/**
|
||||
* Pk
|
||||
*/
|
||||
pk: number;
|
||||
};
|
||||
query?: never;
|
||||
url: "/deal-group/{pk}";
|
||||
};
|
||||
|
||||
export type UpdateDealGroupErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type UpdateDealGroupError =
|
||||
UpdateDealGroupErrors[keyof UpdateDealGroupErrors];
|
||||
|
||||
export type UpdateDealGroupResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: UpdateDealGroupResponse;
|
||||
};
|
||||
|
||||
export type UpdateDealGroupResponse2 =
|
||||
UpdateDealGroupResponses[keyof UpdateDealGroupResponses];
|
||||
|
||||
export type CreateDealGroupData = {
|
||||
body: CreateDealGroupRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: "/deal-group/";
|
||||
};
|
||||
|
||||
export type CreateDealGroupErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type CreateDealGroupError =
|
||||
CreateDealGroupErrors[keyof CreateDealGroupErrors];
|
||||
|
||||
export type CreateDealGroupResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: CreateDealGroupResponse;
|
||||
};
|
||||
|
||||
export type CreateDealGroupResponse2 =
|
||||
CreateDealGroupResponses[keyof CreateDealGroupResponses];
|
||||
|
||||
export type RemoveDealData = {
|
||||
body: DealRemoveFromGroupRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: "/deal-group/deal";
|
||||
};
|
||||
|
||||
export type RemoveDealErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type RemoveDealError = RemoveDealErrors[keyof RemoveDealErrors];
|
||||
|
||||
export type RemoveDealResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: DealRemoveFromGroupResponse;
|
||||
};
|
||||
|
||||
export type RemoveDealResponse = RemoveDealResponses[keyof RemoveDealResponses];
|
||||
|
||||
export type AddDealData = {
|
||||
body: AddDealToGroupRequest;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: "/deal-group/deal";
|
||||
};
|
||||
|
||||
export type AddDealErrors = {
|
||||
/**
|
||||
* Validation Error
|
||||
*/
|
||||
422: HttpValidationError;
|
||||
};
|
||||
|
||||
export type AddDealError = AddDealErrors[keyof AddDealErrors];
|
||||
|
||||
export type AddDealResponses = {
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
200: AddDealToGroupResponse;
|
||||
};
|
||||
|
||||
export type AddDealResponse = AddDealResponses[keyof AddDealResponses];
|
||||
|
||||
export type GetBuiltInModulesData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
|
||||
@ -2,6 +2,21 @@
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* AddDealToGroupRequest
|
||||
*/
|
||||
export const zAddDealToGroupRequest = z.object({
|
||||
dealId: z.int(),
|
||||
groupId: z.int(),
|
||||
});
|
||||
|
||||
/**
|
||||
* AddDealToGroupResponse
|
||||
*/
|
||||
export const zAddDealToGroupResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* BarcodeTemplateAttributeSchema
|
||||
*/
|
||||
@ -193,6 +208,30 @@ export const zCreateClientResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* CreateDealGroupRequest
|
||||
*/
|
||||
export const zCreateDealGroupRequest = z.object({
|
||||
draggingDealId: z.int(),
|
||||
hoveredDealId: z.int(),
|
||||
});
|
||||
|
||||
/**
|
||||
* DealGroupSchema
|
||||
*/
|
||||
export const zDealGroupSchema = z.object({
|
||||
id: z.int(),
|
||||
name: z.optional(z.union([z.string(), z.null()])),
|
||||
lexorank: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* CreateDealGroupResponse
|
||||
*/
|
||||
export const zCreateDealGroupResponse = z.object({
|
||||
entity: zDealGroupSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
* CreateDealProductSchema
|
||||
*/
|
||||
@ -336,6 +375,7 @@ export const zDealSchema = z.object({
|
||||
createdAt: z.iso.datetime({
|
||||
offset: true,
|
||||
}),
|
||||
group: z.union([zDealGroupSchema, z.null()]),
|
||||
productsQuantity: z.optional(z.int()).default(0),
|
||||
totalPrice: z.optional(z.number()).default(0),
|
||||
client: z.optional(z.union([zClientSchema, z.null()])),
|
||||
@ -654,6 +694,20 @@ export const zDealProductAddKitResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* DealRemoveFromGroupRequest
|
||||
*/
|
||||
export const zDealRemoveFromGroupRequest = z.object({
|
||||
dealId: z.int(),
|
||||
});
|
||||
|
||||
/**
|
||||
* DealRemoveFromGroupResponse
|
||||
*/
|
||||
export const zDealRemoveFromGroupResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* DeleteBarcodeTemplateResponse
|
||||
*/
|
||||
@ -1034,6 +1088,29 @@ export const zUpdateClientResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* UpdateDealGroupSchema
|
||||
*/
|
||||
export const zUpdateDealGroupSchema = z.object({
|
||||
name: z.optional(z.union([z.string(), z.null()])),
|
||||
lexorank: z.optional(z.union([z.string(), z.null()])),
|
||||
statusId: z.optional(z.union([z.int(), z.null()])),
|
||||
});
|
||||
|
||||
/**
|
||||
* UpdateDealGroupRequest
|
||||
*/
|
||||
export const zUpdateDealGroupRequest = z.object({
|
||||
entity: zUpdateDealGroupSchema,
|
||||
});
|
||||
|
||||
/**
|
||||
* UpdateDealGroupResponse
|
||||
*/
|
||||
export const zUpdateDealGroupResponse = z.object({
|
||||
message: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* UpdateDealProductSchema
|
||||
*/
|
||||
@ -1412,6 +1489,52 @@ export const zUpdateDealData = z.object({
|
||||
*/
|
||||
export const zUpdateDealResponse2 = zUpdateDealResponse;
|
||||
|
||||
export const zUpdateDealGroupData = z.object({
|
||||
body: zUpdateDealGroupRequest,
|
||||
path: z.object({
|
||||
pk: z.int(),
|
||||
}),
|
||||
query: z.optional(z.never()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
export const zUpdateDealGroupResponse2 = zUpdateDealGroupResponse;
|
||||
|
||||
export const zCreateDealGroupData = z.object({
|
||||
body: zCreateDealGroupRequest,
|
||||
path: z.optional(z.never()),
|
||||
query: z.optional(z.never()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
export const zCreateDealGroupResponse2 = zCreateDealGroupResponse;
|
||||
|
||||
export const zRemoveDealData = z.object({
|
||||
body: zDealRemoveFromGroupRequest,
|
||||
path: z.optional(z.never()),
|
||||
query: z.optional(z.never()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
export const zRemoveDealResponse = zDealRemoveFromGroupResponse;
|
||||
|
||||
export const zAddDealData = z.object({
|
||||
body: zAddDealToGroupRequest,
|
||||
path: z.optional(z.never()),
|
||||
query: z.optional(z.never()),
|
||||
});
|
||||
|
||||
/**
|
||||
* Successful Response
|
||||
*/
|
||||
export const zAddDealResponse = zAddDealToGroupResponse;
|
||||
|
||||
export const zGetBuiltInModulesData = z.object({
|
||||
body: z.optional(z.never()),
|
||||
path: z.optional(z.never()),
|
||||
|
||||
7
src/types/GroupWithDealsSchema.ts
Normal file
7
src/types/GroupWithDealsSchema.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { DealGroupSchema, DealSchema } from "@/lib/client";
|
||||
|
||||
type GroupWithDealsSchema = DealGroupSchema & {
|
||||
items: DealSchema[];
|
||||
};
|
||||
|
||||
export default GroupWithDealsSchema;
|
||||
Reference in New Issue
Block a user