@retikolo/drag-drop-content-types 1.3.11 → 1.4.0
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/.prettierrc +7 -0
- package/README.md +4 -0
- package/admin/src/components/SortModal/index.js +13 -9
- package/package.json +3 -2
- package/server/controllers/sort.js +70 -3
- package/server/routes/sort.js +16 -9
package/.prettierrc
ADDED
package/README.md
CHANGED
|
@@ -47,3 +47,7 @@ http://localhost:1337/api/foo?sort=rank:asc
|
|
|
47
47
|
|
|
48
48
|
## 🤝 Contribute
|
|
49
49
|
Feel free to fork and make pull requests to this plugin. All input is welcome - thanks for all contributions so far!
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## ⭐️ Support
|
|
53
|
+
I you like this project, please give it a star. Maybe this will help it getting integrated to strapi's core some day 😊.
|
|
@@ -126,6 +126,7 @@ const SortModal = () => {
|
|
|
126
126
|
try {
|
|
127
127
|
// Increase performance by breaking loop after last element having a rank change is updated
|
|
128
128
|
const sortedList = arrayMoveImmutable(data, oldIndex, newIndex);
|
|
129
|
+
const rankUpdates = [];
|
|
129
130
|
let rankHasChanged = false;
|
|
130
131
|
// Iterate over all results and append them to the list
|
|
131
132
|
for (let i = 0; i < sortedList.length; i++) {
|
|
@@ -133,20 +134,23 @@ const SortModal = () => {
|
|
|
133
134
|
if (sortedList[i].id != data[i].id) {
|
|
134
135
|
const newRank =
|
|
135
136
|
parseInt(pageSize * (currentPage - 1) + i) || 0;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
rankFieldName: settings.rank,
|
|
142
|
-
rank: newRank,
|
|
143
|
-
}
|
|
144
|
-
);
|
|
137
|
+
const update = {
|
|
138
|
+
id: sortedList[i].id,
|
|
139
|
+
rank: newRank,
|
|
140
|
+
};
|
|
141
|
+
rankUpdates.push(update);
|
|
145
142
|
rankHasChanged = true;
|
|
146
143
|
} else if (rankHasChanged) {
|
|
147
144
|
break;
|
|
148
145
|
}
|
|
149
146
|
}
|
|
147
|
+
|
|
148
|
+
// Batch Update DB with new ranks
|
|
149
|
+
await axiosInstance.put("/drag-drop-content-types/batch-update", {
|
|
150
|
+
contentType: contentTypePath,
|
|
151
|
+
updates: rankUpdates,
|
|
152
|
+
});
|
|
153
|
+
|
|
150
154
|
// distinguish last page from full/first page
|
|
151
155
|
let sortedListViewEntries =
|
|
152
156
|
currentPage == 1
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retikolo/drag-drop-content-types",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "This plugin add a drag and droppable list that allows you to sort content type entries.",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"name": "drag-drop-content-types",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"array-move": "^4.0.0",
|
|
20
|
-
"react-sortable-hoc": "^2.0.0"
|
|
20
|
+
"react-sortable-hoc": "^2.0.0",
|
|
21
|
+
"zod": "^3.20.2"
|
|
21
22
|
},
|
|
22
23
|
"author": {
|
|
23
24
|
"name": "Aeneas Meier",
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
4
|
+
const { z } = require('zod');
|
|
5
|
+
|
|
3
6
|
// Get the store from the drag-drop plugin
|
|
4
7
|
function getPluginStore() {
|
|
5
8
|
return strapi.store({
|
|
@@ -17,6 +20,7 @@ async function createDefaultConfig() {
|
|
|
17
20
|
rank: 'rank',
|
|
18
21
|
title: '',
|
|
19
22
|
}
|
|
23
|
+
|
|
20
24
|
};
|
|
21
25
|
await pluginStore.set({ key: 'settings', value });
|
|
22
26
|
return pluginStore.get({ key: 'settings' });
|
|
@@ -43,7 +47,7 @@ async function setSettings(settings) {
|
|
|
43
47
|
// Search for entries ordered by rank
|
|
44
48
|
async function index(contentType, start, limit, locale, rankFieldName) {
|
|
45
49
|
let indexData = {
|
|
46
|
-
sort: {
|
|
50
|
+
sort: {},
|
|
47
51
|
populate: '*',
|
|
48
52
|
start: start,
|
|
49
53
|
limit: limit,
|
|
@@ -51,7 +55,7 @@ async function index(contentType, start, limit, locale, rankFieldName) {
|
|
|
51
55
|
}
|
|
52
56
|
indexData.sort[rankFieldName] = 'asc'
|
|
53
57
|
try {
|
|
54
|
-
return await strapi.entityService.findMany(contentType, indexData
|
|
58
|
+
return await strapi.entityService.findMany(contentType, indexData);
|
|
55
59
|
} catch (err) {
|
|
56
60
|
return {};
|
|
57
61
|
}
|
|
@@ -67,6 +71,40 @@ async function update(id, contentType, rank, rankFieldName) {
|
|
|
67
71
|
return await strapi.query(contentType).update(updateData);
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
*
|
|
76
|
+
* @param {RankUpdate[]} updates
|
|
77
|
+
* @param {string} contentType
|
|
78
|
+
*/
|
|
79
|
+
async function batchUpdate(updates, contentType) {
|
|
80
|
+
const config = await getSettings();
|
|
81
|
+
const sortFieldName = config.body.rank;
|
|
82
|
+
const results = [];
|
|
83
|
+
|
|
84
|
+
for (const update of updates) {
|
|
85
|
+
// update entry's rank in db
|
|
86
|
+
try {
|
|
87
|
+
const updatedEntry = await strapi.db.query(contentType).update({
|
|
88
|
+
where: { id: update.id },
|
|
89
|
+
data: {
|
|
90
|
+
[sortFieldName]: update.rank,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
updatedEntry?.id && results.push(updatedEntry);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.log(err);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (results?.length !== updates?.length) {
|
|
99
|
+
throw new Error('Error updating rank entries.');
|
|
100
|
+
} else {
|
|
101
|
+
return results.map((entry) => ({
|
|
102
|
+
id: entry.id,
|
|
103
|
+
rank: entry[sortFieldName],
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
70
108
|
module.exports = {
|
|
71
109
|
async getSettings(ctx) {
|
|
72
110
|
try {
|
|
@@ -98,4 +136,33 @@ module.exports = {
|
|
|
98
136
|
ctx.throw(500, err);
|
|
99
137
|
}
|
|
100
138
|
},
|
|
101
|
-
|
|
139
|
+
async batchUpdate(ctx) {
|
|
140
|
+
try {
|
|
141
|
+
const payload = await BatchUpdateRequestSchema.parseAsync(
|
|
142
|
+
ctx.request.body
|
|
143
|
+
);
|
|
144
|
+
try {
|
|
145
|
+
ctx.body = await batchUpdate(payload.updates, payload.contentType);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
ctx.throw(500, err);
|
|
148
|
+
}
|
|
149
|
+
} catch (err) {
|
|
150
|
+
ctx.throw(400, err);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// TYPE-SAFETY AND VALIDATION
|
|
156
|
+
|
|
157
|
+
// Request schema used for parsing/validating incoming API requests. Uses Zod which works well with Typescript if used in the future.
|
|
158
|
+
const RankUpdateSchema = z.object({
|
|
159
|
+
id: z.number(),
|
|
160
|
+
rank: z.number(),
|
|
161
|
+
});
|
|
162
|
+
const BatchUpdateRequestSchema = z.object({
|
|
163
|
+
contentType: z.string(),
|
|
164
|
+
updates: z.array(RankUpdateSchema),
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Try to enforce Typescript types in Javascript using JSDoc
|
|
168
|
+
/** @typedef { z.infer<typeof RankUpdateSchema> } RankUpdate */
|
package/server/routes/sort.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
1
|
module.exports = {
|
|
4
2
|
type: 'admin',
|
|
5
3
|
routes: [
|
|
@@ -22,22 +20,31 @@ module.exports = {
|
|
|
22
20
|
},
|
|
23
21
|
},
|
|
24
22
|
{
|
|
25
|
-
method:
|
|
26
|
-
path:
|
|
27
|
-
handler:
|
|
23
|
+
method: 'POST',
|
|
24
|
+
path: '/sort-index',
|
|
25
|
+
handler: 'sort.index',
|
|
26
|
+
config: {
|
|
27
|
+
policies: [],
|
|
28
|
+
auth: false,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
method: 'PUT',
|
|
33
|
+
path: '/batch-update',
|
|
34
|
+
handler: 'sort.batchUpdate',
|
|
28
35
|
config: {
|
|
29
36
|
policies: [],
|
|
30
37
|
auth: false,
|
|
31
38
|
},
|
|
32
39
|
},
|
|
33
40
|
{
|
|
34
|
-
method:
|
|
35
|
-
path:
|
|
36
|
-
handler:
|
|
41
|
+
method: 'PUT',
|
|
42
|
+
path: '/sort-update/:id',
|
|
43
|
+
handler: 'sort.update',
|
|
37
44
|
config: {
|
|
38
45
|
policies: [],
|
|
39
46
|
auth: false,
|
|
40
47
|
},
|
|
41
48
|
},
|
|
42
49
|
],
|
|
43
|
-
};
|
|
50
|
+
};
|