@primershop/strapi-plugin-status-manager 0.0.2 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/dist/admin/src/components/Initializer.js +0 -13
- package/dist/admin/src/components/PluginIcon.js +0 -4
- package/dist/admin/src/components/ProductStatusField.js +0 -64
- package/dist/admin/src/components/StatusManager.js +0 -239
- package/dist/admin/src/index.js +0 -43
- package/dist/admin/src/listView/StatusFilter.js +0 -47
- package/dist/admin/src/listView/add-status-column-hook.js +0 -18
- package/dist/admin/src/listView/status-cell.js +0 -18
- package/dist/admin/src/pages/HomePage.js +0 -18
- package/dist/admin/src/pluginId.js +0 -1
- package/dist/server/src/bootstrap.js +0 -32
- package/dist/server/src/config/dev.js +0 -13
- package/dist/server/src/content-types/index.js +0 -15
- package/dist/server/src/content-types/status-link.js +0 -40
- package/dist/server/src/content-types/status.js +0 -47
- package/dist/server/src/controllers/content.js +0 -44
- package/dist/server/src/controllers/index.js +0 -9
- package/dist/server/src/controllers/status.js +0 -106
- package/dist/server/src/index.js +0 -23
- package/dist/server/src/middlewares/add-status-field.js +0 -51
- package/dist/server/src/middlewares/filter-by-status.js +0 -44
- package/dist/server/src/middlewares/filter-published.js +0 -31
- package/dist/server/src/permissions.js +0 -41
- package/dist/server/src/pluginId.js +0 -4
- package/dist/server/src/register.js +0 -28
- package/dist/server/src/routes/content-management.js +0 -34
- package/dist/server/src/routes/index.js +0 -15
- package/dist/server/src/routes/status-management.js +0 -109
- package/dist/server/src/services/index.js +0 -9
- package/dist/server/src/services/status-link.js +0 -33
- package/dist/server/src/services/status.js +0 -96
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primershop/strapi-plugin-status-manager",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Enables more status variations for Strapi",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./strapi-admin": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist/"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"build": "npm-run-all clean --parallel build:code
|
|
22
|
+
"build": "npm-run-all clean --parallel build:code",
|
|
23
23
|
"build:code": "rollup -c",
|
|
24
24
|
"build:dev": "rollup -c rollup.config.dev.mjs",
|
|
25
25
|
"build:types": "run-p build:types:server build:types:admin",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"keywords": [
|
|
39
39
|
"strapi",
|
|
40
40
|
"plugin",
|
|
41
|
+
"strapi-plugin",
|
|
41
42
|
"status",
|
|
42
43
|
"manager"
|
|
43
44
|
],
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from "react";
|
|
2
|
-
import { PLUGIN_ID } from "../pluginId";
|
|
3
|
-
/**
|
|
4
|
-
* @type {import('react').FC<{ setPlugin: (id: string) => void }>}
|
|
5
|
-
*/
|
|
6
|
-
const Initializer = ({ setPlugin }) => {
|
|
7
|
-
const ref = useRef(setPlugin);
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
ref.current(PLUGIN_ID);
|
|
10
|
-
}, []);
|
|
11
|
-
return null;
|
|
12
|
-
};
|
|
13
|
-
export { Initializer };
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState, useCallback } from "react";
|
|
3
|
-
import { SingleSelect, SingleSelectOption, Box, Typography, Flex, } from "@strapi/design-system";
|
|
4
|
-
import { useFetchClient, unstable_useContentManagerContext as useContentManagerContext, } from "@strapi/strapi/admin";
|
|
5
|
-
const ProductStatusField = () => {
|
|
6
|
-
const { contentType, id } = useContentManagerContext();
|
|
7
|
-
const [statuses, setStatuses] = useState([]);
|
|
8
|
-
const [currentStatus, setCurrentStatus] = useState("");
|
|
9
|
-
const [message, setMessage] = useState("");
|
|
10
|
-
const { get, put } = useFetchClient();
|
|
11
|
-
const handleStatusChange = useCallback(async (newStatus) => {
|
|
12
|
-
if (!id) {
|
|
13
|
-
setMessage("Save the product first and then change the status");
|
|
14
|
-
return;
|
|
15
|
-
}
|
|
16
|
-
try {
|
|
17
|
-
await put(`primershop-status-manager/content-status`, {
|
|
18
|
-
contentTypeUid: "api::product.product",
|
|
19
|
-
contentDocumentId: id,
|
|
20
|
-
statusId: statuses.find((status) => status.name === newStatus)
|
|
21
|
-
?.documentId,
|
|
22
|
-
});
|
|
23
|
-
setMessage(`Status updated ${currentStatus ? `from ${currentStatus}` : ""} to ${newStatus}`);
|
|
24
|
-
setCurrentStatus(newStatus || "");
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
setMessage("Error updating status");
|
|
28
|
-
console.error("Error updating status:", error);
|
|
29
|
-
}
|
|
30
|
-
}, [id, statuses, currentStatus, put]);
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
async function fetchCurrentStatus() {
|
|
33
|
-
try {
|
|
34
|
-
const { data: productData } = await get(`primershop-status-manager/content-status?contentDocumentId=${id}&contentTypeUid=api::product.product`);
|
|
35
|
-
const status = productData?.status;
|
|
36
|
-
if (status && status.name)
|
|
37
|
-
return setCurrentStatus(status.name);
|
|
38
|
-
if (statuses.length)
|
|
39
|
-
return handleStatusChange(statuses[0].name);
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
console.error("Error fetching product status:", error);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (id && !currentStatus.length)
|
|
46
|
-
fetchCurrentStatus();
|
|
47
|
-
if (!id && statuses.length)
|
|
48
|
-
setCurrentStatus(statuses[0].name);
|
|
49
|
-
}, [id, statuses, get]);
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
async function fetchStatuses() {
|
|
52
|
-
try {
|
|
53
|
-
const { data } = await get("primershop-status-manager/statuses");
|
|
54
|
-
setStatuses(data);
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
console.error("Error fetching statuses:", error);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
fetchStatuses();
|
|
61
|
-
}, [get]);
|
|
62
|
-
return (_jsxs(Flex, { direction: "column", justifyContent: "center", alignItems: "stretch", width: "100%", children: [_jsx(Box, { padding: 2, children: _jsxs(Typography, { variant: "sigma", children: [contentType?.info.displayName, " status"] }) }), _jsx(SingleSelect, { placeholder: currentStatus, onChange: handleStatusChange, children: statuses.map((status) => (_jsx(SingleSelectOption, { value: status.name, children: status.name }, status.documentId))) }), _jsx(Box, { padding: 2, children: _jsx(Typography, { variant: "sigma", children: message }) })] }));
|
|
63
|
-
};
|
|
64
|
-
export { ProductStatusField };
|
|
@@ -1,239 +0,0 @@
|
|
|
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 };
|
package/dist/admin/src/index.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
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;
|
|
@@ -1,47 +0,0 @@
|
|
|
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 };
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
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 };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const PLUGIN_ID = "primershop-status-manager";
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.bootstrap = void 0;
|
|
7
|
-
const permissions_1 = __importDefault(require("./permissions"));
|
|
8
|
-
const bootstrap = async ({ strapi }) => {
|
|
9
|
-
var _a, _b, _c;
|
|
10
|
-
await strapi
|
|
11
|
-
.service("admin::permission")
|
|
12
|
-
.actionProvider.registerMany(permissions_1.default.actions);
|
|
13
|
-
// Register lifecycle hooks for status filtering
|
|
14
|
-
(_c = (_b = (_a = strapi.db) === null || _a === void 0 ? void 0 : _a.lifecycles) === null || _b === void 0 ? void 0 : _b.subscribe) === null || _c === void 0 ? void 0 : _c.call(_b, {
|
|
15
|
-
// catch all models
|
|
16
|
-
models: ["*"],
|
|
17
|
-
async afterDelete(event) {
|
|
18
|
-
var _a;
|
|
19
|
-
const modelUid = (_a = event === null || event === void 0 ? void 0 : event.model) === null || _a === void 0 ? void 0 : _a.uid;
|
|
20
|
-
const deleted = event === null || event === void 0 ? void 0 : event.result;
|
|
21
|
-
const documentId = deleted === null || deleted === void 0 ? void 0 : deleted.documentId;
|
|
22
|
-
if (!modelUid || !documentId)
|
|
23
|
-
return;
|
|
24
|
-
await strapi.db
|
|
25
|
-
.query("plugin::primershop-status-manager.status-link")
|
|
26
|
-
.deleteMany({
|
|
27
|
-
where: { targetUid: modelUid, targetDocumentId: documentId },
|
|
28
|
-
});
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
};
|
|
32
|
-
exports.bootstrap = bootstrap;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isDebug = exports.isDevelopment = exports.devConfig = void 0;
|
|
4
|
-
exports.devConfig = {
|
|
5
|
-
debug: true,
|
|
6
|
-
logLevel: "verbose",
|
|
7
|
-
enableSourceMaps: true,
|
|
8
|
-
preserveStackTraces: true,
|
|
9
|
-
enableConsoleLogs: true,
|
|
10
|
-
enablePerformanceLogs: true,
|
|
11
|
-
};
|
|
12
|
-
exports.isDevelopment = process.env.NODE_ENV === "development";
|
|
13
|
-
exports.isDebug = process.env.DEBUG === "true" || exports.isDevelopment;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const status_1 = __importDefault(require("./status"));
|
|
7
|
-
const status_link_1 = __importDefault(require("./status-link"));
|
|
8
|
-
exports.default = {
|
|
9
|
-
status: {
|
|
10
|
-
schema: status_1.default,
|
|
11
|
-
},
|
|
12
|
-
"status-link": {
|
|
13
|
-
schema: status_link_1.default,
|
|
14
|
-
},
|
|
15
|
-
};
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = {
|
|
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
|
-
};
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = {
|
|
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
|
-
};
|