@ittinc/strapi-plugin-kanban-board 0.0.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +74 -0
  3. package/admin/custom.d.ts +2 -0
  4. package/admin/src/components/Initializer.tsx +19 -0
  5. package/admin/src/components/KanbanInput/index.tsx +605 -0
  6. package/admin/src/components/PluginIcon.tsx +5 -0
  7. package/admin/src/index.ts +136 -0
  8. package/admin/src/pages/App.tsx +15 -0
  9. package/admin/src/pages/HomePage.tsx +398 -0
  10. package/admin/src/pluginId.ts +1 -0
  11. package/admin/src/translations/en.json +5 -0
  12. package/admin/src/translations/ru.json +5 -0
  13. package/admin/src/utils/getTranslation.ts +5 -0
  14. package/admin/tsconfig.build.json +10 -0
  15. package/admin/tsconfig.json +8 -0
  16. package/dist/_chunks/App-BEiW65up.js +343 -0
  17. package/dist/_chunks/App-DXTlN9Fm.mjs +341 -0
  18. package/dist/_chunks/en-C0sbENwZ.js +8 -0
  19. package/dist/_chunks/en-CHHvJuav.mjs +8 -0
  20. package/dist/_chunks/index-9nQMm6ez.js +465 -0
  21. package/dist/_chunks/index-DI_QN_uF.mjs +463 -0
  22. package/dist/_chunks/ru-B7uE6tx_.mjs +8 -0
  23. package/dist/_chunks/ru-Bl2jLOwG.js +8 -0
  24. package/dist/admin/index.js +156 -0
  25. package/dist/admin/index.mjs +157 -0
  26. package/dist/admin/src/components/Initializer.d.ts +5 -0
  27. package/dist/admin/src/components/KanbanInput/index.d.ts +33 -0
  28. package/dist/admin/src/components/PluginIcon.d.ts +2 -0
  29. package/dist/admin/src/index.d.ts +10 -0
  30. package/dist/admin/src/pages/App.d.ts +2 -0
  31. package/dist/admin/src/pages/HomePage.d.ts +2 -0
  32. package/dist/admin/src/pluginId.d.ts +1 -0
  33. package/dist/admin/src/utils/getTranslation.d.ts +2 -0
  34. package/dist/server/index.js +73 -0
  35. package/dist/server/index.mjs +74 -0
  36. package/dist/server/src/bootstrap.d.ts +5 -0
  37. package/dist/server/src/config/index.d.ts +5 -0
  38. package/dist/server/src/content-types/index.d.ts +2 -0
  39. package/dist/server/src/controllers/controller.d.ts +7 -0
  40. package/dist/server/src/controllers/index.d.ts +2 -0
  41. package/dist/server/src/destroy.d.ts +5 -0
  42. package/dist/server/src/index.d.ts +2 -0
  43. package/dist/server/src/middlewares/index.d.ts +2 -0
  44. package/dist/server/src/policies/index.d.ts +2 -0
  45. package/dist/server/src/register.d.ts +5 -0
  46. package/dist/server/src/routes/admin/index.d.ts +5 -0
  47. package/dist/server/src/routes/content-api/index.d.ts +12 -0
  48. package/dist/server/src/routes/index.d.ts +18 -0
  49. package/dist/server/src/services/index.d.ts +2 -0
  50. package/dist/server/src/services/service.d.ts +7 -0
  51. package/package.json +101 -0
  52. package/server/src/bootstrap.ts +7 -0
  53. package/server/src/config/index.ts +4 -0
  54. package/server/src/content-types/index.ts +1 -0
  55. package/server/src/controllers/controller.ts +13 -0
  56. package/server/src/controllers/index.ts +5 -0
  57. package/server/src/destroy.ts +7 -0
  58. package/server/src/index.ts +30 -0
  59. package/server/src/middlewares/index.ts +1 -0
  60. package/server/src/policies/index.ts +1 -0
  61. package/server/src/register.ts +13 -0
  62. package/server/src/routes/admin/index.ts +4 -0
  63. package/server/src/routes/content-api/index.ts +14 -0
  64. package/server/src/routes/index.ts +9 -0
  65. package/server/src/services/index.ts +5 -0
  66. package/server/src/services/service.ts +9 -0
  67. package/server/tsconfig.build.json +10 -0
  68. package/server/tsconfig.json +8 -0
@@ -0,0 +1,463 @@
1
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
+ import { useState, useMemo, useEffect } from "react";
3
+ import { Box, Flex, Typography, Button, Modal, TextInput } from "@strapi/design-system";
4
+ import { Plus, Pencil, Trash } from "@strapi/icons";
5
+ import styled from "styled-components";
6
+ import { useIntl } from "react-intl";
7
+ const DEFAULT_ITEM_SCHEMA = [
8
+ { name: "title", label: "Title", type: "text", required: true },
9
+ { name: "subtitle", label: "Subtitle", type: "text" }
10
+ ];
11
+ const DEFAULT_COLUMNS = ["To Do", "In Progress", "Done"];
12
+ const BoardContainer = styled(Flex)`
13
+ gap: 16px;
14
+ padding: 16px;
15
+ align-items: flex-start;
16
+ overflow-x: auto;
17
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
18
+ border-radius: ${({ theme }) => theme.borderRadius};
19
+ background: ${({ theme }) => theme.colors.neutral100};
20
+ `;
21
+ const ColumnContainer = styled(Box)`
22
+ background: ${({ theme }) => theme.colors.neutral0};
23
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
24
+ border-radius: ${({ theme }) => theme.borderRadius};
25
+ min-width: 280px;
26
+ width: 280px;
27
+ display: flex;
28
+ flex-direction: column;
29
+ flex-shrink: 0;
30
+ `;
31
+ const ColumnHeader = styled(Box)`
32
+ padding: 12px;
33
+ border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};
34
+ background: ${({ theme }) => theme.colors.neutral100};
35
+ border-top-left-radius: ${({ theme }) => theme.borderRadius};
36
+ border-top-right-radius: ${({ theme }) => theme.borderRadius};
37
+ `;
38
+ const ItemList = styled(Box)`
39
+ padding: 12px;
40
+ flex-grow: 1;
41
+ min-height: 80px;
42
+ background: ${({ theme, $isDraggingOver }) => $isDraggingOver ? theme.colors.primary100 : "transparent"};
43
+ transition: background 0.2s;
44
+ `;
45
+ const CardItem = styled(Box)`
46
+ background: ${({ theme }) => theme.colors.neutral0};
47
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
48
+ border-radius: ${({ theme }) => theme.borderRadius};
49
+ padding: 8px;
50
+ margin-bottom: 8px;
51
+ box-shadow: ${({ theme }) => theme.shadows.filterShadow};
52
+ cursor: grab;
53
+ opacity: ${({ $isDragging }) => $isDragging ? 0.5 : 1};
54
+
55
+ &:hover {
56
+ border-color: ${({ theme }) => theme.colors.primary500};
57
+ }
58
+ `;
59
+ const AddItemButton = styled.button`
60
+ background: none;
61
+ border: none;
62
+ color: ${({ theme }) => theme.colors.primary600};
63
+ cursor: pointer;
64
+ padding: 8px;
65
+ width: 100%;
66
+ text-align: left;
67
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
68
+
69
+ &:hover {
70
+ text-decoration: underline;
71
+ background: ${({ theme }) => theme.colors.neutral100};
72
+ }
73
+ `;
74
+ const KanbanInput = ({ name, value, onChange, intlLabel, attribute }) => {
75
+ const { formatMessage } = useIntl();
76
+ const [columns, setColumns] = useState([]);
77
+ const [draggedItem, setDraggedItem] = useState(null);
78
+ const [dragOverColId, setDragOverColId] = useState(null);
79
+ const options = attribute?.options || {};
80
+ const canAddColumns = options.canAddColumns !== false;
81
+ const canDeleteColumns = options.canDeleteColumns !== false;
82
+ const canRenameColumns = options.canRenameColumns !== false;
83
+ const itemSchema = useMemo(() => {
84
+ let schema = options.itemSchema;
85
+ if (!schema) return DEFAULT_ITEM_SCHEMA;
86
+ if (typeof schema === "string") {
87
+ try {
88
+ schema = JSON.parse(schema);
89
+ } catch (e) {
90
+ console.error("KanbanBoard: Invalid Item Schema JSON string", e);
91
+ return DEFAULT_ITEM_SCHEMA;
92
+ }
93
+ }
94
+ return Array.isArray(schema) ? schema : DEFAULT_ITEM_SCHEMA;
95
+ }, [options.itemSchema]);
96
+ const defaultColumnsList = useMemo(() => {
97
+ let cols = options.defaultColumns;
98
+ if (!cols) return DEFAULT_COLUMNS;
99
+ if (typeof cols === "string") {
100
+ try {
101
+ cols = JSON.parse(cols);
102
+ } catch (e) {
103
+ console.error("KanbanBoard: Invalid Default Columns JSON string", e);
104
+ return DEFAULT_COLUMNS;
105
+ }
106
+ }
107
+ return Array.isArray(cols) ? cols : DEFAULT_COLUMNS;
108
+ }, [options.defaultColumns]);
109
+ const [isModalOpen, setIsModalOpen] = useState(false);
110
+ const [activeColId, setActiveColId] = useState(null);
111
+ const [newItemData, setNewItemData] = useState({});
112
+ const [editingItemId, setEditingItemId] = useState(null);
113
+ const [isColumnModalOpen, setIsColumnModalOpen] = useState(false);
114
+ const [columnFormData, setColumnFormData] = useState({ id: "", title: "", code: "" });
115
+ const [editingColumnId, setEditingColumnId] = useState(null);
116
+ useEffect(() => {
117
+ if (value) {
118
+ try {
119
+ const parsed = typeof value === "string" ? JSON.parse(value) : value;
120
+ if (Array.isArray(parsed) && parsed.length > 0) {
121
+ setColumns(parsed);
122
+ return;
123
+ }
124
+ } catch (e) {
125
+ console.error("Failed to parse Kanban data", e);
126
+ }
127
+ }
128
+ if (Array.isArray(defaultColumnsList)) {
129
+ const initCols = defaultColumnsList.map((col, index) => {
130
+ if (typeof col === "object" && col !== null && col.title) {
131
+ return {
132
+ id: col.id || `col-${index}-${Date.now()}`,
133
+ title: col.title,
134
+ code: col.code || "",
135
+ items: []
136
+ };
137
+ }
138
+ return {
139
+ id: `col-${index}-${Date.now()}`,
140
+ title: String(col),
141
+ items: []
142
+ };
143
+ });
144
+ setColumns(initCols);
145
+ }
146
+ }, [value, defaultColumnsList]);
147
+ const updateState = (newCols) => {
148
+ setColumns(newCols);
149
+ onChange({
150
+ target: {
151
+ name,
152
+ value: newCols,
153
+ type: "json"
154
+ }
155
+ });
156
+ };
157
+ const handleDragStart = (e, itemId, sourceColId) => {
158
+ e.stopPropagation();
159
+ setDraggedItem({ itemId, sourceColId });
160
+ e.dataTransfer.effectAllowed = "move";
161
+ };
162
+ const handleDragOver = (e, colId) => {
163
+ e.preventDefault();
164
+ e.stopPropagation();
165
+ e.dataTransfer.dropEffect = "move";
166
+ setDragOverColId(colId);
167
+ };
168
+ const handleDrop = (e, targetColId, targetItemId) => {
169
+ e.preventDefault();
170
+ e.stopPropagation();
171
+ setDragOverColId(null);
172
+ if (!draggedItem) return;
173
+ const { itemId: sourceItemId, sourceColId } = draggedItem;
174
+ const newColumns = JSON.parse(JSON.stringify(columns));
175
+ const sourceCol = newColumns.find((c) => c.id === sourceColId);
176
+ const targetCol = newColumns.find((c) => c.id === targetColId);
177
+ if (!sourceCol || !targetCol) return;
178
+ const itemIndex = sourceCol.items.findIndex((i) => i.id === sourceItemId);
179
+ if (itemIndex === -1) return;
180
+ const [itemToMove] = sourceCol.items.splice(itemIndex, 1);
181
+ if (!targetItemId) {
182
+ targetCol.items.push(itemToMove);
183
+ } else {
184
+ const targetItemIndex = targetCol.items.findIndex((i) => i.id === targetItemId);
185
+ if (targetItemIndex !== -1) {
186
+ targetCol.items.splice(targetItemIndex, 0, itemToMove);
187
+ } else {
188
+ targetCol.items.push(itemToMove);
189
+ }
190
+ }
191
+ updateState(newColumns);
192
+ setDraggedItem(null);
193
+ };
194
+ const handleOpenAddItem = (colId) => {
195
+ setActiveColId(colId);
196
+ setEditingItemId(null);
197
+ setNewItemData({});
198
+ setIsModalOpen(true);
199
+ };
200
+ const handleOpenEditItem = (colId, item) => {
201
+ setActiveColId(colId);
202
+ setEditingItemId(item.id);
203
+ setNewItemData({ ...item });
204
+ setIsModalOpen(true);
205
+ };
206
+ const handleCloseModal = () => {
207
+ setIsModalOpen(false);
208
+ setActiveColId(null);
209
+ setEditingItemId(null);
210
+ };
211
+ const handleConfirmItemForm = () => {
212
+ if (!activeColId) return;
213
+ for (const field of itemSchema) {
214
+ if (field.required && !newItemData[field.name]) {
215
+ alert(`${field.label || field.name} is required`);
216
+ return;
217
+ }
218
+ }
219
+ if (editingItemId) {
220
+ const newColumns = columns.map((col) => {
221
+ if (col.id === activeColId) {
222
+ return {
223
+ ...col,
224
+ items: col.items.map(
225
+ (item) => item.id === editingItemId ? { ...newItemData, id: editingItemId } : item
226
+ )
227
+ };
228
+ }
229
+ return col;
230
+ });
231
+ updateState(newColumns);
232
+ } else {
233
+ const newItem = {
234
+ id: Date.now().toString(),
235
+ ...newItemData
236
+ };
237
+ const newColumns = columns.map((col) => {
238
+ if (col.id === activeColId) {
239
+ return { ...col, items: [...col.items, newItem] };
240
+ }
241
+ return col;
242
+ });
243
+ updateState(newColumns);
244
+ }
245
+ handleCloseModal();
246
+ };
247
+ const handleDeleteItem = (colId, itemId) => {
248
+ if (confirm("Are you sure you want to delete this item?")) {
249
+ const newColumns = columns.map((col) => {
250
+ if (col.id === colId) {
251
+ return {
252
+ ...col,
253
+ items: col.items.filter((item) => item.id !== itemId)
254
+ };
255
+ }
256
+ return col;
257
+ });
258
+ updateState(newColumns);
259
+ }
260
+ };
261
+ const handleOpenAddColumn = () => {
262
+ if (!canAddColumns) return;
263
+ setEditingColumnId(null);
264
+ setColumnFormData({ id: "", title: "", code: "" });
265
+ setIsColumnModalOpen(true);
266
+ };
267
+ const handleOpenEditColumn = (col) => {
268
+ if (!canRenameColumns) return;
269
+ setEditingColumnId(col.id);
270
+ setColumnFormData({ id: col.id, title: col.title, code: col.code || "" });
271
+ setIsColumnModalOpen(true);
272
+ };
273
+ const handleCloseColumnModal = () => {
274
+ setIsColumnModalOpen(false);
275
+ setColumnFormData({ id: "", title: "", code: "" });
276
+ setEditingColumnId(null);
277
+ };
278
+ const handleConfirmColumnForm = () => {
279
+ if (!columnFormData.title) {
280
+ alert("Column Title is required");
281
+ return;
282
+ }
283
+ if (editingColumnId) {
284
+ const newColumns = columns.map(
285
+ (col) => col.id === editingColumnId ? { ...col, title: columnFormData.title, code: columnFormData.code } : col
286
+ );
287
+ updateState(newColumns);
288
+ } else {
289
+ const newCol = {
290
+ id: `col-${Date.now()}`,
291
+ title: columnFormData.title,
292
+ code: columnFormData.code,
293
+ items: []
294
+ };
295
+ updateState([...columns, newCol]);
296
+ }
297
+ handleCloseColumnModal();
298
+ };
299
+ const handleDeleteColumn = (colId) => {
300
+ if (!canDeleteColumns) return;
301
+ if (confirm("Are you sure you want to delete this column? All items in it will be lost.")) {
302
+ const newColumns = columns.filter((col) => col.id !== colId);
303
+ updateState(newColumns);
304
+ }
305
+ };
306
+ let labelText = name;
307
+ if (intlLabel) {
308
+ if (typeof intlLabel === "string") labelText = intlLabel;
309
+ else if (intlLabel.id) {
310
+ try {
311
+ labelText = formatMessage(intlLabel);
312
+ } catch (e) {
313
+ labelText = intlLabel.defaultMessage || name;
314
+ }
315
+ }
316
+ }
317
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { children: [
318
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", paddingBottom: 2, children: [
319
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", fontWeight: "bold", children: labelText }),
320
+ canAddColumns && /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: handleOpenAddColumn, size: "S", variant: "secondary", children: "Add Column" })
321
+ ] }),
322
+ /* @__PURE__ */ jsx(BoardContainer, { children: columns.map((col) => /* @__PURE__ */ jsxs(ColumnContainer, { children: [
323
+ /* @__PURE__ */ jsx(ColumnHeader, { children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
324
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "flex-start", children: [
325
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", fontWeight: "bold", children: [
326
+ col.title,
327
+ " (",
328
+ col.items.length,
329
+ ")"
330
+ ] }),
331
+ col.code && /* @__PURE__ */ jsxs(Typography, { variant: "sigma", textColor: "neutral600", children: [
332
+ "Code: ",
333
+ col.code
334
+ ] })
335
+ ] }),
336
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, children: [
337
+ canRenameColumns && /* @__PURE__ */ jsx(
338
+ Button,
339
+ {
340
+ onClick: () => handleOpenEditColumn(col),
341
+ variant: "ghost",
342
+ size: "S",
343
+ startIcon: /* @__PURE__ */ jsx(Pencil, {})
344
+ }
345
+ ),
346
+ canDeleteColumns && /* @__PURE__ */ jsx(
347
+ Button,
348
+ {
349
+ onClick: () => handleDeleteColumn(col.id),
350
+ variant: "ghost",
351
+ size: "S",
352
+ startIcon: /* @__PURE__ */ jsx(Trash, {})
353
+ }
354
+ )
355
+ ] })
356
+ ] }) }),
357
+ /* @__PURE__ */ jsx(
358
+ ItemList,
359
+ {
360
+ $isDraggingOver: dragOverColId === col.id,
361
+ onDragOver: (e) => handleDragOver(e, col.id),
362
+ onDragLeave: () => setDragOverColId(null),
363
+ onDrop: (e) => handleDrop(e, col.id),
364
+ children: col.items.map((item) => /* @__PURE__ */ jsx(
365
+ CardItem,
366
+ {
367
+ draggable: true,
368
+ onDragStart: (e) => handleDragStart(e, item.id, col.id),
369
+ onDragOver: (e) => {
370
+ e.preventDefault();
371
+ e.stopPropagation();
372
+ e.dataTransfer.dropEffect = "move";
373
+ },
374
+ onDrop: (e) => handleDrop(e, col.id, item.id),
375
+ $isDragging: draggedItem?.itemId === item.id,
376
+ children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "start", children: [
377
+ /* @__PURE__ */ jsx(Box, { flexGrow: 1, children: itemSchema.map((field, idx) => {
378
+ const val = item[field.name];
379
+ if (!val) return null;
380
+ if (idx === 0) {
381
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", as: "div", children: val }, field.name);
382
+ }
383
+ return /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", as: "div", children: val }, field.name);
384
+ }) }),
385
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, style: { opacity: 0.5 }, children: [
386
+ /* @__PURE__ */ jsx(
387
+ Button,
388
+ {
389
+ onClick: () => handleOpenEditItem(col.id, item),
390
+ variant: "ghost",
391
+ size: "S",
392
+ startIcon: /* @__PURE__ */ jsx(Pencil, {})
393
+ }
394
+ ),
395
+ /* @__PURE__ */ jsx(
396
+ Button,
397
+ {
398
+ onClick: () => handleDeleteItem(col.id, item.id),
399
+ variant: "ghost",
400
+ size: "S",
401
+ startIcon: /* @__PURE__ */ jsx(Trash, {})
402
+ }
403
+ )
404
+ ] })
405
+ ] })
406
+ },
407
+ item.id
408
+ ))
409
+ }
410
+ ),
411
+ /* @__PURE__ */ jsx(AddItemButton, { type: "button", onClick: () => handleOpenAddItem(col.id), children: "+ Add Item" })
412
+ ] }, col.id)) }),
413
+ /* @__PURE__ */ jsx(Modal.Root, { open: isModalOpen, onOpenChange: setIsModalOpen, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
414
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: editingItemId ? "Edit Item" : "Add New Item" }) }),
415
+ /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsx(Flex, { direction: "column", gap: 4, children: itemSchema.map((field) => /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(
416
+ TextInput,
417
+ {
418
+ label: field.label || field.name,
419
+ placeholder: `Enter ${field.label || field.name}`,
420
+ value: newItemData[field.name] || "",
421
+ onChange: (e) => setNewItemData({ ...newItemData, [field.name]: e.target.value }),
422
+ required: field.required
423
+ }
424
+ ) }, field.name)) }) }),
425
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
426
+ /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: handleCloseModal, children: "Cancel" }) }),
427
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirmItemForm, children: editingItemId ? "Save" : "Add" })
428
+ ] })
429
+ ] }) }),
430
+ /* @__PURE__ */ jsx(Modal.Root, { open: isColumnModalOpen, onOpenChange: setIsColumnModalOpen, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
431
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: editingColumnId ? "Edit Column" : "Add New Column" }) }),
432
+ /* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 4, children: [
433
+ /* @__PURE__ */ jsx(
434
+ TextInput,
435
+ {
436
+ label: "Column Title",
437
+ placeholder: "Enter column title",
438
+ value: columnFormData.title,
439
+ onChange: (e) => setColumnFormData({ ...columnFormData, title: e.target.value }),
440
+ required: true
441
+ }
442
+ ),
443
+ /* @__PURE__ */ jsx(
444
+ TextInput,
445
+ {
446
+ label: "Column Code",
447
+ placeholder: "Enter column code (optional)",
448
+ hint: "A unique code for frontend logic (e.g. 'todo', 'done')",
449
+ value: columnFormData.code,
450
+ onChange: (e) => setColumnFormData({ ...columnFormData, code: e.target.value })
451
+ }
452
+ )
453
+ ] }) }),
454
+ /* @__PURE__ */ jsxs(Modal.Footer, { children: [
455
+ /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: handleCloseColumnModal, children: "Cancel" }) }),
456
+ /* @__PURE__ */ jsx(Button, { onClick: handleConfirmColumnForm, children: editingColumnId ? "Save" : "Add" })
457
+ ] })
458
+ ] }) })
459
+ ] }) });
460
+ };
461
+ export {
462
+ KanbanInput
463
+ };
@@ -0,0 +1,8 @@
1
+ const ru = {
2
+ "kanban-plugin.kanban-board.label": "Канбан-доска",
3
+ "kanban-plugin.kanban-board.description": "Перетаскивайте карточки между колонками",
4
+ "kanban-plugin.plugin.name": "Канбан-доска"
5
+ };
6
+ export {
7
+ ru as default
8
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const ru = {
4
+ "kanban-plugin.kanban-board.label": "Канбан-доска",
5
+ "kanban-plugin.kanban-board.description": "Перетаскивайте карточки между колонками",
6
+ "kanban-plugin.plugin.name": "Канбан-доска"
7
+ };
8
+ exports.default = ru;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ const react = require("react");
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const icons = require("@strapi/icons");
5
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
6
+ const v = glob[path];
7
+ if (v) {
8
+ return typeof v === "function" ? v() : Promise.resolve(v);
9
+ }
10
+ return new Promise((_, reject) => {
11
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
12
+ reject.bind(
13
+ null,
14
+ new Error(
15
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
16
+ )
17
+ )
18
+ );
19
+ });
20
+ };
21
+ const PLUGIN_ID = "kanban-board";
22
+ const Initializer = ({ setPlugin }) => {
23
+ const ref = react.useRef(setPlugin);
24
+ react.useEffect(() => {
25
+ ref.current(PLUGIN_ID);
26
+ }, []);
27
+ return null;
28
+ };
29
+ const PluginIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.PuzzlePiece, {});
30
+ const index = {
31
+ register(app) {
32
+ app.addMenuLink({
33
+ to: `plugins/${PLUGIN_ID}`,
34
+ icon: PluginIcon,
35
+ intlLabel: {
36
+ id: `${PLUGIN_ID}.plugin.name`,
37
+ defaultMessage: PLUGIN_ID
38
+ },
39
+ Component: async () => {
40
+ const { App } = await Promise.resolve().then(() => require("../_chunks/App-BEiW65up.js"));
41
+ return App;
42
+ }
43
+ });
44
+ app.registerPlugin({
45
+ id: PLUGIN_ID,
46
+ initializer: Initializer,
47
+ isReady: false,
48
+ name: PLUGIN_ID
49
+ });
50
+ app.customFields.register({
51
+ name: "kanban-board",
52
+ pluginId: PLUGIN_ID,
53
+ type: "json",
54
+ intlLabel: {
55
+ id: `${PLUGIN_ID}.kanban-board.label`,
56
+ defaultMessage: "Kanban Board"
57
+ },
58
+ intlDescription: {
59
+ id: `${PLUGIN_ID}.kanban-board.description`,
60
+ defaultMessage: "Drag and drop items between columns"
61
+ },
62
+ icon: PluginIcon,
63
+ components: {
64
+ Input: async () => Promise.resolve().then(() => require("../_chunks/index-9nQMm6ez.js")).then((module2) => ({
65
+ default: module2.KanbanInput
66
+ }))
67
+ },
68
+ options: {
69
+ base: [
70
+ {
71
+ sectionTitle: {
72
+ id: "kanban-plugin.section.settings",
73
+ defaultMessage: "Board Configuration"
74
+ },
75
+ items: [
76
+ {
77
+ intlLabel: {
78
+ id: "kanban-plugin.options.defaultColumns",
79
+ defaultMessage: "Default Columns (JSON Array)"
80
+ },
81
+ intlDescription: {
82
+ id: "kanban-plugin.options.defaultColumns.desc",
83
+ defaultMessage: 'e.g. ["To Do", "In Progress", "Done"]'
84
+ },
85
+ name: "options.defaultColumns",
86
+ type: "text",
87
+ defaultValue: '[{"code": "todo", "name": "To Do"}, {"code": "in-progress", "name": "In Progress"}, {"code": "done", "name": "Done"}]'
88
+ },
89
+ {
90
+ intlLabel: {
91
+ id: "kanban-plugin.options.itemSchema",
92
+ defaultMessage: "Item Schema (JSON Array)"
93
+ },
94
+ intlDescription: {
95
+ id: "kanban-plugin.options.itemSchema.desc",
96
+ defaultMessage: 'Define fields: [{"name": "title", "label": "Title", "type": "text", "required": true}]'
97
+ },
98
+ name: "options.itemSchema",
99
+ type: "text",
100
+ defaultValue: '[{"name": "title", "label": "Title", "type": "text", "required": true}, {"name": "subtitle", "label": "Subtitle", "type": "text"}]'
101
+ }
102
+ ]
103
+ },
104
+ {
105
+ sectionTitle: {
106
+ id: "kanban-plugin.section.permissions",
107
+ defaultMessage: "Permissions"
108
+ },
109
+ items: [
110
+ {
111
+ intlLabel: {
112
+ id: "kanban-plugin.options.canAddColumns",
113
+ defaultMessage: "Allow Adding Columns"
114
+ },
115
+ name: "options.canAddColumns",
116
+ type: "checkbox",
117
+ defaultValue: true
118
+ },
119
+ {
120
+ intlLabel: {
121
+ id: "kanban-plugin.options.canDeleteColumns",
122
+ defaultMessage: "Allow Deleting Columns"
123
+ },
124
+ name: "options.canDeleteColumns",
125
+ type: "checkbox",
126
+ defaultValue: true
127
+ },
128
+ {
129
+ intlLabel: {
130
+ id: "kanban-plugin.options.canRenameColumns",
131
+ defaultMessage: "Allow Renaming Columns"
132
+ },
133
+ name: "options.canRenameColumns",
134
+ type: "checkbox",
135
+ defaultValue: true
136
+ }
137
+ ]
138
+ }
139
+ ]
140
+ }
141
+ });
142
+ },
143
+ async registerTrads({ locales }) {
144
+ return Promise.all(
145
+ locales.map(async (locale) => {
146
+ try {
147
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("../_chunks/en-C0sbENwZ.js")), "./translations/ru.json": () => Promise.resolve().then(() => require("../_chunks/ru-Bl2jLOwG.js")) }), `./translations/${locale}.json`, 3);
148
+ return { data, locale };
149
+ } catch {
150
+ return { data: {}, locale };
151
+ }
152
+ })
153
+ );
154
+ }
155
+ };
156
+ module.exports = index;