chore(next): lints

This commit is contained in:
Jacob Fletcher
2024-02-25 22:15:03 -05:00
parent 5f7b4a9434
commit 6b9b98ffdd
126 changed files with 1201 additions and 1089 deletions

View File

@@ -0,0 +1,10 @@
.tmp
**/.git
**/.hg
**/.pnp.*
**/.svn
**/.yarn/**
**/build
**/dist/**
**/node_modules
**/temp

View File

@@ -0,0 +1,44 @@
module.exports = {
extends: [
'@payloadcms',
// 'next'
],
overrides: [
{
extends: ['plugin:@typescript-eslint/disable-type-checked'],
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
},
{
files: ['package.json', 'tsconfig.json'],
rules: {
'perfectionist/sort-array-includes': 'off',
'perfectionist/sort-astro-attributes': 'off',
'perfectionist/sort-classes': 'off',
'perfectionist/sort-enums': 'off',
'perfectionist/sort-exports': 'off',
'perfectionist/sort-imports': 'off',
'perfectionist/sort-interfaces': 'off',
'perfectionist/sort-jsx-props': 'off',
'perfectionist/sort-keys': 'off',
'perfectionist/sort-maps': 'off',
'perfectionist/sort-named-exports': 'off',
'perfectionist/sort-named-imports': 'off',
'perfectionist/sort-object-types': 'off',
'perfectionist/sort-objects': 'off',
'perfectionist/sort-svelte-attributes': 'off',
'perfectionist/sort-union-types': 'off',
'perfectionist/sort-vue-attributes': 'off',
},
},
],
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname,
},
root: true,
settings: {
next: {
rootDir: '../../app/',
},
},
}

View File

@@ -14,6 +14,8 @@
"clean": "rimraf {dist,*.tsbuildinfo}",
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" \"src/app/api/**\" dist/ && pnpm copyfiles:api",
"copyfiles:api": "copyfiles -u 1 \"src/app/api/**\" dist/template",
"fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
"lint": "eslint \"src/**/*.{ts,tsx}\"",
"prepublishOnly": "pnpm clean && pnpm build"
},
"exports": {
@@ -38,6 +40,9 @@
}
},
"devDependencies": {
"@next/eslint-plugin-next": "^14.1.0",
"@types/react": "18.2.15",
"@types/react-dom": "18.2.7",
"@payloadcms/eslint-config": "workspace:*",
"payload": "workspace:*",
"sass": "^1.70.0"

View File

@@ -1,7 +1,7 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import { REST_DELETE, REST_GET, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from 'payload-config'
import { REST_GET, REST_DELETE, REST_PATCH, REST_POST } from '@payloadcms/next/routes'
export const GET = REST_GET(config)
export const POST = REST_POST(config)

View File

@@ -1,6 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import { GET_STATIC_FILE } from '@payloadcms/next/routes'
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from 'payload-config'
import { GET_STATIC_FILE } from '@payloadcms/next/routes'
export const GET = GET_STATIC_FILE(config)

View File

@@ -1,6 +1,6 @@
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
import { GRAPHQL_POST } from '@payloadcms/next/routes'
/* DO NOT MODIFY it because it could be re-written at any time. */
import config from 'payload-config'
import { GRAPHQL_POST } from '@payloadcms/next/routes'
export const POST = GRAPHQL_POST(config)

View File

@@ -11,7 +11,7 @@ import { install } from './install'
main()
async function main() {
if (debug) console.log({ pwd: process.cwd(), debug })
if (debug) console.log({ debug, pwd: process.cwd() })
switch (script.toLowerCase()) {
case 'install': {
if (debug) console.log('Running install')

View File

@@ -17,11 +17,11 @@ export const install = (args?: { debug?: boolean }): Promise<void> => {
if (debug) {
console.log({
useSrc,
basePath,
apiTemplateDir,
userApiDir,
basePath,
cwd: process.cwd(),
useSrc,
userApiDir,
})
}

View File

@@ -1,3 +1,3 @@
import { SanitizedConfig } from 'payload/config'
import type { SanitizedConfig } from 'payload/config'
export default {} as Promise<SanitizedConfig>

View File

@@ -1,13 +1,14 @@
import React from 'react'
import type { SanitizedConfig } from 'payload/types'
import { DefaultTemplate } from '@payloadcms/ui'
import { SanitizedConfig } from 'payload/types'
import '@payloadcms/ui/scss/app.scss'
import React from 'react'
import { initPage } from '../../utilities/initPage'
import '@payloadcms/ui/scss/app.scss'
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
title: 'Next.js',
}
export const AdminLayout = async ({
@@ -17,10 +18,10 @@ export const AdminLayout = async ({
children: React.ReactNode
config: Promise<SanitizedConfig> | SanitizedConfig
}) => {
const { user, permissions, i18n } = await initPage({ config })
const { i18n, permissions, user } = await initPage({ config })
return (
<DefaultTemplate config={config} user={user} permissions={permissions} i18n={i18n}>
<DefaultTemplate config={config} i18n={i18n} permissions={permissions} user={user}>
{children}
</DefaultTemplate>
)

View File

@@ -1,39 +1,40 @@
import React, { Fragment } from 'react'
import { SanitizedConfig } from 'payload/types'
import type { SanitizedConfig } from 'payload/types'
import '@payloadcms/ui/scss/app.scss'
import { initPage } from '../../utilities/initPage'
import { DocumentHeader } from '@payloadcms/ui'
import '@payloadcms/ui/scss/app.scss'
import React, { Fragment } from 'react'
import { initPage } from '../../utilities/initPage'
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
title: 'Next.js',
}
export const DocumentLayout = async ({
children,
config: configPromise,
collectionSlug,
config: configPromise,
globalSlug,
}: {
children: React.ReactNode
config: Promise<SanitizedConfig>
collectionSlug?: string
config: Promise<SanitizedConfig>
globalSlug?: string
}) => {
const { config, collectionConfig, globalConfig, i18n } = await initPage({
config: configPromise,
const { collectionConfig, config, globalConfig, i18n } = await initPage({
collectionSlug,
config: configPromise,
globalSlug,
})
return (
<Fragment>
<DocumentHeader
i18n={i18n}
config={config}
collectionConfig={collectionConfig}
config={config}
globalConfig={globalConfig}
i18n={i18n}
/>
{children}
</Fragment>

View File

@@ -1,17 +1,18 @@
import React from 'react'
import { headers, cookies } from 'next/headers'
import type { SanitizedConfig } from 'payload/types'
import { translations } from '@payloadcms/translations/client'
import { RootProvider, buildComponentMap } from '@payloadcms/ui'
import { SanitizedConfig } from 'payload/types'
import '@payloadcms/ui/scss/app.scss'
import { cookies, headers } from 'next/headers'
import { deepMerge } from 'payload/utilities'
import React from 'react'
import { createClientConfig } from '../../utilities/createClientConfig'
import { getRequestLanguage } from '../../utilities/getRequestLanguage'
import { deepMerge } from 'payload/utilities'
import '@payloadcms/ui/scss/app.scss'
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
title: 'Next.js',
}
const rtlLanguages = ['ar', 'fa', 'ha', 'ku', 'ur', 'ps', 'dv', 'ks', 'khw', 'he', 'yi']
@@ -28,8 +29,8 @@ export const RootLayout = async ({
const lang =
getRequestLanguage({
headers: headers(),
cookies: cookies(),
headers: headers(),
}) ?? clientConfig.i18n.fallbackLanguage
const dir = rtlLanguages.includes(lang) ? 'RTL' : 'LTR'
@@ -44,15 +45,15 @@ export const RootLayout = async ({
const componentMap = buildComponentMap({ config })
return (
<html lang={lang} dir={dir}>
<html dir={dir} lang={lang}>
<body>
<RootProvider
config={clientConfig}
translations={mergedTranslations[lang]}
lang={lang}
fallbackLang={clientConfig.i18n.fallbackLanguage}
languageOptions={languageOptions}
componentMap={componentMap}
config={clientConfig}
fallbackLang={clientConfig.i18n.fallbackLanguage}
lang={lang}
languageOptions={languageOptions}
translations={mergedTranslations[lang]}
>
{children}
</RootProvider>

View File

@@ -1,10 +1,11 @@
import { FileShape, NextFileUploadOptions } from '.'
import type { FileShape, NextFileUploadOptions } from '.'
import {
isFunc,
checkAndMakeDir,
debugLog,
isFunc,
moveFile,
promiseCallback,
checkAndMakeDir,
saveBufferToFile,
} from './utilities'
@@ -34,13 +35,13 @@ const moveFromBuffer: MoveFile = (filePath, options, fileUploadOptions) => (reso
type FileFactoryOptions = {
buffer: Buffer
name: string
tempFilePath: string
hash: Buffer | string
size: number
encoding: string
truncated: boolean
hash: Buffer | string
mimetype: string
name: string
size: number
tempFilePath: string
truncated: boolean
}
type FileFactory = (
options: FileFactoryOptions,
@@ -56,12 +57,9 @@ export const fileFactory: FileFactory = (options, fileUploadOptions) => {
return {
name: options.name,
data: options.buffer,
size: options.size,
encoding: options.encoding,
tempFilePath: options.tempFilePath,
truncated: options.truncated,
mimetype: options.mimetype,
md5: options.hash,
mimetype: options.mimetype,
mv: (filePath: string, callback) => {
// Define a proper move function.
const moveFunc = fileUploadOptions.useTempFiles
@@ -73,5 +71,8 @@ export const fileFactory: FileFactory = (options, fileUploadOptions) => {
const defaultReject = () => undefined
return isFunc(callback) ? moveFunc(callback, defaultReject) : new Promise(moveFunc)
},
size: options.size,
tempFilePath: options.tempFilePath,
truncated: options.truncated,
}
}

View File

@@ -1,21 +1,22 @@
import { NextFileUploadOptions } from '.'
import crypto from 'crypto'
import fs, { WriteStream } from 'fs'
import path from 'path'
import crypto from 'crypto'
import { debugLog, checkAndMakeDir, getTempFilename, deleteFile } from './utilities'
import type { NextFileUploadOptions } from '.'
import { checkAndMakeDir, debugLog, deleteFile, getTempFilename } from './utilities'
type Handler = (
options: NextFileUploadOptions,
fieldname: string,
filename: string,
) => {
cleanup: () => void
complete: () => Buffer
dataHandler: (data: Buffer) => void
getFilePath: () => string
getFileSize: () => number
getHash: () => string
complete: () => Buffer
cleanup: () => void
getWritePromise: () => Promise<boolean>
}
@@ -41,6 +42,23 @@ export const tempFileHandler: Handler = (options, fieldname, filename) => {
})
return {
cleanup: () => {
completed = true
debugLog(options, `Cleaning up temporary file ${tempFilePath}...`)
writeStream.end()
deleteFile(tempFilePath, (err) =>
err
? debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`)
: debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`),
)
},
complete: () => {
completed = true
debugLog(options, `Upload ${fieldname}->${filename} completed, bytes:${fileSize}.`)
if (writeStream instanceof WriteStream) writeStream.end()
// Return empty buff since data was uploaded into a temp file.
return Buffer.concat([])
},
dataHandler: (data) => {
if (completed === true) {
debugLog(options, `Error: got ${fieldname}->${filename} data chunk for completed upload!`)
@@ -54,23 +72,6 @@ export const tempFileHandler: Handler = (options, fieldname, filename) => {
getFilePath: () => tempFilePath,
getFileSize: () => fileSize,
getHash: () => hash.digest('hex'),
complete: () => {
completed = true
debugLog(options, `Upload ${fieldname}->${filename} completed, bytes:${fileSize}.`)
if (writeStream instanceof WriteStream) writeStream.end()
// Return empty buff since data was uploaded into a temp file.
return Buffer.concat([])
},
cleanup: () => {
completed = true
debugLog(options, `Cleaning up temporary file ${tempFilePath}...`)
writeStream.end()
deleteFile(tempFilePath, (err) =>
err
? debugLog(options, `Cleaning up temporary file ${tempFilePath} failed: ${err}`)
: debugLog(options, `Cleaning up temporary file ${tempFilePath} done.`),
)
},
getWritePromise: () => writePromise,
}
}
@@ -84,6 +85,14 @@ export const memHandler: Handler = (options, fieldname, filename) => {
const getBuffer = () => Buffer.concat(buffers, fileSize)
return {
cleanup: () => {
completed = true
},
complete: () => {
debugLog(options, `Upload ${fieldname}->${filename} completed, bytes:${fileSize}.`)
completed = true
return getBuffer()
},
dataHandler: (data) => {
if (completed === true) {
debugLog(options, `Error: got ${fieldname}->${filename} data chunk for completed upload!`)
@@ -97,14 +106,6 @@ export const memHandler: Handler = (options, fieldname, filename) => {
getFilePath: () => '',
getFileSize: () => fileSize,
getHash: () => hash.digest('hex'),
complete: () => {
debugLog(options, `Upload ${fieldname}->${filename} completed, bytes:${fileSize}.`)
completed = true
return getBuffer()
},
cleanup: () => {
completed = true
},
getWritePromise: () => Promise.resolve(true),
}
}

View File

@@ -1,65 +1,70 @@
import path from 'path'
import type { BusboyConfig } from 'busboy'
import { processMultipart } from './processMultipart'
import path from 'path'
import { isEligibleRequest } from './isEligibleRequest'
import { processMultipart } from './processMultipart'
import { debugLog } from './utilities'
const DEFAULT_OPTIONS = {
debug: false,
uploadTimeout: 60000,
fileHandler: false,
uriDecodeFileNames: false,
safeFileNames: false,
preserveExtension: false,
abortOnLimit: false,
responseOnLimit: 'File size limit has been reached',
limitHandler: false,
createParentPath: false,
debug: false,
fileHandler: false,
limitHandler: false,
parseNested: false,
useTempFiles: false,
preserveExtension: false,
responseOnLimit: 'File size limit has been reached',
safeFileNames: false,
tempFileDir: path.join(process.cwd(), 'tmp'),
uploadTimeout: 60000,
uriDecodeFileNames: false,
useTempFiles: false,
}
export type FileShape = {
name: string
data: Buffer
size: number
encoding: string
md5: Buffer | string
mimetype: string
mv: (filePath: string, callback: () => void) => Promise<void> | void
name: string
size: number
tempFilePath: string
truncated: boolean
mimetype: string
md5: Buffer | string
mv: (filePath: string, callback: () => void) => void | Promise<void>
}
export type NextFileUploadOptions = {
/**
* Returns a HTTP 413 when the file is bigger than the size limit if `true`.
* Otherwise, it will add a `truncated = true` to the resulting file structure.
* @default false
*/
abortOnLimit?: boolean | undefined
/**
* Automatically creates the directory path specified in `.mv(filePathName)`
* @default false
*/
createParentPath?: boolean | undefined
/**
* Applies uri decoding to file names if set `true`.
* Turn on/off upload process logging. Can be useful for troubleshooting.
* @default false
*/
uriDecodeFileNames?: boolean | undefined
debug?: boolean | undefined
/**
* Strips characters from the upload's filename.
* You can use custom regex to determine what to strip.
* If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped.
* This option is off by default.
* User defined limit handler which will be invoked if the file is bigger than configured limits.
* @default false
*
* @example
* // strip slashes from file names
* app.use(fileUpload({ safeFileNames: /\\/g }))
*
* @example
* app.use(fileUpload({ safeFileNames: true }))
*/
safeFileNames?: boolean | RegExp | undefined
limitHandler?: ((args: { request: Request; size: number }) => void) | boolean | undefined
/**
* By default, `req.body` and `req.files` are flattened like this:
* `{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}
*
* When this option is enabled they are parsed in order to be nested like this:
* `{'name': 'John', 'hobbies': ['Cinema', 'Bike']}`
* @default false
*/
parseNested?: boolean | undefined
/**
* Preserves filename extension when using `safeFileNames` option.
* If set to `true`, will default to an extension length of `3`.
@@ -79,30 +84,26 @@ export type NextFileUploadOptions = {
* // myFileName.ext --> myFileNamee.xt
*/
preserveExtension?: boolean | number | undefined
/**
* Returns a HTTP 413 when the file is bigger than the size limit if `true`.
* Otherwise, it will add a `truncated = true` to the resulting file structure.
* @default false
*/
abortOnLimit?: boolean | undefined
/**
* Response which will be send to client if file size limit exceeded when `abortOnLimit` set to `true`.
* @default 'File size limit has been reached'
*/
responseOnLimit?: string | undefined
/**
* User defined limit handler which will be invoked if the file is bigger than configured limits.
* Strips characters from the upload's filename.
* You can use custom regex to determine what to strip.
* If set to `true`, non-alphanumeric characters _except_ dashes and underscores will be stripped.
* This option is off by default.
* @default false
*
* @example
* // strip slashes from file names
* app.use(fileUpload({ safeFileNames: /\\/g }))
*
* @example
* app.use(fileUpload({ safeFileNames: true }))
*/
limitHandler?: boolean | ((args: { request: Request; size: number }) => void) | undefined
/**
* By default this module uploads files into RAM.
* Setting this option to `true` turns on using temporary files instead of utilising RAM.
* This avoids memory overflow issues when uploading large files or in case of uploading
* lots of files at same time.
* @default false
*/
useTempFiles?: boolean | undefined
safeFileNames?: RegExp | boolean | undefined
/**
* Path to store temporary files.
* Used along with the `useTempFiles` option. By default this module uses `'tmp'` folder
@@ -111,25 +112,24 @@ export type NextFileUploadOptions = {
* @default './tmp'
*/
tempFileDir?: string | undefined
/**
* By default, `req.body` and `req.files` are flattened like this:
* `{'name': 'John', 'hobbies[0]': 'Cinema', 'hobbies[1]': 'Bike'}
*
* When this option is enabled they are parsed in order to be nested like this:
* `{'name': 'John', 'hobbies': ['Cinema', 'Bike']}`
* @default false
*/
parseNested?: boolean | undefined
/**
* Turn on/off upload process logging. Can be useful for troubleshooting.
* @default false
*/
debug?: boolean | undefined
/**
* This defines how long to wait for data before aborting. Set to `0` if you want to turn off timeout checks.
* @default 60_000
*/
uploadTimeout?: number | undefined
/**
* Applies uri decoding to file names if set `true`.
* @default false
*/
uriDecodeFileNames?: boolean | undefined
/**
* By default this module uploads files into RAM.
* Setting this option to `true` turns on using temporary files instead of utilising RAM.
* This avoids memory overflow issues when uploading large files or in case of uploading
* lots of files at same time.
* @default false
*/
useTempFiles?: boolean | undefined
} & Partial<BusboyConfig>
type NextFileUploadResponseFile = {
@@ -141,29 +141,29 @@ type NextFileUploadResponseFile = {
}
export type NextFileUploadResponse = {
files: Record<string, NextFileUploadResponseFile>
fields: Record<string, string>
error?: {
code: number
message: string
}
fields: Record<string, string>
files: Record<string, NextFileUploadResponseFile>
}
type NextFileUpload = (args: {
options?: NextFileUploadOptions
request: Request
}) => Promise<NextFileUploadResponse>
export const nextFileUpload: NextFileUpload = async ({ request, options }) => {
export const nextFileUpload: NextFileUpload = async ({ options, request }) => {
const uploadOptions = { ...DEFAULT_OPTIONS, ...options }
if (!isEligibleRequest(request)) {
debugLog(uploadOptions, 'Request is not eligible for file upload!')
return {
files: undefined,
fields: undefined,
error: {
code: 500,
message: 'Request is not eligible for file upload',
},
fields: undefined,
files: undefined,
}
} else {
return processMultipart({ options: uploadOptions, request })

View File

@@ -1,4 +1,4 @@
const ACCEPTABLE_CONTENT_TYPE = /^multipart\/[\w'"()+-_?/:=,.]+(?:; ?[\w'"()+-_?/:=,.]*)+$/i
const ACCEPTABLE_CONTENT_TYPE = /^multipart\/['"()+-_]+(?:; ?['"()+-_]*)+$/i
const UNACCEPTABLE_METHODS = new Set(['GET', 'HEAD', 'DELETE', 'OPTIONS', 'CONNECT', 'TRACE'])
const hasBody = (req: Request): boolean => {

View File

@@ -1,22 +1,23 @@
import Busboy from 'busboy'
import { createUploadTimer } from './uploadTimer'
import { fileFactory } from './fileFactory'
import { tempFileHandler, memHandler } from './handlers'
import { processNested } from './processNested'
import { isFunc, debugLog, buildFields, parseFileName } from './utilities'
import { NextFileUploadOptions, NextFileUploadResponse } from '.'
import { APIError } from 'payload/errors'
import type { NextFileUploadOptions, NextFileUploadResponse } from '.'
import { fileFactory } from './fileFactory'
import { memHandler, tempFileHandler } from './handlers'
import { processNested } from './processNested'
import { createUploadTimer } from './uploadTimer'
import { buildFields, debugLog, isFunc, parseFileName } from './utilities'
const waitFlushProperty = Symbol('wait flush property symbol')
type ProcessMultipart = (args: {
options: NextFileUploadOptions
request: Request
}) => Promise<NextFileUploadResponse>
export const processMultipart: ProcessMultipart = async ({ request, options }) => {
export const processMultipart: ProcessMultipart = async ({ options, request }) => {
let parsingRequest = true
let result: NextFileUploadResponse = {
const result: NextFileUploadResponse = {
fields: undefined,
files: undefined,
}
@@ -36,11 +37,11 @@ export const processMultipart: ProcessMultipart = async ({ request, options }) =
// Build req.files fields
busboy.on('file', (field, file, info) => {
// Parse file name(cutting huge names, decoding, etc..).
const { filename: name, encoding, mimeType: mime } = info
const { encoding, filename: name, mimeType: mime } = info
const filename = parseFileName(options, name)
// Define methods and handlers for upload process.
const { dataHandler, getFilePath, getFileSize, getHash, complete, cleanup, getWritePromise } =
const { cleanup, complete, dataHandler, getFilePath, getFileSize, getHash, getWritePromise } =
options.useTempFiles
? tempFileHandler(options, field, filename) // Upload into temporary file.
: memHandler(options, field, filename) // Upload into RAM.
@@ -99,14 +100,14 @@ export const processMultipart: ProcessMultipart = async ({ request, options }) =
field,
fileFactory(
{
buffer: complete(),
name: filename,
tempFilePath: getFilePath(),
hash: getHash(),
size,
buffer: complete(),
encoding,
truncated: Boolean('truncated' in file && file.truncated),
hash: getHash(),
mimetype: mime,
size,
tempFilePath: getFilePath(),
truncated: Boolean('truncated' in file && file.truncated),
},
options,
),

View File

@@ -3,7 +3,7 @@ import { isSafeFromPollution } from './utilities'
export const processNested = function (data) {
if (!data || data.length < 1) return Object.create(null)
let d = Object.create(null),
const d = Object.create(null),
keys = Object.keys(data)
for (let i = 0; i < keys.length; i++) {
@@ -13,7 +13,7 @@ export const processNested = function (data) {
keyParts = key.replace(new RegExp(/\[/g), '.').replace(new RegExp(/\]/g), '').split('.')
for (let index = 0; index < keyParts.length; index++) {
let k = keyParts[index]
const k = keyParts[index]
// Ensure we don't allow prototype pollution
if (!isSafeFromPollution(current, k)) {

View File

@@ -1,7 +1,8 @@
import fs from 'fs'
import path from 'path'
import { Readable } from 'stream'
import { NextFileUploadOptions } from '.'
import type { NextFileUploadOptions } from '.'
// Parameters for safe file name parsing.
const SAFE_FILE_NAME_REGEX = /[^\w-]/g
@@ -132,16 +133,16 @@ type CopyFile = (src: string, dst: string, callback: (err: Error) => void) => vo
const copyFile: CopyFile = (src, dst, callback) => {
// cbCalled flag and runCb helps to run cb only once.
let cbCalled = false
let runCb = (err?: Error) => {
const runCb = (err?: Error) => {
if (cbCalled) return
cbCalled = true
callback(err)
}
// Create read stream
let readable = fs.createReadStream(src)
const readable = fs.createReadStream(src)
readable.on('error', runCb)
// Create write stream
let writable = fs.createWriteStream(dst)
const writable = fs.createWriteStream(dst)
writable.on('error', (err: Error) => {
readable.destroy()
runCb(err)
@@ -182,13 +183,13 @@ export const saveBufferToFile = (buffer, filePath, callback) => {
}
// Setup readable stream from buffer.
let streamData = buffer
let readStream = new Readable()
const readStream = new Readable()
readStream._read = () => {
readStream.push(streamData)
streamData = null
}
// Setup file system writable stream.
let fstream = fs.createWriteStream(filePath)
const fstream = fs.createWriteStream(filePath)
// console.log("Calling saveBuffer");
fstream.on('error', (err) => {
// console.log("err cb")
@@ -217,7 +218,7 @@ const uriDecodeFileName = (opts, fileName) => {
try {
return decodeURIComponent(fileName)
} catch (err) {
const matcher = /(%[a-f0-9]{2})/gi
const matcher = /(%[a-f\d]{2})/gi
return fileName
.split(matcher)
.map((str) => {
@@ -238,8 +239,8 @@ type ParseFileNameExtension = (
preserveExtension: boolean | number,
fileName: string,
) => {
name: string
extension: string
name: string
}
export const parseFileNameExtension: ParseFileNameExtension = (preserveExtension, fileName) => {
const defaultResult = {

View File

@@ -3,7 +3,7 @@ import fs from 'fs'
function iteratorToStream(iterator) {
return new ReadableStream({
async pull(controller) {
const { value, done } = await iterator.next()
const { done, value } = await iterator.next()
if (done) {
controller.close()
} else {

View File

@@ -1,7 +1,7 @@
'use client'
import { Chevron } from '@payloadcms/ui'
import * as React from 'react'
import { Chevron } from '@payloadcms/ui'
import './index.scss'
const chars = {

View File

@@ -1,34 +1,35 @@
'use client'
import * as React from 'react'
import type { EditViewProps } from 'payload/config'
import {
CopyToClipboard,
Gutter,
Checkbox,
SetDocumentStepNav as SetStepNav,
CopyToClipboard,
Form,
Select,
Number as NumberInput,
useConfig,
Gutter,
MinimizeMaximize,
Number as NumberInput,
Select,
SetDocumentStepNav as SetStepNav,
useActions,
useTranslation,
useLocale,
useConfig,
useDocumentInfo,
useLocale,
useTranslation,
} from '@payloadcms/ui'
import { RenderJSON } from './RenderJSON'
import { useSearchParams } from 'next/navigation'
import qs from 'qs'
import * as React from 'react'
import { toast } from 'react-toastify'
import { RenderJSON } from './RenderJSON'
import './index.scss'
import { EditViewProps } from 'payload/config'
const baseClass = 'query-inspector'
export const APIViewClient: React.FC<EditViewProps> = (props) => {
const { collectionSlug, globalSlug } = props
const { initialData, id } = useDocumentInfo()
const { id, initialData } = useDocumentInfo()
const searchParams = useSearchParams()
const { setViewActions } = useActions()
@@ -36,11 +37,11 @@ export const APIViewClient: React.FC<EditViewProps> = (props) => {
const { code } = useLocale()
const {
collections,
globals,
localization,
routes: { api: apiRoute },
serverURL,
collections,
globals,
} = useConfig()
const collectionConfig =
@@ -78,9 +79,9 @@ export const APIViewClient: React.FC<EditViewProps> = (props) => {
const fetchURL = `${serverURL}${apiRoute}${docEndpoint}${qs.stringify(
{
locale,
draft,
depth,
draft,
locale,
},
{ addQueryPrefix: true },
)}`
@@ -89,11 +90,11 @@ export const APIViewClient: React.FC<EditViewProps> = (props) => {
const fetchData = async () => {
try {
const res = await fetch(fetchURL, {
method: 'GET',
credentials: authenticated ? 'include' : 'omit',
headers: {
'Accept-Language': i18n.language,
},
method: 'GET',
})
try {
@@ -131,12 +132,12 @@ export const APIViewClient: React.FC<EditViewProps> = (props) => {
>
<SetStepNav
collectionSlug={collectionSlug}
useAsTitle={collectionConfig ? collectionConfig?.admin?.useAsTitle : undefined}
pluralLabel={collectionConfig ? collectionConfig?.labels?.plural : undefined}
globalLabel={globalConfig?.label}
globalSlug={globalSlug}
id={id}
isEditing={isEditing}
pluralLabel={collectionConfig ? collectionConfig?.labels?.plural : undefined}
useAsTitle={collectionConfig ? collectionConfig?.admin?.useAsTitle : undefined}
view="API"
/>
<div className={`${baseClass}__configuration`}>
@@ -151,24 +152,24 @@ export const APIViewClient: React.FC<EditViewProps> = (props) => {
<Form
initialState={{
authenticated: {
value: authenticated || false,
initialValue: authenticated || false,
valid: true,
},
draft: {
value: draft || false,
initialValue: draft || false,
valid: true,
value: authenticated || false,
},
depth: {
value: Number(depth || 0),
initialValue: Number(depth || 0),
valid: true,
value: Number(depth || 0),
},
draft: {
initialValue: draft || false,
valid: true,
value: draft || false,
},
locale: {
value: locale,
initialValue: locale,
valid: true,
value: locale,
},
}}
>
@@ -176,36 +177,36 @@ export const APIViewClient: React.FC<EditViewProps> = (props) => {
<div className={`${baseClass}__filter-query-checkboxes`}>
{draftsEnabled && (
<Checkbox
name="draft"
path="draft"
label="Draft"
name="draft"
onChange={() => setDraft(!draft)}
path="draft"
/>
)}
<Checkbox
name="authenticated"
path="authenticated"
label="Authenticated"
name="authenticated"
onChange={() => setAuthenticated(!authenticated)}
path="authenticated"
/>
</div>
{localeOptions && (
<Select
label="Locale"
name="locale"
onChange={(value) => setLocale(value)}
options={localeOptions}
path="locale"
onChange={(value) => setLocale(value)}
/>
)}
<NumberInput
label="Depth"
name="depth"
path="depth"
min={0}
max={10}
step={1}
min={0}
name="depth"
onChange={(value) => setDepth(value.toString())}
path="depth"
step={1}
/>
</div>
</Form>

View File

@@ -1,7 +1,9 @@
import React from 'react'
import { ServerSideEditViewProps } from '../../../../ui/src/views/types'
import { APIViewClient } from './index.client'
import type { ServerSideEditViewProps } from '../../../../ui/src/views/types'
import { sanitizedEditViewProps } from '../Edit/sanitizedEditViewProps'
import { APIViewClient } from './index.client'
export const APIView: React.FC<ServerSideEditViewProps> = async (props) => {
const clientSideProps = sanitizedEditViewProps(props)

View File

@@ -1,8 +1,8 @@
'use client'
import React from 'react'
import { Label, ReactSelect } from '@payloadcms/ui'
import { useTranslation } from '@payloadcms/ui/providers'
import React from 'react'
import { ReactSelect, Label } from '@payloadcms/ui'
import { ToggleTheme } from '../ToggleTheme'
import './index.scss'
@@ -13,7 +13,7 @@ export const Settings: React.FC<{
}> = (props) => {
const { className } = props
const { i18n, t, languageOptions } = useTranslation()
const { i18n, languageOptions, t } = useTranslation()
return (
<div className={[baseClass, className].filter(Boolean).join(' ')}>

View File

@@ -1,9 +1,9 @@
'use client'
import React, { useCallback } from 'react'
import { useTranslation } from '@payloadcms/ui'
import type { OnChange, Theme } from '@payloadcms/ui'
import { useTheme, RadioGroupInput } from '@payloadcms/ui'
import { useTranslation } from '@payloadcms/ui'
import { RadioGroupInput, useTheme } from '@payloadcms/ui'
import React, { useCallback } from 'react'
export const ToggleTheme: React.FC = () => {
const {

View File

@@ -1,15 +1,17 @@
import { Data, DocumentPreferences, SanitizedConfig } from 'payload/types'
import React, { Fragment } from 'react'
import type { ServerSideEditViewProps } from '@payloadcms/ui'
import type { Data, DocumentPreferences, SanitizedConfig } from 'payload/types'
import {
RenderCustomComponent,
HydrateClientUser,
RenderCustomComponent,
SetDocumentInfo,
buildStateFromSchema,
formatFields,
ServerSideEditViewProps,
SetDocumentInfo,
} from '@payloadcms/ui'
import { initPage } from '../../utilities/initPage'
import { notFound } from 'next/navigation'
import React, { Fragment } from 'react'
import { initPage } from '../../utilities/initPage'
import { EditView } from '../Edit'
import { Settings } from './Settings'
@@ -20,15 +22,15 @@ export const Account = async ({
config: Promise<SanitizedConfig>
searchParams: { [key: string]: string | string[] | undefined }
}) => {
const { config, payload, permissions, user, i18n, locale } = await initPage({
const { config, i18n, locale, payload, permissions, user } = await initPage({
config: configPromise,
redirectUnauthenticatedUser: true,
searchParams,
route: `/account`,
searchParams,
})
const {
admin: { user: userSlug, components: { views: { Account: CustomAccountComponent } = {} } = {} },
admin: { components: { views: { Account: CustomAccountComponent } = {} } = {}, user: userSlug },
routes: { api },
serverURL,
} = config
@@ -44,8 +46,8 @@ export const Account = async ({
try {
data = await payload.findByID({
collection: userSlug,
id: user.id,
collection: userSlug,
depth: 0,
user,
})
@@ -64,12 +66,12 @@ export const Account = async ({
const { docs: [{ value: docPreferences } = { value: null }] = [] } = (await payload.find({
collection: 'payload-preferences',
depth: 0,
limit: 1,
where: {
key: {
equals: preferencesKey,
},
},
limit: 1,
})) as any as { docs: { value: DocumentPreferences }[] }
const initialState = await buildStateFromSchema({
@@ -84,38 +86,38 @@ export const Account = async ({
})
const componentProps: ServerSideEditViewProps = {
id: user?.id,
action: `${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale}`,
apiURL: `${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale}`,
collectionSlug: userSlug,
config,
data,
hasSavePermission: collectionPermissions?.update?.permission,
initialState,
docPermissions: collectionPermissions,
docPreferences,
user,
updatedAt: '', // TODO
id: user?.id,
config,
hasSavePermission: collectionPermissions?.update?.permission,
i18n,
initialState,
payload,
permissions,
searchParams,
updatedAt: '', // TODO
user,
}
return (
<Fragment>
<HydrateClientUser user={user} permissions={permissions} />
<HydrateClientUser permissions={permissions} user={user} />
<SetDocumentInfo
AfterFields={<Settings />}
action={`${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale}`}
apiURL={`${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale}`}
collectionSlug={userSlug}
initialData={data}
hasSavePermission={collectionPermissions?.update?.permission}
initialState={initialState}
docPermissions={collectionPermissions}
docPreferences={docPreferences}
hasSavePermission={collectionPermissions?.update?.permission}
id={user?.id}
AfterFields={<Settings />}
initialData={data}
initialState={initialState}
/>
<RenderCustomComponent
CustomComponent={

View File

@@ -1,7 +1,6 @@
'use client'
import React from 'react'
import { RenderFields, useComponentMap } from '@payloadcms/ui'
import React from 'react'
export const CreateFirstUserFields: React.FC<{
userSlug: string

View File

@@ -1,14 +1,14 @@
import type { Metadata } from 'next'
import type { Field } from 'payload/types'
import type { SanitizedConfig } from 'payload/types'
import { Form, FormSubmit, MinimalTemplate, buildStateFromSchema } from '@payloadcms/ui'
import React from 'react'
import type { Field } from 'payload/types'
import { getNextT } from '../../utilities/getNextT'
import { Form, FormSubmit, MinimalTemplate, buildStateFromSchema } from '@payloadcms/ui'
import { SanitizedConfig } from 'payload/types'
import { Metadata } from 'next'
import { meta } from '../../utilities/meta'
import { initPage } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import { CreateFirstUserFields } from './index.client'
import './index.scss'
const baseClass = 'create-first-user'
@@ -23,10 +23,10 @@ export const generateMetadata = async ({
})
return meta({
title: t('authentication:createFirstUser'),
config,
description: t('authentication:createFirstUser'),
keywords: t('general:create'),
config,
title: t('authentication:createFirstUser'),
})
}
@@ -35,17 +35,17 @@ export const CreateFirstUser: React.FC<{
}> = async ({ config: configPromise }) => {
const {
config,
user,
locale,
i18n: { t },
locale,
user,
} = await initPage({
config: configPromise,
redirectUnauthenticatedUser: false,
})
const {
routes: { api: apiRoute, admin: adminRoute },
admin: { user: userSlug },
routes: { admin: adminRoute, api: apiRoute },
serverURL,
} = config
@@ -85,11 +85,11 @@ export const CreateFirstUser: React.FC<{
<p>{t('authentication:beginCreateFirstUser')}</p>
<Form
action={`${serverURL}${apiRoute}/${userSlug}/first-register`}
initialState={formState}
method="POST"
// onSuccess={onSuccess}
redirect={adminRoute}
validationOperation="create"
initialState={formState}
>
<CreateFirstUserFields userSlug={userSlug} />
<FormSubmit>{t('general:create')}</FormSubmit>

View File

@@ -1,18 +1,18 @@
'use client'
import React, { Fragment, useEffect, useState } from 'react'
import type { EntityToGroup, Group } from '@payloadcms/ui'
import { getTranslation } from '@payloadcms/translations'
import {
EntityType,
groupNavItems,
Button,
Card,
EntityType,
groupNavItems,
useActions,
useAuth,
useConfig,
useActions,
useTranslation,
} from '@payloadcms/ui'
import { getTranslation } from '@payloadcms/translations'
import React, { Fragment, useEffect, useState } from 'react'
import './index.scss'
@@ -119,9 +119,11 @@ export const DefaultDashboardClient: React.FC<{
return (
<li key={entityIndex}>
<Card
Link={Link}
actions={
hasCreatePermission && type === EntityType.collection ? (
<Button
Link={Link}
aria-label={t('general:createNewLabel', {
label: getTranslation(entity.labels.singular, i18n),
})}
@@ -131,16 +133,14 @@ export const DefaultDashboardClient: React.FC<{
iconStyle="with-border"
round
to={createHREF}
Link={Link}
/>
) : undefined
}
buttonAriaLabel={buttonAriaLabel}
href={href}
id={`card-${entity.slug}`}
title={title}
titleAs="h3"
Link={Link}
href={href}
/>
</li>
)

View File

@@ -1,24 +1,24 @@
import React from 'react'
import type { SanitizedConfig } from 'payload/types'
import { Gutter, SetStepNav } from '@payloadcms/ui'
import { DefaultDashboardClient } from './index.client'
import React from 'react'
import { DefaultDashboardClient } from './index.client'
import './index.scss'
const baseClass = 'dashboard'
export const DefaultDashboard: React.FC<{
config: SanitizedConfig
Link: React.ComponentType<any>
config: SanitizedConfig
}> = (props) => {
const {
Link,
config: {
admin: {
components: { afterDashboard, beforeDashboard },
},
},
Link,
} = props
return (

View File

@@ -1,7 +1,9 @@
import { SanitizedConfig } from 'payload/types'
import React, { Fragment } from 'react'
import { RenderCustomComponent, HydrateClientUser } from '@payloadcms/ui'
import type { SanitizedConfig } from 'payload/types'
import { HydrateClientUser, RenderCustomComponent } from '@payloadcms/ui'
import Link from 'next/link'
import React, { Fragment } from 'react'
import { initPage } from '../../utilities/initPage'
import { DefaultDashboard } from './Default'
@@ -12,7 +14,7 @@ export const Dashboard = async ({
config: Promise<SanitizedConfig>
searchParams: { [key: string]: string | string[] | undefined }
}) => {
const { config, user, permissions } = await initPage({
const { config, permissions, user } = await initPage({
config: configPromise,
redirectUnauthenticatedUser: true,
route: '',
@@ -23,15 +25,15 @@ export const Dashboard = async ({
return (
<Fragment>
<HydrateClientUser user={user} permissions={permissions} />
<HydrateClientUser permissions={permissions} user={user} />
<RenderCustomComponent
CustomComponent={
typeof CustomDashboardComponent === 'function' ? CustomDashboardComponent : undefined
}
DefaultComponent={DefaultDashboard}
componentProps={{
config,
Link,
config,
user,
}}
/>

View File

@@ -1,5 +1,5 @@
import { AdminViewComponent } from 'payload/config'
import { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
import type { AdminViewComponent } from 'payload/config'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
export const getCustomViewByKey = (
views:
@@ -10,13 +10,13 @@ export const getCustomViewByKey = (
return typeof views?.Edit === 'function'
? views?.Edit
: typeof views?.Edit === 'object' &&
views?.Edit?.[customViewKey] &&
typeof views?.Edit?.[customViewKey] === 'function'
? views?.Edit?.[customViewKey]
: views?.Edit?.[customViewKey]
? typeof views?.Edit?.[customViewKey] === 'object' &&
'Component' in views?.Edit?.[customViewKey] &&
typeof views?.Edit?.[customViewKey].Component === 'function' &&
views?.Edit?.[customViewKey].Component
: null
views?.Edit?.[customViewKey] &&
typeof views?.Edit?.[customViewKey] === 'function'
? views?.Edit?.[customViewKey]
: views?.Edit?.[customViewKey]
? typeof views?.Edit?.[customViewKey] === 'object' &&
'Component' in views?.Edit?.[customViewKey] &&
typeof views?.Edit?.[customViewKey].Component === 'function' &&
views?.Edit?.[customViewKey].Component
: null
}

View File

@@ -1,5 +1,5 @@
import { AdminViewComponent } from 'payload/config'
import { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
import type { AdminViewComponent } from 'payload/config'
import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload/types'
export const getCustomViewByPath = (
views:

View File

@@ -1,22 +1,28 @@
import { AdminViewComponent } from 'payload/config'
import { SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig } from 'payload/types'
import type { CollectionPermission, GlobalPermission } from 'payload/auth'
import type { AdminViewComponent } from 'payload/config'
import type {
SanitizedCollectionConfig,
SanitizedConfig,
SanitizedGlobalConfig,
} from 'payload/types'
import { lazy } from 'react'
import { getCustomViewByKey } from './getCustomViewByKey'
import { CollectionPermission, GlobalPermission } from 'payload/auth'
import { getCustomViewByPath } from './getCustomViewByPath'
export const getViewsFromConfig = async ({
routeSegments,
docPermissions,
config,
collectionConfig,
config,
docPermissions,
globalConfig,
routeSegments,
}: {
routeSegments: string[]
config: SanitizedConfig
collectionConfig?: SanitizedCollectionConfig
globalConfig?: SanitizedGlobalConfig
config: SanitizedConfig
docPermissions: CollectionPermission | GlobalPermission
globalConfig?: SanitizedGlobalConfig
routeSegments: string[]
}): Promise<{
CustomView: AdminViewComponent
DefaultView: AdminViewComponent

View File

@@ -1,39 +1,42 @@
import type { QueryParamTypes } from '@payloadcms/ui'
import type { AdminViewComponent } from 'payload/config'
import type {
DocumentPreferences,
Document as DocumentType,
Field,
SanitizedConfig,
} from 'payload/types'
import React, { Fragment } from 'react'
import { initPage } from '../../utilities/initPage'
import type { DocumentPermissions } from 'payload/types'
import {
EditDepthProvider,
FormQueryParamsProvider,
HydrateClientUser,
RenderCustomComponent,
SetDocumentInfo,
buildStateFromSchema,
formatFields,
FormQueryParamsProvider,
QueryParamTypes,
HydrateClientUser,
SetDocumentInfo,
} from '@payloadcms/ui'
import queryString from 'qs'
import { notFound } from 'next/navigation'
import { AdminViewComponent } from 'payload/config'
import queryString from 'qs'
import React, { Fragment } from 'react'
import type { ServerSideEditViewProps } from '../../../../ui/src/views/types'
import { initPage } from '../../utilities/initPage'
import { getViewsFromConfig } from './getViewsFromConfig'
import type { DocumentPermissions } from 'payload/types'
import { ServerSideEditViewProps } from '../../../../ui/src/views/types'
export const Document = async ({
params,
config: configPromise,
params,
searchParams,
}: {
config: Promise<SanitizedConfig> | SanitizedConfig
params: {
segments: string[]
collection?: string
global?: string
segments: string[]
}
config: Promise<SanitizedConfig> | SanitizedConfig
searchParams: { [key: string]: string | string[] | undefined }
}) => {
const collectionSlug = params.collection
@@ -45,14 +48,14 @@ export const Document = async ({
const route = `/${collectionSlug || globalSlug + (params.segments?.length ? `/${params.segments.join('/')}` : '')}`
const { config, payload, permissions, user, collectionConfig, globalConfig, locale, i18n } =
const { collectionConfig, config, globalConfig, i18n, locale, payload, permissions, user } =
await initPage({
config: configPromise,
redirectUnauthenticatedUser: true,
collectionSlug,
config: configPromise,
globalSlug,
searchParams,
redirectUnauthenticatedUser: true,
route,
searchParams,
})
if (!collectionConfig && !globalConfig) {
@@ -88,10 +91,10 @@ export const Document = async ({
}`
const collectionViews = await getViewsFromConfig({
routeSegments: params.segments,
collectionConfig,
config,
docPermissions,
routeSegments: params.segments,
})
CustomView = collectionViews?.CustomView
@@ -99,11 +102,11 @@ export const Document = async ({
try {
data = await payload.findByID({
collection: collectionSlug,
id,
collection: collectionSlug,
depth: 0,
user,
locale: locale.code,
user,
})
} catch (error) {}
@@ -123,20 +126,20 @@ export const Document = async ({
}`
const globalViews = await getViewsFromConfig({
routeSegments: params.segments,
globalConfig,
config,
docPermissions,
globalConfig,
routeSegments: params.segments,
})
CustomView = globalViews?.CustomView
DefaultView = globalViews?.DefaultView
data = await payload.findGlobal({
slug: globalSlug,
depth: 0,
user,
locale: locale.code,
slug: globalSlug,
user,
})
preferencesKey = `global-${globalSlug}`
@@ -145,12 +148,12 @@ export const Document = async ({
const { docs: [{ value: docPreferences } = { value: null }] = [] } = (await payload.find({
collection: 'payload-preferences',
depth: 0,
limit: 1,
where: {
key: {
equals: preferencesKey,
},
},
limit: 1,
})) as any as { docs: { value: DocumentPreferences }[] }
const initialState = await buildStateFromSchema({
@@ -176,24 +179,24 @@ export const Document = async ({
action: `${action}?${queryString.stringify(formQueryParams)}`,
apiURL,
canAccessAdmin: permissions?.canAccessAdmin,
collectionConfig,
collectionSlug,
globalSlug,
config,
data,
hasSavePermission,
isEditing,
docPermissions,
docPreferences,
updatedAt: data?.updatedAt?.toString(),
payload,
config,
searchParams,
i18n,
collectionConfig,
globalConfig,
params,
permissions,
user,
globalSlug,
hasSavePermission,
i18n,
initialState,
isEditing,
params,
payload,
permissions,
searchParams,
updatedAt: data?.updatedAt?.toString(),
user,
}
if (!DefaultView && !CustomView) {
@@ -202,19 +205,19 @@ export const Document = async ({
return (
<Fragment>
<HydrateClientUser user={user} permissions={permissions} />
<HydrateClientUser permissions={permissions} user={user} />
<SetDocumentInfo
action={action}
apiURL={apiURL}
collectionSlug={collectionConfig?.slug}
disableActions={false}
docPermissions={docPermissions}
docPreferences={docPreferences}
globalSlug={globalConfig?.slug}
hasSavePermission={hasSavePermission}
id={id}
initialData={data}
initialState={initialState}
docPermissions={docPermissions}
docPreferences={docPreferences}
apiURL={apiURL}
action={action}
hasSavePermission={hasSavePermission}
disableActions={false}
/>
<EditDepthProvider depth={1} key={`${collectionSlug || globalSlug}-${locale}`}>
<FormQueryParamsProvider formQueryParams={formQueryParams}>

View File

@@ -1,11 +1,12 @@
'use client'
import React, { Fragment } from 'react'
import { useComponentMap, useDocumentInfo } from '@payloadcms/ui'
import React, { Fragment } from 'react'
import { useCallback } from 'react'
import { LoadingOverlay } from '../../../../ui/src/elements/Loading'
export const DefaultEditViewClient: React.FC = () => {
const { id, getVersions, getDocPermissions, collectionSlug, globalSlug } = useDocumentInfo()
const { id, collectionSlug, getDocPermissions, getVersions, globalSlug } = useDocumentInfo()
const { componentMap } = useComponentMap()

View File

@@ -1,5 +1,7 @@
import React from 'react'
import { ServerSideEditViewProps } from '../../../../ui/src/views/types'
import type { ServerSideEditViewProps } from '../../../../ui/src/views/types'
import { DefaultEditViewClient } from './index.client'
export const EditView: React.FC<ServerSideEditViewProps> = async () => {

View File

@@ -1,5 +1,5 @@
import { ServerSideEditViewProps } from '@payloadcms/ui'
import { EditViewProps } from 'payload/config'
import type { ServerSideEditViewProps } from '@payloadcms/ui'
import type { EditViewProps } from 'payload/config'
export const sanitizedEditViewProps = (props: ServerSideEditViewProps) => {
const clientSideProps = { ...props }

View File

@@ -1,12 +1,13 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { Button, Email, Form, FormSubmit, MinimalTemplate, Translation } from '@payloadcms/ui'
import Link from 'next/link'
import React from 'react'
import { Button, Form, FormSubmit, Email, MinimalTemplate, Translation } from '@payloadcms/ui'
import { SanitizedConfig } from 'payload/types'
import Link from 'next/link'
import { getNextT } from '../../utilities/getNextT'
import { initPage } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import { Metadata } from 'next'
import { getNextT } from '../../utilities/getNextT'
const baseClass = 'forgot-password'
@@ -20,17 +21,17 @@ export const generateMetadata = async ({
})
return meta({
title: t('authentication:forgotPassword'),
config,
description: t('authentication:forgotPassword'),
keywords: t('authentication:forgotPassword'),
config,
title: t('authentication:forgotPassword'),
})
}
export const ForgotPassword: React.FC<{
config: Promise<SanitizedConfig>
}> = async ({ config: configPromise }) => {
const { config, user, i18n } = await initPage({ config: configPromise })
const { config, i18n, user } = await initPage({ config: configPromise })
const {
admin: { user: userSlug },
@@ -55,11 +56,11 @@ export const ForgotPassword: React.FC<{
<h1>{i18n.t('authentication:alreadyLoggedIn')}</h1>
<p>
<Translation
t={i18n.t}
i18nKey="authentication:loggedInChangePassword"
elements={{
'0': ({ children }) => <Link href={`${admin}/account`} children={children} />,
'0': ({ children }) => <Link children={children} href={`${admin}/account`} />,
}}
i18nKey="authentication:loggedInChangePassword"
t={i18n.t}
/>
</p>
<br />

View File

@@ -1,32 +1,35 @@
import { SanitizedConfig } from 'payload/types'
import React, { Fragment } from 'react'
import type { DefaultListViewProps } from '@payloadcms/ui'
import type { SanitizedConfig } from 'payload/types'
import {
RenderCustomComponent,
DefaultList,
HydrateClientUser,
DefaultListViewProps,
RenderCustomComponent,
TableColumnsProvider,
} from '@payloadcms/ui'
import { initPage } from '../../utilities/initPage'
import { notFound } from 'next/navigation'
import { ListPreferences } from '../../../../ui/src/views/List/types'
import React, { Fragment } from 'react'
import type { ListPreferences } from '../../../../ui/src/views/List/types'
import { ListInfoProvider } from '../../../../ui/src/providers/ListInfo'
import { initPage } from '../../utilities/initPage'
export const ListView = async ({
collectionSlug,
config: configPromise,
searchParams,
route,
searchParams,
}: {
collectionSlug: string
config: Promise<SanitizedConfig>
searchParams: { [key: string]: string | string[] | undefined }
route
searchParams: { [key: string]: string | string[] | undefined }
}) => {
const { config, payload, permissions, user, collectionConfig } = await initPage({
const { collectionConfig, config, payload, permissions, user } = await initPage({
collectionSlug,
config: configPromise,
redirectUnauthenticatedUser: true,
collectionSlug,
route,
searchParams,
})
@@ -37,13 +40,13 @@ export const ListView = async ({
listPreferences = (await payload
.find({
collection: 'payload-preferences',
depth: 0,
limit: 1,
where: {
key: {
equals: `${collectionSlug}-list`,
},
},
limit: 1,
depth: 0,
})
?.then((res) => res?.docs?.[0]?.value)) as unknown as ListPreferences
} catch (error) {}
@@ -80,13 +83,13 @@ export const ListView = async ({
return (
<Fragment>
<HydrateClientUser user={user} permissions={permissions} />
<HydrateClientUser permissions={permissions} user={user} />
<ListInfoProvider
collectionSlug={collectionSlug}
data={data}
hasCreatePermission={permissions?.collections?.[collectionSlug]?.create?.permission}
limit={limit}
newDocumentURL={`${admin}/collections/${collectionSlug}/create`}
collectionSlug={collectionSlug}
>
<TableColumnsProvider collectionSlug={collectionSlug} listPreferences={listPreferences}>
<RenderCustomComponent

View File

@@ -24,23 +24,24 @@ export const ToolbarControls: React.FC<EditViewProps> = () => {
<div className={baseClass}>
{breakpoints?.length > 0 && (
<Popup
className={`${baseClass}__breakpoint`}
button={
<>
<React.Fragment>
<span>
{breakpoints.find((bp) => bp.name == breakpoint)?.label ?? customOption.label}
</span>
&nbsp;
<Chevron className={`${baseClass}__chevron`} />
</>
</React.Fragment>
}
className={`${baseClass}__breakpoint`}
horizontalAlign="right"
render={({ close }) => (
<PopupList.ButtonGroup>
<React.Fragment>
{breakpoints.map((bp) => (
<PopupList.Button
key={bp.name}
active={bp.name == breakpoint}
key={bp.name}
onClick={() => {
setBreakpoint(bp.name)
close()
@@ -66,7 +67,6 @@ export const ToolbarControls: React.FC<EditViewProps> = () => {
)}
showScrollbar
verticalAlign="bottom"
horizontalAlign="right"
/>
)}
<div className={`${baseClass}__device-size`}>
@@ -77,21 +77,22 @@ export const ToolbarControls: React.FC<EditViewProps> = () => {
<PreviewFrameSizeInput axis="y" />
</div>
<Popup
className={`${baseClass}__zoom`}
button={
<>
<React.Fragment>
<span>{zoom * 100}%</span>
&nbsp;
<Chevron className={`${baseClass}__chevron`} />
</>
</React.Fragment>
}
className={`${baseClass}__zoom`}
horizontalAlign="right"
render={({ close }) => (
<PopupList.ButtonGroup>
<React.Fragment>
{zoomOptions.map((zoomValue) => (
<PopupList.Button
key={zoomValue}
active={zoom * 100 == zoomValue}
key={zoomValue}
onClick={() => {
setZoom(zoomValue / 100)
close()
@@ -105,7 +106,6 @@ export const ToolbarControls: React.FC<EditViewProps> = () => {
)}
showScrollbar
verticalAlign="bottom"
horizontalAlign="right"
/>
<a
className={`${baseClass}__external`}

View File

@@ -1,6 +1,6 @@
import { getTranslation } from '@payloadcms/translations'
import React, { Fragment, useEffect } from 'react'
import { useTranslation } from '@payloadcms/ui'
import React, { Fragment, useEffect } from 'react'
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
import type { LivePreviewConfig } from '../../../../exports/config'
@@ -124,8 +124,8 @@ const PreviewView: React.FC<
fields={fields}
forceSidebarWrap
hasSavePermission={hasSavePermission}
permissions={permissions}
i18n={i18n}
permissions={permissions}
/>
</div>
<LivePreview {...props} />

View File

@@ -1,16 +1,17 @@
'use client'
import React from 'react'
import type { FormState } from '@payloadcms/ui'
import {
Email,
Form,
FormLoadingOverlayToggle,
FormState,
FormSubmit,
Password,
useConfig,
useTranslation,
} from '@payloadcms/ui'
import Link from 'next/link'
import React from 'react'
const baseClass = 'login__form'
@@ -33,13 +34,13 @@ export const LoginForm: React.FC<{
const initialState: FormState = {
email: {
initialValue: prefillForm ? autoLogin.email : undefined,
value: prefillForm ? autoLogin.email : undefined,
valid: true,
value: prefillForm ? autoLogin.email : undefined,
},
password: {
initialValue: prefillForm ? autoLogin.password : undefined,
value: prefillForm ? autoLogin.password : undefined,
valid: true,
value: prefillForm ? autoLogin.password : undefined,
},
}
@@ -49,9 +50,9 @@ export const LoginForm: React.FC<{
className={`${baseClass}__form`}
disableSuccessStatus
initialState={initialState}
method="POST"
redirect={`${admin}${searchParams?.redirect || ''}`}
waitForAutocomplete
method="POST"
>
<FormLoadingOverlayToggle action="loading" name="login-form" />
<div className={`${baseClass}__inputWrap`}>

View File

@@ -1,15 +1,15 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { MinimalTemplate } from '@payloadcms/ui'
import { Logo } from '@payloadcms/ui/graphics'
import { redirect } from 'next/navigation'
import React, { Fragment } from 'react'
import { Logo } from '@payloadcms/ui/graphics'
import { MinimalTemplate } from '@payloadcms/ui'
import type { SanitizedConfig } from 'payload/types'
import { meta } from '../../utilities/meta'
import { Metadata } from 'next'
import { initPage } from '../../utilities/initPage'
import { redirect } from 'next/navigation'
import { getNextT } from '../../utilities/getNextT'
import { initPage } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import { LoginForm } from './LoginForm'
import './index.scss'
const baseClass = 'login'
@@ -24,10 +24,10 @@ export const generateMetadata = async ({
})
return meta({
title: t('authentication:login'),
config,
description: `${t('authentication:login')}`,
keywords: `${t('authentication:login')}`,
config,
title: t('authentication:login'),
})
}
@@ -35,12 +35,12 @@ export const Login: React.FC<{
config: Promise<SanitizedConfig>
searchParams: { [key: string]: string | string[] | undefined }
}> = async ({ config: configPromise, searchParams }) => {
const { config, user } = await initPage({ config: configPromise, searchParams, route: '/login' })
const { config, user } = await initPage({ config: configPromise, route: '/login', searchParams })
const {
admin: { components: { afterLogin, beforeLogin } = {}, user: userSlug },
routes: { admin },
collections,
routes: { admin },
} = config
if (user) {

View File

@@ -1,15 +1,16 @@
'use client'
import React, { Fragment, useEffect } from 'react'
import { useAuth } from '../../../../ui/src/providers/Auth'
import { Button, useTranslation } from '@payloadcms/ui'
import Link from 'next/link'
import React, { Fragment, useEffect } from 'react'
import { useAuth } from '../../../../ui/src/providers/Auth'
export const LogoutClient: React.FC<{
inactivity?: boolean
adminRoute: string
inactivity?: boolean
redirect: string
}> = (props) => {
const { inactivity, adminRoute, redirect } = props
const { adminRoute, inactivity, redirect } = props
const [isLoggingOut, setIsLoggingOut] = React.useState<boolean | undefined>(undefined)
const { logOut } = useAuth()
@@ -28,12 +29,12 @@ export const LogoutClient: React.FC<{
{inactivity && <h2>{t('authentication:loggedOutInactivity')}</h2>}
{!inactivity && <h2>{t('authentication:loggedOutSuccessfully')}</h2>}
<Button
Link={Link}
buttonStyle="secondary"
el="link"
url={`${adminRoute}/login${
redirect && redirect.length > 0 ? `?redirect=${encodeURIComponent(redirect)}` : ''
}`}
Link={Link}
>
{t('authentication:logBackIn')}
</Button>

View File

@@ -1,12 +1,12 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { Button, MinimalTemplate } from '@payloadcms/ui'
import React from 'react'
import { MinimalTemplate, Button } from '@payloadcms/ui'
import { meta } from '../../utilities/meta'
import { Metadata } from 'next'
import { SanitizedConfig } from 'payload/types'
import { LogoutClient } from './LogoutClient'
import { getNextT } from '../../utilities/getNextT'
import { meta } from '../../utilities/meta'
import { LogoutClient } from './LogoutClient'
import './index.scss'
const baseClass = 'logout'
@@ -21,18 +21,18 @@ export const generateMetadata = async ({
})
return meta({
title: t('authentication:logout'),
config,
description: `${t('authentication:logoutUser')}`,
keywords: `${t('authentication:logout')}`,
config,
title: t('authentication:logout'),
})
}
export const Logout: React.FC<{
inactivity?: boolean
config: Promise<SanitizedConfig>
searchParams: { [key: string]: string[] | string }
}> = async ({ searchParams, config: configPromise, inactivity }) => {
inactivity?: boolean
searchParams: { [key: string]: string | string[] }
}> = async ({ config: configPromise, inactivity, searchParams }) => {
const config = await configPromise
const {
@@ -43,8 +43,8 @@ export const Logout: React.FC<{
<MinimalTemplate className={baseClass}>
<div className={`${baseClass}__wrap`}>
<LogoutClient
inactivity={inactivity}
adminRoute={admin}
inactivity={inactivity}
redirect={searchParams.redirect as string}
/>
</div>

View File

@@ -1,7 +1,7 @@
'use client'
import { Button, Gutter, useConfig, useStepNav, useTranslation } from '@payloadcms/ui'
import React from 'react'
import { Button, Gutter, useStepNav, useConfig, useTranslation } from '@payloadcms/ui'
// import Meta from '../../utilities/Meta'
import './index.scss'

View File

@@ -1,22 +1,22 @@
import React from 'react'
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import {
MinimalTemplate,
Button,
ConfirmPassword,
Form,
FormSubmit,
ConfirmPassword,
HiddenInput,
MinimalTemplate,
Password,
Translation,
} from '@payloadcms/ui'
import { SanitizedConfig } from 'payload/types'
import Link from 'next/link'
import { initPage } from '../../utilities/initPage'
import { Metadata } from 'next'
import { meta } from '../../utilities/meta'
import { getNextT } from '../../utilities/getNextT'
import React from 'react'
import { getNextT } from '../../utilities/getNextT'
import { initPage } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import './index.scss'
const baseClass = 'reset-password'
@@ -31,10 +31,10 @@ export const generateMetadata = async ({
})
return meta({
title: t('authentication:resetPassword'),
config,
description: t('authentication:resetPassword'),
keywords: t('authentication:resetPassword'),
config,
title: t('authentication:resetPassword'),
})
}
@@ -42,7 +42,7 @@ export const ResetPassword: React.FC<{
config: Promise<SanitizedConfig>
token: string
}> = async ({ config: configPromise, token }) => {
const { config, user, i18n } = await initPage({ config: configPromise })
const { config, i18n, user } = await initPage({ config: configPromise })
const {
admin: { logoutRoute, user: userSlug },
@@ -67,11 +67,11 @@ export const ResetPassword: React.FC<{
<h1>{i18n.t('authentication:alreadyLoggedIn')}</h1>
<p>
<Translation
t={i18n.t}
i18nKey="authentication:loggedInChangePassword"
elements={{
'0': ({ children }) => <Link href={`${admin}/account`} children={children} />,
'0': ({ children }) => <Link children={children} href={`${admin}/account`} />,
}}
i18nKey="authentication:loggedInChangePassword"
t={i18n.t}
/>
</p>
<br />

View File

@@ -1,5 +1,6 @@
import type { SanitizedConfig } from 'payload/types'
import { redirect } from 'next/navigation'
import { SanitizedConfig } from 'payload/types'
export const RootPage = async ({ config: configPromise }: { config: Promise<SanitizedConfig> }) => {
const config = await configPromise

View File

@@ -1,18 +1,18 @@
'use client'
import React from 'react'
import { Button, useTranslation } from '@payloadcms/ui'
import React from 'react'
export const UnauthorizedClient: React.FC<{ logoutRoute: string }> = ({ logoutRoute }) => {
const { t } = useTranslation()
return (
<>
<React.Fragment>
<h2>{t('error:unauthorized')}</h2>
<p>{t('error:notAllowedToAccessPage')}</p>
<br />
<Button el="link" to={logoutRoute}>
{t('authentication:logOut')}
</Button>
</>
</React.Fragment>
)
}

View File

@@ -1,10 +1,11 @@
import React from 'react'
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { MinimalTemplate } from '@payloadcms/ui'
import { SanitizedConfig } from 'payload/types'
import { meta } from '../../utilities/meta'
import { Metadata } from 'next'
import React from 'react'
import { getNextT } from '../../utilities/getNextT'
import { meta } from '../../utilities/meta'
import { UnauthorizedClient } from './UnauthorizedClient'
export const generateMetadata = async ({
@@ -17,10 +18,10 @@ export const generateMetadata = async ({
})
return meta({
title: t('error:unauthorized'),
config,
description: t('error:unauthorized'),
keywords: t('error:unauthorized'),
config,
title: t('error:unauthorized'),
})
}

View File

@@ -1,13 +1,13 @@
import type { Metadata } from 'next'
import type { SanitizedConfig } from 'payload/types'
import { Button, Logo, MinimalTemplate } from '@payloadcms/ui'
import { redirect } from 'next/navigation'
import React from 'react'
import { Button, MinimalTemplate, Logo } from '@payloadcms/ui'
import { initPage } from '../../utilities/initPage'
import { SanitizedConfig } from 'payload/types'
import { redirect } from 'next/navigation'
import { Metadata } from 'next'
import { meta } from '../../utilities/meta'
import { getNextT } from '../../utilities/getNextT'
import { initPage } from '../../utilities/initPage'
import { meta } from '../../utilities/meta'
import './index.scss'
const baseClass = 'verify'
@@ -22,10 +22,10 @@ export const generateMetadata = async ({
})
return meta({
config,
description: t('authentication:verifyUser'),
keywords: t('authentication:verify'),
title: t('authentication:verify'),
config,
})
}
@@ -36,7 +36,7 @@ export const Verify: React.FC<{
config: configPromise,
// token
}) => {
const { config, user, i18n } = await initPage({ config: configPromise })
const { config, i18n, user } = await initPage({ config: configPromise })
const {
admin: { user: userSlug },
@@ -44,7 +44,7 @@ export const Verify: React.FC<{
// serverURL,
} = config
let verifyResult = null
const verifyResult = null
// const [verifyResult, setVerifyResult] = useState<Response | null>(null)
// useEffect(() => {

View File

@@ -1,25 +1,20 @@
import type { FieldMap, StepNavItem } from '@payloadcms/ui'
import type { FieldAffectingData, SanitizedCollectionConfig } from 'payload/types'
import type React from 'react'
import { getTranslation } from '@payloadcms/translations'
import {
FieldMap,
StepNavItem,
formatDate,
useConfig,
useLocale,
useStepNav,
useTranslation,
} from '@payloadcms/ui'
import { FieldAffectingData, SanitizedCollectionConfig } from 'payload/types'
import React, { useEffect } from 'react'
import { formatDate, useConfig, useLocale, useStepNav, useTranslation } from '@payloadcms/ui'
import { useEffect } from 'react'
export const SetStepNav: React.FC<{
collectionSlug?: string
globalSlug?: string
mostRecentDoc: any
doc: any
id?: string | number
fieldMap: FieldMap
collectionConfig?: SanitizedCollectionConfig
}> = ({ collectionSlug, globalSlug, mostRecentDoc, doc, id, fieldMap, collectionConfig }) => {
collectionSlug?: string
doc: any
fieldMap: FieldMap
globalSlug?: string
id?: number | string
mostRecentDoc: any
}> = ({ id, collectionConfig, collectionSlug, doc, fieldMap, globalSlug, mostRecentDoc }) => {
const config = useConfig()
const { setStepNav } = useStepNav()
const { i18n, t } = useTranslation()
@@ -42,7 +37,7 @@ export const SetStepNav: React.FC<{
if (mostRecentDoc) {
if (useAsTitle !== 'id') {
const titleField = fieldMap.find(
({ isFieldAffectingData, name: fieldName }) =>
({ name: fieldName, isFieldAffectingData }) =>
isFieldAffectingData && fieldName === useAsTitle,
) as FieldAffectingData

View File

@@ -1,37 +1,39 @@
'use client'
import React, { useState } from 'react'
import type { CompareOption, DefaultVersionsViewProps } from './types'
import type { Option } from '@payloadcms/ui'
import {
Gutter,
Option,
formatDate,
useComponentMap,
useConfig,
usePayloadAPI,
useTranslation,
} from '@payloadcms/ui'
import Restore from '../Restore'
import { mostRecentVersionOption } from '../shared'
import diffComponents from '../RenderFieldsToDiff/fields'
import RenderFieldsToDiff from '../RenderFieldsToDiff'
import { SetStepNav } from './SetStepNav'
import { SelectLocales } from '../SelectLocales'
import { SelectComparison } from '../SelectComparison'
import React, { useState } from 'react'
import type { CompareOption, DefaultVersionsViewProps } from './types'
import RenderFieldsToDiff from '../RenderFieldsToDiff'
import diffComponents from '../RenderFieldsToDiff/fields'
import Restore from '../Restore'
import { SelectComparison } from '../SelectComparison'
import { SelectLocales } from '../SelectLocales'
import { mostRecentVersionOption } from '../shared'
import { SetStepNav } from './SetStepNav'
import './index.scss'
const baseClass = 'view-version'
export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
id,
collectionSlug,
doc,
mostRecentDoc,
publishedDoc,
docPermissions,
globalSlug,
initialComparisonDoc,
localeOptions,
docPermissions,
collectionSlug,
globalSlug,
id,
mostRecentDoc,
publishedDoc,
versionID,
}) => {
const config = useConfig()
@@ -53,8 +55,8 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
const {
admin: { dateFormat },
routes: { api: apiRoute },
localization,
routes: { api: apiRoute },
serverURL,
} = config
@@ -86,29 +88,29 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
: `${compareBaseURL}/${compareValue.value}`
const [{ data: currentComparisonDoc }] = usePayloadAPI(compareFetchURL, {
initialParams: { depth: 1, draft: 'true', locale: '*' },
initialData: initialComparisonDoc,
initialParams: { depth: 1, draft: 'true', locale: '*' },
})
const comparison =
compareValue?.value === 'mostRecent'
? mostRecentDoc
: compareValue?.value === 'published'
? publishedDoc
: currentComparisonDoc?.version // the `version` key is only present on `versions` documents
? publishedDoc
: currentComparisonDoc?.version // the `version` key is only present on `versions` documents
const canUpdate = docPermissions?.update?.permission
return (
<main className={baseClass}>
<SetStepNav
collectionSlug={collectionSlug}
globalSlug={globalSlug}
mostRecentDoc={mostRecentDoc}
doc={doc}
id={id}
fieldMap={fieldMap}
collectionConfig={collectionConfig}
collectionSlug={collectionSlug}
doc={doc}
fieldMap={fieldMap}
globalSlug={globalSlug}
id={id}
mostRecentDoc={mostRecentDoc}
/>
<Gutter className={`${baseClass}__wrap`}>
<div className={`${baseClass}__header-wrap`}>
@@ -148,16 +150,16 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
{doc?.version && (
<RenderFieldsToDiff
comparison={comparison}
fieldPermissions={docPermissions?.fields}
diffComponents={diffComponents}
fieldMap={fieldMap}
fieldPermissions={docPermissions?.fields}
i18n={i18n}
locales={
locales
? locales.map(({ label }) => (typeof label === 'string' ? label : undefined))
: []
}
version={doc?.version}
i18n={i18n}
diffComponents={diffComponents}
/>
)}
</Gutter>

View File

@@ -1,7 +1,6 @@
import { Option } from '@payloadcms/ui'
import { CollectionPermission, GlobalPermission, Permissions, User } from 'payload/auth'
import { Document, SanitizedCollectionConfig } from 'payload/types'
import type { Option } from '@payloadcms/ui'
import type { CollectionPermission, GlobalPermission, Permissions, User } from 'payload/auth'
import type { Document, SanitizedCollectionConfig } from 'payload/types'
export type CompareOption = {
label: string
@@ -11,16 +10,16 @@ export type CompareOption = {
}
export type DefaultVersionsViewProps = {
collectionSlug?: SanitizedCollectionConfig['slug']
doc: Document
mostRecentDoc: Document
publishedDoc: Document
docPermissions: CollectionPermission | GlobalPermission
globalSlug?: SanitizedCollectionConfig['slug']
id?: number | string
initialComparisonDoc: Document
localeOptions: Option[]
user: User
mostRecentDoc: Document
permissions: Permissions
id?: string | number
publishedDoc: Document
user: User
versionID?: string
docPermissions: CollectionPermission | GlobalPermission
collectionSlug?: SanitizedCollectionConfig['slug']
globalSlug?: SanitizedCollectionConfig['slug']
}

View File

@@ -1,27 +1,27 @@
import type { MappedField } from '@payloadcms/ui'
import type { Field } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { getUniqueListBy } from 'payload/utilities'
import React from 'react'
import type { Field } from 'payload/types'
import type { Props } from '../types'
import RenderFieldsToDiff from '../..'
import { getUniqueListBy } from 'payload/utilities'
import Label from '../../Label'
import { MappedField } from '@payloadcms/ui'
import './index.scss'
const baseClass = 'iterable-diff'
const Iterable: React.FC<Props> = ({
comparison,
diffComponents,
field,
i18n,
locale,
locales,
permissions,
version,
i18n,
diffComponents,
}) => {
const versionRowCount = Array.isArray(version) ? version.length : 0
const comparisonRowCount = Array.isArray(comparison) ? comparison.length : 0
@@ -80,12 +80,12 @@ const Iterable: React.FC<Props> = ({
<div className={`${baseClass}__wrap`} key={i}>
<RenderFieldsToDiff
comparison={comparisonRow}
diffComponents={diffComponents}
fieldMap={subFields}
fieldPermissions={permissions}
i18n={i18n}
locales={locales}
version={versionRow}
i18n={i18n}
diffComponents={diffComponents}
/>
</div>
)

View File

@@ -1,5 +1,5 @@
import React from 'react'
import { getTranslation } from '@payloadcms/translations'
import React from 'react'
import type { Props } from '../types'
@@ -11,15 +11,15 @@ const baseClass = 'nested-diff'
const Nested: React.FC<Props> = ({
comparison,
diffComponents,
disableGutter = false,
field,
fieldMap,
i18n,
locale,
locales,
permissions,
version,
i18n,
fieldMap,
diffComponents,
}) => {
return (
<div className={baseClass}>
@@ -36,12 +36,12 @@ const Nested: React.FC<Props> = ({
>
<RenderFieldsToDiff
comparison={comparison}
diffComponents={diffComponents}
fieldMap={fieldMap}
fieldPermissions={permissions}
i18n={i18n}
locales={locales}
version={version}
i18n={i18n}
diffComponents={diffComponents}
/>
</div>
</div>

View File

@@ -1,14 +1,14 @@
import React from 'react'
import type { RelationshipField, SanitizedCollectionConfig } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
import React from 'react'
import ReactDiffViewer from 'react-diff-viewer-continued'
import type { RelationshipField, SanitizedCollectionConfig } from 'payload/types'
import type { Props } from '../types'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/types'
import Label from '../../Label'
import { diffStyles } from '../styles'
import './index.scss'
const baseClass = 'relationship-diff'
@@ -68,9 +68,9 @@ const generateLabelFromValue = (
const Relationship: React.FC<Props & { field: RelationshipField }> = ({
comparison,
field,
version,
i18n,
locale,
version,
}) => {
let placeholder = ''

View File

@@ -5,10 +5,10 @@ import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued'
export const DiffViewer: React.FC<{
comparisonToRender: string
diffMethod: string
diffStyles: any
placeholder: string
versionToRender: string
diffStyles: any
}> = ({ comparisonToRender, diffMethod, placeholder, versionToRender, diffStyles }) => {
}> = ({ comparisonToRender, diffMethod, diffStyles, placeholder, versionToRender }) => {
return (
<ReactDiffViewer
compareMethod={DiffMethod[diffMethod]}

View File

@@ -1,12 +1,14 @@
import { getTranslation, I18n } from '@payloadcms/translations'
import type { I18n } from '@payloadcms/translations'
import type { OptionObject, SelectField } from 'payload/types'
import { getTranslation } from '@payloadcms/translations'
import React from 'react'
import type { OptionObject, SelectField } from 'payload/types'
import type { Props } from '../types'
import Label from '../../Label'
import { diffStyles } from '../styles'
import { DiffViewer } from './DiffViewer'
import './index.scss'
const baseClass = 'select-diff'
@@ -42,7 +44,7 @@ const getTranslatedOptions = (
return typeof options === 'string' ? options : getTranslation(options.label, i18n)
}
const Select: React.FC<Props> = ({ comparison, diffMethod, field, locale, version, i18n }) => {
const Select: React.FC<Props> = ({ comparison, diffMethod, field, i18n, locale, version }) => {
let placeholder = ''
if (version === comparison) placeholder = `[${i18n.t('general:noValue')}]`
@@ -64,11 +66,11 @@ const Select: React.FC<Props> = ({ comparison, diffMethod, field, locale, versio
{getTranslation(field.label || '', i18n)}
</Label>
<DiffViewer
diffMethod={diffMethod}
versionToRender={versionToRender}
comparisonToRender={comparisonToRender}
placeholder={placeholder}
diffMethod={diffMethod}
diffStyles={diffStyles}
placeholder={placeholder}
versionToRender={versionToRender}
/>
</div>
)

View File

@@ -9,13 +9,13 @@ const baseClass = 'tabs-diff'
const Tabs: React.FC<Props> = ({
comparison,
diffComponents,
field,
i18n,
locale,
locales,
permissions,
version,
i18n,
locale,
diffComponents,
}) => {
return (
<div className={baseClass}>
@@ -25,15 +25,15 @@ const Tabs: React.FC<Props> = ({
return (
<Nested
comparison={comparison?.[tab.name]}
diffComponents={diffComponents}
field={field}
fieldMap={tab.subfields}
i18n={i18n}
key={i}
locale={locale}
locales={locales}
permissions={permissions}
version={version?.[tab.name]}
i18n={i18n}
locale={locale}
field={field}
diffComponents={diffComponents}
/>
)
}
@@ -41,13 +41,13 @@ const Tabs: React.FC<Props> = ({
return (
<RenderFieldsToDiff
comparison={comparison}
diffComponents={diffComponents}
fieldMap={tab.subfields}
fieldPermissions={permissions}
i18n={i18n}
key={i}
locales={locales}
version={version}
i18n={i18n}
diffComponents={diffComponents}
/>
)
})}

View File

@@ -5,10 +5,10 @@ import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued'
export const DiffViewer: React.FC<{
comparisonToRender: string
diffMethod: string
diffStyles: any
placeholder: string
versionToRender: string
diffStyles: any
}> = ({ comparisonToRender, diffMethod, placeholder, versionToRender, diffStyles }) => {
}> = ({ comparisonToRender, diffMethod, diffStyles, placeholder, versionToRender }) => {
return (
<ReactDiffViewer
compareMethod={DiffMethod[diffMethod]}

View File

@@ -1,12 +1,11 @@
import React from 'react'
import { getTranslation } from '@payloadcms/translations'
import React from 'react'
import type { Props } from '../types'
import Label from '../../Label'
import { diffStyles } from '../styles'
import { DiffViewer } from './DiffViewer'
import './index.scss'
const baseClass = 'text-diff'
@@ -15,10 +14,10 @@ const Text: React.FC<Props> = ({
comparison,
diffMethod,
field,
i18n,
isRichText = false,
locale,
version,
i18n,
}) => {
let placeholder = ''
@@ -41,9 +40,9 @@ const Text: React.FC<Props> = ({
<DiffViewer
comparisonToRender={comparisonToRender}
diffMethod={diffMethod}
diffStyles={diffStyles}
placeholder={placeholder}
versionToRender={versionToRender}
diffStyles={diffStyles}
/>
</div>
)

View File

@@ -1,23 +1,22 @@
import type { I18n } from '@payloadcms/translations'
import type { FieldMap, MappedField } from '@payloadcms/ui'
import type { FieldPermissions } from 'payload/auth'
import type React from 'react'
import type { DiffMethod } from 'react-diff-viewer-continued'
import type { FieldPermissions } from 'payload/auth'
import { FieldMap, MappedField } from '@payloadcms/ui'
import type { I18n } from '@payloadcms/translations'
export type DiffComponents = Record<string, React.FC<Props>>
export type Props = {
comparison: any
diffComponents: DiffComponents
diffMethod?: DiffMethod
disableGutter?: boolean
field: MappedField
fieldMap: FieldMap
i18n: I18n
isRichText?: boolean
locale?: string
locales?: string[]
permissions?: Record<string, FieldPermissions>
version: any
fieldMap: FieldMap
i18n: I18n
diffComponents: DiffComponents
}

View File

@@ -3,23 +3,22 @@ import type { DiffMethod } from 'react-diff-viewer-continued'
import React from 'react'
import type { Props, FieldDiffProps } from './types'
import type { FieldDiffProps, Props } from './types'
import Nested from './fields/Nested'
import { diffMethods } from './fields/diffMethods'
import './index.scss'
const baseClass = 'render-field-diffs'
const RenderFieldsToDiff: React.FC<Props> = ({
comparison,
fieldPermissions,
diffComponents,
fieldMap,
fieldPermissions,
i18n,
locales,
version,
i18n,
diffComponents,
}) => {
return (
<div className={baseClass}>
@@ -50,15 +49,15 @@ const RenderFieldsToDiff: React.FC<Props> = ({
if (hasPermission === false) return null
const baseCellProps: FieldDiffProps = {
comparison: comparisonValue,
diffComponents,
diffMethod,
field,
isRichText,
locales: locales,
fieldMap: 'subfields' in field ? field.subfields : fieldMap,
fieldPermissions: subFieldPermissions,
i18n,
fieldMap: 'subfields' in field ? field.subfields : fieldMap,
diffComponents,
comparison: comparisonValue,
isRichText,
locales: locales,
version: versionValue,
}
@@ -71,8 +70,8 @@ const RenderFieldsToDiff: React.FC<Props> = ({
const cellProps = {
...baseCellProps,
version: versionLocaleValue,
comparison: comparisonLocaleValue,
version: versionLocaleValue,
}
return (
@@ -100,13 +99,13 @@ const RenderFieldsToDiff: React.FC<Props> = ({
return (
<Tabs
comparison={comparison}
diffComponents={diffComponents}
field={field}
fieldMap={field.subfields}
i18n={i18n}
key={i}
locales={locales}
version={version}
i18n={i18n}
field={field}
fieldMap={field.subfields}
diffComponents={diffComponents}
/>
)
}
@@ -116,15 +115,15 @@ const RenderFieldsToDiff: React.FC<Props> = ({
return (
<Nested
comparison={comparison}
diffComponents={diffComponents}
disableGutter
field={field}
fieldMap={field.subfields}
i18n={i18n}
key={i}
locales={locales}
permissions={fieldPermissions}
version={version}
i18n={i18n}
diffComponents={diffComponents}
/>
)
}

View File

@@ -1,21 +1,22 @@
import type { FieldPermissions } from 'payload/auth'
import { FieldMap, MappedField } from '@payloadcms/ui'
import type { I18n } from '@payloadcms/translations'
import { DiffComponents } from './fields/types'
import type { FieldMap, MappedField } from '@payloadcms/ui'
import type { FieldPermissions } from 'payload/auth'
import type { DiffMethod } from 'react-diff-viewer-continued'
import type { DiffComponents } from './fields/types'
export type Props = {
comparison: Record<string, any>
fieldPermissions: Record<string, FieldPermissions>
diffComponents: DiffComponents
fieldMap: FieldMap
fieldPermissions: Record<string, FieldPermissions>
i18n: I18n
locales: string[]
version: Record<string, any>
i18n: I18n
diffComponents: DiffComponents
}
export type FieldDiffProps = Props & {
field: MappedField
diffMethod: DiffMethod
field: MappedField
isRichText: boolean
}

View File

@@ -1,12 +1,12 @@
'use client'
import { Modal, useModal } from '@faceless-ui/modal'
import { getTranslation } from '@payloadcms/translations'
import { Button, MinimalTemplate, Pill, useConfig, useTranslation } from '@payloadcms/ui'
import React, { Fragment, useCallback, useState } from 'react'
import { toast } from 'react-toastify'
import type { Props } from './types'
import { Button, MinimalTemplate, Pill, useConfig, useTranslation } from '@payloadcms/ui'
// import { requests } from '../../../../api'
import './index.scss'

View File

@@ -5,7 +5,7 @@ export type Props = {
collectionSlug?: SanitizedCollectionConfig['slug']
globalSlug?: SanitizedGlobalConfig['slug']
label: SanitizedCollectionConfig['labels']['singular'] | SanitizedGlobalConfig['label']
originalDocID: string | number
originalDocID: number | string
versionDate: string
versionID: string
}

View File

@@ -1,13 +1,14 @@
'use client'
import qs from 'qs'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from '@payloadcms/ui'
import type { PaginatedDocs } from 'payload/database'
import type { Where } from 'payload/types'
import { useTranslation } from '@payloadcms/ui'
import { ReactSelect, fieldBaseClass, formatDate, useConfig } from '@payloadcms/ui'
import qs from 'qs'
import React, { useCallback, useEffect, useState } from 'react'
import type { Props } from './types'
import { formatDate, ReactSelect, fieldBaseClass, useConfig } from '@payloadcms/ui'
import { mostRecentVersionOption, publishedVersionOption } from '../shared'
import './index.scss'

View File

@@ -1,11 +1,12 @@
import type { SanitizedCollectionConfig } from 'payload/types'
import type { PaginatedDocs } from 'payload/database'
import type { SanitizedCollectionConfig } from 'payload/types'
import type { CompareOption } from '../Default/types'
export type Props = {
baseURL: string
onChange: (val: CompareOption) => void
parentID?: string | number
parentID?: number | string
publishedDoc: any
value: CompareOption
versionID: string

View File

@@ -1,9 +1,9 @@
import React from 'react'
import { useTranslation } from '@payloadcms/ui'
import { ReactSelect, useLocale } from '@payloadcms/ui'
import React from 'react'
import type { Props } from './types'
import { ReactSelect, useLocale } from '@payloadcms/ui'
import './index.scss'
const baseClass = 'select-version-locales'

View File

@@ -1,13 +1,14 @@
import type { Option, ServerSideEditViewProps } from '@payloadcms/ui'
import type { CollectionPermission, GlobalPermission } from 'payload/auth'
import type { Document } from 'payload/types'
import { notFound } from 'next/navigation'
import React from 'react'
import { DefaultVersionView } from './Default'
import { Document } from 'payload/types'
import type { Option, ServerSideEditViewProps } from '@payloadcms/ui'
import { CollectionPermission, GlobalPermission } from 'payload/auth'
import { notFound } from 'next/navigation'
export const VersionView: React.FC<ServerSideEditViewProps> = async (props) => {
const { config, permissions, payload, user, params } = props
const { config, params, payload, permissions, user } = props
const versionID = params.segments[2]
@@ -33,23 +34,23 @@ export const VersionView: React.FC<ServerSideEditViewProps> = async (props) => {
try {
doc = await payload.findVersionByID({
collection: slug,
id: versionID,
collection: slug,
depth: 1,
locale: '*',
})
publishedDoc = await payload.findByID({
collection: slug,
id,
collection: slug,
depth: 1,
draft: false,
locale: '*',
})
mostRecentDoc = await payload.findByID({
collection: slug,
id,
collection: slug,
depth: 1,
draft: true,
locale: '*',
@@ -65,24 +66,24 @@ export const VersionView: React.FC<ServerSideEditViewProps> = async (props) => {
try {
doc = payload.findGlobalVersionByID({
slug,
id: versionID,
depth: 1,
locale: '*',
slug,
})
publishedDoc = payload.findGlobal({
slug,
depth: 1,
draft: false,
locale: '*',
slug,
})
mostRecentDoc = payload.findGlobal({
slug,
depth: 1,
draft: true,
locale: '*',
slug,
})
} catch (error) {
return notFound()
@@ -104,17 +105,17 @@ export const VersionView: React.FC<ServerSideEditViewProps> = async (props) => {
return (
<DefaultVersionView
collectionSlug={collectionSlug}
globalSlug={globalSlug}
initialComparisonDoc={mostRecentDoc}
doc={doc}
docPermissions={docPermissions}
globalSlug={globalSlug}
id={id}
initialComparisonDoc={mostRecentDoc}
localeOptions={localeOptions}
mostRecentDoc={mostRecentDoc}
id={id}
permissions={permissions}
publishedDoc={publishedDoc}
user={user}
versionID={versionID}
docPermissions={docPermissions}
/>
)
}

View File

@@ -1,30 +1,30 @@
import React from 'react'
import type { I18n } from '@payloadcms/translations'
import type { Column } from '@payloadcms/ui'
import type {
SanitizedCollectionConfig,
SanitizedConfig,
SanitizedGlobalConfig,
} from 'payload/types'
import type { Column } from '@payloadcms/ui'
import { SortColumn } from '@payloadcms/ui'
import { I18n } from '@payloadcms/translations'
import React from 'react'
import { AutosaveCell } from './cells/AutosaveCell'
import { CreatedAtCell } from './cells/CreatedAt'
import { IDCell } from './cells/ID'
import { AutosaveCell } from './cells/AutosaveCell'
export const buildVersionColumns = ({
config,
collectionConfig,
globalConfig,
config,
docID,
globalConfig,
i18n: { t },
i18n,
}: {
config: SanitizedConfig
collectionConfig?: SanitizedCollectionConfig
config: SanitizedConfig
docID?: number | string
globalConfig?: SanitizedGlobalConfig
docID?: string | number
i18n: I18n
}): Column[] => [
{
@@ -32,14 +32,14 @@ export const buildVersionColumns = ({
accessor: 'updatedAt',
active: true,
components: {
Heading: <SortColumn label={t('general:updatedAt')} name="updatedAt" />,
Cell: (
<CreatedAtCell
docID={docID}
collectionSlug={collectionConfig?.slug}
docID={docID}
globalSlug={globalConfig?.slug}
/>
),
Heading: <SortColumn label={t('general:updatedAt')} name="updatedAt" />,
},
label: '',
},
@@ -48,8 +48,8 @@ export const buildVersionColumns = ({
accessor: 'id',
active: true,
components: {
Heading: <SortColumn disable label={t('version:versionID')} name="id" />,
Cell: <IDCell />,
Heading: <SortColumn disable label={t('version:versionID')} name="id" />,
},
label: '',
},
@@ -58,8 +58,8 @@ export const buildVersionColumns = ({
accessor: 'autosave',
active: true,
components: {
Heading: <SortColumn disable label={t('version:type')} name="autosave" />,
Cell: <AutosaveCell />,
Heading: <SortColumn disable label={t('version:type')} name="autosave" />,
},
label: '',
},

View File

@@ -1,6 +1,6 @@
'use client'
import { Pill, useTableCell, useTranslation } from '@payloadcms/ui'
import React, { Fragment } from 'react'
import { useTranslation, useTableCell, Pill } from '@payloadcms/ui'
export const AutosaveCell: React.FC = () => {
const { t } = useTranslation()

View File

@@ -1,22 +1,22 @@
'use client'
import React from 'react'
import { formatDate, useConfig, useTranslation, useTableCell } from '@payloadcms/ui'
import { formatDate, useConfig, useTableCell, useTranslation } from '@payloadcms/ui'
import Link from 'next/link'
import React from 'react'
type CreatedAtCellProps = {
collectionSlug?: string
docID?: number | string
globalSlug?: string
docID?: string | number
}
export const CreatedAtCell: React.FC<CreatedAtCellProps> = ({
collectionSlug,
globalSlug,
docID,
globalSlug,
}) => {
const {
routes: { admin },
admin: { dateFormat },
routes: { admin },
} = useConfig()
const { i18n } = useTranslation()

View File

@@ -1,6 +1,6 @@
'use client'
import React, { Fragment } from 'react'
import { useTableCell } from '@payloadcms/ui'
import React, { Fragment } from 'react'
export const IDCell: React.FC = () => {
const { cellData } = useTableCell()

View File

@@ -1,6 +1,9 @@
'use client'
import type { Column } from '@payloadcms/ui'
import type { PaginatedDocs } from 'payload/database'
import type { SanitizedCollectionConfig } from 'payload/types'
import {
Column,
LoadingOverlayToggle,
Pagination,
PerPage,
@@ -9,21 +12,19 @@ import {
useTranslation,
} from '@payloadcms/ui'
import { useSearchParams } from 'next/navigation'
import { PaginatedDocs } from 'payload/database'
import { SanitizedCollectionConfig } from 'payload/types'
import React, { Fragment, useEffect, useRef } from 'react'
export const VersionsViewClient: React.FC<{
initialData: PaginatedDocs
columns: Column[]
baseClass: string
fetchURL: string
collectionSlug?: string
columns: Column[]
fetchURL: string
globalSlug?: string
id?: string | number
id?: number | string
initialData: PaginatedDocs
paginationLimits?: SanitizedCollectionConfig['admin']['pagination']['limits']
}> = (props) => {
const { initialData, columns, baseClass, collectionSlug, fetchURL, id, paginationLimits } = props
const { id, baseClass, collectionSlug, columns, fetchURL, initialData, paginationLimits } = props
const searchParams = useSearchParams()
const limit = searchParams.get('limit')

View File

@@ -1,17 +1,18 @@
import type { ServerSideEditViewProps } from '@payloadcms/ui'
import { getTranslation } from '@payloadcms/translations'
import { Gutter, SetDocumentStepNav as SetStepNav } from '@payloadcms/ui'
import { notFound } from 'next/navigation'
import React from 'react'
import { Gutter, SetDocumentStepNav as SetStepNav, ServerSideEditViewProps } from '@payloadcms/ui'
import { buildVersionColumns } from './buildColumns'
import { notFound } from 'next/navigation'
import { getTranslation } from '@payloadcms/translations'
import { VersionsViewClient } from './index.client'
import './index.scss'
export const baseClass = 'versions'
export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) => {
const { user, payload, config, searchParams, i18n } = props
const { config, i18n, payload, searchParams, user } = props
const id = 'id' in props ? props.id : undefined
const collectionConfig = 'collectionConfig' in props && props?.collectionConfig
@@ -37,10 +38,10 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
versionsData = await payload.findVersions({
collection: collectionSlug,
depth: 0,
user,
limit: limit ? parseInt(limit?.toString(), 10) : undefined,
page: page ? parseInt(page.toString(), 10) : undefined,
sort: sort as string,
limit: limit ? parseInt(limit?.toString(), 10) : undefined,
user,
where: {
parent: {
equals: id,
@@ -59,11 +60,11 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
if (globalSlug) {
try {
versionsData = await payload.findGlobalVersions({
slug: globalSlug,
depth: 0,
user,
page: page ? parseInt(page as string, 10) : undefined,
slug: globalSlug,
sort: sort as string,
user,
where: {
parent: {
equals: id,
@@ -84,18 +85,18 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
}
const columns = buildVersionColumns({
config,
collectionConfig,
globalConfig,
config,
docID: id,
globalConfig,
i18n,
})
const fetchURL = collectionSlug
? `${serverURL}${apiRoute}/${collectionSlug}/versions`
: globalSlug
? `${serverURL}${apiRoute}/globals/${globalSlug}/versions`
: ''
? `${serverURL}${apiRoute}/globals/${globalSlug}/versions`
: ''
return (
<React.Fragment>
@@ -110,13 +111,13 @@ export const VersionsView: React.FC<ServerSideEditViewProps> = async (props) =>
<main className={baseClass}>
<Gutter className={`${baseClass}__wrap`}>
<VersionsViewClient
initialData={versionsData}
columns={columns}
fetchURL={fetchURL}
baseClass={baseClass}
collectionSlug={collectionSlug}
columns={columns}
fetchURL={fetchURL}
globalSlug={globalSlug}
id={id}
initialData={versionsData}
paginationLimits={collectionConfig?.admin?.pagination?.limits}
/>
</Gutter>

View File

@@ -1,23 +1,23 @@
import type { I18n } from '@payloadcms/translations'
import type { User } from 'payload/auth'
import type { PaginatedDocs } from 'payload/database'
import type {
SanitizedCollectionConfig,
SanitizedConfig,
SanitizedGlobalConfig,
} from 'payload/types'
import type { PaginatedDocs } from 'payload/database'
import { User } from 'payload/auth'
import { I18n } from '@payloadcms/translations'
export type DefaultVersionsViewProps = {
canAccessAdmin: boolean
config: SanitizedConfig
collectionConfig?: SanitizedCollectionConfig
globalConfig?: SanitizedGlobalConfig
config: SanitizedConfig
data: Document
versionsData: PaginatedDocs<Document>
editURL: string
entityLabel: string
id: string | number
user: User
limit: number
globalConfig?: SanitizedGlobalConfig
i18n: I18n
id: number | string
limit: number
user: User
versionsData: PaginatedDocs<Document>
}

View File

@@ -1,10 +1,12 @@
import { GraphQLError } from 'graphql'
import httpStatus from 'http-status'
import { configToSchema } from '@payloadcms/graphql'
import type { Payload, CollectionAfterErrorHook, SanitizedConfig } from 'payload/types'
import type { GraphQLFormattedError } from 'graphql'
import { createPayloadRequest } from '../../utilities/createPayloadRequest'
import type { GraphQLError } from 'graphql'
import type { CollectionAfterErrorHook, Payload, SanitizedConfig } from 'payload/types'
import { configToSchema } from '@payloadcms/graphql'
import { createHandler } from 'graphql-http/lib/use/fetch'
import httpStatus from 'http-status'
import { createPayloadRequest } from '../../utilities/createPayloadRequest'
const handleError = async (
payload: Payload,
@@ -75,8 +77,8 @@ export const POST =
(config: Promise<SanitizedConfig> | SanitizedConfig) => async (request: Request) => {
const originalRequest = request.clone()
const req = await createPayloadRequest({
request,
config,
request,
})
const { schema, validationRules } = await getGraphql(config)
@@ -87,7 +89,7 @@ export const POST =
const headers = {}
const apiResponse = await createHandler({
context: { req, headers },
context: { headers, req },
onOperation: async (request, args, result) => {
const response =
typeof payload.extensions === 'function'
@@ -113,12 +115,12 @@ export const POST =
})(originalRequest)
const resHeaders = new Headers(apiResponse.headers)
for (let key in headers) {
for (const key in headers) {
resHeaders.append(key, headers[key])
}
return new Response(apiResponse.body, {
status: apiResponse.status,
headers: new Headers(resHeaders),
status: apiResponse.status,
})
}

View File

@@ -1,11 +1,13 @@
import { renderPlaygroundPage } from 'graphql-playground-html'
import { createPayloadRequest } from '../../utilities/createPayloadRequest'
import type { SanitizedConfig } from 'payload/types'
import { renderPlaygroundPage } from 'graphql-playground-html'
import { createPayloadRequest } from '../../utilities/createPayloadRequest'
export const GET = (config: Promise<SanitizedConfig>) => async (request: Request) => {
const req = await createPayloadRequest({
request,
config,
request,
})
if (
@@ -16,16 +18,16 @@ export const GET = (config: Promise<SanitizedConfig>) => async (request: Request
) {
return new Response(
renderPlaygroundPage({
endpoint: `${req.payload.config.routes.api}${req.payload.config.routes.graphQL}`,
settings: {
'request.credentials': 'include',
},
endpoint: `${req.payload.config.routes.api}${req.payload.config.routes.graphQL}`,
}),
{
status: 200,
headers: {
'Content-Type': 'text/html',
},
status: 200,
},
)
} else {

View File

@@ -1,10 +1,10 @@
export { GRAPHQL_PLAYGROUND_GET, GRAPHQL_POST } from './graphql'
export {
GET as REST_GET,
POST as REST_POST,
DELETE as REST_DELETE,
GET as REST_GET,
PATCH as REST_PATCH,
POST as REST_POST,
} from './rest'
export { GET as GET_STATIC_FILE } from './rest/[collection]/file/[filename]/route'
export { GRAPHQL_POST, GRAPHQL_PLAYGROUND_GET } from './graphql'

View File

@@ -1,6 +1,7 @@
import type { Collection, PayloadRequest } from 'payload/types'
import { APIError } from 'payload/errors'
import httpStatus from 'http-status'
import { APIError } from 'payload/errors'
export type ErrorResponse = { data?: any; errors: unknown[]; stack?: string }
@@ -58,13 +59,13 @@ const formatErrors = (incoming: { [key: string]: unknown } | APIError | Error):
}
export const RouteError = async ({
req,
err,
collection,
err,
req,
}: {
req: PayloadRequest
err: APIError
collection?: Collection
err: APIError
req: PayloadRequest
}) => {
if (!req?.payload) {
return Response.json(

View File

@@ -1,25 +1,27 @@
import path from 'path'
import { streamFile } from '../../../../../next-stream-file'
import fsPromises from 'fs/promises'
import type { Collection, PayloadRequest, SanitizedConfig, Where } from 'payload/types'
import fsPromises from 'fs/promises'
import httpStatus from 'http-status'
import path from 'path'
import { executeAccess } from 'payload/auth'
import { APIError, Forbidden } from 'payload/errors'
import { RouteError } from '../../../RouteError'
import { streamFile } from '../../../../../next-stream-file'
import { createPayloadRequest } from '../../../../../utilities/createPayloadRequest'
import httpStatus from 'http-status'
import { RouteError } from '../../../RouteError'
import { endpointsAreDisabled } from '../../../checkEndpoints'
async function checkFileAccess({
req,
filename,
collection,
filename,
req,
}: {
req: PayloadRequest
filename: string
collection: Collection
filename: string
req: PayloadRequest
}) {
const { config } = collection
const disableEndpoints = endpointsAreDisabled({ request: req, endpoints: config.endpoints })
const disableEndpoints = endpointsAreDisabled({ endpoints: config.endpoints, request: req })
if (disableEndpoints) return disableEndpoints
const accessResult = await executeAccess({ isReadingStaticFile: true, req }, config.access.read)
@@ -71,9 +73,9 @@ export const GET =
try {
req = await createPayloadRequest({
request,
config,
params: { collection: collectionSlug },
request,
})
collection = req.payload.collections?.[collectionSlug]
@@ -96,9 +98,9 @@ export const GET =
}
await checkFileAccess({
req,
filename,
collection,
filename,
req,
})
const fileDir = collection.config.upload?.staticDir || collection.config.slug
@@ -106,16 +108,16 @@ export const GET =
const stats = await fsPromises.stat(filePath)
const data = streamFile(filePath)
return new Response(data, {
status: httpStatus.OK,
headers: new Headers({
'content-length': stats.size + '',
}),
status: httpStatus.OK,
})
} catch (error) {
return RouteError({
req,
collection,
err: error,
req,
})
}
}

View File

@@ -1,6 +1,7 @@
import httpStatus from 'http-status'
import { accessOperation } from 'payload/operations'
import { BaseRouteHandler } from '../types'
import type { BaseRouteHandler } from '../types'
export const access: BaseRouteHandler = async ({ req }) => {
const results = await accessOperation({

View File

@@ -1,8 +1,9 @@
import { forgotPasswordOperation } from 'payload/operations'
import httpStatus from 'http-status'
import { CollectionRouteHandler } from '../types'
import { forgotPasswordOperation } from 'payload/operations'
export const forgotPassword: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const forgotPassword: CollectionRouteHandler = async ({ collection, req }) => {
await forgotPasswordOperation({
collection,
data: {

View File

@@ -1,7 +1,8 @@
import { initOperation } from 'payload/operations'
import { CollectionRouteHandler } from '../types'
export const init: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const init: CollectionRouteHandler = async ({ collection, req }) => {
const initialized = await initOperation({
collection: collection.config.slug,
req,

View File

@@ -1,10 +1,11 @@
import httpStatus from 'http-status'
import { generatePayloadCookie } from 'payload/auth'
import { loginOperation } from 'payload/operations'
import { isNumber } from 'payload/utilities'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const login: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const login: CollectionRouteHandler = async ({ collection, req }) => {
const { searchParams } = req
const depth = searchParams.get('depth')
@@ -19,9 +20,9 @@ export const login: CollectionRouteHandler = async ({ req, collection }) => {
})
const cookie = generatePayloadCookie({
token: result.token,
payload: req.payload,
collectionConfig: collection.config,
payload: req.payload,
token: result.token,
})
if (collection.config.auth.removeTokenFromResponses) {

View File

@@ -1,9 +1,10 @@
import httpStatus from 'http-status'
import { logoutOperation } from 'payload/operations'
import { generateExpiredPayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
import { logoutOperation } from 'payload/operations'
export const logout: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const logout: CollectionRouteHandler = async ({ collection, req }) => {
const result = logoutOperation({
collection,
req,

View File

@@ -1,15 +1,16 @@
import httpStatus from 'http-status'
import { meOperation } from 'payload/operations'
import { extractJWT } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
import { meOperation } from 'payload/operations'
export const me: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const me: CollectionRouteHandler = async ({ collection, req }) => {
const currentToken = extractJWT(req)
const result = await meOperation({
collection,
req,
currentToken,
req,
})
if (collection.config.auth.removeTokenFromResponses) {

View File

@@ -1,10 +1,11 @@
import { extractJWT } from 'payload/auth'
import { refreshOperation } from 'payload/operations'
import httpStatus from 'http-status'
import { extractJWT } from 'payload/auth'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
import { refreshOperation } from 'payload/operations'
export const refresh: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const refresh: CollectionRouteHandler = async ({ collection, req }) => {
const token = typeof req.data?.token === 'string' ? req.data.token : extractJWT(req)
if (!token) {
@@ -20,15 +21,15 @@ export const refresh: CollectionRouteHandler = async ({ req, collection }) => {
}
const result = await refreshOperation({
token,
req,
collection,
req,
token,
})
const cookie = generatePayloadCookie({
token: result.refreshedToken,
payload: req.payload,
collectionConfig: collection.config,
payload: req.payload,
token: result.refreshedToken,
})
if (collection.config.auth.removeTokenFromResponses) {

View File

@@ -1,9 +1,10 @@
import httpStatus from 'http-status'
import { registerFirstUserOperation } from 'payload/operations'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
import { registerFirstUserOperation } from 'payload/operations'
export const registerFirstUser: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const registerFirstUser: CollectionRouteHandler = async ({ collection, req }) => {
const result = await registerFirstUserOperation({
collection,
data: {
@@ -14,9 +15,9 @@ export const registerFirstUser: CollectionRouteHandler = async ({ req, collectio
})
const cookie = generatePayloadCookie({
token: result.token,
payload: req.payload,
collectionConfig: collection.config,
payload: req.payload,
token: result.token,
})
return Response.json(

View File

@@ -1,10 +1,10 @@
import httpStatus from 'http-status'
import { resetPasswordOperation } from 'payload/operations'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
import { resetPasswordOperation } from 'payload/operations'
export const resetPassword: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const resetPassword: CollectionRouteHandler = async ({ collection, req }) => {
const { searchParams } = req
const depth = searchParams.get('depth')
@@ -19,9 +19,9 @@ export const resetPassword: CollectionRouteHandler = async ({ req, collection })
})
const cookie = generatePayloadCookie({
token: result.token,
payload: req.payload,
collectionConfig: collection.config,
payload: req.payload,
token: result.token,
})
if (collection.config.auth.removeTokenFromResponses) {

View File

@@ -1,9 +1,9 @@
import httpStatus from 'http-status'
import { unlockOperation } from 'payload/operations'
import { CollectionRouteHandler } from '../types'
export const unlock: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const unlock: CollectionRouteHandler = async ({ collection, req }) => {
await unlockOperation({
collection,
data: { email: req.data.email as string },

View File

@@ -1,9 +1,9 @@
import httpStatus from 'http-status'
import { verifyEmailOperation } from 'payload/operations'
import { CollectionRouteHandlerWithID } from '../types'
export const verifyEmail: CollectionRouteHandlerWithID = async ({ req, id, collection }) => {
import type { CollectionRouteHandlerWithID } from '../types'
export const verifyEmail: CollectionRouteHandlerWithID = async ({ id, collection, req }) => {
await verifyEmailOperation({
collection,
req,

View File

@@ -1,13 +1,8 @@
import httpStatus from 'http-status'
import type { BuildFormStateArgs, FieldSchemaMap } from '@payloadcms/ui'
import type { Field, PayloadRequest, SanitizedConfig } from 'payload/types'
import {
BuildFormStateArgs,
FieldSchemaMap,
buildFieldSchemaMap,
buildStateFromSchema,
reduceFieldsToValues,
} from '@payloadcms/ui'
import { Field, PayloadRequest, SanitizedConfig } from 'payload/types'
import { buildFieldSchemaMap, buildStateFromSchema, reduceFieldsToValues } from '@payloadcms/ui'
import httpStatus from 'http-status'
let cached = global._payload_fieldSchemaMap
@@ -27,7 +22,7 @@ export const getFieldSchemaMap = (config: SanitizedConfig): FieldSchemaMap => {
}
export const buildFormState = async ({ req }: { req: PayloadRequest }) => {
const { data: reqData, user, t, locale } = req
const { data: reqData, locale, t, user } = req
// TODO: run ADMIN access control for user
@@ -35,10 +30,10 @@ export const buildFormState = async ({ req }: { req: PayloadRequest }) => {
const {
id,
operation,
data: incomingData,
docPreferences,
formState,
data: incomingData,
operation,
schemaPath,
} = reqData as BuildFormStateArgs

View File

@@ -2,11 +2,11 @@ import httpStatus from 'http-status'
import { CollectionConfig, GlobalConfig } from 'payload/types'
export const endpointsAreDisabled = ({
request,
endpoints,
request,
}: {
endpoints: false | unknown[]
request: Partial<Request>
endpoints: unknown[] | false
}) => {
if (!endpoints) {
return Response.json(

View File

@@ -1,10 +1,10 @@
import httpStatus from 'http-status'
import { isNumber } from 'payload/utilities'
import { createOperation } from 'payload/operations'
import { CollectionRouteHandler } from '../types'
import { isNumber } from 'payload/utilities'
export const create: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const create: CollectionRouteHandler = async ({ collection, req }) => {
const { searchParams } = req
const autosave = searchParams.get('autosave') === 'true'
const draft = searchParams.get('draft') === 'true'

View File

@@ -1,19 +1,20 @@
import httpStatus from 'http-status'
import type { Where } from 'payload/types'
import { isNumber } from 'payload/utilities'
import { getTranslation } from '@payloadcms/translations'
import httpStatus from 'http-status'
import { deleteOperation } from 'payload/operations'
import { CollectionRouteHandler } from '../types'
import { isNumber } from 'payload/utilities'
import qs from 'qs'
export const deleteDoc: CollectionRouteHandler = async ({ req, collection }) => {
import type { CollectionRouteHandler } from '../types'
export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) => {
const { searchParams } = req
// parse using `qs` to handle `where` queries
const { where, depth } = qs.parse(searchParams.toString()) as {
where?: Where
const { depth, where } = qs.parse(searchParams.toString()) as {
depth?: string
where?: Where
}
const result = await deleteOperation({

View File

@@ -1,10 +1,10 @@
import httpStatus from 'http-status'
import { isNumber } from 'payload/utilities'
import { deleteByIDOperation } from 'payload/operations'
import { CollectionRouteHandlerWithID } from '../types'
import { isNumber } from 'payload/utilities'
export const deleteByID: CollectionRouteHandlerWithID = async ({ req, collection, id }) => {
import type { CollectionRouteHandlerWithID } from '../types'
export const deleteByID: CollectionRouteHandlerWithID = async ({ id, collection, req }) => {
const { searchParams } = req
const depth = searchParams.get('depth')
const doc = await deleteByIDOperation({

Some files were not shown because too many files have changed in this diff Show More