fixes issues with hidden fields on frontend and on schema creation
This commit is contained in:
@@ -26,13 +26,13 @@ ButtonContents.propTypes = {
|
||||
|
||||
const Button = (props) => {
|
||||
const {
|
||||
className, type, el, to, url, children, onClick, disabled, icon,
|
||||
className, type, el, to, url, children, onClick, disabled, icon, buttonStyle,
|
||||
} = props;
|
||||
|
||||
const classes = [
|
||||
baseClass,
|
||||
className && className,
|
||||
type && `${baseClass}--${type}`,
|
||||
buttonStyle && `${baseClass}--${buttonStyle}`,
|
||||
icon && `${baseClass}--icon`,
|
||||
disabled && `${baseClass}--disabled`,
|
||||
].filter(Boolean).join(' ');
|
||||
@@ -43,6 +43,7 @@ const Button = (props) => {
|
||||
}
|
||||
|
||||
const buttonProps = {
|
||||
type,
|
||||
className: classes,
|
||||
onClick: handleClick,
|
||||
};
|
||||
@@ -75,7 +76,7 @@ const Button = (props) => {
|
||||
default:
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
type="submit"
|
||||
{...buttonProps}
|
||||
>
|
||||
<ButtonContents icon={icon}>
|
||||
@@ -88,7 +89,8 @@ const Button = (props) => {
|
||||
|
||||
Button.defaultProps = {
|
||||
className: null,
|
||||
type: 'primary',
|
||||
type: 'submit',
|
||||
buttonStyle: 'primary',
|
||||
el: null,
|
||||
to: null,
|
||||
url: null,
|
||||
@@ -100,7 +102,8 @@ Button.defaultProps = {
|
||||
|
||||
Button.propTypes = {
|
||||
className: PropTypes.string,
|
||||
type: PropTypes.oneOf(['primary', 'secondary', 'error', undefined]),
|
||||
type: PropTypes.oneOf(['submit', 'button']),
|
||||
buttonStyle: PropTypes.oneOf(['primary', 'secondary', 'error', undefined]),
|
||||
el: PropTypes.oneOf(['link', 'anchor', undefined]),
|
||||
to: PropTypes.string,
|
||||
url: PropTypes.string,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
margin-bottom: base(1);
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
|
||||
&--primary {
|
||||
|
||||
@@ -1,16 +1,31 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useReducer } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Pill from '../Pill';
|
||||
import Plus from '../../icons/Plus';
|
||||
import X from '../../icons/X';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'column-selector';
|
||||
|
||||
const reducer = (state, { type, column }) => {
|
||||
if (type === 'enable') {
|
||||
return [
|
||||
...state,
|
||||
column,
|
||||
];
|
||||
}
|
||||
|
||||
return state.filter(remainingColumn => remainingColumn !== column);
|
||||
};
|
||||
|
||||
const ColumnSelector = (props) => {
|
||||
const {
|
||||
handleChange,
|
||||
fields,
|
||||
} = props;
|
||||
|
||||
const [columns, setColumns] = useState('');
|
||||
const [columns, dispatchColumns] = useReducer(reducer, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof handleChange === 'function') handleChange(columns);
|
||||
@@ -18,19 +33,33 @@ const ColumnSelector = (props) => {
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
Columns
|
||||
{fields && fields.map((field) => {
|
||||
if (field?.hidden !== true || field?.hidden?.admin !== true) {
|
||||
const isEnabled = columns.find(column => column === field.name);
|
||||
return (
|
||||
<Pill
|
||||
onClick={() => dispatchColumns({ column: field.name, type: isEnabled ? 'disable' : 'enable' })}
|
||||
alignIcon="left"
|
||||
key={field.name}
|
||||
icon={isEnabled ? <X /> : <Plus />}
|
||||
pillStyle={isEnabled ? 'dark' : undefined}
|
||||
className={`${baseClass}__active-column`}
|
||||
>
|
||||
{field.label}
|
||||
</Pill>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ColumnSelector.defaultProps = {
|
||||
fieldName: 'id',
|
||||
fieldLabel: 'ID',
|
||||
};
|
||||
|
||||
ColumnSelector.propTypes = {
|
||||
fieldName: PropTypes.string,
|
||||
fieldLabel: PropTypes.string,
|
||||
fields: PropTypes.arrayOf(
|
||||
PropTypes.shape({}),
|
||||
).isRequired,
|
||||
handleChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@import '../../../scss/styles.scss';
|
||||
|
||||
.column-selector {
|
||||
background: $color-background-gray;
|
||||
padding: base(1) base(1) 0;
|
||||
|
||||
.pill {
|
||||
margin-right: base(1);
|
||||
margin-bottom: base(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ const ListControls = (props) => {
|
||||
}, [useAsTitle, fields]);
|
||||
|
||||
useEffect(() => {
|
||||
const newState = {};
|
||||
const newState = {
|
||||
columns,
|
||||
};
|
||||
|
||||
if (search) {
|
||||
newState.where = {
|
||||
@@ -61,7 +63,7 @@ const ListControls = (props) => {
|
||||
/>
|
||||
<Button
|
||||
className={`${baseClass}__toggle-columns`}
|
||||
type={visibleDrawer === 'columns' ? 'secondary' : undefined}
|
||||
buttonStyle={visibleDrawer === 'columns' ? 'secondary' : undefined}
|
||||
onClick={() => setVisibleDrawer(visibleDrawer !== 'columns' ? 'columns' : false)}
|
||||
icon={<Chevron />}
|
||||
>
|
||||
@@ -69,7 +71,7 @@ const ListControls = (props) => {
|
||||
</Button>
|
||||
<Button
|
||||
className={`${baseClass}__toggle-where`}
|
||||
type={visibleDrawer === 'where' ? 'secondary' : undefined}
|
||||
buttonStyle={visibleDrawer === 'where' ? 'secondary' : undefined}
|
||||
onClick={() => setVisibleDrawer(visibleDrawer !== 'where' ? 'where' : false)}
|
||||
icon={<Chevron />}
|
||||
>
|
||||
@@ -80,7 +82,10 @@ const ListControls = (props) => {
|
||||
className={`${baseClass}__columns`}
|
||||
height={visibleDrawer === 'columns' ? 'auto' : 0}
|
||||
>
|
||||
<ColumnSelector handleChange={setColumns} />
|
||||
<ColumnSelector
|
||||
fields={fields}
|
||||
handleChange={setColumns}
|
||||
/>
|
||||
</AnimateHeight>
|
||||
<AnimateHeight
|
||||
className={`${baseClass}__where`}
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.column-selector,
|
||||
.where-builder {
|
||||
margin-top: base(1);
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
.btn {
|
||||
margin: 0 0 0 base(.5);
|
||||
|
||||
@@ -6,39 +6,64 @@ import './index.scss';
|
||||
|
||||
const baseClass = 'pill';
|
||||
|
||||
const Pill = ({ children, className, to }) => {
|
||||
const Pill = ({
|
||||
children, className, to, icon, alignIcon, onClick, pillStyle,
|
||||
}) => {
|
||||
const classes = [
|
||||
baseClass,
|
||||
`${baseClass}--style-${pillStyle}`,
|
||||
className && className,
|
||||
to && `${baseClass}--has-link`,
|
||||
(to || onClick) && `${baseClass}--has-action`,
|
||||
icon && `${baseClass}--has-icon`,
|
||||
icon && `${baseClass}--align-icon-${alignIcon}`,
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link
|
||||
to={to}
|
||||
className={classes}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
let RenderedType = 'div';
|
||||
|
||||
if (onClick && !to) RenderedType = 'button';
|
||||
if (to) RenderedType = Link;
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<RenderedType
|
||||
className={classes}
|
||||
onClick={onClick}
|
||||
type={RenderedType === 'button' ? 'button' : undefined}
|
||||
to={to || undefined}
|
||||
>
|
||||
{(icon && alignIcon === 'left') && (
|
||||
<>
|
||||
{icon}
|
||||
</>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
{(icon && alignIcon === 'right') && (
|
||||
<>
|
||||
{icon}
|
||||
</>
|
||||
)}
|
||||
</RenderedType>
|
||||
);
|
||||
};
|
||||
|
||||
Pill.defaultProps = {
|
||||
children: undefined,
|
||||
className: '',
|
||||
to: undefined,
|
||||
icon: undefined,
|
||||
alignIcon: 'right',
|
||||
onClick: undefined,
|
||||
pillStyle: 'light',
|
||||
};
|
||||
|
||||
Pill.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
to: PropTypes.string,
|
||||
icon: PropTypes.node,
|
||||
alignIcon: PropTypes.oneOf(['left', 'right']),
|
||||
onClick: PropTypes.func,
|
||||
pillStyle: PropTypes.oneOf(['light', 'dark']),
|
||||
};
|
||||
|
||||
export default Pill;
|
||||
|
||||
@@ -1,15 +1,42 @@
|
||||
@import '../../../scss/styles.scss';
|
||||
|
||||
.pill {
|
||||
font-size: 1rem;
|
||||
line-height: base(1);
|
||||
border: 0;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
background: $color-light-gray;
|
||||
color: $color-dark-gray;
|
||||
border-radius: $style-radius-s;
|
||||
padding: 0 base(.25);
|
||||
padding-left: base(.0875 + .25);
|
||||
|
||||
&--has-link {
|
||||
text-decoration: none;
|
||||
&:active,
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&--has-action {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&--has-icon {
|
||||
svg {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&--align-icon-left {
|
||||
padding-left: base(.125);
|
||||
}
|
||||
|
||||
&--align-icon-right {
|
||||
padding-right: base(.125);
|
||||
}
|
||||
|
||||
&--style-light {
|
||||
&:hover {
|
||||
background: lighten($color-light-gray, 3%);
|
||||
}
|
||||
@@ -18,4 +45,21 @@
|
||||
background: lighten($color-light-gray, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
&--style-dark {
|
||||
background: $color-dark-gray;
|
||||
color: white;
|
||||
|
||||
svg {
|
||||
@include color-svg(white);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: lighten($color-dark-gray, 3%);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: lighten($color-dark-gray, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,34 +11,40 @@ const RenderFields = ({
|
||||
<>
|
||||
{fieldSchema.map((field, i) => {
|
||||
const { defaultValue } = field;
|
||||
let FieldComponent = field?.hidden?.admin ? fieldTypes.hidden : fieldTypes[field.type];
|
||||
if (customComponents?.[field.name]?.field) {
|
||||
FieldComponent = customComponents[field.name].field;
|
||||
}
|
||||
if (FieldComponent) {
|
||||
if (field?.hidden !== 'api' && field?.hidden !== true) {
|
||||
let FieldComponent = field?.hidden === 'admin' ? fieldTypes.hidden : fieldTypes[field.type];
|
||||
|
||||
if (customComponents?.[field.name]?.field) {
|
||||
FieldComponent = customComponents[field.name].field;
|
||||
}
|
||||
|
||||
if (FieldComponent) {
|
||||
return (
|
||||
<FieldComponent
|
||||
fieldTypes={fieldTypes}
|
||||
key={field.name}
|
||||
{...field}
|
||||
validate={field.validate ? value => field.validate(value, field) : undefined}
|
||||
defaultValue={initialData[field.name] || defaultValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FieldComponent
|
||||
fieldTypes={fieldTypes}
|
||||
key={field.name}
|
||||
{...field}
|
||||
validate={field.validate ? value => field.validate(value, field) : undefined}
|
||||
defaultValue={initialData[field.name] || defaultValue}
|
||||
/>
|
||||
<div
|
||||
className="missing-field"
|
||||
key={i}
|
||||
>
|
||||
No matched field found for
|
||||
{' '}
|
||||
"
|
||||
{field.label}
|
||||
"
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="missing-field"
|
||||
key={i}
|
||||
>
|
||||
No matched field found for
|
||||
{' '}
|
||||
"
|
||||
{field.label}
|
||||
"
|
||||
</div>
|
||||
);
|
||||
return null;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -133,7 +133,7 @@ const Repeater = (props) => {
|
||||
<div className={`${baseClass}__add-button-wrap`}>
|
||||
<Button
|
||||
onClick={() => addRow(rowCount)}
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
>
|
||||
{`Add ${singularLabel}`}
|
||||
</Button>
|
||||
|
||||
@@ -5,6 +5,8 @@ import './index.scss';
|
||||
const Plus = () => {
|
||||
return (
|
||||
<svg
|
||||
width="25"
|
||||
height="25"
|
||||
className="icon icon--plus"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 25 25"
|
||||
@@ -14,12 +16,14 @@ const Plus = () => {
|
||||
y1="16.9175"
|
||||
x2="12.4589"
|
||||
y2="8.50115"
|
||||
className="stroke"
|
||||
/>
|
||||
<line
|
||||
x1="8.05164"
|
||||
y1="12.594"
|
||||
x2="16.468"
|
||||
y2="12.594"
|
||||
className="stroke"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import '../../../scss/styles';
|
||||
|
||||
.icon--plus {
|
||||
path {
|
||||
.stroke {
|
||||
stroke: $color-dark-gray;
|
||||
stroke-width: $style-stroke-width-s;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import React from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const Close = () => {
|
||||
const X = () => {
|
||||
return (
|
||||
<svg
|
||||
width="25"
|
||||
height="25"
|
||||
className="icon icon--close"
|
||||
className="icon icon--x"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 25 25"
|
||||
>
|
||||
@@ -29,4 +29,4 @@ const Close = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Close;
|
||||
export default X;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import '../../../scss/styles';
|
||||
|
||||
.icon--close {
|
||||
.icon--x {
|
||||
line {
|
||||
stroke: $color-dark-gray;
|
||||
stroke-width: $style-stroke-width-s;
|
||||
|
||||
@@ -33,7 +33,7 @@ const StayLoggedInModal = (props) => {
|
||||
Stay logged in
|
||||
</Button>
|
||||
<Button
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
onClick={() => {
|
||||
closeAllModals();
|
||||
history.push(`${admin}/logout`);
|
||||
|
||||
@@ -52,7 +52,7 @@ const ForgotPassword = () => {
|
||||
<br />
|
||||
<Button
|
||||
el="link"
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
to={admin}
|
||||
>
|
||||
Back to Dashboard
|
||||
@@ -71,7 +71,7 @@ const ForgotPassword = () => {
|
||||
<br />
|
||||
<Button
|
||||
el="link"
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
to={`${admin}/login`}
|
||||
>
|
||||
Go to login
|
||||
|
||||
@@ -51,7 +51,7 @@ const Login = () => {
|
||||
<br />
|
||||
<Button
|
||||
el="link"
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
to={admin}
|
||||
>
|
||||
Back to Dashboard
|
||||
|
||||
@@ -23,7 +23,7 @@ const Logout = () => {
|
||||
<br />
|
||||
<Button
|
||||
el="anchor"
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
url={`${admin}/login`}
|
||||
>
|
||||
Log back in
|
||||
|
||||
@@ -44,7 +44,7 @@ const ResetPassword = () => {
|
||||
<br />
|
||||
<Button
|
||||
el="link"
|
||||
type="secondary"
|
||||
buttonStyle="secondary"
|
||||
to={admin}
|
||||
>
|
||||
Back to Dashboard
|
||||
|
||||
@@ -8,6 +8,7 @@ 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 './index.scss';
|
||||
|
||||
@@ -21,6 +22,7 @@ const DefaultList = (props) => {
|
||||
collection: {
|
||||
slug,
|
||||
labels: {
|
||||
singular: singularLabel,
|
||||
plural: pluralLabel,
|
||||
},
|
||||
},
|
||||
@@ -51,7 +53,7 @@ const DefaultList = (props) => {
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h1>{pluralLabel}</h1>
|
||||
<Pill to={newDocumentURL}>
|
||||
Add New
|
||||
Create New
|
||||
</Pill>
|
||||
</header>
|
||||
<ListControls
|
||||
@@ -73,11 +75,25 @@ const DefaultList = (props) => {
|
||||
)}
|
||||
{(!data.docs || data.docs.length === 0) && (
|
||||
<div className={`${baseClass}__no-results`}>
|
||||
No
|
||||
{' '}
|
||||
{pluralLabel}
|
||||
{' '}
|
||||
found
|
||||
<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>
|
||||
)}
|
||||
<Paginator
|
||||
@@ -98,6 +114,7 @@ const DefaultList = (props) => {
|
||||
DefaultList.propTypes = {
|
||||
collection: PropTypes.shape({
|
||||
labels: PropTypes.shape({
|
||||
singular: PropTypes.string,
|
||||
plural: PropTypes.string,
|
||||
}),
|
||||
slug: PropTypes.string,
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
margin-bottom: $baseline;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
flex-wrap: wrap;
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
margin: 0 base(.75) 0 0;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -20,9 +21,7 @@
|
||||
}
|
||||
|
||||
.pill {
|
||||
margin-left: base(.75);
|
||||
position: relative;
|
||||
top: - base(.25);
|
||||
margin: base(.5) 0 base(.25);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +34,7 @@
|
||||
|
||||
&__header {
|
||||
.pill {
|
||||
top: - base(.0625);
|
||||
margin-bottom: base(.0625);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ h5 {
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 base(.5);
|
||||
margin: 0 0 $baseline;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
||||
@@ -36,7 +36,7 @@ $font-body : 'Suisse Intl';
|
||||
|
||||
$color-dark-gray : #333333;
|
||||
$color-gray : #9A9A9A;
|
||||
$color-light-gray : #E7E7E7;
|
||||
$color-light-gray : #DADADA;
|
||||
$color-background-gray : #F3F3F3;
|
||||
$color-red : #ff6f76;
|
||||
$color-yellow : #FDFFA4;
|
||||
|
||||
@@ -2,11 +2,10 @@ const { Schema } = require('mongoose');
|
||||
|
||||
const formatBaseSchema = (field) => {
|
||||
return {
|
||||
hidden: field.hidden || false,
|
||||
hide: field.hidden === 'api' || field.hidden === true,
|
||||
localized: field.localized || false,
|
||||
unique: field.unique || false,
|
||||
required: (field.required && !field.localized) || false,
|
||||
required: (field.required && !field.localized && !field.hidden) || false,
|
||||
default: field.defaultValue || undefined,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user