feat: initial payload scaffolding
This commit is contained in:
parent
e4a367489f
commit
4d639387f4
34 changed files with 948 additions and 339 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"singleQuote": false,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"semi": false
|
||||
"semi": true
|
||||
}
|
||||
|
|
|
|||
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
|
|
@ -1,3 +1,6 @@
|
|||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
|
|
@ -9,8 +9,12 @@
|
|||
"type": "node",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/node_modules/next/dist/bin/next",
|
||||
"runtimeArgs": ["--inspect"],
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"runtimeArgs": [
|
||||
"--inspect"
|
||||
],
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"serverReadyAction": {
|
||||
"action": "debugWithChrome",
|
||||
"killOnServerStop": true,
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -4,7 +4,8 @@ This template comes configured with the bare minimum to get started on anything
|
|||
|
||||
## Quick start
|
||||
|
||||
This template can be deployed directly from our Cloud hosting and it will setup MongoDB and cloud S3 object storage for media.
|
||||
This template can be deployed directly from our Cloud hosting and it will setup MongoDB and cloud S3 object storage for
|
||||
media.
|
||||
|
||||
## Quick Start - local setup
|
||||
|
||||
|
|
@ -12,21 +13,27 @@ To spin up this template locally, follow these steps:
|
|||
|
||||
### Clone
|
||||
|
||||
After you click the `Deploy` button above, you'll want to have standalone copy of this repo on your machine. If you've already cloned this repo, skip to [Development](#development).
|
||||
After you click the `Deploy` button above, you'll want to have standalone copy of this repo on your machine. If you've
|
||||
already cloned this repo, skip to [Development](#development).
|
||||
|
||||
### Development
|
||||
|
||||
1. First [clone the repo](#clone) if you have not done so already
|
||||
2. `cd my-project && cp .env.example .env` to copy the example environment variables. You'll need to add the `MONGODB_URI` from your Cloud project to your `.env` if you want to use S3 storage and the MongoDB database that was created for you.
|
||||
2. `cd my-project && cp .env.example .env` to copy the example environment variables. You'll need to add the
|
||||
`MONGODB_URI` from your Cloud project to your `.env` if you want to use S3 storage and the MongoDB database that was
|
||||
created for you.
|
||||
|
||||
3. `pnpm install && pnpm dev` to install dependencies and start the dev server
|
||||
4. open `http://localhost:3000` to open the app in your browser
|
||||
|
||||
That's it! Changes made in `./src` will be reflected in your app. Follow the on-screen instructions to login and create your first admin user. Then check out [Production](#production) once you're ready to build and serve your app, and [Deployment](#deployment) when you're ready to go live.
|
||||
That's it! Changes made in `./src` will be reflected in your app. Follow the on-screen instructions to login and create
|
||||
your first admin user. Then check out [Production](#production) once you're ready to build and serve your app,
|
||||
and [Deployment](#deployment) when you're ready to go live.
|
||||
|
||||
#### Docker (Optional)
|
||||
|
||||
If you prefer to use Docker for local development instead of a local MongoDB instance, the provided docker-compose.yml file can be used.
|
||||
If you prefer to use Docker for local development instead of a local MongoDB instance, the provided docker-compose.yml
|
||||
file can be used.
|
||||
|
||||
To do so, follow these steps:
|
||||
|
||||
|
|
@ -40,28 +47,35 @@ The Payload config is tailored specifically to the needs of most websites. It is
|
|||
|
||||
### Collections
|
||||
|
||||
See the [Collections](https://payloadcms.com/docs/configuration/collections) docs for details on how to extend this functionality.
|
||||
See the [Collections](https://payloadcms.com/docs/configuration/collections) docs for details on how to extend this
|
||||
functionality.
|
||||
|
||||
- #### Users (Authentication)
|
||||
|
||||
Users are auth-enabled collections that have access to the admin panel.
|
||||
|
||||
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) or the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
|
||||
For additional help, see the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth) or
|
||||
the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs.
|
||||
|
||||
- #### Media
|
||||
|
||||
This is the uploads enabled collection. It features pre-configured sizes, focal point and manual resizing to help you manage your pictures.
|
||||
This is the uploads enabled collection. It features pre-configured sizes, focal point and manual resizing to help you
|
||||
manage your pictures.
|
||||
|
||||
### Docker
|
||||
|
||||
Alternatively, you can use [Docker](https://www.docker.com) to spin up this template locally. To do so, follow these steps:
|
||||
Alternatively, you can use [Docker](https://www.docker.com) to spin up this template locally. To do so, follow these
|
||||
steps:
|
||||
|
||||
1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root
|
||||
1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in
|
||||
your project root
|
||||
1. Next run `docker-compose up`
|
||||
1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user
|
||||
|
||||
That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams.
|
||||
That's it! The Docker instance will help you get up and running quickly while also standardizing the development
|
||||
environment across your teams.
|
||||
|
||||
## Questions
|
||||
|
||||
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
|
||||
If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start
|
||||
a [GitHub discussion](https://github.com/payloadcms/payload/discussions).
|
||||
|
|
|
|||
|
|
@ -1,38 +1,38 @@
|
|||
import { dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { FlatCompat } from '@eslint/eslintrc'
|
||||
import { dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
})
|
||||
});
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends('next/core-web-vitals', 'next/typescript'),
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': 'warn',
|
||||
'@typescript-eslint/no-empty-object-type': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
"@typescript-eslint/ban-ts-comment": "warn",
|
||||
"@typescript-eslint/no-empty-object-type": "warn",
|
||||
"@typescript-eslint/no-explicit-any": "warn",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
vars: 'all',
|
||||
args: 'after-used',
|
||||
vars: "all",
|
||||
args: "after-used",
|
||||
ignoreRestSiblings: false,
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
destructuredArrayIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^(_|ignore)',
|
||||
argsIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
caughtErrorsIgnorePattern: "^(_|ignore)",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['.next/'],
|
||||
ignores: [".next/"],
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
export default eslintConfig
|
||||
export default eslintConfig;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { withPayload } from '@payloadcms/next/withPayload'
|
||||
import { withPayload } from "@payloadcms/next/withPayload";
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// Your Next.js config here
|
||||
webpack: (webpackConfig) => {
|
||||
webpackConfig.resolve.extensionAlias = {
|
||||
'.cjs': ['.cts', '.cjs'],
|
||||
'.js': ['.ts', '.tsx', '.js', '.jsx'],
|
||||
'.mjs': ['.mts', '.mjs'],
|
||||
}
|
||||
".cjs": [".cts", ".cjs"],
|
||||
".js": [".ts", ".tsx", ".js", ".jsx"],
|
||||
".mjs": [".mts", ".mjs"],
|
||||
};
|
||||
|
||||
return webpackConfig
|
||||
return webpackConfig;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default withPayload(nextConfig, { devBundleServerPackages: false })
|
||||
export default withPayload(nextConfig, { devBundleServerPackages: false });
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import { defineConfig, devices } from '@playwright/test'
|
||||
import { defineConfig, devices } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
import 'dotenv/config'
|
||||
import "dotenv/config";
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './tests/e2e',
|
||||
testDir: "./tests/e2e",
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
|
|
@ -18,24 +18,24 @@ export default defineConfig({
|
|||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
// baseURL: 'http://localhost:3000',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
trace: "on-first-retry",
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
],
|
||||
webServer: {
|
||||
command: 'pnpm dev',
|
||||
command: "pnpm dev",
|
||||
reuseExistingServer: true,
|
||||
url: 'http://localhost:3000',
|
||||
url: "http://localhost:3000",
|
||||
},
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import React from 'react'
|
||||
import './styles.css'
|
||||
import React from "react";
|
||||
import "./styles.css";
|
||||
|
||||
export const metadata = {
|
||||
description: 'A blank template using Payload in a Next.js app.',
|
||||
title: 'Payload Blank Template',
|
||||
}
|
||||
description: "A blank template using Payload in a Next.js app.",
|
||||
title: "Payload Blank Template",
|
||||
};
|
||||
|
||||
export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||
const { children } = props
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<html lang="en">
|
||||
|
|
@ -15,5 +15,5 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
|||
<main>{children}</main>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
import { headers as getHeaders } from 'next/headers.js'
|
||||
import Image from 'next/image'
|
||||
import { getPayload } from 'payload'
|
||||
import React from 'react'
|
||||
import { fileURLToPath } from 'url'
|
||||
import { headers as getHeaders } from "next/headers.js";
|
||||
import Image from "next/image";
|
||||
import { getPayload } from "payload";
|
||||
import React from "react";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
import config from '@/payload.config'
|
||||
import './styles.css'
|
||||
import config from "@/payload.config";
|
||||
import "./styles.css";
|
||||
|
||||
export default async function HomePage() {
|
||||
const headers = await getHeaders()
|
||||
const payloadConfig = await config
|
||||
const payload = await getPayload({ config: payloadConfig })
|
||||
const { user } = await payload.auth({ headers })
|
||||
const headers = await getHeaders();
|
||||
const payloadConfig = await config;
|
||||
const payload = await getPayload({ config: payloadConfig });
|
||||
const { user } = await payload.auth({ headers });
|
||||
|
||||
const fileURL = `vscode://file/${fileURLToPath(import.meta.url)}`
|
||||
const fileURL = `webstorm://file/${fileURLToPath(import.meta.url)}`;
|
||||
|
||||
return (
|
||||
<div className="home">
|
||||
|
|
@ -27,8 +27,10 @@ export default async function HomePage() {
|
|||
width={65}
|
||||
/>
|
||||
</picture>
|
||||
{!user && <h1>Welcome to your new project.</h1>}
|
||||
{user && <h1>Welcome back, {user.email}</h1>}
|
||||
<h1>Polaris Task Force</h1>
|
||||
{/*{!user && <h1>Welcome to your new project.</h1>}
|
||||
{user && <h1>Welcome back, {user.email}</h1>}*/}
|
||||
<p>This site is under construction.</p>
|
||||
<div className="links">
|
||||
<a
|
||||
className="admin"
|
||||
|
|
@ -48,12 +50,12 @@ export default async function HomePage() {
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer">
|
||||
{/* <div className="footer">
|
||||
<p>Update this page by editing</p>
|
||||
<a className="codeLink" href={fileURL}>
|
||||
<code>app/(frontend)/page.tsx</code>
|
||||
</a>
|
||||
</div>*/}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import type { Metadata } from 'next'
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import config from '@payload-config'
|
||||
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views'
|
||||
import { importMap } from '../importMap'
|
||||
import config from "@payload-config";
|
||||
import { generatePageMetadata, NotFoundPage } from "@payloadcms/next/views";
|
||||
import { importMap } from "../importMap";
|
||||
|
||||
type Args = {
|
||||
params: Promise<{
|
||||
segments: string[]
|
||||
}>
|
||||
segments: string[];
|
||||
}>;
|
||||
searchParams: Promise<{
|
||||
[key: string]: string | string[]
|
||||
}>
|
||||
}
|
||||
[key: string]: string | string[];
|
||||
}>;
|
||||
};
|
||||
|
||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||
generatePageMetadata({ config, params, searchParams })
|
||||
generatePageMetadata({ config, params, searchParams });
|
||||
|
||||
const NotFound = ({ params, searchParams }: Args) =>
|
||||
NotFoundPage({ config, params, searchParams, importMap })
|
||||
NotFoundPage({ config, params, searchParams, importMap });
|
||||
|
||||
export default NotFound
|
||||
export default NotFound;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import type { Metadata } from 'next'
|
||||
import type { Metadata } from "next";
|
||||
|
||||
import config from '@payload-config'
|
||||
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
|
||||
import { importMap } from '../importMap'
|
||||
import config from "@payload-config";
|
||||
import { generatePageMetadata, RootPage } from "@payloadcms/next/views";
|
||||
import { importMap } from "../importMap";
|
||||
|
||||
type Args = {
|
||||
params: Promise<{
|
||||
segments: string[]
|
||||
}>
|
||||
segments: string[];
|
||||
}>;
|
||||
searchParams: Promise<{
|
||||
[key: string]: string | string[]
|
||||
}>
|
||||
}
|
||||
[key: string]: string | string[];
|
||||
}>;
|
||||
};
|
||||
|
||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||
generatePageMetadata({ config, params, searchParams })
|
||||
generatePageMetadata({ config, params, searchParams });
|
||||
|
||||
const Page = ({ params, searchParams }: Args) =>
|
||||
RootPage({ config, params, searchParams, importMap })
|
||||
RootPage({ config, params, searchParams, importMap });
|
||||
|
||||
export default Page
|
||||
export default Page;
|
||||
|
|
|
|||
|
|
@ -1 +1,49 @@
|
|||
export const importMap = {}
|
||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||
|
||||
export const importMap = {
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,12 @@
|
|||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import config from '@payload-config'
|
||||
import '@payloadcms/next/css'
|
||||
import {
|
||||
REST_DELETE,
|
||||
REST_GET,
|
||||
REST_OPTIONS,
|
||||
REST_PATCH,
|
||||
REST_POST,
|
||||
REST_PUT,
|
||||
} from '@payloadcms/next/routes'
|
||||
import config from "@payload-config";
|
||||
import "@payloadcms/next/css";
|
||||
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST, REST_PUT, } from "@payloadcms/next/routes";
|
||||
|
||||
export const GET = REST_GET(config)
|
||||
export const POST = REST_POST(config)
|
||||
export const DELETE = REST_DELETE(config)
|
||||
export const PATCH = REST_PATCH(config)
|
||||
export const PUT = REST_PUT(config)
|
||||
export const OPTIONS = REST_OPTIONS(config)
|
||||
export const GET = REST_GET(config);
|
||||
export const POST = REST_POST(config);
|
||||
export const DELETE = REST_DELETE(config);
|
||||
export const PATCH = REST_PATCH(config);
|
||||
export const PUT = REST_PUT(config);
|
||||
export const OPTIONS = REST_OPTIONS(config);
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ import config from '@payload-config'
|
|||
import '@payloadcms/next/css'
|
||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes'
|
||||
|
||||
export const GET = GRAPHQL_PLAYGROUND_GET(config)
|
||||
export const GET = GRAPHQL_PLAYGROUND_GET(config);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
import config from '@payload-config'
|
||||
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes'
|
||||
|
||||
export const POST = GRAPHQL_POST(config)
|
||||
export const POST = GRAPHQL_POST(config);
|
||||
|
||||
export const OPTIONS = REST_OPTIONS(config)
|
||||
export const OPTIONS = REST_OPTIONS(config);
|
||||
|
|
|
|||
|
|
@ -1,31 +1,31 @@
|
|||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import config from '@payload-config'
|
||||
import '@payloadcms/next/css'
|
||||
import type { ServerFunctionClient } from 'payload'
|
||||
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
|
||||
import React from 'react'
|
||||
import config from "@payload-config";
|
||||
import "@payloadcms/next/css";
|
||||
import type { ServerFunctionClient } from "payload";
|
||||
import { handleServerFunctions, RootLayout } from "@payloadcms/next/layouts";
|
||||
import React from "react";
|
||||
|
||||
import { importMap } from './admin/importMap.js'
|
||||
import './custom.scss'
|
||||
import { importMap } from "./admin/importMap.js";
|
||||
import "./custom.scss";
|
||||
|
||||
type Args = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const serverFunction: ServerFunctionClient = async function (args) {
|
||||
'use server'
|
||||
"use server";
|
||||
return handleServerFunctions({
|
||||
...args,
|
||||
config,
|
||||
importMap,
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const Layout = ({ children }: Args) => (
|
||||
<RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
|
||||
{children}
|
||||
</RootLayout>
|
||||
)
|
||||
);
|
||||
|
||||
export default Layout
|
||||
export default Layout;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import configPromise from '@payload-config'
|
||||
import { getPayload } from 'payload'
|
||||
import configPromise from "@payload-config";
|
||||
import { getPayload } from "payload";
|
||||
|
||||
export const GET = async (request: Request) => {
|
||||
const payload = await getPayload({
|
||||
config: configPromise,
|
||||
})
|
||||
});
|
||||
|
||||
return Response.json({
|
||||
message: 'This is an example of a custom route.',
|
||||
})
|
||||
}
|
||||
message: "This is an example of a custom route.",
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import type { CollectionConfig } from 'payload'
|
||||
import type { CollectionConfig } from "payload";
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: 'media',
|
||||
slug: "media",
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'alt',
|
||||
type: 'text',
|
||||
name: "alt",
|
||||
type: "text",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
upload: true,
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import type { CollectionConfig } from 'payload'
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
auth: true,
|
||||
fields: [
|
||||
// Email added by default
|
||||
// Add more fields as needed
|
||||
],
|
||||
}
|
||||
57
src/collections/users/Awards.ts
Normal file
57
src/collections/users/Awards.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { CollectionConfig } from "payload";
|
||||
import { isAdmin } from "@/utils/access-control/isRole";
|
||||
|
||||
export const Awards: CollectionConfig = {
|
||||
slug: "awards",
|
||||
access: {
|
||||
create: isAdmin,
|
||||
update: isAdmin,
|
||||
delete: isAdmin,
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: "name",
|
||||
group: "Users",
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
type: "richText",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
type: "select",
|
||||
options: [
|
||||
{
|
||||
label: "Commendation",
|
||||
value: "commendation",
|
||||
},
|
||||
{
|
||||
label: "Ribbon",
|
||||
value: "ribbon",
|
||||
},
|
||||
{
|
||||
label: "Medal",
|
||||
value: "medal",
|
||||
},
|
||||
],
|
||||
defaultValue: "commendation",
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
},
|
||||
{
|
||||
name: "experiencePoints",
|
||||
type: "number",
|
||||
min: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
98
src/collections/users/Profiles.ts
Normal file
98
src/collections/users/Profiles.ts
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { CollectionConfig } from "payload";
|
||||
import { Award } from "@/payload-types";
|
||||
|
||||
export const Profiles: CollectionConfig = {
|
||||
slug: "profiles",
|
||||
admin: {
|
||||
useAsTitle: "profileTitle",
|
||||
group: "Users",
|
||||
defaultColumns: ["profileTitle", "user", "rank"],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: "user",
|
||||
type: "relationship",
|
||||
relationTo: "users",
|
||||
},
|
||||
{
|
||||
name: "rank",
|
||||
type: "relationship",
|
||||
relationTo: "ranks",
|
||||
},
|
||||
{
|
||||
name: "profileTitle",
|
||||
type: "text",
|
||||
virtual: "user.displayName",
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
label: "Operator",
|
||||
},
|
||||
{
|
||||
name: "dossier",
|
||||
label: "Personnel Dossier",
|
||||
type: "group",
|
||||
fields: [
|
||||
{
|
||||
name: "personalExcerpt",
|
||||
type: "textarea",
|
||||
},
|
||||
{
|
||||
name: "intelExcerpt",
|
||||
label: "Intelligence Record",
|
||||
type: "richText",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "progression",
|
||||
label: "Progression & XP",
|
||||
type: "group",
|
||||
fields: [
|
||||
{
|
||||
name: "xp",
|
||||
label: "Experience Points (XP)",
|
||||
type: "number",
|
||||
virtual: true,
|
||||
defaultValue: 0,
|
||||
admin: {
|
||||
readOnly: true,
|
||||
},
|
||||
hooks: {
|
||||
// beforeChange: [({ siblingData }) => delete siblingData["xp"]],
|
||||
afterRead: [
|
||||
async ({ req, siblingData }) => {
|
||||
let xpAcc = 0;
|
||||
|
||||
await req.payload
|
||||
.find({
|
||||
collection: "awards",
|
||||
where: {
|
||||
id: {
|
||||
in: (siblingData as { awards: number[] }).awards,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
const awards: Award[] = res.docs;
|
||||
xpAcc += awards.reduce((prev, cur) => prev + (cur.experiencePoints ?? 0), 0);
|
||||
});
|
||||
|
||||
return xpAcc;
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "awards",
|
||||
type: "relationship",
|
||||
relationTo: "awards",
|
||||
hasMany: true,
|
||||
admin: {
|
||||
appearance: "drawer",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
40
src/collections/users/Ranks.ts
Normal file
40
src/collections/users/Ranks.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { CollectionConfig } from "payload";
|
||||
import { isAdmin } from "@/utils/access-control/isRole";
|
||||
|
||||
export const Ranks: CollectionConfig = {
|
||||
slug: "ranks",
|
||||
access: {
|
||||
create: isAdmin,
|
||||
update: isAdmin,
|
||||
delete: isAdmin,
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: "name",
|
||||
group: "Users"
|
||||
},
|
||||
orderable: true,
|
||||
fields: [
|
||||
{
|
||||
name: "name",
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: "abbreviation",
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
type: "text",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "insignia",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
},
|
||||
],
|
||||
};
|
||||
64
src/collections/users/Users.ts
Normal file
64
src/collections/users/Users.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import type { CollectionConfig } from "payload";
|
||||
import { isAdmin } from "@/utils/access-control/isRole";
|
||||
|
||||
export const Users: CollectionConfig = {
|
||||
slug: "users",
|
||||
access: {
|
||||
admin: isAdmin,
|
||||
unlock: isAdmin,
|
||||
},
|
||||
admin: {
|
||||
useAsTitle: "username",
|
||||
group: "Users",
|
||||
},
|
||||
auth: {
|
||||
loginWithUsername: {
|
||||
requireUsername: true,
|
||||
allowEmailLogin: false,
|
||||
requireEmail: false,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
// Email added by default by Payload, but essentially ignored in this application
|
||||
{
|
||||
name: "username",
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: "discordUsername",
|
||||
type: "text",
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: "displayName",
|
||||
type: "text",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "roles",
|
||||
type: "select",
|
||||
hasMany: true,
|
||||
options: [
|
||||
{
|
||||
label: "Guest",
|
||||
value: "guest",
|
||||
},
|
||||
{
|
||||
label: "User",
|
||||
value: "user",
|
||||
},
|
||||
{
|
||||
label: "Admin",
|
||||
value: "admin",
|
||||
},
|
||||
{
|
||||
label: "Developer",
|
||||
value: "developer",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -69,6 +69,9 @@ export interface Config {
|
|||
collections: {
|
||||
users: User;
|
||||
media: Media;
|
||||
ranks: Rank;
|
||||
profiles: Profile;
|
||||
awards: Award;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
|
|
@ -77,12 +80,15 @@ export interface Config {
|
|||
collectionsSelect: {
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
media: MediaSelect<false> | MediaSelect<true>;
|
||||
ranks: RanksSelect<false> | RanksSelect<true>;
|
||||
profiles: ProfilesSelect<false> | ProfilesSelect<true>;
|
||||
awards: AwardsSelect<false> | AwardsSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
defaultIDType: number;
|
||||
};
|
||||
globals: {};
|
||||
globalsSelect: {};
|
||||
|
|
@ -97,20 +103,18 @@ export interface Config {
|
|||
}
|
||||
export interface UserAuthOperations {
|
||||
forgotPassword: {
|
||||
email: string;
|
||||
password: string;
|
||||
username: string;
|
||||
};
|
||||
login: {
|
||||
email: string;
|
||||
password: string;
|
||||
username: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
username: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
password: string;
|
||||
username: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
|
|
@ -118,10 +122,14 @@ export interface UserAuthOperations {
|
|||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
id: number;
|
||||
discordUsername: string;
|
||||
displayName: string;
|
||||
roles?: ('guest' | 'user' | 'admin' | 'developer')[] | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
email?: string | null;
|
||||
username: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
|
|
@ -142,7 +150,7 @@ export interface User {
|
|||
* via the `definition` "media".
|
||||
*/
|
||||
export interface Media {
|
||||
id: string;
|
||||
id: number;
|
||||
alt: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
|
|
@ -156,25 +164,113 @@ export interface Media {
|
|||
focalX?: number | null;
|
||||
focalY?: number | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "ranks".
|
||||
*/
|
||||
export interface Rank {
|
||||
id: number;
|
||||
_order?: string | null;
|
||||
name: string;
|
||||
abbreviation: string;
|
||||
description: string;
|
||||
insignia?: (number | null) | Media;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "profiles".
|
||||
*/
|
||||
export interface Profile {
|
||||
id: number;
|
||||
user?: (number | null) | User;
|
||||
rank?: (number | null) | Rank;
|
||||
profileTitle?: string | null;
|
||||
dossier?: {
|
||||
personalExcerpt?: string | null;
|
||||
intelExcerpt?: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: any;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
} | null;
|
||||
};
|
||||
progression?: {
|
||||
xp?: number | null;
|
||||
awards?: (number | Award)[] | null;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "awards".
|
||||
*/
|
||||
export interface Award {
|
||||
id: number;
|
||||
name: string;
|
||||
description: {
|
||||
root: {
|
||||
type: string;
|
||||
children: {
|
||||
type: any;
|
||||
version: number;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
direction: ('ltr' | 'rtl') | null;
|
||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||
indent: number;
|
||||
version: number;
|
||||
};
|
||||
[k: string]: unknown;
|
||||
};
|
||||
type?: ('commendation' | 'ribbon' | 'medal') | null;
|
||||
image?: (number | null) | Media;
|
||||
experiencePoints?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: string;
|
||||
id: number;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
value: number | User;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'media';
|
||||
value: string | Media;
|
||||
value: number | Media;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'ranks';
|
||||
value: number | Rank;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'profiles';
|
||||
value: number | Profile;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'awards';
|
||||
value: number | Award;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
value: number | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
|
|
@ -184,10 +280,10 @@ export interface PayloadLockedDocument {
|
|||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: string;
|
||||
id: number;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
value: number | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
|
|
@ -207,7 +303,7 @@ export interface PayloadPreference {
|
|||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: string;
|
||||
id: number;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
|
|
@ -218,9 +314,13 @@ export interface PayloadMigration {
|
|||
* via the `definition` "users_select".
|
||||
*/
|
||||
export interface UsersSelect<T extends boolean = true> {
|
||||
discordUsername?: T;
|
||||
displayName?: T;
|
||||
roles?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
email?: T;
|
||||
username?: T;
|
||||
resetPasswordToken?: T;
|
||||
resetPasswordExpiration?: T;
|
||||
salt?: T;
|
||||
|
|
@ -253,6 +353,55 @@ export interface MediaSelect<T extends boolean = true> {
|
|||
focalX?: T;
|
||||
focalY?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "ranks_select".
|
||||
*/
|
||||
export interface RanksSelect<T extends boolean = true> {
|
||||
_order?: T;
|
||||
name?: T;
|
||||
abbreviation?: T;
|
||||
description?: T;
|
||||
insignia?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "profiles_select".
|
||||
*/
|
||||
export interface ProfilesSelect<T extends boolean = true> {
|
||||
user?: T;
|
||||
rank?: T;
|
||||
profileTitle?: T;
|
||||
dossier?:
|
||||
| T
|
||||
| {
|
||||
personalExcerpt?: T;
|
||||
intelExcerpt?: T;
|
||||
};
|
||||
progression?:
|
||||
| T
|
||||
| {
|
||||
xp?: T;
|
||||
awards?: T;
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "awards_select".
|
||||
*/
|
||||
export interface AwardsSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
description?: T;
|
||||
type?: T;
|
||||
image?: T;
|
||||
experiencePoints?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents_select".
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
// storage-adapter-import-placeholder
|
||||
import { postgresAdapter } from '@payloadcms/db-postgres'
|
||||
import { payloadCloudPlugin } from '@payloadcms/payload-cloud'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
import path from 'path'
|
||||
import { buildConfig } from 'payload'
|
||||
import { fileURLToPath } from 'url'
|
||||
import sharp from 'sharp'
|
||||
import { postgresAdapter } from "@payloadcms/db-postgres";
|
||||
import { payloadCloudPlugin } from "@payloadcms/payload-cloud";
|
||||
import { lexicalEditor } from "@payloadcms/richtext-lexical";
|
||||
import path from "path";
|
||||
import { buildConfig } from "payload";
|
||||
import { fileURLToPath } from "url";
|
||||
import sharp from "sharp";
|
||||
|
||||
import { Users } from './collections/Users'
|
||||
import { Media } from './collections/Media'
|
||||
import { Users } from "@/collections/users/Users";
|
||||
import { Media } from "@/collections/Media";
|
||||
import { Profiles } from "@/collections/users/Profiles";
|
||||
import { Ranks } from "@/collections/users/Ranks";
|
||||
import { Awards } from "@/collections/users/Awards";
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
const filename = fileURLToPath(import.meta.url);
|
||||
const dirname = path.dirname(filename);
|
||||
|
||||
export default buildConfig({
|
||||
admin: {
|
||||
|
|
@ -20,15 +23,15 @@ export default buildConfig({
|
|||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
collections: [Users, Media],
|
||||
collections: [Users, Media, Ranks, Profiles, Awards],
|
||||
editor: lexicalEditor(),
|
||||
secret: process.env.PAYLOAD_SECRET || '',
|
||||
secret: process.env.PAYLOAD_SECRET || "",
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
outputFile: path.resolve(dirname, "payload-types.ts"),
|
||||
},
|
||||
db: postgresAdapter({
|
||||
pool: {
|
||||
connectionString: process.env.DATABASE_URI || '',
|
||||
connectionString: process.env.DATABASE_URI || "",
|
||||
},
|
||||
}),
|
||||
sharp,
|
||||
|
|
@ -36,4 +39,4 @@ export default buildConfig({
|
|||
payloadCloudPlugin(),
|
||||
// storage-adapter-placeholder
|
||||
],
|
||||
})
|
||||
});
|
||||
|
|
|
|||
122
src/tools/seed/seedRanks.ts
Normal file
122
src/tools/seed/seedRanks.ts
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import { getPayload } from "payload";
|
||||
import config from "@payload-config";
|
||||
|
||||
const DEFAULT_USMC_RANKS = [
|
||||
{
|
||||
name: "Recruit",
|
||||
abbreviation: "Rct",
|
||||
},
|
||||
{
|
||||
name: "Private",
|
||||
abbreviation: "Pvt",
|
||||
},
|
||||
{
|
||||
name: "Private First Class",
|
||||
abbreviation: "PFC",
|
||||
},
|
||||
{
|
||||
name: "Lance Corporal",
|
||||
abbreviation: "LCpl",
|
||||
},
|
||||
{
|
||||
name: "Corporal",
|
||||
abbreviation: "Cpl",
|
||||
},
|
||||
{
|
||||
name: "Sergeant",
|
||||
abbreviation: "Sgt",
|
||||
},
|
||||
{
|
||||
name: "Staff Sergeant",
|
||||
abbreviation: "SSgt",
|
||||
},
|
||||
{
|
||||
name: "Gunnery Sergeant",
|
||||
abbreviation: "GySgt",
|
||||
},
|
||||
{
|
||||
name: "Master Sergeant",
|
||||
abbreviation: "MSgt",
|
||||
},
|
||||
{
|
||||
name: "First Sergeant",
|
||||
abbreviation: "1stSgt",
|
||||
},
|
||||
{
|
||||
name: "Sergeant Major",
|
||||
abbreviation: "SgtMaj",
|
||||
},
|
||||
{
|
||||
name: "Master Gunnery Sergeant",
|
||||
abbreviation: "MGySgt",
|
||||
},
|
||||
{
|
||||
name: "Second Lieutenant",
|
||||
abbreviation: "2ndLt",
|
||||
},
|
||||
{
|
||||
name: "First Lieutenant",
|
||||
abbreviation: "1stLt",
|
||||
},
|
||||
{
|
||||
name: "Captain",
|
||||
abbreviation: "Cpt",
|
||||
},
|
||||
{
|
||||
name: "Major",
|
||||
abbreviation: "Maj",
|
||||
},
|
||||
{
|
||||
name: "Lieutenant Colonel",
|
||||
abbreviation: "LtCol",
|
||||
},
|
||||
{
|
||||
name: "Colonel",
|
||||
abbreviation: "Col",
|
||||
},
|
||||
{
|
||||
name: "Brigadier General",
|
||||
abbreviation: "BGen",
|
||||
},
|
||||
{
|
||||
name: "Major General",
|
||||
abbreviation: "MajGen",
|
||||
},
|
||||
{
|
||||
name: "Lieutenant General",
|
||||
abbreviation: "LtGen",
|
||||
},
|
||||
{
|
||||
name: "General",
|
||||
abbreviation: "Gen",
|
||||
},
|
||||
];
|
||||
|
||||
export const seedRanks = async () => {
|
||||
const payload = await getPayload({ config });
|
||||
|
||||
payload.logger.info("Seeding rank data...");
|
||||
|
||||
payload.logger.warn("Deleting all existing ranks...");
|
||||
await payload.delete({
|
||||
collection: "ranks",
|
||||
where: {
|
||||
name: { exists: true },
|
||||
},
|
||||
});
|
||||
|
||||
payload.logger.info(`Creating ${DEFAULT_USMC_RANKS.length} new ranks...`);
|
||||
for (const rank of DEFAULT_USMC_RANKS) {
|
||||
const result = await payload.create({
|
||||
collection: "ranks",
|
||||
data: {
|
||||
...rank,
|
||||
description: "A rank that needs a description. ",
|
||||
},
|
||||
});
|
||||
|
||||
payload.logger.info(`Rank '${result.name}' created.`);
|
||||
}
|
||||
};
|
||||
|
||||
await seedRanks();
|
||||
7
src/utils/access-control/hasRoles.ts
Normal file
7
src/utils/access-control/hasRoles.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { User } from "@/payload-types";
|
||||
|
||||
export default function hasRoles(roles: NonNullable<User['roles']>, user: User | null | undefined) {
|
||||
if (!user) return false;
|
||||
|
||||
return roles.some((role) => user.roles?.some((urole) => urole === role || urole === "developer"));
|
||||
}
|
||||
18
src/utils/access-control/isRole.ts
Normal file
18
src/utils/access-control/isRole.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import hasRoles from "@/utils/access-control/hasRoles";
|
||||
import { PayloadRequest } from "payload";
|
||||
|
||||
export function isDeveloper({ req: { user } }: { req: PayloadRequest }) {
|
||||
return hasRoles(["developer"], user);
|
||||
}
|
||||
|
||||
export function isGuest({ req: { user } }: { req: PayloadRequest }) {
|
||||
return hasRoles(["guest"], user);
|
||||
}
|
||||
|
||||
export function isUser({ req: { user } }: { req: PayloadRequest }) {
|
||||
return hasRoles(["user"], user);
|
||||
}
|
||||
|
||||
export function isAdmin({ req: { user } }: { req: PayloadRequest }) {
|
||||
return hasRoles(["admin"], user);
|
||||
}
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
import { test, expect, Page } from '@playwright/test'
|
||||
import { expect, Page, test } from "@playwright/test";
|
||||
|
||||
test.describe('Frontend', () => {
|
||||
let page: Page
|
||||
test.describe("Frontend", () => {
|
||||
let page: Page;
|
||||
|
||||
test.beforeAll(async ({ browser }, testInfo) => {
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
})
|
||||
const context = await browser.newContext();
|
||||
page = await context.newPage();
|
||||
});
|
||||
|
||||
test('can go on homepage', async ({ page }) => {
|
||||
await page.goto('http://localhost:3000')
|
||||
test("can go on homepage", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000");
|
||||
|
||||
await expect(page).toHaveTitle(/Payload Blank Template/)
|
||||
await expect(page).toHaveTitle(/Payload Blank Template/);
|
||||
|
||||
const heading = page.locator('h1').first()
|
||||
const heading = page.locator("h1").first();
|
||||
|
||||
await expect(heading).toHaveText('Welcome to your new project.')
|
||||
})
|
||||
})
|
||||
await expect(heading).toHaveText("Welcome to your new project.");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
import { getPayload, Payload } from 'payload'
|
||||
import config from '@/payload.config'
|
||||
import { getPayload, Payload } from "payload";
|
||||
import config from "@/payload.config";
|
||||
|
||||
import { describe, it, beforeAll, expect } from 'vitest'
|
||||
import { beforeAll, describe, expect, it } from "vitest";
|
||||
|
||||
let payload: Payload
|
||||
let payload: Payload;
|
||||
|
||||
describe('API', () => {
|
||||
describe("API", () => {
|
||||
beforeAll(async () => {
|
||||
const payloadConfig = await config
|
||||
payload = await getPayload({ config: payloadConfig })
|
||||
})
|
||||
const payloadConfig = await config;
|
||||
payload = await getPayload({ config: payloadConfig });
|
||||
});
|
||||
|
||||
it('fetches users', async () => {
|
||||
it("fetches users", async () => {
|
||||
const users = await payload.find({
|
||||
collection: 'users',
|
||||
})
|
||||
expect(users).toBeDefined()
|
||||
})
|
||||
})
|
||||
collection: "users",
|
||||
});
|
||||
expect(users).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
"./src/payload.config.ts"
|
||||
]
|
||||
},
|
||||
"target": "ES2022",
|
||||
"target": "ES2022"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
|
|
@ -40,5 +40,5 @@
|
|||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { defineConfig } from 'vitest/config'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tsconfigPaths from 'vite-tsconfig-paths'
|
||||
import { defineConfig } from "vitest/config";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths(), react()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
include: ['tests/int/**/*.int.spec.ts'],
|
||||
environment: "jsdom",
|
||||
setupFiles: ["./vitest.setup.ts"],
|
||||
include: ["tests/int/**/*.int.spec.ts"],
|
||||
},
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Any setup scripts you might need go here
|
||||
|
||||
// Load .env files
|
||||
import 'dotenv/config'
|
||||
import "dotenv/config";
|
||||
|
|
|
|||
Loading…
Reference in a new issue