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": {