Initial
This commit is contained in:
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
payload-types.ts
|
||||
23
.eslintrc.json
Normal file
23
.eslintrc.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"globals": {},
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"@matt-fidd"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest"
|
||||
},
|
||||
"rules": {
|
||||
"max-len": "off"
|
||||
}
|
||||
}
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
build/
|
||||
.env
|
||||
src/media
|
||||
4
nodemon.json
Normal file
4
nodemon.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"ext": "ts",
|
||||
"exec": "ts-node src/server.ts"
|
||||
}
|
||||
31420
package-lock.json
generated
Normal file
31420
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
package.json
Normal file
34
package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "payload-starter-typescript",
|
||||
"description": "Blank template - no collections",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/server.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build",
|
||||
"build:server": "tsc",
|
||||
"build": "yarn build:payload && yarn build:server",
|
||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
|
||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
|
||||
"lint": "npm run lint:check",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"lint:check": "eslint ."
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"payload": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@matt-fidd/eslint-config": "^1.3.2",
|
||||
"@types/express": "^4.17.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.7",
|
||||
"@typescript-eslint/parser": "^5.30.7",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.20.0",
|
||||
"nodemon": "^2.0.6",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.7.4"
|
||||
}
|
||||
}
|
||||
29
src/blocks/Quote.ts
Normal file
29
src/blocks/Quote.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Block } from 'payload/types';
|
||||
|
||||
const QuoteBlock: Block = {
|
||||
slug: 'Quote',
|
||||
fields: [
|
||||
{
|
||||
name: 'quoteText',
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'quoteAttribution',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'date',
|
||||
label: 'Date',
|
||||
type: 'text'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default QuoteBlock;
|
||||
37
src/blocks/Spacer.ts
Normal file
37
src/blocks/Spacer.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Block } from 'payload/types';
|
||||
|
||||
const SpacerBlock: Block = {
|
||||
slug: 'spacer',
|
||||
labels: {
|
||||
singular: 'Spacer',
|
||||
plural: 'Spacers'
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'size',
|
||||
label: 'Size',
|
||||
type: 'radio',
|
||||
required: true,
|
||||
defaultValue: 'medium',
|
||||
options: [
|
||||
{
|
||||
label: 'Small',
|
||||
value: 'small'
|
||||
},
|
||||
{
|
||||
label: 'Medium',
|
||||
value: 'medium'
|
||||
},
|
||||
{
|
||||
label: 'Large',
|
||||
value: 'large'
|
||||
}
|
||||
],
|
||||
admin: {
|
||||
layout: 'horizontal'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default SpacerBlock;
|
||||
33
src/collections/Media.ts
Normal file
33
src/collections/Media.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
access: {
|
||||
read: (): boolean => true
|
||||
},
|
||||
upload: {
|
||||
adminThumbnail: 'card',
|
||||
imageSizes: [
|
||||
{
|
||||
name: 'card',
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
{
|
||||
name: 'feature',
|
||||
width: 1024,
|
||||
height: 576
|
||||
}
|
||||
]
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
label: 'Alt Text',
|
||||
type: 'text',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default Media;
|
||||
27
src/collections/Pages.ts
Normal file
27
src/collections/Pages.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import slug from '../fields/Slug';
|
||||
|
||||
const Pages: CollectionConfig = {
|
||||
slug: 'pages',
|
||||
admin: {
|
||||
useAsTitle: 'title'
|
||||
},
|
||||
access: {
|
||||
read: (): boolean => true // Everyone can read Pages
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
label: 'Content',
|
||||
type: 'richText'
|
||||
},
|
||||
slug
|
||||
]
|
||||
};
|
||||
|
||||
export default Pages;
|
||||
21
src/collections/Users.ts
Normal file
21
src/collections/Users.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
useAsTitle: 'email'
|
||||
},
|
||||
access: {
|
||||
read: () => true
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default Users;
|
||||
63
src/fields/Link.ts
Normal file
63
src/fields/Link.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Field } from 'payload/types';
|
||||
|
||||
const link: Field = {
|
||||
name: 'link',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'type',
|
||||
type: 'radio',
|
||||
options: [
|
||||
{
|
||||
label: 'Page',
|
||||
value: 'page'
|
||||
},
|
||||
{
|
||||
label: 'Custom URL',
|
||||
value: 'custom'
|
||||
}
|
||||
],
|
||||
defaultValue: 'page',
|
||||
admin: {
|
||||
layout: 'horizontal'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'label',
|
||||
label: 'Label',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
width: '50%'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'page',
|
||||
label: 'Page to link to',
|
||||
type: 'relationship',
|
||||
relationTo: 'pages',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.type === 'page',
|
||||
width: '50%'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'url',
|
||||
label: 'Custom URL',
|
||||
type: 'text',
|
||||
required: true,
|
||||
admin: {
|
||||
condition: (_, siblingData) => siblingData?.type === 'custom',
|
||||
width: '50%'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default link;
|
||||
18
src/fields/Slug.ts
Normal file
18
src/fields/Slug.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Field } from 'payload/types';
|
||||
import formatSlug from '../utilities/formatSlug';
|
||||
|
||||
const slug: Field = {
|
||||
name: 'slug',
|
||||
label: 'Slug',
|
||||
type: 'text',
|
||||
admin: {
|
||||
position: 'sidebar'
|
||||
},
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
formatSlug('title')
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export default slug;
|
||||
24
src/globals/Nav.ts
Normal file
24
src/globals/Nav.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { GlobalConfig } from 'payload/types';
|
||||
|
||||
import Link from '../fields/Link';
|
||||
|
||||
const Nav: GlobalConfig = {
|
||||
slug: 'nav',
|
||||
fields: [
|
||||
{
|
||||
name: 'items',
|
||||
type: 'array',
|
||||
required: true,
|
||||
labels: {
|
||||
singular: 'Link',
|
||||
plural: 'Links'
|
||||
},
|
||||
maxRows: 8,
|
||||
fields: [
|
||||
Link
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default Nav;
|
||||
87
src/payload-types.ts
Normal file
87
src/payload-types.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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` "nav".
|
||||
*/
|
||||
export interface Nav {
|
||||
id: string;
|
||||
items: {
|
||||
link?: {
|
||||
type?: 'page' | 'custom';
|
||||
label: string;
|
||||
page: string | Page;
|
||||
url: string;
|
||||
};
|
||||
id?: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "pages".
|
||||
*/
|
||||
export interface Page {
|
||||
id: string;
|
||||
title?: string;
|
||||
content?: {
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
slug?: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
email?: string;
|
||||
resetPasswordToken?: string;
|
||||
resetPasswordExpiration?: string;
|
||||
loginAttempts?: number;
|
||||
lockUntil?: string;
|
||||
name: string;
|
||||
createdAt: string;
|
||||
updatedAt: 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?: {
|
||||
card?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
feature?: {
|
||||
url?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
mimeType?: string;
|
||||
filesize?: number;
|
||||
filename?: string;
|
||||
};
|
||||
};
|
||||
alt: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
26
src/payload.config.ts
Normal file
26
src/payload.config.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { buildConfig } from 'payload/config';
|
||||
import path from 'path';
|
||||
|
||||
import Users from './collections/Users';
|
||||
import Media from './collections/Media';
|
||||
import Pages from './collections/Pages';
|
||||
|
||||
import Nav from './globals/Nav';
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: 'http://localhost:3000',
|
||||
admin: {
|
||||
user: Users.slug
|
||||
},
|
||||
collections: [
|
||||
Users,
|
||||
Media,
|
||||
Pages
|
||||
],
|
||||
globals: [
|
||||
Nav
|
||||
],
|
||||
typescript: {
|
||||
outputFile: path.resolve(__dirname, 'payload-types.ts')
|
||||
}
|
||||
});
|
||||
26
src/server.ts
Normal file
26
src/server.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import express from 'express';
|
||||
import payload from 'payload';
|
||||
|
||||
import * as dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
|
||||
// Redirect root to Admin panel
|
||||
app.get('/', (_, res) => {
|
||||
res.redirect('/admin');
|
||||
});
|
||||
|
||||
// Initialize Payload
|
||||
payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Add your own express routes here
|
||||
|
||||
app.listen(3000);
|
||||
18
src/utilities/formatSlug.ts
Normal file
18
src/utilities/formatSlug.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { FieldHook } from 'payload/types';
|
||||
|
||||
const format = (val: string): string => val.replace(/ /g, '-').replace(/[^\w-/]+/g, '').toLowerCase();
|
||||
|
||||
const formatSlug = (fallback: string): FieldHook => ({ value, originalDoc, data }) => {
|
||||
if (typeof value === 'string')
|
||||
return format(value);
|
||||
|
||||
const fallbackData = data && data[fallback] || originalDoc && originalDoc[fallback];
|
||||
|
||||
if (fallbackData && typeof fallbackData === 'string')
|
||||
return format(fallbackData);
|
||||
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export default formatSlug;
|
||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"jsx": "react",
|
||||
},
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user