Compare commits
29 Commits
fe6e87f97c
...
detached
| Author | SHA1 | Date | |
|---|---|---|---|
| 095f3dae76 | |||
| cd034bcce6 | |||
| 316cca712d | |||
| 74f7cc7664 | |||
| 7bb8ab97c7 | |||
| abbf782945 | |||
| c13cc4a0a5 | |||
| 236c0dcf10 | |||
| c98a5cc811 | |||
| 24de9f5446 | |||
| f13417e73a | |||
| 2ae9c619c7 | |||
| 315e7db3db | |||
| 5435750fb5 | |||
| 624c94155c | |||
| 3e1d544b33 | |||
| 459487a896 | |||
| 8ae198897d | |||
| 586af488da | |||
| 943b2d63f5 | |||
| 921ab4c89f | |||
| d13997ba80 | |||
| 5137836265 | |||
| 5fe9ea6747 | |||
| 8af4a908e6 | |||
| 128a1b3c4f | |||
| cb168b6415 | |||
| b8d431ae99 | |||
| cb6a814918 |
1
.env.example
Normal file
1
.env.example
Normal file
@ -0,0 +1 @@
|
|||||||
|
NEXT_PUBLIC_API_URL=http://api.example.com/api
|
||||||
@ -1,35 +0,0 @@
|
|||||||
/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */
|
|
||||||
const config = {
|
|
||||||
printWidth: 100,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'es5',
|
|
||||||
plugins: ['@ianvs/prettier-plugin-sort-imports'],
|
|
||||||
importOrder: [
|
|
||||||
'.*styles.css$',
|
|
||||||
'',
|
|
||||||
'dayjs',
|
|
||||||
'^react$',
|
|
||||||
'^next$',
|
|
||||||
'^next/.*$',
|
|
||||||
'<BUILTIN_MODULES>',
|
|
||||||
'<THIRD_PARTY_MODULES>',
|
|
||||||
'^@mantine/(.*)$',
|
|
||||||
'^@mantinex/(.*)$',
|
|
||||||
'^@mantine-tests/(.*)$',
|
|
||||||
'^@docs/(.*)$',
|
|
||||||
'^@/.*$',
|
|
||||||
'^../(?!.*.css$).*$',
|
|
||||||
'^./(?!.*.css$).*$',
|
|
||||||
'\\.css$',
|
|
||||||
],
|
|
||||||
overrides: [
|
|
||||||
{
|
|
||||||
files: '*.mdx',
|
|
||||||
options: {
|
|
||||||
printWidth: 70,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
@ -17,5 +17,14 @@ export default tseslint.config(
|
|||||||
"react/jsx-curly-brace-presence": "off",
|
"react/jsx-curly-brace-presence": "off",
|
||||||
"curly": "off",
|
"curly": "off",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ["src/client/**/*.{ts,tsx}"],
|
||||||
|
rules: {
|
||||||
|
"import/no-useless-path-segments": "off",
|
||||||
|
},
|
||||||
|
linterOptions: {
|
||||||
|
reportUnusedDisableDirectives: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
28
openapi-ts.config.ts
Normal file
28
openapi-ts.config.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { defineConfig } from "@hey-api/openapi-ts";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
input: "http://localhost:8000/openapi.json",
|
||||||
|
output: "src/lib/client",
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
"@hey-api/client-axios",
|
||||||
|
"@tanstack/react-query",
|
||||||
|
"@hey-api/typescript",
|
||||||
|
{
|
||||||
|
name: "zod",
|
||||||
|
requests: true,
|
||||||
|
responses: true,
|
||||||
|
definitions: true,
|
||||||
|
metadata: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "@hey-api/sdk",
|
||||||
|
asClass: false,
|
||||||
|
validator: "zod",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "@hey-api/client-next",
|
||||||
|
runtimeConfigPath: "./src/hey-api-config.ts",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
151
package.json
151
package.json
@ -1,72 +1,83 @@
|
|||||||
{
|
{
|
||||||
"name": "crm-frontend",
|
"name": "crm-frontend",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
},
|
"generate-client": "openapi-ts && prettier --write ./src/client/**/*.ts && git add ./src/client"
|
||||||
"dependencies": {
|
},
|
||||||
"@mantine/core": "8.1.2",
|
"dependencies": {
|
||||||
"@mantine/form": "^8.1.3",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@mantine/hooks": "8.1.2",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@mantine/modals": "^8.2.1",
|
"@mantine/core": "8.1.2",
|
||||||
"@mantine/notifications": "^8.2.1",
|
"@mantine/form": "^8.1.3",
|
||||||
"@next/bundle-analyzer": "^15.3.3",
|
"@mantine/hooks": "8.1.2",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@mantine/modals": "^8.2.1",
|
||||||
"@tabler/icons-react": "^3.34.0",
|
"@mantine/notifications": "^8.2.1",
|
||||||
"@tailwindcss/postcss": "^4.1.11",
|
"@next/bundle-analyzer": "^15.3.3",
|
||||||
"axios": "^1.11.0",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"classnames": "^2.5.1",
|
"@tabler/icons-react": "^3.34.0",
|
||||||
"framer-motion": "^12.23.7",
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
"i18n-iso-countries": "^7.14.0",
|
"@tanstack/react-query": "^5.83.0",
|
||||||
"libphonenumber-js": "^1.12.10",
|
"axios": "^1.11.0",
|
||||||
"next": "15.3.3",
|
"classnames": "^2.5.1",
|
||||||
"react": "19.1.0",
|
"framer-motion": "^12.23.7",
|
||||||
"react-dom": "19.1.0",
|
"i18n-iso-countries": "^7.14.0",
|
||||||
"react-imask": "^7.6.1",
|
"lexorank": "^1.0.5",
|
||||||
"react-redux": "^9.2.0",
|
"libphonenumber-js": "^1.12.10",
|
||||||
"redux-persist": "^6.0.0",
|
"next": "15.3.3",
|
||||||
"sharp": "^0.34.3"
|
"react": "19.1.0",
|
||||||
},
|
"react-dom": "19.1.0",
|
||||||
"devDependencies": {
|
"react-imask": "^7.6.1",
|
||||||
"@babel/core": "^7.27.4",
|
"react-redux": "^9.2.0",
|
||||||
"@eslint/js": "^9.29.0",
|
"redux-persist": "^6.0.0",
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
"sharp": "^0.34.3",
|
||||||
"@storybook/nextjs": "^8.6.8",
|
"zod": "^4.0.14"
|
||||||
"@storybook/react": "^8.6.8",
|
},
|
||||||
"@testing-library/dom": "^10.4.0",
|
"devDependencies": {
|
||||||
"@testing-library/jest-dom": "^6.6.3",
|
"@babel/core": "^7.27.4",
|
||||||
"@testing-library/react": "^16.3.0",
|
"@eslint/js": "^9.29.0",
|
||||||
"@testing-library/user-event": "^14.6.1",
|
"@hey-api/client-axios": "^0.9.1",
|
||||||
"@types/eslint-plugin-jsx-a11y": "^6",
|
"@hey-api/client-next": "^0.5.1",
|
||||||
"@types/jest": "^29.5.14",
|
"@hey-api/openapi-ts": "^0.80.1",
|
||||||
"@types/node": "^22.13.11",
|
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
||||||
"@types/react": "19.1.8",
|
"@storybook/nextjs": "^8.6.8",
|
||||||
"@types/react-redux": "^7.1.34",
|
"@storybook/react": "^8.6.8",
|
||||||
"@types/redux-persist": "^4.3.1",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"autoprefixer": "^10.4.21",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"babel-loader": "^10.0.0",
|
"@testing-library/react": "^16.3.0",
|
||||||
"eslint": "^9.29.0",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"eslint-config-mantine": "^4.0.3",
|
"@types/eslint-plugin-jsx-a11y": "^6",
|
||||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
"@types/jest": "^29.5.14",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"@types/lodash": "^4.17.20",
|
||||||
"jest": "^30.0.0",
|
"@types/node": "^22.13.11",
|
||||||
"jest-environment-jsdom": "^30.0.0",
|
"@types/react": "19.1.8",
|
||||||
"postcss": "^8.5.6",
|
"@types/react-redux": "^7.1.34",
|
||||||
"postcss-preset-mantine": "1.17.0",
|
"@types/redux-persist": "^4.3.1",
|
||||||
"postcss-simple-vars": "^7.0.1",
|
"autoprefixer": "^10.4.21",
|
||||||
"prettier": "^3.5.3",
|
"babel-loader": "^10.0.0",
|
||||||
"storybook": "^8.6.8",
|
"eslint": "^9.29.0",
|
||||||
"storybook-dark-mode": "^4.0.2",
|
"eslint-config-mantine": "^4.0.3",
|
||||||
"stylelint": "^16.20.0",
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||||
"stylelint-config-standard-scss": "^15.0.1",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"tailwindcss": "^4.1.11",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"ts-jest": "^29.4.0",
|
"jest": "^30.0.0",
|
||||||
"typescript": "5.8.3",
|
"jest-environment-jsdom": "^30.0.0",
|
||||||
"typescript-eslint": "^8.34.0"
|
"postcss": "^8.5.6",
|
||||||
},
|
"postcss-preset-mantine": "1.17.0",
|
||||||
"packageManager": "yarn@4.9.2"
|
"postcss-simple-vars": "^7.0.1",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"storybook": "^8.6.8",
|
||||||
|
"storybook-dark-mode": "^4.0.2",
|
||||||
|
"stylelint": "^16.20.0",
|
||||||
|
"stylelint-config-standard-scss": "^15.0.1",
|
||||||
|
"tailwindcss": "^4.1.11",
|
||||||
|
"ts-jest": "^29.4.0",
|
||||||
|
"typescript": "5.8.3",
|
||||||
|
"typescript-eslint": "^8.34.0"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@4.9.2"
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/app/deals/components/Board/Board.tsx
Normal file
13
src/app/deals/components/Board/Board.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React, { FC } from "react";
|
||||||
|
import { Box } from "@mantine/core";
|
||||||
|
import { BoardSchema } from "@/lib/client";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
board: BoardSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Board: FC<Props> = ({ board }) => {
|
||||||
|
return <Box miw={100} style={{ borderWidth: 1, margin: 0 }}>{board.name}</Box>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Board;
|
||||||
65
src/app/deals/components/Boards/Boards.tsx
Normal file
65
src/app/deals/components/Boards/Boards.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { ScrollArea } from "@mantine/core";
|
||||||
|
import Board from "@/app/deals/components/Board/Board";
|
||||||
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
|
import { BoardSchema } from "@/lib/client";
|
||||||
|
import { updateBoardMutation } from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
import SortableDnd from "@/components/SortableDnd";
|
||||||
|
import { notifications } from "@/lib/notifications";
|
||||||
|
|
||||||
|
const Boards = () => {
|
||||||
|
const { boards, setSelectedBoard, refetchBoards } = useBoardsContext();
|
||||||
|
|
||||||
|
const updateBoard = useMutation({
|
||||||
|
...updateBoardMutation(),
|
||||||
|
onError: error => {
|
||||||
|
console.error(error);
|
||||||
|
notifications.error({
|
||||||
|
message: error.response?.data?.detail as string | undefined,
|
||||||
|
});
|
||||||
|
refetchBoards();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderBoard = (board: BoardSchema) => {
|
||||||
|
return <Board board={board} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDragEnd = (itemId: number, newLexorank: string) => {
|
||||||
|
updateBoard.mutate({
|
||||||
|
path: {
|
||||||
|
boardId: itemId,
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
board: {
|
||||||
|
lexorank: newLexorank,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectBoard = (board: BoardSchema) => {
|
||||||
|
setSelectedBoard(board);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollArea
|
||||||
|
offsetScrollbars={"x"}
|
||||||
|
scrollbars={"x"}
|
||||||
|
scrollbarSize={0}
|
||||||
|
w={"100%"}>
|
||||||
|
<SortableDnd
|
||||||
|
initialItems={boards}
|
||||||
|
renderItem={renderBoard}
|
||||||
|
onDragEnd={onDragEnd}
|
||||||
|
onItemClick={selectBoard}
|
||||||
|
rowStyle={{ flexWrap: "nowrap" }}
|
||||||
|
/>
|
||||||
|
</ScrollArea>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Boards;
|
||||||
12
src/app/deals/components/DealCard/DealCard.tsx
Normal file
12
src/app/deals/components/DealCard/DealCard.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Card } from "@mantine/core";
|
||||||
|
import { DealSchema } from "@/lib/client";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
deal: DealSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DealCard = ({ deal }: Props) => {
|
||||||
|
return <Card>{deal.name}</Card>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DealCard;
|
||||||
25
src/app/deals/components/DealContainer/DealContainer.tsx
Normal file
25
src/app/deals/components/DealContainer/DealContainer.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { FC, useMemo } from "react";
|
||||||
|
import { Box } from "@mantine/core";
|
||||||
|
import DealCard from "@/app/deals/components/DealCard/DealCard";
|
||||||
|
import { DealSchema } from "@/lib/client";
|
||||||
|
import { SortableItem } from "@/components/SortableDnd/SortableItem";
|
||||||
|
|
||||||
|
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}>
|
||||||
|
{dealBody}
|
||||||
|
</SortableItem>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DealContainer;
|
||||||
36
src/app/deals/components/DndOverlay/DndOverlay.tsx
Normal file
36
src/app/deals/components/DndOverlay/DndOverlay.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
24
src/app/deals/components/Header/Header.tsx
Normal file
24
src/app/deals/components/Header/Header.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Group } from "@mantine/core";
|
||||||
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
|
import ProjectSelect from "@/components/selects/ProjectSelect/ProjectSelect";
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
const { projects, setSelectedProject, selectedProject } =
|
||||||
|
useProjectsContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
justify={"flex-end"}
|
||||||
|
w={"100%"}>
|
||||||
|
<ProjectSelect
|
||||||
|
data={projects}
|
||||||
|
value={selectedProject}
|
||||||
|
onChange={value => value && setSelectedProject(value)}
|
||||||
|
/>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
37
src/app/deals/components/SortableItem/SortableItem.tsx
Normal file
37
src/app/deals/components/SortableItem/SortableItem.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SortableItem = ({ children, id }: Props) => {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
isDragging,
|
||||||
|
} = useSortable({ id });
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
transform: CSS.Transform.toString(transform),
|
||||||
|
transition,
|
||||||
|
opacity: isDragging ? 0 : 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SortableItem;
|
||||||
67
src/app/deals/components/StatusColumn/StatusColumn.tsx
Normal file
67
src/app/deals/components/StatusColumn/StatusColumn.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
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;
|
||||||
76
src/app/deals/components/StatusColumns/StatusColumns.tsx
Normal file
76
src/app/deals/components/StatusColumns/StatusColumns.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"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;
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
"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/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;
|
||||||
80
src/app/deals/contexts/BoardsContext.tsx
Normal file
80
src/app/deals/contexts/BoardsContext.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
FC,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { useProjectsContext } from "@/app/deals/contexts/ProjectsContext";
|
||||||
|
import { BoardSchema } from "@/lib/client";
|
||||||
|
import useBoardsList from "@/hooks/useBoardsList";
|
||||||
|
|
||||||
|
type BoardsContextState = {
|
||||||
|
boards: BoardSchema[];
|
||||||
|
setBoards: React.Dispatch<React.SetStateAction<BoardSchema[]>>;
|
||||||
|
selectedBoard: BoardSchema | null;
|
||||||
|
setSelectedBoard: React.Dispatch<React.SetStateAction<BoardSchema | null>>;
|
||||||
|
refetchBoards: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BoardsContext = createContext<BoardsContextState | undefined>(undefined);
|
||||||
|
|
||||||
|
const useBoardsContextState = () => {
|
||||||
|
const { selectedProject: project } = useProjectsContext();
|
||||||
|
const { boards, setBoards, refetch: refetchBoards } = useBoardsList({ projectId: project?.id });
|
||||||
|
const [selectedBoard, setSelectedBoard] = useState<BoardSchema | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (boards.length > 0 && selectedBoard === null) {
|
||||||
|
setSelectedBoard(boards[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedBoard) {
|
||||||
|
let newBoard = boards.find(board => board.id === selectedBoard.id);
|
||||||
|
|
||||||
|
if (!newBoard && boards.length > 0) {
|
||||||
|
newBoard = boards[0];
|
||||||
|
}
|
||||||
|
setSelectedBoard(newBoard ?? null);
|
||||||
|
}
|
||||||
|
}, [boards]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
boards,
|
||||||
|
setBoards,
|
||||||
|
selectedBoard,
|
||||||
|
setSelectedBoard,
|
||||||
|
refetchBoards,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type BoardsContextProviderProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BoardsContextProvider: FC<BoardsContextProviderProps> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const state = useBoardsContextState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BoardsContext.Provider value={state}>
|
||||||
|
{children}
|
||||||
|
</BoardsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBoardsContext = () => {
|
||||||
|
const context = useContext(BoardsContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useBoardsContext must be used within a BoardsContextProvider"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
77
src/app/deals/contexts/ProjectsContext.tsx
Normal file
77
src/app/deals/contexts/ProjectsContext.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, {
|
||||||
|
createContext,
|
||||||
|
FC,
|
||||||
|
useContext,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { ProjectSchema } from "@/lib/client";
|
||||||
|
import useProjectsList from "@/hooks/useProjectsList";
|
||||||
|
|
||||||
|
type ProjectsContextState = {
|
||||||
|
selectedProject: ProjectSchema | null;
|
||||||
|
setSelectedProject: React.Dispatch<
|
||||||
|
React.SetStateAction<ProjectSchema | null>
|
||||||
|
>;
|
||||||
|
projects: ProjectSchema[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProjectsContext = createContext<ProjectsContextState | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const useProjectsContextState = () => {
|
||||||
|
const { projects } = useProjectsList();
|
||||||
|
const [selectedProject, setSelectedProject] =
|
||||||
|
useState<ProjectSchema | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (projects.length > 0) {
|
||||||
|
if (selectedProject) {
|
||||||
|
setSelectedProject(
|
||||||
|
projects.find(
|
||||||
|
project => project.id === selectedProject.id
|
||||||
|
) ?? null
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelectedProject(projects[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelectedProject(null);
|
||||||
|
}, [projects]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
projects,
|
||||||
|
selectedProject,
|
||||||
|
setSelectedProject,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type ProjectsContextProviderProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProjectsContextProvider: FC<ProjectsContextProviderProps> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const state = useProjectsContextState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProjectsContext.Provider value={state}>
|
||||||
|
{children}
|
||||||
|
</ProjectsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useProjectsContext = () => {
|
||||||
|
const context = useContext(ProjectsContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useProjectsContext must be used within a ProjectsContextProvider"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
76
src/app/deals/contexts/StatusesContext.tsx
Normal file
76
src/app/deals/contexts/StatusesContext.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { createContext, FC, useContext, useEffect } from "react";
|
||||||
|
import { useBoardsContext } from "@/app/deals/contexts/BoardsContext";
|
||||||
|
import { DealSchema, StatusSchema } from "@/lib/client";
|
||||||
|
import useDealsList from "@/hooks/useDealsList";
|
||||||
|
import useStatusesList from "@/hooks/useStatusesList";
|
||||||
|
|
||||||
|
type StatusesContextState = {
|
||||||
|
statuses: StatusSchema[];
|
||||||
|
setStatuses: React.Dispatch<React.SetStateAction<StatusSchema[]>>;
|
||||||
|
deals: DealSchema[];
|
||||||
|
setDeals: React.Dispatch<React.SetStateAction<DealSchema[]>>;
|
||||||
|
refetchStatuses: () => void;
|
||||||
|
refetchDeals: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StatusesContext = createContext<StatusesContextState | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
|
const useStatusesContextState = () => {
|
||||||
|
const { selectedBoard } = useBoardsContext();
|
||||||
|
const {
|
||||||
|
statuses,
|
||||||
|
setStatuses,
|
||||||
|
refetch: refetchStatuses,
|
||||||
|
} = useStatusesList({
|
||||||
|
boardId: selectedBoard?.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
deals,
|
||||||
|
setDeals,
|
||||||
|
refetch: refetchDeals,
|
||||||
|
} = useDealsList({ boardId: selectedBoard?.id });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refetchStatuses();
|
||||||
|
}, [selectedBoard]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statuses,
|
||||||
|
setStatuses,
|
||||||
|
deals,
|
||||||
|
setDeals,
|
||||||
|
refetchStatuses,
|
||||||
|
refetchDeals,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type StatusesContextProviderProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StatusesContextProvider: FC<StatusesContextProviderProps> = ({
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const state = useStatusesContextState();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StatusesContext.Provider value={state}>
|
||||||
|
{children}
|
||||||
|
</StatusesContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useStatusesContext = () => {
|
||||||
|
const context = useContext(StatusesContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useStatusesContext must be used within a StatusesContextProvider"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
221
src/app/deals/hooks/useDealsAndStatusesDnd.ts
Normal file
221
src/app/deals/hooks/useDealsAndStatusesDnd.ts
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import { DragOverEvent, DragStartEvent, Over } from "@dnd-kit/core";
|
||||||
|
import { useDebouncedCallback } from "@mantine/hooks";
|
||||||
|
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||||
|
import useGetNewRank from "@/app/deals/hooks/useGetNewRank";
|
||||||
|
import { getStatusId, isStatusId } from "@/app/deals/utils/statusId";
|
||||||
|
import { DealSchema, StatusSchema } from "@/lib/client";
|
||||||
|
import { sortByLexorank } from "@/utils/lexorank";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onDealDragEnd: (
|
||||||
|
dealId: number,
|
||||||
|
statusId: number,
|
||||||
|
lexorank?: string
|
||||||
|
) => void;
|
||||||
|
onStatusDragEnd: (statusId: number, lexorank: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealsAndStatusesDnd = (props: Props) => {
|
||||||
|
const [activeDeal, setActiveDeal] = useState<DealSchema | null>(null);
|
||||||
|
const [activeStatus, setActiveStatus] = useState<StatusSchema | null>(null);
|
||||||
|
const { statuses, deals, setDeals, setStatuses } = useStatusesContext();
|
||||||
|
const sortedStatuses = useMemo(() => sortByLexorank(statuses), [statuses]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
getNewRankForSameStatus,
|
||||||
|
getNewRankForAnotherStatus,
|
||||||
|
getNewStatusRank,
|
||||||
|
} = useGetNewRank();
|
||||||
|
|
||||||
|
const debouncedSetStatuses = useDebouncedCallback(setStatuses, 200);
|
||||||
|
const debouncedSetDeals = useDebouncedCallback(setDeals, 200);
|
||||||
|
|
||||||
|
const getStatusByDealId = (dealId: number) => {
|
||||||
|
const deal = deals.find(deal => deal.id === dealId);
|
||||||
|
if (!deal) return;
|
||||||
|
return statuses.find(status => status.id === deal.statusId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = ({ active, over }: DragOverEvent) => {
|
||||||
|
if (!over) return;
|
||||||
|
const activeId = active.id as string | number;
|
||||||
|
|
||||||
|
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||||
|
handleColumnDragOver(activeId, over);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleDealDragOver(activeId, over);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDealDragOver = (activeId: string | number, over: Over) => {
|
||||||
|
const activeDealId = Number(activeId);
|
||||||
|
const activeStatusId = getStatusByDealId(activeDealId)?.id;
|
||||||
|
if (!activeStatusId) return;
|
||||||
|
|
||||||
|
const { overStatusId, newLexorank } = getDropTarget(
|
||||||
|
over.id,
|
||||||
|
activeDealId,
|
||||||
|
activeStatusId
|
||||||
|
);
|
||||||
|
if (!overStatusId) return;
|
||||||
|
|
||||||
|
debouncedSetDeals(deals =>
|
||||||
|
deals.map(deal =>
|
||||||
|
deal.id === activeDealId
|
||||||
|
? {
|
||||||
|
...deal,
|
||||||
|
statusId: overStatusId,
|
||||||
|
lexorank: newLexorank || deal.lexorank,
|
||||||
|
}
|
||||||
|
: deal
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleColumnDragOver = (activeId: string, over: Over) => {
|
||||||
|
const activeStatusId = getStatusId(activeId);
|
||||||
|
let overStatusId: number;
|
||||||
|
|
||||||
|
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||||
|
overStatusId = getStatusId(over.id);
|
||||||
|
} else {
|
||||||
|
const deal = deals.find(deal => deal.id === over.id);
|
||||||
|
if (!deal) return;
|
||||||
|
overStatusId = deal.statusId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overStatusId || activeStatusId === overStatusId) return;
|
||||||
|
|
||||||
|
const newRank = getNewStatusRank(activeStatusId, overStatusId);
|
||||||
|
if (!newRank) return;
|
||||||
|
|
||||||
|
debouncedSetStatuses(statuses =>
|
||||||
|
statuses.map(status =>
|
||||||
|
status.id === activeStatusId
|
||||||
|
? { ...status, lexorank: newRank }
|
||||||
|
: status
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDropTarget = (
|
||||||
|
overId: string | number,
|
||||||
|
activeDealId: number,
|
||||||
|
activeStatusId: number,
|
||||||
|
isOnDragEnd: boolean = false
|
||||||
|
) => {
|
||||||
|
if (typeof overId === "string") {
|
||||||
|
return {
|
||||||
|
overStatusId: getStatusId(overId),
|
||||||
|
newLexorank: undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const overDealId = Number(overId);
|
||||||
|
const overStatusId = getStatusByDealId(overDealId)?.id;
|
||||||
|
|
||||||
|
if (!overStatusId || (!isOnDragEnd && activeDealId === overDealId)) {
|
||||||
|
return { overStatusId: undefined, newLexorank: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusDeals = sortByLexorank(
|
||||||
|
deals.filter(deal => deal.statusId === overStatusId)
|
||||||
|
);
|
||||||
|
const overDealIndex = statusDeals.findIndex(
|
||||||
|
deal => deal.id === overDealId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (activeStatusId === overStatusId) {
|
||||||
|
const newLexorank = getNewRankForSameStatus(
|
||||||
|
statusDeals,
|
||||||
|
overDealIndex,
|
||||||
|
activeDealId
|
||||||
|
);
|
||||||
|
return { overStatusId, newLexorank };
|
||||||
|
}
|
||||||
|
|
||||||
|
const newLexorank = getNewRankForAnotherStatus(
|
||||||
|
statusDeals,
|
||||||
|
overDealIndex
|
||||||
|
);
|
||||||
|
return { overStatusId, newLexorank };
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragEnd = ({ active, over }: DragOverEvent) => {
|
||||||
|
setActiveDeal(null);
|
||||||
|
setActiveStatus(null);
|
||||||
|
if (!over) return;
|
||||||
|
|
||||||
|
const activeId: string | number = active.id;
|
||||||
|
|
||||||
|
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||||
|
handleStatusColumnDragEnd(activeId, over);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleDealDragEnd(activeId, over);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStatusColumnDragEnd = (activeId: string, over: Over) => {
|
||||||
|
const activeStatusId = getStatusId(activeId);
|
||||||
|
let overStatusId: number;
|
||||||
|
|
||||||
|
if (typeof over.id === "string" && isStatusId(over.id)) {
|
||||||
|
overStatusId = getStatusId(over.id);
|
||||||
|
} else {
|
||||||
|
const deal = deals.find(deal => deal.statusId === over.id);
|
||||||
|
if (!deal) return;
|
||||||
|
overStatusId = deal.statusId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overStatusId) return;
|
||||||
|
|
||||||
|
const newRank = getNewStatusRank(activeStatusId, overStatusId);
|
||||||
|
if (!newRank) return;
|
||||||
|
|
||||||
|
props.onStatusDragEnd?.(activeStatusId, newRank);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDealDragEnd = (activeId: number | string, over: Over) => {
|
||||||
|
const activeDealId = Number(activeId);
|
||||||
|
const activeStatusId = getStatusByDealId(activeDealId)?.id;
|
||||||
|
if (!activeStatusId) return;
|
||||||
|
|
||||||
|
const { overStatusId, newLexorank } = getDropTarget(
|
||||||
|
over.id,
|
||||||
|
activeDealId,
|
||||||
|
activeStatusId,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (!overStatusId) return;
|
||||||
|
|
||||||
|
props.onDealDragEnd(activeDealId, overStatusId, newLexorank);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragStart = ({ active }: DragStartEvent) => {
|
||||||
|
const activeId = active.id as string | number;
|
||||||
|
|
||||||
|
if (typeof activeId === "string" && isStatusId(activeId)) {
|
||||||
|
const statusId = getStatusId(activeId);
|
||||||
|
setActiveStatus(
|
||||||
|
statuses.find(status => status.id === statusId) ?? null
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveDeal(
|
||||||
|
deals.find(deal => deal.id === (activeId as number)) ?? null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sortedStatuses,
|
||||||
|
handleDragStart,
|
||||||
|
handleDragOver,
|
||||||
|
handleDragEnd,
|
||||||
|
activeStatus,
|
||||||
|
activeDeal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDealsAndStatusesDnd;
|
||||||
84
src/app/deals/hooks/useGetNewRank.ts
Normal file
84
src/app/deals/hooks/useGetNewRank.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { LexoRank } from "lexorank";
|
||||||
|
import { useStatusesContext } from "@/app/deals/contexts/StatusesContext";
|
||||||
|
import { DealSchema } from "@/lib/client";
|
||||||
|
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";
|
||||||
|
|
||||||
|
const useGetNewRank = () => {
|
||||||
|
const { statuses } = useStatusesContext();
|
||||||
|
|
||||||
|
const getNewRankForSameStatus = (
|
||||||
|
statusDeals: DealSchema[],
|
||||||
|
overDealIndex: number,
|
||||||
|
activeDealId: number
|
||||||
|
) => {
|
||||||
|
const activeDealIndex = statusDeals.findIndex(
|
||||||
|
deal => deal.id === activeDealId
|
||||||
|
);
|
||||||
|
const [leftIndex, rightIndex] =
|
||||||
|
overDealIndex < activeDealIndex
|
||||||
|
? [overDealIndex - 1, overDealIndex]
|
||||||
|
: [overDealIndex, overDealIndex + 1];
|
||||||
|
|
||||||
|
const leftLexorank =
|
||||||
|
leftIndex >= 0
|
||||||
|
? LexoRank.parse(statusDeals[leftIndex].lexorank)
|
||||||
|
: null;
|
||||||
|
const rightLexorank =
|
||||||
|
rightIndex < statusDeals.length
|
||||||
|
? LexoRank.parse(statusDeals[rightIndex].lexorank)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNewRankForAnotherStatus = (
|
||||||
|
statusDeals: DealSchema[],
|
||||||
|
overDealIndex: number
|
||||||
|
) => {
|
||||||
|
const leftLexorank =
|
||||||
|
overDealIndex > 0
|
||||||
|
? LexoRank.parse(statusDeals[overDealIndex - 1].lexorank)
|
||||||
|
: null;
|
||||||
|
const rightLexorank = LexoRank.parse(
|
||||||
|
statusDeals[overDealIndex].lexorank
|
||||||
|
);
|
||||||
|
|
||||||
|
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNewStatusRank = (activeStatusId: number, overStatusId: number) => {
|
||||||
|
const sortedStatusList = sortByLexorank(statuses);
|
||||||
|
const overIndex = sortedStatusList.findIndex(
|
||||||
|
s => s.id === overStatusId
|
||||||
|
);
|
||||||
|
const activeIndex = sortedStatusList.findIndex(
|
||||||
|
s => s.id === activeStatusId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (overIndex === -1 || activeIndex === -1) return null;
|
||||||
|
|
||||||
|
const [leftIndex, rightIndex] =
|
||||||
|
overIndex < activeIndex
|
||||||
|
? [overIndex - 1, overIndex]
|
||||||
|
: [overIndex, overIndex + 1];
|
||||||
|
|
||||||
|
const leftLexorank =
|
||||||
|
leftIndex >= 0
|
||||||
|
? LexoRank.parse(statuses[leftIndex].lexorank)
|
||||||
|
: null;
|
||||||
|
const rightLexorank =
|
||||||
|
rightIndex < statuses.length
|
||||||
|
? LexoRank.parse(statuses[rightIndex].lexorank)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return getNewLexorank(leftLexorank, rightLexorank).toString();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getNewRankForSameStatus,
|
||||||
|
getNewRankForAnotherStatus,
|
||||||
|
getNewStatusRank,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useGetNewRank;
|
||||||
26
src/app/deals/hooks/useSensors.ts
Normal file
26
src/app/deals/hooks/useSensors.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import {
|
||||||
|
KeyboardSensor,
|
||||||
|
PointerSensor,
|
||||||
|
TouchSensor,
|
||||||
|
useSensor,
|
||||||
|
useSensors,
|
||||||
|
} from "@dnd-kit/core";
|
||||||
|
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
||||||
|
|
||||||
|
const useDndSensors = () => {
|
||||||
|
const sensorOptions = {
|
||||||
|
activationConstraint: {
|
||||||
|
distance: 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return useSensors(
|
||||||
|
useSensor(PointerSensor, sensorOptions),
|
||||||
|
useSensor(KeyboardSensor, {
|
||||||
|
coordinateGetter: sortableKeyboardCoordinates,
|
||||||
|
}),
|
||||||
|
useSensor(TouchSensor, sensorOptions)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDndSensors;
|
||||||
28
src/app/deals/page.tsx
Normal file
28
src/app/deals/page.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Divider } from "@mantine/core";
|
||||||
|
import Boards from "@/app/deals/components/Boards/Boards";
|
||||||
|
import Header from "@/app/deals/components/Header/Header";
|
||||||
|
import StatusColumns from "@/app/deals/components/StatusColumns/StatusColumns";
|
||||||
|
import { BoardsContextProvider } from "@/app/deals/contexts/BoardsContext";
|
||||||
|
import { ProjectsContextProvider } from "@/app/deals/contexts/ProjectsContext";
|
||||||
|
import { StatusesContextProvider } from "@/app/deals/contexts/StatusesContext";
|
||||||
|
import PageBlock from "@/components/PageBlock/PageBlock";
|
||||||
|
import PageContainer from "@/components/PageContainer/PageContainer";
|
||||||
|
|
||||||
|
export default function DealsPage() {
|
||||||
|
return (
|
||||||
|
<PageContainer>
|
||||||
|
<PageBlock>
|
||||||
|
<ProjectsContextProvider>
|
||||||
|
<BoardsContextProvider>
|
||||||
|
<Header />
|
||||||
|
<Boards />
|
||||||
|
<Divider my={"xl"} />
|
||||||
|
<StatusesContextProvider>
|
||||||
|
<StatusColumns />
|
||||||
|
</StatusesContextProvider>
|
||||||
|
</BoardsContextProvider>
|
||||||
|
</ProjectsContextProvider>
|
||||||
|
</PageBlock>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
6
src/app/deals/utils/statusId.ts
Normal file
6
src/app/deals/utils/statusId.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const STATUS_POSTFIX = "-status";
|
||||||
|
|
||||||
|
export const isStatusId = (rawId: string) => rawId.endsWith(STATUS_POSTFIX);
|
||||||
|
|
||||||
|
export const getStatusId = (rawId: string) =>
|
||||||
|
Number(rawId.replace(STATUS_POSTFIX, ""));
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import "@mantine/core/styles.css";
|
import "@mantine/core/styles.css";
|
||||||
import "@mantine/notifications/styles.css";
|
import "@mantine/notifications/styles.css";
|
||||||
import React, { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import {
|
import {
|
||||||
ColorSchemeScript,
|
ColorSchemeScript,
|
||||||
mantineHtmlProps,
|
mantineHtmlProps,
|
||||||
@ -8,10 +8,11 @@ import {
|
|||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { theme } from "@/theme";
|
import { theme } from "@/theme";
|
||||||
import "@/app/global.css";
|
import "@/app/global.css";
|
||||||
import { Notifications } from "@mantine/notifications";
|
|
||||||
import ReduxProvider from "@/providers/ReduxProvider";
|
|
||||||
import { ModalsProvider } from "@mantine/modals";
|
import { ModalsProvider } from "@mantine/modals";
|
||||||
|
import { Notifications } from "@mantine/notifications";
|
||||||
import { modals } from "@/modals/modals";
|
import { modals } from "@/modals/modals";
|
||||||
|
import { ReactQueryProvider } from "@/providers/ReactQueryProvider";
|
||||||
|
import ReduxProvider from "@/providers/ReduxProvider";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "CRM LogiDex",
|
title: "CRM LogiDex",
|
||||||
@ -53,14 +54,16 @@ export default function RootLayout({ children }: Props) {
|
|||||||
<MantineProvider
|
<MantineProvider
|
||||||
theme={theme}
|
theme={theme}
|
||||||
defaultColorScheme={"auto"}>
|
defaultColorScheme={"auto"}>
|
||||||
<ReduxProvider>
|
<ReactQueryProvider>
|
||||||
<ModalsProvider
|
<ReduxProvider>
|
||||||
labels={{ confirm: "Да", cancel: "Нет" }}
|
<ModalsProvider
|
||||||
modals={modals}>
|
labels={{ confirm: "Да", cancel: "Нет" }}
|
||||||
{children}
|
modals={modals}>
|
||||||
</ModalsProvider>
|
{children}
|
||||||
</ReduxProvider>
|
</ModalsProvider>
|
||||||
<Notifications position="bottom-right" />
|
</ReduxProvider>
|
||||||
|
<Notifications position="bottom-right" />
|
||||||
|
</ReactQueryProvider>
|
||||||
</MantineProvider>
|
</MantineProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,11 +1,5 @@
|
|||||||
import { ColorSchemeToggle } from "@/components/ColorSchemeToggle/ColorSchemeToggle";
|
import { redirect } from "next/navigation";
|
||||||
import { Welcome } from "@/components/Welcome/Welcome";
|
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
return (
|
redirect("/deals");
|
||||||
<>
|
|
||||||
<Welcome />
|
|
||||||
<ColorSchemeToggle />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/components/Draggable/Draggable.tsx
Normal file
30
src/components/Draggable/Draggable.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React, { FC, ReactNode } from "react";
|
||||||
|
import { useDraggable } from "@dnd-kit/core";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Draggable: FC<Props> = props => {
|
||||||
|
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
||||||
|
id: "draggable",
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = transform
|
||||||
|
? {
|
||||||
|
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}
|
||||||
|
{...listeners}
|
||||||
|
{...attributes}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Draggable;
|
||||||
25
src/components/Droppable/Droppable.tsx
Normal file
25
src/components/Droppable/Droppable.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { FC, ReactNode } from "react";
|
||||||
|
import { useDroppable } from "@dnd-kit/core";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Droppable: FC<Props> = ({ children }) => {
|
||||||
|
const { isOver, setNodeRef } = useDroppable({
|
||||||
|
id: "droppable",
|
||||||
|
});
|
||||||
|
const style = {
|
||||||
|
color: isOver ? "green" : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Droppable;
|
||||||
41
src/components/PageBlock/PageBlock.module.css
Normal file
41
src/components/PageBlock/PageBlock.module.css
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
.container {
|
||||||
|
border-radius: rem(40);
|
||||||
|
background-color: white;
|
||||||
|
@mixin dark {
|
||||||
|
background-color: var(--mantine-color-dark-8);
|
||||||
|
box-shadow: 5px 5px 30px 1px var(--mantine-color-dark-6);
|
||||||
|
}
|
||||||
|
@mixin light {
|
||||||
|
box-shadow: 5px 5px 24px rgba(0, 0, 0, 0.16);
|
||||||
|
}
|
||||||
|
padding: rem(35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-full-height {
|
||||||
|
min-height: calc(100vh - (rem(20) * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-full-height-fixed {
|
||||||
|
height: calc(100vh - (rem(20) * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-no-border-radius {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-full-screen-mobile {
|
||||||
|
@media (max-width: 48em) {
|
||||||
|
min-height: 100vh;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
padding: rem(40) rem(20) rem(20);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 100;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/components/PageBlock/PageBlock.tsx
Normal file
36
src/components/PageBlock/PageBlock.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { CSSProperties, FC, ReactNode } from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import styles from "./PageBlock.module.css";
|
||||||
|
5
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
style?: CSSProperties;
|
||||||
|
fullHeight?: boolean;
|
||||||
|
fullHeightFixed?: boolean;
|
||||||
|
noBorderRadius?: boolean;
|
||||||
|
fullScreenMobile?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PageBlock: FC<Props> = ({
|
||||||
|
children,
|
||||||
|
style,
|
||||||
|
fullHeight = false,
|
||||||
|
fullHeightFixed = false,
|
||||||
|
noBorderRadius = false,
|
||||||
|
fullScreenMobile = false,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={style}
|
||||||
|
className={classNames(
|
||||||
|
styles.container,
|
||||||
|
fullHeight && styles["container-full-height"],
|
||||||
|
fullHeightFixed && styles["container-full-height-fixed"],
|
||||||
|
noBorderRadius && styles["container-no-border-radius"],
|
||||||
|
fullScreenMobile && styles["container-full-screen-mobile"]
|
||||||
|
)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default PageBlock;
|
||||||
7
src/components/PageContainer/PageContainer.module.css
Normal file
7
src/components/PageContainer/PageContainer.module.css
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: rem(10);
|
||||||
|
min-height: 86vh;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
24
src/components/PageContainer/PageContainer.tsx
Normal file
24
src/components/PageContainer/PageContainer.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { CSSProperties, FC, ReactNode } from "react";
|
||||||
|
import styles from "./PageContainer.module.css";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
style?: CSSProperties;
|
||||||
|
center?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PageContainer: FC<Props> = ({ children, style, center }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={styles.container}
|
||||||
|
style={{
|
||||||
|
...style,
|
||||||
|
alignItems: center ? "center" : "",
|
||||||
|
justifyContent: center ? "center" : "",
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PageContainer;
|
||||||
27
src/components/SortableDnd/DragHandle.tsx
Normal file
27
src/components/SortableDnd/DragHandle.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React, { CSSProperties, ReactNode, useContext } from "react";
|
||||||
|
import SortableItemContext from "@/components/SortableDnd/SortableItemContext";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
style?: CSSProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DragHandle = ({ children, style }: Props) => {
|
||||||
|
const { attributes, listeners, ref } = useContext(SortableItemContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...attributes}
|
||||||
|
{...listeners}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
cursor: "grab",
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
ref={ref}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragHandle;
|
||||||
130
src/components/SortableDnd/SortableDnd.tsx
Normal file
130
src/components/SortableDnd/SortableDnd.tsx
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, {
|
||||||
|
CSSProperties,
|
||||||
|
ReactNode,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
|
||||||
|
import { SortableContext } from "@dnd-kit/sortable";
|
||||||
|
import { LexoRank } from "lexorank";
|
||||||
|
import { Box, Group } from "@mantine/core";
|
||||||
|
import useDndSensors from "@/app/deals/hooks/useSensors";
|
||||||
|
import { SortableItem } from "@/components/SortableDnd/SortableItem";
|
||||||
|
import { SortableOverlay } from "@/components/SortableDnd/SortableOverlay";
|
||||||
|
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";
|
||||||
|
|
||||||
|
type BaseItem = {
|
||||||
|
id: number;
|
||||||
|
lexorank: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props<T extends BaseItem> = {
|
||||||
|
initialItems: T[];
|
||||||
|
renderItem: (item: T) => ReactNode;
|
||||||
|
onDragEnd: (itemId: number, newLexorank: string) => void;
|
||||||
|
onItemClick: (item: T) => void;
|
||||||
|
rowStyle?: CSSProperties;
|
||||||
|
itemStyle?: CSSProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SortableDnd = <T extends BaseItem>({
|
||||||
|
initialItems,
|
||||||
|
renderItem,
|
||||||
|
onDragEnd,
|
||||||
|
onItemClick,
|
||||||
|
rowStyle,
|
||||||
|
itemStyle,
|
||||||
|
}: Props<T>) => {
|
||||||
|
const [active, setActive] = useState<Active | null>(null);
|
||||||
|
const [items, setItems] = useState<T[]>([]);
|
||||||
|
const activeItem = useMemo(
|
||||||
|
() => initialItems.find(item => item.id === active?.id),
|
||||||
|
[active, items]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setItems(sortByLexorank(initialItems));
|
||||||
|
}, [initialItems]);
|
||||||
|
|
||||||
|
const sensors = useDndSensors();
|
||||||
|
|
||||||
|
const onDragEndLocal = ({ active, over }: DragEndEvent) => {
|
||||||
|
if (over && active.id !== over?.id && activeItem) {
|
||||||
|
const overIndex: number = items.findIndex(
|
||||||
|
({ id }) => id === over.id
|
||||||
|
);
|
||||||
|
const activeIndex: number = items.findIndex(
|
||||||
|
({ id }) => id === activeItem.id
|
||||||
|
);
|
||||||
|
|
||||||
|
let leftIndex = overIndex;
|
||||||
|
let rightIndex = overIndex + 1;
|
||||||
|
if (overIndex < activeIndex) {
|
||||||
|
leftIndex = overIndex - 1;
|
||||||
|
rightIndex = overIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftLexorank: LexoRank | null =
|
||||||
|
leftIndex >= 0
|
||||||
|
? LexoRank.parse(items[leftIndex].lexorank)
|
||||||
|
: null;
|
||||||
|
const rightLexorank: LexoRank | null =
|
||||||
|
rightIndex < items.length
|
||||||
|
? LexoRank.parse(items[rightIndex].lexorank)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const newLexorank = getNewLexorank(
|
||||||
|
leftLexorank,
|
||||||
|
rightLexorank
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
items[activeIndex].lexorank = newLexorank;
|
||||||
|
onDragEnd(items[activeIndex].id, newLexorank);
|
||||||
|
const sortedItems = sortByLexorank(items);
|
||||||
|
setItems([...sortedItems]);
|
||||||
|
}
|
||||||
|
setActive(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DndContext
|
||||||
|
sensors={sensors}
|
||||||
|
onDragStart={({ active }) => setActive(active)}
|
||||||
|
onDragEnd={onDragEndLocal}
|
||||||
|
onDragCancel={() => setActive(null)}>
|
||||||
|
<SortableContext items={items}>
|
||||||
|
<Group
|
||||||
|
gap={0}
|
||||||
|
style={rowStyle}
|
||||||
|
role="application">
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<Box
|
||||||
|
key={index}
|
||||||
|
onClick={e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
onItemClick(item);
|
||||||
|
}}>
|
||||||
|
<SortableItem
|
||||||
|
dragHandleStyle={{ cursor: "pointer" }}
|
||||||
|
itemStyle={itemStyle}
|
||||||
|
id={item.id}>
|
||||||
|
{renderItem(item)}
|
||||||
|
</SortableItem>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Group>
|
||||||
|
</SortableContext>
|
||||||
|
<SortableOverlay>
|
||||||
|
<div style={{ cursor: "grabbing" }}>
|
||||||
|
{activeItem ? renderItem(activeItem) : null}
|
||||||
|
</div>
|
||||||
|
</SortableOverlay>
|
||||||
|
</DndContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SortableDnd;
|
||||||
54
src/components/SortableDnd/SortableItem.tsx
Normal file
54
src/components/SortableDnd/SortableItem.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import React, { CSSProperties, PropsWithChildren, useMemo } from "react";
|
||||||
|
import { useSortable } from "@dnd-kit/sortable";
|
||||||
|
import { CSS } from "@dnd-kit/utilities";
|
||||||
|
import DragHandle from "@/components/SortableDnd/DragHandle";
|
||||||
|
import SortableItemContext from "./SortableItemContext";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: number | string;
|
||||||
|
itemStyle?: CSSProperties;
|
||||||
|
dragHandleStyle?: CSSProperties;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SortableItem = ({
|
||||||
|
children,
|
||||||
|
itemStyle,
|
||||||
|
id,
|
||||||
|
dragHandleStyle,
|
||||||
|
}: PropsWithChildren<Props>) => {
|
||||||
|
const {
|
||||||
|
attributes,
|
||||||
|
isDragging,
|
||||||
|
listeners,
|
||||||
|
setNodeRef,
|
||||||
|
setActivatorNodeRef,
|
||||||
|
transform,
|
||||||
|
transition,
|
||||||
|
} = useSortable({ id });
|
||||||
|
|
||||||
|
const context = useMemo(
|
||||||
|
() => ({
|
||||||
|
attributes,
|
||||||
|
listeners,
|
||||||
|
ref: setActivatorNodeRef,
|
||||||
|
}),
|
||||||
|
[attributes, listeners, setActivatorNodeRef]
|
||||||
|
);
|
||||||
|
|
||||||
|
const style: CSSProperties = {
|
||||||
|
opacity: isDragging ? 0.4 : undefined,
|
||||||
|
transform: CSS.Translate.toString(transform),
|
||||||
|
transition,
|
||||||
|
...itemStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SortableItemContext.Provider value={context}>
|
||||||
|
<div
|
||||||
|
ref={setNodeRef}
|
||||||
|
style={style}>
|
||||||
|
<DragHandle style={dragHandleStyle}>{children}</DragHandle>
|
||||||
|
</div>
|
||||||
|
</SortableItemContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
16
src/components/SortableDnd/SortableItemContext.tsx
Normal file
16
src/components/SortableDnd/SortableItemContext.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { DraggableSyntheticListeners } from "@dnd-kit/core";
|
||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
attributes: Record<string, any>;
|
||||||
|
listeners: DraggableSyntheticListeners;
|
||||||
|
ref: (node: HTMLElement | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SortableItemContext = createContext<Context>({
|
||||||
|
attributes: {},
|
||||||
|
listeners: undefined,
|
||||||
|
ref() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SortableItemContext;
|
||||||
24
src/components/SortableDnd/SortableOverlay.tsx
Normal file
24
src/components/SortableDnd/SortableOverlay.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import type { PropsWithChildren } from "react";
|
||||||
|
import {
|
||||||
|
defaultDropAnimationSideEffects,
|
||||||
|
DragOverlay,
|
||||||
|
DropAnimation,
|
||||||
|
} from "@dnd-kit/core";
|
||||||
|
|
||||||
|
const dropAnimationConfig: DropAnimation = {
|
||||||
|
sideEffects: defaultDropAnimationSideEffects({
|
||||||
|
styles: {
|
||||||
|
active: {
|
||||||
|
opacity: "0.4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SortableOverlay({ children }: PropsWithChildren) {
|
||||||
|
return (
|
||||||
|
<DragOverlay dropAnimation={dropAnimationConfig}>
|
||||||
|
{children}
|
||||||
|
</DragOverlay>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
src/components/SortableDnd/index.ts
Normal file
3
src/components/SortableDnd/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import SortableDnd from "@/components/SortableDnd/SortableDnd";
|
||||||
|
|
||||||
|
export default SortableDnd;
|
||||||
@ -1,31 +0,0 @@
|
|||||||
import { Anchor, Text, Title } from "@mantine/core";
|
|
||||||
|
|
||||||
export function Welcome() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Title
|
|
||||||
ta="center"
|
|
||||||
mt={100}
|
|
||||||
className={"font-bold underline"}>
|
|
||||||
Welcome to Mantine
|
|
||||||
</Title>
|
|
||||||
<Text
|
|
||||||
c="dimmed"
|
|
||||||
ta="center"
|
|
||||||
size="lg"
|
|
||||||
maw={580}
|
|
||||||
mx="auto"
|
|
||||||
mt="xl">
|
|
||||||
This starter Next.js project includes a minimal setup for server
|
|
||||||
side rendering, if you want to learn more on Mantine + Next.js
|
|
||||||
integration follow{" "}
|
|
||||||
<Anchor
|
|
||||||
href="https://mantine.dev/guides/next/"
|
|
||||||
size="lg">
|
|
||||||
this guide
|
|
||||||
</Anchor>
|
|
||||||
. To get started edit page.tsx file.
|
|
||||||
</Text>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
110
src/components/selects/ObjectSelect/ObjectSelect.tsx
Normal file
110
src/components/selects/ObjectSelect/ObjectSelect.tsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import { groupBy, omit } from "lodash";
|
||||||
|
import { Select, SelectProps } from "@mantine/core";
|
||||||
|
|
||||||
|
interface ObjectWithIdAndName {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SelectObjectType<T> = T;
|
||||||
|
|
||||||
|
type ControlledValueProps<T> = {
|
||||||
|
value: SelectObjectType<T>;
|
||||||
|
onChange: (value: SelectObjectType<T>) => void;
|
||||||
|
};
|
||||||
|
type CustomLabelAndKeyProps<T> = {
|
||||||
|
getLabelFn: (item: SelectObjectType<T>) => string;
|
||||||
|
getValueFn: (item: SelectObjectType<T>) => string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type RestProps<T> = {
|
||||||
|
defaultValue?: SelectObjectType<T>;
|
||||||
|
onChange: (value: SelectObjectType<T>) => void;
|
||||||
|
data: SelectObjectType<T>[];
|
||||||
|
groupBy?: (item: SelectObjectType<T>) => string;
|
||||||
|
filterBy?: (item: SelectObjectType<T>) => boolean;
|
||||||
|
};
|
||||||
|
const defaultGetLabelFn = <T extends { name: string }>(item: T): string => {
|
||||||
|
return item.name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultGetValueFn = <T extends { id: number }>(item: T): string => {
|
||||||
|
if (!item) return item;
|
||||||
|
return item.id.toString();
|
||||||
|
};
|
||||||
|
export type ObjectSelectProps<T> = (RestProps<T> &
|
||||||
|
Partial<ControlledValueProps<T>>) &
|
||||||
|
Omit<SelectProps, "value" | "onChange" | "data"> &
|
||||||
|
(T extends ObjectWithIdAndName
|
||||||
|
? Partial<CustomLabelAndKeyProps<T>>
|
||||||
|
: CustomLabelAndKeyProps<T>);
|
||||||
|
|
||||||
|
const ObjectSelect = <T,>(props: ObjectSelectProps<T>) => {
|
||||||
|
const isControlled = "value" in props;
|
||||||
|
const haveGetValueFn = "getValueFn" in props;
|
||||||
|
const haveGetLabelFn = "getLabelFn" in props;
|
||||||
|
const [internalValue, setInternalValue] = useState<
|
||||||
|
SelectObjectType<T> | undefined
|
||||||
|
>(props.defaultValue);
|
||||||
|
|
||||||
|
const value = isControlled ? props.value : internalValue;
|
||||||
|
|
||||||
|
const getValueFn =
|
||||||
|
(haveGetValueFn && props.getValueFn) || defaultGetValueFn;
|
||||||
|
const getLabelFn =
|
||||||
|
(haveGetLabelFn && props.getLabelFn) || defaultGetLabelFn;
|
||||||
|
|
||||||
|
const data = useMemo(() => {
|
||||||
|
const propsData = props.filterBy
|
||||||
|
? props.data.filter(props.filterBy)
|
||||||
|
: props.data;
|
||||||
|
if (props.groupBy) {
|
||||||
|
const groupedData = groupBy(propsData, props.groupBy);
|
||||||
|
return Object.entries(groupedData).map(([group, items]) => ({
|
||||||
|
group,
|
||||||
|
items: items.map(item => ({
|
||||||
|
label: getLabelFn(item),
|
||||||
|
value: getValueFn(item),
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return propsData.map(item => ({
|
||||||
|
label: getLabelFn(item),
|
||||||
|
value: getValueFn(item),
|
||||||
|
}));
|
||||||
|
}, [props.data, props.groupBy]);
|
||||||
|
|
||||||
|
const handleOnChange = (event: string | null) => {
|
||||||
|
if (!event) return;
|
||||||
|
const object = props.data.find(item => event === getValueFn(item));
|
||||||
|
if (!object) return;
|
||||||
|
if (isControlled) {
|
||||||
|
props.onChange(object);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setInternalValue(object);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isControlled || !internalValue) return;
|
||||||
|
props.onChange(internalValue);
|
||||||
|
}, [internalValue]);
|
||||||
|
|
||||||
|
const restProps = omit(props, [
|
||||||
|
"filterBy",
|
||||||
|
"groupBy",
|
||||||
|
"getValueFn",
|
||||||
|
"getLabelFn",
|
||||||
|
]);
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
{...restProps}
|
||||||
|
value={value && getValueFn(value)}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ObjectSelect;
|
||||||
28
src/components/selects/ProjectSelect/ProjectSelect.tsx
Normal file
28
src/components/selects/ProjectSelect/ProjectSelect.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { FC } from "react";
|
||||||
|
import { ProjectSchema } from "@/lib/client";
|
||||||
|
import ObjectSelect, {
|
||||||
|
ObjectSelectProps,
|
||||||
|
} from "@/components/selects/ObjectSelect/ObjectSelect";
|
||||||
|
|
||||||
|
type Props = Omit<
|
||||||
|
ObjectSelectProps<ProjectSchema | null>,
|
||||||
|
"getLabelFn" | "getValueFn"
|
||||||
|
>;
|
||||||
|
|
||||||
|
const ProjectSelect: FC<Props> = ({ data, ...props }) => {
|
||||||
|
const onClear = () => props.onChange(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ObjectSelect
|
||||||
|
data={data}
|
||||||
|
searchable
|
||||||
|
placeholder={"Выберите проект"}
|
||||||
|
onClear={onClear}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProjectSelect;
|
||||||
6
src/hey-api-config.ts
Normal file
6
src/hey-api-config.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import type {CreateClientConfig} from "@/lib/client/client.gen";
|
||||||
|
|
||||||
|
export const createClientConfig: CreateClientConfig = config => ({
|
||||||
|
...config,
|
||||||
|
baseUrl: process.env.NEXT_PUBLIC_API_URL,
|
||||||
|
});
|
||||||
31
src/hooks/useBoardsList.ts
Normal file
31
src/hooks/useBoardsList.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { BoardSchema } from "@/lib/client";
|
||||||
|
import { getBoardsOptions } from "@/lib/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: { projectId: projectId! } }),
|
||||||
|
enabled: projectId !== undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (projectId === undefined) {
|
||||||
|
setBoards([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data?.boards) {
|
||||||
|
setBoards(data.boards);
|
||||||
|
}
|
||||||
|
}, [data?.boards, projectId]);
|
||||||
|
|
||||||
|
return { boards, setBoards, refetch, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useBoardsList;
|
||||||
31
src/hooks/useDealsList.ts
Normal file
31
src/hooks/useDealsList.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { DealSchema } from "@/lib/client";
|
||||||
|
import { getDealsOptions } from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
boardId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useDealsList = ({ boardId }: Props) => {
|
||||||
|
const [deals, setDeals] = useState<DealSchema[]>([]);
|
||||||
|
|
||||||
|
const { data, refetch, isLoading } = useQuery({
|
||||||
|
...getDealsOptions({ path: { boardId: boardId! } }),
|
||||||
|
enabled: boardId !== undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (boardId === undefined) {
|
||||||
|
setDeals([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data?.deals) {
|
||||||
|
setDeals(data.deals);
|
||||||
|
}
|
||||||
|
}, [data?.deals, boardId]);
|
||||||
|
|
||||||
|
return { deals, setDeals, refetch, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDealsList;
|
||||||
12
src/hooks/useProjectsList.ts
Normal file
12
src/hooks/useProjectsList.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { getProjectsOptions } from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
const useProjectsList = () => {
|
||||||
|
const { data, refetch, isLoading } = useQuery({
|
||||||
|
...getProjectsOptions(),
|
||||||
|
});
|
||||||
|
const projects = !data ? [] : data.projects;
|
||||||
|
return { projects, refetch, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useProjectsList;
|
||||||
31
src/hooks/useStatusesList.ts
Normal file
31
src/hooks/useStatusesList.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { StatusSchema } from "@/lib/client";
|
||||||
|
import { getStatusesOptions } from "@/lib/client/@tanstack/react-query.gen";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
boardId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useStatusesList = ({ boardId }: Props) => {
|
||||||
|
const [statuses, setStatuses] = useState<StatusSchema[]>([]);
|
||||||
|
|
||||||
|
const { data, refetch, isLoading } = useQuery({
|
||||||
|
...getStatusesOptions({ path: { boardId: boardId! } }),
|
||||||
|
enabled: boardId !== undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (boardId === undefined) {
|
||||||
|
setStatuses([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data?.statuses) {
|
||||||
|
setStatuses(data.statuses);
|
||||||
|
}
|
||||||
|
}, [data?.statuses, boardId]);
|
||||||
|
|
||||||
|
return { statuses, setStatuses, refetch, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useStatusesList;
|
||||||
169
src/lib/client/@tanstack/react-query.gen.ts
Normal file
169
src/lib/client/@tanstack/react-query.gen.ts
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
import { type Options, getBoards, updateBoard, getDeals, updateDeal, getProjects, getStatuses, updateStatus } from '../sdk.gen';
|
||||||
|
import { queryOptions, type UseMutationOptions } from '@tanstack/react-query';
|
||||||
|
import type { GetBoardsData, UpdateBoardData, UpdateBoardError, UpdateBoardResponse2, GetDealsData, UpdateDealData, UpdateDealError, UpdateDealResponse2, GetProjectsData, GetStatusesData, UpdateStatusData, UpdateStatusError, UpdateStatusResponse2 } from '../types.gen';
|
||||||
|
import type { AxiosError } from 'axios';
|
||||||
|
import { client as _heyApiClient } from '../client.gen';
|
||||||
|
|
||||||
|
export type QueryKey<TOptions extends Options> = [
|
||||||
|
Pick<TOptions, 'baseURL' | 'body' | 'headers' | 'path' | 'query'> & {
|
||||||
|
_id: string;
|
||||||
|
_infinite?: boolean;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const createQueryKey = <TOptions extends Options>(id: string, options?: TOptions, infinite?: boolean): [
|
||||||
|
QueryKey<TOptions>[0]
|
||||||
|
] => {
|
||||||
|
const params: QueryKey<TOptions>[0] = { _id: id, baseURL: options?.baseURL || (options?.client ?? _heyApiClient).getConfig().baseURL } as QueryKey<TOptions>[0];
|
||||||
|
if (infinite) {
|
||||||
|
params._infinite = infinite;
|
||||||
|
}
|
||||||
|
if (options?.body) {
|
||||||
|
params.body = options.body;
|
||||||
|
}
|
||||||
|
if (options?.headers) {
|
||||||
|
params.headers = options.headers;
|
||||||
|
}
|
||||||
|
if (options?.path) {
|
||||||
|
params.path = options.path;
|
||||||
|
}
|
||||||
|
if (options?.query) {
|
||||||
|
params.query = options.query;
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
params
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Board
|
||||||
|
*/
|
||||||
|
export const updateBoardMutation = (options?: Partial<Options<UpdateBoardData>>): UseMutationOptions<UpdateBoardResponse2, AxiosError<UpdateBoardError>, Options<UpdateBoardData>> => {
|
||||||
|
const mutationOptions: UseMutationOptions<UpdateBoardResponse2, AxiosError<UpdateBoardError>, Options<UpdateBoardData>> = {
|
||||||
|
mutationFn: async (localOptions) => {
|
||||||
|
const { data } = await updateBoard({
|
||||||
|
...options,
|
||||||
|
...localOptions,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return mutationOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDealsQueryKey = (options: Options<GetDealsData>) => createQueryKey('getDeals', options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Deals
|
||||||
|
*/
|
||||||
|
export const getDealsOptions = (options: Options<GetDealsData>) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await getDeals({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: getDealsQueryKey(options)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Deal
|
||||||
|
*/
|
||||||
|
export const updateDealMutation = (options?: Partial<Options<UpdateDealData>>): UseMutationOptions<UpdateDealResponse2, AxiosError<UpdateDealError>, Options<UpdateDealData>> => {
|
||||||
|
const mutationOptions: UseMutationOptions<UpdateDealResponse2, AxiosError<UpdateDealError>, Options<UpdateDealData>> = {
|
||||||
|
mutationFn: async (localOptions) => {
|
||||||
|
const { data } = await updateDeal({
|
||||||
|
...options,
|
||||||
|
...localOptions,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return mutationOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getProjectsQueryKey = (options?: Options<GetProjectsData>) => createQueryKey('getProjects', options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Projects
|
||||||
|
*/
|
||||||
|
export const getProjectsOptions = (options?: Options<GetProjectsData>) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await getProjects({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: getProjectsQueryKey(options)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStatusesQueryKey = (options: Options<GetStatusesData>) => createQueryKey('getStatuses', options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Statuses
|
||||||
|
*/
|
||||||
|
export const getStatusesOptions = (options: Options<GetStatusesData>) => {
|
||||||
|
return queryOptions({
|
||||||
|
queryFn: async ({ queryKey, signal }) => {
|
||||||
|
const { data } = await getStatuses({
|
||||||
|
...options,
|
||||||
|
...queryKey[0],
|
||||||
|
signal,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
queryKey: getStatusesQueryKey(options)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Status
|
||||||
|
*/
|
||||||
|
export const updateStatusMutation = (options?: Partial<Options<UpdateStatusData>>): UseMutationOptions<UpdateStatusResponse2, AxiosError<UpdateStatusError>, Options<UpdateStatusData>> => {
|
||||||
|
const mutationOptions: UseMutationOptions<UpdateStatusResponse2, AxiosError<UpdateStatusError>, Options<UpdateStatusData>> = {
|
||||||
|
mutationFn: async (localOptions) => {
|
||||||
|
const { data } = await updateStatus({
|
||||||
|
...options,
|
||||||
|
...localOptions,
|
||||||
|
throwOnError: true
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return mutationOptions;
|
||||||
|
};
|
||||||
19
src/lib/client/client.gen.ts
Normal file
19
src/lib/client/client.gen.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
import type { ClientOptions } from './types.gen';
|
||||||
|
import { type Config, type ClientOptions as DefaultClientOptions, createClient, createConfig } from './client';
|
||||||
|
import { createClientConfig } from '../../hey-api-config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `createClientConfig()` function will be called on client initialization
|
||||||
|
* and the returned object will become the client's initial configuration.
|
||||||
|
*
|
||||||
|
* You may want to initialize your client this way instead of calling
|
||||||
|
* `setConfig()`. This is useful for example if you're using Next.js
|
||||||
|
* to ensure your client always has the correct values.
|
||||||
|
*/
|
||||||
|
export type CreateClientConfig<T extends DefaultClientOptions = ClientOptions> = (override?: Config<DefaultClientOptions & T>) => Config<Required<DefaultClientOptions> & T>;
|
||||||
|
|
||||||
|
export const client = createClient(createClientConfig(createConfig<ClientOptions>({
|
||||||
|
baseURL: '/api'
|
||||||
|
})));
|
||||||
115
src/lib/client/client/client.ts
Normal file
115
src/lib/client/client/client.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import type { AxiosError, RawAxiosRequestHeaders } from 'axios';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
import type { Client, Config } from './types';
|
||||||
|
import {
|
||||||
|
buildUrl,
|
||||||
|
createConfig,
|
||||||
|
mergeConfigs,
|
||||||
|
mergeHeaders,
|
||||||
|
setAuthParams,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
export const createClient = (config: Config = {}): Client => {
|
||||||
|
let _config = mergeConfigs(createConfig(), config);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { auth, ...configWithoutAuth } = _config;
|
||||||
|
const instance = axios.create(configWithoutAuth);
|
||||||
|
|
||||||
|
const getConfig = (): Config => ({ ..._config });
|
||||||
|
|
||||||
|
const setConfig = (config: Config): Config => {
|
||||||
|
_config = mergeConfigs(_config, config);
|
||||||
|
instance.defaults = {
|
||||||
|
...instance.defaults,
|
||||||
|
..._config,
|
||||||
|
// @ts-expect-error
|
||||||
|
headers: mergeHeaders(instance.defaults.headers, _config.headers),
|
||||||
|
};
|
||||||
|
return getConfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
const request: Client['request'] = async (options) => {
|
||||||
|
const opts = {
|
||||||
|
..._config,
|
||||||
|
...options,
|
||||||
|
axios: options.axios ?? _config.axios ?? instance,
|
||||||
|
headers: mergeHeaders(_config.headers, options.headers),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opts.security) {
|
||||||
|
await setAuthParams({
|
||||||
|
...opts,
|
||||||
|
security: opts.security,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.requestValidator) {
|
||||||
|
await opts.requestValidator(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.body && opts.bodySerializer) {
|
||||||
|
opts.body = opts.bodySerializer(opts.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = buildUrl(opts);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// assign Axios here for consistency with fetch
|
||||||
|
const _axios = opts.axios!;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { auth, ...optsWithoutAuth } = opts;
|
||||||
|
const response = await _axios({
|
||||||
|
...optsWithoutAuth,
|
||||||
|
baseURL: opts.baseURL as string,
|
||||||
|
data: opts.body,
|
||||||
|
headers: opts.headers as RawAxiosRequestHeaders,
|
||||||
|
// let `paramsSerializer()` handle query params if it exists
|
||||||
|
params: opts.paramsSerializer ? opts.query : undefined,
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
|
||||||
|
let { data } = response;
|
||||||
|
|
||||||
|
if (opts.responseType === 'json') {
|
||||||
|
if (opts.responseValidator) {
|
||||||
|
await opts.responseValidator(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.responseTransformer) {
|
||||||
|
data = await opts.responseTransformer(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
data: data ?? {},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const e = error as AxiosError;
|
||||||
|
if (opts.throwOnError) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
// @ts-expect-error
|
||||||
|
e.error = e.response?.data ?? {};
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
buildUrl,
|
||||||
|
delete: (options) => request({ ...options, method: 'DELETE' }),
|
||||||
|
get: (options) => request({ ...options, method: 'GET' }),
|
||||||
|
getConfig,
|
||||||
|
head: (options) => request({ ...options, method: 'HEAD' }),
|
||||||
|
instance,
|
||||||
|
options: (options) => request({ ...options, method: 'OPTIONS' }),
|
||||||
|
patch: (options) => request({ ...options, method: 'PATCH' }),
|
||||||
|
post: (options) => request({ ...options, method: 'POST' }),
|
||||||
|
put: (options) => request({ ...options, method: 'PUT' }),
|
||||||
|
request,
|
||||||
|
setConfig,
|
||||||
|
} as Client;
|
||||||
|
};
|
||||||
21
src/lib/client/client/index.ts
Normal file
21
src/lib/client/client/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export type { Auth } from '../core/auth';
|
||||||
|
export type { QuerySerializerOptions } from '../core/bodySerializer';
|
||||||
|
export {
|
||||||
|
formDataBodySerializer,
|
||||||
|
jsonBodySerializer,
|
||||||
|
urlSearchParamsBodySerializer,
|
||||||
|
} from '../core/bodySerializer';
|
||||||
|
export { buildClientParams } from '../core/params';
|
||||||
|
export { createClient } from './client';
|
||||||
|
export type {
|
||||||
|
Client,
|
||||||
|
ClientOptions,
|
||||||
|
Config,
|
||||||
|
CreateClientConfig,
|
||||||
|
Options,
|
||||||
|
OptionsLegacyParser,
|
||||||
|
RequestOptions,
|
||||||
|
RequestResult,
|
||||||
|
TDataShape,
|
||||||
|
} from './types';
|
||||||
|
export { createConfig } from './utils';
|
||||||
179
src/lib/client/client/types.ts
Normal file
179
src/lib/client/client/types.ts
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
import type {
|
||||||
|
AxiosError,
|
||||||
|
AxiosInstance,
|
||||||
|
AxiosRequestHeaders,
|
||||||
|
AxiosResponse,
|
||||||
|
AxiosStatic,
|
||||||
|
CreateAxiosDefaults,
|
||||||
|
} from 'axios';
|
||||||
|
|
||||||
|
import type { Auth } from '../core/auth';
|
||||||
|
import type {
|
||||||
|
Client as CoreClient,
|
||||||
|
Config as CoreConfig,
|
||||||
|
} from '../core/types';
|
||||||
|
|
||||||
|
export interface Config<T extends ClientOptions = ClientOptions>
|
||||||
|
extends Omit<CreateAxiosDefaults, 'auth' | 'baseURL' | 'headers' | 'method'>,
|
||||||
|
CoreConfig {
|
||||||
|
/**
|
||||||
|
* Axios implementation. You can use this option to provide a custom
|
||||||
|
* Axios instance.
|
||||||
|
*
|
||||||
|
* @default axios
|
||||||
|
*/
|
||||||
|
axios?: AxiosStatic;
|
||||||
|
/**
|
||||||
|
* Base URL for all requests made by this client.
|
||||||
|
*/
|
||||||
|
baseURL?: T['baseURL'];
|
||||||
|
/**
|
||||||
|
* An object containing any HTTP headers that you want to pre-populate your
|
||||||
|
* `Headers` object with.
|
||||||
|
*
|
||||||
|
* {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
|
||||||
|
*/
|
||||||
|
headers?:
|
||||||
|
| AxiosRequestHeaders
|
||||||
|
| Record<
|
||||||
|
string,
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| (string | number | boolean)[]
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| unknown
|
||||||
|
>;
|
||||||
|
/**
|
||||||
|
* Throw an error instead of returning it in the response?
|
||||||
|
*
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
throwOnError?: T['throwOnError'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestOptions<
|
||||||
|
ThrowOnError extends boolean = boolean,
|
||||||
|
Url extends string = string,
|
||||||
|
> extends Config<{
|
||||||
|
throwOnError: ThrowOnError;
|
||||||
|
}> {
|
||||||
|
/**
|
||||||
|
* Any body that you want to add to your request.
|
||||||
|
*
|
||||||
|
* {@link https://developer.mozilla.org/docs/Web/API/fetch#body}
|
||||||
|
*/
|
||||||
|
body?: unknown;
|
||||||
|
path?: Record<string, unknown>;
|
||||||
|
query?: Record<string, unknown>;
|
||||||
|
/**
|
||||||
|
* Security mechanism(s) to use for the request.
|
||||||
|
*/
|
||||||
|
security?: ReadonlyArray<Auth>;
|
||||||
|
url: Url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RequestResult<
|
||||||
|
TData = unknown,
|
||||||
|
TError = unknown,
|
||||||
|
ThrowOnError extends boolean = boolean,
|
||||||
|
> = ThrowOnError extends true
|
||||||
|
? Promise<
|
||||||
|
AxiosResponse<
|
||||||
|
TData extends Record<string, unknown> ? TData[keyof TData] : TData
|
||||||
|
>
|
||||||
|
>
|
||||||
|
: Promise<
|
||||||
|
| (AxiosResponse<
|
||||||
|
TData extends Record<string, unknown> ? TData[keyof TData] : TData
|
||||||
|
> & { error: undefined })
|
||||||
|
| (AxiosError<
|
||||||
|
TError extends Record<string, unknown> ? TError[keyof TError] : TError
|
||||||
|
> & {
|
||||||
|
data: undefined;
|
||||||
|
error: TError extends Record<string, unknown>
|
||||||
|
? TError[keyof TError]
|
||||||
|
: TError;
|
||||||
|
})
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface ClientOptions {
|
||||||
|
baseURL?: string;
|
||||||
|
throwOnError?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type MethodFn = <
|
||||||
|
TData = unknown,
|
||||||
|
TError = unknown,
|
||||||
|
ThrowOnError extends boolean = false,
|
||||||
|
>(
|
||||||
|
options: Omit<RequestOptions<ThrowOnError>, 'method'>,
|
||||||
|
) => RequestResult<TData, TError, ThrowOnError>;
|
||||||
|
|
||||||
|
type RequestFn = <
|
||||||
|
TData = unknown,
|
||||||
|
TError = unknown,
|
||||||
|
ThrowOnError extends boolean = false,
|
||||||
|
>(
|
||||||
|
options: Omit<RequestOptions<ThrowOnError>, 'method'> &
|
||||||
|
Pick<Required<RequestOptions<ThrowOnError>>, 'method'>,
|
||||||
|
) => RequestResult<TData, TError, ThrowOnError>;
|
||||||
|
|
||||||
|
type BuildUrlFn = <
|
||||||
|
TData extends {
|
||||||
|
body?: unknown;
|
||||||
|
path?: Record<string, unknown>;
|
||||||
|
query?: Record<string, unknown>;
|
||||||
|
url: string;
|
||||||
|
},
|
||||||
|
>(
|
||||||
|
options: Pick<TData, 'url'> & Omit<Options<TData>, 'axios'>,
|
||||||
|
) => string;
|
||||||
|
|
||||||
|
export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn> & {
|
||||||
|
instance: AxiosInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `createClientConfig()` function will be called on client initialization
|
||||||
|
* and the returned object will become the client's initial configuration.
|
||||||
|
*
|
||||||
|
* You may want to initialize your client this way instead of calling
|
||||||
|
* `setConfig()`. This is useful for example if you're using Next.js
|
||||||
|
* to ensure your client always has the correct values.
|
||||||
|
*/
|
||||||
|
export type CreateClientConfig<T extends ClientOptions = ClientOptions> = (
|
||||||
|
override?: Config<ClientOptions & T>,
|
||||||
|
) => Config<Required<ClientOptions> & T>;
|
||||||
|
|
||||||
|
export interface TDataShape {
|
||||||
|
body?: unknown;
|
||||||
|
headers?: unknown;
|
||||||
|
path?: unknown;
|
||||||
|
query?: unknown;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type OmitKeys<T, K> = Pick<T, Exclude<keyof T, K>>;
|
||||||
|
|
||||||
|
export type Options<
|
||||||
|
TData extends TDataShape = TDataShape,
|
||||||
|
ThrowOnError extends boolean = boolean,
|
||||||
|
> = OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'path' | 'query' | 'url'> &
|
||||||
|
Omit<TData, 'url'>;
|
||||||
|
|
||||||
|
export type OptionsLegacyParser<
|
||||||
|
TData = unknown,
|
||||||
|
ThrowOnError extends boolean = boolean,
|
||||||
|
> = TData extends { body?: any }
|
||||||
|
? TData extends { headers?: any }
|
||||||
|
? OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'headers' | 'url'> & TData
|
||||||
|
: OmitKeys<RequestOptions<ThrowOnError>, 'body' | 'url'> &
|
||||||
|
TData &
|
||||||
|
Pick<RequestOptions<ThrowOnError>, 'headers'>
|
||||||
|
: TData extends { headers?: any }
|
||||||
|
? OmitKeys<RequestOptions<ThrowOnError>, 'headers' | 'url'> &
|
||||||
|
TData &
|
||||||
|
Pick<RequestOptions<ThrowOnError>, 'body'>
|
||||||
|
: OmitKeys<RequestOptions<ThrowOnError>, 'url'> & TData;
|
||||||
286
src/lib/client/client/utils.ts
Normal file
286
src/lib/client/client/utils.ts
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
import { getAuthToken } from '../core/auth';
|
||||||
|
import type {
|
||||||
|
QuerySerializer,
|
||||||
|
QuerySerializerOptions,
|
||||||
|
} from '../core/bodySerializer';
|
||||||
|
import type { ArraySeparatorStyle } from '../core/pathSerializer';
|
||||||
|
import {
|
||||||
|
serializeArrayParam,
|
||||||
|
serializeObjectParam,
|
||||||
|
serializePrimitiveParam,
|
||||||
|
} from '../core/pathSerializer';
|
||||||
|
import type { Client, ClientOptions, Config, RequestOptions } from './types';
|
||||||
|
|
||||||
|
interface PathSerializer {
|
||||||
|
path: Record<string, unknown>;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PATH_PARAM_RE = /\{[^{}]+\}/g;
|
||||||
|
|
||||||
|
const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => {
|
||||||
|
let url = _url;
|
||||||
|
const matches = _url.match(PATH_PARAM_RE);
|
||||||
|
if (matches) {
|
||||||
|
for (const match of matches) {
|
||||||
|
let explode = false;
|
||||||
|
let name = match.substring(1, match.length - 1);
|
||||||
|
let style: ArraySeparatorStyle = 'simple';
|
||||||
|
|
||||||
|
if (name.endsWith('*')) {
|
||||||
|
explode = true;
|
||||||
|
name = name.substring(0, name.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith('.')) {
|
||||||
|
name = name.substring(1);
|
||||||
|
style = 'label';
|
||||||
|
} else if (name.startsWith(';')) {
|
||||||
|
name = name.substring(1);
|
||||||
|
style = 'matrix';
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = path[name];
|
||||||
|
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
url = url.replace(
|
||||||
|
match,
|
||||||
|
serializeArrayParam({ explode, name, style, value }),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
url = url.replace(
|
||||||
|
match,
|
||||||
|
serializeObjectParam({
|
||||||
|
explode,
|
||||||
|
name,
|
||||||
|
style,
|
||||||
|
value: value as Record<string, unknown>,
|
||||||
|
valueOnly: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style === 'matrix') {
|
||||||
|
url = url.replace(
|
||||||
|
match,
|
||||||
|
`;${serializePrimitiveParam({
|
||||||
|
name,
|
||||||
|
value: value as string,
|
||||||
|
})}`,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const replaceValue = encodeURIComponent(
|
||||||
|
style === 'label' ? `.${value as string}` : (value as string),
|
||||||
|
);
|
||||||
|
url = url.replace(match, replaceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createQuerySerializer = <T = unknown>({
|
||||||
|
allowReserved,
|
||||||
|
array,
|
||||||
|
object,
|
||||||
|
}: QuerySerializerOptions = {}) => {
|
||||||
|
const querySerializer = (queryParams: T) => {
|
||||||
|
const search: string[] = [];
|
||||||
|
if (queryParams && typeof queryParams === 'object') {
|
||||||
|
for (const name in queryParams) {
|
||||||
|
const value = queryParams[name];
|
||||||
|
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
const serializedArray = serializeArrayParam({
|
||||||
|
allowReserved,
|
||||||
|
explode: true,
|
||||||
|
name,
|
||||||
|
style: 'form',
|
||||||
|
value,
|
||||||
|
...array,
|
||||||
|
});
|
||||||
|
if (serializedArray) search.push(serializedArray);
|
||||||
|
} else if (typeof value === 'object') {
|
||||||
|
const serializedObject = serializeObjectParam({
|
||||||
|
allowReserved,
|
||||||
|
explode: true,
|
||||||
|
name,
|
||||||
|
style: 'deepObject',
|
||||||
|
value: value as Record<string, unknown>,
|
||||||
|
...object,
|
||||||
|
});
|
||||||
|
if (serializedObject) search.push(serializedObject);
|
||||||
|
} else {
|
||||||
|
const serializedPrimitive = serializePrimitiveParam({
|
||||||
|
allowReserved,
|
||||||
|
name,
|
||||||
|
value: value as string,
|
||||||
|
});
|
||||||
|
if (serializedPrimitive) search.push(serializedPrimitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return search.join('&');
|
||||||
|
};
|
||||||
|
return querySerializer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setAuthParams = async ({
|
||||||
|
security,
|
||||||
|
...options
|
||||||
|
}: Pick<Required<RequestOptions>, 'security'> &
|
||||||
|
Pick<RequestOptions, 'auth' | 'query'> & {
|
||||||
|
headers: Record<any, unknown>;
|
||||||
|
}) => {
|
||||||
|
for (const auth of security) {
|
||||||
|
const token = await getAuthToken(auth, options.auth);
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = auth.name ?? 'Authorization';
|
||||||
|
|
||||||
|
switch (auth.in) {
|
||||||
|
case 'query':
|
||||||
|
if (!options.query) {
|
||||||
|
options.query = {};
|
||||||
|
}
|
||||||
|
options.query[name] = token;
|
||||||
|
break;
|
||||||
|
case 'cookie': {
|
||||||
|
const value = `${name}=${token}`;
|
||||||
|
if ('Cookie' in options.headers && options.headers['Cookie']) {
|
||||||
|
options.headers['Cookie'] = `${options.headers['Cookie']}; ${value}`;
|
||||||
|
} else {
|
||||||
|
options.headers['Cookie'] = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'header':
|
||||||
|
default:
|
||||||
|
options.headers[name] = token;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildUrl: Client['buildUrl'] = (options) => {
|
||||||
|
const url = getUrl({
|
||||||
|
path: options.path,
|
||||||
|
// let `paramsSerializer()` handle query params if it exists
|
||||||
|
query: !options.paramsSerializer ? options.query : undefined,
|
||||||
|
querySerializer:
|
||||||
|
typeof options.querySerializer === 'function'
|
||||||
|
? options.querySerializer
|
||||||
|
: createQuerySerializer(options.querySerializer),
|
||||||
|
url: options.url,
|
||||||
|
});
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUrl = ({
|
||||||
|
path,
|
||||||
|
query,
|
||||||
|
querySerializer,
|
||||||
|
url: _url,
|
||||||
|
}: {
|
||||||
|
path?: Record<string, unknown>;
|
||||||
|
query?: Record<string, unknown>;
|
||||||
|
querySerializer: QuerySerializer;
|
||||||
|
url: string;
|
||||||
|
}) => {
|
||||||
|
const pathUrl = _url.startsWith('/') ? _url : `/${_url}`;
|
||||||
|
let url = pathUrl;
|
||||||
|
if (path) {
|
||||||
|
url = defaultPathSerializer({ path, url });
|
||||||
|
}
|
||||||
|
let search = query ? querySerializer(query) : '';
|
||||||
|
if (search.startsWith('?')) {
|
||||||
|
search = search.substring(1);
|
||||||
|
}
|
||||||
|
if (search) {
|
||||||
|
url += `?${search}`;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mergeConfigs = (a: Config, b: Config): Config => {
|
||||||
|
const config = { ...a, ...b };
|
||||||
|
config.headers = mergeHeaders(a.headers, b.headers);
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special Axios headers keywords allowing to set headers by request method.
|
||||||
|
*/
|
||||||
|
export const axiosHeadersKeywords = [
|
||||||
|
'common',
|
||||||
|
'delete',
|
||||||
|
'get',
|
||||||
|
'head',
|
||||||
|
'patch',
|
||||||
|
'post',
|
||||||
|
'put',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const mergeHeaders = (
|
||||||
|
...headers: Array<Required<Config>['headers'] | undefined>
|
||||||
|
): Record<any, unknown> => {
|
||||||
|
const mergedHeaders: Record<any, unknown> = {};
|
||||||
|
for (const header of headers) {
|
||||||
|
if (!header || typeof header !== 'object') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const iterator = Object.entries(header);
|
||||||
|
|
||||||
|
for (const [key, value] of iterator) {
|
||||||
|
if (
|
||||||
|
axiosHeadersKeywords.includes(
|
||||||
|
key as (typeof axiosHeadersKeywords)[number],
|
||||||
|
) &&
|
||||||
|
typeof value === 'object'
|
||||||
|
) {
|
||||||
|
mergedHeaders[key] = {
|
||||||
|
...(mergedHeaders[key] as Record<any, unknown>),
|
||||||
|
...value,
|
||||||
|
};
|
||||||
|
} else if (value === null) {
|
||||||
|
delete mergedHeaders[key];
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
for (const v of value) {
|
||||||
|
// @ts-expect-error
|
||||||
|
mergedHeaders[key] = [...(mergedHeaders[key] ?? []), v as string];
|
||||||
|
}
|
||||||
|
} else if (value !== undefined) {
|
||||||
|
// assume object headers are meant to be JSON stringified, i.e. their
|
||||||
|
// content value in OpenAPI specification is 'application/json'
|
||||||
|
mergedHeaders[key] =
|
||||||
|
typeof value === 'object' ? JSON.stringify(value) : (value as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mergedHeaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createConfig = <T extends ClientOptions = ClientOptions>(
|
||||||
|
override: Config<Omit<ClientOptions, keyof T> & T> = {},
|
||||||
|
): Config<Omit<ClientOptions, keyof T> & T> => ({
|
||||||
|
...override,
|
||||||
|
});
|
||||||
40
src/lib/client/core/auth.ts
Normal file
40
src/lib/client/core/auth.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
export type AuthToken = string | undefined;
|
||||||
|
|
||||||
|
export interface Auth {
|
||||||
|
/**
|
||||||
|
* Which part of the request do we use to send the auth?
|
||||||
|
*
|
||||||
|
* @default 'header'
|
||||||
|
*/
|
||||||
|
in?: 'header' | 'query' | 'cookie';
|
||||||
|
/**
|
||||||
|
* Header or query parameter name.
|
||||||
|
*
|
||||||
|
* @default 'Authorization'
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
|
scheme?: 'basic' | 'bearer';
|
||||||
|
type: 'apiKey' | 'http';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAuthToken = async (
|
||||||
|
auth: Auth,
|
||||||
|
callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
|
||||||
|
): Promise<string | undefined> => {
|
||||||
|
const token =
|
||||||
|
typeof callback === 'function' ? await callback(auth) : callback;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.scheme === 'bearer') {
|
||||||
|
return `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.scheme === 'basic') {
|
||||||
|
return `Basic ${btoa(token)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
};
|
||||||
88
src/lib/client/core/bodySerializer.ts
Normal file
88
src/lib/client/core/bodySerializer.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import type {
|
||||||
|
ArrayStyle,
|
||||||
|
ObjectStyle,
|
||||||
|
SerializerOptions,
|
||||||
|
} from './pathSerializer';
|
||||||
|
|
||||||
|
export type QuerySerializer = (query: Record<string, unknown>) => string;
|
||||||
|
|
||||||
|
export type BodySerializer = (body: any) => any;
|
||||||
|
|
||||||
|
export interface QuerySerializerOptions {
|
||||||
|
allowReserved?: boolean;
|
||||||
|
array?: SerializerOptions<ArrayStyle>;
|
||||||
|
object?: SerializerOptions<ObjectStyle>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializeFormDataPair = (
|
||||||
|
data: FormData,
|
||||||
|
key: string,
|
||||||
|
value: unknown,
|
||||||
|
): void => {
|
||||||
|
if (typeof value === 'string' || value instanceof Blob) {
|
||||||
|
data.append(key, value);
|
||||||
|
} else {
|
||||||
|
data.append(key, JSON.stringify(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const serializeUrlSearchParamsPair = (
|
||||||
|
data: URLSearchParams,
|
||||||
|
key: string,
|
||||||
|
value: unknown,
|
||||||
|
): void => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
data.append(key, value);
|
||||||
|
} else {
|
||||||
|
data.append(key, JSON.stringify(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formDataBodySerializer = {
|
||||||
|
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(
|
||||||
|
body: T,
|
||||||
|
): FormData => {
|
||||||
|
const data = new FormData();
|
||||||
|
|
||||||
|
Object.entries(body).forEach(([key, value]) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((v) => serializeFormDataPair(data, key, v));
|
||||||
|
} else {
|
||||||
|
serializeFormDataPair(data, key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const jsonBodySerializer = {
|
||||||
|
bodySerializer: <T>(body: T): string =>
|
||||||
|
JSON.stringify(body, (_key, value) =>
|
||||||
|
typeof value === 'bigint' ? value.toString() : value,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const urlSearchParamsBodySerializer = {
|
||||||
|
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(
|
||||||
|
body: T,
|
||||||
|
): string => {
|
||||||
|
const data = new URLSearchParams();
|
||||||
|
|
||||||
|
Object.entries(body).forEach(([key, value]) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((v) => serializeUrlSearchParamsPair(data, key, v));
|
||||||
|
} else {
|
||||||
|
serializeUrlSearchParamsPair(data, key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.toString();
|
||||||
|
},
|
||||||
|
};
|
||||||
151
src/lib/client/core/params.ts
Normal file
151
src/lib/client/core/params.ts
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
type Slot = 'body' | 'headers' | 'path' | 'query';
|
||||||
|
|
||||||
|
export type Field =
|
||||||
|
| {
|
||||||
|
in: Exclude<Slot, 'body'>;
|
||||||
|
/**
|
||||||
|
* Field name. This is the name we want the user to see and use.
|
||||||
|
*/
|
||||||
|
key: string;
|
||||||
|
/**
|
||||||
|
* Field mapped name. This is the name we want to use in the request.
|
||||||
|
* If omitted, we use the same value as `key`.
|
||||||
|
*/
|
||||||
|
map?: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
in: Extract<Slot, 'body'>;
|
||||||
|
/**
|
||||||
|
* Key isn't required for bodies.
|
||||||
|
*/
|
||||||
|
key?: string;
|
||||||
|
map?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface Fields {
|
||||||
|
allowExtra?: Partial<Record<Slot, boolean>>;
|
||||||
|
args?: ReadonlyArray<Field>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FieldsConfig = ReadonlyArray<Field | Fields>;
|
||||||
|
|
||||||
|
const extraPrefixesMap: Record<string, Slot> = {
|
||||||
|
$body_: 'body',
|
||||||
|
$headers_: 'headers',
|
||||||
|
$path_: 'path',
|
||||||
|
$query_: 'query',
|
||||||
|
};
|
||||||
|
const extraPrefixes = Object.entries(extraPrefixesMap);
|
||||||
|
|
||||||
|
type KeyMap = Map<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
in: Slot;
|
||||||
|
map?: string;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => {
|
||||||
|
if (!map) {
|
||||||
|
map = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const config of fields) {
|
||||||
|
if ('in' in config) {
|
||||||
|
if (config.key) {
|
||||||
|
map.set(config.key, {
|
||||||
|
in: config.in,
|
||||||
|
map: config.map,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (config.args) {
|
||||||
|
buildKeyMap(config.args, map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface Params {
|
||||||
|
body: unknown;
|
||||||
|
headers: Record<string, unknown>;
|
||||||
|
path: Record<string, unknown>;
|
||||||
|
query: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripEmptySlots = (params: Params) => {
|
||||||
|
for (const [slot, value] of Object.entries(params)) {
|
||||||
|
if (value && typeof value === 'object' && !Object.keys(value).length) {
|
||||||
|
delete params[slot as Slot];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildClientParams = (
|
||||||
|
args: ReadonlyArray<unknown>,
|
||||||
|
fields: FieldsConfig,
|
||||||
|
) => {
|
||||||
|
const params: Params = {
|
||||||
|
body: {},
|
||||||
|
headers: {},
|
||||||
|
path: {},
|
||||||
|
query: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const map = buildKeyMap(fields);
|
||||||
|
|
||||||
|
let config: FieldsConfig[number] | undefined;
|
||||||
|
|
||||||
|
for (const [index, arg] of args.entries()) {
|
||||||
|
if (fields[index]) {
|
||||||
|
config = fields[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('in' in config) {
|
||||||
|
if (config.key) {
|
||||||
|
const field = map.get(config.key)!;
|
||||||
|
const name = field.map || config.key;
|
||||||
|
(params[field.in] as Record<string, unknown>)[name] = arg;
|
||||||
|
} else {
|
||||||
|
params.body = arg;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const [key, value] of Object.entries(arg ?? {})) {
|
||||||
|
const field = map.get(key);
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
const name = field.map || key;
|
||||||
|
(params[field.in] as Record<string, unknown>)[name] = value;
|
||||||
|
} else {
|
||||||
|
const extra = extraPrefixes.find(([prefix]) =>
|
||||||
|
key.startsWith(prefix),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (extra) {
|
||||||
|
const [prefix, slot] = extra;
|
||||||
|
(params[slot] as Record<string, unknown>)[
|
||||||
|
key.slice(prefix.length)
|
||||||
|
] = value;
|
||||||
|
} else {
|
||||||
|
for (const [slot, allowed] of Object.entries(
|
||||||
|
config.allowExtra ?? {},
|
||||||
|
)) {
|
||||||
|
if (allowed) {
|
||||||
|
(params[slot as Slot] as Record<string, unknown>)[key] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stripEmptySlots(params);
|
||||||
|
|
||||||
|
return params;
|
||||||
|
};
|
||||||
179
src/lib/client/core/pathSerializer.ts
Normal file
179
src/lib/client/core/pathSerializer.ts
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
interface SerializeOptions<T>
|
||||||
|
extends SerializePrimitiveOptions,
|
||||||
|
SerializerOptions<T> {}
|
||||||
|
|
||||||
|
interface SerializePrimitiveOptions {
|
||||||
|
allowReserved?: boolean;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SerializerOptions<T> {
|
||||||
|
/**
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
explode: boolean;
|
||||||
|
style: T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited';
|
||||||
|
export type ArraySeparatorStyle = ArrayStyle | MatrixStyle;
|
||||||
|
type MatrixStyle = 'label' | 'matrix' | 'simple';
|
||||||
|
export type ObjectStyle = 'form' | 'deepObject';
|
||||||
|
type ObjectSeparatorStyle = ObjectStyle | MatrixStyle;
|
||||||
|
|
||||||
|
interface SerializePrimitiveParam extends SerializePrimitiveOptions {
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
|
||||||
|
switch (style) {
|
||||||
|
case 'label':
|
||||||
|
return '.';
|
||||||
|
case 'matrix':
|
||||||
|
return ';';
|
||||||
|
case 'simple':
|
||||||
|
return ',';
|
||||||
|
default:
|
||||||
|
return '&';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
|
||||||
|
switch (style) {
|
||||||
|
case 'form':
|
||||||
|
return ',';
|
||||||
|
case 'pipeDelimited':
|
||||||
|
return '|';
|
||||||
|
case 'spaceDelimited':
|
||||||
|
return '%20';
|
||||||
|
default:
|
||||||
|
return ',';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
|
||||||
|
switch (style) {
|
||||||
|
case 'label':
|
||||||
|
return '.';
|
||||||
|
case 'matrix':
|
||||||
|
return ';';
|
||||||
|
case 'simple':
|
||||||
|
return ',';
|
||||||
|
default:
|
||||||
|
return '&';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serializeArrayParam = ({
|
||||||
|
allowReserved,
|
||||||
|
explode,
|
||||||
|
name,
|
||||||
|
style,
|
||||||
|
value,
|
||||||
|
}: SerializeOptions<ArraySeparatorStyle> & {
|
||||||
|
value: unknown[];
|
||||||
|
}) => {
|
||||||
|
if (!explode) {
|
||||||
|
const joinedValues = (
|
||||||
|
allowReserved ? value : value.map((v) => encodeURIComponent(v as string))
|
||||||
|
).join(separatorArrayNoExplode(style));
|
||||||
|
switch (style) {
|
||||||
|
case 'label':
|
||||||
|
return `.${joinedValues}`;
|
||||||
|
case 'matrix':
|
||||||
|
return `;${name}=${joinedValues}`;
|
||||||
|
case 'simple':
|
||||||
|
return joinedValues;
|
||||||
|
default:
|
||||||
|
return `${name}=${joinedValues}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const separator = separatorArrayExplode(style);
|
||||||
|
const joinedValues = value
|
||||||
|
.map((v) => {
|
||||||
|
if (style === 'label' || style === 'simple') {
|
||||||
|
return allowReserved ? v : encodeURIComponent(v as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serializePrimitiveParam({
|
||||||
|
allowReserved,
|
||||||
|
name,
|
||||||
|
value: v as string,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.join(separator);
|
||||||
|
return style === 'label' || style === 'matrix'
|
||||||
|
? separator + joinedValues
|
||||||
|
: joinedValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serializePrimitiveParam = ({
|
||||||
|
allowReserved,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
}: SerializePrimitiveParam) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
throw new Error(
|
||||||
|
'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const serializeObjectParam = ({
|
||||||
|
allowReserved,
|
||||||
|
explode,
|
||||||
|
name,
|
||||||
|
style,
|
||||||
|
value,
|
||||||
|
valueOnly,
|
||||||
|
}: SerializeOptions<ObjectSeparatorStyle> & {
|
||||||
|
value: Record<string, unknown> | Date;
|
||||||
|
valueOnly?: boolean;
|
||||||
|
}) => {
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style !== 'deepObject' && !explode) {
|
||||||
|
let values: string[] = [];
|
||||||
|
Object.entries(value).forEach(([key, v]) => {
|
||||||
|
values = [
|
||||||
|
...values,
|
||||||
|
key,
|
||||||
|
allowReserved ? (v as string) : encodeURIComponent(v as string),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
const joinedValues = values.join(',');
|
||||||
|
switch (style) {
|
||||||
|
case 'form':
|
||||||
|
return `${name}=${joinedValues}`;
|
||||||
|
case 'label':
|
||||||
|
return `.${joinedValues}`;
|
||||||
|
case 'matrix':
|
||||||
|
return `;${name}=${joinedValues}`;
|
||||||
|
default:
|
||||||
|
return joinedValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const separator = separatorObjectExplode(style);
|
||||||
|
const joinedValues = Object.entries(value)
|
||||||
|
.map(([key, v]) =>
|
||||||
|
serializePrimitiveParam({
|
||||||
|
allowReserved,
|
||||||
|
name: style === 'deepObject' ? `${name}[${key}]` : key,
|
||||||
|
value: v as string,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.join(separator);
|
||||||
|
return style === 'label' || style === 'matrix'
|
||||||
|
? separator + joinedValues
|
||||||
|
: joinedValues;
|
||||||
|
};
|
||||||
118
src/lib/client/core/types.ts
Normal file
118
src/lib/client/core/types.ts
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import type { Auth, AuthToken } from './auth';
|
||||||
|
import type {
|
||||||
|
BodySerializer,
|
||||||
|
QuerySerializer,
|
||||||
|
QuerySerializerOptions,
|
||||||
|
} from './bodySerializer';
|
||||||
|
|
||||||
|
export interface Client<
|
||||||
|
RequestFn = never,
|
||||||
|
Config = unknown,
|
||||||
|
MethodFn = never,
|
||||||
|
BuildUrlFn = never,
|
||||||
|
> {
|
||||||
|
/**
|
||||||
|
* Returns the final request URL.
|
||||||
|
*/
|
||||||
|
buildUrl: BuildUrlFn;
|
||||||
|
connect: MethodFn;
|
||||||
|
delete: MethodFn;
|
||||||
|
get: MethodFn;
|
||||||
|
getConfig: () => Config;
|
||||||
|
head: MethodFn;
|
||||||
|
options: MethodFn;
|
||||||
|
patch: MethodFn;
|
||||||
|
post: MethodFn;
|
||||||
|
put: MethodFn;
|
||||||
|
request: RequestFn;
|
||||||
|
setConfig: (config: Config) => Config;
|
||||||
|
trace: MethodFn;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config {
|
||||||
|
/**
|
||||||
|
* Auth token or a function returning auth token. The resolved value will be
|
||||||
|
* added to the request payload as defined by its `security` array.
|
||||||
|
*/
|
||||||
|
auth?: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken;
|
||||||
|
/**
|
||||||
|
* A function for serializing request body parameter. By default,
|
||||||
|
* {@link JSON.stringify()} will be used.
|
||||||
|
*/
|
||||||
|
bodySerializer?: BodySerializer | null;
|
||||||
|
/**
|
||||||
|
* An object containing any HTTP headers that you want to pre-populate your
|
||||||
|
* `Headers` object with.
|
||||||
|
*
|
||||||
|
* {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more}
|
||||||
|
*/
|
||||||
|
headers?:
|
||||||
|
| RequestInit['headers']
|
||||||
|
| Record<
|
||||||
|
string,
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| (string | number | boolean)[]
|
||||||
|
| null
|
||||||
|
| undefined
|
||||||
|
| unknown
|
||||||
|
>;
|
||||||
|
/**
|
||||||
|
* The request method.
|
||||||
|
*
|
||||||
|
* {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more}
|
||||||
|
*/
|
||||||
|
method?:
|
||||||
|
| 'CONNECT'
|
||||||
|
| 'DELETE'
|
||||||
|
| 'GET'
|
||||||
|
| 'HEAD'
|
||||||
|
| 'OPTIONS'
|
||||||
|
| 'PATCH'
|
||||||
|
| 'POST'
|
||||||
|
| 'PUT'
|
||||||
|
| 'TRACE';
|
||||||
|
/**
|
||||||
|
* A function for serializing request query parameters. By default, arrays
|
||||||
|
* will be exploded in form style, objects will be exploded in deepObject
|
||||||
|
* style, and reserved characters are percent-encoded.
|
||||||
|
*
|
||||||
|
* This method will have no effect if the native `paramsSerializer()` Axios
|
||||||
|
* API function is used.
|
||||||
|
*
|
||||||
|
* {@link https://swagger.io/docs/specification/serialization/#query View examples}
|
||||||
|
*/
|
||||||
|
querySerializer?: QuerySerializer | QuerySerializerOptions;
|
||||||
|
/**
|
||||||
|
* A function validating request data. This is useful if you want to ensure
|
||||||
|
* the request conforms to the desired shape, so it can be safely sent to
|
||||||
|
* the server.
|
||||||
|
*/
|
||||||
|
requestValidator?: (data: unknown) => Promise<unknown>;
|
||||||
|
/**
|
||||||
|
* A function transforming response data before it's returned. This is useful
|
||||||
|
* for post-processing data, e.g. converting ISO strings into Date objects.
|
||||||
|
*/
|
||||||
|
responseTransformer?: (data: unknown) => Promise<unknown>;
|
||||||
|
/**
|
||||||
|
* A function validating response data. This is useful if you want to ensure
|
||||||
|
* the response conforms to the desired shape, so it can be safely passed to
|
||||||
|
* the transformers and returned to the user.
|
||||||
|
*/
|
||||||
|
responseValidator?: (data: unknown) => Promise<unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsExactlyNeverOrNeverUndefined<T> = [T] extends [never]
|
||||||
|
? true
|
||||||
|
: [T] extends [never | undefined]
|
||||||
|
? [undefined] extends [T]
|
||||||
|
? false
|
||||||
|
: true
|
||||||
|
: false;
|
||||||
|
|
||||||
|
export type OmitNever<T extends Record<string, unknown>> = {
|
||||||
|
[K in keyof T as IsExactlyNeverOrNeverUndefined<T[K]> extends true
|
||||||
|
? never
|
||||||
|
: K]: T[K];
|
||||||
|
};
|
||||||
3
src/lib/client/index.ts
Normal file
3
src/lib/client/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
export * from './types.gen';
|
||||||
|
export * from './sdk.gen';
|
||||||
151
src/lib/client/sdk.gen.ts
Normal file
151
src/lib/client/sdk.gen.ts
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
import type { Options as ClientOptions, TDataShape, Client } from './client';
|
||||||
|
import type { GetBoardsData, GetBoardsResponses, GetBoardsErrors, UpdateBoardData, UpdateBoardResponses, UpdateBoardErrors, GetDealsData, GetDealsResponses, GetDealsErrors, UpdateDealData, UpdateDealResponses, UpdateDealErrors, GetProjectsData, GetProjectsResponses, GetStatusesData, GetStatusesResponses, GetStatusesErrors, UpdateStatusData, UpdateStatusResponses, UpdateStatusErrors } from './types.gen';
|
||||||
|
import { zGetBoardsData, zGetBoardsResponse2, zUpdateBoardData, zUpdateBoardResponse2, zGetDealsData, zGetDealsResponse2, zUpdateDealData, zUpdateDealResponse2, zGetProjectsData, zGetProjectsResponse2, zGetStatusesData, zGetStatusesResponse2, zUpdateStatusData, zUpdateStatusResponse2 } from './zod.gen';
|
||||||
|
import { client as _heyApiClient } from './client.gen';
|
||||||
|
|
||||||
|
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
||||||
|
/**
|
||||||
|
* You can provide a client instance returned by `createClient()` instead of
|
||||||
|
* individual options. This might be also useful if you want to implement a
|
||||||
|
* custom client.
|
||||||
|
*/
|
||||||
|
client?: Client;
|
||||||
|
/**
|
||||||
|
* You can pass arbitrary values through the `meta` object. This can be
|
||||||
|
* used to access values that aren't defined as part of the SDK function.
|
||||||
|
*/
|
||||||
|
meta?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Boards
|
||||||
|
*/
|
||||||
|
export const getBoards = <ThrowOnError extends boolean = false>(options: Options<GetBoardsData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).get<GetBoardsResponses, GetBoardsErrors, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zGetBoardsData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zGetBoardsResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/board/{projectId}',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Board
|
||||||
|
*/
|
||||||
|
export const updateBoard = <ThrowOnError extends boolean = false>(options: Options<UpdateBoardData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<UpdateBoardResponses, UpdateBoardErrors, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zUpdateBoardData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zUpdateBoardResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/board/{boardId}',
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Deals
|
||||||
|
*/
|
||||||
|
export const getDeals = <ThrowOnError extends boolean = false>(options: Options<GetDealsData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).get<GetDealsResponses, GetDealsErrors, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zGetDealsData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zGetDealsResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/deal/{boardId}',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Deal
|
||||||
|
*/
|
||||||
|
export const updateDeal = <ThrowOnError extends boolean = false>(options: Options<UpdateDealData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<UpdateDealResponses, UpdateDealErrors, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zUpdateDealData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zUpdateDealResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/deal/{dealId}',
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Projects
|
||||||
|
*/
|
||||||
|
export const getProjects = <ThrowOnError extends boolean = false>(options?: Options<GetProjectsData, ThrowOnError>) => {
|
||||||
|
return (options?.client ?? _heyApiClient).get<GetProjectsResponses, unknown, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zGetProjectsData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zGetProjectsResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/project/',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Statuses
|
||||||
|
*/
|
||||||
|
export const getStatuses = <ThrowOnError extends boolean = false>(options: Options<GetStatusesData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).get<GetStatusesResponses, GetStatusesErrors, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zGetStatusesData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zGetStatusesResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/status/{boardId}',
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update Status
|
||||||
|
*/
|
||||||
|
export const updateStatus = <ThrowOnError extends boolean = false>(options: Options<UpdateStatusData, ThrowOnError>) => {
|
||||||
|
return (options.client ?? _heyApiClient).patch<UpdateStatusResponses, UpdateStatusErrors, ThrowOnError>({
|
||||||
|
requestValidator: async (data) => {
|
||||||
|
return await zUpdateStatusData.parseAsync(data);
|
||||||
|
},
|
||||||
|
responseType: 'json',
|
||||||
|
responseValidator: async (data) => {
|
||||||
|
return await zUpdateStatusResponse2.parseAsync(data);
|
||||||
|
},
|
||||||
|
url: '/status/{statusId}',
|
||||||
|
...options,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
...options.headers
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
438
src/lib/client/types.gen.ts
Normal file
438
src/lib/client/types.gen.ts
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BoardSchema
|
||||||
|
*/
|
||||||
|
export type BoardSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Id
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DealSchema
|
||||||
|
*/
|
||||||
|
export type DealSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Id
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank: string;
|
||||||
|
/**
|
||||||
|
* Statusid
|
||||||
|
*/
|
||||||
|
statusId: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetBoardsResponse
|
||||||
|
*/
|
||||||
|
export type GetBoardsResponse = {
|
||||||
|
/**
|
||||||
|
* Boards
|
||||||
|
*/
|
||||||
|
boards: Array<BoardSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetDealsResponse
|
||||||
|
*/
|
||||||
|
export type GetDealsResponse = {
|
||||||
|
/**
|
||||||
|
* Deals
|
||||||
|
*/
|
||||||
|
deals: Array<DealSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetProjectsResponse
|
||||||
|
*/
|
||||||
|
export type GetProjectsResponse = {
|
||||||
|
/**
|
||||||
|
* Projects
|
||||||
|
*/
|
||||||
|
projects: Array<ProjectSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetStatusesResponse
|
||||||
|
*/
|
||||||
|
export type GetStatusesResponse = {
|
||||||
|
/**
|
||||||
|
* Statuses
|
||||||
|
*/
|
||||||
|
statuses: Array<StatusSchema>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTPValidationError
|
||||||
|
*/
|
||||||
|
export type HttpValidationError = {
|
||||||
|
/**
|
||||||
|
* Detail
|
||||||
|
*/
|
||||||
|
detail?: Array<ValidationError>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectSchema
|
||||||
|
*/
|
||||||
|
export type ProjectSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Id
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusSchema
|
||||||
|
*/
|
||||||
|
export type StatusSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* Id
|
||||||
|
*/
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateBoardRequest
|
||||||
|
*/
|
||||||
|
export type UpdateBoardRequest = {
|
||||||
|
board: UpdateBoardSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateBoardResponse
|
||||||
|
*/
|
||||||
|
export type UpdateBoardResponse = {
|
||||||
|
/**
|
||||||
|
* Message
|
||||||
|
*/
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateBoardSchema
|
||||||
|
*/
|
||||||
|
export type UpdateBoardSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name?: string | null;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealRequest
|
||||||
|
*/
|
||||||
|
export type UpdateDealRequest = {
|
||||||
|
deal: UpdateDealSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealResponse
|
||||||
|
*/
|
||||||
|
export type UpdateDealResponse = {
|
||||||
|
/**
|
||||||
|
* Message
|
||||||
|
*/
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealSchema
|
||||||
|
*/
|
||||||
|
export type UpdateDealSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name?: string | null;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank?: string | null;
|
||||||
|
/**
|
||||||
|
* Statusid
|
||||||
|
*/
|
||||||
|
statusId?: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateStatusRequest
|
||||||
|
*/
|
||||||
|
export type UpdateStatusRequest = {
|
||||||
|
status: UpdateStatusSchema;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateStatusResponse
|
||||||
|
*/
|
||||||
|
export type UpdateStatusResponse = {
|
||||||
|
/**
|
||||||
|
* Message
|
||||||
|
*/
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateStatusSchema
|
||||||
|
*/
|
||||||
|
export type UpdateStatusSchema = {
|
||||||
|
/**
|
||||||
|
* Name
|
||||||
|
*/
|
||||||
|
name?: string | null;
|
||||||
|
/**
|
||||||
|
* Lexorank
|
||||||
|
*/
|
||||||
|
lexorank?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValidationError
|
||||||
|
*/
|
||||||
|
export type ValidationError = {
|
||||||
|
/**
|
||||||
|
* Location
|
||||||
|
*/
|
||||||
|
loc: Array<string | number>;
|
||||||
|
/**
|
||||||
|
* Message
|
||||||
|
*/
|
||||||
|
msg: string;
|
||||||
|
/**
|
||||||
|
* Error Type
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetBoardsData = {
|
||||||
|
body?: never;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Projectid
|
||||||
|
*/
|
||||||
|
projectId: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/board/{projectId}';
|
||||||
|
};
|
||||||
|
|
||||||
|
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 UpdateBoardData = {
|
||||||
|
body: UpdateBoardRequest;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Boardid
|
||||||
|
*/
|
||||||
|
boardId: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/board/{boardId}';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateBoardErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateBoardError = UpdateBoardErrors[keyof UpdateBoardErrors];
|
||||||
|
|
||||||
|
export type UpdateBoardResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: UpdateBoardResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateBoardResponse2 = UpdateBoardResponses[keyof UpdateBoardResponses];
|
||||||
|
|
||||||
|
export type GetDealsData = {
|
||||||
|
body?: never;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Boardid
|
||||||
|
*/
|
||||||
|
boardId: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/deal/{boardId}';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetDealsErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetDealsError = GetDealsErrors[keyof GetDealsErrors];
|
||||||
|
|
||||||
|
export type GetDealsResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: GetDealsResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetDealsResponse2 = GetDealsResponses[keyof GetDealsResponses];
|
||||||
|
|
||||||
|
export type UpdateDealData = {
|
||||||
|
body: UpdateDealRequest;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Dealid
|
||||||
|
*/
|
||||||
|
dealId: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/deal/{dealId}';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateDealErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateDealError = UpdateDealErrors[keyof UpdateDealErrors];
|
||||||
|
|
||||||
|
export type UpdateDealResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: UpdateDealResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateDealResponse2 = UpdateDealResponses[keyof UpdateDealResponses];
|
||||||
|
|
||||||
|
export type GetProjectsData = {
|
||||||
|
body?: never;
|
||||||
|
path?: never;
|
||||||
|
query?: never;
|
||||||
|
url: '/project/';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetProjectsResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: GetProjectsResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetProjectsResponse2 = GetProjectsResponses[keyof GetProjectsResponses];
|
||||||
|
|
||||||
|
export type GetStatusesData = {
|
||||||
|
body?: never;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Boardid
|
||||||
|
*/
|
||||||
|
boardId: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/status/{boardId}';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetStatusesErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetStatusesError = GetStatusesErrors[keyof GetStatusesErrors];
|
||||||
|
|
||||||
|
export type GetStatusesResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: GetStatusesResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetStatusesResponse2 = GetStatusesResponses[keyof GetStatusesResponses];
|
||||||
|
|
||||||
|
export type UpdateStatusData = {
|
||||||
|
body: UpdateStatusRequest;
|
||||||
|
path: {
|
||||||
|
/**
|
||||||
|
* Statusid
|
||||||
|
*/
|
||||||
|
statusId: number;
|
||||||
|
};
|
||||||
|
query?: never;
|
||||||
|
url: '/status/{statusId}';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateStatusErrors = {
|
||||||
|
/**
|
||||||
|
* Validation Error
|
||||||
|
*/
|
||||||
|
422: HttpValidationError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateStatusError = UpdateStatusErrors[keyof UpdateStatusErrors];
|
||||||
|
|
||||||
|
export type UpdateStatusResponses = {
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
200: UpdateStatusResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateStatusResponse2 = UpdateStatusResponses[keyof UpdateStatusResponses];
|
||||||
|
|
||||||
|
export type ClientOptions = {
|
||||||
|
baseURL: `${string}://${string}/api` | (string & {});
|
||||||
|
};
|
||||||
263
src/lib/client/zod.gen.ts
Normal file
263
src/lib/client/zod.gen.ts
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
// This file is auto-generated by @hey-api/openapi-ts
|
||||||
|
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BoardSchema
|
||||||
|
*/
|
||||||
|
export const zBoardSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.int(),
|
||||||
|
lexorank: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DealSchema
|
||||||
|
*/
|
||||||
|
export const zDealSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.int(),
|
||||||
|
lexorank: z.string(),
|
||||||
|
statusId: z.int()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetBoardsResponse
|
||||||
|
*/
|
||||||
|
export const zGetBoardsResponse = z.object({
|
||||||
|
boards: z.array(zBoardSchema)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetDealsResponse
|
||||||
|
*/
|
||||||
|
export const zGetDealsResponse = z.object({
|
||||||
|
deals: z.array(zDealSchema)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProjectSchema
|
||||||
|
*/
|
||||||
|
export const zProjectSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.int()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetProjectsResponse
|
||||||
|
*/
|
||||||
|
export const zGetProjectsResponse = z.object({
|
||||||
|
projects: z.array(zProjectSchema)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StatusSchema
|
||||||
|
*/
|
||||||
|
export const zStatusSchema = z.object({
|
||||||
|
name: z.string(),
|
||||||
|
id: z.int(),
|
||||||
|
lexorank: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetStatusesResponse
|
||||||
|
*/
|
||||||
|
export const zGetStatusesResponse = z.object({
|
||||||
|
statuses: z.array(zStatusSchema)
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValidationError
|
||||||
|
*/
|
||||||
|
export const zValidationError = z.object({
|
||||||
|
loc: z.array(z.union([
|
||||||
|
z.string(),
|
||||||
|
z.int()
|
||||||
|
])),
|
||||||
|
msg: z.string(),
|
||||||
|
type: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTPValidationError
|
||||||
|
*/
|
||||||
|
export const zHttpValidationError = z.object({
|
||||||
|
detail: z.optional(z.array(zValidationError))
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateBoardSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateBoardSchema = z.object({
|
||||||
|
name: z.optional(z.union([
|
||||||
|
z.string(),
|
||||||
|
z.null()
|
||||||
|
])),
|
||||||
|
lexorank: z.optional(z.union([
|
||||||
|
z.string(),
|
||||||
|
z.null()
|
||||||
|
]))
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateBoardRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateBoardRequest = z.object({
|
||||||
|
board: zUpdateBoardSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateBoardResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateBoardResponse = z.object({
|
||||||
|
message: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateDealSchema = 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()
|
||||||
|
]))
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateDealRequest = z.object({
|
||||||
|
deal: zUpdateDealSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateDealResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateDealResponse = z.object({
|
||||||
|
message: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateStatusSchema
|
||||||
|
*/
|
||||||
|
export const zUpdateStatusSchema = z.object({
|
||||||
|
name: z.optional(z.union([
|
||||||
|
z.string(),
|
||||||
|
z.null()
|
||||||
|
])),
|
||||||
|
lexorank: z.optional(z.union([
|
||||||
|
z.string(),
|
||||||
|
z.null()
|
||||||
|
]))
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateStatusRequest
|
||||||
|
*/
|
||||||
|
export const zUpdateStatusRequest = z.object({
|
||||||
|
status: zUpdateStatusSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UpdateStatusResponse
|
||||||
|
*/
|
||||||
|
export const zUpdateStatusResponse = z.object({
|
||||||
|
message: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export const zGetBoardsData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
projectId: z.int()
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetBoardsResponse2 = zGetBoardsResponse;
|
||||||
|
|
||||||
|
export const zUpdateBoardData = z.object({
|
||||||
|
body: zUpdateBoardRequest,
|
||||||
|
path: z.object({
|
||||||
|
boardId: z.int()
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateBoardResponse2 = zUpdateBoardResponse;
|
||||||
|
|
||||||
|
export const zGetDealsData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
boardId: z.int()
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetDealsResponse2 = zGetDealsResponse;
|
||||||
|
|
||||||
|
export const zUpdateDealData = z.object({
|
||||||
|
body: zUpdateDealRequest,
|
||||||
|
path: z.object({
|
||||||
|
dealId: z.int()
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateDealResponse2 = zUpdateDealResponse;
|
||||||
|
|
||||||
|
export const zGetProjectsData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.optional(z.never()),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetProjectsResponse2 = zGetProjectsResponse;
|
||||||
|
|
||||||
|
export const zGetStatusesData = z.object({
|
||||||
|
body: z.optional(z.never()),
|
||||||
|
path: z.object({
|
||||||
|
boardId: z.int()
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zGetStatusesResponse2 = zGetStatusesResponse;
|
||||||
|
|
||||||
|
export const zUpdateStatusData = z.object({
|
||||||
|
body: zUpdateStatusRequest,
|
||||||
|
path: z.object({
|
||||||
|
statusId: z.int()
|
||||||
|
}),
|
||||||
|
query: z.optional(z.never())
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Successful Response
|
||||||
|
*/
|
||||||
|
export const zUpdateStatusResponse2 = zUpdateStatusResponse;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { combineReducers } from "@reduxjs/toolkit";
|
import { combineReducers } from "@reduxjs/toolkit";
|
||||||
import authReducer from "@/lib/features/auth/authSlice";
|
import authReducer from "@/lib/store/features/auth/authSlice";
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
auth: authReducer,
|
auth: authReducer,
|
||||||
@ -2,7 +2,7 @@ import { configureStore } from "@reduxjs/toolkit";
|
|||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { persistReducer, persistStore } from "redux-persist";
|
import { persistReducer, persistStore } from "redux-persist";
|
||||||
import storage from "redux-persist/lib/storage";
|
import storage from "redux-persist/lib/storage";
|
||||||
import rootReducer from "@/lib/features/rootReducer";
|
import rootReducer from "@/lib/store/features/rootReducer";
|
||||||
|
|
||||||
const persistConfig = {
|
const persistConfig = {
|
||||||
key: "root",
|
key: "root",
|
||||||
27
src/providers/ReactQueryProvider.tsx
Normal file
27
src/providers/ReactQueryProvider.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ReactNode, useState } from "react";
|
||||||
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ReactQueryProvider({ children }: Props) {
|
||||||
|
const [queryClient] = useState(
|
||||||
|
() =>
|
||||||
|
new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
staleTime: 3 * 60 * 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{children}
|
||||||
|
</QueryClientProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from "react-redux";
|
||||||
import { PersistGate } from "redux-persist/integration/react";
|
import { PersistGate } from "redux-persist/integration/react";
|
||||||
import { persistor, store } from "@/lib/store";
|
import { persistor, store } from "@/lib/store/store";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
|||||||
@ -13,8 +13,8 @@ export const myColor: MantineColorsTuple = [
|
|||||||
"#00718c",
|
"#00718c",
|
||||||
];
|
];
|
||||||
|
|
||||||
const radius = "lg";
|
const radius = "md";
|
||||||
const size = "lg";
|
const size = "md";
|
||||||
|
|
||||||
export const theme = createTheme({
|
export const theme = createTheme({
|
||||||
colors: {
|
colors: {
|
||||||
|
|||||||
36
src/utils/lexorank.ts
Normal file
36
src/utils/lexorank.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { LexoRank } from "lexorank";
|
||||||
|
|
||||||
|
type LexorankSortable = {
|
||||||
|
lexorank: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function compareByLexorank<T extends LexorankSortable>(
|
||||||
|
a: T,
|
||||||
|
b: T
|
||||||
|
): -1 | 1 | 0 {
|
||||||
|
if (a.lexorank < b.lexorank) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.lexorank > b.lexorank) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortByLexorank<T extends LexorankSortable>(items: T[]): T[] {
|
||||||
|
return items.sort(compareByLexorank);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNewLexorank(
|
||||||
|
left?: LexoRank | null,
|
||||||
|
right?: LexoRank | null
|
||||||
|
): LexoRank {
|
||||||
|
if (right) {
|
||||||
|
if (left) return left?.between(right);
|
||||||
|
return right.between(LexoRank.min());
|
||||||
|
}
|
||||||
|
if (left) {
|
||||||
|
return left.between(LexoRank.max());
|
||||||
|
}
|
||||||
|
return LexoRank.middle();
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": ["node", "jest", "@testing-library/jest-dom"],
|
"types": ["node", "jest", "@testing-library/jest-dom"],
|
||||||
"target": "es5",
|
"target": "ES2020",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|||||||
595
yarn.lock
595
yarn.lock
@ -1533,6 +1533,55 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@dnd-kit/accessibility@npm:^3.1.1":
|
||||||
|
version: 3.1.1
|
||||||
|
resolution: "@dnd-kit/accessibility@npm:3.1.1"
|
||||||
|
dependencies:
|
||||||
|
tslib: "npm:^2.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.8.0"
|
||||||
|
checksum: 10c0/be0bf41716dc58f9386bc36906ec1ce72b7b42b6d1d0e631d347afe9bd8714a829bd6f58a346dd089b1519e93918ae2f94497411a61a4f5e4d9247c6cfd1fef8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@dnd-kit/core@npm:^6.3.1":
|
||||||
|
version: 6.3.1
|
||||||
|
resolution: "@dnd-kit/core@npm:6.3.1"
|
||||||
|
dependencies:
|
||||||
|
"@dnd-kit/accessibility": "npm:^3.1.1"
|
||||||
|
"@dnd-kit/utilities": "npm:^3.2.2"
|
||||||
|
tslib: "npm:^2.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.8.0"
|
||||||
|
react-dom: ">=16.8.0"
|
||||||
|
checksum: 10c0/196db95d81096d9dc248983533eab91ba83591770fa5c894b1ac776f42af0d99522b3fd5bb3923411470e4733fcfa103e6ee17adc17b9b7eb54c7fbec5ff7c52
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@dnd-kit/sortable@npm:^10.0.0":
|
||||||
|
version: 10.0.0
|
||||||
|
resolution: "@dnd-kit/sortable@npm:10.0.0"
|
||||||
|
dependencies:
|
||||||
|
"@dnd-kit/utilities": "npm:^3.2.2"
|
||||||
|
tslib: "npm:^2.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
"@dnd-kit/core": ^6.3.0
|
||||||
|
react: ">=16.8.0"
|
||||||
|
checksum: 10c0/37ee48bc6789fb512dc0e4c374a96d19abe5b2b76dc34856a5883aaa96c3297891b94cc77bbc409e074dcce70967ebcb9feb40cd9abadb8716fc280b4c7f99af
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@dnd-kit/utilities@npm:^3.2.2":
|
||||||
|
version: 3.2.2
|
||||||
|
resolution: "@dnd-kit/utilities@npm:3.2.2"
|
||||||
|
dependencies:
|
||||||
|
tslib: "npm:^2.0.0"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.8.0"
|
||||||
|
checksum: 10c0/9aa90526f3e3fd567b5acc1b625a63177b9e8d00e7e50b2bd0e08fa2bf4dba7e19529777e001fdb8f89a7ce69f30b190c8364d390212634e0afdfa8c395e85a0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@dual-bundle/import-meta-resolve@npm:^4.1.0":
|
"@dual-bundle/import-meta-resolve@npm:^4.1.0":
|
||||||
version: 4.1.0
|
version: 4.1.0
|
||||||
resolution: "@dual-bundle/import-meta-resolve@npm:4.1.0"
|
resolution: "@dual-bundle/import-meta-resolve@npm:4.1.0"
|
||||||
@ -1888,6 +1937,57 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@hey-api/client-axios@npm:^0.9.1":
|
||||||
|
version: 0.9.1
|
||||||
|
resolution: "@hey-api/client-axios@npm:0.9.1"
|
||||||
|
peerDependencies:
|
||||||
|
"@hey-api/openapi-ts": < 2
|
||||||
|
axios: ">= 1.0.0 < 2"
|
||||||
|
checksum: 10c0/61cca384edcb1b1a877554d82846837a907b93380c1bee1725b41b048619115d60a5e2405980e7df1e2f0d318a8bc750248f6d2672a5a0f398fcba3b639a8f1c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@hey-api/client-next@npm:^0.5.1":
|
||||||
|
version: 0.5.1
|
||||||
|
resolution: "@hey-api/client-next@npm:0.5.1"
|
||||||
|
peerDependencies:
|
||||||
|
"@hey-api/openapi-ts": < 2
|
||||||
|
checksum: 10c0/111ac8f4f85244661b99561e7dae36eee99558752b953dee070a5a588d5f50433034ed528035c4d911e2914850704e88ad1312de5475bc13ffe73748cceace62
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@hey-api/json-schema-ref-parser@npm:1.0.6":
|
||||||
|
version: 1.0.6
|
||||||
|
resolution: "@hey-api/json-schema-ref-parser@npm:1.0.6"
|
||||||
|
dependencies:
|
||||||
|
"@jsdevtools/ono": "npm:^7.1.3"
|
||||||
|
"@types/json-schema": "npm:^7.0.15"
|
||||||
|
js-yaml: "npm:^4.1.0"
|
||||||
|
lodash: "npm:^4.17.21"
|
||||||
|
checksum: 10c0/a3d15b2316ef5a4442131386aa61e196af4f0e01064d5c66a598cf0ea5b9217dce57cb40e98278cdac9548a7e27ecc5cf9146fe301b9371aefd81a0933553252
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@hey-api/openapi-ts@npm:^0.80.1":
|
||||||
|
version: 0.80.1
|
||||||
|
resolution: "@hey-api/openapi-ts@npm:0.80.1"
|
||||||
|
dependencies:
|
||||||
|
"@hey-api/json-schema-ref-parser": "npm:1.0.6"
|
||||||
|
ansi-colors: "npm:4.1.3"
|
||||||
|
c12: "npm:2.0.1"
|
||||||
|
color-support: "npm:1.1.3"
|
||||||
|
commander: "npm:13.0.0"
|
||||||
|
handlebars: "npm:4.7.8"
|
||||||
|
open: "npm:10.1.2"
|
||||||
|
semver: "npm:7.7.2"
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ^5.5.3
|
||||||
|
bin:
|
||||||
|
openapi-ts: bin/index.cjs
|
||||||
|
checksum: 10c0/5c203712b6d3e4ad502518c830b702f239c46150d31544b491e85873c419c85ade3f12109699716d39acb8b9351c80c399355d49a5fc92fd458cab9380a7e6d0
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@humanfs/core@npm:^0.19.1":
|
"@humanfs/core@npm:^0.19.1":
|
||||||
version: 0.19.1
|
version: 0.19.1
|
||||||
resolution: "@humanfs/core@npm:0.19.1"
|
resolution: "@humanfs/core@npm:0.19.1"
|
||||||
@ -2730,6 +2830,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@jsdevtools/ono@npm:^7.1.3":
|
||||||
|
version: 7.1.3
|
||||||
|
resolution: "@jsdevtools/ono@npm:7.1.3"
|
||||||
|
checksum: 10c0/a9f7e3e8e3bc315a34959934a5e2f874c423cf4eae64377d3fc9de0400ed9f36cb5fd5ebce3300d2e8f4085f557c4a8b591427a583729a87841fda46e6c216b9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@keyv/serialize@npm:^1.1.0":
|
"@keyv/serialize@npm:^1.1.0":
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
resolution: "@keyv/serialize@npm:1.1.0"
|
resolution: "@keyv/serialize@npm:1.1.0"
|
||||||
@ -3573,6 +3680,24 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@tanstack/query-core@npm:5.83.0":
|
||||||
|
version: 5.83.0
|
||||||
|
resolution: "@tanstack/query-core@npm:5.83.0"
|
||||||
|
checksum: 10c0/e6dc480bc99eaca552a9ad65423788b60368cf99308681252fc7dbe42a3f2c1c978db0d3471cc3349b9112cfb4d967ace4e192a1d7e987e30c5c1ff74809c77c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"@tanstack/react-query@npm:^5.83.0":
|
||||||
|
version: 5.83.0
|
||||||
|
resolution: "@tanstack/react-query@npm:5.83.0"
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/query-core": "npm:5.83.0"
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18 || ^19
|
||||||
|
checksum: 10c0/883229f9219ca906a54d7caafd44d59b57db5dbe87e954f8a7027f460e9f8b97842dfbd0d676dc3111d577baf312c64f6c1fdd67cd1e4b0f0bf574e29670c606
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@testing-library/dom@npm:10.4.0, @testing-library/dom@npm:^10.4.0":
|
"@testing-library/dom@npm:10.4.0, @testing-library/dom@npm:^10.4.0":
|
||||||
version: 10.4.0
|
version: 10.4.0
|
||||||
resolution: "@testing-library/dom@npm:10.4.0"
|
resolution: "@testing-library/dom@npm:10.4.0"
|
||||||
@ -3837,6 +3962,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/lodash@npm:^4.17.20":
|
||||||
|
version: 4.17.20
|
||||||
|
resolution: "@types/lodash@npm:4.17.20"
|
||||||
|
checksum: 10c0/98cdd0faae22cbb8079a01a3bb65aa8f8c41143367486c1cbf5adc83f16c9272a2a5d2c1f541f61d0d73da543c16ee1d21cf2ef86cb93cd0cc0ac3bced6dd88f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:*":
|
"@types/node@npm:*":
|
||||||
version: 24.1.0
|
version: 24.1.0
|
||||||
resolution: "@types/node@npm:24.1.0"
|
resolution: "@types/node@npm:24.1.0"
|
||||||
@ -4576,6 +4708,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ansi-colors@npm:4.1.3":
|
||||||
|
version: 4.1.3
|
||||||
|
resolution: "ansi-colors@npm:4.1.3"
|
||||||
|
checksum: 10c0/ec87a2f59902f74e61eada7f6e6fe20094a628dab765cfdbd03c3477599368768cffccdb5d3bb19a1b6c99126783a143b1fee31aab729b31ffe5836c7e5e28b9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ansi-escapes@npm:^4.3.2":
|
"ansi-escapes@npm:^4.3.2":
|
||||||
version: 4.3.2
|
version: 4.3.2
|
||||||
resolution: "ansi-escapes@npm:4.3.2"
|
resolution: "ansi-escapes@npm:4.3.2"
|
||||||
@ -5292,6 +5431,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"bundle-name@npm:^4.1.0":
|
||||||
|
version: 4.1.0
|
||||||
|
resolution: "bundle-name@npm:4.1.0"
|
||||||
|
dependencies:
|
||||||
|
run-applescript: "npm:^7.0.0"
|
||||||
|
checksum: 10c0/8e575981e79c2bcf14d8b1c027a3775c095d362d1382312f444a7c861b0e21513c0bd8db5bd2b16e50ba0709fa622d4eab6b53192d222120305e68359daece29
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"busboy@npm:1.6.0":
|
"busboy@npm:1.6.0":
|
||||||
version: 1.6.0
|
version: 1.6.0
|
||||||
resolution: "busboy@npm:1.6.0"
|
resolution: "busboy@npm:1.6.0"
|
||||||
@ -5301,6 +5449,31 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"c12@npm:2.0.1":
|
||||||
|
version: 2.0.1
|
||||||
|
resolution: "c12@npm:2.0.1"
|
||||||
|
dependencies:
|
||||||
|
chokidar: "npm:^4.0.1"
|
||||||
|
confbox: "npm:^0.1.7"
|
||||||
|
defu: "npm:^6.1.4"
|
||||||
|
dotenv: "npm:^16.4.5"
|
||||||
|
giget: "npm:^1.2.3"
|
||||||
|
jiti: "npm:^2.3.0"
|
||||||
|
mlly: "npm:^1.7.1"
|
||||||
|
ohash: "npm:^1.1.4"
|
||||||
|
pathe: "npm:^1.1.2"
|
||||||
|
perfect-debounce: "npm:^1.0.0"
|
||||||
|
pkg-types: "npm:^1.2.0"
|
||||||
|
rc9: "npm:^2.1.2"
|
||||||
|
peerDependencies:
|
||||||
|
magicast: ^0.3.5
|
||||||
|
peerDependenciesMeta:
|
||||||
|
magicast:
|
||||||
|
optional: true
|
||||||
|
checksum: 10c0/6a019047918102eda28c29988990fdf38aa88df05f94c5e06cbc501365dcdd80df2a7726d17d67882871ca931fd8d025a66d46bbc361275eeb0bcb849a06369a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"cacache@npm:^19.0.1":
|
"cacache@npm:^19.0.1":
|
||||||
version: 19.0.1
|
version: 19.0.1
|
||||||
resolution: "cacache@npm:19.0.1"
|
resolution: "cacache@npm:19.0.1"
|
||||||
@ -5481,6 +5654,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"chokidar@npm:^4.0.1":
|
||||||
|
version: 4.0.3
|
||||||
|
resolution: "chokidar@npm:4.0.3"
|
||||||
|
dependencies:
|
||||||
|
readdirp: "npm:^4.0.1"
|
||||||
|
checksum: 10c0/a58b9df05bb452f7d105d9e7229ac82fa873741c0c40ddcc7bb82f8a909fbe3f7814c9ebe9bc9a2bef9b737c0ec6e2d699d179048ef06ad3ec46315df0ebe6ad
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"chownr@npm:^2.0.0":
|
||||||
|
version: 2.0.0
|
||||||
|
resolution: "chownr@npm:2.0.0"
|
||||||
|
checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"chownr@npm:^3.0.0":
|
"chownr@npm:^3.0.0":
|
||||||
version: 3.0.0
|
version: 3.0.0
|
||||||
resolution: "chownr@npm:3.0.0"
|
resolution: "chownr@npm:3.0.0"
|
||||||
@ -5519,6 +5708,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"citty@npm:^0.1.6":
|
||||||
|
version: 0.1.6
|
||||||
|
resolution: "citty@npm:0.1.6"
|
||||||
|
dependencies:
|
||||||
|
consola: "npm:^3.2.3"
|
||||||
|
checksum: 10c0/d26ad82a9a4a8858c7e149d90b878a3eceecd4cfd3e2ed3cd5f9a06212e451fb4f8cbe0fa39a3acb1b3e8f18e22db8ee5def5829384bad50e823d4b301609b48
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"cjs-module-lexer@npm:^1.2.3":
|
"cjs-module-lexer@npm:^1.2.3":
|
||||||
version: 1.4.3
|
version: 1.4.3
|
||||||
resolution: "cjs-module-lexer@npm:1.4.3"
|
resolution: "cjs-module-lexer@npm:1.4.3"
|
||||||
@ -5614,6 +5812,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"color-support@npm:1.1.3":
|
||||||
|
version: 1.1.3
|
||||||
|
resolution: "color-support@npm:1.1.3"
|
||||||
|
bin:
|
||||||
|
color-support: bin.js
|
||||||
|
checksum: 10c0/8ffeaa270a784dc382f62d9be0a98581db43e11eee301af14734a6d089bd456478b1a8b3e7db7ca7dc5b18a75f828f775c44074020b51c05fc00e6d0992b1cc6
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"color@npm:^4.2.3":
|
"color@npm:^4.2.3":
|
||||||
version: 4.2.3
|
version: 4.2.3
|
||||||
resolution: "color@npm:4.2.3"
|
resolution: "color@npm:4.2.3"
|
||||||
@ -5647,6 +5854,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"commander@npm:13.0.0":
|
||||||
|
version: 13.0.0
|
||||||
|
resolution: "commander@npm:13.0.0"
|
||||||
|
checksum: 10c0/8ba1e2b83bfdbcefd967aa505f5f5dc58202aa5f8e10437f61f6980dd8a69f868dba439a261f9fb72fc543c5f6fe58140e37b001a4c70b92ae22527abe94dfe1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"commander@npm:^2.20.0":
|
"commander@npm:^2.20.0":
|
||||||
version: 2.20.3
|
version: 2.20.3
|
||||||
resolution: "commander@npm:2.20.3"
|
resolution: "commander@npm:2.20.3"
|
||||||
@ -5689,6 +5903,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"confbox@npm:^0.1.7, confbox@npm:^0.1.8":
|
||||||
|
version: 0.1.8
|
||||||
|
resolution: "confbox@npm:0.1.8"
|
||||||
|
checksum: 10c0/fc2c68d97cb54d885b10b63e45bd8da83a8a71459d3ecf1825143dd4c7f9f1b696b3283e07d9d12a144c1301c2ebc7842380bdf0014e55acc4ae1c9550102418
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"consola@npm:^3.2.3, consola@npm:^3.4.0":
|
||||||
|
version: 3.4.2
|
||||||
|
resolution: "consola@npm:3.4.2"
|
||||||
|
checksum: 10c0/7cebe57ecf646ba74b300bcce23bff43034ed6fbec9f7e39c27cee1dc00df8a21cd336b466ad32e304ea70fba04ec9e890c200270de9a526ce021ba8a7e4c11a
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"console-browserify@npm:^1.2.0":
|
"console-browserify@npm:^1.2.0":
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
resolution: "console-browserify@npm:1.2.0"
|
resolution: "console-browserify@npm:1.2.0"
|
||||||
@ -5824,7 +6052,12 @@ __metadata:
|
|||||||
resolution: "crm-frontend@workspace:."
|
resolution: "crm-frontend@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/core": "npm:^7.27.4"
|
"@babel/core": "npm:^7.27.4"
|
||||||
|
"@dnd-kit/core": "npm:^6.3.1"
|
||||||
|
"@dnd-kit/sortable": "npm:^10.0.0"
|
||||||
"@eslint/js": "npm:^9.29.0"
|
"@eslint/js": "npm:^9.29.0"
|
||||||
|
"@hey-api/client-axios": "npm:^0.9.1"
|
||||||
|
"@hey-api/client-next": "npm:^0.5.1"
|
||||||
|
"@hey-api/openapi-ts": "npm:^0.80.1"
|
||||||
"@ianvs/prettier-plugin-sort-imports": "npm:^4.4.2"
|
"@ianvs/prettier-plugin-sort-imports": "npm:^4.4.2"
|
||||||
"@mantine/core": "npm:8.1.2"
|
"@mantine/core": "npm:8.1.2"
|
||||||
"@mantine/form": "npm:^8.1.3"
|
"@mantine/form": "npm:^8.1.3"
|
||||||
@ -5837,12 +6070,14 @@ __metadata:
|
|||||||
"@storybook/react": "npm:^8.6.8"
|
"@storybook/react": "npm:^8.6.8"
|
||||||
"@tabler/icons-react": "npm:^3.34.0"
|
"@tabler/icons-react": "npm:^3.34.0"
|
||||||
"@tailwindcss/postcss": "npm:^4.1.11"
|
"@tailwindcss/postcss": "npm:^4.1.11"
|
||||||
|
"@tanstack/react-query": "npm:^5.83.0"
|
||||||
"@testing-library/dom": "npm:^10.4.0"
|
"@testing-library/dom": "npm:^10.4.0"
|
||||||
"@testing-library/jest-dom": "npm:^6.6.3"
|
"@testing-library/jest-dom": "npm:^6.6.3"
|
||||||
"@testing-library/react": "npm:^16.3.0"
|
"@testing-library/react": "npm:^16.3.0"
|
||||||
"@testing-library/user-event": "npm:^14.6.1"
|
"@testing-library/user-event": "npm:^14.6.1"
|
||||||
"@types/eslint-plugin-jsx-a11y": "npm:^6"
|
"@types/eslint-plugin-jsx-a11y": "npm:^6"
|
||||||
"@types/jest": "npm:^29.5.14"
|
"@types/jest": "npm:^29.5.14"
|
||||||
|
"@types/lodash": "npm:^4.17.20"
|
||||||
"@types/node": "npm:^22.13.11"
|
"@types/node": "npm:^22.13.11"
|
||||||
"@types/react": "npm:19.1.8"
|
"@types/react": "npm:19.1.8"
|
||||||
"@types/react-redux": "npm:^7.1.34"
|
"@types/react-redux": "npm:^7.1.34"
|
||||||
@ -5853,12 +6088,14 @@ __metadata:
|
|||||||
classnames: "npm:^2.5.1"
|
classnames: "npm:^2.5.1"
|
||||||
eslint: "npm:^9.29.0"
|
eslint: "npm:^9.29.0"
|
||||||
eslint-config-mantine: "npm:^4.0.3"
|
eslint-config-mantine: "npm:^4.0.3"
|
||||||
|
eslint-plugin-eslint-comments: "npm:^3.2.0"
|
||||||
eslint-plugin-jsx-a11y: "npm:^6.10.2"
|
eslint-plugin-jsx-a11y: "npm:^6.10.2"
|
||||||
eslint-plugin-react: "npm:^7.37.5"
|
eslint-plugin-react: "npm:^7.37.5"
|
||||||
framer-motion: "npm:^12.23.7"
|
framer-motion: "npm:^12.23.7"
|
||||||
i18n-iso-countries: "npm:^7.14.0"
|
i18n-iso-countries: "npm:^7.14.0"
|
||||||
jest: "npm:^30.0.0"
|
jest: "npm:^30.0.0"
|
||||||
jest-environment-jsdom: "npm:^30.0.0"
|
jest-environment-jsdom: "npm:^30.0.0"
|
||||||
|
lexorank: "npm:^1.0.5"
|
||||||
libphonenumber-js: "npm:^1.12.10"
|
libphonenumber-js: "npm:^1.12.10"
|
||||||
next: "npm:15.3.3"
|
next: "npm:15.3.3"
|
||||||
postcss: "npm:^8.5.6"
|
postcss: "npm:^8.5.6"
|
||||||
@ -5879,6 +6116,7 @@ __metadata:
|
|||||||
ts-jest: "npm:^29.4.0"
|
ts-jest: "npm:^29.4.0"
|
||||||
typescript: "npm:5.8.3"
|
typescript: "npm:5.8.3"
|
||||||
typescript-eslint: "npm:^8.34.0"
|
typescript-eslint: "npm:^8.34.0"
|
||||||
|
zod: "npm:^4.0.14"
|
||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
@ -6123,6 +6361,23 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"default-browser-id@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "default-browser-id@npm:5.0.0"
|
||||||
|
checksum: 10c0/957fb886502594c8e645e812dfe93dba30ed82e8460d20ce39c53c5b0f3e2afb6ceaec2249083b90bdfbb4cb0f34e1f73fde3d68cac00becdbcfd894156b5ead
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"default-browser@npm:^5.2.1":
|
||||||
|
version: 5.2.1
|
||||||
|
resolution: "default-browser@npm:5.2.1"
|
||||||
|
dependencies:
|
||||||
|
bundle-name: "npm:^4.1.0"
|
||||||
|
default-browser-id: "npm:^5.0.0"
|
||||||
|
checksum: 10c0/73f17dc3c58026c55bb5538749597db31f9561c0193cd98604144b704a981c95a466f8ecc3c2db63d8bfd04fb0d426904834cfc91ae510c6aeb97e13c5167c4d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4":
|
"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4":
|
||||||
version: 1.1.4
|
version: 1.1.4
|
||||||
resolution: "define-data-property@npm:1.1.4"
|
resolution: "define-data-property@npm:1.1.4"
|
||||||
@ -6141,6 +6396,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"define-lazy-prop@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "define-lazy-prop@npm:3.0.0"
|
||||||
|
checksum: 10c0/5ab0b2bf3fa58b3a443140bbd4cd3db1f91b985cc8a246d330b9ac3fc0b6a325a6d82bddc0b055123d745b3f9931afeea74a5ec545439a1630b9c8512b0eeb49
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"define-properties@npm:^1.1.3, define-properties@npm:^1.2.1":
|
"define-properties@npm:^1.1.3, define-properties@npm:^1.2.1":
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
resolution: "define-properties@npm:1.2.1"
|
resolution: "define-properties@npm:1.2.1"
|
||||||
@ -6152,6 +6414,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"defu@npm:^6.1.4":
|
||||||
|
version: 6.1.4
|
||||||
|
resolution: "defu@npm:6.1.4"
|
||||||
|
checksum: 10c0/2d6cc366262dc0cb8096e429368e44052fdf43ed48e53ad84cc7c9407f890301aa5fcb80d0995abaaf842b3949f154d060be4160f7a46cb2bc2f7726c81526f5
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"delayed-stream@npm:~1.0.0":
|
"delayed-stream@npm:~1.0.0":
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
resolution: "delayed-stream@npm:1.0.0"
|
resolution: "delayed-stream@npm:1.0.0"
|
||||||
@ -6176,6 +6445,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"destr@npm:^2.0.3":
|
||||||
|
version: 2.0.5
|
||||||
|
resolution: "destr@npm:2.0.5"
|
||||||
|
checksum: 10c0/efabffe7312a45ad90d79975376be958c50069f1156b94c181199763a7f971e113bd92227c26b94a169c71ca7dbc13583b7e96e5164743969fc79e1ff153e646
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"detect-libc@npm:^2.0.3, detect-libc@npm:^2.0.4":
|
"detect-libc@npm:^2.0.3, detect-libc@npm:^2.0.4":
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
resolution: "detect-libc@npm:2.0.4"
|
resolution: "detect-libc@npm:2.0.4"
|
||||||
@ -6337,6 +6613,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"dotenv@npm:^16.4.5":
|
||||||
|
version: 16.6.1
|
||||||
|
resolution: "dotenv@npm:16.6.1"
|
||||||
|
checksum: 10c0/15ce56608326ea0d1d9414a5c8ee6dcf0fffc79d2c16422b4ac2268e7e2d76ff5a572d37ffe747c377de12005f14b3cc22361e79fc7f1061cce81f77d2c973dc
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1":
|
"dunder-proto@npm:^1.0.0, dunder-proto@npm:^1.0.1":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "dunder-proto@npm:1.0.1"
|
resolution: "dunder-proto@npm:1.0.1"
|
||||||
@ -6754,6 +7037,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"escape-string-regexp@npm:^1.0.5":
|
||||||
|
version: 1.0.5
|
||||||
|
resolution: "escape-string-regexp@npm:1.0.5"
|
||||||
|
checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"escape-string-regexp@npm:^2.0.0":
|
"escape-string-regexp@npm:^2.0.0":
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
resolution: "escape-string-regexp@npm:2.0.0"
|
resolution: "escape-string-regexp@npm:2.0.0"
|
||||||
@ -6781,6 +7071,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"eslint-plugin-eslint-comments@npm:^3.2.0":
|
||||||
|
version: 3.2.0
|
||||||
|
resolution: "eslint-plugin-eslint-comments@npm:3.2.0"
|
||||||
|
dependencies:
|
||||||
|
escape-string-regexp: "npm:^1.0.5"
|
||||||
|
ignore: "npm:^5.0.5"
|
||||||
|
peerDependencies:
|
||||||
|
eslint: ">=4.19.1"
|
||||||
|
checksum: 10c0/c71db824592dc8ea498021572a0bd33d763ef26126bdb3b84a027ca75a1adbe0894ec95024f7de39ef12308560e62cbf8af0d06ffe472be5ba8bd9169c928e96
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"eslint-plugin-jsx-a11y@npm:^6.10.2":
|
"eslint-plugin-jsx-a11y@npm:^6.10.2":
|
||||||
version: 6.10.2
|
version: 6.10.2
|
||||||
resolution: "eslint-plugin-jsx-a11y@npm:6.10.2"
|
resolution: "eslint-plugin-jsx-a11y@npm:6.10.2"
|
||||||
@ -7393,6 +7695,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"fs-minipass@npm:^2.0.0":
|
||||||
|
version: 2.1.0
|
||||||
|
resolution: "fs-minipass@npm:2.1.0"
|
||||||
|
dependencies:
|
||||||
|
minipass: "npm:^3.0.0"
|
||||||
|
checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"fs-minipass@npm:^3.0.0":
|
"fs-minipass@npm:^3.0.0":
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
resolution: "fs-minipass@npm:3.0.3"
|
resolution: "fs-minipass@npm:3.0.3"
|
||||||
@ -7537,6 +7848,23 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"giget@npm:^1.2.3":
|
||||||
|
version: 1.2.5
|
||||||
|
resolution: "giget@npm:1.2.5"
|
||||||
|
dependencies:
|
||||||
|
citty: "npm:^0.1.6"
|
||||||
|
consola: "npm:^3.4.0"
|
||||||
|
defu: "npm:^6.1.4"
|
||||||
|
node-fetch-native: "npm:^1.6.6"
|
||||||
|
nypm: "npm:^0.5.4"
|
||||||
|
pathe: "npm:^2.0.3"
|
||||||
|
tar: "npm:^6.2.1"
|
||||||
|
bin:
|
||||||
|
giget: dist/cli.mjs
|
||||||
|
checksum: 10c0/0c541589b8a10274f5adb6cd34a568829939182f50b3d80f8bb891e974b889f0fc629a5d702920456037cc9c90fba84cf3860bad7a22a46bc51a5c55998f24a9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
|
"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2":
|
||||||
version: 5.1.2
|
version: 5.1.2
|
||||||
resolution: "glob-parent@npm:5.1.2"
|
resolution: "glob-parent@npm:5.1.2"
|
||||||
@ -7680,6 +8008,24 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"handlebars@npm:4.7.8":
|
||||||
|
version: 4.7.8
|
||||||
|
resolution: "handlebars@npm:4.7.8"
|
||||||
|
dependencies:
|
||||||
|
minimist: "npm:^1.2.5"
|
||||||
|
neo-async: "npm:^2.6.2"
|
||||||
|
source-map: "npm:^0.6.1"
|
||||||
|
uglify-js: "npm:^3.1.4"
|
||||||
|
wordwrap: "npm:^1.0.0"
|
||||||
|
dependenciesMeta:
|
||||||
|
uglify-js:
|
||||||
|
optional: true
|
||||||
|
bin:
|
||||||
|
handlebars: bin/handlebars
|
||||||
|
checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"has-bigints@npm:^1.0.2":
|
"has-bigints@npm:^1.0.2":
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
resolution: "has-bigints@npm:1.1.0"
|
resolution: "has-bigints@npm:1.1.0"
|
||||||
@ -7968,7 +8314,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"ignore@npm:^5.2.0":
|
"ignore@npm:^5.0.5, ignore@npm:^5.2.0":
|
||||||
version: 5.3.2
|
version: 5.3.2
|
||||||
resolution: "ignore@npm:5.3.2"
|
resolution: "ignore@npm:5.3.2"
|
||||||
checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337
|
checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337
|
||||||
@ -8212,6 +8558,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-docker@npm:^3.0.0":
|
||||||
|
version: 3.0.0
|
||||||
|
resolution: "is-docker@npm:3.0.0"
|
||||||
|
bin:
|
||||||
|
is-docker: cli.js
|
||||||
|
checksum: 10c0/d2c4f8e6d3e34df75a5defd44991b6068afad4835bb783b902fa12d13ebdb8f41b2a199dcb0b5ed2cb78bfee9e4c0bbdb69c2d9646f4106464674d3e697a5856
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-extglob@npm:^2.1.1":
|
"is-extglob@npm:^2.1.1":
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
resolution: "is-extglob@npm:2.1.1"
|
resolution: "is-extglob@npm:2.1.1"
|
||||||
@ -8263,6 +8618,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-inside-container@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "is-inside-container@npm:1.0.0"
|
||||||
|
dependencies:
|
||||||
|
is-docker: "npm:^3.0.0"
|
||||||
|
bin:
|
||||||
|
is-inside-container: cli.js
|
||||||
|
checksum: 10c0/a8efb0e84f6197e6ff5c64c52890fa9acb49b7b74fed4da7c95383965da6f0fa592b4dbd5e38a79f87fc108196937acdbcd758fcefc9b140e479b39ce1fcd1cd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"is-map@npm:^2.0.3":
|
"is-map@npm:^2.0.3":
|
||||||
version: 2.0.3
|
version: 2.0.3
|
||||||
resolution: "is-map@npm:2.0.3"
|
resolution: "is-map@npm:2.0.3"
|
||||||
@ -8418,6 +8784,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"is-wsl@npm:^3.1.0":
|
||||||
|
version: 3.1.0
|
||||||
|
resolution: "is-wsl@npm:3.1.0"
|
||||||
|
dependencies:
|
||||||
|
is-inside-container: "npm:^1.0.0"
|
||||||
|
checksum: 10c0/d3317c11995690a32c362100225e22ba793678fe8732660c6de511ae71a0ff05b06980cf21f98a6bf40d7be0e9e9506f859abe00a1118287d63e53d0a3d06947
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"isarray@npm:^2.0.5":
|
"isarray@npm:^2.0.5":
|
||||||
version: 2.0.5
|
version: 2.0.5
|
||||||
resolution: "isarray@npm:2.0.5"
|
resolution: "isarray@npm:2.0.5"
|
||||||
@ -9077,7 +9452,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"jiti@npm:^2.4.2":
|
"jiti@npm:^2.3.0, jiti@npm:^2.4.2":
|
||||||
version: 2.5.1
|
version: 2.5.1
|
||||||
resolution: "jiti@npm:2.5.1"
|
resolution: "jiti@npm:2.5.1"
|
||||||
bin:
|
bin:
|
||||||
@ -9329,6 +9704,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"lexorank@npm:^1.0.5":
|
||||||
|
version: 1.0.5
|
||||||
|
resolution: "lexorank@npm:1.0.5"
|
||||||
|
checksum: 10c0/76b4f4d9a837850a2f3e6d3c32dfd8700aa3bad9fb94921adf6ea0d8241c07ba89f62f106eaf54af0fac85c0f8653ef07da1489dd81556d2edd4f5e5fb4045cf
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"libphonenumber-js@npm:^1.12.10":
|
"libphonenumber-js@npm:^1.12.10":
|
||||||
version: 1.12.10
|
version: 1.12.10
|
||||||
resolution: "libphonenumber-js@npm:1.12.10"
|
resolution: "libphonenumber-js@npm:1.12.10"
|
||||||
@ -9832,7 +10214,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"minimist@npm:^1.2.6":
|
"minimist@npm:^1.2.5, minimist@npm:^1.2.6":
|
||||||
version: 1.2.8
|
version: 1.2.8
|
||||||
resolution: "minimist@npm:1.2.8"
|
resolution: "minimist@npm:1.2.8"
|
||||||
checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6
|
checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6
|
||||||
@ -9899,6 +10281,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"minipass@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "minipass@npm:5.0.0"
|
||||||
|
checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
|
"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2":
|
||||||
version: 7.1.2
|
version: 7.1.2
|
||||||
resolution: "minipass@npm:7.1.2"
|
resolution: "minipass@npm:7.1.2"
|
||||||
@ -9906,6 +10295,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"minizlib@npm:^2.1.1":
|
||||||
|
version: 2.1.2
|
||||||
|
resolution: "minizlib@npm:2.1.2"
|
||||||
|
dependencies:
|
||||||
|
minipass: "npm:^3.0.0"
|
||||||
|
yallist: "npm:^4.0.0"
|
||||||
|
checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"minizlib@npm:^3.0.1":
|
"minizlib@npm:^3.0.1":
|
||||||
version: 3.0.2
|
version: 3.0.2
|
||||||
resolution: "minizlib@npm:3.0.2"
|
resolution: "minizlib@npm:3.0.2"
|
||||||
@ -9915,6 +10314,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"mkdirp@npm:^1.0.3":
|
||||||
|
version: 1.0.4
|
||||||
|
resolution: "mkdirp@npm:1.0.4"
|
||||||
|
bin:
|
||||||
|
mkdirp: bin/cmd.js
|
||||||
|
checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"mkdirp@npm:^3.0.1":
|
"mkdirp@npm:^3.0.1":
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
resolution: "mkdirp@npm:3.0.1"
|
resolution: "mkdirp@npm:3.0.1"
|
||||||
@ -9924,6 +10332,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"mlly@npm:^1.7.1, mlly@npm:^1.7.4":
|
||||||
|
version: 1.7.4
|
||||||
|
resolution: "mlly@npm:1.7.4"
|
||||||
|
dependencies:
|
||||||
|
acorn: "npm:^8.14.0"
|
||||||
|
pathe: "npm:^2.0.1"
|
||||||
|
pkg-types: "npm:^1.3.0"
|
||||||
|
ufo: "npm:^1.5.4"
|
||||||
|
checksum: 10c0/69e738218a13d6365caf930e0ab4e2b848b84eec261597df9788cefb9930f3e40667be9cb58a4718834ba5f97a6efeef31d3b5a95f4388143fd4e0d0deff72ff
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"motion-dom@npm:^12.23.9":
|
"motion-dom@npm:^12.23.9":
|
||||||
version: 12.23.9
|
version: 12.23.9
|
||||||
resolution: "motion-dom@npm:12.23.9"
|
resolution: "motion-dom@npm:12.23.9"
|
||||||
@ -10071,6 +10491,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"node-fetch-native@npm:^1.6.6":
|
||||||
|
version: 1.6.7
|
||||||
|
resolution: "node-fetch-native@npm:1.6.7"
|
||||||
|
checksum: 10c0/8b748300fb053d21ca4d3db9c3ff52593d5e8f8a2d9fe90cbfad159676e324b954fdaefab46aeca007b5b9edab3d150021c4846444e4e8ab1f4e44cd3807be87
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"node-gyp@npm:latest":
|
"node-gyp@npm:latest":
|
||||||
version: 11.2.0
|
version: 11.2.0
|
||||||
resolution: "node-gyp@npm:11.2.0"
|
resolution: "node-gyp@npm:11.2.0"
|
||||||
@ -10190,6 +10617,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"nypm@npm:^0.5.4":
|
||||||
|
version: 0.5.4
|
||||||
|
resolution: "nypm@npm:0.5.4"
|
||||||
|
dependencies:
|
||||||
|
citty: "npm:^0.1.6"
|
||||||
|
consola: "npm:^3.4.0"
|
||||||
|
pathe: "npm:^2.0.3"
|
||||||
|
pkg-types: "npm:^1.3.1"
|
||||||
|
tinyexec: "npm:^0.3.2"
|
||||||
|
ufo: "npm:^1.5.4"
|
||||||
|
bin:
|
||||||
|
nypm: dist/cli.mjs
|
||||||
|
checksum: 10c0/4b4661d2e460f4f8e96338669776dc3be4ed895bd34208ac188b5b8b438553aab737d41a5699cdc716f078fba9048b3d40b7d8a55c2544f9453536f837d323dc
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"object-assign@npm:^4.1.1":
|
"object-assign@npm:^4.1.1":
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
resolution: "object-assign@npm:4.1.1"
|
resolution: "object-assign@npm:4.1.1"
|
||||||
@ -10278,6 +10721,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ohash@npm:^1.1.4":
|
||||||
|
version: 1.1.6
|
||||||
|
resolution: "ohash@npm:1.1.6"
|
||||||
|
checksum: 10c0/3c25dde77662fffa3fa956b8975d7324311e3df15d9cf8d79b466ac3508f313e02b8ed3041d967810f68e00af344a5a3c4288be0208143c1844ba0f3cc83f777
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"once@npm:^1.3.0":
|
"once@npm:^1.3.0":
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
resolution: "once@npm:1.4.0"
|
resolution: "once@npm:1.4.0"
|
||||||
@ -10296,6 +10746,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"open@npm:10.1.2":
|
||||||
|
version: 10.1.2
|
||||||
|
resolution: "open@npm:10.1.2"
|
||||||
|
dependencies:
|
||||||
|
default-browser: "npm:^5.2.1"
|
||||||
|
define-lazy-prop: "npm:^3.0.0"
|
||||||
|
is-inside-container: "npm:^1.0.0"
|
||||||
|
is-wsl: "npm:^3.1.0"
|
||||||
|
checksum: 10c0/1bee796f06e549ce764f693272100323fbc04da8fa3c5b0402d6c2d11b3d76fa0aac0be7535e710015ff035326638e3b9a563f3b0e7ac3266473ed5663caae6d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"open@npm:^8.0.4":
|
"open@npm:^8.0.4":
|
||||||
version: 8.4.2
|
version: 8.4.2
|
||||||
resolution: "open@npm:8.4.2"
|
resolution: "open@npm:8.4.2"
|
||||||
@ -10553,6 +11015,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"pathe@npm:^1.1.2":
|
||||||
|
version: 1.1.2
|
||||||
|
resolution: "pathe@npm:1.1.2"
|
||||||
|
checksum: 10c0/64ee0a4e587fb0f208d9777a6c56e4f9050039268faaaaecd50e959ef01bf847b7872785c36483fa5cdcdbdfdb31fef2ff222684d4fc21c330ab60395c681897
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"pathe@npm:^2.0.1, pathe@npm:^2.0.3":
|
||||||
|
version: 2.0.3
|
||||||
|
resolution: "pathe@npm:2.0.3"
|
||||||
|
checksum: 10c0/c118dc5a8b5c4166011b2b70608762e260085180bb9e33e80a50dcdb1e78c010b1624f4280c492c92b05fc276715a4c357d1f9edc570f8f1b3d90b6839ebaca1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"pathval@npm:^2.0.0":
|
"pathval@npm:^2.0.0":
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
resolution: "pathval@npm:2.0.1"
|
resolution: "pathval@npm:2.0.1"
|
||||||
@ -10574,6 +11050,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"perfect-debounce@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "perfect-debounce@npm:1.0.0"
|
||||||
|
checksum: 10c0/e2baac416cae046ef1b270812cf9ccfb0f91c04ea36ac7f5b00bc84cb7f41bdbba087c0ab21b4e02a7ef3a1f1f6db399f137cecec46868bd7d8d88c2a9ee431f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1":
|
"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "picocolors@npm:1.1.1"
|
resolution: "picocolors@npm:1.1.1"
|
||||||
@ -10620,6 +11103,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"pkg-types@npm:^1.2.0, pkg-types@npm:^1.3.0, pkg-types@npm:^1.3.1":
|
||||||
|
version: 1.3.1
|
||||||
|
resolution: "pkg-types@npm:1.3.1"
|
||||||
|
dependencies:
|
||||||
|
confbox: "npm:^0.1.8"
|
||||||
|
mlly: "npm:^1.7.4"
|
||||||
|
pathe: "npm:^2.0.1"
|
||||||
|
checksum: 10c0/19e6cb8b66dcc66c89f2344aecfa47f2431c988cfa3366bdfdcfb1dd6695f87dcce37fbd90fe9d1605e2f4440b77f391e83c23255347c35cf84e7fd774d7fcea
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"pnp-webpack-plugin@npm:^1.7.0":
|
"pnp-webpack-plugin@npm:^1.7.0":
|
||||||
version: 1.7.0
|
version: 1.7.0
|
||||||
resolution: "pnp-webpack-plugin@npm:1.7.0"
|
resolution: "pnp-webpack-plugin@npm:1.7.0"
|
||||||
@ -11039,6 +11533,16 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"rc9@npm:^2.1.2":
|
||||||
|
version: 2.1.2
|
||||||
|
resolution: "rc9@npm:2.1.2"
|
||||||
|
dependencies:
|
||||||
|
defu: "npm:^6.1.4"
|
||||||
|
destr: "npm:^2.0.3"
|
||||||
|
checksum: 10c0/a2ead3b94bf033e35e4ea40d70062a09feddb8f589c3f5a8fe4e9342976974296aee9f6e9e72bd5e78e6ae4b7bc16dc244f63699fd7322c16314e3238db982c9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"react-docgen-typescript@npm:^2.2.2":
|
"react-docgen-typescript@npm:^2.2.2":
|
||||||
version: 2.4.0
|
version: 2.4.0
|
||||||
resolution: "react-docgen-typescript@npm:2.4.0"
|
resolution: "react-docgen-typescript@npm:2.4.0"
|
||||||
@ -11271,6 +11775,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"readdirp@npm:^4.0.1":
|
||||||
|
version: 4.1.2
|
||||||
|
resolution: "readdirp@npm:4.1.2"
|
||||||
|
checksum: 10c0/60a14f7619dec48c9c850255cd523e2717001b0e179dc7037cfa0895da7b9e9ab07532d324bfb118d73a710887d1e35f79c495fa91582784493e085d18c72c62
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"readdirp@npm:~3.6.0":
|
"readdirp@npm:~3.6.0":
|
||||||
version: 3.6.0
|
version: 3.6.0
|
||||||
resolution: "readdirp@npm:3.6.0"
|
resolution: "readdirp@npm:3.6.0"
|
||||||
@ -11603,6 +12114,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"run-applescript@npm:^7.0.0":
|
||||||
|
version: 7.0.0
|
||||||
|
resolution: "run-applescript@npm:7.0.0"
|
||||||
|
checksum: 10c0/bd821bbf154b8e6c8ecffeaf0c33cebbb78eb2987476c3f6b420d67ab4c5301faa905dec99ded76ebb3a7042b4e440189ae6d85bbbd3fc6e8d493347ecda8bfe
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"run-parallel@npm:^1.1.9":
|
"run-parallel@npm:^1.1.9":
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
resolution: "run-parallel@npm:1.2.0"
|
resolution: "run-parallel@npm:1.2.0"
|
||||||
@ -11732,6 +12250,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"semver@npm:7.7.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3, semver@npm:^7.7.2":
|
||||||
|
version: 7.7.2
|
||||||
|
resolution: "semver@npm:7.7.2"
|
||||||
|
bin:
|
||||||
|
semver: bin/semver.js
|
||||||
|
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"semver@npm:^6.0.0, semver@npm:^6.3.1":
|
"semver@npm:^6.0.0, semver@npm:^6.3.1":
|
||||||
version: 6.3.1
|
version: 6.3.1
|
||||||
resolution: "semver@npm:6.3.1"
|
resolution: "semver@npm:6.3.1"
|
||||||
@ -11741,15 +12268,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3, semver@npm:^7.7.2":
|
|
||||||
version: 7.7.2
|
|
||||||
resolution: "semver@npm:7.7.2"
|
|
||||||
bin:
|
|
||||||
semver: bin/semver.js
|
|
||||||
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"serialize-javascript@npm:^6.0.2":
|
"serialize-javascript@npm:^6.0.2":
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
resolution: "serialize-javascript@npm:6.0.2"
|
resolution: "serialize-javascript@npm:6.0.2"
|
||||||
@ -12134,7 +12652,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:~0.6.0, source-map@npm:~0.6.1":
|
"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1":
|
||||||
version: 0.6.1
|
version: 0.6.1
|
||||||
resolution: "source-map@npm:0.6.1"
|
resolution: "source-map@npm:0.6.1"
|
||||||
checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
|
checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011
|
||||||
@ -12715,6 +13233,20 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"tar@npm:^6.2.1":
|
||||||
|
version: 6.2.1
|
||||||
|
resolution: "tar@npm:6.2.1"
|
||||||
|
dependencies:
|
||||||
|
chownr: "npm:^2.0.0"
|
||||||
|
fs-minipass: "npm:^2.0.0"
|
||||||
|
minipass: "npm:^5.0.0"
|
||||||
|
minizlib: "npm:^2.1.1"
|
||||||
|
mkdirp: "npm:^1.0.3"
|
||||||
|
yallist: "npm:^4.0.0"
|
||||||
|
checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"tar@npm:^7.4.3":
|
"tar@npm:^7.4.3":
|
||||||
version: 7.4.3
|
version: 7.4.3
|
||||||
resolution: "tar@npm:7.4.3"
|
resolution: "tar@npm:7.4.3"
|
||||||
@ -12792,6 +13324,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"tinyexec@npm:^0.3.2":
|
||||||
|
version: 0.3.2
|
||||||
|
resolution: "tinyexec@npm:0.3.2"
|
||||||
|
checksum: 10c0/3efbf791a911be0bf0821eab37a3445c2ba07acc1522b1fa84ae1e55f10425076f1290f680286345ed919549ad67527d07281f1c19d584df3b74326909eb1f90
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"tinyglobby@npm:^0.2.12":
|
"tinyglobby@npm:^0.2.12":
|
||||||
version: 0.2.14
|
version: 0.2.14
|
||||||
resolution: "tinyglobby@npm:0.2.14"
|
resolution: "tinyglobby@npm:0.2.14"
|
||||||
@ -13114,6 +13653,22 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ufo@npm:^1.5.4":
|
||||||
|
version: 1.6.1
|
||||||
|
resolution: "ufo@npm:1.6.1"
|
||||||
|
checksum: 10c0/5a9f041e5945fba7c189d5410508cbcbefef80b253ed29aa2e1f8a2b86f4bd51af44ee18d4485e6d3468c92be9bf4a42e3a2b72dcaf27ce39ce947ec994f1e6b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"uglify-js@npm:^3.1.4":
|
||||||
|
version: 3.19.3
|
||||||
|
resolution: "uglify-js@npm:3.19.3"
|
||||||
|
bin:
|
||||||
|
uglifyjs: bin/uglifyjs
|
||||||
|
checksum: 10c0/83b0a90eca35f778e07cad9622b80c448b6aad457c9ff8e568afed978212b42930a95f9e1be943a1ffa4258a3340fbb899f41461131c05bb1d0a9c303aed8479
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"unbox-primitive@npm:^1.1.0":
|
"unbox-primitive@npm:^1.1.0":
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
resolution: "unbox-primitive@npm:1.1.0"
|
resolution: "unbox-primitive@npm:1.1.0"
|
||||||
@ -13685,6 +14240,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"wordwrap@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "wordwrap@npm:1.0.0"
|
||||||
|
checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
|
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0":
|
||||||
version: 7.0.0
|
version: 7.0.0
|
||||||
resolution: "wrap-ansi@npm:7.0.0"
|
resolution: "wrap-ansi@npm:7.0.0"
|
||||||
@ -13845,3 +14407,10 @@ __metadata:
|
|||||||
checksum: 10c0/5762caa3d0b421f4bdb7a1926b2ae2189fc6e4a14469258f183600028eb16db3e9e0306f46e8ebf5a52ff4b81a881f22637afefbef5399d6ad440824e9b27f9f
|
checksum: 10c0/5762caa3d0b421f4bdb7a1926b2ae2189fc6e4a14469258f183600028eb16db3e9e0306f46e8ebf5a52ff4b81a881f22637afefbef5399d6ad440824e9b27f9f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"zod@npm:^4.0.14":
|
||||||
|
version: 4.0.14
|
||||||
|
resolution: "zod@npm:4.0.14"
|
||||||
|
checksum: 10c0/ec8681050d393f3be1c2c8f30d7dd4f56bec3746855fb17288be856185a0fe65e68d1b24aec657bbf956f64c05e69332d3751eafd6c103d19385bcc46075612b
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|||||||
Reference in New Issue
Block a user