@yunusemrejs/drag-drop-content-types-strapi5
Version:
A powerful Strapi plugin that enables intuitive drag-and-drop sorting of content types with real-time updates, optimized performance, and a seamless user experience.
301 lines (300 loc) • 8.02 kB
JavaScript
;
const zod = require("zod");
const bootstrap = ({ strapi: strapi2 }) => {
};
const destroy = ({ strapi: strapi2 }) => {
};
const register = ({ strapi: strapi2 }) => {
};
const config = {
default: {},
validator() {
}
};
const contentTypes = {};
const RankUpdateSchema = zod.z.object({ id: zod.z.number(), rank: zod.z.number() });
const BatchUpdateRequestSchema = zod.z.object({
contentType: zod.z.string(),
updates: zod.z.array(RankUpdateSchema)
});
const controller = ({ strapi: strapi2 }) => ({
async welcome(ctx) {
const dragdropService = strapi2.plugin("drag-drop-content-types-strapi5").service("dragdrop");
try {
ctx.body = await dragdropService.getWelcomeMessage();
} catch (err) {
ctx.throw(500, err);
}
},
async sortIndex(ctx) {
const dragdropService = strapi2.plugin("drag-drop-content-types-strapi5").service("dragdrop");
try {
ctx.body = await dragdropService.sortIndex(
ctx.request.body.contentType,
ctx.request.body.start,
ctx.request.body.limit,
ctx.request.body.locale,
ctx.request.body.rankFieldName
);
} catch (err) {
ctx.throw(500, err);
}
},
async batchUpdate(ctx) {
const settingService = strapi2.plugin("drag-drop-content-types-strapi5").service("settings");
const dragdropService = strapi2.plugin("drag-drop-content-types-strapi5").service("dragdrop");
try {
const config2 = await settingService.getSettings();
const payload = await BatchUpdateRequestSchema.parseAsync(ctx.request.body);
try {
ctx.body = await dragdropService.batchUpdate(config2, payload.updates, payload.contentType);
} catch (err) {
ctx.throw(500, err);
}
} catch (err) {
ctx.throw(400, err);
}
}
});
const settings$2 = ({ strapi: strapi2 }) => ({
/**
* Gets the settings.
* @async
* @param {object} ctx - The context object.
* @returns {Promise<void>}
* @throws {Error}
*/
async getSettings(ctx) {
const settingService = strapi2.plugin("drag-drop-content-types-strapi5").service("settings");
try {
ctx.body = await settingService.getSettings();
} catch (err) {
ctx.throw(500, err);
}
},
/**
* Sets the settings.
* @async
* @param {object} ctx - The context object.
* @returns {Promise<void>}
* @throws {Error}
*/
async setSettings(ctx) {
const settingService = strapi2.plugin("drag-drop-content-types-strapi5").service("settings");
const { body } = ctx.request;
try {
await settingService.setSettings(body);
ctx.body = await settingService.getSettings();
} catch (err) {
ctx.throw(500, err);
}
}
});
const controllers = {
dragdrop: controller,
settings: settings$2
};
const middlewares = {};
const policies = {};
const dragdrop$1 = {
//type: admin: internal and can be accessible only by the admin part (front-end part) of the plugin
//type: content-api: accessible from external classical rest api, need to set access in strapi's Users & Permissions plugin
//call: http://localhost:1337/api/drag-drop-content-types-strapi5/ and you'll receive getWelcomeMessage()
type: "admin",
//changed from content-api to admin
routes: [
{
method: "GET",
path: "/welcome",
handler: "dragdrop.welcome",
config: {
policies: [],
auth: false
}
},
{
method: "POST",
path: "/sort-index",
handler: "dragdrop.sortIndex",
config: {
policies: [],
auth: false
}
},
{
method: "PUT",
path: "/batch-update",
handler: "dragdrop.batchUpdate",
config: {
policies: [],
auth: false
}
}
]
};
const settings$1 = {
type: "admin",
routes: [
{
method: "GET",
path: "/settings",
handler: "settings.getSettings",
config: {
policies: []
}
},
{
method: "POST",
path: "/settings",
handler: "settings.setSettings",
config: {
policies: []
}
}
]
};
const routes = {
dragdrop: dragdrop$1,
settings: settings$1
};
const dragdrop = ({ strapi: strapi2 }) => ({
/**
* Returns a welcome message.
* @returns {{ body: string }}
*/
getWelcomeMessage() {
return {
body: "Welcome to Strapi 🚀"
};
},
/**
* Retrieves sorted index data for a content type.
* @async
* @param {StrapiTypes.UID.CollectionType} contentType - The content type UID.
* @param {number} start - The start index.
* @param {number} limit - The limit of results.
* @param {string} locale - The locale.
* @param {string} rankFieldName - The field name to sort by.
* @returns {Promise<any>}
*/
async sortIndex(contentType, start, limit, locale, rankFieldName) {
let indexData = {
sort: {},
populate: "*",
start,
limit,
locale
};
indexData.sort[rankFieldName] = "asc";
try {
return await strapi2.documents(contentType).findMany(indexData);
} catch (err) {
return {};
}
},
/**
* Updates the rank of multiple entries in a content type.
* @async
* @param {PluginSettingsResponse} config - The plugin settings.
* @param {RankUpdate[]} updates - The array of rank updates.
* @param {StrapiTypes.UID.CollectionType} contentType - The content type UID.
* @returns {Promise<any>}
*/
async batchUpdate(config2, updates, contentType) {
const shouldTriggerWebhooks = config2.body.triggerWebhooks;
const sortFieldName = config2.body.rank;
const results = [];
strapi2["apiUpdate"] = true;
for (const update of updates) {
const allLocalizations = await strapi2.db.query(contentType).findOne({
where: { id: update.id },
populate: ["localizations"]
});
const { localizations, ...origin } = allLocalizations;
for (const entry of [origin, ...localizations]) {
const wasPublished = entry.publishedAt !== null;
const updatedEntry = await strapi2.db.query(contentType).update({
where: { id: entry.id },
data: {
[sortFieldName]: update.rank,
publishedAt: wasPublished ? /* @__PURE__ */ new Date() : entry.publishedAt
}
});
if (updatedEntry?.id) {
results.push(updatedEntry);
}
}
if (shouldTriggerWebhooks) {
const info = {
model: contentType.split(".").at(-1),
entry: {
id: origin.id,
...origin
}
};
await strapi2.get("webhookRunner").executeListener({
event: "entry.update",
info
});
}
}
strapi2["apiUpdate"] = void 0;
return results.map((entry) => ({
id: entry.id,
rank: entry[sortFieldName]
}));
}
});
const getPluginStore = () => {
return strapi.store({
environment: "",
type: "plugin",
name: "drag-drop-content-types-strapi5"
});
};
const createDefaultConfig = async () => {
const pluginStore = getPluginStore();
const value = {
body: {
rank: "rank",
title: "",
subtitle: "",
triggerWebhooks: false
}
};
await pluginStore.set({ key: "settings", value });
return pluginStore.get({ key: "settings" });
};
const settings = ({ strapi: strapi2 }) => ({
async getSettings() {
const pluginStore = getPluginStore();
let config2 = await pluginStore.get({ key: "settings" });
if (!config2) {
config2 = await createDefaultConfig();
}
return config2;
},
async setSettings(settings2) {
const value = settings2;
const pluginStore = getPluginStore();
await pluginStore.set({ key: "settings", value });
return pluginStore.get({ key: "settings" });
}
});
const services = {
dragdrop,
settings
};
const index = {
register,
bootstrap,
destroy,
config,
controllers,
routes,
services,
contentTypes,
policies,
middlewares
};
module.exports = index;