@btst/stack 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.cjs +1 -1
- package/dist/node_modules/.pnpm/@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/@dnd-kit/core/dist/core.esm.mjs +1 -1
- package/dist/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/sortable/dist/sortable.esm.cjs +77 -0
- package/dist/node_modules/.pnpm/@dnd-kit_sortable@10.0.0_@dnd-kit_core@6.3.1_react-dom@19.2.0_react@19.2.0__react@19.2.0__react@19.2.0/node_modules/@dnd-kit/sortable/dist/sortable.esm.mjs +79 -3
- package/dist/node_modules/.pnpm/@radix-ui_react-avatar@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react_850cfbef1935a6e49a6ad6c93c7ca70d/node_modules/@radix-ui/react-avatar/dist/index.cjs +140 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-avatar@1.1.11_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react_850cfbef1935a6e49a6ad6c93c7ca70d/node_modules/@radix-ui/react-avatar/dist/index.mjs +119 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-context@1.1.3_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-context/dist/index.cjs +80 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-context@1.1.3_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-context/dist/index.mjs +64 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.cjs +18 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.2.6_react@19.2.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.mjs +16 -0
- package/dist/packages/better-stack/src/plugins/kanban/api/plugin.cjs +846 -0
- package/dist/packages/better-stack/src/plugins/kanban/api/plugin.mjs +844 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/board-form.cjs +85 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/board-form.mjs +83 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/column-form.cjs +72 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/column-form.mjs +70 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/task-form.cjs +200 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/forms/task-form.mjs +198 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/board-skeleton.cjs +47 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/board-skeleton.mjs +45 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/boards-list-skeleton.cjs +30 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/loading/boards-list-skeleton.mjs +28 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/404-page.cjs +27 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/404-page.mjs +25 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.cjs +31 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.internal.cjs +458 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.internal.mjs +456 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/board-page.mjs +29 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.cjs +30 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.cjs +72 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.internal.mjs +70 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/boards-list-page.mjs +28 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.cjs +30 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.internal.cjs +51 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.internal.mjs +49 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/pages/new-board-page.mjs +28 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/column-content.cjs +76 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/column-content.mjs +74 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/default-error.cjs +27 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/default-error.mjs +25 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/empty-state.cjs +32 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/empty-state.mjs +30 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/kanban-board.cjs +78 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/kanban-board.mjs +76 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/page-wrapper.cjs +15 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/page-wrapper.mjs +13 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/task-card.cjs +68 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/task-card.mjs +66 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/user-avatar.cjs +32 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/components/shared/user-avatar.mjs +30 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/hooks/kanban-hooks.cjs +391 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/hooks/kanban-hooks.mjs +381 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/plugin.cjs +290 -0
- package/dist/packages/better-stack/src/plugins/kanban/client/plugin.mjs +288 -0
- package/dist/packages/better-stack/src/plugins/kanban/db.cjs +125 -0
- package/dist/packages/better-stack/src/plugins/kanban/db.mjs +123 -0
- package/dist/packages/better-stack/src/plugins/kanban/schemas.cjs +117 -0
- package/dist/packages/better-stack/src/plugins/kanban/schemas.mjs +102 -0
- package/dist/packages/better-stack/src/plugins/kanban/utils.cjs +49 -0
- package/dist/packages/better-stack/src/plugins/kanban/utils.mjs +45 -0
- package/dist/packages/ui/src/components/avatar.cjs +58 -0
- package/dist/packages/ui/src/components/avatar.mjs +54 -0
- package/dist/packages/ui/src/components/command.cjs +3 -3
- package/dist/packages/ui/src/components/command.mjs +3 -3
- package/dist/packages/ui/src/components/form-builder/index.mjs +2 -2
- package/dist/packages/ui/src/components/kanban.cjs +835 -0
- package/dist/packages/ui/src/components/kanban.mjs +805 -0
- package/dist/packages/ui/src/components/popover.cjs +8 -3
- package/dist/packages/ui/src/components/popover.mjs +9 -4
- package/dist/packages/ui/src/components/search-select.cjs +75 -0
- package/dist/packages/ui/src/components/search-select.mjs +73 -0
- package/dist/packages/ui/src/lib/compose-refs.cjs +56 -0
- package/dist/packages/ui/src/lib/compose-refs.mjs +39 -0
- package/dist/plugins/blog/api/index.d.cts +1 -1
- package/dist/plugins/blog/api/index.d.mts +1 -1
- package/dist/plugins/blog/api/index.d.ts +1 -1
- package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
- package/dist/plugins/blog/client/index.d.cts +1 -1
- package/dist/plugins/blog/client/index.d.mts +1 -1
- package/dist/plugins/blog/client/index.d.ts +1 -1
- package/dist/plugins/blog/query-keys.d.cts +2 -2
- package/dist/plugins/blog/query-keys.d.mts +2 -2
- package/dist/plugins/blog/query-keys.d.ts +2 -2
- package/dist/plugins/kanban/api/index.cjs +7 -0
- package/dist/plugins/kanban/api/index.d.cts +403 -0
- package/dist/plugins/kanban/api/index.d.mts +403 -0
- package/dist/plugins/kanban/api/index.d.ts +403 -0
- package/dist/plugins/kanban/api/index.mjs +1 -0
- package/dist/plugins/kanban/client/components/index.cjs +35 -0
- package/dist/plugins/kanban/client/components/index.d.cts +102 -0
- package/dist/plugins/kanban/client/components/index.d.mts +102 -0
- package/dist/plugins/kanban/client/components/index.d.ts +102 -0
- package/dist/plugins/kanban/client/components/index.mjs +15 -0
- package/dist/plugins/kanban/client/hooks/index.cjs +15 -0
- package/dist/plugins/kanban/client/hooks/index.d.cts +143 -0
- package/dist/plugins/kanban/client/hooks/index.d.mts +143 -0
- package/dist/plugins/kanban/client/hooks/index.d.ts +143 -0
- package/dist/plugins/kanban/client/hooks/index.mjs +1 -0
- package/dist/plugins/kanban/client/index.cjs +7 -0
- package/dist/plugins/kanban/client/index.d.cts +196 -0
- package/dist/plugins/kanban/client/index.d.mts +196 -0
- package/dist/plugins/kanban/client/index.d.ts +196 -0
- package/dist/plugins/kanban/client/index.mjs +1 -0
- package/dist/plugins/kanban/client.css +68 -0
- package/dist/plugins/kanban/query-keys.cjs +105 -0
- package/dist/plugins/kanban/query-keys.d.cts +59 -0
- package/dist/plugins/kanban/query-keys.d.mts +59 -0
- package/dist/plugins/kanban/query-keys.d.ts +59 -0
- package/dist/plugins/kanban/query-keys.mjs +103 -0
- package/dist/plugins/kanban/style.css +7 -0
- package/dist/plugins/ui-builder/style.css +6 -0
- package/dist/shared/stack.DKDMI-QO.d.cts +70 -0
- package/dist/shared/stack.DKDMI-QO.d.mts +70 -0
- package/dist/shared/stack.DKDMI-QO.d.ts +70 -0
- package/dist/shared/stack.FeaWkglm.d.cts +190 -0
- package/dist/shared/stack.FeaWkglm.d.mts +190 -0
- package/dist/shared/stack.FeaWkglm.d.ts +190 -0
- package/package.json +56 -2
- package/src/plugins/kanban/api/index.ts +6 -0
- package/src/plugins/kanban/api/plugin.ts +1245 -0
- package/src/plugins/kanban/client/components/forms/board-form.tsx +108 -0
- package/src/plugins/kanban/client/components/forms/column-form.tsx +97 -0
- package/src/plugins/kanban/client/components/forms/task-form.tsx +274 -0
- package/src/plugins/kanban/client/components/index.tsx +21 -0
- package/src/plugins/kanban/client/components/loading/board-skeleton.tsx +49 -0
- package/src/plugins/kanban/client/components/loading/boards-list-skeleton.tsx +34 -0
- package/src/plugins/kanban/client/components/loading/index.tsx +2 -0
- package/src/plugins/kanban/client/components/pages/404-page.tsx +28 -0
- package/src/plugins/kanban/client/components/pages/board-page.internal.tsx +575 -0
- package/src/plugins/kanban/client/components/pages/board-page.tsx +31 -0
- package/src/plugins/kanban/client/components/pages/boards-list-page.internal.tsx +101 -0
- package/src/plugins/kanban/client/components/pages/boards-list-page.tsx +26 -0
- package/src/plugins/kanban/client/components/pages/new-board-page.internal.tsx +65 -0
- package/src/plugins/kanban/client/components/pages/new-board-page.tsx +26 -0
- package/src/plugins/kanban/client/components/shared/column-content.tsx +108 -0
- package/src/plugins/kanban/client/components/shared/default-error.tsx +32 -0
- package/src/plugins/kanban/client/components/shared/empty-state.tsx +37 -0
- package/src/plugins/kanban/client/components/shared/kanban-board.tsx +87 -0
- package/src/plugins/kanban/client/components/shared/page-wrapper.tsx +20 -0
- package/src/plugins/kanban/client/components/shared/task-card.tsx +79 -0
- package/src/plugins/kanban/client/components/shared/user-avatar.tsx +63 -0
- package/src/plugins/kanban/client/hooks/index.tsx +11 -0
- package/src/plugins/kanban/client/hooks/kanban-hooks.tsx +560 -0
- package/src/plugins/kanban/client/index.ts +8 -0
- package/src/plugins/kanban/client/localization/index.ts +28 -0
- package/src/plugins/kanban/client/localization/kanban-common.ts +69 -0
- package/src/plugins/kanban/client/localization/kanban-forms.ts +70 -0
- package/src/plugins/kanban/client/localization/kanban-list.ts +36 -0
- package/src/plugins/kanban/client/overrides.ts +145 -0
- package/src/plugins/kanban/client/plugin.tsx +463 -0
- package/src/plugins/kanban/client.css +68 -0
- package/src/plugins/kanban/db.ts +125 -0
- package/src/plugins/kanban/query-keys.ts +154 -0
- package/src/plugins/kanban/schemas.ts +143 -0
- package/src/plugins/kanban/style.css +7 -0
- package/src/plugins/kanban/types.ts +106 -0
- package/src/plugins/kanban/utils.ts +107 -0
- package/src/plugins/ui-builder/style.css +6 -0
- package/dist/shared/{stack.DLhzx1-D.d.cts → stack.CcI4sYJP.d.cts} +1 -1
- package/dist/shared/{stack.DLhzx1-D.d.mts → stack.CcI4sYJP.d.mts} +1 -1
- package/dist/shared/{stack.DLhzx1-D.d.ts → stack.CcI4sYJP.d.ts} +1 -1
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useMutation,
|
|
5
|
+
useQuery,
|
|
6
|
+
useQueryClient,
|
|
7
|
+
useSuspenseQuery,
|
|
8
|
+
} from "@tanstack/react-query";
|
|
9
|
+
import { createApiClient } from "@btst/stack/plugins/client";
|
|
10
|
+
import { usePluginOverrides } from "@btst/stack/context";
|
|
11
|
+
import type { KanbanApiRouter } from "../../api";
|
|
12
|
+
import { createKanbanQueryKeys } from "../../query-keys";
|
|
13
|
+
import type { KanbanPluginOverrides, KanbanUser } from "../overrides";
|
|
14
|
+
import type {
|
|
15
|
+
SerializedBoard,
|
|
16
|
+
SerializedBoardWithColumns,
|
|
17
|
+
SerializedColumn,
|
|
18
|
+
SerializedTask,
|
|
19
|
+
Priority,
|
|
20
|
+
} from "../../types";
|
|
21
|
+
|
|
22
|
+
// ============ Error Handling ============
|
|
23
|
+
|
|
24
|
+
// Type guard for better-call error responses
|
|
25
|
+
function isErrorResponse(
|
|
26
|
+
response: unknown,
|
|
27
|
+
): response is { error: unknown; data?: never } {
|
|
28
|
+
return (
|
|
29
|
+
typeof response === "object" &&
|
|
30
|
+
response !== null &&
|
|
31
|
+
"error" in response &&
|
|
32
|
+
response.error !== null &&
|
|
33
|
+
response.error !== undefined
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper to convert error to a proper Error object with meaningful message
|
|
38
|
+
function toError(error: unknown): Error {
|
|
39
|
+
if (error instanceof Error) {
|
|
40
|
+
return error;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (typeof error === "object" && error !== null) {
|
|
44
|
+
const errorObj = error as Record<string, unknown>;
|
|
45
|
+
const message =
|
|
46
|
+
(typeof errorObj.message === "string" ? errorObj.message : null) ||
|
|
47
|
+
(typeof errorObj.error === "string" ? errorObj.error : null) ||
|
|
48
|
+
JSON.stringify(error);
|
|
49
|
+
|
|
50
|
+
const err = new Error(message);
|
|
51
|
+
Object.assign(err, error);
|
|
52
|
+
return err;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return new Error(String(error));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ============ API Client Hook ============
|
|
59
|
+
|
|
60
|
+
function useKanbanClient() {
|
|
61
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
62
|
+
usePluginOverrides<KanbanPluginOverrides>("kanban");
|
|
63
|
+
|
|
64
|
+
const client = createApiClient<KanbanApiRouter>({
|
|
65
|
+
baseURL: apiBaseURL,
|
|
66
|
+
basePath: apiBasePath,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return { client, headers };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============ Board Hooks ============
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Hook to fetch list of boards
|
|
76
|
+
*/
|
|
77
|
+
export function useBoards(params?: {
|
|
78
|
+
slug?: string;
|
|
79
|
+
ownerId?: string;
|
|
80
|
+
organizationId?: string;
|
|
81
|
+
}) {
|
|
82
|
+
const { client, headers } = useKanbanClient();
|
|
83
|
+
const queries = createKanbanQueryKeys(client, headers);
|
|
84
|
+
|
|
85
|
+
return useQuery({
|
|
86
|
+
...queries.boards.list(params),
|
|
87
|
+
staleTime: 30_000,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Hook to fetch list of boards with suspense
|
|
93
|
+
*/
|
|
94
|
+
export function useSuspenseBoards(params?: {
|
|
95
|
+
slug?: string;
|
|
96
|
+
ownerId?: string;
|
|
97
|
+
organizationId?: string;
|
|
98
|
+
}) {
|
|
99
|
+
const { client, headers } = useKanbanClient();
|
|
100
|
+
const queries = createKanbanQueryKeys(client, headers);
|
|
101
|
+
|
|
102
|
+
const result = useSuspenseQuery({
|
|
103
|
+
...queries.boards.list(params),
|
|
104
|
+
staleTime: 30_000,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (result.error && !result.isFetching) {
|
|
108
|
+
throw result.error;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Hook to fetch a single board by ID
|
|
116
|
+
*/
|
|
117
|
+
export function useBoard(boardId: string) {
|
|
118
|
+
const { client, headers } = useKanbanClient();
|
|
119
|
+
const queries = createKanbanQueryKeys(client, headers);
|
|
120
|
+
|
|
121
|
+
return useQuery({
|
|
122
|
+
...queries.boards.detail(boardId),
|
|
123
|
+
staleTime: 30_000,
|
|
124
|
+
enabled: !!boardId,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Hook to fetch a single board with suspense
|
|
130
|
+
*/
|
|
131
|
+
export function useSuspenseBoard(boardId: string) {
|
|
132
|
+
const { client, headers } = useKanbanClient();
|
|
133
|
+
const queries = createKanbanQueryKeys(client, headers);
|
|
134
|
+
|
|
135
|
+
const result = useSuspenseQuery({
|
|
136
|
+
...queries.boards.detail(boardId),
|
|
137
|
+
staleTime: 30_000,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (result.error && !result.isFetching) {
|
|
141
|
+
throw result.error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ============ Board Mutations ============
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Hook for board CRUD mutations
|
|
151
|
+
*/
|
|
152
|
+
export function useBoardMutations() {
|
|
153
|
+
const { client, headers } = useKanbanClient();
|
|
154
|
+
const queryClient = useQueryClient();
|
|
155
|
+
|
|
156
|
+
const createMutation = useMutation({
|
|
157
|
+
mutationFn: async (data: {
|
|
158
|
+
name: string;
|
|
159
|
+
description?: string;
|
|
160
|
+
ownerId?: string;
|
|
161
|
+
organizationId?: string;
|
|
162
|
+
}) => {
|
|
163
|
+
const response = await client("@post/boards", {
|
|
164
|
+
method: "POST",
|
|
165
|
+
body: data,
|
|
166
|
+
headers,
|
|
167
|
+
});
|
|
168
|
+
if (isErrorResponse(response)) {
|
|
169
|
+
const errorResponse = response as { error: unknown };
|
|
170
|
+
throw toError(errorResponse.error);
|
|
171
|
+
}
|
|
172
|
+
return response.data as unknown as SerializedBoardWithColumns;
|
|
173
|
+
},
|
|
174
|
+
onSuccess: () => {
|
|
175
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const updateMutation = useMutation({
|
|
180
|
+
mutationFn: async ({
|
|
181
|
+
id,
|
|
182
|
+
data,
|
|
183
|
+
}: {
|
|
184
|
+
id: string;
|
|
185
|
+
data: Partial<{
|
|
186
|
+
name: string;
|
|
187
|
+
description: string;
|
|
188
|
+
slug: string;
|
|
189
|
+
}>;
|
|
190
|
+
}) => {
|
|
191
|
+
const response = await client("@put/boards/:id", {
|
|
192
|
+
method: "PUT",
|
|
193
|
+
params: { id },
|
|
194
|
+
body: data,
|
|
195
|
+
headers,
|
|
196
|
+
});
|
|
197
|
+
if (isErrorResponse(response)) {
|
|
198
|
+
const errorResponse = response as { error: unknown };
|
|
199
|
+
throw toError(errorResponse.error);
|
|
200
|
+
}
|
|
201
|
+
return response.data as unknown as SerializedBoard;
|
|
202
|
+
},
|
|
203
|
+
onSuccess: () => {
|
|
204
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const deleteMutation = useMutation({
|
|
209
|
+
mutationFn: async (id: string) => {
|
|
210
|
+
const response = await client("@delete/boards/:id", {
|
|
211
|
+
method: "DELETE",
|
|
212
|
+
params: { id },
|
|
213
|
+
headers,
|
|
214
|
+
});
|
|
215
|
+
if (isErrorResponse(response)) {
|
|
216
|
+
const errorResponse = response as { error: unknown };
|
|
217
|
+
throw toError(errorResponse.error);
|
|
218
|
+
}
|
|
219
|
+
return response.data as unknown as { success: boolean };
|
|
220
|
+
},
|
|
221
|
+
onSuccess: () => {
|
|
222
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
createBoard: createMutation.mutateAsync,
|
|
228
|
+
updateBoard: (
|
|
229
|
+
id: string,
|
|
230
|
+
data: Parameters<typeof updateMutation.mutateAsync>[0]["data"],
|
|
231
|
+
) => updateMutation.mutateAsync({ id, data }),
|
|
232
|
+
deleteBoard: deleteMutation.mutateAsync,
|
|
233
|
+
isCreating: createMutation.isPending,
|
|
234
|
+
isUpdating: updateMutation.isPending,
|
|
235
|
+
isDeleting: deleteMutation.isPending,
|
|
236
|
+
createError: createMutation.error,
|
|
237
|
+
updateError: updateMutation.error,
|
|
238
|
+
deleteError: deleteMutation.error,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ============ Column Mutations ============
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Hook for column CRUD mutations
|
|
246
|
+
*/
|
|
247
|
+
export function useColumnMutations() {
|
|
248
|
+
const { client, headers } = useKanbanClient();
|
|
249
|
+
const queryClient = useQueryClient();
|
|
250
|
+
|
|
251
|
+
const createMutation = useMutation({
|
|
252
|
+
mutationFn: async (data: {
|
|
253
|
+
title: string;
|
|
254
|
+
boardId: string;
|
|
255
|
+
order?: number;
|
|
256
|
+
}) => {
|
|
257
|
+
const response = await client("@post/columns", {
|
|
258
|
+
method: "POST",
|
|
259
|
+
body: data,
|
|
260
|
+
headers,
|
|
261
|
+
});
|
|
262
|
+
if (isErrorResponse(response)) {
|
|
263
|
+
const errorResponse = response as { error: unknown };
|
|
264
|
+
throw toError(errorResponse.error);
|
|
265
|
+
}
|
|
266
|
+
return response.data as unknown as SerializedColumn;
|
|
267
|
+
},
|
|
268
|
+
onSuccess: () => {
|
|
269
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const updateMutation = useMutation({
|
|
274
|
+
mutationFn: async ({
|
|
275
|
+
id,
|
|
276
|
+
data,
|
|
277
|
+
}: {
|
|
278
|
+
id: string;
|
|
279
|
+
data: Partial<{
|
|
280
|
+
title: string;
|
|
281
|
+
order: number;
|
|
282
|
+
}>;
|
|
283
|
+
}) => {
|
|
284
|
+
const response = await client("@put/columns/:id", {
|
|
285
|
+
method: "PUT",
|
|
286
|
+
params: { id },
|
|
287
|
+
body: data,
|
|
288
|
+
headers,
|
|
289
|
+
});
|
|
290
|
+
if (isErrorResponse(response)) {
|
|
291
|
+
const errorResponse = response as { error: unknown };
|
|
292
|
+
throw toError(errorResponse.error);
|
|
293
|
+
}
|
|
294
|
+
return response.data as unknown as SerializedColumn;
|
|
295
|
+
},
|
|
296
|
+
onSuccess: () => {
|
|
297
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const deleteMutation = useMutation({
|
|
302
|
+
mutationFn: async (id: string) => {
|
|
303
|
+
const response = await client("@delete/columns/:id", {
|
|
304
|
+
method: "DELETE",
|
|
305
|
+
params: { id },
|
|
306
|
+
headers,
|
|
307
|
+
});
|
|
308
|
+
if (isErrorResponse(response)) {
|
|
309
|
+
const errorResponse = response as { error: unknown };
|
|
310
|
+
throw toError(errorResponse.error);
|
|
311
|
+
}
|
|
312
|
+
return response.data as unknown as { success: boolean };
|
|
313
|
+
},
|
|
314
|
+
onSuccess: () => {
|
|
315
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const reorderMutation = useMutation({
|
|
320
|
+
mutationFn: async ({
|
|
321
|
+
boardId,
|
|
322
|
+
columnIds,
|
|
323
|
+
}: {
|
|
324
|
+
boardId: string;
|
|
325
|
+
columnIds: string[];
|
|
326
|
+
}) => {
|
|
327
|
+
const response = await client("@post/columns/reorder", {
|
|
328
|
+
method: "POST",
|
|
329
|
+
body: { boardId, columnIds },
|
|
330
|
+
headers,
|
|
331
|
+
});
|
|
332
|
+
if (isErrorResponse(response)) {
|
|
333
|
+
const errorResponse = response as { error: unknown };
|
|
334
|
+
throw toError(errorResponse.error);
|
|
335
|
+
}
|
|
336
|
+
return response.data as unknown as { success: boolean };
|
|
337
|
+
},
|
|
338
|
+
onSuccess: () => {
|
|
339
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
createColumn: createMutation.mutateAsync,
|
|
345
|
+
updateColumn: (
|
|
346
|
+
id: string,
|
|
347
|
+
data: Parameters<typeof updateMutation.mutateAsync>[0]["data"],
|
|
348
|
+
) => updateMutation.mutateAsync({ id, data }),
|
|
349
|
+
deleteColumn: deleteMutation.mutateAsync,
|
|
350
|
+
reorderColumns: (boardId: string, columnIds: string[]) =>
|
|
351
|
+
reorderMutation.mutateAsync({ boardId, columnIds }),
|
|
352
|
+
isCreating: createMutation.isPending,
|
|
353
|
+
isUpdating: updateMutation.isPending,
|
|
354
|
+
isDeleting: deleteMutation.isPending,
|
|
355
|
+
isReordering: reorderMutation.isPending,
|
|
356
|
+
createError: createMutation.error,
|
|
357
|
+
updateError: updateMutation.error,
|
|
358
|
+
deleteError: deleteMutation.error,
|
|
359
|
+
reorderError: reorderMutation.error,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ============ Task Mutations ============
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Hook for task CRUD mutations
|
|
367
|
+
*/
|
|
368
|
+
export function useTaskMutations() {
|
|
369
|
+
const { client, headers } = useKanbanClient();
|
|
370
|
+
const queryClient = useQueryClient();
|
|
371
|
+
|
|
372
|
+
const createMutation = useMutation({
|
|
373
|
+
mutationFn: async (data: {
|
|
374
|
+
title: string;
|
|
375
|
+
description?: string;
|
|
376
|
+
priority?: Priority;
|
|
377
|
+
columnId: string;
|
|
378
|
+
assigneeId?: string;
|
|
379
|
+
order?: number;
|
|
380
|
+
}) => {
|
|
381
|
+
const response = await client("@post/tasks", {
|
|
382
|
+
method: "POST",
|
|
383
|
+
body: data,
|
|
384
|
+
headers,
|
|
385
|
+
});
|
|
386
|
+
if (isErrorResponse(response)) {
|
|
387
|
+
const errorResponse = response as { error: unknown };
|
|
388
|
+
throw toError(errorResponse.error);
|
|
389
|
+
}
|
|
390
|
+
return response.data as unknown as SerializedTask;
|
|
391
|
+
},
|
|
392
|
+
onSuccess: () => {
|
|
393
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const updateMutation = useMutation({
|
|
398
|
+
mutationFn: async ({
|
|
399
|
+
id,
|
|
400
|
+
data,
|
|
401
|
+
}: {
|
|
402
|
+
id: string;
|
|
403
|
+
data: Partial<{
|
|
404
|
+
title: string;
|
|
405
|
+
description: string;
|
|
406
|
+
priority: Priority;
|
|
407
|
+
columnId: string;
|
|
408
|
+
assigneeId: string | null;
|
|
409
|
+
order: number;
|
|
410
|
+
isArchived: boolean;
|
|
411
|
+
}>;
|
|
412
|
+
}) => {
|
|
413
|
+
const response = await client("@put/tasks/:id", {
|
|
414
|
+
method: "PUT",
|
|
415
|
+
params: { id },
|
|
416
|
+
body: data,
|
|
417
|
+
headers,
|
|
418
|
+
});
|
|
419
|
+
if (isErrorResponse(response)) {
|
|
420
|
+
const errorResponse = response as { error: unknown };
|
|
421
|
+
throw toError(errorResponse.error);
|
|
422
|
+
}
|
|
423
|
+
return response.data as unknown as SerializedTask;
|
|
424
|
+
},
|
|
425
|
+
onSuccess: () => {
|
|
426
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
427
|
+
},
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const deleteMutation = useMutation({
|
|
431
|
+
mutationFn: async (id: string) => {
|
|
432
|
+
const response = await client("@delete/tasks/:id", {
|
|
433
|
+
method: "DELETE",
|
|
434
|
+
params: { id },
|
|
435
|
+
headers,
|
|
436
|
+
});
|
|
437
|
+
if (isErrorResponse(response)) {
|
|
438
|
+
const errorResponse = response as { error: unknown };
|
|
439
|
+
throw toError(errorResponse.error);
|
|
440
|
+
}
|
|
441
|
+
return response.data as unknown as { success: boolean };
|
|
442
|
+
},
|
|
443
|
+
onSuccess: () => {
|
|
444
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const moveMutation = useMutation({
|
|
449
|
+
mutationFn: async ({
|
|
450
|
+
taskId,
|
|
451
|
+
targetColumnId,
|
|
452
|
+
targetOrder,
|
|
453
|
+
}: {
|
|
454
|
+
taskId: string;
|
|
455
|
+
targetColumnId: string;
|
|
456
|
+
targetOrder: number;
|
|
457
|
+
}) => {
|
|
458
|
+
const response = await client("@post/tasks/move", {
|
|
459
|
+
method: "POST",
|
|
460
|
+
body: { taskId, targetColumnId, targetOrder },
|
|
461
|
+
headers,
|
|
462
|
+
});
|
|
463
|
+
if (isErrorResponse(response)) {
|
|
464
|
+
const errorResponse = response as { error: unknown };
|
|
465
|
+
throw toError(errorResponse.error);
|
|
466
|
+
}
|
|
467
|
+
return response.data as unknown as SerializedTask;
|
|
468
|
+
},
|
|
469
|
+
onSuccess: () => {
|
|
470
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const reorderMutation = useMutation({
|
|
475
|
+
mutationFn: async ({
|
|
476
|
+
columnId,
|
|
477
|
+
taskIds,
|
|
478
|
+
}: {
|
|
479
|
+
columnId: string;
|
|
480
|
+
taskIds: string[];
|
|
481
|
+
}) => {
|
|
482
|
+
const response = await client("@post/tasks/reorder", {
|
|
483
|
+
method: "POST",
|
|
484
|
+
body: { columnId, taskIds },
|
|
485
|
+
headers,
|
|
486
|
+
});
|
|
487
|
+
if (isErrorResponse(response)) {
|
|
488
|
+
const errorResponse = response as { error: unknown };
|
|
489
|
+
throw toError(errorResponse.error);
|
|
490
|
+
}
|
|
491
|
+
return response.data as unknown as { success: boolean };
|
|
492
|
+
},
|
|
493
|
+
onSuccess: () => {
|
|
494
|
+
queryClient.invalidateQueries({ queryKey: ["boards"] });
|
|
495
|
+
},
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
createTask: createMutation.mutateAsync,
|
|
500
|
+
updateTask: (
|
|
501
|
+
id: string,
|
|
502
|
+
data: Parameters<typeof updateMutation.mutateAsync>[0]["data"],
|
|
503
|
+
) => updateMutation.mutateAsync({ id, data }),
|
|
504
|
+
deleteTask: deleteMutation.mutateAsync,
|
|
505
|
+
moveTask: (taskId: string, targetColumnId: string, targetOrder: number) =>
|
|
506
|
+
moveMutation.mutateAsync({ taskId, targetColumnId, targetOrder }),
|
|
507
|
+
reorderTasks: (columnId: string, taskIds: string[]) =>
|
|
508
|
+
reorderMutation.mutateAsync({ columnId, taskIds }),
|
|
509
|
+
isCreating: createMutation.isPending,
|
|
510
|
+
isUpdating: updateMutation.isPending,
|
|
511
|
+
isDeleting: deleteMutation.isPending,
|
|
512
|
+
isMoving: moveMutation.isPending,
|
|
513
|
+
isReordering: reorderMutation.isPending,
|
|
514
|
+
createError: createMutation.error,
|
|
515
|
+
updateError: updateMutation.error,
|
|
516
|
+
deleteError: deleteMutation.error,
|
|
517
|
+
moveError: moveMutation.error,
|
|
518
|
+
reorderError: reorderMutation.error,
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// ============ User Resolution Hooks ============
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Hook to resolve a user from their ID
|
|
526
|
+
* Caches results to avoid repeated lookups
|
|
527
|
+
*/
|
|
528
|
+
export function useResolveUser(userId: string | undefined | null) {
|
|
529
|
+
const { resolveUser } = usePluginOverrides<KanbanPluginOverrides>("kanban");
|
|
530
|
+
|
|
531
|
+
return useQuery<KanbanUser | null>({
|
|
532
|
+
queryKey: ["kanban", "users", userId],
|
|
533
|
+
queryFn: async () => {
|
|
534
|
+
if (!userId) return null;
|
|
535
|
+
const result = await resolveUser(userId);
|
|
536
|
+
return result;
|
|
537
|
+
},
|
|
538
|
+
enabled: !!userId,
|
|
539
|
+
staleTime: 5 * 60 * 1000, // Cache user info for 5 minutes
|
|
540
|
+
gcTime: 10 * 60 * 1000, // Keep in cache for 10 minutes
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Hook to search for users available for assignment
|
|
546
|
+
* @param query - Search query (empty string for all users)
|
|
547
|
+
* @param boardId - Optional board context for scoped user lists
|
|
548
|
+
*/
|
|
549
|
+
export function useSearchUsers(query: string, boardId?: string) {
|
|
550
|
+
const { searchUsers } = usePluginOverrides<KanbanPluginOverrides>("kanban");
|
|
551
|
+
|
|
552
|
+
return useQuery<KanbanUser[]>({
|
|
553
|
+
queryKey: ["kanban", "users", "search", query, boardId],
|
|
554
|
+
queryFn: async () => {
|
|
555
|
+
const result = await searchUsers(query, boardId);
|
|
556
|
+
return result;
|
|
557
|
+
},
|
|
558
|
+
staleTime: 30_000, // Cache search results for 30 seconds
|
|
559
|
+
});
|
|
560
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type KanbanCommonLocalization,
|
|
3
|
+
defaultKanbanCommonLocalization,
|
|
4
|
+
} from "./kanban-common";
|
|
5
|
+
import {
|
|
6
|
+
type KanbanFormsLocalization,
|
|
7
|
+
defaultKanbanFormsLocalization,
|
|
8
|
+
} from "./kanban-forms";
|
|
9
|
+
import {
|
|
10
|
+
type KanbanListLocalization,
|
|
11
|
+
defaultKanbanListLocalization,
|
|
12
|
+
} from "./kanban-list";
|
|
13
|
+
|
|
14
|
+
export type KanbanLocalization = Partial<
|
|
15
|
+
KanbanCommonLocalization & KanbanFormsLocalization & KanbanListLocalization
|
|
16
|
+
>;
|
|
17
|
+
|
|
18
|
+
export const defaultKanbanLocalization: Required<KanbanLocalization> = {
|
|
19
|
+
...defaultKanbanCommonLocalization,
|
|
20
|
+
...defaultKanbanFormsLocalization,
|
|
21
|
+
...defaultKanbanListLocalization,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
KanbanCommonLocalization,
|
|
26
|
+
KanbanFormsLocalization,
|
|
27
|
+
KanbanListLocalization,
|
|
28
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface KanbanCommonLocalization {
|
|
2
|
+
// Navigation
|
|
3
|
+
backToBoards: string;
|
|
4
|
+
// Actions
|
|
5
|
+
actions: string;
|
|
6
|
+
create: string;
|
|
7
|
+
edit: string;
|
|
8
|
+
delete: string;
|
|
9
|
+
cancel: string;
|
|
10
|
+
save: string;
|
|
11
|
+
// Status
|
|
12
|
+
loading: string;
|
|
13
|
+
saving: string;
|
|
14
|
+
deleting: string;
|
|
15
|
+
// Labels
|
|
16
|
+
board: string;
|
|
17
|
+
boards: string;
|
|
18
|
+
column: string;
|
|
19
|
+
columns: string;
|
|
20
|
+
task: string;
|
|
21
|
+
tasks: string;
|
|
22
|
+
// Priorities
|
|
23
|
+
priorityLow: string;
|
|
24
|
+
priorityMedium: string;
|
|
25
|
+
priorityHigh: string;
|
|
26
|
+
priorityUrgent: string;
|
|
27
|
+
// Empty states
|
|
28
|
+
noBoards: string;
|
|
29
|
+
noColumns: string;
|
|
30
|
+
noTasks: string;
|
|
31
|
+
// Errors
|
|
32
|
+
errorGeneric: string;
|
|
33
|
+
errorNotFound: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const defaultKanbanCommonLocalization: KanbanCommonLocalization = {
|
|
37
|
+
// Navigation
|
|
38
|
+
backToBoards: "Back to Boards",
|
|
39
|
+
// Actions
|
|
40
|
+
actions: "Actions",
|
|
41
|
+
create: "Create",
|
|
42
|
+
edit: "Edit",
|
|
43
|
+
delete: "Delete",
|
|
44
|
+
cancel: "Cancel",
|
|
45
|
+
save: "Save",
|
|
46
|
+
// Status
|
|
47
|
+
loading: "Loading...",
|
|
48
|
+
saving: "Saving...",
|
|
49
|
+
deleting: "Deleting...",
|
|
50
|
+
// Labels
|
|
51
|
+
board: "Board",
|
|
52
|
+
boards: "Boards",
|
|
53
|
+
column: "Column",
|
|
54
|
+
columns: "Columns",
|
|
55
|
+
task: "Task",
|
|
56
|
+
tasks: "Tasks",
|
|
57
|
+
// Priorities
|
|
58
|
+
priorityLow: "Low",
|
|
59
|
+
priorityMedium: "Medium",
|
|
60
|
+
priorityHigh: "High",
|
|
61
|
+
priorityUrgent: "Urgent",
|
|
62
|
+
// Empty states
|
|
63
|
+
noBoards: "No boards yet",
|
|
64
|
+
noColumns: "No columns yet",
|
|
65
|
+
noTasks: "No tasks yet",
|
|
66
|
+
// Errors
|
|
67
|
+
errorGeneric: "Something went wrong",
|
|
68
|
+
errorNotFound: "Not found",
|
|
69
|
+
};
|