@webbio/strapi-plugin-page-builder 0.3.2 → 0.3.3-legacy

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 (77) hide show
  1. package/README.md +21 -6
  2. package/admin/src/api/collection-type.ts +1 -7
  3. package/admin/src/components/EditView/CollectionTypeSearch/index.tsx +11 -14
  4. package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/index.tsx +27 -9
  5. package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +16 -6
  6. package/admin/src/components/EditView/Details/index.tsx +1 -1
  7. package/admin/src/components/EditView/PageSettings/index.tsx +6 -57
  8. package/admin/src/components/PageTypeFilter/index.tsx +17 -0
  9. package/admin/src/components/PageTypeFilter/page-type-filter.tsx +130 -0
  10. package/admin/src/index.tsx +2 -2
  11. package/admin/src/utils/sanitizeModules.ts +92 -9
  12. package/dist/package.json +11 -9
  13. package/dist/server/bootstrap.js +24 -40
  14. package/dist/server/content-types/user-categories/schema.json +18 -0
  15. package/dist/server/content-types/user-category/schema.json +23 -0
  16. package/dist/server/controllers/index.js +1 -3
  17. package/dist/server/controllers/platform.js +5 -5
  18. package/dist/server/controllers/private-content.js +18 -0
  19. package/dist/server/controllers/sitemap.js +29 -0
  20. package/dist/server/controllers/user-category.js +4 -0
  21. package/dist/server/graphql/page-by-path.js +113 -0
  22. package/dist/server/graphql/pages-by-uid.js +7 -9
  23. package/dist/server/policies/isAuthorizedPage.js +11 -0
  24. package/dist/server/routes/index.js +0 -21
  25. package/dist/server/routes/user-category.js +4 -0
  26. package/dist/server/schema/page-end.json +0 -5
  27. package/dist/server/schema/platform-start.json +0 -10
  28. package/dist/server/schema/template-end.json +40 -0
  29. package/dist/server/services/builder.js +5 -15
  30. package/dist/server/services/email.js +160 -0
  31. package/dist/server/services/index.js +1 -3
  32. package/dist/server/services/platform.js +6 -11
  33. package/dist/server/services/private-content/components/admin-email.json +22 -0
  34. package/dist/server/services/private-content/components/email.json +22 -0
  35. package/dist/server/services/private-content/components/platform-email.json +33 -0
  36. package/dist/server/services/private-content/constants/index.js +16 -0
  37. package/dist/server/services/private-content/graphql/index.js +77 -0
  38. package/dist/server/services/private-content/graphql/resolvers/findOnePage.js +40 -0
  39. package/dist/server/services/private-content/graphql/resolvers/findPage.js +44 -0
  40. package/dist/server/services/private-content/graphql/resolvers/forgot-password.js +29 -0
  41. package/dist/server/services/private-content/graphql/resolvers/login.js +49 -0
  42. package/dist/server/services/private-content/graphql/resolvers/register.js +68 -0
  43. package/dist/server/services/private-content/graphql/resolvers/reset-password.js +41 -0
  44. package/dist/server/services/private-content/graphql/types/index.js +89 -0
  45. package/dist/server/services/private-content/index.js +94 -0
  46. package/dist/server/services/private-content/mail-template/txtMail.email.template.text.js +12 -0
  47. package/dist/server/services/private-content/page.js +22 -0
  48. package/dist/server/services/private-content/platform.js +22 -0
  49. package/dist/server/services/private-content/schemas/index.js +30 -0
  50. package/dist/server/services/private-content/user.js +170 -0
  51. package/dist/server/services/sitemap.js +78 -0
  52. package/dist/server/services/template.js +1 -2
  53. package/dist/server/services/user-category.js +4 -0
  54. package/dist/server/utils/strapi.js +1 -4
  55. package/dist/shared/utils/constants.js +1 -3
  56. package/dist/tsconfig.server.tsbuildinfo +1 -1
  57. package/package.json +11 -9
  58. package/server/bootstrap/collection-type-lifecycles.ts +1 -1
  59. package/server/bootstrap.ts +24 -43
  60. package/server/controllers/index.ts +1 -3
  61. package/server/graphql/pages-by-uid.ts +7 -9
  62. package/server/routes/index.ts +0 -21
  63. package/server/schema/page-end.json +0 -5
  64. package/server/services/builder.ts +6 -18
  65. package/server/services/index.ts +1 -3
  66. package/server/services/template.ts +1 -2
  67. package/server/utils/strapi.ts +1 -5
  68. package/shared/utils/constants.ts +0 -2
  69. package/admin/src/api/platform.ts +0 -34
  70. package/admin/src/components/EditView/Platform/platform-select.tsx +0 -30
  71. package/admin/src/components/PageFilters/PageTypeFilter/index.tsx +0 -39
  72. package/admin/src/components/PageFilters/PlatformFilter/index.tsx +0 -28
  73. package/admin/src/components/PageFilters/filters.tsx +0 -180
  74. package/admin/src/components/PageFilters/index.tsx +0 -30
  75. package/server/controllers/platform.ts +0 -21
  76. package/server/schema/platform-start.json +0 -31
  77. package/server/services/platform.ts +0 -36
package/README.md CHANGED
@@ -45,6 +45,9 @@ Voor elk collection type die een page type is, moet deze properties toegevoegd w
45
45
 
46
46
  Zodra dit goed is ingesteld, kan je via deze collectie types direct een pagina aanmaken.
47
47
 
48
+ > **Notitie:**
49
+ > `hasPage` wordt gebruikt om aan te tonen dan een collection type item een `published` pagina heeft.
50
+
48
51
  ## Page
49
52
 
50
53
  Op een page kan je in de Edit View een page type selecteren. Op basis daarvan wordt er een template geselecteerd (indien gekozen in een page type) en kan je zoeken in verschillende entiteiten binnen die page type. Deze kan je koppelen aan een pagina.
@@ -60,19 +63,31 @@ Om wijzigignen aan te brengen in een platform, moeten deze in de schema aangepas
60
63
  Voor elk collectiontype die je wil toevoegen aan het platform, moet deze relatie toegevoegd worden:
61
64
 
62
65
  ```
63
- "platform": {
64
- "type": "relation",
65
- "relation": "oneToOne",
66
- "target": "api::platform.platform"
67
- }
66
+ "collectiontype": {
67
+ "type": "relation",
68
+ "relation": "oneToMany",
69
+ "target": "UID van collection type",
70
+ "mappedBy": "platform"
71
+ }
72
+ ```
73
+
74
+ bijvoorbeeld :
75
+
76
+ ```
77
+ "faqs": {
78
+ "type": "relation",
79
+ "relation": "oneToMany",
80
+ "target": "api::faq.faq",
81
+ "mappedBy": "platform"
82
+ }
68
83
  ```
69
84
 
70
85
  Een platform zit altijd aan een pagina gekoppeld. Deze moet handmatig in de pagina toegevoegd worden.
71
86
 
72
87
  # Known bugs
73
88
 
89
+ - Bij het opslaan van een collectietype wil de gekoppelde pagina of type in het ui wel eens verdwijnen.
74
90
  - Na het ontkoppelen en opslaan van een collectie item op een pagina, staat deze nog als "Geselecteerd" in de dropdown.
75
- - Sorteren op pages binnen een collectie type kan niet. Dus stel ik haal alle FAQ's op waarbij ik wil sorteren op een property op de page, kan dat niet.
76
91
 
77
92
  # Improvements
78
93
 
@@ -26,7 +26,6 @@ type SearchEntitiesQueryParams = {
26
26
  searchQuery?: string;
27
27
  currentCollectionTypeId?: number;
28
28
  currentPageId?: number;
29
- platformTitle?: string;
30
29
  };
31
30
 
32
31
  const QUERY_KEY = ['entities'];
@@ -38,8 +37,7 @@ export const getSearchEntities = async ({
38
37
  locale,
39
38
  searchQuery,
40
39
  currentCollectionTypeId,
41
- currentPageId,
42
- platformTitle
40
+ currentPageId
43
41
  }: SearchEntitiesQueryParams): Promise<SearchResult> => {
44
42
  try {
45
43
  const { get } = fetchClient;
@@ -55,10 +53,6 @@ export const getSearchEntities = async ({
55
53
  searchParams.append('_q', searchQuery);
56
54
  }
57
55
 
58
- if (platformTitle) {
59
- searchParams.append('filters[$and][0][platform][title][$contains]', String(platformTitle));
60
- }
61
-
62
56
  const { data } = await get(`/content-manager/collection-types/${uid}?${searchParams.toString()}`);
63
57
 
64
58
  const filteredResults = data.results.filter((p: Record<string, any>) => {
@@ -14,10 +14,9 @@ const PAGE = 1;
14
14
 
15
15
  interface Props {
16
16
  uid: string;
17
- platformTitle?: string;
18
17
  }
19
18
 
20
- export const CollectionTypeSearch = ({ uid, platformTitle }: Props) => {
19
+ export const CollectionTypeSearch = ({ uid }: Props) => {
21
20
  const fetchClient = useFetchClient();
22
21
  const form = useCMEditViewDataManager() as any;
23
22
  const { locales } = useSelector((state: any) => state.i18n_locales);
@@ -25,7 +24,6 @@ export const CollectionTypeSearch = ({ uid, platformTitle }: Props) => {
25
24
  const defaultLocale = locales.find((locale: any) => locale.isDefault);
26
25
  const selectedLocale = form.initialData?.locale ?? urlLocale ?? defaultLocale.code;
27
26
  const prevUid = usePrevious(uid);
28
- const prevPlatformTitle = usePrevious(platformTitle);
29
27
 
30
28
  const [selected, setSelected] = useState<SingleValue<IReactSelectValue | null>>(
31
29
  getInitialSelectItem(form.initialData?.collectionTypeId, form.initialData?.collectionTypeTitle)
@@ -40,7 +38,7 @@ export const CollectionTypeSearch = ({ uid, platformTitle }: Props) => {
40
38
  }
41
39
  }, []);
42
40
 
43
- const getItems = async (inputValue?: string, platformTitle?: string): Promise<IReactSelectValue[]> => {
41
+ const getItems = async (inputValue?: string): Promise<IReactSelectValue[]> => {
44
42
  const searchEntities = await getSearchEntities({
45
43
  fetchClient,
46
44
  page: PAGE,
@@ -48,8 +46,7 @@ export const CollectionTypeSearch = ({ uid, platformTitle }: Props) => {
48
46
  uid,
49
47
  searchQuery: inputValue,
50
48
  currentCollectionTypeId: form.initialData?.collectionTypeId,
51
- currentPageId: form.initialData?.id,
52
- platformTitle
49
+ currentPageId: form.initialData?.id
53
50
  });
54
51
 
55
52
  return searchEntities.results.map((x) => ({
@@ -76,28 +73,28 @@ export const CollectionTypeSearch = ({ uid, platformTitle }: Props) => {
76
73
  };
77
74
 
78
75
  useEffect(() => {
79
- if ((prevUid !== null && prevUid !== uid) || (prevPlatformTitle !== null && prevPlatformTitle !== platformTitle)) {
76
+ if (prevUid !== null && prevUid !== uid) {
80
77
  setSelected(null);
81
78
  }
82
- }, [uid, platformTitle]);
79
+ }, [uid]);
83
80
 
84
- const debouncedFetch = debounce((searchTerm, callback, selectedPlatformTitle?: string) => {
85
- promiseOptions(searchTerm, selectedPlatformTitle).then((result) => {
81
+ const debouncedFetch = debounce((searchTerm, callback) => {
82
+ promiseOptions(searchTerm).then((result) => {
86
83
  return callback(result || []);
87
84
  });
88
85
  }, SEARCH_DEBOUNCE_MS);
89
86
 
90
- const promiseOptions = (inputValue: string, selectedPlatformTitle?: string): Promise<IReactSelectValue[]> =>
87
+ const promiseOptions = (inputValue: string): Promise<IReactSelectValue[]> =>
91
88
  new Promise<IReactSelectValue[]>((resolve) => {
92
- resolve(getItems(inputValue, selectedPlatformTitle));
89
+ resolve(getItems(inputValue));
93
90
  });
94
91
 
95
92
  return (
96
93
  <Combobox
97
- key={`rerenderOnUidOrPlatformChange-${uid}-${platformTitle}`}
94
+ key={`rerenderOnUidChange-${uid}`}
98
95
  id="collectionTypeSearch"
99
96
  label="Entity"
100
- loadOptions={(i, c) => debouncedFetch(i, c, platformTitle)}
97
+ loadOptions={debouncedFetch}
101
98
  cacheOptions
102
99
  onChange={handleChange}
103
100
  customOption={CustomOption}
@@ -1,4 +1,6 @@
1
- import React, { useEffect } from 'react';
1
+ import React from 'react';
2
+ import slugify from 'slugify';
3
+ import { useSelector } from 'react-redux';
2
4
  import { useHistory } from 'react-router-dom';
3
5
 
4
6
  import { Plus } from '@strapi/icons';
@@ -14,6 +16,7 @@ import S from './styles';
14
16
  export const CreatePageButton = () => {
15
17
  const history = useHistory();
16
18
  const { layout, initialData } = useCMEditViewDataManager() as any;
19
+ const { locales } = useSelector((state: any) => state.i18n_locales);
17
20
 
18
21
  const { post, get, put } = useFetchClient();
19
22
  const url = `/content-manager/collectionType/${PAGE_UID}/create`;
@@ -28,8 +31,17 @@ export const CreatePageButton = () => {
28
31
  getRequestUrl(`/collection-types-page-links/${layout.uid}/${mappedLocalizations}`)
29
32
  );
30
33
  const createLocalizedPage = !linkedPages?.data?.find((x) => x.locale === initialData?.locale);
31
- const locale = initialData?.locale;
32
- const modules = sanitizeModules(pageType?.template?.modules || []);
34
+ const defaultLocale = locales.find((locale: any) => locale.isDefault);
35
+ const locale = initialData?.locale || defaultLocale?.code;
36
+
37
+ // Get data inside template
38
+ let templateData;
39
+
40
+ if (pageType?.template?.id) {
41
+ templateData = await get(getRequestUrl(`/template/${pageType.template.id}`));
42
+ }
43
+
44
+ const modules = sanitizeModules(templateData?.data?.modules || [], false);
33
45
 
34
46
  const newPage = await createNewPage({
35
47
  title: initialData?.title,
@@ -37,16 +49,15 @@ export const CreatePageButton = () => {
37
49
  post,
38
50
  locale,
39
51
  pageTypeId: pageType.id,
40
- platformId: initialData.platform[0]?.id,
41
52
  collectionTypeId: initialData.id,
42
53
  layoutUid: layout.uid,
43
54
  relatedEntityId: createLocalizedPage ? linkedPages.data?.[0]?.id : undefined
44
55
  });
45
56
  if (newPage?.id) {
46
57
  await put(`/content-manager/collection-types/${layout.uid}/${initialData.id}`, {
47
- hasPage: true
58
+ hasPage: false
48
59
  });
49
- history.push(`/content-manager/collectionType/${PAGE_UID}/${newPage.id}?plugins[i18n][locale]=${locale}`);
60
+ history.push(`/content-manager/collection-types/${PAGE_UID}/${newPage.id}?plugins[i18n][locale]=${locale}`);
50
61
  }
51
62
  } catch (error) {
52
63
  console.error(error);
@@ -68,7 +79,6 @@ interface ICreateNewPageProps {
68
79
  modules?: Record<string, any>[];
69
80
  post: any;
70
81
  pageTypeId: number;
71
- platformId: number;
72
82
  relatedEntityId?: number;
73
83
  }
74
84
 
@@ -80,17 +90,25 @@ const createNewPage = async ({
80
90
  layoutUid,
81
91
  modules,
82
92
  pageTypeId,
83
- platformId,
84
93
  relatedEntityId
85
94
  }: ICreateNewPageProps) => {
86
95
  // Including locale in url is neccesary.
87
96
  const relatedEntityIdQuery = relatedEntityId ? `&plugins[i18n][relatedEntityId]=${relatedEntityId}` : '';
88
97
  const url = `/content-manager/collection-types/${PAGE_UID}?plugins[i18n][locale]=${locale}${relatedEntityIdQuery}`;
98
+ const slugData = title
99
+ ? {
100
+ slug: slugify(title, {
101
+ lower: true,
102
+ trim: true
103
+ })
104
+ }
105
+ : {};
106
+
89
107
  const { data: page } = await post(url, {
108
+ ...slugData,
90
109
  title,
91
110
  locale,
92
111
  pageType: { connect: [{ id: pageTypeId }] },
93
- platform: { connect: [{ id: platformId }] },
94
112
  collectionTypeData: {
95
113
  id: collectionTypeId,
96
114
  __type: layoutUid,
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
2
 
3
3
  import { useCMEditViewDataManager } from '@strapi/helper-plugin';
4
4
  import { Flex } from '@strapi/design-system';
@@ -10,14 +10,24 @@ import { PAGE_TYPE_PAGE, PAGE_UID } from '../../../../../shared/utils/constants'
10
10
  import S from '../Details/styles';
11
11
 
12
12
  export const CollectionTypeSettings = () => {
13
- const { layout, isCreatingEntry, initialData, onChange } = useCMEditViewDataManager() as any;
13
+ const { layout, isCreatingEntry, initialData, modifiedData, onChange } = useCMEditViewDataManager() as any;
14
14
 
15
15
  const isUserCreatedContentType = layout.uid.startsWith('api::');
16
- const linkedPage = initialData.page?.[0];
16
+ const [linkedPage, setLinkedPage] = useState<Record<string, any> | undefined>(initialData.page?.[0]);
17
17
 
18
18
  const showCreatePageButton = isUserCreatedContentType && !isCreatingEntry && !linkedPage;
19
19
  const url = generateLink(linkedPage?.id, initialData?.locale);
20
20
 
21
+ useEffect(() => {
22
+ if (modifiedData.page?.[0]) {
23
+ setLinkedPage(modifiedData.page?.[0]);
24
+ }
25
+
26
+ if (modifiedData.page === null) {
27
+ setLinkedPage(undefined);
28
+ }
29
+ }, [modifiedData.page?.[0]]);
30
+
21
31
  useEffect(() => {
22
32
  if (isCreatingEntry) {
23
33
  onChange({
@@ -44,9 +54,9 @@ export const CollectionTypeSettings = () => {
44
54
  {PAGE_TYPE_PAGE}
45
55
  </S.SubtleType>
46
56
  <S.EntityLinkWrapper variant="pi" textColor="neutral800">
47
- <S.EntityLink title={linkedPage.title || '-'} to={url} variant="pi">
57
+ <S.EntityLink title={linkedPage?.title || '-'} to={url} variant="pi">
48
58
  <Link />
49
- {linkedPage.title || '-'}
59
+ {linkedPage?.title || '-'}
50
60
  </S.EntityLink>
51
61
  </S.EntityLinkWrapper>
52
62
  </Flex>
@@ -60,5 +70,5 @@ const generateLink = (entityId?: string, locale?: string) => {
60
70
  if (!entityId) {
61
71
  return '';
62
72
  }
63
- return `/content-manager/collectionType/${PAGE_UID}/${entityId}?plugins[i18n][locale]=${locale}`;
73
+ return `/content-manager/collection-types/${PAGE_UID}/${entityId}?plugins[i18n][locale]=${locale}`;
64
74
  };
@@ -43,5 +43,5 @@ const generateLink = ({ locale, pageType, entityId }: Props) => {
43
43
  return '';
44
44
  }
45
45
 
46
- return `/content-manager/collectionType/${pageType.uid}/${entityId}?plugins[i18n][locale]=${locale}`;
46
+ return `/content-manager/collection-types/${pageType.uid}/${entityId}?plugins[i18n][locale]=${locale}`;
47
47
  };
@@ -3,7 +3,7 @@ import React, { useEffect, useMemo, useState } from 'react';
3
3
  import { useCMEditViewDataManager } from '@strapi/helper-plugin';
4
4
  import { Stack, Flex } from '@strapi/design-system';
5
5
  import { Cog, Cross } from '@strapi/icons';
6
- import { getFetchClient } from '@strapi/helper-plugin';
6
+
7
7
  import { TemplateSelect } from '../Template/TemplateSelect';
8
8
  import { PageTypeSelect } from '../page-type-select';
9
9
  import { CollectionTypeSearch } from '../CollectionTypeSearch';
@@ -11,18 +11,11 @@ import { Wrapper } from '../wrapper';
11
11
  import { PageType, useGetPageTypes } from '../../../api/page-type';
12
12
  import { Details } from '../Details';
13
13
  import S from '../Details/styles';
14
- import { Platform, useGetPlatforms } from '../../../api/platform';
15
- import { PlatformSelect } from '../Platform/platform-select';
16
- import getRequestUrl from '../../../utils/getRequestUrl';
17
14
 
18
15
  export const PageSettings = () => {
19
- const { isCreatingEntry, initialData, onChange, modifiedData, ...rest } = useCMEditViewDataManager() as any;
20
- const { data: allPageTypes } = useGetPageTypes({});
21
- const { data: platforms } = useGetPlatforms({});
22
- const [pageTypes, setPageTypes] = useState(allPageTypes);
16
+ const { isCreatingEntry, initialData, onChange, modifiedData } = useCMEditViewDataManager() as any;
17
+ const { data: pageTypes } = useGetPageTypes({});
23
18
  const [selectedPageType, setSelectedPageType] = useState<PageType | undefined | null>(initialData?.initialPageType);
24
- const [selectedPlatform, setSelectedPlatform] = useState<Platform | undefined | null>(initialData?.platform?.[0]);
25
- const noPlatformSelected = useMemo(() => !Boolean(selectedPlatform), [selectedPlatform]);
26
19
  const [isEditting, setIsEditting] = useState(false);
27
20
  const showEditFields = useMemo(
28
21
  () => isCreatingEntry || !selectedPageType?.id || !initialData.collectionTypeTitle || isEditting,
@@ -42,24 +35,15 @@ export const PageSettings = () => {
42
35
  useEffect(() => {
43
36
  setSelectedPageType(initialData?.initialPageType);
44
37
 
45
- setSelectedPlatform(initialData?.platform?.[0]);
46
38
  setIsEditting(false);
47
39
  }, [initialData]);
48
40
 
49
- useEffect(() => {
50
- if (selectedPlatform?.title) {
51
- setPageTypesByPlatform(selectedPlatform.title);
52
- }
53
- }, [selectedPlatform, allPageTypes]);
54
-
55
41
  const cancelEdit = () => {
56
42
  setIsEditting(false);
57
43
  setSelectedPageType(initialData?.initialPageType || []);
58
- setSelectedPlatform(initialData?.platform?.[0]);
59
44
  setFormValue('pageType', initialData?.initialPageType || []);
60
45
  setFormValue('collectionTypeTitle', initialData?.collectionTypeTitle);
61
46
  setFormValue('collectionTypeId', initialData?.collectionTypeId);
62
- setFormValue('platform', initialData?.platform);
63
47
  };
64
48
 
65
49
  const removePageType = () => {
@@ -86,49 +70,14 @@ export const PageSettings = () => {
86
70
  }
87
71
  };
88
72
 
89
- const handleSelectPlatform = async (platformId: string) => {
90
- const platform = platforms?.find((platform) => platform.id === Number(platformId));
91
-
92
- if (platform && platform.title) {
93
- setSelectedPlatform(platform);
94
- const formPlatform = {
95
- ...platform,
96
- label: platform.title,
97
- value: platform.id
98
- };
99
- setFormValue('platform', [formPlatform]);
100
- setPageTypesByPlatform(platform.title);
101
- } else {
102
- setFormValue('platform', []);
103
- setSelectedPlatform(null);
104
- removePageType();
105
- }
106
- };
107
-
108
- const setPageTypesByPlatform = async (platform: string) => {
109
- const pageTypeUrl = getRequestUrl(`/platform/${platform}`);
110
- const { get } = getFetchClient();
111
- const { data: platFormData } = await get(pageTypeUrl);
112
-
113
- setPageTypes(platFormData[0].pageTypes);
114
- };
115
-
116
73
  return (
117
74
  <Wrapper title="Gekoppelde type">
118
75
  <Flex direction="column" gap={4} width="100%">
119
76
  {showEditFields ? (
120
77
  <Stack spacing={4} width="100%">
121
- <PlatformSelect platforms={platforms} selectedPlatform={selectedPlatform} onChange={handleSelectPlatform} />
122
- <PageTypeSelect
123
- pageTypes={pageTypes}
124
- selectedPageType={selectedPageType}
125
- onChange={handleSelectPageType}
126
- disabled={noPlatformSelected}
127
- />
128
- <TemplateSelect disabled={noPlatformSelected} />
129
- {selectedPageType?.uid && (
130
- <CollectionTypeSearch uid={selectedPageType.uid} platformTitle={selectedPlatform?.title} />
131
- )}
78
+ <PageTypeSelect pageTypes={pageTypes} selectedPageType={selectedPageType} onChange={handleSelectPageType} />
79
+ <TemplateSelect />
80
+ {selectedPageType?.uid && <CollectionTypeSearch uid={selectedPageType.uid} />}
132
81
  </Stack>
133
82
  ) : (
134
83
  <Details
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+
4
+ import { PageTypeFilter } from './page-type-filter';
5
+ import { PAGE_UID } from '../../../../shared/utils/constants';
6
+
7
+ const PageTypeFilterContainer = () => {
8
+ const { pathname } = useLocation();
9
+
10
+ if (pathname === `/content-manager/collection-type/${PAGE_UID}`) {
11
+ return <PageTypeFilter />;
12
+ }
13
+
14
+ return null;
15
+ };
16
+
17
+ export default PageTypeFilterContainer;
@@ -0,0 +1,130 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import set from 'lodash/set';
3
+
4
+ import { SingleSelectOption, SingleSelect } from '@strapi/design-system';
5
+ import { useQueryParams } from '@strapi/helper-plugin';
6
+
7
+ import { useGetPageTypes } from '../../api/page-type';
8
+ import { PAGE_TYPE_NO_FILTER } from '../../constants';
9
+ import { PAGE_TYPE_PAGE } from '../../../../shared/utils/constants';
10
+
11
+ const PageTypeFilter = () => {
12
+ const [{ query }, setQuery] = useQueryParams() as any;
13
+ const { data: pageTypes } = useGetPageTypes({});
14
+ const initialPageTypeUid = getInitialPageType(query, pageTypes);
15
+ const [selectedPageTypeUid, setSelectedPageTypeUid] = useState(initialPageTypeUid);
16
+
17
+ const removeFilters = () => {
18
+ const newAndFilters = query.filters?.$and?.filter(
19
+ (x?: Record<string, any>) => Object.keys(x || {})?.[0] !== 'pageType'
20
+ );
21
+ const filters = { ...query.filters, $and: newAndFilters };
22
+
23
+ if (newAndFilters && newAndFilters.length === 0) {
24
+ delete filters.$and;
25
+ }
26
+
27
+ setQuery({
28
+ page: 1,
29
+ filters
30
+ });
31
+ };
32
+
33
+ const handleFilterChange = (filters: Record<string, any>) => {
34
+ const currentFilters = query.filters?.$and ? query.filters : { ...query.filters, $and: [] }; // Make sure $and exists.
35
+ const pageTypeFilterIndex = currentFilters.$and.findIndex(
36
+ (x?: Record<string, any>) => Object.keys(x || {})?.[0] === 'pageType'
37
+ );
38
+
39
+ if (pageTypeFilterIndex > -1) {
40
+ set(currentFilters, `$and[${pageTypeFilterIndex}]`, filters); // If the pageType filter already exists, replace it
41
+ } else {
42
+ currentFilters.$and.push(filters);
43
+ }
44
+
45
+ setQuery({
46
+ page: 1,
47
+ filters: currentFilters
48
+ });
49
+ };
50
+
51
+ const handleSelect = (value: string) => {
52
+ setSelectedPageTypeUid(value);
53
+
54
+ if (value === 'all') {
55
+ removeFilters();
56
+ return;
57
+ }
58
+
59
+ if (value === PAGE_TYPE_PAGE) {
60
+ handleFilterChange(nullPageTypeQueryFilter);
61
+ return;
62
+ }
63
+
64
+ handleFilterChange(getPageTypeQueryFilter(value));
65
+ };
66
+
67
+ useEffect(() => {
68
+ setSelectedPageTypeUid(getInitialPageType(query, pageTypes));
69
+ }, [pageTypes]);
70
+
71
+ useEffect(() => {
72
+ if (!query?.filters) {
73
+ setSelectedPageTypeUid(null);
74
+ }
75
+ }, [query]);
76
+
77
+ return (
78
+ <SingleSelect placeholder="Select page type" onChange={handleSelect} size="S" value={selectedPageTypeUid}>
79
+ <SingleSelectOption key={PAGE_TYPE_NO_FILTER} value={PAGE_TYPE_NO_FILTER}>
80
+ Alle Paginatypes
81
+ </SingleSelectOption>
82
+ <SingleSelectOption key={PAGE_TYPE_PAGE} value={PAGE_TYPE_PAGE}>
83
+ Pagina
84
+ </SingleSelectOption>
85
+ {pageTypes?.map((pageType) => (
86
+ <SingleSelectOption key={pageType.uid} value={pageType.uid}>
87
+ {pageType.title || pageType.uid}
88
+ </SingleSelectOption>
89
+ ))}
90
+ </SingleSelect>
91
+ );
92
+ };
93
+
94
+ export { PageTypeFilter };
95
+
96
+ const getPageTypeQueryFilter = (pageType: string) => ({
97
+ pageType: {
98
+ uid: {
99
+ $eq: pageType
100
+ }
101
+ }
102
+ });
103
+
104
+ const nullPageTypeQueryFilter = {
105
+ pageType: {
106
+ uid: {
107
+ $null: true
108
+ }
109
+ }
110
+ };
111
+
112
+ const getInitialPageType = (query: any, pageTypes: any) => {
113
+ const pageTypeFromQuery = query?.filters?.$and?.find(
114
+ (x?: Record<string, any>) => Object.keys(x || {})?.[0] === 'pageType'
115
+ )?.pageType;
116
+
117
+ if (pageTypeFromQuery?.uid?.$null === 'true') {
118
+ return PAGE_TYPE_PAGE;
119
+ }
120
+
121
+ if (pageTypeFromQuery?.uid?.$eq) {
122
+ const matchingPageType = pageTypes?.find((pageType: any) => pageType?.uid === pageTypeFromQuery.uid.$eq);
123
+
124
+ if (matchingPageType) {
125
+ return matchingPageType.uid;
126
+ }
127
+ }
128
+
129
+ return PAGE_TYPE_NO_FILTER;
130
+ };
@@ -3,7 +3,7 @@ import { prefixPluginTranslations } from '@strapi/helper-plugin';
3
3
  import pluginPkg from '../../package.json';
4
4
  import pluginId from './pluginId';
5
5
  import Initializer from './components/Initializer';
6
- import PageFiltersContainer from './components/PageFilters';
6
+ import PageTypeFilter from './components/PageTypeFilter';
7
7
  import { EditView } from './components/EditView';
8
8
  import middlewares from './middlewares';
9
9
 
@@ -29,7 +29,7 @@ export default {
29
29
  });
30
30
  app.injectContentManagerComponent('listView', 'actions', {
31
31
  name: 'page-filters',
32
- Component: PageFiltersContainer
32
+ Component: PageTypeFilter
33
33
  });
34
34
  },
35
35
 
@@ -1,10 +1,93 @@
1
- export const sanitizeModules = (modules: Record<string, any>[]) => {
2
- return modules?.map((module, idx) => {
3
- delete module.id;
4
-
5
- return {
6
- ...module,
7
- __temp_key__: idx
8
- };
1
+ export const sanitizeModules = (modules: Record<string, any>[], convertToJSON = true) =>
2
+ modules?.map((module, idx) => {
3
+ try {
4
+ const sanitizedModule = sanitizeObject(module, idx, convertToJSON);
5
+
6
+ // go through top level properties in the module
7
+ for (let prop in sanitizedModule) {
8
+ // Strapi didn't give us the properties in an array when we used the Strapi API to get modules
9
+ // But it crashes when we try and have single relations not in an array, so we put them into an array.
10
+ if (
11
+ sanitizedModule[prop] &&
12
+ sanitizedModule[prop].hasOwnProperty('__is_array__') &&
13
+ !Array.isArray(sanitizedModule[prop])
14
+ ) {
15
+ sanitizedModule[prop] = [{ ...sanitizedModule[prop] }];
16
+ }
17
+
18
+ // If a property is null, Strapi decides it should crash completely so we remove those.
19
+ if (sanitizedModule[prop] === null) {
20
+ delete sanitizedModule[prop];
21
+ }
22
+ }
23
+
24
+ const result = {
25
+ ...sanitizedModule
26
+ };
27
+
28
+ return result;
29
+ } catch (error) {
30
+ console.error('ERROR IN SANITIZE MODULES', error);
31
+ }
9
32
  });
10
- };
33
+
34
+ function sanitizeObject(obj: any, idx: number, convertToJSON = true): any {
35
+ try {
36
+ for (let key in obj) {
37
+ try {
38
+ if (typeof obj[key] === 'object' && obj[key] !== null && obj[key] !== undefined) {
39
+ sanitizeObject(obj[key], idx);
40
+ }
41
+ } catch (error) {
42
+ console.error('ERROR in loop', error);
43
+ return undefined;
44
+ }
45
+ }
46
+
47
+ // We have to remove the id for components, but not for relations to other collection items
48
+ // We use createdAt to check, only collection items have this property (hopefully)
49
+ if (obj.hasOwnProperty('id') && !obj.hasOwnProperty('createdAt')) {
50
+ obj['__temp_key__'] = idx;
51
+ delete obj['id'];
52
+ } else if (obj.hasOwnProperty('id') && obj.hasOwnProperty('createdAt')) {
53
+ // if it's a relation to a collection item, it has to be in an array because it crashes otherwise.
54
+ // We use this property at the top of this file, only for top level properties.
55
+ obj['__is_array__'] = true;
56
+ }
57
+
58
+ try {
59
+ if (obj.hasOwnProperty('defaultModuleOptions')) {
60
+ delete obj['defaultModuleOptions'].id;
61
+
62
+ let spacing = obj['defaultModuleOptions']?.spacing;
63
+
64
+ if (convertToJSON && spacing) {
65
+ spacing = JSON.stringify(spacing);
66
+ }
67
+
68
+ obj['defaultModuleOptions'] = {
69
+ ...obj['defaultModuleOptions'],
70
+ spacing,
71
+ __temp_key__: idx
72
+ };
73
+ }
74
+ } catch (error) {
75
+ console.error('error in defaultmodule options', error);
76
+ }
77
+
78
+ // Strapi doesnt give us the mainfield and label properties when we get the data using the Strapi API
79
+ // So we add them here using other properties. This might be expanded with more if statements if required,
80
+ if (obj.hasOwnProperty('name') && !obj.hasOwnProperty('mainField') && !obj.hasOwnProperty('label')) {
81
+ obj['label'] = obj['name'];
82
+ obj['mainField'] = obj['name'];
83
+ }
84
+ if (obj.hasOwnProperty('title') && !obj.hasOwnProperty('mainField') && !obj.hasOwnProperty('label')) {
85
+ obj['label'] = obj['title'];
86
+ obj['mainField'] = obj['title'];
87
+ }
88
+
89
+ return obj;
90
+ } catch (error) {
91
+ console.error('error in root sanitize object', error);
92
+ }
93
+ }