Compare commits
1 Commits
feat/colum
...
feat/serve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09d29a6ec9 |
37
.github/CODEOWNERS
vendored
Normal file
37
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# Order matters. The last matching pattern takes precedence.
|
||||
|
||||
### Package Exports
|
||||
|
||||
**/exports/ @denolfe @jmikrut @DanRibbens
|
||||
|
||||
### Packages
|
||||
|
||||
/packages/plugin-cloud*/src/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/email-*/src/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/live-preview*/src/ @jacobsfletch
|
||||
/packages/plugin-stripe/src/ @jacobsfletch
|
||||
/packages/plugin-multi-tenant/src/ @JarrodMFlesch
|
||||
/packages/richtext-*/src/ @AlessioGr
|
||||
/packages/next/src/ @jmikrut @jacobsfletch @AlessioGr @JarrodMFlesch
|
||||
/packages/ui/src/ @jmikrut @jacobsfletch @AlessioGr @JarrodMFlesch
|
||||
/packages/storage-*/src/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/create-payload-app/src/ @denolfe @jmikrut @DanRibbens
|
||||
/packages/eslint-*/ @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||
|
||||
### Templates
|
||||
|
||||
/templates/_data/ @denolfe @jmikrut @DanRibbens
|
||||
/templates/_template/ @denolfe @jmikrut @DanRibbens
|
||||
|
||||
### Build Files
|
||||
|
||||
**/tsconfig*.json @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||
**/jest.config.js @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||
|
||||
### Root
|
||||
|
||||
/package.json @denolfe @jmikrut @DanRibbens
|
||||
/tools/ @denolfe @jmikrut @DanRibbens
|
||||
/.husky/ @denolfe @jmikrut @DanRibbens
|
||||
/.vscode/ @denolfe @jmikrut @DanRibbens @AlessioGr
|
||||
/.github/ @denolfe @jmikrut @DanRibbens
|
||||
@@ -1,6 +1,5 @@
|
||||
import type {
|
||||
AdminViewServerProps,
|
||||
ColumnPreference,
|
||||
ListPreferences,
|
||||
ListQuery,
|
||||
ListViewClientProps,
|
||||
@@ -12,7 +11,7 @@ import { DefaultListView, HydrateAuthProvider, ListQueryProvider } from '@payloa
|
||||
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
|
||||
import { renderFilters, renderTable, upsertPreferences } from '@payloadcms/ui/rsc'
|
||||
import { formatAdminURL, mergeListSearchAndWhere } from '@payloadcms/ui/shared'
|
||||
import { notFound } from 'next/navigation.js'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import { isNumber } from 'payload/shared'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
@@ -72,23 +71,13 @@ export const renderListView = async (
|
||||
}
|
||||
|
||||
const query = queryFromArgs || queryFromReq
|
||||
|
||||
let columns: ColumnPreference[]
|
||||
|
||||
if (query.columns) {
|
||||
try {
|
||||
columns = JSON.parse(query?.columns as string) as ColumnPreference[]
|
||||
} catch (error) {
|
||||
console.error('Error parsing columns from URL:', error) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
const limitFromQuery = isNumber(query?.limit) ? Number(query.limit) : undefined
|
||||
|
||||
const listPreferences = await upsertPreferences<ListPreferences>({
|
||||
key: `${collectionSlug}-list`,
|
||||
req,
|
||||
value: {
|
||||
columns,
|
||||
limit: isNumber(query?.limit) ? Number(query.limit) : undefined,
|
||||
limit: limitFromQuery,
|
||||
sort: query?.sort as string,
|
||||
},
|
||||
})
|
||||
@@ -153,7 +142,6 @@ export const renderListView = async (
|
||||
clientCollectionConfig,
|
||||
collectionConfig,
|
||||
columnPreferences: listPreferences?.columns,
|
||||
columns,
|
||||
customCellProps,
|
||||
docs: data.docs,
|
||||
drawerSlug,
|
||||
@@ -216,11 +204,9 @@ export const renderListView = async (
|
||||
<Fragment>
|
||||
<HydrateAuthProvider permissions={permissions} />
|
||||
<ListQueryProvider
|
||||
columns={columnState.map(({ accessor, active }) => ({ [accessor]: active }))}
|
||||
data={data}
|
||||
defaultLimit={limit}
|
||||
defaultSort={sort}
|
||||
listPreferences={listPreferences}
|
||||
modifySearchParams={!isInDrawer}
|
||||
>
|
||||
{RenderServerComponent({
|
||||
@@ -253,6 +239,19 @@ export const renderListView = async (
|
||||
}
|
||||
|
||||
export const ListView: React.FC<RenderListViewArgs> = async (args) => {
|
||||
const {
|
||||
initPageResult: { collectionConfig, req },
|
||||
} = args
|
||||
|
||||
if (!req.query?.limit) {
|
||||
return redirect(
|
||||
`${req.url}?${new URLSearchParams({
|
||||
...req.query,
|
||||
limit: String(collectionConfig.admin.pagination.defaultLimit),
|
||||
}).toString()}`,
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
const { List: RenderedList } = await renderListView({ ...args, enableRowSelections: true })
|
||||
return RenderedList
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import type * as AWS from '@aws-sdk/client-s3'
|
||||
import type { CognitoUserSession } from 'amazon-cognito-identity-js'
|
||||
|
||||
import type { GetStorageClient } from './refreshSession.js'
|
||||
import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity'
|
||||
import * as AWS from '@aws-sdk/client-s3'
|
||||
import { fromCognitoIdentityPool } from '@aws-sdk/credential-providers'
|
||||
|
||||
import { refreshSession } from './refreshSession.js'
|
||||
import { authAsCognitoUser } from './authAsCognitoUser.js'
|
||||
|
||||
export let storageClient: AWS.S3 | null = null
|
||||
export let session: CognitoUserSession | null = null
|
||||
export let identityID: string
|
||||
export type GetStorageClient = () => Promise<{
|
||||
identityID: string
|
||||
storageClient: AWS.S3
|
||||
}>
|
||||
|
||||
let storageClient: AWS.S3 | null = null
|
||||
let session: CognitoUserSession | null = null
|
||||
let identityID: string
|
||||
|
||||
export const getStorageClient: GetStorageClient = async () => {
|
||||
if (storageClient && session?.isValid()) {
|
||||
@@ -17,8 +23,6 @@ export const getStorageClient: GetStorageClient = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
;({ identityID, session, storageClient } = await refreshSession())
|
||||
|
||||
if (!process.env.PAYLOAD_CLOUD_PROJECT_ID) {
|
||||
throw new Error('PAYLOAD_CLOUD_PROJECT_ID is required')
|
||||
}
|
||||
@@ -29,6 +33,34 @@ export const getStorageClient: GetStorageClient = async () => {
|
||||
throw new Error('PAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID is required')
|
||||
}
|
||||
|
||||
session = await authAsCognitoUser(
|
||||
process.env.PAYLOAD_CLOUD_PROJECT_ID,
|
||||
process.env.PAYLOAD_CLOUD_COGNITO_PASSWORD,
|
||||
)
|
||||
|
||||
const cognitoIdentity = new CognitoIdentityClient({
|
||||
credentials: fromCognitoIdentityPool({
|
||||
clientConfig: {
|
||||
region: 'us-east-1',
|
||||
},
|
||||
identityPoolId: process.env.PAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID,
|
||||
logins: {
|
||||
[`cognito-idp.us-east-1.amazonaws.com/${process.env.PAYLOAD_CLOUD_COGNITO_USER_POOL_ID}`]:
|
||||
session.getIdToken().getJwtToken(),
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
const credentials = await cognitoIdentity.config.credentials()
|
||||
|
||||
// @ts-expect-error - Incorrect AWS types
|
||||
identityID = credentials.identityId
|
||||
|
||||
storageClient = new AWS.S3({
|
||||
credentials,
|
||||
region: process.env.PAYLOAD_CLOUD_BUCKET_REGION,
|
||||
})
|
||||
|
||||
return {
|
||||
identityID,
|
||||
storageClient,
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { CognitoIdentityClient } from '@aws-sdk/client-cognito-identity'
|
||||
import * as AWS from '@aws-sdk/client-s3'
|
||||
import { fromCognitoIdentityPool } from '@aws-sdk/credential-providers'
|
||||
|
||||
import { authAsCognitoUser } from './authAsCognitoUser.js'
|
||||
|
||||
export type GetStorageClient = () => Promise<{
|
||||
identityID: string
|
||||
storageClient: AWS.S3
|
||||
}>
|
||||
|
||||
export const refreshSession = async () => {
|
||||
const session = await authAsCognitoUser(
|
||||
process.env.PAYLOAD_CLOUD_PROJECT_ID || '',
|
||||
process.env.PAYLOAD_CLOUD_COGNITO_PASSWORD || '',
|
||||
)
|
||||
|
||||
const cognitoIdentity = new CognitoIdentityClient({
|
||||
credentials: fromCognitoIdentityPool({
|
||||
clientConfig: {
|
||||
region: 'us-east-1',
|
||||
},
|
||||
identityPoolId: process.env.PAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID || '',
|
||||
logins: {
|
||||
[`cognito-idp.us-east-1.amazonaws.com/${process.env.PAYLOAD_CLOUD_COGNITO_USER_POOL_ID}`]:
|
||||
session.getIdToken().getJwtToken(),
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
const credentials = await cognitoIdentity.config.credentials()
|
||||
|
||||
// @ts-expect-error - Incorrect AWS types
|
||||
const identityID = credentials.identityId
|
||||
|
||||
const storageClient = new AWS.S3({
|
||||
credentials,
|
||||
region: process.env.PAYLOAD_CLOUD_BUCKET_REGION,
|
||||
})
|
||||
|
||||
return {
|
||||
identityID,
|
||||
session,
|
||||
storageClient,
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,6 @@
|
||||
"dataloader": "2.2.3",
|
||||
"deepmerge": "4.3.1",
|
||||
"file-type": "19.3.0",
|
||||
"fractional-indexing": "3.2.0",
|
||||
"get-tsconfig": "4.8.1",
|
||||
"http-status": "2.1.0",
|
||||
"image-size": "1.2.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ImportMap } from '../../bin/generateImportMap/index.js'
|
||||
import type { SanitizedConfig } from '../../config/types.js'
|
||||
import type { PaginatedDocs } from '../../database/types.js'
|
||||
import type { CollectionSlug, ColumnPreference } from '../../index.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest, Sort, Where } from '../../types/index.js'
|
||||
|
||||
export type DefaultServerFunctionArgs = {
|
||||
@@ -38,7 +38,6 @@ export type ServerFunctionHandler = (
|
||||
) => Promise<unknown>
|
||||
|
||||
export type ListQuery = {
|
||||
columns?: ColumnPreference[]
|
||||
limit?: string
|
||||
page?: string
|
||||
/*
|
||||
@@ -51,7 +50,7 @@ export type ListQuery = {
|
||||
|
||||
export type BuildTableStateArgs = {
|
||||
collectionSlug: string | string[]
|
||||
columns?: ColumnPreference[]
|
||||
columns?: { accessor: string; active: boolean }[]
|
||||
docs?: PaginatedDocs['docs']
|
||||
enableRowSelections?: boolean
|
||||
parent?: {
|
||||
|
||||
@@ -42,14 +42,8 @@ export type ListViewClientProps = {
|
||||
disableBulkEdit?: boolean
|
||||
enableRowSelections?: boolean
|
||||
hasCreatePermission: boolean
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
listPreferences?: ListPreferences
|
||||
newDocumentURL: string
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
preferenceKey?: string
|
||||
renderedFilters?: Map<string, React.ReactNode>
|
||||
resolvedFilterOptions?: Map<string, ResolvedFilterOptions>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
// @ts-strict-ignore
|
||||
import type { AuthStrategyFunctionArgs, AuthStrategyResult } from './index.js'
|
||||
|
||||
export const executeAuthStrategies = async (
|
||||
args: AuthStrategyFunctionArgs,
|
||||
): Promise<AuthStrategyResult> => {
|
||||
if (!args.payload.authStrategies?.length) {
|
||||
return { user: null }
|
||||
}
|
||||
return args.payload.authStrategies.reduce(
|
||||
async (accumulatorPromise, strategy) => {
|
||||
const result: AuthStrategyResult = await accumulatorPromise
|
||||
if (!result.user) {
|
||||
// add the configured AuthStrategy `name` to the strategy function args
|
||||
args.strategyName = strategy.name
|
||||
|
||||
for (const strategy of args.payload.authStrategies) {
|
||||
// add the configured AuthStrategy `name` to the strategy function args
|
||||
args.strategyName = strategy.name
|
||||
|
||||
const result = await strategy.authenticate(args)
|
||||
if (result.user) {
|
||||
return strategy.authenticate(args)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
return { user: null }
|
||||
},
|
||||
Promise.resolve({ user: null }),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,18 +64,18 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'forgotPassword',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'forgotPassword',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
@@ -190,11 +190,10 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
|
||||
// afterForgotPassword - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterForgotPassword?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterForgotPassword) {
|
||||
await hook({ args, collection: args.collection?.config, context: req.context })
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterForgotPassword.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
await hook({ args, collection: args.collection?.config, context: req.context })
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
|
||||
@@ -51,18 +51,18 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'login',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
operation: 'login',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
@@ -227,17 +227,17 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
|
||||
// beforeLogin - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeLogin?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeLogin) {
|
||||
user =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
req: args.req,
|
||||
user,
|
||||
})) || user
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeLogin.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
user =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
req: args.req,
|
||||
user,
|
||||
})) || user
|
||||
}, Promise.resolve())
|
||||
|
||||
const { exp, token } = await jwtSign({
|
||||
fieldsToSign,
|
||||
@@ -251,18 +251,18 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
|
||||
// afterLogin - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterLogin?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterLogin) {
|
||||
user =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
req: args.req,
|
||||
token,
|
||||
user,
|
||||
})) || user
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterLogin.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
user =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
req: args.req,
|
||||
token,
|
||||
user,
|
||||
})) || user
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Fields
|
||||
@@ -286,17 +286,17 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
user =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: req.context,
|
||||
doc: user,
|
||||
req,
|
||||
})) || user
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
user =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: req.context,
|
||||
doc: user,
|
||||
req,
|
||||
})) || user
|
||||
}, Promise.resolve())
|
||||
|
||||
let result: { user: DataFromCollectionSlug<TSlug> } & Result = {
|
||||
exp,
|
||||
|
||||
@@ -25,16 +25,16 @@ export const logoutOperation = async (incomingArgs: Arguments): Promise<boolean>
|
||||
throw new APIError('Incorrect collection', httpStatus.FORBIDDEN)
|
||||
}
|
||||
|
||||
if (collectionConfig.hooks?.afterLogout?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterLogout) {
|
||||
args =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: req.context,
|
||||
req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterLogout.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: req.context,
|
||||
req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -86,17 +86,17 @@ export const meOperation = async (args: Arguments): Promise<MeOperationResult> =
|
||||
// After Me - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collection.config.hooks?.afterMe?.length) {
|
||||
for (const hook of collection.config.hooks.afterMe) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collection?.config,
|
||||
context: req.context,
|
||||
req,
|
||||
response: result,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collection.config.hooks.afterMe.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collection?.config,
|
||||
context: req.context,
|
||||
req,
|
||||
response: result,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -35,8 +35,10 @@ export const refreshOperation = async (incomingArgs: Arguments): Promise<Result>
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
@@ -45,8 +47,9 @@ export const refreshOperation = async (incomingArgs: Arguments): Promise<Result>
|
||||
operation: 'refresh',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Refresh
|
||||
@@ -119,18 +122,18 @@ export const refreshOperation = async (incomingArgs: Arguments): Promise<Result>
|
||||
// After Refresh - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRefresh?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRefresh) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
exp: result.exp,
|
||||
req: args.req,
|
||||
token: result.refreshedToken,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRefresh.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: args.collection?.config,
|
||||
context: args.req.context,
|
||||
exp: result.exp,
|
||||
req: args.req,
|
||||
token: result.refreshedToken,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
|
||||
@@ -91,17 +91,17 @@ export const resetPasswordOperation = async (args: Arguments): Promise<Result> =
|
||||
// beforeValidate - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeValidate?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeValidate) {
|
||||
await hook({
|
||||
collection: args.collection?.config,
|
||||
context: req.context,
|
||||
data: user,
|
||||
operation: 'update',
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
await hook({
|
||||
collection: args.collection?.config,
|
||||
context: req.context,
|
||||
data: user,
|
||||
operation: 'update',
|
||||
req,
|
||||
})
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Update new password
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { generateKeyBetween, generateNKeysBetween } from 'fractional-indexing'
|
||||
|
||||
// @ts-strict-ignore
|
||||
import type { LoginWithUsernameOptions } from '../../auth/types.js'
|
||||
import type { Config, Endpoint, PayloadHandler, SanitizedConfig } from '../../config/types.js'
|
||||
import type { Field } from '../../fields/config/types.js'
|
||||
import type { Config, SanitizedConfig } from '../../config/types.js'
|
||||
import type {
|
||||
BeforeChangeHook,
|
||||
CollectionConfig,
|
||||
SanitizedCollectionConfig,
|
||||
SanitizedJoin,
|
||||
@@ -242,134 +238,6 @@ export const sanitizeCollection = async (
|
||||
|
||||
validateUseAsTitle(sanitized)
|
||||
|
||||
// duplicated in the UI package too. Don't change one without changing the other.
|
||||
const ORDER_FIELD_NAME = 'payload-order'
|
||||
|
||||
// Enable custom order
|
||||
if (collection.enableCustomOrder) {
|
||||
// 1. Add field
|
||||
const orderField: Field = {
|
||||
name: ORDER_FIELD_NAME,
|
||||
type: 'text',
|
||||
admin: {
|
||||
disableBulkEdit: true,
|
||||
hidden: true,
|
||||
},
|
||||
index: true,
|
||||
label: 'Order',
|
||||
}
|
||||
|
||||
sanitized.fields.unshift(orderField)
|
||||
|
||||
// 2. Add hook
|
||||
if (!sanitized.hooks) {
|
||||
sanitized.hooks = {}
|
||||
}
|
||||
if (!sanitized.hooks.beforeChange) {
|
||||
sanitized.hooks.beforeChange = []
|
||||
}
|
||||
|
||||
const orderBeforeChangeHook: BeforeChangeHook = async ({ data, operation, req }) => {
|
||||
// Only set _order on create, not on update (unless explicitly provided)
|
||||
if (operation === 'create') {
|
||||
// Find the last document to place this one after
|
||||
const lastDoc = await req.payload.find({
|
||||
collection: sanitized.slug,
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
sort: `-${ORDER_FIELD_NAME}`,
|
||||
})
|
||||
|
||||
const lastOrderValue = lastDoc.docs[0]?.[ORDER_FIELD_NAME] || null
|
||||
data[ORDER_FIELD_NAME] = generateKeyBetween(lastOrderValue, null)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
sanitized.hooks.beforeChange.push(orderBeforeChangeHook)
|
||||
|
||||
// 3. Add endpoint
|
||||
const moveBetweenHandler: PayloadHandler = async (req) => {
|
||||
const body = await req.json()
|
||||
const { betweenIds, docIds } = body as {
|
||||
betweenIds: [string | undefined, string | undefined] // tuple [beforeId, afterId]
|
||||
docIds: string[] // array of docIds to be moved between the two reference points
|
||||
}
|
||||
|
||||
if (!Array.isArray(docIds) || docIds.length === 0) {
|
||||
return new Response(JSON.stringify({ error: 'Invalid or empty docIds array' }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
status: 400,
|
||||
})
|
||||
}
|
||||
|
||||
if (!Array.isArray(betweenIds) || betweenIds.length !== 2) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'betweenIds must be a tuple of two elements' }),
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
status: 400,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const [beforeId, afterId] = betweenIds
|
||||
|
||||
// Fetch the order values of the documents we're inserting between
|
||||
let beforeOrderValue = null
|
||||
let afterOrderValue = null
|
||||
|
||||
// TODO: maybe the endpoint can receive directly the order values?
|
||||
if (beforeId) {
|
||||
const beforeDoc = await req.payload.findByID({
|
||||
id: beforeId,
|
||||
collection: sanitized.slug,
|
||||
})
|
||||
beforeOrderValue = beforeDoc?.[ORDER_FIELD_NAME] || null
|
||||
}
|
||||
|
||||
if (afterId) {
|
||||
const afterDoc = await req.payload.findByID({
|
||||
id: afterId,
|
||||
collection: sanitized.slug,
|
||||
})
|
||||
afterOrderValue = afterDoc?.[ORDER_FIELD_NAME] || null
|
||||
}
|
||||
|
||||
const orderValues = generateNKeysBetween(beforeOrderValue, afterOrderValue, docIds.length)
|
||||
|
||||
// Update each document with its new order value
|
||||
const updatePromises = docIds.map((id, index) => {
|
||||
return req.payload.update({
|
||||
id,
|
||||
collection: sanitized.slug,
|
||||
data: {
|
||||
[ORDER_FIELD_NAME]: orderValues[index],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
const results = await Promise.all(updatePromises)
|
||||
|
||||
return new Response(JSON.stringify({ results, success: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
status: 200,
|
||||
})
|
||||
}
|
||||
|
||||
const moveBetweenEndpoint: Endpoint = {
|
||||
handler: moveBetweenHandler,
|
||||
method: 'post',
|
||||
path: '/reorder',
|
||||
}
|
||||
|
||||
if (!sanitized.endpoints) {
|
||||
sanitized.endpoints = []
|
||||
}
|
||||
sanitized.endpoints.push(moveBetweenEndpoint)
|
||||
}
|
||||
|
||||
const sanitizedConfig = sanitized as SanitizedCollectionConfig
|
||||
|
||||
sanitizedConfig.joins = joins
|
||||
|
||||
@@ -409,15 +409,6 @@ export type CollectionConfig<TSlug extends CollectionSlug = any> = {
|
||||
* When true, do not show the "Duplicate" button while editing documents within this collection and prevent `duplicate` from all APIs
|
||||
*/
|
||||
disableDuplicate?: boolean
|
||||
/**
|
||||
* If true, enables custom ordering for the collection, and documents in the listView can be reordered via drag and drop.
|
||||
* New documents are inserted at the end of the list according to this parameter.
|
||||
*
|
||||
* Under the hood, a field with {@link https://observablehq.com/@dgreensp/implementing-fractional-indexing|fractional indexing} is used to optimize inserts and reorderings.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
enableCustomOrder?: boolean
|
||||
/**
|
||||
* Custom rest api endpoints, set false to disable all rest endpoints for this collection.
|
||||
*/
|
||||
|
||||
@@ -44,9 +44,7 @@ const batchAndLoadDocs =
|
||||
*
|
||||
**/
|
||||
|
||||
const batchByFindArgs = {}
|
||||
|
||||
for (const key of keys) {
|
||||
const batchByFindArgs = keys.reduce((batches, key) => {
|
||||
const [
|
||||
transactionID,
|
||||
collection,
|
||||
@@ -79,16 +77,27 @@ const batchAndLoadDocs =
|
||||
const batchKey = JSON.stringify(batchKeyArray)
|
||||
|
||||
const idType = payload.collections?.[collection].customIDType || payload.db.defaultIDType
|
||||
const sanitizedID = idType === 'number' ? parseFloat(id) : id
|
||||
|
||||
let sanitizedID: number | string = id
|
||||
|
||||
if (idType === 'number') {
|
||||
sanitizedID = parseFloat(id)
|
||||
}
|
||||
|
||||
if (isValidID(sanitizedID, idType)) {
|
||||
batchByFindArgs[batchKey] = [...(batchByFindArgs[batchKey] || []), sanitizedID]
|
||||
return {
|
||||
...batches,
|
||||
[batchKey]: [...(batches[batchKey] || []), sanitizedID],
|
||||
}
|
||||
}
|
||||
}
|
||||
return batches
|
||||
}, {})
|
||||
|
||||
// Run find requests one after another, so as to not hang transactions
|
||||
|
||||
for (const [batchKey, ids] of Object.entries(batchByFindArgs)) {
|
||||
await Object.entries(batchByFindArgs).reduce(async (priorFind, [batchKey, ids]) => {
|
||||
await priorFind
|
||||
|
||||
const [
|
||||
transactionID,
|
||||
collection,
|
||||
@@ -128,7 +137,8 @@ const batchAndLoadDocs =
|
||||
|
||||
// For each returned doc, find index in original keys
|
||||
// Inject doc within docs array if index exists
|
||||
for (const doc of result.docs) {
|
||||
|
||||
result.docs.forEach((doc) => {
|
||||
const docKey = createDataloaderCacheKey({
|
||||
collectionSlug: collection,
|
||||
currentDepth,
|
||||
@@ -148,8 +158,8 @@ const batchAndLoadDocs =
|
||||
if (docsIndex > -1) {
|
||||
docs[docsIndex] = doc
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}, Promise.resolve())
|
||||
|
||||
// Return docs array,
|
||||
// which has now been injected with all fetched docs
|
||||
|
||||
@@ -28,18 +28,18 @@ export const countOperation = async <TSlug extends CollectionSlug>(
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'count',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'count',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
|
||||
@@ -28,18 +28,18 @@ export const countVersionsOperation = async <TSlug extends CollectionSlug>(
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'countVersions',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'countVersions',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
|
||||
@@ -78,8 +78,10 @@ export const createOperation = async <
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
@@ -88,8 +90,9 @@ export const createOperation = async <
|
||||
operation: 'create',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
autosave = false,
|
||||
@@ -180,8 +183,10 @@ export const createOperation = async <
|
||||
// beforeValidate - Collections
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks.beforeValidate?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeValidate) {
|
||||
await collectionConfig.hooks.beforeValidate.reduce(
|
||||
async (priorHook: BeforeValidateHook | Promise<void>, hook: BeforeValidateHook) => {
|
||||
await priorHook
|
||||
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
@@ -191,26 +196,27 @@ export const createOperation = async <
|
||||
originalDoc: duplicatedFromDoc,
|
||||
req,
|
||||
})) || data
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeChange - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeChange?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeChange) {
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
data,
|
||||
operation: 'create',
|
||||
originalDoc: duplicatedFromDoc,
|
||||
req,
|
||||
})) || data
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
data,
|
||||
operation: 'create',
|
||||
originalDoc: duplicatedFromDoc,
|
||||
req,
|
||||
})) || data
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeChange - Fields
|
||||
@@ -326,17 +332,17 @@ export const createOperation = async <
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterChange - Fields
|
||||
@@ -357,8 +363,10 @@ export const createOperation = async <
|
||||
// afterChange - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterChange?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterChange) {
|
||||
await collectionConfig.hooks.afterChange.reduce(
|
||||
async (priorHook: AfterChangeHook | Promise<void>, hook: AfterChangeHook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
@@ -368,8 +376,9 @@ export const createOperation = async <
|
||||
previousDoc: {},
|
||||
req: args.req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
|
||||
@@ -54,8 +54,10 @@ export const deleteOperation = async <
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
@@ -64,8 +66,9 @@ export const deleteOperation = async <
|
||||
operation: 'delete',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
@@ -144,16 +147,16 @@ export const deleteOperation = async <
|
||||
// beforeDelete - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeDelete?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeDelete) {
|
||||
await hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeDelete.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
return hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
req,
|
||||
})
|
||||
}, Promise.resolve())
|
||||
|
||||
await deleteAssociatedFiles({
|
||||
collectionConfig,
|
||||
@@ -226,34 +229,34 @@ export const deleteOperation = async <
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result || doc,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result || doc,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterDelete - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterDelete?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterDelete) {
|
||||
result =
|
||||
(await hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterDelete.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 8. Return results
|
||||
|
||||
@@ -48,8 +48,10 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
await args.collection.config.hooks.beforeOperation.reduce(
|
||||
async (priorHook: BeforeOperationHook | Promise<void>, hook: BeforeOperationHook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
@@ -58,8 +60,9 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
||||
operation: 'delete',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
const {
|
||||
id,
|
||||
@@ -92,16 +95,16 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
||||
// beforeDelete - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeDelete?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeDelete) {
|
||||
await hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeDelete.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
return hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
req,
|
||||
})
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Retrieve document
|
||||
@@ -212,34 +215,34 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterDelete - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterDelete?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterDelete) {
|
||||
result =
|
||||
(await hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterDelete.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
id,
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
|
||||
@@ -63,18 +63,18 @@ export const findOperation = async <
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
@@ -257,7 +257,9 @@ export const findOperation = async <
|
||||
result.docs.map(async (doc) => {
|
||||
let docRef = doc
|
||||
|
||||
for (const hook of collectionConfig.hooks.beforeRead) {
|
||||
await collectionConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
docRef =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
@@ -266,7 +268,7 @@ export const findOperation = async <
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || docRef
|
||||
}
|
||||
}, Promise.resolve())
|
||||
|
||||
return docRef
|
||||
}),
|
||||
@@ -308,7 +310,9 @@ export const findOperation = async <
|
||||
result.docs.map(async (doc) => {
|
||||
let docRef = doc
|
||||
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
docRef =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
@@ -318,7 +322,7 @@ export const findOperation = async <
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || doc
|
||||
}
|
||||
}, Promise.resolve())
|
||||
|
||||
return docRef
|
||||
}),
|
||||
|
||||
@@ -54,18 +54,18 @@ export const findByIDOperation = async <
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'read',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
id,
|
||||
@@ -221,18 +221,18 @@ export const findByIDOperation = async <
|
||||
// beforeRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
query: findOneArgs.where,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
query: findOneArgs.where,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Fields
|
||||
@@ -259,18 +259,18 @@ export const findByIDOperation = async <
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
query: findOneArgs.where,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
query: findOneArgs.where,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
|
||||
@@ -101,18 +101,18 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
||||
// beforeRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeRead) {
|
||||
result.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || result.version
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || result.version
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Fields
|
||||
@@ -139,18 +139,18 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || result.version
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || result.version
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
|
||||
@@ -96,19 +96,18 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
||||
if (!docRef.version) {
|
||||
;(docRef as any).version = {}
|
||||
}
|
||||
await collectionConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
if (collectionConfig.hooks?.beforeRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeRead) {
|
||||
docRef.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: docRef.version,
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || docRef.version
|
||||
}
|
||||
}
|
||||
docRef.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: docRef.version,
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || docRef.version
|
||||
}, Promise.resolve())
|
||||
|
||||
return docRef
|
||||
}),
|
||||
@@ -148,7 +147,9 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
||||
result.docs.map(async (doc) => {
|
||||
const docRef = doc
|
||||
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
docRef.version =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
@@ -158,7 +159,7 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || doc.version
|
||||
}
|
||||
}, Promise.resolve())
|
||||
|
||||
return docRef
|
||||
}),
|
||||
|
||||
@@ -165,17 +165,17 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterChange - Fields
|
||||
@@ -196,19 +196,19 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
// afterChange - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterChange?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterChange) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
operation: 'update',
|
||||
previousDoc: prevDocWithLocales,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
operation: 'update',
|
||||
previousDoc: prevDocWithLocales,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
|
||||
@@ -62,18 +62,18 @@ export const updateOperation = async <
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
|
||||
@@ -64,18 +64,18 @@ export const updateByIDOperation = async <
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (args.collection.config.hooks?.beforeOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.beforeOperation) {
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}
|
||||
}
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'update',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
if (args.publishSpecificLocale) {
|
||||
args.req.locale = args.publishSpecificLocale
|
||||
|
||||
@@ -171,19 +171,19 @@ export const updateDocument = async <
|
||||
// beforeValidate - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeValidate?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeValidate) {
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Write files to local storage
|
||||
@@ -197,19 +197,19 @@ export const updateDocument = async <
|
||||
// beforeChange - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.beforeChange?.length) {
|
||||
for (const hook of collectionConfig.hooks.beforeChange) {
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
data =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeChange - Fields
|
||||
@@ -338,17 +338,17 @@ export const updateDocument = async <
|
||||
// afterRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterChange - Fields
|
||||
@@ -369,19 +369,19 @@ export const updateDocument = async <
|
||||
// afterChange - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (collectionConfig.hooks?.afterChange?.length) {
|
||||
for (const hook of collectionConfig.hooks.afterChange) {
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
operation: 'update',
|
||||
previousDoc: originalDoc,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
collection: collectionConfig,
|
||||
context: req.context,
|
||||
doc: result,
|
||||
operation: 'update',
|
||||
previousDoc: originalDoc,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
return result as TransformCollectionWithSelect<TSlug, TSelect>
|
||||
}
|
||||
|
||||
@@ -125,8 +125,10 @@ export const buildAfterOperation = async <
|
||||
|
||||
let newResult = result as OperationResult<TOperationGeneric, O>
|
||||
|
||||
if (args.collection.config.hooks?.afterOperation?.length) {
|
||||
for (const hook of args.collection.config.hooks.afterOperation) {
|
||||
await args.collection.config.hooks.afterOperation.reduce(
|
||||
async (priorHook, hook: AfterOperationHook<TOperationGeneric>) => {
|
||||
await priorHook
|
||||
|
||||
const hookResult = await hook({
|
||||
args,
|
||||
collection,
|
||||
@@ -138,8 +140,9 @@ export const buildAfterOperation = async <
|
||||
if (hookResult !== undefined) {
|
||||
newResult = hookResult as OperationResult<TOperationGeneric, O>
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Promise.resolve(),
|
||||
)
|
||||
|
||||
return newResult
|
||||
}
|
||||
|
||||
@@ -9,10 +9,11 @@ import { sanitizeConfig } from './sanitize.js'
|
||||
*/
|
||||
export async function buildConfig(config: Config): Promise<SanitizedConfig> {
|
||||
if (Array.isArray(config.plugins)) {
|
||||
let configAfterPlugins = config
|
||||
for (const plugin of config.plugins) {
|
||||
configAfterPlugins = await plugin(configAfterPlugins)
|
||||
}
|
||||
const configAfterPlugins = await config.plugins.reduce(async (acc, plugin) => {
|
||||
const configAfterPlugin = await acc
|
||||
return plugin(configAfterPlugin)
|
||||
}, Promise.resolve(config))
|
||||
|
||||
return await sanitizeConfig(configAfterPlugins)
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,10 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
}))
|
||||
} else {
|
||||
// is Locale[], so convert to string[] for localeCodes
|
||||
config.localization.localeCodes = config.localization.locales.map((locale) => locale.code)
|
||||
config.localization.localeCodes = config.localization.locales.reduce((locales, locale) => {
|
||||
locales.push(locale.code)
|
||||
return locales
|
||||
}, [] as string[])
|
||||
|
||||
config.localization.locales = (
|
||||
config.localization as LocalizationConfigWithLabels
|
||||
|
||||
@@ -133,17 +133,17 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
// Execute before global hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.beforeRead?.length) {
|
||||
for (const hook of globalConfig.hooks.beforeRead) {
|
||||
doc =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || doc
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
doc =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || doc
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Execute globalType field if not selected
|
||||
@@ -182,17 +182,17 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
// Execute after global hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of globalConfig.hooks.afterRead) {
|
||||
doc =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || doc
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
doc =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || doc
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
|
||||
@@ -102,17 +102,17 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
||||
// beforeRead - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.beforeRead?.length) {
|
||||
for (const hook of globalConfig.hooks.beforeRead) {
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || result.version
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.beforeRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || result.version
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Fields
|
||||
@@ -139,18 +139,18 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
||||
// afterRead - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of globalConfig.hooks.afterRead) {
|
||||
result.version =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
global: globalConfig,
|
||||
query: findGlobalVersionsArgs.where,
|
||||
req,
|
||||
})) || result.version
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result.version =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result.version,
|
||||
global: globalConfig,
|
||||
query: findGlobalVersionsArgs.where,
|
||||
req,
|
||||
})) || result.version
|
||||
}, Promise.resolve())
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
|
||||
@@ -126,12 +126,15 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
// afterRead - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterRead?.length) {
|
||||
result.docs = await Promise.all(
|
||||
result = {
|
||||
...result,
|
||||
docs: await Promise.all(
|
||||
result.docs.map(async (doc) => {
|
||||
const docRef = doc
|
||||
|
||||
for (const hook of globalConfig.hooks.afterRead) {
|
||||
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
docRef.version =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
@@ -141,11 +144,11 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
query: fullWhere,
|
||||
req,
|
||||
})) || doc.version
|
||||
}
|
||||
}, Promise.resolve())
|
||||
|
||||
return docRef
|
||||
}),
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -143,17 +143,17 @@ export const restoreVersionOperation = async <T extends TypeWithVersion<T> = any
|
||||
// afterRead - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of globalConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterChange - Fields
|
||||
@@ -174,18 +174,18 @@ export const restoreVersionOperation = async <T extends TypeWithVersion<T> = any
|
||||
// afterChange - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterChange?.length) {
|
||||
for (const hook of globalConfig.hooks.afterChange) {
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
previousDoc,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
previousDoc,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
if (shouldCommit) {
|
||||
await commitTransaction(req)
|
||||
|
||||
@@ -168,35 +168,35 @@ export const updateOperation = async <
|
||||
// beforeValidate - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.beforeValidate?.length) {
|
||||
for (const hook of globalConfig.hooks.beforeValidate) {
|
||||
data =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
data,
|
||||
global: globalConfig,
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
data =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
data,
|
||||
global: globalConfig,
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeChange - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.beforeChange?.length) {
|
||||
for (const hook of globalConfig.hooks.beforeChange) {
|
||||
data =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
data,
|
||||
global: globalConfig,
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
data =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
data,
|
||||
global: globalConfig,
|
||||
originalDoc,
|
||||
req,
|
||||
})) || data
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeChange - Fields
|
||||
@@ -326,17 +326,17 @@ export const updateOperation = async <
|
||||
// afterRead - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterRead?.length) {
|
||||
for (const hook of globalConfig.hooks.afterRead) {
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterChange - Fields
|
||||
@@ -357,18 +357,18 @@ export const updateOperation = async <
|
||||
// afterChange - Global
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (globalConfig.hooks?.afterChange?.length) {
|
||||
for (const hook of globalConfig.hooks.afterChange) {
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
previousDoc: originalDoc,
|
||||
req,
|
||||
})) || result
|
||||
}
|
||||
}
|
||||
await globalConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
result =
|
||||
(await hook({
|
||||
context: req.context,
|
||||
doc: result,
|
||||
global: globalConfig,
|
||||
previousDoc: originalDoc,
|
||||
req,
|
||||
})) || result
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
|
||||
@@ -1374,7 +1374,6 @@ export { restoreVersionOperation as restoreVersionOperationGlobal } from './glob
|
||||
export { updateOperation as updateOperationGlobal } from './globals/operations/update.js'
|
||||
export type {
|
||||
CollapsedPreferences,
|
||||
ColumnPreference,
|
||||
DocumentPreferences,
|
||||
FieldsPreferences,
|
||||
InsideFieldsPreferences,
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* @todo remove this function and subsequent hooks in v4
|
||||
* They are used to transform the old shape of `columnPreferences` to new shape
|
||||
* i.e. ({ accessor: string, active: boolean })[] to ({ [accessor: string]: boolean })[]
|
||||
* In v4 can we use the new shape directly
|
||||
*/
|
||||
export const migrateColumns = (value: Record<string, any>) => {
|
||||
if (value && typeof value === 'object' && 'columns' in value && Array.isArray(value.columns)) {
|
||||
value.columns = value.columns.map((col) => {
|
||||
if ('accessor' in col) {
|
||||
return { [col.accessor]: col.active }
|
||||
}
|
||||
|
||||
return col
|
||||
})
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
import type { CollectionConfig } from '../collections/config/types.js'
|
||||
import type { Access, Config } from '../config/types.js'
|
||||
|
||||
import { migrateColumns } from './migrateColumns.js'
|
||||
import { deleteHandler } from './requestHandlers/delete.js'
|
||||
import { findByIDHandler } from './requestHandlers/findOne.js'
|
||||
import { updateHandler } from './requestHandlers/update.js'
|
||||
@@ -77,14 +76,6 @@ const getPreferencesCollection = (config: Config): CollectionConfig => ({
|
||||
{
|
||||
name: 'value',
|
||||
type: 'json',
|
||||
/**
|
||||
* @todo remove these hooks in v4
|
||||
* See `migrateColumns` for more information
|
||||
*/
|
||||
hooks: {
|
||||
afterRead: [({ value }) => migrateColumns(value)],
|
||||
beforeValidate: [({ value }) => migrateColumns(value)],
|
||||
},
|
||||
validate: (value) => {
|
||||
if (value) {
|
||||
try {
|
||||
|
||||
@@ -28,12 +28,8 @@ export type DocumentPreferences = {
|
||||
fields: FieldsPreferences
|
||||
}
|
||||
|
||||
export type ColumnPreference = {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
||||
export type ListPreferences = {
|
||||
columns?: ColumnPreference[]
|
||||
columns?: { accessor: string; active: boolean }[]
|
||||
limit?: number
|
||||
sort?: string
|
||||
}
|
||||
|
||||
@@ -1156,13 +1156,14 @@ export function configToJSONSchema(
|
||||
)
|
||||
: {}
|
||||
|
||||
const blocksDefinition: JSONSchema4 | undefined = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
required: [],
|
||||
}
|
||||
let blocksDefinition: JSONSchema4 | undefined = undefined
|
||||
if (config?.blocks?.length) {
|
||||
blocksDefinition = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
required: [],
|
||||
}
|
||||
for (const block of config.blocks) {
|
||||
const blockFieldSchemas = fieldsToJSONSchema(
|
||||
collectionIDFieldTypes,
|
||||
|
||||
@@ -4,9 +4,12 @@ import { useModal } from '@faceless-ui/modal'
|
||||
import React from 'react'
|
||||
|
||||
import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import { ConfirmationModal } from '../../ConfirmationModal/index.js'
|
||||
import { Button } from '../../Button/index.js'
|
||||
import { FullscreenModal } from '../../FullscreenModal/index.js'
|
||||
import { useBulkUpload } from '../index.js'
|
||||
|
||||
export const discardBulkUploadModalSlug = 'bulk-upload--discard-without-saving'
|
||||
const baseClass = 'leave-without-saving'
|
||||
|
||||
export function DiscardWithoutSaving() {
|
||||
const { t } = useTranslation()
|
||||
@@ -23,14 +26,21 @@ export function DiscardWithoutSaving() {
|
||||
}, [closeModal, drawerSlug])
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
body={t('general:changesNotSaved')}
|
||||
cancelLabel={t('general:stayOnThisPage')}
|
||||
confirmLabel={t('general:leaveAnyway')}
|
||||
heading={t('general:leaveWithoutSaving')}
|
||||
modalSlug={discardBulkUploadModalSlug}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
/>
|
||||
<FullscreenModal className={baseClass} slug={discardBulkUploadModalSlug}>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
<div className={`${baseClass}__content`}>
|
||||
<h1>{t('general:leaveWithoutSaving')}</h1>
|
||||
<p>{t('general:changesNotSaved')}</p>
|
||||
</div>
|
||||
<div className={`${baseClass}__controls`}>
|
||||
<Button buttonStyle="secondary" onClick={onCancel} size="large">
|
||||
{t('general:stayOnThisPage')}
|
||||
</Button>
|
||||
<Button onClick={onConfirm} size="large">
|
||||
{t('general:leaveAnyway')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</FullscreenModal>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use client'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { useRouter } from 'next/navigation.js'
|
||||
import { useSearchParams } from 'next/navigation.js'
|
||||
import * as qs from 'qs-esm'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import { useLocale, useLocaleLoading } from '../../providers/Locale/index.js'
|
||||
import { useRouteTransition } from '../../providers/RouteTransition/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { parseSearchParams } from '../../utilities/parseSearchParams.js'
|
||||
import { Popup, PopupList } from '../Popup/index.js'
|
||||
import './index.scss'
|
||||
import { LocalizerLabel } from './LocalizerLabel/index.js'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'localizer'
|
||||
|
||||
@@ -21,10 +21,7 @@ export const Localizer: React.FC<{
|
||||
const {
|
||||
config: { localization },
|
||||
} = useConfig()
|
||||
|
||||
const router = useRouter()
|
||||
const { startRouteTransition } = useRouteTransition()
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const { setLocaleIsLoading } = useLocaleLoading()
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
@@ -47,28 +44,17 @@ export const Localizer: React.FC<{
|
||||
<PopupList.Button
|
||||
active={locale.code === localeOption.code}
|
||||
disabled={locale.code === localeOption.code}
|
||||
href={qs.stringify(
|
||||
{
|
||||
...parseSearchParams(searchParams),
|
||||
locale: localeOption.code,
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)}
|
||||
key={localeOption.code}
|
||||
onClick={() => {
|
||||
setLocaleIsLoading(true)
|
||||
close()
|
||||
|
||||
// can't use `useSearchParams` here because it is stale due to `window.history.pushState` in `ListQueryProvider`
|
||||
const searchParams = new URLSearchParams(window.location.search)
|
||||
|
||||
const url = qs.stringify(
|
||||
{
|
||||
...qs.parse(searchParams.toString(), {
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
}),
|
||||
locale: localeOption.code,
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
|
||||
startRouteTransition(() => {
|
||||
router.push(url)
|
||||
})
|
||||
}}
|
||||
>
|
||||
{localeOptionLabel !== localeOption.code ? (
|
||||
|
||||
@@ -222,7 +222,7 @@ export function PublishButton({ label: labelProp }: PublishButtonClientProps) {
|
||||
)}
|
||||
{localization && canPublish && (
|
||||
<PopupList.ButtonGroup>
|
||||
<PopupList.Button id="publish-locale" onClick={secondaryPublish}>
|
||||
<PopupList.Button onClick={secondaryPublish}>
|
||||
{secondaryLabel}
|
||||
</PopupList.Button>
|
||||
</PopupList.ButtonGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import type {
|
||||
CollectionSlug,
|
||||
Column,
|
||||
ColumnPreference,
|
||||
JoinFieldClient,
|
||||
ListQuery,
|
||||
PaginatedDocs,
|
||||
@@ -26,6 +25,7 @@ import { useServerFunctions } from '../../providers/ServerFunctions/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { hoistQueryParamsToAnd } from '../../utilities/mergeListSearchAndWhere.js'
|
||||
import { AnimateHeight } from '../AnimateHeight/index.js'
|
||||
import './index.scss'
|
||||
import { ColumnSelector } from '../ColumnSelector/index.js'
|
||||
import { useDocumentDrawer } from '../DocumentDrawer/index.js'
|
||||
import { Popup, PopupList } from '../Popup/index.js'
|
||||
@@ -33,7 +33,6 @@ import { RelationshipProvider } from '../Table/RelationshipProvider/index.js'
|
||||
import { TableColumnsProvider } from '../TableColumns/index.js'
|
||||
import { DrawerLink } from './cells/DrawerLink/index.js'
|
||||
import { RelationshipTablePagination } from './Pagination.js'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'relationship-table'
|
||||
|
||||
@@ -124,10 +123,11 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
newQuery.where = hoistQueryParamsToAnd(newQuery.where, filterOptions)
|
||||
}
|
||||
|
||||
// map columns from string[] to ColumnPreference[]
|
||||
const defaultColumns: ColumnPreference[] = field.admin.defaultColumns
|
||||
// map columns from string[] to ListPreferences['columns']
|
||||
const defaultColumns = field.admin.defaultColumns
|
||||
? field.admin.defaultColumns.map((accessor) => ({
|
||||
[accessor]: true,
|
||||
accessor,
|
||||
active: true,
|
||||
}))
|
||||
: undefined
|
||||
|
||||
@@ -137,7 +137,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
Table: NewTable,
|
||||
} = await getTableState({
|
||||
collectionSlug: relationTo,
|
||||
columns: query?.columns || defaultColumns,
|
||||
columns: defaultColumns,
|
||||
docs,
|
||||
enableRowSelections: false,
|
||||
parent,
|
||||
@@ -154,6 +154,7 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
[
|
||||
field.defaultLimit,
|
||||
field.defaultSort,
|
||||
field.admin.defaultColumns,
|
||||
collectionConfig?.admin?.pagination?.defaultLimit,
|
||||
collectionConfig?.defaultSort,
|
||||
query,
|
||||
@@ -214,6 +215,8 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
[data?.docs, renderTable],
|
||||
)
|
||||
|
||||
const preferenceKey = `${Array.isArray(relationTo) ? `${parent.collectionSlug}-${parent.joinPath}` : relationTo}-list`
|
||||
|
||||
const canCreate =
|
||||
allowCreate !== false &&
|
||||
permissions?.collections?.[Array.isArray(relationTo) ? relationTo[0] : relationTo]?.create
|
||||
@@ -323,7 +326,6 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
{data?.docs && data.docs.length > 0 && (
|
||||
<RelationshipProvider>
|
||||
<ListQueryProvider
|
||||
columns={columnState.map(({ accessor, active }) => ({ [accessor]: active }))}
|
||||
data={data}
|
||||
defaultLimit={
|
||||
field.defaultLimit ?? collectionConfig?.admin?.pagination?.defaultLimit
|
||||
@@ -334,9 +336,17 @@ export const RelationshipTable: React.FC<RelationshipTableComponentProps> = (pro
|
||||
<TableColumnsProvider
|
||||
collectionSlug={Array.isArray(relationTo) ? relationTo[0] : relationTo}
|
||||
columnState={columnState}
|
||||
docs={data.docs}
|
||||
LinkedCellOverride={
|
||||
<DrawerLink onDrawerDelete={onDrawerDelete} onDrawerSave={onDrawerSave} />
|
||||
}
|
||||
preferenceKey={preferenceKey}
|
||||
renderRowTypes
|
||||
setTable={setTable}
|
||||
sortColumnProps={{
|
||||
appearance: 'condensed',
|
||||
}}
|
||||
tableAppearance="condensed"
|
||||
>
|
||||
<AnimateHeight
|
||||
className={`${baseClass}__columns`}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
@import '../../scss/styles.scss';
|
||||
|
||||
@layer payload-default {
|
||||
.sort-row {
|
||||
cursor: grab;
|
||||
|
||||
&__icon {
|
||||
display: block;
|
||||
width: min-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
import { DragHandleIcon } from '../../icons/DragHandle/index.js'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'sort-row'
|
||||
|
||||
export const SortRow = () => {
|
||||
return (
|
||||
<div className={baseClass} role="button" tabIndex={0}>
|
||||
<DragHandleIcon className={`${baseClass}__icon`} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -5,155 +5,55 @@ import type { Column } from 'payload'
|
||||
import React from 'react'
|
||||
|
||||
import './index.scss'
|
||||
import { useListQuery } from '../../providers/ListQuery/index.js'
|
||||
import { DraggableSortableItem } from '../DraggableSortable/DraggableSortableItem/index.js'
|
||||
import { DraggableSortable } from '../DraggableSortable/index.js'
|
||||
|
||||
const baseClass = 'table'
|
||||
|
||||
// if you change this, you need to change it in a couple of places where it's duplicated
|
||||
const ORDER_FIELD_NAME = 'payload-order'
|
||||
|
||||
export type Props = {
|
||||
readonly appearance?: 'condensed' | 'default'
|
||||
readonly columns?: Column[]
|
||||
readonly data: { [key: string]: unknown; id: string; 'payload-order': string }[]
|
||||
readonly data: Record<string, unknown>[]
|
||||
}
|
||||
|
||||
export const Table: React.FC<Props> = ({ appearance, columns, data: initialData }) => {
|
||||
const { handleSortChange, query } = useListQuery()
|
||||
const [data, setData] = React.useState(initialData)
|
||||
|
||||
// Force re-sort when data changes
|
||||
React.useEffect(() => {
|
||||
if (query.sort) {
|
||||
void handleSortChange(query.sort as string).catch((error) => {
|
||||
throw error
|
||||
})
|
||||
}
|
||||
}, [data, handleSortChange, query.sort])
|
||||
|
||||
export const Table: React.FC<Props> = ({ appearance, columns, data }) => {
|
||||
const activeColumns = columns?.filter((col) => col?.active)
|
||||
|
||||
if (!activeColumns || activeColumns.length === 0) {
|
||||
return <div>No columns selected</div>
|
||||
}
|
||||
|
||||
const handleDragEnd = async ({ moveFromIndex, moveToIndex }) => {
|
||||
if (moveFromIndex === moveToIndex) {
|
||||
return
|
||||
}
|
||||
|
||||
const movedId = data[moveFromIndex].id
|
||||
const newBeforeRow = moveToIndex > moveFromIndex ? data[moveToIndex] : data[moveToIndex - 1]
|
||||
const newAfterRow = moveToIndex > moveFromIndex ? data[moveToIndex + 1] : data[moveToIndex]
|
||||
|
||||
// To debug:
|
||||
// console.log(
|
||||
// `moving ${data[moveFromIndex]?.text} between ${newBeforeRow?.text} and ${newAfterRow?.text}`,
|
||||
// )
|
||||
|
||||
// Store the original data for rollback
|
||||
const previousData = [...data]
|
||||
|
||||
// TODO: this optimistic update is not working (the table is not re-rendered)
|
||||
// you can't debug it commenting the try block. Every move needs to be followed by
|
||||
// a refresh of the page to see the changes.
|
||||
setData((currentData) => {
|
||||
const newData = [...currentData]
|
||||
newData[moveFromIndex] = {
|
||||
...newData[moveFromIndex],
|
||||
[ORDER_FIELD_NAME]: `${newBeforeRow?.[ORDER_FIELD_NAME]}_pending`,
|
||||
}
|
||||
// move from index to moveToIndex
|
||||
newData.splice(moveToIndex, 0, newData.splice(moveFromIndex, 1)[0])
|
||||
|
||||
return newData
|
||||
})
|
||||
|
||||
try {
|
||||
// Assuming we're in the context of a collection
|
||||
const collectionSlug = window.location.pathname.split('/').filter(Boolean)[2]
|
||||
const response = await fetch(`/api/${collectionSlug}/reorder`, {
|
||||
body: JSON.stringify({
|
||||
betweenIds: [newBeforeRow?.id, newAfterRow?.id],
|
||||
docIds: [movedId],
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to reorder')
|
||||
}
|
||||
|
||||
// no need to update the data here, the data is updated in the useListQuery provider
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error reordering:', error)
|
||||
// Rollback to previous state if the request fails
|
||||
setData(previousData)
|
||||
// Optionally show an error notification
|
||||
}
|
||||
}
|
||||
|
||||
const rowIds = data.map((row) => row.id || String(Math.random()))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[baseClass, appearance && `${baseClass}--appearance-${appearance}`]
|
||||
.filter(Boolean)
|
||||
.join(' ')}
|
||||
>
|
||||
<DraggableSortable ids={rowIds} onDragEnd={handleDragEnd}>
|
||||
<table cellPadding="0" cellSpacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
{activeColumns.map((col, i) => (
|
||||
<th id={`heading-${col.accessor}`} key={i}>
|
||||
{col.Heading}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((row, rowIndex) => (
|
||||
<DraggableSortableItem id={rowIds[rowIndex]} key={rowIds[rowIndex]}>
|
||||
{({ attributes, listeners, setNodeRef, transform, transition }) => (
|
||||
<tr
|
||||
className={`row-${rowIndex + 1}`}
|
||||
ref={setNodeRef}
|
||||
style={{
|
||||
transform,
|
||||
transition,
|
||||
}}
|
||||
>
|
||||
{activeColumns.map((col, colIndex) => {
|
||||
const { accessor } = col
|
||||
if (accessor === '_dragHandle') {
|
||||
return (
|
||||
<td className={`cell-${accessor}`} key={colIndex}>
|
||||
<div {...attributes} {...listeners}>
|
||||
{col.renderedCells[rowIndex]}
|
||||
</div>
|
||||
</td>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<td className={`cell-${accessor}`} key={colIndex}>
|
||||
{col.renderedCells[rowIndex]}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)}
|
||||
</DraggableSortableItem>
|
||||
<table cellPadding="0" cellSpacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
{activeColumns.map((col, i) => (
|
||||
<th id={`heading-${col.accessor}`} key={i}>
|
||||
{col.Heading}
|
||||
</th>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</DraggableSortable>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data &&
|
||||
data.map((row, rowIndex) => (
|
||||
<tr className={`row-${rowIndex + 1}`} key={rowIndex}>
|
||||
{activeColumns.map((col, colIndex) => {
|
||||
const { accessor } = col
|
||||
|
||||
return (
|
||||
<td className={`cell-${accessor}`} key={colIndex}>
|
||||
{col.renderedCells[rowIndex]}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import type {
|
||||
ClientComponentProps,
|
||||
ClientField,
|
||||
Column,
|
||||
ColumnPreference,
|
||||
DefaultCellComponentProps,
|
||||
DefaultServerCellComponentProps,
|
||||
Field,
|
||||
ListPreferences,
|
||||
PaginatedDocs,
|
||||
Payload,
|
||||
SanitizedCollectionConfig,
|
||||
@@ -39,8 +39,8 @@ type Args = {
|
||||
beforeRows?: Column[]
|
||||
clientCollectionConfig: ClientCollectionConfig
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
columnPreferences: ColumnPreference[]
|
||||
columns?: ColumnPreference[]
|
||||
columnPreferences: ListPreferences['columns']
|
||||
columns?: ListPreferences['columns']
|
||||
customCellProps: DefaultCellComponentProps['customCellProps']
|
||||
docs: PaginatedDocs['docs']
|
||||
enableRowSelections: boolean
|
||||
@@ -99,10 +99,10 @@ export const buildColumnState = (args: Args): Column[] => {
|
||||
|
||||
const sortTo = columnPreferences || columns
|
||||
|
||||
const sortFieldMap = (fieldMap, sortTo: ColumnPreference[]) =>
|
||||
const sortFieldMap = (fieldMap, sortTo) =>
|
||||
fieldMap?.sort((a, b) => {
|
||||
const aIndex = sortTo.findIndex((column) => 'name' in a && a.name in column)
|
||||
const bIndex = sortTo.findIndex((column) => 'name' in b && b.name in column)
|
||||
const aIndex = sortTo.findIndex((column) => 'name' in a && column.accessor === a.name)
|
||||
const bIndex = sortTo.findIndex((column) => 'name' in b && column.accessor === b.name)
|
||||
|
||||
if (aIndex === -1 && bIndex === -1) {
|
||||
return 0
|
||||
@@ -136,12 +136,18 @@ export const buildColumnState = (args: Args): Column[] => {
|
||||
(f) => 'name' in field && 'name' in f && f.name === field.name,
|
||||
)
|
||||
|
||||
const columnPreference = columnPreferences?.find(
|
||||
(preference) => field && 'name' in field && preference.accessor === field.name,
|
||||
)
|
||||
|
||||
let active = false
|
||||
|
||||
if (columnPreferences) {
|
||||
active = 'name' in field && columnPreferences?.some((col) => col?.[field.name])
|
||||
if (columnPreference) {
|
||||
active = columnPreference.active
|
||||
} else if (columns && Array.isArray(columns) && columns.length > 0) {
|
||||
active = 'name' in field && columns.some((col) => col?.[field.name])
|
||||
active = columns.find(
|
||||
(column) => field && 'name' in field && column.accessor === field.name,
|
||||
)?.active
|
||||
} else if (activeColumnsIndices.length < 4) {
|
||||
active = true
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import type { I18nClient } from '@payloadcms/translations'
|
||||
import type {
|
||||
ClientField,
|
||||
Column,
|
||||
ColumnPreference,
|
||||
DefaultCellComponentProps,
|
||||
DefaultServerCellComponentProps,
|
||||
Field,
|
||||
ListPreferences,
|
||||
PaginatedDocs,
|
||||
Payload,
|
||||
SanitizedCollectionConfig,
|
||||
@@ -36,8 +36,8 @@ import { filterFields } from './filterFields.js'
|
||||
|
||||
type Args = {
|
||||
beforeRows?: Column[]
|
||||
columnPreferences: ColumnPreference[]
|
||||
columns?: ColumnPreference[]
|
||||
columnPreferences: ListPreferences['columns']
|
||||
columns?: ListPreferences['columns']
|
||||
customCellProps: DefaultCellComponentProps['customCellProps']
|
||||
docs: PaginatedDocs['docs']
|
||||
enableRowSelections: boolean
|
||||
@@ -92,8 +92,8 @@ export const buildPolymorphicColumnState = (args: Args): Column[] => {
|
||||
|
||||
const sortFieldMap = (fieldMap, sortTo) =>
|
||||
fieldMap?.sort((a, b) => {
|
||||
const aIndex = sortTo.findIndex((column) => 'name' in a && a.name in column)
|
||||
const bIndex = sortTo.findIndex((column) => 'name' in b && b.name in column)
|
||||
const aIndex = sortTo.findIndex((column) => 'name' in a && column.accessor === a.name)
|
||||
const bIndex = sortTo.findIndex((column) => 'name' in b && column.accessor === b.name)
|
||||
|
||||
if (aIndex === -1 && bIndex === -1) {
|
||||
return 0
|
||||
@@ -127,12 +127,18 @@ export const buildPolymorphicColumnState = (args: Args): Column[] => {
|
||||
(f) => 'name' in field && 'name' in f && f.name === field.name,
|
||||
)
|
||||
|
||||
const columnPreference = columnPreferences?.find(
|
||||
(preference) => field && 'name' in field && preference.accessor === field.name,
|
||||
)
|
||||
|
||||
let active = false
|
||||
|
||||
if (columnPreferences) {
|
||||
active = 'name' in field && columnPreferences?.some((col) => col?.[field.name])
|
||||
if (columnPreference) {
|
||||
active = columnPreference.active
|
||||
} else if (columns && Array.isArray(columns) && columns.length > 0) {
|
||||
active = 'name' in field && columns.some((col) => col?.[field.name])
|
||||
active = columns.find(
|
||||
(column) => field && 'name' in field && column.accessor === field.name,
|
||||
)?.active
|
||||
} else if (activeColumnsIndices.length < 4) {
|
||||
active = true
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
|
||||
import type { ITableColumns } from './types.js'
|
||||
|
||||
export const TableColumnContext = createContext<ITableColumns>({} as ITableColumns)
|
||||
|
||||
export const useTableColumns = (): ITableColumns => useContext(TableColumnContext)
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { ClientField, CollectionConfig, ColumnPreference, Field } from 'payload'
|
||||
import type { ClientField, CollectionConfig, Field, ListPreferences } from 'payload'
|
||||
|
||||
import { fieldAffectsData } from 'payload/shared'
|
||||
|
||||
const getRemainingColumns = <T extends ClientField[] | Field[]>(
|
||||
fields: T,
|
||||
useAsTitle: string,
|
||||
): ColumnPreference[] =>
|
||||
): ListPreferences['columns'] =>
|
||||
fields?.reduce((remaining, field) => {
|
||||
if (fieldAffectsData(field) && field.name === useAsTitle) {
|
||||
return remaining
|
||||
@@ -40,7 +40,7 @@ export const getInitialColumns = <T extends ClientField[] | Field[]>(
|
||||
fields: T,
|
||||
useAsTitle: CollectionConfig['admin']['useAsTitle'],
|
||||
defaultColumns: CollectionConfig['admin']['defaultColumns'],
|
||||
): ColumnPreference[] => {
|
||||
): ListPreferences['columns'] => {
|
||||
let initialColumns = []
|
||||
|
||||
if (Array.isArray(defaultColumns) && defaultColumns.length >= 1) {
|
||||
@@ -57,6 +57,7 @@ export const getInitialColumns = <T extends ClientField[] | Field[]>(
|
||||
}
|
||||
|
||||
return initialColumns.map((column) => ({
|
||||
[column]: true,
|
||||
accessor: column,
|
||||
active: true,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,99 +1,293 @@
|
||||
'use client'
|
||||
import type { Column } from 'payload'
|
||||
import type { Column, ListPreferences, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import React, { startTransition, useCallback } from 'react'
|
||||
import React, { createContext, useCallback, useContext, useEffect } from 'react'
|
||||
|
||||
import type { TableColumnsProviderProps } from './types.js'
|
||||
import type { SortColumnProps } from '../SortColumn/index.js'
|
||||
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import { useListQuery } from '../../providers/ListQuery/index.js'
|
||||
import { TableColumnContext } from './context.js'
|
||||
import { usePreferences } from '../../providers/Preferences/index.js'
|
||||
import { useServerFunctions } from '../../providers/ServerFunctions/index.js'
|
||||
import { abortAndIgnore, handleAbortRef } from '../../utilities/abortAndIgnore.js'
|
||||
|
||||
export { useTableColumns } from './context.js'
|
||||
export interface ITableColumns {
|
||||
columns: Column[]
|
||||
LinkedCellOverride?: React.ReactNode
|
||||
moveColumn: (args: { fromIndex: number; toIndex: number }) => Promise<void>
|
||||
resetColumnsState: () => Promise<void>
|
||||
setActiveColumns: (columns: string[]) => Promise<void>
|
||||
toggleColumn: (column: string) => Promise<void>
|
||||
}
|
||||
|
||||
export const TableColumnsProvider: React.FC<TableColumnsProviderProps> = ({
|
||||
export const TableColumnContext = createContext<ITableColumns>({} as ITableColumns)
|
||||
|
||||
export const useTableColumns = (): ITableColumns => useContext(TableColumnContext)
|
||||
|
||||
type Props = {
|
||||
readonly children: React.ReactNode
|
||||
readonly collectionSlug: string | string[]
|
||||
readonly columnState: Column[]
|
||||
readonly docs: any[]
|
||||
readonly enableRowSelections?: boolean
|
||||
readonly LinkedCellOverride?: React.ReactNode
|
||||
readonly listPreferences?: ListPreferences
|
||||
readonly preferenceKey: string
|
||||
readonly renderRowTypes?: boolean
|
||||
readonly setTable: (Table: React.ReactNode) => void
|
||||
readonly sortColumnProps?: Partial<SortColumnProps>
|
||||
readonly tableAppearance?: 'condensed' | 'default'
|
||||
}
|
||||
|
||||
// strip out Heading, Label, and renderedCells properties, they cannot be sent to the server
|
||||
const sanitizeColumns = (columns: Column[]) => {
|
||||
return columns.map(({ accessor, active }) => ({
|
||||
accessor,
|
||||
active,
|
||||
}))
|
||||
}
|
||||
|
||||
export const TableColumnsProvider: React.FC<Props> = ({
|
||||
children,
|
||||
collectionSlug,
|
||||
columnState: columnStateFromProps,
|
||||
columnState,
|
||||
docs,
|
||||
enableRowSelections,
|
||||
LinkedCellOverride,
|
||||
listPreferences,
|
||||
preferenceKey,
|
||||
renderRowTypes,
|
||||
setTable,
|
||||
sortColumnProps,
|
||||
tableAppearance,
|
||||
}) => {
|
||||
const { getEntityConfig } = useConfig()
|
||||
const { query: currentQuery, refineListData } = useListQuery()
|
||||
|
||||
const { admin: { defaultColumns } = {} } = getEntityConfig({
|
||||
const { getTableState } = useServerFunctions()
|
||||
|
||||
const { admin: { defaultColumns, useAsTitle } = {}, fields } = getEntityConfig({
|
||||
collectionSlug,
|
||||
})
|
||||
|
||||
const [columnState, setOptimisticColumnState] = React.useOptimistic(
|
||||
columnStateFromProps,
|
||||
(state, action: Column[]) => action,
|
||||
const prevCollection = React.useRef<SanitizedCollectionConfig['slug']>(
|
||||
Array.isArray(collectionSlug) ? collectionSlug[0] : collectionSlug,
|
||||
)
|
||||
const { getPreference } = usePreferences()
|
||||
|
||||
const [tableColumns, setTableColumns] = React.useState(columnState)
|
||||
const abortTableStateRef = React.useRef<AbortController>(null)
|
||||
const abortToggleColumnRef = React.useRef<AbortController>(null)
|
||||
|
||||
const moveColumn = useCallback(
|
||||
async (args: { fromIndex: number; toIndex: number }) => {
|
||||
const controller = handleAbortRef(abortTableStateRef)
|
||||
|
||||
const { fromIndex, toIndex } = args
|
||||
const withMovedColumn = [...tableColumns]
|
||||
const [columnToMove] = withMovedColumn.splice(fromIndex, 1)
|
||||
withMovedColumn.splice(toIndex, 0, columnToMove)
|
||||
|
||||
setTableColumns(withMovedColumn)
|
||||
|
||||
const result = await getTableState({
|
||||
collectionSlug,
|
||||
columns: sanitizeColumns(withMovedColumn),
|
||||
docs,
|
||||
enableRowSelections,
|
||||
renderRowTypes,
|
||||
signal: controller.signal,
|
||||
tableAppearance,
|
||||
})
|
||||
|
||||
if (result) {
|
||||
setTableColumns(result.state)
|
||||
setTable(result.Table)
|
||||
}
|
||||
|
||||
abortTableStateRef.current = null
|
||||
},
|
||||
[
|
||||
tableColumns,
|
||||
collectionSlug,
|
||||
docs,
|
||||
getTableState,
|
||||
setTable,
|
||||
enableRowSelections,
|
||||
renderRowTypes,
|
||||
tableAppearance,
|
||||
],
|
||||
)
|
||||
|
||||
const toggleColumn = useCallback(
|
||||
async (column: string) => {
|
||||
const newColumnState = (columnState || []).map((col) => {
|
||||
if (col.accessor === column) {
|
||||
return { ...col, active: !col.active }
|
||||
}
|
||||
return col
|
||||
})
|
||||
const controller = handleAbortRef(abortToggleColumnRef)
|
||||
|
||||
startTransition(() => {
|
||||
setOptimisticColumnState(newColumnState)
|
||||
})
|
||||
|
||||
await refineListData({
|
||||
columns: newColumnState.map((col) => ({ [col.accessor]: col.active })),
|
||||
})
|
||||
},
|
||||
[refineListData, columnState, setOptimisticColumnState],
|
||||
)
|
||||
|
||||
const moveColumn = useCallback(
|
||||
async (args: { fromIndex: number; toIndex: number }) => {
|
||||
const { fromIndex, toIndex } = args
|
||||
const newColumnState = [...(columnState || [])]
|
||||
const [columnToMove] = newColumnState.splice(fromIndex, 1)
|
||||
newColumnState.splice(toIndex, 0, columnToMove)
|
||||
|
||||
startTransition(() => {
|
||||
setOptimisticColumnState(newColumnState)
|
||||
})
|
||||
|
||||
await refineListData({
|
||||
columns: newColumnState.map((col) => ({ [col.accessor]: col.active })),
|
||||
})
|
||||
},
|
||||
[columnState, refineListData, setOptimisticColumnState],
|
||||
)
|
||||
|
||||
const setActiveColumns = useCallback(
|
||||
async (columns: string[]) => {
|
||||
const newColumnState = currentQuery.columns
|
||||
|
||||
columns.forEach((colName) => {
|
||||
const colIndex = newColumnState.findIndex((c) => colName in c)
|
||||
|
||||
if (colIndex !== undefined) {
|
||||
newColumnState[colIndex] = {
|
||||
[colName]: true,
|
||||
const { newColumnState, toggledColumns } = tableColumns.reduce<{
|
||||
newColumnState: Column[]
|
||||
toggledColumns: Pick<Column, 'accessor' | 'active'>[]
|
||||
}>(
|
||||
(acc, col) => {
|
||||
if (col.accessor === column) {
|
||||
acc.newColumnState.push({
|
||||
...col,
|
||||
accessor: col.accessor,
|
||||
active: !col.active,
|
||||
})
|
||||
acc.toggledColumns.push({
|
||||
accessor: col.accessor,
|
||||
active: !col.active,
|
||||
})
|
||||
} else {
|
||||
acc.newColumnState.push(col)
|
||||
acc.toggledColumns.push({
|
||||
accessor: col.accessor,
|
||||
active: col.active,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
{ newColumnState: [], toggledColumns: [] },
|
||||
)
|
||||
|
||||
setTableColumns(newColumnState)
|
||||
|
||||
const result = await getTableState({
|
||||
collectionSlug,
|
||||
columns: toggledColumns,
|
||||
docs,
|
||||
enableRowSelections,
|
||||
renderRowTypes,
|
||||
signal: controller.signal,
|
||||
tableAppearance,
|
||||
})
|
||||
|
||||
await refineListData({ columns: newColumnState })
|
||||
if (result) {
|
||||
setTableColumns(result.state)
|
||||
setTable(result.Table)
|
||||
}
|
||||
|
||||
abortToggleColumnRef.current = null
|
||||
},
|
||||
[currentQuery, refineListData],
|
||||
[
|
||||
tableColumns,
|
||||
getTableState,
|
||||
setTable,
|
||||
collectionSlug,
|
||||
docs,
|
||||
enableRowSelections,
|
||||
renderRowTypes,
|
||||
tableAppearance,
|
||||
],
|
||||
)
|
||||
|
||||
const setActiveColumns = React.useCallback(
|
||||
async (activeColumnAccessors: string[]) => {
|
||||
const activeColumns: Pick<Column, 'accessor' | 'active'>[] = tableColumns
|
||||
.map((col) => {
|
||||
return {
|
||||
accessor: col.accessor,
|
||||
active: activeColumnAccessors.includes(col.accessor),
|
||||
}
|
||||
})
|
||||
.sort((first, second) => {
|
||||
const indexOfFirst = activeColumnAccessors.indexOf(first.accessor)
|
||||
const indexOfSecond = activeColumnAccessors.indexOf(second.accessor)
|
||||
|
||||
if (indexOfFirst === -1 || indexOfSecond === -1) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return indexOfFirst > indexOfSecond ? 1 : -1
|
||||
})
|
||||
|
||||
const { state: columnState, Table } = await getTableState({
|
||||
collectionSlug,
|
||||
columns: activeColumns,
|
||||
docs,
|
||||
enableRowSelections,
|
||||
renderRowTypes,
|
||||
tableAppearance,
|
||||
})
|
||||
|
||||
setTableColumns(columnState)
|
||||
setTable(Table)
|
||||
},
|
||||
[
|
||||
tableColumns,
|
||||
getTableState,
|
||||
setTable,
|
||||
collectionSlug,
|
||||
docs,
|
||||
enableRowSelections,
|
||||
renderRowTypes,
|
||||
tableAppearance,
|
||||
],
|
||||
)
|
||||
|
||||
const resetColumnsState = React.useCallback(async () => {
|
||||
await setActiveColumns(defaultColumns)
|
||||
}, [defaultColumns, setActiveColumns])
|
||||
|
||||
// //////////////////////////////////////////////
|
||||
// Get preferences on collection change (drawers)
|
||||
// //////////////////////////////////////////////
|
||||
|
||||
React.useEffect(() => {
|
||||
const sync = async () => {
|
||||
const defaultCollection = Array.isArray(collectionSlug) ? collectionSlug[0] : collectionSlug
|
||||
const collectionHasChanged = prevCollection.current !== defaultCollection
|
||||
|
||||
if (collectionHasChanged || !listPreferences) {
|
||||
const currentPreferences = await getPreference<{
|
||||
columns: ListPreferences['columns']
|
||||
}>(preferenceKey)
|
||||
|
||||
prevCollection.current = defaultCollection
|
||||
|
||||
if (currentPreferences?.columns) {
|
||||
// setTableColumns()
|
||||
// buildColumnState({
|
||||
// beforeRows,
|
||||
// columnPreferences: currentPreferences?.columns,
|
||||
// columns: initialColumns,
|
||||
// enableRowSelections,
|
||||
// fields,
|
||||
// sortColumnProps,
|
||||
// useAsTitle,
|
||||
// }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sync()
|
||||
}, [
|
||||
preferenceKey,
|
||||
getPreference,
|
||||
collectionSlug,
|
||||
fields,
|
||||
defaultColumns,
|
||||
useAsTitle,
|
||||
listPreferences,
|
||||
enableRowSelections,
|
||||
sortColumnProps,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
setTableColumns(columnState)
|
||||
}, [columnState])
|
||||
|
||||
useEffect(() => {
|
||||
const abortTableState = abortTableStateRef.current
|
||||
|
||||
return () => {
|
||||
abortAndIgnore(abortTableState)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<TableColumnContext.Provider
|
||||
value={{
|
||||
columns: columnState,
|
||||
columns: tableColumns,
|
||||
LinkedCellOverride,
|
||||
moveColumn,
|
||||
resetColumnsState,
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import type { Column, ListPreferences } from 'payload'
|
||||
|
||||
import type { SortColumnProps } from '../SortColumn/index.js'
|
||||
|
||||
export interface ITableColumns {
|
||||
columns: Column[]
|
||||
LinkedCellOverride?: React.ReactNode
|
||||
moveColumn: (args: { fromIndex: number; toIndex: number }) => Promise<void>
|
||||
resetColumnsState: () => Promise<void>
|
||||
setActiveColumns: (columns: string[]) => Promise<void>
|
||||
toggleColumn: (column: string) => Promise<void>
|
||||
}
|
||||
|
||||
export type TableColumnsProviderProps = {
|
||||
readonly children: React.ReactNode
|
||||
readonly collectionSlug: string | string[]
|
||||
readonly columnState: Column[]
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly docs?: any[]
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly enableRowSelections?: boolean
|
||||
readonly LinkedCellOverride?: React.ReactNode
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly listPreferences?: ListPreferences
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly preferenceKey?: string
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly renderRowTypes?: boolean
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly setTable?: (Table: React.ReactNode) => void
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly sortColumnProps?: Partial<SortColumnProps>
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly tableAppearance?: 'condensed' | 'default'
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import type { AddCondition, ReducedField, RemoveCondition, UpdateCondition } from '../types.js'
|
||||
import type { AddCondition, ReducedField, UpdateCondition } from '../types.js'
|
||||
|
||||
export type Props = {
|
||||
readonly addCondition: AddCondition
|
||||
@@ -11,7 +11,7 @@ export type Props = {
|
||||
readonly operator: Operator
|
||||
readonly orIndex: number
|
||||
readonly reducedFields: ReducedField[]
|
||||
readonly removeCondition: RemoveCondition
|
||||
readonly removeCondition: ({ andIndex, orIndex }: { andIndex: number; orIndex: number }) => void
|
||||
readonly RenderedFilter: React.ReactNode
|
||||
readonly updateCondition: UpdateCondition
|
||||
readonly value: string
|
||||
@@ -67,9 +67,9 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
valueOptions = reducedField.field.options
|
||||
}
|
||||
|
||||
const updateValue = useEffectEvent(async (debouncedValue) => {
|
||||
const updateValue = useEffectEvent((debouncedValue) => {
|
||||
if (operator) {
|
||||
await updateCondition({
|
||||
updateCondition({
|
||||
andIndex,
|
||||
field: reducedField,
|
||||
operator,
|
||||
@@ -80,7 +80,7 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
void updateValue(debouncedValue)
|
||||
updateValue(debouncedValue)
|
||||
}, [debouncedValue])
|
||||
|
||||
const disabled =
|
||||
@@ -88,9 +88,9 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
reducedField?.field?.admin?.disableListFilter
|
||||
|
||||
const handleFieldChange = useCallback(
|
||||
async (field: Option<string>) => {
|
||||
(field: Option<string>) => {
|
||||
setInternalValue(undefined)
|
||||
await updateCondition({
|
||||
updateCondition({
|
||||
andIndex,
|
||||
field: reducedFields.find((option) => option.value === field.value),
|
||||
operator,
|
||||
@@ -102,8 +102,8 @@ export const Condition: React.FC<Props> = (props) => {
|
||||
)
|
||||
|
||||
const handleOperatorChange = useCallback(
|
||||
async (operator: Option<Operator>) => {
|
||||
await updateCondition({
|
||||
(operator: Option<Operator>) => {
|
||||
updateCondition({
|
||||
andIndex,
|
||||
field: reducedField,
|
||||
operator: operator.value,
|
||||
|
||||
@@ -4,9 +4,8 @@ import type { Operator, Where } from 'payload'
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import type { AddCondition, RemoveCondition, UpdateCondition, WhereBuilderProps } from './types.js'
|
||||
import type { AddCondition, UpdateCondition, WhereBuilderProps } from './types.js'
|
||||
|
||||
import { useEffectEvent } from '../../hooks/useEffectEvent.js'
|
||||
import { useListQuery } from '../../providers/ListQuery/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { Button } from '../Button/index.js'
|
||||
@@ -32,6 +31,7 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
const reducedFields = useMemo(() => reduceFields({ fields, i18n }), [fields, i18n])
|
||||
|
||||
const { handleWhereChange, query } = useListQuery()
|
||||
const [shouldUpdateQuery, setShouldUpdateQuery] = React.useState(false)
|
||||
|
||||
const [conditions, setConditions] = React.useState<Where[]>(() => {
|
||||
const whereFromSearch = query.where
|
||||
@@ -55,7 +55,7 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
})
|
||||
|
||||
const addCondition: AddCondition = React.useCallback(
|
||||
async ({ andIndex, field, orIndex, relation }) => {
|
||||
({ andIndex, field, orIndex, relation }) => {
|
||||
const newConditions = [...conditions]
|
||||
|
||||
const defaultOperator = fieldTypes[field.field.type].operators[0].value
|
||||
@@ -79,13 +79,12 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
}
|
||||
|
||||
setConditions(newConditions)
|
||||
await handleWhereChange({ or: conditions })
|
||||
},
|
||||
[conditions, handleWhereChange],
|
||||
[conditions],
|
||||
)
|
||||
|
||||
const updateCondition: UpdateCondition = React.useCallback(
|
||||
async ({ andIndex, field, operator: incomingOperator, orIndex, value: valueArg }) => {
|
||||
({ andIndex, field, operator: incomingOperator, orIndex, value: valueArg }) => {
|
||||
const existingRowCondition = conditions[orIndex].and[andIndex]
|
||||
|
||||
const defaults = fieldTypes[field.field.type]
|
||||
@@ -102,14 +101,14 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
newConditions[orIndex].and[andIndex] = newRowCondition
|
||||
|
||||
setConditions(newConditions)
|
||||
await handleWhereChange({ or: conditions })
|
||||
setShouldUpdateQuery(true)
|
||||
}
|
||||
},
|
||||
[conditions, handleWhereChange],
|
||||
[conditions],
|
||||
)
|
||||
|
||||
const removeCondition: RemoveCondition = React.useCallback(
|
||||
async ({ andIndex, orIndex }) => {
|
||||
const removeCondition = React.useCallback(
|
||||
({ andIndex, orIndex }) => {
|
||||
const newConditions = [...conditions]
|
||||
newConditions[orIndex].and.splice(andIndex, 1)
|
||||
|
||||
@@ -118,11 +117,21 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
}
|
||||
|
||||
setConditions(newConditions)
|
||||
await handleWhereChange({ or: conditions })
|
||||
setShouldUpdateQuery(true)
|
||||
},
|
||||
[conditions, handleWhereChange],
|
||||
[conditions],
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (shouldUpdateQuery) {
|
||||
async function handleChange() {
|
||||
await handleWhereChange({ or: conditions })
|
||||
setShouldUpdateQuery(false)
|
||||
}
|
||||
void handleChange()
|
||||
}
|
||||
}, [conditions, handleWhereChange, shouldUpdateQuery])
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
{conditions.length > 0 && (
|
||||
@@ -180,8 +189,8 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
icon="plus"
|
||||
iconPosition="left"
|
||||
iconStyle="with-border"
|
||||
onClick={async () => {
|
||||
await addCondition({
|
||||
onClick={() => {
|
||||
addCondition({
|
||||
andIndex: 0,
|
||||
field: reducedFields[0],
|
||||
orIndex: conditions.length,
|
||||
@@ -202,9 +211,9 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
icon="plus"
|
||||
iconPosition="left"
|
||||
iconStyle="with-border"
|
||||
onClick={async () => {
|
||||
onClick={() => {
|
||||
if (reducedFields.length > 0) {
|
||||
await addCondition({
|
||||
addCondition({
|
||||
andIndex: 0,
|
||||
field: reducedFields.find((field) => !field.field.admin?.disableListFilter),
|
||||
orIndex: conditions.length,
|
||||
|
||||
@@ -65,7 +65,7 @@ export type AddCondition = ({
|
||||
field: ReducedField
|
||||
orIndex: number
|
||||
relation: 'and' | 'or'
|
||||
}) => Promise<void> | void
|
||||
}) => void
|
||||
|
||||
export type UpdateCondition = ({
|
||||
andIndex,
|
||||
@@ -79,12 +79,4 @@ export type UpdateCondition = ({
|
||||
operator: string
|
||||
orIndex: number
|
||||
value: string
|
||||
}) => Promise<void> | void
|
||||
|
||||
export type RemoveCondition = ({
|
||||
andIndex,
|
||||
orIndex,
|
||||
}: {
|
||||
andIndex: number
|
||||
orIndex: number
|
||||
}) => Promise<void> | void
|
||||
}) => void
|
||||
|
||||
@@ -10,10 +10,10 @@ import { useField } from '../../forms/useField/index.js'
|
||||
import { withCondition } from '../../forms/withCondition/index.js'
|
||||
import { FieldDescription } from '../FieldDescription/index.js'
|
||||
import { FieldError } from '../FieldError/index.js'
|
||||
import './index.scss'
|
||||
import { FieldLabel } from '../FieldLabel/index.js'
|
||||
import { mergeFieldStyles } from '../mergeFieldStyles.js'
|
||||
import { fieldBaseClass } from '../shared/index.js'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'json-field'
|
||||
|
||||
@@ -31,9 +31,10 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
readOnly,
|
||||
validate,
|
||||
} = props
|
||||
|
||||
const [stringValue, setStringValue] = useState<string>()
|
||||
const [jsonError, setJsonError] = useState<string>()
|
||||
const inputChangeFromRef = React.useRef<'system' | 'user'>('system')
|
||||
const [editorKey, setEditorKey] = useState<string>('')
|
||||
const [hasLoadedValue, setHasLoadedValue] = useState(false)
|
||||
|
||||
const memoizedValidate = useCallback(
|
||||
(value, options) => {
|
||||
@@ -55,12 +56,6 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
validate: memoizedValidate,
|
||||
})
|
||||
|
||||
const [initialStringValue, setInitialStringValue] = useState<string | undefined>(() =>
|
||||
(value || initialValue) !== undefined
|
||||
? JSON.stringify(value ?? initialValue, null, 2)
|
||||
: undefined,
|
||||
)
|
||||
|
||||
const handleMount = useCallback<OnMount>(
|
||||
(editor, monaco) => {
|
||||
if (!jsonSchema) {
|
||||
@@ -93,7 +88,7 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
if (readOnly) {
|
||||
return
|
||||
}
|
||||
inputChangeFromRef.current = 'user'
|
||||
setStringValue(val)
|
||||
|
||||
try {
|
||||
setValue(val ? JSON.parse(val) : null)
|
||||
@@ -103,21 +98,20 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
setJsonError(e)
|
||||
}
|
||||
},
|
||||
[readOnly, setValue],
|
||||
[readOnly, setValue, setStringValue],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (inputChangeFromRef.current === 'system') {
|
||||
setInitialStringValue(
|
||||
(value || initialValue) !== undefined
|
||||
? JSON.stringify(value ?? initialValue, null, 2)
|
||||
: undefined,
|
||||
)
|
||||
setEditorKey(new Date().toString())
|
||||
if (hasLoadedValue || value === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
inputChangeFromRef.current = 'system'
|
||||
}, [initialValue, value])
|
||||
setStringValue(
|
||||
value || initialValue ? JSON.stringify(value ? value : initialValue, null, 2) : '',
|
||||
)
|
||||
|
||||
setHasLoadedValue(true)
|
||||
}, [initialValue, value, hasLoadedValue])
|
||||
|
||||
const styles = useMemo(() => mergeFieldStyles(field), [field])
|
||||
|
||||
@@ -148,16 +142,12 @@ const JSONFieldComponent: JSONFieldClientComponent = (props) => {
|
||||
{BeforeInput}
|
||||
<CodeEditor
|
||||
defaultLanguage="json"
|
||||
key={editorKey}
|
||||
maxHeight={maxHeight}
|
||||
onChange={handleChange}
|
||||
onMount={handleMount}
|
||||
options={editorOptions}
|
||||
readOnly={readOnly}
|
||||
value={initialStringValue}
|
||||
wrapperProps={{
|
||||
id: `field-${path?.replace(/\./g, '__')}`,
|
||||
}}
|
||||
value={stringValue}
|
||||
/>
|
||||
{AfterInput}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
|
||||
import type { IListQueryContext } from './types.js'
|
||||
|
||||
export const ListQueryContext = createContext({} as IListQueryContext)
|
||||
|
||||
export const useListQuery = (): IListQueryContext => useContext(ListQueryContext)
|
||||
@@ -1,50 +1,57 @@
|
||||
'use client'
|
||||
import type { ColumnPreference, ListQuery, Where } from 'payload'
|
||||
import type { ListQuery, PaginatedDocs, Sort, Where } from 'payload'
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation.js'
|
||||
import { isNumber } from 'payload/shared'
|
||||
import * as qs from 'qs-esm'
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import type { ListQueryProps } from './types.js'
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { useListDrawerContext } from '../../elements/ListDrawer/Provider.js'
|
||||
import { useEffectEvent } from '../../hooks/useEffectEvent.js'
|
||||
import { useRouteTransition } from '../../providers/RouteTransition/index.js'
|
||||
import { parseSearchParams } from '../../utilities/parseSearchParams.js'
|
||||
import { ListQueryContext } from './context.js'
|
||||
|
||||
export { useListQuery } from './context.js'
|
||||
type ContextHandlers = {
|
||||
handlePageChange?: (page: number) => Promise<void>
|
||||
handlePerPageChange?: (limit: number) => Promise<void>
|
||||
handleSearchChange?: (search: string) => Promise<void>
|
||||
handleSortChange?: (sort: string) => Promise<void>
|
||||
handleWhereChange?: (where: Where) => Promise<void>
|
||||
}
|
||||
|
||||
export type ListQueryProps = {
|
||||
readonly children: React.ReactNode
|
||||
readonly collectionSlug?: string
|
||||
readonly data: PaginatedDocs
|
||||
readonly defaultLimit?: number
|
||||
readonly defaultSort?: Sort
|
||||
readonly modifySearchParams?: boolean
|
||||
readonly onQueryChange?: (query: ListQuery) => void
|
||||
readonly preferenceKey?: string
|
||||
}
|
||||
|
||||
export type ListQueryContext = {
|
||||
data: PaginatedDocs
|
||||
defaultLimit?: number
|
||||
defaultSort?: Sort
|
||||
query: ListQuery
|
||||
refineListData: (args: ListQuery) => Promise<void>
|
||||
} & ContextHandlers
|
||||
|
||||
const Context = createContext({} as ListQueryContext)
|
||||
|
||||
export const useListQuery = (): ListQueryContext => useContext(Context)
|
||||
|
||||
export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
children,
|
||||
columns,
|
||||
data,
|
||||
defaultLimit,
|
||||
defaultSort,
|
||||
listPreferences,
|
||||
modifySearchParams,
|
||||
onQueryChange: onQueryChangeFromProps,
|
||||
}) => {
|
||||
'use no memo'
|
||||
const router = useRouter()
|
||||
const rawSearchParams = useSearchParams()
|
||||
const { startRouteTransition } = useRouteTransition()
|
||||
|
||||
const searchParams = useMemo<ListQuery>(() => {
|
||||
const parsed = parseSearchParams(rawSearchParams)
|
||||
const result: ListQuery = parsed
|
||||
|
||||
if (parsed.columns) {
|
||||
try {
|
||||
result.columns = JSON.parse(parsed.columns as string) as ColumnPreference[]
|
||||
} catch (error) {
|
||||
console.error('Error parsing columns from URL:', error) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}, [rawSearchParams])
|
||||
const searchParams = useMemo(() => parseSearchParams(rawSearchParams), [rawSearchParams])
|
||||
|
||||
const { onQueryChange } = useListDrawerContext()
|
||||
|
||||
@@ -56,6 +63,8 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
}
|
||||
})
|
||||
|
||||
const currentQueryRef = React.useRef(currentQuery)
|
||||
|
||||
// If the search params change externally, update the current query
|
||||
useEffect(() => {
|
||||
if (modifySearchParams) {
|
||||
@@ -73,7 +82,6 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
}
|
||||
|
||||
const newQuery: ListQuery = {
|
||||
columns: 'columns' in query ? query.columns : currentQuery.columns,
|
||||
limit: 'limit' in query ? query.limit : (currentQuery?.limit ?? String(defaultLimit)),
|
||||
page,
|
||||
search: 'search' in query ? query.search : currentQuery?.search,
|
||||
@@ -82,14 +90,7 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
}
|
||||
|
||||
if (modifySearchParams) {
|
||||
startRouteTransition(() =>
|
||||
router.replace(
|
||||
`${qs.stringify(
|
||||
{ ...newQuery, columns: JSON.stringify(newQuery.columns) },
|
||||
{ addQueryPrefix: true },
|
||||
)}`,
|
||||
),
|
||||
)
|
||||
router.replace(`${qs.stringify(newQuery, { addQueryPrefix: true })}`)
|
||||
} else if (
|
||||
typeof onQueryChange === 'function' ||
|
||||
typeof onQueryChangeFromProps === 'function'
|
||||
@@ -101,13 +102,7 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
setCurrentQuery(newQuery)
|
||||
},
|
||||
[
|
||||
currentQuery?.columns,
|
||||
currentQuery?.limit,
|
||||
currentQuery?.page,
|
||||
currentQuery?.search,
|
||||
currentQuery?.sort,
|
||||
currentQuery?.where,
|
||||
startRouteTransition,
|
||||
currentQuery,
|
||||
defaultLimit,
|
||||
defaultSort,
|
||||
modifySearchParams,
|
||||
@@ -153,50 +148,34 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
[refineListData],
|
||||
)
|
||||
|
||||
const syncQuery = useEffectEvent(() => {
|
||||
let shouldUpdateQueryString = false
|
||||
const newQuery = { ...(currentQuery || {}) }
|
||||
|
||||
// Allow the URL to override the default limit
|
||||
if (isNumber(defaultLimit) && !('limit' in currentQuery)) {
|
||||
newQuery.limit = String(defaultLimit)
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
|
||||
// Allow the URL to override the default sort
|
||||
if (defaultSort && !('sort' in currentQuery)) {
|
||||
newQuery.sort = defaultSort
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
|
||||
// Only modify columns if they originated from preferences
|
||||
// We can assume they did if `listPreferences.columns` is defined
|
||||
if (columns && listPreferences?.columns && !('columns' in currentQuery)) {
|
||||
newQuery.columns = columns
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
|
||||
if (shouldUpdateQueryString) {
|
||||
setCurrentQuery(newQuery)
|
||||
// Do not use router.replace here to avoid re-rendering on initial load
|
||||
window.history.replaceState(
|
||||
null,
|
||||
'',
|
||||
`?${qs.stringify({ ...newQuery, columns: JSON.stringify(newQuery.columns) })}`,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// If `defaultLimit` or `defaultSort` are updated externally, update the query
|
||||
// I.e. when HMR runs, these properties may be different
|
||||
useEffect(() => {
|
||||
if (modifySearchParams) {
|
||||
syncQuery()
|
||||
let shouldUpdateQueryString = false
|
||||
const newQuery = { ...(currentQueryRef.current || {}) }
|
||||
|
||||
// Allow the URL to override the default limit
|
||||
if (isNumber(defaultLimit) && !('limit' in currentQueryRef.current)) {
|
||||
newQuery.limit = String(defaultLimit)
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
|
||||
// Allow the URL to override the default sort
|
||||
if (defaultSort && !('sort' in currentQueryRef.current)) {
|
||||
newQuery.sort = defaultSort
|
||||
shouldUpdateQueryString = true
|
||||
}
|
||||
|
||||
if (shouldUpdateQueryString) {
|
||||
setCurrentQuery(newQuery)
|
||||
router.replace(`${qs.stringify(newQuery, { addQueryPrefix: true })}`)
|
||||
}
|
||||
}
|
||||
}, [defaultSort, defaultLimit, modifySearchParams, columns])
|
||||
}, [defaultSort, defaultLimit, router, modifySearchParams])
|
||||
|
||||
return (
|
||||
<ListQueryContext.Provider
|
||||
<Context.Provider
|
||||
value={{
|
||||
data,
|
||||
handlePageChange,
|
||||
@@ -209,6 +188,6 @@ export const ListQueryProvider: React.FC<ListQueryProps> = ({
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ListQueryContext.Provider>
|
||||
</Context.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import type {
|
||||
ColumnPreference,
|
||||
ListPreferences,
|
||||
ListQuery,
|
||||
PaginatedDocs,
|
||||
Sort,
|
||||
Where,
|
||||
} from 'payload'
|
||||
|
||||
type ContextHandlers = {
|
||||
handlePageChange?: (page: number) => Promise<void>
|
||||
handlePerPageChange?: (limit: number) => Promise<void>
|
||||
handleSearchChange?: (search: string) => Promise<void>
|
||||
handleSortChange?: (sort: string) => Promise<void>
|
||||
handleWhereChange?: (where: Where) => Promise<void>
|
||||
}
|
||||
|
||||
export type OnListQueryChange = (query: ListQuery) => void
|
||||
|
||||
export type ListQueryProps = {
|
||||
readonly children: React.ReactNode
|
||||
readonly collectionSlug?: string
|
||||
readonly columns?: ColumnPreference[]
|
||||
readonly data: PaginatedDocs
|
||||
readonly defaultLimit?: number
|
||||
readonly defaultSort?: Sort
|
||||
readonly listPreferences?: ListPreferences
|
||||
readonly modifySearchParams?: boolean
|
||||
readonly onQueryChange?: OnListQueryChange
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
readonly preferenceKey?: string
|
||||
}
|
||||
|
||||
export type IListQueryContext = {
|
||||
data: PaginatedDocs
|
||||
defaultLimit?: number
|
||||
defaultSort?: Sort
|
||||
query: ListQuery
|
||||
refineListData: (args: ListQuery) => Promise<void>
|
||||
} & ContextHandlers
|
||||
@@ -3,10 +3,9 @@ import type {
|
||||
ClientConfig,
|
||||
ClientField,
|
||||
CollectionConfig,
|
||||
Column,
|
||||
ColumnPreference,
|
||||
Field,
|
||||
ImportMap,
|
||||
ListPreferences,
|
||||
PaginatedDocs,
|
||||
Payload,
|
||||
SanitizedCollectionConfig,
|
||||
@@ -15,12 +14,15 @@ import type {
|
||||
import { getTranslation, type I18nClient } from '@payloadcms/translations'
|
||||
import { fieldAffectsData, fieldIsHiddenOrDisabled, flattenTopLevelFields } from 'payload/shared'
|
||||
|
||||
// eslint-disable-next-line payload/no-imports-from-exports-dir
|
||||
import type { Column } from '../exports/client/index.js'
|
||||
|
||||
import { RenderServerComponent } from '../elements/RenderServerComponent/index.js'
|
||||
import { SortRow } from '../elements/SortRow/index.js'
|
||||
import { buildColumnState } from '../elements/TableColumns/buildColumnState.js'
|
||||
import { buildPolymorphicColumnState } from '../elements/TableColumns/buildPolymorphicColumnState.js'
|
||||
import { filterFields } from '../elements/TableColumns/filterFields.js'
|
||||
import { getInitialColumns } from '../elements/TableColumns/getInitialColumns.js'
|
||||
|
||||
// eslint-disable-next-line payload/no-imports-from-exports-dir
|
||||
import { Pill, SelectAll, SelectRow, Table } from '../exports/client/index.js'
|
||||
|
||||
@@ -49,9 +51,6 @@ export const renderFilters = (
|
||||
new Map() as Map<string, React.ReactNode>,
|
||||
)
|
||||
|
||||
// Add drag handle column if documents have payload-order field
|
||||
export const ORDER_FIELD_NAME = 'payload-order'
|
||||
|
||||
export const renderTable = ({
|
||||
clientCollectionConfig,
|
||||
clientConfig,
|
||||
@@ -72,8 +71,8 @@ export const renderTable = ({
|
||||
clientConfig?: ClientConfig
|
||||
collectionConfig?: SanitizedCollectionConfig
|
||||
collections?: string[]
|
||||
columnPreferences: ColumnPreference[]
|
||||
columns?: ColumnPreference[]
|
||||
columnPreferences: ListPreferences['columns']
|
||||
columns?: ListPreferences['columns']
|
||||
customCellProps?: Record<string, any>
|
||||
docs: PaginatedDocs['docs']
|
||||
drawerSlug?: string
|
||||
@@ -110,7 +109,7 @@ export const renderTable = ({
|
||||
const columns = columnsFromArgs
|
||||
? columnsFromArgs?.filter((column) =>
|
||||
flattenTopLevelFields(fields, true)?.some(
|
||||
(field) => 'name' in field && column[field.name],
|
||||
(field) => 'name' in field && field.name === column.accessor,
|
||||
),
|
||||
)
|
||||
: getInitialColumns(fields, useAsTitle, [])
|
||||
@@ -131,7 +130,7 @@ export const renderTable = ({
|
||||
const columns = columnsFromArgs
|
||||
? columnsFromArgs?.filter((column) =>
|
||||
flattenTopLevelFields(clientCollectionConfig.fields, true)?.some(
|
||||
(field) => 'name' in field && field.name in column,
|
||||
(field) => 'name' in field && field.name === column.accessor,
|
||||
),
|
||||
)
|
||||
: getInitialColumns(
|
||||
@@ -196,23 +195,6 @@ export const renderTable = ({
|
||||
} as Column)
|
||||
}
|
||||
|
||||
const showDragHandle = docs.length > 0 && ORDER_FIELD_NAME in docs[0]
|
||||
|
||||
if (showDragHandle) {
|
||||
columnsToUse.unshift({
|
||||
accessor: '_dragHandle',
|
||||
active: true,
|
||||
field: {
|
||||
admin: {
|
||||
disabled: true,
|
||||
},
|
||||
hidden: true,
|
||||
},
|
||||
Heading: '', // Empty header
|
||||
renderedCells: docs.map((_, i) => <SortRow key={i} rowData={docs[i]} />),
|
||||
} as Column)
|
||||
}
|
||||
|
||||
return {
|
||||
columnState,
|
||||
Table: <Table appearance={tableAppearance} columns={columnsToUse} data={docs} />,
|
||||
|
||||
@@ -49,12 +49,6 @@
|
||||
display: flex;
|
||||
min-width: unset;
|
||||
}
|
||||
|
||||
#heading-_dragHandle,
|
||||
.cell-_dragHandle {
|
||||
width: 20px;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,9 @@ export function DefaultListView(props: ListViewClientProps) {
|
||||
enableRowSelections,
|
||||
hasCreatePermission: hasCreatePermissionFromProps,
|
||||
listMenuItems,
|
||||
listPreferences,
|
||||
newDocumentURL,
|
||||
preferenceKey,
|
||||
renderedFilters,
|
||||
resolvedFilterOptions,
|
||||
Table: InitialTable,
|
||||
@@ -149,10 +151,17 @@ export function DefaultListView(props: ListViewClientProps) {
|
||||
])
|
||||
}
|
||||
}, [setStepNav, labels, drawerDepth])
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<TableColumnsProvider collectionSlug={collectionSlug} columnState={columnState}>
|
||||
<TableColumnsProvider
|
||||
collectionSlug={collectionSlug}
|
||||
columnState={columnState}
|
||||
docs={docs}
|
||||
enableRowSelections={enableRowSelections}
|
||||
listPreferences={listPreferences}
|
||||
preferenceKey={preferenceKey}
|
||||
setTable={setTable}
|
||||
>
|
||||
<div className={`${baseClass} ${baseClass}--${collectionSlug}`}>
|
||||
<SelectionProvider docs={docs} totalDocs={data.totalDocs} user={user}>
|
||||
{BeforeList}
|
||||
|
||||
91
pnpm-lock.yaml
generated
91
pnpm-lock.yaml
generated
@@ -45,7 +45,7 @@ importers:
|
||||
version: 1.50.0
|
||||
'@sentry/nextjs':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))
|
||||
'@sentry/node':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1
|
||||
@@ -135,7 +135,7 @@ importers:
|
||||
version: 10.1.3(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)
|
||||
next:
|
||||
specifier: 15.1.5
|
||||
version: 15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
version: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
open:
|
||||
specifier: ^10.1.0
|
||||
version: 10.1.0
|
||||
@@ -810,9 +810,6 @@ importers:
|
||||
file-type:
|
||||
specifier: 19.3.0
|
||||
version: 19.3.0
|
||||
fractional-indexing:
|
||||
specifier: 3.2.0
|
||||
version: 3.2.0
|
||||
get-tsconfig:
|
||||
specifier: 4.8.1
|
||||
version: 4.8.1
|
||||
@@ -1014,7 +1011,7 @@ importers:
|
||||
dependencies:
|
||||
next:
|
||||
specifier: ^15.0.3
|
||||
version: 15.1.3(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
version: 15.1.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
devDependencies:
|
||||
'@payloadcms/eslint-config':
|
||||
specifier: workspace:*
|
||||
@@ -1076,7 +1073,7 @@ importers:
|
||||
dependencies:
|
||||
'@sentry/nextjs':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))
|
||||
'@sentry/types':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1
|
||||
@@ -1426,7 +1423,7 @@ importers:
|
||||
version: link:../plugin-cloud-storage
|
||||
uploadthing:
|
||||
specifier: 7.3.0
|
||||
version: 7.3.0(next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))
|
||||
version: 7.3.0(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))
|
||||
devDependencies:
|
||||
payload:
|
||||
specifier: workspace:*
|
||||
@@ -1706,7 +1703,7 @@ importers:
|
||||
version: link:../packages/ui
|
||||
'@sentry/nextjs':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))
|
||||
'@sentry/react':
|
||||
specifier: ^7.77.0
|
||||
version: 7.119.2(react@19.0.0)
|
||||
@@ -1760,7 +1757,7 @@ importers:
|
||||
version: 8.9.5(@aws-sdk/credential-providers@3.687.0(@aws-sdk/client-sso-oidc@3.687.0(@aws-sdk/client-sts@3.687.0)))(socks@2.8.3)
|
||||
next:
|
||||
specifier: 15.1.5
|
||||
version: 15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
version: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
nodemailer:
|
||||
specifier: 6.9.16
|
||||
version: 6.9.16
|
||||
@@ -3658,67 +3655,79 @@ packages:
|
||||
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-arm@1.0.5':
|
||||
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-s390x@1.0.4':
|
||||
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linux-x64@1.0.4':
|
||||
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
|
||||
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
|
||||
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linux-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-arm@0.33.5':
|
||||
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-s390x@0.33.5':
|
||||
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linux-x64@0.33.5':
|
||||
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@img/sharp-linuxmusl-arm64@0.33.5':
|
||||
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-linuxmusl-x64@0.33.5':
|
||||
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@img/sharp-wasm32@0.33.5':
|
||||
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
|
||||
@@ -4034,42 +4043,49 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/nice-linux-arm64-musl@1.0.1':
|
||||
resolution: {integrity: sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@napi-rs/nice-linux-ppc64-gnu@1.0.1':
|
||||
resolution: {integrity: sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/nice-linux-riscv64-gnu@1.0.1':
|
||||
resolution: {integrity: sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/nice-linux-s390x-gnu@1.0.1':
|
||||
resolution: {integrity: sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/nice-linux-x64-gnu@1.0.1':
|
||||
resolution: {integrity: sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@napi-rs/nice-linux-x64-musl@1.0.1':
|
||||
resolution: {integrity: sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@napi-rs/nice-win32-arm64-msvc@1.0.1':
|
||||
resolution: {integrity: sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==}
|
||||
@@ -4158,72 +4174,84 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-arm64-gnu@15.1.3':
|
||||
resolution: {integrity: sha512-YbdaYjyHa4fPK4GR4k2XgXV0p8vbU1SZh7vv6El4bl9N+ZSiMfbmqCuCuNU1Z4ebJMumafaz6UCC2zaJCsdzjw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-arm64-gnu@15.1.5':
|
||||
resolution: {integrity: sha512-rDJC4ctlYbK27tCyFUhgIv8o7miHNlpCjb2XXfTLQszwAUOSbcMN9q2y3urSrrRCyGVOd9ZR9a4S45dRh6JF3A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.0.3':
|
||||
resolution: {integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.1.3':
|
||||
resolution: {integrity: sha512-qgH/aRj2xcr4BouwKG3XdqNu33SDadqbkqB6KaZZkozar857upxKakbRllpqZgWl/NDeSCBYPmUAZPBHZpbA0w==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.1.5':
|
||||
resolution: {integrity: sha512-FG5RApf4Gu+J+pHUQxXPM81oORZrKBYKUaBTylEIQ6Lz17hKVDsLbSXInfXM0giclvXbyiLXjTv42sQMATmZ0A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.0.3':
|
||||
resolution: {integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.1.3':
|
||||
resolution: {integrity: sha512-uzafnTFwZCPN499fNVnS2xFME8WLC9y7PLRs/yqz5lz1X/ySoxfaK2Hbz74zYUdEg+iDZPd8KlsWaw9HKkLEVw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.1.5':
|
||||
resolution: {integrity: sha512-NX2Ar3BCquAOYpnoYNcKz14eH03XuF7SmSlPzTSSU4PJe7+gelAjxo3Y7F2m8+hLT8ZkkqElawBp7SWBdzwqQw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-x64-musl@15.0.3':
|
||||
resolution: {integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-x64-musl@15.1.3':
|
||||
resolution: {integrity: sha512-el6GUFi4SiDYnMTTlJJFMU+GHvw0UIFnffP1qhurrN1qJV3BqaSRUjkDUgVV44T6zpw1Lc6u+yn0puDKHs+Sbw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-x64-musl@15.1.5':
|
||||
resolution: {integrity: sha512-EQgqMiNu3mrV5eQHOIgeuh6GB5UU57tu17iFnLfBEhYfiOfyK+vleYKh2dkRVkV6ayx3eSqbIYgE7J7na4hhcA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-win32-arm64-msvc@15.0.3':
|
||||
resolution: {integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==}
|
||||
@@ -4510,21 +4538,25 @@ packages:
|
||||
resolution: {integrity: sha512-otVbS4zeo3n71zgGLBYRTriDzc0zpruC0WI3ICwjpIk454cLwGV0yzh4jlGYWQJYJk0BRAmXFd3ooKIF+bKBHw==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@oxc-resolver/binding-linux-arm64-musl@1.12.0':
|
||||
resolution: {integrity: sha512-IStQDjIT7Lzmqg1i9wXvPL/NsYsxF24WqaQFS8b8rxra+z0VG7saBOsEnOaa4jcEY8MVpLYabFhTV+fSsA2vnA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@oxc-resolver/binding-linux-x64-gnu@1.12.0':
|
||||
resolution: {integrity: sha512-SipT7EVORz8pOQSFwemOm91TpSiBAGmOjG830/o+aLEsvQ4pEy223+SAnCfITh7+AahldYsJnVoIs519jmIlKQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@oxc-resolver/binding-linux-x64-musl@1.12.0':
|
||||
resolution: {integrity: sha512-mGh0XfUzKdn+WFaqPacziNraCWL5znkHRfQVxG9avGS9zb2KC/N1EBbPzFqutDwixGDP54r2gx4q54YCJEZ4iQ==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@oxc-resolver/binding-wasm32-wasi@1.12.0':
|
||||
resolution: {integrity: sha512-SZN6v7apKmQf/Vwiqb6e/s3Y2Oacw8uW8V2i1AlxtyaEFvnFE0UBn89zq6swEwE3OCajNWs0yPvgAXUMddYc7Q==}
|
||||
@@ -5012,24 +5044,28 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.10.12':
|
||||
resolution: {integrity: sha512-oqhSmV+XauSf0C//MoQnVErNUB/5OzmSiUzuazyLsD5pwqKNN+leC3JtRQ/QVzaCpr65jv9bKexT9+I2Tt3xDw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.10.12':
|
||||
resolution: {integrity: sha512-XldSIHyjD7m1Gh+/8rxV3Ok711ENLI420CU2EGEqSe3VSGZ7pHJvJn9ZFbYpWhsLxPqBYMFjp3Qw+J6OXCPXCA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@swc/core-linux-x64-musl@1.10.12':
|
||||
resolution: {integrity: sha512-wvPXzJxzPgTqhyp1UskOx1hRTtdWxlyFD1cGWOxgLsMik0V9xKRgqKnMPv16Nk7L9xl6quQ6DuUHj9ID7L3oVw==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.10.12':
|
||||
resolution: {integrity: sha512-TUYzWuu1O7uyIcRfxdm6Wh1u+gNnrW5M1DUgDOGZLsyQzgc2Zjwfh2llLhuAIilvCVg5QiGbJlpibRYJ/8QGsg==}
|
||||
@@ -5810,7 +5846,6 @@ packages:
|
||||
bson@6.10.1:
|
||||
resolution: {integrity: sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==}
|
||||
engines: {node: '>=16.20.1'}
|
||||
deprecated: a critical bug affecting only useBigInt64=true deserialization usage is fixed in bson@6.10.3
|
||||
|
||||
buffer-builder@0.2.0:
|
||||
resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==}
|
||||
@@ -6968,10 +7003,6 @@ packages:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
|
||||
fractional-indexing@3.2.0:
|
||||
resolution: {integrity: sha512-PcOxmqwYCW7O2ovKRU8OoQQj2yqTfEB/yeTYk4gPid6dN5ODRfU1hXd9tTVZzax/0NkO7AxpHykvZnT1aYp/BQ==}
|
||||
engines: {node: ^14.13.1 || >=16.0.0}
|
||||
|
||||
fs-constants@1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
|
||||
|
||||
@@ -7846,7 +7877,6 @@ packages:
|
||||
|
||||
libsql@0.4.7:
|
||||
resolution: {integrity: sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw==}
|
||||
cpu: [x64, arm64, wasm32]
|
||||
os: [darwin, linux, win32]
|
||||
|
||||
lie@3.1.1:
|
||||
@@ -13680,7 +13710,7 @@ snapshots:
|
||||
'@sentry/utils': 7.119.2
|
||||
localforage: 1.10.0
|
||||
|
||||
'@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))':
|
||||
'@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4))(react@19.0.0)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0)
|
||||
@@ -13694,9 +13724,9 @@ snapshots:
|
||||
'@sentry/types': 8.37.1
|
||||
'@sentry/utils': 8.37.1
|
||||
'@sentry/vercel-edge': 8.37.1
|
||||
'@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))
|
||||
'@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))
|
||||
chalk: 3.0.0
|
||||
next: 15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
next: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
resolve: 1.22.8
|
||||
rollup: 3.29.5
|
||||
stacktrace-parser: 0.1.10
|
||||
@@ -13804,12 +13834,12 @@ snapshots:
|
||||
'@sentry/types': 8.37.1
|
||||
'@sentry/utils': 8.37.1
|
||||
|
||||
'@sentry/webpack-plugin@2.22.6(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))':
|
||||
'@sentry/webpack-plugin@2.22.6(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))':
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 2.22.6
|
||||
unplugin: 1.0.1
|
||||
uuid: 9.0.0
|
||||
webpack: 5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12)
|
||||
webpack: 5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
@@ -16550,8 +16580,6 @@ snapshots:
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
|
||||
fractional-indexing@3.2.0: {}
|
||||
|
||||
fs-constants@1.0.0: {}
|
||||
|
||||
fs-extra@10.1.0:
|
||||
@@ -18190,7 +18218,7 @@ snapshots:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
next@15.1.3(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4):
|
||||
next@15.1.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4):
|
||||
dependencies:
|
||||
'@next/env': 15.1.3
|
||||
'@swc/counter': 0.1.3
|
||||
@@ -18218,7 +18246,7 @@ snapshots:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4):
|
||||
next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4):
|
||||
dependencies:
|
||||
'@next/env': 15.1.5
|
||||
'@swc/counter': 0.1.3
|
||||
@@ -19619,17 +19647,16 @@ snapshots:
|
||||
ansi-escapes: 4.3.2
|
||||
supports-hyperlinks: 2.3.0
|
||||
|
||||
terser-webpack-plugin@5.3.10(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12)):
|
||||
terser-webpack-plugin@5.3.10(@swc/core@1.10.12(@swc/helpers@0.5.15))(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
jest-worker: 27.5.1
|
||||
schema-utils: 3.3.0
|
||||
serialize-javascript: 6.0.2
|
||||
terser: 5.36.0
|
||||
webpack: 5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12)
|
||||
webpack: 5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.10.12(@swc/helpers@0.5.15)
|
||||
esbuild: 0.19.12
|
||||
|
||||
terser@5.36.0:
|
||||
dependencies:
|
||||
@@ -19910,14 +19937,14 @@ snapshots:
|
||||
escalade: 3.2.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
uploadthing@7.3.0(next@15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)):
|
||||
uploadthing@7.3.0(next@15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)):
|
||||
dependencies:
|
||||
'@effect/platform': 0.69.8(effect@3.10.3)
|
||||
'@uploadthing/mime-types': 0.3.2
|
||||
'@uploadthing/shared': 7.1.1
|
||||
effect: 3.10.3
|
||||
optionalDependencies:
|
||||
next: 15.1.5(@babel/core@7.26.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
next: 15.1.5(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.0.0-beta-714736e-20250131)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.77.4)
|
||||
|
||||
uri-js@4.4.1:
|
||||
dependencies:
|
||||
@@ -20015,7 +20042,7 @@ snapshots:
|
||||
|
||||
webpack-virtual-modules@0.5.0: {}
|
||||
|
||||
webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12):
|
||||
webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)):
|
||||
dependencies:
|
||||
'@types/eslint-scope': 3.7.7
|
||||
'@types/estree': 1.0.6
|
||||
@@ -20037,7 +20064,7 @@ snapshots:
|
||||
neo-async: 2.6.2
|
||||
schema-utils: 3.3.0
|
||||
tapable: 2.2.1
|
||||
terser-webpack-plugin: 5.3.10(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12)(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15))(esbuild@0.19.12))
|
||||
terser-webpack-plugin: 5.3.10(@swc/core@1.10.12(@swc/helpers@0.5.15))(webpack@5.96.1(@swc/core@1.10.12(@swc/helpers@0.5.15)))
|
||||
watchpack: 2.4.2
|
||||
webpack-sources: 3.2.3
|
||||
transitivePeerDependencies:
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useField } from '@payloadcms/ui'
|
||||
|
||||
export function AfterField() {
|
||||
const { setValue } = useField({ path: 'customJSON' })
|
||||
|
||||
return (
|
||||
<button
|
||||
id="set-custom-json"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setValue({
|
||||
users: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
email: 'john.doe@example.com',
|
||||
isActive: true,
|
||||
roles: ['admin', 'editor'],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Jane Smith',
|
||||
email: 'jane.smith@example.com',
|
||||
isActive: false,
|
||||
roles: ['viewer'],
|
||||
},
|
||||
],
|
||||
})
|
||||
}}
|
||||
style={{ marginTop: '5px', padding: '5px 10px' }}
|
||||
type="button"
|
||||
>
|
||||
Set Custom JSON
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -103,24 +103,4 @@ describe('JSON', () => {
|
||||
'"foo.with.periods": "bar"',
|
||||
)
|
||||
})
|
||||
|
||||
test('should update', async () => {
|
||||
const createdDoc = await payload.create({
|
||||
collection: 'json-fields',
|
||||
data: {
|
||||
customJSON: {
|
||||
default: 'value',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await page.goto(url.edit(createdDoc.id))
|
||||
const jsonField = page.locator('.json-field #field-customJSON')
|
||||
await expect(jsonField).toContainText('"default": "value"')
|
||||
|
||||
const originalHeight = (await page.locator('#field-customJSON').boundingBox())?.height || 0
|
||||
await page.locator('#set-custom-json').click()
|
||||
const newHeight = (await page.locator('#field-customJSON').boundingBox())?.height || 0
|
||||
expect(newHeight).toBeGreaterThan(originalHeight)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -67,16 +67,6 @@ const JSON: CollectionConfig = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'customJSON',
|
||||
type: 'json',
|
||||
admin: {
|
||||
components: {
|
||||
afterInput: ['./collections/JSON/AfterField#AfterField'],
|
||||
},
|
||||
},
|
||||
label: 'Custom Json',
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
maxPerDoc: 1,
|
||||
|
||||
@@ -170,7 +170,12 @@ describe('Text', () => {
|
||||
user: client.user,
|
||||
key: 'text-fields-list',
|
||||
value: {
|
||||
columns: [{ disableListColumnText: true }],
|
||||
columns: [
|
||||
{
|
||||
accessor: 'disableListColumnText',
|
||||
active: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -1474,15 +1474,6 @@ export interface JsonField {
|
||||
| boolean
|
||||
| null;
|
||||
};
|
||||
customJSON?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -3174,7 +3165,6 @@ export interface JsonFieldsSelect<T extends boolean = true> {
|
||||
| {
|
||||
jsonWithinGroup?: T;
|
||||
};
|
||||
customJSON?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
@@ -329,9 +329,9 @@ describe('Localization', () => {
|
||||
|
||||
await page.goto(url.list)
|
||||
|
||||
const localeLabel = page.locator(
|
||||
'.localizer.app-header__localizer .localizer-button__current-label',
|
||||
)
|
||||
const localeLabel = page
|
||||
.locator('.localizer.app-header__localizer .localizer-button__current-label')
|
||||
|
||||
|
||||
await expect(localeLabel).not.toHaveText('English')
|
||||
})
|
||||
|
||||
@@ -64,7 +64,6 @@ export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
blocks: {};
|
||||
collections: {
|
||||
richText: RichText;
|
||||
'blocks-fields': BlocksField;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
'use client'
|
||||
|
||||
export const Seed = () => {
|
||||
return (
|
||||
<button
|
||||
onClick={async () => {
|
||||
try {
|
||||
await fetch('/api/seed', { method: 'POST' })
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Seed
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -4,12 +4,8 @@ export const postsSlug = 'posts'
|
||||
|
||||
export const PostsCollection: CollectionConfig = {
|
||||
slug: postsSlug,
|
||||
enableCustomOrder: true,
|
||||
admin: {
|
||||
useAsTitle: 'text',
|
||||
components: {
|
||||
beforeList: ['/Seed#Seed'],
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { CollectionSlug, Payload } from 'payload'
|
||||
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
|
||||
@@ -19,30 +17,6 @@ export default buildConfigWithDefaults({
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
endpoints: [
|
||||
{
|
||||
path: '/seed',
|
||||
method: 'post',
|
||||
handler: async (req) => {
|
||||
await req.payload.delete({ collection: 'posts', where: {} })
|
||||
await createData(req.payload, 'posts', [
|
||||
{ text: 'Post 1' },
|
||||
{ text: 'Post 2' },
|
||||
{ text: 'Post 3' },
|
||||
{ text: 'Post 4' },
|
||||
{ text: 'Post 5' },
|
||||
{ text: 'Post 6' },
|
||||
{ text: 'Post 7' },
|
||||
{ text: 'Post 8' },
|
||||
{ text: 'Post 9' },
|
||||
])
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
status: 200,
|
||||
})
|
||||
},
|
||||
},
|
||||
],
|
||||
cors: ['http://localhost:3000', 'http://localhost:3001'],
|
||||
localization: {
|
||||
locales: ['en', 'nb'],
|
||||
@@ -56,33 +30,8 @@ export default buildConfigWithDefaults({
|
||||
password: devUser.password,
|
||||
},
|
||||
})
|
||||
await createData(payload, 'posts', [
|
||||
{ text: 'Post 1', number: 1, number2: 10, group: { number: 100 } },
|
||||
{ text: 'Post 2', number: 2, number2: 10, group: { number: 200 } },
|
||||
{ text: 'Post 3', number: 3, number2: 5, group: { number: 150 } },
|
||||
{ text: 'Post 10', number: 10, number2: 5, group: { number: 200 } },
|
||||
{ text: 'Post 11', number: 11, number2: 20, group: { number: 150 } },
|
||||
{ text: 'Post 12', number: 12, number2: 20, group: { number: 100 } },
|
||||
])
|
||||
await createData(payload, 'default-sort', [
|
||||
{ text: 'Post default-5 b', number: 5 },
|
||||
{ text: 'Post default-10', number: 10 },
|
||||
{ text: 'Post default-5 a', number: 5 },
|
||||
{ text: 'Post default-1', number: 1 },
|
||||
])
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
})
|
||||
|
||||
export async function createData(
|
||||
payload: Payload,
|
||||
collection: CollectionSlug,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
data: Record<string, any>[],
|
||||
) {
|
||||
for (const item of data) {
|
||||
await payload.create({ collection, data: item })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,65 @@
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Supported timezones in IANA format.
|
||||
*
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "supportedTimezones".
|
||||
*/
|
||||
export type SupportedTimezones =
|
||||
| 'Pacific/Midway'
|
||||
| 'Pacific/Niue'
|
||||
| 'Pacific/Honolulu'
|
||||
| 'Pacific/Rarotonga'
|
||||
| 'America/Anchorage'
|
||||
| 'Pacific/Gambier'
|
||||
| 'America/Los_Angeles'
|
||||
| 'America/Tijuana'
|
||||
| 'America/Denver'
|
||||
| 'America/Phoenix'
|
||||
| 'America/Chicago'
|
||||
| 'America/Guatemala'
|
||||
| 'America/New_York'
|
||||
| 'America/Bogota'
|
||||
| 'America/Caracas'
|
||||
| 'America/Santiago'
|
||||
| 'America/Buenos_Aires'
|
||||
| 'America/Sao_Paulo'
|
||||
| 'Atlantic/South_Georgia'
|
||||
| 'Atlantic/Azores'
|
||||
| 'Atlantic/Cape_Verde'
|
||||
| 'Europe/London'
|
||||
| 'Europe/Berlin'
|
||||
| 'Africa/Lagos'
|
||||
| 'Europe/Athens'
|
||||
| 'Africa/Cairo'
|
||||
| 'Europe/Moscow'
|
||||
| 'Asia/Riyadh'
|
||||
| 'Asia/Dubai'
|
||||
| 'Asia/Baku'
|
||||
| 'Asia/Karachi'
|
||||
| 'Asia/Tashkent'
|
||||
| 'Asia/Calcutta'
|
||||
| 'Asia/Dhaka'
|
||||
| 'Asia/Almaty'
|
||||
| 'Asia/Jakarta'
|
||||
| 'Asia/Bangkok'
|
||||
| 'Asia/Shanghai'
|
||||
| 'Asia/Singapore'
|
||||
| 'Asia/Tokyo'
|
||||
| 'Asia/Seoul'
|
||||
| 'Australia/Sydney'
|
||||
| 'Pacific/Guam'
|
||||
| 'Pacific/Noumea'
|
||||
| 'Pacific/Auckland'
|
||||
| 'Pacific/Fiji';
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
blocks: {};
|
||||
collections: {
|
||||
posts: Post;
|
||||
drafts: Draft;
|
||||
@@ -69,7 +124,6 @@ export interface UserAuthOperations {
|
||||
*/
|
||||
export interface Post {
|
||||
id: string;
|
||||
'payload-order'?: string | null;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
number2?: number | null;
|
||||
@@ -211,7 +265,6 @@ export interface PayloadMigration {
|
||||
* via the `definition` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
'payload-order'?: T;
|
||||
text?: T;
|
||||
number?: T;
|
||||
number2?: T;
|
||||
|
||||
@@ -868,7 +868,7 @@ describe('Versions', () => {
|
||||
const publishOptions = page.locator('.doc-controls__controls .popup')
|
||||
await publishOptions.click()
|
||||
|
||||
const publishSpecificLocale = page.locator('#publish-locale')
|
||||
const publishSpecificLocale = page.locator('.popup-button-list button').first()
|
||||
await expect(publishSpecificLocale).toContainText('English')
|
||||
await publishSpecificLocale.click()
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@payload-config": ["./test/admin/config.ts"],
|
||||
"@payload-config": ["./test/fields-relationship/config.ts"],
|
||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
||||
"@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user