@btst/stack 2.8.1 → 2.9.1
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/README.md +3 -2
- package/dist/components/markdown/index.d.cts +15 -2
- package/dist/components/markdown/index.d.mts +15 -2
- package/dist/components/markdown/index.d.ts +15 -2
- package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.cjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.mjs +30 -1
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.cjs +49 -9
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.mjs +50 -10
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.cjs +77 -9
- package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.mjs +77 -9
- package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +24 -5
- package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +24 -5
- package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.cjs +47 -13
- package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.mjs +47 -13
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.cjs +1 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.mjs +1 -1
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +6 -2
- package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +6 -2
- package/dist/packages/stack/src/plugins/media/api/adapters/local.cjs +55 -0
- package/dist/packages/stack/src/plugins/media/api/adapters/local.mjs +37 -0
- package/dist/packages/stack/src/plugins/media/api/getters.cjs +83 -0
- package/dist/packages/stack/src/plugins/media/api/getters.mjs +78 -0
- package/dist/packages/stack/src/plugins/media/api/mutations.cjs +88 -0
- package/dist/packages/stack/src/plugins/media/api/mutations.mjs +82 -0
- package/dist/packages/stack/src/plugins/media/api/plugin.cjs +525 -0
- package/dist/packages/stack/src/plugins/media/api/plugin.mjs +523 -0
- package/dist/packages/stack/src/plugins/media/api/query-key-defs.cjs +19 -0
- package/dist/packages/stack/src/plugins/media/api/query-key-defs.mjs +16 -0
- package/dist/packages/stack/src/plugins/media/api/serializers.cjs +17 -0
- package/dist/packages/stack/src/plugins/media/api/serializers.mjs +14 -0
- package/dist/packages/stack/src/plugins/media/api/storage-adapter.cjs +15 -0
- package/dist/packages/stack/src/plugins/media/api/storage-adapter.mjs +11 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.cjs +129 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.mjs +127 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.cjs +58 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.mjs +56 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.cjs +94 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.mjs +92 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.cjs +171 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.mjs +168 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.cjs +308 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.mjs +305 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.cjs +104 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.mjs +102 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.cjs +70 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.mjs +68 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.cjs +21 -0
- package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.mjs +17 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.cjs +35 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.cjs +125 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.mjs +123 -0
- package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.mjs +33 -0
- package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +222 -0
- package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +214 -0
- package/dist/packages/stack/src/plugins/media/client/plugin.cjs +94 -0
- package/dist/packages/stack/src/plugins/media/client/plugin.mjs +92 -0
- package/dist/packages/stack/src/plugins/media/client/upload.cjs +121 -0
- package/dist/packages/stack/src/plugins/media/client/upload.mjs +119 -0
- package/dist/packages/stack/src/plugins/media/client/utils/image-compression.cjs +67 -0
- package/dist/packages/stack/src/plugins/media/client/utils/image-compression.mjs +65 -0
- package/dist/packages/stack/src/plugins/media/db.cjs +62 -0
- package/dist/packages/stack/src/plugins/media/db.mjs +60 -0
- package/dist/packages/stack/src/plugins/media/schemas.cjs +41 -0
- package/dist/packages/stack/src/plugins/media/schemas.mjs +35 -0
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.cjs +18 -1
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.mjs +19 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.cjs +2 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.mjs +2 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.cjs +3 -2
- package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.mjs +3 -2
- package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.cjs +12 -5
- package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.mjs +12 -5
- package/dist/plugins/blog/client/index.d.cts +58 -1
- package/dist/plugins/blog/client/index.d.mts +58 -1
- package/dist/plugins/blog/client/index.d.ts +58 -1
- package/dist/plugins/cms/client/index.d.cts +73 -3
- package/dist/plugins/cms/client/index.d.mts +73 -3
- package/dist/plugins/cms/client/index.d.ts +73 -3
- 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/client/hooks/index.d.cts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
- package/dist/plugins/kanban/client/index.d.cts +1 -1
- package/dist/plugins/kanban/client/index.d.mts +1 -1
- package/dist/plugins/kanban/client/index.d.ts +1 -1
- 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/plugins/media/api/adapters/s3.cjs +106 -0
- package/dist/plugins/media/api/adapters/s3.d.cts +60 -0
- package/dist/plugins/media/api/adapters/s3.d.mts +60 -0
- package/dist/plugins/media/api/adapters/s3.d.ts +60 -0
- package/dist/plugins/media/api/adapters/s3.mjs +104 -0
- package/dist/plugins/media/api/adapters/vercel-blob.cjs +53 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.cts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.mts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.d.ts +41 -0
- package/dist/plugins/media/api/adapters/vercel-blob.mjs +51 -0
- package/dist/plugins/media/api/index.cjs +26 -0
- package/dist/plugins/media/api/index.d.cts +116 -0
- package/dist/plugins/media/api/index.d.mts +116 -0
- package/dist/plugins/media/api/index.d.ts +116 -0
- package/dist/plugins/media/api/index.mjs +6 -0
- package/dist/plugins/media/client/components/index.cjs +10 -0
- package/dist/plugins/media/client/components/index.d.cts +55 -0
- package/dist/plugins/media/client/components/index.d.mts +55 -0
- package/dist/plugins/media/client/components/index.d.ts +55 -0
- package/dist/plugins/media/client/components/index.mjs +2 -0
- package/dist/plugins/media/client/hooks/index.cjs +13 -0
- package/dist/plugins/media/client/hooks/index.d.cts +53 -0
- package/dist/plugins/media/client/hooks/index.d.mts +53 -0
- package/dist/plugins/media/client/hooks/index.d.ts +53 -0
- package/dist/plugins/media/client/hooks/index.mjs +1 -0
- package/dist/plugins/media/client/index.cjs +9 -0
- package/dist/plugins/media/client/index.d.cts +242 -0
- package/dist/plugins/media/client/index.d.mts +242 -0
- package/dist/plugins/media/client/index.d.ts +242 -0
- package/dist/plugins/media/client/index.mjs +2 -0
- package/dist/plugins/media/client.css +1 -0
- package/dist/plugins/media/query-keys.cjs +72 -0
- package/dist/plugins/media/query-keys.d.cts +49 -0
- package/dist/plugins/media/query-keys.d.mts +49 -0
- package/dist/plugins/media/query-keys.d.ts +49 -0
- package/dist/plugins/media/query-keys.mjs +70 -0
- package/dist/plugins/media/style.css +1 -0
- package/dist/shared/{stack.DRpeDS6X.d.ts → stack.BMx2QYOK.d.ts} +25 -0
- package/dist/shared/stack.BttDsJJn.d.cts +109 -0
- package/dist/shared/stack.BttDsJJn.d.mts +109 -0
- package/dist/shared/stack.BttDsJJn.d.ts +109 -0
- package/dist/shared/stack.C7vfOBmO.d.mts +63 -0
- package/dist/shared/stack.CAni8dnD.d.cts +63 -0
- package/dist/shared/stack.CI8iRKKi.d.cts +286 -0
- package/dist/shared/stack.CLcnSF_b.d.cts +25 -0
- package/dist/shared/stack.CLcnSF_b.d.mts +25 -0
- package/dist/shared/stack.CLcnSF_b.d.ts +25 -0
- package/dist/shared/stack.CYSwntXC.d.ts +63 -0
- package/dist/shared/{stack.Jb0kQDJC.d.mts → stack.Cd6McBu1.d.mts} +25 -0
- package/dist/shared/stack.DJDjdG64.d.ts +286 -0
- package/dist/shared/{stack.BxFl46lB.d.cts → stack.DxQl8Wa1.d.cts} +25 -0
- package/dist/shared/stack.FgBVDSPi.d.mts +286 -0
- package/package.json +113 -4
- package/src/plugins/blog/client/components/forms/image-field.tsx +35 -4
- package/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.tsx +67 -12
- package/src/plugins/blog/client/components/forms/markdown-editor.tsx +106 -10
- package/src/plugins/blog/client/overrides.ts +58 -1
- package/src/plugins/cms/client/components/forms/content-form.tsx +26 -7
- package/src/plugins/cms/client/components/forms/file-upload.tsx +73 -15
- package/src/plugins/cms/client/overrides.ts +57 -2
- package/src/plugins/kanban/client/components/forms/board-form.tsx +1 -1
- package/src/plugins/kanban/client/components/forms/task-form.tsx +7 -1
- package/src/plugins/kanban/client/overrides.ts +25 -0
- package/src/plugins/media/__tests__/__stubs__/vercel-blob-server.ts +9 -0
- package/src/plugins/media/__tests__/getters.test.ts +274 -0
- package/src/plugins/media/__tests__/mutations.test.ts +299 -0
- package/src/plugins/media/__tests__/plugin.test.ts +752 -0
- package/src/plugins/media/__tests__/query-key-defs.test.ts +54 -0
- package/src/plugins/media/__tests__/storage-adapters.test.ts +351 -0
- package/src/plugins/media/api/adapters/local.ts +79 -0
- package/src/plugins/media/api/adapters/s3.ts +198 -0
- package/src/plugins/media/api/adapters/vercel-blob.ts +131 -0
- package/src/plugins/media/api/getters.ts +174 -0
- package/src/plugins/media/api/index.ts +41 -0
- package/src/plugins/media/api/mutations.ts +179 -0
- package/src/plugins/media/api/plugin.ts +855 -0
- package/src/plugins/media/api/query-key-defs.ts +41 -0
- package/src/plugins/media/api/serializers.ts +28 -0
- package/src/plugins/media/api/storage-adapter.ts +139 -0
- package/src/plugins/media/client/components/index.tsx +6 -0
- package/src/plugins/media/client/components/media-picker/asset-card.tsx +150 -0
- package/src/plugins/media/client/components/media-picker/asset-preview-button.tsx +67 -0
- package/src/plugins/media/client/components/media-picker/browse-tab.tsx +116 -0
- package/src/plugins/media/client/components/media-picker/folder-tree.tsx +188 -0
- package/src/plugins/media/client/components/media-picker/index.tsx +347 -0
- package/src/plugins/media/client/components/media-picker/upload-tab.tsx +108 -0
- package/src/plugins/media/client/components/media-picker/url-tab.tsx +72 -0
- package/src/plugins/media/client/components/media-picker/utils.ts +17 -0
- package/src/plugins/media/client/components/pages/library-page.internal.tsx +134 -0
- package/src/plugins/media/client/components/pages/library-page.tsx +42 -0
- package/src/plugins/media/client/hooks/index.tsx +9 -0
- package/src/plugins/media/client/hooks/use-media.tsx +289 -0
- package/src/plugins/media/client/index.ts +4 -0
- package/src/plugins/media/client/overrides.ts +127 -0
- package/src/plugins/media/client/plugin.tsx +184 -0
- package/src/plugins/media/client/upload.ts +171 -0
- package/src/plugins/media/client/utils/image-compression.ts +131 -0
- package/src/plugins/media/client.css +1 -0
- package/src/plugins/media/db.ts +62 -0
- package/src/plugins/media/query-keys.ts +96 -0
- package/src/plugins/media/schemas.ts +37 -0
- package/src/plugins/media/style.css +1 -0
- package/src/plugins/media/types.ts +26 -0
- package/dist/shared/{stack.BOokfhZD.d.cts → stack.B6S3cgwN.d.cts} +16 -16
- package/dist/shared/{stack.CWxAl9K3.d.mts → stack.Bzfx-_lq.d.mts} +16 -16
- package/dist/shared/{stack.BvCR4-9H.d.ts → stack.j5SFLC1d.d.ts} +16 -16
|
@@ -14,25 +14,36 @@ import { CMS_LOCALIZATION } from '../../localization/index.mjs';
|
|
|
14
14
|
import { CMSFileUpload } from './file-upload.mjs';
|
|
15
15
|
import { RelationField } from './relation-field.mjs';
|
|
16
16
|
|
|
17
|
-
function buildFieldConfigFromJsonSchema(jsonSchema, uploadImage, fieldComponents) {
|
|
17
|
+
function buildFieldConfigFromJsonSchema(jsonSchema, uploadImage, fieldComponents, imagePicker, imageInputField) {
|
|
18
18
|
const baseConfig = buildFieldConfigFromJsonSchema$1(jsonSchema, fieldComponents);
|
|
19
19
|
const properties = jsonSchema.properties;
|
|
20
20
|
if (!properties) return baseConfig;
|
|
21
21
|
for (const [key, prop] of Object.entries(properties)) {
|
|
22
22
|
if (prop.fieldType === "file" && !fieldComponents?.["file"]) {
|
|
23
|
-
if (!uploadImage) {
|
|
23
|
+
if (!uploadImage && !imageInputField) {
|
|
24
24
|
baseConfig[key] = {
|
|
25
25
|
...baseConfig[key],
|
|
26
26
|
fieldType: () => /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive bg-destructive/10 p-3 text-sm text-destructive", children: [
|
|
27
27
|
"File upload requires an ",
|
|
28
28
|
/* @__PURE__ */ jsx("code", { children: "uploadImage" }),
|
|
29
|
+
" or",
|
|
30
|
+
" ",
|
|
31
|
+
/* @__PURE__ */ jsx("code", { children: "imageInputField" }),
|
|
29
32
|
" function in CMS overrides."
|
|
30
33
|
] })
|
|
31
34
|
};
|
|
32
35
|
} else {
|
|
33
36
|
baseConfig[key] = {
|
|
34
37
|
...baseConfig[key],
|
|
35
|
-
fieldType: (props) => /* @__PURE__ */ jsx(
|
|
38
|
+
fieldType: (props) => /* @__PURE__ */ jsx(
|
|
39
|
+
CMSFileUpload,
|
|
40
|
+
{
|
|
41
|
+
...props,
|
|
42
|
+
uploadImage: uploadImage ?? (() => Promise.resolve("")),
|
|
43
|
+
imageInputField,
|
|
44
|
+
imagePicker
|
|
45
|
+
}
|
|
46
|
+
)
|
|
36
47
|
};
|
|
37
48
|
}
|
|
38
49
|
}
|
|
@@ -73,6 +84,8 @@ function ContentForm({
|
|
|
73
84
|
const {
|
|
74
85
|
localization: customLocalization,
|
|
75
86
|
uploadImage,
|
|
87
|
+
imagePicker,
|
|
88
|
+
imageInputField,
|
|
76
89
|
fieldComponents
|
|
77
90
|
} = usePluginOverrides("cms");
|
|
78
91
|
const localization = { ...CMS_LOCALIZATION, ...customLocalization };
|
|
@@ -113,8 +126,14 @@ function ContentForm({
|
|
|
113
126
|
}
|
|
114
127
|
}, [jsonSchema]);
|
|
115
128
|
const fieldConfig = useMemo(
|
|
116
|
-
() => buildFieldConfigFromJsonSchema(
|
|
117
|
-
|
|
129
|
+
() => buildFieldConfigFromJsonSchema(
|
|
130
|
+
jsonSchema,
|
|
131
|
+
uploadImage,
|
|
132
|
+
fieldComponents,
|
|
133
|
+
imagePicker,
|
|
134
|
+
imageInputField
|
|
135
|
+
),
|
|
136
|
+
[jsonSchema, uploadImage, fieldComponents, imagePicker, imageInputField]
|
|
118
137
|
);
|
|
119
138
|
const slugSourceField = useMemo(
|
|
120
139
|
() => findSlugSourceField(jsonSchema),
|
|
@@ -17,7 +17,9 @@ function CMSFileUpload({
|
|
|
17
17
|
fieldConfigItem,
|
|
18
18
|
fieldProps,
|
|
19
19
|
field,
|
|
20
|
-
uploadImage
|
|
20
|
+
uploadImage,
|
|
21
|
+
imageInputField: ImageInputField,
|
|
22
|
+
imagePicker: ImagePickerTrigger
|
|
21
23
|
}) {
|
|
22
24
|
const {
|
|
23
25
|
showLabel: _showLabel,
|
|
@@ -61,6 +63,27 @@ function CMSFileUpload({
|
|
|
61
63
|
setPreviewUrl(null);
|
|
62
64
|
field.onChange("");
|
|
63
65
|
}, [field]);
|
|
66
|
+
if (ImageInputField) {
|
|
67
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(form.FormItem, { children: [
|
|
68
|
+
showLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
69
|
+
label,
|
|
70
|
+
{
|
|
71
|
+
label: fieldConfigItem?.label || label$1,
|
|
72
|
+
isRequired
|
|
73
|
+
}
|
|
74
|
+
),
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx(form.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
76
|
+
ImageInputField,
|
|
77
|
+
{
|
|
78
|
+
value: field.value || "",
|
|
79
|
+
onChange: field.onChange,
|
|
80
|
+
isRequired
|
|
81
|
+
}
|
|
82
|
+
) }),
|
|
83
|
+
/* @__PURE__ */ jsxRuntime.jsx(tooltip, { fieldConfigItem }),
|
|
84
|
+
/* @__PURE__ */ jsxRuntime.jsx(form.FormMessage, {})
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
64
87
|
return /* @__PURE__ */ jsxRuntime.jsxs(form.FormItem, { children: [
|
|
65
88
|
showLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
66
89
|
label,
|
|
@@ -69,20 +92,31 @@ function CMSFileUpload({
|
|
|
69
92
|
isRequired
|
|
70
93
|
}
|
|
71
94
|
),
|
|
72
|
-
!previewUrl && /* @__PURE__ */ jsxRuntime.jsx(form.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
73
|
-
/* @__PURE__ */ jsxRuntime.
|
|
74
|
-
|
|
95
|
+
!previewUrl && /* @__PURE__ */ jsxRuntime.jsx(form.FormControl, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
96
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
97
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
98
|
+
input.Input,
|
|
99
|
+
{
|
|
100
|
+
type: "file",
|
|
101
|
+
accept: "image/*",
|
|
102
|
+
...safeFieldProps,
|
|
103
|
+
onChange: handleFileChange,
|
|
104
|
+
disabled: isUploading,
|
|
105
|
+
className: "cursor-pointer",
|
|
106
|
+
"data-testid": "image-upload-input"
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
isUploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background/80", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Loader2, { className: "h-4 w-4 animate-spin" }) })
|
|
110
|
+
] }),
|
|
111
|
+
ImagePickerTrigger && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-testid": "image-picker-trigger", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
112
|
+
ImagePickerTrigger,
|
|
75
113
|
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
disabled: isUploading,
|
|
81
|
-
className: "cursor-pointer",
|
|
82
|
-
"data-testid": "image-upload-input"
|
|
114
|
+
onSelect: (url) => {
|
|
115
|
+
setPreviewUrl(url);
|
|
116
|
+
field.onChange(url);
|
|
117
|
+
}
|
|
83
118
|
}
|
|
84
|
-
)
|
|
85
|
-
isUploading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background/80", children: /* @__PURE__ */ jsxRuntime.jsx(LucideIcons.Loader2, { className: "h-4 w-4 animate-spin" }) })
|
|
119
|
+
) })
|
|
86
120
|
] }) }),
|
|
87
121
|
previewUrl && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
88
122
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-20 w-20 overflow-hidden rounded-md border", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -15,7 +15,9 @@ function CMSFileUpload({
|
|
|
15
15
|
fieldConfigItem,
|
|
16
16
|
fieldProps,
|
|
17
17
|
field,
|
|
18
|
-
uploadImage
|
|
18
|
+
uploadImage,
|
|
19
|
+
imageInputField: ImageInputField,
|
|
20
|
+
imagePicker: ImagePickerTrigger
|
|
19
21
|
}) {
|
|
20
22
|
const {
|
|
21
23
|
showLabel: _showLabel,
|
|
@@ -59,6 +61,27 @@ function CMSFileUpload({
|
|
|
59
61
|
setPreviewUrl(null);
|
|
60
62
|
field.onChange("");
|
|
61
63
|
}, [field]);
|
|
64
|
+
if (ImageInputField) {
|
|
65
|
+
return /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
66
|
+
showLabel && /* @__PURE__ */ jsx(
|
|
67
|
+
AutoFormLabel,
|
|
68
|
+
{
|
|
69
|
+
label: fieldConfigItem?.label || label,
|
|
70
|
+
isRequired
|
|
71
|
+
}
|
|
72
|
+
),
|
|
73
|
+
/* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsx(
|
|
74
|
+
ImageInputField,
|
|
75
|
+
{
|
|
76
|
+
value: field.value || "",
|
|
77
|
+
onChange: field.onChange,
|
|
78
|
+
isRequired
|
|
79
|
+
}
|
|
80
|
+
) }),
|
|
81
|
+
/* @__PURE__ */ jsx(AutoFormTooltip, { fieldConfigItem }),
|
|
82
|
+
/* @__PURE__ */ jsx(FormMessage, {})
|
|
83
|
+
] });
|
|
84
|
+
}
|
|
62
85
|
return /* @__PURE__ */ jsxs(FormItem, { children: [
|
|
63
86
|
showLabel && /* @__PURE__ */ jsx(
|
|
64
87
|
AutoFormLabel,
|
|
@@ -67,20 +90,31 @@ function CMSFileUpload({
|
|
|
67
90
|
isRequired
|
|
68
91
|
}
|
|
69
92
|
),
|
|
70
|
-
!previewUrl && /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsxs("div", { className: "
|
|
71
|
-
/* @__PURE__ */
|
|
72
|
-
|
|
93
|
+
!previewUrl && /* @__PURE__ */ jsx(FormControl, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
Input,
|
|
97
|
+
{
|
|
98
|
+
type: "file",
|
|
99
|
+
accept: "image/*",
|
|
100
|
+
...safeFieldProps,
|
|
101
|
+
onChange: handleFileChange,
|
|
102
|
+
disabled: isUploading,
|
|
103
|
+
className: "cursor-pointer",
|
|
104
|
+
"data-testid": "image-upload-input"
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background/80", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) })
|
|
108
|
+
] }),
|
|
109
|
+
ImagePickerTrigger && /* @__PURE__ */ jsx("div", { "data-testid": "image-picker-trigger", children: /* @__PURE__ */ jsx(
|
|
110
|
+
ImagePickerTrigger,
|
|
73
111
|
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
disabled: isUploading,
|
|
79
|
-
className: "cursor-pointer",
|
|
80
|
-
"data-testid": "image-upload-input"
|
|
112
|
+
onSelect: (url) => {
|
|
113
|
+
setPreviewUrl(url);
|
|
114
|
+
field.onChange(url);
|
|
115
|
+
}
|
|
81
116
|
}
|
|
82
|
-
)
|
|
83
|
-
isUploading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-background/80", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) })
|
|
117
|
+
) })
|
|
84
118
|
] }) }),
|
|
85
119
|
previewUrl && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
86
120
|
/* @__PURE__ */ jsx("div", { className: "relative h-20 w-20 overflow-hidden rounded-md border", children: /* @__PURE__ */ jsx(
|
|
@@ -37,7 +37,7 @@ function BoardForm({ board, onClose, onSuccess }) {
|
|
|
37
37
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
38
38
|
}
|
|
39
39
|
};
|
|
40
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4 overflow-x-hidden", children: [
|
|
41
41
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
42
42
|
/* @__PURE__ */ jsxRuntime.jsx(label.Label, { htmlFor: "name", children: "Name *" }),
|
|
43
43
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -35,7 +35,7 @@ function BoardForm({ board, onClose, onSuccess }) {
|
|
|
35
35
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
38
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4 overflow-x-hidden", children: [
|
|
39
39
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
40
40
|
/* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "Name *" }),
|
|
41
41
|
/* @__PURE__ */ jsx(
|
|
@@ -10,6 +10,7 @@ const label = require('../../../../../../../ui/src/components/label.cjs');
|
|
|
10
10
|
const select = require('../../../../../../../ui/src/components/select.cjs');
|
|
11
11
|
const minimalTiptap = require('../../../../../../../ui/src/components/minimal-tiptap/minimal-tiptap.cjs');
|
|
12
12
|
const searchSelect = require('../../../../../../../ui/src/components/search-select.cjs');
|
|
13
|
+
const context = require('@btst/stack/context');
|
|
13
14
|
const kanbanHooks = require('../../hooks/kanban-hooks.cjs');
|
|
14
15
|
const utils = require('../../../utils.cjs');
|
|
15
16
|
|
|
@@ -24,6 +25,7 @@ function TaskForm({
|
|
|
24
25
|
onDelete
|
|
25
26
|
}) {
|
|
26
27
|
const isEditing = !!taskId;
|
|
28
|
+
const { uploadImage, imagePicker: imagePickerTrigger } = context.usePluginOverrides("kanban");
|
|
27
29
|
const {
|
|
28
30
|
createTask,
|
|
29
31
|
updateTask,
|
|
@@ -101,7 +103,7 @@ function TaskForm({
|
|
|
101
103
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
102
104
|
}
|
|
103
105
|
};
|
|
104
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
106
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-4 overflow-x-hidden", children: [
|
|
105
107
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
106
108
|
/* @__PURE__ */ jsxRuntime.jsx(label.Label, { htmlFor: "title", children: "Title *" }),
|
|
107
109
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -160,7 +162,9 @@ function TaskForm({
|
|
|
160
162
|
onChange: (value) => setDescription(typeof value === "string" ? value : ""),
|
|
161
163
|
output: "markdown",
|
|
162
164
|
placeholder: "Describe the task...",
|
|
163
|
-
className: "min-h-[150px]"
|
|
165
|
+
className: "min-h-[150px]",
|
|
166
|
+
uploader: uploadImage,
|
|
167
|
+
imagePickerTrigger
|
|
164
168
|
}
|
|
165
169
|
)
|
|
166
170
|
] }),
|
|
@@ -8,6 +8,7 @@ import { Label } from '../../../../../../../ui/src/components/label.mjs';
|
|
|
8
8
|
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../../../../../../ui/src/components/select.mjs';
|
|
9
9
|
import { MinimalTiptapEditor } from '../../../../../../../ui/src/components/minimal-tiptap/minimal-tiptap.mjs';
|
|
10
10
|
import SearchSelect from '../../../../../../../ui/src/components/search-select.mjs';
|
|
11
|
+
import { usePluginOverrides } from '@btst/stack/context';
|
|
11
12
|
import { useTaskMutations, useSearchUsers } from '../../hooks/kanban-hooks.mjs';
|
|
12
13
|
import { PRIORITY_OPTIONS } from '../../../utils.mjs';
|
|
13
14
|
|
|
@@ -22,6 +23,7 @@ function TaskForm({
|
|
|
22
23
|
onDelete
|
|
23
24
|
}) {
|
|
24
25
|
const isEditing = !!taskId;
|
|
26
|
+
const { uploadImage, imagePicker: imagePickerTrigger } = usePluginOverrides("kanban");
|
|
25
27
|
const {
|
|
26
28
|
createTask,
|
|
27
29
|
updateTask,
|
|
@@ -99,7 +101,7 @@ function TaskForm({
|
|
|
99
101
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
100
102
|
}
|
|
101
103
|
};
|
|
102
|
-
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
104
|
+
return /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4 overflow-x-hidden", children: [
|
|
103
105
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
104
106
|
/* @__PURE__ */ jsx(Label, { htmlFor: "title", children: "Title *" }),
|
|
105
107
|
/* @__PURE__ */ jsx(
|
|
@@ -158,7 +160,9 @@ function TaskForm({
|
|
|
158
160
|
onChange: (value) => setDescription(typeof value === "string" ? value : ""),
|
|
159
161
|
output: "markdown",
|
|
160
162
|
placeholder: "Describe the task...",
|
|
161
|
-
className: "min-h-[150px]"
|
|
163
|
+
className: "min-h-[150px]",
|
|
164
|
+
uploader: uploadImage,
|
|
165
|
+
imagePickerTrigger
|
|
162
166
|
}
|
|
163
167
|
)
|
|
164
168
|
] }),
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const crypto = require('node:crypto');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceCompat(e) {
|
|
8
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
9
|
+
const n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
for (const k in e) {
|
|
12
|
+
n[k] = e[k];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
n.default = e;
|
|
16
|
+
return n;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const fs__namespace = /*#__PURE__*/_interopNamespaceCompat(fs);
|
|
20
|
+
const path__namespace = /*#__PURE__*/_interopNamespaceCompat(path);
|
|
21
|
+
const crypto__namespace = /*#__PURE__*/_interopNamespaceCompat(crypto);
|
|
22
|
+
|
|
23
|
+
function localAdapter(options = {}) {
|
|
24
|
+
const uploadDir = options.uploadDir ?? "./public/uploads";
|
|
25
|
+
const publicPath = options.publicPath ?? "/uploads";
|
|
26
|
+
return {
|
|
27
|
+
type: "local",
|
|
28
|
+
async upload(buffer, { filename }) {
|
|
29
|
+
await fs__namespace.mkdir(uploadDir, { recursive: true });
|
|
30
|
+
const ext = path__namespace.extname(filename);
|
|
31
|
+
const base = path__namespace.basename(filename, ext);
|
|
32
|
+
const unique = crypto__namespace.randomBytes(8).toString("hex");
|
|
33
|
+
const storedFilename = `${base}-${unique}${ext}`;
|
|
34
|
+
const filePath = path__namespace.join(uploadDir, storedFilename);
|
|
35
|
+
await fs__namespace.writeFile(filePath, buffer);
|
|
36
|
+
const url = `${publicPath.replace(/\/$/, "")}/${encodeURIComponent(storedFilename)}`;
|
|
37
|
+
return { url };
|
|
38
|
+
},
|
|
39
|
+
async delete(url) {
|
|
40
|
+
const encodedFilename = url.split("/").pop();
|
|
41
|
+
if (!encodedFilename) return;
|
|
42
|
+
const filename = decodeURIComponent(encodedFilename);
|
|
43
|
+
const filePath = path__namespace.join(uploadDir, filename);
|
|
44
|
+
try {
|
|
45
|
+
await fs__namespace.unlink(filePath);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
if (err.code !== "ENOENT") {
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exports.localAdapter = localAdapter;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as crypto from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
function localAdapter(options = {}) {
|
|
6
|
+
const uploadDir = options.uploadDir ?? "./public/uploads";
|
|
7
|
+
const publicPath = options.publicPath ?? "/uploads";
|
|
8
|
+
return {
|
|
9
|
+
type: "local",
|
|
10
|
+
async upload(buffer, { filename }) {
|
|
11
|
+
await fs.mkdir(uploadDir, { recursive: true });
|
|
12
|
+
const ext = path.extname(filename);
|
|
13
|
+
const base = path.basename(filename, ext);
|
|
14
|
+
const unique = crypto.randomBytes(8).toString("hex");
|
|
15
|
+
const storedFilename = `${base}-${unique}${ext}`;
|
|
16
|
+
const filePath = path.join(uploadDir, storedFilename);
|
|
17
|
+
await fs.writeFile(filePath, buffer);
|
|
18
|
+
const url = `${publicPath.replace(/\/$/, "")}/${encodeURIComponent(storedFilename)}`;
|
|
19
|
+
return { url };
|
|
20
|
+
},
|
|
21
|
+
async delete(url) {
|
|
22
|
+
const encodedFilename = url.split("/").pop();
|
|
23
|
+
if (!encodedFilename) return;
|
|
24
|
+
const filename = decodeURIComponent(encodedFilename);
|
|
25
|
+
const filePath = path.join(uploadDir, filename);
|
|
26
|
+
try {
|
|
27
|
+
await fs.unlink(filePath);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
if (err.code !== "ENOENT") {
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { localAdapter };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function listAssets(adapter, params) {
|
|
4
|
+
const query = params ?? {};
|
|
5
|
+
const whereConditions = [];
|
|
6
|
+
if (query.folderId !== void 0) {
|
|
7
|
+
whereConditions.push({
|
|
8
|
+
field: "folderId",
|
|
9
|
+
value: query.folderId,
|
|
10
|
+
operator: "eq"
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
if (query.mimeType) {
|
|
14
|
+
whereConditions.push({
|
|
15
|
+
field: "mimeType",
|
|
16
|
+
value: query.mimeType,
|
|
17
|
+
operator: "eq"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const needsInMemoryFilter = !!query.query;
|
|
21
|
+
const dbWhere = whereConditions.length > 0 ? whereConditions : void 0;
|
|
22
|
+
const dbTotal = !needsInMemoryFilter ? await adapter.count({ model: "mediaAsset", where: dbWhere }) : void 0;
|
|
23
|
+
let assets = await adapter.findMany({
|
|
24
|
+
model: "mediaAsset",
|
|
25
|
+
limit: !needsInMemoryFilter ? query.limit : void 0,
|
|
26
|
+
offset: !needsInMemoryFilter ? query.offset : void 0,
|
|
27
|
+
where: dbWhere,
|
|
28
|
+
sortBy: { field: "createdAt", direction: "desc" }
|
|
29
|
+
});
|
|
30
|
+
if (query.query) {
|
|
31
|
+
const searchLower = query.query.toLowerCase();
|
|
32
|
+
assets = assets.filter(
|
|
33
|
+
(asset) => asset.filename.toLowerCase().includes(searchLower) || asset.originalName.toLowerCase().includes(searchLower) || asset.alt?.toLowerCase().includes(searchLower)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (needsInMemoryFilter) {
|
|
37
|
+
const total = assets.length;
|
|
38
|
+
const offset = query.offset ?? 0;
|
|
39
|
+
const limit = query.limit;
|
|
40
|
+
assets = assets.slice(
|
|
41
|
+
offset,
|
|
42
|
+
limit !== void 0 ? offset + limit : void 0
|
|
43
|
+
);
|
|
44
|
+
return { items: assets, total, limit: query.limit, offset: query.offset };
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
items: assets,
|
|
48
|
+
total: dbTotal ?? assets.length,
|
|
49
|
+
limit: query.limit,
|
|
50
|
+
offset: query.offset
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function getAssetById(adapter, id) {
|
|
54
|
+
return adapter.findOne({
|
|
55
|
+
model: "mediaAsset",
|
|
56
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async function listFolders(adapter, params) {
|
|
60
|
+
const where = params?.parentId !== void 0 ? [
|
|
61
|
+
{
|
|
62
|
+
field: "parentId",
|
|
63
|
+
value: params.parentId,
|
|
64
|
+
operator: "eq"
|
|
65
|
+
}
|
|
66
|
+
] : void 0;
|
|
67
|
+
return adapter.findMany({
|
|
68
|
+
model: "mediaFolder",
|
|
69
|
+
where,
|
|
70
|
+
sortBy: { field: "name", direction: "asc" }
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async function getFolderById(adapter, id) {
|
|
74
|
+
return adapter.findOne({
|
|
75
|
+
model: "mediaFolder",
|
|
76
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
exports.getAssetById = getAssetById;
|
|
81
|
+
exports.getFolderById = getFolderById;
|
|
82
|
+
exports.listAssets = listAssets;
|
|
83
|
+
exports.listFolders = listFolders;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
async function listAssets(adapter, params) {
|
|
2
|
+
const query = params ?? {};
|
|
3
|
+
const whereConditions = [];
|
|
4
|
+
if (query.folderId !== void 0) {
|
|
5
|
+
whereConditions.push({
|
|
6
|
+
field: "folderId",
|
|
7
|
+
value: query.folderId,
|
|
8
|
+
operator: "eq"
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
if (query.mimeType) {
|
|
12
|
+
whereConditions.push({
|
|
13
|
+
field: "mimeType",
|
|
14
|
+
value: query.mimeType,
|
|
15
|
+
operator: "eq"
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const needsInMemoryFilter = !!query.query;
|
|
19
|
+
const dbWhere = whereConditions.length > 0 ? whereConditions : void 0;
|
|
20
|
+
const dbTotal = !needsInMemoryFilter ? await adapter.count({ model: "mediaAsset", where: dbWhere }) : void 0;
|
|
21
|
+
let assets = await adapter.findMany({
|
|
22
|
+
model: "mediaAsset",
|
|
23
|
+
limit: !needsInMemoryFilter ? query.limit : void 0,
|
|
24
|
+
offset: !needsInMemoryFilter ? query.offset : void 0,
|
|
25
|
+
where: dbWhere,
|
|
26
|
+
sortBy: { field: "createdAt", direction: "desc" }
|
|
27
|
+
});
|
|
28
|
+
if (query.query) {
|
|
29
|
+
const searchLower = query.query.toLowerCase();
|
|
30
|
+
assets = assets.filter(
|
|
31
|
+
(asset) => asset.filename.toLowerCase().includes(searchLower) || asset.originalName.toLowerCase().includes(searchLower) || asset.alt?.toLowerCase().includes(searchLower)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (needsInMemoryFilter) {
|
|
35
|
+
const total = assets.length;
|
|
36
|
+
const offset = query.offset ?? 0;
|
|
37
|
+
const limit = query.limit;
|
|
38
|
+
assets = assets.slice(
|
|
39
|
+
offset,
|
|
40
|
+
limit !== void 0 ? offset + limit : void 0
|
|
41
|
+
);
|
|
42
|
+
return { items: assets, total, limit: query.limit, offset: query.offset };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
items: assets,
|
|
46
|
+
total: dbTotal ?? assets.length,
|
|
47
|
+
limit: query.limit,
|
|
48
|
+
offset: query.offset
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function getAssetById(adapter, id) {
|
|
52
|
+
return adapter.findOne({
|
|
53
|
+
model: "mediaAsset",
|
|
54
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function listFolders(adapter, params) {
|
|
58
|
+
const where = params?.parentId !== void 0 ? [
|
|
59
|
+
{
|
|
60
|
+
field: "parentId",
|
|
61
|
+
value: params.parentId,
|
|
62
|
+
operator: "eq"
|
|
63
|
+
}
|
|
64
|
+
] : void 0;
|
|
65
|
+
return adapter.findMany({
|
|
66
|
+
model: "mediaFolder",
|
|
67
|
+
where,
|
|
68
|
+
sortBy: { field: "name", direction: "asc" }
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async function getFolderById(adapter, id) {
|
|
72
|
+
return adapter.findOne({
|
|
73
|
+
model: "mediaFolder",
|
|
74
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { getAssetById, getFolderById, listAssets, listFolders };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
async function createAsset(adapter, input) {
|
|
4
|
+
return adapter.create({
|
|
5
|
+
model: "mediaAsset",
|
|
6
|
+
data: {
|
|
7
|
+
filename: input.filename,
|
|
8
|
+
originalName: input.originalName,
|
|
9
|
+
mimeType: input.mimeType,
|
|
10
|
+
size: input.size,
|
|
11
|
+
url: input.url,
|
|
12
|
+
folderId: input.folderId,
|
|
13
|
+
alt: input.alt,
|
|
14
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
async function updateAsset(adapter, id, input) {
|
|
19
|
+
const update = {};
|
|
20
|
+
if (input.alt !== void 0) {
|
|
21
|
+
update.alt = input.alt;
|
|
22
|
+
}
|
|
23
|
+
if ("folderId" in input) {
|
|
24
|
+
update.folderId = input.folderId;
|
|
25
|
+
}
|
|
26
|
+
return adapter.update({
|
|
27
|
+
model: "mediaAsset",
|
|
28
|
+
where: [{ field: "id", value: id, operator: "eq" }],
|
|
29
|
+
update
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async function deleteAsset(adapter, id) {
|
|
33
|
+
await adapter.delete({
|
|
34
|
+
model: "mediaAsset",
|
|
35
|
+
where: [{ field: "id", value: id, operator: "eq" }]
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async function createFolder(adapter, input) {
|
|
39
|
+
return adapter.create({
|
|
40
|
+
model: "mediaFolder",
|
|
41
|
+
data: {
|
|
42
|
+
name: input.name,
|
|
43
|
+
parentId: input.parentId,
|
|
44
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function deleteFolder(adapter, id) {
|
|
49
|
+
const allFolderIds = [id];
|
|
50
|
+
const queue = [id];
|
|
51
|
+
while (queue.length > 0) {
|
|
52
|
+
const parentId = queue.shift();
|
|
53
|
+
const children = await adapter.findMany({
|
|
54
|
+
model: "mediaFolder",
|
|
55
|
+
where: [{ field: "parentId", value: parentId, operator: "eq" }]
|
|
56
|
+
});
|
|
57
|
+
for (const child of children) {
|
|
58
|
+
allFolderIds.push(child.id);
|
|
59
|
+
queue.push(child.id);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let totalAssets = 0;
|
|
63
|
+
for (const folderId of allFolderIds) {
|
|
64
|
+
totalAssets += await adapter.count({
|
|
65
|
+
model: "mediaAsset",
|
|
66
|
+
where: [{ field: "folderId", value: folderId, operator: "eq" }]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (totalAssets > 0) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Cannot delete folder: it or one of its subfolders contains ${totalAssets} asset(s). Move or delete them first.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
await adapter.transaction(async (tx) => {
|
|
75
|
+
for (const folderId of [...allFolderIds].reverse()) {
|
|
76
|
+
await tx.delete({
|
|
77
|
+
model: "mediaFolder",
|
|
78
|
+
where: [{ field: "id", value: folderId, operator: "eq" }]
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.createAsset = createAsset;
|
|
85
|
+
exports.createFolder = createFolder;
|
|
86
|
+
exports.deleteAsset = deleteAsset;
|
|
87
|
+
exports.deleteFolder = deleteFolder;
|
|
88
|
+
exports.updateAsset = updateAsset;
|