refactor: moved dnd part from Funnel into FunnelDnd

This commit is contained in:
2025-08-06 18:21:07 +04:00
parent 96c53380e0
commit 4b843d8e5d
23 changed files with 410 additions and 287 deletions

View File

@ -6,6 +6,7 @@ type Props = {
}; };
const DealCard = ({ deal }: Props) => { const DealCard = ({ deal }: Props) => {
console.log("deal");
return <Card>{deal.name}</Card>; return <Card>{deal.name}</Card>;
}; };

View File

@ -1,8 +1,8 @@
import React, { FC, useMemo } from "react"; import React, { FC, useMemo } from "react";
import { Box } from "@mantine/core"; import { Box } from "@mantine/core";
import DealCard from "@/app/deals/components/DealCard/DealCard"; import DealCard from "@/app/deals/components/DealCard/DealCard";
import SortableItem from "@/components/dnd/SortableItem";
import { DealSchema } from "@/lib/client"; import { DealSchema } from "@/lib/client";
import { SortableItem } from "@/components/dnd/SortableDnd/SortableItem";
type Props = { type Props = {
deal: DealSchema; deal: DealSchema;

View File

@ -1,36 +0,0 @@
import React from "react";
import { defaultDropAnimation, DragOverlay } from "@dnd-kit/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, StatusSchema } from "@/lib/client";
type Props = {
activeDeal: DealSchema | null;
activeStatus: StatusSchema | null;
};
const DndOverlay = ({ activeStatus, activeDeal }: Props) => {
const { deals } = useStatusesContext();
return (
<DragOverlay dropAnimation={defaultDropAnimation}>
<div style={{ cursor: "grabbing" }}>
{activeDeal ? (
<DealCard deal={activeDeal} />
) : activeStatus ? (
<StatusColumn
id={`${activeStatus.id}-status`}
status={activeStatus}
deals={deals.filter(
deal => deal.statusId === activeStatus.id
)}
isDragging
/>
) : null}
</div>
</DragOverlay>
);
};
export default DndOverlay;

View File

@ -0,0 +1,68 @@
"use client";
import React, { FC, ReactNode } from "react";
import DealCard from "@/app/deals/components/DealCard/DealCard";
import DealContainer from "@/app/deals/components/DealContainer/DealContainer";
import StatusColumnWrapper from "@/app/deals/components/StatusColumnWrapper/StatusColumnWrapper";
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
import useDealsAndStatusesDnd from "@/app/deals/hooks/useDealsAndStatusesDnd";
import FunnelDnd from "@/components/dnd/FunnelDnd/FunnelDnd";
import { DealSchema, StatusSchema } from "@/lib/client";
import { sortByLexorank } from "@/utils/lexorank";
const Funnel: FC = () => {
const { deals } = useStatusesContext();
const {
sortedStatuses,
handleDragStart,
handleDragOver,
handleDragEnd,
activeStatus,
activeDeal,
} = useDealsAndStatusesDnd();
return (
<FunnelDnd
containers={sortedStatuses}
items={deals}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
getContainerId={(status: StatusSchema) => `${status.id}-status`}
getItemsByContainer={(status: StatusSchema, items: DealSchema[]) =>
sortByLexorank(
items.filter(deal => deal.statusId === status.id)
)
}
renderContainer={(
status: StatusSchema,
funnelColumnComponent: ReactNode
) => (
<StatusColumnWrapper
status={status}
isDragging={activeStatus?.id === status.id}>
{funnelColumnComponent}
</StatusColumnWrapper>
)}
renderItem={(deal: DealSchema) => (
<DealContainer
key={deal.id}
deal={deal}
/>
)}
activeContainer={activeStatus}
activeItem={activeDeal}
renderItemOverlay={(deal: DealSchema) => <DealCard deal={deal} />}
renderContainerOverlay={(status: StatusSchema, children) => (
<StatusColumnWrapper
status={status}
isDragging>
{children}
</StatusColumnWrapper>
)}
/>
);
};
export default Funnel;

View File

@ -1,67 +0,0 @@
import React, { useMemo } from "react";
import { useDroppable } from "@dnd-kit/core";
import {
SortableContext,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { Box, Stack, Text } from "@mantine/core";
import DealContainer from "@/app/deals/components/DealContainer/DealContainer";
import { DealSchema, StatusSchema } from "@/lib/client";
import { sortByLexorank } from "@/utils/lexorank";
type Props = {
id: string;
status: StatusSchema;
deals: DealSchema[];
isDragging?: boolean;
};
const StatusColumn = ({ id, status, deals, isDragging }: Props) => {
const { setNodeRef } = useDroppable({ id });
const sortedDeals = useMemo(
() => sortByLexorank(deals.filter(deal => deal.statusId === status.id)),
[deals]
);
const columnBody = useMemo(() => {
return (
<SortableContext
id={id}
items={sortedDeals}
strategy={verticalListSortingStrategy}>
<Stack
gap={"xs"}
ref={setNodeRef}>
{sortedDeals.map(deal => (
<DealContainer
key={deal.id}
deal={deal}
/>
))}
</Stack>
</SortableContext>
);
}, [sortedDeals]);
return (
<Box
style={{
backgroundColor: "#eee",
padding: 2,
width: "15vw",
minWidth: 150,
}}>
<Text
style={{
cursor: "grab",
userSelect: "none",
opacity: isDragging ? 0.5 : 1,
}}>
{status.name}
</Text>
{columnBody}
</Box>
);
};
export default StatusColumn;

View File

@ -0,0 +1,33 @@
import React, { ReactNode } from "react";
import { Box, Text } from "@mantine/core";
import { StatusSchema } from "@/lib/client";
type Props = {
status: StatusSchema;
isDragging?: boolean;
children: ReactNode;
};
const StatusColumnWrapper = ({ status, children, isDragging = false }: Props) => {
return (
<Box
style={{
backgroundColor: "#eee",
padding: 2,
width: "15vw",
minWidth: 150,
}}>
<Text
style={{
cursor: "grab",
userSelect: "none",
opacity: isDragging ? 0.5 : 1,
}}>
{status.name}
</Text>
{children}
</Box>
);
};
export default StatusColumnWrapper;

View File

@ -1,76 +0,0 @@
"use client";
import { useMutation } from "@tanstack/react-query";
import StatusColumnsDnd from "@/app/deals/components/StatusColumnsDnd/StatusColumnsDnd";
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
import {
updateDealMutation,
updateStatusMutation,
} from "@/lib/client/@tanstack/react-query.gen";
import { notifications } from "@/lib/notifications";
const StatusColumns = () => {
const { refetchStatuses, refetchDeals } = useStatusesContext();
const updateStatus = useMutation({
...updateStatusMutation(),
onError: error => {
console.error(error);
notifications.error({
message: error.response?.data?.detail as string | undefined,
});
refetchStatuses();
},
});
const updateDeals = useMutation({
...updateDealMutation(),
onError: error => {
console.error(error);
notifications.error({
message: error.response?.data?.detail as string | undefined,
});
refetchDeals();
},
});
const onDealDragEnd = (
dealId: number,
statusId: number,
lexorank?: string
) => {
updateDeals.mutate({
path: {
dealId,
},
body: {
deal: {
statusId,
lexorank,
},
},
});
};
const onStatusDragEnd = (statusId: number, lexorank: string) => {
updateStatus.mutate({
path: {
statusId,
},
body: {
status: {
lexorank,
},
},
});
};
return (
<StatusColumnsDnd
onDealDragEnd={onDealDragEnd}
onStatusDragEnd={onStatusDragEnd}
/>
);
};
export default StatusColumns;

View File

@ -1,80 +0,0 @@
"use client";
import React, { FC } from "react";
import { closestCorners, DndContext } from "@dnd-kit/core";
import {
horizontalListSortingStrategy,
SortableContext,
} from "@dnd-kit/sortable";
import { Group, ScrollArea } from "@mantine/core";
import DndOverlay from "@/app/deals/components/DndOverlay/DndOverlay";
import StatusColumn from "@/app/deals/components/StatusColumn/StatusColumn";
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
import useDealsAndStatusesDnd from "@/app/deals/hooks/useDealsAndStatusesDnd";
import { SortableItem } from "@/components/dnd/SortableDnd/SortableItem";
import useDndSensors from "../../hooks/useSensors";
type Props = {
onDealDragEnd: (
dealId: number,
statusId: number,
lexorank?: string
) => void;
onStatusDragEnd: (statusId: number, lexorank: string) => void;
};
const StatusColumnsDnd: FC<Props> = props => {
const { deals } = useStatusesContext();
const {
sortedStatuses,
handleDragStart,
handleDragOver,
handleDragEnd,
activeStatus,
activeDeal,
} = useDealsAndStatusesDnd(props);
const sensors = useDndSensors();
return (
<ScrollArea
offsetScrollbars={"x"}
scrollbarSize={"0.5rem"}>
<DndContext
sensors={sensors}
collisionDetection={closestCorners}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}>
<SortableContext
items={sortedStatuses.map(status => `${status.id}-status`)}
strategy={horizontalListSortingStrategy}>
<Group
gap={"xs"}
wrap={"nowrap"}
align={"start"}>
{sortedStatuses.map(status => (
<SortableItem
key={status.id}
id={`${status.id}-status`}>
<StatusColumn
id={`${status.id}-status`}
status={status}
deals={deals}
isDragging={activeStatus?.id === status.id}
/>
</SortableItem>
))}
<DndOverlay
activeStatus={activeStatus}
activeDeal={activeDeal}
/>
</Group>
</SortableContext>
</DndContext>
</ScrollArea>
);
};
export default StatusColumnsDnd;

View File

@ -1,17 +1,43 @@
"use client"; "use client";
import React, { createContext, FC, useContext, useEffect } from "react"; import React, { createContext, FC, useContext, useEffect } from "react";
import { useMutation, UseMutationResult } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext"; import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
import { DealSchema, StatusSchema } from "@/lib/client";
import useDealsList from "@/hooks/useDealsList"; import useDealsList from "@/hooks/useDealsList";
import useStatusesList from "@/hooks/useStatusesList"; import useStatusesList from "@/hooks/useStatusesList";
import {
DealSchema,
HttpValidationError,
Options,
StatusSchema,
UpdateDealData,
UpdateDealResponse,
UpdateStatusData,
UpdateStatusResponse,
} from "@/lib/client";
import {
updateDealMutation,
updateStatusMutation,
} from "@/lib/client/@tanstack/react-query.gen";
import { notifications } from "@/lib/notifications";
type StatusesContextState = { type StatusesContextState = {
statuses: StatusSchema[]; statuses: StatusSchema[];
setStatuses: React.Dispatch<React.SetStateAction<StatusSchema[]>>; setStatuses: React.Dispatch<React.SetStateAction<StatusSchema[]>>;
updateStatus: UseMutationResult<
UpdateStatusResponse,
AxiosError<HttpValidationError>,
Options<UpdateStatusData>
>;
refetchStatuses: () => void;
deals: DealSchema[]; deals: DealSchema[];
setDeals: React.Dispatch<React.SetStateAction<DealSchema[]>>; setDeals: React.Dispatch<React.SetStateAction<DealSchema[]>>;
refetchStatuses: () => void; updateDeal: UseMutationResult<
UpdateDealResponse,
AxiosError<HttpValidationError>,
Options<UpdateDealData>
>;
refetchDeals: () => void; refetchDeals: () => void;
}; };
@ -39,12 +65,36 @@ const useStatusesContextState = () => {
refetchStatuses(); refetchStatuses();
}, [selectedBoard]); }, [selectedBoard]);
const updateStatus = useMutation({
...updateStatusMutation(),
onError: error => {
console.error(error);
notifications.error({
message: error.response?.data?.detail as string | undefined,
});
refetchStatuses();
},
});
const updateDeal = useMutation({
...updateDealMutation(),
onError: error => {
console.error(error);
notifications.error({
message: error.response?.data?.detail as string | undefined,
});
refetchDeals();
},
});
return { return {
statuses, statuses,
setStatuses, setStatuses,
updateStatus,
refetchStatuses,
deals, deals,
setDeals, setDeals,
refetchStatuses, updateDeal,
refetchDeals, refetchDeals,
}; };
}; };

View File

@ -7,19 +7,11 @@ import { getStatusId, isStatusId } from "@/app/deals/utils/statusId";
import { DealSchema, StatusSchema } from "@/lib/client"; import { DealSchema, StatusSchema } from "@/lib/client";
import { sortByLexorank } from "@/utils/lexorank"; import { sortByLexorank } from "@/utils/lexorank";
type Props = { const useDealsAndStatusesDnd = () => {
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 [activeDeal, setActiveDeal] = useState<DealSchema | null>(null);
const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null); const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null);
const { statuses, deals, setDeals, setStatuses } = useStatusesContext(); const { statuses, deals, setDeals, setStatuses, updateDeal, updateStatus } =
useStatusesContext();
const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]); const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]);
const { const {
@ -173,7 +165,20 @@ const useDealsAndStatusesDnd = (props: Props) => {
const newRank = getNewStatusRank(activeStatusId, overStatusId); const newRank = getNewStatusRank(activeStatusId, overStatusId);
if (!newRank) return; if (!newRank) return;
props.onStatusDragEnd?.(activeStatusId, newRank); onStatusDragEnd?.(activeStatusId, newRank);
};
const onStatusDragEnd = (statusId: number, lexorank: string) => {
updateStatus.mutate({
path: {
statusId,
},
body: {
status: {
lexorank,
},
},
});
}; };
const handleDealDragEnd = (activeId: number | string, over: Over) => { const handleDealDragEnd = (activeId: number | string, over: Over) => {
@ -189,7 +194,25 @@ const useDealsAndStatusesDnd = (props: Props) => {
); );
if (!overStatusId) return; if (!overStatusId) return;
props.onDealDragEnd(activeDealId, overStatusId, newLexorank); onDealDragEnd(activeDealId, overStatusId, newLexorank);
};
const onDealDragEnd = (
dealId: number,
statusId: number,
lexorank?: string
) => {
updateDeal.mutate({
path: {
dealId,
},
body: {
deal: {
statusId,
lexorank,
},
},
});
}; };
const handleDragStart = ({ active }: DragStartEvent) => { const handleDragStart = ({ active }: DragStartEvent) => {

View File

@ -1,7 +1,7 @@
import { Divider } from "@mantine/core"; import { Divider } from "@mantine/core";
import Boards from "@/app/deals/components/Boards/Boards"; import Boards from "@/app/deals/components/Boards/Boards";
import Funnel from "@/app/deals/components/Funnel/Funnel";
import Header from "@/app/deals/components/Header/Header"; import Header from "@/app/deals/components/Header/Header";
import StatusColumns from "@/app/deals/components/StatusColumns/StatusColumns";
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext"; import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext"; import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext";
import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext"; import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext";
@ -18,7 +18,7 @@ export default function DealsPage() {
<Boards /> <Boards />
<Divider my={"xl"} /> <Divider my={"xl"} />
<StatusesContextProvider> <StatusesContextProvider>
<StatusColumns /> <Funnel />
</StatusesContextProvider> </StatusesContextProvider>
</BoardsContextProvider> </BoardsContextProvider>
</ProjectsContextProvider> </ProjectsContextProvider>

View File

@ -0,0 +1,42 @@
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;

View File

@ -0,0 +1,126 @@
"use client";
import React, { ReactNode } from "react";
import {
closestCorners,
DndContext,
DragEndEvent,
DragOverEvent,
DragStartEvent,
} from "@dnd-kit/core";
import {
horizontalListSortingStrategy,
SortableContext,
} from "@dnd-kit/sortable";
import { Group, ScrollArea } from "@mantine/core";
import useDndSensors from "@/app/deals/hooks/useSensors";
import SortableItem from "@/components/dnd/SortableItem";
import { BaseDraggable } from "@/components/dnd/types/types";
import FunnelColumn from "./FunnelColumn";
import FunnelOverlay from "./FunnelOverlay";
type Props<TContainer, TItem> = {
containers: TContainer[];
items: TItem[];
onDragStart: (event: DragStartEvent) => void;
onDragOver: (event: DragOverEvent) => void;
onDragEnd: (event: DragEndEvent) => void;
renderContainer: (container: TContainer, children: ReactNode) => ReactNode;
renderContainerOverlay: (
container: TContainer,
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;
};
const FunnelDnd = <
TContainer extends BaseDraggable,
TItem extends BaseDraggable,
>({
containers,
items,
onDragStart,
onDragOver,
onDragEnd,
renderContainer,
renderContainerOverlay,
renderItem,
renderItemOverlay,
getContainerId,
getItemsByContainer,
activeContainer,
activeItem,
}: Props<TContainer, TItem>) => {
const sensors = useDndSensors();
return (
<ScrollArea
offsetScrollbars="x"
scrollbarSize="0.5rem">
<DndContext
sensors={sensors}
collisionDetection={closestCorners}
onDragStart={onDragStart}
onDragOver={onDragOver}
onDragEnd={onDragEnd}>
<SortableContext
items={containers.map(getContainerId)}
strategy={horizontalListSortingStrategy}>
<Group
gap="xs"
wrap="nowrap"
align="start">
{containers.map(container => {
const containerItems = getItemsByContainer(
container,
items
);
const containerId = getContainerId(container);
return (
<SortableItem
key={containerId}
id={containerId}>
{renderContainer(
container,
<FunnelColumn
id={containerId}
items={containerItems}
renderItem={renderItem}
/>
)}
</SortableItem>
);
})}
<FunnelOverlay
activeContainer={activeContainer}
activeItem={activeItem}
renderContainer={container => {
const containerItems = getItemsByContainer(
container,
items
);
const containerId = getContainerId(container);
return renderContainerOverlay(
container,
<FunnelColumn
id={containerId}
items={containerItems}
renderItem={renderItem}
/>
);
}}
renderItem={renderItemOverlay}
/>
</Group>
</SortableContext>
</DndContext>
</ScrollArea>
);
};
export default FunnelDnd;

View File

@ -0,0 +1,30 @@
import React, { ReactNode } from "react";
import { defaultDropAnimation, DragOverlay } from "@dnd-kit/core";
type Props<TContainer, TItem> = {
activeContainer: TContainer | null;
activeItem: TItem | null;
renderContainer: (container: TContainer) => ReactNode;
renderItem: (item: TItem) => ReactNode;
};
const FunnelOverlay = <TContainer, TItem>({
activeContainer,
activeItem,
renderContainer,
renderItem,
}: Props<TContainer, TItem>) => {
return (
<DragOverlay dropAnimation={defaultDropAnimation}>
<div style={{ cursor: "grabbing" }}>
{activeItem
? renderItem(activeItem)
: activeContainer
? renderContainer(activeContainer)
: null}
</div>
</DragOverlay>
);
};
export default FunnelOverlay;

View File

@ -0,0 +1,3 @@
import FunnelDnd from "./FunnelDnd";
export default FunnelDnd;

View File

@ -12,8 +12,8 @@ import { SortableContext } from "@dnd-kit/sortable";
import { LexoRank } from "lexorank"; import { LexoRank } from "lexorank";
import { Box, Group } from "@mantine/core"; import { Box, Group } from "@mantine/core";
import useDndSensors from "@/app/deals/hooks/useSensors"; import useDndSensors from "@/app/deals/hooks/useSensors";
import { SortableItem } from "@/components/dnd/SortableDnd/SortableItem";
import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay"; import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay";
import SortableItem from "@/components/dnd/SortableItem";
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank"; import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";
type BaseItem = { type BaseItem = {
@ -119,9 +119,7 @@ const SortableDnd = <T extends BaseItem>({
</Group> </Group>
</SortableContext> </SortableContext>
<SortableOverlay> <SortableOverlay>
<div style={{ cursor: "grabbing" }}>
{activeItem ? renderItem(activeItem) : null} {activeItem ? renderItem(activeItem) : null}
</div>
</SortableOverlay> </SortableOverlay>
</DndContext> </DndContext>
); );

View File

@ -18,7 +18,7 @@ const dropAnimationConfig: DropAnimation = {
export function SortableOverlay({ children }: PropsWithChildren) { export function SortableOverlay({ children }: PropsWithChildren) {
return ( return (
<DragOverlay dropAnimation={dropAnimationConfig}> <DragOverlay dropAnimation={dropAnimationConfig}>
{children} <div style={{ cursor: "grabbing" }}>{children}</div>
</DragOverlay> </DragOverlay>
); );
} }

View File

@ -1,5 +1,5 @@
import React, { CSSProperties, ReactNode, useContext } from "react"; import React, { CSSProperties, ReactNode, useContext } from "react";
import SortableItemContext from "@/components/dnd/SortableDnd/SortableItemContext"; import SortableItemContext from "@/components/dnd/SortableItem/SortableItemContext";
type Props = { type Props = {
children: ReactNode; children: ReactNode;

View File

@ -1,7 +1,7 @@
import React, { CSSProperties, PropsWithChildren, useMemo } from "react"; import React, { CSSProperties, PropsWithChildren, useMemo } from "react";
import { useSortable } from "@dnd-kit/sortable"; import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
import DragHandle from "@/components/dnd/SortableDnd/DragHandle"; import DragHandle from "./DragHandle";
import SortableItemContext from "./SortableItemContext"; import SortableItemContext from "./SortableItemContext";
type Props = { type Props = {
@ -10,7 +10,7 @@ type Props = {
dragHandleStyle?: CSSProperties; dragHandleStyle?: CSSProperties;
}; };
export const SortableItem = ({ const SortableItem = ({
children, children,
itemStyle, itemStyle,
id, id,
@ -52,3 +52,5 @@ export const SortableItem = ({
</SortableItemContext.Provider> </SortableItemContext.Provider>
); );
}; };
export default SortableItem;

View File

@ -0,0 +1,3 @@
import SortableItem from "./SortableItem";
export default SortableItem;

View File

@ -0,0 +1,3 @@
export type BaseDraggable = {
id: number;
};

View File

@ -2,5 +2,5 @@ import type { CreateClientConfig } from "@/lib/client/client.gen";
export const createClientConfig: CreateClientConfig = config => ({ export const createClientConfig: CreateClientConfig = config => ({
...config, ...config,
baseUrl: process.env.NEXT_PUBLIC_API_URL, baseURL: process.env.NEXT_PUBLIC_API_URL,
}); });