Compare commits
1 Commits
alpha-post
...
feat/db-ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c02e4df4bc |
@@ -19,11 +19,3 @@ indent_size = 2
|
||||
[*.mdx]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
@@ -1,13 +0,0 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
playwright.config.ts
|
||||
jest.config.js
|
||||
test/live-preview/next-app
|
||||
@@ -1,64 +0,0 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
ignorePatterns: ['README.md', 'packages/**/*.spec.ts'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['packages/**'],
|
||||
plugins: ['payload'],
|
||||
rules: {
|
||||
'payload/no-jsx-import-statements': 'warn',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['scripts/**'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'no-console': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['packages/eslint-config-payload/**'],
|
||||
rules: {
|
||||
'perfectionist/sort-objects': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
|
||||
EXPERIMENTAL_useProjectService: true,
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
104
.eslintrc.js
Normal file
104
.eslintrc.js
Normal file
@@ -0,0 +1,104 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
],
|
||||
extends: [
|
||||
'./eslint-config',
|
||||
],
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
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',
|
||||
'jest-dom/prefer-to-have-attribute': 'off',
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
rules: {
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'import/no-unresolved': [
|
||||
2,
|
||||
{
|
||||
ignore: [
|
||||
'payload-config',
|
||||
'payload/generated-types',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.spec.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.e2e.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'jest/expect-expect': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': ['error', { packageDir: './' }],
|
||||
'react/jsx-filename-extension': [2, { extensions: ['.js', '.jsx', '.ts', '.tsx'] }],
|
||||
'import/prefer-default-export': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
'react/no-unused-prop-types': 'off',
|
||||
'no-console': 'warn',
|
||||
'no-sparse-arrays': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'arrow-body-style': 0,
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
# lint and format
|
||||
ae7d6f97d205491390f15850e5104c7abded1550
|
||||
1fbda85cd04a774cb978778b0f813001664c53dd
|
||||
|
||||
# prettier all templates
|
||||
75a428ddc4672903455998eaba7ae9f9d710bf85
|
||||
|
||||
# re-run prettier and eslint everywhere again
|
||||
cdaa0acd61d3001407609915bd573b78565d5571
|
||||
|
||||
# prettier write again
|
||||
dfac7395fed95fc5d8ebca21b786ce70821942bb
|
||||
|
||||
# lint and format plugin-cloud
|
||||
fb7d1be2f3325d076b7c967b1730afcef37922c2
|
||||
|
||||
# lint and format create-payload-app
|
||||
5fd3d430001efe86515262ded5e26f00c1451181
|
||||
|
||||
# 3.0 prettier & lint everywhere
|
||||
6789e61488a1d3de56f472ac3214faf344030005
|
||||
41
.github/CODEOWNERS
vendored
41
.github/CODEOWNERS
vendored
@@ -1,41 +0,0 @@
|
||||
# Order matters. The last matching pattern takes precedence.
|
||||
|
||||
### Core ###
|
||||
/packages/payload/src/uploads/ @denolfe
|
||||
/packages/payload/src/admin/ @jmikrut @jacobsfletch @JarrodMFlesch
|
||||
|
||||
### Adapters ###
|
||||
/packages/db-*/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/richtext-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||
|
||||
### Plugins ###
|
||||
/packages/plugin-*/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/plugin-cloud*/ @denolfe
|
||||
/packages/plugin-form-builder/ @jacobsfletch
|
||||
/packages/plugin-live-preview*/ @jacobsfletch
|
||||
/packages/plugin-nested-docs/ @jacobsfletch
|
||||
/packages/plugin-redirects/ @jacobsfletch
|
||||
/packages/plugin-search/ @jacobsfletch
|
||||
/packages/plugin-sentry/ @JessChowdhury
|
||||
/packages/plugin-seo/ @jacobsfletch
|
||||
/packages/plugin-stripe/ @jacobsfletch
|
||||
|
||||
### Examples ###
|
||||
/examples/ @jacobsfletch
|
||||
/examples/testing/ @JarrodMFlesch
|
||||
/examples/email/ @JessChowdhury
|
||||
/examples/whitelabel/ @JessChowdhury
|
||||
|
||||
### Templates ###
|
||||
/templates/ @jacobsfletch @denolfe
|
||||
|
||||
### Misc ###
|
||||
/packages/create-payload-app/ @denolfe
|
||||
/packages/eslint-config-payload/ @denolfe
|
||||
/packages/payload-admin-bar/ @jacobsfletch
|
||||
|
||||
### Root ###
|
||||
/package.json @denolfe
|
||||
/scripts/ @denolfe
|
||||
/.github/ @denolfe
|
||||
/.github/CODEOWNERS @denolfe
|
||||
19
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
19
.github/ISSUE_TEMPLATE/1.bug_report.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Create a bug report for Payload
|
||||
labels: ['[possible-bug]']
|
||||
labels: ["possible-bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -10,12 +10,7 @@ body:
|
||||
id: reproduction-link
|
||||
attributes:
|
||||
label: Link to reproduction
|
||||
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: Please add a link to a reproduction. See the fork [reproduction-guide](https://github.com/payloadcms/payload/blob/master/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
@@ -24,6 +19,11 @@ body:
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
@@ -31,11 +31,6 @@ body:
|
||||
description: What version of Payload are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: adapters-plugins
|
||||
attributes:
|
||||
label: Adapters and Plugins
|
||||
description: What adapters and plugins are you using? ie. db-mongodb, db-postgres, bundler-webpack, etc.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
|
||||
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- Please include a summary of the pull request and any related issues it fixes. Please also include relevant motivation and context. -->
|
||||
|
||||
- [ ] I have read and understand the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository.
|
||||
- [ ] I have read and understand the [CONTRIBUTING.md](../CONTRIBUTING.md) document in this repository.
|
||||
|
||||
## Type of change
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Change to the [templates](https://github.com/payloadcms/payload/tree/main/templates) directory (does not affect core functionality)
|
||||
- [ ] Change to the [examples](https://github.com/payloadcms/payload/tree/main/examples) directory (does not affect core functionality)
|
||||
- [ ] Change to the [templates](../templates/) directory (does not affect core functionality)
|
||||
- [ ] Change to the [examples](../examples/) directory (does not affect core functionality)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
## Checklist:
|
||||
|
||||
28
.github/reproduction-guide.md
vendored
28
.github/reproduction-guide.md
vendored
@@ -1,16 +1,14 @@
|
||||
# Reproduction Guide
|
||||
|
||||
1. [Fork](https://github.com/payloadcms/payload/fork) this repo
|
||||
2. Optionally, create a new branch for your reproduction
|
||||
3. Run `pnpm install` to install dependencies
|
||||
4. Open up the `test/_community` directory
|
||||
5. Add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
|
||||
6. Run `pnpm dev _community` to start the admin panel
|
||||
1. [fork](https://github.com/payloadcms/payload/fork) this repo
|
||||
2. run `yarn` to install dependencies
|
||||
3. open up the `test/_community` directory
|
||||
4. add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
|
||||
5. run `yarn dev _community` to start the admin panel
|
||||
|
||||
**NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config.
|
||||
|
||||
## Example test directory file tree
|
||||
|
||||
```text
|
||||
.
|
||||
├── config.ts
|
||||
@@ -22,43 +20,39 @@
|
||||
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
|
||||
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
|
||||
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
|
||||
|
||||
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
|
||||
|
||||
<br />
|
||||
|
||||
## Testing is optional but encouraged
|
||||
|
||||
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below:
|
||||
|
||||
### Running integration tests (Payload API tests)
|
||||
|
||||
First install [Jest Runner for VSVode](https://marketplace.visualstudio.com/items?itemName=firsttris.vscode-jest-runner).
|
||||
|
||||
There are a couple ways run integration tests:
|
||||
|
||||
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/int-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/int-debug.png" />
|
||||
|
||||
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
|
||||
|
||||
```bash
|
||||
pnpm test:int _community
|
||||
yarn test:int _community
|
||||
```
|
||||
|
||||
### Running E2E tests (Admin Panel UI tests)
|
||||
|
||||
The easiest way to run E2E tests is to install
|
||||
|
||||
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
|
||||
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
|
||||
|
||||
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
|
||||
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/github/e2e-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
|
||||
|
||||
|
||||
#### Notes
|
||||
|
||||
The default credentials are `dev@payloadcms.com` as email and `test` as password. They can be found in `test/credentials.ts`. By default, these will be autofilled, so no log-in is required.
|
||||
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.
|
||||
|
||||
370
.github/workflows/main.yml
vendored
370
.github/workflows/main.yml
vendored
@@ -1,370 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches: ['main', 'alpha']
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
needs_build: ${{ steps.filter.outputs.needs_build }}
|
||||
templates: ${{ steps.filter.outputs.templates }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
needs_build:
|
||||
- '.github/workflows/**'
|
||||
- 'packages/**'
|
||||
- 'test/**'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'package.json'
|
||||
templates:
|
||||
- 'templates/**'
|
||||
- name: Log all filter results
|
||||
run: |
|
||||
echo "needs_build: ${{ steps.filter.outputs.needs_build }}"
|
||||
echo "templates: ${{ steps.filter.outputs.templates }}"
|
||||
|
||||
core-build:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:core
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
plugins-build:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.needs_build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
|
||||
- run: pnpm install
|
||||
- run: pnpm run build:plugins
|
||||
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
database:
|
||||
- mongodb
|
||||
- postgres
|
||||
- postgres-custom-schema
|
||||
- postgres-uuid
|
||||
- supabase
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: payloadtests
|
||||
AWS_ENDPOINT_URL: http://127.0.0.1:4566
|
||||
AWS_ACCESS_KEY_ID: localstack
|
||||
AWS_SECRET_ACCESS_KEY: localstack
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Start LocalStack
|
||||
run: pnpm docker:start
|
||||
|
||||
- name: Start PostgreSQL
|
||||
uses: CasperWA/postgresql-action@v1.2
|
||||
with:
|
||||
postgresql version: '14' # See https://hub.docker.com/_/postgres for available versions
|
||||
postgresql db: ${{ env.POSTGRES_DB }}
|
||||
postgresql user: ${{ env.POSTGRES_USER }}
|
||||
postgresql password: ${{ env.POSTGRES_PASSWORD }}
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
|
||||
- name: Install Supabase CLI
|
||||
uses: supabase/setup-cli@v1
|
||||
with:
|
||||
version: latest
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Initialize Supabase
|
||||
run: |
|
||||
supabase init
|
||||
supabase start
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Wait for PostgreSQL
|
||||
run: sleep 30
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
|
||||
- name: Configure PostgreSQL
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE ROLE runner SUPERUSER LOGIN;"
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "SELECT version();"
|
||||
echo "POSTGRES_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" >> $GITHUB_ENV
|
||||
if: startsWith(matrix.database, 'postgres')
|
||||
|
||||
- name: Configure PostgreSQL with custom schema
|
||||
run: |
|
||||
psql "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB" -c "CREATE SCHEMA custom;"
|
||||
if: matrix.database == 'postgres-custom-schema'
|
||||
|
||||
- name: Configure Supabase
|
||||
run: |
|
||||
echo "POSTGRES_URL=postgresql://postgres:postgres@127.0.0.1:54322/postgres" >> $GITHUB_ENV
|
||||
if: matrix.database == 'supabase'
|
||||
|
||||
- name: Integration Tests
|
||||
run: pnpm test:int --testPathIgnorePatterns=test/fields # Ignore fields tests until reworked
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8096
|
||||
PAYLOAD_DATABASE: ${{ matrix.database }}
|
||||
POSTGRES_URL: ${{ env.POSTGRES_URL }}
|
||||
|
||||
tests-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# find test -type f -name 'e2e.spec.ts' | sort | xargs dirname | xargs -I {} basename {}
|
||||
suite:
|
||||
- _community
|
||||
- access-control
|
||||
# - admin
|
||||
- auth
|
||||
# - field-error-states
|
||||
# - fields-relationship
|
||||
# - fields
|
||||
- fields/lexical
|
||||
- live-preview
|
||||
# - localization
|
||||
# - plugin-nested-docs
|
||||
# - plugin-seo
|
||||
# - refresh-permissions
|
||||
# - uploads
|
||||
# - versions
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Install Playwright
|
||||
run: pnpm exec playwright install
|
||||
|
||||
- name: E2E Tests
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
retry_on: error
|
||||
max_attempts: 2
|
||||
timeout_minutes: 15
|
||||
command: pnpm test:e2e ${{ matrix.suite }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: test/test-results/
|
||||
retention-days: 1
|
||||
|
||||
tests-type-generation:
|
||||
if: false # This should be replaced with gen on a real Payload project
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Generate Payload Types
|
||||
run: pnpm dev:generate-types fields
|
||||
|
||||
- name: Generate GraphQL schema file
|
||||
run: pnpm dev:generate-graphql-schema graphql-schema-gen
|
||||
|
||||
plugins:
|
||||
runs-on: ubuntu-latest
|
||||
needs: core-build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pkg:
|
||||
- create-payload-app
|
||||
- plugin-cloud
|
||||
- plugin-cloud-storage
|
||||
- plugin-form-builder
|
||||
- plugin-nested-docs
|
||||
- plugin-search
|
||||
- plugin-sentry
|
||||
- plugin-seo
|
||||
|
||||
steps:
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Build ${{ matrix.pkg }}
|
||||
run: pnpm turbo run build --filter=${{ matrix.pkg }}
|
||||
|
||||
- name: Test ${{ matrix.pkg }}
|
||||
run: pnpm --filter ${{ matrix.pkg }} run test
|
||||
|
||||
templates:
|
||||
needs: changes
|
||||
if: false # Disable until templates are updated for 3.0
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
template: [blank, website, ecommerce]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 25
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@1.10.0
|
||||
with:
|
||||
mongodb-version: 6.0
|
||||
|
||||
- name: Build Template
|
||||
run: |
|
||||
cd templates/${{ matrix.template }}
|
||||
cp .env.example .env
|
||||
yarn install
|
||||
yarn build
|
||||
yarn generate:types
|
||||
88
.github/workflows/tests.yml
vendored
Normal file
88
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
jobs:
|
||||
build_yarn:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org
|
||||
scope: "@payloadcms"
|
||||
always-auth: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- run: yarn
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- run: yarn build
|
||||
|
||||
- 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: Generate GraphQL schema file
|
||||
run: yarn dev:generate-graphql-schema graphql-schema-gen
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: E2E Tests
|
||||
run: yarn test:e2e --bail
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: test-results/
|
||||
retention-days: 30
|
||||
|
||||
install_npm:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: https://registry.npmjs.org
|
||||
scope: "@payloadcms"
|
||||
always-auth: true
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-npm-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-npm-${{ env.cache-name }}-
|
||||
${{ runner.os }}-npm-
|
||||
${{ runner.os }}-
|
||||
- run: npm install
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
223
.gitignore
vendored
223
.gitignore
vendored
@@ -1,28 +1,13 @@
|
||||
coverage
|
||||
package-lock.json
|
||||
dist
|
||||
/.idea/*
|
||||
!/.idea/runConfigurations
|
||||
|
||||
.idea
|
||||
test-results
|
||||
.devcontainer
|
||||
.localstack
|
||||
/migrations
|
||||
.localstack
|
||||
.turbo
|
||||
|
||||
.turbo
|
||||
|
||||
# Ignore test directory media folder/files
|
||||
/media
|
||||
/versions
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
# Created by https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
@@ -45,10 +30,6 @@ Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### macOS Patch ###
|
||||
# iCloud generated files
|
||||
*.icloud
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
@@ -56,11 +37,6 @@ logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
@@ -73,12 +49,11 @@ lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
@@ -87,18 +62,15 @@ bower_components
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
# Typescript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
@@ -106,15 +78,6 @@ web_modules/
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
@@ -124,87 +87,34 @@ web_modules/
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
# Yarn Berry
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
### SublimeText ###
|
||||
# Cache files for Sublime Text
|
||||
# cache files for sublime text
|
||||
*.tmlanguage.cache
|
||||
*.tmPreferences.cache
|
||||
*.stTheme.cache
|
||||
|
||||
# Workspace files are user-specific
|
||||
# workspace files are user-specific
|
||||
*.sublime-workspace
|
||||
|
||||
# Project files should be checked into the repository, unless a significant
|
||||
# proportion of contributors will probably not be using Sublime Text
|
||||
# project files should be checked into the repository, unless a significant
|
||||
# proportion of contributors will probably not be using SublimeText
|
||||
# *.sublime-project
|
||||
|
||||
# SFTP configuration file
|
||||
# sftp configuration file
|
||||
sftp-config.json
|
||||
sftp-config-alt*.json
|
||||
|
||||
# Package control specific files
|
||||
Package Control.last-run
|
||||
@@ -224,53 +134,84 @@ GitHub.sublime-settings
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
.history
|
||||
.ionide
|
||||
|
||||
### WebStorm ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
cmake-build-debug/
|
||||
|
||||
# File-based project format
|
||||
# MongoDB Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Ruby plugin and RubyMine
|
||||
/.rakeTasks
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
### WebStorm Patch ###
|
||||
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||
|
||||
# *.iml
|
||||
# modules.xml
|
||||
# .idea/misc.xml
|
||||
# *.ipr
|
||||
|
||||
# Sonarlint plugin
|
||||
.idea/sonarlint
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
@@ -278,14 +219,26 @@ $RECYCLE.BIN/
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
# End of https://www.gitignore.io/api/node,macos,windows,webstorm,sublimetext,visualstudiocode
|
||||
|
||||
/build
|
||||
.swc
|
||||
# Ignore all uploads
|
||||
demo/upload
|
||||
demo/media
|
||||
demo/files
|
||||
|
||||
# Ignore build folder
|
||||
build
|
||||
|
||||
# Ignore built components
|
||||
components/index.js
|
||||
components/styles.css
|
||||
|
||||
# Ignore generated
|
||||
demo/generated-types.ts
|
||||
demo/generated-schema.graphql
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
pnpm run lint-staged --quiet
|
||||
8
.idea/runConfigurations/Run_Dev_Fields.xml
generated
8
.idea/runConfigurations/Run_Dev_Fields.xml
generated
@@ -1,8 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev Fields" type="NodeJSConfigurationType" application-parameters="--no-deprecation fields" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
|
||||
<envs>
|
||||
<env name="NODE_OPTIONS" value="--no-deprecation" />
|
||||
</envs>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
8
.idea/runConfigurations/Run_Dev__community.xml
generated
8
.idea/runConfigurations/Run_Dev__community.xml
generated
@@ -1,8 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run Dev _community" type="NodeJSConfigurationType" application-parameters="--no-deprecation _community" path-to-js-file="test/dev.js" working-dir="$PROJECT_DIR$">
|
||||
<envs>
|
||||
<env name="NODE_OPTIONS" value="--no-deprecation" />
|
||||
</envs>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
935
.migrations/drizzle-snapshot.json
Normal file
935
.migrations/drizzle-snapshot.json
Normal file
@@ -0,0 +1,935 @@
|
||||
{
|
||||
"id": "4b6f2243-b055-45d8-9afa-53cd2b729a12",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
"tables": {
|
||||
"posts_my_array_my_sub_array": {
|
||||
"name": "posts_my_array_my_sub_array",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"_order": {
|
||||
"name": "_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"sub_sub_field": {
|
||||
"name": "sub_sub_field",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_my_array_my_sub_array__parent_id_posts_my_array_id_fk": {
|
||||
"name": "posts_my_array_my_sub_array__parent_id_posts_my_array_id_fk",
|
||||
"tableFrom": "posts_my_array_my_sub_array",
|
||||
"tableTo": "posts_my_array",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_my_array": {
|
||||
"name": "posts_my_array",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"_order": {
|
||||
"name": "_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_my_array__parent_id_posts_id_fk": {
|
||||
"name": "posts_my_array__parent_id_posts_id_fk",
|
||||
"tableFrom": "posts_my_array",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_my_array_locales": {
|
||||
"name": "posts_my_array_locales",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"sub_field": {
|
||||
"name": "sub_field",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"_locale": {
|
||||
"name": "_locale",
|
||||
"type": "_locales",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_my_array_locales__parent_id_posts_my_array_id_fk": {
|
||||
"name": "posts_my_array_locales__parent_id_posts_my_array_id_fk",
|
||||
"tableFrom": "posts_my_array_locales",
|
||||
"tableTo": "posts_my_array",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_block1": {
|
||||
"name": "posts_block1",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"_order": {
|
||||
"name": "_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_path": {
|
||||
"name": "_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"non_localized_text": {
|
||||
"name": "non_localized_text",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"block_name": {
|
||||
"name": "block_name",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_block1__parent_id_posts_id_fk": {
|
||||
"name": "posts_block1__parent_id_posts_id_fk",
|
||||
"tableFrom": "posts_block1",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_block1_locales": {
|
||||
"name": "posts_block1_locales",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"localized_text": {
|
||||
"name": "localized_text",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"_locale": {
|
||||
"name": "_locale",
|
||||
"type": "_locales",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_block1_locales__parent_id_posts_block1_id_fk": {
|
||||
"name": "posts_block1_locales__parent_id_posts_block1_id_fk",
|
||||
"tableFrom": "posts_block1_locales",
|
||||
"tableTo": "posts_block1",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_block2_block_array": {
|
||||
"name": "posts_block2_block_array",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"_order": {
|
||||
"name": "_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"sub_block_array": {
|
||||
"name": "sub_block_array",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_block2_block_array__parent_id_posts_block2_id_fk": {
|
||||
"name": "posts_block2_block_array__parent_id_posts_block2_id_fk",
|
||||
"tableFrom": "posts_block2_block_array",
|
||||
"tableTo": "posts_block2",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_block2": {
|
||||
"name": "posts_block2",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"_order": {
|
||||
"name": "_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_path": {
|
||||
"name": "_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"number": {
|
||||
"name": "number",
|
||||
"type": "numeric",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"block_name": {
|
||||
"name": "block_name",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_block2__parent_id_posts_id_fk": {
|
||||
"name": "posts_block2__parent_id_posts_id_fk",
|
||||
"tableFrom": "posts_block2",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_my_group_group_array": {
|
||||
"name": "posts_my_group_group_array",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"_order": {
|
||||
"name": "_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"group_array_text": {
|
||||
"name": "group_array_text",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_my_group_group_array__parent_id_posts_id_fk": {
|
||||
"name": "posts_my_group_group_array__parent_id_posts_id_fk",
|
||||
"tableFrom": "posts_my_group_group_array",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts": {
|
||||
"name": "posts",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"my_group_sub_field": {
|
||||
"name": "my_group_sub_field",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"my_group_sub_group_sub_sub_field": {
|
||||
"name": "my_group_sub_group_sub_sub_field",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_locales": {
|
||||
"name": "posts_locales",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"number": {
|
||||
"name": "number",
|
||||
"type": "numeric",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"my_group_sub_field_localized": {
|
||||
"name": "my_group_sub_field_localized",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"my_group_sub_group_sub_sub_field_localized": {
|
||||
"name": "my_group_sub_group_sub_sub_field_localized",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"_locale": {
|
||||
"name": "_locale",
|
||||
"type": "_locales",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"_parent_id": {
|
||||
"name": "_parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_locales__parent_id_posts_id_fk": {
|
||||
"name": "posts_locales__parent_id_posts_id_fk",
|
||||
"tableFrom": "posts_locales",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"_parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"posts_relationships": {
|
||||
"name": "posts_relationships",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"parent_id": {
|
||||
"name": "parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"path": {
|
||||
"name": "path",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"order": {
|
||||
"name": "order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"pages_id": {
|
||||
"name": "pages_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"people_id": {
|
||||
"name": "people_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"posts_id": {
|
||||
"name": "posts_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"posts_relationships_parent_id_posts_id_fk": {
|
||||
"name": "posts_relationships_parent_id_posts_id_fk",
|
||||
"tableFrom": "posts_relationships",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"posts_relationships_pages_id_pages_id_fk": {
|
||||
"name": "posts_relationships_pages_id_pages_id_fk",
|
||||
"tableFrom": "posts_relationships",
|
||||
"tableTo": "pages",
|
||||
"columnsFrom": [
|
||||
"pages_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"posts_relationships_people_id_people_id_fk": {
|
||||
"name": "posts_relationships_people_id_people_id_fk",
|
||||
"tableFrom": "posts_relationships",
|
||||
"tableTo": "people",
|
||||
"columnsFrom": [
|
||||
"people_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"posts_relationships_posts_id_posts_id_fk": {
|
||||
"name": "posts_relationships_posts_id_posts_id_fk",
|
||||
"tableFrom": "posts_relationships",
|
||||
"tableTo": "posts",
|
||||
"columnsFrom": [
|
||||
"posts_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"pages": {
|
||||
"name": "pages",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"people": {
|
||||
"name": "people",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"full_name": {
|
||||
"name": "full_name",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"users": {
|
||||
"name": "users",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"reset_password_token": {
|
||||
"name": "reset_password_token",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"salt": {
|
||||
"name": "salt",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"hash": {
|
||||
"name": "hash",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"login_attempts": {
|
||||
"name": "login_attempts",
|
||||
"type": "numeric",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email_idx": {
|
||||
"name": "email_idx",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"payload_preferences": {
|
||||
"name": "payload_preferences",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"payload_preferences_relationships": {
|
||||
"name": "payload_preferences_relationships",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"parent_id": {
|
||||
"name": "parent_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"path": {
|
||||
"name": "path",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"order": {
|
||||
"name": "order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"users_id": {
|
||||
"name": "users_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"payload_preferences_relationships_parent_id_payload_preferences_id_fk": {
|
||||
"name": "payload_preferences_relationships_parent_id_payload_preferences_id_fk",
|
||||
"tableFrom": "payload_preferences_relationships",
|
||||
"tableTo": "payload_preferences",
|
||||
"columnsFrom": [
|
||||
"parent_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"payload_preferences_relationships_users_id_users_id_fk": {
|
||||
"name": "payload_preferences_relationships_users_id_users_id_fk",
|
||||
"tableFrom": "payload_preferences_relationships",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"users_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"payload_migrations": {
|
||||
"name": "payload_migrations",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"batch": {
|
||||
"name": "batch",
|
||||
"type": "numeric",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"schema": {
|
||||
"name": "schema",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"_locales": {
|
||||
"name": "_locales",
|
||||
"values": {
|
||||
"en": "en",
|
||||
"es": "es"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schemas": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
v18.19.1
|
||||
v16.14.2
|
||||
|
||||
2
.npmrc
2
.npmrc
@@ -1,2 +0,0 @@
|
||||
symlink=true
|
||||
node-linker=isolated # due to a typescript bug, isolated mode requires @types/express-serve-static-core, terser and monaco-editor to be installed https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189 along with two other changes in the code which I've marked with (tsbugisolatedmode) in the code
|
||||
@@ -1,12 +0,0 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
**/docs/**
|
||||
tsconfig.json
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"semi": false
|
||||
}
|
||||
22
.release-it.json
Normal file
22
.release-it.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"verbose": true,
|
||||
"git": {
|
||||
"commitMessage": "chore(release): v${version}",
|
||||
"requireCleanWorkingDir": true
|
||||
},
|
||||
"github": {
|
||||
"release": true
|
||||
},
|
||||
"npm": {
|
||||
"skipChecks": true
|
||||
},
|
||||
"hooks": {
|
||||
"before:init": ["yarn", "yarn clean", "yarn test"]
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/conventional-changelog": {
|
||||
"preset": "angular",
|
||||
"infile": "CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
.release-it.pre.json
Normal file
25
.release-it.pre.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"verbose": true,
|
||||
"git": {
|
||||
"requireCleanWorkingDir": false,
|
||||
"commit": false,
|
||||
"push": false,
|
||||
"tag": false
|
||||
},
|
||||
"github": {
|
||||
"release": true
|
||||
},
|
||||
"npm": {
|
||||
"skipChecks": true,
|
||||
"tag": "canary"
|
||||
},
|
||||
"hooks": {
|
||||
"before:init": ["yarn", "yarn clean", "yarn test"]
|
||||
},
|
||||
"plugins": {
|
||||
"@release-it/conventional-changelog": {
|
||||
"preset": "angular",
|
||||
"infile": "CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
15
.swcrc
15
.swcrc
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "es6"
|
||||
}
|
||||
}
|
||||
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"firsttris.vscode-jest-runner",
|
||||
"ms-playwright.playwright"
|
||||
]
|
||||
}
|
||||
199
.vscode/launch.json
vendored
199
.vscode/launch.json
vendored
@@ -1,187 +1,112 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "pnpm generate:types",
|
||||
"name": "Generate Types CLI",
|
||||
"command": "yarn run dev _community",
|
||||
"name": "Run Dev Community",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Community",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev live-preview -- --no-turbo",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Live Preview",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev plugin-cloud-storage",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev - plugin-cloud-storage",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"env": {
|
||||
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"command": "yarn run dev fields",
|
||||
"name": "Run Dev Fields",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev:postgres versions",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"command": "yarn run dev:postgres postgres -- -I", // Allow input
|
||||
"name": "Run Dev Postgres",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
"type": "node-terminal",
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev versions",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"command": "yarn run dev versions",
|
||||
"name": "Run Dev Versions",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev localization",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Localization",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev uploads",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Uploads",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "PAYLOAD_BUNDLER=vite pnpm run dev fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Fields (Vite)",
|
||||
"request": "launch",
|
||||
"type": "node-terminal",
|
||||
"env": {
|
||||
"NODE_ENV": "production"
|
||||
}
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run test:int live-preview",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Live Preview Int Tests",
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run test:int plugin-search",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Search Plugin Int Tests",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts build",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/fields/config.ts",
|
||||
"PAYLOAD_BUNDLER": "vite",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
},
|
||||
"name": "Build CLI - Vite",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts build",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/fields/config.ts",
|
||||
"PAYLOAD_ANALYZE_BUNDLE": "true",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
},
|
||||
"name": "Build CLI - Webpack",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:status",
|
||||
"name": "Migrate CLI - migrate",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:status",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - status",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:create yass",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:create yass",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - create",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:down",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "mongoose",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:down",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - down",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:reset",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "mongoose",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:reset",
|
||||
"request": "launch",
|
||||
"name": "Migrate CLI - reset",
|
||||
"outputCapture": "std",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "ts-node ./packages/payload/src/bin/index.ts migrate:refresh",
|
||||
"env": {
|
||||
"PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_DATABASE": "mongoose",
|
||||
"DISABLE_SWC": "true" // SWC messes up debugging the bin scripts
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"name": "Migrate CLI - refresh",
|
||||
"outputCapture": "std",
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"command": "ts-node src/bin/migrate.ts migrate:refresh",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
],
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0"
|
||||
"name": "Migrate CLI - refresh",
|
||||
"env": {
|
||||
// "PAYLOAD_CONFIG_PATH": "test/migrations-cli/config.ts",
|
||||
"PAYLOAD_CONFIG_PATH": "test/postgres/config.ts",
|
||||
"PAYLOAD_DATABASE": "postgres",
|
||||
// "PAYLOAD_DROP_DATABASE": "true",
|
||||
},
|
||||
"outputCapture": "std",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
44
.vscode/settings.json
vendored
44
.vscode/settings.json
vendored
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"npm.packageManager": "pnpm",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"editor.formatOnSaveMode": "file",
|
||||
// All ESLint rules to 'warn' to differentate from TypeScript's 'error' level
|
||||
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
// Load .git-blame-ignore-revs file
|
||||
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],
|
||||
"[javascript][typescript][typescriptreact]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
}
|
||||
}
|
||||
1594
CHANGELOG.md
1594
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@ If you find a vulnerability within the core Payload repository, and we determine
|
||||
|
||||
## Documentation edits
|
||||
|
||||
Payload documentation can be found directly within its codebase, and you can feel free to make changes / improvements to any of it through opening a PR. We utilize these files directly in our website and will periodically deploy documentation updates as necessary.
|
||||
Payload documentation can be found directly within its codebase and you can feel free to make changes / improvements to any of it through opening a PR. We utilize these files directly in our website and will periodically deploy documentation updates as necessary.
|
||||
|
||||
## Building additional features
|
||||
|
||||
@@ -30,17 +30,9 @@ Our design review ensures that proposed changes fit seamlessly with other compon
|
||||
|
||||
To help us work on new features, you can create a new feature request post in [GitHub Discussion](https://github.com/payloadcms/payload/discussions) or discuss it in our [Discord](https://discord.com/invite/payload). New functionality often has large implications across the entire Payload repo, so it is best to discuss the architecture and approach before starting work on a pull request.
|
||||
|
||||
### Installation & Requirements
|
||||
|
||||
Payload is structured as a Monorepo, encompassing not only the core Payload platform but also various plugins and packages. To install all required dependencies, you have to run `pnpm install` once in the root directory. **PNPM IS REQUIRED!** Yarn or npm will not work - you will have to use pnpm to develop in the core repository. In most systems, the easiest way to install pnpm is to run `corepack enable` in your terminal.
|
||||
|
||||
If you're coming from a very outdated version of payload, it is recommended to nuke the node_modules folder before running pnpm install. On UNIX systems, you can easily do that using the `pnpm clean:unix` command, which will delete all node_modules folders and build artefacts.
|
||||
|
||||
It is also recommended to use at least Node v18 or higher. You can check your current node version by typing `node --version` in your terminal. The easiest way to switch between different node versions is to use [nvm](https://github.com/nvm-sh/nvm#intro).
|
||||
|
||||
### Code
|
||||
|
||||
Most new functionality should keep testing in mind. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
|
||||
Most new functionality should keep testing in mind. With 1.0, testability of new features has been vastly improved. All top-level directories within the `test/` directory are for testing a specific category: `fields`, `collections`, etc.
|
||||
|
||||
If it makes sense to add your feature to an existing test directory, please do so.
|
||||
|
||||
@@ -57,35 +49,21 @@ A typical directory with `test/` will be structured like this:
|
||||
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
|
||||
- `int.spec.ts` - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
|
||||
- `e2e.spec.ts` - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests. These tests are typically only needed if a large change is being made to the Admin UI.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types my-test-dir`. Replace `my-test-dir` with the name of your testing directory.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types my-test-dir`.
|
||||
|
||||
Each test directory is split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
|
||||
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config.
|
||||
|
||||
The following command will start Payload with your config: `pnpm dev my-test-dir`. Example: `pnpm dev fields` for the test/`fields` test suite. This command will start up Payload using your config and refresh a test database on every restart. If you're using VS Code, the most common run configs are automatically added to your editor - you should be able to find them in your VS Code launch tab.
|
||||
The following command will start Payload with your config: `yarn dev my-test-dir`. This command will start up Payload using your config and refresh a test database on every restart.
|
||||
|
||||
By default, payload will [automatically log you in](https://payloadcms.com/docs/authentication/config#admin-autologin) with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `pnpm dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
|
||||
By default, it will automatically log you in with the default credentials. To disable that, you can either pass in the --no-auto-login flag (example: `yarn dev my-test-dir --no-auto-login`) or set the `PAYLOAD_PUBLIC_DISABLE_AUTO_LOGIN` environment variable to `false`.
|
||||
|
||||
The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password. These are used in the auto-login.
|
||||
|
||||
### Testing with your own MongoDB database
|
||||
|
||||
If you wish to use your own MongoDB database for the `test` directory instead of using the in memory database, all you need to do is add the following env vars to the `test/dev.ts` file:
|
||||
If you wish to use to your own Mongo database for the `test` directory instead of using the in memory database, all you need to do is add the following env vars to the `test/dev.ts` file:
|
||||
|
||||
- `process.env.NODE_ENV`
|
||||
- `process.env.PAYLOAD_TEST_MONGO_URL`
|
||||
- Simply set `process.env.NODE_ENV` to `test` and set `process.env.PAYLOAD_TEST_MONGO_URL` to your MongoDB URL e.g. `mongodb://127.0.0.1/your-test-db`.
|
||||
- Simply set `process.env.NODE_ENV` to `test` and set `process.env.PAYLOAD_TEST_MONGO_URL` to your mongo url e.g. `mongodb://127.0.0.1/your-test-db`.
|
||||
|
||||
### Using Postgres
|
||||
|
||||
If you have postgres installed on your system, you can also run the test suites using postgres. By default, mongodb is used.
|
||||
|
||||
To do that, simply set the `PAYLOAD_DATABASE` environment variable to `postgres`.
|
||||
|
||||
### Running the e2e and int tests
|
||||
|
||||
You can run the entire test suite using `pnpm test`. If you wish to only run e2e tests, you can use `pnpm test:e2e`. If you wish to only run int tests, you can use `pnpm test:int`.
|
||||
|
||||
By default, `pnpm test:int` will only run int test against MongoDB. To run int tests against postgres, you can use `pnpm test:int:postgres`. You will have to have postgres installed on your system for this to work.
|
||||
NOTE: It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as E-Mail and `test` as password.
|
||||
|
||||
### Commits
|
||||
|
||||
@@ -111,14 +89,3 @@ If you are committing to [templates](./templates) or [examples](./examples), use
|
||||
## Pull Requests
|
||||
|
||||
For all Pull Requests, you should be extremely descriptive about both your problem and proposed solution. If there are any affected open or closed issues, please leave the issue number in your PR message.
|
||||
|
||||
## Previewing docs
|
||||
|
||||
This is how you can preview changes you made locally to the docs:
|
||||
|
||||
1. Clone our [website repository](https://github.com/payloadcms/website)
|
||||
2. Run `yarn install`
|
||||
3. Duplicate the `.env.example` file and rename it to `.env`
|
||||
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
|
||||
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: *Docs successfully written to /.../website/src/app/docs.json*. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
|
||||
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
|
||||
|
||||
@@ -6,10 +6,9 @@ To report an issue, please follow the steps below:
|
||||
2. Add necessary collections/globals/fields to the `test/_community` directory to recreate the issue you are experiencing
|
||||
3. Create an issue and add a link to your forked repo
|
||||
|
||||
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/\_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
|
||||
**The goal is to isolate the problem by reducing the number of fields/collections you add to the test/_community folder. This folder is not meant for you to copy your project into, but to recreate the issue you are experiencing with minimal config.**
|
||||
|
||||
## Test directory file tree explanation
|
||||
|
||||
```text
|
||||
.
|
||||
├── config.ts
|
||||
@@ -21,49 +20,45 @@ To report an issue, please follow the steps below:
|
||||
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
|
||||
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
|
||||
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
|
||||
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
|
||||
|
||||
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
|
||||
|
||||
## How to start test collection admin UI
|
||||
|
||||
To start the admin panel so you can manually recreate your issue, you can run the following command:
|
||||
|
||||
```bash
|
||||
# This command will start up Payload using your config
|
||||
# NOTE: it will wipe the test database on restart
|
||||
pnpm dev _community
|
||||
```
|
||||
```bash
|
||||
# This command will start up Payload using your config
|
||||
# NOTE: it will wipe the test database on restart
|
||||
yarn dev _community
|
||||
```
|
||||
|
||||
|
||||
## Testing is optional but encouraged
|
||||
|
||||
An issue does not need to have failing tests — reproduction steps with your forked repo are enough at this point. Some people like to dive deeper and we want to give you the guidance/tools to do so. Read more below.
|
||||
|
||||
### How to run integration tests (Payload API tests)
|
||||
|
||||
There are a couple ways to do this:
|
||||
|
||||
- **Granularly** - you can run individual tests in vscode by installing the Jest Runner plugin and using that to run individual tests. Clicking the `debug` button will run the test in debug mode allowing you to set break points.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/int-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/int-debug.png" />
|
||||
|
||||
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
|
||||
|
||||
```bash
|
||||
pnpm test:int _community
|
||||
yarn test:int _community
|
||||
```
|
||||
|
||||
### How to run E2E tests (Admin Panel UI tests)
|
||||
|
||||
The easiest way to run E2E tests is to install
|
||||
|
||||
- [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright)
|
||||
- [Playwright Runner](https://marketplace.visualstudio.com/items?itemName=ortoni.ortoni)
|
||||
|
||||
Once they are installed you can open the `testing` tab in vscode sidebar and drill down to the test you want to run, i.e. `/test/_community/e2e.spec.ts`
|
||||
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/main/src/admin/assets/images/github/e2e-debug.png" />
|
||||
<img src="https://raw.githubusercontent.com/payloadcms/payload/master/src/admin/assets/images/github/e2e-debug.png" />
|
||||
|
||||
|
||||
#### Notes
|
||||
|
||||
- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password.
|
||||
|
||||
54
README.md
54
README.md
@@ -1,24 +1,30 @@
|
||||
<a href="https://payloadcms.com"><img width="100%" src="https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/assets/images/github-banner-alt.jpg?raw=true" alt="Payload headless CMS Admin panel built with React" /></a>
|
||||
<a href="https://payloadcms.com">
|
||||
<img width="100%" src="src/admin/assets/images/github-banner-alt.jpg" alt="Payload headless CMS Admin panel built with React" />
|
||||
</a>
|
||||
<br />
|
||||
<br />
|
||||
<p align="left">
|
||||
<a href="https://github.com/payloadcms/payload/actions"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/main.yml?style=flat-square"></a>
|
||||
<a href="https://github.com/payloadcms/payload/actions">
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/payloadcms/payload/tests.yml?style=flat-square">
|
||||
</a>
|
||||
|
||||
<a href="https://discord.gg/payload"><img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" /></a>
|
||||
<a href="https://discord.gg/payload">
|
||||
<img alt="Discord" src="https://img.shields.io/discord/967097582721572934?label=Discord&color=7289da&style=flat-square" />
|
||||
</a>
|
||||
|
||||
<a href="https://www.npmjs.com/package/payload"><img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" /></a>
|
||||
<a href="https://www.npmjs.com/package/payload">
|
||||
<img alt="npm" src="https://img.shields.io/npm/v/payload?style=flat-square" />
|
||||
</a>
|
||||
|
||||
<a href="https://twitter.com/payloadcms"><img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" /></a>
|
||||
<a href="https://twitter.com/payloadcms">
|
||||
<img src="https://img.shields.io/badge/follow-payloadcms-1DA1F2?logo=twitter&style=flat-square" alt="Payload Twitter" />
|
||||
</a>
|
||||
</p>
|
||||
<hr/>
|
||||
<h4>
|
||||
<a target="_blank" href="https://payloadcms.com/docs/getting-started/what-is-payload" rel="dofollow"><strong>Explore the Docs</strong></a> · <a target="_blank" href="https://payloadcms.com/community-help" rel="dofollow"><strong>Community Help</strong></a> · <a target="_blank" href="https://demo.payloadcms.com/" rel="dofollow"><strong>Try Live Demo</strong></a> · <a target="_blank" href="https://github.com/payloadcms/payload/discussions/1539" rel="dofollow"><strong>Roadmap</strong></a> · <a target="_blank" href="https://www.g2.com/products/payload-cms/reviews#reviews" rel="dofollow"><strong>View G2 Reviews</strong></a>
|
||||
</h4>
|
||||
<hr/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🎉 <strong>Payload 2.0 is now available!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>.
|
||||
|
||||
<h3>Benefits over a regular CMS</h3>
|
||||
<ul>
|
||||
<li>Don’t hit some third-party SaaS API, hit your own API</li>
|
||||
@@ -33,7 +39,6 @@
|
||||
</ul>
|
||||
|
||||
## ☁️ Deploy instantly with Payload Cloud.
|
||||
|
||||
Create a cloud account, connect your GitHub, and [deploy in minutes](https://payloadcms.com/new).
|
||||
|
||||
## 🚀 Get started by self-hosting completely free, forever.
|
||||
@@ -41,27 +46,18 @@ Create a cloud account, connect your GitHub, and [deploy in minutes](https://pay
|
||||
Before beginning to work with Payload, make sure you have all of the [required software](https://payloadcms.com/docs/getting-started/installation).
|
||||
|
||||
```text
|
||||
npx create-payload-app@latest
|
||||
npx create-payload-app
|
||||
```
|
||||
|
||||
Alternatively, it only takes about five minutes to [create an app from scratch](https://payloadcms.com/docs/getting-started/installation#from-scratch).
|
||||
|
||||
## 🖱️ One-click templates
|
||||
### 🛒 [E-Commerce](https://github.com/payloadcms/payload/tree/master/templates/ecommerce)
|
||||
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Best of all, you can extend it as much as you need.
|
||||
|
||||
Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.
|
||||
[All Official Templates](https://github.com/orgs/payloadcms/repositories?q=topic%3Apayload-template) · [Community Templates](https://github.com/topics/payload-template)
|
||||
|
||||
### [🛒 E-Commerce](https://github.com/payloadcms/payload/tree/main/templates/ecommerce)
|
||||
|
||||
Eliminate the need to combine Shopify and a CMS, and instead do it all with Payload + Stripe. Comes with a beautiful, fully functional front-end complete with shopping cart, checkout, orders, and much more.
|
||||
|
||||
### [🌐 Website](https://github.com/payloadcms/payload/tree/main/templates/website)
|
||||
|
||||
Build any kind of website, blog, or portfolio from small to enterprise. Comes with a beautiful, fully functional front-end complete with posts, projects, comments, and much more.
|
||||
|
||||
We're constantly adding more templates to our [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates). If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.
|
||||
|
||||
- [Official Templates](https://github.com/payloadcms/payload/tree/main/templates)
|
||||
- [Community Templates](https://github.com/topics/payload-template)
|
||||
**If you maintain your own template, consider adding the `payload-template` topic to your GitHub repository for others to find.**
|
||||
|
||||
## ✨ Features
|
||||
|
||||
@@ -89,19 +85,15 @@ We're constantly adding more templates to our [Templates Directory](https://gith
|
||||
|
||||
Check out the [Payload website](https://payloadcms.com/docs/getting-started/what-is-payload) to find in-depth documentation for everything that Payload offers.
|
||||
|
||||
Migrating from v1 to v2? Check out the [2.0 Release Notes](https://github.com/payloadcms/payload/releases/tag/v2.0.0) on how to do it.
|
||||
|
||||
## 🙋 Contributing
|
||||
|
||||
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./CONTRIBUTING.md).
|
||||
If you want to add contributions to this repository, please follow the instructions in [contributing.md](./contributing.md).
|
||||
|
||||
## 📚 Examples
|
||||
|
||||
The [Examples Directory](./examples) is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.
|
||||
The examples directory is a great resource for learning how to setup Payload in a variety of different ways.
|
||||
|
||||
- [Examples Directory](./examples)
|
||||
- [Payload Blog](https://payloadcms.com/blog)
|
||||
- [Payload YouTube](https://www.youtube.com/@payloadcms)
|
||||
[Examples Directory](./examples)
|
||||
|
||||
## 🔌 Plugins
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
import config from '@payload-config'
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import { NotFoundView } from '@payloadcms/next/views/NotFound/index.js'
|
||||
|
||||
type Args = {
|
||||
params: {
|
||||
segments: string[]
|
||||
}
|
||||
searchParams: {
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
}
|
||||
|
||||
const NotFound = ({ params, searchParams }: Args) => NotFoundView({ config, params, searchParams })
|
||||
|
||||
export default NotFound
|
||||
@@ -1,22 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import config from '@payload-config'
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import { RootPage, generatePageMetadata } from '@payloadcms/next/views/Root/index.js'
|
||||
|
||||
type Args = {
|
||||
params: {
|
||||
segments: string[]
|
||||
}
|
||||
searchParams: {
|
||||
[key: string]: string | string[]
|
||||
}
|
||||
}
|
||||
|
||||
export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
|
||||
generatePageMetadata({ config, params, searchParams })
|
||||
|
||||
const Page = ({ params, searchParams }: Args) => RootPage({ config, params, searchParams })
|
||||
|
||||
export default Page
|
||||
@@ -1,9 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
import config from '@payload-config'
|
||||
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes/index.js'
|
||||
|
||||
export const GET = REST_GET(config)
|
||||
export const POST = REST_POST(config)
|
||||
export const DELETE = REST_DELETE(config)
|
||||
export const PATCH = REST_PATCH(config)
|
||||
@@ -1,6 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
import config from '@payload-config'
|
||||
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes/index.js'
|
||||
|
||||
export const GET = GRAPHQL_PLAYGROUND_GET(config)
|
||||
@@ -1,6 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
import config from '@payload-config'
|
||||
import { GRAPHQL_POST } from '@payloadcms/next/routes/index.js'
|
||||
|
||||
export const POST = GRAPHQL_POST(config)
|
||||
@@ -1,15 +0,0 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
import configPromise from '@payload-config'
|
||||
import { RootLayout } from '@payloadcms/next/layouts/Root/index.js'
|
||||
/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
|
||||
import React from 'react'
|
||||
|
||||
import './custom.scss'
|
||||
|
||||
type Args = {
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const Layout = ({ children }: Args) => <RootLayout config={configPromise}>{children}</RootLayout>
|
||||
|
||||
export default Layout
|
||||
@@ -1,43 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useLivePreview } from '@payloadcms/live-preview-react'
|
||||
import React from 'react'
|
||||
|
||||
import type { Page as PageType } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '../../_api/serverURL.js'
|
||||
import { Blocks } from '../../_components/Blocks/index.js'
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import { Hero } from '../../_components/Hero/index.js'
|
||||
|
||||
export const PageClient: React.FC<{
|
||||
page: PageType
|
||||
}> = ({ page: initialPage }) => {
|
||||
const { data } = useLivePreview<PageType>({
|
||||
depth: 2,
|
||||
initialData: initialPage,
|
||||
serverURL: PAYLOAD_SERVER_URL,
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Gutter>
|
||||
<h1 id="page-title">{data.title}</h1>
|
||||
</Gutter>
|
||||
<Hero {...data?.hero} />
|
||||
<Blocks
|
||||
blocks={[
|
||||
...(data?.layout ?? []),
|
||||
{
|
||||
blockName: 'Relationships',
|
||||
blockType: 'relationships',
|
||||
data,
|
||||
},
|
||||
]}
|
||||
disableTopPadding={
|
||||
!data?.hero || data?.hero?.type === 'none' || data?.hero?.type === 'lowImpact'
|
||||
}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { notFound } from 'next/navigation.js'
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { fetchDoc } from '../../_api/fetchDoc.js'
|
||||
import { fetchDocs } from '../../_api/fetchDocs.js'
|
||||
import { PageClient } from './page.client.js'
|
||||
|
||||
// eslint-disable-next-line no-restricted-exports
|
||||
export default async function Page({ params: { slug = 'home' } }) {
|
||||
let page: Page | null = null
|
||||
|
||||
try {
|
||||
page = await fetchDoc<Page>({
|
||||
slug,
|
||||
collection: 'pages',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
return notFound()
|
||||
}
|
||||
|
||||
return <PageClient page={page} />
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
try {
|
||||
const pages = await fetchDocs<Page>('pages')
|
||||
return pages?.map(({ slug }) => slug)
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useLivePreview } from '@payloadcms/live-preview-react'
|
||||
import React from 'react'
|
||||
|
||||
import type { Post as PostType } from '../../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
|
||||
import { Blocks } from '../../../_components/Blocks/index.js'
|
||||
import { PostHero } from '../../../_heros/PostHero/index.js'
|
||||
|
||||
export const PostClient: React.FC<{
|
||||
post: PostType
|
||||
}> = ({ post: initialPost }) => {
|
||||
const { data } = useLivePreview<PostType>({
|
||||
depth: 2,
|
||||
initialData: initialPost,
|
||||
serverURL: PAYLOAD_SERVER_URL,
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<PostHero post={data} />
|
||||
<Blocks blocks={data?.layout} />
|
||||
<Blocks
|
||||
blocks={[
|
||||
{
|
||||
blockName: 'Related Posts',
|
||||
blockType: 'relatedPosts',
|
||||
docs: data?.relatedPosts,
|
||||
introContent: [
|
||||
{
|
||||
type: 'h4',
|
||||
children: [
|
||||
{
|
||||
text: 'Related posts',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'p',
|
||||
children: [
|
||||
{
|
||||
text: 'The posts displayed here are individually selected for this page. Admins can select any number of related posts to display here and the layout will adjust accordingly. Alternatively, you could swap this out for the "Archive" block to automatically populate posts by category complete with pagination. To manage related posts, ',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
children: [
|
||||
{
|
||||
text: 'navigate to the admin dashboard',
|
||||
},
|
||||
],
|
||||
url: `/admin/collections/posts/${data?.id}`,
|
||||
},
|
||||
{
|
||||
text: '.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
relationTo: 'posts',
|
||||
},
|
||||
]}
|
||||
disableTopPadding
|
||||
/>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { notFound } from 'next/navigation.js'
|
||||
import React from 'react'
|
||||
|
||||
import type { Post } from '../../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { fetchDoc } from '../../../_api/fetchDoc.js'
|
||||
import { fetchDocs } from '../../../_api/fetchDocs.js'
|
||||
import { PostClient } from './page.client.js'
|
||||
|
||||
export default async function Post({ params: { slug = '' } }) {
|
||||
let post: Post | null = null
|
||||
|
||||
try {
|
||||
post = await fetchDoc<Post>({
|
||||
slug,
|
||||
collection: 'posts',
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
if (!post) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
return <PostClient post={post} />
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
try {
|
||||
const posts = await fetchDocs<Post>('posts')
|
||||
return posts?.map(({ slug }) => slug)
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import QueryString from 'qs'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||
|
||||
export const fetchDoc = async <T>(args: {
|
||||
collection: string
|
||||
depth?: number
|
||||
id?: string
|
||||
slug?: string
|
||||
}): Promise<T> => {
|
||||
const { id, slug, collection, depth = 2 } = args || {}
|
||||
|
||||
const queryString = QueryString.stringify(
|
||||
{
|
||||
...(slug ? { 'where[slug][equals]': slug } : {}),
|
||||
...(depth ? { depth } : {}),
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
|
||||
const doc: T = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}${queryString}`, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'GET',
|
||||
})
|
||||
?.then((res) => res.json())
|
||||
?.then((res) => {
|
||||
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching doc')
|
||||
return res?.docs?.[0]
|
||||
})
|
||||
|
||||
return doc
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||
|
||||
export const fetchDocs = async <T>(collection: string): Promise<T[]> => {
|
||||
const docs: T[] = await fetch(`${PAYLOAD_SERVER_URL}/api/${collection}?depth=0&limit=100`, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'GET',
|
||||
})
|
||||
?.then((res) => res.json())
|
||||
?.then((res) => {
|
||||
if (res.errors) throw new Error(res?.errors?.[0]?.message ?? 'Error fetching docs')
|
||||
|
||||
return res?.docs
|
||||
})
|
||||
|
||||
return docs
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Footer } from '../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||
|
||||
export async function fetchFooter(): Promise<Footer> {
|
||||
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
|
||||
|
||||
const footer = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/footer`, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'GET',
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error('Error fetching doc')
|
||||
return res.json()
|
||||
})
|
||||
?.then((res) => {
|
||||
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching footer')
|
||||
return res
|
||||
})
|
||||
|
||||
return footer
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { Header } from '../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from './serverURL.js'
|
||||
|
||||
export async function fetchHeader(): Promise<Header> {
|
||||
if (!PAYLOAD_SERVER_URL) throw new Error('PAYLOAD_SERVER_URL not found')
|
||||
|
||||
const header = await fetch(`${PAYLOAD_SERVER_URL}/api/globals/header`, {
|
||||
cache: 'no-store',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'GET',
|
||||
})
|
||||
?.then((res) => {
|
||||
if (!res.ok) throw new Error('Error fetching doc')
|
||||
return res.json()
|
||||
})
|
||||
?.then((res) => {
|
||||
if (res?.errors) throw new Error(res?.errors[0]?.message || 'Error fetching header')
|
||||
return res
|
||||
})
|
||||
|
||||
return header
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export const PAYLOAD_SERVER_URL = 'http://localhost:3000'
|
||||
@@ -1,13 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.archiveBlock {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.introContent {
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { ArchiveBlockProps } from './types.js'
|
||||
|
||||
import { CollectionArchive } from '../../_components/CollectionArchive/index.js'
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import RichText from '../../_components/RichText/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const ArchiveBlock: React.FC<
|
||||
ArchiveBlockProps & {
|
||||
id?: string
|
||||
}
|
||||
> = (props) => {
|
||||
const {
|
||||
id,
|
||||
categories,
|
||||
introContent,
|
||||
limit,
|
||||
populateBy,
|
||||
populatedDocs,
|
||||
populatedDocsTotal,
|
||||
relationTo,
|
||||
selectedDocs,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<div className={classes.archiveBlock} id={`block-${id}`}>
|
||||
{introContent && (
|
||||
<Gutter className={classes.introContent}>
|
||||
<RichText content={introContent} />
|
||||
</Gutter>
|
||||
)}
|
||||
<CollectionArchive
|
||||
categories={categories}
|
||||
limit={limit}
|
||||
populateBy={populateBy}
|
||||
populatedDocs={populatedDocs}
|
||||
populatedDocsTotal={populatedDocsTotal}
|
||||
relationTo={relationTo}
|
||||
selectedDocs={selectedDocs}
|
||||
sort="-publishedDate"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
export type ArchiveBlockProps = Extract<
|
||||
Exclude<Page['layout'], undefined>[0],
|
||||
{ blockType: 'archive' }
|
||||
>
|
||||
@@ -1,47 +0,0 @@
|
||||
@use '../../_css/queries.scss' as *;
|
||||
|
||||
$spacer-h: calc(var(--block-padding) / 2);
|
||||
|
||||
.callToAction {
|
||||
padding-left: $spacer-h;
|
||||
padding-right: $spacer-h;
|
||||
position: relative;
|
||||
background-color: var(--color-base-100);
|
||||
color: var(--color-base-1000);
|
||||
}
|
||||
|
||||
.invert {
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
gap: $spacer-h;
|
||||
align-items: center;
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.linkGroup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
|
||||
> * {
|
||||
margin-bottom: calc(var(--base) / 2);
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import { CMSLink } from '../../_components/Link/index.js'
|
||||
import RichText from '../../_components/RichText/index.js'
|
||||
import { VerticalPadding } from '../../_components/VerticalPadding/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'cta' }>
|
||||
|
||||
export const CallToActionBlock: React.FC<
|
||||
Props & {
|
||||
id?: string
|
||||
}
|
||||
> = ({ invertBackground, links, richText }) => {
|
||||
return (
|
||||
<Gutter>
|
||||
<VerticalPadding
|
||||
className={[classes.callToAction, invertBackground && classes.invert]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<div className={classes.wrap}>
|
||||
<div className={classes.content}>
|
||||
<RichText className={classes.richText} content={richText} />
|
||||
</div>
|
||||
<div className={classes.linkGroup}>
|
||||
{(links || []).map(({ link }, i) => {
|
||||
return <CMSLink key={i} {...link} invert={invertBackground} />
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</VerticalPadding>
|
||||
</Gutter>
|
||||
)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
gap: var(--base) calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: var(--base) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column--oneThird {
|
||||
grid-column-end: span 4;
|
||||
}
|
||||
|
||||
.column--half {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
.column--twoThirds {
|
||||
grid-column-end: span 8;
|
||||
}
|
||||
|
||||
.column--full {
|
||||
grid-column-end: span 12;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-top: var(--base);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import { CMSLink } from '../../_components/Link/index.js'
|
||||
import RichText from '../../_components/RichText/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'content' }>
|
||||
|
||||
export const ContentBlock: React.FC<
|
||||
Props & {
|
||||
id?: string
|
||||
}
|
||||
> = (props) => {
|
||||
const { columns } = props
|
||||
|
||||
return (
|
||||
<Gutter className={classes.content}>
|
||||
<div className={classes.grid}>
|
||||
{columns && columns.length > 0 ? (
|
||||
<Fragment>
|
||||
{columns.map((col, index) => {
|
||||
const { enableLink, link, richText, size } = col
|
||||
|
||||
return (
|
||||
<div className={[classes.column, classes[`column--${size}`]].join(' ')} key={index}>
|
||||
<RichText content={richText} />
|
||||
{enableLink && <CMSLink className={classes.link} {...link} />}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</Fragment>
|
||||
) : null}
|
||||
</div>
|
||||
</Gutter>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
.mediaBlock {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: var(--color-base-500);
|
||||
margin-top: var(--base);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import type { StaticImageData } from 'next/image.js'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import { Media } from '../../_components/Media/index.js'
|
||||
import RichText from '../../_components/RichText/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = Extract<Exclude<Page['layout'], undefined>[0], { blockType: 'mediaBlock' }> & {
|
||||
id?: string
|
||||
staticImage?: StaticImageData
|
||||
}
|
||||
|
||||
export const MediaBlock: React.FC<Props> = (props) => {
|
||||
const { media, position = 'default', staticImage } = props
|
||||
|
||||
let caption
|
||||
if (media && typeof media === 'object') caption = media.caption
|
||||
|
||||
return (
|
||||
<div className={classes.mediaBlock}>
|
||||
{position === 'fullscreen' && (
|
||||
<div className={classes.fullscreen}>
|
||||
<Media resource={media} src={staticImage} />
|
||||
</div>
|
||||
)}
|
||||
{position === 'default' && (
|
||||
<Gutter>
|
||||
<Media resource={media} src={staticImage} />
|
||||
</Gutter>
|
||||
)}
|
||||
{caption && (
|
||||
<Gutter className={classes.caption}>
|
||||
<RichText content={caption} />
|
||||
</Gutter>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.introContent {
|
||||
position: relative;
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
width: 100%;
|
||||
gap: var(--base) 40px;
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
grid-column-end: span 12;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.cols-half {
|
||||
grid-column-end: span 6;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.cols-thirds {
|
||||
grid-column-end: span 3;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Post } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { Card } from '../../_components/Card/index.js'
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import RichText from '../../_components/RichText/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type RelatedPostsProps = {
|
||||
blockName: string
|
||||
blockType: 'relatedPosts'
|
||||
docs?: (Post | string)[] | null
|
||||
introContent?: any
|
||||
relationTo: 'posts'
|
||||
}
|
||||
|
||||
export const RelatedPosts: React.FC<RelatedPostsProps> = (props) => {
|
||||
const { docs, introContent, relationTo } = props
|
||||
|
||||
return (
|
||||
<div className={classes.relatedPosts}>
|
||||
{introContent && (
|
||||
<Gutter className={classes.introContent}>
|
||||
<RichText content={introContent} />
|
||||
</Gutter>
|
||||
)}
|
||||
<Gutter>
|
||||
<div className={classes.grid}>
|
||||
{docs?.map((doc, index) => {
|
||||
if (typeof doc === 'string') return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
classes.column,
|
||||
docs.length === 2 && classes['cols-half'],
|
||||
docs.length >= 3 && classes['cols-thirds'],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
key={index}
|
||||
>
|
||||
<Card doc={doc} relationTo={relationTo} showCategories />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Gutter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.relationshipsBlock {
|
||||
}
|
||||
|
||||
.array {
|
||||
border: 1px solid var(--color-base-100);
|
||||
padding: var(--base);
|
||||
|
||||
& > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { Gutter } from '../../_components/Gutter/index.js'
|
||||
import RichText from '../../_components/RichText/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type RelationshipsBlockProps = {
|
||||
blockName: string
|
||||
blockType: 'relationships'
|
||||
data: Page
|
||||
}
|
||||
|
||||
export const RelationshipsBlock: React.FC<RelationshipsBlockProps> = (props) => {
|
||||
const { data } = props
|
||||
|
||||
return (
|
||||
<div className={classes.relationshipsBlock}>
|
||||
<Gutter>
|
||||
<p>
|
||||
This block is for testing purposes only. It renders every possible type of relationship.
|
||||
</p>
|
||||
<p>
|
||||
<b>Rich Text — Slate:</b>
|
||||
</p>
|
||||
{data?.richTextSlate && <RichText content={data.richTextSlate} renderUploadFilenameOnly />}
|
||||
<p>
|
||||
<b>Rich Text — Lexical:</b>
|
||||
</p>
|
||||
{data?.richTextLexical && (
|
||||
<RichText content={data.richTextLexical} renderUploadFilenameOnly serializer="lexical" />
|
||||
)}
|
||||
<p>
|
||||
<b>Upload:</b>
|
||||
</p>
|
||||
{data?.relationshipAsUpload ? (
|
||||
<div>
|
||||
{typeof data?.relationshipAsUpload === 'string'
|
||||
? data?.relationshipAsUpload
|
||||
: data?.relationshipAsUpload.filename}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has One:</b>
|
||||
</p>
|
||||
{data?.relationshipMonoHasOne ? (
|
||||
<div>
|
||||
{typeof data?.relationshipMonoHasOne === 'string'
|
||||
? data?.relationshipMonoHasOne
|
||||
: data?.relationshipMonoHasOne.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has Many:</b>
|
||||
</p>
|
||||
{data?.relationshipMonoHasMany ? (
|
||||
<Fragment>
|
||||
{data?.relationshipMonoHasMany.length
|
||||
? data?.relationshipMonoHasMany?.map((item, index) =>
|
||||
item ? (
|
||||
<div key={index}>{typeof item === 'string' ? item : item.title}</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has One:</b>
|
||||
</p>
|
||||
{data?.relationshipPolyHasOne ? (
|
||||
<div>
|
||||
{typeof data?.relationshipPolyHasOne.value === 'string'
|
||||
? data?.relationshipPolyHasOne.value
|
||||
: data?.relationshipPolyHasOne.value.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has Many:</b>
|
||||
</p>
|
||||
{data?.relationshipPolyHasMany ? (
|
||||
<Fragment>
|
||||
{data?.relationshipPolyHasMany.length
|
||||
? data?.relationshipPolyHasMany?.map((item, index) =>
|
||||
item.value ? (
|
||||
<div key={index}>
|
||||
{typeof item.value === 'string' ? item.value : item.value.title}
|
||||
</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Array of Relationships:</b>
|
||||
</p>
|
||||
{data?.arrayOfRelationships?.map((item, index) => (
|
||||
<div className={classes.array} key={index}>
|
||||
<p>
|
||||
<b>Rich Text:</b>
|
||||
</p>
|
||||
{item?.richTextInArray && <RichText content={item.richTextInArray} />}
|
||||
<p>
|
||||
<b>Upload:</b>
|
||||
</p>
|
||||
{item?.uploadInArray ? (
|
||||
<div>
|
||||
{typeof item?.uploadInArray === 'string'
|
||||
? item?.uploadInArray
|
||||
: item?.uploadInArray.filename}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has One:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayMonoHasOne ? (
|
||||
<div>
|
||||
{typeof item?.relationshipInArrayMonoHasOne === 'string'
|
||||
? item?.relationshipInArrayMonoHasOne
|
||||
: item?.relationshipInArrayMonoHasOne.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Monomorphic Has Many:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayMonoHasMany ? (
|
||||
<Fragment>
|
||||
{item?.relationshipInArrayMonoHasMany.length
|
||||
? item?.relationshipInArrayMonoHasMany?.map((rel, relIndex) =>
|
||||
rel ? (
|
||||
<div key={relIndex}>{typeof rel === 'string' ? rel : rel.title}</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has One:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayPolyHasOne ? (
|
||||
<div>
|
||||
{typeof item?.relationshipInArrayPolyHasOne.value === 'string'
|
||||
? item?.relationshipInArrayPolyHasOne.value
|
||||
: item?.relationshipInArrayPolyHasOne.value.title}
|
||||
</div>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
<p>
|
||||
<b>Polymorphic Has Many:</b>
|
||||
</p>
|
||||
{item?.relationshipInArrayPolyHasMany ? (
|
||||
<Fragment>
|
||||
{item?.relationshipInArrayPolyHasMany.length
|
||||
? item?.relationshipInArrayPolyHasMany?.map((rel, relIndex) =>
|
||||
rel.value ? (
|
||||
<div key={relIndex}>
|
||||
{typeof rel.value === 'string' ? rel.value : rel.value.title}
|
||||
</div>
|
||||
) : (
|
||||
'null'
|
||||
),
|
||||
)
|
||||
: 'None'}
|
||||
</Fragment>
|
||||
) : (
|
||||
<div>None</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Gutter>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
.invert {
|
||||
background-color: var(--color-base-750);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
id?: string
|
||||
invert?: boolean | null
|
||||
}
|
||||
|
||||
export const BackgroundColor: React.FC<Props> = (props) => {
|
||||
const { id, children, className, invert } = props
|
||||
|
||||
return (
|
||||
<div className={[invert && classes.invert, className].filter(Boolean).join(' ')} id={id}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
import type { RelationshipsBlockProps } from '../../_blocks/Relationships/index.js'
|
||||
import type { VerticalPaddingOptions } from '../VerticalPadding/index.js'
|
||||
|
||||
import { ArchiveBlock } from '../../_blocks/ArchiveBlock/index.js'
|
||||
import { CallToActionBlock } from '../../_blocks/CallToAction/index.js'
|
||||
import { ContentBlock } from '../../_blocks/Content/index.js'
|
||||
import { MediaBlock } from '../../_blocks/MediaBlock/index.js'
|
||||
import { RelatedPosts, type RelatedPostsProps } from '../../_blocks/RelatedPosts/index.js'
|
||||
import { RelationshipsBlock } from '../../_blocks/Relationships/index.js'
|
||||
import { toKebabCase } from '../../_utilities/toKebabCase.js'
|
||||
import { BackgroundColor } from '../BackgroundColor/index.js'
|
||||
import { VerticalPadding } from '../VerticalPadding/index.js'
|
||||
|
||||
const blockComponents = {
|
||||
archive: ArchiveBlock,
|
||||
content: ContentBlock,
|
||||
cta: CallToActionBlock,
|
||||
mediaBlock: MediaBlock,
|
||||
relatedPosts: RelatedPosts,
|
||||
relationships: RelationshipsBlock,
|
||||
}
|
||||
|
||||
type Block = NonNullable<Page['layout']>[number]
|
||||
|
||||
export const Blocks: React.FC<{
|
||||
blocks?: (Block | RelatedPostsProps | RelationshipsBlockProps)[] | null
|
||||
disableTopPadding?: boolean
|
||||
}> = (props) => {
|
||||
const { blocks, disableTopPadding } = props
|
||||
|
||||
const hasBlocks = blocks && Array.isArray(blocks) && blocks.length > 0
|
||||
|
||||
if (hasBlocks) {
|
||||
return (
|
||||
<Fragment>
|
||||
{blocks.map((block, index) => {
|
||||
const { blockName, blockType } = block
|
||||
|
||||
if (blockType && blockType in blockComponents) {
|
||||
const Block = blockComponents[blockType]
|
||||
|
||||
// the cta block is containerized, so we don't consider it to be inverted at the block-level
|
||||
const blockIsInverted =
|
||||
'invertBackground' in block && blockType !== 'cta' ? block.invertBackground : false
|
||||
const prevBlock = blocks[index - 1]
|
||||
|
||||
const prevBlockInverted =
|
||||
prevBlock && 'invertBackground' in prevBlock && prevBlock?.invertBackground
|
||||
|
||||
const isPrevSame = Boolean(blockIsInverted) === Boolean(prevBlockInverted)
|
||||
|
||||
let paddingTop: VerticalPaddingOptions = 'large'
|
||||
let paddingBottom: VerticalPaddingOptions = 'large'
|
||||
|
||||
if (prevBlock && isPrevSame) {
|
||||
paddingTop = 'none'
|
||||
}
|
||||
|
||||
if (index === blocks.length - 1) {
|
||||
paddingBottom = 'large'
|
||||
}
|
||||
|
||||
if (disableTopPadding && index === 0) {
|
||||
paddingTop = 'none'
|
||||
}
|
||||
|
||||
if (Block) {
|
||||
return (
|
||||
<BackgroundColor invert={blockIsInverted} key={index}>
|
||||
<VerticalPadding bottom={paddingBottom} top={paddingTop}>
|
||||
{/* @ts-expect-error */}
|
||||
<Block id={toKebabCase(blockName)} {...block} />
|
||||
</VerticalPadding>
|
||||
</BackgroundColor>
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
})}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
@import '../../_css/type.scss';
|
||||
|
||||
.button {
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
background-color: transparent;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
padding: 12px 24px;
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
svg {
|
||||
margin-right: calc(var(--base) / 2);
|
||||
width: var(--base);
|
||||
height: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
@extend %label;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.appearance--primary {
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
}
|
||||
|
||||
.appearance--secondary {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--color-base-1000);
|
||||
}
|
||||
|
||||
.primary--invert {
|
||||
background-color: var(--color-base-0);
|
||||
color: var(--color-base-1000);
|
||||
}
|
||||
|
||||
.secondary--invert {
|
||||
background-color: var(--color-base-1000);
|
||||
box-shadow: inset 0 0 0 1px var(--color-base-0);
|
||||
}
|
||||
|
||||
.appearance--default {
|
||||
padding: 0;
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
.appearance--none {
|
||||
padding: 0;
|
||||
color: var(--theme-text);
|
||||
|
||||
&:local() {
|
||||
.label {
|
||||
text-transform: none;
|
||||
line-height: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
import LinkWithDefault from 'next/link.js'
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
|
||||
|
||||
export type Props = {
|
||||
appearance?: 'default' | 'none' | 'primary' | 'secondary'
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
el?: 'a' | 'button' | 'link'
|
||||
href?: string
|
||||
invert?: boolean
|
||||
label?: string
|
||||
newTab?: boolean
|
||||
onClick?: () => void
|
||||
type?: 'button' | 'submit'
|
||||
}
|
||||
|
||||
export const Button: React.FC<Props> = ({
|
||||
type = 'button',
|
||||
appearance,
|
||||
className: classNameFromProps,
|
||||
disabled,
|
||||
el: elFromProps = 'link',
|
||||
href,
|
||||
invert,
|
||||
label,
|
||||
newTab,
|
||||
onClick,
|
||||
}) => {
|
||||
let el = elFromProps
|
||||
|
||||
const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
|
||||
|
||||
const className = [
|
||||
classes.button,
|
||||
classNameFromProps,
|
||||
classes[`appearance--${appearance}`],
|
||||
invert && classes[`${appearance}--invert`],
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
|
||||
const content = (
|
||||
<div className={classes.content}>
|
||||
<span className={classes.label}>{label}</span>
|
||||
</div>
|
||||
)
|
||||
|
||||
if (onClick || type === 'submit') el = 'button'
|
||||
|
||||
if (el === 'link') {
|
||||
return (
|
||||
<Link className={className} href={href || ''} {...newTabProps} onClick={onClick}>
|
||||
{content}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const Element: ElementType = el
|
||||
|
||||
return (
|
||||
<Element
|
||||
className={className}
|
||||
href={href}
|
||||
type={type}
|
||||
{...newTabProps}
|
||||
disabled={disabled}
|
||||
onClick={onClick}
|
||||
>
|
||||
{content}
|
||||
</Element>
|
||||
)
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.card {
|
||||
border: 1px var(--color-base-200) solid;
|
||||
border-radius: 4px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.horizontal {
|
||||
flex-direction: row;
|
||||
|
||||
&:local() {
|
||||
.mediaWrapper {
|
||||
width: 150px;
|
||||
|
||||
@include mid-break {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: var(--base);
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--base) / 2);
|
||||
|
||||
@include small-break {
|
||||
padding: calc(var(--base) / 2);
|
||||
gap: calc(var(--base) / 4);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.titleLink {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.centerAlign {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.body {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.leader {
|
||||
@extend %label;
|
||||
display: flex;
|
||||
gap: var(--base);
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hideImageOnMobile {
|
||||
@include mid-break {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mediaWrapper {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
.image {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
background-color: var(--color-base-50);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@include mid-break {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import LinkWithDefault from 'next/link.js'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Post } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { Media } from '../Media/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
|
||||
|
||||
export const Card: React.FC<{
|
||||
alignItems?: 'center'
|
||||
className?: string
|
||||
doc?: Post
|
||||
hideImagesOnMobile?: boolean
|
||||
orientation?: 'horizontal' | 'vertical'
|
||||
relationTo?: 'posts'
|
||||
showCategories?: boolean
|
||||
title?: string
|
||||
}> = (props) => {
|
||||
const {
|
||||
className,
|
||||
doc,
|
||||
orientation = 'vertical',
|
||||
relationTo,
|
||||
showCategories,
|
||||
title: titleFromProps,
|
||||
} = props
|
||||
|
||||
const { slug, categories, meta, title } = doc || {}
|
||||
const { description, image: metaImage } = meta || {}
|
||||
|
||||
const hasCategories = categories && Array.isArray(categories) && categories.length > 0
|
||||
const titleToUse = titleFromProps || title
|
||||
const sanitizedDescription = description?.replace(/\s/g, ' ') // replace non-breaking space with white space
|
||||
const href = `/live-preview/${relationTo}/${slug}`
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[classes.card, className, orientation && classes[orientation]]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<Link className={classes.mediaWrapper} href={href}>
|
||||
{!metaImage && <div className={classes.placeholder}>No image</div>}
|
||||
{metaImage && typeof metaImage !== 'string' && (
|
||||
<Media fill imgClassName={classes.image} resource={metaImage} />
|
||||
)}
|
||||
</Link>
|
||||
<div className={classes.content}>
|
||||
{showCategories && hasCategories && (
|
||||
<div className={classes.leader}>
|
||||
{showCategories && hasCategories && (
|
||||
<div>
|
||||
{categories?.map((category, index) => {
|
||||
const titleFromCategory = typeof category === 'string' ? category : category.title
|
||||
|
||||
const categoryTitle = titleFromCategory || 'Untitled category'
|
||||
|
||||
const isLast = index === categories.length - 1
|
||||
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{categoryTitle}
|
||||
{!isLast && <Fragment>, </Fragment>}
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{titleToUse && (
|
||||
<h4 className={classes.title}>
|
||||
<Link className={classes.titleLink} href={href}>
|
||||
{titleToUse}
|
||||
</Link>
|
||||
</h4>
|
||||
)}
|
||||
{description && (
|
||||
<div className={classes.body}>
|
||||
{description && <p className={classes.description}>{sanitizedDescription}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export const Chevron: React.FC<{
|
||||
className?: string
|
||||
rotate?: number
|
||||
}> = ({ className, rotate }) => {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
height="100%"
|
||||
style={{
|
||||
transform: typeof rotate === 'number' ? `rotate(${rotate || 0}deg)` : undefined,
|
||||
}}
|
||||
viewBox="0 0 24 24"
|
||||
width="100%"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M23.245 4l-11.245 14.374-11.219-14.374-.781.619 12 15.381 12-15.391-.755-.609z"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
vectorEffect="non-scaling-stroke"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
@import '../../../_css/common';
|
||||
|
||||
// this is to make up for the space taken by the fixed header, since the scroll method does not accept an offset parameter
|
||||
.scrollRef {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(var(--base) * -5);
|
||||
@include mid-break {
|
||||
top: calc(var(--base) * -2);
|
||||
}
|
||||
}
|
||||
|
||||
.introContent {
|
||||
position: relative;
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.resultCountWrapper {
|
||||
display: flex;
|
||||
margin-bottom: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.pageRange {
|
||||
margin-bottom: var(--base);
|
||||
|
||||
@include mid-break {
|
||||
margin-bottom: var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
width: 100%;
|
||||
gap: var(--base) 40px;
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
grid-column-end: span 4;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: calc(var(--base) * 2);
|
||||
|
||||
@include mid-break {
|
||||
margin-top: var(--base);
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import qs from 'qs'
|
||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import type { Post } from '../../../../../test/live-preview/payload-types.js'
|
||||
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
|
||||
import { Card } from '../../Card/index.js'
|
||||
import { Gutter } from '../../Gutter/index.js'
|
||||
import { PageRange } from '../../PageRange/index.js'
|
||||
import { Pagination } from '../../Pagination/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Result = {
|
||||
docs: (Post | string)[]
|
||||
hasNextPage: boolean
|
||||
hasPrevPage: boolean
|
||||
nextPage: number
|
||||
page: number
|
||||
prevPage: number
|
||||
totalDocs: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
|
||||
className?: string
|
||||
onResultChange?: (result: Result) => void // eslint-disable-line no-unused-vars
|
||||
showPageRange?: boolean
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const CollectionArchiveByCollection: React.FC<Props> = (props) => {
|
||||
const {
|
||||
categories: catsFromProps,
|
||||
className,
|
||||
limit = 10,
|
||||
onResultChange,
|
||||
populatedDocs,
|
||||
populatedDocsTotal,
|
||||
relationTo,
|
||||
showPageRange,
|
||||
sort = '-createdAt',
|
||||
} = props
|
||||
|
||||
const [results, setResults] = useState<Result>({
|
||||
docs: populatedDocs?.map((doc) => doc.value) || [],
|
||||
hasNextPage: false,
|
||||
hasPrevPage: false,
|
||||
nextPage: 1,
|
||||
page: 1,
|
||||
prevPage: 1,
|
||||
totalDocs: typeof populatedDocsTotal === 'number' ? populatedDocsTotal : 0,
|
||||
totalPages: 1,
|
||||
})
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string | undefined>(undefined)
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
const hasHydrated = useRef(false)
|
||||
const [page, setPage] = useState(1)
|
||||
|
||||
const scrollToRef = useCallback(() => {
|
||||
const { current } = scrollRef
|
||||
if (current) {
|
||||
// current.scrollIntoView({
|
||||
// behavior: 'smooth',
|
||||
// })
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && typeof results.page !== 'undefined') {
|
||||
// scrollToRef()
|
||||
}
|
||||
}, [isLoading, scrollToRef, results])
|
||||
|
||||
useEffect(() => {
|
||||
// hydrate the block with fresh content after first render
|
||||
// don't show loader unless the request takes longer than x ms
|
||||
// and don't show it during initial hydration
|
||||
const timer = setTimeout(() => {
|
||||
if (hasHydrated) {
|
||||
setIsLoading(true)
|
||||
}
|
||||
}, 500)
|
||||
|
||||
const searchQuery = qs.stringify(
|
||||
{
|
||||
depth: 1,
|
||||
limit,
|
||||
page,
|
||||
sort,
|
||||
where: {
|
||||
...(catsFromProps && catsFromProps?.length > 0
|
||||
? {
|
||||
categories: {
|
||||
in:
|
||||
typeof catsFromProps === 'string'
|
||||
? [catsFromProps]
|
||||
: catsFromProps
|
||||
.map((cat) => (typeof cat === 'object' && cat !== null ? cat.id : cat))
|
||||
.join(','),
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
{ encode: false },
|
||||
)
|
||||
|
||||
const makeRequest = async () => {
|
||||
try {
|
||||
const req = await fetch(`${PAYLOAD_SERVER_URL}/api/${relationTo}?${searchQuery}`)
|
||||
const json = await req.json()
|
||||
clearTimeout(timer)
|
||||
hasHydrated.current = true
|
||||
|
||||
const { docs } = json as { docs: Post[] }
|
||||
|
||||
if (docs && Array.isArray(docs)) {
|
||||
setResults(json)
|
||||
setIsLoading(false)
|
||||
if (typeof onResultChange === 'function') {
|
||||
onResultChange(json)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err) // eslint-disable-line no-console
|
||||
setIsLoading(false)
|
||||
setError(`Unable to load "${relationTo} archive" data at this time.`)
|
||||
}
|
||||
}
|
||||
|
||||
void makeRequest()
|
||||
|
||||
return () => {
|
||||
if (timer) clearTimeout(timer)
|
||||
}
|
||||
}, [page, catsFromProps, relationTo, onResultChange, sort, limit])
|
||||
|
||||
return (
|
||||
<div className={[classes.collectionArchive, className].filter(Boolean).join(' ')}>
|
||||
<div className={classes.scrollRef} ref={scrollRef} />
|
||||
{!isLoading && error && <Gutter>{error}</Gutter>}
|
||||
<Fragment>
|
||||
{showPageRange !== false && (
|
||||
<Gutter>
|
||||
<div className={classes.pageRange}>
|
||||
<PageRange
|
||||
collection={relationTo}
|
||||
currentPage={results.page}
|
||||
limit={limit}
|
||||
totalDocs={results.totalDocs}
|
||||
/>
|
||||
</div>
|
||||
</Gutter>
|
||||
)}
|
||||
<Gutter>
|
||||
<div className={classes.grid}>
|
||||
{results.docs?.map((result, index) => {
|
||||
if (typeof result === 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.column} key={index}>
|
||||
<Card doc={result} relationTo="posts" showCategories />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{results.totalPages > 1 && (
|
||||
<Pagination
|
||||
className={classes.pagination}
|
||||
onClick={setPage}
|
||||
page={results.page}
|
||||
totalPages={results.totalPages}
|
||||
/>
|
||||
)}
|
||||
</Gutter>
|
||||
</Fragment>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
@import '../../../_css/common';
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(12, 1fr);
|
||||
width: 100%;
|
||||
gap: var(--base) 40px;
|
||||
|
||||
@include mid-break {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
}
|
||||
|
||||
.column {
|
||||
grid-column-end: span 4;
|
||||
|
||||
@include mid-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
grid-column-end: span 6;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { ArchiveBlockProps } from '../../../_blocks/ArchiveBlock/types.js'
|
||||
|
||||
import { Card } from '../../Card/index.js'
|
||||
import { Gutter } from '../../Gutter/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type Props = {
|
||||
className?: string
|
||||
selectedDocs?: ArchiveBlockProps['selectedDocs']
|
||||
}
|
||||
|
||||
export const CollectionArchiveBySelection: React.FC<Props> = (props) => {
|
||||
const { className, selectedDocs } = props
|
||||
|
||||
const result = selectedDocs?.map((doc) => doc.value)
|
||||
|
||||
return (
|
||||
<div className={[classes.collectionArchive, className].filter(Boolean).join(' ')}>
|
||||
<Fragment>
|
||||
<Gutter>
|
||||
<div className={classes.grid}>
|
||||
{result?.map((result, index) => {
|
||||
if (typeof result === 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.column} key={index}>
|
||||
<Card doc={result} relationTo="posts" showCategories />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Gutter>
|
||||
</Fragment>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { ArchiveBlockProps } from '../../_blocks/ArchiveBlock/types.js'
|
||||
|
||||
import { CollectionArchiveByCollection } from './PopulateByCollection/index.js'
|
||||
import { CollectionArchiveBySelection } from './PopulateBySelection/index.js'
|
||||
|
||||
export type Props = Omit<ArchiveBlockProps, 'blockType'> & {
|
||||
className?: string
|
||||
sort?: string
|
||||
}
|
||||
|
||||
export const CollectionArchive: React.FC<Props> = (props) => {
|
||||
const { className, populateBy, selectedDocs } = props
|
||||
|
||||
if (populateBy === 'selection') {
|
||||
return <CollectionArchiveBySelection className={className} selectedDocs={selectedDocs} />
|
||||
}
|
||||
|
||||
if (populateBy === 'collection') {
|
||||
return <CollectionArchiveByCollection {...props} className={className} />
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
@use '../../_css/queries.scss' as *;
|
||||
|
||||
.footer {
|
||||
padding: calc(var(--base) * 4) 0;
|
||||
background-color: var(--color-base-1000);
|
||||
color: var(--color-base-0);
|
||||
|
||||
@include small-break {
|
||||
padding: calc(var(--base) * 2) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: calc(var(--base) / 4) var(--base);
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
opacity: 1;
|
||||
transition: opacity 100ms linear;
|
||||
visibility: visible;
|
||||
|
||||
> * {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import LinkWithDefault from 'next/link.js'
|
||||
import React from 'react'
|
||||
|
||||
import { fetchFooter } from '../../_api/fetchFooter.js'
|
||||
import { Gutter } from '../Gutter/index.js'
|
||||
import { CMSLink } from '../Link/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
|
||||
|
||||
export async function Footer() {
|
||||
const footer = await fetchFooter()
|
||||
|
||||
const navItems = footer?.navItems || []
|
||||
|
||||
return (
|
||||
<footer className={classes.footer}>
|
||||
<Gutter className={classes.wrap}>
|
||||
<Link href="/">
|
||||
<picture>
|
||||
<img
|
||||
alt="Payload Logo"
|
||||
className={classes.logo}
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-light.svg"
|
||||
/>
|
||||
</picture>
|
||||
</Link>
|
||||
<nav className={classes.nav}>
|
||||
{navItems.map(({ link }, i) => {
|
||||
return <CMSLink key={i} {...link} />
|
||||
})}
|
||||
<Link href="/admin">Admin</Link>
|
||||
<Link href="https://github.com/payloadcms/payload/tree/main/templates/ecommerce">
|
||||
Source Code
|
||||
</Link>
|
||||
<Link href="https://github.com/payloadcms/payload">Payload</Link>
|
||||
</nav>
|
||||
</Gutter>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
.gutter {
|
||||
max-width: 1920px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.gutterLeft {
|
||||
padding-left: var(--gutter-h);
|
||||
}
|
||||
|
||||
.gutterRight {
|
||||
padding-right: var(--gutter-h);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { Ref } from 'react'
|
||||
|
||||
import React, { forwardRef } from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
left?: boolean
|
||||
ref?: Ref<HTMLDivElement>
|
||||
right?: boolean
|
||||
}
|
||||
|
||||
export const Gutter: React.FC<Props> = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
const { children, className, left = true, right = true } = props
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
classes.gutter,
|
||||
left && classes.gutterLeft,
|
||||
right && classes.gutterRight,
|
||||
className,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
Gutter.displayName = 'Gutter'
|
||||
@@ -1,12 +0,0 @@
|
||||
@use '../../../_css/queries.scss' as *;
|
||||
|
||||
.nav {
|
||||
display: flex;
|
||||
gap: calc(var(--base) / 4) var(--base);
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
> * {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import type { Header as HeaderType } from '../../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { CMSLink } from '../../Link/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const HeaderNav: React.FC<{ header: HeaderType }> = ({ header }) => {
|
||||
const navItems = header?.navItems || []
|
||||
|
||||
return (
|
||||
<nav className={classes.nav}>
|
||||
{navItems.map(({ link }, i) => {
|
||||
return <CMSLink key={i} {...link} />
|
||||
})}
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
@use '../../_css/queries.scss' as *;
|
||||
|
||||
.header {
|
||||
padding: var(--base) 0;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: calc(var(--base) / 2) var(--base);
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import LinkWithDefault from 'next/link.js'
|
||||
import React from 'react'
|
||||
|
||||
import { fetchHeader } from '../../_api/fetchHeader.js'
|
||||
import { Gutter } from '../Gutter/index.js'
|
||||
import { HeaderNav } from './Nav/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
|
||||
|
||||
export async function Header() {
|
||||
const header = await fetchHeader()
|
||||
|
||||
return (
|
||||
<header className={classes.header}>
|
||||
<Gutter className={classes.wrap}>
|
||||
<Link href="/live-preview">
|
||||
<img
|
||||
alt="Payload Logo"
|
||||
className={classes.logo}
|
||||
src="https://raw.githubusercontent.com/payloadcms/payload/main/packages/payload/src/admin/assets/images/payload-logo-dark.svg"
|
||||
/>
|
||||
</Link>
|
||||
<HeaderNav header={header} />
|
||||
</Gutter>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import type { Page } from '../../../../test/live-preview/payload-types.js'
|
||||
|
||||
import { HighImpactHero } from '../../_heros/HighImpact/index.js'
|
||||
import { LowImpactHero } from '../../_heros/LowImpact/index.js'
|
||||
|
||||
const heroes = {
|
||||
highImpact: HighImpactHero,
|
||||
lowImpact: LowImpactHero,
|
||||
}
|
||||
|
||||
export const Hero: React.FC<Page['hero']> = (props) => {
|
||||
const { type } = props || {}
|
||||
|
||||
if (!type || type === 'none') return null
|
||||
|
||||
const HeroToRender = heroes[type]
|
||||
|
||||
if (!HeroToRender) return null
|
||||
|
||||
return <HeroToRender {...props} />
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import LinkWithDefault from 'next/link.js'
|
||||
import React from 'react'
|
||||
|
||||
import type { Page, Post } from '../../../../test/live-preview/payload-types.js'
|
||||
import type { Props as ButtonProps } from '../Button/index.js'
|
||||
|
||||
import { Button } from '../Button/index.js'
|
||||
|
||||
const Link = (LinkWithDefault.default || LinkWithDefault) as typeof LinkWithDefault.default
|
||||
|
||||
type CMSLinkType = {
|
||||
appearance?: ButtonProps['appearance']
|
||||
children?: React.ReactNode
|
||||
className?: string
|
||||
invert?: ButtonProps['invert']
|
||||
label?: string
|
||||
newTab?: boolean
|
||||
reference?: {
|
||||
relationTo: 'pages' | 'posts'
|
||||
value: Page | Post | string
|
||||
}
|
||||
type?: 'custom' | 'reference'
|
||||
url?: string
|
||||
}
|
||||
|
||||
export const CMSLink: React.FC<CMSLinkType> = ({
|
||||
type,
|
||||
appearance,
|
||||
children,
|
||||
className,
|
||||
invert,
|
||||
label,
|
||||
newTab,
|
||||
reference,
|
||||
url,
|
||||
}) => {
|
||||
const href =
|
||||
type === 'reference' && typeof reference?.value === 'object' && reference.value.slug
|
||||
? `/${reference.value.slug}`
|
||||
: url
|
||||
|
||||
if (!href) return null
|
||||
|
||||
if (!appearance) {
|
||||
const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
|
||||
|
||||
if (href || url) {
|
||||
return (
|
||||
<Link {...newTabProps} className={className} href={href || url || ''}>
|
||||
{label && label}
|
||||
{children && children}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
appearance={appearance}
|
||||
className={className}
|
||||
href={href}
|
||||
invert={invert}
|
||||
label={label}
|
||||
newTab={newTab}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
.placeholder-color-light {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
background-color: var(--color-base-50);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { StaticImageData } from 'next/image.js'
|
||||
|
||||
import NextImageWithDefault from 'next/image.js'
|
||||
import React from 'react'
|
||||
|
||||
import type { Props as MediaProps } from '../types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
|
||||
import cssVariables from '../../../cssVariables.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const { breakpoints } = cssVariables
|
||||
|
||||
const NextImage = (NextImageWithDefault.default ||
|
||||
NextImageWithDefault) as typeof NextImageWithDefault.default
|
||||
|
||||
export const Image: React.FC<MediaProps> = (props) => {
|
||||
const {
|
||||
alt: altFromProps,
|
||||
fill,
|
||||
imgClassName,
|
||||
onClick,
|
||||
onLoad: onLoadFromProps,
|
||||
priority,
|
||||
resource,
|
||||
src: srcFromProps,
|
||||
} = props
|
||||
|
||||
const [isLoading, setIsLoading] = React.useState(true)
|
||||
|
||||
let width: number | undefined
|
||||
let height: number | undefined
|
||||
let alt = altFromProps
|
||||
let src: StaticImageData | string = srcFromProps || ''
|
||||
|
||||
if (!src && resource && typeof resource !== 'string') {
|
||||
const {
|
||||
alt: altFromResource,
|
||||
filename: fullFilename,
|
||||
height: fullHeight,
|
||||
width: fullWidth,
|
||||
} = resource
|
||||
|
||||
width = fullWidth || undefined
|
||||
height = fullHeight || undefined
|
||||
alt = altFromResource
|
||||
|
||||
const filename = fullFilename
|
||||
|
||||
src = `${PAYLOAD_SERVER_URL}/api/media/file/${filename}`
|
||||
}
|
||||
|
||||
// NOTE: this is used by the browser to determine which image to download at different screen sizes
|
||||
const sizes = Object.entries(breakpoints)
|
||||
.map(([, value]) => `(max-width: ${value}px) ${value}px`)
|
||||
.join(', ')
|
||||
|
||||
return (
|
||||
<NextImage
|
||||
alt={alt || ''}
|
||||
className={[isLoading && classes.placeholder, classes.image, imgClassName]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
fill={fill}
|
||||
height={!fill ? height : undefined}
|
||||
onClick={onClick}
|
||||
onLoad={() => {
|
||||
setIsLoading(false)
|
||||
if (typeof onLoadFromProps === 'function') {
|
||||
onLoadFromProps()
|
||||
}
|
||||
}}
|
||||
priority={priority}
|
||||
sizes={sizes}
|
||||
src={src}
|
||||
width={!fill ? width : undefined}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.video {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
background-color: var(--color-base-50);
|
||||
}
|
||||
|
||||
.cover {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
|
||||
import type { Props as MediaProps } from '../types.js'
|
||||
|
||||
import { PAYLOAD_SERVER_URL } from '../../../_api/serverURL.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Video: React.FC<MediaProps> = (props) => {
|
||||
const { onClick, resource, videoClassName } = props
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
// const [showFallback] = useState<boolean>()
|
||||
|
||||
useEffect(() => {
|
||||
const { current: video } = videoRef
|
||||
if (video) {
|
||||
video.addEventListener('suspend', () => {
|
||||
// setShowFallback(true);
|
||||
// console.warn('Video was suspended, rendering fallback image.')
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (resource && typeof resource !== 'string') {
|
||||
const { filename } = resource
|
||||
|
||||
return (
|
||||
<video
|
||||
autoPlay
|
||||
className={[classes.video, videoClassName].filter(Boolean).join(' ')}
|
||||
controls={false}
|
||||
loop
|
||||
muted
|
||||
onClick={onClick}
|
||||
playsInline
|
||||
ref={videoRef}
|
||||
>
|
||||
<source src={`${PAYLOAD_SERVER_URL}/media/${filename}`} />
|
||||
</video>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import type { ElementType } from 'react'
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import type { Props } from './types.js'
|
||||
|
||||
import { Image } from './Image/index.js'
|
||||
import { Video } from './Video/index.js'
|
||||
|
||||
export const Media: React.FC<Props> = (props) => {
|
||||
const { className, htmlElement = 'div', resource } = props
|
||||
|
||||
const isVideo = typeof resource !== 'string' && resource?.mimeType?.includes('video')
|
||||
const Tag = (htmlElement as ElementType) || Fragment
|
||||
|
||||
return (
|
||||
<Tag
|
||||
{...(htmlElement !== null
|
||||
? {
|
||||
className,
|
||||
}
|
||||
: {})}
|
||||
>
|
||||
{isVideo ? (
|
||||
<Video {...props} />
|
||||
) : (
|
||||
<Image {...props} /> // eslint-disable-line
|
||||
)}
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type { StaticImageData } from 'next/image'
|
||||
import type { ElementType, Ref } from 'react'
|
||||
|
||||
import type { Media as MediaType } from '../../../payload-types'
|
||||
|
||||
export interface Props {
|
||||
alt?: string
|
||||
className?: string
|
||||
fill?: boolean // for NextImage only
|
||||
htmlElement?: ElementType | null
|
||||
imgClassName?: string
|
||||
onClick?: () => void
|
||||
onLoad?: () => void
|
||||
priority?: boolean // for NextImage only
|
||||
ref?: Ref<HTMLImageElement | HTMLVideoElement | null>
|
||||
resource?: MediaType | string // for Payload media
|
||||
size?: string // for NextImage only
|
||||
src?: StaticImageData // for static media
|
||||
videoClassName?: string
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
@import '../../_css/common';
|
||||
|
||||
.pageRange {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 var(--base(0.5));
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.hyperlink {
|
||||
display: flex;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
const defaultLabels = {
|
||||
plural: 'Docs',
|
||||
singular: 'Doc',
|
||||
}
|
||||
|
||||
const defaultCollectionLabels = {
|
||||
products: {
|
||||
plural: 'Products',
|
||||
singular: 'Product',
|
||||
},
|
||||
}
|
||||
|
||||
export const PageRange: React.FC<{
|
||||
className?: string
|
||||
collection?: string
|
||||
collectionLabels?: {
|
||||
plural?: string
|
||||
singular?: string
|
||||
}
|
||||
currentPage?: number
|
||||
limit?: number
|
||||
totalDocs?: number
|
||||
}> = (props) => {
|
||||
const {
|
||||
className,
|
||||
collection,
|
||||
collectionLabels: collectionLabelsFromProps,
|
||||
currentPage,
|
||||
limit,
|
||||
totalDocs,
|
||||
} = props
|
||||
|
||||
const indexStart = (currentPage ? currentPage - 1 : 1) * (limit || 1) + 1
|
||||
let indexEnd = (currentPage || 1) * (limit || 1)
|
||||
if (totalDocs && indexEnd > totalDocs) indexEnd = totalDocs
|
||||
|
||||
const { plural, singular } =
|
||||
collectionLabelsFromProps || defaultCollectionLabels[collection || ''] || defaultLabels || {}
|
||||
|
||||
return (
|
||||
<div className={[className, classes.pageRange].filter(Boolean).join(' ')}>
|
||||
{(typeof totalDocs === 'undefined' || totalDocs === 0) && 'Search produced no results.'}
|
||||
{typeof totalDocs !== 'undefined' &&
|
||||
totalDocs > 0 &&
|
||||
`Showing ${indexStart} - ${indexEnd} of ${totalDocs} ${totalDocs > 1 ? plural : singular}`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
@import '../../_css/type.scss';
|
||||
|
||||
.pagination {
|
||||
@extend %label;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--base) / 2);
|
||||
}
|
||||
|
||||
.button {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: calc(var(--base) / 2);
|
||||
color: var(--color-base-500);
|
||||
border: 1px solid var(--color-base-200);
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--color-base-200);
|
||||
border-color: var(--color-base-150);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: calc(var(--base) / 2);
|
||||
height: calc(var(--base) / 2);
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import { Chevron } from '../Chevron/index.js'
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export const Pagination: React.FC<{
|
||||
className?: string
|
||||
onClick: (page: number) => void
|
||||
page: number
|
||||
totalPages: number
|
||||
}> = (props) => {
|
||||
const { className, onClick, page, totalPages } = props
|
||||
const hasNextPage = page < totalPages
|
||||
const hasPrevPage = page > 1
|
||||
|
||||
return (
|
||||
<div className={[classes.pagination, className].filter(Boolean).join(' ')}>
|
||||
<button
|
||||
className={classes.button}
|
||||
disabled={!hasPrevPage}
|
||||
onClick={() => {
|
||||
onClick(page - 1)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Chevron className={classes.icon} rotate={90} />
|
||||
</button>
|
||||
<div className={classes.pageRange}>
|
||||
<span className={classes.pageRangeLabel}>
|
||||
Page {page} of {totalPages}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
className={classes.button}
|
||||
disabled={!hasNextPage}
|
||||
onClick={() => {
|
||||
onClick(page + 1)
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<Chevron className={classes.icon} rotate={-90} />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
.richText {
|
||||
> *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
> *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
import serializeLexical from './serializeLexical.js'
|
||||
import serializeSlate from './serializeSlate.js'
|
||||
|
||||
const RichText: React.FC<{
|
||||
className?: string
|
||||
content: any
|
||||
renderUploadFilenameOnly?: boolean
|
||||
serializer?: 'lexical' | 'slate'
|
||||
}> = ({ className, content, renderUploadFilenameOnly, serializer = 'slate' }) => {
|
||||
if (!content) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[classes.richText, className].filter(Boolean).join(' ')}>
|
||||
{serializer === 'slate'
|
||||
? serializeSlate(content, renderUploadFilenameOnly)
|
||||
: serializeLexical(content, renderUploadFilenameOnly)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RichText
|
||||
@@ -1,92 +0,0 @@
|
||||
import type { SerializedEditorState } from 'lexical'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import { CMSLink } from '../Link/index.js'
|
||||
import { Media } from '../Media/index.js'
|
||||
|
||||
const serializer = (
|
||||
content?: SerializedEditorState['root']['children'],
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode | React.ReactNode[] =>
|
||||
content?.map((node, i) => {
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h1>
|
||||
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h2>
|
||||
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h3>
|
||||
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h4>
|
||||
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h5>
|
||||
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</h6>
|
||||
|
||||
case 'quote':
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serializeLexical(node?.children, renderUploadFilenameOnly)}
|
||||
</blockquote>
|
||||
)
|
||||
|
||||
case 'ul':
|
||||
return <ul key={i}>{serializeLexical(node?.children, renderUploadFilenameOnly)}</ul>
|
||||
|
||||
case 'ol':
|
||||
return <ol key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</ol>
|
||||
|
||||
case 'li':
|
||||
return <li key={i}>{serializeLexical(node.children, renderUploadFilenameOnly)}</li>
|
||||
|
||||
case 'relationship':
|
||||
return (
|
||||
<span key={i}>
|
||||
{node.value && typeof node.value === 'object'
|
||||
? node.value.title || node.value.id
|
||||
: node.value}
|
||||
</span>
|
||||
)
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
<CMSLink
|
||||
key={i}
|
||||
newTab={Boolean(node?.newTab)}
|
||||
reference={node.doc as any}
|
||||
type={node.linkType === 'internal' ? 'reference' : 'custom'}
|
||||
url={node.url}
|
||||
>
|
||||
{serializer(node?.children, renderUploadFilenameOnly)}
|
||||
</CMSLink>
|
||||
)
|
||||
|
||||
case 'upload':
|
||||
if (renderUploadFilenameOnly) {
|
||||
return <span key={i}>{node.value.filename}</span>
|
||||
}
|
||||
|
||||
return <Media key={i} resource={node?.value} />
|
||||
|
||||
case 'paragraph':
|
||||
return <p key={i}>{serializer(node?.children, renderUploadFilenameOnly)}</p>
|
||||
|
||||
case 'text':
|
||||
return <span key={i}>{node.text}</span>
|
||||
}
|
||||
})
|
||||
|
||||
const serializeLexical = (
|
||||
content?: SerializedEditorState,
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode | React.ReactNode[] => {
|
||||
return serializer(content?.root?.children, renderUploadFilenameOnly)
|
||||
}
|
||||
|
||||
export default serializeLexical
|
||||
@@ -1,131 +0,0 @@
|
||||
import escapeHTML from 'escape-html'
|
||||
import React, { Fragment } from 'react'
|
||||
import { Text } from 'slate'
|
||||
|
||||
import { CMSLink } from '../Link/index.js'
|
||||
import { Media } from '../Media/index.js'
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
type Children = Leaf[]
|
||||
|
||||
type Leaf = {
|
||||
[key: string]: unknown
|
||||
children?: Children
|
||||
type: string
|
||||
url?: string
|
||||
value?: any
|
||||
}
|
||||
|
||||
const serializeSlate = (
|
||||
children?: Children,
|
||||
renderUploadFilenameOnly?: boolean,
|
||||
): React.ReactNode[] =>
|
||||
children?.map((node, i) => {
|
||||
if (Text.isText(node)) {
|
||||
let text = <span dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} />
|
||||
|
||||
if (node.bold) {
|
||||
text = <strong key={i}>{text}</strong>
|
||||
}
|
||||
|
||||
if (node.code) {
|
||||
text = <code key={i}>{text}</code>
|
||||
}
|
||||
|
||||
if (node.italic) {
|
||||
text = <em key={i}>{text}</em>
|
||||
}
|
||||
|
||||
if (node.underline) {
|
||||
text = (
|
||||
<span key={i} style={{ textDecoration: 'underline' }}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
if (node.strikethrough) {
|
||||
text = (
|
||||
<span key={i} style={{ textDecoration: 'line-through' }}>
|
||||
{text}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
return <Fragment key={i}>{text}</Fragment>
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'h1':
|
||||
return <h1 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h1>
|
||||
|
||||
case 'h2':
|
||||
return <h2 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h2>
|
||||
|
||||
case 'h3':
|
||||
return <h3 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h3>
|
||||
|
||||
case 'h4':
|
||||
return <h4 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h4>
|
||||
|
||||
case 'h5':
|
||||
return <h5 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h5>
|
||||
|
||||
case 'h6':
|
||||
return <h6 key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</h6>
|
||||
|
||||
case 'quote':
|
||||
return (
|
||||
<blockquote key={i}>
|
||||
{serializeSlate(node?.children, renderUploadFilenameOnly)}
|
||||
</blockquote>
|
||||
)
|
||||
|
||||
case 'ul':
|
||||
return <ul key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</ul>
|
||||
|
||||
case 'ol':
|
||||
return <ol key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</ol>
|
||||
|
||||
case 'li':
|
||||
return <li key={i}>{serializeSlate(node.children, renderUploadFilenameOnly)}</li>
|
||||
|
||||
case 'relationship':
|
||||
return (
|
||||
<span key={i}>
|
||||
{node.value && typeof node.value === 'object'
|
||||
? node.value.title || node.value.id
|
||||
: node.value}
|
||||
</span>
|
||||
)
|
||||
|
||||
case 'link':
|
||||
return (
|
||||
<CMSLink
|
||||
key={i}
|
||||
newTab={Boolean(node?.newTab)}
|
||||
reference={node.doc as any}
|
||||
type={node.linkType === 'internal' ? 'reference' : 'custom'}
|
||||
url={node.url}
|
||||
>
|
||||
{serializeSlate(node?.children, renderUploadFilenameOnly)}
|
||||
</CMSLink>
|
||||
)
|
||||
|
||||
case 'upload':
|
||||
if (renderUploadFilenameOnly) {
|
||||
return <span key={i}>{node.value.filename}</span>
|
||||
}
|
||||
|
||||
return <Media key={i} resource={node?.value} />
|
||||
|
||||
default:
|
||||
return <p key={i}>{serializeSlate(node?.children, renderUploadFilenameOnly)}</p>
|
||||
}
|
||||
}) || []
|
||||
|
||||
export default serializeSlate
|
||||
@@ -1,15 +0,0 @@
|
||||
.top-large {
|
||||
padding-top: var(--block-padding);
|
||||
}
|
||||
|
||||
.top-medium {
|
||||
padding-top: calc(var(--block-padding) / 2);
|
||||
}
|
||||
|
||||
.bottom-large {
|
||||
padding-bottom: var(--block-padding);
|
||||
}
|
||||
|
||||
.bottom-medium {
|
||||
padding-bottom: calc(var(--block-padding) / 2);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
import classes from './index.module.scss'
|
||||
|
||||
export type VerticalPaddingOptions = 'large' | 'medium' | 'none'
|
||||
|
||||
type Props = {
|
||||
bottom?: VerticalPaddingOptions
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
top?: VerticalPaddingOptions
|
||||
}
|
||||
|
||||
export const VerticalPadding: React.FC<Props> = ({
|
||||
bottom = 'medium',
|
||||
children,
|
||||
className,
|
||||
top = 'medium',
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={[className, classes[`top-${top}`], classes[`bottom-${bottom}`]]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
@use './queries.scss' as *;
|
||||
@use './colors.scss' as *;
|
||||
@use './type.scss' as *;
|
||||
|
||||
:root {
|
||||
--base: 24px;
|
||||
--font-body: system-ui;
|
||||
--font-mono: 'Roboto Mono', monospace;
|
||||
|
||||
--gutter-h: 180px;
|
||||
--block-padding: 120px;
|
||||
|
||||
--theme-text: var(--color-base-750);
|
||||
|
||||
@include large-break {
|
||||
--gutter-h: 144px;
|
||||
--block-padding: 96px;
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
--gutter-h: 24px;
|
||||
--block-padding: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
@extend %body;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body);
|
||||
margin: 0;
|
||||
color: var(--theme-text);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--color-success-500);
|
||||
color: var(--color-base-800);
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: var(--color-success-500);
|
||||
color: var(--color-base-800);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@extend %h1;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@extend %h2;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@extend %h3;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@extend %h4;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@extend %h5;
|
||||
}
|
||||
|
||||
h6 {
|
||||
@extend %h6;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: var(--base) 0;
|
||||
|
||||
@include mid-break {
|
||||
margin: calc(var(--base) * 0.75) 0;
|
||||
}
|
||||
}
|
||||
|
||||
#page-title {
|
||||
@extend %h6;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: var(--base);
|
||||
margin: 0 0 var(--base);
|
||||
}
|
||||
|
||||
a {
|
||||
color: currentColor;
|
||||
|
||||
&:focus {
|
||||
opacity: 0.8;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.7;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
// Keep these in sync with the colors exported in '../cssVariables.js'
|
||||
|
||||
:root {
|
||||
--color-base-0: rgb(255, 255, 255);
|
||||
--color-base-50: rgb(245, 245, 245);
|
||||
--color-base-100: rgb(235, 235, 235);
|
||||
--color-base-150: rgb(221, 221, 221);
|
||||
--color-base-200: rgb(208, 208, 208);
|
||||
--color-base-250: rgb(195, 195, 195);
|
||||
--color-base-300: rgb(181, 181, 181);
|
||||
--color-base-350: rgb(168, 168, 168);
|
||||
--color-base-400: rgb(154, 154, 154);
|
||||
--color-base-450: rgb(141, 141, 141);
|
||||
--color-base-500: rgb(128, 128, 128);
|
||||
--color-base-550: rgb(114, 114, 114);
|
||||
--color-base-600: rgb(101, 101, 101);
|
||||
--color-base-650: rgb(87, 87, 87);
|
||||
--color-base-700: rgb(74, 74, 74);
|
||||
--color-base-750: rgb(60, 60, 60);
|
||||
--color-base-800: rgb(47, 47, 47);
|
||||
--color-base-850: rgb(34, 34, 34);
|
||||
--color-base-900: rgb(20, 20, 20);
|
||||
--color-base-950: rgb(7, 7, 7);
|
||||
--color-base-1000: rgb(0, 0, 0);
|
||||
|
||||
--color-success-50: rgb(247, 255, 251);
|
||||
--color-success-100: rgb(240, 255, 247);
|
||||
--color-success-150: rgb(232, 255, 243);
|
||||
--color-success-200: rgb(224, 255, 239);
|
||||
--color-success-250: rgb(217, 255, 235);
|
||||
--color-success-300: rgb(209, 255, 230);
|
||||
--color-success-350: rgb(201, 255, 226);
|
||||
--color-success-400: rgb(193, 255, 222);
|
||||
--color-success-450: rgb(186, 255, 218);
|
||||
--color-success-500: rgb(178, 255, 214);
|
||||
--color-success-550: rgb(160, 230, 193);
|
||||
--color-success-600: rgb(142, 204, 171);
|
||||
--color-success-650: rgb(125, 179, 150);
|
||||
--color-success-700: rgb(107, 153, 128);
|
||||
--color-success-750: rgb(89, 128, 107);
|
||||
--color-success-800: rgb(71, 102, 86);
|
||||
--color-success-850: rgb(53, 77, 64);
|
||||
--color-success-900: rgb(36, 51, 43);
|
||||
--color-success-950: rgb(18, 25, 21);
|
||||
|
||||
--color-warning-50: rgb(255, 255, 246);
|
||||
--color-warning-100: rgb(255, 255, 237);
|
||||
--color-warning-150: rgb(254, 255, 228);
|
||||
--color-warning-200: rgb(254, 255, 219);
|
||||
--color-warning-250: rgb(254, 255, 210);
|
||||
--color-warning-300: rgb(254, 255, 200);
|
||||
--color-warning-350: rgb(254, 255, 191);
|
||||
--color-warning-400: rgb(253, 255, 182);
|
||||
--color-warning-450: rgb(253, 255, 173);
|
||||
--color-warning-500: rgb(253, 255, 164);
|
||||
--color-warning-550: rgb(228, 230, 148);
|
||||
--color-warning-600: rgb(202, 204, 131);
|
||||
--color-warning-650: rgb(177, 179, 115);
|
||||
--color-warning-700: rgb(152, 153, 98);
|
||||
--color-warning-750: rgb(127, 128, 82);
|
||||
--color-warning-800: rgb(101, 102, 66);
|
||||
--color-warning-850: rgb(76, 77, 49);
|
||||
--color-warning-900: rgb(51, 51, 33);
|
||||
--color-warning-950: rgb(25, 25, 16);
|
||||
|
||||
--color-error-50: rgb(255, 241, 241);
|
||||
--color-error-100: rgb(255, 226, 228);
|
||||
--color-error-150: rgb(255, 212, 214);
|
||||
--color-error-200: rgb(255, 197, 200);
|
||||
--color-error-250: rgb(255, 183, 187);
|
||||
--color-error-300: rgb(255, 169, 173);
|
||||
--color-error-350: rgb(255, 154, 159);
|
||||
--color-error-400: rgb(255, 140, 145);
|
||||
--color-error-450: rgb(255, 125, 132);
|
||||
--color-error-500: rgb(255, 111, 118);
|
||||
--color-error-550: rgb(230, 100, 106);
|
||||
--color-error-600: rgb(204, 89, 94);
|
||||
--color-error-650: rgb(179, 78, 83);
|
||||
--color-error-700: rgb(153, 67, 71);
|
||||
--color-error-750: rgb(128, 56, 59);
|
||||
--color-error-800: rgb(102, 44, 47);
|
||||
--color-error-850: rgb(77, 33, 35);
|
||||
--color-error-900: rgb(51, 22, 24);
|
||||
--color-error-950: rgb(25, 11, 12);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user