diff --git a/demo/client/components/layout/Sidebar/index.js b/demo/client/components/layout/Sidebar/index.js
new file mode 100644
index 000000000..a33c1ace9
--- /dev/null
+++ b/demo/client/components/layout/Sidebar/index.js
@@ -0,0 +1,5 @@
+import React from 'react';
+
+const Sidebar = () =>
fake sidebar
+
+export default Sidebar;
diff --git a/demo/client/scss/overrides.scss b/demo/client/scss/overrides.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/demo/collections/Category.js b/demo/collections/Category.js
new file mode 100644
index 000000000..8cc0d01ae
--- /dev/null
+++ b/demo/collections/Category.js
@@ -0,0 +1,85 @@
+const checkRole = require('../policies/checkRole');
+
+module.exports = {
+ slug: 'categories',
+ labels: {
+ singular: 'Category',
+ plural: 'Categories',
+ },
+ useAsTitle: 'title',
+ policies: {
+ create: user => checkRole(['user', 'admin'], user),
+ read: () => true,
+ update: user => checkRole(['user', 'admin'], user),
+ destroy: user => checkRole(['user', 'admin'], user),
+ },
+ fields: [
+ {
+ name: 'title',
+ label: 'Title',
+ type: 'text',
+ maxLength: 100,
+ required: true,
+ unique: true,
+ localized: true,
+ },
+ {
+ name: 'description',
+ label: 'Description',
+ type: 'textarea',
+ height: 100,
+ required: true,
+ localized: true,
+ },
+ {
+ name: 'post',
+ label: 'Post',
+ type: 'relationship',
+ relationTo: ['posts', 'categories'],
+ localized: false,
+ hasMany: false,
+ },
+ {
+ name: 'demoSelect',
+ label: 'Demo Select',
+ type: 'select',
+ options: [{
+ value: 'Option 1',
+ label: 'Here is a label for Option 1',
+ }, {
+ value: 'Option 2',
+ label: 'Option 2 Label',
+ }, {
+ value: 'Option 3',
+ label: 'Option 3 Label',
+ }, {
+ value: 'Option 4',
+ label: 'Option 4 Label',
+ }],
+ defaultValue: 'Option 1',
+ required: true,
+ },
+ {
+ name: 'demoSelectMany',
+ label: 'Demo Select w/ hasMany',
+ type: 'select',
+ options: [{
+ value: 'Option 1',
+ label: 'Here is a label for Option 1',
+ }, {
+ value: 'Option 2',
+ label: 'Option 2 Label',
+ }, {
+ value: 'Option 3',
+ label: 'Option 3 Label',
+ }, {
+ value: 'Option 4',
+ label: 'Option 4 Label',
+ }],
+ defaultValue: 'Option 1',
+ required: true,
+ hasMany: true,
+ },
+ ],
+ timestamps: true,
+};
diff --git a/demo/collections/Layout/index.js b/demo/collections/Layout/index.js
new file mode 100644
index 000000000..fbace1c83
--- /dev/null
+++ b/demo/collections/Layout/index.js
@@ -0,0 +1,42 @@
+const checkRole = require('../../policies/checkRole');
+const Email = require('../../content-blocks/Email');
+const Quote = require('../../content-blocks/Quote');
+const NumberBlock = require('../../content-blocks/Number');
+
+module.exports = {
+ slug: 'layouts',
+ labels: {
+ singular: 'Layout',
+ plural: 'Layouts',
+ },
+ useAsTitle: 'title',
+ policies: {
+ // options: create, read, update, delete
+ // null or undefined policies will default to requiring auth
+ // any policy can use req.user to see that the user is logged
+ create: null,
+ read: () => true,
+ update: user => checkRole(['user', 'admin'], user),
+ destroy: user => checkRole(['user', 'admin'], user),
+ },
+ fields: [
+ {
+ name: 'title',
+ label: 'Page Title',
+ type: 'text',
+ unique: true,
+ localized: true,
+ maxLength: 100,
+ required: true,
+ },
+ {
+ name: 'layout',
+ label: 'Layout Blocks',
+ singularLabel: 'Block',
+ type: 'flexible',
+ blocks: [Email, NumberBlock, Quote],
+ localized: true,
+ },
+ ],
+ timestamps: true,
+};
diff --git a/demo/collections/Page/index.js b/demo/collections/Page/index.js
index 5a120d23f..86e2f06f9 100644
--- a/demo/collections/Page/index.js
+++ b/demo/collections/Page/index.js
@@ -1,126 +1,161 @@
+const path = require('path');
const checkRole = require('../../policies/checkRole');
const Quote = require('../../content-blocks/Quote');
const CallToAction = require('../../content-blocks/CallToAction');
module.exports = {
- slug: 'pages',
- labels: {
- singular: 'Page',
- plural: 'Pages',
- },
- useAsTitle: 'title',
- policies: {
- // options: create, read, update, delete
- // null or undefined policies will default to requiring auth
- // any policy can use req.user to see that the user is logged
- create: null,
- read: () => true,
- update: user => checkRole(['user', 'admin'], user),
- destroy: user => checkRole(['user', 'admin'], user),
- },
- fields: [
- {
- name: 'title',
- label: 'Page Title',
- type: 'text',
- unique: true,
- localized: true,
- maxLength: 100,
- required: true,
- },
- {
- name: 'content',
- label: 'Content',
- type: 'textarea',
- localized: true,
- height: 100,
- required: true,
- },
- // {
- // name: 'category',
- // label: 'Localized Category',
- // type: 'relationship',
- // relationType: 'reference',
- // relationTo: 'categories',
- // hasMany: false,
- // localized: true,
- // },
- {
- name: 'categories',
- label: 'Categories hasMany',
- type: 'relationship',
- relationType: 'reference',
- relationTo: 'categories',
- hasMany: true,
- localized: true,
- },
- {
- name: 'nonLocalizedCategory',
- label: 'Non-Localized Category',
- type: 'relationship',
- relationType: 'reference',
- relationTo: 'categories',
- hasMany: false,
- localized: false,
- },
- {
- name: 'image',
- label: 'Image',
- type: 'upload',
- required: false,
- },
- {
- name: 'slides',
- label: 'Slides',
- type: 'repeater',
- id: false,
- fields: [
- {
- name: 'title',
- type: 'text',
- label: 'Title',
- },
- {
- name: 'description',
- type: 'textarea',
- label: 'Description',
- },
- ],
- },
- {
- name: 'layout',
- label: 'Layout Blocks',
- type: 'flexible',
- blocks: [Quote, CallToAction],
- localized: true,
- },
- {
- label: 'Meta Information',
- type: 'group',
- name: 'meta',
- fields: [
- {
- name: 'title',
- type: 'text',
- maxLength: 100,
- label: 'Title',
- width: 50,
- },
- {
- name: 'keywords',
- type: 'text',
- maxLength: 100,
- label: 'Keywords',
- width: 50,
- },
- {
- name: 'desc',
- type: 'textarea',
- label: 'Description',
- height: 100,
- },
- ],
- },
- ],
- timestamps: true,
+ slug: 'pages',
+ labels: {
+ singular: 'Page',
+ plural: 'Pages',
+ },
+ useAsTitle: 'title',
+ policies: {
+ // options: create, read, update, delete
+ // null or undefined policies will default to requiring auth
+ // any policy can use req.user to see that the user is logged
+ create: null,
+ read: () => true,
+ update: user => checkRole(['user', 'admin'], user),
+ destroy: user => checkRole(['user', 'admin'], user),
+ },
+ fields: [
+ {
+ name: 'title',
+ label: 'Page Title',
+ type: 'text',
+ unique: true,
+ localized: true,
+ maxLength: 100,
+ required: true,
+ },
+ {
+ name: 'content',
+ label: 'Content',
+ type: 'textarea',
+ localized: true,
+ height: 100,
+ required: true,
+ },
+ {
+ name: 'category',
+ label: 'Localized Category',
+ type: 'relationship',
+ relationType: 'reference',
+ relationTo: 'categories',
+ hasMany: false,
+ localized: true,
+ },
+ {
+ name: 'categories',
+ label: 'Categories hasMany',
+ type: 'relationship',
+ relationType: 'reference',
+ relationTo: 'categories',
+ hasMany: true,
+ localized: true,
+ },
+ {
+ name: 'nonLocalizedCategory',
+ label: 'Non-Localized Category',
+ type: 'relationship',
+ relationType: 'reference',
+ relationTo: 'categories',
+ hasMany: false,
+ localized: false,
+ },
+ {
+ name: 'image',
+ label: 'Image',
+ type: 'upload',
+ required: false,
+ },
+ {
+ name: 'otherDate',
+ type: 'date',
+ label: 'Example Start Date',
+ width: 50,
+ useTime: true,
+ },
+ {
+ name: 'slides',
+ label: 'Slides',
+ singularLabel: 'Slide',
+ type: 'repeater',
+ fields: [
+ {
+ name: 'cards',
+ label: 'Cards',
+ singularLabel: 'Card',
+ type: 'repeater',
+ fields: [
+ {
+ name: 'cardNumber',
+ type: 'text',
+ label: 'Card Number',
+ },
+ {
+ name: 'description',
+ type: 'textarea',
+ label: 'Description',
+ condition: (fields, siblings) => {
+ return (siblings.cardNumber && siblings.cardNumber.value);
+ },
+ },
+ ],
+ },
+ {
+ name: 'title',
+ type: 'text',
+ label: 'Title',
+ },
+ {
+ name: 'description',
+ type: 'textarea',
+ label: 'Description',
+ },
+ ],
+ },
+ {
+ name: 'layout',
+ label: 'Layout Blocks',
+ singularLabel: 'Block',
+ type: 'flexible',
+ blocks: [Quote, CallToAction],
+ localized: true,
+ },
+ {
+ label: 'Meta Information',
+ type: 'group',
+ name: 'meta',
+ fields: [
+ {
+ name: 'title',
+ type: 'text',
+ maxLength: 100,
+ label: 'Title',
+ width: 50,
+ },
+ {
+ name: 'keywords',
+ type: 'text',
+ maxLength: 100,
+ label: 'Keywords',
+ width: 50,
+ },
+ {
+ name: 'desc',
+ type: 'textarea',
+ label: 'Description',
+ height: 100,
+ },
+ ],
+ },
+ ],
+ // components: {
+ // views: {
+ // List: path.resolve(__dirname, 'components/List/index.js'),
+ // }
+ // },
+ timestamps: true,
};
diff --git a/demo/collections/Post/components/fields/Description/Cell/index.js b/demo/collections/Post/components/fields/Description/Cell/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/demo/collections/Post/components/fields/Description/Field/index.js b/demo/collections/Post/components/fields/Description/Field/index.js
new file mode 100644
index 000000000..3323a4a9d
--- /dev/null
+++ b/demo/collections/Post/components/fields/Description/Field/index.js
@@ -0,0 +1,5 @@
+import React from 'react';
+
+const Description = () => fake description field
+
+export default Description;
diff --git a/demo/collections/Post/index.js b/demo/collections/Post/index.js
new file mode 100644
index 000000000..604a85697
--- /dev/null
+++ b/demo/collections/Post/index.js
@@ -0,0 +1,54 @@
+const checkRole = require('../../policies/checkRole');
+const path = require('path');
+
+module.exports = {
+ slug: 'posts',
+ labels: {
+ singular: 'Post',
+ plural: 'Posts',
+ },
+ useAsTitle: 'title',
+ policies: {
+ create: user => checkRole(['user', 'admin'], user),
+ read: () => true,
+ update: user => checkRole(['user', 'admin'], user),
+ destroy: user => checkRole(['user', 'admin'], user),
+ },
+ fields: [
+ {
+ name: 'title',
+ label: 'Title',
+ type: 'text',
+ maxLength: 100,
+ required: true,
+ unique: true,
+ localized: true,
+ },
+ {
+ name: 'description',
+ label: 'Description',
+ type: 'textarea',
+ height: 100,
+ required: true,
+ localized: true,
+ components: {
+ field: path.resolve(__dirname, 'components/fields/Description/Field/index.js'),
+ cell: path.resolve(__dirname, 'components/fields/Description/Cell/index.js')
+ },
+ },
+ {
+ name: 'startDate',
+ label: 'Example Start Date',
+ type: 'date',
+ placeholder: 'test',
+ width: 50,
+ },
+ {
+ name: 'priority',
+ label: 'Priority',
+ type: 'number',
+ required: true,
+ },
+ ],
+ timestamps: true,
+};
diff --git a/demo/collections/Upload.js b/demo/collections/Upload.js
new file mode 100644
index 000000000..1b132499a
--- /dev/null
+++ b/demo/collections/Upload.js
@@ -0,0 +1,19 @@
+const checkRole = require('../policies/checkRole');
+
+module.exports = {
+ slug: 'uploads',
+ labels: {
+ singular: 'Upload',
+ plural: 'Uploads',
+ },
+ useAsTitle: 'filename',
+ policies: {
+ create: user => checkRole(['user', 'admin'], user),
+ read: user => checkRole(['user', 'admin'], user),
+ update: user => checkRole(['user', 'admin'], user),
+ destroy: user => checkRole(['user', 'admin'], user),
+ },
+ fields: [
+ ],
+ timestamps: true,
+};
diff --git a/demo/collections/User.js b/demo/collections/User.js
new file mode 100644
index 000000000..c190e05f0
--- /dev/null
+++ b/demo/collections/User.js
@@ -0,0 +1,43 @@
+const roles = require('../policies/roles');
+const checkRole = require('../policies/checkRole');
+
+module.exports = {
+ slug: 'users',
+ labels: {
+ singular: 'User',
+ plural: 'Users',
+ },
+ useAsTitle: 'email',
+ policies: {
+ create: user => checkRole(['admin', 'user'], user),
+ read: null,
+ update: user => checkRole(['admin', 'user'], user),
+ destroy: user => checkRole(['admin', 'user'], user),
+ },
+ auth: {
+ passwordIndex: 1,
+ useAsUsername: 'email',
+ tokenExpiration: 7200,
+ secretKey: 'SECRET_KEY',
+ },
+ fields: [
+ {
+ name: 'email',
+ label: 'Email Address',
+ type: 'input',
+ unique: true,
+ maxLength: 100,
+ required: true,
+ },
+ {
+ name: 'role',
+ label: 'Role',
+ type: 'select',
+ options: roles,
+ defaultValue: 'user',
+ required: true,
+ saveToJWT: true,
+ },
+ ],
+ timestamps: true,
+};
diff --git a/demo/content-blocks/CallToAction.js b/demo/content-blocks/CallToAction.js
new file mode 100644
index 000000000..3cd21d18a
--- /dev/null
+++ b/demo/content-blocks/CallToAction.js
@@ -0,0 +1,23 @@
+module.exports = {
+ slug: 'cta',
+ labels: {
+ singular: 'Call to Action',
+ plural: 'Calls to Action',
+ },
+ fields: [
+ {
+ name: 'label',
+ label: 'Label',
+ type: 'text',
+ maxLength: 100,
+ required: true,
+ },
+ {
+ name: 'url',
+ label: 'URL',
+ type: 'text',
+ height: 100,
+ required: true,
+ },
+ ],
+};
diff --git a/demo/content-blocks/Email.js b/demo/content-blocks/Email.js
new file mode 100644
index 000000000..538a6b5b6
--- /dev/null
+++ b/demo/content-blocks/Email.js
@@ -0,0 +1,16 @@
+module.exports = {
+ slug: 'email',
+ labels: {
+ singular: 'Email',
+ plural: 'Emails',
+ },
+ fields: [
+ {
+ name: 'testEmail',
+ label: 'Test Email Field',
+ type: 'email',
+ maxLength: 100,
+ required: true,
+ },
+ ],
+};
diff --git a/demo/content-blocks/Number.js b/demo/content-blocks/Number.js
new file mode 100644
index 000000000..6823fbbd9
--- /dev/null
+++ b/demo/content-blocks/Number.js
@@ -0,0 +1,16 @@
+module.exports = {
+ slug: 'number',
+ labels: {
+ singular: 'Number',
+ plural: 'Numbers',
+ },
+ fields: [
+ {
+ name: 'testNumber',
+ label: 'Test Number Field',
+ type: 'number',
+ maxLength: 100,
+ required: true,
+ },
+ ],
+};
diff --git a/demo/content-blocks/Quote.js b/demo/content-blocks/Quote.js
new file mode 100644
index 000000000..bedf28508
--- /dev/null
+++ b/demo/content-blocks/Quote.js
@@ -0,0 +1,30 @@
+module.exports = {
+ slug: 'quote',
+ labels: {
+ singular: 'Quote',
+ plural: 'Quotes',
+ },
+ fields: [
+ {
+ name: 'author',
+ label: 'Author',
+ type: 'text',
+ maxLength: 100,
+ required: true,
+ },
+ {
+ name: 'quote',
+ label: 'Quote',
+ type: 'textarea',
+ height: 100,
+ required: true,
+ },
+ {
+ name: 'color',
+ label: 'Color',
+ type: 'text',
+ maxLength: 7,
+ required: true,
+ },
+ ],
+};
diff --git a/demo/globals/Footer.js b/demo/globals/Footer.js
new file mode 100644
index 000000000..aed765a70
--- /dev/null
+++ b/demo/globals/Footer.js
@@ -0,0 +1,20 @@
+const checkRole = require('../policies/checkRole');
+
+module.exports = {
+ slug: 'footer',
+ label: 'Footer',
+ policies: {
+ create: user => checkRole(['admin', 'user'], user),
+ read: () => true,
+ update: user => checkRole(['admin', 'user'], user),
+ destroy: user => checkRole(['admin', 'user'], user),
+ },
+ fields: [
+ {
+ name: 'copyright',
+ label: 'Copyright',
+ type: 'text',
+ localized: true,
+ },
+ ],
+};
diff --git a/demo/globals/Header.js b/demo/globals/Header.js
new file mode 100644
index 000000000..3ecef0416
--- /dev/null
+++ b/demo/globals/Header.js
@@ -0,0 +1,37 @@
+const checkRole = require('../policies/checkRole');
+const Quote = require('../content-blocks/Quote');
+const CallToAction = require('../content-blocks/CallToAction');
+
+module.exports = {
+ slug: 'header',
+ label: 'Header',
+ policies: {
+ create: user => checkRole(['admin', 'user'], user),
+ read: () => true,
+ update: user => checkRole(['admin', 'user'], user),
+ destroy: user => checkRole(['admin', 'user'], user),
+ },
+ fields: [
+ {
+ name: 'title',
+ label: 'Site Title',
+ type: 'text',
+ localized: true,
+ maxLength: 100,
+ required: true,
+ },
+ {
+ name: 'logo',
+ label: 'Logo',
+ type: 'upload',
+ required: false,
+ },
+ {
+ name: 'flexibleGlobal',
+ label: 'Global Flexible Block',
+ type: 'flexible',
+ blocks: [Quote, CallToAction],
+ localized: true,
+ },
+ ],
+};
diff --git a/demo/payload.config.js b/demo/payload.config.js
index 62c09f648..90406906e 100644
--- a/demo/payload.config.js
+++ b/demo/payload.config.js
@@ -2,6 +2,7 @@ const path = require('path');
const Page = require('./collections/Page');
const Category = require('./collections/Category');
const Post = require('./collections/Post');
+const Layout = require('./collections/Layout');
const User = require('./collections/User');
const Upload = require('./collections/Upload');
const Header = require('./globals/Header');
@@ -9,7 +10,7 @@ const Footer = require('./globals/Footer');
module.exports = {
// disableAdmin: true,
- collections: [Page, Category, Post],
+ collections: [Page, Category, Post, Layout],
user: User,
upload: Upload,
globals: [Header, Footer],
@@ -19,12 +20,12 @@ module.exports = {
routes: {
api: '/api',
admin: '/admin',
+ graphQL: '/graphql',
+ graphQLPlayground: '/graphql-playground',
},
compression: {},
paths: {
scssOverrides: path.resolve(__dirname, 'client/scss/overrides.scss'),
- config: path.resolve(__dirname, 'payload.config.js'),
- components: path.resolve(__dirname, 'client/components.js'),
},
mongoURL: 'mongodb://localhost/payload',
localization: {
@@ -68,13 +69,14 @@ module.exports = {
],
},
},
- staticUrl: '/uploads',
+ staticURL: '/uploads',
staticDir: 'demo/upload',
email: {
provider: 'mock',
},
- graphQL: {
- path: '/graphql',
- graphiql: true,
+ components: {
+ layout: {
+ // Sidebar: path.resolve(__dirname, 'client/components/layout/Sidebar/index.js'),
+ },
},
};
diff --git a/demo/policies/checkRole.js b/demo/policies/checkRole.js
new file mode 100644
index 000000000..448483fd0
--- /dev/null
+++ b/demo/policies/checkRole.js
@@ -0,0 +1,11 @@
+/**
+ * authorize a request by comparing the current user with one or more roles
+ * @param roles
+ * @param user
+ * @returns {Function}
+ */
+const checkRole = (roles, user) => {
+ return !!(user && roles.some(role => role === user.role));
+};
+
+module.exports = checkRole;
diff --git a/demo/policies/roles.js b/demo/policies/roles.js
new file mode 100644
index 000000000..40a0fa845
--- /dev/null
+++ b/demo/policies/roles.js
@@ -0,0 +1,7 @@
+module.exports = [
+ 'admin',
+ 'editor',
+ 'moderator',
+ 'user',
+ 'viewer',
+];
diff --git a/demo/server.js b/demo/server.js
new file mode 100644
index 000000000..ae8024238
--- /dev/null
+++ b/demo/server.js
@@ -0,0 +1,14 @@
+const express = require('express');
+const Payload = require('../src');
+const config = require('./payload.config');
+
+const expressApp = express();
+
+const payload = new Payload({
+ config,
+ express: expressApp,
+});
+
+expressApp.listen(config.port, () => {
+ console.log(`listening on ${config.port}...`);
+});
diff --git a/package.json b/package.json
index 073336d5b..9b1f68229 100644
--- a/package.json
+++ b/package.json
@@ -13,8 +13,7 @@
"test": "jest --config src/tests/jest.config.js",
"test:int": "jest --config src/tests/jest.config.integration.js",
"cov": "npm run core:build && node ./node_modules/jest/bin/jest.js src/tests --coverage",
- "debug": "nodemon --inspect ./demo/init.js",
- "dev": "nodemon demo/init.js",
+ "dev": "nodemon demo/server.js",
"lint": "eslint **/*.js"
},
"bin": {