@webbio/strapi-plugin-page-builder 0.0.1

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 (75) hide show
  1. package/.prettierignore +17 -0
  2. package/README.md +3 -0
  3. package/admin/src/api/collection-type.ts +110 -0
  4. package/admin/src/api/has-page-relation.ts +34 -0
  5. package/admin/src/api/page-type.ts +31 -0
  6. package/admin/src/api/template.ts +25 -0
  7. package/admin/src/components/Combobox/index.tsx +77 -0
  8. package/admin/src/components/Combobox/react-select-custom-styles.tsx +111 -0
  9. package/admin/src/components/Combobox/styles.ts +22 -0
  10. package/admin/src/components/ConfirmModal/index.tsx +90 -0
  11. package/admin/src/components/EditView/CollectionTypeSearch/index.tsx +118 -0
  12. package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/index.tsx +95 -0
  13. package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/styles.ts +26 -0
  14. package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +53 -0
  15. package/admin/src/components/EditView/Details/index.tsx +47 -0
  16. package/admin/src/components/EditView/Details/styles.ts +51 -0
  17. package/admin/src/components/EditView/PageSettings/index.tsx +104 -0
  18. package/admin/src/components/EditView/Template/TemplateConfirmModal/index.tsx +36 -0
  19. package/admin/src/components/EditView/Template/TemplateSelect/index.tsx +64 -0
  20. package/admin/src/components/EditView/Template/TemplateSelect/use-template-modules.ts +30 -0
  21. package/admin/src/components/EditView/index.tsx +27 -0
  22. package/admin/src/components/EditView/page-type-select.tsx +30 -0
  23. package/admin/src/components/EditView/wrapper.tsx +35 -0
  24. package/admin/src/components/Initializer/index.tsx +24 -0
  25. package/admin/src/components/PageTypeFilter/index.tsx +17 -0
  26. package/admin/src/components/PageTypeFilter/page-type-filter.tsx +130 -0
  27. package/admin/src/components/PluginIcon/index.tsx +12 -0
  28. package/admin/src/constants.ts +1 -0
  29. package/admin/src/index.tsx +115 -0
  30. package/admin/src/pages/App/index.tsx +25 -0
  31. package/admin/src/pages/HomePage/index.tsx +19 -0
  32. package/admin/src/pluginId.ts +5 -0
  33. package/admin/src/redux/initialData.reducer.ts +0 -0
  34. package/admin/src/translations/en.json +6 -0
  35. package/admin/src/translations/nl.json +6 -0
  36. package/admin/src/utils/getRequestUrl.ts +11 -0
  37. package/admin/src/utils/getTrad.ts +5 -0
  38. package/admin/src/utils/hooks/useDebounce.ts +17 -0
  39. package/admin/src/utils/hooks/useGetLocaleFromUrl.ts +9 -0
  40. package/admin/src/utils/hooks/usePrevious.ts +12 -0
  41. package/admin/src/utils/sanitizeModules.ts +10 -0
  42. package/custom.d.ts +5 -0
  43. package/package.json +71 -0
  44. package/server/bootstrap.ts +106 -0
  45. package/server/config/index.ts +4 -0
  46. package/server/content-types/index.ts +1 -0
  47. package/server/controllers/collection-types.ts +27 -0
  48. package/server/controllers/index.ts +9 -0
  49. package/server/controllers/page-type.ts +12 -0
  50. package/server/controllers/page.ts +20 -0
  51. package/server/destroy.ts +5 -0
  52. package/server/index.ts +23 -0
  53. package/server/middlewares/index.ts +1 -0
  54. package/server/policies/index.ts +1 -0
  55. package/server/register.ts +5 -0
  56. package/server/routes/index.ts +42 -0
  57. package/server/schema/page-end.json +97 -0
  58. package/server/schema/page-start.json +87 -0
  59. package/server/schema/page-type-end.json +49 -0
  60. package/server/schema/page-type-start.json +44 -0
  61. package/server/schema/template.json +35 -0
  62. package/server/services/builder.ts +121 -0
  63. package/server/services/collection-types.ts +49 -0
  64. package/server/services/index.ts +11 -0
  65. package/server/services/page-type.ts +22 -0
  66. package/server/services/page.ts +24 -0
  67. package/server/utils/graphql.ts +110 -0
  68. package/server/utils/reload-strapi-on-load.ts +13 -0
  69. package/shared/utils/constants.ts +4 -0
  70. package/shared/utils/sleep.ts +1 -0
  71. package/strapi-admin.js +3 -0
  72. package/strapi-server.js +3 -0
  73. package/tsconfig.json +20 -0
  74. package/tsconfig.server.json +25 -0
  75. package/tsconfig.tsbuildinfo +1 -0
@@ -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();
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
+ All Pagetypes
81
+ </SingleSelectOption>
82
+ <SingleSelectOption key={PAGE_TYPE_PAGE} value={PAGE_TYPE_PAGE}>
83
+ Page
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
+ };
@@ -0,0 +1,12 @@
1
+ /**
2
+ *
3
+ * PluginIcon
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import { Puzzle } from '@strapi/icons';
9
+
10
+ const PluginIcon = () => <Puzzle />;
11
+
12
+ export default PluginIcon;
@@ -0,0 +1 @@
1
+ export const PAGE_TYPE_NO_FILTER = 'all';
@@ -0,0 +1,115 @@
1
+ import { prefixPluginTranslations } from '@strapi/helper-plugin';
2
+
3
+ import { getFetchClient } from '@strapi/helper-plugin';
4
+
5
+ import pluginPkg from '../../package.json';
6
+ import pluginId from './pluginId';
7
+ import Initializer from './components/Initializer';
8
+ import PluginIcon from './components/PluginIcon';
9
+ import PageTypeFilter from './components/PageTypeFilter';
10
+ import { EditView } from './components/EditView';
11
+ import getRequestUrl from './utils/getRequestUrl';
12
+ import { PAGE_UID } from '../../shared/utils/constants';
13
+
14
+ const name = pluginPkg.strapi.name;
15
+
16
+ const middleware =
17
+ () =>
18
+ ({ getState }: any) =>
19
+ (next: any) =>
20
+ async (action: any) => {
21
+ // If data is being fetched or after a save
22
+ if (
23
+ action.type === 'ContentManager/CrudReducer/GET_DATA_SUCCEEDED' ||
24
+ action.type === 'ContentManager/CrudReducer/SUBMIT_SUCCEEDED'
25
+ ) {
26
+ const store = getState();
27
+ const layoutSlice = store['content-manager_editViewLayoutManager'];
28
+ const uid = layoutSlice.currentLayout.contentType.uid;
29
+
30
+ if (uid !== PAGE_UID) {
31
+ return next(action);
32
+ }
33
+
34
+ const { get } = await getFetchClient();
35
+ const { data: pageData } = await get(`${getRequestUrl('page')}/${action.data.id}`);
36
+
37
+ action.data.collectionTypeTitle = pageData?.collectionTypeData?.title;
38
+ action.data.collectionTypeId = pageData?.collectionTypeData?.id;
39
+ action.data.initialPageType = pageData?.pageType ? pageData.pageType : undefined;
40
+
41
+ return next(action);
42
+ }
43
+ return next(action);
44
+ };
45
+
46
+ const middlewares = [middleware];
47
+
48
+ export default {
49
+ register(app: any) {
50
+ app.addMiddlewares(middlewares);
51
+ app.addMenuLink({
52
+ to: `/plugins/${pluginId}`,
53
+ icon: PluginIcon,
54
+ intlLabel: {
55
+ id: `${pluginId}.plugin.name`,
56
+ defaultMessage: name
57
+ },
58
+ Component: async () => {
59
+ const component = await import(/* webpackChunkName: "[request]" */ './pages/App');
60
+
61
+ return component;
62
+ },
63
+ permissions: [
64
+ // Uncomment to set the permissions of the plugin here
65
+ // {
66
+ // action: '', // the action name should be plugin::plugin-name.actionType
67
+ // subject: null,
68
+ // },
69
+ ]
70
+ });
71
+ const plugin = {
72
+ id: pluginId,
73
+ initializer: Initializer,
74
+ isReady: false,
75
+ name
76
+ };
77
+
78
+ app.registerPlugin(plugin);
79
+ },
80
+
81
+ bootstrap(app: any) {
82
+ app.injectContentManagerComponent('editView', 'right-links', {
83
+ name: 'collectionType-picker',
84
+ Component: EditView
85
+ });
86
+ app.injectContentManagerComponent('listView', 'actions', {
87
+ name: 'collectionType-picker',
88
+ Component: PageTypeFilter
89
+ });
90
+ },
91
+
92
+ async registerTrads(app: any) {
93
+ const { locales } = app;
94
+
95
+ const importedTrads = await Promise.all(
96
+ (locales as string[]).map((locale) => {
97
+ return import(`./translations/${locale}.json`)
98
+ .then(({ default: data }) => {
99
+ return {
100
+ data: prefixPluginTranslations(data, pluginId),
101
+ locale
102
+ };
103
+ })
104
+ .catch(() => {
105
+ return {
106
+ data: {},
107
+ locale
108
+ };
109
+ });
110
+ })
111
+ );
112
+
113
+ return Promise.resolve(importedTrads);
114
+ }
115
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ *
3
+ * This component is the skeleton around the actual pages, and should only
4
+ * contain code that should be seen on all pages. (e.g. navigation bar)
5
+ *
6
+ */
7
+
8
+ import React from 'react';
9
+ import { Switch, Route } from 'react-router-dom';
10
+ import { AnErrorOccurred } from '@strapi/helper-plugin';
11
+ import pluginId from '../../pluginId';
12
+ import HomePage from '../HomePage';
13
+
14
+ const App = () => {
15
+ return (
16
+ <div>
17
+ <Switch>
18
+ <Route path={`/plugins/${pluginId}`} component={HomePage} exact />
19
+ <Route component={AnErrorOccurred} />
20
+ </Switch>
21
+ </div>
22
+ );
23
+ };
24
+
25
+ export default App;
@@ -0,0 +1,19 @@
1
+ /*
2
+ *
3
+ * HomePage
4
+ *
5
+ */
6
+
7
+ import React from 'react';
8
+ import pluginId from '../../pluginId';
9
+
10
+ const HomePage = () => {
11
+ return (
12
+ <div>
13
+ <h1>{pluginId}&apos;s HomePage</h1>
14
+ <p>Happy coding</p>
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export default HomePage;
@@ -0,0 +1,5 @@
1
+ import pluginPkg from '../../package.json';
2
+
3
+ const pluginId = pluginPkg.strapi.name;
4
+
5
+ export default pluginId;
File without changes
@@ -0,0 +1,6 @@
1
+ {
2
+ "template.confirmModal.title": "Replace all page modules",
3
+ "template.confirmModal.body": "You are about to replace all modules on this page. Are you sure you want to continue?",
4
+ "template.confirmModal.buttons.cancel": "No, cancel",
5
+ "template.confirmModal.buttons.submit": "Yes, replace all"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "template.confirmModal.title": "Pagina modules vervangen",
3
+ "template.confirmModal.body": "Weet je zeker dat je alle modules op deze pagina wilt vervangen?",
4
+ "template.confirmModal.buttons.cancel": "Nee, annuleer",
5
+ "template.confirmModal.buttons.submit": "Ja, vervang modules"
6
+ }
@@ -0,0 +1,11 @@
1
+ import pluginId from '../pluginId';
2
+
3
+ const getRequestUrl = (path: string) => {
4
+ if (path.startsWith('/')) {
5
+ return `/${pluginId}${path}`;
6
+ }
7
+
8
+ return `/${pluginId}/${path}`;
9
+ };
10
+
11
+ export default getRequestUrl;
@@ -0,0 +1,5 @@
1
+ import pluginId from '../pluginId';
2
+
3
+ const getTrad = (id: string) => `${pluginId}.${id}`;
4
+
5
+ export default getTrad;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ export function useDebounce<T>(value: T, delay: number): T {
4
+ const [debouncedValue, setDebouncedValue] = React.useState(value);
5
+
6
+ React.useEffect(() => {
7
+ const handler = setTimeout(() => {
8
+ setDebouncedValue(value);
9
+ }, delay);
10
+
11
+ return () => {
12
+ clearTimeout(handler);
13
+ };
14
+ }, [value, delay]);
15
+
16
+ return debouncedValue;
17
+ }
@@ -0,0 +1,9 @@
1
+ import { useQueryParams } from '@strapi/helper-plugin';
2
+
3
+ const useGetLocaleFromUrl = () => {
4
+ const [{ query }] = useQueryParams();
5
+
6
+ return query?.plugins?.i18n?.locale;
7
+ };
8
+
9
+ export { useGetLocaleFromUrl };
@@ -0,0 +1,12 @@
1
+ import { useRef, useEffect } from 'react';
2
+
3
+ // Hook
4
+ export const usePrevious = <T>(value: T): T | null => {
5
+ const ref: React.MutableRefObject<T | null> = useRef(null);
6
+
7
+ useEffect(() => {
8
+ ref.current = value;
9
+ }, [value]);
10
+
11
+ return ref.current;
12
+ };
@@ -0,0 +1,10 @@
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
+ };
9
+ });
10
+ };
package/custom.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ declare module '@strapi/design-system/*';
2
+ declare module '@strapi/design-system';
3
+ declare module '@strapi/icons';
4
+ declare module '@strapi/icons/*';
5
+ declare module '@strapi/helper-plugin';
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@webbio/strapi-plugin-page-builder",
3
+ "version": "0.0.1",
4
+ "description": "This is the description of the plugin.",
5
+ "scripts": {
6
+ "develop": "tsc -p tsconfig.server.json -w",
7
+ "build": "tsc -p tsconfig.server.json",
8
+ "spu": "yarn upgrade-interactive --latest",
9
+ "prepublish": "tsc",
10
+ "format": "prettier --write ."
11
+ },
12
+ "strapi": {
13
+ "name": "page-builder",
14
+ "description": "Description of page-builder plugin",
15
+ "displayName": "Page Builder",
16
+ "kind": "plugin"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/webbio/strapi-plugin-internal-links.git"
21
+ },
22
+ "dependencies": {
23
+ "@strapi/design-system": "^1.8.2",
24
+ "@strapi/helper-plugin": "^4.12.0",
25
+ "@strapi/icons": "^1.8.2",
26
+ "@strapi/typescript-utils": "^4.12.0",
27
+ "@strapi/utils": "^4.12.0",
28
+ "react-select": "^5.7.4"
29
+ },
30
+ "devDependencies": {
31
+ "@strapi/typescript-utils": "^4.12.0",
32
+ "@types/react": "^17.0.53",
33
+ "@types/react-dom": "^18.0.28",
34
+ "@types/react-router-dom": "^5.3.3",
35
+ "@types/styled-components": "^5.1.26",
36
+ "react": "^18.2.0",
37
+ "react-dom": "^18.2.0",
38
+ "react-router-dom": "^5.3.4",
39
+ "styled-components": "^5.3.6",
40
+ "typescript": "5.1.6"
41
+ },
42
+ "peerDependencies": {
43
+ "@strapi/strapi": "^4.12.0",
44
+ "@webbio/strapi-plugin-slug": "^0.1.0",
45
+ "react": "^17.0.0 || ^18.0.0",
46
+ "react-dom": "^17.0.0 || ^18.0.0",
47
+ "react-router-dom": "^5.3.4",
48
+ "styled-components": "^5.3.6"
49
+ },
50
+ "author": {
51
+ "name": "Webbio B.V.",
52
+ "email": "info@webbio.nl",
53
+ "url": "https://webbio.nl"
54
+ },
55
+ "maintainers": [
56
+ {
57
+ "name": "Webbio B.V.",
58
+ "email": "info@webbio.nl",
59
+ "url": "https://webbio.nl"
60
+ }
61
+ ],
62
+ "engines": {
63
+ "node": ">=14.19.1 <=18.x.x",
64
+ "npm": ">=6.0.0"
65
+ },
66
+ "license": "MIT",
67
+ "publishConfig": {
68
+ "access": "public",
69
+ "registry": "https://registry.npmjs.org"
70
+ }
71
+ }
@@ -0,0 +1,106 @@
1
+ import { Strapi } from '@strapi/strapi';
2
+ import { errors } from '@strapi/utils';
3
+
4
+ import { PAGE_UID } from '../shared/utils/constants';
5
+
6
+ export default async ({ strapi }: { strapi: Strapi }) => {
7
+ // TODO: refactor to own function
8
+ try {
9
+ const pageTypes = await strapi.entityService.findMany('api::page-type.page-type', {
10
+ limit: -1
11
+ });
12
+ const pagePermissions = pageTypes.map((pageType) => {
13
+ const name = `page-type-is-${pageType.uid}`;
14
+ const displayName = pageType.title;
15
+
16
+ return {
17
+ plugin: 'page-builder',
18
+ name,
19
+ displayName,
20
+ category: 'Page type',
21
+ handler: async () => {
22
+ return {
23
+ $or: [
24
+ {
25
+ 'pageType.uid': {
26
+ $eq: pageType.uid
27
+ }
28
+ },
29
+ {
30
+ uid: {
31
+ $eq: pageType.uid
32
+ }
33
+ }
34
+ ]
35
+ };
36
+ }
37
+ };
38
+ });
39
+ // @ts-ignore shitty types
40
+ await strapi.admin.services.permission.conditionProvider.registerMany(pagePermissions);
41
+ } catch {
42
+ console.log('Cannot set page permissions');
43
+ }
44
+
45
+ const updateCollectionTypeData = (data: any, collectionTypeId: number, uid: string) => {
46
+ data.collectionTypeData = {
47
+ id: collectionTypeId,
48
+ __type: uid,
49
+ __pivot: {
50
+ field: 'page'
51
+ }
52
+ };
53
+
54
+ return data;
55
+ };
56
+
57
+ strapi.db.lifecycles.subscribe({
58
+ // @ts-ignore
59
+ models: [PAGE_UID],
60
+ async beforeCreate(event) {
61
+ let { data } = event.params;
62
+ const collectionTypeId = data?.collectionTypeId;
63
+ const pageTypeId = data?.pageType.connect?.[0]?.id || data.initialPageType;
64
+
65
+ if (collectionTypeId && pageTypeId) {
66
+ const { uid } = await strapi.entityService.findOne('api::page-type.page-type', pageTypeId);
67
+ const collectionToConnect = await strapi.entityService.findOne(uid, collectionTypeId, {
68
+ populate: { page: true }
69
+ });
70
+
71
+ if (collectionToConnect.page && collectionToConnect.page.length > 0) {
72
+ throw new errors.ValidationError('You can only link one CollectionType to one page');
73
+ }
74
+
75
+ data = updateCollectionTypeData(data, collectionTypeId, uid);
76
+ }
77
+ },
78
+ async beforeUpdate(event) {
79
+ let data = event?.params?.data;
80
+ const collectionTypeId = data?.collectionTypeId;
81
+ const pageTypeId = data?.pageType?.connect?.[0]?.id || data?.initialPageType;
82
+ const removedPageType = data?.pageType?.disconnect?.[0]?.id;
83
+
84
+ if (removedPageType || collectionTypeId === null) {
85
+ data.collectionTypeData = undefined;
86
+ }
87
+
88
+ if (collectionTypeId && pageTypeId && !removedPageType) {
89
+ const { uid } = await strapi.entityService.findOne('api::page-type.page-type', pageTypeId);
90
+ const collectionToConnect = await strapi.entityService.findOne(uid, collectionTypeId, {
91
+ populate: { page: true }
92
+ });
93
+
94
+ if (
95
+ collectionToConnect.page &&
96
+ collectionToConnect.page.length > 0 &&
97
+ !collectionToConnect?.page.some((p) => p.id === data.id)
98
+ ) {
99
+ throw new errors.ValidationError('You can only link one CollectionType to one page');
100
+ }
101
+
102
+ data = updateCollectionTypeData(data, collectionTypeId, uid);
103
+ }
104
+ }
105
+ });
106
+ };
@@ -0,0 +1,4 @@
1
+ export default {
2
+ default: {},
3
+ validator() {}
4
+ };
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,27 @@
1
+ export default {
2
+ async hasPageRelation(ctx: any) {
3
+ const uid = ctx.params?.uid;
4
+
5
+ // @ts-ignore
6
+ return strapi.service('plugin::page-builder.collection-types').hasPageRelation(uid);
7
+ },
8
+ async getTranslationPageLinks(ctx: any): Promise<IGetTranslationPageLinks[] | undefined> {
9
+ const { uid, ids } = ctx.params || {};
10
+ const idsArr = (ids as string)
11
+ ?.split(',')
12
+ .map(Number)
13
+ .filter((x) => !isNaN(x));
14
+
15
+ if (!uid || idsArr?.length === 0) {
16
+ return undefined;
17
+ }
18
+
19
+ // @ts-ignore
20
+ return strapi.service('plugin::page-builder.collection-types').getTranslationPageLinks(uid, idsArr);
21
+ }
22
+ };
23
+
24
+ export interface IGetTranslationPageLinks {
25
+ id: number;
26
+ locale: string;
27
+ }
@@ -0,0 +1,9 @@
1
+ import page from './page';
2
+ import pageType from './page-type';
3
+ import collectionTypes from './collection-types';
4
+
5
+ export default {
6
+ page,
7
+ 'page-type': pageType,
8
+ 'collection-types': collectionTypes
9
+ };
@@ -0,0 +1,12 @@
1
+ export default {
2
+ async findOneByUid(ctx: any) {
3
+ const uid = ctx?.params?.uid;
4
+
5
+ if (!uid) {
6
+ return ctx.badRequest('uid is missing.');
7
+ }
8
+
9
+ // @ts-ignore strapi typings
10
+ return await strapi.service('plugin::page-builder.page-type').findOneByUid(uid);
11
+ }
12
+ };
@@ -0,0 +1,20 @@
1
+ import { Strapi } from '@strapi/strapi';
2
+
3
+ import { PAGE_UID } from '../../shared/utils/constants';
4
+
5
+ export default {
6
+ async getPage(ctx) {
7
+ const id = ctx.params?.id;
8
+
9
+ return (strapi as Strapi).service('plugin::page-builder.page')?.getPage(id);
10
+ },
11
+
12
+ async findOneBySlug(ctx) {
13
+ const { slug = '' } = ctx.params;
14
+
15
+ const entity = await (strapi as Strapi).service(PAGE_UID)?.find({ filters: { slug } });
16
+ const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
17
+
18
+ return this.transformResponse(sanitizedEntity);
19
+ }
20
+ };
@@ -0,0 +1,5 @@
1
+ import { Strapi } from '@strapi/strapi';
2
+
3
+ export default ({ strapi }: { strapi: Strapi }) => {
4
+ // destroy phase
5
+ };