mirror of
https://github.com/strapi/strapi.git
synced 2025-12-05 19:42:05 +00:00
336 lines
9.6 KiB
JavaScript
Executable File
336 lines
9.6 KiB
JavaScript
Executable File
/*
|
|
*
|
|
* List
|
|
*
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { connect } from 'react-redux';
|
|
import { bindActionCreators, compose } from 'redux';
|
|
import { createStructuredSelector } from 'reselect';
|
|
import PropTypes from 'prop-types';
|
|
import { isEmpty, isUndefined, map, get, toInteger } from 'lodash';
|
|
import { router } from 'app';
|
|
|
|
// Selectors.
|
|
import { makeSelectModels, makeSelectSchema } from 'containers/App/selectors';
|
|
|
|
// Components.
|
|
import Table from 'components/Table';
|
|
import TableFooter from 'components/TableFooter';
|
|
import PluginHeader from 'components/PluginHeader';
|
|
import PopUpWarning from 'components/PopUpWarning';
|
|
|
|
import injectReducer from 'utils/injectReducer';
|
|
import injectSaga from 'utils/injectSaga';
|
|
|
|
// Utils
|
|
import getQueryParameters from 'utils/getQueryParameters';
|
|
|
|
// Actions.
|
|
import {
|
|
deleteRecord,
|
|
} from '../Edit/actions';
|
|
|
|
// Styles.
|
|
import styles from './styles.scss';
|
|
|
|
// Actions.
|
|
import {
|
|
setCurrentModelName,
|
|
loadRecords,
|
|
loadCount,
|
|
changePage,
|
|
changeSort,
|
|
changeLimit,
|
|
} from './actions';
|
|
|
|
// Selectors.
|
|
import {
|
|
makeSelectRecords,
|
|
makeSelectCurrentModelName,
|
|
makeSelectCurrentModelNamePluralized,
|
|
makeSelectCount,
|
|
makeSelectCurrentPage,
|
|
makeSelectLimit,
|
|
makeSelectSort,
|
|
makeSelectLoadingCount,
|
|
} from './selectors';
|
|
|
|
import reducer from './reducer';
|
|
import saga from './sagas';
|
|
|
|
export class List extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
showWarning: false,
|
|
source: getQueryParameters(props.location.search, 'source'),
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
// Init the view
|
|
this.init(this.props);
|
|
}
|
|
|
|
componentWillReceiveProps(nextProps) {
|
|
const locationChanged = nextProps.location.pathname !== this.props.location.pathname;
|
|
|
|
if (locationChanged) {
|
|
this.init(nextProps);
|
|
}
|
|
|
|
if (!isEmpty(nextProps.location.search) && this.props.location.search !== nextProps.location.search) {
|
|
this.props.loadRecords(this.state.source);
|
|
}
|
|
}
|
|
|
|
init(props) {
|
|
const slug = props.match.params.slug;
|
|
// Set current model name
|
|
this.props.setCurrentModelName(slug.toLowerCase());
|
|
|
|
const sort = (isEmpty(props.location.search) ?
|
|
get(this.props.models, ['models', slug.toLowerCase(), 'primaryKey']) || get(this.props.models.plugins, [this.state.source, 'models', slug.toLowerCase(), 'primaryKey']) :
|
|
getQueryParameters('sort')) || 'id';
|
|
|
|
if (!isEmpty(props.location.search)) {
|
|
this.props.changePage(toInteger(getQueryParameters('page')), this.state.source);
|
|
this.props.changeLimit(toInteger(getQueryParameters('limit')), this.state.source);
|
|
}
|
|
|
|
this.props.changeSort(sort, this.state.source);
|
|
|
|
// Load records
|
|
this.props.loadRecords(this.state.source);
|
|
|
|
// Get the records count
|
|
this.props.loadCount(this.state.source);
|
|
|
|
// Define the `create` route url
|
|
this.addRoute = `${this.props.match.path.replace(':slug', slug)}/create`;
|
|
}
|
|
|
|
handleChangeLimit = ({ target }) => {
|
|
this.props.changeLimit(toInteger(target.value), this.state.source);
|
|
router.push({
|
|
pathname: this.props.location.pathname,
|
|
search: `?page=${this.props.currentPage}&limit=${target.value}&sort=${this.props.sort}&source=${this.state.source}`,
|
|
});
|
|
}
|
|
|
|
handleChangePage = (page) => {
|
|
router.push({
|
|
pathname: this.props.location.pathname,
|
|
search: `?page=${page}&limit=${this.props.limit}&sort=${this.props.sort}&source=${this.state.source}`,
|
|
});
|
|
this.props.changePage(page, this.state.source);
|
|
}
|
|
|
|
handleChangeSort = (sort) => {
|
|
router.push({
|
|
pathname: this.props.location.pathname,
|
|
search: `?page=${this.props.currentPage}&limit=${this.props.limit}&sort=${sort}&source=${this.state.source}`,
|
|
});
|
|
this.props.changeSort(sort, this.state.source);
|
|
}
|
|
|
|
handleDelete = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
this.props.deleteRecord(this.state.target, this.props.currentModelName, this.state.source);
|
|
this.setState({ showWarning: false });
|
|
}
|
|
|
|
toggleModalWarning = (e) => {
|
|
if (!isUndefined(e)) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this.setState({
|
|
target: e.target.id,
|
|
});
|
|
}
|
|
|
|
this.setState({ showWarning: !this.state.showWarning });
|
|
}
|
|
|
|
render() {
|
|
// Detect current model structure from models list
|
|
const currentModel = get(this.props.models, ['models', this.props.currentModelName]) || get(this.props.models, ['plugins', this.state.source, 'models', this.props.currentModelName]);
|
|
const currentSchema = get(this.props.schema, [this.props.currentModelName]) || get(this.props.schema, ['plugins', this.state.source, this.props.currentModelName]);
|
|
|
|
if (!this.props.currentModelName || !currentSchema) {
|
|
return <div />;
|
|
}
|
|
|
|
// Define table headers
|
|
const tableHeaders = map(currentSchema.list, (value) => ({
|
|
name: value,
|
|
label: currentSchema.fields[value].label,
|
|
type: currentSchema.fields[value].type,
|
|
}));
|
|
|
|
tableHeaders.splice(0, 0, { name: currentModel.primaryKey || 'id', label: 'Id', type: 'string' });
|
|
|
|
const content = (
|
|
<Table
|
|
records={this.props.records}
|
|
route={this.props.match}
|
|
routeParams={this.props.match.params}
|
|
headers={tableHeaders}
|
|
onChangeSort={this.handleChangeSort}
|
|
sort={this.props.sort}
|
|
history={this.props.history}
|
|
primaryKey={currentModel.primaryKey || 'id'}
|
|
handleDelete={this.toggleModalWarning}
|
|
redirectUrl={`?redirectUrl=/plugins/content-manager/${this.props.currentModelName.toLowerCase()}/?page=${this.props.currentPage}&limit=${this.props.limit}&sort=${this.props.sort}&source=${this.state.source}`}
|
|
/>
|
|
);
|
|
|
|
// Plugin header config
|
|
const pluginHeaderTitle = currentSchema.label || 'Content Manager';
|
|
|
|
// Define plugin header actions
|
|
const pluginHeaderActions = [
|
|
{
|
|
label: 'content-manager.containers.List.addAnEntry',
|
|
labelValues: {
|
|
entity: pluginHeaderTitle,
|
|
},
|
|
kind: 'primaryAddShape',
|
|
onClick: () => this.context.router.history.push({
|
|
pathname: this.addRoute,
|
|
search: `?source=${this.state.source}`,
|
|
}),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div>
|
|
<div className={`container-fluid ${styles.containerFluid}`}>
|
|
<PluginHeader
|
|
title={{
|
|
id: pluginHeaderTitle,
|
|
}}
|
|
description={{
|
|
id: 'content-manager.containers.List.pluginHeaderDescription',
|
|
values: {
|
|
label: this.props.count,
|
|
},
|
|
}}
|
|
actions={pluginHeaderActions}
|
|
/>
|
|
<div className={`row ${styles.row}`}>
|
|
<div className='col-lg-12'>
|
|
{content}
|
|
<PopUpWarning
|
|
isOpen={this.state.showWarning}
|
|
toggleModal={this.toggleModalWarning}
|
|
content={{
|
|
title: 'content-manager.popUpWarning.title',
|
|
message: 'content-manager.popUpWarning.bodyMessage.contentType.delete',
|
|
cancel: 'content-manager.popUpWarning.button.cancel',
|
|
confirm: 'content-manager.popUpWarning.button.confirm',
|
|
}}
|
|
popUpWarningType={'danger'}
|
|
onConfirm={this.handleDelete}
|
|
/>
|
|
<TableFooter
|
|
limit={this.props.limit}
|
|
currentPage={this.props.currentPage}
|
|
onChangePage={this.handleChangePage}
|
|
count={this.props.count}
|
|
className="push-lg-right"
|
|
onChangeLimit={this.handleChangeLimit}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
List.contextTypes = {
|
|
router: PropTypes.object.isRequired,
|
|
};
|
|
|
|
List.propTypes = {
|
|
changeLimit: PropTypes.func.isRequired,
|
|
changePage: PropTypes.func.isRequired,
|
|
changeSort: PropTypes.func.isRequired,
|
|
count: PropTypes.oneOfType([
|
|
PropTypes.number,
|
|
PropTypes.bool,
|
|
]).isRequired,
|
|
currentModelName: PropTypes.oneOfType([
|
|
PropTypes.string,
|
|
PropTypes.bool,
|
|
]).isRequired,
|
|
currentPage: PropTypes.number.isRequired,
|
|
deleteRecord: PropTypes.func.isRequired,
|
|
history: PropTypes.object.isRequired,
|
|
limit: PropTypes.number.isRequired,
|
|
loadCount: PropTypes.func.isRequired,
|
|
loadRecords: PropTypes.func.isRequired,
|
|
location: PropTypes.object.isRequired,
|
|
match: PropTypes.object.isRequired,
|
|
models: PropTypes.oneOfType([
|
|
PropTypes.object,
|
|
PropTypes.bool,
|
|
]).isRequired,
|
|
records: PropTypes.oneOfType([
|
|
PropTypes.array,
|
|
PropTypes.object,
|
|
]).isRequired,
|
|
// route: PropTypes.object.isRequired,
|
|
schema: PropTypes.oneOfType([
|
|
PropTypes.bool,
|
|
PropTypes.object,
|
|
]).isRequired,
|
|
setCurrentModelName: PropTypes.func.isRequired,
|
|
sort: PropTypes.string.isRequired,
|
|
};
|
|
|
|
function mapDispatchToProps(dispatch) {
|
|
return bindActionCreators(
|
|
{
|
|
deleteRecord,
|
|
setCurrentModelName,
|
|
loadRecords,
|
|
loadCount,
|
|
changePage,
|
|
changeSort,
|
|
changeLimit,
|
|
},
|
|
dispatch
|
|
);
|
|
}
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
records: makeSelectRecords(),
|
|
count: makeSelectCount(),
|
|
loadingCount: makeSelectLoadingCount(),
|
|
models: makeSelectModels(),
|
|
currentPage: makeSelectCurrentPage(),
|
|
limit: makeSelectLimit(),
|
|
sort: makeSelectSort(),
|
|
currentModelName: makeSelectCurrentModelName(),
|
|
currentModelNamePluralized: makeSelectCurrentModelNamePluralized(),
|
|
schema: makeSelectSchema(),
|
|
});
|
|
|
|
const withConnect = connect(mapStateToProps, mapDispatchToProps);
|
|
|
|
const withReducer = injectReducer({ key: 'list', reducer });
|
|
const withSaga = injectSaga({ key: 'list', saga });
|
|
|
|
export default compose(
|
|
withReducer,
|
|
withSaga,
|
|
withConnect,
|
|
)(List);
|