builds RenderCustomComponent
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AnimateHeight from 'react-animate-height';
|
||||
// import customComponents from '../../customComponents';
|
||||
import SearchFilter from '../SearchFilter';
|
||||
import ColumnSelector from '../ColumnSelector';
|
||||
import WhereBuilder from '../WhereBuilder';
|
||||
|
||||
@@ -3,11 +3,14 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'condition-value-date';
|
||||
|
||||
const DateField = ({ onChange, value }) => {
|
||||
return (
|
||||
<input
|
||||
className={baseClass}
|
||||
type="text"
|
||||
onChange={onChange}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
@import '../../../../../scss/styles.scss';
|
||||
@import '../../../../forms/field-types/shared.scss';
|
||||
|
||||
.condition-value-date {
|
||||
@include formInput;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,27 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'condition-value-number';
|
||||
|
||||
const NumberField = ({ onChange, value }) => {
|
||||
return (
|
||||
<input
|
||||
placeholder="Enter a value"
|
||||
className={baseClass}
|
||||
type="number"
|
||||
onChange={onChange}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
NumberField.defaultProps = {
|
||||
value: null,
|
||||
};
|
||||
|
||||
NumberField.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default NumberField;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
@import '../../../../../scss/styles.scss';
|
||||
@import '../../../../forms/field-types/shared.scss';
|
||||
|
||||
.condition-value-number {
|
||||
@include formInput;
|
||||
}
|
||||
|
||||
@@ -3,19 +3,27 @@ import PropTypes from 'prop-types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'condition-value-text';
|
||||
|
||||
const Text = ({ onChange, value }) => {
|
||||
return (
|
||||
<input
|
||||
placeholder="Enter a value"
|
||||
className={baseClass}
|
||||
type="text"
|
||||
onChange={onChange}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Text.defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
Text.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Text;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
@import '../../../../../scss/styles.scss';
|
||||
@import '../../../../forms/field-types/shared.scss';
|
||||
|
||||
.condition-value-text {
|
||||
@include formInput;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import RenderCustomComponent from '../../../utilities/RenderCustomComponent';
|
||||
import ReactSelect from '../../ReactSelect';
|
||||
import Button from '../../Button';
|
||||
import Date from './Date';
|
||||
@@ -23,6 +24,7 @@ const Condition = (props) => {
|
||||
value,
|
||||
orIndex,
|
||||
andIndex,
|
||||
collectionSlug,
|
||||
} = props;
|
||||
|
||||
const [activeField, setActiveField] = useState({ operators: [] });
|
||||
@@ -35,6 +37,8 @@ const Condition = (props) => {
|
||||
}
|
||||
}, [value, fields]);
|
||||
|
||||
const ValueComponent = valueFields[activeField.component] || valueFields.Text;
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
@@ -64,15 +68,18 @@ const Condition = (props) => {
|
||||
/>
|
||||
</div>
|
||||
<div className={`${baseClass}__value`}>
|
||||
<ReactSelect
|
||||
value={value.value}
|
||||
options={[
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'option-1',
|
||||
},
|
||||
]}
|
||||
onChange={newValue => console.log('changing value', newValue)}
|
||||
<RenderCustomComponent
|
||||
path={`${collectionSlug}.fields.${activeField.value}.filter`}
|
||||
DefaultComponent={ValueComponent}
|
||||
componentProps={{
|
||||
value: value.value,
|
||||
onChange: updatedValue => dispatch({
|
||||
type: 'update',
|
||||
orIndex,
|
||||
andIndex,
|
||||
value: updatedValue || '',
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -125,6 +132,7 @@ Condition.propTypes = {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
orIndex: PropTypes.number.isRequired,
|
||||
andIndex: PropTypes.number.isRequired,
|
||||
collectionSlug: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Condition;
|
||||
|
||||
@@ -13,6 +13,7 @@ const WhereBuilder = (props) => {
|
||||
const {
|
||||
collection: {
|
||||
fields,
|
||||
slug,
|
||||
labels: {
|
||||
plural,
|
||||
} = {},
|
||||
@@ -26,13 +27,15 @@ const WhereBuilder = (props) => {
|
||||
useEffect(() => {
|
||||
setReducedFields(fields.reduce((reduced, field) => {
|
||||
if (typeof fieldTypes[field.type] === 'object') {
|
||||
const formattedField = {
|
||||
label: field.label,
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
};
|
||||
|
||||
return [
|
||||
...reduced,
|
||||
{
|
||||
label: field.label,
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
},
|
||||
formattedField,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -74,6 +77,7 @@ const WhereBuilder = (props) => {
|
||||
</div>
|
||||
)}
|
||||
<Condition
|
||||
collectionSlug={slug}
|
||||
value={where[orIndex][andIndex]}
|
||||
orIndex={orIndex}
|
||||
andIndex={andIndex}
|
||||
@@ -119,6 +123,7 @@ const WhereBuilder = (props) => {
|
||||
WhereBuilder.propTypes = {
|
||||
handleChange: PropTypes.func.isRequired,
|
||||
collection: PropTypes.shape({
|
||||
slug: PropTypes.string,
|
||||
fields: PropTypes.arrayOf(
|
||||
PropTypes.shape({}),
|
||||
),
|
||||
|
||||
@@ -51,7 +51,7 @@ const reducer = (state, action = {}) => {
|
||||
newState[orIndex][andIndex].field = field;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
if (value !== undefined) {
|
||||
newState[orIndex][andIndex].value = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { Suspense } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import customComponents from '../../customComponents';
|
||||
import Loading from '../../elements/Loading';
|
||||
|
||||
const RenderCustomComponent = (props) => {
|
||||
const { path, DefaultComponent, componentProps } = props;
|
||||
|
||||
const CustomComponent = path.split('.').reduce((res, prop) => {
|
||||
if (res) {
|
||||
return res[prop];
|
||||
}
|
||||
|
||||
return false;
|
||||
}, customComponents);
|
||||
|
||||
if (CustomComponent) {
|
||||
return (
|
||||
<Suspense fallback={<Loading />}>
|
||||
<CustomComponent {...componentProps} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DefaultComponent {...componentProps} />
|
||||
);
|
||||
};
|
||||
|
||||
RenderCustomComponent.propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
DefaultComponent: PropTypes.oneOfType([
|
||||
PropTypes.func,
|
||||
PropTypes.node,
|
||||
PropTypes.element,
|
||||
]).isRequired,
|
||||
componentProps: PropTypes.shape({}).isRequired,
|
||||
};
|
||||
|
||||
export default RenderCustomComponent;
|
||||
161
src/client/components/views/collections/List/Default.js
Normal file
161
src/client/components/views/collections/List/Default.js
Normal file
@@ -0,0 +1,161 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import queryString from 'qs';
|
||||
import PropTypes from 'prop-types';
|
||||
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
|
||||
import Paginator from '../../../elements/Paginator';
|
||||
import ListControls from '../../../elements/ListControls';
|
||||
import Pill from '../../../elements/Pill';
|
||||
import Button from '../../../elements/Button';
|
||||
import SortColumn from '../../../elements/SortColumn';
|
||||
import Table from '../../../elements/Table';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const { serverURL, routes: { api, admin } } = PAYLOAD_CONFIG;
|
||||
|
||||
const baseClass = 'collection-list';
|
||||
|
||||
const DefaultList = (props) => {
|
||||
const {
|
||||
collection,
|
||||
collection: {
|
||||
fields,
|
||||
slug,
|
||||
labels: {
|
||||
singular: singularLabel,
|
||||
plural: pluralLabel,
|
||||
},
|
||||
},
|
||||
} = props;
|
||||
|
||||
const location = useLocation();
|
||||
const [listControls, setListControls] = useState({});
|
||||
const [sort, setSort] = useState(null);
|
||||
const newDocumentURL = `${admin}/collections/${slug}/create`;
|
||||
|
||||
const { page } = queryString.parse(location.search, { ignoreQueryPrefix: true });
|
||||
|
||||
const apiURL = `${serverURL}${api}/${slug}`;
|
||||
|
||||
const [{ data }, { setParams }] = usePayloadAPI(apiURL, {});
|
||||
|
||||
useEffect(() => {
|
||||
const params = {};
|
||||
|
||||
if (page) params.page = page;
|
||||
if (sort) params.sort = sort;
|
||||
if (listControls?.where) params.where = listControls.where;
|
||||
|
||||
setParams(params);
|
||||
}, [setParams, page, sort, listControls]);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h1>{pluralLabel}</h1>
|
||||
<Pill to={newDocumentURL}>
|
||||
Create New
|
||||
</Pill>
|
||||
</header>
|
||||
<ListControls
|
||||
handleChange={setListControls}
|
||||
collection={collection}
|
||||
/>
|
||||
{(data.docs && data.docs.length > 0) && (
|
||||
<Table
|
||||
data={data.docs}
|
||||
columns={listControls.columns.map((col, i) => {
|
||||
const field = fields.find(fieldToCheck => fieldToCheck.name === col);
|
||||
return {
|
||||
accessor: field.name,
|
||||
components: {
|
||||
Heading: (
|
||||
<SortColumn
|
||||
label={field.label}
|
||||
name={field.name}
|
||||
handleChange={setSort}
|
||||
/>
|
||||
),
|
||||
renderCell: (rowData, cellData) => {
|
||||
if (i === 0) {
|
||||
return (
|
||||
<>
|
||||
<Link to={`${admin}/collections/${collection.slug}/${rowData.id}`}>
|
||||
{typeof cellData === 'string' && cellData}
|
||||
{typeof cellData === 'object' && JSON.stringify(cellData)}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return cellData;
|
||||
},
|
||||
},
|
||||
};
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{(!data.docs || data.docs.length === 0) && (
|
||||
<div className={`${baseClass}__no-results`}>
|
||||
<p>
|
||||
No
|
||||
{' '}
|
||||
{pluralLabel}
|
||||
{' '}
|
||||
found. Either no
|
||||
{' '}
|
||||
{pluralLabel}
|
||||
{' '}
|
||||
exist yet or none match the filters you've specified above.
|
||||
</p>
|
||||
<Button
|
||||
el="link"
|
||||
to={newDocumentURL}
|
||||
>
|
||||
Create new
|
||||
{' '}
|
||||
{singularLabel}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__page-controls`}>
|
||||
<Paginator
|
||||
totalDocs={data.totalDocs}
|
||||
limit={data.limit}
|
||||
totalPages={data.totalPages}
|
||||
page={data.page}
|
||||
hasPrevPage={data.hasPrevPage}
|
||||
hasNextPage={data.hasNextPage}
|
||||
prevPage={data.prevPage}
|
||||
nextPage={data.nextPage}
|
||||
numberOfNeighbors={1}
|
||||
/>
|
||||
<div className={`${baseClass}__page-info`}>
|
||||
{data.page}
|
||||
-
|
||||
{data.totalPages > 1 ? data.limit : data.totalDocs}
|
||||
{' '}
|
||||
of
|
||||
{' '}
|
||||
{data.totalDocs}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
DefaultList.propTypes = {
|
||||
collection: PropTypes.shape({
|
||||
labels: PropTypes.shape({
|
||||
singular: PropTypes.string,
|
||||
plural: PropTypes.string,
|
||||
}),
|
||||
slug: PropTypes.string,
|
||||
useAsTitle: PropTypes.string,
|
||||
fields: PropTypes.arrayOf(PropTypes.shape),
|
||||
timestamps: PropTypes.bool,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default DefaultList;
|
||||
@@ -1,166 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import queryString from 'qs';
|
||||
import PropTypes from 'prop-types';
|
||||
import DefaultList from './Default';
|
||||
import customComponents from '../../../customComponents';
|
||||
import { useStepNav } from '../../../elements/StepNav';
|
||||
import usePayloadAPI from '../../../../hooks/usePayloadAPI';
|
||||
import Paginator from '../../../elements/Paginator';
|
||||
import ListControls from '../../../elements/ListControls';
|
||||
import Pill from '../../../elements/Pill';
|
||||
import Button from '../../../elements/Button';
|
||||
import SortColumn from '../../../elements/SortColumn';
|
||||
import Table from '../../../elements/Table';
|
||||
import formatListFields from './formatListFields';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const { serverURL, routes: { api, admin } } = PAYLOAD_CONFIG;
|
||||
|
||||
const baseClass = 'collection-list';
|
||||
|
||||
const DefaultList = (props) => {
|
||||
const {
|
||||
collection,
|
||||
collection: {
|
||||
fields,
|
||||
slug,
|
||||
labels: {
|
||||
singular: singularLabel,
|
||||
plural: pluralLabel,
|
||||
},
|
||||
},
|
||||
} = props;
|
||||
|
||||
const location = useLocation();
|
||||
const [listControls, setListControls] = useState({});
|
||||
const [sort, setSort] = useState(null);
|
||||
const newDocumentURL = `${admin}/collections/${slug}/create`;
|
||||
|
||||
const { page } = queryString.parse(location.search, { ignoreQueryPrefix: true });
|
||||
|
||||
const apiURL = `${serverURL}${api}/${slug}`;
|
||||
|
||||
const [{ data }, { setParams }] = usePayloadAPI(apiURL, {});
|
||||
|
||||
useEffect(() => {
|
||||
const params = {};
|
||||
|
||||
if (page) params.page = page;
|
||||
if (sort) params.sort = sort;
|
||||
if (listControls?.where) params.where = listControls.where;
|
||||
|
||||
setParams(params);
|
||||
}, [setParams, page, sort, listControls]);
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h1>{pluralLabel}</h1>
|
||||
<Pill to={newDocumentURL}>
|
||||
Create New
|
||||
</Pill>
|
||||
</header>
|
||||
<ListControls
|
||||
handleChange={setListControls}
|
||||
collection={collection}
|
||||
/>
|
||||
{(data.docs && data.docs.length > 0) && (
|
||||
<Table
|
||||
data={data.docs}
|
||||
columns={listControls.columns.map((col, i) => {
|
||||
const field = fields.find(fieldToCheck => fieldToCheck.name === col);
|
||||
return {
|
||||
accessor: field.name,
|
||||
components: {
|
||||
Heading: (
|
||||
<SortColumn
|
||||
label={field.label}
|
||||
name={field.name}
|
||||
handleChange={setSort}
|
||||
/>
|
||||
),
|
||||
renderCell: (rowData, cellData) => {
|
||||
if (i === 0) {
|
||||
return (
|
||||
<>
|
||||
<Link to={`${admin}/collections/${collection.slug}/${rowData.id}`}>
|
||||
{typeof cellData === 'string' && cellData}
|
||||
{typeof cellData === 'object' && JSON.stringify(cellData)}
|
||||
</Link>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return cellData;
|
||||
},
|
||||
},
|
||||
};
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{(!data.docs || data.docs.length === 0) && (
|
||||
<div className={`${baseClass}__no-results`}>
|
||||
<p>
|
||||
No
|
||||
{' '}
|
||||
{pluralLabel}
|
||||
{' '}
|
||||
found. Either no
|
||||
{' '}
|
||||
{pluralLabel}
|
||||
{' '}
|
||||
exist yet or none match the filters you've specified above.
|
||||
</p>
|
||||
<Button
|
||||
el="link"
|
||||
to={newDocumentURL}
|
||||
>
|
||||
Create new
|
||||
{' '}
|
||||
{singularLabel}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__page-controls`}>
|
||||
<Paginator
|
||||
totalDocs={data.totalDocs}
|
||||
limit={data.limit}
|
||||
totalPages={data.totalPages}
|
||||
page={data.page}
|
||||
hasPrevPage={data.hasPrevPage}
|
||||
hasNextPage={data.hasNextPage}
|
||||
prevPage={data.prevPage}
|
||||
nextPage={data.nextPage}
|
||||
numberOfNeighbors={1}
|
||||
/>
|
||||
<div className={`${baseClass}__page-info`}>
|
||||
{data.page}
|
||||
-
|
||||
{data.totalPages > 1 ? data.limit : data.totalDocs}
|
||||
{' '}
|
||||
of
|
||||
{' '}
|
||||
{data.totalDocs}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
DefaultList.propTypes = {
|
||||
collection: PropTypes.shape({
|
||||
labels: PropTypes.shape({
|
||||
singular: PropTypes.string,
|
||||
plural: PropTypes.string,
|
||||
}),
|
||||
slug: PropTypes.string,
|
||||
useAsTitle: PropTypes.string,
|
||||
fields: PropTypes.arrayOf(PropTypes.shape),
|
||||
timestamps: PropTypes.bool,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
const ListView = (props) => {
|
||||
const {
|
||||
collection,
|
||||
|
||||
Reference in New Issue
Block a user