@btst/stack 2.3.0 → 2.4.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/packages/stack/src/client/components/compose.cjs +1 -2
- package/dist/packages/stack/src/client/components/compose.mjs +1 -2
- package/dist/packages/stack/src/plugins/ai-chat/api/page-tools.cjs +71 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/page-tools.mjs +68 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +54 -7
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +54 -7
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.cjs +2 -2
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-input.mjs +2 -2
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +89 -22
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +90 -23
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +110 -33
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +112 -35
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.cjs +1 -1
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-sidebar.mjs +1 -1
- package/dist/packages/stack/src/plugins/ai-chat/schemas.cjs +17 -1
- package/dist/packages/stack/src/plugins/ai-chat/schemas.mjs +17 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.cjs +15 -2
- package/dist/packages/stack/src/plugins/blog/client/components/forms/post-forms.mjs +16 -3
- package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.cjs +24 -1
- package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.mjs +24 -1
- package/dist/packages/stack/src/plugins/blog/client/components/pages/fill-blog-form-handler.cjs +26 -0
- package/dist/packages/stack/src/plugins/blog/client/components/pages/fill-blog-form-handler.mjs +24 -0
- package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.cjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.mjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.cjs +18 -0
- package/dist/packages/stack/src/plugins/blog/client/components/pages/post-page.internal.mjs +18 -0
- package/dist/packages/stack/src/plugins/cms/api/mutations.cjs +48 -0
- package/dist/packages/stack/src/plugins/cms/api/mutations.mjs +46 -0
- package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +7 -1
- package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +7 -1
- package/dist/packages/stack/src/plugins/kanban/api/mutations.cjs +91 -0
- package/dist/packages/stack/src/plugins/kanban/api/mutations.mjs +87 -0
- package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +6 -1
- package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +6 -1
- package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.cjs +7 -3
- package/dist/packages/stack/src/plugins/kanban/client/hooks/kanban-hooks.mjs +7 -3
- package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.cjs +89 -0
- package/dist/packages/stack/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.mjs +89 -0
- package/dist/plugins/ai-chat/api/index.d.cts +1 -1
- package/dist/plugins/ai-chat/api/index.d.mts +1 -1
- package/dist/plugins/ai-chat/api/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/context/page-ai-context.cjs +92 -0
- package/dist/plugins/ai-chat/client/context/page-ai-context.d.cts +84 -0
- package/dist/plugins/ai-chat/client/context/page-ai-context.d.mts +84 -0
- package/dist/plugins/ai-chat/client/context/page-ai-context.d.ts +84 -0
- package/dist/plugins/ai-chat/client/context/page-ai-context.mjs +88 -0
- package/dist/plugins/ai-chat/client/hooks/index.d.cts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.mts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/index.d.cts +2 -2
- package/dist/plugins/ai-chat/client/index.d.mts +2 -2
- package/dist/plugins/ai-chat/client/index.d.ts +2 -2
- package/dist/plugins/ai-chat/query-keys.d.cts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.mts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.ts +1 -1
- package/dist/plugins/blog/api/index.d.cts +2 -2
- package/dist/plugins/blog/api/index.d.mts +2 -2
- package/dist/plugins/blog/api/index.d.ts +2 -2
- 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/api/index.cjs +2 -0
- package/dist/plugins/cms/api/index.d.cts +1 -1
- package/dist/plugins/cms/api/index.d.mts +1 -1
- package/dist/plugins/cms/api/index.d.ts +1 -1
- package/dist/plugins/cms/api/index.mjs +1 -0
- package/dist/plugins/cms/query-keys.d.cts +1 -1
- package/dist/plugins/cms/query-keys.d.mts +1 -1
- package/dist/plugins/cms/query-keys.d.ts +1 -1
- package/dist/plugins/form-builder/api/index.d.cts +1 -1
- package/dist/plugins/form-builder/api/index.d.mts +1 -1
- package/dist/plugins/form-builder/api/index.d.ts +1 -1
- package/dist/plugins/form-builder/query-keys.d.cts +1 -1
- package/dist/plugins/form-builder/query-keys.d.mts +1 -1
- package/dist/plugins/form-builder/query-keys.d.ts +1 -1
- package/dist/plugins/kanban/api/index.cjs +4 -0
- package/dist/plugins/kanban/api/index.d.cts +1 -1
- package/dist/plugins/kanban/api/index.d.mts +1 -1
- package/dist/plugins/kanban/api/index.d.ts +1 -1
- package/dist/plugins/kanban/api/index.mjs +1 -0
- package/dist/plugins/kanban/query-keys.d.cts +1 -1
- package/dist/plugins/kanban/query-keys.d.mts +1 -1
- package/dist/plugins/kanban/query-keys.d.ts +1 -1
- package/dist/shared/{stack.BeSm90va.d.ts → stack.BEn34wW6.d.ts} +60 -2
- package/dist/shared/{stack.IdtKDRka.d.cts → stack.BUkC2EsZ.d.cts} +32 -2
- package/dist/shared/{stack.DaOcgmrM.d.ts → stack.BV9hnvu4.d.cts} +31 -7
- package/dist/shared/{stack.DaOcgmrM.d.cts → stack.BV9hnvu4.d.mts} +31 -7
- package/dist/shared/{stack.DaOcgmrM.d.mts → stack.BV9hnvu4.d.ts} +31 -7
- package/dist/shared/{stack.rTy7-wQU.d.mts → stack.BepFXT3w.d.mts} +70 -15
- package/dist/shared/{stack.BKfolAyK.d.ts → stack.CL8ts1Mu.d.ts} +3 -3
- package/dist/shared/{stack.CP68pFEH.d.mts → stack.CczspVn2.d.mts} +32 -2
- package/dist/shared/{stack.TIBF2AOx.d.ts → stack.CgWzG5jH.d.ts} +70 -15
- package/dist/shared/{stack.BpolpQpf.d.cts → stack.D3GB6wKv.d.cts} +70 -15
- package/dist/shared/{stack.B1EeBt1b.d.ts → stack.DASmUVjX.d.ts} +32 -2
- package/dist/shared/{stack.Dg09R0oB.d.mts → stack.DTDxgFj8.d.mts} +60 -2
- package/dist/shared/{stack.CMh_EdxW.d.cts → stack.DWoCZff7.d.cts} +60 -2
- package/dist/shared/{stack.snB1EDP7.d.cts → stack.Dk5r4W1F.d.mts} +3 -3
- package/dist/shared/{stack.BIXEI6v_.d.mts → stack.heOA9gzA.d.cts} +3 -3
- package/package.json +14 -1
- package/src/client/components/compose.tsx +7 -4
- package/src/plugins/ai-chat/api/page-tools.ts +111 -0
- package/src/plugins/ai-chat/api/plugin.ts +180 -9
- package/src/plugins/ai-chat/client/components/chat-input.tsx +2 -2
- package/src/plugins/ai-chat/client/components/chat-interface.tsx +154 -58
- package/src/plugins/ai-chat/client/components/chat-layout.tsx +166 -32
- package/src/plugins/ai-chat/client/components/chat-sidebar.tsx +1 -1
- package/src/plugins/ai-chat/client/context/page-ai-context.tsx +240 -0
- package/src/plugins/ai-chat/schemas.ts +16 -0
- package/src/plugins/blog/client/components/forms/post-forms.tsx +29 -2
- package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +28 -0
- package/src/plugins/blog/client/components/pages/fill-blog-form-handler.ts +38 -0
- package/src/plugins/blog/client/components/pages/new-post-page.internal.tsx +33 -1
- package/src/plugins/blog/client/components/pages/post-page.internal.tsx +20 -0
- package/src/plugins/cms/api/index.ts +4 -0
- package/src/plugins/cms/api/mutations.ts +84 -0
- package/src/plugins/cms/api/plugin.ts +9 -0
- package/src/plugins/kanban/api/index.ts +6 -0
- package/src/plugins/kanban/api/mutations.ts +169 -0
- package/src/plugins/kanban/api/plugin.ts +12 -0
- package/src/plugins/kanban/client/hooks/kanban-hooks.tsx +4 -0
- package/src/plugins/ui-builder/client/components/pages/page-builder-page.internal.tsx +132 -0
- package/dist/shared/{stack.C5dtIncc.d.mts → stack.B7ONvlD_.d.mts} +1 -1
- package/dist/shared/{stack.CBON0dWL.d.cts → stack.BQmuNl5p.d.cts} +2 -2
- package/dist/shared/{stack.CBON0dWL.d.mts → stack.BQmuNl5p.d.mts} +2 -2
- package/dist/shared/{stack.CBON0dWL.d.ts → stack.BQmuNl5p.d.ts} +2 -2
- package/dist/shared/{stack.CIP6QS9l.d.ts → stack.Kq2-QzOC.d.ts} +1 -1
- package/dist/shared/{stack.Dw0Ly2TM.d.cts → stack.kcdnD4gA.d.cts} +1 -1
|
@@ -250,9 +250,14 @@ const CustomPostUpdateSchema = schemas.updatePostSchema.omit({
|
|
|
250
250
|
const addPostFormPropsAreEqual = (prevProps, nextProps) => {
|
|
251
251
|
if (prevProps.onClose !== nextProps.onClose) return false;
|
|
252
252
|
if (prevProps.onSuccess !== nextProps.onSuccess) return false;
|
|
253
|
+
if (prevProps.onFormReady !== nextProps.onFormReady) return false;
|
|
253
254
|
return true;
|
|
254
255
|
};
|
|
255
|
-
const AddPostFormComponent = ({
|
|
256
|
+
const AddPostFormComponent = ({
|
|
257
|
+
onClose,
|
|
258
|
+
onSuccess,
|
|
259
|
+
onFormReady
|
|
260
|
+
}) => {
|
|
256
261
|
const [featuredImageUploading, setFeaturedImageUploading] = React.useState(false);
|
|
257
262
|
const { localization } = context.usePluginOverrides("blog", {
|
|
258
263
|
localization: index.BLOG_LOCALIZATION
|
|
@@ -290,6 +295,9 @@ const AddPostFormComponent = ({ onClose, onSuccess }) => {
|
|
|
290
295
|
tags: []
|
|
291
296
|
}
|
|
292
297
|
});
|
|
298
|
+
React.useEffect(() => {
|
|
299
|
+
onFormReady?.(form);
|
|
300
|
+
}, []);
|
|
293
301
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
294
302
|
PostFormBody,
|
|
295
303
|
{
|
|
@@ -309,13 +317,15 @@ const editPostFormPropsAreEqual = (prevProps, nextProps) => {
|
|
|
309
317
|
if (prevProps.onClose !== nextProps.onClose) return false;
|
|
310
318
|
if (prevProps.onSuccess !== nextProps.onSuccess) return false;
|
|
311
319
|
if (prevProps.onDelete !== nextProps.onDelete) return false;
|
|
320
|
+
if (prevProps.onFormReady !== nextProps.onFormReady) return false;
|
|
312
321
|
return true;
|
|
313
322
|
};
|
|
314
323
|
const EditPostFormComponent = ({
|
|
315
324
|
postSlug,
|
|
316
325
|
onClose,
|
|
317
326
|
onSuccess,
|
|
318
|
-
onDelete
|
|
327
|
+
onDelete,
|
|
328
|
+
onFormReady
|
|
319
329
|
}) => {
|
|
320
330
|
const [featuredImageUploading, setFeaturedImageUploading] = React.useState(false);
|
|
321
331
|
const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);
|
|
@@ -391,6 +401,9 @@ const EditPostFormComponent = ({
|
|
|
391
401
|
},
|
|
392
402
|
values: initialData
|
|
393
403
|
});
|
|
404
|
+
React.useEffect(() => {
|
|
405
|
+
onFormReady?.(form);
|
|
406
|
+
}, []);
|
|
394
407
|
if (!post) {
|
|
395
408
|
return /* @__PURE__ */ jsxRuntime.jsx(emptyList.EmptyList, { message: localization.BLOG_PAGE_NOT_FOUND_DESCRIPTION });
|
|
396
409
|
}
|
|
@@ -11,7 +11,7 @@ import { slugify } from '../../../utils.mjs';
|
|
|
11
11
|
import { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction } from '../../../../../../../ui/src/components/alert-dialog.mjs';
|
|
12
12
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
13
13
|
import { Loader2 } from 'lucide-react';
|
|
14
|
-
import { memo, useState, useMemo, Suspense, lazy } from 'react';
|
|
14
|
+
import { memo, useState, useEffect, useMemo, Suspense, lazy } from 'react';
|
|
15
15
|
import { useForm } from 'react-hook-form';
|
|
16
16
|
import { toast } from 'sonner';
|
|
17
17
|
import { FeaturedImageField } from './image-field.mjs';
|
|
@@ -248,9 +248,14 @@ const CustomPostUpdateSchema = updatePostSchema.omit({
|
|
|
248
248
|
const addPostFormPropsAreEqual = (prevProps, nextProps) => {
|
|
249
249
|
if (prevProps.onClose !== nextProps.onClose) return false;
|
|
250
250
|
if (prevProps.onSuccess !== nextProps.onSuccess) return false;
|
|
251
|
+
if (prevProps.onFormReady !== nextProps.onFormReady) return false;
|
|
251
252
|
return true;
|
|
252
253
|
};
|
|
253
|
-
const AddPostFormComponent = ({
|
|
254
|
+
const AddPostFormComponent = ({
|
|
255
|
+
onClose,
|
|
256
|
+
onSuccess,
|
|
257
|
+
onFormReady
|
|
258
|
+
}) => {
|
|
254
259
|
const [featuredImageUploading, setFeaturedImageUploading] = useState(false);
|
|
255
260
|
const { localization } = usePluginOverrides("blog", {
|
|
256
261
|
localization: BLOG_LOCALIZATION
|
|
@@ -288,6 +293,9 @@ const AddPostFormComponent = ({ onClose, onSuccess }) => {
|
|
|
288
293
|
tags: []
|
|
289
294
|
}
|
|
290
295
|
});
|
|
296
|
+
useEffect(() => {
|
|
297
|
+
onFormReady?.(form);
|
|
298
|
+
}, []);
|
|
291
299
|
return /* @__PURE__ */ jsx(
|
|
292
300
|
PostFormBody,
|
|
293
301
|
{
|
|
@@ -307,13 +315,15 @@ const editPostFormPropsAreEqual = (prevProps, nextProps) => {
|
|
|
307
315
|
if (prevProps.onClose !== nextProps.onClose) return false;
|
|
308
316
|
if (prevProps.onSuccess !== nextProps.onSuccess) return false;
|
|
309
317
|
if (prevProps.onDelete !== nextProps.onDelete) return false;
|
|
318
|
+
if (prevProps.onFormReady !== nextProps.onFormReady) return false;
|
|
310
319
|
return true;
|
|
311
320
|
};
|
|
312
321
|
const EditPostFormComponent = ({
|
|
313
322
|
postSlug,
|
|
314
323
|
onClose,
|
|
315
324
|
onSuccess,
|
|
316
|
-
onDelete
|
|
325
|
+
onDelete,
|
|
326
|
+
onFormReady
|
|
317
327
|
}) => {
|
|
318
328
|
const [featuredImageUploading, setFeaturedImageUploading] = useState(false);
|
|
319
329
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
@@ -389,6 +399,9 @@ const EditPostFormComponent = ({
|
|
|
389
399
|
},
|
|
390
400
|
values: initialData
|
|
391
401
|
});
|
|
402
|
+
useEffect(() => {
|
|
403
|
+
onFormReady?.(form);
|
|
404
|
+
}, []);
|
|
392
405
|
if (!post) {
|
|
393
406
|
return /* @__PURE__ */ jsx(EmptyList, { message: localization.BLOG_PAGE_NOT_FOUND_DESCRIPTION });
|
|
394
407
|
}
|
package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.cjs
CHANGED
|
@@ -8,6 +8,9 @@ const pageHeader = require('../shared/page-header.cjs');
|
|
|
8
8
|
const pageWrapper = require('../shared/page-wrapper.cjs');
|
|
9
9
|
const index = require('../../localization/index.cjs');
|
|
10
10
|
const useRouteLifecycle = require('../../../../../../../ui/src/hooks/use-route-lifecycle.cjs');
|
|
11
|
+
const context$1 = require('@btst/stack/plugins/ai-chat/client/context');
|
|
12
|
+
const React = require('react');
|
|
13
|
+
const fillBlogFormHandler = require('./fill-blog-form-handler.cjs');
|
|
11
14
|
|
|
12
15
|
function EditPostPage({ slug }) {
|
|
13
16
|
const overrides = context.usePluginOverrides("blog", {
|
|
@@ -30,6 +33,25 @@ function EditPostPage({ slug }) {
|
|
|
30
33
|
return true;
|
|
31
34
|
}
|
|
32
35
|
});
|
|
36
|
+
const formRef = React.useRef(null);
|
|
37
|
+
const handleFormReady = React.useCallback((form) => {
|
|
38
|
+
formRef.current = form;
|
|
39
|
+
}, []);
|
|
40
|
+
context$1.useRegisterPageAIContext({
|
|
41
|
+
routeName: "blog-edit-post",
|
|
42
|
+
pageDescription: `User is editing a blog post (slug: "${slug}") in the admin editor.`,
|
|
43
|
+
suggestions: [
|
|
44
|
+
"Improve this post's title",
|
|
45
|
+
"Rewrite the intro paragraph",
|
|
46
|
+
"Suggest better tags"
|
|
47
|
+
],
|
|
48
|
+
clientTools: {
|
|
49
|
+
fillBlogForm: fillBlogFormHandler.createFillBlogFormHandler(
|
|
50
|
+
formRef,
|
|
51
|
+
"Form updated successfully"
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
});
|
|
33
55
|
const handleClose = () => {
|
|
34
56
|
navigate(`${basePath}/blog`);
|
|
35
57
|
};
|
|
@@ -53,7 +75,8 @@ function EditPostPage({ slug }) {
|
|
|
53
75
|
postSlug: slug,
|
|
54
76
|
onClose: handleClose,
|
|
55
77
|
onSuccess: handleSuccess,
|
|
56
|
-
onDelete: handleDelete
|
|
78
|
+
onDelete: handleDelete,
|
|
79
|
+
onFormReady: handleFormReady
|
|
57
80
|
}
|
|
58
81
|
)
|
|
59
82
|
] });
|
package/dist/packages/stack/src/plugins/blog/client/components/pages/edit-post-page.internal.mjs
CHANGED
|
@@ -6,6 +6,9 @@ import { PageHeader } from '../shared/page-header.mjs';
|
|
|
6
6
|
import { PageWrapper } from '../shared/page-wrapper.mjs';
|
|
7
7
|
import { BLOG_LOCALIZATION } from '../../localization/index.mjs';
|
|
8
8
|
import { useRouteLifecycle } from '../../../../../../../ui/src/hooks/use-route-lifecycle.mjs';
|
|
9
|
+
import { useRegisterPageAIContext } from '@btst/stack/plugins/ai-chat/client/context';
|
|
10
|
+
import { useRef, useCallback } from 'react';
|
|
11
|
+
import { createFillBlogFormHandler } from './fill-blog-form-handler.mjs';
|
|
9
12
|
|
|
10
13
|
function EditPostPage({ slug }) {
|
|
11
14
|
const overrides = usePluginOverrides("blog", {
|
|
@@ -28,6 +31,25 @@ function EditPostPage({ slug }) {
|
|
|
28
31
|
return true;
|
|
29
32
|
}
|
|
30
33
|
});
|
|
34
|
+
const formRef = useRef(null);
|
|
35
|
+
const handleFormReady = useCallback((form) => {
|
|
36
|
+
formRef.current = form;
|
|
37
|
+
}, []);
|
|
38
|
+
useRegisterPageAIContext({
|
|
39
|
+
routeName: "blog-edit-post",
|
|
40
|
+
pageDescription: `User is editing a blog post (slug: "${slug}") in the admin editor.`,
|
|
41
|
+
suggestions: [
|
|
42
|
+
"Improve this post's title",
|
|
43
|
+
"Rewrite the intro paragraph",
|
|
44
|
+
"Suggest better tags"
|
|
45
|
+
],
|
|
46
|
+
clientTools: {
|
|
47
|
+
fillBlogForm: createFillBlogFormHandler(
|
|
48
|
+
formRef,
|
|
49
|
+
"Form updated successfully"
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
});
|
|
31
53
|
const handleClose = () => {
|
|
32
54
|
navigate(`${basePath}/blog`);
|
|
33
55
|
};
|
|
@@ -51,7 +73,8 @@ function EditPostPage({ slug }) {
|
|
|
51
73
|
postSlug: slug,
|
|
52
74
|
onClose: handleClose,
|
|
53
75
|
onSuccess: handleSuccess,
|
|
54
|
-
onDelete: handleDelete
|
|
76
|
+
onDelete: handleDelete,
|
|
77
|
+
onFormReady: handleFormReady
|
|
55
78
|
}
|
|
56
79
|
)
|
|
57
80
|
] });
|
package/dist/packages/stack/src/plugins/blog/client/components/pages/fill-blog-form-handler.cjs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function createFillBlogFormHandler(formRef, successMessage) {
|
|
4
|
+
return async ({
|
|
5
|
+
title,
|
|
6
|
+
content,
|
|
7
|
+
excerpt,
|
|
8
|
+
tags
|
|
9
|
+
}) => {
|
|
10
|
+
const form = formRef.current;
|
|
11
|
+
if (!form) return { success: false, message: "Form not ready" };
|
|
12
|
+
if (title !== void 0)
|
|
13
|
+
form.setValue("title", title, { shouldValidate: true });
|
|
14
|
+
if (content !== void 0)
|
|
15
|
+
form.setValue("content", content, { shouldValidate: true });
|
|
16
|
+
if (excerpt !== void 0) form.setValue("excerpt", excerpt);
|
|
17
|
+
if (tags !== void 0)
|
|
18
|
+
form.setValue(
|
|
19
|
+
"tags",
|
|
20
|
+
tags.map((name) => ({ name }))
|
|
21
|
+
);
|
|
22
|
+
return { success: true, message: successMessage };
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
exports.createFillBlogFormHandler = createFillBlogFormHandler;
|
package/dist/packages/stack/src/plugins/blog/client/components/pages/fill-blog-form-handler.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function createFillBlogFormHandler(formRef, successMessage) {
|
|
2
|
+
return async ({
|
|
3
|
+
title,
|
|
4
|
+
content,
|
|
5
|
+
excerpt,
|
|
6
|
+
tags
|
|
7
|
+
}) => {
|
|
8
|
+
const form = formRef.current;
|
|
9
|
+
if (!form) return { success: false, message: "Form not ready" };
|
|
10
|
+
if (title !== void 0)
|
|
11
|
+
form.setValue("title", title, { shouldValidate: true });
|
|
12
|
+
if (content !== void 0)
|
|
13
|
+
form.setValue("content", content, { shouldValidate: true });
|
|
14
|
+
if (excerpt !== void 0) form.setValue("excerpt", excerpt);
|
|
15
|
+
if (tags !== void 0)
|
|
16
|
+
form.setValue(
|
|
17
|
+
"tags",
|
|
18
|
+
tags.map((name) => ({ name }))
|
|
19
|
+
);
|
|
20
|
+
return { success: true, message: successMessage };
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export { createFillBlogFormHandler };
|
package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.cjs
CHANGED
|
@@ -8,6 +8,9 @@ const pageHeader = require('../shared/page-header.cjs');
|
|
|
8
8
|
const pageWrapper = require('../shared/page-wrapper.cjs');
|
|
9
9
|
const index = require('../../localization/index.cjs');
|
|
10
10
|
const useRouteLifecycle = require('../../../../../../../ui/src/hooks/use-route-lifecycle.cjs');
|
|
11
|
+
const context$1 = require('@btst/stack/plugins/ai-chat/client/context');
|
|
12
|
+
const React = require('react');
|
|
13
|
+
const fillBlogFormHandler = require('./fill-blog-form-handler.cjs');
|
|
11
14
|
|
|
12
15
|
function NewPostPage() {
|
|
13
16
|
const overrides = context.usePluginOverrides("blog", {
|
|
@@ -29,6 +32,25 @@ function NewPostPage() {
|
|
|
29
32
|
return true;
|
|
30
33
|
}
|
|
31
34
|
});
|
|
35
|
+
const formRef = React.useRef(null);
|
|
36
|
+
const handleFormReady = React.useCallback((form) => {
|
|
37
|
+
formRef.current = form;
|
|
38
|
+
}, []);
|
|
39
|
+
context$1.useRegisterPageAIContext({
|
|
40
|
+
routeName: "blog-new-post",
|
|
41
|
+
pageDescription: "User is creating a new blog post in the admin editor. IMPORTANT: When asked to write, draft, or create a blog post, you MUST call the fillBlogForm tool to populate the form fields directly \u2014 do NOT just output the text in your response.",
|
|
42
|
+
suggestions: [
|
|
43
|
+
"Write a post about AI trends",
|
|
44
|
+
"Draft an intro paragraph",
|
|
45
|
+
"Suggest 5 tags for this post"
|
|
46
|
+
],
|
|
47
|
+
clientTools: {
|
|
48
|
+
fillBlogForm: fillBlogFormHandler.createFillBlogFormHandler(
|
|
49
|
+
formRef,
|
|
50
|
+
"Form filled successfully"
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
});
|
|
32
54
|
const handleClose = () => {
|
|
33
55
|
navigate(`${basePath}/blog`);
|
|
34
56
|
};
|
|
@@ -47,7 +69,14 @@ function NewPostPage() {
|
|
|
47
69
|
description: localization.BLOG_POST_ADD_DESCRIPTION
|
|
48
70
|
}
|
|
49
71
|
),
|
|
50
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
73
|
+
postForms.AddPostForm,
|
|
74
|
+
{
|
|
75
|
+
onClose: handleClose,
|
|
76
|
+
onSuccess: handleSuccess,
|
|
77
|
+
onFormReady: handleFormReady
|
|
78
|
+
}
|
|
79
|
+
)
|
|
51
80
|
] });
|
|
52
81
|
}
|
|
53
82
|
|
package/dist/packages/stack/src/plugins/blog/client/components/pages/new-post-page.internal.mjs
CHANGED
|
@@ -6,6 +6,9 @@ import { PageHeader } from '../shared/page-header.mjs';
|
|
|
6
6
|
import { PageWrapper } from '../shared/page-wrapper.mjs';
|
|
7
7
|
import { BLOG_LOCALIZATION } from '../../localization/index.mjs';
|
|
8
8
|
import { useRouteLifecycle } from '../../../../../../../ui/src/hooks/use-route-lifecycle.mjs';
|
|
9
|
+
import { useRegisterPageAIContext } from '@btst/stack/plugins/ai-chat/client/context';
|
|
10
|
+
import { useRef, useCallback } from 'react';
|
|
11
|
+
import { createFillBlogFormHandler } from './fill-blog-form-handler.mjs';
|
|
9
12
|
|
|
10
13
|
function NewPostPage() {
|
|
11
14
|
const overrides = usePluginOverrides("blog", {
|
|
@@ -27,6 +30,25 @@ function NewPostPage() {
|
|
|
27
30
|
return true;
|
|
28
31
|
}
|
|
29
32
|
});
|
|
33
|
+
const formRef = useRef(null);
|
|
34
|
+
const handleFormReady = useCallback((form) => {
|
|
35
|
+
formRef.current = form;
|
|
36
|
+
}, []);
|
|
37
|
+
useRegisterPageAIContext({
|
|
38
|
+
routeName: "blog-new-post",
|
|
39
|
+
pageDescription: "User is creating a new blog post in the admin editor. IMPORTANT: When asked to write, draft, or create a blog post, you MUST call the fillBlogForm tool to populate the form fields directly \u2014 do NOT just output the text in your response.",
|
|
40
|
+
suggestions: [
|
|
41
|
+
"Write a post about AI trends",
|
|
42
|
+
"Draft an intro paragraph",
|
|
43
|
+
"Suggest 5 tags for this post"
|
|
44
|
+
],
|
|
45
|
+
clientTools: {
|
|
46
|
+
fillBlogForm: createFillBlogFormHandler(
|
|
47
|
+
formRef,
|
|
48
|
+
"Form filled successfully"
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
});
|
|
30
52
|
const handleClose = () => {
|
|
31
53
|
navigate(`${basePath}/blog`);
|
|
32
54
|
};
|
|
@@ -45,7 +67,14 @@ function NewPostPage() {
|
|
|
45
67
|
description: localization.BLOG_POST_ADD_DESCRIPTION
|
|
46
68
|
}
|
|
47
69
|
),
|
|
48
|
-
/* @__PURE__ */ jsx(
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
71
|
+
AddPostForm,
|
|
72
|
+
{
|
|
73
|
+
onClose: handleClose,
|
|
74
|
+
onSuccess: handleSuccess,
|
|
75
|
+
onFormReady: handleFormReady
|
|
76
|
+
}
|
|
77
|
+
)
|
|
49
78
|
] });
|
|
50
79
|
}
|
|
51
80
|
|
|
@@ -16,6 +16,7 @@ const recentPostsCarousel = require('../shared/recent-posts-carousel.cjs');
|
|
|
16
16
|
const badge = require('../../../../../../../ui/src/components/badge.cjs');
|
|
17
17
|
const useRouteLifecycle = require('../../../../../../../ui/src/hooks/use-route-lifecycle.cjs');
|
|
18
18
|
const onThisPage = require('../shared/on-this-page.cjs');
|
|
19
|
+
const context$1 = require('@btst/stack/plugins/ai-chat/client/context');
|
|
19
20
|
|
|
20
21
|
function PostPage({ slug }) {
|
|
21
22
|
const overrides = context.usePluginOverrides("blog", {
|
|
@@ -50,6 +51,23 @@ function PostPage({ slug }) {
|
|
|
50
51
|
excludeSlug: slug,
|
|
51
52
|
enabled: !!post
|
|
52
53
|
});
|
|
54
|
+
context$1.useRegisterPageAIContext(
|
|
55
|
+
post ? {
|
|
56
|
+
routeName: "blog-post",
|
|
57
|
+
pageDescription: `Blog post: "${post.title}"
|
|
58
|
+
Author: ${post.authorId ?? "Unknown"}
|
|
59
|
+
|
|
60
|
+
${post.content ?? ""}`.slice(
|
|
61
|
+
0,
|
|
62
|
+
16e3
|
|
63
|
+
),
|
|
64
|
+
suggestions: [
|
|
65
|
+
"Summarize this post",
|
|
66
|
+
"What are the key takeaways?",
|
|
67
|
+
"Explain this in simpler terms"
|
|
68
|
+
]
|
|
69
|
+
} : null
|
|
70
|
+
);
|
|
53
71
|
if (!slug || !post) {
|
|
54
72
|
return /* @__PURE__ */ jsxRuntime.jsx(pageWrapper.PageWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(emptyList.EmptyList, { message: localization.BLOG_PAGE_NOT_FOUND_DESCRIPTION }) });
|
|
55
73
|
}
|
|
@@ -14,6 +14,7 @@ import { RecentPostsCarousel } from '../shared/recent-posts-carousel.mjs';
|
|
|
14
14
|
import { Badge } from '../../../../../../../ui/src/components/badge.mjs';
|
|
15
15
|
import { useRouteLifecycle } from '../../../../../../../ui/src/hooks/use-route-lifecycle.mjs';
|
|
16
16
|
import { OnThisPageSelect, OnThisPage } from '../shared/on-this-page.mjs';
|
|
17
|
+
import { useRegisterPageAIContext } from '@btst/stack/plugins/ai-chat/client/context';
|
|
17
18
|
|
|
18
19
|
function PostPage({ slug }) {
|
|
19
20
|
const overrides = usePluginOverrides("blog", {
|
|
@@ -48,6 +49,23 @@ function PostPage({ slug }) {
|
|
|
48
49
|
excludeSlug: slug,
|
|
49
50
|
enabled: !!post
|
|
50
51
|
});
|
|
52
|
+
useRegisterPageAIContext(
|
|
53
|
+
post ? {
|
|
54
|
+
routeName: "blog-post",
|
|
55
|
+
pageDescription: `Blog post: "${post.title}"
|
|
56
|
+
Author: ${post.authorId ?? "Unknown"}
|
|
57
|
+
|
|
58
|
+
${post.content ?? ""}`.slice(
|
|
59
|
+
0,
|
|
60
|
+
16e3
|
|
61
|
+
),
|
|
62
|
+
suggestions: [
|
|
63
|
+
"Summarize this post",
|
|
64
|
+
"What are the key takeaways?",
|
|
65
|
+
"Explain this in simpler terms"
|
|
66
|
+
]
|
|
67
|
+
} : null
|
|
68
|
+
);
|
|
51
69
|
if (!slug || !post) {
|
|
52
70
|
return /* @__PURE__ */ jsx(PageWrapper, { children: /* @__PURE__ */ jsx(EmptyList, { message: localization.BLOG_PAGE_NOT_FOUND_DESCRIPTION }) });
|
|
53
71
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const getters = require('./getters.cjs');
|
|
4
|
+
|
|
5
|
+
async function createCMSContentItem(adapter, contentTypeSlug, input) {
|
|
6
|
+
const contentType = await adapter.findOne({
|
|
7
|
+
model: "contentType",
|
|
8
|
+
where: [
|
|
9
|
+
{
|
|
10
|
+
field: "slug",
|
|
11
|
+
value: contentTypeSlug,
|
|
12
|
+
operator: "eq"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
});
|
|
16
|
+
if (!contentType) {
|
|
17
|
+
throw new Error(`Content type "${contentTypeSlug}" not found`);
|
|
18
|
+
}
|
|
19
|
+
const existing = await adapter.findOne({
|
|
20
|
+
model: "contentItem",
|
|
21
|
+
where: [
|
|
22
|
+
{
|
|
23
|
+
field: "contentTypeId",
|
|
24
|
+
value: contentType.id,
|
|
25
|
+
operator: "eq"
|
|
26
|
+
},
|
|
27
|
+
{ field: "slug", value: input.slug, operator: "eq" }
|
|
28
|
+
]
|
|
29
|
+
});
|
|
30
|
+
if (existing) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Content item with slug "${input.slug}" already exists in type "${contentTypeSlug}"`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const item = await adapter.create({
|
|
36
|
+
model: "contentItem",
|
|
37
|
+
data: {
|
|
38
|
+
contentTypeId: contentType.id,
|
|
39
|
+
slug: input.slug,
|
|
40
|
+
data: JSON.stringify(input.data),
|
|
41
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
42
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return getters.serializeContentItem(item);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
exports.createCMSContentItem = createCMSContentItem;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { serializeContentItem } from './getters.mjs';
|
|
2
|
+
|
|
3
|
+
async function createCMSContentItem(adapter, contentTypeSlug, input) {
|
|
4
|
+
const contentType = await adapter.findOne({
|
|
5
|
+
model: "contentType",
|
|
6
|
+
where: [
|
|
7
|
+
{
|
|
8
|
+
field: "slug",
|
|
9
|
+
value: contentTypeSlug,
|
|
10
|
+
operator: "eq"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
});
|
|
14
|
+
if (!contentType) {
|
|
15
|
+
throw new Error(`Content type "${contentTypeSlug}" not found`);
|
|
16
|
+
}
|
|
17
|
+
const existing = await adapter.findOne({
|
|
18
|
+
model: "contentItem",
|
|
19
|
+
where: [
|
|
20
|
+
{
|
|
21
|
+
field: "contentTypeId",
|
|
22
|
+
value: contentType.id,
|
|
23
|
+
operator: "eq"
|
|
24
|
+
},
|
|
25
|
+
{ field: "slug", value: input.slug, operator: "eq" }
|
|
26
|
+
]
|
|
27
|
+
});
|
|
28
|
+
if (existing) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Content item with slug "${input.slug}" already exists in type "${contentTypeSlug}"`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
const item = await adapter.create({
|
|
34
|
+
model: "contentItem",
|
|
35
|
+
data: {
|
|
36
|
+
contentTypeId: contentType.id,
|
|
37
|
+
slug: input.slug,
|
|
38
|
+
data: JSON.stringify(input.data),
|
|
39
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
40
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return serializeContentItem(item);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export { createCMSContentItem };
|
|
@@ -7,6 +7,7 @@ const db = require('../db.cjs');
|
|
|
7
7
|
const schemas = require('../schemas.cjs');
|
|
8
8
|
const utils = require('../utils.cjs');
|
|
9
9
|
const getters = require('./getters.cjs');
|
|
10
|
+
const mutations = require('./mutations.cjs');
|
|
10
11
|
const queryKeyDefs = require('./query-key-defs.cjs');
|
|
11
12
|
|
|
12
13
|
async function syncContentTypes(adapter, config) {
|
|
@@ -327,7 +328,12 @@ const cmsBackendPlugin = (config) => {
|
|
|
327
328
|
await ensureSynced(adapter);
|
|
328
329
|
return getters.getContentItemById(adapter, id);
|
|
329
330
|
},
|
|
330
|
-
prefetchForRoute: createCMSPrefetchForRoute(adapter)
|
|
331
|
+
prefetchForRoute: createCMSPrefetchForRoute(adapter),
|
|
332
|
+
// Mutations
|
|
333
|
+
createContentItem: async (typeSlug, input) => {
|
|
334
|
+
await ensureSynced(adapter);
|
|
335
|
+
return mutations.createCMSContentItem(adapter, typeSlug, input);
|
|
336
|
+
}
|
|
331
337
|
}),
|
|
332
338
|
routes: (adapter) => {
|
|
333
339
|
const getContentType = async (slug) => {
|
|
@@ -5,6 +5,7 @@ import { cmsSchema } from '../db.mjs';
|
|
|
5
5
|
import { listContentQuerySchema } from '../schemas.mjs';
|
|
6
6
|
import { slugify } from '../utils.mjs';
|
|
7
7
|
import { serializeContentType, getAllContentItems, serializeContentItemWithType, serializeContentItem, getContentItemById, getContentItemBySlug, getAllContentTypes } from './getters.mjs';
|
|
8
|
+
import { createCMSContentItem } from './mutations.mjs';
|
|
8
9
|
import { CMS_QUERY_KEYS } from './query-key-defs.mjs';
|
|
9
10
|
|
|
10
11
|
async function syncContentTypes(adapter, config) {
|
|
@@ -325,7 +326,12 @@ const cmsBackendPlugin = (config) => {
|
|
|
325
326
|
await ensureSynced(adapter);
|
|
326
327
|
return getContentItemById(adapter, id);
|
|
327
328
|
},
|
|
328
|
-
prefetchForRoute: createCMSPrefetchForRoute(adapter)
|
|
329
|
+
prefetchForRoute: createCMSPrefetchForRoute(adapter),
|
|
330
|
+
// Mutations
|
|
331
|
+
createContentItem: async (typeSlug, input) => {
|
|
332
|
+
await ensureSynced(adapter);
|
|
333
|
+
return createCMSContentItem(adapter, typeSlug, input);
|
|
334
|
+
}
|
|
329
335
|
}),
|
|
330
336
|
routes: (adapter) => {
|
|
331
337
|
const getContentType = async (slug) => {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function createKanbanTask(adapter, input) {
|
|
4
|
+
const existingTasks = await adapter.findMany({
|
|
5
|
+
model: "kanbanTask",
|
|
6
|
+
where: [
|
|
7
|
+
{
|
|
8
|
+
field: "columnId",
|
|
9
|
+
value: input.columnId,
|
|
10
|
+
operator: "eq"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
});
|
|
14
|
+
const nextOrder = existingTasks.length > 0 ? Math.max(...existingTasks.map((t) => t.order)) + 1 : 0;
|
|
15
|
+
return adapter.create({
|
|
16
|
+
model: "kanbanTask",
|
|
17
|
+
data: {
|
|
18
|
+
title: input.title,
|
|
19
|
+
columnId: input.columnId,
|
|
20
|
+
description: input.description,
|
|
21
|
+
priority: input.priority ?? "MEDIUM",
|
|
22
|
+
order: nextOrder,
|
|
23
|
+
assigneeId: input.assigneeId,
|
|
24
|
+
isArchived: false,
|
|
25
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
26
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const _pendingBoardCreations = /* @__PURE__ */ new Map();
|
|
31
|
+
async function findOrCreateKanbanBoard(adapter, slug, name, columnTitles) {
|
|
32
|
+
const existing = await adapter.findOne({
|
|
33
|
+
model: "kanbanBoard",
|
|
34
|
+
where: [{ field: "slug", value: slug, operator: "eq" }]
|
|
35
|
+
});
|
|
36
|
+
if (existing) return existing;
|
|
37
|
+
const inflight = _pendingBoardCreations.get(slug);
|
|
38
|
+
if (inflight) return inflight;
|
|
39
|
+
const creation = (async () => {
|
|
40
|
+
try {
|
|
41
|
+
const board = await adapter.create({
|
|
42
|
+
model: "kanbanBoard",
|
|
43
|
+
data: {
|
|
44
|
+
name,
|
|
45
|
+
slug,
|
|
46
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
47
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
await Promise.all(
|
|
51
|
+
columnTitles.map(
|
|
52
|
+
(title, index) => adapter.create({
|
|
53
|
+
model: "kanbanColumn",
|
|
54
|
+
data: {
|
|
55
|
+
title,
|
|
56
|
+
boardId: board.id,
|
|
57
|
+
order: index,
|
|
58
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
59
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
)
|
|
63
|
+
);
|
|
64
|
+
return board;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
const winner = await adapter.findOne({
|
|
67
|
+
model: "kanbanBoard",
|
|
68
|
+
where: [{ field: "slug", value: slug, operator: "eq" }]
|
|
69
|
+
});
|
|
70
|
+
if (winner) return winner;
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
_pendingBoardCreations.set(slug, creation);
|
|
75
|
+
try {
|
|
76
|
+
return await creation;
|
|
77
|
+
} finally {
|
|
78
|
+
_pendingBoardCreations.delete(slug);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function getKanbanColumnsByBoardId(adapter, boardId) {
|
|
82
|
+
return adapter.findMany({
|
|
83
|
+
model: "kanbanColumn",
|
|
84
|
+
where: [{ field: "boardId", value: boardId, operator: "eq" }],
|
|
85
|
+
sortBy: { field: "order", direction: "asc" }
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
exports.createKanbanTask = createKanbanTask;
|
|
90
|
+
exports.findOrCreateKanbanBoard = findOrCreateKanbanBoard;
|
|
91
|
+
exports.getKanbanColumnsByBoardId = getKanbanColumnsByBoardId;
|