feat: boards with statuses fetch
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { Box } from "@mantine/core";
|
import { Box } from "@mantine/core";
|
||||||
import { BoardSchema } from "@/types/BoardSchema";
|
import { BoardSchema } from "@/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
board: BoardSchema;
|
board: BoardSchema;
|
||||||
|
|||||||
@ -4,11 +4,11 @@ import React from "react";
|
|||||||
import { ScrollArea } from "@mantine/core";
|
import { ScrollArea } from "@mantine/core";
|
||||||
import Board from "@/app/deals/components/Board/Board";
|
import Board from "@/app/deals/components/Board/Board";
|
||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
|
import { BoardSchema } from "@/client";
|
||||||
import SortableDnd from "@/components/SortableDnd";
|
import SortableDnd from "@/components/SortableDnd";
|
||||||
import { BoardSchema } from "@/types/BoardSchema";
|
|
||||||
|
|
||||||
const Boards = () => {
|
const Boards = () => {
|
||||||
const { boards } = useBoardsContext();
|
const { boards, setSelectedBoard } = useBoardsContext();
|
||||||
|
|
||||||
const renderBoard = (board: BoardSchema) => {
|
const renderBoard = (board: BoardSchema) => {
|
||||||
return <Board board={board} />;
|
return <Board board={board} />;
|
||||||
@ -18,6 +18,11 @@ const Boards = () => {
|
|||||||
console.log("onDragEnd:", itemId, newLexorank);
|
console.log("onDragEnd:", itemId, newLexorank);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectBoard = (board: BoardSchema) => {
|
||||||
|
console.log("Board selecting:", board);
|
||||||
|
setSelectedBoard(board);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
offsetScrollbars={"x"}
|
offsetScrollbars={"x"}
|
||||||
@ -28,6 +33,7 @@ const Boards = () => {
|
|||||||
initialItems={boards}
|
initialItems={boards}
|
||||||
renderItem={renderBoard}
|
renderItem={renderBoard}
|
||||||
onDragEnd={onDragEnd}
|
onDragEnd={onDragEnd}
|
||||||
|
onItemClick={selectBoard}
|
||||||
rowStyle={{ flexWrap: "nowrap" }}
|
rowStyle={{ flexWrap: "nowrap" }}
|
||||||
/>
|
/>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import DealCard from "@/app/deals/components/DealCard/DealCard";
|
|||||||
import StatusColumn from "@/app/deals/components/StatusColumn/StatusColumn";
|
import StatusColumn from "@/app/deals/components/StatusColumn/StatusColumn";
|
||||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||||
import { DealSchema } from "@/types/DealSchema";
|
import { DealSchema } from "@/types/DealSchema";
|
||||||
import { StatusSchema } from "@/types/StatusSchema";
|
import { StatusSchema } from "@/client";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
activeDeal: DealSchema | null;
|
activeDeal: DealSchema | null;
|
||||||
|
|||||||
@ -6,8 +6,8 @@ import {
|
|||||||
} from "@dnd-kit/sortable";
|
} from "@dnd-kit/sortable";
|
||||||
import { Box, Stack, Text } from "@mantine/core";
|
import { Box, Stack, Text } from "@mantine/core";
|
||||||
import DealContainer from "@/app/deals/components/DealContainer/DealContainer";
|
import DealContainer from "@/app/deals/components/DealContainer/DealContainer";
|
||||||
|
import { StatusSchema } from "@/client";
|
||||||
import { DealSchema } from "@/types/DealSchema";
|
import { DealSchema } from "@/types/DealSchema";
|
||||||
import { StatusSchema } from "@/types/StatusSchema";
|
|
||||||
import { sortByLexorank } from "@/utils/lexorank";
|
import { sortByLexorank } from "@/utils/lexorank";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import React, {
|
|||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
import useBoards from "@/app/deals/hooks/useBoards";
|
import { BoardSchema } from "@/client";
|
||||||
import { BoardSchema } from "@/types/BoardSchema";
|
import useBoardsList from "@/hooks/useBoardsList";
|
||||||
|
|
||||||
type BoardsContextState = {
|
type BoardsContextState = {
|
||||||
boards: BoardSchema[];
|
boards: BoardSchema[];
|
||||||
@ -21,11 +21,11 @@ type BoardsContextState = {
|
|||||||
const BoardsContext = createContext<BoardsContextState | undefined>(undefined);
|
const BoardsContext = createContext<BoardsContextState | undefined>(undefined);
|
||||||
|
|
||||||
const useBoardsContextState = () => {
|
const useBoardsContextState = () => {
|
||||||
const { boards, setBoards } = useBoards();
|
const { selectedProject: project } = useProjectsContext();
|
||||||
|
const { boards, setBoards } = useBoardsList({ projectId: project?.id });
|
||||||
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(
|
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
const { selectedProject: project } = useProjectsContext();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (boards.length > 0 && selectedBoard === null) {
|
if (boards.length > 0 && selectedBoard === null) {
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { createContext, FC, useContext } from "react";
|
import React, {
|
||||||
|
createContext,
|
||||||
|
FC,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
import useDeals from "@/app/deals/hooks/useDeals";
|
import useDeals from "@/app/deals/hooks/useDeals";
|
||||||
import useStatuses from "@/app/deals/hooks/useStatuses";
|
import { StatusSchema } from "@/client";
|
||||||
import { DealSchema } from "@/types/DealSchema";
|
import { DealSchema } from "@/types/DealSchema";
|
||||||
import { StatusSchema } from "@/types/StatusSchema";
|
|
||||||
|
|
||||||
type StatusesContextState = {
|
type StatusesContextState = {
|
||||||
statuses: StatusSchema[];
|
statuses: StatusSchema[];
|
||||||
@ -19,10 +24,14 @@ const StatusesContext = createContext<StatusesContextState | undefined>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const useStatusesContextState = () => {
|
const useStatusesContextState = () => {
|
||||||
const { statuses, setStatuses } = useStatuses();
|
const [statuses, setStatuses] = useState<StatusSchema[]>([]);
|
||||||
const { deals, setDeals } = useDeals();
|
const { deals, setDeals } = useDeals();
|
||||||
const { selectedBoard } = useBoardsContext();
|
const { selectedBoard } = useBoardsContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStatuses(selectedBoard?.statuses ?? []);
|
||||||
|
}, [selectedBoard]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statuses,
|
statuses,
|
||||||
setStatuses,
|
setStatuses,
|
||||||
|
|||||||
@ -1,19 +1,9 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { BoardSchema } from "@/types/BoardSchema";
|
import { BoardSchema } from "@/client";
|
||||||
|
|
||||||
const useBoards = () => {
|
const useBoards = () => {
|
||||||
const [boards, setBoards] = useState<BoardSchema[]>([]);
|
const [boards, setBoards] = useState<BoardSchema[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setBoards([
|
|
||||||
{ id: 1, name: "1 Item", rank: "0|aaaaaa:" },
|
|
||||||
{ id: 2, name: "2 Item", rank: "0|gggggg:" },
|
|
||||||
{ id: 3, name: "3 Item", rank: "0|mmmmmm:" },
|
|
||||||
{ id: 4, name: "4 Item", rank: "0|ssssss:" },
|
|
||||||
{ id: 5, name: "5 Item", rank: "0|zzzzzz:" },
|
|
||||||
]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boards,
|
boards,
|
||||||
setBoards,
|
setBoards,
|
||||||
|
|||||||
@ -9,19 +9,19 @@ const useDeals = () => {
|
|||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "Deal 1",
|
name: "Deal 1",
|
||||||
rank: "0|gggggg:",
|
lexorank: "0|gggggg:",
|
||||||
statusId: 1,
|
statusId: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: "Deal 2",
|
name: "Deal 2",
|
||||||
rank: "0|mmmmmm:",
|
lexorank: "0|mmmmmm:",
|
||||||
statusId: 1,
|
statusId: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: "Deal 3",
|
name: "Deal 3",
|
||||||
rank: "0|ssssss:",
|
lexorank: "0|ssssss:",
|
||||||
statusId: 2,
|
statusId: 2,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import { throttle } from "lodash";
|
|||||||
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||||
import useGetNewRank from "@/app/deals/hooks/useGetNewRank";
|
import useGetNewRank from "@/app/deals/hooks/useGetNewRank";
|
||||||
import { getStatusId, isStatusId } from "@/app/deals/utils/statusId";
|
import { getStatusId, isStatusId } from "@/app/deals/utils/statusId";
|
||||||
|
import { StatusSchema } from "@/client";
|
||||||
import { DealSchema } from "@/types/DealSchema";
|
import { DealSchema } from "@/types/DealSchema";
|
||||||
import { StatusSchema } from "@/types/StatusSchema";
|
|
||||||
import { sortByLexorank } from "@/utils/lexorank";
|
import { sortByLexorank } from "@/utils/lexorank";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -73,7 +73,7 @@ const useDnd = (props: Props) => {
|
|||||||
? {
|
? {
|
||||||
...deal,
|
...deal,
|
||||||
statusId: overStatusId,
|
statusId: overStatusId,
|
||||||
rank: newLexorank || deal.rank,
|
lexorank: newLexorank || deal.lexorank,
|
||||||
}
|
}
|
||||||
: deal
|
: deal
|
||||||
)
|
)
|
||||||
@ -100,7 +100,7 @@ const useDnd = (props: Props) => {
|
|||||||
throttledSetStatuses(statuses =>
|
throttledSetStatuses(statuses =>
|
||||||
statuses.map(status =>
|
statuses.map(status =>
|
||||||
status.id === activeStatusId
|
status.id === activeStatusId
|
||||||
? { ...status, rank: newRank }
|
? { ...status, lexorank: newRank }
|
||||||
: status
|
: status
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -20,10 +20,10 @@ const useGetNewRank = () => {
|
|||||||
: [overDealIndex, overDealIndex + 1];
|
: [overDealIndex, overDealIndex + 1];
|
||||||
|
|
||||||
const leftLexorank =
|
const leftLexorank =
|
||||||
leftIndex >= 0 ? LexoRank.parse(deals[leftIndex].rank) : null;
|
leftIndex >= 0 ? LexoRank.parse(deals[leftIndex].lexorank) : null;
|
||||||
const rightLexorank =
|
const rightLexorank =
|
||||||
rightIndex < deals.length
|
rightIndex < deals.length
|
||||||
? LexoRank.parse(deals[rightIndex].rank)
|
? LexoRank.parse(deals[rightIndex].lexorank)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||||
@ -35,9 +35,11 @@ const useGetNewRank = () => {
|
|||||||
) => {
|
) => {
|
||||||
const leftLexorank =
|
const leftLexorank =
|
||||||
overDealIndex > 0
|
overDealIndex > 0
|
||||||
? LexoRank.parse(statusDeals[overDealIndex - 1].rank)
|
? LexoRank.parse(statusDeals[overDealIndex - 1].lexorank)
|
||||||
: null;
|
: null;
|
||||||
const rightLexorank = LexoRank.parse(statusDeals[overDealIndex].rank);
|
const rightLexorank = LexoRank.parse(
|
||||||
|
statusDeals[overDealIndex].lexorank
|
||||||
|
);
|
||||||
|
|
||||||
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||||
};
|
};
|
||||||
@ -59,10 +61,12 @@ const useGetNewRank = () => {
|
|||||||
: [overIndex, overIndex + 1];
|
: [overIndex, overIndex + 1];
|
||||||
|
|
||||||
const leftLexorank =
|
const leftLexorank =
|
||||||
leftIndex >= 0 ? LexoRank.parse(statuses[leftIndex].rank) : null;
|
leftIndex >= 0
|
||||||
|
? LexoRank.parse(statuses[leftIndex].lexorank)
|
||||||
|
: null;
|
||||||
const rightLexorank =
|
const rightLexorank =
|
||||||
rightIndex < statuses.length
|
rightIndex < statuses.length
|
||||||
? LexoRank.parse(statuses[rightIndex].rank)
|
? LexoRank.parse(statuses[rightIndex].lexorank)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import { StatusSchema } from "@/types/StatusSchema";
|
|
||||||
|
|
||||||
const useStatuses = () => {
|
|
||||||
const [statuses, setStatuses] = useState<StatusSchema[]>([]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setStatuses([
|
|
||||||
{ id: 1, name: "Todo", rank: "0|aaaaaa:" },
|
|
||||||
{ id: 2, name: "In progress", rank: "0|gggggg:" },
|
|
||||||
{ id: 3, name: "Testing", rank: "0|mmmmmm:" },
|
|
||||||
{ id: 4, name: "Ready", rank: "0|ssssss:" },
|
|
||||||
]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statuses,
|
|
||||||
setStatuses,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useStatuses;
|
|
||||||
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import { queryOptions } from "@tanstack/react-query";
|
import { queryOptions } from "@tanstack/react-query";
|
||||||
import { client as _heyApiClient } from "../client.gen";
|
import { client as _heyApiClient } from "../client.gen";
|
||||||
import { getProjects, type Options } from "../sdk.gen";
|
import { getBoards, getProjects, type Options } from "../sdk.gen";
|
||||||
import type { GetProjectsData } from "../types.gen";
|
import type { GetBoardsData, GetProjectsData } from "../types.gen";
|
||||||
|
|
||||||
export type QueryKey<TOptions extends Options> = [
|
export type QueryKey<TOptions extends Options> = [
|
||||||
Pick<TOptions, "baseURL" | "body" | "headers" | "path" | "query"> & {
|
Pick<TOptions, "baseURL" | "body" | "headers" | "path" | "query"> & {
|
||||||
@ -61,3 +61,24 @@ export const getProjectsOptions = (options?: Options<GetProjectsData>) => {
|
|||||||
queryKey: getProjectsQueryKey(options),
|
queryKey: getProjectsQueryKey(options),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getBoardsQueryKey = (options: Options<GetBoardsData>) =>
|
||||||
|
createQueryKey("getBoards", options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Boards
|
||||||
|
*/
|
||||||
|
export const getBoardsOptions = (options: Options<GetBoardsData>) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await getBoards({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true,
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: getBoardsQueryKey(options),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
import type { Client, Options as ClientOptions, TDataShape } from "./client";
|
import type { Client, Options as ClientOptions, TDataShape } from "./client";
|
||||||
import { client as _heyApiClient } from "./client.gen";
|
import { client as _heyApiClient } from "./client.gen";
|
||||||
import type { GetProjectsData, GetProjectsResponses } from "./types.gen";
|
import type {
|
||||||
|
GetBoardsData,
|
||||||
|
GetBoardsErrors,
|
||||||
|
GetBoardsResponses,
|
||||||
|
GetProjectsData,
|
||||||
|
GetProjectsResponses,
|
||||||
|
} from "./types.gen";
|
||||||
|
|
||||||
export type Options<
|
export type Options<
|
||||||
TData extends TDataShape = TDataShape,
|
TData extends TDataShape = TDataShape,
|
||||||
@ -37,3 +43,20 @@ export const getProjects = <ThrowOnError extends boolean = false>(
|
|||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Boards
|
||||||
|
*/
|
||||||
|
export const getBoards = <ThrowOnError extends boolean = false>(
|
||||||
|
options: Options<GetBoardsData, ThrowOnError>
|
||||||
|
) => {
|
||||||
|
return (options.client ?? _heyApiClient).get<
|
||||||
|
GetBoardsResponses,
|
||||||
|
GetBoardsErrors,
|
||||||
|
ThrowOnError
|
||||||
|
>({
|
||||||
|
responseType: "json",
|
||||||
|
url: "/board/{project_id}",
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@ -1,5 +1,37 @@
|
|||||||
// This file is auto-generated by @hey-api/openapi-ts
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BoardSchema
|
||||||
|
*/
|
||||||
|
export type BoardSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Id
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank: string;
|
||||||
|
/**
|
||||||
|
* Statuses
|
||||||
|
*/
|
||||||
|
statuses: Array<StatusSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetBoardsResponse
|
||||||
|
*/
|
||||||
|
export type GetBoardsResponse = {
|
||||||
|
/**
|
||||||
|
* Boards
|
||||||
|
*/
|
||||||
|
boards: Array<BoardSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GetProjectsResponse
|
* GetProjectsResponse
|
||||||
*/
|
*/
|
||||||
@ -10,6 +42,16 @@ export type GetProjectsResponse = {
|
|||||||
projects: Array<ProjectSchema>;
|
projects: Array<ProjectSchema>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTPValidationError
|
||||||
|
*/
|
||||||
|
export type HttpValidationError = {
|
||||||
|
/**
|
||||||
|
* Detail
|
||||||
|
*/
|
||||||
|
detail?: Array<ValidationError>;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProjectSchema
|
* ProjectSchema
|
||||||
*/
|
*/
|
||||||
@ -24,6 +66,42 @@ export type ProjectSchema = {
|
|||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusSchema
|
||||||
|
*/
|
||||||
|
export type StatusSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Id
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValidationError
|
||||||
|
*/
|
||||||
|
export type ValidationError = {
|
||||||
|
/**
|
||||||
|
* Location
|
||||||
|
*/
|
||||||
|
loc: Array<string | number>;
|
||||||
|
/**
|
||||||
|
* Message
|
||||||
|
*/
|
||||||
|
msg: string;
|
||||||
|
/**
|
||||||
|
* Error Type
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type GetProjectsData = {
|
export type GetProjectsData = {
|
||||||
body?: never;
|
body?: never;
|
||||||
path?: never;
|
path?: never;
|
||||||
@ -41,6 +119,36 @@ export type GetProjectsResponses = {
|
|||||||
export type GetProjectsResponse2 =
|
export type GetProjectsResponse2 =
|
||||||
GetProjectsResponses[keyof GetProjectsResponses];
|
GetProjectsResponses[keyof GetProjectsResponses];
|
||||||
|
|
||||||
|
export type GetBoardsData = {
|
||||||
|
body?: never;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Project Id
|
||||||
|
*/
|
||||||
|
project_id: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: "/board/{project_id}";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetBoardsErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetBoardsError = GetBoardsErrors[keyof GetBoardsErrors];
|
||||||
|
|
||||||
|
export type GetBoardsResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: GetBoardsResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetBoardsResponse2 = GetBoardsResponses[keyof GetBoardsResponses];
|
||||||
|
|
||||||
export type ClientOptions = {
|
export type ClientOptions = {
|
||||||
baseURL: "http://localhost:8000" | (string & {});
|
baseURL: "http://localhost:8000" | (string & {});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,34 +7,25 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import {
|
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
|
||||||
Active,
|
import { SortableContext } from "@dnd-kit/sortable";
|
||||||
DndContext,
|
|
||||||
DragEndEvent,
|
|
||||||
KeyboardSensor,
|
|
||||||
PointerSensor,
|
|
||||||
useSensor,
|
|
||||||
useSensors,
|
|
||||||
} from "@dnd-kit/core";
|
|
||||||
import {
|
|
||||||
SortableContext,
|
|
||||||
sortableKeyboardCoordinates,
|
|
||||||
} from "@dnd-kit/sortable";
|
|
||||||
import { LexoRank } from "lexorank";
|
import { LexoRank } from "lexorank";
|
||||||
import { Group } from "@mantine/core";
|
import { Box, Group } from "@mantine/core";
|
||||||
|
import useDndSensors from "@/app/deals/hooks/useSensors";
|
||||||
import { SortableItem } from "@/components/SortableDnd/SortableItem";
|
import { SortableItem } from "@/components/SortableDnd/SortableItem";
|
||||||
import { SortableOverlay } from "@/components/SortableDnd/SortableOverlay";
|
import { SortableOverlay } from "@/components/SortableDnd/SortableOverlay";
|
||||||
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";
|
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";
|
||||||
|
|
||||||
type BaseItem = {
|
type BaseItem = {
|
||||||
id: number;
|
id: number;
|
||||||
rank: string;
|
lexorank: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props<T extends BaseItem> = {
|
type Props<T extends BaseItem> = {
|
||||||
initialItems: T[];
|
initialItems: T[];
|
||||||
renderItem: (item: T) => ReactNode;
|
renderItem: (item: T) => ReactNode;
|
||||||
onDragEnd: (itemId: number, newLexorank: string) => void;
|
onDragEnd: (itemId: number, newLexorank: string) => void;
|
||||||
|
onItemClick: (item: T) => void;
|
||||||
rowStyle?: CSSProperties;
|
rowStyle?: CSSProperties;
|
||||||
itemStyle?: CSSProperties;
|
itemStyle?: CSSProperties;
|
||||||
};
|
};
|
||||||
@ -43,6 +34,7 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
initialItems,
|
initialItems,
|
||||||
renderItem,
|
renderItem,
|
||||||
onDragEnd,
|
onDragEnd,
|
||||||
|
onItemClick,
|
||||||
rowStyle,
|
rowStyle,
|
||||||
itemStyle,
|
itemStyle,
|
||||||
}: Props<T>) => {
|
}: Props<T>) => {
|
||||||
@ -57,12 +49,7 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
setItems(sortByLexorank(initialItems));
|
setItems(sortByLexorank(initialItems));
|
||||||
}, [initialItems]);
|
}, [initialItems]);
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useDndSensors();
|
||||||
useSensor(PointerSensor),
|
|
||||||
useSensor(KeyboardSensor, {
|
|
||||||
coordinateGetter: sortableKeyboardCoordinates,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const onDragEndLocal = ({ active, over }: DragEndEvent) => {
|
const onDragEndLocal = ({ active, over }: DragEndEvent) => {
|
||||||
if (over && active.id !== over?.id && activeItem) {
|
if (over && active.id !== over?.id && activeItem) {
|
||||||
@ -81,10 +68,12 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const leftLexorank: LexoRank | null =
|
const leftLexorank: LexoRank | null =
|
||||||
leftIndex >= 0 ? LexoRank.parse(items[leftIndex].rank) : null;
|
leftIndex >= 0
|
||||||
|
? LexoRank.parse(items[leftIndex].lexorank)
|
||||||
|
: null;
|
||||||
const rightLexorank: LexoRank | null =
|
const rightLexorank: LexoRank | null =
|
||||||
rightIndex < items.length
|
rightIndex < items.length
|
||||||
? LexoRank.parse(items[rightIndex].rank)
|
? LexoRank.parse(items[rightIndex].lexorank)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const newLexorank = getNewLexorank(
|
const newLexorank = getNewLexorank(
|
||||||
@ -92,7 +81,7 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
rightLexorank
|
rightLexorank
|
||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
items[activeIndex].rank = newLexorank;
|
items[activeIndex].lexorank = newLexorank;
|
||||||
onDragEnd(items[activeIndex].id, newLexorank);
|
onDragEnd(items[activeIndex].id, newLexorank);
|
||||||
const sortedItems = sortByLexorank(items);
|
const sortedItems = sortByLexorank(items);
|
||||||
setItems([...sortedItems]);
|
setItems([...sortedItems]);
|
||||||
@ -112,12 +101,19 @@ const SortableDnd = <T extends BaseItem>({
|
|||||||
style={rowStyle}
|
style={rowStyle}
|
||||||
role="application">
|
role="application">
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<SortableItem
|
<Box
|
||||||
key={index}
|
key={index}
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onItemClick(item);
|
||||||
|
}}>
|
||||||
|
<SortableItem
|
||||||
itemStyle={itemStyle}
|
itemStyle={itemStyle}
|
||||||
id={item.id}>
|
id={item.id}>
|
||||||
{renderItem(item)}
|
{renderItem(item)}
|
||||||
</SortableItem>
|
</SortableItem>
|
||||||
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Group>
|
</Group>
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
|
|||||||
@ -9,7 +9,11 @@ type Props = {
|
|||||||
itemStyle?: CSSProperties;
|
itemStyle?: CSSProperties;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SortableItem = ({ children, id }: PropsWithChildren<Props>) => {
|
export const SortableItem = ({
|
||||||
|
children,
|
||||||
|
itemStyle,
|
||||||
|
id,
|
||||||
|
}: PropsWithChildren<Props>) => {
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
isDragging,
|
isDragging,
|
||||||
@ -33,6 +37,7 @@ export const SortableItem = ({ children, id }: PropsWithChildren<Props>) => {
|
|||||||
opacity: isDragging ? 0.4 : undefined,
|
opacity: isDragging ? 0.4 : undefined,
|
||||||
transform: CSS.Translate.toString(transform),
|
transform: CSS.Translate.toString(transform),
|
||||||
transition,
|
transition,
|
||||||
|
...itemStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
29
src/hooks/useBoardsList.ts
Normal file
29
src/hooks/useBoardsList.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { BoardSchema } from "@/client";
|
||||||
|
import { getBoardsOptions } from "@/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
projectId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useBoardsList = ({ projectId }: Props) => {
|
||||||
|
const [boards, setBoards] = useState<BoardSchema[]>([]);
|
||||||
|
|
||||||
|
const { data, refetch, isLoading } = useQuery({
|
||||||
|
...getBoardsOptions({ path: { project_id: projectId! } }),
|
||||||
|
enabled: projectId !== undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (projectId === undefined) {
|
||||||
|
setBoards([]);
|
||||||
|
} else if (data?.boards) {
|
||||||
|
setBoards(data.boards);
|
||||||
|
}
|
||||||
|
}, [data?.boards, projectId]);
|
||||||
|
|
||||||
|
return { boards, setBoards, refetch, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useBoardsList;
|
||||||
@ -1,5 +0,0 @@
|
|||||||
export type BoardSchema = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
rank: string;
|
|
||||||
};
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
export type DealSchema = {
|
export type DealSchema = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
rank: string;
|
lexorank: string;
|
||||||
statusId: number;
|
statusId: number;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
export type StatusSchema = {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
rank: string;
|
|
||||||
};
|
|
||||||
@ -1,17 +1,17 @@
|
|||||||
import { LexoRank } from "lexorank";
|
import { LexoRank } from "lexorank";
|
||||||
|
|
||||||
type LexorankSortable = {
|
type LexorankSortable = {
|
||||||
rank: string;
|
lexorank: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function compareByLexorank<T extends LexorankSortable>(
|
export function compareByLexorank<T extends LexorankSortable>(
|
||||||
a: T,
|
a: T,
|
||||||
b: T
|
b: T
|
||||||
): -1 | 1 | 0 {
|
): -1 | 1 | 0 {
|
||||||
if (a.rank < b.rank) {
|
if (a.lexorank < b.lexorank) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (a.rank > b.rank) {
|
if (a.lexorank > b.lexorank) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user