scaffolds RenderFields, moves Init into Routes
This commit is contained in:
@@ -15,7 +15,7 @@ const requests = {
|
||||
headers: {
|
||||
...setJWT()
|
||||
}
|
||||
}).then(res => res.body);
|
||||
});
|
||||
},
|
||||
|
||||
post: (url, body) =>
|
||||
@@ -25,7 +25,7 @@ const requests = {
|
||||
headers: {
|
||||
...setJWT()
|
||||
},
|
||||
}).then(res => res.body),
|
||||
}),
|
||||
|
||||
put: (url, body) =>
|
||||
fetch(`${url}`, {
|
||||
@@ -34,7 +34,7 @@ const requests = {
|
||||
headers: {
|
||||
...setJWT()
|
||||
},
|
||||
}).then(res => res.body),
|
||||
}),
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Cookies from 'universal-cookie';
|
||||
import {
|
||||
Route, Switch, withRouter, Redirect,
|
||||
@@ -7,102 +7,127 @@ import config from 'payload-config';
|
||||
import DefaultTemplate from './layout/DefaultTemplate';
|
||||
import Dashboard from './views/Dashboard';
|
||||
import Login from './views/Login';
|
||||
import CreateFirstUser from './views/CreateFirstUser';
|
||||
import CreateUser from './views/CreateUser';
|
||||
import MediaLibrary from './views/MediaLibrary';
|
||||
import Edit from './views/collections/Edit';
|
||||
import List from './views/collections/List';
|
||||
import { requests } from '../api';
|
||||
|
||||
const cookies = new Cookies();
|
||||
|
||||
const Routes = () => {
|
||||
const [initialized, setInitialized] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
requests.get('/init').then(res => res.json().then((data) => {
|
||||
if (data && 'initialized' in data) {
|
||||
setInitialized(data.initialized);
|
||||
}
|
||||
}));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Route
|
||||
path="/admin"
|
||||
render={({ match }) => {
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
path={`${match.url}/login`}
|
||||
render={routeProps => <Login {...routeProps} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/forgot`}
|
||||
component={() => { return <h1>Forgot Password</h1>; }}
|
||||
/>
|
||||
<Route
|
||||
render={() => {
|
||||
if (cookies.get('token')) {
|
||||
return (
|
||||
<DefaultTemplate>
|
||||
<Route
|
||||
path={`${match.url}/media-library`}
|
||||
component={MediaLibrary}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/create-user`}
|
||||
component={CreateUser}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/`}
|
||||
exact
|
||||
component={Dashboard}
|
||||
/>
|
||||
if (initialized === false) {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={`${match.url}/create-first-user`}>
|
||||
<CreateFirstUser />
|
||||
</Route>
|
||||
<Route>
|
||||
<Redirect to="/admin/create-first-user" />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
} if (initialized === true) {
|
||||
return (
|
||||
<Switch>
|
||||
<Route path={`${match.url}/login`}>
|
||||
<Login />
|
||||
</Route>
|
||||
<Route path={`${match.url}/forgot`}>
|
||||
<h1>Forgot Password</h1>
|
||||
</Route>
|
||||
<Route
|
||||
render={() => {
|
||||
if (cookies.get('token')) {
|
||||
return (
|
||||
<DefaultTemplate>
|
||||
<Route
|
||||
path={`${match.url}/media-library`}
|
||||
component={MediaLibrary}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/create-user`}
|
||||
component={CreateUser}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/`}
|
||||
exact
|
||||
component={Dashboard}
|
||||
/>
|
||||
|
||||
{config.collections.map((collection) => {
|
||||
const components = collection.components ? collection.components : {};
|
||||
return (
|
||||
<Switch key={collection.slug}>
|
||||
<Route
|
||||
path={`${match.url}/collections/${collection.slug}/create`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
return (
|
||||
<Edit
|
||||
{...routeProps}
|
||||
collection={collection}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{config.collections.map((collection) => {
|
||||
const components = collection.components ? collection.components : {};
|
||||
return (
|
||||
<Switch key={collection.slug}>
|
||||
<Route
|
||||
path={`${match.url}/collections/${collection.slug}/create`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
return (
|
||||
<Edit
|
||||
{...routeProps}
|
||||
collection={collection}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={`${match.url}/collections/${collection.slug}/:id`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
return (
|
||||
<Edit
|
||||
{...routeProps}
|
||||
collection={collection}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/collections/${collection.slug}/:id`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
return (
|
||||
<Edit
|
||||
{...routeProps}
|
||||
collection={collection}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path={`${match.url}/collections/${collection.slug}`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
const ListComponent = components.List ? components.List : List;
|
||||
return (
|
||||
<ListComponent
|
||||
{...routeProps}
|
||||
collection={collection}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={`${match.url}/collections/${collection.slug}`}
|
||||
exact
|
||||
render={(routeProps) => {
|
||||
const ListComponent = components.List ? components.List : List;
|
||||
return (
|
||||
<ListComponent
|
||||
{...routeProps}
|
||||
collection={collection}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
</Switch>
|
||||
);
|
||||
})}
|
||||
</DefaultTemplate>
|
||||
);
|
||||
}
|
||||
return <Redirect to="/admin/login" />;
|
||||
}}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
</Switch>
|
||||
);
|
||||
})}
|
||||
</DefaultTemplate>
|
||||
);
|
||||
}
|
||||
return <Redirect to="/admin/login" />;
|
||||
}}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
21
src/client/components/forms/RenderFields/index.js
Normal file
21
src/client/components/forms/RenderFields/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import fieldTypes from '../field-types';
|
||||
|
||||
const RenderFields = ({ fields }) => {
|
||||
if (fields) {
|
||||
return fields.map((field, i) => {
|
||||
return field.name + i;
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
RenderFields.propTypes = {
|
||||
fields: PropTypes.arrayOf(
|
||||
PropTypes.shape({}),
|
||||
).isRequired,
|
||||
};
|
||||
|
||||
export default RenderFields;
|
||||
@@ -1,32 +1,28 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import FormContext from '../Form/Context';
|
||||
import Button from '../../controls/Button';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
class FormSubmit extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="form-submit">
|
||||
<Button disabled={this.props.context.processing ? 'disabled' : ''}>
|
||||
{this.props.children}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
const baseClass = 'form-submit';
|
||||
|
||||
const ContextFormSubmit = (props) => {
|
||||
const FormSubmit = ({ children }) => {
|
||||
const formContext = useContext(FormContext);
|
||||
return (
|
||||
<FormContext.Consumer>
|
||||
{context => (
|
||||
<FormSubmit
|
||||
{...props}
|
||||
context={context}
|
||||
/>
|
||||
)}
|
||||
</FormContext.Consumer>
|
||||
<div className={baseClass}>
|
||||
<Button disabled={formContext.processing ? 'disabled' : ''}>
|
||||
{children}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContextFormSubmit;
|
||||
FormSubmit.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
export default FormSubmit;
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Section } from 'payload/components';
|
||||
import PropTypes from 'prop-types';
|
||||
import Section from '../../../layout/Section';
|
||||
|
||||
const Group = props => {
|
||||
const Group = ({ label, children }) => {
|
||||
return (
|
||||
<Section heading={props.label} className="field-group">
|
||||
{props.children}
|
||||
<Section
|
||||
heading={label}
|
||||
className="field-group"
|
||||
>
|
||||
{children}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
Group.defaultProps = {
|
||||
label: '',
|
||||
};
|
||||
|
||||
Group.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
label: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Group;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { fieldType } from 'payload/components';
|
||||
import fieldType from '../fieldType';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import { fieldType, UploadMedia } from 'payload/components';
|
||||
import fieldType from '../fieldType';
|
||||
import UploadMedia from '../../../modules/UploadMedia';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Section } from 'payload/components';
|
||||
import Section from '../../../layout/Section';
|
||||
|
||||
class Repeater extends Component {
|
||||
constructor(props) {
|
||||
|
||||
@@ -1,28 +1,70 @@
|
||||
import React from 'react';
|
||||
import { fieldType } from 'payload/components';
|
||||
import PropTypes from 'prop-types';
|
||||
import fieldType from '../fieldType';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const error = 'Please fill in the textarea';
|
||||
const errorMessage = 'Please fill in the textarea';
|
||||
|
||||
const validate = value => value.length > 0;
|
||||
|
||||
const Textarea = props => {
|
||||
const Textarea = (props) => {
|
||||
const {
|
||||
className,
|
||||
style,
|
||||
error,
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
placeholder,
|
||||
id,
|
||||
name,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className={props.className} style={props.style}>
|
||||
{props.error}
|
||||
{props.label}
|
||||
<div
|
||||
className={className}
|
||||
style={style}
|
||||
>
|
||||
{error}
|
||||
{label}
|
||||
<textarea
|
||||
value={props.value || ''}
|
||||
onChange={props.onChange}
|
||||
disabled={props.disabled}
|
||||
placeholder={props.placeholder}
|
||||
type={props.type}
|
||||
id={props.id ? props.id : props.name}
|
||||
name={props.name}>
|
||||
</textarea>
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
placeholder={placeholder}
|
||||
id={id || name}
|
||||
name={name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default fieldType(Textarea, 'textarea', validate, error);
|
||||
Textarea.defaultProps = {
|
||||
className: null,
|
||||
style: {},
|
||||
error: null,
|
||||
label: null,
|
||||
value: '',
|
||||
onChange: null,
|
||||
disabled: null,
|
||||
placeholder: null,
|
||||
id: null,
|
||||
name: 'textarea',
|
||||
};
|
||||
|
||||
Textarea.propTypes = {
|
||||
className: PropTypes.string,
|
||||
style: PropTypes.shape({}),
|
||||
error: PropTypes.node,
|
||||
label: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
};
|
||||
|
||||
export default fieldType(Textarea, 'textarea', validate, errorMessage);
|
||||
|
||||
8
src/client/components/forms/field-types/index.js
Normal file
8
src/client/components/forms/field-types/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export { default as Email } from './Email';
|
||||
export { default as Group } from './Group';
|
||||
export { default as HiddenInput } from './HiddenInput';
|
||||
export { default as Input } from './Input';
|
||||
export { default as Media } from './Media';
|
||||
export { default as Password } from './Password';
|
||||
export { default as Repeater } from './Repeater';
|
||||
export { default as Textarea } from './Textarea';
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import Init from './utilities/Init';
|
||||
import { SearchParamsProvider } from './utilities/SearchParams';
|
||||
import { LocaleProvider } from './utilities/Locale';
|
||||
import { StatusListProvider } from './modules/Status';
|
||||
@@ -11,17 +10,15 @@ import '../scss/app.scss';
|
||||
|
||||
const Index = () => {
|
||||
return (
|
||||
<Init>
|
||||
<Router>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StatusListProvider>
|
||||
<Routes />
|
||||
</StatusListProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
</Router>
|
||||
</Init>
|
||||
<Router>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StatusListProvider>
|
||||
<Routes />
|
||||
</StatusListProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { Component } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { connect } from 'react-redux';
|
||||
import { Button } from 'payload/components';
|
||||
import api from 'payload/api';
|
||||
import Button from '../../controls/Button';
|
||||
import api from '../../../api';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Init = ({ children }) => {
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
|
||||
if (initialized) {
|
||||
return (
|
||||
{ children }
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<h1>Not initialized</h1>
|
||||
);
|
||||
};
|
||||
|
||||
Init.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]).isRequired,
|
||||
};
|
||||
|
||||
|
||||
export default Init;
|
||||
38
src/client/components/views/CreateFirstUser/index.js
Normal file
38
src/client/components/views/CreateFirstUser/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
import Cookies from 'universal-cookie';
|
||||
import config from 'payload-config';
|
||||
import ContentBlock from '../../layout/ContentBlock';
|
||||
import Form from '../../forms/Form';
|
||||
import RenderFields from '../../forms/RenderFields';
|
||||
import FormSubmit from '../../forms/Submit';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const cookies = new Cookies();
|
||||
|
||||
const handleAjaxResponse = (res) => {
|
||||
cookies.set('token', res.token, { path: '/' });
|
||||
};
|
||||
|
||||
const baseClass = 'create-first-user';
|
||||
|
||||
const CreateFirstUser = () => {
|
||||
return (
|
||||
<ContentBlock className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
<h1>Welcome to Payload</h1>
|
||||
<p>To begin, create your first user.</p>
|
||||
<Form
|
||||
handleAjaxResponse={handleAjaxResponse}
|
||||
method="POST"
|
||||
action="/first-user"
|
||||
>
|
||||
<RenderFields fields={config.user.fields} />
|
||||
<FormSubmit>Create</FormSubmit>
|
||||
</Form>
|
||||
</div>
|
||||
</ContentBlock>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateFirstUser;
|
||||
16
src/client/components/views/CreateFirstUser/index.scss
Normal file
16
src/client/components/views/CreateFirstUser/index.scss
Normal file
@@ -0,0 +1,16 @@
|
||||
@import '../../../scss/styles';
|
||||
|
||||
.create-first-user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
min-height: 100vh;
|
||||
|
||||
&__wrap {
|
||||
margin: 0 auto base(1);
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,15 @@ const handleAjaxResponse = (res) => {
|
||||
cookies.set('token', res.token, { path: '/' });
|
||||
};
|
||||
|
||||
const baseClass = 'login';
|
||||
|
||||
const Login = () => {
|
||||
return (
|
||||
<ContentBlock
|
||||
className="login"
|
||||
className={baseClass}
|
||||
width="narrow"
|
||||
>
|
||||
<div className="wrap">
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
<Form
|
||||
handleAjaxResponse={handleAjaxResponse}
|
||||
method="POST"
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
flex-wrap: wrap;
|
||||
min-height: 100vh;
|
||||
|
||||
.logo-wrap {
|
||||
&__wrap {
|
||||
margin: 0 auto base(1);
|
||||
max-width: 200px;
|
||||
|
||||
svg {
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user