
diff --git a/packages/strapi-plugin-content-manager/admin/src/components/Table/index.js b/packages/strapi-plugin-content-manager/admin/src/components/Table/index.js
index f2c1534373..e18cc0a8a3 100644
--- a/packages/strapi-plugin-content-manager/admin/src/components/Table/index.js
+++ b/packages/strapi-plugin-content-manager/admin/src/components/Table/index.js
@@ -90,6 +90,8 @@ Table.defaultProps = {
enableBulkActions: true,
entriesToDelete: [],
handleDelete: () => {},
+ records: [],
+ routeParams: {},
search: '',
showLoader: false,
};
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/actions.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/actions.js
index e69de29bb2..89430a0575 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/actions.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/actions.js
@@ -0,0 +1,21 @@
+import { GET_DATA, GET_DATA_SUCCEEDED, RESET_PROPS } from './constants';
+
+export function getData(uid, params) {
+ return {
+ type: GET_DATA,
+ uid,
+ params,
+ };
+}
+
+export function getDataSucceeded(count, data) {
+ return {
+ type: GET_DATA_SUCCEEDED,
+ count,
+ data,
+ };
+}
+
+export function resetProps() {
+ return { type: RESET_PROPS };
+}
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/constants.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/constants.js
index e69de29bb2..3f9ecafb04 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/constants.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/constants.js
@@ -0,0 +1,3 @@
+export const GET_DATA = 'ContentManager/ListView/GET_DATA';
+export const GET_DATA_SUCCEEDED = 'ContentManager/ListView/GET_DATA_SUCCEEDED';
+export const RESET_PROPS = 'ContentManager/ListView/RESET_PROPS';
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js
index 89a4b1796c..1c681a275e 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/index.js
@@ -1,45 +1,124 @@
-import React, { memo, useEffect } from 'react';
+import React, { memo, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
+import { capitalize, get } from 'lodash';
-import { LoadingIndicatorPage } from 'strapi-helper-plugin';
+import { PluginHeader, getQueryParameters } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
-import { getLayout } from '../Main/actions';
+import Container from '../../components/Container';
+import Search from '../../components/Search';
+import { getData, resetProps } from './actions';
import reducer from './reducer';
import saga from './saga';
import makeSelectListView from './selectors';
function ListView({
- getLayout,
+ count,
+ emitEvent,
+ location: { search },
+ getData,
layouts,
+ isLoading,
+ history: { push },
match: {
params: { slug },
},
+ resetProps,
}) {
strapi.useInjectReducer({ key: 'listView', reducer, pluginId });
strapi.useInjectSaga({ key: 'listView', saga, pluginId });
- // Display a loader if the layout from the main reducer is empty
- const shouldShowLoader = layouts[slug] === undefined;
+ const getLayoutSetting = useCallback(
+ settingName => get(layouts, [slug, 'settings', settingName], ''),
+ [layouts, slug]
+ );
+
+ const generateSearchParams = useCallback(
+ (updatedParams = {}) => {
+ return {
+ _limit:
+ getQueryParameters(search, '_limit') || getLayoutSetting('pageSize'),
+ _page: getQueryParameters(search, '_page') || 1,
+ _q: getQueryParameters(search, '_q') || '',
+ _sort:
+ getQueryParameters(search, '_sort') ||
+ `${getLayoutSetting('defaultSortBy')}:${getLayoutSetting(
+ 'defaultSortOrder'
+ )}`,
+ source: getQueryParameters(search, 'source'),
+ ...updatedParams,
+ };
+ },
+ [getLayoutSetting, search]
+ );
useEffect(() => {
- if (shouldShowLoader) {
- getLayout(slug);
- }
- }, [shouldShowLoader]);
+ getData(slug, generateSearchParams());
- if (shouldShowLoader) {
- return
;
- }
+ return () => {
+ resetProps();
+ };
+ /* eslint-disable-next-line react-hooks/exhaustive-deps */
+ }, [slug]);
+ console.log(getLayoutSetting('layouts.list'));
+
+ const pluginHeaderActions = [
+ {
+ id: 'addEntry',
+ label: 'content-manager.containers.List.addAnEntry',
+ labelValues: {
+ entity: capitalize(slug) || 'Content Manager',
+ },
+ kind: 'primaryAddShape',
+ onClick: () => {
+ emitEvent('willCreateEntry');
+ },
+ },
+ ];
+ const handleChangeParams = ({ target: { name, value } }) => {
+ const updatedSearch = generateSearchParams({ [name]: value });
+ const newSearch = Object.keys(updatedSearch)
+ .map(key => `${key}=${updatedSearch[key]}`)
+ .join('&');
+
+ push({ search: newSearch });
+ resetProps();
+ getData(slug, updatedSearch);
+ };
return (
-
+ <>
+
+ 1
+ ? `${pluginId}.containers.List.pluginHeaderDescription`
+ : `${pluginId}.containers.List.pluginHeaderDescription.singular`,
+ values: {
+ label: count,
+ },
+ }}
+ title={{
+ id: slug || 'Content Manager',
+ }}
+ withDescriptionAnim={isLoading}
+ />
+ {getLayoutSetting('searchable') && (
+
+ )}
+
+ >
);
}
ListView.defaultProps = {
@@ -47,13 +126,20 @@ ListView.defaultProps = {
};
ListView.propTypes = {
- getLayout: PropTypes.func.isRequired,
+ count: PropTypes.number.isRequired,
+ emitEvent: PropTypes.func.isRequired,
layouts: PropTypes.object,
+ location: PropTypes.shape({
+ search: PropTypes.string.isRequired,
+ }),
+ getData: PropTypes.func.isRequired,
+ isLoading: PropTypes.bool.isRequired,
match: PropTypes.shape({
params: PropTypes.shape({
slug: PropTypes.string.isRequired,
}),
}),
+ resetProps: PropTypes.func.isRequired,
};
const mapStateToProps = makeSelectListView();
@@ -61,7 +147,8 @@ const mapStateToProps = makeSelectListView();
export function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
- getLayout,
+ getData,
+ resetProps,
},
dispatch
);
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/reducer.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/reducer.js
index 279372e1bc..24a4cfc163 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/reducer.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/reducer.js
@@ -3,13 +3,24 @@
* listView reducer
*/
-import { fromJS } from 'immutable';
-// import { } from './constants';
+import { fromJS, List } from 'immutable';
+import { GET_DATA_SUCCEEDED, RESET_PROPS } from './constants';
-export const initialState = fromJS({});
+export const initialState = fromJS({
+ count: 0,
+ data: List([]),
+ isLoading: true,
+});
function listViewReducer(state = initialState, action) {
switch (action.type) {
+ case GET_DATA_SUCCEEDED:
+ return state
+ .update('count', () => action.count)
+ .update('data', () => List(action.data))
+ .update('isLoading', () => false);
+ case RESET_PROPS:
+ return initialState;
default:
return state;
}
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/saga.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/saga.js
index b1193b55e8..1528a366b0 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/ListView/saga.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListView/saga.js
@@ -1,29 +1,50 @@
-// import { all, fork, put, call, takeLatest, select } from 'redux-saga/effects';
-// import { request } from 'strapi-helper-plugin';
+import { all, fork, put, call, takeLatest, select } from 'redux-saga/effects';
+import { request } from 'strapi-helper-plugin';
+import { set, unset } from 'lodash';
+import pluginId from '../../pluginId';
-// import pluginId from '../../pluginId';
-
-// import { } from './actions';
-// import { } from './constants';
+import { getDataSucceeded } from './actions';
+import { GET_DATA } from './constants';
// import {} from './selectors';
-// const getRequestUrl = path => `/${pluginId}/fixtures/${path}`;
+const getRequestUrl = path => `/${pluginId}/explorer/${path}`;
-export function* getData() {
- // try {
- // } catch (err) {
- // strapi.notification.error('content-manager.error.model.fetch');
- // }
+// eslint-disable-next-line require-yield
+export function* getData({ uid, params }) {
+ try {
+ const _start = (params._page - 1) * parseInt(params._limit, 10);
+
+ set(params, '_start', _start);
+ unset(params, '_page');
+
+ if (params._q === '') {
+ unset(params, '_q');
+ }
+
+ const [{ count }, data] = yield all([
+ call(request, getRequestUrl(`${uid}/count`), {
+ method: 'GET',
+ params,
+ }),
+ call(request, getRequestUrl(`${uid}`), {
+ method: 'GET',
+ params,
+ }),
+ ]);
+
+ yield put(getDataSucceeded(count, data));
+ } catch (err) {
+ console.log({ err });
+ strapi.notification.error('content-manager.error.model.fetch');
+ }
}
function* defaultSaga() {
- // try {
- // yield all([
- // fork(takeLatest, GET_DATA, getData)
- // ]);
- // } catch (err) {
- // // Do nothing
- // }
+ try {
+ yield all([fork(takeLatest, GET_DATA, getData)]);
+ } catch (err) {
+ // Do nothing
+ }
}
export default defaultSaga;
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/Main/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/Main/index.js
index 172db7e051..8b145920a7 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/Main/index.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/Main/index.js
@@ -1,4 +1,4 @@
-import React, { memo } from 'react';
+import React, { memo, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
@@ -12,15 +12,25 @@ import SettingViewModel from '../SettingViewModel';
import SettingViewGroup from '../SettingViewGroup';
import SettingsView from '../SettingsView';
+import { getLayout } from './actions';
import reducer from './reducer';
import saga from './saga';
import makeSelectMain from './selectors';
-function Main({ isLoading, emitEvent, layouts }) {
+function Main({ emitEvent, getLayout, layouts, location: { pathname } }) {
strapi.useInjectReducer({ key: 'main', reducer, pluginId });
strapi.useInjectSaga({ key: 'main', saga, pluginId });
+ const slug = pathname.split('/')[3];
+ const shouldShowLoader =
+ slug !== 'ctm-configurations' && layouts[slug] === undefined;
- if (isLoading) {
+ useEffect(() => {
+ if (shouldShowLoader) {
+ getLayout(slug);
+ }
+ }, [getLayout, shouldShowLoader, slug]);
+
+ if (shouldShowLoader) {
return
;
}
@@ -52,14 +62,23 @@ function Main({ isLoading, emitEvent, layouts }) {
Main.propTypes = {
emitEvent: PropTypes.func.isRequired,
- isLoading: PropTypes.bool.isRequired,
+ getLayout: PropTypes.func.isRequired,
+
layouts: PropTypes.object.isRequired,
+ location: PropTypes.shape({
+ pathname: PropTypes.string.isRequired,
+ }),
};
const mapStateToProps = makeSelectMain();
export function mapDispatchToProps(dispatch) {
- return bindActionCreators({}, dispatch);
+ return bindActionCreators(
+ {
+ getLayout,
+ },
+ dispatch
+ );
}
const withConnect = connect(
mapStateToProps,
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/Main/reducer.js b/packages/strapi-plugin-content-manager/admin/src/containers/Main/reducer.js
index 94ccef3577..170104cc42 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/Main/reducer.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/Main/reducer.js
@@ -11,16 +11,12 @@ import {
} from './constants';
export const initialState = fromJS({
- isLoading: false,
- layouts: fromJS({
- article: {},
- }),
+ layouts: fromJS({}),
});
function mainReducer(state = initialState, action) {
switch (action.type) {
case DELETE_LAYOUT:
- console.log({ action });
return state.removeIn(['layouts', action.uid]);
case DELETE_LAYOUTS:
return state.update('layouts', () => fromJS({}));
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/SettingViewModel/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/SettingViewModel/index.js
index 15d8398c81..cbc14b08da 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/SettingViewModel/index.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/SettingViewModel/index.js
@@ -79,13 +79,13 @@ function SettingViewModel({
return () => {
resetProps();
};
- }, []);
+ }, [getData, name, resetProps]);
useEffect(() => {
if (showWarningSubmit) {
toggleWarningSubmit();
}
- }, [shouldToggleModalSubmit]);
+ }, [shouldToggleModalSubmit, showWarningSubmit]);
if (isLoading) {
return
;
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/SettingsView/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/SettingsView/index.js
index 2a099c73e6..627f712335 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/SettingsView/index.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/SettingsView/index.js
@@ -53,19 +53,18 @@ function SettingsView({
const [showWarningCancel, setWarningCancel] = useState(false);
const [showWarningSubmit, setWarningSubmit] = useState(false);
const toggleWarningCancel = () => setWarningCancel(prevState => !prevState);
- const toggleWarningSubmit = () =>
- setWarningSubmit(prevState => !prevState);
+ const toggleWarningSubmit = () => setWarningSubmit(prevState => !prevState);
useEffect(() => {
if (showWarningSubmit) {
toggleWarningSubmit();
}
- }, [shouldToggleModalSubmit]);
+ }, [shouldToggleModalSubmit, showWarningSubmit]);
useEffect(() => {
if (isEmpty(initialData)) {
getData();
}
- }, [initialData]);
+ }, [getData, initialData]);
if (isLoading) {
return
;
diff --git a/packages/strapi-plugin-content-manager/controllers/ContentManagerFixtures.js b/packages/strapi-plugin-content-manager/controllers/ContentManagerFixtures.js
index b9118c0e79..8c7e1b6382 100644
--- a/packages/strapi-plugin-content-manager/controllers/ContentManagerFixtures.js
+++ b/packages/strapi-plugin-content-manager/controllers/ContentManagerFixtures.js
@@ -24,6 +24,192 @@ module.exports = {
getLayout: ctx => {
const layouts = {
+ tag: {
+ uid: 'article',
+ schema: {
+ // good old schema
+ connection: 'default',
+ collectionName: 'articles',
+ options: {},
+ infos: {
+ name: 'article',
+ description: '',
+ },
+ attributes: {
+ title: {
+ type: 'string',
+ },
+ content: {
+ type: 'text',
+ },
+ json: {
+ type: 'string',
+ },
+ number: {
+ type: 'integer',
+ },
+ date: {
+ type: 'date',
+ },
+ enum: {
+ enum: ['morning,', 'noon'],
+ type: 'enumeration',
+ },
+ pic: {
+ model: 'file',
+ via: 'related',
+ plugin: 'upload',
+ },
+ bool: {
+ type: 'boolean',
+ },
+ tags: {
+ collection: 'tag',
+ via: 'articles',
+ },
+ },
+ },
+ settings: {
+ mainField: 'id',
+ defaultSortBy: 'id',
+ defaultSortOrder: 'ASC',
+ searchable: true,
+ filterable: true,
+ bulkable: false,
+ pageSize: 10,
+ },
+ metadata: {
+ id: {
+ edit: {},
+ list: {
+ label: 'Id',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ title: {
+ edit: {
+ label: 'title',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {
+ label: 'title',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ content: {
+ edit: {
+ label: 'content',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {
+ label: 'content',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ json: {
+ edit: {
+ label: 'json',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {},
+ },
+ number: {
+ edit: {
+ label: 'number',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {
+ label: 'number',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ date: {
+ edit: {
+ label: 'date',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {
+ label: 'date',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ enum: {
+ edit: {
+ label: 'enum',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {
+ label: 'enum',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ pic: {
+ edit: {
+ label: 'pic',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {},
+ },
+ tags: {
+ edit: {
+ label: 'tags',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {},
+ },
+ bool: {
+ edit: {
+ label: 'bool',
+ description: '....',
+ editable: true,
+ visible: true,
+ },
+ list: {
+ label: 'bool',
+ searchable: true,
+ sortable: true,
+ },
+ },
+ },
+ layouts: {
+ list: ['id', 'title', 'content'],
+ editRelations: [],
+ edit: [
+ [
+ {
+ name: 'title',
+ size: 6,
+ },
+ {
+ name: 'content',
+ size: 6,
+ },
+ ],
+ ],
+ },
+ },
article: {
uid: 'article',
schema: {
@@ -405,6 +591,11 @@ module.exports = {
label: 'Article',
destination: 'article',
},
+ {
+ name: 'tag',
+ label: 'Tag',
+ destination: 'tag',
+ },
{
name: 'administrator',
label: 'Administrator',
diff --git a/yarn.lock b/yarn.lock
index a9f7c02701..f8433c6f28 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6544,6 +6544,11 @@ eslint-config-prettier@^4.3.0:
dependencies:
get-stdin "^6.0.0"
+eslint-plugin-react-hooks@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.6.1.tgz#3c66a5515ea3e0a221ffc5d4e75c971c217b1a4c"
+ integrity sha512-wHhmGJyVuijnYIJXZJHDUF2WM+rJYTjulUTqF9k61d3BTk8etydz+M4dXUVH7M76ZRS85rqBTCx0Es/lLsrjnA==
+
eslint-plugin-react@^7.13.0:
version "7.13.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz#bc13fd7101de67996ea51b33873cd9dc2b7e5758"