@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,343 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const admin = require("@strapi/strapi/admin");
5
+ const reactRouterDom = require("react-router-dom");
6
+ const react = require("react");
7
+ const designSystem = require("@strapi/design-system");
8
+ const icons = require("@strapi/icons");
9
+ const styled = require("styled-components");
10
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
12
+ const initialData = [
13
+ {
14
+ id: "col-1",
15
+ title: "To Do",
16
+ items: [
17
+ { id: "1", title: "Research Plugins", subtitle: "Read Strapi v5 docs" },
18
+ { id: "2", title: "Setup Environment", subtitle: "Install dependencies" }
19
+ ]
20
+ },
21
+ {
22
+ id: "col-2",
23
+ title: "In Progress",
24
+ items: [
25
+ { id: "3", title: "Develop Feature", subtitle: "Implement Drag & Drop" }
26
+ ]
27
+ },
28
+ {
29
+ id: "col-3",
30
+ title: "Done",
31
+ items: [
32
+ { id: "4", title: "Initialize Project", subtitle: "Run CLI commands" }
33
+ ]
34
+ }
35
+ ];
36
+ const BoardContainer = styled__default.default(designSystem.Flex)`
37
+ gap: 16px;
38
+ padding: 24px;
39
+ height: 100%;
40
+ align-items: flex-start;
41
+ overflow-x: auto;
42
+ `;
43
+ const ColumnContainer = styled__default.default(designSystem.Box)`
44
+ background: ${({ theme }) => theme.colors.neutral100};
45
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
46
+ border-radius: ${({ theme }) => theme.borderRadius};
47
+ min-width: 300px;
48
+ width: 300px;
49
+ display: flex;
50
+ flex-direction: column;
51
+ max-height: 100%;
52
+ flex-shrink: 0;
53
+ `;
54
+ const ColumnHeader = styled__default.default(designSystem.Flex)`
55
+ padding: 16px;
56
+ border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};
57
+ background: ${({ theme }) => theme.colors.neutral150};
58
+ border-top-left-radius: ${({ theme }) => theme.borderRadius};
59
+ border-top-right-radius: ${({ theme }) => theme.borderRadius};
60
+ justify-content: space-between;
61
+ align-items: center;
62
+ gap: 8px;
63
+ `;
64
+ const ItemList = styled__default.default(designSystem.Box)`
65
+ padding: 16px;
66
+ flex-grow: 1;
67
+ min-height: 100px;
68
+ background: ${({ theme, $isDraggingOver }) => $isDraggingOver ? theme.colors.primary100 : "transparent"};
69
+ transition: background 0.2s;
70
+ overflow-y: auto;
71
+ `;
72
+ const CardItem = styled__default.default(designSystem.Box)`
73
+ background: ${({ theme }) => theme.colors.neutral0};
74
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
75
+ border-radius: ${({ theme }) => theme.borderRadius};
76
+ padding: 12px;
77
+ margin-bottom: 8px;
78
+ box-shadow: ${({ theme }) => theme.shadows.filterShadow};
79
+ cursor: grab;
80
+ opacity: ${({ $isDragging }) => $isDragging ? 0.5 : 1};
81
+
82
+ &:hover {
83
+ border-color: ${({ theme }) => theme.colors.primary500};
84
+ box-shadow: ${({ theme }) => theme.shadows.tableShadow};
85
+ }
86
+
87
+ &:active {
88
+ cursor: grabbing;
89
+ }
90
+ `;
91
+ const ScrollableMain = styled__default.default(designSystem.Main)`
92
+ height: 100vh;
93
+ overflow-y: hidden;
94
+ display: flex;
95
+ flex-direction: column;
96
+ `;
97
+ const HomePage = () => {
98
+ const [columns, setColumns] = react.useState(initialData);
99
+ const [draggedItem, setDraggedItem] = react.useState(null);
100
+ const [dragOverColId, setDragOverColId] = react.useState(null);
101
+ const [editingColId, setEditingColId] = react.useState(null);
102
+ const [tempColTitle, setTempColTitle] = react.useState("");
103
+ const [editingItemId, setEditingItemId] = react.useState(null);
104
+ const [tempItemTitle, setTempItemTitle] = react.useState("");
105
+ const [tempItemSubtitle, setTempItemSubtitle] = react.useState("");
106
+ const handleDragStart = (e, itemId, sourceColId) => {
107
+ setDraggedItem({ itemId, sourceColId });
108
+ e.dataTransfer.effectAllowed = "move";
109
+ };
110
+ const handleDragOver = (e, colId) => {
111
+ e.preventDefault();
112
+ setDragOverColId(colId);
113
+ };
114
+ const handleDragLeave = () => {
115
+ setDragOverColId(null);
116
+ };
117
+ const handleDrop = (e, targetColId) => {
118
+ e.preventDefault();
119
+ setDragOverColId(null);
120
+ if (!draggedItem) return;
121
+ const { itemId, sourceColId } = draggedItem;
122
+ if (sourceColId === targetColId) {
123
+ setDraggedItem(null);
124
+ return;
125
+ }
126
+ const newColumns = columns.map((col) => {
127
+ if (col.id === sourceColId) {
128
+ return {
129
+ ...col,
130
+ items: col.items.filter((item) => item.id !== itemId)
131
+ };
132
+ }
133
+ if (col.id === targetColId) {
134
+ const itemToMove = columns.find((c) => c.id === sourceColId)?.items.find((i) => i.id === itemId);
135
+ if (itemToMove) {
136
+ return {
137
+ ...col,
138
+ items: [...col.items, itemToMove]
139
+ };
140
+ }
141
+ }
142
+ return col;
143
+ });
144
+ setColumns(newColumns);
145
+ setDraggedItem(null);
146
+ };
147
+ const handleAddColumn = () => {
148
+ const newCol = {
149
+ id: `col-${Date.now()}`,
150
+ title: "New Column",
151
+ items: []
152
+ };
153
+ setColumns([...columns, newCol]);
154
+ };
155
+ const handleStartRename = (col) => {
156
+ setEditingColId(col.id);
157
+ setTempColTitle(col.title);
158
+ };
159
+ const handleSaveRename = (colId) => {
160
+ if (!tempColTitle.trim()) return;
161
+ setColumns(columns.map(
162
+ (col) => col.id === colId ? { ...col, title: tempColTitle } : col
163
+ ));
164
+ setEditingColId(null);
165
+ };
166
+ const handleDeleteColumn = (colId) => {
167
+ if (confirm("Are you sure you want to delete this column?")) {
168
+ setColumns(columns.filter((col) => col.id !== colId));
169
+ }
170
+ };
171
+ const handleStartEditItem = (item) => {
172
+ setEditingItemId(item.id);
173
+ setTempItemTitle(item.title);
174
+ setTempItemSubtitle(item.subtitle);
175
+ };
176
+ const handleSaveEditItem = (colId, itemId) => {
177
+ if (!tempItemTitle.trim()) return;
178
+ setColumns(columns.map((col) => {
179
+ if (col.id === colId) {
180
+ return {
181
+ ...col,
182
+ items: col.items.map(
183
+ (item) => item.id === itemId ? { ...item, title: tempItemTitle, subtitle: tempItemSubtitle } : item
184
+ )
185
+ };
186
+ }
187
+ return col;
188
+ }));
189
+ setEditingItemId(null);
190
+ };
191
+ const handleDeleteItem = (colId, itemId) => {
192
+ if (confirm("Are you sure you want to delete this item?")) {
193
+ setColumns(columns.map((col) => {
194
+ if (col.id === colId) {
195
+ return {
196
+ ...col,
197
+ items: col.items.filter((item) => item.id !== itemId)
198
+ };
199
+ }
200
+ return col;
201
+ }));
202
+ }
203
+ };
204
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs(ScrollableMain, { children: [
205
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
206
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
207
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", children: "Kanban Board" }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "epsilon", textColor: "neutral600", as: "div", children: "Manage your project tasks visually" })
209
+ ] }),
210
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}), onClick: handleAddColumn, children: "Add Column" })
211
+ ] }) }),
212
+ /* @__PURE__ */ jsxRuntime.jsx(BoardContainer, { children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs(ColumnContainer, { children: [
213
+ /* @__PURE__ */ jsxRuntime.jsx(ColumnHeader, { children: editingColId === col.id ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, style: { flexGrow: 1 }, children: [
214
+ /* @__PURE__ */ jsxRuntime.jsx(
215
+ designSystem.TextInput,
216
+ {
217
+ "aria-label": "Column title",
218
+ value: tempColTitle,
219
+ onChange: (e) => setTempColTitle(e.target.value),
220
+ size: "S"
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsxRuntime.jsx(
224
+ designSystem.Button,
225
+ {
226
+ onClick: () => handleSaveRename(col.id),
227
+ size: "S",
228
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
229
+ children: "Save"
230
+ }
231
+ )
232
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
233
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
234
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", fontWeight: "bold", children: col.title }),
235
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", style: { marginLeft: "8px" }, children: [
236
+ "(",
237
+ col.items.length,
238
+ ")"
239
+ ] })
240
+ ] }),
241
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, children: [
242
+ /* @__PURE__ */ jsxRuntime.jsx(
243
+ designSystem.Button,
244
+ {
245
+ onClick: () => handleStartRename(col),
246
+ variant: "ghost",
247
+ size: "S",
248
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
249
+ }
250
+ ),
251
+ /* @__PURE__ */ jsxRuntime.jsx(
252
+ designSystem.Button,
253
+ {
254
+ onClick: () => handleDeleteColumn(col.id),
255
+ variant: "ghost",
256
+ size: "S",
257
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
258
+ }
259
+ )
260
+ ] })
261
+ ] }) }),
262
+ /* @__PURE__ */ jsxRuntime.jsx(
263
+ ItemList,
264
+ {
265
+ $isDraggingOver: dragOverColId === col.id,
266
+ onDragOver: (e) => handleDragOver(e, col.id),
267
+ onDragLeave: handleDragLeave,
268
+ onDrop: (e) => handleDrop(e, col.id),
269
+ children: col.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
270
+ CardItem,
271
+ {
272
+ draggable: editingItemId !== item.id,
273
+ onDragStart: (e) => handleDragStart(e, item.id, col.id),
274
+ $isDragging: draggedItem?.itemId === item.id,
275
+ children: editingItemId === item.id ? /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 2, children: [
276
+ /* @__PURE__ */ jsxRuntime.jsx(
277
+ designSystem.TextInput,
278
+ {
279
+ "aria-label": "Item title",
280
+ value: tempItemTitle,
281
+ onChange: (e) => setTempItemTitle(e.target.value),
282
+ size: "S"
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsxRuntime.jsx(
286
+ designSystem.TextInput,
287
+ {
288
+ "aria-label": "Item subtitle",
289
+ value: tempItemSubtitle,
290
+ onChange: (e) => setTempItemSubtitle(e.target.value),
291
+ size: "S"
292
+ }
293
+ ),
294
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, justifyContent: "flex-end", style: { width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
295
+ designSystem.Button,
296
+ {
297
+ onClick: () => handleSaveEditItem(col.id, item.id),
298
+ size: "S",
299
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
300
+ children: "Save"
301
+ }
302
+ ) })
303
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "start", children: [
304
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
305
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", as: "div", marginBottom: 1, children: item.title }),
306
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", as: "div", children: item.subtitle })
307
+ ] }),
308
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, style: { opacity: 0.5 }, children: [
309
+ /* @__PURE__ */ jsxRuntime.jsx(
310
+ designSystem.Button,
311
+ {
312
+ onClick: () => handleStartEditItem(item),
313
+ variant: "ghost",
314
+ size: "S",
315
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
316
+ }
317
+ ),
318
+ /* @__PURE__ */ jsxRuntime.jsx(
319
+ designSystem.Button,
320
+ {
321
+ onClick: () => handleDeleteItem(col.id, item.id),
322
+ variant: "ghost",
323
+ size: "S",
324
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
325
+ }
326
+ )
327
+ ] })
328
+ ] })
329
+ },
330
+ item.id
331
+ ))
332
+ }
333
+ )
334
+ ] }, col.id)) })
335
+ ] }) });
336
+ };
337
+ const App = () => {
338
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Routes, { children: [
339
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { index: true, element: /* @__PURE__ */ jsxRuntime.jsx(HomePage, {}) }),
340
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Route, { path: "*", element: /* @__PURE__ */ jsxRuntime.jsx(admin.Page.Error, {}) })
341
+ ] });
342
+ };
343
+ exports.App = App;
@@ -0,0 +1,341 @@
1
+ import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
+ import { Page } from "@strapi/strapi/admin";
3
+ import { Routes, Route } from "react-router-dom";
4
+ import { useState } from "react";
5
+ import { Box, Flex, Typography, Button, TextInput, Main } from "@strapi/design-system";
6
+ import { Plus, Check, Pencil, Trash } from "@strapi/icons";
7
+ import styled from "styled-components";
8
+ const initialData = [
9
+ {
10
+ id: "col-1",
11
+ title: "To Do",
12
+ items: [
13
+ { id: "1", title: "Research Plugins", subtitle: "Read Strapi v5 docs" },
14
+ { id: "2", title: "Setup Environment", subtitle: "Install dependencies" }
15
+ ]
16
+ },
17
+ {
18
+ id: "col-2",
19
+ title: "In Progress",
20
+ items: [
21
+ { id: "3", title: "Develop Feature", subtitle: "Implement Drag & Drop" }
22
+ ]
23
+ },
24
+ {
25
+ id: "col-3",
26
+ title: "Done",
27
+ items: [
28
+ { id: "4", title: "Initialize Project", subtitle: "Run CLI commands" }
29
+ ]
30
+ }
31
+ ];
32
+ const BoardContainer = styled(Flex)`
33
+ gap: 16px;
34
+ padding: 24px;
35
+ height: 100%;
36
+ align-items: flex-start;
37
+ overflow-x: auto;
38
+ `;
39
+ const ColumnContainer = styled(Box)`
40
+ background: ${({ theme }) => theme.colors.neutral100};
41
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
42
+ border-radius: ${({ theme }) => theme.borderRadius};
43
+ min-width: 300px;
44
+ width: 300px;
45
+ display: flex;
46
+ flex-direction: column;
47
+ max-height: 100%;
48
+ flex-shrink: 0;
49
+ `;
50
+ const ColumnHeader = styled(Flex)`
51
+ padding: 16px;
52
+ border-bottom: 1px solid ${({ theme }) => theme.colors.neutral200};
53
+ background: ${({ theme }) => theme.colors.neutral150};
54
+ border-top-left-radius: ${({ theme }) => theme.borderRadius};
55
+ border-top-right-radius: ${({ theme }) => theme.borderRadius};
56
+ justify-content: space-between;
57
+ align-items: center;
58
+ gap: 8px;
59
+ `;
60
+ const ItemList = styled(Box)`
61
+ padding: 16px;
62
+ flex-grow: 1;
63
+ min-height: 100px;
64
+ background: ${({ theme, $isDraggingOver }) => $isDraggingOver ? theme.colors.primary100 : "transparent"};
65
+ transition: background 0.2s;
66
+ overflow-y: auto;
67
+ `;
68
+ const CardItem = styled(Box)`
69
+ background: ${({ theme }) => theme.colors.neutral0};
70
+ border: 1px solid ${({ theme }) => theme.colors.neutral200};
71
+ border-radius: ${({ theme }) => theme.borderRadius};
72
+ padding: 12px;
73
+ margin-bottom: 8px;
74
+ box-shadow: ${({ theme }) => theme.shadows.filterShadow};
75
+ cursor: grab;
76
+ opacity: ${({ $isDragging }) => $isDragging ? 0.5 : 1};
77
+
78
+ &:hover {
79
+ border-color: ${({ theme }) => theme.colors.primary500};
80
+ box-shadow: ${({ theme }) => theme.shadows.tableShadow};
81
+ }
82
+
83
+ &:active {
84
+ cursor: grabbing;
85
+ }
86
+ `;
87
+ const ScrollableMain = styled(Main)`
88
+ height: 100vh;
89
+ overflow-y: hidden;
90
+ display: flex;
91
+ flex-direction: column;
92
+ `;
93
+ const HomePage = () => {
94
+ const [columns, setColumns] = useState(initialData);
95
+ const [draggedItem, setDraggedItem] = useState(null);
96
+ const [dragOverColId, setDragOverColId] = useState(null);
97
+ const [editingColId, setEditingColId] = useState(null);
98
+ const [tempColTitle, setTempColTitle] = useState("");
99
+ const [editingItemId, setEditingItemId] = useState(null);
100
+ const [tempItemTitle, setTempItemTitle] = useState("");
101
+ const [tempItemSubtitle, setTempItemSubtitle] = useState("");
102
+ const handleDragStart = (e, itemId, sourceColId) => {
103
+ setDraggedItem({ itemId, sourceColId });
104
+ e.dataTransfer.effectAllowed = "move";
105
+ };
106
+ const handleDragOver = (e, colId) => {
107
+ e.preventDefault();
108
+ setDragOverColId(colId);
109
+ };
110
+ const handleDragLeave = () => {
111
+ setDragOverColId(null);
112
+ };
113
+ const handleDrop = (e, targetColId) => {
114
+ e.preventDefault();
115
+ setDragOverColId(null);
116
+ if (!draggedItem) return;
117
+ const { itemId, sourceColId } = draggedItem;
118
+ if (sourceColId === targetColId) {
119
+ setDraggedItem(null);
120
+ return;
121
+ }
122
+ const newColumns = columns.map((col) => {
123
+ if (col.id === sourceColId) {
124
+ return {
125
+ ...col,
126
+ items: col.items.filter((item) => item.id !== itemId)
127
+ };
128
+ }
129
+ if (col.id === targetColId) {
130
+ const itemToMove = columns.find((c) => c.id === sourceColId)?.items.find((i) => i.id === itemId);
131
+ if (itemToMove) {
132
+ return {
133
+ ...col,
134
+ items: [...col.items, itemToMove]
135
+ };
136
+ }
137
+ }
138
+ return col;
139
+ });
140
+ setColumns(newColumns);
141
+ setDraggedItem(null);
142
+ };
143
+ const handleAddColumn = () => {
144
+ const newCol = {
145
+ id: `col-${Date.now()}`,
146
+ title: "New Column",
147
+ items: []
148
+ };
149
+ setColumns([...columns, newCol]);
150
+ };
151
+ const handleStartRename = (col) => {
152
+ setEditingColId(col.id);
153
+ setTempColTitle(col.title);
154
+ };
155
+ const handleSaveRename = (colId) => {
156
+ if (!tempColTitle.trim()) return;
157
+ setColumns(columns.map(
158
+ (col) => col.id === colId ? { ...col, title: tempColTitle } : col
159
+ ));
160
+ setEditingColId(null);
161
+ };
162
+ const handleDeleteColumn = (colId) => {
163
+ if (confirm("Are you sure you want to delete this column?")) {
164
+ setColumns(columns.filter((col) => col.id !== colId));
165
+ }
166
+ };
167
+ const handleStartEditItem = (item) => {
168
+ setEditingItemId(item.id);
169
+ setTempItemTitle(item.title);
170
+ setTempItemSubtitle(item.subtitle);
171
+ };
172
+ const handleSaveEditItem = (colId, itemId) => {
173
+ if (!tempItemTitle.trim()) return;
174
+ setColumns(columns.map((col) => {
175
+ if (col.id === colId) {
176
+ return {
177
+ ...col,
178
+ items: col.items.map(
179
+ (item) => item.id === itemId ? { ...item, title: tempItemTitle, subtitle: tempItemSubtitle } : item
180
+ )
181
+ };
182
+ }
183
+ return col;
184
+ }));
185
+ setEditingItemId(null);
186
+ };
187
+ const handleDeleteItem = (colId, itemId) => {
188
+ if (confirm("Are you sure you want to delete this item?")) {
189
+ setColumns(columns.map((col) => {
190
+ if (col.id === colId) {
191
+ return {
192
+ ...col,
193
+ items: col.items.filter((item) => item.id !== itemId)
194
+ };
195
+ }
196
+ return col;
197
+ }));
198
+ }
199
+ };
200
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(ScrollableMain, { children: [
201
+ /* @__PURE__ */ jsx(Box, { padding: 8, background: "neutral100", children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
202
+ /* @__PURE__ */ jsxs(Box, { children: [
203
+ /* @__PURE__ */ jsx(Typography, { variant: "alpha", children: "Kanban Board" }),
204
+ /* @__PURE__ */ jsx(Typography, { variant: "epsilon", textColor: "neutral600", as: "div", children: "Manage your project tasks visually" })
205
+ ] }),
206
+ /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: handleAddColumn, children: "Add Column" })
207
+ ] }) }),
208
+ /* @__PURE__ */ jsx(BoardContainer, { children: columns.map((col) => /* @__PURE__ */ jsxs(ColumnContainer, { children: [
209
+ /* @__PURE__ */ jsx(ColumnHeader, { children: editingColId === col.id ? /* @__PURE__ */ jsxs(Flex, { gap: 2, style: { flexGrow: 1 }, children: [
210
+ /* @__PURE__ */ jsx(
211
+ TextInput,
212
+ {
213
+ "aria-label": "Column title",
214
+ value: tempColTitle,
215
+ onChange: (e) => setTempColTitle(e.target.value),
216
+ size: "S"
217
+ }
218
+ ),
219
+ /* @__PURE__ */ jsx(
220
+ Button,
221
+ {
222
+ onClick: () => handleSaveRename(col.id),
223
+ size: "S",
224
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
225
+ children: "Save"
226
+ }
227
+ )
228
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
229
+ /* @__PURE__ */ jsxs(Box, { children: [
230
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", children: col.title }),
231
+ /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", style: { marginLeft: "8px" }, children: [
232
+ "(",
233
+ col.items.length,
234
+ ")"
235
+ ] })
236
+ ] }),
237
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, children: [
238
+ /* @__PURE__ */ jsx(
239
+ Button,
240
+ {
241
+ onClick: () => handleStartRename(col),
242
+ variant: "ghost",
243
+ size: "S",
244
+ startIcon: /* @__PURE__ */ jsx(Pencil, {})
245
+ }
246
+ ),
247
+ /* @__PURE__ */ jsx(
248
+ Button,
249
+ {
250
+ onClick: () => handleDeleteColumn(col.id),
251
+ variant: "ghost",
252
+ size: "S",
253
+ startIcon: /* @__PURE__ */ jsx(Trash, {})
254
+ }
255
+ )
256
+ ] })
257
+ ] }) }),
258
+ /* @__PURE__ */ jsx(
259
+ ItemList,
260
+ {
261
+ $isDraggingOver: dragOverColId === col.id,
262
+ onDragOver: (e) => handleDragOver(e, col.id),
263
+ onDragLeave: handleDragLeave,
264
+ onDrop: (e) => handleDrop(e, col.id),
265
+ children: col.items.map((item) => /* @__PURE__ */ jsx(
266
+ CardItem,
267
+ {
268
+ draggable: editingItemId !== item.id,
269
+ onDragStart: (e) => handleDragStart(e, item.id, col.id),
270
+ $isDragging: draggedItem?.itemId === item.id,
271
+ children: editingItemId === item.id ? /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, children: [
272
+ /* @__PURE__ */ jsx(
273
+ TextInput,
274
+ {
275
+ "aria-label": "Item title",
276
+ value: tempItemTitle,
277
+ onChange: (e) => setTempItemTitle(e.target.value),
278
+ size: "S"
279
+ }
280
+ ),
281
+ /* @__PURE__ */ jsx(
282
+ TextInput,
283
+ {
284
+ "aria-label": "Item subtitle",
285
+ value: tempItemSubtitle,
286
+ onChange: (e) => setTempItemSubtitle(e.target.value),
287
+ size: "S"
288
+ }
289
+ ),
290
+ /* @__PURE__ */ jsx(Flex, { gap: 2, justifyContent: "flex-end", style: { width: "100%" }, children: /* @__PURE__ */ jsx(
291
+ Button,
292
+ {
293
+ onClick: () => handleSaveEditItem(col.id, item.id),
294
+ size: "S",
295
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
296
+ children: "Save"
297
+ }
298
+ ) })
299
+ ] }) : /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "start", children: [
300
+ /* @__PURE__ */ jsxs(Box, { children: [
301
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", as: "div", marginBottom: 1, children: item.title }),
302
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", as: "div", children: item.subtitle })
303
+ ] }),
304
+ /* @__PURE__ */ jsxs(Flex, { gap: 1, style: { opacity: 0.5 }, children: [
305
+ /* @__PURE__ */ jsx(
306
+ Button,
307
+ {
308
+ onClick: () => handleStartEditItem(item),
309
+ variant: "ghost",
310
+ size: "S",
311
+ startIcon: /* @__PURE__ */ jsx(Pencil, {})
312
+ }
313
+ ),
314
+ /* @__PURE__ */ jsx(
315
+ Button,
316
+ {
317
+ onClick: () => handleDeleteItem(col.id, item.id),
318
+ variant: "ghost",
319
+ size: "S",
320
+ startIcon: /* @__PURE__ */ jsx(Trash, {})
321
+ }
322
+ )
323
+ ] })
324
+ ] })
325
+ },
326
+ item.id
327
+ ))
328
+ }
329
+ )
330
+ ] }, col.id)) })
331
+ ] }) });
332
+ };
333
+ const App = () => {
334
+ return /* @__PURE__ */ jsxs(Routes, { children: [
335
+ /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(HomePage, {}) }),
336
+ /* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(Page.Error, {}) })
337
+ ] });
338
+ };
339
+ export {
340
+ App
341
+ };
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const en = {
4
+ "kanban-plugin.kanban-board.label": "Kanban Board",
5
+ "kanban-plugin.kanban-board.description": "Drag and drop items between columns",
6
+ "kanban-plugin.plugin.name": "Kanban Board"
7
+ };
8
+ exports.default = en;
@@ -0,0 +1,8 @@
1
+ const en = {
2
+ "kanban-plugin.kanban-board.label": "Kanban Board",
3
+ "kanban-plugin.kanban-board.description": "Drag and drop items between columns",
4
+ "kanban-plugin.plugin.name": "Kanban Board"
5
+ };
6
+ export {
7
+ en as default
8
+ };