@primershop/strapi-plugin-status-manager 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 (153) hide show
  1. package/README.md +52 -0
  2. package/dist/admin/components/Initializer.js +17 -0
  3. package/dist/admin/components/Initializer.js.map +1 -0
  4. package/dist/admin/components/Initializer.mjs +15 -0
  5. package/dist/admin/components/Initializer.mjs.map +1 -0
  6. package/dist/admin/components/PluginIcon.js +9 -0
  7. package/dist/admin/components/PluginIcon.js.map +1 -0
  8. package/dist/admin/components/PluginIcon.mjs +7 -0
  9. package/dist/admin/components/PluginIcon.mjs.map +1 -0
  10. package/dist/admin/components/ProductStatusField.js +104 -0
  11. package/dist/admin/components/ProductStatusField.js.map +1 -0
  12. package/dist/admin/components/ProductStatusField.mjs +102 -0
  13. package/dist/admin/components/ProductStatusField.mjs.map +1 -0
  14. package/dist/admin/components/StatusManager.js +367 -0
  15. package/dist/admin/components/StatusManager.js.map +1 -0
  16. package/dist/admin/components/StatusManager.mjs +365 -0
  17. package/dist/admin/components/StatusManager.mjs.map +1 -0
  18. package/dist/admin/index.js +46 -0
  19. package/dist/admin/index.js.map +1 -0
  20. package/dist/admin/index.mjs +44 -0
  21. package/dist/admin/index.mjs.map +1 -0
  22. package/dist/admin/listView/StatusFilter.js +76 -0
  23. package/dist/admin/listView/StatusFilter.js.map +1 -0
  24. package/dist/admin/listView/StatusFilter.mjs +74 -0
  25. package/dist/admin/listView/StatusFilter.mjs.map +1 -0
  26. package/dist/admin/listView/add-status-column-hook.js +34 -0
  27. package/dist/admin/listView/add-status-column-hook.js.map +1 -0
  28. package/dist/admin/listView/add-status-column-hook.mjs +32 -0
  29. package/dist/admin/listView/add-status-column-hook.mjs.map +1 -0
  30. package/dist/admin/listView/status-cell.js +28 -0
  31. package/dist/admin/listView/status-cell.js.map +1 -0
  32. package/dist/admin/listView/status-cell.mjs +26 -0
  33. package/dist/admin/listView/status-cell.mjs.map +1 -0
  34. package/dist/admin/pages/HomePage.js +51 -0
  35. package/dist/admin/pages/HomePage.js.map +1 -0
  36. package/dist/admin/pages/HomePage.mjs +49 -0
  37. package/dist/admin/pages/HomePage.mjs.map +1 -0
  38. package/dist/admin/pluginId.js +6 -0
  39. package/dist/admin/pluginId.js.map +1 -0
  40. package/dist/admin/pluginId.mjs +4 -0
  41. package/dist/admin/pluginId.mjs.map +1 -0
  42. package/dist/admin/src/components/Initializer.js +13 -0
  43. package/dist/admin/src/components/PluginIcon.js +4 -0
  44. package/dist/admin/src/components/ProductStatusField.js +64 -0
  45. package/dist/admin/src/components/StatusManager.js +239 -0
  46. package/dist/admin/src/index.js +43 -0
  47. package/dist/admin/src/listView/StatusFilter.js +47 -0
  48. package/dist/admin/src/listView/add-status-column-hook.js +18 -0
  49. package/dist/admin/src/listView/status-cell.js +18 -0
  50. package/dist/admin/src/pages/HomePage.js +18 -0
  51. package/dist/admin/src/pluginId.js +1 -0
  52. package/dist/server/bootstrap.js +29 -0
  53. package/dist/server/bootstrap.js.map +1 -0
  54. package/dist/server/bootstrap.mjs +27 -0
  55. package/dist/server/bootstrap.mjs.map +1 -0
  56. package/dist/server/content-types/index.js +16 -0
  57. package/dist/server/content-types/index.js.map +1 -0
  58. package/dist/server/content-types/index.mjs +14 -0
  59. package/dist/server/content-types/index.mjs.map +1 -0
  60. package/dist/server/content-types/status-link.js +43 -0
  61. package/dist/server/content-types/status-link.js.map +1 -0
  62. package/dist/server/content-types/status-link.mjs +41 -0
  63. package/dist/server/content-types/status-link.mjs.map +1 -0
  64. package/dist/server/content-types/status.js +50 -0
  65. package/dist/server/content-types/status.js.map +1 -0
  66. package/dist/server/content-types/status.mjs +48 -0
  67. package/dist/server/content-types/status.mjs.map +1 -0
  68. package/dist/server/controllers/content.js +33 -0
  69. package/dist/server/controllers/content.js.map +1 -0
  70. package/dist/server/controllers/content.mjs +31 -0
  71. package/dist/server/controllers/content.mjs.map +1 -0
  72. package/dist/server/controllers/index.js +12 -0
  73. package/dist/server/controllers/index.js.map +1 -0
  74. package/dist/server/controllers/index.mjs +10 -0
  75. package/dist/server/controllers/index.mjs.map +1 -0
  76. package/dist/server/controllers/status.js +106 -0
  77. package/dist/server/controllers/status.js.map +1 -0
  78. package/dist/server/controllers/status.mjs +104 -0
  79. package/dist/server/controllers/status.mjs.map +1 -0
  80. package/dist/server/index.js +23 -0
  81. package/dist/server/index.js.map +1 -0
  82. package/dist/server/index.mjs +21 -0
  83. package/dist/server/index.mjs.map +1 -0
  84. package/dist/server/middlewares/add-status-field.js +48 -0
  85. package/dist/server/middlewares/add-status-field.js.map +1 -0
  86. package/dist/server/middlewares/add-status-field.mjs +46 -0
  87. package/dist/server/middlewares/add-status-field.mjs.map +1 -0
  88. package/dist/server/middlewares/filter-by-status.js +42 -0
  89. package/dist/server/middlewares/filter-by-status.js.map +1 -0
  90. package/dist/server/middlewares/filter-by-status.mjs +40 -0
  91. package/dist/server/middlewares/filter-by-status.mjs.map +1 -0
  92. package/dist/server/middlewares/filter-published.js +35 -0
  93. package/dist/server/middlewares/filter-published.js.map +1 -0
  94. package/dist/server/middlewares/filter-published.mjs +33 -0
  95. package/dist/server/middlewares/filter-published.mjs.map +1 -0
  96. package/dist/server/permissions.js +46 -0
  97. package/dist/server/permissions.js.map +1 -0
  98. package/dist/server/permissions.mjs +44 -0
  99. package/dist/server/permissions.mjs.map +1 -0
  100. package/dist/server/pluginId.js +6 -0
  101. package/dist/server/pluginId.js.map +1 -0
  102. package/dist/server/pluginId.mjs +4 -0
  103. package/dist/server/pluginId.mjs.map +1 -0
  104. package/dist/server/register.js +27 -0
  105. package/dist/server/register.js.map +1 -0
  106. package/dist/server/register.mjs +25 -0
  107. package/dist/server/register.mjs.map +1 -0
  108. package/dist/server/routes/content-management.js +41 -0
  109. package/dist/server/routes/content-management.js.map +1 -0
  110. package/dist/server/routes/content-management.mjs +39 -0
  111. package/dist/server/routes/content-management.mjs.map +1 -0
  112. package/dist/server/routes/index.js +17 -0
  113. package/dist/server/routes/index.js.map +1 -0
  114. package/dist/server/routes/index.mjs +15 -0
  115. package/dist/server/routes/index.mjs.map +1 -0
  116. package/dist/server/routes/status-management.js +122 -0
  117. package/dist/server/routes/status-management.js.map +1 -0
  118. package/dist/server/routes/status-management.mjs +120 -0
  119. package/dist/server/routes/status-management.mjs.map +1 -0
  120. package/dist/server/services/index.js +12 -0
  121. package/dist/server/services/index.js.map +1 -0
  122. package/dist/server/services/index.mjs +10 -0
  123. package/dist/server/services/index.mjs.map +1 -0
  124. package/dist/server/services/status-link.js +38 -0
  125. package/dist/server/services/status-link.js.map +1 -0
  126. package/dist/server/services/status-link.mjs +36 -0
  127. package/dist/server/services/status-link.mjs.map +1 -0
  128. package/dist/server/services/status.js +110 -0
  129. package/dist/server/services/status.js.map +1 -0
  130. package/dist/server/services/status.mjs +108 -0
  131. package/dist/server/services/status.mjs.map +1 -0
  132. package/dist/server/src/bootstrap.js +32 -0
  133. package/dist/server/src/config/dev.js +13 -0
  134. package/dist/server/src/content-types/index.js +15 -0
  135. package/dist/server/src/content-types/status-link.js +40 -0
  136. package/dist/server/src/content-types/status.js +47 -0
  137. package/dist/server/src/controllers/content.js +44 -0
  138. package/dist/server/src/controllers/index.js +9 -0
  139. package/dist/server/src/controllers/status.js +106 -0
  140. package/dist/server/src/index.js +23 -0
  141. package/dist/server/src/middlewares/add-status-field.js +51 -0
  142. package/dist/server/src/middlewares/filter-by-status.js +44 -0
  143. package/dist/server/src/middlewares/filter-published.js +31 -0
  144. package/dist/server/src/permissions.js +41 -0
  145. package/dist/server/src/pluginId.js +4 -0
  146. package/dist/server/src/register.js +28 -0
  147. package/dist/server/src/routes/content-management.js +34 -0
  148. package/dist/server/src/routes/index.js +15 -0
  149. package/dist/server/src/routes/status-management.js +109 -0
  150. package/dist/server/src/services/index.js +9 -0
  151. package/dist/server/src/services/status-link.js +33 -0
  152. package/dist/server/src/services/status.js +96 -0
  153. package/package.json +104 -0
@@ -0,0 +1,239 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback } from "react";
3
+ import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
4
+ import { draggable, dropTargetForElements, monitorForElements, } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
5
+ import { pointerOutsideOfPreview } from "@atlaskit/pragmatic-drag-and-drop/element/pointer-outside-of-preview";
6
+ import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
7
+ import { reorder } from "@atlaskit/pragmatic-drag-and-drop/reorder";
8
+ import { attachClosestEdge, extractClosestEdge, } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge";
9
+ import { TextInput, Button, Flex, Typography, Box, Dialog, SingleSelect, SingleSelectOption, } from "@strapi/design-system";
10
+ import { Plus, Trash, Drag } from "@strapi/icons";
11
+ import { useFetchClient } from "@strapi/strapi/admin";
12
+ const StatusManager = () => {
13
+ const [statuses, setStatuses] = useState([]);
14
+ const [newStatus, setNewStatus] = useState("");
15
+ const [statusToDelete, setStatusToDelete] = useState(null);
16
+ const [replacementStatus, setReplacementStatus] = useState("");
17
+ const { get, post, put } = useFetchClient();
18
+ const [instanceId] = useState(() => Symbol("instance-id"));
19
+ // Fetch statuses
20
+ useEffect(() => {
21
+ const loadStatuses = async () => {
22
+ const { data } = await get("primershop-status-manager/statuses");
23
+ setStatuses(data);
24
+ };
25
+ loadStatuses();
26
+ }, [get]);
27
+ // Validate input (Latin characters only)
28
+ const validateInput = (value) => /^[a-zA-Z\s]+$/.test(value);
29
+ // Add new status
30
+ const addStatus = async () => {
31
+ if (!newStatus || !validateInput(newStatus))
32
+ return alert("Only Latin characters allowed!");
33
+ try {
34
+ const { data } = await post("primershop-status-manager/status", {
35
+ name: newStatus,
36
+ published: false,
37
+ });
38
+ setStatuses([...statuses, data]);
39
+ setNewStatus("");
40
+ }
41
+ catch (error) {
42
+ console.error("Error creating status:", error);
43
+ }
44
+ };
45
+ const reorderItem = useCallback(async ({ startIndex, indexOfTarget, closestEdgeOfTarget, }) => {
46
+ // Calculate the final index based on the target position and edge
47
+ let finishIndex = indexOfTarget;
48
+ if (closestEdgeOfTarget === "bottom") {
49
+ finishIndex = indexOfTarget + 1;
50
+ }
51
+ // If moving an item down, we need to adjust for the removed item
52
+ if (startIndex < finishIndex) {
53
+ finishIndex--;
54
+ }
55
+ if (finishIndex === startIndex) {
56
+ return;
57
+ }
58
+ const reordered = reorder({
59
+ list: statuses,
60
+ startIndex,
61
+ finishIndex,
62
+ });
63
+ // Send new order to API
64
+ const orderedIds = reordered.map((status, index) => ({
65
+ documentId: status.documentId,
66
+ order: index,
67
+ }));
68
+ await put("/primershop-status-manager/statuses/reorder", {
69
+ statuses: orderedIds,
70
+ });
71
+ setStatuses(reordered);
72
+ }, [statuses, put]);
73
+ // Setup drag and drop
74
+ useEffect(() => {
75
+ const statusElements = document.querySelectorAll("[data-status-id]");
76
+ const cleanupFunctions = [];
77
+ statusElements.forEach((element) => {
78
+ const statusId = element.getAttribute("data-status-id");
79
+ const index = statuses.findIndex((s) => s.documentId === statusId);
80
+ const dragHandle = element.querySelector("[data-drag-handle]");
81
+ if (!dragHandle)
82
+ return;
83
+ // Setup draggable
84
+ const draggableCleanup = draggable({
85
+ element: dragHandle,
86
+ getInitialData: () => ({
87
+ statusId,
88
+ index,
89
+ instanceId,
90
+ }),
91
+ onGenerateDragPreview({ nativeSetDragImage }) {
92
+ setCustomNativeDragPreview({
93
+ nativeSetDragImage,
94
+ getOffset: pointerOutsideOfPreview({
95
+ x: "16px",
96
+ y: "8px",
97
+ }),
98
+ render({ container }) {
99
+ const preview = document.createElement("div");
100
+ preview.style.padding = "8px 16px";
101
+ preview.style.backgroundColor = "#fff";
102
+ preview.style.border = "1px solid #ccc";
103
+ preview.style.borderRadius = "4px";
104
+ preview.style.boxShadow = "0 2px 4px rgba(0,0,0,0.1)";
105
+ const statusNameElement = element.querySelector("[data-status-name]");
106
+ preview.textContent = statusNameElement?.textContent || "";
107
+ container.appendChild(preview);
108
+ return () => container.removeChild(preview);
109
+ },
110
+ });
111
+ },
112
+ });
113
+ // Setup drop target
114
+ const dropTargetCleanup = dropTargetForElements({
115
+ element: element,
116
+ canDrop: ({ source }) => source.data.instanceId === instanceId,
117
+ getData({ input }) {
118
+ return attachClosestEdge({ statusId, index, instanceId }, {
119
+ element,
120
+ input,
121
+ allowedEdges: ["top", "bottom"],
122
+ });
123
+ },
124
+ onDrag({ source, self }) {
125
+ const isSource = source.element === dragHandle;
126
+ if (isSource)
127
+ return;
128
+ const closestEdge = extractClosestEdge(self.data);
129
+ const sourceIndex = Number(source.data.index);
130
+ const isItemBeforeSource = index === sourceIndex - 1;
131
+ const isItemAfterSource = index === sourceIndex + 1;
132
+ const isDropIndicatorHidden = (isItemBeforeSource && closestEdge === "bottom") ||
133
+ (isItemAfterSource && closestEdge === "top");
134
+ if (isDropIndicatorHidden)
135
+ return;
136
+ // Add visual feedback for drop target
137
+ element.style.background =
138
+ `linear-gradient(${closestEdge === "top" ? 180 : 0}deg, rgba(136,131,214,0.4) 0%, rgba(255,255,255,0) 50%)`;
139
+ },
140
+ onDragLeave() {
141
+ element.style.background = "";
142
+ },
143
+ onDrop({ source, self }) {
144
+ element.style.background = "";
145
+ const sourceData = source.data;
146
+ const targetData = self.data;
147
+ const indexOfTarget = statuses.findIndex((s) => s.documentId === targetData.statusId);
148
+ if (indexOfTarget < 0)
149
+ return;
150
+ const closestEdgeOfTarget = extractClosestEdge(targetData);
151
+ reorderItem({
152
+ startIndex: sourceData.index,
153
+ indexOfTarget,
154
+ closestEdgeOfTarget,
155
+ });
156
+ },
157
+ });
158
+ // Combine cleanup functions
159
+ const combinedCleanup = combine(draggableCleanup, dropTargetCleanup);
160
+ cleanupFunctions.push(combinedCleanup);
161
+ });
162
+ // Monitor for drops
163
+ const monitorCleanup = monitorForElements({
164
+ canMonitor: ({ source }) => source.data.instanceId === instanceId,
165
+ onDrop({ location, source }) {
166
+ const target = location.current.dropTargets[0];
167
+ if (!target)
168
+ return;
169
+ const sourceData = source.data;
170
+ const targetData = target.data;
171
+ const indexOfTarget = statuses.findIndex((s) => s.documentId === targetData.statusId);
172
+ if (indexOfTarget < 0)
173
+ return;
174
+ const closestEdgeOfTarget = extractClosestEdge(targetData);
175
+ reorderItem({
176
+ startIndex: sourceData.index,
177
+ indexOfTarget,
178
+ closestEdgeOfTarget,
179
+ });
180
+ },
181
+ });
182
+ // Cleanup function
183
+ return () => {
184
+ cleanupFunctions.forEach((cleanup) => cleanup());
185
+ monitorCleanup();
186
+ };
187
+ }, [statuses, reorderItem, instanceId]);
188
+ // Open delete dialog
189
+ const confirmDelete = (status) => {
190
+ setStatusToDelete(status);
191
+ };
192
+ // Delete status and replace with selected one
193
+ const deleteStatus = async () => {
194
+ if (!replacementStatus)
195
+ return alert("Select a replacement status!");
196
+ const replacementStatusObj = statuses.find((s) => s.name === replacementStatus);
197
+ if (!replacementStatusObj)
198
+ return alert("Replacement status not found!");
199
+ try {
200
+ await put("/primershop-status-manager/statuses/delete", {
201
+ statusId: statusToDelete?.documentId,
202
+ replacementId: replacementStatusObj.documentId,
203
+ });
204
+ // Remove the deleted status from the list
205
+ setStatuses(statuses.filter((s) => s.documentId !== statusToDelete?.documentId));
206
+ setStatusToDelete(null);
207
+ setReplacementStatus("");
208
+ }
209
+ catch (error) {
210
+ console.error("Error deleting status:", error);
211
+ }
212
+ };
213
+ // Toggle publish status
214
+ const togglePublish = async (id, published) => {
215
+ try {
216
+ await put(`/primershop-status-manager/statuses/${id}`, {
217
+ published: !published,
218
+ });
219
+ setStatuses(statuses.map((s) => s.documentId === id ? { ...s, published: !published } : s));
220
+ }
221
+ catch (error) {
222
+ console.error("Error toggling publish status:", error);
223
+ }
224
+ };
225
+ return (_jsxs(Box, { padding: 4, children: [_jsx(Typography, { variant: "beta", children: "Status Manager" }), _jsxs(Flex, { marginTop: 4, gap: 2, children: [_jsx(TextInput, { placeholder: "Enter a status...", value: newStatus, onChange: (e) => setNewStatus(e.target.value) }), _jsx(Button, { onClick: addStatus, startIcon: _jsx(Plus, {}), children: "Add Status" })] }), _jsx(Box, { marginTop: 4, children: statuses.map((status) => (_jsxs(Flex, { "data-status-id": status.documentId, alignItems: "center", gap: 2, marginBottom: 2, paddingBottom: 2, style: {
226
+ borderBottom: `1px solid gray`,
227
+ minWidth: 300,
228
+ userSelect: "none",
229
+ touchAction: "none",
230
+ }, children: [_jsx(Box, { "data-drag-handle": true, style: {
231
+ cursor: "grab",
232
+ padding: "4px",
233
+ display: "flex",
234
+ alignItems: "center",
235
+ }, children: _jsx(Drag, {}) }, `dragHandle-${status.documentId}`), _jsx(Typography, { variant: "sigma", style: { display: "inline-block", marginRight: "auto" }, "data-status-name": true, children: status.name }), _jsx(Button, { variant: status.published ? "success-light" : "secondary", onClick: () => togglePublish(status.documentId, status.published), children: status.published ? "Published" : "Unpublished" }), _jsxs(Dialog.Root, { onOpenChange: () => confirmDelete(status), children: [_jsx(Dialog.Trigger, { children: _jsx(Button, { variant: "tertiary", startIcon: _jsx(Trash, {}), children: "Delete" }) }), _jsxs(Dialog.Content, { children: [_jsx(Dialog.Header, { children: "Delete status" }), statuses.length > 1 && statusToDelete && (_jsxs(Dialog.Body, { children: [_jsx(Typography, { children: "Choose a replacement status before deleting:" }), _jsx(SingleSelect, { onChange: (value) => setReplacementStatus(value), placeholder: "Select replacement", children: statuses
236
+ .filter((s) => s.documentId !== statusToDelete.documentId)
237
+ .map((s) => (_jsx(SingleSelectOption, { value: s.name, children: s.name }, `statusChoice-${s.documentId}`))) }), replacementStatus && (_jsxs(Typography, { children: ["Replacing ", statusToDelete.name, " with ", replacementStatus] }))] })), _jsxs(Dialog.Footer, { children: [_jsx(Dialog.Cancel, { children: _jsx(Button, { fullWidth: true, variant: "tertiary", children: "Cancel" }) }), _jsx(Dialog.Action, { children: _jsx(Button, { fullWidth: true, variant: "danger-light", onClick: deleteStatus, children: "Yes, delete" }) })] })] })] })] }, `status-${status.documentId}`))) }, statuses.length)] }));
238
+ };
239
+ export { StatusManager };
@@ -0,0 +1,43 @@
1
+ import { Initializer } from "./components/Initializer";
2
+ import { PluginIcon } from "./components/PluginIcon";
3
+ import { ProductStatusField } from "./components/ProductStatusField";
4
+ import { PLUGIN_ID } from "./pluginId";
5
+ import { addStatusColumnHook } from "./listView/add-status-column-hook";
6
+ import { StatusFilter } from "./listView/StatusFilter";
7
+ const plugin = {
8
+ register(app) {
9
+ app.registerPlugin({
10
+ id: PLUGIN_ID,
11
+ initializer: Initializer,
12
+ isReady: true,
13
+ name: PLUGIN_ID,
14
+ });
15
+ app.addMenuLink({
16
+ to: `plugins/${PLUGIN_ID}`,
17
+ icon: PluginIcon,
18
+ intlLabel: {
19
+ id: `${PLUGIN_ID}.plugin.name`,
20
+ defaultMessage: "Status manager",
21
+ },
22
+ permissions: [],
23
+ Component: () => import("./pages/HomePage").then((module) => ({
24
+ default: module.HomePage,
25
+ })),
26
+ });
27
+ },
28
+ bootstrap(app) {
29
+ app
30
+ .getPlugin("content-manager")
31
+ .injectComponent("editView", "right-links", {
32
+ name: "Status",
33
+ Component: ProductStatusField,
34
+ });
35
+ app.registerHook('Admin/CM/pages/ListView/inject-column-in-table', addStatusColumnHook);
36
+ const contentManager = app.getPlugin('content-manager');
37
+ contentManager.injectComponent('listView', 'actions', {
38
+ name: 'status-filter',
39
+ Component: StatusFilter,
40
+ });
41
+ },
42
+ };
43
+ export default plugin;
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { SingleSelect, SingleSelectOption, Flex } from "@strapi/design-system";
4
+ import { useFetchClient, unstable_useContentManagerContext as useContentManagerContext, useQueryParams, } from "@strapi/strapi/admin";
5
+ const StatusFilter = () => {
6
+ const { contentType } = useContentManagerContext();
7
+ const [statuses, setStatuses] = useState([]);
8
+ const [selected, setSelected] = useState("");
9
+ const { get } = useFetchClient();
10
+ const [{ query }, setQuery] = useQueryParams();
11
+ const handleStatusChange = useCallback((name) => {
12
+ setQuery({
13
+ page: 1,
14
+ plugins: {
15
+ ...query.plugins,
16
+ "primershop-status-manager": { statusName: name.toLowerCase() },
17
+ },
18
+ }, "push", true);
19
+ }, [query.plugins, setQuery]);
20
+ useEffect(() => {
21
+ const selectedStatusName = query.plugins?.["primershop-status-manager"]?.statusName;
22
+ if (!selectedStatusName)
23
+ return;
24
+ const status = statuses.find((status) => status.name.toLowerCase() === selectedStatusName);
25
+ if (status) {
26
+ setSelected(status.name);
27
+ }
28
+ }, [query, statuses]);
29
+ useEffect(() => {
30
+ async function fetchStatuses() {
31
+ try {
32
+ const { data } = await get("primershop-status-manager/statuses");
33
+ const allStatusesObject = {
34
+ documentId: "all",
35
+ name: "All",
36
+ };
37
+ setStatuses([allStatusesObject, ...data]);
38
+ }
39
+ catch (error) {
40
+ console.error("Error fetching statuses:", error);
41
+ }
42
+ }
43
+ fetchStatuses();
44
+ }, [get]);
45
+ return (_jsx(Flex, { direction: "column", justifyContent: "center", children: _jsx(SingleSelect, { size: "S", placeholder: `${contentType?.info.displayName} status`, value: selected, onChange: handleStatusChange, children: statuses.map((status) => (_jsx(SingleSelectOption, { value: status.name, children: status.name }, status.documentId))) }) }));
46
+ };
47
+ export { StatusFilter };
@@ -0,0 +1,18 @@
1
+ import { StatusCell } from "./status-cell";
2
+ import React from "react";
3
+ export const addStatusColumnHook = ({ displayedHeaders, layout, }) => {
4
+ const statusHeader = {
5
+ attribute: { type: "custom" },
6
+ name: "statusLabel",
7
+ label: { id: "primershop-status-manager.status", defaultMessage: "Status" },
8
+ searchable: false,
9
+ sortable: false,
10
+ cellFormatter: (row) => {
11
+ return React.createElement(StatusCell, { row });
12
+ },
13
+ };
14
+ return {
15
+ displayedHeaders: [...displayedHeaders, statusHeader],
16
+ layout,
17
+ };
18
+ };
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ export const StatusCell = ({ row }) => {
4
+ const [status, setStatus] = useState(null);
5
+ useEffect(() => {
6
+ setStatus(row.statusField);
7
+ }, [row]);
8
+ if (!status)
9
+ return null;
10
+ return (_jsx("span", { style: {
11
+ padding: "4px 8px",
12
+ borderRadius: 4,
13
+ background: status.published ? "#eafbe7" : "#f0f0ff",
14
+ color: status.published ? "#2f6846" : "#271fe0",
15
+ border: `1px solid ${status.published ? "#2f6846" : "#271fe0"}`,
16
+ fontSize: 14,
17
+ }, children: status.name }));
18
+ };
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Flex, Typography, Box } from "@strapi/design-system";
3
+ import { Layouts, Page } from "@strapi/strapi/admin";
4
+ import { StatusManager } from "../components/StatusManager";
5
+ const HomePage = () => {
6
+ return (_jsxs(Layouts.Root, { children: [_jsx(Page.Title, { children: "Status Manager" }), _jsx(Page.Main, { children: _jsx(Layouts.Content, { children: _jsx(Box, { children: _jsx(Flex, { padding: 10, gap: {
7
+ initial: 1,
8
+ medium: 4,
9
+ large: 8,
10
+ }, direction: {
11
+ initial: "column",
12
+ medium: "row",
13
+ }, alignItems: {
14
+ initial: "center",
15
+ medium: "flex-start",
16
+ }, children: _jsxs(Box, { padding: 1, children: [_jsx(Typography, { variant: "alpha", children: "Status manager" }), _jsx(StatusManager, {})] }) }) }) }) })] }));
17
+ };
18
+ export { HomePage };
@@ -0,0 +1 @@
1
+ export const PLUGIN_ID = "primershop-status-manager";
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ var permissions = require('./permissions.js');
4
+
5
+ const bootstrap = async ({ strapi })=>{
6
+ await strapi.service("admin::permission").actionProvider.registerMany(permissions.actions);
7
+ // Register lifecycle hooks for status filtering
8
+ strapi.db?.lifecycles?.subscribe?.({
9
+ // catch all models
10
+ models: [
11
+ "*"
12
+ ],
13
+ async afterDelete (event) {
14
+ const modelUid = event?.model?.uid;
15
+ const deleted = event?.result;
16
+ const documentId = deleted?.documentId;
17
+ if (!modelUid || !documentId) return;
18
+ await strapi.db.query("plugin::primershop-status-manager.status-link").deleteMany({
19
+ where: {
20
+ targetUid: modelUid,
21
+ targetDocumentId: documentId
22
+ }
23
+ });
24
+ }
25
+ });
26
+ };
27
+
28
+ exports.bootstrap = bootstrap;
29
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sources":["../../server/src/bootstrap.ts"],"sourcesContent":["import statusActions from \"./permissions\";\r\nimport type { Core } from \"@strapi/strapi\";\r\n\r\nexport const bootstrap = async ({ strapi }: { strapi: Core.Strapi }) => {\r\n await strapi\r\n .service(\"admin::permission\")\r\n .actionProvider.registerMany(statusActions.actions);\r\n\r\n // Register lifecycle hooks for status filtering\r\n strapi.db?.lifecycles?.subscribe?.({\r\n // catch all models\r\n models: [\"*\"],\r\n\r\n async afterDelete(event: {\r\n model?: { uid: string };\r\n result?: { documentId: string };\r\n }) {\r\n const modelUid = event?.model?.uid;\r\n const deleted = event?.result;\r\n const documentId = deleted?.documentId;\r\n if (!modelUid || !documentId) return;\r\n\r\n await strapi.db\r\n .query(\"plugin::primershop-status-manager.status-link\")\r\n .deleteMany({\r\n where: { targetUid: modelUid, targetDocumentId: documentId },\r\n });\r\n },\r\n });\r\n};\r\n"],"names":["bootstrap","strapi","service","actionProvider","registerMany","statusActions","actions","db","lifecycles","subscribe","models","afterDelete","event","modelUid","model","uid","deleted","result","documentId","query","deleteMany","where","targetUid","targetDocumentId"],"mappings":";;;;AAGO,MAAMA,SAAAA,GAAY,OAAO,EAAEC,MAAM,EAA2B,GAAA;IACjE,MAAMA,MAAAA,CACHC,OAAO,CAAC,mBAAA,CAAA,CACRC,cAAc,CAACC,YAAY,CAACC,WAAAA,CAAcC,OAAO,CAAA;;IAGpDL,MAAAA,CAAOM,EAAE,EAAEC,UAAAA,EAAYC,SAAAA,GAAY;;QAEjCC,MAAAA,EAAQ;AAAC,YAAA;AAAI,SAAA;AAEb,QAAA,MAAMC,aAAYC,KAGjB,EAAA;YACC,MAAMC,QAAAA,GAAWD,OAAOE,KAAAA,EAAOC,GAAAA;AAC/B,YAAA,MAAMC,UAAUJ,KAAAA,EAAOK,MAAAA;AACvB,YAAA,MAAMC,aAAaF,OAAAA,EAASE,UAAAA;YAC5B,IAAI,CAACL,QAAAA,IAAY,CAACK,UAAAA,EAAY;AAE9B,YAAA,MAAMjB,OAAOM,EAAE,CACZY,KAAK,CAAC,+CAAA,CAAA,CACNC,UAAU,CAAC;gBACVC,KAAAA,EAAO;oBAAEC,SAAAA,EAAWT,QAAAA;oBAAUU,gBAAAA,EAAkBL;AAAW;AAC7D,aAAA,CAAA;AACJ,QAAA;AACF,KAAA,CAAA;AACF;;;;"}
@@ -0,0 +1,27 @@
1
+ import statusActions from './permissions.mjs';
2
+
3
+ const bootstrap = async ({ strapi })=>{
4
+ await strapi.service("admin::permission").actionProvider.registerMany(statusActions.actions);
5
+ // Register lifecycle hooks for status filtering
6
+ strapi.db?.lifecycles?.subscribe?.({
7
+ // catch all models
8
+ models: [
9
+ "*"
10
+ ],
11
+ async afterDelete (event) {
12
+ const modelUid = event?.model?.uid;
13
+ const deleted = event?.result;
14
+ const documentId = deleted?.documentId;
15
+ if (!modelUid || !documentId) return;
16
+ await strapi.db.query("plugin::primershop-status-manager.status-link").deleteMany({
17
+ where: {
18
+ targetUid: modelUid,
19
+ targetDocumentId: documentId
20
+ }
21
+ });
22
+ }
23
+ });
24
+ };
25
+
26
+ export { bootstrap };
27
+ //# sourceMappingURL=bootstrap.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.mjs","sources":["../../server/src/bootstrap.ts"],"sourcesContent":["import statusActions from \"./permissions\";\r\nimport type { Core } from \"@strapi/strapi\";\r\n\r\nexport const bootstrap = async ({ strapi }: { strapi: Core.Strapi }) => {\r\n await strapi\r\n .service(\"admin::permission\")\r\n .actionProvider.registerMany(statusActions.actions);\r\n\r\n // Register lifecycle hooks for status filtering\r\n strapi.db?.lifecycles?.subscribe?.({\r\n // catch all models\r\n models: [\"*\"],\r\n\r\n async afterDelete(event: {\r\n model?: { uid: string };\r\n result?: { documentId: string };\r\n }) {\r\n const modelUid = event?.model?.uid;\r\n const deleted = event?.result;\r\n const documentId = deleted?.documentId;\r\n if (!modelUid || !documentId) return;\r\n\r\n await strapi.db\r\n .query(\"plugin::primershop-status-manager.status-link\")\r\n .deleteMany({\r\n where: { targetUid: modelUid, targetDocumentId: documentId },\r\n });\r\n },\r\n });\r\n};\r\n"],"names":["bootstrap","strapi","service","actionProvider","registerMany","statusActions","actions","db","lifecycles","subscribe","models","afterDelete","event","modelUid","model","uid","deleted","result","documentId","query","deleteMany","where","targetUid","targetDocumentId"],"mappings":";;AAGO,MAAMA,SAAAA,GAAY,OAAO,EAAEC,MAAM,EAA2B,GAAA;IACjE,MAAMA,MAAAA,CACHC,OAAO,CAAC,mBAAA,CAAA,CACRC,cAAc,CAACC,YAAY,CAACC,aAAAA,CAAcC,OAAO,CAAA;;IAGpDL,MAAAA,CAAOM,EAAE,EAAEC,UAAAA,EAAYC,SAAAA,GAAY;;QAEjCC,MAAAA,EAAQ;AAAC,YAAA;AAAI,SAAA;AAEb,QAAA,MAAMC,aAAYC,KAGjB,EAAA;YACC,MAAMC,QAAAA,GAAWD,OAAOE,KAAAA,EAAOC,GAAAA;AAC/B,YAAA,MAAMC,UAAUJ,KAAAA,EAAOK,MAAAA;AACvB,YAAA,MAAMC,aAAaF,OAAAA,EAASE,UAAAA;YAC5B,IAAI,CAACL,QAAAA,IAAY,CAACK,UAAAA,EAAY;AAE9B,YAAA,MAAMjB,OAAOM,EAAE,CACZY,KAAK,CAAC,+CAAA,CAAA,CACNC,UAAU,CAAC;gBACVC,KAAAA,EAAO;oBAAEC,SAAAA,EAAWT,QAAAA;oBAAUU,gBAAAA,EAAkBL;AAAW;AAC7D,aAAA,CAAA;AACJ,QAAA;AACF,KAAA,CAAA;AACF;;;;"}
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ var status = require('./status.js');
4
+ var statusLink = require('./status-link.js');
5
+
6
+ var contentTypes = {
7
+ status: {
8
+ schema: status
9
+ },
10
+ "status-link": {
11
+ schema: statusLink
12
+ }
13
+ };
14
+
15
+ module.exports = contentTypes;
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../server/src/content-types/index.ts"],"sourcesContent":["import status from \"./status\";\r\nimport statusLink from \"./status-link\";\r\n\r\nexport default {\r\n status: {\r\n schema: status,\r\n },\r\n \"status-link\": {\r\n schema: statusLink,\r\n },\r\n};\r\n"],"names":["status","schema","statusLink"],"mappings":";;;;;AAGA,mBAAe;IACbA,MAAAA,EAAQ;QACNC,MAAAA,EAAQD;AACV,KAAA;IACA,aAAA,EAAe;QACbC,MAAAA,EAAQC;AACV;AACF,CAAA;;;;"}
@@ -0,0 +1,14 @@
1
+ import status from './status.mjs';
2
+ import statusLink from './status-link.mjs';
3
+
4
+ var contentTypes = {
5
+ status: {
6
+ schema: status
7
+ },
8
+ "status-link": {
9
+ schema: statusLink
10
+ }
11
+ };
12
+
13
+ export { contentTypes as default };
14
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../../../server/src/content-types/index.ts"],"sourcesContent":["import status from \"./status\";\r\nimport statusLink from \"./status-link\";\r\n\r\nexport default {\r\n status: {\r\n schema: status,\r\n },\r\n \"status-link\": {\r\n schema: statusLink,\r\n },\r\n};\r\n"],"names":["status","schema","statusLink"],"mappings":";;;AAGA,mBAAe;IACbA,MAAAA,EAAQ;QACNC,MAAAA,EAAQD;AACV,KAAA;IACA,aAAA,EAAe;QACbC,MAAAA,EAAQC;AACV;AACF,CAAA;;;;"}
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ var statusLink = {
4
+ kind: "collectionType",
5
+ collectionName: "status_links",
6
+ info: {
7
+ singularName: "status-link",
8
+ pluralName: "status-links",
9
+ displayName: "Status Link",
10
+ description: "Association between any content type document and a single status"
11
+ },
12
+ options: {
13
+ draftAndPublish: false
14
+ },
15
+ pluginOptions: {
16
+ "content-manager": {
17
+ visible: false
18
+ },
19
+ "content-type-builder": {
20
+ visible: false
21
+ }
22
+ },
23
+ attributes: {
24
+ targetUid: {
25
+ type: "string",
26
+ required: true
27
+ },
28
+ targetDocumentId: {
29
+ type: "string",
30
+ required: true
31
+ },
32
+ status: {
33
+ type: "relation",
34
+ relation: "manyToOne",
35
+ target: "plugin::primershop-status-manager.status",
36
+ required: false,
37
+ configurable: false
38
+ }
39
+ }
40
+ };
41
+
42
+ module.exports = statusLink;
43
+ //# sourceMappingURL=status-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-link.js","sources":["../../../server/src/content-types/status-link.ts"],"sourcesContent":["export default {\r\n kind: \"collectionType\",\r\n collectionName: \"status_links\",\r\n info: {\r\n singularName: \"status-link\",\r\n pluralName: \"status-links\",\r\n displayName: \"Status Link\",\r\n description:\r\n \"Association between any content type document and a single status\",\r\n },\r\n options: {\r\n draftAndPublish: false,\r\n },\r\n pluginOptions: {\r\n \"content-manager\": {\r\n visible: false,\r\n },\r\n \"content-type-builder\": {\r\n visible: false,\r\n },\r\n },\r\n attributes: {\r\n targetUid: {\r\n type: \"string\",\r\n required: true,\r\n },\r\n targetDocumentId: {\r\n type: \"string\",\r\n required: true,\r\n },\r\n status: {\r\n type: \"relation\",\r\n relation: \"manyToOne\",\r\n target: \"plugin::primershop-status-manager.status\",\r\n required: false,\r\n configurable: false,\r\n },\r\n },\r\n} as const;\r\n"],"names":["kind","collectionName","info","singularName","pluralName","displayName","description","options","draftAndPublish","pluginOptions","visible","attributes","targetUid","type","required","targetDocumentId","status","relation","target","configurable"],"mappings":";;AAAA,iBAAe;IACbA,IAAAA,EAAM,gBAAA;IACNC,cAAAA,EAAgB,cAAA;IAChBC,IAAAA,EAAM;QACJC,YAAAA,EAAc,aAAA;QACdC,UAAAA,EAAY,cAAA;QACZC,WAAAA,EAAa,aAAA;QACbC,WAAAA,EACE;AACJ,KAAA;IACAC,OAAAA,EAAS;QACPC,eAAAA,EAAiB;AACnB,KAAA;IACAC,aAAAA,EAAe;QACb,iBAAA,EAAmB;YACjBC,OAAAA,EAAS;AACX,SAAA;QACA,sBAAA,EAAwB;YACtBA,OAAAA,EAAS;AACX;AACF,KAAA;IACAC,UAAAA,EAAY;QACVC,SAAAA,EAAW;YACTC,IAAAA,EAAM,QAAA;YACNC,QAAAA,EAAU;AACZ,SAAA;QACAC,gBAAAA,EAAkB;YAChBF,IAAAA,EAAM,QAAA;YACNC,QAAAA,EAAU;AACZ,SAAA;QACAE,MAAAA,EAAQ;YACNH,IAAAA,EAAM,UAAA;YACNI,QAAAA,EAAU,WAAA;YACVC,MAAAA,EAAQ,0CAAA;YACRJ,QAAAA,EAAU,KAAA;YACVK,YAAAA,EAAc;AAChB;AACF;AACF,CAAA;;;;"}
@@ -0,0 +1,41 @@
1
+ var statusLink = {
2
+ kind: "collectionType",
3
+ collectionName: "status_links",
4
+ info: {
5
+ singularName: "status-link",
6
+ pluralName: "status-links",
7
+ displayName: "Status Link",
8
+ description: "Association between any content type document and a single status"
9
+ },
10
+ options: {
11
+ draftAndPublish: false
12
+ },
13
+ pluginOptions: {
14
+ "content-manager": {
15
+ visible: false
16
+ },
17
+ "content-type-builder": {
18
+ visible: false
19
+ }
20
+ },
21
+ attributes: {
22
+ targetUid: {
23
+ type: "string",
24
+ required: true
25
+ },
26
+ targetDocumentId: {
27
+ type: "string",
28
+ required: true
29
+ },
30
+ status: {
31
+ type: "relation",
32
+ relation: "manyToOne",
33
+ target: "plugin::primershop-status-manager.status",
34
+ required: false,
35
+ configurable: false
36
+ }
37
+ }
38
+ };
39
+
40
+ export { statusLink as default };
41
+ //# sourceMappingURL=status-link.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-link.mjs","sources":["../../../server/src/content-types/status-link.ts"],"sourcesContent":["export default {\r\n kind: \"collectionType\",\r\n collectionName: \"status_links\",\r\n info: {\r\n singularName: \"status-link\",\r\n pluralName: \"status-links\",\r\n displayName: \"Status Link\",\r\n description:\r\n \"Association between any content type document and a single status\",\r\n },\r\n options: {\r\n draftAndPublish: false,\r\n },\r\n pluginOptions: {\r\n \"content-manager\": {\r\n visible: false,\r\n },\r\n \"content-type-builder\": {\r\n visible: false,\r\n },\r\n },\r\n attributes: {\r\n targetUid: {\r\n type: \"string\",\r\n required: true,\r\n },\r\n targetDocumentId: {\r\n type: \"string\",\r\n required: true,\r\n },\r\n status: {\r\n type: \"relation\",\r\n relation: \"manyToOne\",\r\n target: \"plugin::primershop-status-manager.status\",\r\n required: false,\r\n configurable: false,\r\n },\r\n },\r\n} as const;\r\n"],"names":["kind","collectionName","info","singularName","pluralName","displayName","description","options","draftAndPublish","pluginOptions","visible","attributes","targetUid","type","required","targetDocumentId","status","relation","target","configurable"],"mappings":"AAAA,iBAAe;IACbA,IAAAA,EAAM,gBAAA;IACNC,cAAAA,EAAgB,cAAA;IAChBC,IAAAA,EAAM;QACJC,YAAAA,EAAc,aAAA;QACdC,UAAAA,EAAY,cAAA;QACZC,WAAAA,EAAa,aAAA;QACbC,WAAAA,EACE;AACJ,KAAA;IACAC,OAAAA,EAAS;QACPC,eAAAA,EAAiB;AACnB,KAAA;IACAC,aAAAA,EAAe;QACb,iBAAA,EAAmB;YACjBC,OAAAA,EAAS;AACX,SAAA;QACA,sBAAA,EAAwB;YACtBA,OAAAA,EAAS;AACX;AACF,KAAA;IACAC,UAAAA,EAAY;QACVC,SAAAA,EAAW;YACTC,IAAAA,EAAM,QAAA;YACNC,QAAAA,EAAU;AACZ,SAAA;QACAC,gBAAAA,EAAkB;YAChBF,IAAAA,EAAM,QAAA;YACNC,QAAAA,EAAU;AACZ,SAAA;QACAE,MAAAA,EAAQ;YACNH,IAAAA,EAAM,UAAA;YACNI,QAAAA,EAAU,WAAA;YACVC,MAAAA,EAAQ,0CAAA;YACRJ,QAAAA,EAAU,KAAA;YACVK,YAAAA,EAAc;AAChB;AACF;AACF,CAAA;;;;"}
@@ -0,0 +1,50 @@
1
+ 'use strict';
2
+
3
+ var status = {
4
+ kind: "collectionType",
5
+ collectionName: "status",
6
+ info: {
7
+ singularName: "status",
8
+ pluralName: "statuses",
9
+ displayName: "Status",
10
+ description: "Status for products"
11
+ },
12
+ options: {
13
+ draftAndPublish: false
14
+ },
15
+ pluginOptions: {
16
+ "content-manager": {
17
+ visible: false
18
+ },
19
+ "content-type-builder": {
20
+ visible: false
21
+ }
22
+ },
23
+ attributes: {
24
+ name: {
25
+ type: "string",
26
+ minLength: 1,
27
+ maxLength: 50,
28
+ required: true,
29
+ configurable: false
30
+ },
31
+ published: {
32
+ type: "boolean",
33
+ default: false
34
+ },
35
+ order: {
36
+ type: "integer",
37
+ default: 0
38
+ },
39
+ links: {
40
+ type: "relation",
41
+ relation: "oneToMany",
42
+ target: "plugin::primershop-status-manager.status-link",
43
+ mappedBy: "status",
44
+ configurable: false
45
+ }
46
+ }
47
+ };
48
+
49
+ module.exports = status;
50
+ //# sourceMappingURL=status.js.map