@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,23 @@
1
+ import register from './register';
2
+ import bootstrap from './bootstrap';
3
+ import destroy from './destroy';
4
+ import config from './config';
5
+ import contentTypes from './content-types';
6
+ import controllers from './controllers';
7
+ import routes from './routes';
8
+ import middlewares from './middlewares';
9
+ import policies from './policies';
10
+ import services from './services';
11
+
12
+ export default {
13
+ register,
14
+ bootstrap,
15
+ destroy,
16
+ config,
17
+ controllers,
18
+ routes,
19
+ services,
20
+ contentTypes,
21
+ policies,
22
+ middlewares
23
+ };
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1 @@
1
+ export default {};
@@ -0,0 +1,5 @@
1
+ import { Strapi } from '@strapi/strapi';
2
+
3
+ export default async ({ strapi }: { strapi: Strapi }) => {
4
+ await strapi.services?.['plugin::page-builder.builder']?.buildContentTypes();
5
+ };
@@ -0,0 +1,42 @@
1
+ const routes = {
2
+ 'page-type': {
3
+ type: 'admin',
4
+ prefix: undefined,
5
+ routes: [
6
+ {
7
+ method: 'GET',
8
+ path: '/page-types/:uid',
9
+ handler: 'page-type.findOneByUid'
10
+ }
11
+ ]
12
+ },
13
+ page: {
14
+ type: 'admin',
15
+ prefix: undefined,
16
+ routes: [
17
+ {
18
+ method: 'GET',
19
+ path: '/page/:id',
20
+ handler: 'page.getPage'
21
+ }
22
+ ]
23
+ },
24
+ 'collection-types': {
25
+ type: 'admin',
26
+ prefix: undefined,
27
+ routes: [
28
+ {
29
+ method: 'GET',
30
+ path: '/collection-types/:uid',
31
+ handler: 'collection-types.hasPageRelation'
32
+ },
33
+ {
34
+ method: 'GET',
35
+ path: '/collection-types-page-links/:uid/:ids?',
36
+ handler: 'collection-types.getTranslationPageLinks'
37
+ }
38
+ ]
39
+ }
40
+ };
41
+
42
+ export default routes;
@@ -0,0 +1,97 @@
1
+ {
2
+ "draftAndPublish": true,
3
+ "displayName": "Pagina's",
4
+ "singularName": "page",
5
+ "pluralName": "pages",
6
+ "description": "",
7
+ "plugin": "page-builder",
8
+ "pluginOptions": {
9
+ "i18n": {
10
+ "localized": true
11
+ }
12
+ },
13
+ "kind": "collectionType",
14
+ "collectionName": "pages",
15
+ "attributes": {
16
+ "collectionTypeData": {
17
+ "type": "relation",
18
+ "relation": "morphToMany",
19
+ "configurable": false
20
+ },
21
+ "title": {
22
+ "pluginOptions": {
23
+ "i18n": {
24
+ "localized": true
25
+ }
26
+ },
27
+ "type": "string",
28
+ "required": true
29
+ },
30
+ "slug": {
31
+ "pluginOptions": {
32
+ "i18n": {
33
+ "localized": true
34
+ },
35
+ "slug": {
36
+ "field": "slug",
37
+ "targetField": "title"
38
+ }
39
+ },
40
+ "type": "customField",
41
+ "customField": "plugin::slug.slug",
42
+ "required": true
43
+ },
44
+ "path": {
45
+ "pluginOptions": {
46
+ "i18n": {
47
+ "localized": true
48
+ }
49
+ },
50
+ "type": "string",
51
+ "required": false
52
+ },
53
+ "parent": {
54
+ "type": "relation",
55
+ "relation": "oneToOne",
56
+ "target": "api::page.page",
57
+ "private": false
58
+ },
59
+ "excerpt": {
60
+ "pluginOptions": {
61
+ "i18n": {
62
+ "localized": true
63
+ }
64
+ },
65
+ "type": "text"
66
+ },
67
+ "modules": {
68
+ "pluginOptions": {
69
+ "i18n": {
70
+ "localized": true
71
+ }
72
+ },
73
+ "type": "dynamiczone",
74
+ "components": []
75
+ },
76
+ "seo": {
77
+ "type": "component",
78
+ "pluginOptions": {
79
+ "i18n": {
80
+ "localized": true
81
+ }
82
+ },
83
+ "component": "shared.seo"
84
+ },
85
+ "template": {
86
+ "relation": "manyToOne",
87
+ "target": "api::template.template",
88
+ "targetAttribute": "pages",
89
+ "type": "relation"
90
+ },
91
+ "pageType": {
92
+ "type": "relation",
93
+ "relation": "oneToOne",
94
+ "target": "api::page-type.page-type"
95
+ }
96
+ }
97
+ }
@@ -0,0 +1,87 @@
1
+ {
2
+ "draftAndPublish": true,
3
+ "displayName": "Pagina's",
4
+ "singularName": "page",
5
+ "pluralName": "pages",
6
+ "description": "",
7
+ "plugin": "page-builder",
8
+ "pluginOptions": {
9
+ "i18n": {
10
+ "localized": true
11
+ }
12
+ },
13
+ "kind": "collectionType",
14
+ "collectionName": "pages",
15
+ "attributes": {
16
+ "collectionTypeData": {
17
+ "type": "relation",
18
+ "relation": "morphToMany",
19
+ "configurable": false,
20
+ "private": false
21
+ },
22
+ "title": {
23
+ "pluginOptions": {
24
+ "i18n": {
25
+ "localized": true
26
+ }
27
+ },
28
+ "type": "string",
29
+ "required": true
30
+ },
31
+ "slug": {
32
+ "pluginOptions": {
33
+ "i18n": {
34
+ "localized": true
35
+ },
36
+ "slug": {
37
+ "field": "slug",
38
+ "targetField": "title"
39
+ }
40
+ },
41
+ "type": "customField",
42
+ "customField": "plugin::slug.slug",
43
+ "required": true
44
+ },
45
+ "path": {
46
+ "pluginOptions": {
47
+ "i18n": {
48
+ "localized": true
49
+ }
50
+ },
51
+ "type": "string",
52
+ "required": false
53
+ },
54
+ "parent": {
55
+ "type": "relation",
56
+ "relation": "oneToOne",
57
+ "target": "api::page.page",
58
+ "private": false
59
+ },
60
+ "excerpt": {
61
+ "pluginOptions": {
62
+ "i18n": {
63
+ "localized": true
64
+ }
65
+ },
66
+ "type": "text"
67
+ },
68
+ "modules": {
69
+ "pluginOptions": {
70
+ "i18n": {
71
+ "localized": true
72
+ }
73
+ },
74
+ "type": "dynamiczone",
75
+ "components": []
76
+ },
77
+ "seo": {
78
+ "type": "component",
79
+ "pluginOptions": {
80
+ "i18n": {
81
+ "localized": true
82
+ }
83
+ },
84
+ "component": "shared.seo"
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "draftAndPublish": false,
3
+ "displayName": "Pagina Types",
4
+ "singularName": "page-type",
5
+ "pluralName": "page-types",
6
+ "description": "",
7
+ "plugin": "page-builder",
8
+ "pluginOptions": {
9
+ "i18n": {
10
+ "localized": true
11
+ }
12
+ },
13
+ "kind": "collectionType",
14
+ "collectionName": "page_types",
15
+ "attributes": {
16
+ "uid": {
17
+ "type": "string",
18
+ "required": true,
19
+ "unique": true,
20
+ "pluginOptions": {
21
+ "i18n": {
22
+ "localized": true
23
+ }
24
+ }
25
+ },
26
+ "title": {
27
+ "type": "string",
28
+ "pluginOptions": {
29
+ "i18n": {
30
+ "localized": true
31
+ }
32
+ }
33
+ },
34
+ "modules": {
35
+ "type": "dynamiczone",
36
+ "components": [],
37
+ "pluginOptions": {
38
+ "i18n": {
39
+ "localized": true
40
+ }
41
+ }
42
+ },
43
+ "template": {
44
+ "type": "relation",
45
+ "relation": "oneToOne",
46
+ "target": "api::template.template"
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "draftAndPublish": false,
3
+ "displayName": "Pagina Types",
4
+ "singularName": "page-type",
5
+ "pluralName": "page-types",
6
+ "description": "",
7
+ "plugin": "page-builder",
8
+ "pluginOptions": {
9
+ "i18n": {
10
+ "localized": true
11
+ }
12
+ },
13
+ "kind": "collectionType",
14
+ "collectionName": "page_types",
15
+ "attributes": {
16
+ "uid": {
17
+ "type": "string",
18
+ "required": true,
19
+ "unique": true,
20
+ "pluginOptions": {
21
+ "i18n": {
22
+ "localized": true
23
+ }
24
+ }
25
+ },
26
+ "title": {
27
+ "type": "string",
28
+ "pluginOptions": {
29
+ "i18n": {
30
+ "localized": true
31
+ }
32
+ }
33
+ },
34
+ "modules": {
35
+ "type": "dynamiczone",
36
+ "components": [],
37
+ "pluginOptions": {
38
+ "i18n": {
39
+ "localized": true
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "draftAndPublish": false,
3
+ "displayName": "Templates",
4
+ "singularName": "template",
5
+ "pluralName": "templates",
6
+ "description": "",
7
+ "plugin": "page-builder",
8
+ "pluginOptions": {
9
+ "i18n": {
10
+ "localized": true
11
+ }
12
+ },
13
+ "kind": "collectionType",
14
+ "collectionName": "templates",
15
+ "attributes": {
16
+ "title": {
17
+ "type": "string",
18
+ "required": true,
19
+ "pluginOptions": {
20
+ "i18n": {
21
+ "localized": true
22
+ }
23
+ }
24
+ },
25
+ "modules": {
26
+ "type": "dynamiczone",
27
+ "components": [],
28
+ "pluginOptions": {
29
+ "i18n": {
30
+ "localized": true
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,121 @@
1
+ import partition from 'lodash/partition';
2
+
3
+ import { Service } from '@strapi/strapi/lib/types/core/common';
4
+
5
+ import pageStart from '../schema/page-start.json';
6
+ import pageEnd from '../schema/page-end.json';
7
+ import pluginId from '../../admin/src/pluginId';
8
+ import { PAGE_UID, TEMPLATE_UID, PAGE_TYPE_UID } from '../../shared/utils/constants';
9
+ import { reloadStrapiOnLoad } from '../utils/reload-strapi-on-load';
10
+ import pageTypeStart from '../schema/page-type-start.json';
11
+ import pageTypeEnd from '../schema/page-type-end.json';
12
+ import template from '../schema/template.json';
13
+
14
+ const UIDS = [TEMPLATE_UID, PAGE_TYPE_UID, PAGE_UID];
15
+
16
+ export default {
17
+ async buildContentTypes() {
18
+ this.listenToCreatedContentTypes();
19
+
20
+ await this.createContentTypes();
21
+
22
+ await this.updateContentTypes();
23
+ },
24
+ listenToCreatedContentTypes() {
25
+ strapi.eventHub.on('content-type.create', async (e) => {
26
+ if (e.contentType.uid === PAGE_UID) {
27
+ reloadStrapiOnLoad();
28
+ }
29
+ });
30
+ },
31
+
32
+ async createContentTypes() {
33
+ const newContentTypes = UIDS.filter((c) => !Boolean(strapi.contentType(c))).map(
34
+ (c) => this.getContentType(c)?.create
35
+ );
36
+
37
+ await this.synchronizeContentTypes(newContentTypes);
38
+ },
39
+ async updateContentTypes() {
40
+ const updateContentTypes = UIDS.filter((c) => Boolean(strapi.contentType(c))).map(
41
+ (c) => this.getContentType(c).update
42
+ );
43
+
44
+ await this.synchronizeContentTypes(updateContentTypes);
45
+ },
46
+ async synchronizeContentTypes(contentTypes: { uid: string; contentType: Record<string, any> }[]) {
47
+ const ctb = strapi.service('plugin::content-type-builder.content-types') as Service;
48
+
49
+ const [oldTypes, newTypes] = partition(contentTypes, (c) => Boolean(strapi.contentType(c.uid)));
50
+
51
+ if (newTypes.length > 0) {
52
+ console.log(
53
+ '[Plugin Page Builder]: Creating content types',
54
+ newTypes.map((x) => x.uid)
55
+ );
56
+ }
57
+
58
+ if (oldTypes.length > 0) {
59
+ console.log(
60
+ '[Plugin Page Builder]: Updating content types',
61
+ oldTypes.map((x) => x.uid)
62
+ );
63
+ }
64
+
65
+ if ((newTypes || []).length > 0) {
66
+ await ctb.createContentTypes(newTypes.map((c) => ({ contentType: c.contentType, components: [] })));
67
+ }
68
+
69
+ if ((oldTypes || []).length > 0) {
70
+ for (const oldType of oldTypes) {
71
+ await ctb.editContentType(oldType.uid, { contentType: oldType.contentType, components: [] });
72
+ }
73
+ }
74
+ },
75
+ getContentType(uid: string) {
76
+ const contentTypes = {
77
+ [PAGE_UID]: {
78
+ create: this.getPageContentType(true),
79
+ update: this.getPageContentType()
80
+ },
81
+ [PAGE_TYPE_UID]: {
82
+ create: this.getPageTypeContentType(true),
83
+ update: this.getPageTypeContentType()
84
+ },
85
+ [TEMPLATE_UID]: {
86
+ create: this.getTemplateContentType(),
87
+ update: this.getTemplateContentType()
88
+ }
89
+ };
90
+
91
+ return contentTypes?.[uid];
92
+ },
93
+ getPageContentType(create?: boolean) {
94
+ const page = create ? pageStart : pageEnd;
95
+ const contentType = this.addModulesToCollectionType(page);
96
+
97
+ return { uid: PAGE_UID, contentType };
98
+ },
99
+ getPageTypeContentType(create?: boolean) {
100
+ const pageType = create ? pageTypeStart : pageTypeEnd;
101
+ const contentType = this.addModulesToCollectionType(pageType);
102
+
103
+ return { uid: PAGE_TYPE_UID, contentType };
104
+ },
105
+ getTemplateContentType() {
106
+ const contentType = this.addModulesToCollectionType(template);
107
+
108
+ return { uid: TEMPLATE_UID, contentType };
109
+ },
110
+ addModulesToCollectionType(collectionType: Record<string, any>) {
111
+ const components = this.getConfigModuleComponents();
112
+
113
+ return {
114
+ ...collectionType,
115
+ attributes: { ...collectionType.attributes, modules: { ...collectionType.attributes.modules, components } }
116
+ };
117
+ },
118
+ getConfigModuleComponents() {
119
+ return (strapi.config.get(`plugin.${pluginId}`)?.modules || []).filter(Boolean);
120
+ }
121
+ };
@@ -0,0 +1,49 @@
1
+ import { Strapi } from '@strapi/strapi';
2
+ import uniqBy from 'lodash/uniqBy';
3
+
4
+ import { IGetTranslationPageLinks } from '../controllers/collection-types';
5
+
6
+ export default {
7
+ async hasPageRelation(uid: string): Promise<{ hasPageRelation: boolean }> {
8
+ const contentType = strapi.contentTypes?.[uid];
9
+
10
+ return {
11
+ hasPageRelation: Boolean(
12
+ contentType?.attributes?.page && contentType?.attributes?.page?.morphBy === 'collectionTypeData'
13
+ )
14
+ };
15
+ },
16
+ async getTranslationPageLinks(uid: string, ids: string[]) {
17
+ try {
18
+ const entities = await (strapi as Strapi).entityService.findMany(uid, {
19
+ filters: {
20
+ id: { $in: ids }
21
+ },
22
+ populate: {
23
+ page: {
24
+ populate: {
25
+ localizations: true
26
+ }
27
+ }
28
+ }
29
+ });
30
+
31
+ let pages: IGetTranslationPageLinks[] = [];
32
+
33
+ // Get all pages linked to the entity
34
+ entities?.forEach((x: any) => {
35
+ x?.page?.forEach((p: any) => {
36
+ pages.push({ id: p.id, locale: p.locale });
37
+
38
+ p.localizations.forEach((l: any) => {
39
+ pages.push({ id: l.id, locale: l.locale });
40
+ });
41
+ });
42
+ });
43
+
44
+ return uniqBy(pages, 'id');
45
+ } catch {
46
+ return [];
47
+ }
48
+ }
49
+ };
@@ -0,0 +1,11 @@
1
+ import page from './page';
2
+ import builder from './builder';
3
+ import pageType from './page-type';
4
+ import collectionTypes from './collection-types';
5
+
6
+ export default {
7
+ page,
8
+ builder,
9
+ 'page-type': pageType,
10
+ 'collection-types': collectionTypes
11
+ };
@@ -0,0 +1,22 @@
1
+ export default {
2
+ async findOneByUid(uid: string) {
3
+ const result = await strapi.entityService.findMany('api::page-type.page-type', {
4
+ populate: {
5
+ template: {
6
+ populate: {
7
+ modules: true
8
+ }
9
+ }
10
+ },
11
+ filters: {
12
+ uid
13
+ }
14
+ });
15
+
16
+ if (result.length > 0) {
17
+ return result[0];
18
+ } else {
19
+ return null;
20
+ }
21
+ }
22
+ };
@@ -0,0 +1,24 @@
1
+ import { PAGE_UID } from '../../shared/utils/constants';
2
+
3
+ export default {
4
+ async getPage(id: string) {
5
+ // @ts-ignore
6
+ const page = await strapi.db.query(PAGE_UID).findOne({
7
+ // @ts-ignore
8
+ select: 'id',
9
+ populate: {
10
+ // @ts-ignore
11
+ collectionTypeData: true,
12
+ pageType: true
13
+ },
14
+ where: {
15
+ id
16
+ }
17
+ });
18
+
19
+ return {
20
+ pageType: page.pageType,
21
+ collectionTypeData: page.collectionTypeData?.[0] ?? null
22
+ };
23
+ }
24
+ };