Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21708bcc1c | ||
|
|
23d605c5aa | ||
|
|
b4ffa22885 | ||
|
|
d2bc4b72a0 | ||
|
|
27c352c2ff | ||
|
|
19a354863f | ||
|
|
33f4a1322d | ||
|
|
563f98e2fb | ||
|
|
1e4b94c508 | ||
|
|
cd8d1c7ace | ||
|
|
2ea545f0cf | ||
|
|
e8a1cdafba | ||
|
|
1f8b5d8e1a | ||
|
|
793dfe96b9 | ||
|
|
6533af9c0e | ||
|
|
87ead2f89b | ||
|
|
28e3928094 | ||
|
|
dc41372acb | ||
|
|
6a9ea638d5 | ||
|
|
2a7651eb9c | ||
|
|
9021d5352d | ||
|
|
dc5c9ee3a0 | ||
|
|
079623f40a | ||
|
|
b59ba230de | ||
|
|
6826e44072 | ||
|
|
878fd4ab92 | ||
|
|
abfb1868ab | ||
|
|
7319a8e90d | ||
|
|
d2be893a5a | ||
|
|
cc8b636248 | ||
|
|
a103665bd9 | ||
|
|
0d1603b467 | ||
|
|
d6a7b8c319 | ||
|
|
f5ad7a163a | ||
|
|
d433351dbd | ||
|
|
3601ef90bc | ||
|
|
76b6c736e1 | ||
|
|
a194a64845 | ||
|
|
b00d8584f3 | ||
|
|
39d075b999 | ||
|
|
e57a5741e5 | ||
|
|
a6a9ab15b7 | ||
|
|
4280a59c77 | ||
|
|
3259e34e27 | ||
|
|
3132d35e27 | ||
|
|
7376246592 | ||
|
|
69ce6d78da | ||
|
|
b43e8c5db0 | ||
|
|
e348bdcc21 | ||
|
|
039c3ec110 | ||
|
|
f98ef62ad3 | ||
|
|
17ebcc6925 | ||
|
|
6347a2febf | ||
|
|
2ef8a1e35a | ||
|
|
df11478905 | ||
|
|
e7d2bdb45a | ||
|
|
1564fcaeb3 | ||
|
|
cf38e8d520 | ||
|
|
bb9f28fb08 | ||
|
|
d8a28acfa8 | ||
|
|
b0eccdd12e | ||
|
|
1427fb4078 | ||
|
|
1812be2e16 | ||
|
|
db87179576 | ||
|
|
0d8d41ce7e | ||
|
|
a2f95f6cea | ||
|
|
fa9bd6191c | ||
|
|
bc9de859c4 | ||
|
|
73d04262ad | ||
|
|
6e7d547da5 | ||
|
|
6060e4cb27 | ||
|
|
b5ce54c7ae | ||
|
|
492a1308cc | ||
|
|
147a4f8624 | ||
|
|
eac982398e | ||
|
|
9afd51b7dd | ||
|
|
dd810a3593 | ||
|
|
75a4c52071 | ||
|
|
2707af1d45 | ||
|
|
b7d49db536 | ||
|
|
3a98c6ad53 | ||
|
|
79f3de8042 | ||
|
|
81024e4753 | ||
|
|
e2c333ac02 | ||
|
|
1bdacb9bda | ||
|
|
2fa680cdf8 | ||
|
|
3a17a8a001 | ||
|
|
afbb014cbd | ||
|
|
c75c67ec86 | ||
|
|
e8b72f186f | ||
|
|
a777032894 | ||
|
|
624e25c077 | ||
|
|
34d0b89376 | ||
|
|
fcd3b0d2cb | ||
|
|
5512022a10 | ||
|
|
28e31fee72 | ||
|
|
779a60a42c | ||
|
|
ba29898d97 | ||
|
|
a636c65e34 | ||
|
|
ff3e137c78 | ||
|
|
8c4e0fa7b2 | ||
|
|
3ae1c26a07 | ||
|
|
7a6f2392fa | ||
|
|
07949525ef | ||
|
|
3b9fdf3ffd | ||
|
|
2694c0d5bd | ||
|
|
4f8b5b9f85 | ||
|
|
e75cca4512 | ||
|
|
68e7c41fdc | ||
|
|
735e385537 | ||
|
|
c995f797fe | ||
|
|
54162b52cc | ||
|
|
66fc06895a | ||
|
|
85b7a490eb | ||
|
|
4b0a257246 | ||
|
|
29f1af7eb4 | ||
|
|
ba79b4446c | ||
|
|
40b6afff2d | ||
|
|
a664b627f6 | ||
|
|
3a4657e368 | ||
|
|
4cb565f1cc | ||
|
|
31ca363982 | ||
|
|
4a3588e965 | ||
|
|
11600930b7 | ||
|
|
4bb0d3994f | ||
|
|
5fc4f3adad | ||
|
|
41a0ba5780 | ||
|
|
f7a81d70ac | ||
|
|
baf4664073 | ||
|
|
48700a93e3 | ||
|
|
49d09a349f | ||
|
|
31bc4c6532 | ||
|
|
aa89251a3b | ||
|
|
057846e250 | ||
|
|
5a5e3b589c | ||
|
|
270dd22f08 | ||
|
|
beec04a1bc | ||
|
|
60bfb1c3b8 | ||
|
|
98676bea69 | ||
|
|
8589fdefda | ||
|
|
8ba25e8602 | ||
|
|
de43e21ebc | ||
|
|
90ba15f9bd | ||
|
|
b9f9f15d77 | ||
|
|
cac5266c79 | ||
|
|
420afc6838 | ||
|
|
4e3c4e9c0c | ||
|
|
2c27812ba1 | ||
|
|
2f57a983cd | ||
|
|
91c4ef226b | ||
|
|
38b52bf67b | ||
|
|
281985970d | ||
|
|
03f28a4804 | ||
|
|
c0acba94c6 | ||
|
|
8d550d411e | ||
|
|
e8064371b0 | ||
|
|
166bd31506 | ||
|
|
4055908bc8 | ||
|
|
d68bb8c292 | ||
|
|
c8be171f24 | ||
|
|
43eafd4b9f | ||
|
|
ce1c99b01c | ||
|
|
4b2bc36f89 | ||
|
|
58587525e5 | ||
|
|
df76f60e7f | ||
|
|
2c66ad8689 | ||
|
|
6016e2346c | ||
|
|
56cdd943fd | ||
|
|
6d02f7d3ac | ||
|
|
18f8790062 | ||
|
|
854b63ec34 | ||
|
|
beccbbd3e8 | ||
|
|
91d0d84c65 | ||
|
|
7dd67a8d39 | ||
|
|
ad43cbc808 | ||
|
|
2b2a562d83 | ||
|
|
d9e7696384 | ||
|
|
01bc1fef1e | ||
|
|
567d8c19bf | ||
|
|
b722bed24f | ||
|
|
0af66d68a7 | ||
|
|
f3b7dcff57 | ||
|
|
67331eb975 | ||
|
|
d9ef803d20 | ||
|
|
9fd171b26d | ||
|
|
91e33d1c1c | ||
|
|
601e69ab0d | ||
|
|
81aa74bbbe | ||
|
|
e84b7a9c58 | ||
|
|
b6b0ffb674 | ||
|
|
62bd2db5f9 | ||
|
|
74342a4dea | ||
|
|
c78d77446a | ||
|
|
6eb4fc04b1 | ||
|
|
f1b00e85fc | ||
|
|
7c73f2c1d9 | ||
|
|
704e543c7e | ||
|
|
fcaa1454dc | ||
|
|
9d388f7a89 | ||
|
|
918062de2f | ||
|
|
375738d99c | ||
|
|
1c37ec3902 | ||
|
|
7eb804daf9 | ||
|
|
d99a67ca95 | ||
|
|
3d5ed93fce | ||
|
|
b80006be45 | ||
|
|
70edb0ba38 | ||
|
|
a1fe17d05d | ||
|
|
7083225abd | ||
|
|
af6479bf34 | ||
|
|
44c12325b4 | ||
|
|
f2bf2399fa | ||
|
|
7fe2fece6c |
23
.eslintrc.js
23
.eslintrc.js
@@ -21,6 +21,27 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['test/**/int.spec.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': 'warn',
|
||||
'jest/prefer-strict-equal': 'off',
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['test/**/e2e.spec.ts'],
|
||||
extends: [
|
||||
'plugin:playwright/playwright-test'
|
||||
],
|
||||
rules: {
|
||||
'jest/consistent-test-it': 'off',
|
||||
'jest/require-top-level-describe': 'off',
|
||||
'jest/no-test-callback': 'off',
|
||||
'jest/prefer-strict-equal': 'off',
|
||||
'jest/expect-expect': 'off',
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
@@ -66,7 +87,7 @@ module.exports = {
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'arrow-body-style': 0,
|
||||
'@typescript-eslint/no-use-before-define': ['error'],
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
|
||||
26
.github/workflows/tests.yml
vendored
26
.github/workflows/tests.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -31,11 +31,25 @@ jobs:
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- run: yarn build
|
||||
- run: yarn test:client
|
||||
- run: yarn test:int # In-memory db + api tests
|
||||
- run: yarn demo:generate:types
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Component Tests
|
||||
run: yarn test:components
|
||||
- name: Integration Tests
|
||||
run: yarn test:int
|
||||
|
||||
- name: Generate Payload Types
|
||||
run: yarn dev:generate-types fields
|
||||
|
||||
# - name: Install Playwright Browsers
|
||||
# run: npx playwright install --with-deps
|
||||
# - name: E2E Tests
|
||||
# run: yarn test:e2e
|
||||
# - uses: actions/upload-artifact@v2
|
||||
# if: always()
|
||||
# with:
|
||||
# name: playwright-report
|
||||
# path: playwright-report/
|
||||
# retention-days: 30
|
||||
install_npm:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -2,8 +2,6 @@ coverage
|
||||
package-lock.json
|
||||
dist
|
||||
.idea
|
||||
cypress/videos
|
||||
cypress/screenshots
|
||||
|
||||
# Created by https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
@@ -88,6 +86,15 @@ typings/
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# Yarn Berry
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
@@ -233,3 +240,4 @@ components/styles.css
|
||||
|
||||
# Ignore generated
|
||||
demo/generated-types.ts
|
||||
demo/generated-schema.graphql
|
||||
|
||||
44
.vscode/launch.json
vendored
44
.vscode/launch.json
vendored
@@ -29,55 +29,17 @@
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
|
||||
"BABEL_ENV": "development"
|
||||
},
|
||||
"program": "${workspaceFolder}/demo/index.js",
|
||||
"program": "${workspaceFolder}/test/dev.js",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program - Production",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
|
||||
"NODE_ENV": "production",
|
||||
"BABEL_ENV": "development"
|
||||
},
|
||||
"program": "${workspaceFolder}/demo/index.js",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/babel-node",
|
||||
"outputCapture": "std",
|
||||
"runtimeArgs": [
|
||||
"--nolazy"
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against Localhost",
|
||||
"url": "http://localhost:3000/admin",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Debug Payload Generate Types",
|
||||
"program": "${workspaceFolder}/src/bin/generateTypes.ts",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "demo/payload.config.ts",
|
||||
},
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/dist/**/*.js",
|
||||
"!**/node_modules/**"
|
||||
"args": [
|
||||
"fields"
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
493
CHANGELOG.md
493
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -2,3 +2,4 @@ export { default as Button } from '../dist/admin/components/elements/Button';
|
||||
export { default as Card } from '../dist/admin/components/elements/Card';
|
||||
export { default as Eyebrow } from '../dist/admin/components/elements/Eyebrow';
|
||||
export { default as Nav } from '../dist/admin/components/elements/Nav';
|
||||
export { default as Gutter } from '../dist/admin/components/elements/Gutter';
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"$schema": "https://on.cypress.io/cypress.schema.json",
|
||||
"testFiles": "**/*e2e.ts",
|
||||
"ignoreTestFiles": "**/examples/*spec.js",
|
||||
"viewportWidth": 1440,
|
||||
"viewportHeight": 900,
|
||||
"baseUrl": "http://localhost:3000"
|
||||
}
|
||||
16
cypress/cypress.d.ts
vendored
16
cypress/cypress.d.ts
vendored
@@ -1,16 +0,0 @@
|
||||
export { };
|
||||
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
visitAdmin(): Chainable<any>
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
* Creates user if not exists
|
||||
*/
|
||||
login(): Chainable<any>
|
||||
apiLogin(): Chainable<any>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { adminURL } from './common/constants';
|
||||
import { credentials } from './common/credentials';
|
||||
|
||||
describe('Collections', () => {
|
||||
const collectionName = 'Admins';
|
||||
|
||||
before(() => {
|
||||
cy.apiLogin();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.visitAdmin();
|
||||
});
|
||||
|
||||
it('can view collection', () => {
|
||||
cy.contains(collectionName).click();
|
||||
|
||||
cy.get('.collection-list__wrap')
|
||||
.should('be.visible');
|
||||
cy.get('.collection-list__header')
|
||||
.contains(collectionName)
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('.table')
|
||||
.contains(credentials.email)
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
it('can create new', () => {
|
||||
cy.contains(collectionName).click();
|
||||
|
||||
cy.contains('Create New').click();
|
||||
cy.url().should('contain', `${adminURL}/collections/${collectionName.toLowerCase()}/create`);
|
||||
});
|
||||
it('can create new - plus button', () => {
|
||||
cy.contains(collectionName)
|
||||
.get('.card__actions')
|
||||
.first()
|
||||
.click();
|
||||
|
||||
cy.url().should('contain', `${adminURL}/collections/${collectionName.toLowerCase()}/create`);
|
||||
});
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
export const adminURL = 'http://localhost:3000/admin';
|
||||
@@ -1,5 +0,0 @@
|
||||
export const credentials = {
|
||||
email: 'test@test.com',
|
||||
password: 'test123',
|
||||
roles: ['admin'],
|
||||
};
|
||||
@@ -1,76 +0,0 @@
|
||||
describe('Fields', () => {
|
||||
before(() => {
|
||||
cy.apiLogin();
|
||||
});
|
||||
|
||||
describe('Array', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/admin/collections/all-fields/create');
|
||||
});
|
||||
|
||||
it('can add and remove rows', () => {
|
||||
cy.contains('Add Array').click();
|
||||
|
||||
cy.contains('Array Text 1')
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('.action-panel__add-row').first().click();
|
||||
|
||||
cy.get('.field-type.array')
|
||||
.filter(':contains("Editable Array")')
|
||||
.should('contain', '02');
|
||||
|
||||
cy.get('.action-panel__remove-row').first().click();
|
||||
|
||||
cy.get('.field-type.array')
|
||||
.filter(':contains("Editable Array")')
|
||||
.should('not.contain', '02')
|
||||
.should('contain', '01');
|
||||
});
|
||||
|
||||
it('can be readOnly', () => {
|
||||
cy.get('.field-type.array')
|
||||
.filter(':contains("readOnly Array")')
|
||||
.should('not.contain', 'Add Row');
|
||||
|
||||
cy.get('.field-type.array')
|
||||
.filter(':contains("readOnly Array")')
|
||||
.children('.action-panel__add-row')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('.field-type.array')
|
||||
.filter(':contains("readOnly Array")')
|
||||
.children('.position-panel__move-backward')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('.field-type.array')
|
||||
.filter(':contains("readOnly Array")')
|
||||
.children('.position-panel__move-forward')
|
||||
.should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Admin', () => {
|
||||
describe('Conditions', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/admin/collections/conditions/create');
|
||||
});
|
||||
|
||||
it('can see conditional fields', () => {
|
||||
cy.get('#simpleCondition')
|
||||
.should('not.exist');
|
||||
|
||||
cy.get('#customComponent')
|
||||
.should('not.exist');
|
||||
|
||||
cy.contains('Enable Test').click();
|
||||
|
||||
cy.get('#simpleCondition')
|
||||
.should('be.visible');
|
||||
|
||||
cy.get('#customComponent')
|
||||
.should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,99 +0,0 @@
|
||||
/* eslint-disable jest/expect-expect */
|
||||
import { adminURL } from './common/constants';
|
||||
import { credentials } from './common/credentials';
|
||||
|
||||
// running login more than one time is not working
|
||||
const viewportSizes: Cypress.ViewportPreset[] = [
|
||||
'macbook-15',
|
||||
// 'iphone-x',
|
||||
// 'ipad-2',
|
||||
];
|
||||
|
||||
// intermittent failure
|
||||
describe.skip('Payload Login', () => {
|
||||
beforeEach(() => {
|
||||
cy.clearCookies();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.apiLogin();
|
||||
});
|
||||
|
||||
viewportSizes.forEach((viewportSize) => {
|
||||
describe(`Login (${viewportSize})`, () => {
|
||||
beforeEach(() => {
|
||||
cy.visit(adminURL);
|
||||
});
|
||||
|
||||
it('success', () => {
|
||||
cy.viewport(viewportSize);
|
||||
|
||||
cy.url().should('include', '/admin/login');
|
||||
|
||||
cy.get('.field-type.email input').type(credentials.email);
|
||||
cy.get('.field-type.password input').type(credentials.password);
|
||||
cy.get('form')
|
||||
.contains('form', 'Login')
|
||||
.should('be.visible')
|
||||
.submit();
|
||||
cy.get('.template-default')
|
||||
.find('h3.dashboard__label')
|
||||
.should('have.length', 2); // TODO: Should assert label content
|
||||
cy.url().should('eq', adminURL);
|
||||
});
|
||||
|
||||
// skip due to issue with cookies not being reset between tests
|
||||
it.skip('bad Password', () => {
|
||||
cy.viewport(viewportSize);
|
||||
|
||||
cy.visit(adminURL);
|
||||
cy.get('#email').type(credentials.email);
|
||||
cy.get('#password').type('badpassword');
|
||||
cy.get('form')
|
||||
.contains('form', 'Login')
|
||||
.should('be.visible')
|
||||
.submit();
|
||||
|
||||
cy.get('.Toastify')
|
||||
.contains('The email or password provided is incorrect.')
|
||||
.should('be.visible');
|
||||
});
|
||||
|
||||
// skip due to issue with cookies not being reset between tests
|
||||
it.skip('bad Password - Retry Success', () => {
|
||||
cy.viewport(viewportSize);
|
||||
|
||||
cy.visit(adminURL);
|
||||
cy.get('#email').type(credentials.email);
|
||||
cy.get('#password').type('badpassword');
|
||||
cy.get('form')
|
||||
.contains('form', 'Login')
|
||||
.should('be.visible')
|
||||
.submit();
|
||||
|
||||
cy.get('.Toastify')
|
||||
.contains('The email or password provided is incorrect.')
|
||||
.should('be.visible');
|
||||
|
||||
// Dismiss notification
|
||||
cy.wait(500);
|
||||
cy.get('.Toastify__toast-body').click();
|
||||
cy.wait(200);
|
||||
cy.get('.Toastify__toast-body').should('not.be.visible');
|
||||
cy.url().should('eq', `${adminURL}/login`);
|
||||
|
||||
cy.get('#password').clear().type(credentials.password);
|
||||
cy.get('form')
|
||||
.contains('form', 'Login')
|
||||
.should('be.visible')
|
||||
.submit();
|
||||
|
||||
cy.get('.template-default')
|
||||
.find('h3.dashboard__label')
|
||||
.should('have.length', 2); // TODO: Should assert label content
|
||||
|
||||
cy.url().should('eq', adminURL);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
require('isomorphic-fetch');
|
||||
const { credentials } = require('../integration/common/credentials');
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
module.exports = (on) => {
|
||||
on('before:run', () => {
|
||||
return fetch('http://localhost:3000/api/admins/first-register', {
|
||||
body: JSON.stringify(credentials),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'post',
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,54 +0,0 @@
|
||||
import { adminURL } from '../integration/common/constants';
|
||||
import { credentials } from '../integration/common/credentials';
|
||||
|
||||
Cypress.Commands.add('visitAdmin', () => {
|
||||
cy.visit(adminURL);
|
||||
});
|
||||
|
||||
Cypress.Commands.add('login', () => {
|
||||
cy.clearCookies();
|
||||
cy.visit(adminURL);
|
||||
cy.get('#email').type(credentials.email);
|
||||
cy.get('#password').type(credentials.password);
|
||||
|
||||
cy.get('body')
|
||||
.then((body) => {
|
||||
if (body.find('.dashboard__card-list').length) {
|
||||
cy.get('.dashboard__card-list')
|
||||
.should('be.visible');
|
||||
}
|
||||
|
||||
if (body.find('#confirm-password').length) {
|
||||
cy.get('#confirm-password').type(credentials.password);
|
||||
cy.get('.rs__indicators').first()
|
||||
.click();
|
||||
cy.get('.rs__menu').first().contains('admin')
|
||||
.click();
|
||||
|
||||
cy.get('form')
|
||||
.contains('form', 'Create')
|
||||
.should('be.visible')
|
||||
.submit();
|
||||
}
|
||||
|
||||
if (body.find('form').length) {
|
||||
cy.get('form')
|
||||
.contains('form', 'Login')
|
||||
.should('be.visible')
|
||||
.submit();
|
||||
}
|
||||
cy.get('.dashboard__card-list')
|
||||
.should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('apiLogin', () => {
|
||||
cy.api({
|
||||
url: '/api/admins/login',
|
||||
method: 'POST',
|
||||
body: credentials,
|
||||
failOnStatusCode: true,
|
||||
}).should(({ status }) => {
|
||||
cy.wrap(status).should('equal', 200);
|
||||
});
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import '@bahmutov/cy-api';
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
Cypress.Cookies.defaults({
|
||||
preserve: 'payload-token',
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom"
|
||||
],
|
||||
"types": [
|
||||
"cypress"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* authorize a request by comparing the current user with one or more roles
|
||||
* @param allRoles
|
||||
* @param user
|
||||
* @returns {Function}
|
||||
*/
|
||||
const checkRole = (allRoles, user) => {
|
||||
if (user) {
|
||||
if (allRoles.some((role) => user.roles && user.roles.some((individualRole) => individualRole === role))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export default checkRole;
|
||||
@@ -1,7 +0,0 @@
|
||||
export default [
|
||||
'admin',
|
||||
'editor',
|
||||
'moderator',
|
||||
'user',
|
||||
'viewer',
|
||||
];
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const CTA: Block = {
|
||||
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',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default CTA;
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const Email: Block = {
|
||||
slug: 'email',
|
||||
labels: {
|
||||
singular: 'Email',
|
||||
plural: 'Emails',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'testEmail',
|
||||
label: 'Test Email Field',
|
||||
type: 'email',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Email;
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const NumberBlock: Block = {
|
||||
slug: 'number',
|
||||
labels: {
|
||||
singular: 'Number',
|
||||
plural: 'Numbers',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'testNumber',
|
||||
label: 'Test Number Field',
|
||||
type: 'number',
|
||||
max: 100,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default NumberBlock;
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const Quote: Block = {
|
||||
imageURL: '/static/assets/images/generic-block-image.svg',
|
||||
slug: 'quote',
|
||||
labels: {
|
||||
singular: 'Quote',
|
||||
plural: 'Quotes',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'quote',
|
||||
label: 'Quote',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'color',
|
||||
label: 'Color',
|
||||
type: 'text',
|
||||
maxLength: 7,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Quote;
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
// As this is the demo folder, we import Payload SCSS functions relatively.
|
||||
@import '../../../../scss';
|
||||
|
||||
// In your own projects, you'd import as follows:
|
||||
// @import '~payload/scss';
|
||||
|
||||
.after-dashboard {
|
||||
margin-top: base(2);
|
||||
|
||||
&__cards {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useConfig } from '../../../../src/admin/components/utilities/Config';
|
||||
|
||||
// As this is the demo project, we import our dependencies from the `src` directory.
|
||||
import Card from '../../../../src/admin/components/elements/Card';
|
||||
|
||||
// In your projects, you can import as follows:
|
||||
// import { Card } from 'payload/components/elements';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'after-dashboard';
|
||||
|
||||
const AfterDashboard: React.FC = () => {
|
||||
const history = useHistory();
|
||||
const { routes: { admin: adminRoute } } = useConfig();
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<h3>Custom Routes & Dashboard Components</h3>
|
||||
<p>This is a custom component that is rendered within the built-in dashboard component after its contents are rendered. Below, there are two cards that link to custom routes.</p>
|
||||
<ul className="dashboard__card-list">
|
||||
<li>
|
||||
<Card
|
||||
title="Default Template"
|
||||
onClick={() => history.push(`${adminRoute}/custom-default-route`)}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<Card
|
||||
title="Minimal Template"
|
||||
onClick={() => history.push(`${adminRoute}/custom-minimal-route`)}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AfterDashboard;
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { useConfig } from '../../../../src/admin/components/utilities/Config';
|
||||
|
||||
// As this is the demo project, we import our dependencies from the `src` directory.
|
||||
import Chevron from '../../../../src/admin/components/icons/Chevron';
|
||||
|
||||
// In your projects, you can import as follows:
|
||||
// import { Chevron } from 'payload/components/icons';
|
||||
|
||||
|
||||
const baseClass = 'after-nav-links';
|
||||
|
||||
const AfterNavLinks: React.FC = () => {
|
||||
const { routes: { admin: adminRoute } } = useConfig();
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<span className="nav__label">Custom Routes</span>
|
||||
<nav>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
to={`${adminRoute}/custom-default-route`}
|
||||
>
|
||||
<Chevron />
|
||||
Default Template
|
||||
</NavLink>
|
||||
<NavLink
|
||||
activeClassName="active"
|
||||
to={`${adminRoute}/custom-minimal-route`}
|
||||
>
|
||||
<Chevron />
|
||||
Minimal Template
|
||||
</NavLink>
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AfterNavLinks;
|
||||
@@ -1,24 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const BeforeLogin: React.FC = () => {
|
||||
return (
|
||||
<div>
|
||||
<h3>Welcome</h3>
|
||||
<p>
|
||||
This demo is a set up to configure Payload for the develop and testing of features. To see a product demo of a Payload project
|
||||
please visit:
|
||||
{' '}
|
||||
<a
|
||||
href="https://demo.payloadcms.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
demo.payloadcms.com
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BeforeLogin;
|
||||
@@ -1,29 +0,0 @@
|
||||
import React, { createContext, useState, useContext } from 'react';
|
||||
|
||||
type CustomContext = {
|
||||
getCustom
|
||||
setCustom
|
||||
}
|
||||
|
||||
const Context = createContext({} as CustomContext);
|
||||
|
||||
const CustomProvider: React.FC = ({ children }) => {
|
||||
const [getCustom, setCustom] = useState({});
|
||||
|
||||
const value = {
|
||||
getCustom,
|
||||
setCustom,
|
||||
};
|
||||
|
||||
console.log('custom provider called');
|
||||
|
||||
return (
|
||||
<Context.Provider value={value}>
|
||||
{children}
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomProvider;
|
||||
|
||||
export const useCustom = () => useContext(Context);
|
||||
@@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const DemoUIFieldCell: React.FC = () => (
|
||||
<p>Demo UI Field Cell</p>
|
||||
);
|
||||
|
||||
export default DemoUIFieldCell;
|
||||
@@ -1,7 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const DemoUIField: React.FC = () => (
|
||||
<p>Demo UI Field</p>
|
||||
);
|
||||
|
||||
export default DemoUIField;
|
||||
@@ -1,33 +0,0 @@
|
||||
@import '../../../../../../../src/admin/scss/styles.scss';
|
||||
|
||||
.button-rich-text-button {
|
||||
.btn {
|
||||
margin-right: base(1);
|
||||
}
|
||||
|
||||
&__modal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
|
||||
&.payload__modal-item--enterDone {
|
||||
@include blur-bg;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
width: 100%;
|
||||
margin-bottom: $baseline;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: base(1.5);
|
||||
height: base(1.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
import React, { Fragment, useCallback } from 'react';
|
||||
import { Modal, useModal } from '@faceless-ui/modal';
|
||||
import { Transforms } from 'slate';
|
||||
import { useSlate, ReactEditor } from 'slate-react';
|
||||
import MinimalTemplate from '../../../../../../../src/admin/components/templates/Minimal';
|
||||
import ElementButton from '../../../../../../../src/admin/components/forms/field-types/RichText/elements/Button';
|
||||
import X from '../../../../../../../src/admin/components/icons/X';
|
||||
import Button from '../../../../../../../src/admin/components/elements/Button';
|
||||
import Form from '../../../../../../../src/admin/components/forms/Form';
|
||||
import Submit from '../../../../../../../src/admin/components/forms/Submit';
|
||||
import reduceFieldsToValues from '../../../../../../../src/admin/components/forms/Form/reduceFieldsToValues';
|
||||
import Text from '../../../../../../../src/admin/components/forms/field-types/Text';
|
||||
import Checkbox from '../../../../../../../src/admin/components/forms/field-types/Checkbox';
|
||||
import Select from '../../../../../../../src/admin/components/forms/field-types/Select';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'button-rich-text-button';
|
||||
|
||||
const initialFormData = {
|
||||
style: 'primary',
|
||||
};
|
||||
|
||||
const insertButton = (editor, { href, label, style, newTab = false }: any) => {
|
||||
const text = { text: ' ' };
|
||||
const button = {
|
||||
type: 'button',
|
||||
href,
|
||||
style,
|
||||
newTab,
|
||||
label,
|
||||
children: [
|
||||
text,
|
||||
],
|
||||
};
|
||||
|
||||
const nodes = [button, { children: [{ text: '' }] }];
|
||||
|
||||
if (editor.blurSelection) {
|
||||
Transforms.select(editor, editor.blurSelection);
|
||||
}
|
||||
|
||||
Transforms.insertNodes(editor, nodes);
|
||||
|
||||
const currentPath = editor.selection.anchor.path[0];
|
||||
const newSelection = { anchor: { path: [currentPath + 1, 0], offset: 0 }, focus: { path: [currentPath + 1, 0], offset: 0 } };
|
||||
|
||||
Transforms.select(editor, newSelection);
|
||||
ReactEditor.focus(editor);
|
||||
};
|
||||
|
||||
const ToolbarButton: React.FC<{path: string}> = ({ path }) => {
|
||||
const { open, closeAll } = useModal();
|
||||
const editor = useSlate();
|
||||
|
||||
const handleAddButton = useCallback((fields) => {
|
||||
const data = reduceFieldsToValues(fields);
|
||||
insertButton(editor, data);
|
||||
closeAll();
|
||||
}, [editor, closeAll]);
|
||||
|
||||
const modalSlug = `${path}-add-button`;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<ElementButton
|
||||
className={baseClass}
|
||||
format="button"
|
||||
onClick={() => open(modalSlug)}
|
||||
>
|
||||
Button
|
||||
</ElementButton>
|
||||
<Modal
|
||||
slug={modalSlug}
|
||||
className={`${baseClass}__modal`}
|
||||
>
|
||||
<MinimalTemplate>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h3>Add button</h3>
|
||||
<Button
|
||||
buttonStyle="none"
|
||||
onClick={closeAll}
|
||||
>
|
||||
<X />
|
||||
</Button>
|
||||
</header>
|
||||
<Form
|
||||
onSubmit={handleAddButton}
|
||||
initialData={initialFormData}
|
||||
>
|
||||
<Text
|
||||
label="Label"
|
||||
name="label"
|
||||
required
|
||||
/>
|
||||
<Text
|
||||
label="URL"
|
||||
name="href"
|
||||
required
|
||||
/>
|
||||
<Select
|
||||
label="Style"
|
||||
name="style"
|
||||
options={[
|
||||
{
|
||||
label: 'Primary',
|
||||
value: 'primary',
|
||||
},
|
||||
{
|
||||
label: 'Secondary',
|
||||
value: 'secondary',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Checkbox
|
||||
label="Open in new tab"
|
||||
name="newTab"
|
||||
/>
|
||||
<Submit>
|
||||
Add button
|
||||
</Submit>
|
||||
</Form>
|
||||
</MinimalTemplate>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToolbarButton;
|
||||
@@ -1,19 +0,0 @@
|
||||
@import '../../../../../../../src/admin/scss/styles.scss';
|
||||
|
||||
.rich-text-button {
|
||||
margin: $baseline 0;
|
||||
}
|
||||
|
||||
.rich-text-button__button {
|
||||
padding: base(.5) base(1.5);
|
||||
border-radius: $style-radius-s;
|
||||
|
||||
&--primary {
|
||||
background-color: $color-dark-gray;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
background-color: $color-light-gray;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'rich-text-button';
|
||||
|
||||
const ButtonElement: React.FC = ({ attributes, children, element }) => {
|
||||
const { style = 'primary', label } = element;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={baseClass}
|
||||
contentEditable={false}
|
||||
>
|
||||
<span
|
||||
{...attributes}
|
||||
className={[
|
||||
`${baseClass}__button`,
|
||||
`${baseClass}__button--${style}`,
|
||||
].join(' ')}
|
||||
>
|
||||
{label}
|
||||
{children}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonElement;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { RichTextCustomElement } from '../../../../../../src/fields/config/types';
|
||||
import Button from './Button';
|
||||
import Element from './Element';
|
||||
import plugin from './plugin';
|
||||
|
||||
const button: RichTextCustomElement = {
|
||||
name: 'button',
|
||||
Button,
|
||||
Element,
|
||||
plugins: [
|
||||
plugin,
|
||||
],
|
||||
};
|
||||
|
||||
export default button;
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Editor } from 'slate';
|
||||
|
||||
const withButton = (incomingEditor: Editor): Editor => {
|
||||
const editor = incomingEditor;
|
||||
const { isVoid } = editor;
|
||||
|
||||
editor.isVoid = (element) => (element.type === 'button' ? true : isVoid(element));
|
||||
|
||||
return editor;
|
||||
};
|
||||
|
||||
export default withButton;
|
||||
@@ -1,10 +0,0 @@
|
||||
import React from 'react';
|
||||
import LeafButton from '../../../../../../../src/admin/components/forms/field-types/RichText/leaves/Button';
|
||||
|
||||
const Button = () => (
|
||||
<LeafButton format="purple-background">
|
||||
Purple Background
|
||||
</LeafButton>
|
||||
);
|
||||
|
||||
export default Button;
|
||||
@@ -1,12 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const PurpleBackground: React.FC<any> = ({ attributes, children }) => (
|
||||
<span
|
||||
{...attributes}
|
||||
style={{ backgroundColor: 'purple' }}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
export default PurpleBackground;
|
||||
@@ -1,8 +0,0 @@
|
||||
import Button from './Button';
|
||||
import Leaf from './Leaf';
|
||||
|
||||
export default {
|
||||
name: 'purple-background',
|
||||
Button,
|
||||
Leaf,
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { useConfig } from '../../../../../src/admin/components/utilities/Config';
|
||||
|
||||
// As this is the demo project, we import our dependencies from the `src` directory.
|
||||
import DefaultTemplate from '../../../../../src/admin/components/templates/Default';
|
||||
import Button from '../../../../../src/admin/components/elements/Button';
|
||||
import Eyebrow from '../../../../../src/admin/components/elements/Eyebrow';
|
||||
import { AdminView } from '../../../../../src/config/types';
|
||||
import { useStepNav } from '../../../../../src/admin/components/elements/StepNav';
|
||||
import Meta from '../../../../../src/admin/components/utilities/Meta';
|
||||
|
||||
// In your projects, you can import as follows:
|
||||
// import { DefaultTemplate } from 'payload/components/templates';
|
||||
// import { Button, Eyebrow } from 'payload/components/elements';
|
||||
// import { AdminView } from 'payload/config';
|
||||
// import { useStepNav } from 'payload/components/hooks';
|
||||
// import { Meta } from 'payload/components/utilities';
|
||||
|
||||
const CustomDefaultRoute: AdminView = ({ user, canAccessAdmin }) => {
|
||||
const { routes: { admin: adminRoute } } = useConfig();
|
||||
const { setStepNav } = useStepNav();
|
||||
|
||||
// This effect will only run one time and will allow us
|
||||
// to set the step nav to display our custom route name
|
||||
|
||||
useEffect(() => {
|
||||
setStepNav([
|
||||
{
|
||||
label: 'Custom Route with Default Template',
|
||||
},
|
||||
]);
|
||||
}, [setStepNav]);
|
||||
|
||||
// If an unauthorized user tries to navigate straight to this page,
|
||||
// Boot 'em out
|
||||
if (!user || (user && !canAccessAdmin)) {
|
||||
return (
|
||||
<Redirect to={`${adminRoute}/unauthorized`} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DefaultTemplate>
|
||||
<Meta
|
||||
title="Custom Route with Default Template"
|
||||
description="Building custom routes into Payload is easy."
|
||||
keywords="Custom React Components, Payload, CMS"
|
||||
/>
|
||||
<Eyebrow />
|
||||
<h1>Custom Route</h1>
|
||||
<p>Here is a custom route that was added in the Payload config. It uses the Default Template, so the sidebar is rendered.</p>
|
||||
<Button
|
||||
el="link"
|
||||
to={`${adminRoute}`}
|
||||
buttonStyle="secondary"
|
||||
>
|
||||
Go to Dashboard
|
||||
</Button>
|
||||
</DefaultTemplate>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomDefaultRoute;
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
// As this is the demo folder, we import Payload SCSS functions relatively.
|
||||
@import '../../../../../scss';
|
||||
|
||||
// In your own projects, you'd import as follows:
|
||||
// @import '~payload/scss';
|
||||
|
||||
.custom-minimal-route {
|
||||
&__login-btn {
|
||||
margin-right: base(.5);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// As this is the demo project, we import our dependencies from the `src` directory.
|
||||
import MinimalTemplate from '../../../../../src/admin/components/templates/Minimal';
|
||||
import Button from '../../../../../src/admin/components/elements/Button';
|
||||
import { useConfig } from '../../../../../src/admin/components/utilities/Config';
|
||||
|
||||
// In your projects, you can import as follows:
|
||||
// import { MinimalTemplate } from 'payload/components/templates';
|
||||
// import { Button } from 'payload/components/elements';
|
||||
// import { useConfig } from 'payload/components/utilities';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'custom-minimal-route';
|
||||
|
||||
const CustomMinimalRoute: React.FC = () => {
|
||||
const { routes: { admin: adminRoute } } = useConfig();
|
||||
|
||||
return (
|
||||
<MinimalTemplate className={baseClass}>
|
||||
<h1>Custom Route</h1>
|
||||
<p>Here is a custom route that was added in the Payload config.</p>
|
||||
<Button
|
||||
className={`${baseClass}__login-btn`}
|
||||
el="link"
|
||||
to={`${adminRoute}/login`}
|
||||
>
|
||||
Go to Login
|
||||
</Button>
|
||||
<Button
|
||||
el="link"
|
||||
to={`${adminRoute}`}
|
||||
buttonStyle="secondary"
|
||||
>
|
||||
Go to Dashboard
|
||||
</Button>
|
||||
</MinimalTemplate>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomMinimalRoute;
|
||||
@@ -1,14 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="portal"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,9 +0,0 @@
|
||||
<svg width="82" height="53" viewBox="0 0 82 53" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<rect x="0.713013" width="80.574" height="52.7791" fill="url(#pattern0)"/>
|
||||
<defs>
|
||||
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
|
||||
<use xlink:href="#image0" transform="scale(0.00387597 0.00591716)"/>
|
||||
</pattern>
|
||||
<image id="image0" width="258" height="169" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQIAAACpCAYAAADA4zPJAAALpklEQVR4Ae2dSY/UOhSF+///DCSGHQuEgAVI7BBITAI2zCCGZuhmpqEBP53W8yOvK1UdO3Zyr/NFilJdXalyTo4/X984zlZgQQEUWLwCW4tXAAFQAAUCIMAEKIACgAAPoAAKBECACVAABQABHkABFAiAABOgAAoAAjyAAiggBbhqgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMogAJ0DfAACqCAFCBHgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMogAJ0DfAACqCAFCBHgA9QAAUAAR5AARQgIsADKIACdA3wAAqggBQgR4APUAAFAAEeQAEUICLAAyiAAnQN8AAKoIAUIEeAD1AABQABHkABFCAiwAMLVmB/fz98+/YtfPr0KXz48CHs7u6GnZ2dg+3Hjx/D169fw48fP8KfP3+aV4muQfOnmAPsKqDKr4r/9u3bsL29PXgVKPb29pqFAiDouoTXzSrw8+fPg5Y+pfL3ffbdu3fh+/fvzekECJo7pRxQVwGF9YoA+ir1mPfUhVB00coCCFo5kxzHigKqqGrBx1T4Tfu+efOmmegAEKzYhzdaUEBdAVXUTRW51P+UVPS+AALvZ5DyryigSGAqCESYeIcBIFixEW94VuDXr1/JVwRiZR671VUFrwsg8HrmKPeKAkoMKok3tkLn7q8oRCDyuAACj2eNMvcqoMFBuZW41H4alORxAQQezxplXlHg9+/fk+cF1sHDYxcBEKxYijc8KvDly5fZo4EIhvfv37uTEBC4O2UU+LACyg2kDhmOlbbWVpcvPS2AwNPZoqy9CigUr1Whc79Xoxk9LYDA09mirL0K1BhCnAuAuJ8iFE8LIPB0tihrrwI1hxHHip2z9XQpERD0Wos3vSig/EBOJZ1iH09XDwCBF8dTzl4FNJx4ikqd8xu6kuFlAQRezhTl7FVAMwjlVNIp9vn8+XNvmS2+aR4E6md56mtZPMktl8niFYMIGU9XDkyDQCdZ47e1eupvtVzxrB0bEUGZM2ISBEoAafLISNa4FWH1PxYUiApo4E70h7UtOYJ4ljK2OrGbLgfpfy1NEZUhEbt0FNA9BtYAEMvjaW5DMxGBWnpN7hBFPGrrfSKIjpd5OVIBa8OLo3c9DTM2AQJRXbdvRgGHbrWP9mVZtgKaanyoZ6b6nPJanrqxs4MgJgRzTxCJxGVDQEevEDzXP7X2E5w8LbOBQLQsOUZ8TCLx9evX4fHjx+HRo0esTjW4efNmKL3eu3cvyBs5sPB2lWsWENSaZlr3gacmEi9fvhyOHTvGiga9Hjh79mwyDJSz8NQtUOQyOQhSEoI5JNY+mrJqyPLy5cvekw8YAGPXA7du3UqKCob6b4hHp/rMZCDITQjmwmBIIvH+/fuAgEjgSA9cuXJlMAh0edtbNCDYTAICjf6a4xKPflO/vW4BBLT83ZZ/3esUEHi6ZNitF1VBIDLqxovcVr3UfipDH6UBASBYV/m77w8FgaeRhF0I6HU1EChpp+Rdqco89nv6EomAABB0K/y610NAoCHxfY3N4Qpn9e8qILAwv3wfODTmoJvIef78+ZH9w3Xm4P3lQOTGjRsbGzSNGfAMgeIRgRKCFkd5HYaCyqiyaj1//jwwIGG41gOnT58Ourp02EPx7zHjVyxFB8UigrkSgvGEpG5jIlEw0ECiO3fusDaowe3bt8PVq1eDwvvU9fr162sh0NqI1tEgsJIQTAVB/Py6RKIlWlOWcQrIo+oSlrpypShADUhLyygQaOYgSwnBWLlTtzoGZkFqydb9xxKBkONZQUSNRqs+yQaBbvRQeJRa6ax+Xsfi6f7xfqvz7lAFVKEVJSjbLzCookc/a6uBQcol6ZKgxgZ4TwYepUsyCBQSeUgI5gJHxmgt7DvKBPwfBZJAIDKW6mflVtQp9tMxeh0hhqVRIEeBQSBQWKQQaYpKaOk3dMyth4Q5pmGf9hQ4EgTqS+3s7CwOAhFIOvZWE0Tt2ZkjylVgIwhaSwjGyp26JZGYay/286JALwiULOubTjy1ArX2ee/jyb2YknJOr8AKCJQk06WT1ipxqeORNiQSpzcqv1hXgf9AsNSEYC4gSCTWNSbfPq0CByBYekIwFwYkEqc1K79WT4GtsdOJ51aiVvZTItHbjLX17MQ3e1Vgq5UKOfdxtHI7ajSy7iYlFxLVaH8LCLa3iyVGlUhMnU7dmsWUK+pOL6dZpxlUZe0slS8PICgIghiV6F4M3ct+6dIlV+vFixcPJmo5d+5c6K4XLlwI+p/l47l27RpdtBF8AAQVQHDmzJm1M94wxVm9Kc6kOzeM5dEAEBQGgR6dRmWvV9mP0lbdM5Z0BQBBYRA8ePAAEMw4B6KeVciSrgAgAARNgQsQpENAewACQAAI8upOU3sBgsIgePjwYVMV66g+ubX/ExHk8QkQFAaB5sA/ceIEMJghT3D8+HHmnczjAF2DeO2/5Pbu3bvh1KlTwGBCGJw8eTI8efIksxqwGxFB4YggAuXVq1fh2bNn4enTp0euL168OJj/QaP4Sq26O1JzLw75/Rqf0czApY5lyPcwfmAczABBJRBEIAzd6ual7nMZx5xWK3eTxkfLjTkW9p1GAUBgBAQRGGMrj7W7SRWV6AYmFtsKAAJjIBAQciqPbgyyPL0cj5YDBMXu7out5lK2QyuPl+nleLScXRgQERiMCLqg2zQLkqIAJdK6n7f+mhmhbcIAEBgHgSp2X+VRlnx3d9cVBLqQGpsLsVmd/JYKEDgAQaxA8bmM1hKCsXypWx4tZwccgMARCFTRFB2kVjjrn2dG6PmBAAicgcB6pc4t36ZcyPzVpP0SAAJAYCbC6MuFtF8FbRwhIAAEZkAQowkeLTc9HAABIDAHAgGBR8tNCwNAAAhMgiBGByQSpwECIAAEpkEgIJBIrA8DQAAIzINAMFAikUfL1QMCIAAELkAQuwqtPVquXtVO+2ZAAAhcgUBAaOHRcmnVtP6nAQEgcAeCGB3ohiuWMgoAAkDgFgQCgm68Ypqy8TAABIDANQgEAxKJgMC9iWOYy3b84+lJJOYDgYiAiKApmGoWpP39/fwasdA9AQEgaAoEMbIqNSP0UrgACABBkyAQEEgkDscYIAAEzYJAMMiZEXp49Wnnk4AAEDQNgthVGDojdDtVO+1IAAEgWAQIBAQSievhAAgAwWJAIBhozAGJxFUgAAJAsCgQxK4C06n/HwaAABAsEgQCAonEvzAABIBgsSCI0QGJxBAAASBYPAgEhKU/lxEQAAJA8K8HljydOiAABIDgkAfio+X+9qDbfwUIDpkg9hvZjr8b0LOGS3suIyAABEQEGzywlOnUAcEGE3hu0Sh7uYhmCdOpAwJAQEQwwAOtJxIBwQAT0LqWa129a9nqcxkBASAgIkj0QIvPZQQEiSbw3qJR/nLRTUuJREAACIgIRniglUQiIBhhAlrXcq2rZy1bmE4dEAACIoJCHvA8nTogKGQCzy0aZS8X2Xh9LiMgAAREBBU84O25jICgggloYcu1sJ619DSdOiAABEQEFT3gJZEICCqawHNrRtnLRjXWE4mAABAQEUzkAcvTqQOCiUxAC1u2hfWsp8Xp1AEBICAimMED1hKJgGAGE3huzSh7ucjG0nTqgAAQEBHM7AEL06kDgplNQAtbroX1rOXciURAAAiICIx4QGMO5kokAgIjJvDcmlH2slHNHM9lBASAgIjAoAemTiQCAoMmoIUt28J61nOqRCIgAAREBMY9MMUsSIDAuAk8t2aUvVxkU3s6dUAACIgIHHmg1nMZAYEjE9DClmthPWtZYzp1QAAIiAiceqDkdOqAwKkJPLdolL1cZFMqkbilkUysaIAH/Hpgb28vjF3+AbSb48mcXO9tAAAAAElFTkSuQmCC"/>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.4 KiB |
@@ -1,67 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import roles from '../access/roles';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const access = ({ req: { user } }) => {
|
||||
const result = checkRole(['admin'], user);
|
||||
return result;
|
||||
};
|
||||
|
||||
const Admin: CollectionConfig = {
|
||||
slug: 'admins',
|
||||
labels: {
|
||||
singular: 'Admin',
|
||||
plural: 'Admins',
|
||||
},
|
||||
access: {
|
||||
create: access,
|
||||
read: access,
|
||||
update: access,
|
||||
delete: access,
|
||||
admin: () => true,
|
||||
},
|
||||
auth: {
|
||||
tokenExpiration: 7200, // 2 hours
|
||||
verify: false,
|
||||
maxLoginAttempts: 5,
|
||||
lockTime: 600 * 1000, // lock time in ms
|
||||
useAPIKey: true,
|
||||
depth: 0,
|
||||
cookies: {
|
||||
secure: false,
|
||||
sameSite: 'lax',
|
||||
domain: undefined,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'roles',
|
||||
label: 'Role',
|
||||
type: 'select',
|
||||
options: roles,
|
||||
defaultValue: 'user',
|
||||
required: true,
|
||||
saveToJWT: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'publicUser',
|
||||
type: 'relationship',
|
||||
hasMany: true,
|
||||
relationTo: 'public-users',
|
||||
},
|
||||
{
|
||||
name: 'apiKey',
|
||||
type: 'text',
|
||||
access: {
|
||||
read: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
};
|
||||
|
||||
export default Admin;
|
||||
@@ -1,363 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
import CollectionDescription from '../customComponents/CollectionDescription';
|
||||
import DemoUIField from '../client/components/DemoUIField/Field';
|
||||
import DemoUIFieldCell from '../client/components/DemoUIField/Cell';
|
||||
|
||||
const AllFields: CollectionConfig = {
|
||||
slug: 'all-fields',
|
||||
labels: {
|
||||
singular: 'All Fields',
|
||||
plural: 'All Fields',
|
||||
},
|
||||
admin: {
|
||||
defaultColumns: ['text', 'demo', 'createdAt'],
|
||||
useAsTitle: 'text',
|
||||
preview: (doc, { token }) => {
|
||||
const { text } = doc;
|
||||
|
||||
if (doc && text) {
|
||||
return `http://localhost:3000/previewable-posts/${text}?preview=true&token=${token}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
description: CollectionDescription,
|
||||
},
|
||||
versions: {
|
||||
maxPerDoc: 20,
|
||||
retainDeleted: true,
|
||||
drafts: false,
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
label: 'Text',
|
||||
required: true,
|
||||
defaultValue: 'Default Value',
|
||||
unique: true,
|
||||
access: {
|
||||
create: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'descriptionText',
|
||||
type: 'text',
|
||||
label: 'Text with text description',
|
||||
defaultValue: 'Default Value',
|
||||
admin: {
|
||||
description: 'This text describes the field',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'descriptionFunction',
|
||||
type: 'text',
|
||||
label: 'Text with function description',
|
||||
defaultValue: 'Default Value',
|
||||
maxLength: 20,
|
||||
admin: {
|
||||
description: ({ value }) => (typeof value === 'string' ? `${20 - value.length} characters left` : ''),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
label: 'Image',
|
||||
relationTo: 'media',
|
||||
admin: {
|
||||
description: 'No selfies',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
label: 'Select',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
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: 'selectMany',
|
||||
label: 'Select w/ hasMany',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
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,
|
||||
},
|
||||
{
|
||||
name: 'dayOnlyDateFieldExample',
|
||||
label: 'Day Only',
|
||||
type: 'date',
|
||||
required: true,
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'dayOnly',
|
||||
monthsToShow: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'timeOnlyDateFieldExample',
|
||||
label: 'Time Only',
|
||||
type: 'date',
|
||||
admin: {
|
||||
date: {
|
||||
pickerAppearance: 'timeOnly',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'point',
|
||||
label: 'Point Field (GeoJSON)',
|
||||
type: 'point',
|
||||
},
|
||||
{
|
||||
name: 'radioGroupExample',
|
||||
label: 'Radio Group Example',
|
||||
type: 'radio',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Options 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}],
|
||||
defaultValue: 'option-2',
|
||||
required: true,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
}, {
|
||||
name: 'number',
|
||||
label: 'Number',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Group',
|
||||
name: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedText1',
|
||||
label: 'Nested Text 1',
|
||||
type: 'text',
|
||||
}, {
|
||||
name: 'nestedText2',
|
||||
label: 'Nested Text 2',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Editable Array',
|
||||
name: 'array',
|
||||
minRows: 2,
|
||||
maxRows: 4,
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'arrayText1',
|
||||
label: 'Array Text 1',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrayText2',
|
||||
label: 'Array Text 2',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
access: {
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'arrayText3',
|
||||
label: 'Array Text 3',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'readOnlyArray',
|
||||
label: 'readOnly Array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
defaultValue: [{
|
||||
text: 'text in readOnly array one',
|
||||
},
|
||||
{
|
||||
text: 'text in readOnly array two',
|
||||
}],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
label: 'Blocks Content',
|
||||
name: 'blocks',
|
||||
minRows: 2,
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to One Collection',
|
||||
name: 'relationship',
|
||||
relationTo: 'conditions',
|
||||
admin: {
|
||||
description: 'Relates to description',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship hasMany',
|
||||
name: 'relationshipHasMany',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to Multiple Collections',
|
||||
name: 'relationshipMultipleCollections',
|
||||
relationTo: ['localized-posts', 'conditions'],
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
label: 'Textarea',
|
||||
name: 'textarea',
|
||||
admin: {
|
||||
description: 'Hello textarea description',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
label: 'Rich Text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'ui',
|
||||
name: 'demo',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
components: {
|
||||
Field: DemoUIField,
|
||||
Cell: DemoUIFieldCell,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
label: 'Slug',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
localized: true,
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
type: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'dateFieldExample',
|
||||
label: 'Day and Time',
|
||||
type: 'date',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
date: {
|
||||
timeIntervals: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default AllFields;
|
||||
@@ -1,102 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const AutoLabel: CollectionConfig = {
|
||||
slug: 'auto-label',
|
||||
admin: {
|
||||
useAsTitle: 'autoLabelField',
|
||||
enableRichTextRelationship: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'autoLabelField',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'noLabel',
|
||||
type: 'text',
|
||||
label: false,
|
||||
},
|
||||
{
|
||||
name: 'labelOverride',
|
||||
type: 'text',
|
||||
label: 'Custom Label',
|
||||
},
|
||||
{
|
||||
name: 'testRelationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'all-fields',
|
||||
},
|
||||
{
|
||||
name: 'specialBlock',
|
||||
type: 'blocks',
|
||||
minRows: 1,
|
||||
maxRows: 20,
|
||||
// Will auto-label
|
||||
// labels: {
|
||||
// singular: 'Special Block',
|
||||
// plural: 'Special Blocks',
|
||||
// },
|
||||
blocks: [
|
||||
{
|
||||
slug: 'number',
|
||||
// Will auto-label
|
||||
// labels: {
|
||||
// singular: 'Number',
|
||||
// plural: 'Numbers',
|
||||
// },
|
||||
fields: [
|
||||
{
|
||||
name: 'testNumber',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'noLabelBlock',
|
||||
type: 'blocks',
|
||||
label: false,
|
||||
minRows: 1,
|
||||
maxRows: 20,
|
||||
blocks: [
|
||||
{
|
||||
slug: 'number',
|
||||
// labels: {
|
||||
// singular: 'Number',
|
||||
// plural: 'Numbers',
|
||||
// },
|
||||
fields: [
|
||||
{
|
||||
name: 'testNumber',
|
||||
type: 'number',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'itemName',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'noLabelArray',
|
||||
type: 'array',
|
||||
label: false,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'textField',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default AutoLabel;
|
||||
@@ -1,46 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
|
||||
const Blocks: CollectionConfig = {
|
||||
slug: 'blocks',
|
||||
labels: {
|
||||
singular: 'Blocks',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'layout',
|
||||
label: 'Layout Blocks',
|
||||
labels: {
|
||||
singular: 'Block',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
type: 'blocks',
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
localized: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'nonLocalizedLayout',
|
||||
label: 'Non Localized Layout',
|
||||
labels: {
|
||||
singular: 'Layout',
|
||||
plural: 'Layouts',
|
||||
},
|
||||
type: 'blocks',
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Blocks;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Code: CollectionConfig = {
|
||||
slug: 'code',
|
||||
labels: {
|
||||
singular: 'Code',
|
||||
plural: 'Codes',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'code',
|
||||
type: 'code',
|
||||
label: 'Code',
|
||||
required: true,
|
||||
admin: {
|
||||
language: 'js',
|
||||
description: 'javascript example',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Code;
|
||||
@@ -1,86 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
import Text from './CustomComponents/components/fields/Text/Field';
|
||||
|
||||
const Conditions: CollectionConfig = {
|
||||
slug: 'conditions',
|
||||
labels: {
|
||||
singular: 'Conditions',
|
||||
plural: 'Conditions',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'enableTest',
|
||||
type: 'checkbox',
|
||||
label: 'Enable Test',
|
||||
},
|
||||
{
|
||||
name: 'number',
|
||||
type: 'number',
|
||||
label: 'Number Field',
|
||||
},
|
||||
{
|
||||
name: 'simpleCondition',
|
||||
type: 'text',
|
||||
label: 'Enable Test is checked',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings.enableTest === true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'orCondition',
|
||||
type: 'text',
|
||||
label: 'Number is greater than 20 OR enableTest is checked',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings.number > 20 || siblings.enableTest === true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'nestedConditions',
|
||||
type: 'text',
|
||||
label: 'Number is either greater than 20 AND enableTest is checked, OR number is less than 20 and enableTest is NOT checked',
|
||||
admin: {
|
||||
condition: (_, siblings) => (siblings.number > 20 && siblings.enableTest === true) || (siblings.number < 20 && siblings.enableTest === false),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'blocks',
|
||||
label: 'Blocks',
|
||||
labels: {
|
||||
singular: 'Block',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
type: 'blocks',
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings?.enableTest === true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'customComponent',
|
||||
type: 'text',
|
||||
label: 'Custom Component with Enable Test is checked',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblings) => siblings?.enableTest === true,
|
||||
components: {
|
||||
Field: Text,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Conditions;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Cell: React.FC = () => <div className="description">fake description cell</div>;
|
||||
|
||||
export default Cell;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const Description: React.FC = () => <div className="description">fake description field</div>;
|
||||
|
||||
export default Description;
|
||||
@@ -1,3 +0,0 @@
|
||||
.custom-description-filter {
|
||||
background: lightgray;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Props } from './types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const Filter: React.FC<Props> = ({ onChange, value }) => (
|
||||
<input
|
||||
className="custom-description-filter"
|
||||
type="text"
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
value={value}
|
||||
/>
|
||||
);
|
||||
|
||||
export default Filter;
|
||||
@@ -1,4 +0,0 @@
|
||||
export type Props = {
|
||||
value?: string
|
||||
onChange?: (value: string) => void
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Group } from '../../../../../../../components/forms';
|
||||
|
||||
const CustomGroup: React.FC = (props) => (
|
||||
<div className="custom-group">
|
||||
<Group {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
CustomGroup.defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
CustomGroup.propTypes = {
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default CustomGroup;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NestedArrayCustomField = () => <div className="nested-array-custom-field">Nested array custom field</div>;
|
||||
|
||||
export default NestedArrayCustomField;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NestedGroupCustomField = () => <div className="nested-group-custom-field">Nested group custom field</div>;
|
||||
|
||||
export default NestedGroupCustomField;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const NestedText1 = () => <div className="nested-text-1">Nested Text 1</div>;
|
||||
|
||||
export default NestedText1;
|
||||
@@ -1,61 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import SelectInput from '../../../../../../../src/admin/components/forms/field-types/Select/Input';
|
||||
import { Props as SelectFieldType } from '../../../../../../../src/admin/components/forms/field-types/Select/types';
|
||||
import useField from '../../../../../../../src/admin/components/forms/useField';
|
||||
|
||||
const Select: React.FC<SelectFieldType> = (props) => {
|
||||
const {
|
||||
path,
|
||||
name,
|
||||
label,
|
||||
options,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
showError,
|
||||
value,
|
||||
setValue,
|
||||
} = useField({
|
||||
path,
|
||||
});
|
||||
|
||||
const onChange = useCallback((incomingOption) => {
|
||||
const { value: incomingValue } = incomingOption;
|
||||
|
||||
const sendToCRM = async () => {
|
||||
try {
|
||||
const req = await fetch('https://fake-crm.com', {
|
||||
method: 'post',
|
||||
body: JSON.stringify({
|
||||
someKey: incomingValue,
|
||||
}),
|
||||
});
|
||||
|
||||
const res = await req.json();
|
||||
if (res.ok) {
|
||||
console.log('Successfully synced to CRM.'); // eslint-disable-line no-console
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
sendToCRM();
|
||||
setValue(incomingValue);
|
||||
}, [
|
||||
setValue,
|
||||
]);
|
||||
|
||||
return (
|
||||
<SelectInput
|
||||
name={name}
|
||||
label={label}
|
||||
options={options}
|
||||
value={value as string}
|
||||
onChange={onChange}
|
||||
showError={showError}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -1,44 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import TextInput from '../../../../../../../src/admin/components/forms/field-types/Text/Input';
|
||||
import { Props as TextFieldType } from '../../../../../../../src/admin/components/forms/field-types/Text/types';
|
||||
import useField from '../../../../../../../src/admin/components/forms/useField';
|
||||
|
||||
const Text: React.FC<TextFieldType> = (props) => {
|
||||
const {
|
||||
path,
|
||||
name,
|
||||
label,
|
||||
} = props;
|
||||
|
||||
const field = useField({
|
||||
path,
|
||||
enableDebouncedValue: true,
|
||||
});
|
||||
|
||||
const {
|
||||
showError,
|
||||
value,
|
||||
setValue,
|
||||
} = field;
|
||||
|
||||
const onChange = useCallback((e) => {
|
||||
const { value: incomingValue } = e.target;
|
||||
const valueWithoutSpaces = incomingValue.replace(/\s/g, '');
|
||||
setValue(valueWithoutSpaces);
|
||||
}, [
|
||||
setValue,
|
||||
]);
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
path={path}
|
||||
name={name}
|
||||
value={value as string || ''}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
showError={showError}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Text;
|
||||
@@ -1,41 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import TextAreaInput from '../../../../../../../src/admin/components/forms/field-types/Textarea/Input';
|
||||
import { Props as TextFieldType } from '../../../../../../../src/admin/components/forms/field-types/Text/types';
|
||||
import useField from '../../../../../../../src/admin/components/forms/useField';
|
||||
|
||||
const TextArea: React.FC<TextFieldType> = (props) => {
|
||||
const {
|
||||
path,
|
||||
name,
|
||||
label,
|
||||
} = props;
|
||||
|
||||
const field = useField({
|
||||
path,
|
||||
});
|
||||
|
||||
const {
|
||||
showError,
|
||||
value,
|
||||
setValue,
|
||||
} = field;
|
||||
|
||||
const onChange = useCallback((e) => {
|
||||
const { value: incomingValue } = e.target;
|
||||
setValue(incomingValue);
|
||||
}, [
|
||||
setValue,
|
||||
]);
|
||||
|
||||
return (
|
||||
<TextAreaInput
|
||||
name={name}
|
||||
value={value as string || ''}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
showError={showError}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TextArea;
|
||||
@@ -1,50 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import TextInput from '../../../../../../../src/admin/components/forms/field-types/Text';
|
||||
import { UIField as UIFieldType } from '../../../../../../../src/fields/config/types';
|
||||
import SelectInput from '../../../../../../../src/admin/components/forms/field-types/Select';
|
||||
|
||||
const UIField: React.FC<UIFieldType> = () => {
|
||||
const [textValue, setTextValue] = React.useState('');
|
||||
const [selectValue, setSelectValue] = React.useState('');
|
||||
|
||||
const onTextChange = useCallback((incomingValue) => {
|
||||
setTextValue(incomingValue);
|
||||
}, [])
|
||||
|
||||
const onSelectChange = useCallback((incomingValue) => {
|
||||
setSelectValue(incomingValue);
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TextInput
|
||||
name="ui-text"
|
||||
label="Presentation-only text field (does not submit)"
|
||||
value={textValue as string}
|
||||
onChange={onTextChange}
|
||||
/>
|
||||
<SelectInput
|
||||
name="ui-select"
|
||||
label="Presentation-only select field (does not submit)"
|
||||
options={[
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'option-1'
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: 'option-2'
|
||||
},
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: 'option-4'
|
||||
}
|
||||
]}
|
||||
value={selectValue as string}
|
||||
onChange={onSelectChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export default UIField;
|
||||
@@ -1,57 +0,0 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import UploadInput from '../../../../../../../src/admin/components/forms/field-types/Upload/Input';
|
||||
import { Props as UploadFieldType } from '../../../../../../../src/admin/components/forms/field-types/Upload/types';
|
||||
import useField from '../../../../../../../src/admin/components/forms/useField';
|
||||
import { SanitizedCollectionConfig } from '../../../../../../../src/collections/config/types';
|
||||
import { useConfig } from '../../../../../../../src/admin/components/utilities/Config';
|
||||
|
||||
const Text: React.FC<UploadFieldType> = (props) => {
|
||||
const {
|
||||
path,
|
||||
name,
|
||||
label,
|
||||
relationTo,
|
||||
fieldTypes,
|
||||
} = props;
|
||||
|
||||
const {
|
||||
value,
|
||||
setValue,
|
||||
showError,
|
||||
} = useField({
|
||||
path,
|
||||
});
|
||||
|
||||
const onChange = useCallback((incomingValue) => {
|
||||
const incomingID = incomingValue?.id || incomingValue;
|
||||
setValue(incomingID);
|
||||
}, [setValue]);
|
||||
|
||||
const {
|
||||
collections,
|
||||
serverURL,
|
||||
routes: {
|
||||
api,
|
||||
},
|
||||
} = useConfig();
|
||||
|
||||
const collection = collections.find((coll) => coll.slug === relationTo) || undefined;
|
||||
|
||||
return (
|
||||
<UploadInput
|
||||
path={path}
|
||||
relationTo={relationTo}
|
||||
fieldTypes={fieldTypes}
|
||||
name={name}
|
||||
label={label}
|
||||
value={value as string}
|
||||
onChange={onChange}
|
||||
showError={showError}
|
||||
collection={collection as SanitizedCollectionConfig}
|
||||
serverURL={serverURL}
|
||||
api={api}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Text;
|
||||
@@ -1,5 +0,0 @@
|
||||
$color: purple;
|
||||
|
||||
.custom-list {
|
||||
color: $color;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
import DefaultList from '../../../../../../src/admin/components/views/collections/List/Default';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const CustomListView: React.FC = (props) => (
|
||||
<div className="custom-list">
|
||||
<p>This is a custom Pages list view</p>
|
||||
<p>Sup</p>
|
||||
<DefaultList {...props} />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CustomListView;
|
||||
@@ -1,224 +0,0 @@
|
||||
import { CollectionConfig } from '../../../src/collections/config/types';
|
||||
import DescriptionField from './components/fields/Description/Field';
|
||||
import TextField from './components/fields/Text/Field';
|
||||
import SelectField from './components/fields/Select/Field';
|
||||
import TextAreaField from './components/fields/TextArea/Field';
|
||||
import UploadField from './components/fields/Upload/Field';
|
||||
import DescriptionCell from './components/fields/Description/Cell';
|
||||
import DescriptionFilter from './components/fields/Description/Filter';
|
||||
import NestedArrayField from './components/fields/NestedArrayCustomField/Field';
|
||||
import GroupField from './components/fields/Group/Field';
|
||||
import NestedGroupField from './components/fields/NestedGroupCustomField/Field';
|
||||
import NestedText1Field from './components/fields/NestedText1/Field';
|
||||
import UIField from './components/fields/UI/Field';
|
||||
import ListView from './components/views/List';
|
||||
import CustomDescriptionComponent from '../../customComponents/Description';
|
||||
|
||||
const CustomComponents: CollectionConfig = {
|
||||
slug: 'custom-components',
|
||||
labels: {
|
||||
singular: 'Custom Component',
|
||||
plural: 'Custom Components',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'normalText',
|
||||
label: 'Normal text field',
|
||||
type: 'text',
|
||||
// required: true,
|
||||
},
|
||||
{
|
||||
name: 'customText',
|
||||
label: 'Custom text field (removes whitespace)',
|
||||
type: 'text',
|
||||
// required: true,
|
||||
admin: {
|
||||
components: {
|
||||
Field: TextField,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'normalSelect',
|
||||
label: 'Normal select field',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'customSelect',
|
||||
label: 'Custom select field (syncs value with crm)',
|
||||
type: 'select',
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: '1',
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: '2',
|
||||
},
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: '3',
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
components: {
|
||||
Field: SelectField,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'normalTextarea',
|
||||
label: 'Normal textarea field',
|
||||
type: 'textarea',
|
||||
},
|
||||
{
|
||||
name: 'customTextarea',
|
||||
label: 'Custom textarea field',
|
||||
type: 'textarea',
|
||||
admin: {
|
||||
components: {
|
||||
Field: TextAreaField,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ui',
|
||||
label: 'UI',
|
||||
type: 'ui',
|
||||
admin: {
|
||||
components: {
|
||||
Field: UIField,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'normalUpload',
|
||||
label: 'Normal upload field',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
},
|
||||
{
|
||||
name: 'customUpload',
|
||||
label: 'Custom upload field',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
admin: {
|
||||
components: {
|
||||
Field: UploadField,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
admin: {
|
||||
components: {
|
||||
Field: DescriptionField,
|
||||
Cell: DescriptionCell,
|
||||
Filter: DescriptionFilter,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'componentDescription',
|
||||
label: 'Component ViewDescription',
|
||||
type: 'text',
|
||||
admin: {
|
||||
description: CustomDescriptionComponent,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'array',
|
||||
label: 'Array',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'nestedArrayCustomField',
|
||||
label: 'Nested Array Custom Field',
|
||||
admin: {
|
||||
components: {
|
||||
Field: NestedArrayField,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'group',
|
||||
label: 'Group',
|
||||
type: 'group',
|
||||
admin: {
|
||||
components: {
|
||||
Field: GroupField,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'nestedGroupCustomField',
|
||||
label: 'Nested Group Custom Field',
|
||||
admin: {
|
||||
components: {
|
||||
Field: NestedGroupField,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedText1',
|
||||
label: 'Nested Text 1',
|
||||
type: 'text',
|
||||
admin: {
|
||||
components: {
|
||||
Field: NestedText1Field,
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: 'nestedText2',
|
||||
label: 'Nested Text 2',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
components: {
|
||||
views: {
|
||||
List: ListView,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default CustomComponents;
|
||||
@@ -1,22 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const CustomID: CollectionConfig = {
|
||||
slug: 'custom-id',
|
||||
labels: {
|
||||
singular: 'CustomID',
|
||||
plural: 'CustomIDs',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default CustomID;
|
||||
@@ -1,329 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
import Email from '../blocks/Email';
|
||||
import Quote from '../blocks/Quote';
|
||||
import NumberBlock from '../blocks/Number';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
|
||||
const DefaultValues: CollectionConfig = {
|
||||
slug: 'default-values',
|
||||
labels: {
|
||||
singular: 'Default Value Test',
|
||||
plural: 'Default Value Tests',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'text',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
label: 'Text',
|
||||
defaultValue: 'Default Value',
|
||||
unique: true,
|
||||
access: {
|
||||
create: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 'upload',
|
||||
label: 'Image',
|
||||
relationTo: 'media',
|
||||
},
|
||||
{
|
||||
name: 'select',
|
||||
label: 'Select',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}, {
|
||||
value: 'option-4',
|
||||
label: 'Option 4 Label',
|
||||
}],
|
||||
defaultValue: 'option-1',
|
||||
},
|
||||
{
|
||||
name: 'selectMany',
|
||||
label: 'Select w/ hasMany',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Option 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}, {
|
||||
value: 'option-4',
|
||||
label: 'Option 4 Label',
|
||||
}],
|
||||
defaultValue: ['option-1', 'option-4'],
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'radioGroupExample',
|
||||
label: 'Radio Group Example',
|
||||
type: 'radio',
|
||||
options: [{
|
||||
value: 'option-1',
|
||||
label: 'Options 1 Label',
|
||||
}, {
|
||||
value: 'option-2',
|
||||
label: 'Option 2 Label',
|
||||
}, {
|
||||
value: 'option-3',
|
||||
label: 'Option 3 Label',
|
||||
}],
|
||||
defaultValue: 'option-2',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
type: 'email',
|
||||
defaultValue: 'some@email.com',
|
||||
}, {
|
||||
name: 'number',
|
||||
label: 'Number',
|
||||
type: 'number',
|
||||
defaultValue: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
label: 'Group',
|
||||
name: 'group',
|
||||
defaultValue: {
|
||||
nestedText2: 'nested default text 2',
|
||||
nestedText3: 'neat',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'nestedText1',
|
||||
label: 'Nested Text 1',
|
||||
type: 'text',
|
||||
defaultValue: 'this should take priority',
|
||||
},
|
||||
{
|
||||
name: 'nestedText2',
|
||||
label: 'Nested Text 2',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'nestedText3',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Array',
|
||||
name: 'array',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
defaultValue: [
|
||||
{
|
||||
arrayText1: 'Get out',
|
||||
arrayText2: 'Get in',
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'arrayText1',
|
||||
label: 'Array Text 1',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
defaultValue: 'default array text',
|
||||
},
|
||||
{
|
||||
name: 'arrayText2',
|
||||
label: 'Array Text 2',
|
||||
type: 'text',
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
access: {
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'arrayText3',
|
||||
label: 'Array Text 3',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
type: 'checkbox',
|
||||
defaultValue: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'blocks',
|
||||
label: 'Blocks Content',
|
||||
name: 'blocks',
|
||||
labels: {
|
||||
singular: 'Block',
|
||||
plural: 'Blocks',
|
||||
},
|
||||
blocks: [Email, NumberBlock, Quote, CallToAction],
|
||||
localized: true,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
defaultValue: [
|
||||
{
|
||||
blockType: 'email',
|
||||
testEmail: 'dev@payloadcms.com',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to One Collection',
|
||||
name: 'relationship',
|
||||
relationTo: 'conditions',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship hasMany',
|
||||
name: 'relationshipHasMany',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
label: 'Relationship to Multiple Collections',
|
||||
name: 'relationshipMultipleCollections',
|
||||
relationTo: ['localized-posts', 'conditions'],
|
||||
},
|
||||
{
|
||||
type: 'textarea',
|
||||
label: 'Textarea',
|
||||
name: 'textarea',
|
||||
defaultValue: 'my textarea text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
label: 'Slug',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
localized: true,
|
||||
unique: true,
|
||||
defaultValue: 'my-slug',
|
||||
},
|
||||
{
|
||||
name: 'checkbox',
|
||||
type: 'checkbox',
|
||||
label: 'Checkbox',
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
defaultValue: true,
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
label: 'Rich Text',
|
||||
admin: {
|
||||
elements: [
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'blockquote',
|
||||
'ul',
|
||||
'ol',
|
||||
'link',
|
||||
],
|
||||
leaves: [
|
||||
'bold',
|
||||
'italic',
|
||||
'underline',
|
||||
'strikethrough',
|
||||
],
|
||||
},
|
||||
defaultValue: [{
|
||||
children: [{ text: 'Cookin now' }],
|
||||
}],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'asyncArray',
|
||||
defaultValue: () => {
|
||||
return [{ child: 'ok' }];
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'child',
|
||||
type: 'text',
|
||||
defaultValue: () => {
|
||||
return 'async child';
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'asyncText',
|
||||
type: 'text',
|
||||
defaultValue: async (): Promise<string> => {
|
||||
return new Promise((resolve) => setTimeout(() => {
|
||||
resolve('asyncFunction');
|
||||
}, 50));
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'function',
|
||||
type: 'text',
|
||||
defaultValue: (args): string => {
|
||||
const { locale } = args;
|
||||
if (locale === 'en') {
|
||||
return 'function';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
export default DefaultValues;
|
||||
@@ -1,70 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const access = ({ req: { user } }) => {
|
||||
const isAdmin = checkRole(['admin'], user);
|
||||
|
||||
if (isAdmin) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
owner: {
|
||||
equals: user.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const Files: CollectionConfig = {
|
||||
slug: 'files',
|
||||
labels: {
|
||||
singular: 'File',
|
||||
plural: 'Files',
|
||||
},
|
||||
upload: {
|
||||
staticURL: '/files',
|
||||
staticDir: './files',
|
||||
},
|
||||
access: {
|
||||
create: () => true,
|
||||
read: access,
|
||||
update: access,
|
||||
delete: access,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'Type 1',
|
||||
label: 'Type 1 Label',
|
||||
}, {
|
||||
value: 'Type 2',
|
||||
label: 'Type 2 Label',
|
||||
}, {
|
||||
value: 'Type 3',
|
||||
label: 'Type 3 Label',
|
||||
}],
|
||||
defaultValue: 'Type 1',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'owner',
|
||||
label: 'Owner',
|
||||
type: 'relationship',
|
||||
relationTo: 'admins',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
admin: {
|
||||
useAsTitle: 'filename',
|
||||
},
|
||||
};
|
||||
|
||||
export default Files;
|
||||
@@ -1,43 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const HiddenFields: CollectionConfig = {
|
||||
slug: 'hidden-fields',
|
||||
labels: {
|
||||
singular: 'Hidden Fields',
|
||||
plural: 'Hidden Fields',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title - Not Hidden',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'hiddenAdmin',
|
||||
type: 'text',
|
||||
label: 'Hidden on Admin',
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
required: true,
|
||||
defaultValue: 'should be hidden from admin, visible in API',
|
||||
},
|
||||
{
|
||||
name: 'hiddenAPI',
|
||||
type: 'text',
|
||||
label: 'Hidden on API',
|
||||
hidden: true,
|
||||
required: true,
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
({ value }) => {
|
||||
return value || 'should be hidden from API';
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default HiddenFields;
|
||||
@@ -1,98 +0,0 @@
|
||||
/* eslint-disable no-param-reassign, no-console */
|
||||
// If importing outside of demo project, should import CollectionAfterReadHook, CollectionBeforeChangeHook, etc
|
||||
import { AfterChangeHook, AfterDeleteHook, AfterReadHook, BeforeChangeHook, BeforeDeleteHook, BeforeReadHook, CollectionConfig } from '../../src/collections/config/types';
|
||||
import { FieldHook } from '../../src/fields/config/types';
|
||||
import { Hook } from '../payload-types';
|
||||
|
||||
const Hooks: CollectionConfig = {
|
||||
slug: 'hooks',
|
||||
labels: {
|
||||
singular: 'Hook',
|
||||
plural: 'Hooks',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
access: {
|
||||
create: () => true,
|
||||
read: () => true,
|
||||
update: () => true,
|
||||
delete: () => true,
|
||||
},
|
||||
hooks: {
|
||||
beforeRead: [
|
||||
((operation) => {
|
||||
if (operation.req.headers.hook === 'beforeRead') {
|
||||
console.log('before reading Hooks document');
|
||||
}
|
||||
}) as BeforeReadHook<Hook>,
|
||||
],
|
||||
beforeChange: [
|
||||
((operation) => {
|
||||
if (operation.req.headers.hook === 'beforeChange') {
|
||||
operation.data.description += '-beforeChangeSuffix';
|
||||
}
|
||||
return operation.data;
|
||||
}) as BeforeChangeHook<Hook>,
|
||||
],
|
||||
beforeDelete: [
|
||||
((operation) => {
|
||||
if (operation.req.headers.hook === 'beforeDelete') {
|
||||
// TODO: Find a better hook operation to assert against in tests
|
||||
operation.req.headers.hook = 'afterDelete';
|
||||
}
|
||||
}) as BeforeDeleteHook,
|
||||
],
|
||||
afterRead: [
|
||||
((operation) => {
|
||||
const { doc, findMany } = operation;
|
||||
doc.afterReadHook = true;
|
||||
doc.findMany = findMany;
|
||||
|
||||
return doc;
|
||||
}) as AfterReadHook<Hook & { afterReadHook: boolean, findMany: boolean }>,
|
||||
],
|
||||
afterChange: [
|
||||
((operation) => {
|
||||
if (operation.req.headers.hook === 'afterChange') {
|
||||
operation.doc.afterChangeHook = true;
|
||||
}
|
||||
return operation.doc;
|
||||
}) as AfterChangeHook<Hook & { afterChangeHook: boolean }>,
|
||||
],
|
||||
afterDelete: [
|
||||
((operation) => {
|
||||
if (operation.req.headers.hook === 'afterDelete') {
|
||||
operation.doc.afterDeleteHook = true;
|
||||
}
|
||||
return operation.doc;
|
||||
}) as AfterDeleteHook,
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ value }) => (value ? value.toUpperCase() : null) as FieldHook<Hook, 'title'>,
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Hooks;
|
||||
@@ -1,22 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Images: CollectionConfig = {
|
||||
slug: 'images',
|
||||
admin: {
|
||||
description: 'Used to test upload relationship queries',
|
||||
},
|
||||
labels: {
|
||||
singular: 'Image',
|
||||
plural: 'Images',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'upload',
|
||||
type: 'upload',
|
||||
relationTo: 'media',
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Images;
|
||||
@@ -1,38 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const LocalOperations: CollectionConfig = {
|
||||
slug: 'local-operations',
|
||||
labels: {
|
||||
singular: 'Local Operation',
|
||||
plural: 'Local Operations',
|
||||
},
|
||||
hooks: {
|
||||
afterRead: [
|
||||
async ({ req, doc }) => {
|
||||
const formattedData = { ...doc };
|
||||
const localizedPosts = await req.payload.find({
|
||||
collection: 'localized-posts',
|
||||
});
|
||||
|
||||
const blocksGlobal = await req.payload.findGlobal({
|
||||
slug: 'blocks-global',
|
||||
});
|
||||
|
||||
formattedData.localizedPosts = localizedPosts;
|
||||
formattedData.blocksGlobal = blocksGlobal;
|
||||
|
||||
return formattedData;
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'title',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default LocalOperations;
|
||||
@@ -1,146 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { PayloadRequest } from '../../src/express/types';
|
||||
import { Block } from '../../src/fields/config/types';
|
||||
|
||||
const validateLocalizationTransform = (hook: string, value, req: PayloadRequest) => {
|
||||
if (req.locale !== 'all' && value !== undefined && typeof value !== 'string' && value !== null) {
|
||||
console.error(hook, value);
|
||||
throw new Error('Locale transformation should happen before hook is called');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const RichTextBlock: Block = {
|
||||
slug: 'richTextBlock',
|
||||
labels: {
|
||||
singular: 'Rich Text Block',
|
||||
plural: 'Rich Text Blocks',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'content',
|
||||
localized: true,
|
||||
type: 'richText',
|
||||
admin: {
|
||||
hideGutter: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const LocalizedPosts: CollectionConfig = {
|
||||
slug: 'localized-posts',
|
||||
labels: {
|
||||
singular: 'Localized Post',
|
||||
plural: 'Localized Posts',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
defaultColumns: [
|
||||
'title',
|
||||
'priority',
|
||||
'createdAt',
|
||||
],
|
||||
enableRichTextRelationship: true,
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
hooks: {
|
||||
beforeValidate: [({ value, req }) => validateLocalizationTransform('beforeValidate', value, req)],
|
||||
beforeChange: [({ value, req }) => validateLocalizationTransform('beforeChange', value, req)],
|
||||
afterChange: [({ value, req }) => validateLocalizationTransform('afterChange', value, req)],
|
||||
afterRead: [({ value, req }) => validateLocalizationTransform('afterRead', value, req)],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'summary',
|
||||
label: 'Summary',
|
||||
type: 'text',
|
||||
index: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
type: 'richText',
|
||||
name: 'richText',
|
||||
label: 'Rich Text',
|
||||
},
|
||||
{
|
||||
name: 'priority',
|
||||
label: 'Priority',
|
||||
type: 'number',
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'localizedGroup',
|
||||
label: 'Localized Group',
|
||||
type: 'group',
|
||||
localized: true,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
label: 'Text',
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'demoHiddenField',
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'nonLocalizedGroup',
|
||||
label: 'Non-Localized Group',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'text',
|
||||
label: 'Text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Non-Localized Array',
|
||||
name: 'nonLocalizedArray',
|
||||
maxRows: 3,
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'localizedEmbeddedText',
|
||||
label: 'Localized Embedded Text',
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Blocks',
|
||||
name: 'richTextBlocks',
|
||||
type: 'blocks',
|
||||
blocks: [
|
||||
RichTextBlock,
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default LocalizedPosts;
|
||||
@@ -1,79 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import { FieldAccess } from '../../src/fields/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const PublicReadabilityAccess: FieldAccess = ({ req: { user }, siblingData }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (siblingData?.allowPublicReadability) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const LocalizedArrays: CollectionConfig = {
|
||||
slug: 'localized-arrays',
|
||||
labels: {
|
||||
singular: 'Localized Array',
|
||||
plural: 'Localized Arrays',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'array',
|
||||
label: false,
|
||||
name: 'array',
|
||||
localized: true,
|
||||
required: true,
|
||||
minRows: 2,
|
||||
maxRows: 4,
|
||||
fields: [
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'allowPublicReadability',
|
||||
label: 'Allow Public Readability',
|
||||
type: 'checkbox',
|
||||
},
|
||||
{
|
||||
name: 'arrayText1',
|
||||
label: 'Array Text 1',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
access: {
|
||||
read: PublicReadabilityAccess,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'arrayText2',
|
||||
label: 'Array Text 2',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'arrayText3',
|
||||
label: 'Array Text 3',
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default LocalizedArrays;
|
||||
@@ -1,81 +0,0 @@
|
||||
import { CollectionConfig, BeforeChangeHook } from '../../src/collections/config/types';
|
||||
|
||||
const checkForUploadSizesHook: BeforeChangeHook = ({ req: { payloadUploadSizes }, data }) => {
|
||||
if (typeof payloadUploadSizes === 'object') {
|
||||
return {
|
||||
...data,
|
||||
foundUploadSizes: true,
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
labels: {
|
||||
singular: 'Media',
|
||||
plural: 'Media',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
admin: {
|
||||
enableRichTextRelationship: true,
|
||||
description: 'No selfies please',
|
||||
},
|
||||
hooks: {
|
||||
beforeChange: [
|
||||
checkForUploadSizesHook,
|
||||
],
|
||||
},
|
||||
upload: {
|
||||
staticURL: '/media',
|
||||
staticDir: './media',
|
||||
adminThumbnail: ({ doc }) => `/media/${doc.filename}`,
|
||||
imageSizes: [
|
||||
{
|
||||
name: 'maintainedAspectRatio',
|
||||
width: 1024,
|
||||
height: null,
|
||||
crop: 'center',
|
||||
},
|
||||
{
|
||||
name: 'tablet',
|
||||
width: 640,
|
||||
height: 480,
|
||||
crop: 'left top',
|
||||
},
|
||||
{
|
||||
name: 'mobile',
|
||||
width: 320,
|
||||
height: 240,
|
||||
crop: 'left top',
|
||||
},
|
||||
{
|
||||
name: 'icon',
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
],
|
||||
staticOptions: {
|
||||
maxAge: 21600000, // 6 hours in milliseconds
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt Text',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'foundUploadSizes',
|
||||
type: 'checkbox',
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Media;
|
||||
@@ -1,71 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const NestedArray: CollectionConfig = {
|
||||
slug: 'nested-arrays',
|
||||
labels: {
|
||||
singular: 'Nested Array',
|
||||
plural: 'Nested Arrays',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Array',
|
||||
name: 'array',
|
||||
labels: {
|
||||
singular: 'Parent Row',
|
||||
plural: 'Parent Rows',
|
||||
},
|
||||
required: true,
|
||||
minRows: 2,
|
||||
maxRows: 4,
|
||||
fields: [
|
||||
{
|
||||
name: 'parentIdentifier',
|
||||
label: 'Parent Identifier',
|
||||
defaultValue: ' ',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'nestedArray',
|
||||
labels: {
|
||||
singular: 'Child Row',
|
||||
plural: 'Child Rows',
|
||||
},
|
||||
required: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'childIdentifier',
|
||||
label: 'Child Identifier',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
name: 'deeplyNestedArray',
|
||||
labels: {
|
||||
singular: 'Grandchild Row',
|
||||
plural: 'Grandchild Rows',
|
||||
},
|
||||
required: true,
|
||||
fields: [
|
||||
{
|
||||
name: 'grandchildIdentifier',
|
||||
label: 'Grandchild Identifier',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default NestedArray;
|
||||
@@ -1,37 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Preview: CollectionConfig = {
|
||||
slug: 'previewable-post',
|
||||
labels: {
|
||||
singular: 'Previewable Post',
|
||||
plural: 'Previewable Posts',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
preview: async (doc, { token }) => {
|
||||
const { title } = doc;
|
||||
if (title) {
|
||||
const mockAsyncReq = await fetch(`http://localhost:3000/api/previewable-post?depth=0`)
|
||||
const mockJSON = await mockAsyncReq.json();
|
||||
const mockParam = mockJSON?.docs?.[0]?.title || '';
|
||||
return `http://localhost:3000/previewable-posts/${title}?preview=true&token=${token}&mockParam=${mockParam}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
unique: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default Preview;
|
||||
@@ -1,61 +0,0 @@
|
||||
import checkRole from '../access/checkRole';
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const access = ({ req: { user } }) => checkRole(['admin'], user);
|
||||
|
||||
const PublicUsers: CollectionConfig = {
|
||||
slug: 'public-users',
|
||||
labels: {
|
||||
singular: 'Public User',
|
||||
plural: 'Public Users',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
admin: () => false,
|
||||
create: () => true,
|
||||
read: () => true,
|
||||
update: ({ req: { user } }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
id: user.id,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
delete: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
auth: {
|
||||
tokenExpiration: 300,
|
||||
verify: true,
|
||||
maxLoginAttempts: 5,
|
||||
lockTime: 600 * 1000, // lock time in ms
|
||||
cookies: {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
sameSite: 'lax',
|
||||
domain: undefined,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'adminOnly',
|
||||
label: 'This field should only be readable and editable by Admins with "admin" role',
|
||||
type: 'text',
|
||||
defaultValue: 'test',
|
||||
access: {
|
||||
create: access,
|
||||
read: access,
|
||||
update: access,
|
||||
},
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default PublicUsers;
|
||||
@@ -1,86 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const RelationshipA: CollectionConfig = {
|
||||
slug: 'relationship-a',
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
labels: {
|
||||
singular: 'Relationship A',
|
||||
plural: 'Relationship A',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-b',
|
||||
},
|
||||
{
|
||||
name: 'LocalizedPost',
|
||||
label: 'Localized Post',
|
||||
type: 'relationship',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'postLocalizedMultiple',
|
||||
label: 'Localized Post Multiple',
|
||||
type: 'relationship',
|
||||
relationTo: ['localized-posts', 'all-fields', 'custom-id'],
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'postManyRelationships',
|
||||
label: 'Post Many Relationships',
|
||||
type: 'relationship',
|
||||
relationTo: ['relationship-b'],
|
||||
localized: true,
|
||||
hasMany: false,
|
||||
},
|
||||
{
|
||||
name: 'postMaxDepth',
|
||||
maxDepth: 0,
|
||||
label: 'Post With MaxDepth',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-b',
|
||||
hasMany: false,
|
||||
},
|
||||
{
|
||||
name: 'customID',
|
||||
label: 'CustomID Relation',
|
||||
type: 'relationship',
|
||||
relationTo: 'custom-id',
|
||||
hasMany: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'filterRelationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-b',
|
||||
filterOptions: {
|
||||
disableRelation: {
|
||||
not_equals: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'files',
|
||||
type: 'upload',
|
||||
relationTo: 'files',
|
||||
filterOptions: {
|
||||
type: { equals: 'Type 2' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'demoHiddenField',
|
||||
type: 'text',
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default RelationshipA;
|
||||
@@ -1,65 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const RelationshipB: CollectionConfig = {
|
||||
slug: 'relationship-b',
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
labels: {
|
||||
singular: 'Relationship B',
|
||||
plural: 'Relationship B',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'disableRelation', // used on RelationshipA.filterRelationship field
|
||||
type: 'checkbox',
|
||||
required: true,
|
||||
admin: {
|
||||
position: 'sidebar',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'post',
|
||||
label: 'Post',
|
||||
type: 'relationship',
|
||||
relationTo: 'relationship-a',
|
||||
localized: false,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'postManyRelationships',
|
||||
label: 'Post Many Relationships',
|
||||
type: 'relationship',
|
||||
relationTo: ['relationship-a', 'media'],
|
||||
localized: true,
|
||||
hasMany: false,
|
||||
},
|
||||
{
|
||||
name: 'localizedPosts',
|
||||
label: 'Localized Posts',
|
||||
type: 'relationship',
|
||||
hasMany: true,
|
||||
relationTo: ['localized-posts', 'previewable-post'],
|
||||
},
|
||||
{
|
||||
name: 'nonLocalizedRelationToMany',
|
||||
type: 'relationship',
|
||||
relationTo: ['localized-posts', 'relationship-a'],
|
||||
},
|
||||
{
|
||||
name: 'strictAccess',
|
||||
type: 'relationship',
|
||||
relationTo: 'strict-access',
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default RelationshipB;
|
||||
@@ -1,112 +0,0 @@
|
||||
import Button from '../client/components/richText/elements/Button';
|
||||
import PurpleBackground from '../client/components/richText/leaves/PurpleBackground';
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const RichText: CollectionConfig = {
|
||||
slug: 'rich-text',
|
||||
labels: {
|
||||
singular: 'Rich Text',
|
||||
plural: 'Rich Texts',
|
||||
},
|
||||
access: {
|
||||
read: ({ req }) => {
|
||||
if (req.user) return true;
|
||||
|
||||
return {
|
||||
_status: {
|
||||
equals: 'published',
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
versions: {
|
||||
drafts: {
|
||||
autosave: false,
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
preview: () => 'https://payloadcms.com',
|
||||
useAsTitle: 'title',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'defaultRichText',
|
||||
type: 'richText',
|
||||
label: 'Default Rich Text',
|
||||
required: true,
|
||||
admin: {
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
type: 'richText',
|
||||
name: 'caption',
|
||||
label: 'Caption',
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'admins',
|
||||
name: 'linkToAdmin',
|
||||
label: 'Link to Admin',
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
name: 'imageAlignment',
|
||||
label: 'Image Alignment',
|
||||
options: [
|
||||
{
|
||||
label: 'Left',
|
||||
value: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Center',
|
||||
value: 'center',
|
||||
},
|
||||
{
|
||||
label: 'Right',
|
||||
value: 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'wrapText',
|
||||
label: 'Wrap Text',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'customRichText',
|
||||
type: 'richText',
|
||||
label: 'Customized Rich Text',
|
||||
required: true,
|
||||
admin: {
|
||||
elements: [
|
||||
'h2',
|
||||
'h3',
|
||||
Button,
|
||||
],
|
||||
leaves: [
|
||||
'bold',
|
||||
PurpleBackground,
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default RichText;
|
||||
@@ -1,99 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Select: CollectionConfig = {
|
||||
slug: 'select',
|
||||
labels: {
|
||||
singular: 'Select',
|
||||
plural: 'Selects',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'select',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}, {
|
||||
value: 'three',
|
||||
label: 'Three',
|
||||
}],
|
||||
label: 'Select From',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'selectHasMany',
|
||||
type: 'select',
|
||||
options: [{
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}, {
|
||||
value: 'three',
|
||||
label: 'Three',
|
||||
}],
|
||||
label: 'Select HasMany',
|
||||
required: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'selectJustStrings',
|
||||
type: 'select',
|
||||
options: ['blue', 'green', 'yellow'],
|
||||
label: 'Select Just Strings',
|
||||
required: true,
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'selectWithEmptyString',
|
||||
type: 'select',
|
||||
defaultValue: '',
|
||||
options: [{
|
||||
value: '',
|
||||
label: 'None',
|
||||
}, {
|
||||
value: 'option',
|
||||
label: 'Option',
|
||||
}],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'radio',
|
||||
type: 'radio',
|
||||
options: [{
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}, {
|
||||
value: 'three',
|
||||
label: 'Three',
|
||||
}],
|
||||
label: 'Choose From',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'radioWithEmptyString',
|
||||
type: 'radio',
|
||||
defaultValue: '',
|
||||
options: [{
|
||||
value: '',
|
||||
label: 'None',
|
||||
}, {
|
||||
value: 'one',
|
||||
label: 'One',
|
||||
}, {
|
||||
value: 'two',
|
||||
label: 'Two',
|
||||
}],
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -1,76 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const StrictAccess: CollectionConfig = {
|
||||
slug: 'strict-access',
|
||||
labels: {
|
||||
singular: 'Strict Access',
|
||||
plural: 'Strict Access',
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: 'address',
|
||||
},
|
||||
access: {
|
||||
create: () => true,
|
||||
read: ({ req: { user } }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
owner: {
|
||||
equals: user.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
update: ({ req: { user } }) => {
|
||||
if (checkRole(['admin'], user)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
return {
|
||||
owner: {
|
||||
equals: user.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
delete: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'address',
|
||||
type: 'text',
|
||||
label: 'Address',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'city',
|
||||
type: 'text',
|
||||
label: 'City',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'state',
|
||||
type: 'text',
|
||||
label: 'State',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'zip',
|
||||
type: 'number',
|
||||
label: 'ZIP Code',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
timestamps: true,
|
||||
};
|
||||
|
||||
export default StrictAccess;
|
||||
@@ -1,25 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Uniques: CollectionConfig = {
|
||||
slug: 'uniques',
|
||||
labels: {
|
||||
singular: 'Unique',
|
||||
plural: 'Uniques',
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
label: 'Title',
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'textarea',
|
||||
label: 'Description',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Uniques;
|
||||
@@ -1,35 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const UnstoredMedia: CollectionConfig = {
|
||||
slug: 'unstored-media',
|
||||
labels: {
|
||||
singular: 'Unstored Media',
|
||||
plural: 'Unstored Media',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
upload: {
|
||||
staticURL: '/unstored-media',
|
||||
disableLocalStorage: true,
|
||||
imageSizes: [
|
||||
{
|
||||
name: 'tablet',
|
||||
width: 640,
|
||||
height: 480,
|
||||
crop: 'left top',
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt Text',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default UnstoredMedia;
|
||||
@@ -1,141 +0,0 @@
|
||||
import { CollectionConfig } from '../../src/collections/config/types';
|
||||
|
||||
const Validations: CollectionConfig = {
|
||||
slug: 'validations',
|
||||
labels: {
|
||||
singular: 'Validation',
|
||||
plural: 'Validations',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'validationOptions',
|
||||
type: 'text',
|
||||
label: 'Text with siblingData Validation',
|
||||
required: true,
|
||||
validate: (value: string, { data, siblingData, id, operation, user }) => {
|
||||
if (typeof value === 'undefined') {
|
||||
return 'Validation is missing value';
|
||||
}
|
||||
if (data?.text !== 'test') {
|
||||
return 'The next field should be test';
|
||||
}
|
||||
if (siblingData?.text !== 'test') {
|
||||
return 'The next field should be test';
|
||||
}
|
||||
if (!user) {
|
||||
return 'ValidationOptions is missing "user"';
|
||||
}
|
||||
if (typeof operation === 'undefined') {
|
||||
return 'ValidationOptions is missing "operation"';
|
||||
}
|
||||
if (operation === 'update' && typeof id === 'undefined') {
|
||||
return 'ValidationOptions is missing "id"';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
label: 'Text',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value === 'test';
|
||||
|
||||
if (!result) {
|
||||
return 'The only accepted value of this field is "test".';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'lessThan10',
|
||||
label: 'Less than 10',
|
||||
type: 'number',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = parseInt(value, 10) < 10;
|
||||
|
||||
if (!result) {
|
||||
return 'The value of this field needs to be less than 10.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}, {
|
||||
name: 'greaterThan10LessThan50',
|
||||
label: 'Greater than 10, Less than 50',
|
||||
type: 'number',
|
||||
required: true,
|
||||
min: 10,
|
||||
max: 50,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Should have at least 3 rows',
|
||||
name: 'atLeast3Rows',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value >= 3;
|
||||
|
||||
if (!result) {
|
||||
return 'This array needs to have at least 3 rows.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'greaterThan30',
|
||||
label: 'Number should be greater than 30',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value > 30;
|
||||
|
||||
if (!result) {
|
||||
return 'This value of this field needs to be greater than 30.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
label: 'Default array validation',
|
||||
name: 'array',
|
||||
required: true,
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'lessThan20',
|
||||
label: 'Number should be less than 20',
|
||||
required: true,
|
||||
validate: (value) => {
|
||||
const result = value < 20;
|
||||
|
||||
if (!result) {
|
||||
return 'This value of this field needs to be less than 20.';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Validations;
|
||||
@@ -1,15 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="application-name" content="My Payload Application" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<div id="portal"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CollectionDescription: React.FC = () => (
|
||||
<div>
|
||||
Collection description
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CollectionDescription;
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomDescriptionComponent: React.FC = ({ value }) => (
|
||||
<div>
|
||||
Character count:
|
||||
{' '}
|
||||
{ value?.length || 0 }
|
||||
</div>
|
||||
);
|
||||
|
||||
export default CustomDescriptionComponent;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomAccountView: React.FC = () => <div>fake account view</div>;
|
||||
|
||||
export default CustomAccountView;
|
||||
@@ -1,5 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
const CustomDashboardView: React.FC = () => <div>fake dashboard view</div>;
|
||||
|
||||
export default CustomDashboardView;
|
||||
@@ -1,43 +0,0 @@
|
||||
import checkRole from '../access/checkRole';
|
||||
import Quote from '../blocks/Quote';
|
||||
import CallToAction from '../blocks/CallToAction';
|
||||
import { GlobalConfig } from '../../src/globals/config/types';
|
||||
|
||||
const BlocksGlobal: GlobalConfig = {
|
||||
slug: 'blocks-global',
|
||||
label: 'Blocks Global',
|
||||
versions: {
|
||||
max: 20,
|
||||
drafts: {
|
||||
autosave: true,
|
||||
},
|
||||
},
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ draft, req: { user } }) => {
|
||||
// To read a draft of this global, you need to be authenticated
|
||||
if (draft) {
|
||||
return Boolean(user);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
localized: true,
|
||||
},
|
||||
{
|
||||
name: 'blocks',
|
||||
label: 'Blocks',
|
||||
type: 'blocks',
|
||||
blocks: [Quote, CallToAction],
|
||||
localized: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default BlocksGlobal;
|
||||
@@ -1,37 +0,0 @@
|
||||
import { GlobalConfig } from '../../src/globals/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
|
||||
const GlobalWithAccess: GlobalConfig = {
|
||||
slug: 'global-with-access',
|
||||
label: 'Global with Strict Access',
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Site Title',
|
||||
type: 'text',
|
||||
maxLength: 100,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'relationship',
|
||||
label: 'Test Relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'localized-posts',
|
||||
hasMany: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'singleRelationship',
|
||||
label: 'Test Single Relationship',
|
||||
type: 'relationship',
|
||||
relationTo: 'localized-posts',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default GlobalWithAccess;
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Response } from 'express';
|
||||
import { GlobalConfig } from '../../src/globals/config/types';
|
||||
import checkRole from '../access/checkRole';
|
||||
import { NavigationArray as TNavigationArray } from '../payload-types';
|
||||
import { PayloadRequest } from '../../src/express/types';
|
||||
|
||||
const NavigationArray: GlobalConfig = {
|
||||
slug: 'navigation-array',
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin', 'user'], user),
|
||||
read: () => true,
|
||||
},
|
||||
admin: {
|
||||
description: 'A description for the editor',
|
||||
},
|
||||
endpoints: [
|
||||
{
|
||||
path: '/count',
|
||||
method: 'get',
|
||||
handler: async (req: PayloadRequest, res: Response): Promise<void> => {
|
||||
const { array } = await req.payload.findGlobal<TNavigationArray>({
|
||||
slug: 'navigation-array',
|
||||
});
|
||||
|
||||
res.json({ count: array.length });
|
||||
},
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'array',
|
||||
label: 'Array',
|
||||
type: 'array',
|
||||
localized: true,
|
||||
fields: [{
|
||||
name: 'text',
|
||||
label: 'Text',
|
||||
type: 'text',
|
||||
}, {
|
||||
name: 'textarea',
|
||||
label: 'Textarea',
|
||||
type: 'textarea',
|
||||
}],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default NavigationArray;
|
||||
@@ -1,14 +0,0 @@
|
||||
const babelConfig = require('../babel.config');
|
||||
|
||||
require('@babel/register')({
|
||||
...babelConfig,
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
env: {
|
||||
development: {
|
||||
sourceMaps: 'inline',
|
||||
retainLines: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
require('./server.ts');
|
||||
@@ -1,760 +0,0 @@
|
||||
/* tslint:disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload CMS.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
export interface Config {}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "blocks-global".
|
||||
*/
|
||||
export interface BlocksGlobal {
|
||||
id: string;
|
||||
_status?: 'draft' | 'published';
|
||||
title: string;
|
||||
blocks?: (
|
||||
| {
|
||||
quote: string;
|
||||
color: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'quote';
|
||||
}
|
||||
| {
|
||||
label: string;
|
||||
url: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "navigation-array".
|
||||
*/
|
||||
export interface NavigationArray {
|
||||
id: string;
|
||||
array?: {
|
||||
text?: string;
|
||||
textarea?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "global-with-access".
|
||||
*/
|
||||
export interface GlobalWithStrictAccess {
|
||||
id: string;
|
||||
title: string;
|
||||
relationship: (string | LocalizedPost)[];
|
||||
singleRelationship: string | LocalizedPost;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "localized-posts".
|
||||
*/
|
||||
export interface LocalizedPost {
|
||||
id: string;
|
||||
title: string;
|
||||
summary?: string;
|
||||
description: string;
|
||||
richText?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
priority?: number;
|
||||
localizedGroup?: {
|
||||
text?: string;
|
||||
demoHiddenField?: string;
|
||||
};
|
||||
nonLocalizedGroup?: {
|
||||
text?: string;
|
||||
};
|
||||
nonLocalizedArray?: {
|
||||
localizedEmbeddedText?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
richTextBlocks?: {
|
||||
content?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'richTextBlock';
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "admins".
|
||||
*/
|
||||
export interface Admin {
|
||||
id: string;
|
||||
email?: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
enableAPIKey?: boolean;
|
||||
apiKey?: string;
|
||||
apiKeyIndex?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
roles: ('admin' | 'editor' | 'moderator' | 'user' | 'viewer')[];
|
||||
publicUser?: (string | PublicUser)[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "public-users".
|
||||
*/
|
||||
export interface PublicUser {
|
||||
id: string;
|
||||
email?: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
_verified?: boolean;
|
||||
_verificationToken?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
adminOnly?: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "all-fields".
|
||||
*/
|
||||
export interface AllFields {
|
||||
id: string;
|
||||
text: string;
|
||||
descriptionText?: string;
|
||||
descriptionFunction?: string;
|
||||
image?: string | Media;
|
||||
select: 'option-1' | 'option-2' | 'option-3' | 'option-4';
|
||||
selectMany: ('option-1' | 'option-2' | 'option-3' | 'option-4')[];
|
||||
dayOnlyDateFieldExample: string;
|
||||
timeOnlyDateFieldExample?: string;
|
||||
point?: [number, number];
|
||||
radioGroupExample: 'option-1' | 'option-2' | 'option-3';
|
||||
email?: string;
|
||||
number?: number;
|
||||
group?: {
|
||||
nestedText1?: string;
|
||||
nestedText2?: string;
|
||||
};
|
||||
array?: {
|
||||
arrayText1: string;
|
||||
arrayText2: string;
|
||||
arrayText3?: string;
|
||||
checkbox?: boolean;
|
||||
id?: string;
|
||||
}[];
|
||||
readOnlyArray?: {
|
||||
text?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
blocks: (
|
||||
| {
|
||||
testEmail: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'email';
|
||||
}
|
||||
| {
|
||||
testNumber: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}
|
||||
| {
|
||||
quote: string;
|
||||
color: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'quote';
|
||||
}
|
||||
| {
|
||||
label: string;
|
||||
url: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[];
|
||||
relationship?: string | Conditions;
|
||||
relationshipHasMany?: (string | LocalizedPost)[];
|
||||
relationshipMultipleCollections?:
|
||||
| {
|
||||
value: string | LocalizedPost;
|
||||
relationTo: 'localized-posts';
|
||||
}
|
||||
| {
|
||||
value: string | Conditions;
|
||||
relationTo: 'conditions';
|
||||
};
|
||||
textarea?: string;
|
||||
richText: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
slug: string;
|
||||
checkbox?: boolean;
|
||||
dateFieldExample?: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "media".
|
||||
*/
|
||||
export interface Media {
|
||||
id: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
maintainedAspectRatio?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
tablet?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
mobile?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
icon?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
alt: string;
|
||||
foundUploadSizes?: boolean;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "conditions".
|
||||
*/
|
||||
export interface Conditions {
|
||||
id: string;
|
||||
title: string;
|
||||
enableTest?: boolean;
|
||||
number?: number;
|
||||
simpleCondition: string;
|
||||
orCondition: string;
|
||||
nestedConditions?: string;
|
||||
blocks: (
|
||||
| {
|
||||
testEmail: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'email';
|
||||
}
|
||||
| {
|
||||
testNumber: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}
|
||||
| {
|
||||
quote: string;
|
||||
color: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'quote';
|
||||
}
|
||||
| {
|
||||
label: string;
|
||||
url: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[];
|
||||
customComponent: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "auto-label".
|
||||
*/
|
||||
export interface AutoLabel {
|
||||
id: string;
|
||||
autoLabelField?: string;
|
||||
noLabel?: string;
|
||||
labelOverride?: string;
|
||||
testRelationship?: string | AllFields;
|
||||
specialBlock?: {
|
||||
testNumber?: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}[];
|
||||
noLabelBlock?: {
|
||||
testNumber?: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}[];
|
||||
items?: {
|
||||
itemName?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
noLabelArray?: {
|
||||
textField?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "autosave-posts".
|
||||
*/
|
||||
export interface AutosavePost {
|
||||
id: string;
|
||||
_status?: 'draft' | 'published';
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "blocks".
|
||||
*/
|
||||
export interface Blocks {
|
||||
id: string;
|
||||
_status?: 'draft' | 'published';
|
||||
layout: (
|
||||
| {
|
||||
testEmail: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'email';
|
||||
}
|
||||
| {
|
||||
testNumber: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}
|
||||
| {
|
||||
quote: string;
|
||||
color: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'quote';
|
||||
}
|
||||
| {
|
||||
label: string;
|
||||
url: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[];
|
||||
nonLocalizedLayout: (
|
||||
| {
|
||||
testEmail: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'email';
|
||||
}
|
||||
| {
|
||||
testNumber: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}
|
||||
| {
|
||||
quote: string;
|
||||
color: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'quote';
|
||||
}
|
||||
| {
|
||||
label: string;
|
||||
url: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "code".
|
||||
*/
|
||||
export interface Code {
|
||||
id: string;
|
||||
code: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "custom-id".
|
||||
*/
|
||||
export interface CustomID {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "default-values".
|
||||
*/
|
||||
export interface DefaultValueTest {
|
||||
id: string;
|
||||
text?: string;
|
||||
image?: string | Media;
|
||||
select?: 'option-1' | 'option-2' | 'option-3' | 'option-4';
|
||||
selectMany?: ('option-1' | 'option-2' | 'option-3' | 'option-4')[];
|
||||
radioGroupExample?: 'option-1' | 'option-2' | 'option-3';
|
||||
email?: string;
|
||||
number?: number;
|
||||
group?: {
|
||||
nestedText1?: string;
|
||||
nestedText2?: string;
|
||||
nestedText3?: string;
|
||||
};
|
||||
array?: {
|
||||
arrayText1?: string;
|
||||
arrayText2?: string;
|
||||
arrayText3?: string;
|
||||
checkbox?: boolean;
|
||||
id?: string;
|
||||
}[];
|
||||
blocks?: (
|
||||
| {
|
||||
testEmail: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'email';
|
||||
}
|
||||
| {
|
||||
testNumber: number;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'number';
|
||||
}
|
||||
| {
|
||||
quote: string;
|
||||
color: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'quote';
|
||||
}
|
||||
| {
|
||||
label: string;
|
||||
url: string;
|
||||
id?: string;
|
||||
blockName?: string;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[];
|
||||
relationship?: string | Conditions;
|
||||
relationshipHasMany?: (string | LocalizedPost)[];
|
||||
relationshipMultipleCollections?:
|
||||
| {
|
||||
value: string | LocalizedPost;
|
||||
relationTo: 'localized-posts';
|
||||
}
|
||||
| {
|
||||
value: string | Conditions;
|
||||
relationTo: 'conditions';
|
||||
};
|
||||
textarea?: string;
|
||||
slug?: string;
|
||||
checkbox?: boolean;
|
||||
richText?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
asyncArray?: {
|
||||
child?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
asyncText?: string;
|
||||
function?: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "endpoints".
|
||||
*/
|
||||
export interface Endpoint {
|
||||
id: string;
|
||||
title?: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "files".
|
||||
*/
|
||||
export interface File {
|
||||
id: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
type: 'Type 1' | 'Type 2' | 'Type 3';
|
||||
owner: string | Admin;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "geolocation".
|
||||
*/
|
||||
export interface Geolocation {
|
||||
id: string;
|
||||
location?: [number, number];
|
||||
localizedPoint?: [number, number];
|
||||
group?: {
|
||||
point?: [number, number];
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "hidden-fields".
|
||||
*/
|
||||
export interface HiddenFields {
|
||||
id: string;
|
||||
title: string;
|
||||
hiddenAdmin: string;
|
||||
hiddenAPI: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "hooks".
|
||||
*/
|
||||
export interface Hook {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "localized-arrays".
|
||||
*/
|
||||
export interface LocalizedArray {
|
||||
id: string;
|
||||
array: {
|
||||
allowPublicReadability?: boolean;
|
||||
arrayText1: string;
|
||||
arrayText2: string;
|
||||
arrayText3?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "local-operations".
|
||||
*/
|
||||
export interface LocalOperation {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "images".
|
||||
*/
|
||||
export interface Image {
|
||||
id: string;
|
||||
upload?: string | Media;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "nested-arrays".
|
||||
*/
|
||||
export interface NestedArray {
|
||||
id: string;
|
||||
array: {
|
||||
parentIdentifier: string;
|
||||
nestedArray: {
|
||||
childIdentifier: string;
|
||||
deeplyNestedArray: {
|
||||
grandchildIdentifier?: string;
|
||||
id?: string;
|
||||
}[];
|
||||
id?: string;
|
||||
}[];
|
||||
id?: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "previewable-post".
|
||||
*/
|
||||
export interface PreviewablePost {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "relationship-a".
|
||||
*/
|
||||
export interface RelationshipA {
|
||||
id: string;
|
||||
post?: string | RelationshipB;
|
||||
LocalizedPost?: (string | LocalizedPost)[];
|
||||
postLocalizedMultiple?: (
|
||||
| {
|
||||
value: string | LocalizedPost;
|
||||
relationTo: 'localized-posts';
|
||||
}
|
||||
| {
|
||||
value: string | AllFields;
|
||||
relationTo: 'all-fields';
|
||||
}
|
||||
| {
|
||||
value: number | CustomID;
|
||||
relationTo: 'custom-id';
|
||||
}
|
||||
)[];
|
||||
postManyRelationships?: {
|
||||
value: string | RelationshipB;
|
||||
relationTo: 'relationship-b';
|
||||
};
|
||||
postMaxDepth?: string | RelationshipB;
|
||||
customID?: (number | CustomID)[];
|
||||
filterRelationship?: string | RelationshipB;
|
||||
files?: string | File;
|
||||
demoHiddenField?: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "relationship-b".
|
||||
*/
|
||||
export interface RelationshipB {
|
||||
id: string;
|
||||
title?: string;
|
||||
disableRelation: boolean;
|
||||
post?: (string | RelationshipA)[];
|
||||
postManyRelationships?:
|
||||
| {
|
||||
value: string | RelationshipA;
|
||||
relationTo: 'relationship-a';
|
||||
}
|
||||
| {
|
||||
value: string | Media;
|
||||
relationTo: 'media';
|
||||
};
|
||||
localizedPosts?: (
|
||||
| {
|
||||
value: string | LocalizedPost;
|
||||
relationTo: 'localized-posts';
|
||||
}
|
||||
| {
|
||||
value: string | PreviewablePost;
|
||||
relationTo: 'previewable-post';
|
||||
}
|
||||
)[];
|
||||
nonLocalizedRelationToMany?:
|
||||
| {
|
||||
value: string | LocalizedPost;
|
||||
relationTo: 'localized-posts';
|
||||
}
|
||||
| {
|
||||
value: string | RelationshipA;
|
||||
relationTo: 'relationship-a';
|
||||
};
|
||||
strictAccess?: string | StrictAccess;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "strict-access".
|
||||
*/
|
||||
export interface StrictAccess {
|
||||
id: string;
|
||||
address: string;
|
||||
city: string;
|
||||
state: string;
|
||||
zip: number;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "rich-text".
|
||||
*/
|
||||
export interface RichText {
|
||||
id: string;
|
||||
_status?: 'draft' | 'published';
|
||||
title?: string;
|
||||
defaultRichText: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
customRichText: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "select".
|
||||
*/
|
||||
export interface Select {
|
||||
id: string;
|
||||
select: 'one' | 'two' | 'three';
|
||||
selectHasMany: ('one' | 'two' | 'three')[];
|
||||
selectJustStrings: ('blue' | 'green' | 'yellow')[];
|
||||
selectWithEmptyString: '' | 'option';
|
||||
radio: 'one' | 'two' | 'three';
|
||||
radioWithEmptyString: '' | 'one' | 'two';
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "validations".
|
||||
*/
|
||||
export interface Validation {
|
||||
id: string;
|
||||
validationOptions: string;
|
||||
text: string;
|
||||
lessThan10: number;
|
||||
greaterThan10LessThan50: number;
|
||||
atLeast3Rows: {
|
||||
greaterThan30: number;
|
||||
id?: string;
|
||||
}[];
|
||||
array: {
|
||||
lessThan20: number;
|
||||
id?: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "uniques".
|
||||
*/
|
||||
export interface Unique {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "unstored-media".
|
||||
*/
|
||||
export interface UnstoredMedia {
|
||||
id: string;
|
||||
url?: string;
|
||||
filename?: string;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
sizes?: {
|
||||
tablet?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
alt: string;
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
import path from 'path';
|
||||
import { buildConfig } from '../src/config/build';
|
||||
|
||||
import Admin from './collections/Admin';
|
||||
import AllFields from './collections/AllFields';
|
||||
import AutoLabel from './collections/AutoLabel';
|
||||
import Autosave from './collections/Autosave';
|
||||
import Code from './collections/Code';
|
||||
import Conditions from './collections/Conditions';
|
||||
// import CustomComponents from './collections/CustomComponents';
|
||||
import Endpoints from './collections/Endpoints';
|
||||
import File from './collections/File';
|
||||
import Blocks from './collections/Blocks';
|
||||
import CustomID from './collections/CustomID';
|
||||
import DefaultValues from './collections/DefaultValues';
|
||||
import HiddenFields from './collections/HiddenFields';
|
||||
import Hooks from './collections/Hooks';
|
||||
import Localized from './collections/Localized';
|
||||
import LocalizedArray from './collections/LocalizedArray';
|
||||
import LocalOperations from './collections/LocalOperations';
|
||||
import Media from './collections/Media';
|
||||
import Images from './collections/Images';
|
||||
import NestedArrays from './collections/NestedArrays';
|
||||
import Preview from './collections/Preview';
|
||||
import PublicUsers from './collections/PublicUsers';
|
||||
import RelationshipA from './collections/RelationshipA';
|
||||
import RelationshipB from './collections/RelationshipB';
|
||||
import RichText from './collections/RichText';
|
||||
import Select from './collections/Select';
|
||||
import StrictAccess from './collections/StrictAccess';
|
||||
import Validations from './collections/Validations';
|
||||
import Uniques from './collections/Uniques';
|
||||
import Geolocation from './collections/Geolocation';
|
||||
|
||||
import BlocksGlobal from './globals/BlocksGlobal';
|
||||
import NavigationArray from './globals/NavigationArray';
|
||||
import GlobalWithStrictAccess from './globals/GlobalWithStrictAccess';
|
||||
import UnstoredMedia from './collections/UnstoredMedia';
|
||||
import CustomRouteWithMinimalTemplate from './client/components/views/CustomMinimal';
|
||||
import CustomRouteWithDefaultTemplate from './client/components/views/CustomDefault';
|
||||
import AfterDashboard from './client/components/AfterDashboard';
|
||||
import AfterNavLinks from './client/components/AfterNavLinks';
|
||||
import BeforeLogin from './client/components/BeforeLogin';
|
||||
// import CustomProvider from './client/components/CustomProvider';
|
||||
|
||||
export default buildConfig({
|
||||
cookiePrefix: 'payload',
|
||||
serverURL: 'http://localhost:3000',
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, './payload-types.ts'),
|
||||
},
|
||||
admin: {
|
||||
user: 'admins',
|
||||
indexHTML: path.resolve(__dirname, './client/index.html'),
|
||||
// meta: {
|
||||
// titleSuffix: '- Payload Demo',
|
||||
// // ogImage: '/static/find-image-here.jpg',
|
||||
// // favicon: '/img/whatever.png',
|
||||
// },
|
||||
// disable: true,
|
||||
scss: path.resolve(__dirname, './client/scss/overrides.scss'),
|
||||
components: {
|
||||
// providers: [CustomProvider, CustomProvider],
|
||||
routes: [
|
||||
{
|
||||
path: '/custom-minimal-route',
|
||||
Component: CustomRouteWithMinimalTemplate,
|
||||
},
|
||||
{
|
||||
path: '/custom-default-route',
|
||||
Component: CustomRouteWithDefaultTemplate,
|
||||
},
|
||||
],
|
||||
afterDashboard: [
|
||||
AfterDashboard,
|
||||
],
|
||||
beforeLogin: [
|
||||
BeforeLogin,
|
||||
],
|
||||
afterNavLinks: [
|
||||
AfterNavLinks,
|
||||
],
|
||||
// Nav: () => (
|
||||
// <div>Hello</div>
|
||||
// ),
|
||||
views: {
|
||||
// Dashboard: CustomDashboardView,
|
||||
// Account: CustomAccountView,
|
||||
},
|
||||
},
|
||||
webpack: (config) => config,
|
||||
},
|
||||
collections: [
|
||||
Admin,
|
||||
AllFields,
|
||||
AutoLabel,
|
||||
Autosave,
|
||||
Blocks,
|
||||
Code,
|
||||
Conditions,
|
||||
// CustomComponents,
|
||||
CustomID,
|
||||
DefaultValues,
|
||||
Endpoints,
|
||||
File,
|
||||
Geolocation,
|
||||
HiddenFields,
|
||||
Hooks,
|
||||
Localized,
|
||||
LocalizedArray,
|
||||
LocalOperations,
|
||||
Media,
|
||||
Images,
|
||||
NestedArrays,
|
||||
Preview,
|
||||
PublicUsers,
|
||||
RelationshipA,
|
||||
RelationshipB,
|
||||
RichText,
|
||||
Select,
|
||||
StrictAccess,
|
||||
Validations,
|
||||
Uniques,
|
||||
UnstoredMedia,
|
||||
],
|
||||
globals: [
|
||||
BlocksGlobal,
|
||||
NavigationArray,
|
||||
GlobalWithStrictAccess,
|
||||
],
|
||||
// cors: [
|
||||
// 'http://localhost',
|
||||
// 'http://localhost:3000',
|
||||
// 'http://localhost:8080',
|
||||
// 'http://localhost:8081',
|
||||
// ],
|
||||
// csrf: [
|
||||
// 'http://localhost:3000',
|
||||
// 'https://other-app-here.com',
|
||||
// ],
|
||||
routes: {
|
||||
api: '/api',
|
||||
admin: '/admin',
|
||||
graphQL: '/graphql',
|
||||
graphQLPlayground: '/graphql-playground',
|
||||
},
|
||||
defaultDepth: 2,
|
||||
graphQL: {
|
||||
maxComplexity: 1000,
|
||||
disablePlaygroundInProduction: false,
|
||||
disable: false,
|
||||
},
|
||||
// rateLimit: {
|
||||
// window: 15 * 60 * 100,
|
||||
// max: 100,
|
||||
// trustProxy: true,
|
||||
// skip: (req) => req.ip === '127.0.0.1',
|
||||
// },
|
||||
maxDepth: 10,
|
||||
localization: {
|
||||
locales: [
|
||||
'en',
|
||||
'es',
|
||||
],
|
||||
defaultLocale: 'en',
|
||||
fallback: true,
|
||||
},
|
||||
// indexSortableFields: true,
|
||||
hooks: {
|
||||
afterError: (err) => {
|
||||
if (process.env.DISABLE_LOGGING !== 'true') {
|
||||
console.error('global error config handler', err);
|
||||
}
|
||||
},
|
||||
},
|
||||
upload: {
|
||||
limits: {
|
||||
fileSize: 10000000, // 10MB
|
||||
},
|
||||
},
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user