@btst/stack 1.5.2 → 1.6.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_accessibility@3.1.1_react@19.2.0/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.cjs +68 -0
- package/dist/node_modules/.pnpm/@dnd-kit_accessibility@3.1.1_react@19.2.0/node_modules/@dnd-kit/accessibility/dist/accessibility.esm.mjs +60 -0
- 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 +3937 -0
- 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 +3907 -0
- package/dist/node_modules/.pnpm/@dnd-kit_modifiers@9.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/modifiers/dist/modifiers.esm.cjs +30 -0
- package/dist/node_modules/.pnpm/@dnd-kit_modifiers@9.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/modifiers/dist/modifiers.esm.mjs +28 -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.cjs +675 -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 +661 -0
- package/dist/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.2.0/node_modules/@dnd-kit/utilities/dist/utilities.esm.cjs +358 -0
- package/dist/node_modules/.pnpm/@dnd-kit_utilities@3.2.2_react@19.2.0/node_modules/@dnd-kit/utilities/dist/utilities.esm.mjs +332 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-tabs@1.1.13_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_865f042350eb43f3338b0fffb33f6246/node_modules/@radix-ui/react-tabs/dist/index.cjs +211 -0
- package/dist/node_modules/.pnpm/@radix-ui_react-tabs@1.1.13_@types_react-dom@19.2.3_@types_react@19.2.6__@types_react@1_865f042350eb43f3338b0fffb33f6246/node_modules/@radix-ui/react-tabs/dist/index.mjs +188 -0
- package/dist/packages/better-stack/src/plugins/cms/api/plugin.cjs +3 -2
- package/dist/packages/better-stack/src/plugins/cms/api/plugin.mjs +3 -2
- package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.cjs +15 -15
- package/dist/packages/better-stack/src/plugins/cms/client/components/forms/content-form.mjs +16 -16
- package/dist/packages/better-stack/src/plugins/form-builder/api/plugin.cjs +588 -0
- package/dist/packages/better-stack/src/plugins/form-builder/api/plugin.mjs +586 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/forms/form-renderer.cjs +131 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/forms/form-renderer.mjs +129 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-builder-skeleton.cjs +32 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-builder-skeleton.mjs +30 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-list-skeleton.cjs +21 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/form-list-skeleton.mjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/submissions-skeleton.cjs +34 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/loading/submissions-skeleton.mjs +32 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/404-page.cjs +20 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/404-page.mjs +18 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.cjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.cjs +186 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.internal.mjs +184 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-builder-page.mjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.cjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.cjs +165 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.internal.mjs +163 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/form-list-page.mjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.cjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +177 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +175 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/pages/submissions-page.mjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/default-error.cjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/default-error.mjs +15 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/empty-state.cjs +16 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/empty-state.mjs +14 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/page-wrapper.cjs +27 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/page-wrapper.mjs +25 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/pagination.cjs +39 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/components/shared/pagination.mjs +37 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/hooks/form-builder-hooks.cjs +551 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/hooks/form-builder-hooks.mjs +537 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-common.cjs +36 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-common.mjs +34 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-editor.cjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-editor.mjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-list.cjs +21 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-list.mjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-submissions.cjs +19 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-submissions.mjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-toasts.cjs +14 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/form-builder-toasts.mjs +12 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/index.cjs +17 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/localization/index.mjs +15 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/plugin.cjs +278 -0
- package/dist/packages/better-stack/src/plugins/form-builder/client/plugin.mjs +276 -0
- package/dist/packages/better-stack/src/plugins/form-builder/db.cjs +99 -0
- package/dist/packages/better-stack/src/plugins/form-builder/db.mjs +97 -0
- package/dist/packages/better-stack/src/plugins/form-builder/schemas.cjs +82 -0
- package/dist/packages/better-stack/src/plugins/form-builder/schemas.mjs +74 -0
- package/dist/packages/better-stack/src/plugins/form-builder/utils.cjs +37 -0
- package/dist/packages/better-stack/src/plugins/form-builder/utils.mjs +29 -0
- package/dist/packages/ui/src/components/auto-form/index.cjs +2 -12
- package/dist/packages/ui/src/components/auto-form/index.mjs +2 -9
- package/dist/packages/ui/src/components/auto-form/stepped-auto-form.cjs +377 -0
- package/dist/packages/ui/src/components/auto-form/stepped-auto-form.mjs +368 -0
- package/dist/packages/ui/src/components/auto-form/utils.cjs +1 -56
- package/dist/packages/ui/src/components/auto-form/utils.mjs +2 -56
- package/dist/packages/ui/src/components/form-builder/canvas.cjs +111 -0
- package/dist/packages/ui/src/components/form-builder/canvas.mjs +109 -0
- package/dist/packages/ui/src/components/form-builder/components/index.cjs +570 -0
- package/dist/packages/ui/src/components/form-builder/components/index.mjs +553 -0
- package/dist/packages/ui/src/components/form-builder/edit-field-dialog.cjs +131 -0
- package/dist/packages/ui/src/components/form-builder/edit-field-dialog.mjs +129 -0
- package/dist/packages/ui/src/components/form-builder/form-preview.cjs +73 -0
- package/dist/packages/ui/src/components/form-builder/form-preview.mjs +71 -0
- package/dist/packages/ui/src/components/form-builder/index.cjs +353 -0
- package/dist/packages/ui/src/components/form-builder/index.mjs +344 -0
- package/dist/packages/ui/src/components/form-builder/nested-field-editor-dialog.cjs +263 -0
- package/dist/packages/ui/src/components/form-builder/nested-field-editor-dialog.mjs +261 -0
- package/dist/packages/ui/src/components/form-builder/palette.cjs +52 -0
- package/dist/packages/ui/src/components/form-builder/palette.mjs +49 -0
- package/dist/packages/ui/src/components/form-builder/schema-utils.cjs +120 -0
- package/dist/packages/ui/src/components/form-builder/schema-utils.mjs +114 -0
- package/dist/packages/ui/src/components/form-builder/sortable-field.cjs +151 -0
- package/dist/packages/ui/src/components/form-builder/sortable-field.mjs +148 -0
- package/dist/packages/ui/src/components/form-builder/step-tabs.cjs +180 -0
- package/dist/packages/ui/src/components/form-builder/step-tabs.mjs +178 -0
- package/dist/packages/ui/src/components/form-builder/types.cjs +7 -0
- package/dist/packages/ui/src/components/form-builder/types.mjs +5 -0
- package/dist/packages/ui/src/components/form-builder/validation-schemas.cjs +67 -0
- package/dist/packages/ui/src/components/form-builder/validation-schemas.mjs +56 -0
- package/dist/packages/ui/src/components/tabs.cjs +70 -0
- package/dist/packages/ui/src/components/tabs.mjs +65 -0
- package/dist/packages/ui/src/lib/schema-converter.cjs +130 -0
- package/dist/packages/ui/src/lib/schema-converter.mjs +124 -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/cms/client/index.cjs +6 -0
- package/dist/plugins/cms/client/index.d.cts +6 -113
- package/dist/plugins/cms/client/index.d.mts +6 -113
- package/dist/plugins/cms/client/index.d.ts +6 -113
- package/dist/plugins/cms/client/index.mjs +1 -0
- package/dist/plugins/form-builder/api/index.cjs +7 -0
- package/dist/plugins/form-builder/api/index.d.cts +141 -0
- package/dist/plugins/form-builder/api/index.d.mts +141 -0
- package/dist/plugins/form-builder/api/index.d.ts +141 -0
- package/dist/plugins/form-builder/api/index.mjs +1 -0
- package/dist/plugins/form-builder/client/components/index.cjs +29 -0
- package/dist/plugins/form-builder/client/components/index.d.cts +93 -0
- package/dist/plugins/form-builder/client/components/index.d.mts +93 -0
- package/dist/plugins/form-builder/client/components/index.d.ts +93 -0
- package/dist/plugins/form-builder/client/components/index.mjs +18 -0
- package/dist/plugins/form-builder/client/hooks/index.cjs +19 -0
- package/dist/plugins/form-builder/client/hooks/index.d.cts +154 -0
- package/dist/plugins/form-builder/client/hooks/index.d.mts +154 -0
- package/dist/plugins/form-builder/client/hooks/index.d.ts +154 -0
- package/dist/plugins/form-builder/client/hooks/index.mjs +1 -0
- package/dist/plugins/form-builder/client/index.cjs +13 -0
- package/dist/plugins/form-builder/client/index.d.cts +381 -0
- package/dist/plugins/form-builder/client/index.d.mts +381 -0
- package/dist/plugins/form-builder/client/index.d.ts +381 -0
- package/dist/plugins/form-builder/client/index.mjs +2 -0
- package/dist/plugins/form-builder/client.css +3 -0
- package/dist/plugins/form-builder/query-keys.cjs +143 -0
- package/dist/plugins/form-builder/query-keys.d.cts +74 -0
- package/dist/plugins/form-builder/query-keys.d.mts +74 -0
- package/dist/plugins/form-builder/query-keys.d.ts +74 -0
- package/dist/plugins/form-builder/query-keys.mjs +141 -0
- package/dist/plugins/form-builder/style.css +19 -0
- package/dist/shared/stack.AX5nZ6A3.d.cts +86 -0
- package/dist/shared/stack.AX5nZ6A3.d.mts +86 -0
- package/dist/shared/stack.AX5nZ6A3.d.ts +86 -0
- package/dist/shared/stack.BIh2AXaW.d.cts +123 -0
- package/dist/shared/stack.BIh2AXaW.d.mts +123 -0
- package/dist/shared/stack.BIh2AXaW.d.ts +123 -0
- package/dist/shared/stack.DzH_wcvr.d.cts +195 -0
- package/dist/shared/stack.DzH_wcvr.d.mts +195 -0
- package/dist/shared/stack.DzH_wcvr.d.ts +195 -0
- package/package.json +54 -1
- package/src/plugins/cms/api/plugin.ts +9 -4
- package/src/plugins/cms/client/components/forms/content-form.tsx +23 -25
- package/src/plugins/cms/client/index.ts +11 -0
- package/src/plugins/form-builder/api/index.ts +1 -0
- package/src/plugins/form-builder/api/plugin.ts +776 -0
- package/src/plugins/form-builder/client/components/forms/form-renderer.tsx +253 -0
- package/src/plugins/form-builder/client/components/index.tsx +24 -0
- package/src/plugins/form-builder/client/components/loading/form-builder-skeleton.tsx +42 -0
- package/src/plugins/form-builder/client/components/loading/form-list-skeleton.tsx +25 -0
- package/src/plugins/form-builder/client/components/loading/index.tsx +3 -0
- package/src/plugins/form-builder/client/components/loading/submissions-skeleton.tsx +40 -0
- package/src/plugins/form-builder/client/components/pages/404-page.tsx +28 -0
- package/src/plugins/form-builder/client/components/pages/form-builder-page.internal.tsx +253 -0
- package/src/plugins/form-builder/client/components/pages/form-builder-page.tsx +26 -0
- package/src/plugins/form-builder/client/components/pages/form-list-page.internal.tsx +231 -0
- package/src/plugins/form-builder/client/components/pages/form-list-page.tsx +22 -0
- package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +268 -0
- package/src/plugins/form-builder/client/components/pages/submissions-page.tsx +26 -0
- package/src/plugins/form-builder/client/components/shared/default-error.tsx +30 -0
- package/src/plugins/form-builder/client/components/shared/empty-state.tsx +26 -0
- package/src/plugins/form-builder/client/components/shared/page-wrapper.tsx +32 -0
- package/src/plugins/form-builder/client/components/shared/pagination.tsx +52 -0
- package/src/plugins/form-builder/client/hooks/form-builder-hooks.tsx +799 -0
- package/src/plugins/form-builder/client/hooks/index.tsx +1 -0
- package/src/plugins/form-builder/client/index.ts +22 -0
- package/src/plugins/form-builder/client/localization/form-builder-common.ts +36 -0
- package/src/plugins/form-builder/client/localization/form-builder-editor.ts +18 -0
- package/src/plugins/form-builder/client/localization/form-builder-list.ts +17 -0
- package/src/plugins/form-builder/client/localization/form-builder-submissions.ts +17 -0
- package/src/plugins/form-builder/client/localization/form-builder-toasts.ts +10 -0
- package/src/plugins/form-builder/client/localization/index.ts +15 -0
- package/src/plugins/form-builder/client/overrides.ts +146 -0
- package/src/plugins/form-builder/client/plugin.tsx +488 -0
- package/src/plugins/form-builder/client.css +3 -0
- package/src/plugins/form-builder/db.ts +99 -0
- package/src/plugins/form-builder/query-keys.ts +198 -0
- package/src/plugins/form-builder/schemas.ts +122 -0
- package/src/plugins/form-builder/style.css +19 -0
- package/src/plugins/form-builder/types.ts +317 -0
- package/src/plugins/form-builder/utils.ts +63 -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,799 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
useQuery,
|
|
5
|
+
useMutation,
|
|
6
|
+
useQueryClient,
|
|
7
|
+
useSuspenseQuery,
|
|
8
|
+
useInfiniteQuery,
|
|
9
|
+
useSuspenseInfiniteQuery,
|
|
10
|
+
type InfiniteData,
|
|
11
|
+
} from "@tanstack/react-query";
|
|
12
|
+
import { createApiClient } from "@btst/stack/plugins/client";
|
|
13
|
+
import { usePluginOverrides } from "@btst/stack/context";
|
|
14
|
+
import type { FormBuilderApiRouter } from "../../api";
|
|
15
|
+
import type {
|
|
16
|
+
SerializedForm,
|
|
17
|
+
PaginatedForms,
|
|
18
|
+
SerializedFormSubmission,
|
|
19
|
+
SerializedFormSubmissionWithData,
|
|
20
|
+
PaginatedFormSubmissions,
|
|
21
|
+
} from "../../types";
|
|
22
|
+
import type { FormBuilderPluginOverrides } from "../overrides";
|
|
23
|
+
import { createFormBuilderQueryKeys } from "../../query-keys";
|
|
24
|
+
|
|
25
|
+
// Type guard for better-call error responses
|
|
26
|
+
function isErrorResponse(
|
|
27
|
+
response: unknown,
|
|
28
|
+
): response is { error: unknown; data?: never } {
|
|
29
|
+
if (typeof response !== "object" || response === null) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const obj = response as Record<string, unknown>;
|
|
33
|
+
return "error" in obj && obj.error !== null && obj.error !== undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Helper to convert error to a proper Error object with meaningful message
|
|
37
|
+
function toError(error: unknown): Error {
|
|
38
|
+
if (error instanceof Error) {
|
|
39
|
+
return error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (typeof error === "object" && error !== null) {
|
|
43
|
+
const errorObj = error as Record<string, unknown>;
|
|
44
|
+
const message =
|
|
45
|
+
(typeof errorObj.message === "string" ? errorObj.message : null) ||
|
|
46
|
+
(typeof errorObj.error === "string" ? errorObj.error : null) ||
|
|
47
|
+
JSON.stringify(error);
|
|
48
|
+
|
|
49
|
+
const err = new Error(message);
|
|
50
|
+
Object.assign(err, error);
|
|
51
|
+
return err;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return new Error(String(error));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Shared React Query configuration for all Form Builder queries
|
|
59
|
+
* Prevents automatic refetching to avoid hydration mismatches in SSR
|
|
60
|
+
*/
|
|
61
|
+
const SHARED_QUERY_CONFIG = {
|
|
62
|
+
retry: false,
|
|
63
|
+
refetchOnWindowFocus: false,
|
|
64
|
+
refetchOnMount: false,
|
|
65
|
+
refetchOnReconnect: false,
|
|
66
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
67
|
+
gcTime: 1000 * 60 * 10, // 10 minutes
|
|
68
|
+
} as const;
|
|
69
|
+
|
|
70
|
+
// ========== Forms Hooks (Admin) ==========
|
|
71
|
+
|
|
72
|
+
export interface UseFormsOptions {
|
|
73
|
+
/** Filter by status */
|
|
74
|
+
status?: "active" | "inactive" | "archived";
|
|
75
|
+
/** Number of items per page (default: 20) */
|
|
76
|
+
limit?: number;
|
|
77
|
+
/** Whether to enable the query (default: true) */
|
|
78
|
+
enabled?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface UseFormsResult {
|
|
82
|
+
forms: SerializedForm[];
|
|
83
|
+
total: number;
|
|
84
|
+
isLoading: boolean;
|
|
85
|
+
error: Error | null;
|
|
86
|
+
loadMore: () => void;
|
|
87
|
+
hasMore: boolean;
|
|
88
|
+
isLoadingMore: boolean;
|
|
89
|
+
refetch: () => void;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Hook for fetching paginated forms (admin)
|
|
94
|
+
*/
|
|
95
|
+
export function useForms(options: UseFormsOptions = {}): UseFormsResult {
|
|
96
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
97
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
98
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
99
|
+
baseURL: apiBaseURL,
|
|
100
|
+
basePath: apiBasePath,
|
|
101
|
+
});
|
|
102
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
103
|
+
const { status, limit = 20, enabled = true } = options;
|
|
104
|
+
|
|
105
|
+
const baseQuery = queries.forms.list({ status, limit, offset: 0 });
|
|
106
|
+
|
|
107
|
+
const {
|
|
108
|
+
data,
|
|
109
|
+
isLoading,
|
|
110
|
+
error,
|
|
111
|
+
fetchNextPage,
|
|
112
|
+
hasNextPage,
|
|
113
|
+
isFetchingNextPage,
|
|
114
|
+
refetch,
|
|
115
|
+
} = useInfiniteQuery({
|
|
116
|
+
queryKey: baseQuery.queryKey,
|
|
117
|
+
queryFn: async ({ pageParam = 0 }) => {
|
|
118
|
+
const response: unknown = await client("/forms", {
|
|
119
|
+
method: "GET",
|
|
120
|
+
query: { status, limit, offset: pageParam },
|
|
121
|
+
headers,
|
|
122
|
+
});
|
|
123
|
+
if (isErrorResponse(response)) {
|
|
124
|
+
throw toError(response.error);
|
|
125
|
+
}
|
|
126
|
+
return (response as { data?: unknown }).data as PaginatedForms;
|
|
127
|
+
},
|
|
128
|
+
...SHARED_QUERY_CONFIG,
|
|
129
|
+
initialPageParam: 0,
|
|
130
|
+
getNextPageParam: (lastPage, allPages) => {
|
|
131
|
+
if (!lastPage || typeof lastPage !== "object") return undefined;
|
|
132
|
+
const items = (lastPage as PaginatedForms)?.items;
|
|
133
|
+
if (!Array.isArray(items) || items.length < limit) return undefined;
|
|
134
|
+
const loadedCount = (allPages || []).reduce(
|
|
135
|
+
(sum, page) =>
|
|
136
|
+
sum +
|
|
137
|
+
(Array.isArray((page as PaginatedForms)?.items)
|
|
138
|
+
? (page as PaginatedForms).items.length
|
|
139
|
+
: 0),
|
|
140
|
+
0,
|
|
141
|
+
);
|
|
142
|
+
const total = (lastPage as PaginatedForms)?.total ?? 0;
|
|
143
|
+
if (loadedCount >= total) return undefined;
|
|
144
|
+
return loadedCount;
|
|
145
|
+
},
|
|
146
|
+
enabled,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const pages = (data as InfiniteData<PaginatedForms, number> | undefined)
|
|
150
|
+
?.pages;
|
|
151
|
+
const forms = (pages?.flatMap((page) =>
|
|
152
|
+
Array.isArray(page?.items) ? page.items : [],
|
|
153
|
+
) ?? []) as SerializedForm[];
|
|
154
|
+
const total = pages?.[0]?.total ?? 0;
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
forms,
|
|
158
|
+
total,
|
|
159
|
+
isLoading,
|
|
160
|
+
error,
|
|
161
|
+
loadMore: fetchNextPage,
|
|
162
|
+
hasMore: !!hasNextPage,
|
|
163
|
+
isLoadingMore: isFetchingNextPage,
|
|
164
|
+
refetch,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Suspense variant of useForms
|
|
170
|
+
*/
|
|
171
|
+
export function useSuspenseForms(options: UseFormsOptions = {}): {
|
|
172
|
+
forms: SerializedForm[];
|
|
173
|
+
total: number;
|
|
174
|
+
loadMore: () => Promise<unknown>;
|
|
175
|
+
hasMore: boolean;
|
|
176
|
+
isLoadingMore: boolean;
|
|
177
|
+
refetch: () => Promise<unknown>;
|
|
178
|
+
} {
|
|
179
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
180
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
181
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
182
|
+
baseURL: apiBaseURL,
|
|
183
|
+
basePath: apiBasePath,
|
|
184
|
+
});
|
|
185
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
186
|
+
const { status, limit = 20 } = options;
|
|
187
|
+
|
|
188
|
+
const baseQuery = queries.forms.list({ status, limit, offset: 0 });
|
|
189
|
+
|
|
190
|
+
const {
|
|
191
|
+
data,
|
|
192
|
+
fetchNextPage,
|
|
193
|
+
hasNextPage,
|
|
194
|
+
isFetchingNextPage,
|
|
195
|
+
refetch,
|
|
196
|
+
error,
|
|
197
|
+
isFetching,
|
|
198
|
+
} = useSuspenseInfiniteQuery({
|
|
199
|
+
queryKey: baseQuery.queryKey,
|
|
200
|
+
queryFn: async ({ pageParam = 0 }) => {
|
|
201
|
+
const response: unknown = await client("/forms", {
|
|
202
|
+
method: "GET",
|
|
203
|
+
query: { status, limit, offset: pageParam },
|
|
204
|
+
headers,
|
|
205
|
+
});
|
|
206
|
+
if (isErrorResponse(response)) {
|
|
207
|
+
throw toError(response.error);
|
|
208
|
+
}
|
|
209
|
+
return (response as { data?: unknown }).data as PaginatedForms;
|
|
210
|
+
},
|
|
211
|
+
...SHARED_QUERY_CONFIG,
|
|
212
|
+
initialPageParam: 0,
|
|
213
|
+
getNextPageParam: (lastPage, allPages) => {
|
|
214
|
+
if (!lastPage || typeof lastPage !== "object") return undefined;
|
|
215
|
+
const items = (lastPage as PaginatedForms)?.items;
|
|
216
|
+
if (!Array.isArray(items) || items.length < limit) return undefined;
|
|
217
|
+
const loadedCount = (allPages || []).reduce(
|
|
218
|
+
(sum, page) =>
|
|
219
|
+
sum +
|
|
220
|
+
(Array.isArray((page as PaginatedForms)?.items)
|
|
221
|
+
? (page as PaginatedForms).items.length
|
|
222
|
+
: 0),
|
|
223
|
+
0,
|
|
224
|
+
);
|
|
225
|
+
const total = (lastPage as PaginatedForms)?.total ?? 0;
|
|
226
|
+
if (loadedCount >= total) return undefined;
|
|
227
|
+
return loadedCount;
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (error && !isFetching) {
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const pages = data.pages as PaginatedForms[];
|
|
236
|
+
const forms = (pages?.flatMap((page) =>
|
|
237
|
+
Array.isArray(page?.items) ? page.items : [],
|
|
238
|
+
) ?? []) as SerializedForm[];
|
|
239
|
+
const total = pages?.[0]?.total ?? 0;
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
forms,
|
|
243
|
+
total,
|
|
244
|
+
loadMore: fetchNextPage,
|
|
245
|
+
hasMore: !!hasNextPage,
|
|
246
|
+
isLoadingMore: isFetchingNextPage,
|
|
247
|
+
refetch,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Hook for fetching a form by ID (admin)
|
|
253
|
+
*/
|
|
254
|
+
export function useFormById(id: string): {
|
|
255
|
+
form: SerializedForm | null;
|
|
256
|
+
isLoading: boolean;
|
|
257
|
+
error: Error | null;
|
|
258
|
+
refetch: () => void;
|
|
259
|
+
} {
|
|
260
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
261
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
262
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
263
|
+
baseURL: apiBaseURL,
|
|
264
|
+
basePath: apiBasePath,
|
|
265
|
+
});
|
|
266
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
267
|
+
const baseQuery = queries.forms.byId(id);
|
|
268
|
+
|
|
269
|
+
const { data, isLoading, error, refetch } = useQuery({
|
|
270
|
+
...baseQuery,
|
|
271
|
+
...SHARED_QUERY_CONFIG,
|
|
272
|
+
enabled: !!id,
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
form: data ?? null,
|
|
277
|
+
isLoading,
|
|
278
|
+
error,
|
|
279
|
+
refetch,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Suspense variant of useFormById
|
|
285
|
+
*/
|
|
286
|
+
export function useSuspenseFormById(id: string): {
|
|
287
|
+
form: SerializedForm | null;
|
|
288
|
+
refetch: () => Promise<unknown>;
|
|
289
|
+
} {
|
|
290
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
291
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
292
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
293
|
+
baseURL: apiBaseURL,
|
|
294
|
+
basePath: apiBasePath,
|
|
295
|
+
});
|
|
296
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
297
|
+
const baseQuery = queries.forms.byId(id);
|
|
298
|
+
|
|
299
|
+
const { data, refetch, error, isFetching } = useSuspenseQuery({
|
|
300
|
+
...baseQuery,
|
|
301
|
+
...SHARED_QUERY_CONFIG,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
if (error && !isFetching) {
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
form: data ?? null,
|
|
310
|
+
refetch,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Hook for fetching a form by slug (public)
|
|
316
|
+
*/
|
|
317
|
+
export function useFormBySlug(slug: string): {
|
|
318
|
+
form: SerializedForm | null;
|
|
319
|
+
isLoading: boolean;
|
|
320
|
+
error: Error | null;
|
|
321
|
+
refetch: () => void;
|
|
322
|
+
} {
|
|
323
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
324
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
325
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
326
|
+
baseURL: apiBaseURL,
|
|
327
|
+
basePath: apiBasePath,
|
|
328
|
+
});
|
|
329
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
330
|
+
const baseQuery = queries.forms.bySlug(slug);
|
|
331
|
+
|
|
332
|
+
const { data, isLoading, error, refetch } = useQuery({
|
|
333
|
+
...baseQuery,
|
|
334
|
+
...SHARED_QUERY_CONFIG,
|
|
335
|
+
enabled: !!slug,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
form: data ?? null,
|
|
340
|
+
isLoading,
|
|
341
|
+
error,
|
|
342
|
+
refetch,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Suspense variant of useFormBySlug
|
|
348
|
+
*/
|
|
349
|
+
export function useSuspenseFormBySlug(slug: string): {
|
|
350
|
+
form: SerializedForm | null;
|
|
351
|
+
refetch: () => Promise<unknown>;
|
|
352
|
+
} {
|
|
353
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
354
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
355
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
356
|
+
baseURL: apiBaseURL,
|
|
357
|
+
basePath: apiBasePath,
|
|
358
|
+
});
|
|
359
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
360
|
+
const baseQuery = queries.forms.bySlug(slug);
|
|
361
|
+
|
|
362
|
+
const { data, refetch, error, isFetching } = useSuspenseQuery({
|
|
363
|
+
...baseQuery,
|
|
364
|
+
...SHARED_QUERY_CONFIG,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
if (error && !isFetching) {
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
form: data ?? null,
|
|
373
|
+
refetch,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ========== Form Mutations ==========
|
|
378
|
+
|
|
379
|
+
export interface CreateFormInput {
|
|
380
|
+
name: string;
|
|
381
|
+
slug: string;
|
|
382
|
+
description?: string;
|
|
383
|
+
schema: string;
|
|
384
|
+
successMessage?: string;
|
|
385
|
+
redirectUrl?: string;
|
|
386
|
+
status?: "active" | "inactive" | "archived";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export interface UpdateFormInput {
|
|
390
|
+
name?: string;
|
|
391
|
+
slug?: string;
|
|
392
|
+
description?: string;
|
|
393
|
+
schema?: string;
|
|
394
|
+
successMessage?: string;
|
|
395
|
+
redirectUrl?: string;
|
|
396
|
+
status?: "active" | "inactive" | "archived";
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Hook for creating a form
|
|
401
|
+
*/
|
|
402
|
+
export function useCreateForm() {
|
|
403
|
+
const { refresh, apiBaseURL, apiBasePath, headers } =
|
|
404
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
405
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
406
|
+
baseURL: apiBaseURL,
|
|
407
|
+
basePath: apiBasePath,
|
|
408
|
+
});
|
|
409
|
+
const queryClient = useQueryClient();
|
|
410
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
411
|
+
|
|
412
|
+
return useMutation<SerializedForm, Error, CreateFormInput>({
|
|
413
|
+
mutationKey: [...queries.forms._def, "create"],
|
|
414
|
+
mutationFn: async (data) => {
|
|
415
|
+
const response: unknown = await client("@post/forms", {
|
|
416
|
+
method: "POST",
|
|
417
|
+
body: data,
|
|
418
|
+
headers,
|
|
419
|
+
});
|
|
420
|
+
if (isErrorResponse(response)) {
|
|
421
|
+
throw toError(response.error);
|
|
422
|
+
}
|
|
423
|
+
return (response as { data?: unknown }).data as SerializedForm;
|
|
424
|
+
},
|
|
425
|
+
onSuccess: async () => {
|
|
426
|
+
await queryClient.invalidateQueries({
|
|
427
|
+
queryKey: queries.forms._def,
|
|
428
|
+
});
|
|
429
|
+
if (refresh) {
|
|
430
|
+
await refresh();
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Hook for updating a form
|
|
438
|
+
*/
|
|
439
|
+
export function useUpdateForm() {
|
|
440
|
+
const { refresh, apiBaseURL, apiBasePath, headers } =
|
|
441
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
442
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
443
|
+
baseURL: apiBaseURL,
|
|
444
|
+
basePath: apiBasePath,
|
|
445
|
+
});
|
|
446
|
+
const queryClient = useQueryClient();
|
|
447
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
448
|
+
|
|
449
|
+
return useMutation<
|
|
450
|
+
SerializedForm,
|
|
451
|
+
Error,
|
|
452
|
+
{ id: string; data: UpdateFormInput }
|
|
453
|
+
>({
|
|
454
|
+
mutationKey: [...queries.forms._def, "update"],
|
|
455
|
+
mutationFn: async ({ id, data }) => {
|
|
456
|
+
const response: unknown = await client("@put/forms/:id", {
|
|
457
|
+
method: "PUT",
|
|
458
|
+
params: { id },
|
|
459
|
+
body: data,
|
|
460
|
+
headers,
|
|
461
|
+
});
|
|
462
|
+
if (isErrorResponse(response)) {
|
|
463
|
+
throw toError(response.error);
|
|
464
|
+
}
|
|
465
|
+
return (response as { data?: unknown }).data as SerializedForm;
|
|
466
|
+
},
|
|
467
|
+
onSuccess: async (updated) => {
|
|
468
|
+
if (updated) {
|
|
469
|
+
queryClient.setQueryData(
|
|
470
|
+
queries.forms.byId(updated.id).queryKey,
|
|
471
|
+
updated,
|
|
472
|
+
);
|
|
473
|
+
queryClient.setQueryData(
|
|
474
|
+
queries.forms.bySlug(updated.slug).queryKey,
|
|
475
|
+
updated,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
await queryClient.invalidateQueries({
|
|
479
|
+
queryKey: queries.forms._def,
|
|
480
|
+
});
|
|
481
|
+
if (refresh) {
|
|
482
|
+
await refresh();
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Hook for deleting a form
|
|
490
|
+
*/
|
|
491
|
+
export function useDeleteForm() {
|
|
492
|
+
const { refresh, apiBaseURL, apiBasePath, headers } =
|
|
493
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
494
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
495
|
+
baseURL: apiBaseURL,
|
|
496
|
+
basePath: apiBasePath,
|
|
497
|
+
});
|
|
498
|
+
const queryClient = useQueryClient();
|
|
499
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
500
|
+
|
|
501
|
+
return useMutation<{ success: boolean }, Error, string>({
|
|
502
|
+
mutationKey: [...queries.forms._def, "delete"],
|
|
503
|
+
mutationFn: async (id) => {
|
|
504
|
+
const response: unknown = await client("@delete/forms/:id", {
|
|
505
|
+
method: "DELETE",
|
|
506
|
+
params: { id },
|
|
507
|
+
headers,
|
|
508
|
+
});
|
|
509
|
+
if (isErrorResponse(response)) {
|
|
510
|
+
throw toError(response.error);
|
|
511
|
+
}
|
|
512
|
+
return (response as { data?: unknown }).data as { success: boolean };
|
|
513
|
+
},
|
|
514
|
+
onSuccess: async () => {
|
|
515
|
+
await queryClient.invalidateQueries({
|
|
516
|
+
queryKey: queries.forms._def,
|
|
517
|
+
});
|
|
518
|
+
if (refresh) {
|
|
519
|
+
await refresh();
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// ========== Form Submission Hooks ==========
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Hook for submitting a form (public)
|
|
529
|
+
*/
|
|
530
|
+
export function useSubmitForm(slug: string) {
|
|
531
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
532
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
533
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
534
|
+
baseURL: apiBaseURL,
|
|
535
|
+
basePath: apiBasePath,
|
|
536
|
+
});
|
|
537
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
538
|
+
|
|
539
|
+
return useMutation<
|
|
540
|
+
SerializedFormSubmission & {
|
|
541
|
+
form: { successMessage?: string; redirectUrl?: string };
|
|
542
|
+
},
|
|
543
|
+
Error,
|
|
544
|
+
{ data: Record<string, unknown> }
|
|
545
|
+
>({
|
|
546
|
+
mutationKey: [...queries.forms._def, slug, "submit"],
|
|
547
|
+
mutationFn: async ({ data }) => {
|
|
548
|
+
const response: unknown = await client("@post/forms/:slug/submit", {
|
|
549
|
+
method: "POST",
|
|
550
|
+
params: { slug },
|
|
551
|
+
body: { data },
|
|
552
|
+
headers,
|
|
553
|
+
});
|
|
554
|
+
if (isErrorResponse(response)) {
|
|
555
|
+
throw toError(response.error);
|
|
556
|
+
}
|
|
557
|
+
return (response as { data?: unknown })
|
|
558
|
+
.data as SerializedFormSubmission & {
|
|
559
|
+
form: { successMessage?: string; redirectUrl?: string };
|
|
560
|
+
};
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// ========== Submissions Management Hooks (Admin) ==========
|
|
566
|
+
|
|
567
|
+
export interface UseSubmissionsOptions {
|
|
568
|
+
/** Number of items per page (default: 20) */
|
|
569
|
+
limit?: number;
|
|
570
|
+
/** Whether to enable the query (default: true) */
|
|
571
|
+
enabled?: boolean;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export interface UseSubmissionsResult {
|
|
575
|
+
submissions: SerializedFormSubmissionWithData[];
|
|
576
|
+
total: number;
|
|
577
|
+
isLoading: boolean;
|
|
578
|
+
error: Error | null;
|
|
579
|
+
loadMore: () => void;
|
|
580
|
+
hasMore: boolean;
|
|
581
|
+
isLoadingMore: boolean;
|
|
582
|
+
refetch: () => void;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Hook for fetching paginated submissions for a form (admin)
|
|
587
|
+
*/
|
|
588
|
+
export function useSubmissions(
|
|
589
|
+
formId: string,
|
|
590
|
+
options: UseSubmissionsOptions = {},
|
|
591
|
+
): UseSubmissionsResult {
|
|
592
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
593
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
594
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
595
|
+
baseURL: apiBaseURL,
|
|
596
|
+
basePath: apiBasePath,
|
|
597
|
+
});
|
|
598
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
599
|
+
const { limit = 20, enabled = true } = options;
|
|
600
|
+
|
|
601
|
+
const baseQuery = queries.formSubmissions.list({
|
|
602
|
+
formId,
|
|
603
|
+
limit,
|
|
604
|
+
offset: 0,
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
const {
|
|
608
|
+
data,
|
|
609
|
+
isLoading,
|
|
610
|
+
error,
|
|
611
|
+
fetchNextPage,
|
|
612
|
+
hasNextPage,
|
|
613
|
+
isFetchingNextPage,
|
|
614
|
+
refetch,
|
|
615
|
+
} = useInfiniteQuery({
|
|
616
|
+
queryKey: baseQuery.queryKey,
|
|
617
|
+
queryFn: async ({ pageParam = 0 }) => {
|
|
618
|
+
const response: unknown = await client("/forms/:formId/submissions", {
|
|
619
|
+
method: "GET",
|
|
620
|
+
params: { formId },
|
|
621
|
+
query: { limit, offset: pageParam },
|
|
622
|
+
headers,
|
|
623
|
+
});
|
|
624
|
+
if (isErrorResponse(response)) {
|
|
625
|
+
throw toError(response.error);
|
|
626
|
+
}
|
|
627
|
+
return (response as { data?: unknown }).data as PaginatedFormSubmissions;
|
|
628
|
+
},
|
|
629
|
+
...SHARED_QUERY_CONFIG,
|
|
630
|
+
initialPageParam: 0,
|
|
631
|
+
getNextPageParam: (lastPage, allPages) => {
|
|
632
|
+
if (!lastPage || typeof lastPage !== "object") return undefined;
|
|
633
|
+
const items = (lastPage as PaginatedFormSubmissions)?.items;
|
|
634
|
+
if (!Array.isArray(items) || items.length < limit) return undefined;
|
|
635
|
+
const loadedCount = (allPages || []).reduce(
|
|
636
|
+
(sum, page) =>
|
|
637
|
+
sum +
|
|
638
|
+
(Array.isArray((page as PaginatedFormSubmissions)?.items)
|
|
639
|
+
? (page as PaginatedFormSubmissions).items.length
|
|
640
|
+
: 0),
|
|
641
|
+
0,
|
|
642
|
+
);
|
|
643
|
+
const total = (lastPage as PaginatedFormSubmissions)?.total ?? 0;
|
|
644
|
+
if (loadedCount >= total) return undefined;
|
|
645
|
+
return loadedCount;
|
|
646
|
+
},
|
|
647
|
+
enabled: enabled && !!formId,
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
const pages = (
|
|
651
|
+
data as InfiniteData<PaginatedFormSubmissions, number> | undefined
|
|
652
|
+
)?.pages;
|
|
653
|
+
const submissions = (pages?.flatMap((page) =>
|
|
654
|
+
Array.isArray(page?.items) ? page.items : [],
|
|
655
|
+
) ?? []) as SerializedFormSubmissionWithData[];
|
|
656
|
+
const total = pages?.[0]?.total ?? 0;
|
|
657
|
+
|
|
658
|
+
return {
|
|
659
|
+
submissions,
|
|
660
|
+
total,
|
|
661
|
+
isLoading,
|
|
662
|
+
error,
|
|
663
|
+
loadMore: fetchNextPage,
|
|
664
|
+
hasMore: !!hasNextPage,
|
|
665
|
+
isLoadingMore: isFetchingNextPage,
|
|
666
|
+
refetch,
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Suspense variant of useSubmissions
|
|
672
|
+
*/
|
|
673
|
+
export function useSuspenseSubmissions(
|
|
674
|
+
formId: string,
|
|
675
|
+
options: UseSubmissionsOptions = {},
|
|
676
|
+
): {
|
|
677
|
+
submissions: SerializedFormSubmissionWithData[];
|
|
678
|
+
total: number;
|
|
679
|
+
loadMore: () => Promise<unknown>;
|
|
680
|
+
hasMore: boolean;
|
|
681
|
+
isLoadingMore: boolean;
|
|
682
|
+
refetch: () => Promise<unknown>;
|
|
683
|
+
} {
|
|
684
|
+
const { apiBaseURL, apiBasePath, headers } =
|
|
685
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
686
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
687
|
+
baseURL: apiBaseURL,
|
|
688
|
+
basePath: apiBasePath,
|
|
689
|
+
});
|
|
690
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
691
|
+
const { limit = 20 } = options;
|
|
692
|
+
|
|
693
|
+
const baseQuery = queries.formSubmissions.list({
|
|
694
|
+
formId,
|
|
695
|
+
limit,
|
|
696
|
+
offset: 0,
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
const {
|
|
700
|
+
data,
|
|
701
|
+
fetchNextPage,
|
|
702
|
+
hasNextPage,
|
|
703
|
+
isFetchingNextPage,
|
|
704
|
+
refetch,
|
|
705
|
+
error,
|
|
706
|
+
isFetching,
|
|
707
|
+
} = useSuspenseInfiniteQuery({
|
|
708
|
+
queryKey: baseQuery.queryKey,
|
|
709
|
+
queryFn: async ({ pageParam = 0 }) => {
|
|
710
|
+
const response: unknown = await client("/forms/:formId/submissions", {
|
|
711
|
+
method: "GET",
|
|
712
|
+
params: { formId },
|
|
713
|
+
query: { limit, offset: pageParam },
|
|
714
|
+
headers,
|
|
715
|
+
});
|
|
716
|
+
if (isErrorResponse(response)) {
|
|
717
|
+
throw toError(response.error);
|
|
718
|
+
}
|
|
719
|
+
return (response as { data?: unknown }).data as PaginatedFormSubmissions;
|
|
720
|
+
},
|
|
721
|
+
...SHARED_QUERY_CONFIG,
|
|
722
|
+
initialPageParam: 0,
|
|
723
|
+
getNextPageParam: (lastPage, allPages) => {
|
|
724
|
+
if (!lastPage || typeof lastPage !== "object") return undefined;
|
|
725
|
+
const items = (lastPage as PaginatedFormSubmissions)?.items;
|
|
726
|
+
if (!Array.isArray(items) || items.length < limit) return undefined;
|
|
727
|
+
const loadedCount = (allPages || []).reduce(
|
|
728
|
+
(sum, page) =>
|
|
729
|
+
sum +
|
|
730
|
+
(Array.isArray((page as PaginatedFormSubmissions)?.items)
|
|
731
|
+
? (page as PaginatedFormSubmissions).items.length
|
|
732
|
+
: 0),
|
|
733
|
+
0,
|
|
734
|
+
);
|
|
735
|
+
const total = (lastPage as PaginatedFormSubmissions)?.total ?? 0;
|
|
736
|
+
if (loadedCount >= total) return undefined;
|
|
737
|
+
return loadedCount;
|
|
738
|
+
},
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
if (error && !isFetching) {
|
|
742
|
+
throw error;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const pages = data.pages as PaginatedFormSubmissions[];
|
|
746
|
+
const submissions = (pages?.flatMap((page) =>
|
|
747
|
+
Array.isArray(page?.items) ? page.items : [],
|
|
748
|
+
) ?? []) as SerializedFormSubmissionWithData[];
|
|
749
|
+
const total = pages?.[0]?.total ?? 0;
|
|
750
|
+
|
|
751
|
+
return {
|
|
752
|
+
submissions,
|
|
753
|
+
total,
|
|
754
|
+
loadMore: fetchNextPage,
|
|
755
|
+
hasMore: !!hasNextPage,
|
|
756
|
+
isLoadingMore: isFetchingNextPage,
|
|
757
|
+
refetch,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Hook for deleting a submission
|
|
763
|
+
*/
|
|
764
|
+
export function useDeleteSubmission(formId: string) {
|
|
765
|
+
const { refresh, apiBaseURL, apiBasePath, headers } =
|
|
766
|
+
usePluginOverrides<FormBuilderPluginOverrides>("form-builder");
|
|
767
|
+
const client = createApiClient<FormBuilderApiRouter>({
|
|
768
|
+
baseURL: apiBaseURL,
|
|
769
|
+
basePath: apiBasePath,
|
|
770
|
+
});
|
|
771
|
+
const queryClient = useQueryClient();
|
|
772
|
+
const queries = createFormBuilderQueryKeys(client, headers);
|
|
773
|
+
|
|
774
|
+
return useMutation<{ success: boolean }, Error, string>({
|
|
775
|
+
mutationKey: [...queries.formSubmissions._def, formId, "delete"],
|
|
776
|
+
mutationFn: async (subId) => {
|
|
777
|
+
const response: unknown = await client(
|
|
778
|
+
"@delete/forms/:formId/submissions/:subId",
|
|
779
|
+
{
|
|
780
|
+
method: "DELETE",
|
|
781
|
+
params: { formId, subId },
|
|
782
|
+
headers,
|
|
783
|
+
},
|
|
784
|
+
);
|
|
785
|
+
if (isErrorResponse(response)) {
|
|
786
|
+
throw toError(response.error);
|
|
787
|
+
}
|
|
788
|
+
return (response as { data?: unknown }).data as { success: boolean };
|
|
789
|
+
},
|
|
790
|
+
onSuccess: async () => {
|
|
791
|
+
await queryClient.invalidateQueries({
|
|
792
|
+
queryKey: queries.formSubmissions._def,
|
|
793
|
+
});
|
|
794
|
+
if (refresh) {
|
|
795
|
+
await refresh();
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
});
|
|
799
|
+
}
|