@mattisvensson/strapi-plugin-webatlas 0.1.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.
Files changed (94) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +140 -0
  3. package/dist/_chunks/_commonjsHelpers-BxmBWJD2.mjs +33 -0
  4. package/dist/_chunks/_commonjsHelpers-Cq6wktVC.js +32 -0
  5. package/dist/_chunks/en-B4KWt_jN.js +4 -0
  6. package/dist/_chunks/en-Byx4XI2L.mjs +4 -0
  7. package/dist/_chunks/index-B76GOOtR.js +18024 -0
  8. package/dist/_chunks/index-B8hoZtTR.js +47 -0
  9. package/dist/_chunks/index-B8i-28Jt.mjs +774 -0
  10. package/dist/_chunks/index-BFv_JAKx.mjs +6814 -0
  11. package/dist/_chunks/index-CJUhT0VV.js +773 -0
  12. package/dist/_chunks/index-D9yK1vNq.js +6814 -0
  13. package/dist/_chunks/index-DClS2XL-.mjs +47 -0
  14. package/dist/_chunks/index-DbKqIKw_.mjs +17984 -0
  15. package/dist/admin/index.js +4 -0
  16. package/dist/admin/index.mjs +5 -0
  17. package/dist/admin/src/components/CMEditViewAside/Alias.d.ts +5 -0
  18. package/dist/admin/src/components/CMEditViewAside/index.d.ts +2 -0
  19. package/dist/admin/src/components/Initializer.d.ts +5 -0
  20. package/dist/admin/src/components/PluginIcon.d.ts +3 -0
  21. package/dist/admin/src/components/Tooltip.d.ts +3 -0
  22. package/dist/admin/src/components/UI/Center.d.ts +5 -0
  23. package/dist/admin/src/components/URLInfo.d.ts +6 -0
  24. package/dist/admin/src/components/modals/Delete.d.ts +14 -0
  25. package/dist/admin/src/components/modals/NavCreate.d.ts +1 -0
  26. package/dist/admin/src/components/modals/NavEdit.d.ts +7 -0
  27. package/dist/admin/src/components/modals/NavModal.d.ts +15 -0
  28. package/dist/admin/src/components/modals/NavOverview.d.ts +8 -0
  29. package/dist/admin/src/components/modals/externalItem/index.d.ts +9 -0
  30. package/dist/admin/src/components/modals/index.d.ts +12 -0
  31. package/dist/admin/src/components/modals/internalItem/internalItemCreate.d.ts +3 -0
  32. package/dist/admin/src/components/modals/internalItem/internalItemEdit.d.ts +3 -0
  33. package/dist/admin/src/components/modals/useModalSharedLogic.d.ts +2 -0
  34. package/dist/admin/src/components/modals/withModalSharedLogic.d.ts +3 -0
  35. package/dist/admin/src/components/modals/wrapperItem/index.d.ts +9 -0
  36. package/dist/admin/src/contexts/index.d.ts +13 -0
  37. package/dist/admin/src/hooks/index.d.ts +6 -0
  38. package/dist/admin/src/hooks/useAllContentTypes.d.ts +7 -0
  39. package/dist/admin/src/hooks/useAllEntities.d.ts +7 -0
  40. package/dist/admin/src/hooks/useApi.d.ts +15 -0
  41. package/dist/admin/src/hooks/useNavigations.d.ts +5 -0
  42. package/dist/admin/src/hooks/usePluginConfig.d.ts +9 -0
  43. package/dist/admin/src/index.d.ts +12 -0
  44. package/dist/admin/src/pages/Navigation/EmptyNav.d.ts +5 -0
  45. package/dist/admin/src/pages/Navigation/Header.d.ts +6 -0
  46. package/dist/admin/src/pages/Navigation/RouteItem.d.ts +15 -0
  47. package/dist/admin/src/pages/Navigation/SortableRouteItem.d.ts +2 -0
  48. package/dist/admin/src/pages/Navigation/index.d.ts +2 -0
  49. package/dist/admin/src/pages/Routes/index.d.ts +2 -0
  50. package/dist/admin/src/pages/Settings/index.d.ts +2 -0
  51. package/dist/admin/src/utils/countChildren.d.ts +2 -0
  52. package/dist/admin/src/utils/debounce.d.ts +1 -0
  53. package/dist/admin/src/utils/dnd.d.ts +13 -0
  54. package/dist/admin/src/utils/duplicateCheck.d.ts +8 -0
  55. package/dist/admin/src/utils/getTranslation.d.ts +2 -0
  56. package/dist/admin/src/utils/index.d.ts +6 -0
  57. package/dist/admin/src/utils/typeChecks.d.ts +3 -0
  58. package/dist/pluginId.d.ts +3 -0
  59. package/dist/server/index.js +1861 -0
  60. package/dist/server/index.mjs +1862 -0
  61. package/dist/server/src/bootstrap.d.ts +5 -0
  62. package/dist/server/src/config/index.d.ts +5 -0
  63. package/dist/server/src/content-types/index.d.ts +189 -0
  64. package/dist/server/src/content-types/navigation/index.d.ts +49 -0
  65. package/dist/server/src/content-types/navigation/schema.d.ts +47 -0
  66. package/dist/server/src/content-types/navitem/index.d.ts +50 -0
  67. package/dist/server/src/content-types/navitem/schema.d.ts +48 -0
  68. package/dist/server/src/content-types/route/index.d.ts +90 -0
  69. package/dist/server/src/content-types/route/schema.d.ts +88 -0
  70. package/dist/server/src/controllers/admin.d.ts +16 -0
  71. package/dist/server/src/controllers/client.d.ts +8 -0
  72. package/dist/server/src/controllers/index.d.ts +24 -0
  73. package/dist/server/src/destroy.d.ts +5 -0
  74. package/dist/server/src/index.d.ts +279 -0
  75. package/dist/server/src/middlewares/index.d.ts +2 -0
  76. package/dist/server/src/policies/index.d.ts +2 -0
  77. package/dist/server/src/register.d.ts +5 -0
  78. package/dist/server/src/routes/admin.d.ts +13 -0
  79. package/dist/server/src/routes/client.d.ts +12 -0
  80. package/dist/server/src/routes/index.d.ts +26 -0
  81. package/dist/server/src/services/admin.d.ts +19 -0
  82. package/dist/server/src/services/client.d.ts +8 -0
  83. package/dist/server/src/services/index.d.ts +26 -0
  84. package/dist/server/src/utils/duplicateCheck.d.ts +1 -0
  85. package/dist/server/src/utils/pluginHelpers.d.ts +6 -0
  86. package/dist/utils/buildStructuredNavigation.d.ts +11 -0
  87. package/dist/utils/cleanRootKeys.d.ts +1 -0
  88. package/dist/utils/extractRouteAndItems.d.ts +2 -0
  89. package/dist/utils/getFullPath.d.ts +1 -0
  90. package/dist/utils/index.d.ts +9 -0
  91. package/dist/utils/populateDeep.d.ts +4 -0
  92. package/dist/utils/removeWaFields.d.ts +1 -0
  93. package/dist/utils/transformToUrl.d.ts +1 -0
  94. package/package.json +102 -0
@@ -0,0 +1,773 @@
1
+ "use strict";
2
+ const jsxRuntime = require("react/jsx-runtime");
3
+ const React = require("react");
4
+ const icons = require("@strapi/icons");
5
+ const admin = require("@strapi/strapi/admin");
6
+ const designSystem = require("@strapi/design-system");
7
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
8
+ const v = glob[path];
9
+ if (v) {
10
+ return typeof v === "function" ? v() : Promise.resolve(v);
11
+ }
12
+ return new Promise((_, reject) => {
13
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
14
+ reject.bind(
15
+ null,
16
+ new Error(
17
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
18
+ )
19
+ )
20
+ );
21
+ });
22
+ };
23
+ const version = "0.1.0";
24
+ const keywords = [];
25
+ const type = "commonjs";
26
+ const exports$1 = {
27
+ "./package.json": "./package.json",
28
+ "./strapi-admin": {
29
+ types: "./dist/admin/src/index.d.ts",
30
+ source: "./admin/src/index.tsx",
31
+ "import": "./dist/admin/index.mjs",
32
+ require: "./dist/admin/index.js",
33
+ "default": "./dist/admin/index.js"
34
+ },
35
+ "./strapi-server": {
36
+ types: "./dist/server/src/index.d.ts",
37
+ source: "./server/src/index.ts",
38
+ "import": "./dist/server/index.mjs",
39
+ require: "./dist/server/index.js",
40
+ "default": "./dist/server/index.js"
41
+ }
42
+ };
43
+ const files = [
44
+ "dist"
45
+ ];
46
+ const scripts = {
47
+ build: "strapi-plugin build && yalc push --publish",
48
+ watch: "strapi-plugin watch",
49
+ "watch:link": "strapi-plugin watch:link",
50
+ verify: "strapi-plugin verify",
51
+ "test:ts:front": "tsc -p admin/tsconfig.json",
52
+ "test:ts:back": "tsc -p server/tsconfig.json",
53
+ "test:jest": "ENV_PATH=./playground/.env jest --verbose --runInBand --forceExit",
54
+ "test:cypress": "cypress run",
55
+ "test:cypress:open": "cypress open",
56
+ "playground:install": "yarn playground:yalc-add-link && cd playground && yarn install",
57
+ "playground:yalc-add": "cd playground && yalc add strapi-plugin-boilerplate",
58
+ "playground:yalc-add-link": "cd playground && yalc add --link strapi-plugin-boilerplate",
59
+ "playground:build": "cd playground && yarn build",
60
+ "playground:develop": "cd playground && yarn develop",
61
+ "playground:start": "cd playground && yarn start"
62
+ };
63
+ const dependencies = {
64
+ "@dnd-kit/core": "^6.3.1",
65
+ "@dnd-kit/sortable": "^10.0.0",
66
+ "@dnd-kit/utilities": "^3.2.2",
67
+ "@strapi/design-system": "^2.0.0-rc.14",
68
+ "@strapi/icons": "^2.0.0-rc.14",
69
+ "react-intl": "^7.1.0"
70
+ };
71
+ const devDependencies = {
72
+ "@strapi/sdk-plugin": "^5.0.0",
73
+ "@strapi/strapi": "^5.0.0",
74
+ "@strapi/typescript-utils": "^5.0.0",
75
+ "@types/react": "^19.0.0",
76
+ "@types/react-dom": "^19.0.0",
77
+ cypress: "^13.9.0",
78
+ "cypress-terminal-report": "^6.0.2",
79
+ jest: "^29.7.0",
80
+ prettier: "^3.4.2",
81
+ react: "^19.0.0",
82
+ "react-router-dom": "^6.0.0",
83
+ supertest: "^7.0.0",
84
+ typescript: "^5.7.2"
85
+ };
86
+ const peerDependencies = {
87
+ "@strapi/strapi": "^5.0.0",
88
+ react: "^17.0.0 || ^18.0.0",
89
+ "react-dom": "^17.0.0 || ^18.0.0",
90
+ "react-router-dom": "^6.0.0",
91
+ "styled-components": "^6.0.0"
92
+ };
93
+ const description = "A strapi plugin to manage URL routes and navigations.";
94
+ const strapi = {
95
+ name: "webatlas",
96
+ displayName: "Webatlas",
97
+ description: "A strapi plugin to manage URL routes and navigations.",
98
+ kind: "plugin"
99
+ };
100
+ const name = "@mattisvensson/strapi-plugin-webatlas";
101
+ const license = "MIT";
102
+ const repository = {
103
+ type: "git",
104
+ url: "git+ssh://git@github.com:mattisvensson/strapi-plugin-webatlas.git"
105
+ };
106
+ const bugs = {
107
+ url: "https://github.com/mattisvensson/strapi-plugin-webatlas/issues"
108
+ };
109
+ const homepage = "https://github.com/mattisvensson/strapi-plugin-webatlas#readme";
110
+ const author = {
111
+ name: "Matti Svensson",
112
+ email: "mattisvensson@web.de",
113
+ url: "https://mattisvensson.com"
114
+ };
115
+ const maintainers = [
116
+ {
117
+ name: "Matti Svensson",
118
+ email: "mattisvensson@web.de",
119
+ url: "https://mattisvensson.com"
120
+ }
121
+ ];
122
+ const packageManager = "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e";
123
+ const pluginPkg = {
124
+ version,
125
+ keywords,
126
+ type,
127
+ exports: exports$1,
128
+ files,
129
+ scripts,
130
+ dependencies,
131
+ devDependencies,
132
+ peerDependencies,
133
+ description,
134
+ strapi,
135
+ name,
136
+ license,
137
+ repository,
138
+ bugs,
139
+ homepage,
140
+ author,
141
+ maintainers,
142
+ packageManager
143
+ };
144
+ const PLUGIN_ID = pluginPkg.strapi.name.replace(/^(@[^-,.][\w,-]+\/|strapi-)plugin-/i, "") || "webatlas";
145
+ const PLUGIN_NAME = pluginPkg.strapi.displayName;
146
+ const Initializer = ({ setPlugin }) => {
147
+ const ref = React.useRef(setPlugin);
148
+ React.useEffect(() => {
149
+ ref.current(PLUGIN_ID);
150
+ }, []);
151
+ return null;
152
+ };
153
+ const RouteIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.Link, {});
154
+ const NavigationIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.List, {});
155
+ function useApi() {
156
+ const { get, post, put, del } = admin.useFetchClient();
157
+ const fetchAllContentTypes = async () => {
158
+ const { data } = await get("/content-manager/content-types");
159
+ return data.data;
160
+ };
161
+ const fetchAllEntities = async (contentTypes) => {
162
+ try {
163
+ if (!contentTypes) {
164
+ const { data } = await get("/webatlas/config");
165
+ contentTypes = data?.selectedContentTypes || [];
166
+ }
167
+ const allContentTypes = await fetchAllContentTypes();
168
+ let entities = [];
169
+ if (contentTypes && contentTypes.length > 0) {
170
+ entities = await Promise.all(
171
+ contentTypes.map(async (contentType) => {
172
+ const { data } = await get(`/content-manager/collection-types/${contentType.uid}`);
173
+ const entity = allContentTypes.find((ct) => ct.uid === contentType.uid);
174
+ if (!entity) {
175
+ throw new Error(`Content type ${contentType} not found`);
176
+ }
177
+ return {
178
+ entities: data.results,
179
+ label: entity.info.displayName,
180
+ contentType
181
+ };
182
+ })
183
+ );
184
+ }
185
+ return entities;
186
+ } catch (err) {
187
+ console.error(err);
188
+ throw err;
189
+ }
190
+ };
191
+ const getRouteByRelated = async (relatedDocumentId, populate) => {
192
+ const { data } = await get(`/content-manager/collection-types/plugin::webatlas.route?filters[relatedDocumentId][$eq]=${relatedDocumentId}${populate ? "&populate" + populate : ""}`);
193
+ if (data?.results) return data.results[0];
194
+ return null;
195
+ };
196
+ const createExternalRoute = async (body) => {
197
+ const { data } = await post("/webatlas/route/external", {
198
+ data: {
199
+ ...body
200
+ }
201
+ });
202
+ return data;
203
+ };
204
+ const getRoutes = async () => {
205
+ const { data } = await get("/webatlas/route");
206
+ return data;
207
+ };
208
+ const updateRoute = async (body, documentId) => {
209
+ const { data } = await put(`/webatlas/route?documentId=${documentId}`, {
210
+ data: {
211
+ ...body
212
+ }
213
+ });
214
+ return data;
215
+ };
216
+ const createNavItem = async (body) => {
217
+ const { data } = await post("/webatlas/navitem", {
218
+ data: {
219
+ ...body
220
+ }
221
+ });
222
+ return data;
223
+ };
224
+ const updateNavItem = async (documentId, body) => {
225
+ const { data } = await put(`/webatlas/navitem?documentId=${documentId}`, {
226
+ data: {
227
+ ...body
228
+ }
229
+ });
230
+ return data;
231
+ };
232
+ const deleteNavItem = async (documentId) => {
233
+ const { data } = await del(`/webatlas/navitem?documentId=${documentId}`);
234
+ return data;
235
+ };
236
+ const getStructuredNavigation = async (documentId, variant = "nested") => {
237
+ const { data } = await get(`/webatlas/navigation?documentId=${documentId}&variant=${variant}`);
238
+ return data;
239
+ };
240
+ const deleteNavigation = async (documentId) => {
241
+ const { data } = await del(`/webatlas/navigation?documentId=${documentId}`);
242
+ return data;
243
+ };
244
+ const updateNavigation = async (documentId, body) => {
245
+ const { data } = await put(`/webatlas/navigation?documentId=${documentId}`, {
246
+ data: body
247
+ });
248
+ return data;
249
+ };
250
+ return { fetchAllContentTypes, fetchAllEntities, getRouteByRelated, createExternalRoute, getRoutes, updateRoute, createNavItem, updateNavItem, deleteNavItem, getStructuredNavigation, deleteNavigation, updateNavigation };
251
+ }
252
+ const useAllContentTypes = () => {
253
+ const { fetchAllContentTypes } = useApi();
254
+ const [contentTypes, setContentTypes] = React.useState([]);
255
+ const [loading, setLoading] = React.useState(true);
256
+ const [error, setError] = React.useState(null);
257
+ React.useEffect(() => {
258
+ const fetchEntities = async () => {
259
+ try {
260
+ const result = await fetchAllContentTypes();
261
+ setContentTypes(result);
262
+ setLoading(false);
263
+ } catch (err) {
264
+ setError(err);
265
+ setLoading(false);
266
+ }
267
+ };
268
+ fetchEntities();
269
+ }, []);
270
+ return { contentTypes, loading, error };
271
+ };
272
+ function usePluginConfig() {
273
+ const { put, get } = admin.useFetchClient();
274
+ const [data, setData] = React.useState(null);
275
+ const [loading, setLoading] = React.useState(false);
276
+ const [error, setError] = React.useState(null);
277
+ React.useEffect(() => {
278
+ const fetchData = async () => {
279
+ setLoading(true);
280
+ try {
281
+ const { data: { data: contentTypesArray } } = await get("/content-manager/content-types");
282
+ let { data: config } = await get("/webatlas/config");
283
+ if (!config || !config.selectedContentTypes) {
284
+ throw new Error(`HTTP error! Couldn't fetch plugin config`);
285
+ }
286
+ const allowedContentTypes = contentTypesArray.filter(
287
+ (type2) => type2.pluginOptions?.webatlas?.active === true
288
+ );
289
+ const contentTypeUids = new Set(allowedContentTypes.map((type2) => type2.uid));
290
+ const activeContentTypes = config.selectedContentTypes.filter((type2) => contentTypeUids.has(type2.uid));
291
+ if (JSON.stringify(activeContentTypes) !== JSON.stringify(config.selectedContentTypes)) {
292
+ config = { ...config, selectedContentTypes: activeContentTypes };
293
+ setConfig(config);
294
+ }
295
+ setData(config);
296
+ setLoading(false);
297
+ } catch (error2) {
298
+ setError(error2.message);
299
+ setLoading(false);
300
+ }
301
+ };
302
+ fetchData();
303
+ }, []);
304
+ async function setConfig(body) {
305
+ try {
306
+ await put("/webatlas/config", {
307
+ ...body
308
+ });
309
+ } catch (error2) {
310
+ setError(error2.message);
311
+ }
312
+ }
313
+ return { data, loading, error, setConfig };
314
+ }
315
+ function transformToUrl(input) {
316
+ const specialCharMap = {
317
+ "ü": "ue",
318
+ "ä": "ae",
319
+ "ö": "oe"
320
+ };
321
+ if (!input || typeof input !== "string") return "";
322
+ input = input.toLowerCase();
323
+ input = input.replace(/\/+/g, "/");
324
+ input = input.startsWith("/") ? input.slice(1) : input;
325
+ input = input.endsWith("/") ? input.slice(0, -1) : input;
326
+ for (const char in specialCharMap) {
327
+ const regex = new RegExp(char, "g");
328
+ input = input.replace(regex, specialCharMap[char]);
329
+ }
330
+ input = input.trim();
331
+ input = input.replace(/\s+/g, "-");
332
+ input = input.replace(/[^A-Za-z0-9$\-_.+!*'()/]/g, "");
333
+ input = input.replace(/-+/g, "-");
334
+ return input;
335
+ }
336
+ function Tooltip({ description: description2 }) {
337
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingLeft: 1, paddingRight: 1, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tooltip, { description: description2, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Information, { "aria-hidden": "true" }) }) });
338
+ }
339
+ function debounce(func, wait) {
340
+ let timeout;
341
+ return function executedFunction(...args) {
342
+ const later = () => {
343
+ clearTimeout(timeout);
344
+ func(...args);
345
+ };
346
+ clearTimeout(timeout);
347
+ timeout = setTimeout(later, wait);
348
+ };
349
+ }
350
+ function URLInfo({ validationState, replacement }) {
351
+ if (validationState === "initial") return null;
352
+ let color = "neutral800";
353
+ let text = "Checking if URL is available...";
354
+ if (validationState === "checking") {
355
+ color = "neutral800";
356
+ text = "Checking if URL is available...";
357
+ } else if (validationState === "done") {
358
+ color = replacement ? "danger500" : "success500";
359
+ text = replacement ? `URL is not available. Replaced with "${replacement}".` : "URL is available";
360
+ }
361
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: color, children: text }) });
362
+ }
363
+ async function duplicateCheck(url, routeDocumentId) {
364
+ if (!url) throw new Error("URL is required");
365
+ try {
366
+ const res = await fetch(`/webatlas/checkUniquePath?path=${transformToUrl(url)}${routeDocumentId ? `&targetRouteDocumentId=${routeDocumentId}` : ""}`);
367
+ if (!res.ok) throw new Error(`Network response was not ok: ${res.status} ${res.statusText}`);
368
+ return await res.text();
369
+ } catch (err) {
370
+ console.error("Error in duplicateCheck:", err);
371
+ throw new Error("Failed to check URL uniqueness");
372
+ }
373
+ }
374
+ function reducer(state, action) {
375
+ switch (action.type) {
376
+ case "DEFAULT":
377
+ return {
378
+ ...state,
379
+ value: transformToUrl(action.payload),
380
+ prevValue: state.value,
381
+ needsUrlCheck: true
382
+ };
383
+ case "NO_URL_CHECK":
384
+ return {
385
+ ...state,
386
+ value: transformToUrl(action.payload),
387
+ prevValue: state.value,
388
+ needsUrlCheck: false
389
+ };
390
+ case "NO_TRANSFORM_AND_CHECK":
391
+ return {
392
+ ...state,
393
+ value: action.payload,
394
+ prevValue: state.value,
395
+ needsUrlCheck: false
396
+ };
397
+ case "RESET_URL_CHECK_FLAG":
398
+ return { ...state, needsUrlCheck: false };
399
+ case "SET_UIDPATH":
400
+ return { ...state, uIdPath: action.payload };
401
+ case "SET_DOCUMENTIDPATH":
402
+ return { ...state, documentIdPath: action.payload };
403
+ default:
404
+ throw new Error();
405
+ }
406
+ }
407
+ const Alias = ({ config }) => {
408
+ const { layout, form } = admin.unstable_useContentManagerContext();
409
+ const { initialValues, values, onChange } = form;
410
+ const { getRouteByRelated } = useApi();
411
+ const [routeId, setRouteId] = React.useState();
412
+ const [isOverride, setIsOverride] = React.useState(false);
413
+ const [validationState, setValidationState] = React.useState("initial");
414
+ const [replacement, setReplacement] = React.useState("");
415
+ const [initialLoadComplete, setInitialLoadComplete] = React.useState(false);
416
+ const [path, dispatchPath] = React.useReducer(reducer, {
417
+ needsUrlCheck: false,
418
+ value: "",
419
+ prevValue: "",
420
+ uIdPath: ""
421
+ });
422
+ const hasUserChangedField = React.useRef(false);
423
+ const initialPath = React.useRef("");
424
+ const prevValueRef = React.useRef(null);
425
+ if (!config) return null;
426
+ const debouncedCheckUrl = React.useCallback(debounce(checkUrl, 500), []);
427
+ React.useEffect(() => {
428
+ onChange("webatlas_path", path.value);
429
+ onChange("webatlas_override", isOverride);
430
+ }, [path.value, routeId, isOverride]);
431
+ const debouncedValueEffect = React.useMemo(
432
+ () => debounce((currentValues) => {
433
+ const key = config?.default;
434
+ if (!key) return;
435
+ const currentValue = currentValues[key];
436
+ if (!currentValue) {
437
+ dispatchPath({ type: "NO_URL_CHECK", payload: "" });
438
+ return;
439
+ }
440
+ if (initialLoadComplete && (hasUserChangedField.current || !routeId) && prevValueRef.current !== currentValue && !isOverride) {
441
+ const path2 = config.pattern ? `${config.pattern}/${currentValue}` : `${currentValue}`;
442
+ if (currentValue === initialValues[key]) {
443
+ dispatchPath({ type: "NO_URL_CHECK", payload: path2 });
444
+ } else {
445
+ dispatchPath({ type: "DEFAULT", payload: path2 });
446
+ }
447
+ prevValueRef.current = currentValue;
448
+ }
449
+ }, 500),
450
+ [config?.default, config?.pattern, initialValues, isOverride, initialLoadComplete, routeId]
451
+ );
452
+ React.useEffect(() => {
453
+ if (!initialLoadComplete) return;
454
+ const key = config?.default;
455
+ if (!key) return;
456
+ const currentValue = values[key];
457
+ const initialValue = initialValues[key];
458
+ if (currentValue !== initialValue) {
459
+ hasUserChangedField.current = true;
460
+ }
461
+ debouncedValueEffect(values);
462
+ }, [values, debouncedValueEffect, initialLoadComplete]);
463
+ React.useEffect(() => {
464
+ if (path.needsUrlCheck && path.value) {
465
+ if (path.uIdPath === path.value || initialPath.current === path.value) return;
466
+ debouncedCheckUrl(path.value);
467
+ dispatchPath({ type: "RESET_URL_CHECK_FLAG" });
468
+ }
469
+ }, [path.needsUrlCheck]);
470
+ React.useEffect(() => {
471
+ async function getTypes() {
472
+ if (!initialValues.documentId) {
473
+ setInitialLoadComplete(true);
474
+ return;
475
+ }
476
+ try {
477
+ const route = await getRouteByRelated(initialValues.documentId);
478
+ if (!route) return;
479
+ initialPath.current = initialValues.webatlas_path || route.uIdPath;
480
+ setRouteId(route.id);
481
+ setIsOverride(route.isOverride || false);
482
+ dispatchPath({ type: "NO_TRANSFORM_AND_CHECK", payload: route.fullPath || "" });
483
+ dispatchPath({ type: "SET_UIDPATH", payload: route.uidPath || "" });
484
+ dispatchPath({ type: "SET_DOCUMENTIDPATH", payload: route.documentIdPath || "" });
485
+ const key = config?.default;
486
+ if (key) {
487
+ prevValueRef.current = values[key];
488
+ }
489
+ } catch (err) {
490
+ setRouteId(null);
491
+ console.log(err);
492
+ }
493
+ setInitialLoadComplete(true);
494
+ }
495
+ getTypes();
496
+ }, [config]);
497
+ React.useEffect(() => {
498
+ if (initialValues.webatlas_path) dispatchPath({ type: "NO_URL_CHECK", payload: initialValues.webatlas_path });
499
+ if (initialValues.webatlas_override) setIsOverride(initialValues.webatlas_override);
500
+ }, []);
501
+ async function checkUrl(url) {
502
+ if (!url) return;
503
+ setValidationState("checking");
504
+ setReplacement("");
505
+ try {
506
+ const data = await duplicateCheck(url);
507
+ if (!data || data === url) return;
508
+ dispatchPath({ type: "NO_URL_CHECK", payload: data });
509
+ setReplacement(data);
510
+ } catch (err) {
511
+ console.log(err);
512
+ } finally {
513
+ setValidationState("done");
514
+ }
515
+ }
516
+ return /* @__PURE__ */ jsxRuntime.jsx(
517
+ designSystem.Box,
518
+ {
519
+ as: "aside",
520
+ "aria-labelledby": "URL Route",
521
+ width: "100%",
522
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
523
+ designSystem.Flex,
524
+ {
525
+ direction: "column",
526
+ alignItems: "stretch",
527
+ gap: 4,
528
+ children: [
529
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
530
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { hint: !initialValues.id && !config.default ? "[id] will be replaced with the entry ID" : "", children: [
531
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Label, { children: [
532
+ "URL",
533
+ /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { description: "The following characters are valid: A-Z, a-z, 0-9, /, -, _, $, ., +, !, *, ', (, )" })
534
+ ] }),
535
+ /* @__PURE__ */ jsxRuntime.jsx(
536
+ designSystem.Field.Input,
537
+ {
538
+ id: "url-input",
539
+ value: path.value,
540
+ placeholder: config.default ? `Edit the "${config.default}" field to generate a URL` : `${layout.list.settings.displayName?.toLowerCase()}/[id]`,
541
+ onChange: (e) => dispatchPath({ type: "NO_TRANSFORM_AND_CHECK", payload: e.target.value }),
542
+ disabled: !isOverride,
543
+ onBlur: (e) => {
544
+ if (e.target.value === path.prevValue) return;
545
+ dispatchPath({ type: "DEFAULT", payload: e.target.value });
546
+ }
547
+ }
548
+ ),
549
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
550
+ ] }),
551
+ /* @__PURE__ */ jsxRuntime.jsx(URLInfo, { validationState, replacement }),
552
+ /* @__PURE__ */ jsxRuntime.jsx(
553
+ designSystem.Flex,
554
+ {
555
+ gap: 2,
556
+ paddingTop: 2,
557
+ children: /* @__PURE__ */ jsxRuntime.jsx(
558
+ designSystem.Checkbox,
559
+ {
560
+ id: "override-url",
561
+ checked: isOverride,
562
+ onCheckedChange: () => setIsOverride((prev) => !prev),
563
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "Override generated URL" })
564
+ }
565
+ )
566
+ }
567
+ )
568
+ ] }),
569
+ path.uIdPath && path.documentIdPath && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
570
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Divider, {}) }),
571
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsxs(
572
+ designSystem.Field.Root,
573
+ {
574
+ hint: "Permanent UID path, cannot be changed.",
575
+ label: "UID path",
576
+ children: [
577
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, {}),
578
+ /* @__PURE__ */ jsxRuntime.jsx(
579
+ designSystem.Field.Input,
580
+ {
581
+ value: path.uIdPath,
582
+ disabled: true
583
+ }
584
+ ),
585
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
586
+ ]
587
+ }
588
+ ) }),
589
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsxs(
590
+ designSystem.Field.Root,
591
+ {
592
+ hint: "Permanent DocumentID path, cannot be changed.",
593
+ label: "DocumentID path",
594
+ children: [
595
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, {}),
596
+ /* @__PURE__ */ jsxRuntime.jsx(
597
+ designSystem.Field.Input,
598
+ {
599
+ value: path.documentIdPath,
600
+ disabled: true
601
+ }
602
+ ),
603
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
604
+ ]
605
+ }
606
+ ) })
607
+ ] })
608
+ ]
609
+ }
610
+ )
611
+ }
612
+ );
613
+ };
614
+ const CMEditViewAside = () => {
615
+ const { model } = admin.unstable_useContentManagerContext();
616
+ const { contentTypes } = useAllContentTypes();
617
+ const { data: config } = usePluginConfig();
618
+ const [contentTypeConfig, setContentTypeConfig] = React.useState(null);
619
+ const [isAllowedContentType, setIsAllowedContentType] = React.useState(false);
620
+ const [isActiveContentType, setIsActiveContentType] = React.useState(true);
621
+ const [isLoading, setIsLoading] = React.useState(true);
622
+ React.useEffect(() => {
623
+ if (!config) return;
624
+ const modelConfig = contentTypes?.find((ct) => ct.uid === model);
625
+ if (modelConfig?.pluginOptions?.webatlas?.active) setIsAllowedContentType(true);
626
+ config?.selectedContentTypes?.map((type2) => {
627
+ if (type2.uid === model) {
628
+ setIsActiveContentType(true);
629
+ setContentTypeConfig(type2);
630
+ }
631
+ });
632
+ setIsLoading(false);
633
+ }, [config]);
634
+ React.useEffect(() => {
635
+ const label = Array.from(document.querySelectorAll("label")).find((l) => l.textContent?.startsWith("webatlas_path"));
636
+ if (label) {
637
+ let parentDiv = label.closest("div");
638
+ for (let i = 0; i < 2; i++) {
639
+ if (parentDiv) {
640
+ parentDiv = parentDiv.parentElement;
641
+ }
642
+ }
643
+ if (parentDiv) {
644
+ const grandParentDiv = parentDiv.parentElement;
645
+ if (grandParentDiv && grandParentDiv.children.length === 1) {
646
+ grandParentDiv.parentElement?.remove();
647
+ return;
648
+ } else {
649
+ parentDiv.remove();
650
+ }
651
+ }
652
+ }
653
+ }, []);
654
+ if (!isLoading) return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: "Loading..." });
655
+ if (!isAllowedContentType) return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { textColor: "neutral600", children: [
656
+ "This content type is not allowed for ",
657
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "WebAtlas" }),
658
+ ". If you wish to use it, please contact your administrator."
659
+ ] });
660
+ if (isActiveContentType || !contentTypeConfig) return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { textColor: "neutral600", children: [
661
+ "This content type is not configured for ",
662
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "WebAtlas" }),
663
+ ". If you wish to use it, please configure it in the",
664
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Link, { href: "/admin/settings/webatlas/configuration", marginLeft: 1, children: "settings" }),
665
+ "."
666
+ ] });
667
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(Alias, { config: contentTypeConfig }) });
668
+ };
669
+ const index = {
670
+ register(app) {
671
+ app.addMenuLink({
672
+ to: `/plugins/${PLUGIN_ID}/routes`,
673
+ icon: RouteIcon,
674
+ intlLabel: {
675
+ id: `${PLUGIN_ID}.link.routes`,
676
+ defaultMessage: "Routes"
677
+ },
678
+ Component: async () => {
679
+ const component = await Promise.resolve().then(() => require("./index-B8hoZtTR.js"));
680
+ return { default: component.default };
681
+ },
682
+ permissions: [
683
+ // Uncomment to set the permissions of the plugin here
684
+ // {
685
+ // action: '', // the action name should be plugin::plugin-name.actionType
686
+ // subject: null,
687
+ // },
688
+ ]
689
+ });
690
+ app.addMenuLink({
691
+ to: `/plugins/${PLUGIN_ID}/navigation`,
692
+ icon: NavigationIcon,
693
+ intlLabel: {
694
+ id: `${PLUGIN_ID}.link.navigation`,
695
+ defaultMessage: "Navigation"
696
+ },
697
+ Component: async () => {
698
+ const component = await Promise.resolve().then(() => require("./index-D9yK1vNq.js"));
699
+ return { default: component.default };
700
+ },
701
+ permissions: [
702
+ // Uncomment to set the permissions of the plugin here
703
+ // {
704
+ // action: '', // the action name should be plugin::plugin-name.actionType
705
+ // subject: null,
706
+ // },
707
+ ]
708
+ });
709
+ app.addSettingsLink(
710
+ {
711
+ id: PLUGIN_ID,
712
+ intlLabel: {
713
+ id: `${PLUGIN_ID}.settings.section`,
714
+ defaultMessage: "Webatlas"
715
+ }
716
+ },
717
+ {
718
+ intlLabel: {
719
+ id: `${PLUGIN_ID}.settings.section`,
720
+ defaultMessage: "Configuration"
721
+ },
722
+ id: PLUGIN_ID,
723
+ to: `/settings/${PLUGIN_ID}/configuration`,
724
+ Component: async () => {
725
+ return await Promise.resolve().then(() => require(
726
+ /* webpackChunkName: "webatlas-settings-page" */
727
+ "./index-B76GOOtR.js"
728
+ ));
729
+ },
730
+ permissions: [
731
+ // Uncomment to set the permissions of the plugin here
732
+ // {
733
+ // action: '', // the action name should be plugin::plugin-name.actionType
734
+ // subject: null,
735
+ // },
736
+ ]
737
+ }
738
+ );
739
+ app.registerPlugin({
740
+ id: PLUGIN_ID,
741
+ initializer: Initializer,
742
+ isReady: false,
743
+ name: PLUGIN_NAME
744
+ });
745
+ },
746
+ bootstrap(app) {
747
+ app.getPlugin("content-manager").apis.addEditViewSidePanel([() => ({
748
+ title: "URL Alias",
749
+ content: /* @__PURE__ */ jsxRuntime.jsx(CMEditViewAside, {})
750
+ })]);
751
+ },
752
+ async registerTrads({ locales }) {
753
+ return Promise.all(
754
+ locales.map(async (locale) => {
755
+ try {
756
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-B4KWt_jN.js")) }), `./translations/${locale}.json`, 3);
757
+ return { data, locale };
758
+ } catch {
759
+ return { data: {}, locale };
760
+ }
761
+ })
762
+ );
763
+ }
764
+ };
765
+ exports.Tooltip = Tooltip;
766
+ exports.URLInfo = URLInfo;
767
+ exports.debounce = debounce;
768
+ exports.duplicateCheck = duplicateCheck;
769
+ exports.index = index;
770
+ exports.transformToUrl = transformToUrl;
771
+ exports.useAllContentTypes = useAllContentTypes;
772
+ exports.useApi = useApi;
773
+ exports.usePluginConfig = usePluginConfig;