Compare commits
73 Commits
v3.0.0-bet
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee62ed6ebb | ||
|
|
0283039257 | ||
|
|
b546c7b655 | ||
|
|
a933eb7311 | ||
|
|
b5d65dd1ac | ||
|
|
c4ee623907 | ||
|
|
1cb1e5e8b3 | ||
|
|
e0699838e1 | ||
|
|
46f70d9df4 | ||
|
|
b7e2c59622 | ||
|
|
0cc7184023 | ||
|
|
e905675a05 | ||
|
|
4a20a63563 | ||
|
|
8d1fc6e8fb | ||
|
|
62744e79ac | ||
|
|
e8bed7b315 | ||
|
|
f2b8ddb299 | ||
|
|
ffd8ea516d | ||
|
|
3bf09703e9 | ||
|
|
c15d679b65 | ||
|
|
a422a0d568 | ||
|
|
edaeb1e29f | ||
|
|
6f35c356fe | ||
|
|
0b9397399a | ||
|
|
cdcc35ccdb | ||
|
|
442189ec48 | ||
|
|
5d1cc760c9 | ||
|
|
2f90683c7d | ||
|
|
3f5403a52a | ||
|
|
9bccdfd60a | ||
|
|
62666a9897 | ||
|
|
eb27b84854 | ||
|
|
c3480811d3 | ||
|
|
12ba820de4 | ||
|
|
95fcd13929 | ||
|
|
6141c5950b | ||
|
|
0040e1756c | ||
|
|
1ebd54b315 | ||
|
|
cdb2072a6d | ||
|
|
68553ff974 | ||
|
|
9a3bce1118 | ||
|
|
005befcbe2 | ||
|
|
e65b6478c9 | ||
|
|
a79e92a145 | ||
|
|
995f51d941 | ||
|
|
4d19e64961 | ||
|
|
31143599f6 | ||
|
|
f752804410 | ||
|
|
a18d4061ea | ||
|
|
449c16d28f | ||
|
|
d307d627ab | ||
|
|
075819964d | ||
|
|
1ec78a16f0 | ||
|
|
290ffd3287 | ||
|
|
3d89508ce3 | ||
|
|
b160686fff | ||
|
|
ba6ef6777f | ||
|
|
febd7f7073 | ||
|
|
695ef32d1e | ||
|
|
b5b2bb1907 | ||
|
|
6f5cf5d916 | ||
|
|
aaf3a39f7e | ||
|
|
5ef2951829 | ||
|
|
a943487fca | ||
|
|
3a941c7c8a | ||
|
|
354588898f | ||
|
|
ada9978a8c | ||
|
|
874279c530 | ||
|
|
7ed6634bc5 | ||
|
|
09a0ee3ab9 | ||
|
|
67acab2cd5 | ||
|
|
b3dc6cc811 | ||
|
|
5cd0c7ec7d |
43
.github/workflows/main.yml
vendored
43
.github/workflows/main.yml
vendored
@@ -196,6 +196,7 @@ jobs:
|
||||
- postgres-custom-schema
|
||||
- postgres-uuid
|
||||
- supabase
|
||||
- sqlite
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
@@ -548,3 +549,45 @@ jobs:
|
||||
steps:
|
||||
- if: ${{ always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }}
|
||||
run: exit 1
|
||||
|
||||
publish-canary:
|
||||
name: Publish Canary
|
||||
if: github.ref == 'refs/heads/beta'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- all-green
|
||||
|
||||
steps:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Setup Node@${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v4
|
||||
timeout-minutes: 10
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
|
||||
- name: Load npm token
|
||||
run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Canary release script
|
||||
# dry run hard-coded to true for testing and no npm token provided
|
||||
run: pnpm tsx ./scripts/publish-canary.ts
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: true
|
||||
|
||||
14
.vscode/launch.json
vendored
14
.vscode/launch.json
vendored
@@ -56,6 +56,20 @@
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js fields-relationship",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Fields-Relationship",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "node --no-deprecation test/dev.js login-with-username",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Login-With-Username",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev plugin-cloud-storage",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
@@ -205,7 +205,9 @@ export const MyField: Field = {
|
||||
}
|
||||
```
|
||||
|
||||
Default values can be defined as a static string or a function that returns a string. Functions are called with the following arguments:
|
||||
Default values can be defined as a static value or a function that returns a value. When a `defaultValue` is defined statically, Payload's DB adapters will apply it to the database schema or models.
|
||||
|
||||
Functions can be written to make use of the following argument properties:
|
||||
|
||||
- `user` - the authenticated user object
|
||||
- `locale` - the currently selected locale string
|
||||
|
||||
@@ -57,6 +57,7 @@ export const MyUploadField: Field = {
|
||||
| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |
|
||||
| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |
|
||||
| **`defaultValue`** | Provide data to be used for this field's default value. [More](/docs/fields/overview#default-values) |
|
||||
| **`displayPreview`** | Enable displaying preview of the uploaded file. Overrides related Collection's `displayPreview` option. [More](/docs/upload/overview#collection-upload-options). |
|
||||
| **`localized`** | Enable localization for this field. Requires [localization to be enabled](/docs/configuration/localization) in the Base config. |
|
||||
| **`required`** | Require this field to have a value. |
|
||||
| **`admin`** | Admin-specific configuration. [Admin Options](../admin/fields#admin-options). |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Building Your Own Plugin
|
||||
label: Build Your Own
|
||||
order: 50
|
||||
order: 20
|
||||
desc: Starting to build your own plugin? Find everything you need and learn best practices with the Payload plugin template.
|
||||
keywords: plugins, template, config, configuration, extensions, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
@@ -159,36 +159,60 @@ export const seed = async (payload: Payload): Promise<void> => {
|
||||
|
||||
```
|
||||
|
||||
## Overview of the src folder
|
||||
## Building a Plugin
|
||||
|
||||
Now that we have our environment setup and dev project ready to go - it's time to build the plugin!
|
||||
|
||||
**index.ts**
|
||||
|
||||
First up, the `src/index.ts` file - this is where the plugin should be imported from. It is best practice not to build the plugin directly in this file, instead we use this to export the plugin and types from their respective files.
|
||||
|
||||
**Plugin.ts**
|
||||
|
||||
To reiterate, the essence of a [Payload Plugin](./overview) is simply to extend the [Payload Config](../configuration/overview) - and that is exactly what we are doing in this file.
|
||||
|
||||
```
|
||||
import type { Config } from 'payload'
|
||||
|
||||
export const samplePlugin =
|
||||
(pluginOptions: PluginTypes) =>
|
||||
(incomingConfig: Config): Config => {
|
||||
// create copy of incoming config
|
||||
let config = { ...incomingConfig }
|
||||
|
||||
// do something cool with the config here
|
||||
/**
|
||||
* This is where you could modify the
|
||||
* config based on the plugin options
|
||||
*/
|
||||
|
||||
// If you wanted to add a new collection:
|
||||
config.collections = [
|
||||
...(config.collections || []),
|
||||
newCollection,
|
||||
]
|
||||
|
||||
// If you wanted to add a new global:
|
||||
config.globals = [
|
||||
...(config.globals || []),
|
||||
newGlobal,
|
||||
]
|
||||
|
||||
/**
|
||||
* If you wanted to add a new field to a collection:
|
||||
*
|
||||
* 1. Loop over collections
|
||||
* 2. Find the collection you want to add the field to
|
||||
* 3. Add the field to the collection
|
||||
*/
|
||||
|
||||
// If you wanted to add to the onInit:
|
||||
config.onInit = async payload => {
|
||||
if (incomingConfig.onInit) await incomingConfig.onInit(payload)
|
||||
// Add additional onInit code here
|
||||
}
|
||||
|
||||
// Finally, return the modified config
|
||||
return config
|
||||
}
|
||||
```
|
||||
|
||||
1. First, you need to receive the existing Payload Config along with any plugin options.
|
||||
2. Then set the variable `config` to be equal to a copy of the existing config.
|
||||
3. From here, you can extend the config however you like!
|
||||
4. Finally, return the config and you're all set.
|
||||
To reiterate, the essence of a [Payload Plugin](./overview) is simply to extend the [Payload Config](../configuration/overview) - and that is exactly what we are doing in this file.
|
||||
|
||||
## Spread Syntax
|
||||
|
||||
### Spread syntax
|
||||
|
||||
[Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) (or the spread operator) is a feature in JavaScript that uses the dot notation **(...)** to spread elements from arrays, strings, or objects into various contexts.
|
||||
|
||||
@@ -206,7 +230,7 @@ config.collections = [
|
||||
|
||||
First, you need to spread the `config.collections` to ensure that we don't lose the existing collections. Then you can add any additional collections, just as you would in a regular Payload Config.
|
||||
|
||||
This same logic is applied to other properties like admin, globals, hooks:
|
||||
This same logic is applied to other array and object like properties such as admin, globals and hooks:
|
||||
|
||||
```
|
||||
config.globals = [
|
||||
@@ -220,7 +244,10 @@ config.hooks = {
|
||||
}
|
||||
```
|
||||
|
||||
Some properties will be slightly different to extend, for instance the `onInit` property:
|
||||
### Extending functions
|
||||
Function properties cannot use spread syntax. The way to extend them is to execute the existing function if it exists and then run your additional functionality.
|
||||
|
||||
Here is an example extending the `onInit` property:
|
||||
|
||||
```
|
||||
config.onInit = async payload => {
|
||||
@@ -231,10 +258,6 @@ config.onInit = async payload => {
|
||||
}
|
||||
```
|
||||
|
||||
If you wish to add to the `onInit`, you must include the async/await. We don't use spread syntax in this case, instead you must await the existing `onInit` before running additional functionality.
|
||||
|
||||
In the template, we have stubbed out a basic `onInitExtension` file that you can use, if not needed feel free to delete it.
|
||||
|
||||
## Types
|
||||
|
||||
If your plugin has options, you should define and provide types for these options in a separate file which gets exported from the main `index.ts`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Form Builder Plugin
|
||||
label: Form Builder
|
||||
order: 20
|
||||
order: 40
|
||||
desc: Easily build and manage forms from the Admin Panel. Send dynamic, personalized emails and even accept and process payments.
|
||||
keywords: plugins, plugin, form, forms, form builder
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Nested Docs Plugin
|
||||
label: Nested Docs
|
||||
order: 20
|
||||
order: 40
|
||||
desc: Nested documents in a parent, child, and sibling relationship.
|
||||
keywords: plugins, nested, documents, parent, child, sibling, relationship
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Redirects Plugin
|
||||
label: Redirects
|
||||
order: 20
|
||||
order: 40
|
||||
desc: Automatically create redirects for your Payload application
|
||||
keywords: plugins, redirects, redirect, plugin, payload, cms, seo, indexing, search, search engine
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Search Plugin
|
||||
label: Search
|
||||
order: 20
|
||||
order: 40
|
||||
desc: Generates records of your documents that are extremely fast to search on.
|
||||
keywords: plugins, search, search plugin, search engine, search index, search results, search bar, search box, search field, search form, search input
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Sentry Plugin
|
||||
label: Sentry
|
||||
order: 20
|
||||
order: 40
|
||||
desc: Integrate Sentry error tracking into your Payload application
|
||||
keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: SEO Plugin
|
||||
label: SEO
|
||||
order: 20
|
||||
order: 30
|
||||
desc: Manage SEO metadata from your Payload admin
|
||||
keywords: plugins, seo, meta, search, engine, ranking, google
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Stripe Plugin
|
||||
label: Stripe
|
||||
order: 20
|
||||
order: 40
|
||||
desc: Easily accept payments with Stripe
|
||||
keywords: plugins, stripe, payments, ecommerce
|
||||
---
|
||||
|
||||
@@ -94,6 +94,7 @@ _An asterisk denotes that an option is required._
|
||||
| **`adminThumbnail`** | Set the way that the [Admin Panel](../admin/overview) will display thumbnails for this Collection. [More](#admin-thumbnails) |
|
||||
| **`crop`** | Set to `false` to disable the cropping tool in the [Admin Panel](../admin/overview). Crop is enabled by default. [More](#crop-and-focal-point-selector) |
|
||||
| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |
|
||||
| **`displayPreview`** | Enable displaying preview of the uploaded file in Upload fields related to this Collection. Can be locally overridden by `displayPreview` option in Upload field. [More](/docs/fields/upload#config-options). |
|
||||
| **`externalFileHeaderFilter`** | Accepts existing headers and returns the headers after filtering or modifying. |
|
||||
| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |
|
||||
| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the [Admin Panel](../admin/overview). The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |
|
||||
|
||||
18
package.json
18
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -96,7 +96,7 @@
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@libsql/client": "0.6.2",
|
||||
"@next/bundle-analyzer": "15.0.0-canary.53",
|
||||
"@next/bundle-analyzer": "15.0.0-canary.104",
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"@payloadcms/eslint-plugin": "workspace:*",
|
||||
"@payloadcms/live-preview-react": "workspace:*",
|
||||
@@ -119,7 +119,7 @@
|
||||
"create-payload-app": "workspace:*",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "16.4.5",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"escape-html": "^1.0.3",
|
||||
"execa": "5.1.1",
|
||||
"form-data": "3.0.1",
|
||||
@@ -131,15 +131,15 @@
|
||||
"lint-staged": "15.2.7",
|
||||
"minimist": "1.2.8",
|
||||
"mongodb-memory-server": "^9.0",
|
||||
"next": "15.0.0-canary.53",
|
||||
"next": "15.0.0-canary.104",
|
||||
"open": "^10.1.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"playwright": "1.43.0",
|
||||
"playwright-core": "1.43.0",
|
||||
"prettier": "3.3.2",
|
||||
"prompts": "2.4.2",
|
||||
"react": "^19.0.0-rc-6230622a1a-20240610",
|
||||
"react-dom": "^19.0.0-rc-6230622a1a-20240610",
|
||||
"react": "^19.0.0-rc-06d0b89e-20240801",
|
||||
"react-dom": "^19.0.0-rc-06d0b89e-20240801",
|
||||
"rimraf": "3.0.2",
|
||||
"semver": "^7.5.4",
|
||||
"sharp": "0.32.6",
|
||||
@@ -150,11 +150,11 @@
|
||||
"tempy": "1.0.1",
|
||||
"tsx": "4.16.2",
|
||||
"turbo": "^1.13.3",
|
||||
"typescript": "5.5.3"
|
||||
"typescript": "5.5.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0 || ^19.0.0-rc-6230622a1a-20240610",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-6230622a1a-20240610"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-06d0b89e-20240801",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-06d0b89e-20240801"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.20.2 || >=20.9.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -240,25 +240,63 @@ async function installDeps(projectDir: string, packageManager: PackageManager, d
|
||||
export async function getNextAppDetails(projectDir: string): Promise<NextAppDetails> {
|
||||
const isSrcDir = fs.existsSync(path.resolve(projectDir, 'src'))
|
||||
|
||||
// Match next.config.js, next.config.ts, next.config.mjs, next.config.cjs
|
||||
const nextConfigPath: string | undefined = (
|
||||
await globby('next.config.*(t|j)s', { absolute: true, cwd: projectDir })
|
||||
await globby('next.config.(\\w)?(t|j)s', { absolute: true, cwd: projectDir })
|
||||
)?.[0]
|
||||
|
||||
if (!nextConfigPath || nextConfigPath.length === 0) {
|
||||
return {
|
||||
hasTopLevelLayout: false,
|
||||
isSrcDir,
|
||||
isSupportedNextVersion: false,
|
||||
nextConfigPath: undefined,
|
||||
nextVersion: null,
|
||||
}
|
||||
}
|
||||
|
||||
const packageObj = await fse.readJson(path.resolve(projectDir, 'package.json'))
|
||||
// Check if Next.js version is new enough
|
||||
let nextVersion = null
|
||||
if (packageObj.dependencies?.next) {
|
||||
nextVersion = packageObj.dependencies.next
|
||||
// Match versions using regex matching groups
|
||||
const versionMatch = /(?<major>\d+)/.exec(nextVersion)
|
||||
if (!versionMatch) {
|
||||
p.log.warn(`Could not determine Next.js version from ${nextVersion}`)
|
||||
return {
|
||||
hasTopLevelLayout: false,
|
||||
isSrcDir,
|
||||
isSupportedNextVersion: false,
|
||||
nextConfigPath,
|
||||
nextVersion,
|
||||
}
|
||||
}
|
||||
|
||||
const { major } = versionMatch.groups as { major: string }
|
||||
const majorVersion = parseInt(major)
|
||||
if (majorVersion < 15) {
|
||||
return {
|
||||
hasTopLevelLayout: false,
|
||||
isSrcDir,
|
||||
isSupportedNextVersion: false,
|
||||
nextConfigPath,
|
||||
nextVersion,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isSupportedNextVersion = true
|
||||
|
||||
// Check if Payload already installed
|
||||
if (packageObj.dependencies?.payload) {
|
||||
return {
|
||||
hasTopLevelLayout: false,
|
||||
isPayloadInstalled: true,
|
||||
isSrcDir,
|
||||
isSupportedNextVersion,
|
||||
nextConfigPath,
|
||||
nextVersion,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +319,15 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
|
||||
? fs.existsSync(path.resolve(nextAppDir, 'layout.tsx'))
|
||||
: false
|
||||
|
||||
return { hasTopLevelLayout, isSrcDir, nextAppDir, nextConfigPath, nextConfigType: configType }
|
||||
return {
|
||||
hasTopLevelLayout,
|
||||
isSrcDir,
|
||||
isSupportedNextVersion,
|
||||
nextAppDir,
|
||||
nextConfigPath,
|
||||
nextConfigType: configType,
|
||||
nextVersion,
|
||||
}
|
||||
}
|
||||
|
||||
function getProjectType(args: {
|
||||
|
||||
@@ -29,9 +29,22 @@ const postgresReplacement: DbAdapterReplacement = {
|
||||
packageName: '@payloadcms/db-postgres',
|
||||
}
|
||||
|
||||
const sqliteReplacement: DbAdapterReplacement = {
|
||||
configReplacement: (envName = 'DATABASE_URI') => [
|
||||
' db: sqliteAdapter({',
|
||||
' client: {',
|
||||
` url: process.env.${envName} || '',`,
|
||||
' },',
|
||||
' }),',
|
||||
],
|
||||
importReplacement: "import { sqliteAdapter } from '@payloadcms/db-sqlite'",
|
||||
packageName: '@payloadcms/db-sqlite',
|
||||
}
|
||||
|
||||
export const dbReplacements: Record<DbType, DbAdapterReplacement> = {
|
||||
mongodb: mongodbReplacement,
|
||||
postgres: postgresReplacement,
|
||||
sqlite: sqliteReplacement,
|
||||
}
|
||||
|
||||
type StorageAdapterReplacement = {
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { CliArgs, DbDetails, DbType } from '../types.js'
|
||||
|
||||
type DbChoice = {
|
||||
dbConnectionPrefix: `${string}/`
|
||||
dbConnectionSuffix?: string
|
||||
title: string
|
||||
value: DbType
|
||||
}
|
||||
@@ -20,6 +21,12 @@ const dbChoiceRecord: Record<DbType, DbChoice> = {
|
||||
title: 'PostgreSQL (beta)',
|
||||
value: 'postgres',
|
||||
},
|
||||
sqlite: {
|
||||
dbConnectionPrefix: 'file:./',
|
||||
dbConnectionSuffix: '.db',
|
||||
title: 'SQLite (beta)',
|
||||
value: 'sqlite',
|
||||
},
|
||||
}
|
||||
|
||||
export async function selectDb(args: CliArgs, projectName: string): Promise<DbDetails> {
|
||||
@@ -37,10 +44,10 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
dbType = await p.select<{ label: string; value: DbType }[], DbType>({
|
||||
initialValue: 'mongodb',
|
||||
message: `Select a database`,
|
||||
options: [
|
||||
{ label: 'MongoDB', value: 'mongodb' },
|
||||
{ label: 'Postgres', value: 'postgres' },
|
||||
],
|
||||
options: Object.values(dbChoiceRecord).map((dbChoice) => ({
|
||||
label: dbChoice.title,
|
||||
value: dbChoice.value,
|
||||
})),
|
||||
})
|
||||
if (p.isCancel(dbType)) process.exit(0)
|
||||
}
|
||||
@@ -50,7 +57,7 @@ export async function selectDb(args: CliArgs, projectName: string): Promise<DbDe
|
||||
let dbUri: string | symbol | undefined = undefined
|
||||
const initialDbUri = `${dbChoice.dbConnectionPrefix}${
|
||||
projectName === '.' ? `payload-${getRandomDigitSuffix()}` : slugify(projectName)
|
||||
}`
|
||||
}${dbChoice.dbConnectionSuffix || ''}`
|
||||
|
||||
if (args['--db-accept-recommended']) {
|
||||
dbUri = initialDbUri
|
||||
|
||||
@@ -74,9 +74,11 @@ export async function updatePayloadInProject(
|
||||
info('Payload packages updated successfully.')
|
||||
|
||||
info(`Updating Payload Next.js files...`)
|
||||
const templateFilesPath = dirname.endsWith('dist')
|
||||
? path.resolve(dirname, '../..', 'dist/template')
|
||||
: path.resolve(dirname, '../../../../templates/blank-3.0')
|
||||
|
||||
const templateFilesPath =
|
||||
process.env.JEST_WORKER_ID !== undefined
|
||||
? path.resolve(dirname, '../../../../templates/blank-3.0')
|
||||
: path.resolve(dirname, '../..', 'dist/template')
|
||||
|
||||
const templateSrcDir = path.resolve(templateFilesPath, 'src/app/(payload)')
|
||||
|
||||
|
||||
@@ -85,7 +85,22 @@ export class Main {
|
||||
|
||||
// Detect if inside Next.js project
|
||||
const nextAppDetails = await getNextAppDetails(process.cwd())
|
||||
const { hasTopLevelLayout, isPayloadInstalled, nextAppDir, nextConfigPath } = nextAppDetails
|
||||
const {
|
||||
hasTopLevelLayout,
|
||||
isPayloadInstalled,
|
||||
isSupportedNextVersion,
|
||||
nextAppDir,
|
||||
nextConfigPath,
|
||||
nextVersion,
|
||||
} = nextAppDetails
|
||||
|
||||
if (nextConfigPath && !isSupportedNextVersion) {
|
||||
p.log.warn(
|
||||
`Next.js v${nextVersion} is unsupported. Next.js >= 15 is required to use Payload.`,
|
||||
)
|
||||
p.outro(feedbackOutro())
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Upgrade Payload in existing project
|
||||
if (isPayloadInstalled && nextConfigPath) {
|
||||
|
||||
@@ -57,7 +57,7 @@ interface Template {
|
||||
|
||||
export type PackageManager = 'bun' | 'npm' | 'pnpm' | 'yarn'
|
||||
|
||||
export type DbType = 'mongodb' | 'postgres'
|
||||
export type DbType = 'mongodb' | 'postgres' | 'sqlite'
|
||||
|
||||
export type DbDetails = {
|
||||
dbUri: string
|
||||
@@ -70,9 +70,11 @@ export type NextAppDetails = {
|
||||
hasTopLevelLayout: boolean
|
||||
isPayloadInstalled?: boolean
|
||||
isSrcDir: boolean
|
||||
isSupportedNextVersion: boolean
|
||||
nextAppDir?: string
|
||||
nextConfigPath?: string
|
||||
nextConfigType?: NextConfigType
|
||||
nextVersion: null | string
|
||||
}
|
||||
|
||||
export type NextConfigType = 'cjs' | 'esm' | 'ts'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -54,6 +54,10 @@ export const connect: Connect = async function connect(
|
||||
this.payload.logger.info('---- DROPPED DATABASE ----')
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production' && this.prodMigrations) {
|
||||
await this.migrate({ migrations: this.prodMigrations })
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
this.payload.logger.error(`Error: cannot connect to MongoDB. Details: ${err.message}`, err)
|
||||
|
||||
@@ -64,7 +64,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
forceCountFn: hasNearConstraint,
|
||||
lean: true,
|
||||
leanWithId: true,
|
||||
offset: skip,
|
||||
limit,
|
||||
options,
|
||||
page,
|
||||
pagination,
|
||||
|
||||
@@ -8,7 +8,7 @@ import mongoose from 'mongoose'
|
||||
import path from 'path'
|
||||
import { createDatabaseAdapter } from 'payload'
|
||||
|
||||
import type { CollectionModel, GlobalModel } from './types.js'
|
||||
import type { CollectionModel, GlobalModel, MigrateDownArgs, MigrateUpArgs } from './types.js'
|
||||
|
||||
import { connect } from './connect.js'
|
||||
import { count } from './count.js'
|
||||
@@ -78,6 +78,11 @@ export interface Args {
|
||||
* typed as any to avoid dependency
|
||||
*/
|
||||
mongoMemoryServer?: MongoMemoryReplSet
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
transactionOptions?: TransactionOptions | false
|
||||
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
|
||||
url: false | string
|
||||
@@ -90,6 +95,11 @@ export type MongooseAdapter = {
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
mongoMemoryServer: MongoMemoryReplSet
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
sessions: Record<number | string, ClientSession>
|
||||
versions: {
|
||||
[slug: string]: CollectionModel
|
||||
@@ -107,6 +117,11 @@ declare module 'payload' {
|
||||
connection: Connection
|
||||
globals: GlobalModel
|
||||
mongoMemoryServer: MongoMemoryReplSet
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
sessions: Record<number | string, ClientSession>
|
||||
transactionOptions: TransactionOptions
|
||||
versions: {
|
||||
@@ -121,6 +136,7 @@ export function mongooseAdapter({
|
||||
disableIndexHints = false,
|
||||
migrationDir: migrationDirArg,
|
||||
mongoMemoryServer,
|
||||
prodMigrations,
|
||||
transactionOptions = {},
|
||||
url,
|
||||
}: Args): DatabaseAdapterObj {
|
||||
@@ -167,6 +183,7 @@ export function mongooseAdapter({
|
||||
migrateFresh,
|
||||
migrationDir,
|
||||
payload,
|
||||
prodMigrations,
|
||||
queryDrafts,
|
||||
rollbackTransaction,
|
||||
updateGlobal,
|
||||
|
||||
@@ -52,9 +52,19 @@ type FieldSchemaGenerator = (
|
||||
buildSchemaOptions: BuildSchemaOptions,
|
||||
) => void
|
||||
|
||||
/**
|
||||
* get a field's defaultValue only if defined and not dynamic so that it can be set on the field schema
|
||||
* @param field
|
||||
*/
|
||||
const formatDefaultValue = (field: FieldAffectingData) =>
|
||||
typeof field.defaultValue !== 'undefined' && typeof field.defaultValue !== 'function'
|
||||
? field.defaultValue
|
||||
: undefined
|
||||
|
||||
const formatBaseSchema = (field: FieldAffectingData, buildSchemaOptions: BuildSchemaOptions) => {
|
||||
const { disableUnique, draftsEnabled, indexSortableFields } = buildSchemaOptions
|
||||
const schema: SchemaTypeOptions<unknown> = {
|
||||
default: formatDefaultValue(field),
|
||||
index: field.index || (!disableUnique && field.unique) || indexSortableFields || false,
|
||||
required: false,
|
||||
unique: (!disableUnique && field.unique) || false,
|
||||
@@ -159,7 +169,6 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
},
|
||||
}),
|
||||
],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
@@ -174,7 +183,6 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
): void => {
|
||||
const fieldSchema = {
|
||||
type: [new mongoose.Schema({}, { _id: false, discriminatorKey: 'blockType' })],
|
||||
default: undefined,
|
||||
}
|
||||
|
||||
schema.add({
|
||||
@@ -339,7 +347,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
},
|
||||
coordinates: {
|
||||
type: [Number],
|
||||
default: field.defaultValue || undefined,
|
||||
default: formatDefaultValue(field),
|
||||
required: false,
|
||||
},
|
||||
}
|
||||
@@ -420,7 +428,9 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
|
||||
return {
|
||||
...locales,
|
||||
[locale]: field.hasMany ? { type: [localeSchema], default: undefined } : localeSchema,
|
||||
[locale]: field.hasMany
|
||||
? { type: [localeSchema], default: formatDefaultValue(field) }
|
||||
: localeSchema,
|
||||
}
|
||||
}, {}),
|
||||
localized: true,
|
||||
@@ -440,7 +450,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
if (field.hasMany) {
|
||||
schemaToReturn = {
|
||||
type: [schemaToReturn],
|
||||
default: undefined,
|
||||
default: formatDefaultValue(field),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -453,7 +463,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
|
||||
if (field.hasMany) {
|
||||
schemaToReturn = {
|
||||
type: [schemaToReturn],
|
||||
default: undefined,
|
||||
default: formatDefaultValue(field),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -47,8 +47,8 @@
|
||||
"dependencies": {
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"drizzle-kit": "0.23.2-df9e596",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"pg": "8.11.3",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
|
||||
@@ -53,6 +53,7 @@ export const connect: Connect = async function connect(
|
||||
const { hotReload } = options
|
||||
|
||||
this.schema = {
|
||||
pgSchema: this.pgSchema,
|
||||
...this.tables,
|
||||
...this.relations,
|
||||
...this.enums,
|
||||
@@ -90,4 +91,8 @@ export const connect: Connect = async function connect(
|
||||
}
|
||||
|
||||
if (typeof this.resolveInitializing === 'function') this.resolveInitializing()
|
||||
|
||||
if (process.env.NODE_ENV === 'production' && this.prodMigrations) {
|
||||
await this.migrate({ migrations: this.prodMigrations })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/api'
|
||||
import type { CreateMigration } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
import path from 'path'
|
||||
import { getPredefinedMigration } from 'payload'
|
||||
import { getPredefinedMigration, writeMigrationIndex } from 'payload'
|
||||
import prompts from 'prompts'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
@@ -25,7 +25,7 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir)
|
||||
}
|
||||
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
|
||||
const { generateDrizzleJson, generateMigration, upPgSnapshot } = require('drizzle-kit/api')
|
||||
const drizzleJsonAfter = generateDrizzleJson(this.schema)
|
||||
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
|
||||
const formattedDate = yyymmdd.replace(/\D/g, '')
|
||||
@@ -49,6 +49,12 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
|
||||
let drizzleJsonBefore = defaultDrizzleSnapshot
|
||||
|
||||
if (this.schemaName) {
|
||||
drizzleJsonBefore.schemas = {
|
||||
[this.schemaName]: this.schemaName,
|
||||
}
|
||||
}
|
||||
|
||||
if (!upSQL) {
|
||||
// Get latest migration snapshot
|
||||
const latestSnapshot = fs
|
||||
@@ -58,9 +64,11 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
.reverse()?.[0]
|
||||
|
||||
if (latestSnapshot) {
|
||||
drizzleJsonBefore = JSON.parse(
|
||||
fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'),
|
||||
) as DrizzleSnapshotJSON
|
||||
drizzleJsonBefore = JSON.parse(fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'))
|
||||
|
||||
if (drizzleJsonBefore.version < drizzleJsonAfter.version) {
|
||||
drizzleJsonBefore = upPgSnapshot(drizzleJsonBefore)
|
||||
}
|
||||
}
|
||||
|
||||
const sqlStatementsUp = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
|
||||
@@ -107,5 +115,8 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
upSQL: upSQL || ` // Migration code`,
|
||||
}),
|
||||
)
|
||||
|
||||
writeMigrationIndex({ migrationsDir: payload.db.migrationDir })
|
||||
|
||||
payload.logger.info({ msg: `Migration created at ${filePath}.ts` })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/api'
|
||||
|
||||
export const defaultDrizzleSnapshot: DrizzleSnapshotJSON = {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
@@ -7,10 +7,11 @@ export const defaultDrizzleSnapshot: DrizzleSnapshotJSON = {
|
||||
schemas: {},
|
||||
tables: {},
|
||||
},
|
||||
dialect: 'pg',
|
||||
dialect: 'postgresql',
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
schemas: {},
|
||||
sequences: {},
|
||||
tables: {},
|
||||
version: '5',
|
||||
version: '7',
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
updateOne,
|
||||
updateVersion,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { createDatabaseAdapter } from 'payload'
|
||||
|
||||
import type { Args, PostgresAdapter } from './types.js'
|
||||
@@ -62,12 +63,19 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
const migrationDir = findMigrationDir(args.migrationDir)
|
||||
let resolveInitializing
|
||||
let rejectInitializing
|
||||
let adapterSchema: PostgresAdapter['pgSchema']
|
||||
|
||||
const initializing = new Promise<void>((res, rej) => {
|
||||
resolveInitializing = res
|
||||
rejectInitializing = rej
|
||||
})
|
||||
|
||||
if (args.schemaName) {
|
||||
adapterSchema = pgSchema(args.schemaName)
|
||||
} else {
|
||||
adapterSchema = { enum: pgEnum, table: pgTable }
|
||||
}
|
||||
|
||||
return createDatabaseAdapter<PostgresAdapter>({
|
||||
name: 'postgres',
|
||||
defaultDrizzleSnapshot,
|
||||
@@ -83,9 +91,10 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
operators: operatorMap,
|
||||
pgSchema: undefined,
|
||||
pgSchema: adapterSchema,
|
||||
pool: undefined,
|
||||
poolOptions: args.pool,
|
||||
prodMigrations: args.prodMigrations,
|
||||
push: args.push,
|
||||
relations: {},
|
||||
relationshipsSuffix: args.relationshipsSuffix || '_rels',
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Init, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { createTableName } from '@payloadcms/drizzle'
|
||||
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
|
||||
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
@@ -10,13 +9,8 @@ import type { PostgresAdapter } from './types.js'
|
||||
import { buildTable } from './schema/build.js'
|
||||
|
||||
export const init: Init = function init(this: PostgresAdapter) {
|
||||
if (this.schemaName) {
|
||||
this.pgSchema = pgSchema(this.schemaName)
|
||||
} else {
|
||||
this.pgSchema = { table: pgTable }
|
||||
}
|
||||
if (this.payload.config.localization) {
|
||||
this.enums.enum__locales = pgEnum(
|
||||
this.enums.enum__locales = this.pgSchema.enum(
|
||||
'_locales',
|
||||
this.payload.config.localization.locales.map(({ code }) => code) as [string, ...string[]],
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { TransactionPg } from '@payloadcms/drizzle/types'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/api'
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
@@ -43,7 +43,7 @@ export const migratePostgresV2toV3 = async ({ debug, payload, req }: Args) => {
|
||||
const dir = payload.db.migrationDir
|
||||
|
||||
// get the drizzle migrateUpSQL from drizzle using the last schema
|
||||
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
|
||||
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/api')
|
||||
const drizzleJsonAfter = generateDrizzleJson(adapter.schema)
|
||||
|
||||
// Get the previous migration snapshot
|
||||
|
||||
@@ -2,4 +2,4 @@ import type { RequireDrizzleKit } from '@payloadcms/drizzle/types'
|
||||
|
||||
import { createRequire } from 'module'
|
||||
const require = createRequire(import.meta.url)
|
||||
export const requireDrizzleKit: RequireDrizzleKit = () => require('drizzle-kit/payload')
|
||||
export const requireDrizzleKit: RequireDrizzleKit = () => require('drizzle-kit/api')
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type {
|
||||
ForeignKeyBuilder,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { index, uniqueIndex } from 'drizzle-orm/pg-core'
|
||||
|
||||
import type { GenericColumn } from '../types.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, PgColumnBuilder } from 'drizzle-orm/pg-core'
|
||||
import type { Field, TabAsField } from 'payload'
|
||||
@@ -19,7 +18,6 @@ import {
|
||||
integer,
|
||||
jsonb,
|
||||
numeric,
|
||||
pgEnum,
|
||||
text,
|
||||
timestamp,
|
||||
varchar,
|
||||
@@ -35,6 +33,7 @@ import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
import { parentIDColumnMap } from './parentIDColumnMap.js'
|
||||
import { withDefault } from './withDefault.js'
|
||||
|
||||
type Args = {
|
||||
adapter: PostgresAdapter
|
||||
@@ -170,14 +169,14 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = varchar(columnName)
|
||||
targetTable[fieldName] = withDefault(varchar(columnName), field)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'email':
|
||||
case 'code':
|
||||
case 'textarea': {
|
||||
targetTable[fieldName] = varchar(columnName)
|
||||
targetTable[fieldName] = withDefault(varchar(columnName), field)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -199,23 +198,26 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = numeric(columnName)
|
||||
targetTable[fieldName] = withDefault(numeric(columnName), field)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'richText':
|
||||
case 'json': {
|
||||
targetTable[fieldName] = jsonb(columnName)
|
||||
targetTable[fieldName] = withDefault(jsonb(columnName), field)
|
||||
break
|
||||
}
|
||||
|
||||
case 'date': {
|
||||
targetTable[fieldName] = timestamp(columnName, {
|
||||
mode: 'string',
|
||||
precision: 3,
|
||||
withTimezone: true,
|
||||
})
|
||||
targetTable[fieldName] = withDefault(
|
||||
timestamp(columnName, {
|
||||
mode: 'string',
|
||||
precision: 3,
|
||||
withTimezone: true,
|
||||
}),
|
||||
field,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -234,7 +236,7 @@ export const traverseFields = ({
|
||||
throwValidationError,
|
||||
})
|
||||
|
||||
adapter.enums[enumName] = pgEnum(
|
||||
adapter.enums[enumName] = adapter.pgSchema.enum(
|
||||
enumName,
|
||||
field.options.map((option) => {
|
||||
if (optionIsObject(option)) {
|
||||
@@ -311,13 +313,13 @@ export const traverseFields = ({
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
targetTable[fieldName] = adapter.enums[enumName](fieldName)
|
||||
targetTable[fieldName] = withDefault(adapter.enums[enumName](fieldName), field)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'checkbox': {
|
||||
targetTable[fieldName] = boolean(columnName)
|
||||
targetTable[fieldName] = withDefault(boolean(columnName), field)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
17
packages/db-postgres/src/schema/withDefault.ts
Normal file
17
packages/db-postgres/src/schema/withDefault.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { PgColumnBuilder } from 'drizzle-orm/pg-core'
|
||||
import type { FieldAffectingData } from 'payload'
|
||||
|
||||
export const withDefault = (
|
||||
column: PgColumnBuilder,
|
||||
field: FieldAffectingData,
|
||||
): PgColumnBuilder => {
|
||||
if (typeof field.defaultValue === 'undefined' || typeof field.defaultValue === 'function')
|
||||
return column
|
||||
|
||||
if (typeof field.defaultValue === 'string' && field.defaultValue.includes("'")) {
|
||||
const escapedString = field.defaultValue.replaceAll("'", "''")
|
||||
return column.default(escapedString)
|
||||
}
|
||||
|
||||
return column.default(field.defaultValue)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
DrizzleAdapter,
|
||||
TransactionPg,
|
||||
} from '@payloadcms/drizzle/types'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/api'
|
||||
import type {
|
||||
ColumnBaseConfig,
|
||||
ColumnDataType,
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
PgSchema,
|
||||
PgTableWithColumns,
|
||||
PgTransactionConfig,
|
||||
pgEnum,
|
||||
} from 'drizzle-orm/pg-core'
|
||||
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
@@ -32,6 +33,11 @@ export type Args = {
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
pool: PoolConfig
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
push?: boolean
|
||||
relationshipsSuffix?: string
|
||||
/**
|
||||
@@ -106,6 +112,13 @@ type PostgresDrizzleAdapter = Omit<
|
||||
| 'relations'
|
||||
>
|
||||
|
||||
type Schema =
|
||||
| {
|
||||
enum: typeof pgEnum
|
||||
table: PgTableFn
|
||||
}
|
||||
| PgSchema
|
||||
|
||||
export type PostgresAdapter = {
|
||||
countDistinct: CountDistinct
|
||||
defaultDrizzleSnapshot: DrizzleSnapshotJSON
|
||||
@@ -125,15 +138,19 @@ export type PostgresAdapter = {
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
operators: Operators
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pgSchema?: Schema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
push: boolean
|
||||
rejectInitializing: () => void
|
||||
relations: Record<string, GenericRelation>
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
schemaName?: Args['schemaName']
|
||||
sessions: {
|
||||
[id: string]: {
|
||||
@@ -156,6 +173,7 @@ declare module 'payload' {
|
||||
export interface DatabaseAdapter
|
||||
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
|
||||
DrizzleAdapter {
|
||||
beginTransaction: (options?: PgTransactionConfig) => Promise<null | number | string>
|
||||
drizzle: PostgresDB
|
||||
enums: Record<string, GenericEnum>
|
||||
/**
|
||||
@@ -170,11 +188,16 @@ declare module 'payload' {
|
||||
pgSchema?: { table: PgTableFn } | PgSchema
|
||||
pool: Pool
|
||||
poolOptions: Args['pool']
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
push: boolean
|
||||
rejectInitializing: () => void
|
||||
relationshipsSuffix?: string
|
||||
resolveInitializing: () => void
|
||||
schema: Record<string, GenericEnum | GenericRelation | GenericTable>
|
||||
schema: Record<string, unknown>
|
||||
schemaName?: Args['schemaName']
|
||||
tableNameMap: Map<string, string>
|
||||
versionsSuffix?: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -46,8 +46,8 @@
|
||||
"@libsql/client": "^0.6.2",
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-kit": "0.20.14-1f2c838",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"drizzle-kit": "0.23.2-df9e596",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
|
||||
@@ -52,4 +52,8 @@ export const connect: Connect = async function connect(
|
||||
}
|
||||
|
||||
if (typeof this.resolveInitializing === 'function') this.resolveInitializing()
|
||||
|
||||
if (process.env.NODE_ENV === 'production' && this.prodMigrations) {
|
||||
await this.migrate({ migrations: this.prodMigrations })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { DrizzleSnapshotJSON } from 'drizzle-kit/api'
|
||||
import type { CreateMigration } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import { createRequire } from 'module'
|
||||
import path from 'path'
|
||||
import { getPredefinedMigration } from 'payload'
|
||||
import { getPredefinedMigration, writeMigrationIndex } from 'payload'
|
||||
import prompts from 'prompts'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
@@ -25,7 +25,7 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir)
|
||||
}
|
||||
const { generateSQLiteDrizzleJson, generateSQLiteMigration } = require('drizzle-kit/payload')
|
||||
const { generateSQLiteDrizzleJson, generateSQLiteMigration } = require('drizzle-kit/api')
|
||||
const drizzleJsonAfter = await generateSQLiteDrizzleJson(this.schema)
|
||||
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
|
||||
const formattedDate = yyymmdd.replace(/\D/g, '')
|
||||
@@ -112,5 +112,8 @@ export const createMigration: CreateMigration = async function createMigration(
|
||||
upSQL: upSQL || ` // Migration code`,
|
||||
}),
|
||||
)
|
||||
|
||||
writeMigrationIndex({ migrationsDir: payload.db.migrationDir })
|
||||
|
||||
payload.logger.info({ msg: `Migration created at ${filePath}.ts` })
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { DrizzleSQLiteSnapshotJSON } from 'drizzle-kit/payload'
|
||||
import type { DrizzleSQLiteSnapshotJSON } from 'drizzle-kit/api'
|
||||
|
||||
export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
@@ -10,5 +10,5 @@ export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
tables: {},
|
||||
version: '5',
|
||||
version: '6',
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
localesSuffix: args.localesSuffix || '_locales',
|
||||
logger: args.logger,
|
||||
operators,
|
||||
prodMigrations: args.prodMigrations,
|
||||
push: args.push,
|
||||
relations: {},
|
||||
relationshipsSuffix: args.relationshipsSuffix || '_rels',
|
||||
@@ -105,7 +106,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
versionsSuffix: args.versionsSuffix || '_v',
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction: args.transactionOptions === false ? undefined : beginTransaction,
|
||||
beginTransaction: args.transactionOptions ? beginTransaction : undefined,
|
||||
commitTransaction,
|
||||
connect,
|
||||
convertPathToJSONTraversal,
|
||||
|
||||
@@ -10,6 +10,6 @@ export const requireDrizzleKit: RequireDrizzleKit = () => {
|
||||
const {
|
||||
generateSQLiteDrizzleJson: generateDrizzleJson,
|
||||
pushSQLiteSchema: pushSchema,
|
||||
} = require('drizzle-kit/payload')
|
||||
} = require('drizzle-kit/api')
|
||||
return { generateDrizzleJson, pushSchema }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { ColumnDataType, Relation } from 'drizzle-orm'
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type {
|
||||
AnySQLiteColumn,
|
||||
ForeignKeyBuilder,
|
||||
IndexBuilder,
|
||||
SQLiteColumn,
|
||||
SQLiteColumnBuilder,
|
||||
SQLiteTableWithColumns,
|
||||
UniqueConstraintBuilder,
|
||||
@@ -32,18 +31,7 @@ import { traverseFields } from './traverseFields.js'
|
||||
export type BaseExtraConfig = Record<
|
||||
string,
|
||||
(cols: {
|
||||
[x: string]: SQLiteColumn<{
|
||||
baseColumn: never
|
||||
columnType: string
|
||||
data: unknown
|
||||
dataType: ColumnDataType
|
||||
driverParam: unknown
|
||||
enumValues: string[]
|
||||
hasDefault: false
|
||||
name: string
|
||||
notNull: false
|
||||
tableName: string
|
||||
}>
|
||||
[x: string]: AnySQLiteColumn
|
||||
}) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder
|
||||
>
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { index, uniqueIndex } from 'drizzle-orm/sqlite-core'
|
||||
import type { AnySQLiteColumn } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
import type { GenericColumn } from '../types.js'
|
||||
import { index, uniqueIndex } from 'drizzle-orm/sqlite-core'
|
||||
|
||||
type CreateIndexArgs = {
|
||||
columnName: string
|
||||
@@ -11,7 +10,7 @@ type CreateIndexArgs = {
|
||||
}
|
||||
|
||||
export const createIndex = ({ name, columnName, tableName, unique }: CreateIndexArgs) => {
|
||||
return (table: { [x: string]: GenericColumn }) => {
|
||||
return (table: { [x: string]: AnySQLiteColumn }) => {
|
||||
let columns
|
||||
if (Array.isArray(name)) {
|
||||
columns = name
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Relation } from 'drizzle-orm'
|
||||
import type { IndexBuilder, SQLiteColumnBuilder } from 'drizzle-orm/sqlite-core'
|
||||
import type { Field, TabAsField } from 'payload'
|
||||
@@ -30,6 +29,7 @@ import { buildTable } from './build.js'
|
||||
import { createIndex } from './createIndex.js'
|
||||
import { getIDColumn } from './getIDColumn.js'
|
||||
import { idToUUID } from './idToUUID.js'
|
||||
import { withDefault } from './withDefault.js'
|
||||
|
||||
type Args = {
|
||||
adapter: SQLiteAdapter
|
||||
@@ -166,14 +166,14 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = text(columnName)
|
||||
targetTable[fieldName] = withDefault(text(columnName), field)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'email':
|
||||
case 'code':
|
||||
case 'textarea': {
|
||||
targetTable[fieldName] = text(columnName)
|
||||
targetTable[fieldName] = withDefault(text(columnName), field)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -195,19 +195,19 @@ export const traverseFields = ({
|
||||
)
|
||||
}
|
||||
} else {
|
||||
targetTable[fieldName] = numeric(columnName)
|
||||
targetTable[fieldName] = withDefault(numeric(columnName), field)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'richText':
|
||||
case 'json': {
|
||||
targetTable[fieldName] = text(columnName, { mode: 'json' })
|
||||
targetTable[fieldName] = withDefault(text(columnName, { mode: 'json' }), field)
|
||||
break
|
||||
}
|
||||
|
||||
case 'date': {
|
||||
targetTable[fieldName] = text(columnName)
|
||||
targetTable[fieldName] = withDefault(text(columnName), field)
|
||||
break
|
||||
}
|
||||
|
||||
@@ -295,13 +295,13 @@ export const traverseFields = ({
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
targetTable[fieldName] = text(fieldName, { enum: options })
|
||||
targetTable[fieldName] = withDefault(text(fieldName, { enum: options }), field)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'checkbox': {
|
||||
targetTable[fieldName] = integer(columnName, { mode: 'boolean' })
|
||||
targetTable[fieldName] = withDefault(integer(columnName, { mode: 'boolean' }), field)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
17
packages/db-sqlite/src/schema/withDefault.ts
Normal file
17
packages/db-sqlite/src/schema/withDefault.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { SQLiteColumnBuilder } from 'drizzle-orm/sqlite-core'
|
||||
import type { FieldAffectingData } from 'payload'
|
||||
|
||||
export const withDefault = (
|
||||
column: SQLiteColumnBuilder,
|
||||
field: FieldAffectingData,
|
||||
): SQLiteColumnBuilder => {
|
||||
if (typeof field.defaultValue === 'undefined' || typeof field.defaultValue === 'function')
|
||||
return column
|
||||
|
||||
if (typeof field.defaultValue === 'string' && field.defaultValue.includes("'")) {
|
||||
const escapedString = field.defaultValue.replaceAll("'", "''")
|
||||
return column.default(escapedString)
|
||||
}
|
||||
|
||||
return column.default(field.defaultValue)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Client, Config, ResultSet } from '@libsql/client'
|
||||
import type { Operators } from '@payloadcms/drizzle'
|
||||
import type { BuildQueryJoinAliases, DrizzleAdapter } from '@payloadcms/drizzle/types'
|
||||
import type { ColumnDataType, DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
|
||||
import type { DrizzleConfig, Relation, Relations, SQL } from 'drizzle-orm'
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type {
|
||||
SQLiteColumn,
|
||||
AnySQLiteColumn,
|
||||
SQLiteInsertOnConflictDoUpdateConfig,
|
||||
SQLiteTableWithColumns,
|
||||
SQLiteTransactionConfig,
|
||||
@@ -18,6 +18,11 @@ export type Args = {
|
||||
localesSuffix?: string
|
||||
logger?: DrizzleConfig['logger']
|
||||
migrationDir?: string
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
push?: boolean
|
||||
relationshipsSuffix?: string
|
||||
schemaName?: string
|
||||
@@ -25,24 +30,8 @@ export type Args = {
|
||||
versionsSuffix?: string
|
||||
}
|
||||
|
||||
export type GenericColumn = SQLiteColumn<
|
||||
{
|
||||
baseColumn: never
|
||||
columnType: string
|
||||
data: unknown
|
||||
dataType: ColumnDataType
|
||||
driverParam: unknown
|
||||
enumValues: string[]
|
||||
hasDefault: false
|
||||
name: string
|
||||
notNull: false
|
||||
tableName: string
|
||||
},
|
||||
object
|
||||
>
|
||||
|
||||
export type GenericColumns = {
|
||||
[x: string]: GenericColumn
|
||||
[x: string]: AnySQLiteColumn
|
||||
}
|
||||
|
||||
export type GenericTable = SQLiteTableWithColumns<{
|
||||
@@ -116,6 +105,11 @@ export type SQLiteAdapter = {
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
operators: Operators
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
push: boolean
|
||||
rejectInitializing: () => void
|
||||
relations: Record<string, GenericRelation>
|
||||
@@ -144,6 +138,7 @@ declare module 'payload' {
|
||||
export interface DatabaseAdapter
|
||||
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
|
||||
DrizzleAdapter {
|
||||
beginTransaction: (options?: SQLiteTransactionConfig) => Promise<null | number | string>
|
||||
drizzle: LibSQLDatabase
|
||||
/**
|
||||
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
|
||||
@@ -154,6 +149,11 @@ declare module 'payload' {
|
||||
initializing: Promise<void>
|
||||
localesSuffix?: string
|
||||
logger: DrizzleConfig['logger']
|
||||
prodMigrations?: {
|
||||
down: (args: MigrateDownArgs) => Promise<void>
|
||||
name: string
|
||||
up: (args: MigrateUpArgs) => Promise<void>
|
||||
}[]
|
||||
push: boolean
|
||||
rejectInitializing: () => void
|
||||
relationshipsSuffix?: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -39,7 +39,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"console-table-printer": "2.11.2",
|
||||
"drizzle-orm": "0.29.4",
|
||||
"drizzle-orm": "0.32.1",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "9.0.0"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Count } from 'payload'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
import type { Count , SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
|
||||
@@ -15,7 +14,7 @@ export const count: Count = async function count(
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
|
||||
const { joins, where } = await buildQuery({
|
||||
adapter: this,
|
||||
|
||||
@@ -10,7 +10,7 @@ export const create: Create = async function create(
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, data, req },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function createGlobal<T extends Record<string, unknown>>(
|
||||
this: DrizzleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest }: CreateGlobalArgs,
|
||||
): Promise<T> {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
|
||||
|
||||
@@ -12,7 +12,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
this: DrizzleAdapter,
|
||||
{ autosave, globalSlug, req = {} as PayloadRequest, versionData }: CreateGlobalVersionArgs,
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const global = this.payload.globals.config.find(({ slug }) => slug === globalSlug)
|
||||
|
||||
const tableName = this.tableNameMap.get(`_${toSnakeCase(global.slug)}${this.versionsSuffix}`)
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
versionData,
|
||||
}: CreateVersionArgs<T>,
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const defaultTableName = toSnakeCase(collection.slug)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collectionConfig.slug))
|
||||
|
||||
@@ -14,7 +14,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, req = {} as PayloadRequest, where: whereArg },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
|
||||
@@ -12,7 +12,7 @@ export const deleteVersions: DeleteVersions = async function deleteVersion(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where: where },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = this.tableNameMap.get(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import type { Payload, PayloadRequest } from 'payload'
|
||||
|
||||
import { commitTransaction, initTransaction, killTransaction, readMigrationFiles } from 'payload'
|
||||
@@ -9,9 +8,12 @@ import type { DrizzleAdapter, Migration } from './types.js'
|
||||
import { migrationTableExists } from './utilities/migrationTableExists.js'
|
||||
import { parseError } from './utilities/parseError.js'
|
||||
|
||||
export async function migrate(this: DrizzleAdapter): Promise<void> {
|
||||
export const migrate: DrizzleAdapter['migrate'] = async function migrate(
|
||||
this: DrizzleAdapter,
|
||||
args,
|
||||
): Promise<void> {
|
||||
const { payload } = this
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
const migrationFiles = args?.migrations || (await readMigrationFiles({ payload }))
|
||||
|
||||
if (!migrationFiles.length) {
|
||||
payload.logger.info({ msg: 'No migrations to run.' })
|
||||
@@ -64,7 +66,7 @@ export async function migrate(this: DrizzleAdapter): Promise<void> {
|
||||
|
||||
// If already ran, skip
|
||||
if (alreadyRan) {
|
||||
continue
|
||||
continue
|
||||
}
|
||||
|
||||
await runMigrationFile(payload, migration, newBatch)
|
||||
@@ -72,16 +74,12 @@ export async function migrate(this: DrizzleAdapter): Promise<void> {
|
||||
}
|
||||
|
||||
async function runMigrationFile(payload: Payload, migration: Migration, batch: number) {
|
||||
const db = payload.db as DrizzleAdapter
|
||||
const { generateDrizzleJson } = db.requireDrizzleKit()
|
||||
const start = Date.now()
|
||||
const req = { payload } as PayloadRequest
|
||||
const adapter = payload.db as DrizzleAdapter
|
||||
|
||||
payload.logger.info({ msg: `Migrating: ${migration.name}` })
|
||||
|
||||
const drizzleJSON = await generateDrizzleJson({ schema: adapter.schema })
|
||||
|
||||
try {
|
||||
await initTransaction(req)
|
||||
const db = adapter?.sessions[await req.transactionID]?.db || adapter.drizzle
|
||||
@@ -92,7 +90,6 @@ async function runMigrationFile(payload: Payload, migration: Migration, batch: n
|
||||
data: {
|
||||
name: migration.name,
|
||||
batch,
|
||||
schema: drizzleJSON,
|
||||
},
|
||||
req,
|
||||
})
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function migrateFresh(
|
||||
|
||||
await this.dropDatabase({ adapter: this })
|
||||
|
||||
const migrationFiles = (await readMigrationFiles({ payload })) as Migration[]
|
||||
const migrationFiles = await readMigrationFiles({ payload })
|
||||
payload.logger.debug({
|
||||
msg: `Found ${migrationFiles.length} migration files.`,
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { DrizzleAdapter, DrizzleTransaction } from '../types.js'
|
||||
|
||||
export const beginTransaction: BeginTransaction = async function beginTransaction(
|
||||
this: DrizzleAdapter,
|
||||
options: DrizzleAdapter['transactionOptions'],
|
||||
) {
|
||||
let id
|
||||
try {
|
||||
@@ -41,7 +42,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
|
||||
}
|
||||
transactionReady()
|
||||
})
|
||||
}, this.transactionOptions)
|
||||
}, options || this.transactionOptions)
|
||||
.catch(() => {
|
||||
// swallow
|
||||
})
|
||||
|
||||
@@ -120,6 +120,7 @@ export type RequireDrizzleKit = () => {
|
||||
pushSchema: (
|
||||
schema: Record<string, unknown>,
|
||||
drizzle: DrizzleAdapter['drizzle'],
|
||||
filterSchema?: string[],
|
||||
) => Promise<{ apply; hasDataLoss; warnings }>
|
||||
}
|
||||
|
||||
@@ -132,7 +133,7 @@ export type Migration = {
|
||||
db?: DrizzleTransaction | LibSQLDatabase<Record<string, never>> | PostgresDB
|
||||
payload: Payload
|
||||
req: PayloadRequest
|
||||
}) => Promise<boolean>
|
||||
}) => Promise<void>
|
||||
up: ({
|
||||
db,
|
||||
payload,
|
||||
@@ -141,7 +142,7 @@ export type Migration = {
|
||||
db?: DrizzleTransaction | LibSQLDatabase | PostgresDB
|
||||
payload: Payload
|
||||
req: PayloadRequest
|
||||
}) => Promise<boolean>
|
||||
}) => Promise<void>
|
||||
} & MigrationData
|
||||
|
||||
export type CreateJSONQueryArgs = {
|
||||
|
||||
@@ -12,7 +12,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
this: DrizzleAdapter,
|
||||
{ id, collection: collectionSlug, data, draft, locale, req, where: whereArg },
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
|
||||
@@ -10,7 +10,7 @@ export async function updateGlobal<T extends Record<string, unknown>>(
|
||||
this: DrizzleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest }: UpdateGlobalArgs,
|
||||
): Promise<T> {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
const tableName = this.tableNameMap.get(toSnakeCase(globalConfig.slug))
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
where: whereArg,
|
||||
}: UpdateGlobalVersionArgs<T>,
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const globalConfig: SanitizedGlobalConfig = this.payload.globals.config.find(
|
||||
({ slug }) => slug === global,
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function updateVersion<T extends TypeWithID>(
|
||||
where: whereArg,
|
||||
}: UpdateVersionArgs<T>,
|
||||
) {
|
||||
const db = this.sessions[await req.transactionID]?.db || this.drizzle
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
const whereToUse = whereArg || { id: { equals: id } }
|
||||
const tableName = this.tableNameMap.get(
|
||||
|
||||
@@ -4,8 +4,10 @@ import { fieldAffectsData, fieldHasSubFields } from 'payload/shared'
|
||||
|
||||
export const hasLocalesTable = (fields: Field[]): boolean => {
|
||||
return fields.some((field) => {
|
||||
// arrays always get a separate table
|
||||
if (field.type === 'array') return false
|
||||
if (fieldAffectsData(field) && field.localized) return true
|
||||
if (fieldHasSubFields(field) && field.type !== 'array') return hasLocalesTable(field.fields)
|
||||
if (fieldHasSubFields(field)) return hasLocalesTable(field.fields)
|
||||
if (field.type === 'tabs') return field.tabs.some((tab) => hasLocalesTable(tab.fields))
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ export const migrationTableExists = async (adapter: DrizzleAdapter): Promise<boo
|
||||
|
||||
if (adapter.name === 'postgres') {
|
||||
const prependSchema = adapter.schemaName ? `"${adapter.schemaName}".` : ''
|
||||
statement = `SELECT to_regclass('${prependSchema}"payload_migrations"') exists;`
|
||||
statement = `SELECT to_regclass('${prependSchema}"payload_migrations"') AS exists;`
|
||||
}
|
||||
|
||||
if (adapter.name === 'sqlite') {
|
||||
|
||||
@@ -12,7 +12,11 @@ export const pushDevSchema = async (adapter: DrizzleAdapter) => {
|
||||
const { pushSchema } = adapter.requireDrizzleKit()
|
||||
|
||||
// This will prompt if clarifications are needed for Drizzle to push new schema
|
||||
const { apply, hasDataLoss, warnings } = await pushSchema(adapter.schema, adapter.drizzle)
|
||||
const { apply, hasDataLoss, warnings } = await pushSchema(
|
||||
adapter.schema,
|
||||
adapter.drizzle,
|
||||
adapter.schemaName ? [adapter.schemaName] : undefined,
|
||||
)
|
||||
|
||||
if (warnings.length) {
|
||||
let message = `Warnings detected during schema push: \n\n${warnings.join('\n')}\n\n`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"eslint-plugin-react-hooks": "5.1.0-rc-85acf2d195-20240711",
|
||||
"eslint-plugin-regexp": "2.6.0",
|
||||
"globals": "15.8.0",
|
||||
"typescript": "5.5.3",
|
||||
"typescript": "5.5.4",
|
||||
"typescript-eslint": "7.16.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"eslint-plugin-react-hooks": "5.1.0-rc-85acf2d195-20240711",
|
||||
"eslint-plugin-regexp": "2.6.0",
|
||||
"globals": "15.8.0",
|
||||
"typescript": "5.5.3",
|
||||
"typescript": "5.5.4",
|
||||
"typescript-eslint": "7.16.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { GraphQLJSON, GraphQLJSONObject } from '../packages/graphql-type-json/index.js'
|
||||
export { buildPaginatedListType } from '../schema/buildPaginatedListType.js'
|
||||
export { default as GraphQL } from 'graphql'
|
||||
export * as GraphQL from 'graphql'
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { Context } from '../types.js'
|
||||
export type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
draft?: boolean
|
||||
id: number | string
|
||||
},
|
||||
context: {
|
||||
@@ -20,6 +21,7 @@ export default function restoreVersionResolver(collection: Collection): Resolver
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateObjectProperty(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { Context } from '../types.js'
|
||||
type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
draft?: boolean
|
||||
id: number | string
|
||||
},
|
||||
context: {
|
||||
@@ -18,6 +19,7 @@ export default function restoreVersionResolver(globalConfig: SanitizedGlobalConf
|
||||
const options = {
|
||||
id: args.id,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
globalConfig,
|
||||
req: isolateObjectProperty(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
@@ -342,6 +342,7 @@ function initCollectionsGraphQL({ config, graphqlResult }: InitCollectionsGraphQ
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: versionIDType },
|
||||
draft: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: restoreVersionResolver(collection),
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ function initGlobalsGraphQL({ config, graphqlResult }: InitGlobalsGraphQLArgs):
|
||||
type: graphqlResult.globals.graphQL[slug].versionType,
|
||||
args: {
|
||||
id: { type: idType },
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(config.localization
|
||||
? {
|
||||
fallbackLocale: { type: graphqlResult.types.fallbackLocaleInputType },
|
||||
@@ -171,6 +172,7 @@ function initGlobalsGraphQL({ config, graphqlResult }: InitGlobalsGraphQLArgs):
|
||||
type: graphqlResult.globals.graphQL[slug].type,
|
||||
args: {
|
||||
id: { type: idType },
|
||||
draft: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: restoreVersionResolver(global),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "The official React SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -41,8 +41,8 @@
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.0.0 || ^19.0.0-rc-6230622a1a-20240610",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-6230622a1a-20240610"
|
||||
"react": "^19.0.0 || ^19.0.0-rc-06d0b89e-20240801",
|
||||
"react-dom": "^19.0.0 || ^19.0.0-rc-06d0b89e-20240801"
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
|
||||
@@ -39,6 +39,9 @@ export const RefreshRouteOnSave: React.FC<{
|
||||
ready({
|
||||
serverURL,
|
||||
})
|
||||
|
||||
// refresh after the ready message is sent to get the latest data
|
||||
refresh()
|
||||
}
|
||||
|
||||
return () => {
|
||||
@@ -46,7 +49,7 @@ export const RefreshRouteOnSave: React.FC<{
|
||||
window.removeEventListener('message', onMessage)
|
||||
}
|
||||
}
|
||||
}, [serverURL, onMessage, depth, apiRoute])
|
||||
}, [serverURL, onMessage, depth, apiRoute, refresh])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "The official Vue SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-beta.71",
|
||||
"version": "3.0.0-beta.77",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -94,7 +94,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": "^16.8.1",
|
||||
"next": "^15.0.0-canary.53",
|
||||
"next": "^15.0.0-canary.104",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 2px;
|
||||
border-radius: var(--style-radius-s);
|
||||
background-color: var(--theme-elevation-50);
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -51,6 +51,7 @@
|
||||
}
|
||||
|
||||
&--active {
|
||||
font-weight: 600;
|
||||
&::before {
|
||||
opacity: 1;
|
||||
background-color: var(--theme-elevation-100);
|
||||
@@ -78,14 +79,15 @@
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: calc(var(--base) / 2) calc(var(--base));
|
||||
line-height: base(1.2);
|
||||
padding: base(0.2) base(0.6);
|
||||
}
|
||||
|
||||
&__count {
|
||||
min-width: 22px;
|
||||
line-height: base(0.8);
|
||||
min-width: base(0.8);
|
||||
text-align: center;
|
||||
padding: 2px 7px;
|
||||
background-color: var(--theme-elevation-100);
|
||||
border-radius: 1px;
|
||||
border-radius: var(--style-radius-s);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
import React, { Fragment } from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import { baseClass } from '../../Tab/index.js'
|
||||
|
||||
@@ -12,13 +12,14 @@ export const VersionsPill: React.FC = () => {
|
||||
// documents that are version enabled _always_ have at least one version
|
||||
const hasVersions = versions?.totalDocs > 0
|
||||
|
||||
return (
|
||||
<span
|
||||
className={[`${baseClass}__count`, hasVersions ? `${baseClass}__count--has-count` : '']
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{hasVersions ? versions.totalDocs.toString() : <Fragment> </Fragment>}
|
||||
</span>
|
||||
)
|
||||
if (hasVersions)
|
||||
return (
|
||||
<span
|
||||
className={[`${baseClass}__count`, hasVersions ? `${baseClass}__count--has-count` : '']
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
{versions.totalDocs.toString()}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
103
packages/next/src/elements/EmailAndUsername/index.tsx
Normal file
103
packages/next/src/elements/EmailAndUsername/index.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
'use client'
|
||||
|
||||
import type { FieldPermissions, LoginWithUsernameOptions } from 'payload'
|
||||
|
||||
import { EmailField, RenderFields, TextField, useTranslation } from '@payloadcms/ui'
|
||||
import { email, username } from 'payload/shared'
|
||||
import React from 'react'
|
||||
|
||||
type Props = {
|
||||
loginWithUsername?: LoginWithUsernameOptions | false
|
||||
}
|
||||
function EmailFieldComponent(props: Props) {
|
||||
const { loginWithUsername } = props
|
||||
const { t } = useTranslation()
|
||||
|
||||
const requireEmail = !loginWithUsername || (loginWithUsername && loginWithUsername.requireEmail)
|
||||
const showEmailField =
|
||||
!loginWithUsername || loginWithUsername?.requireEmail || loginWithUsername?.allowEmailLogin
|
||||
|
||||
if (showEmailField) {
|
||||
return (
|
||||
<EmailField
|
||||
autoComplete="off"
|
||||
label={t('general:email')}
|
||||
name="email"
|
||||
path="email"
|
||||
required={requireEmail}
|
||||
validate={email}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function UsernameFieldComponent(props: Props) {
|
||||
const { loginWithUsername } = props
|
||||
const { t } = useTranslation()
|
||||
|
||||
const requireUsername = loginWithUsername && loginWithUsername.requireUsername
|
||||
const showUsernameField = Boolean(loginWithUsername)
|
||||
|
||||
if (showUsernameField) {
|
||||
return (
|
||||
<TextField
|
||||
label={t('authentication:username')}
|
||||
name="username"
|
||||
path="username"
|
||||
required={requireUsername}
|
||||
validate={username}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
type RenderEmailAndUsernameFieldsProps = {
|
||||
className?: string
|
||||
loginWithUsername?: LoginWithUsernameOptions | false
|
||||
operation?: 'create' | 'update'
|
||||
permissions?: {
|
||||
[fieldName: string]: FieldPermissions
|
||||
}
|
||||
readOnly: boolean
|
||||
}
|
||||
export function RenderEmailAndUsernameFields(props: RenderEmailAndUsernameFieldsProps) {
|
||||
const { className, loginWithUsername, operation, permissions, readOnly } = props
|
||||
|
||||
return (
|
||||
<RenderFields
|
||||
className={className}
|
||||
fieldMap={[
|
||||
{
|
||||
name: 'email',
|
||||
type: 'text',
|
||||
CustomField: <EmailFieldComponent loginWithUsername={loginWithUsername} />,
|
||||
cellComponentProps: null,
|
||||
fieldComponentProps: { type: 'email', autoComplete: 'off', readOnly },
|
||||
fieldIsPresentational: false,
|
||||
isFieldAffectingData: true,
|
||||
localized: false,
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
type: 'text',
|
||||
CustomField: <UsernameFieldComponent loginWithUsername={loginWithUsername} />,
|
||||
cellComponentProps: null,
|
||||
fieldComponentProps: { type: 'text', readOnly },
|
||||
fieldIsPresentational: false,
|
||||
isFieldAffectingData: true,
|
||||
localized: false,
|
||||
},
|
||||
]}
|
||||
forceRender
|
||||
operation={operation}
|
||||
path=""
|
||||
permissions={permissions}
|
||||
readOnly={readOnly}
|
||||
schemaPath=""
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -37,10 +37,12 @@ const Component: React.FC<{
|
||||
<p>{t('general:changesNotSaved')}</p>
|
||||
</div>
|
||||
<div className={`${baseClass}__controls`}>
|
||||
<Button buttonStyle="secondary" onClick={onCancel}>
|
||||
<Button buttonStyle="secondary" onClick={onCancel} size="large">
|
||||
{t('general:stayOnThisPage')}
|
||||
</Button>
|
||||
<Button onClick={onConfirm}>{t('general:leaveAnyway')}</Button>
|
||||
<Button onClick={onConfirm} size="large">
|
||||
{t('general:leaveAnyway')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '@payloadcms/ui'
|
||||
import { EntityType, formatAdminURL, groupNavItems } from '@payloadcms/ui/shared'
|
||||
import LinkWithDefault from 'next/link.js'
|
||||
import { usePathname } from 'next/navigation.js'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
const baseClass = 'nav'
|
||||
@@ -21,6 +22,7 @@ const baseClass = 'nav'
|
||||
export const DefaultNavClient: React.FC = () => {
|
||||
const { permissions } = useAuth()
|
||||
const { isEntityVisible } = useEntityVisibility()
|
||||
const pathname = usePathname()
|
||||
|
||||
const {
|
||||
collections,
|
||||
@@ -84,18 +86,23 @@ export const DefaultNavClient: React.FC = () => {
|
||||
LinkWithDefault) as typeof LinkWithDefault.default
|
||||
|
||||
const LinkElement = Link || 'a'
|
||||
const activeCollection = pathname.endsWith(href)
|
||||
|
||||
return (
|
||||
<LinkElement
|
||||
className={`${baseClass}__link`}
|
||||
className={[`${baseClass}__link`, activeCollection && `active`]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
href={href}
|
||||
id={id}
|
||||
key={i}
|
||||
tabIndex={!navOpen ? -1 : undefined}
|
||||
>
|
||||
<span className={`${baseClass}__link-icon`}>
|
||||
<ChevronIcon direction="right" />
|
||||
</span>
|
||||
{activeCollection && (
|
||||
<span className={`${baseClass}__link-icon`}>
|
||||
<ChevronIcon direction="right" />
|
||||
</span>
|
||||
)}
|
||||
<span className={`${baseClass}__link-label`}>{entityLabel}</span>
|
||||
</LinkElement>
|
||||
)
|
||||
|
||||
@@ -110,16 +110,9 @@
|
||||
&__link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.active {
|
||||
.nav__link-icon {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__link-icon {
|
||||
display: none;
|
||||
margin-right: calc(var(--base) * 0.25);
|
||||
top: -1px;
|
||||
position: relative;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { AcceptedLanguages, I18nClient } from '@payloadcms/translations'
|
||||
import type { SanitizedConfig } from 'payload'
|
||||
import type { PayloadRequest, SanitizedConfig } from 'payload'
|
||||
|
||||
import { initI18n, rtlLanguages } from '@payloadcms/translations'
|
||||
import { RootProvider } from '@payloadcms/ui'
|
||||
import '@payloadcms/ui/scss/app.scss'
|
||||
import { buildComponentMap } from '@payloadcms/ui/utilities/buildComponentMap'
|
||||
import { Merriweather } from 'next/font/google'
|
||||
import { headers as getHeaders, cookies as nextCookies } from 'next/headers.js'
|
||||
import { createClientConfig, parseCookies } from 'payload'
|
||||
import { createClientConfig, createLocalReq, parseCookies } from 'payload'
|
||||
import * as qs from 'qs-esm'
|
||||
import React from 'react'
|
||||
|
||||
import { getPayloadHMR } from '../../utilities/getPayloadHMR.js'
|
||||
@@ -16,14 +16,6 @@ import { getRequestTheme } from '../../utilities/getRequestTheme.js'
|
||||
import { DefaultEditView } from '../../views/Edit/Default/index.js'
|
||||
import { DefaultListView } from '../../views/List/Default/index.js'
|
||||
|
||||
const merriweather = Merriweather({
|
||||
display: 'swap',
|
||||
style: ['normal', 'italic'],
|
||||
subsets: ['latin'],
|
||||
variable: '--font-serif',
|
||||
weight: ['400', '900'],
|
||||
})
|
||||
|
||||
export const metadata = {
|
||||
description: 'Generated by Next.js',
|
||||
title: 'Next.js',
|
||||
@@ -61,6 +53,20 @@ export const RootLayout = async ({
|
||||
language: languageCode,
|
||||
})
|
||||
|
||||
const req = await createLocalReq(
|
||||
{
|
||||
fallbackLocale: null,
|
||||
req: {
|
||||
headers,
|
||||
host: headers.get('host'),
|
||||
i18n,
|
||||
url: `${payload.config.serverURL}`,
|
||||
} as PayloadRequest,
|
||||
},
|
||||
payload,
|
||||
)
|
||||
const { permissions, user } = await payload.auth({ headers, req })
|
||||
|
||||
const clientConfig = await createClientConfig({ config, t: i18n.t })
|
||||
|
||||
const dir = (rtlLanguages as unknown as AcceptedLanguages[]).includes(languageCode)
|
||||
@@ -100,7 +106,7 @@ export const RootLayout = async ({
|
||||
})
|
||||
|
||||
return (
|
||||
<html className={merriweather.variable} data-theme={theme} dir={dir} lang={languageCode}>
|
||||
<html data-theme={theme} dir={dir} lang={languageCode}>
|
||||
<body>
|
||||
<RootProvider
|
||||
componentMap={componentMap}
|
||||
@@ -109,9 +115,11 @@ export const RootLayout = async ({
|
||||
fallbackLang={clientConfig.i18n.fallbackLanguage}
|
||||
languageCode={languageCode}
|
||||
languageOptions={languageOptions}
|
||||
permissions={permissions}
|
||||
switchLanguageServerAction={switchLanguageServerAction}
|
||||
theme={theme}
|
||||
translations={i18n.translations}
|
||||
user={user}
|
||||
>
|
||||
{wrappedChildren}
|
||||
</RootProvider>
|
||||
|
||||
@@ -14,6 +14,7 @@ export const restoreVersion: CollectionRouteHandlerWithID = async ({
|
||||
}) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
const draft = searchParams.get('draft')
|
||||
|
||||
const id = sanitizeCollectionID({
|
||||
id: incomingID,
|
||||
@@ -25,6 +26,7 @@ export const restoreVersion: CollectionRouteHandlerWithID = async ({
|
||||
id,
|
||||
collection,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: draft === 'true' ? true : undefined,
|
||||
req,
|
||||
})
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@ import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
export const restoreVersion: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
|
||||
const { searchParams } = req
|
||||
const depth = searchParams.get('depth')
|
||||
const draft = searchParams.get('draft')
|
||||
|
||||
const doc = await restoreVersionOperationGlobal({
|
||||
id,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft: draft === 'true' ? true : undefined,
|
||||
globalConfig,
|
||||
req,
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
@import './styles.scss';
|
||||
@import 'styles';
|
||||
@import './toasts.scss';
|
||||
@import './colors.scss';
|
||||
|
||||
:root {
|
||||
--base-px: 25;
|
||||
--base-px: 20;
|
||||
--base-body-size: 13;
|
||||
--base: calc((var(--base-px) / var(--base-body-size)) * 1rem);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
--theme-baseline-body-size: #{$baseline-body-size};
|
||||
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
|
||||
sans-serif;
|
||||
--font-serif: Georgia, 'Bitstream Charter', 'Charis SIL', Utopia, 'URW Bookman L', serif;
|
||||
--font-mono: monospace;
|
||||
|
||||
--style-radius-s: #{$style-radius-s};
|
||||
@@ -67,12 +68,6 @@ html {
|
||||
@extend %body;
|
||||
background: var(--theme-bg);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
opacity: 0;
|
||||
|
||||
&[data-theme='dark'],
|
||||
&[data-theme='light'] {
|
||||
opacity: initial;
|
||||
}
|
||||
|
||||
&[data-theme='dark'] {
|
||||
--theme-bg: var(--theme-elevation-0);
|
||||
@@ -111,12 +106,12 @@ body {
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: var(--theme-success-500);
|
||||
background: var(--color-success-250);
|
||||
color: var(--theme-base-800);
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
background: var(--theme-success-500);
|
||||
background: var(--color-success-250);
|
||||
color: var(--theme-base-800);
|
||||
}
|
||||
|
||||
|
||||
59
packages/next/src/scss/toastify.scss
Normal file
59
packages/next/src/scss/toastify.scss
Normal file
@@ -0,0 +1,59 @@
|
||||
@import 'vars';
|
||||
@import 'queries';
|
||||
|
||||
.Toastify {
|
||||
.Toastify__toast-container {
|
||||
left: base(5);
|
||||
transform: none;
|
||||
right: base(5);
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.Toastify__toast {
|
||||
padding: base(0.5);
|
||||
border-radius: $style-radius-m;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.Toastify__close-button {
|
||||
align-self: center;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.Toastify__toast--success {
|
||||
color: var(--color-success-900);
|
||||
background: var(--color-success-500);
|
||||
|
||||
.Toastify__progress-bar {
|
||||
background-color: var(--color-success-900);
|
||||
}
|
||||
}
|
||||
|
||||
.Toastify__close-button--success {
|
||||
color: var(--color-success-900);
|
||||
}
|
||||
|
||||
.Toastify__toast--error {
|
||||
background: var(--theme-error-500);
|
||||
color: #fff;
|
||||
|
||||
.Toastify__progress-bar {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.Toastify__close-button--light {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
.Toastify__toast-container {
|
||||
left: $baseline;
|
||||
right: $baseline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,28 @@
|
||||
@import './styles.scss';
|
||||
|
||||
.payload-toast-container {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.payload-toast-close-button {
|
||||
position: absolute;
|
||||
order: 3;
|
||||
left: unset;
|
||||
right: 0.5rem;
|
||||
top: 1.55rem;
|
||||
color: var(--theme-elevation-400);
|
||||
inset-inline-end: base(0.5);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--theme-elevation-600);
|
||||
background: unset;
|
||||
border: none;
|
||||
display: flex;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
width: base(0.75);
|
||||
height: base(0.75);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-elevation-250);
|
||||
background: none;
|
||||
}
|
||||
|
||||
[dir='RTL'] & {
|
||||
@@ -27,16 +31,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
.toast-title {
|
||||
line-height: base(1);
|
||||
}
|
||||
|
||||
.payload-toast-item {
|
||||
padding: 1rem 2.5rem 1rem 1rem;
|
||||
color: var(--theme-text);
|
||||
padding: base(0.5);
|
||||
color: var(--theme-elevation-800);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
border-radius: 0.15rem;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--theme-border-color);
|
||||
background: var(--theme-input-bg);
|
||||
box-shadow:
|
||||
@@ -45,6 +53,7 @@
|
||||
|
||||
.toast-content {
|
||||
transition: opacity 100ms cubic-bezier(0.55, 0.055, 0.675, 0.19);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&[data-front='false'] {
|
||||
@@ -60,51 +69,72 @@
|
||||
}
|
||||
|
||||
.toast-icon {
|
||||
svg {
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
width: base(1);
|
||||
height: base(1);
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
& > * {
|
||||
width: base(1.2);
|
||||
height: base(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-warning {
|
||||
border-color: var(--theme-warning-200);
|
||||
background-color: var(--theme-warning-100);
|
||||
color: var(--theme-warning-800);
|
||||
border-color: var(--theme-warning-150);
|
||||
background-color: var(--theme-warning-50);
|
||||
|
||||
.payload-toast-close-button {
|
||||
color: var(--theme-warning-600);
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-warning-250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-error {
|
||||
border-color: var(--theme-error-300);
|
||||
background-color: var(--theme-error-150);
|
||||
color: var(--theme-error-800);
|
||||
border-color: var(--theme-error-150);
|
||||
background-color: var(--theme-error-50);
|
||||
|
||||
.payload-toast-close-button {
|
||||
color: var(--theme-error-600);
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-error-250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-success {
|
||||
border-color: var(--theme-success-200);
|
||||
background-color: var(--theme-success-100);
|
||||
color: var(--theme-success-800);
|
||||
border-color: var(--theme-success-150);
|
||||
background-color: var(--theme-success-50);
|
||||
|
||||
.payload-toast-close-button {
|
||||
color: var(--theme-success-600);
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-success-250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.toast-info {
|
||||
border-color: var(--theme-elevation-250);
|
||||
background-color: var(--theme-elevation-100);
|
||||
}
|
||||
color: var(--theme-elevation-800);
|
||||
border-color: var(--theme-elevation-150);
|
||||
background-color: var(--theme-elevation-50);
|
||||
|
||||
[data-theme='light'] & {
|
||||
&.toast-warning {
|
||||
border-color: var(--theme-warning-550);
|
||||
background-color: var(--theme-warning-100);
|
||||
}
|
||||
.payload-toast-close-button {
|
||||
color: var(--theme-elevation-600);
|
||||
|
||||
&.toast-error {
|
||||
border-color: var(--theme-error-200);
|
||||
background-color: var(--theme-error-50);
|
||||
}
|
||||
|
||||
&.toast-success {
|
||||
border-color: var(--theme-success-550);
|
||||
background-color: var(--theme-success-50);
|
||||
}
|
||||
|
||||
&.toast-info {
|
||||
border-color: var(--theme-border-color);
|
||||
background-color: var(--theme-elevation-50);
|
||||
&:hover {
|
||||
color: var(--theme-elevation-250);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,10 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
%jumbo {
|
||||
font-size: base(2.5);
|
||||
line-height: 1;
|
||||
margin: 0 0 base(2);
|
||||
}
|
||||
|
||||
%h1 {
|
||||
margin: 0 0 base(1);
|
||||
font-size: base(2);
|
||||
line-height: 1.15;
|
||||
letter-spacing: -1px;
|
||||
font-size: base(1.6);
|
||||
line-height: base(1.8);
|
||||
|
||||
@include small-break {
|
||||
letter-spacing: -0.5px;
|
||||
@@ -35,9 +28,8 @@
|
||||
|
||||
%h2 {
|
||||
margin: 0 0 base(1);
|
||||
font-size: base(1.25);
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.5px;
|
||||
font-size: base(1.3);
|
||||
line-height: base(1.6);
|
||||
|
||||
@include small-break {
|
||||
font-size: base(0.85);
|
||||
@@ -46,9 +38,8 @@
|
||||
|
||||
%h3 {
|
||||
margin: 0 0 base(1);
|
||||
font-size: base(0.925);
|
||||
line-height: 1.25;
|
||||
letter-spacing: -0.5px;
|
||||
font-size: base(1);
|
||||
line-height: base(1.2);
|
||||
|
||||
@include small-break {
|
||||
font-size: base(0.65);
|
||||
@@ -58,27 +49,27 @@
|
||||
|
||||
%h4 {
|
||||
margin: 0 0 $baseline;
|
||||
font-size: base(0.75);
|
||||
line-height: 1.5;
|
||||
font-size: base(0.8);
|
||||
line-height: base(1);
|
||||
letter-spacing: -0.375px;
|
||||
}
|
||||
|
||||
%h5 {
|
||||
margin: 0;
|
||||
font-size: base(0.5625);
|
||||
line-height: 1.5;
|
||||
font-size: base(0.65);
|
||||
line-height: base(0.8);
|
||||
}
|
||||
|
||||
%h6 {
|
||||
margin: 0;
|
||||
font-size: base(0.5);
|
||||
line-height: 1.5;
|
||||
font-size: base(0.6);
|
||||
line-height: base(0.8);
|
||||
}
|
||||
|
||||
%small {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.5;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
|
||||
@@ -13,7 +13,7 @@ $breakpoint-l-width: 1440px !default;
|
||||
// BASELINE GRID
|
||||
//////////////////////////////
|
||||
|
||||
$baseline-px: 25px !default;
|
||||
$baseline-px: 20px !default;
|
||||
$baseline-body-size: 13px !default;
|
||||
$baseline: math.div($baseline-px, $baseline-body-size) + rem;
|
||||
|
||||
@@ -40,7 +40,7 @@ $color-purple: #f3ddf3 !default;
|
||||
|
||||
$style-radius-s: 3px !default;
|
||||
$style-radius-m: 4px !default;
|
||||
$style-radius-l: 9px !default;
|
||||
$style-radius-l: 8px !default;
|
||||
$style-stroke-width: 1px !default;
|
||||
|
||||
$style-stroke-width-s: 1px !default;
|
||||
@@ -50,8 +50,8 @@ $style-stroke-width-m: 2px !default;
|
||||
// MISC
|
||||
//////////////////////////////
|
||||
|
||||
$top-header-offset: calc(var(--base) - 1px);
|
||||
$top-header-offset-m: calc(var(--base) * 3);
|
||||
$top-header-offset: calc(base(1) - 1px);
|
||||
$top-header-offset-m: base(3);
|
||||
$focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
|
||||
|
||||
//////////////////////////////
|
||||
@@ -59,41 +59,19 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
|
||||
//////////////////////////////
|
||||
|
||||
@mixin shadow-sm {
|
||||
box-shadow:
|
||||
0 2px 3px 0 rgba(0, 2, 4, 0.05),
|
||||
0 10px 4px -8px rgba(0, 2, 4, 0.02);
|
||||
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@mixin shadow-m {
|
||||
box-shadow:
|
||||
0 0 30px 0 rgb(0 2 4 / 12%),
|
||||
0 30px 25px -8px rgb(0 2 4 / 10%);
|
||||
box-shadow: 0 4px 8px -3px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@mixin shadow-lg {
|
||||
box-shadow:
|
||||
0 20px 35px -10px rgba(0, 2, 4, 0.2),
|
||||
0 6px 4px -4px rgba(0, 2, 4, 0.02);
|
||||
box-shadow: 0 -2px 16px -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@mixin shadow-lg-top {
|
||||
box-shadow:
|
||||
0 -20px 35px -10px rgba(0, 2, 4, 0.2),
|
||||
0 -6px 4px -4px rgba(0, 2, 4, 0.02);
|
||||
}
|
||||
|
||||
@mixin shadow {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.07);
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin inputShadowActive {
|
||||
box-shadow:
|
||||
0 2px 3px 0 rgba(0, 2, 4, 0.16),
|
||||
0 6px 4px -4px rgba(0, 2, 4, 0.13);
|
||||
box-shadow: 0 2px 16px -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@mixin inputShadow {
|
||||
@@ -101,15 +79,7 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
|
||||
|
||||
&:not(:disabled) {
|
||||
&:hover {
|
||||
box-shadow:
|
||||
0 2px 3px 0 rgba(0, 2, 4, 0.13),
|
||||
0 6px 4px -4px rgba(0, 2, 4, 0.1);
|
||||
}
|
||||
|
||||
&:active,
|
||||
&:focus-within,
|
||||
&:focus {
|
||||
@include inputShadowActive;
|
||||
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,19 +117,33 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
|
||||
@include blur-bg(var(--theme-bg), 0.3);
|
||||
}
|
||||
|
||||
@mixin readOnly {
|
||||
background: var(--theme-elevation-100);
|
||||
color: var(--theme-elevation-400);
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--theme-elevation-150);
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin formInput() {
|
||||
@include inputShadow;
|
||||
font-family: var(--font-body);
|
||||
width: 100%;
|
||||
border: 1px solid var(--theme-elevation-150);
|
||||
border-radius: var(--style-radius-s);
|
||||
background: var(--theme-input-bg);
|
||||
color: var(--theme-elevation-800);
|
||||
border-radius: 0;
|
||||
font-size: 1rem;
|
||||
height: base(2);
|
||||
line-height: base(1);
|
||||
padding: base(0.5) base(0.75);
|
||||
padding: base(0.4) base(0.75);
|
||||
-webkit-appearance: none;
|
||||
transition-property: border, box-shadow;
|
||||
transition-duration: 100ms;
|
||||
transition-timing-function: cubic-bezier(0, 0.2, 0.2, 1);
|
||||
|
||||
&[data-rtl='true'] {
|
||||
direction: rtl;
|
||||
@@ -189,12 +173,7 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: var(--theme-elevation-200);
|
||||
color: var(--theme-elevation-450);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--theme-elevation-150);
|
||||
}
|
||||
@include readOnly;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
|
||||
import { notFound } from 'next/navigation.js'
|
||||
|
||||
import { isAdminAuthRoute, isAdminRoute } from './shared.js'
|
||||
import { getRouteWithoutAdmin, isAdminAuthRoute, isAdminRoute } from './shared.js'
|
||||
|
||||
export const handleAdminPage = ({
|
||||
adminRoute,
|
||||
@@ -20,9 +20,9 @@ export const handleAdminPage = ({
|
||||
permissions: Permissions
|
||||
route: string
|
||||
}) => {
|
||||
if (isAdminRoute(route, adminRoute)) {
|
||||
const baseAdminRoute = adminRoute && adminRoute !== '/' ? route.replace(adminRoute, '') : route
|
||||
const routeSegments = baseAdminRoute.split('/').filter(Boolean)
|
||||
if (isAdminRoute({ adminRoute, config, route })) {
|
||||
const routeWithoutAdmin = getRouteWithoutAdmin({ adminRoute, route })
|
||||
const routeSegments = routeWithoutAdmin.split('/').filter(Boolean)
|
||||
const [entityType, entitySlug, createOrID] = routeSegments
|
||||
const collectionSlug = entityType === 'collections' ? entitySlug : undefined
|
||||
const globalSlug = entityType === 'globals' ? entitySlug : undefined
|
||||
@@ -47,7 +47,7 @@ export const handleAdminPage = ({
|
||||
}
|
||||
}
|
||||
|
||||
if (!permissions.canAccessAdmin && !isAdminAuthRoute(config, route, adminRoute)) {
|
||||
if (!permissions.canAccessAdmin && !isAdminAuthRoute({ adminRoute, config, route })) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user