strapi-plugin-navigation 2.2.14 → 2.2.16

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/README.md CHANGED
@@ -122,7 +122,7 @@ Complete installation requirements are exact same as for Strapi itself and can b
122
122
 
123
123
  **Supported Strapi versions**:
124
124
 
125
- - Strapi v4.13.x (recently tested)
125
+ - Strapi v4.14.x (recently tested)
126
126
  - Strapi v4.x
127
127
 
128
128
  > This plugin is designed for **Strapi v4** and is not working with v3.x. To get version for **Strapi v3** install version [v1.x](https://github.com/VirtusLab-Open-Source/strapi-plugin-navigation/tree/strapi-v3).
@@ -377,6 +377,73 @@ Plugin supports both **REST API** and **GraphQL API** exposed by Strapi.
377
377
  > Version `v2.0.13` introduced breaking change!
378
378
  > All responses have changed their structure. Related field will now be of type ContentType instead of Array\<ContentType\>
379
379
 
380
+ `GET <host>/api/navigation/?locale=<locale>&orderBy=<orderBy>&orderDirection=<orderDirection>`
381
+
382
+ NOTE: All params are optional
383
+
384
+ **Example URL**: `https://localhost:1337/api/navigation?locale=en`
385
+
386
+ **Example response body**
387
+
388
+ ```json
389
+ [
390
+ {
391
+ "id": 383,
392
+ "name": "Floor",
393
+ "slug": "floor-pl",
394
+ "visible": true,
395
+ "createdAt": "2023-09-29T12:45:54.399Z",
396
+ "updatedAt": "2023-09-29T13:44:08.702Z",
397
+ "localeCode": "pl"
398
+ },
399
+ {
400
+ "id": 384,
401
+ "name": "Floor",
402
+ "slug": "floor-fr",
403
+ "visible": true,
404
+ "createdAt": "2023-09-29T12:45:54.399Z",
405
+ "updatedAt": "2023-09-29T13:44:08.725Z",
406
+ "localeCode": "fr"
407
+ },
408
+ {
409
+ "id": 382,
410
+ "name": "Floor",
411
+ "slug": "floor",
412
+ "visible": true,
413
+ "createdAt": "2023-09-29T12:45:54.173Z",
414
+ "updatedAt": "2023-09-29T13:44:08.747Z",
415
+ "localeCode": "en"
416
+ },
417
+ {
418
+ "id": 374,
419
+ "name": "Main navigation",
420
+ "slug": "main-navigation-pl",
421
+ "visible": true,
422
+ "createdAt": "2023-09-29T12:22:30.373Z",
423
+ "updatedAt": "2023-09-29T13:44:08.631Z",
424
+ "localeCode": "pl"
425
+ },
426
+ {
427
+ "id": 375,
428
+ "name": "Main navigation",
429
+ "slug": "main-navigation-fr",
430
+ "visible": true,
431
+ "createdAt": "2023-09-29T12:22:30.373Z",
432
+ "updatedAt": "2023-09-29T13:44:08.658Z",
433
+ "localeCode": "fr"
434
+ },
435
+ {
436
+ "id": 373,
437
+ "name": "Main navigation",
438
+ "slug": "main-navigation",
439
+ "visible": true,
440
+ "createdAt": "2023-09-29T12:22:30.356Z",
441
+ "updatedAt": "2023-09-29T13:44:08.680Z",
442
+ "localeCode": "en"
443
+ }
444
+ ]
445
+ ```
446
+
380
447
  `GET <host>/api/navigation/render/<navigationIdOrSlug>?type=<type>`
381
448
 
382
449
  Return a rendered navigation structure depends on passed type (`TREE`, `RFR` or nothing to render as `FLAT`).
@@ -33,6 +33,8 @@ const Stack_1 = require("@strapi/design-system/Stack");
33
33
  const Button_1 = require("@strapi/design-system/Button");
34
34
  const Check_1 = __importDefault(require("@strapi/icons/Check"));
35
35
  const More_1 = __importDefault(require("@strapi/icons/More"));
36
+ const Information_1 = __importDefault(require("@strapi/icons/Information"));
37
+ const Tag_1 = require("@strapi/design-system/Tag");
36
38
  const translations_1 = require("../../../../translations");
37
39
  const styles_1 = require("./styles");
38
40
  const Select_1 = require("@strapi/design-system/Select");
@@ -57,7 +59,12 @@ const NavigationHeader = ({ activeNavigation, availableNavigations, structureHas
57
59
  const passedActiveNavigation = pickDefaultLocaleNavigation({ activeNavigation, config });
58
60
  const { closeNavigationManagerModal, openNavigationManagerModal, navigationManagerModal } = (0, useNavigationManager_1.useNavigationManager)();
59
61
  const { canUpdate } = permissions;
60
- return (react_1.default.createElement(Layout_1.HeaderLayout, { primaryAction: react_1.default.createElement(Stack_1.Stack, { horizontal: true, size: 2 },
62
+ return (react_1.default.createElement(Layout_1.HeaderLayout, { secondaryAction: react_1.default.createElement(Tag_1.Tag, { icon: react_1.default.createElement(Information_1.default, { "aria-hidden": true }) }, activeNavigation
63
+ ? formatMessage((0, translations_1.getTrad)('header.meta'), {
64
+ id: activeNavigation?.id,
65
+ key: activeNavigation?.slug
66
+ })
67
+ : null), primaryAction: react_1.default.createElement(Stack_1.Stack, { horizontal: true, size: 2 },
61
68
  react_1.default.createElement(Box_1.Box, { marginRight: "8px" },
62
69
  react_1.default.createElement(Grid_1.Grid, { gap: 4 },
63
70
  !hasLocalizations ? (react_1.default.createElement(Grid_1.GridItem, { col: 2 })) : null,
@@ -2,21 +2,21 @@ import * as yup from "yup";
2
2
  import { NavigationItemAdditionalField } from "../../../../../../../types";
3
3
  import { RawFormPayload } from "../types";
4
4
  export declare const schemaFactory: (isSingleSelected: boolean, additionalFields: NavigationItemAdditionalField[]) => import("yup/lib/object").OptionalObjectSchema<{
5
- title: yup.StringSchema<string | undefined, Record<string, any>, string | undefined>;
5
+ title: yup.default<string | undefined, Record<string, any>, string | undefined>;
6
6
  uiRouterKey: import("yup/lib/string").RequiredStringSchema<string | undefined, Record<string, any>>;
7
7
  type: import("yup/lib/string").RequiredStringSchema<string | undefined, Record<string, any>>;
8
- path: yup.StringSchema<string | undefined, Record<string, any>, string | undefined>;
9
- externalPath: yup.StringSchema<string | undefined, Record<string, any>, string | undefined>;
8
+ path: yup.default<string | undefined, Record<string, any>, string | undefined>;
9
+ externalPath: yup.default<string | undefined, Record<string, any>, string | undefined>;
10
10
  menuAttached: yup.BooleanSchema<boolean | undefined, Record<string, any>, boolean | undefined>;
11
11
  relatedType: import("yup/lib/mixed").MixedSchema<any, Record<string, any>, any>;
12
12
  related: import("yup/lib/mixed").MixedSchema<any, Record<string, any>, any>;
13
13
  additionalFields: import("yup/lib/object").OptionalObjectSchema<{}, Record<string, any>, import("yup/lib/object").TypeOfShape<{}>>;
14
14
  }, Record<string, any>, import("yup/lib/object").TypeOfShape<{
15
- title: yup.StringSchema<string | undefined, Record<string, any>, string | undefined>;
15
+ title: yup.default<string | undefined, Record<string, any>, string | undefined>;
16
16
  uiRouterKey: import("yup/lib/string").RequiredStringSchema<string | undefined, Record<string, any>>;
17
17
  type: import("yup/lib/string").RequiredStringSchema<string | undefined, Record<string, any>>;
18
- path: yup.StringSchema<string | undefined, Record<string, any>, string | undefined>;
19
- externalPath: yup.StringSchema<string | undefined, Record<string, any>, string | undefined>;
18
+ path: yup.default<string | undefined, Record<string, any>, string | undefined>;
19
+ externalPath: yup.default<string | undefined, Record<string, any>, string | undefined>;
20
20
  menuAttached: yup.BooleanSchema<boolean | undefined, Record<string, any>, boolean | undefined>;
21
21
  relatedType: import("yup/lib/mixed").MixedSchema<any, Record<string, any>, any>;
22
22
  related: import("yup/lib/mixed").MixedSchema<any, Record<string, any>, any>;
@@ -14,6 +14,7 @@
14
14
  "header.action.manage": "Gestionar",
15
15
  "header.action.newItem": "Element nou",
16
16
  "header.description": "Definiu la vostra navegació del lloc web",
17
+ "header.meta": "ID: { id }, slug: { key }",
17
18
  "header.title": "Navegació",
18
19
  "notification.error": "S'ha produït un error en processar la sol·licitud.",
19
20
  "notification.error.customField.type": "Tipus de camp personalitzat no compatible",
@@ -2,6 +2,7 @@
2
2
  "plugin.name": "UI Navigation",
3
3
  "header.title": "Navigation",
4
4
  "header.description": "Define your portal navigation",
5
+ "header.meta": "ID: { id }, slug: { key }",
5
6
  "header.action.newItem": "New Item",
6
7
  "header.action.manage": "Manage",
7
8
  "header.action.collapseAll": "Collapse All",
@@ -2,6 +2,7 @@
2
2
  "plugin.name": "UI Navigation",
3
3
  "header.title": "Navigation",
4
4
  "header.description": "Définisser votre menu de navigation",
5
+ "header.meta": "ID: { id }, slug: { key }",
5
6
  "submit.cta.cancel": "Annuler",
6
7
  "submit.cta.save": "Sauvegarder",
7
8
  "empty": "Le menu de navigation est vide",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-navigation",
3
- "version": "2.2.14",
3
+ "version": "2.2.16",
4
4
  "description": "Strapi - Navigation plugin",
5
5
  "strapi": {
6
6
  "name": "navigation",
@@ -15,7 +15,7 @@
15
15
  "scripts": {},
16
16
  "dependencies": {
17
17
  "@sindresorhus/slugify": "1.1.0",
18
- "@strapi/utils": "^4.13.6",
18
+ "@strapi/utils": "^4.14.2",
19
19
  "lodash": "^4.17.21",
20
20
  "pluralize": "^8.0.0",
21
21
  "react": "^18.2.0",
@@ -49,6 +49,11 @@
49
49
  "name": "VirtusLab // Maciej Witkowski",
50
50
  "email": "mwitkowski@virtuslab.com",
51
51
  "url": "https://virtuslab.com"
52
+ },
53
+ {
54
+ "name": "VirtusLab // Tomasz Puch",
55
+ "email": "tpuch@virtuslab.com",
56
+ "url": "https://virtuslab.com"
52
57
  }
53
58
  ],
54
59
  "engines": {
@@ -6,6 +6,21 @@ const clientControllers = {
6
6
  getService(name = "client") {
7
7
  return (0, utils_2.getPluginService)(name);
8
8
  },
9
+ async readAll(ctx) {
10
+ const { query = {} } = ctx;
11
+ const { locale, orderBy, orderDirection } = query;
12
+ try {
13
+ return await this.getService().readAll({
14
+ locale, orderBy, orderDirection
15
+ });
16
+ }
17
+ catch (error) {
18
+ if (error instanceof Error) {
19
+ return ctx.badRequest(error.message);
20
+ }
21
+ throw error;
22
+ }
23
+ },
9
24
  async render(ctx) {
10
25
  const { params, query = {} } = ctx;
11
26
  const { type, menu: menuOnly, path: rootPath, locale, populate } = query;
@@ -4,6 +4,7 @@ exports.i18nNavigationSetupStrategy = void 0;
4
4
  const types_1 = require("../../types");
5
5
  const utils_1 = require("../utils");
6
6
  const errors_1 = require("./errors");
7
+ const fp_1 = require("lodash/fp");
7
8
  const i18nNavigationSetupStrategy = async ({ strapi, }) => {
8
9
  const pluginStore = strapi.store({
9
10
  type: "plugin",
@@ -24,48 +25,54 @@ const i18nNavigationSetupStrategy = async ({ strapi, }) => {
24
25
  await createDefaultI18nNavigation({ strapi, defaultLocale }),
25
26
  ];
26
27
  }
27
- if (currentNavigations.some(hasNotAnyLocale)) {
28
- currentNavigations = await Promise.all(currentNavigations.map(async (navigation) => {
29
- return hasNotAnyLocale(navigation)
30
- ? await updateNavigation({
31
- strapi,
32
- current: navigation,
33
- payload: {
34
- localeCode: defaultLocale,
35
- },
36
- populate: utils_1.DEFAULT_POPULATE,
37
- })
38
- : navigation;
39
- }));
28
+ const noLocaleNavigations = currentNavigations.filter(hasNotAnyLocale);
29
+ if (noLocaleNavigations.length) {
30
+ await updateNavigations({
31
+ strapi,
32
+ ids: noLocaleNavigations.map((0, fp_1.prop)("id")).reduce((acc, id) => {
33
+ if (id && Number(id)) {
34
+ acc.push(Number(id));
35
+ }
36
+ return acc;
37
+ }, []),
38
+ payload: {
39
+ localeCode: defaultLocale,
40
+ },
41
+ populate: utils_1.DEFAULT_POPULATE,
42
+ });
43
+ currentNavigations = await getCurrentNavigations(strapi);
40
44
  }
41
- await Promise.all(currentNavigations
42
- .filter(({ localeCode }) => defaultLocale === localeCode)
43
- .flatMap(async (rootNavigation) => {
44
- const localizations = [
45
+ const navigationsToProcess = currentNavigations.filter(({ localeCode }) => defaultLocale === localeCode);
46
+ for (const rootNavigation of navigationsToProcess) {
47
+ let localizations = [
45
48
  ...(rootNavigation.localizations ?? []).map((localization) => (0, types_1.assertEntity)(localization, "Navigation")),
46
49
  rootNavigation,
47
50
  ];
48
- for (const locale of allLocale) {
49
- if (!localizations.some(({ localeCode }) => localeCode === locale)) {
50
- localizations.push(await createNavigation({
51
- strapi,
52
- payload: {
53
- localeCode: locale,
54
- slug: `${rootNavigation.slug}-${locale}`,
55
- name: rootNavigation.name,
56
- visible: true,
57
- },
58
- }));
59
- }
51
+ const missingLocale = allLocale.filter((locale) => !localizations.some(({ localeCode }) => localeCode === locale));
52
+ if (missingLocale.length) {
53
+ const { ids } = await createNavigations({
54
+ strapi,
55
+ payloads: missingLocale.map((locale) => ({
56
+ localeCode: locale,
57
+ slug: `${rootNavigation.slug}-${locale}`,
58
+ name: rootNavigation.name,
59
+ visible: true,
60
+ })),
61
+ });
62
+ localizations = [...(await getCurrentNavigations(strapi, ids))].concat([
63
+ rootNavigation,
64
+ ]);
60
65
  }
61
- return await Promise.all(localizations.map((current) => updateNavigation({
62
- strapi,
63
- current,
64
- payload: {
65
- localizations: localizations.filter(({ id }) => id !== current.id),
66
- },
67
- })));
68
- }));
66
+ for (const current of localizations) {
67
+ await updateNavigation({
68
+ strapi,
69
+ current,
70
+ payload: {
71
+ localizations: localizations.filter((localization) => localization.id !== current.id),
72
+ },
73
+ });
74
+ }
75
+ }
69
76
  }
70
77
  else {
71
78
  if (config.pruneObsoleteI18nNavigations) {
@@ -89,13 +96,17 @@ const i18nNavigationSetupStrategy = async ({ strapi, }) => {
89
96
  return getCurrentNavigations(strapi);
90
97
  };
91
98
  exports.i18nNavigationSetupStrategy = i18nNavigationSetupStrategy;
92
- const getCurrentNavigations = (strapi) => strapi.plugin("navigation").service("admin").get();
99
+ const getCurrentNavigations = (strapi, ids) => strapi.plugin("navigation").service("admin").get(ids);
93
100
  const createNavigation = ({ strapi, payload, populate, }) => strapi.query("plugin::navigation.navigation").create({
94
101
  data: {
95
102
  ...payload,
96
103
  },
97
104
  populate,
98
105
  });
106
+ const createNavigations = ({ strapi, payloads, populate, }) => strapi.query("plugin::navigation.navigation").createMany({
107
+ data: payloads,
108
+ populate,
109
+ });
99
110
  const updateNavigation = ({ strapi, current, payload, populate, }) => strapi.query("plugin::navigation.navigation").update({
100
111
  data: {
101
112
  ...payload,
@@ -105,10 +116,19 @@ const updateNavigation = ({ strapi, current, payload, populate, }) => strapi.que
105
116
  id: current.id,
106
117
  },
107
118
  });
119
+ const updateNavigations = ({ strapi, ids, payload, populate, }) => strapi.query("plugin::navigation.navigation").updateMany({
120
+ data: payload,
121
+ populate,
122
+ where: {
123
+ id: {
124
+ $in: ids,
125
+ },
126
+ },
127
+ });
108
128
  const deleteNavigations = ({ strapi, where, }) => strapi.query("plugin::navigation.navigation").deleteMany({
109
129
  where,
110
130
  });
111
- const createDefaultI18nNavigation = ({ strapi, defaultLocale }) => createNavigation({
131
+ const createDefaultI18nNavigation = ({ strapi, defaultLocale, }) => createNavigation({
112
132
  strapi,
113
133
  payload: {
114
134
  ...utils_1.DEFAULT_NAVIGATION_ITEM,
@@ -18,6 +18,14 @@ const routes = {
18
18
  config: {
19
19
  policies: []
20
20
  }
21
+ },
22
+ {
23
+ method: "GET",
24
+ path: "/",
25
+ handler: "client.readAll",
26
+ config: {
27
+ policies: []
28
+ }
21
29
  }
22
30
  ]
23
31
  };