chore: adds qs, adds query to createPayloadRequest

This commit is contained in:
Jarrod Flesch
2024-03-06 14:26:21 -05:00
parent f76e534b64
commit c73159d2d0
24 changed files with 204 additions and 75 deletions

8
.vscode/launch.json vendored
View File

@@ -3,13 +3,7 @@
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
"configurations": [ "configurations": [
{ {
"command": "pnpm dev", "command": "pnpm run dev _community -- --no-turbo",
"name": "Run Dev 3.0",
"request": "launch",
"type": "node-terminal"
},
{
"command": "pnpm run dev _community",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"name": "Run Dev Community", "name": "Run Dev Community",
"request": "launch", "request": "launch",

View File

@@ -78,6 +78,7 @@
"graphql-http": "^1.22.0", "graphql-http": "^1.22.0",
"graphql-playground-html": "1.6.30", "graphql-playground-html": "1.6.30",
"path-to-regexp": "^6.2.1", "path-to-regexp": "^6.2.1",
"qs": "6.11.2",
"react-diff-viewer-continued": "3.2.6", "react-diff-viewer-continued": "3.2.6",
"react-toastify": "8.2.0", "react-toastify": "8.2.0",
"sass": "^1.71.1", "sass": "^1.71.1",

View File

@@ -10,6 +10,7 @@ import { translations } from '@payloadcms/translations/api'
import { getAuthenticatedUser } from 'payload/auth' import { getAuthenticatedUser } from 'payload/auth'
import { parseCookies } from 'payload/auth' import { parseCookies } from 'payload/auth'
import { getDataLoader } from 'payload/utilities' import { getDataLoader } from 'payload/utilities'
import QueryString from 'qs'
import { URL } from 'url' import { URL } from 'url'
import { getDataAndFile } from './getDataAndFile' import { getDataAndFile } from './getDataAndFile'
@@ -95,6 +96,7 @@ export const createPayloadRequest = async ({
payloadUploadSizes: {}, payloadUploadSizes: {},
port: urlProperties.port, port: urlProperties.port,
protocol: urlProperties.protocol, protocol: urlProperties.protocol,
query: urlProperties.search ? QueryString.parse(urlProperties.search) : {},
routeParams: params || {}, routeParams: params || {},
search: urlProperties.search, search: urlProperties.search,
searchParams: urlProperties.searchParams, searchParams: urlProperties.searchParams,

View File

@@ -19,7 +19,6 @@ export { generateAccountMetadata } from './meta'
export const Account: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => { export const Account: React.FC<AdminViewProps> = async ({ initPageResult, searchParams }) => {
const { const {
locale,
permissions, permissions,
req: { req: {
payload, payload,
@@ -94,8 +93,8 @@ export const Account: React.FC<AdminViewProps> = async ({ initPageResult, search
<HydrateClientUser permissions={permissions} user={user} /> <HydrateClientUser permissions={permissions} user={user} />
<SetDocumentInfo <SetDocumentInfo
AfterFields={<Settings />} AfterFields={<Settings />}
action={`${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale.code}`} action={`${serverURL}${api}/${userSlug}${data?.id ? `/${data.id}` : ''}`}
apiURL={`${serverURL}${api}/${userSlug}/${data?.id}?locale=${locale.code}`} apiURL={`${serverURL}${api}/${userSlug}${data?.id ? `/${data.id}` : ''}`}
collectionSlug={userSlug} collectionSlug={userSlug}
docPermissions={collectionPermissions} docPermissions={collectionPermissions}
docPreferences={docPreferences} docPreferences={docPreferences}

View File

@@ -170,13 +170,6 @@ export const Document: React.FC<AdminViewProps> = async ({
req, req,
}) })
const formQueryParams: QueryParamTypes = {
depth: 0,
'fallback-locale': 'null',
locale: locale.code,
uploadEdits: undefined,
}
const serverSideProps: ServerSideEditViewProps = { const serverSideProps: ServerSideEditViewProps = {
initPageResult, initPageResult,
routeSegments: segments, routeSegments: segments,
@@ -193,7 +186,7 @@ export const Document: React.FC<AdminViewProps> = async ({
/> />
<HydrateClientUser permissions={permissions} user={user} /> <HydrateClientUser permissions={permissions} user={user} />
<SetDocumentInfo <SetDocumentInfo
action={`${action}?${queryString.stringify(formQueryParams)}`} action={action}
apiURL={apiURL} apiURL={apiURL}
collectionSlug={collectionConfig?.slug} collectionSlug={collectionConfig?.slug}
disableActions={false} disableActions={false}
@@ -206,7 +199,14 @@ export const Document: React.FC<AdminViewProps> = async ({
initialState={initialState} initialState={initialState}
/> />
<EditDepthProvider depth={1} key={`${collectionSlug || globalSlug}-${locale.code}`}> <EditDepthProvider depth={1} key={`${collectionSlug || globalSlug}-${locale.code}`}>
<FormQueryParamsProvider formQueryParams={formQueryParams}> <FormQueryParamsProvider
initialParams={{
depth: 0,
'fallback-locale': 'null',
locale: locale.code,
uploadEdits: undefined,
}}
>
<RenderCustomComponent <RenderCustomComponent
CustomComponent={typeof CustomView === 'function' ? CustomView : undefined} CustomComponent={typeof CustomView === 'function' ? CustomView : undefined}
DefaultComponent={DefaultView} DefaultComponent={DefaultView}

View File

@@ -6,6 +6,7 @@ import {
useComponentMap, useComponentMap,
useConfig, useConfig,
useDocumentInfo, useDocumentInfo,
useFormQueryParams,
} from '@payloadcms/ui' } from '@payloadcms/ui'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import React, { Fragment, useEffect } from 'react' import React, { Fragment, useEffect } from 'react'
@@ -20,6 +21,7 @@ export const EditViewClient: React.FC = () => {
} = useConfig() } = useConfig()
const router = useRouter() const router = useRouter()
const { dispatchFormQueryParams } = useFormQueryParams()
const { getComponentMap } = useComponentMap() const { getComponentMap } = useComponentMap()
@@ -38,16 +40,23 @@ export const EditViewClient: React.FC = () => {
if (!isEditing) { if (!isEditing) {
router.push(`${adminRoute}/collections/${collectionSlug}/${json?.doc?.id}`) router.push(`${adminRoute}/collections/${collectionSlug}/${json?.doc?.id}`)
} else { } else {
// buildState(json.doc, { dispatchFormQueryParams({
// fieldSchema: collection.fields, type: 'SET',
// }) params: {
// setFormQueryParams((params) => ({ uploadEdits: null,
// ...params, },
// uploadEdits: undefined, })
// }))
} }
}, },
[getVersions, isEditing, getDocPermissions, collectionSlug, adminRoute, router], [
adminRoute,
collectionSlug,
dispatchFormQueryParams,
getDocPermissions,
getVersions,
isEditing,
router,
],
) )
useEffect(() => { useEffect(() => {

View File

@@ -1,6 +1,8 @@
import type { I18n, TFunction } from '@payloadcms/translations' import type { I18n, TFunction } from '@payloadcms/translations'
import type DataLoader from 'dataloader' import type DataLoader from 'dataloader'
import { QuerySelector } from 'mongoose'
import type payload from '../' import type payload from '../'
import type { User } from '../auth/types' import type { User } from '../auth/types'
import type { TypeWithID, TypeWithTimestamps } from '../collections/config/types' import type { TypeWithID, TypeWithTimestamps } from '../collections/config/types'
@@ -54,6 +56,8 @@ export type CustomPayloadRequest<U = any> = {
payloadDataLoader?: DataLoader<string, TypeWithID> payloadDataLoader?: DataLoader<string, TypeWithID>
/** Resized versions of the image that was uploaded during this request */ /** Resized versions of the image that was uploaded during this request */
payloadUploadSizes?: Record<string, Buffer> payloadUploadSizes?: Record<string, Buffer>
/** Query params on the request */
query: Record<string, unknown>
/** The route parameters /** The route parameters
* @example * @example
* /:collection/:id -> /posts/123 * /:collection/:id -> /posts/123

View File

@@ -52,8 +52,7 @@ export const generateFileData = async <T>({
let file = req.file let file = req.file
const { searchParams } = req const uploadEdits = req.query['uploadEdits'] || {}
const uploadEdits = searchParams.get('uploadEdits') || {}
const { disableLocalStorage, formatOptions, imageSizes, resizeOptions, staticDir, trimOptions } = const { disableLocalStorage, formatOptions, imageSizes, resizeOptions, staticDir, trimOptions } =
collectionConfig.upload collectionConfig.upload

View File

@@ -94,6 +94,7 @@ export const createLocalReq: CreateLocalReq = async (
req.user = user || req?.user || null req.user = user || req?.user || null
req.payloadDataLoader = req?.payloadDataLoader || getDataLoader(req) req.payloadDataLoader = req?.payloadDataLoader || getDataLoader(req)
req.routeParams = req?.routeParams || {} req.routeParams = req?.routeParams || {}
req.query = req?.query || {}
if (!req?.url) attachFakeURLProperties(req) if (!req?.url) attachFakeURLProperties(req)

View File

@@ -59,7 +59,7 @@ export const DeleteMany: React.FC<Props> = (props) => {
toast.success(json.message || t('general:deletedSuccessfully'), { autoClose: 3000 }) toast.success(json.message || t('general:deletedSuccessfully'), { autoClose: 3000 })
toggleAll() toggleAll()
dispatchSearchParams({ dispatchSearchParams({
type: 'set', type: 'SET',
browserHistory: 'replace', browserHistory: 'replace',
params: { page: selectAll ? '1' : undefined }, params: { page: selectAll ? '1' : undefined },
}) })

View File

@@ -154,7 +154,7 @@ export const EditMany: React.FC<Props> = (props) => {
const onSuccess = () => { const onSuccess = () => {
dispatchSearchParams({ dispatchSearchParams({
type: 'set', type: 'SET',
browserHistory: 'replace', browserHistory: 'replace',
params: { page: selectAll === SelectAllStatus.AllAvailable ? '1' : undefined }, params: { page: selectAll === SelectAllStatus.AllAvailable ? '1' : undefined },
}) })

View File

@@ -36,7 +36,7 @@ export const EditUpload: React.FC<{
}> = ({ fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => { }> = ({ fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
const { closeModal } = useModal() const { closeModal } = useModal()
const { t } = useTranslation() const { t } = useTranslation()
const { formQueryParams, setFormQueryParams } = useFormQueryParams() const { dispatchFormQueryParams, formQueryParams } = useFormQueryParams()
const { uploadEdits } = formQueryParams || {} const { uploadEdits } = formQueryParams || {}
const [crop, setCrop] = useState<CropType>({ const [crop, setCrop] = useState<CropType>({
height: uploadEdits?.crop?.height || 100, height: uploadEdits?.crop?.height || 100,
@@ -81,11 +81,16 @@ export const EditUpload: React.FC<{
} }
const saveEdits = () => { const saveEdits = () => {
setFormQueryParams({ dispatchFormQueryParams({
...formQueryParams, type: 'SET',
uploadEdits: { params: {
crop: crop || undefined, uploadEdits:
focalPoint: pointPosition ? pointPosition : undefined, crop || pointPosition
? {
crop: crop || null,
focalPoint: pointPosition ? pointPosition : null,
}
: null,
}, },
}) })
closeModal(editDrawerSlug) closeModal(editDrawerSlug)

View File

@@ -49,7 +49,7 @@ const Localizer: React.FC<{
onClick={() => { onClick={() => {
close() close()
dispatchSearchParams({ dispatchSearchParams({
type: 'set', type: 'SET',
params: { params: {
locale: searchParams.locale, locale: searchParams.locale,
}, },

View File

@@ -64,7 +64,7 @@ export const PublishMany: React.FC<Props> = (props) => {
if (res.status < 400) { if (res.status < 400) {
toast.success(t('general:updatedSuccessfully')) toast.success(t('general:updatedSuccessfully'))
dispatchSearchParams({ dispatchSearchParams({
type: 'set', type: 'SET',
browserHistory: 'replace', browserHistory: 'replace',
params: { page: selectAll ? '1' : undefined }, params: { page: selectAll ? '1' : undefined },
}) })

View File

@@ -61,7 +61,7 @@ export const UnpublishMany: React.FC<Props> = (props) => {
if (res.status < 400) { if (res.status < 400) {
toast.success(t('general:updatedSuccessfully')) toast.success(t('general:updatedSuccessfully'))
dispatchSearchParams({ dispatchSearchParams({
type: 'set', type: 'SET',
browserHistory: 'replace', browserHistory: 'replace',
params: { page: selectAll ? '1' : undefined }, params: { page: selectAll ? '1' : undefined },
}) })

View File

@@ -9,7 +9,6 @@ import { useFormSubmitted } from '../../forms/Form/context'
import reduceFieldsToValues from '../../forms/Form/reduceFieldsToValues' import reduceFieldsToValues from '../../forms/Form/reduceFieldsToValues'
import { fieldBaseClass } from '../../forms/fields/shared' import { fieldBaseClass } from '../../forms/fields/shared'
import useField from '../../forms/useField' import useField from '../../forms/useField'
import { useClientFunctions } from '../../providers/ClientFunction'
import { useDocumentInfo } from '../../providers/DocumentInfo' import { useDocumentInfo } from '../../providers/DocumentInfo'
import { useTranslation } from '../../providers/Translation' import { useTranslation } from '../../providers/Translation'
import { Button } from '../Button' import { Button } from '../Button'
@@ -53,7 +52,6 @@ export const UploadActions = ({ canEdit, showSizePreviews }) => {
export const Upload: React.FC<Props> = (props) => { export const Upload: React.FC<Props> = (props) => {
const { collectionSlug, initialState, onChange, updatedAt, uploadConfig } = props const { collectionSlug, initialState, onChange, updatedAt, uploadConfig } = props
const clientFunctions = useClientFunctions()
const submitted = useFormSubmitted() const submitted = useFormSubmitted()
const [replacingFile, setReplacingFile] = useState(false) const [replacingFile, setReplacingFile] = useState(false)

View File

@@ -6,11 +6,13 @@ import isDeepEqual from 'deep-equal'
import { useRouter } from 'next/navigation' import { useRouter } from 'next/navigation'
import { serialize } from 'object-to-formdata' import { serialize } from 'object-to-formdata'
import { wait } from 'payload/utilities' import { wait } from 'payload/utilities'
import QueryString from 'qs'
import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react' import React, { useCallback, useEffect, useReducer, useRef, useState } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import type { Context as FormContextType, GetDataByPath, Props, SubmitOptions } from './types' import type { Context as FormContextType, GetDataByPath, Props, SubmitOptions } from './types'
import { useFormQueryParams } from '../..'
import { useDebouncedEffect } from '../../hooks/useDebouncedEffect' import { useDebouncedEffect } from '../../hooks/useDebouncedEffect'
import useThrottledEffect from '../../hooks/useThrottledEffect' import useThrottledEffect from '../../hooks/useThrottledEffect'
import { useAuth } from '../../providers/Auth' import { useAuth } from '../../providers/Auth'
@@ -69,6 +71,7 @@ const Form: React.FC<Props> = (props) => {
const { i18n, t } = useTranslation() const { i18n, t } = useTranslation()
const { refreshCookie, user } = useAuth() const { refreshCookie, user } = useAuth()
const operation = useOperation() const operation = useOperation()
const { formQueryParams } = useFormQueryParams()
const config = useConfig() const config = useConfig()
const { const {
@@ -221,7 +224,8 @@ const Form: React.FC<Props> = (props) => {
let res let res
if (typeof actionToUse === 'string') { if (typeof actionToUse === 'string') {
res = await requests[methodToUse.toLowerCase()](actionToUse, { const actionEndpoint = `${actionToUse}${QueryString.stringify(formQueryParams, { addQueryPrefix: true })}`
res = await requests[methodToUse.toLowerCase()](actionEndpoint, {
body: formData, body: formData,
headers: { headers: {
'Accept-Language': i18n.language, 'Accept-Language': i18n.language,
@@ -352,6 +356,7 @@ const Form: React.FC<Props> = (props) => {
i18n, i18n,
waitForAutocomplete, waitForAutocomplete,
beforeSubmit, beforeSubmit,
formQueryParams,
], ],
) )
@@ -562,9 +567,14 @@ const Form: React.FC<Props> = (props) => {
[fields, dispatchFields, onChange], [fields, dispatchFields, onChange],
) )
const actionString =
typeof action === 'string'
? `${action}${QueryString.stringify(formQueryParams, { addQueryPrefix: true })}`
: ''
return ( return (
<form <form
action={action} action={method ? actionString : action}
className={classes} className={classes}
method={method} method={method}
noValidate noValidate

View File

@@ -1,38 +1,65 @@
'use client' 'use client'
import type { UploadEdits } from 'payload/types'
import React, { createContext, useContext, useState } from 'react' import React, { createContext, useContext } from 'react'
export type QueryParamTypes = { import type { Action, FormQueryParamsContext, State } from './types'
depth: number
'fallback-locale': string import { useLocale } from '../Locale'
locale: string
uploadEdits?: UploadEdits export const FormQueryParams = createContext({} as FormQueryParamsContext)
}
export const FormQueryParams = createContext(
{} as {
formQueryParams: QueryParamTypes
setFormQueryParams: (params: QueryParamTypes) => void
},
)
export const FormQueryParamsProvider: React.FC<{ export const FormQueryParamsProvider: React.FC<{
children: React.ReactNode children: React.ReactNode
formQueryParams?: QueryParamTypes initialParams?: State
setFormQueryParams?: (params: QueryParamTypes) => void }> = ({ children, initialParams: formQueryParamsFromProps }) => {
}> = ({ children, formQueryParams: formQueryParamsFromProps }) => { const [formQueryParams, dispatchFormQueryParams] = React.useReducer(
const [formQueryParams, setFormQueryParams] = useState( (state: State, action: Action) => {
formQueryParamsFromProps || ({} as QueryParamTypes), const newState = { ...state }
switch (action.type) {
case 'SET':
if (action.params?.uploadEdits === null && newState?.uploadEdits) {
delete newState.uploadEdits
}
if (action.params?.uploadEdits?.crop === null && newState?.uploadEdits?.crop) {
delete newState.uploadEdits.crop
}
if (
action.params?.uploadEdits?.focalPoint === null &&
newState?.uploadEdits?.focalPoint
) {
delete newState.uploadEdits.focalPoint
}
return {
...newState,
...action.params,
}
default:
return state
}
},
formQueryParamsFromProps || ({} as State),
) )
const locale = useLocale()
React.useEffect(() => {
dispatchFormQueryParams({
type: 'SET',
params: {
locale: locale.code,
},
})
}, [locale.code])
return ( return (
<FormQueryParams.Provider value={{ formQueryParams, setFormQueryParams }}> <FormQueryParams.Provider value={{ dispatchFormQueryParams, formQueryParams }}>
{children} {children}
</FormQueryParams.Provider> </FormQueryParams.Provider>
) )
} }
export const useFormQueryParams = (): { export const useFormQueryParams = (): {
formQueryParams: QueryParamTypes dispatchFormQueryParams: React.Dispatch<Action>
setFormQueryParams: (params: QueryParamTypes) => void formQueryParams: State
} => useContext(FormQueryParams) } => useContext(FormQueryParams)

View File

@@ -0,0 +1,18 @@
import type { UploadEdits } from 'payload/types'
export type FormQueryParamsContext = {
dispatchFormQueryParams: (action: Action) => void
formQueryParams: State
}
export type State = {
depth: number
'fallback-locale': string
locale: string
uploadEdits?: UploadEdits
}
export type Action = {
params: Partial<State>
type: 'SET'
}

View File

@@ -75,7 +75,7 @@ export const LocaleProvider: React.FC<{ children?: React.ReactNode }> = ({ child
useEffect(() => { useEffect(() => {
if (searchParams?.locale) { if (searchParams?.locale) {
dispatchSearchParams({ dispatchSearchParams({
type: 'set', type: 'SET',
params: { params: {
locale: searchParams.locale, locale: searchParams.locale,
}, },

View File

@@ -26,16 +26,16 @@ export const SearchParamsProvider: React.FC<{ children?: React.ReactNode }> = ({
let paramsToSet let paramsToSet
switch (action.type) { switch (action.type) {
case 'set': case 'SET':
paramsToSet = { paramsToSet = {
...state, ...state,
...action.params, ...action.params,
} }
break break
case 'replace': case 'REPLACE':
paramsToSet = action.params paramsToSet = action.params
break break
case 'clear': case 'CLEAR':
paramsToSet = {} paramsToSet = {}
break break
default: default:

View File

@@ -8,14 +8,14 @@ export type State = qs.ParsedQs
export type Action = ( export type Action = (
| { | {
params: qs.ParsedQs params: qs.ParsedQs
type: 'replace' type: 'REPLACE'
} }
| { | {
params: qs.ParsedQs params: qs.ParsedQs
type: 'set' type: 'SET'
} }
| { | {
type: 'clear' type: 'CLEAR'
} }
) & { ) & {
/** /**

44
pnpm-lock.yaml generated
View File

@@ -574,6 +574,9 @@ importers:
path-to-regexp: path-to-regexp:
specifier: ^6.2.1 specifier: ^6.2.1
version: 6.2.1 version: 6.2.1
qs:
specifier: 6.11.2
version: 6.11.2
react-diff-viewer-continued: react-diff-viewer-continued:
specifier: 3.2.6 specifier: 3.2.6
version: 3.2.6(react-dom@18.2.0)(react@18.2.0) version: 3.2.6(react-dom@18.2.0)(react@18.2.0)
@@ -1330,7 +1333,7 @@ importers:
version: 2.3.0 version: 2.3.0
next: next:
specifier: 14.1.1-canary.26 specifier: 14.1.1-canary.26
version: 14.1.1-canary.26(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.1) version: 14.1.1-canary.26(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0)
object-to-formdata: object-to-formdata:
specifier: 4.5.1 specifier: 4.5.1
version: 4.5.1 version: 4.5.1
@@ -13512,6 +13515,45 @@ packages:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: false dev: false
/next@14.1.1-canary.26(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-vHj7hCL9qn8AhRXNEC1ujTO55w3IjckEE1tkmxwyqA3ypTH9PtxSnU6eFfC9C67Xf/Q2C5Btug7Yqvw7pxGkhg==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
'@opentelemetry/api': ^1.1.0
react: ^18.2.0
react-dom: ^18.2.0
sass: ^1.3.0
peerDependenciesMeta:
'@opentelemetry/api':
optional: true
sass:
optional: true
dependencies:
'@next/env': 14.1.1-canary.26
'@swc/helpers': 0.5.2
busboy: 1.6.0
caniuse-lite: 1.0.30001591
graceful-fs: 4.2.11
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(@babel/core@7.24.0)(react@18.2.0)
optionalDependencies:
'@next/swc-darwin-arm64': 14.1.1-canary.26
'@next/swc-darwin-x64': 14.1.1-canary.26
'@next/swc-linux-arm64-gnu': 14.1.1-canary.26
'@next/swc-linux-arm64-musl': 14.1.1-canary.26
'@next/swc-linux-x64-gnu': 14.1.1-canary.26
'@next/swc-linux-x64-musl': 14.1.1-canary.26
'@next/swc-win32-arm64-msvc': 14.1.1-canary.26
'@next/swc-win32-ia32-msvc': 14.1.1-canary.26
'@next/swc-win32-x64-msvc': 14.1.1-canary.26
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
dev: false
/next@14.1.1-canary.26(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.1): /next@14.1.1-canary.26(@babel/core@7.24.0)(react-dom@18.2.0)(react@18.2.0)(sass@1.71.1):
resolution: {integrity: sha512-vHj7hCL9qn8AhRXNEC1ujTO55w3IjckEE1tkmxwyqA3ypTH9PtxSnU6eFfC9C67Xf/Q2C5Btug7Yqvw7pxGkhg==} resolution: {integrity: sha512-vHj7hCL9qn8AhRXNEC1ujTO55w3IjckEE1tkmxwyqA3ypTH9PtxSnU6eFfC9C67Xf/Q2C5Btug7Yqvw7pxGkhg==}
engines: {node: '>=18.17.0'} engines: {node: '>=18.17.0'}

View File

@@ -4,7 +4,27 @@ export const mediaSlug = 'media'
export const MediaCollection: CollectionConfig = { export const MediaCollection: CollectionConfig = {
slug: mediaSlug, slug: mediaSlug,
upload: true, upload: {
crop: true,
focalPoint: true,
imageSizes: [
{
name: 'thumbnail',
width: 200,
height: 200,
},
{
name: 'medium',
width: 800,
height: 800,
},
{
name: 'large',
width: 1200,
height: 1200,
},
],
},
access: { access: {
read: () => true, read: () => true,
create: () => true, create: () => true,