2020-06-19 10:54:31 +02:00
|
|
|
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
2019-07-08 17:01:12 +02:00
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { bindActionCreators, compose } from 'redux';
|
2020-08-24 11:01:24 +02:00
|
|
|
import { get, sortBy, omit } from 'lodash';
|
2019-07-09 09:53:50 +02:00
|
|
|
import { FormattedMessage } from 'react-intl';
|
2020-06-22 12:59:17 +02:00
|
|
|
import { useLocation } from 'react-router-dom';
|
2019-11-07 10:48:02 +01:00
|
|
|
import { Header } from '@buffetjs/custom';
|
|
|
|
import {
|
|
|
|
PopUpWarning,
|
2020-02-20 22:49:51 +01:00
|
|
|
generateFiltersFromSearch,
|
2019-11-07 10:48:02 +01:00
|
|
|
useGlobalContext,
|
2020-02-10 16:33:29 +01:00
|
|
|
request,
|
2020-06-11 09:38:34 +02:00
|
|
|
CheckPermissions,
|
2020-06-19 17:30:50 +02:00
|
|
|
useUserPermissions,
|
2020-06-22 12:59:17 +02:00
|
|
|
useQuery,
|
2019-11-07 10:48:02 +01:00
|
|
|
} from 'strapi-helper-plugin';
|
2019-07-08 17:01:12 +02:00
|
|
|
import pluginId from '../../pluginId';
|
2020-06-10 15:52:10 +02:00
|
|
|
import pluginPermissions from '../../permissions';
|
2020-06-22 17:20:09 +02:00
|
|
|
import { generatePermissionsObject, getRequestUrl } from '../../utils';
|
|
|
|
|
2019-10-15 11:17:26 +02:00
|
|
|
import DisplayedFieldsDropdown from '../../components/DisplayedFieldsDropdown';
|
2019-07-08 20:27:38 +02:00
|
|
|
import Container from '../../components/Container';
|
2019-07-09 16:56:40 +02:00
|
|
|
import CustomTable from '../../components/CustomTable';
|
2019-07-09 12:31:18 +02:00
|
|
|
import FilterPicker from '../../components/FilterPicker';
|
2019-07-08 20:27:38 +02:00
|
|
|
import Search from '../../components/Search';
|
2019-10-31 11:57:40 +01:00
|
|
|
import ListViewProvider from '../ListViewProvider';
|
2019-07-09 09:53:50 +02:00
|
|
|
import { onChangeListLabels, resetListLabels } from '../Main/actions';
|
2019-12-13 15:37:34 +01:00
|
|
|
import { AddFilterCta, FilterIcon, Wrapper } from './components';
|
2019-07-10 12:20:25 +02:00
|
|
|
import Filter from './Filter';
|
2019-07-10 08:43:40 +02:00
|
|
|
import Footer from './Footer';
|
2019-07-09 19:38:39 +02:00
|
|
|
import {
|
2020-06-22 12:59:17 +02:00
|
|
|
getData,
|
2020-02-10 16:33:29 +01:00
|
|
|
getDataSucceeded,
|
2019-07-09 19:38:39 +02:00
|
|
|
onChangeBulk,
|
|
|
|
onChangeBulkSelectall,
|
2020-07-07 17:03:16 +02:00
|
|
|
onDeleteDataError,
|
2020-02-10 16:33:29 +01:00
|
|
|
onDeleteDataSucceeded,
|
|
|
|
onDeleteSeveralDataSucceeded,
|
2019-07-09 19:38:39 +02:00
|
|
|
resetProps,
|
2020-06-22 13:53:29 +02:00
|
|
|
setModalLoadingState,
|
2019-07-10 09:12:48 +02:00
|
|
|
toggleModalDelete,
|
2019-07-09 19:38:39 +02:00
|
|
|
toggleModalDeleteAll,
|
|
|
|
} from './actions';
|
2020-04-20 10:26:43 +02:00
|
|
|
|
2019-07-08 17:01:12 +02:00
|
|
|
import makeSelectListView from './selectors';
|
2020-06-19 17:30:50 +02:00
|
|
|
|
2020-01-21 16:03:31 +01:00
|
|
|
/* eslint-disable react/no-array-index-key */
|
|
|
|
|
2019-07-08 17:01:12 +02:00
|
|
|
function ListView({
|
2019-07-08 20:27:38 +02:00
|
|
|
count,
|
2019-07-09 16:56:40 +02:00
|
|
|
data,
|
2020-06-22 12:59:17 +02:00
|
|
|
didDeleteData,
|
2019-07-08 20:27:38 +02:00
|
|
|
emitEvent,
|
2019-07-09 19:38:39 +02:00
|
|
|
entriesToDelete,
|
2020-05-05 02:05:35 +02:00
|
|
|
isLoading,
|
2020-06-22 12:59:17 +02:00
|
|
|
location: { pathname },
|
|
|
|
getData,
|
2020-02-10 16:33:29 +01:00
|
|
|
getDataSucceeded,
|
2019-07-08 17:01:12 +02:00
|
|
|
layouts,
|
2019-07-08 20:27:38 +02:00
|
|
|
history: { push },
|
2019-07-09 19:38:39 +02:00
|
|
|
onChangeBulk,
|
|
|
|
onChangeBulkSelectall,
|
2019-07-09 09:53:50 +02:00
|
|
|
onChangeListLabels,
|
2020-07-07 17:03:16 +02:00
|
|
|
onDeleteDataError,
|
2020-02-10 16:33:29 +01:00
|
|
|
onDeleteDataSucceeded,
|
|
|
|
onDeleteSeveralDataSucceeded,
|
2019-07-09 09:53:50 +02:00
|
|
|
resetListLabels,
|
2019-07-08 20:27:38 +02:00
|
|
|
resetProps,
|
2020-06-22 13:53:29 +02:00
|
|
|
setModalLoadingState,
|
2019-07-10 09:12:48 +02:00
|
|
|
showWarningDelete,
|
2020-06-22 13:53:29 +02:00
|
|
|
showModalConfirmButtonLoading,
|
|
|
|
showWarningDeleteAll,
|
2019-10-24 17:08:52 +02:00
|
|
|
slug,
|
2019-07-10 09:12:48 +02:00
|
|
|
toggleModalDelete,
|
2019-07-09 19:38:39 +02:00
|
|
|
toggleModalDeleteAll,
|
2019-07-08 17:01:12 +02:00
|
|
|
}) {
|
2020-06-19 17:30:50 +02:00
|
|
|
const viewPermissions = useMemo(() => generatePermissionsObject(slug), [slug]);
|
|
|
|
const {
|
|
|
|
isLoading: isLoadingForPermissions,
|
|
|
|
allowedActions: { canCreate, canRead, canUpdate, canDelete },
|
|
|
|
} = useUserPermissions(viewPermissions);
|
2020-06-22 12:59:17 +02:00
|
|
|
const query = useQuery();
|
|
|
|
const { search } = useLocation();
|
|
|
|
const isFirstRender = useRef(true);
|
2019-11-07 10:48:02 +01:00
|
|
|
const { formatMessage } = useGlobalContext();
|
2020-06-22 12:59:17 +02:00
|
|
|
|
2019-07-09 09:53:50 +02:00
|
|
|
const [isLabelPickerOpen, setLabelPickerState] = useState(false);
|
2019-07-09 12:31:18 +02:00
|
|
|
const [isFilterPickerOpen, setFilterPickerState] = useState(false);
|
2019-07-10 09:12:48 +02:00
|
|
|
const [idToDelete, setIdToDelete] = useState(null);
|
2020-06-19 10:54:31 +02:00
|
|
|
const contentTypePath = useMemo(() => {
|
|
|
|
return [slug, 'contentType'];
|
|
|
|
}, [slug]);
|
2020-08-24 11:01:24 +02:00
|
|
|
const hasDraftAndPublish = useMemo(() => {
|
|
|
|
return get(layouts, [...contentTypePath, 'schema', 'options', 'draftAndPublish'], false);
|
|
|
|
}, [contentTypePath, layouts]);
|
2019-07-09 18:11:09 +02:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
const getLayoutSetting = useCallback(
|
|
|
|
settingName => {
|
|
|
|
return get(layouts, [...contentTypePath, 'settings', settingName], '');
|
|
|
|
},
|
|
|
|
[contentTypePath, layouts]
|
|
|
|
);
|
|
|
|
|
|
|
|
// Related to the search
|
|
|
|
const defaultSort = useMemo(() => {
|
|
|
|
return `${getLayoutSetting('defaultSortBy')}:${getLayoutSetting('defaultSortOrder')}`;
|
|
|
|
}, [getLayoutSetting]);
|
|
|
|
|
|
|
|
const filters = useMemo(() => {
|
|
|
|
const currentSearch = new URLSearchParams(search);
|
|
|
|
|
|
|
|
// Delete all params that are not related to the filters
|
|
|
|
const paramsToDelete = ['_limit', '_page', '_sort', '_q'];
|
|
|
|
|
|
|
|
for (let i = 0; i < paramsToDelete.length; i++) {
|
|
|
|
currentSearch.delete(paramsToDelete[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return generateFiltersFromSearch(currentSearch.toString());
|
|
|
|
}, [search]);
|
|
|
|
const _limit = useMemo(() => {
|
|
|
|
return parseInt(query.get('_limit') || getLayoutSetting('pageSize'), 10);
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [getLayoutSetting, query.get('_limit')]);
|
|
|
|
const _q = useMemo(() => {
|
|
|
|
return query.get('_q') || '';
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [query.get('_q')]);
|
|
|
|
const _page = useMemo(() => {
|
|
|
|
return parseInt(query.get('_page') || 1, 10);
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [query.get('_page')]);
|
|
|
|
const _sort = useMemo(() => {
|
|
|
|
return query.get('_sort') || defaultSort;
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [defaultSort, query.get('_sort')]);
|
|
|
|
const _start = useMemo(() => {
|
|
|
|
return (_page - 1) * parseInt(_limit, 10);
|
|
|
|
}, [_limit, _page]);
|
|
|
|
const searchToSendForRequest = useMemo(() => {
|
|
|
|
const currentSearch = new URLSearchParams(search);
|
|
|
|
|
|
|
|
currentSearch.set('_limit', _limit);
|
|
|
|
currentSearch.set('_sort', _sort);
|
|
|
|
currentSearch.set('_start', _start);
|
|
|
|
currentSearch.delete('_page');
|
|
|
|
|
|
|
|
return currentSearch.toString();
|
|
|
|
}, [_limit, _sort, _start, search]);
|
|
|
|
|
|
|
|
const getDataActionRef = useRef(getData);
|
|
|
|
const getDataSucceededRef = useRef(getDataSucceeded);
|
|
|
|
|
|
|
|
// Settings
|
|
|
|
const isBulkable = useMemo(() => {
|
|
|
|
return getLayoutSetting('bulkable');
|
|
|
|
}, [getLayoutSetting]);
|
|
|
|
const isFilterable = useMemo(() => {
|
|
|
|
return getLayoutSetting('filterable');
|
|
|
|
}, [getLayoutSetting]);
|
|
|
|
const isSearchable = useMemo(() => {
|
|
|
|
return getLayoutSetting('searchable');
|
|
|
|
}, [getLayoutSetting]);
|
|
|
|
const shouldSendRequest = useMemo(() => {
|
|
|
|
return !isLoadingForPermissions && canRead;
|
|
|
|
}, [canRead, isLoadingForPermissions]);
|
|
|
|
|
|
|
|
const fetchData = async (search = searchToSendForRequest) => {
|
2020-02-10 16:33:29 +01:00
|
|
|
try {
|
2020-06-22 12:59:17 +02:00
|
|
|
getDataActionRef.current();
|
2020-02-10 16:33:29 +01:00
|
|
|
const [{ count }, data] = await Promise.all([
|
2020-06-22 12:59:17 +02:00
|
|
|
request(getRequestUrl(`explorer/${slug}/count?${search}`), {
|
2020-02-10 16:33:29 +01:00
|
|
|
method: 'GET',
|
|
|
|
}),
|
2020-06-22 12:59:17 +02:00
|
|
|
request(getRequestUrl(`explorer/${slug}?${search}`), {
|
2020-02-10 16:33:29 +01:00
|
|
|
method: 'GET',
|
|
|
|
}),
|
|
|
|
]);
|
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
getDataSucceededRef.current(count, data);
|
2020-02-10 16:33:29 +01:00
|
|
|
} catch (err) {
|
|
|
|
strapi.notification.error(`${pluginId}.error.model.fetch`);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-06-19 10:54:31 +02:00
|
|
|
const getMetaDatas = useCallback(
|
|
|
|
(path = []) => {
|
|
|
|
return get(layouts, [...contentTypePath, 'metadatas', ...path], {});
|
|
|
|
},
|
|
|
|
[contentTypePath, layouts]
|
|
|
|
);
|
|
|
|
|
|
|
|
const listLayout = useMemo(() => {
|
|
|
|
return get(layouts, [...contentTypePath, 'layouts', 'list'], []);
|
|
|
|
}, [contentTypePath, layouts]);
|
|
|
|
|
|
|
|
const listSchema = useMemo(() => {
|
|
|
|
return get(layouts, [...contentTypePath, 'schema'], {});
|
|
|
|
}, [layouts, contentTypePath]);
|
|
|
|
|
|
|
|
const label = useMemo(() => {
|
|
|
|
return get(listSchema, ['info', 'name'], '');
|
|
|
|
}, [listSchema]);
|
|
|
|
|
|
|
|
const tableHeaders = useMemo(() => {
|
2020-08-24 11:01:24 +02:00
|
|
|
let headers = listLayout.map(label => {
|
2020-06-19 10:54:31 +02:00
|
|
|
return { ...getMetaDatas([label, 'list']), name: label };
|
|
|
|
});
|
2020-08-24 11:01:24 +02:00
|
|
|
|
|
|
|
if (hasDraftAndPublish) {
|
|
|
|
headers.push({ label: 'State', searchable: false, sortable: true, name: 'published_at' });
|
|
|
|
}
|
|
|
|
|
|
|
|
return headers;
|
|
|
|
}, [getMetaDatas, hasDraftAndPublish, listLayout]);
|
2020-06-19 10:54:31 +02:00
|
|
|
|
|
|
|
const getFirstSortableElement = useCallback(
|
|
|
|
(name = '') => {
|
|
|
|
return get(
|
|
|
|
listLayout.filter(h => {
|
|
|
|
return h !== name && getMetaDatas([h, 'list', 'sortable']) === true;
|
|
|
|
}),
|
|
|
|
['0'],
|
|
|
|
'id'
|
|
|
|
);
|
|
|
|
},
|
|
|
|
[getMetaDatas, listLayout]
|
|
|
|
);
|
2019-07-08 20:27:38 +02:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
const allLabels = useMemo(() => {
|
2020-08-24 11:01:24 +02:00
|
|
|
const filteredMetadatas = omit(getMetaDatas(), ['published_at']);
|
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
return sortBy(
|
2020-08-24 11:01:24 +02:00
|
|
|
Object.keys(filteredMetadatas)
|
2020-06-22 12:59:17 +02:00
|
|
|
.filter(
|
|
|
|
key =>
|
|
|
|
!['json', 'component', 'dynamiczone', 'relation', 'richtext'].includes(
|
|
|
|
get(listSchema, ['attributes', key, 'type'], '')
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.map(label => ({
|
|
|
|
name: label,
|
|
|
|
value: listLayout.includes(label),
|
|
|
|
})),
|
|
|
|
['label', 'name']
|
|
|
|
);
|
|
|
|
}, [getMetaDatas, listLayout, listSchema]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
isFirstRender.current = true;
|
|
|
|
};
|
|
|
|
}, [slug]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (!isFirstRender.current) {
|
|
|
|
fetchData(searchToSendForRequest);
|
|
|
|
}
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [searchToSendForRequest]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
|
|
|
resetProps();
|
|
|
|
setFilterPickerState(false);
|
|
|
|
};
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [slug]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (shouldSendRequest) {
|
|
|
|
fetchData();
|
|
|
|
}
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
isFirstRender.current = false;
|
|
|
|
};
|
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [shouldSendRequest]);
|
2020-02-10 16:33:29 +01:00
|
|
|
|
|
|
|
const handleConfirmDeleteData = useCallback(async () => {
|
|
|
|
try {
|
|
|
|
emitEvent('willDeleteEntry');
|
2020-06-22 13:53:29 +02:00
|
|
|
setModalLoadingState();
|
2020-02-10 16:33:29 +01:00
|
|
|
|
|
|
|
await request(getRequestUrl(`explorer/${slug}/${idToDelete}`), {
|
|
|
|
method: 'DELETE',
|
|
|
|
});
|
|
|
|
|
|
|
|
strapi.notification.success(`${pluginId}.success.record.delete`);
|
|
|
|
|
|
|
|
// Close the modal and refetch data
|
|
|
|
onDeleteDataSucceeded();
|
|
|
|
emitEvent('didDeleteEntry');
|
|
|
|
} catch (err) {
|
2020-07-07 17:03:16 +02:00
|
|
|
const errorMessage = get(
|
|
|
|
err,
|
|
|
|
'response.payload.message',
|
|
|
|
formatMessage({ id: `${pluginId}.error.record.delete` })
|
|
|
|
);
|
|
|
|
|
|
|
|
strapi.notification.error(errorMessage);
|
|
|
|
// Close the modal
|
|
|
|
onDeleteDataError();
|
2020-02-10 16:33:29 +01:00
|
|
|
}
|
2020-07-07 17:03:16 +02:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
|
|
}, [setModalLoadingState, slug, idToDelete, onDeleteDataSucceeded]);
|
2020-02-10 16:33:29 +01:00
|
|
|
|
|
|
|
const handleConfirmDeleteAllData = useCallback(async () => {
|
|
|
|
const params = Object.assign(entriesToDelete);
|
|
|
|
|
|
|
|
try {
|
2020-06-22 13:53:29 +02:00
|
|
|
setModalLoadingState();
|
|
|
|
|
2020-02-10 16:33:29 +01:00
|
|
|
await request(getRequestUrl(`explorer/deleteAll/${slug}`), {
|
|
|
|
method: 'DELETE',
|
|
|
|
params,
|
|
|
|
});
|
|
|
|
|
|
|
|
onDeleteSeveralDataSucceeded();
|
|
|
|
} catch (err) {
|
|
|
|
strapi.notification.error(`${pluginId}.error.record.delete`);
|
|
|
|
}
|
2020-06-22 13:53:29 +02:00
|
|
|
}, [entriesToDelete, onDeleteSeveralDataSucceeded, slug, setModalLoadingState]);
|
2020-02-10 16:33:29 +01:00
|
|
|
|
2019-07-09 09:53:50 +02:00
|
|
|
const handleChangeListLabels = ({ name, value }) => {
|
2020-06-22 12:59:17 +02:00
|
|
|
const currentSort = _sort;
|
2019-07-09 16:56:40 +02:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
// Display a notification if trying to remove the last displayed field
|
2020-06-19 10:54:31 +02:00
|
|
|
if (value && listLayout.length === 1) {
|
2020-03-30 03:54:05 -04:00
|
|
|
strapi.notification.error('content-manager.notification.error.displayedFields');
|
2019-07-09 16:56:40 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
// Update the sort when removing the displayed one
|
2019-07-09 16:56:40 +02:00
|
|
|
if (currentSort.split(':')[0] === name && value) {
|
|
|
|
emitEvent('didChangeDisplayedFields');
|
2020-06-22 12:59:17 +02:00
|
|
|
handleChangeSearch({
|
2019-07-09 18:11:09 +02:00
|
|
|
target: {
|
|
|
|
name: '_sort',
|
|
|
|
value: `${getFirstSortableElement(name)}:ASC`,
|
|
|
|
},
|
2019-07-09 16:56:40 +02:00
|
|
|
});
|
|
|
|
}
|
2019-11-26 10:39:26 +01:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
// Update the Main reducer
|
2019-07-09 09:53:50 +02:00
|
|
|
onChangeListLabels({
|
2019-11-21 14:24:11 +01:00
|
|
|
target: {
|
|
|
|
name,
|
|
|
|
slug,
|
|
|
|
value: !value,
|
|
|
|
},
|
2019-07-09 09:53:50 +02:00
|
|
|
});
|
|
|
|
};
|
2019-07-09 18:11:09 +02:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
const handleChangeFilters = ({ target: { value } }) => {
|
|
|
|
const newSearch = new URLSearchParams();
|
|
|
|
|
|
|
|
// Set the default params
|
|
|
|
newSearch.set('_limit', _limit);
|
|
|
|
newSearch.set('_sort', _sort);
|
|
|
|
newSearch.set('_page', 1);
|
|
|
|
|
|
|
|
value.forEach(({ filter, name, value: filterValue }) => {
|
|
|
|
const filterType = filter === '=' ? '' : filter;
|
|
|
|
const filterName = `${name}${filterType}`;
|
|
|
|
|
|
|
|
newSearch.append(filterName, filterValue);
|
|
|
|
});
|
2019-07-09 18:11:09 +02:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
push({ search: newSearch.toString() });
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleChangeSearch = async ({ target: { name, value } }) => {
|
|
|
|
const currentSearch = new URLSearchParams(searchToSendForRequest);
|
|
|
|
|
|
|
|
// Pagination
|
|
|
|
currentSearch.delete('_start');
|
|
|
|
|
|
|
|
if (value === '') {
|
|
|
|
currentSearch.delete(name);
|
|
|
|
} else {
|
|
|
|
currentSearch.set(name, value);
|
2019-07-10 08:43:40 +02:00
|
|
|
}
|
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
const searchToString = currentSearch.toString();
|
|
|
|
|
|
|
|
push({ search: searchToString });
|
2019-07-09 18:11:09 +02:00
|
|
|
};
|
2020-06-19 10:54:31 +02:00
|
|
|
|
2019-07-10 09:12:48 +02:00
|
|
|
const handleClickDelete = id => {
|
|
|
|
setIdToDelete(id);
|
|
|
|
toggleModalDelete();
|
|
|
|
};
|
2020-06-19 10:54:31 +02:00
|
|
|
|
2020-06-22 12:59:17 +02:00
|
|
|
const handleModalClose = () => {
|
|
|
|
if (didDeleteData) {
|
|
|
|
fetchData();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-07-10 17:31:49 +02:00
|
|
|
const handleSubmit = (filters = []) => {
|
2019-07-09 18:11:09 +02:00
|
|
|
emitEvent('didFilterEntries');
|
|
|
|
toggleFilterPickerState();
|
2020-06-22 12:59:17 +02:00
|
|
|
handleChangeFilters({ target: { name: 'filters', value: filters } });
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleFilterPickerState = () => {
|
|
|
|
if (!isFilterPickerOpen) {
|
|
|
|
emitEvent('willFilterEntries');
|
|
|
|
}
|
|
|
|
|
|
|
|
setFilterPickerState(prevState => !prevState);
|
|
|
|
};
|
|
|
|
|
|
|
|
const toggleLabelPickerState = () => {
|
|
|
|
if (!isLabelPickerOpen) {
|
|
|
|
emitEvent('willChangeListFieldsSettings');
|
|
|
|
}
|
|
|
|
|
|
|
|
setLabelPickerState(prevState => !prevState);
|
2019-07-09 18:11:09 +02:00
|
|
|
};
|
|
|
|
|
2019-07-09 12:31:18 +02:00
|
|
|
const filterPickerActions = [
|
|
|
|
{
|
|
|
|
label: `${pluginId}.components.FiltersPickWrapper.PluginHeader.actions.clearAll`,
|
|
|
|
kind: 'secondary',
|
|
|
|
onClick: () => {
|
|
|
|
toggleFilterPickerState();
|
2020-06-22 12:59:17 +02:00
|
|
|
// Delete all filters
|
|
|
|
handleChangeFilters({ target: { name: 'filters', value: [] } });
|
2019-07-09 12:31:18 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: `${pluginId}.components.FiltersPickWrapper.PluginHeader.actions.apply`,
|
|
|
|
kind: 'primary',
|
|
|
|
type: 'submit',
|
|
|
|
},
|
|
|
|
];
|
2019-11-06 12:06:17 +01:00
|
|
|
|
2020-06-19 10:54:31 +02:00
|
|
|
const headerAction = useMemo(
|
2020-06-19 17:30:50 +02:00
|
|
|
() => {
|
|
|
|
if (!canCreate) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return [
|
|
|
|
{
|
|
|
|
label: formatMessage(
|
|
|
|
{
|
|
|
|
id: 'content-manager.containers.List.addAnEntry',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
entity: label || 'Content Manager',
|
|
|
|
}
|
|
|
|
),
|
|
|
|
onClick: () => {
|
|
|
|
emitEvent('willCreateEntry');
|
|
|
|
push({
|
|
|
|
pathname: `${pathname}/create`,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
color: 'primary',
|
|
|
|
type: 'button',
|
|
|
|
icon: true,
|
|
|
|
style: {
|
|
|
|
paddingLeft: 15,
|
|
|
|
paddingRight: 15,
|
|
|
|
fontWeight: 600,
|
2020-06-19 10:54:31 +02:00
|
|
|
},
|
2019-11-07 10:48:02 +01:00
|
|
|
},
|
2020-06-19 17:30:50 +02:00
|
|
|
];
|
|
|
|
},
|
2020-06-19 10:54:31 +02:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2020-07-08 17:29:35 +02:00
|
|
|
[label, pathname, search, canCreate, formatMessage]
|
2020-06-19 10:54:31 +02:00
|
|
|
);
|
2019-07-09 16:56:40 +02:00
|
|
|
|
2020-06-19 10:54:31 +02:00
|
|
|
const headerProps = useMemo(() => {
|
2020-06-19 17:30:50 +02:00
|
|
|
/* eslint-disable indent */
|
2020-06-19 10:54:31 +02:00
|
|
|
return {
|
|
|
|
title: {
|
|
|
|
label: label || 'Content Manager',
|
2019-11-07 10:48:02 +01:00
|
|
|
},
|
2020-06-19 17:30:50 +02:00
|
|
|
content: canRead
|
|
|
|
? formatMessage(
|
|
|
|
{
|
|
|
|
id:
|
|
|
|
count > 1
|
|
|
|
? `${pluginId}.containers.List.pluginHeaderDescription`
|
|
|
|
: `${pluginId}.containers.List.pluginHeaderDescription.singular`,
|
|
|
|
},
|
|
|
|
{ label: count }
|
|
|
|
)
|
|
|
|
: null,
|
2020-06-19 10:54:31 +02:00
|
|
|
actions: headerAction,
|
|
|
|
};
|
2020-06-19 17:30:50 +02:00
|
|
|
/* eslint-enable indent */
|
2020-06-19 10:54:31 +02:00
|
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
2020-07-08 17:29:35 +02:00
|
|
|
}, [count, headerAction, label, canRead, formatMessage]);
|
2019-11-06 12:06:17 +01:00
|
|
|
|
2019-07-08 17:01:12 +02:00
|
|
|
return (
|
2019-07-08 20:27:38 +02:00
|
|
|
<>
|
2019-07-09 18:11:09 +02:00
|
|
|
<ListViewProvider
|
2019-07-09 19:38:39 +02:00
|
|
|
data={data}
|
2019-07-10 08:43:40 +02:00
|
|
|
count={count}
|
2019-07-09 19:38:39 +02:00
|
|
|
entriesToDelete={entriesToDelete}
|
2019-07-10 11:12:30 +02:00
|
|
|
emitEvent={emitEvent}
|
2019-07-09 18:11:09 +02:00
|
|
|
firstSortableElement={getFirstSortableElement()}
|
2020-06-19 10:54:31 +02:00
|
|
|
label={label}
|
2019-07-09 19:38:39 +02:00
|
|
|
onChangeBulk={onChangeBulk}
|
|
|
|
onChangeBulkSelectall={onChangeBulkSelectall}
|
2020-06-22 12:59:17 +02:00
|
|
|
onChangeSearch={handleChangeSearch}
|
2019-07-10 09:12:48 +02:00
|
|
|
onClickDelete={handleClickDelete}
|
2020-06-19 10:54:31 +02:00
|
|
|
schema={listSchema}
|
2019-07-09 18:11:09 +02:00
|
|
|
slug={slug}
|
2019-07-09 19:38:39 +02:00
|
|
|
toggleModalDeleteAll={toggleModalDeleteAll}
|
2020-06-22 12:59:17 +02:00
|
|
|
_limit={_limit}
|
|
|
|
_page={_page}
|
|
|
|
filters={filters}
|
|
|
|
_q={_q}
|
|
|
|
_sort={_sort}
|
2019-07-09 18:11:09 +02:00
|
|
|
>
|
|
|
|
<FilterPicker
|
|
|
|
actions={filterPickerActions}
|
|
|
|
isOpen={isFilterPickerOpen}
|
2020-06-19 10:54:31 +02:00
|
|
|
name={label}
|
2019-07-10 17:31:49 +02:00
|
|
|
toggleFilterPickerState={toggleFilterPickerState}
|
2019-07-09 18:11:09 +02:00
|
|
|
onSubmit={handleSubmit}
|
|
|
|
/>
|
|
|
|
<Container className="container-fluid">
|
2020-06-19 17:30:50 +02:00
|
|
|
{!isFilterPickerOpen && <Header {...headerProps} isLoading={isLoading && canRead} />}
|
2020-06-22 12:59:17 +02:00
|
|
|
{isSearchable && canRead && (
|
|
|
|
<Search changeParams={handleChangeSearch} initValue={_q} model={label} value={_q} />
|
2019-07-09 18:11:09 +02:00
|
|
|
)}
|
2020-06-19 17:30:50 +02:00
|
|
|
{canRead && (
|
|
|
|
<Wrapper>
|
|
|
|
<div className="row" style={{ marginBottom: '5px' }}>
|
|
|
|
<div className="col-10">
|
|
|
|
<div className="row" style={{ marginLeft: 0, marginRight: 0 }}>
|
2020-06-22 12:59:17 +02:00
|
|
|
{isFilterable && (
|
2020-06-19 17:30:50 +02:00
|
|
|
<>
|
|
|
|
<AddFilterCta type="button" onClick={toggleFilterPickerState}>
|
|
|
|
<FilterIcon />
|
|
|
|
<FormattedMessage id="app.utils.filters" />
|
|
|
|
</AddFilterCta>
|
2020-06-22 12:59:17 +02:00
|
|
|
{filters.map((filter, key) => (
|
2020-06-19 17:30:50 +02:00
|
|
|
<Filter
|
|
|
|
{...filter}
|
2020-06-22 12:59:17 +02:00
|
|
|
changeParams={handleChangeFilters}
|
|
|
|
filters={filters}
|
2020-06-19 17:30:50 +02:00
|
|
|
index={key}
|
|
|
|
schema={listSchema}
|
|
|
|
key={key}
|
|
|
|
toggleFilterPickerState={toggleFilterPickerState}
|
|
|
|
isFilterPickerOpen={isFilterPickerOpen}
|
|
|
|
/>
|
|
|
|
))}
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="col-2">
|
|
|
|
<CheckPermissions permissions={pluginPermissions.collectionTypesConfigurations}>
|
|
|
|
<DisplayedFieldsDropdown
|
|
|
|
isOpen={isLabelPickerOpen}
|
|
|
|
items={allLabels}
|
|
|
|
onChange={handleChangeListLabels}
|
|
|
|
onClickReset={() => {
|
|
|
|
resetListLabels(slug);
|
|
|
|
}}
|
|
|
|
slug={slug}
|
|
|
|
toggle={toggleLabelPickerState}
|
|
|
|
/>
|
|
|
|
</CheckPermissions>
|
2019-07-10 12:20:25 +02:00
|
|
|
</div>
|
2019-07-09 18:11:09 +02:00
|
|
|
</div>
|
2020-06-19 17:30:50 +02:00
|
|
|
<div className="row" style={{ paddingTop: '12px' }}>
|
|
|
|
<div className="col-12">
|
|
|
|
<CustomTable
|
|
|
|
data={data}
|
|
|
|
canDelete={canDelete}
|
|
|
|
canUpdate={canUpdate}
|
|
|
|
headers={tableHeaders}
|
2020-06-22 12:59:17 +02:00
|
|
|
isBulkable={isBulkable}
|
|
|
|
onChangeParams={handleChangeSearch}
|
2020-06-19 17:30:50 +02:00
|
|
|
showLoader={isLoading}
|
2020-06-10 15:52:10 +02:00
|
|
|
/>
|
2020-06-19 17:30:50 +02:00
|
|
|
<Footer />
|
|
|
|
</div>
|
2019-07-09 18:11:09 +02:00
|
|
|
</div>
|
2020-06-19 17:30:50 +02:00
|
|
|
</Wrapper>
|
|
|
|
)}
|
2019-07-09 18:11:09 +02:00
|
|
|
</Container>
|
2019-07-10 09:12:48 +02:00
|
|
|
<PopUpWarning
|
|
|
|
isOpen={showWarningDelete}
|
|
|
|
toggleModal={toggleModalDelete}
|
|
|
|
content={{
|
|
|
|
title: `${pluginId}.popUpWarning.title`,
|
|
|
|
message: `${pluginId}.popUpWarning.bodyMessage.contentType.delete`,
|
|
|
|
cancel: `${pluginId}.popUpWarning.button.cancel`,
|
|
|
|
confirm: `${pluginId}.popUpWarning.button.confirm`,
|
|
|
|
}}
|
2020-02-10 16:33:29 +01:00
|
|
|
onConfirm={handleConfirmDeleteData}
|
2019-07-10 09:12:48 +02:00
|
|
|
popUpWarningType="danger"
|
2020-06-22 12:59:17 +02:00
|
|
|
onClosed={handleModalClose}
|
2020-06-22 13:53:29 +02:00
|
|
|
isConfirmButtonLoading={showModalConfirmButtonLoading}
|
2019-07-10 09:12:48 +02:00
|
|
|
/>
|
2019-07-09 19:38:39 +02:00
|
|
|
<PopUpWarning
|
|
|
|
isOpen={showWarningDeleteAll}
|
|
|
|
toggleModal={toggleModalDeleteAll}
|
|
|
|
content={{
|
2019-07-10 09:12:48 +02:00
|
|
|
title: `${pluginId}.popUpWarning.title`,
|
|
|
|
message: `${pluginId}.popUpWarning.bodyMessage.contentType.delete${
|
|
|
|
entriesToDelete.length > 1 ? '.all' : ''
|
|
|
|
}`,
|
|
|
|
cancel: `${pluginId}.popUpWarning.button.cancel`,
|
|
|
|
confirm: `${pluginId}.popUpWarning.button.confirm`,
|
2019-07-09 19:38:39 +02:00
|
|
|
}}
|
|
|
|
popUpWarningType="danger"
|
2020-02-10 16:33:29 +01:00
|
|
|
onConfirm={handleConfirmDeleteAllData}
|
2020-06-22 12:59:17 +02:00
|
|
|
onClosed={handleModalClose}
|
2020-06-22 13:53:29 +02:00
|
|
|
isConfirmButtonLoading={showModalConfirmButtonLoading}
|
2019-07-09 19:38:39 +02:00
|
|
|
/>
|
2019-07-09 18:11:09 +02:00
|
|
|
</ListViewProvider>
|
2019-07-08 20:27:38 +02:00
|
|
|
</>
|
2019-07-08 17:01:12 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
ListView.defaultProps = {
|
|
|
|
layouts: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
ListView.propTypes = {
|
2019-07-08 20:27:38 +02:00
|
|
|
count: PropTypes.number.isRequired,
|
2019-07-09 16:56:40 +02:00
|
|
|
data: PropTypes.array.isRequired,
|
2020-06-22 12:59:17 +02:00
|
|
|
didDeleteData: PropTypes.bool.isRequired,
|
2019-07-08 20:27:38 +02:00
|
|
|
emitEvent: PropTypes.func.isRequired,
|
2019-07-09 19:38:39 +02:00
|
|
|
entriesToDelete: PropTypes.array.isRequired,
|
2020-05-05 02:05:35 +02:00
|
|
|
isLoading: PropTypes.bool.isRequired,
|
2019-07-08 17:01:12 +02:00
|
|
|
layouts: PropTypes.object,
|
2019-07-08 20:27:38 +02:00
|
|
|
location: PropTypes.shape({
|
2019-07-10 17:31:49 +02:00
|
|
|
pathname: PropTypes.string.isRequired,
|
2019-07-08 20:27:38 +02:00
|
|
|
search: PropTypes.string.isRequired,
|
2020-01-21 16:03:31 +01:00
|
|
|
}).isRequired,
|
2019-11-21 12:18:08 +01:00
|
|
|
models: PropTypes.array.isRequired,
|
2020-06-22 12:59:17 +02:00
|
|
|
getData: PropTypes.func.isRequired,
|
2020-02-10 16:33:29 +01:00
|
|
|
getDataSucceeded: PropTypes.func.isRequired,
|
2019-07-09 09:53:50 +02:00
|
|
|
history: PropTypes.shape({
|
|
|
|
push: PropTypes.func.isRequired,
|
2020-01-21 16:03:31 +01:00
|
|
|
}).isRequired,
|
2019-07-09 19:38:39 +02:00
|
|
|
onChangeBulk: PropTypes.func.isRequired,
|
|
|
|
onChangeBulkSelectall: PropTypes.func.isRequired,
|
2019-07-09 09:53:50 +02:00
|
|
|
onChangeListLabels: PropTypes.func.isRequired,
|
2020-07-07 17:03:16 +02:00
|
|
|
onDeleteDataError: PropTypes.func.isRequired,
|
2020-02-10 16:33:29 +01:00
|
|
|
onDeleteDataSucceeded: PropTypes.func.isRequired,
|
|
|
|
onDeleteSeveralDataSucceeded: PropTypes.func.isRequired,
|
2019-07-09 09:53:50 +02:00
|
|
|
resetListLabels: PropTypes.func.isRequired,
|
2019-07-08 20:27:38 +02:00
|
|
|
resetProps: PropTypes.func.isRequired,
|
2020-06-22 13:53:29 +02:00
|
|
|
setModalLoadingState: PropTypes.func.isRequired,
|
|
|
|
showModalConfirmButtonLoading: PropTypes.bool.isRequired,
|
2019-07-10 09:12:48 +02:00
|
|
|
showWarningDelete: PropTypes.bool.isRequired,
|
2019-07-09 19:38:39 +02:00
|
|
|
showWarningDeleteAll: PropTypes.bool.isRequired,
|
2019-10-24 17:08:52 +02:00
|
|
|
slug: PropTypes.string.isRequired,
|
2019-07-10 09:12:48 +02:00
|
|
|
toggleModalDelete: PropTypes.func.isRequired,
|
2019-07-09 19:38:39 +02:00
|
|
|
toggleModalDeleteAll: PropTypes.func.isRequired,
|
2019-07-08 17:01:12 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const mapStateToProps = makeSelectListView();
|
|
|
|
|
|
|
|
export function mapDispatchToProps(dispatch) {
|
|
|
|
return bindActionCreators(
|
|
|
|
{
|
2020-06-22 12:59:17 +02:00
|
|
|
getData,
|
2020-02-10 16:33:29 +01:00
|
|
|
getDataSucceeded,
|
2019-07-09 19:38:39 +02:00
|
|
|
onChangeBulk,
|
|
|
|
onChangeBulkSelectall,
|
2019-07-09 09:53:50 +02:00
|
|
|
onChangeListLabels,
|
2020-07-07 17:03:16 +02:00
|
|
|
onDeleteDataError,
|
2020-02-10 16:33:29 +01:00
|
|
|
onDeleteDataSucceeded,
|
|
|
|
onDeleteSeveralDataSucceeded,
|
2019-07-09 09:53:50 +02:00
|
|
|
resetListLabels,
|
2019-07-08 20:27:38 +02:00
|
|
|
resetProps,
|
2020-06-22 13:53:29 +02:00
|
|
|
setModalLoadingState,
|
2019-07-10 09:12:48 +02:00
|
|
|
toggleModalDelete,
|
2019-07-09 19:38:39 +02:00
|
|
|
toggleModalDeleteAll,
|
2019-07-08 17:01:12 +02:00
|
|
|
},
|
|
|
|
dispatch
|
|
|
|
);
|
|
|
|
}
|
2020-02-13 11:17:32 +01:00
|
|
|
const withConnect = connect(mapStateToProps, mapDispatchToProps);
|
2019-07-08 17:01:12 +02:00
|
|
|
|
2020-02-13 11:17:32 +01:00
|
|
|
export default compose(withConnect, memo)(ListView);
|