@webbio/strapi-plugin-page-builder 0.0.12 → 0.0.13
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/.prettierignore +17 -0
- package/admin/src/api/collection-type.ts +110 -0
- package/admin/src/api/has-page-relation.ts +34 -0
- package/admin/src/api/page-type.ts +31 -0
- package/admin/src/api/template.ts +25 -0
- package/admin/src/components/Combobox/index.tsx +77 -0
- package/admin/src/components/Combobox/react-select-custom-styles.tsx +111 -0
- package/admin/src/components/Combobox/styles.ts +22 -0
- package/admin/src/components/ConfirmModal/index.tsx +90 -0
- package/admin/src/components/EditView/CollectionTypeSearch/index.tsx +118 -0
- package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/index.tsx +95 -0
- package/admin/src/components/EditView/CollectionTypeSettings/CreatePageButton/styles.ts +26 -0
- package/admin/src/components/EditView/CollectionTypeSettings/index.tsx +53 -0
- package/admin/src/components/EditView/Details/index.tsx +47 -0
- package/admin/src/components/EditView/Details/styles.ts +51 -0
- package/admin/src/components/EditView/PageSettings/index.tsx +104 -0
- package/admin/src/components/EditView/Template/TemplateConfirmModal/index.tsx +36 -0
- package/admin/src/components/EditView/Template/TemplateSelect/index.tsx +64 -0
- package/admin/src/components/EditView/Template/TemplateSelect/use-template-modules.ts +30 -0
- package/admin/src/components/EditView/index.tsx +27 -0
- package/admin/src/components/EditView/page-type-select.tsx +30 -0
- package/admin/src/components/EditView/wrapper.tsx +35 -0
- package/admin/src/components/Initializer/index.tsx +24 -0
- package/admin/src/components/PageTypeFilter/index.tsx +17 -0
- package/admin/src/components/PageTypeFilter/page-type-filter.tsx +130 -0
- package/admin/src/components/PluginIcon/index.tsx +12 -0
- package/admin/src/constants.ts +1 -0
- package/admin/src/index.tsx +115 -0
- package/admin/src/pages/App/index.tsx +25 -0
- package/admin/src/pages/HomePage/index.tsx +19 -0
- package/admin/src/pluginId.ts +5 -0
- package/admin/src/redux/initialData.reducer.ts +0 -0
- package/admin/src/translations/en.json +6 -0
- package/admin/src/translations/nl.json +6 -0
- package/admin/src/utils/getRequestUrl.ts +11 -0
- package/admin/src/utils/getTrad.ts +5 -0
- package/admin/src/utils/hooks/useDebounce.ts +17 -0
- package/admin/src/utils/hooks/useGetLocaleFromUrl.ts +9 -0
- package/admin/src/utils/hooks/usePrevious.ts +12 -0
- package/admin/src/utils/sanitizeModules.ts +10 -0
- package/custom.d.ts +5 -0
- package/dist/package.json +1 -1
- package/dist/tsconfig.server.tsbuildinfo +1 -1
- package/package.json +1 -5
- package/server/bootstrap.ts +106 -0
- package/server/config/index.ts +4 -0
- package/server/content-types/index.ts +1 -0
- package/server/controllers/collection-types.ts +27 -0
- package/server/controllers/index.ts +9 -0
- package/server/controllers/page-type.ts +12 -0
- package/server/controllers/page.ts +20 -0
- package/server/destroy.ts +5 -0
- package/server/index.ts +23 -0
- package/server/middlewares/index.ts +1 -0
- package/server/policies/index.ts +1 -0
- package/server/register.ts +5 -0
- package/server/routes/index.ts +42 -0
- package/server/schema/page-end.json +97 -0
- package/server/schema/page-start.json +87 -0
- package/server/schema/page-type-end.json +49 -0
- package/server/schema/page-type-start.json +44 -0
- package/server/schema/template.json +35 -0
- package/server/services/builder.ts +121 -0
- package/server/services/collection-types.ts +49 -0
- package/server/services/index.ts +11 -0
- package/server/services/page-type.ts +22 -0
- package/server/services/page.ts +24 -0
- package/server/utils/graphql.ts +110 -0
- package/server/utils/reload-strapi-on-load.ts +13 -0
- package/shared/utils/constants.ts +4 -0
- package/shared/utils/sleep.ts +1 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +3 -0
- package/tsconfig.json +20 -0
- package/tsconfig.server.json +25 -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 @@
|
|
|
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}'s HomePage</h1>
|
|
14
|
+
<p>Happy coding</p>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default HomePage;
|
|
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,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,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
|
+
};
|
package/custom.d.ts
ADDED