Compare commits
14 Commits
feat/folde
...
feat/query
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd9763d162 | ||
|
|
5400703c98 | ||
|
|
890c03a2e0 | ||
|
|
64f6011ba4 | ||
|
|
8c39eeb821 | ||
|
|
6665a016ec | ||
|
|
e234cad0d5 | ||
|
|
8e642f5f6a | ||
|
|
1235a183ff | ||
|
|
81d333f4b0 | ||
|
|
4fe3423e54 | ||
|
|
e8c2b15e2b | ||
|
|
3127d6ad6d | ||
|
|
72ab319d37 |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -118,13 +118,6 @@
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm tsx --no-deprecation test/dev.ts folder-view",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev Folder View",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm tsx --no-deprecation test/dev.ts localization",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
@@ -81,7 +81,7 @@ To install a Database Adapter, you can run **one** of the following commands:
|
||||
|
||||
#### 2. Copy Payload files into your Next.js app folder
|
||||
|
||||
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](<https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)>) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
|
||||
Payload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:
|
||||
|
||||
```plaintext
|
||||
app/
|
||||
|
||||
@@ -50,6 +50,7 @@ The following options are available for Query Presets:
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `access` | Used to define custom collection-level access control that applies to all presets. [More details](#access-control). |
|
||||
| `constraints` | Used to define custom document-level access control that apply to individual presets. [More details](#document-access-control). |
|
||||
| `hooks` | Used to add custom hooks to the query presets collection. [More details](#hooks). |
|
||||
| `labels` | Custom labels to use for the Query Presets collection. |
|
||||
|
||||
## Access Control
|
||||
@@ -59,7 +60,7 @@ Query Presets are subject to the same [Access Control](../access-control/overvie
|
||||
Access Control for Query Presets can be customized in two ways:
|
||||
|
||||
1. [Collection Access Control](#collection-access-control): Applies to all presets. These rules are not controllable by the user and are statically defined in the config.
|
||||
2. [Document Access Control](#document-access-control): Applies to each individual preset. These rules are controllable by the user and are saved to the document.
|
||||
2. [Document Access Control](#document-access-control): Applies to each individual preset. These rules are controllable by the user and are dynamically defined on each record in the database.
|
||||
|
||||
### Collection Access Control
|
||||
|
||||
@@ -97,7 +98,7 @@ This example restricts all Query Presets to users with the role of `admin`.
|
||||
|
||||
### Document Access Control
|
||||
|
||||
You can also define access control rules that apply to each specific preset. Users have the ability to define and modify these rules on the fly as they manage presets. These are saved dynamically in the database on each document.
|
||||
You can also define access control rules that apply to each specific preset. Users have the ability to define and modify these rules on the fly as they manage presets. These are saved dynamically in the database on each record.
|
||||
|
||||
When a user manages a preset, document-level access control options will be available to them in the Admin Panel for each operation.
|
||||
|
||||
@@ -150,8 +151,8 @@ const config = buildConfig({
|
||||
}),
|
||||
},
|
||||
],
|
||||
// highlight-end
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
})
|
||||
```
|
||||
@@ -171,3 +172,65 @@ The following options are available for each constraint:
|
||||
| `value` | The value to store in the database when this constraint is selected. |
|
||||
| `fields` | An array of fields to render when this constraint is selected. |
|
||||
| `access` | A function that determines the access control rules for this constraint. |
|
||||
|
||||
## Hooks
|
||||
|
||||
You can attach your own [Hooks](../hooks/overview) to the Query Presets collection. This can be useful for defining custom behavior when a preset is created, updated, or deleted.
|
||||
|
||||
For example, you may want to add additional access control rules that prevent certain users from creating or updating presets in a certain way.
|
||||
|
||||
To do this, you can use the `hooks` property in your [Payload Config](../configuration/overview):
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
const config = buildConfig({
|
||||
// ...
|
||||
queryPresets: {
|
||||
// ...
|
||||
// highlight-start
|
||||
hooks: {
|
||||
beforeValidate: [
|
||||
// this is a custom `beforeValidate` hook that runs before the preset is validated
|
||||
// it ensures that only admins can add or remove the "admin" role from a preset
|
||||
({ data, req, originalDoc }) => {
|
||||
const adminRoleChanged = (current, original) => {
|
||||
const currentHasAdmin = current?.roles?.includes('admin') ?? false
|
||||
const originalHasAdmin = original?.roles?.includes('admin') ?? false
|
||||
return currentHasAdmin !== originalHasAdmin
|
||||
}
|
||||
|
||||
const readChanged =
|
||||
data?.access?.read?.constraint === 'specificRoles' &&
|
||||
adminRoleChanged(
|
||||
data?.access?.read,
|
||||
originalDoc?.access?.read || {},
|
||||
)
|
||||
|
||||
const updateChanged =
|
||||
data?.access?.update?.constraint === 'specificRoles' &&
|
||||
adminRoleChanged(
|
||||
data?.access?.update,
|
||||
originalDoc?.access?.update || {},
|
||||
)
|
||||
|
||||
if (
|
||||
(readChanged || updateChanged) &&
|
||||
!req.user?.roles?.includes('admin')
|
||||
) {
|
||||
throw new APIError(
|
||||
'You must be an admin to add or remove the admin role from a preset',
|
||||
403,
|
||||
{},
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
],
|
||||
},
|
||||
// highlight-end
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -150,6 +150,18 @@ export const buildSortParam = ({
|
||||
sort = [sort]
|
||||
}
|
||||
|
||||
// In the case of Mongo, when sorting by a field that is not unique, the results are not guaranteed to be in the same order each time.
|
||||
// So we add a fallback sort to ensure that the results are always in the same order.
|
||||
let fallbackSort = '-id'
|
||||
|
||||
if (timestamps) {
|
||||
fallbackSort = '-createdAt'
|
||||
}
|
||||
|
||||
if (!(sort.includes(fallbackSort) || sort.includes(fallbackSort.replace('-', '')))) {
|
||||
sort.push(fallbackSort)
|
||||
}
|
||||
|
||||
const sorting = sort.reduce<Record<string, string>>((acc, item) => {
|
||||
let sortProperty: string
|
||||
let sortDirection: SortDirection
|
||||
|
||||
@@ -39,8 +39,9 @@ export const buildOrderBy = ({
|
||||
}: Args): BuildQueryResult['orderBy'] => {
|
||||
const orderBy: BuildQueryResult['orderBy'] = []
|
||||
|
||||
const createdAt = adapter.tables[tableName]?.createdAt
|
||||
|
||||
if (!sort) {
|
||||
const createdAt = adapter.tables[tableName]?.createdAt
|
||||
if (createdAt) {
|
||||
sort = '-createdAt'
|
||||
} else {
|
||||
@@ -52,6 +53,18 @@ export const buildOrderBy = ({
|
||||
sort = [sort]
|
||||
}
|
||||
|
||||
// In the case of Mongo, when sorting by a field that is not unique, the results are not guaranteed to be in the same order each time.
|
||||
// So we add a fallback sort to ensure that the results are always in the same order.
|
||||
let fallbackSort = '-id'
|
||||
|
||||
if (createdAt) {
|
||||
fallbackSort = '-createdAt'
|
||||
}
|
||||
|
||||
if (!(sort.includes(fallbackSort) || sort.includes(fallbackSort.replace('-', '')))) {
|
||||
sort.push(fallbackSort)
|
||||
}
|
||||
|
||||
for (const sortItem of sort) {
|
||||
let sortProperty: string
|
||||
let sortDirection: 'asc' | 'desc'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import type { SanitizedConfig } from 'payload'
|
||||
|
||||
import { Button } from '@payloadcms/ui'
|
||||
import { Link } from '@payloadcms/ui'
|
||||
import { useParams, usePathname, useSearchParams } from 'next/navigation.js'
|
||||
import { formatAdminURL } from 'payload/shared'
|
||||
import React from 'react'
|
||||
@@ -13,6 +13,7 @@ export const DocumentTabLink: React.FC<{
|
||||
children?: React.ReactNode
|
||||
href: string
|
||||
isActive?: boolean
|
||||
isCollection?: boolean
|
||||
newTab?: boolean
|
||||
}> = ({
|
||||
adminRoute,
|
||||
@@ -53,17 +54,19 @@ export const DocumentTabLink: React.FC<{
|
||||
isActiveFromProps
|
||||
|
||||
return (
|
||||
<Button
|
||||
<li
|
||||
aria-label={ariaLabel}
|
||||
buttonStyle="tab"
|
||||
className={[baseClass, isActive && `${baseClass}--active`].filter(Boolean).join(' ')}
|
||||
disabled={isActive}
|
||||
el={!isActive || href !== pathname ? 'link' : 'div'}
|
||||
newTab={newTab}
|
||||
size="medium"
|
||||
to={!isActive || href !== pathname ? hrefWithLocale : undefined}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
<Link
|
||||
className={`${baseClass}__link`}
|
||||
href={!isActive || href !== pathname ? hrefWithLocale : ''}
|
||||
prefetch={false}
|
||||
{...(newTab && { rel: 'noopener noreferrer', target: '_blank' })}
|
||||
tabIndex={isActive ? -1 : 0}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,24 +1,74 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
@layer payload-default {
|
||||
.doc-tab {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
@extend %h5;
|
||||
position: relative;
|
||||
|
||||
&__link {
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
||||
// Use a pseudo element for the accessability so that it doesn't take up DOM space
|
||||
// Also because the parent element has `overflow: hidden` which would clip an outline
|
||||
&:focus-visible::after {
|
||||
content: '';
|
||||
border: var(--accessibility-outline);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus:not(:focus-visible) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: var(--style-radius-s);
|
||||
background-color: var(--theme-elevation-50);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.pill-version-count {
|
||||
&::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.doc-tab__count {
|
||||
background-color: var(--theme-elevation-150);
|
||||
}
|
||||
}
|
||||
|
||||
&--active {
|
||||
.pill-version-count {
|
||||
background-color: var(--theme-elevation-250);
|
||||
font-weight: 600;
|
||||
&::before {
|
||||
opacity: 1;
|
||||
background-color: var(--theme-elevation-100);
|
||||
}
|
||||
|
||||
.doc-tab {
|
||||
&__count {
|
||||
background-color: var(--theme-elevation-250);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.pill-version-count {
|
||||
background-color: var(--theme-elevation-250);
|
||||
.doc-tab {
|
||||
&__count {
|
||||
background-color: var(--theme-elevation-250);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +80,16 @@
|
||||
gap: 4px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: calc(var(--base) * 1.2);
|
||||
line-height: base(1.2);
|
||||
padding: base(0.2) base(0.6);
|
||||
}
|
||||
|
||||
&__count {
|
||||
line-height: base(0.8);
|
||||
min-width: base(0.8);
|
||||
text-align: center;
|
||||
background-color: var(--theme-elevation-100);
|
||||
border-radius: var(--style-radius-s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ export const DocumentTab: React.FC<
|
||||
baseClass={baseClass}
|
||||
href={href}
|
||||
isActive={isActive}
|
||||
isCollection={!!collectionConfig && !globalConfig}
|
||||
newTab={newTab}
|
||||
>
|
||||
<span className={`${baseClass}__label`}>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
@layer payload-default {
|
||||
.pill-version-count {
|
||||
line-height: calc(var(--base) * 0.8);
|
||||
min-width: calc(var(--base) * 0.8);
|
||||
text-align: center;
|
||||
background-color: var(--theme-elevation-100);
|
||||
border-radius: var(--style-radius-s);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,7 @@
|
||||
import { useDocumentInfo } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'pill-version-count'
|
||||
import { baseClass } from '../../Tab/index.js'
|
||||
|
||||
export const VersionsPill: React.FC = () => {
|
||||
const { versionCount } = useDocumentInfo()
|
||||
@@ -13,5 +11,5 @@ export const VersionsPill: React.FC = () => {
|
||||
return null
|
||||
}
|
||||
|
||||
return <span className={baseClass}>{versionCount}</span>
|
||||
return <span className={`${baseClass}__count`}>{versionCount}</span>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { groupNavItems } from '@payloadcms/ui/shared'
|
||||
import type { NavPreferences } from 'payload'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { BrowseByFolderButton, Link, NavGroup, useConfig, useTranslation } from '@payloadcms/ui'
|
||||
import { Link, NavGroup, useConfig, useTranslation } from '@payloadcms/ui'
|
||||
import { EntityType } from '@payloadcms/ui/shared'
|
||||
import { usePathname } from 'next/navigation.js'
|
||||
import { formatAdminURL } from 'payload/shared'
|
||||
@@ -20,28 +20,14 @@ export const DefaultNavClient: React.FC<{
|
||||
|
||||
const {
|
||||
config: {
|
||||
admin: {
|
||||
routes: { folders: foldersRoute },
|
||||
},
|
||||
folders: { collections: folderCollections = {}, enabled } = {},
|
||||
routes: { admin: adminRoute },
|
||||
},
|
||||
} = useConfig()
|
||||
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const folderURL = formatAdminURL({
|
||||
adminRoute,
|
||||
path: foldersRoute,
|
||||
})
|
||||
|
||||
const viewingRootFolderView = pathname.startsWith(folderURL)
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{enabled && Object.keys(folderCollections).length > 0 && (
|
||||
<BrowseByFolderButton active={viewingRootFolderView} />
|
||||
)}
|
||||
{groups.map(({ entities, label }, key) => {
|
||||
return (
|
||||
<NavGroup isOpen={navPreferences?.groups?.[label]?.open} key={key} label={label}>
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
import type {
|
||||
AdminViewServerProps,
|
||||
BuildCollectionFolderViewResult,
|
||||
FolderListViewServerPropsOnly,
|
||||
ListQuery,
|
||||
Where,
|
||||
} from 'payload'
|
||||
|
||||
import { DefaultCollectionFolderView, FolderProvider, HydrateAuthProvider } from '@payloadcms/ui'
|
||||
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
|
||||
import { formatAdminURL, mergeListSearchAndWhere } from '@payloadcms/ui/shared'
|
||||
import { redirect } from 'next/navigation.js'
|
||||
import { getFolderData, parseDocumentID } from 'payload'
|
||||
import React from 'react'
|
||||
|
||||
// import { renderFolderViewSlots } from './renderFolderViewSlots.js'
|
||||
|
||||
export type BuildCollectionFolderViewStateArgs = {
|
||||
disableBulkDelete?: boolean
|
||||
disableBulkEdit?: boolean
|
||||
enableRowSelections: boolean
|
||||
folderID?: number | string
|
||||
isInDrawer?: boolean
|
||||
overrideEntityVisibility?: boolean
|
||||
query: ListQuery
|
||||
} & AdminViewServerProps
|
||||
|
||||
/**
|
||||
* Builds the entire view for collection-folder views on the server
|
||||
*/
|
||||
export const buildCollectionFolderView = async (
|
||||
args: BuildCollectionFolderViewStateArgs,
|
||||
): Promise<BuildCollectionFolderViewResult> => {
|
||||
const {
|
||||
disableBulkDelete,
|
||||
disableBulkEdit,
|
||||
enableRowSelections,
|
||||
folderID,
|
||||
initPageResult,
|
||||
isInDrawer,
|
||||
overrideEntityVisibility,
|
||||
params,
|
||||
query: queryFromArgs,
|
||||
searchParams,
|
||||
} = args
|
||||
|
||||
const {
|
||||
collectionConfig,
|
||||
collectionConfig: { slug: collectionSlug },
|
||||
locale: fullLocale,
|
||||
permissions,
|
||||
req: {
|
||||
i18n,
|
||||
payload,
|
||||
payload: { config },
|
||||
query: queryFromReq,
|
||||
user,
|
||||
},
|
||||
visibleEntities,
|
||||
} = initPageResult
|
||||
|
||||
if (!permissions?.collections?.[collectionSlug]?.read) {
|
||||
throw new Error('not-found')
|
||||
}
|
||||
|
||||
if (collectionConfig) {
|
||||
const query = queryFromArgs || queryFromReq
|
||||
|
||||
// const collectionFolderPreferences = await upsertPreferences<ListPreferences>({
|
||||
// key: `${collectionSlug}-collection-folder`,
|
||||
// req,
|
||||
// value: {
|
||||
// },
|
||||
// })
|
||||
|
||||
const {
|
||||
routes: { admin: adminRoute },
|
||||
} = config
|
||||
|
||||
if (
|
||||
(!visibleEntities.collections.includes(collectionSlug) && !overrideEntityVisibility) ||
|
||||
!Object.keys(config.folders.collections).includes(collectionSlug)
|
||||
) {
|
||||
throw new Error('not-found')
|
||||
}
|
||||
|
||||
const whereConstraints = [
|
||||
mergeListSearchAndWhere({
|
||||
collectionConfig,
|
||||
search: typeof query?.search === 'string' ? query.search : undefined,
|
||||
where: (query?.where as Where) || undefined,
|
||||
}),
|
||||
]
|
||||
|
||||
if (folderID) {
|
||||
whereConstraints.push({
|
||||
_folder: {
|
||||
equals: parseDocumentID({ id: folderID, collectionSlug, payload }),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
whereConstraints.push({
|
||||
_folder: {
|
||||
exists: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const { breadcrumbs, documents, subfolders } = await getFolderData({
|
||||
collectionSlug,
|
||||
folderID,
|
||||
payload: initPageResult.req.payload,
|
||||
search: query?.search as string,
|
||||
user: initPageResult.req.user,
|
||||
})
|
||||
|
||||
const resolvedFolderID = breadcrumbs[breadcrumbs.length - 1]?.id
|
||||
|
||||
if (
|
||||
!isInDrawer &&
|
||||
((resolvedFolderID && folderID && folderID !== String(resolvedFolderID)) ||
|
||||
(folderID && !resolvedFolderID))
|
||||
) {
|
||||
return redirect(
|
||||
formatAdminURL({
|
||||
adminRoute,
|
||||
path: `/collections/${collectionSlug}/folders`,
|
||||
serverURL: config.serverURL,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const newDocumentURL = formatAdminURL({
|
||||
adminRoute,
|
||||
path: `/collections/${collectionSlug}/create`,
|
||||
})
|
||||
|
||||
const hasCreatePermission = permissions?.collections?.[collectionSlug]?.create
|
||||
|
||||
const serverProps: FolderListViewServerPropsOnly = {
|
||||
collectionConfig,
|
||||
documents,
|
||||
i18n,
|
||||
locale: fullLocale,
|
||||
params,
|
||||
payload,
|
||||
permissions,
|
||||
searchParams,
|
||||
subfolders,
|
||||
user,
|
||||
}
|
||||
|
||||
// We could support slots in the folder view in the future
|
||||
// const folderViewSlots = renderFolderViewSlots({
|
||||
// clientProps: {
|
||||
// collectionSlug,
|
||||
// hasCreatePermission,
|
||||
// newDocumentURL,
|
||||
// },
|
||||
// collectionConfig,
|
||||
// description: typeof collectionConfig.admin.description === 'function'
|
||||
// ? collectionConfig.admin.description({ t: i18n.t })
|
||||
// : collectionConfig.admin.description,
|
||||
// payload,
|
||||
// serverProps,
|
||||
// })
|
||||
|
||||
const search = query?.search as string
|
||||
|
||||
return {
|
||||
View: (
|
||||
<FolderProvider
|
||||
breadcrumbs={breadcrumbs}
|
||||
collectionSlug={collectionSlug}
|
||||
documents={documents}
|
||||
folderID={folderID}
|
||||
search={search}
|
||||
subfolders={subfolders}
|
||||
>
|
||||
<HydrateAuthProvider permissions={permissions} />
|
||||
{RenderServerComponent({
|
||||
clientProps: {
|
||||
// ...folderViewSlots,
|
||||
collectionSlug,
|
||||
disableBulkDelete,
|
||||
disableBulkEdit,
|
||||
enableRowSelections,
|
||||
hasCreatePermission,
|
||||
newDocumentURL,
|
||||
},
|
||||
Component: collectionConfig?.admin?.components?.views?.list?.Component,
|
||||
Fallback: DefaultCollectionFolderView,
|
||||
importMap: payload.importMap,
|
||||
serverProps,
|
||||
})}
|
||||
</FolderProvider>
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('not-found')
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type React from 'react'
|
||||
|
||||
import { notFound } from 'next/navigation.js'
|
||||
|
||||
import type { BuildCollectionFolderViewStateArgs } from './buildView.js'
|
||||
|
||||
import { buildCollectionFolderView } from './buildView.js'
|
||||
|
||||
export const CollectionFolderView: React.FC<BuildCollectionFolderViewStateArgs> = async (args) => {
|
||||
try {
|
||||
const { View } = await buildCollectionFolderView(args)
|
||||
return View
|
||||
} catch (error) {
|
||||
if (error.message === 'not-found') {
|
||||
notFound()
|
||||
} else {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { Metadata } from 'next'
|
||||
import type { SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
|
||||
import type { GenerateViewMetadata } from '../Root/index.js'
|
||||
|
||||
import { generateMetadata } from '../../utilities/meta.js'
|
||||
|
||||
export const generateCollectionFolderMetadata = async (
|
||||
args: {
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
} & Parameters<GenerateViewMetadata>[0],
|
||||
): Promise<Metadata> => {
|
||||
const { collectionConfig, config, i18n } = args
|
||||
|
||||
let title: string = ''
|
||||
const description: string = ''
|
||||
const keywords: string = ''
|
||||
|
||||
if (collectionConfig) {
|
||||
title = getTranslation(collectionConfig.labels.singular, i18n)
|
||||
}
|
||||
|
||||
title = `${title ? `${title} ` : title}${i18n.t('folder:folders')}`
|
||||
|
||||
return generateMetadata({
|
||||
...(config.admin.meta || {}),
|
||||
description,
|
||||
keywords,
|
||||
serverURL: config.serverURL,
|
||||
title,
|
||||
...(collectionConfig?.admin?.meta || {}),
|
||||
})
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import type {
|
||||
AfterFolderListClientProps,
|
||||
AfterFolderListTableClientProps,
|
||||
AfterFolderListTableServerPropsOnly,
|
||||
BeforeFolderListClientProps,
|
||||
BeforeFolderListServerPropsOnly,
|
||||
BeforeFolderListTableClientProps,
|
||||
BeforeFolderListTableServerPropsOnly,
|
||||
FolderListViewServerPropsOnly,
|
||||
FolderListViewSlots,
|
||||
ListViewSlotSharedClientProps,
|
||||
Payload,
|
||||
SanitizedCollectionConfig,
|
||||
StaticDescription,
|
||||
ViewDescriptionClientProps,
|
||||
ViewDescriptionServerPropsOnly,
|
||||
} from 'payload'
|
||||
|
||||
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
|
||||
|
||||
type Args = {
|
||||
clientProps: ListViewSlotSharedClientProps
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
description?: StaticDescription
|
||||
payload: Payload
|
||||
serverProps: FolderListViewServerPropsOnly
|
||||
}
|
||||
|
||||
export const renderFolderViewSlots = ({
|
||||
clientProps,
|
||||
collectionConfig,
|
||||
description,
|
||||
payload,
|
||||
serverProps,
|
||||
}: Args): FolderListViewSlots => {
|
||||
const result: FolderListViewSlots = {} as FolderListViewSlots
|
||||
|
||||
if (collectionConfig.admin.components?.afterList) {
|
||||
result.AfterFolderList = RenderServerComponent({
|
||||
clientProps: clientProps satisfies AfterFolderListClientProps,
|
||||
Component: collectionConfig.admin.components.afterList,
|
||||
importMap: payload.importMap,
|
||||
serverProps: serverProps satisfies AfterFolderListTableServerPropsOnly,
|
||||
})
|
||||
}
|
||||
|
||||
const listMenuItems = collectionConfig.admin.components?.listMenuItems
|
||||
if (Array.isArray(listMenuItems)) {
|
||||
result.listMenuItems = [
|
||||
RenderServerComponent({
|
||||
clientProps,
|
||||
Component: listMenuItems,
|
||||
importMap: payload.importMap,
|
||||
serverProps,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
if (collectionConfig.admin.components?.afterListTable) {
|
||||
result.AfterFolderListTable = RenderServerComponent({
|
||||
clientProps: clientProps satisfies AfterFolderListTableClientProps,
|
||||
Component: collectionConfig.admin.components.afterListTable,
|
||||
importMap: payload.importMap,
|
||||
serverProps: serverProps satisfies AfterFolderListTableServerPropsOnly,
|
||||
})
|
||||
}
|
||||
|
||||
if (collectionConfig.admin.components?.beforeList) {
|
||||
result.BeforeFolderList = RenderServerComponent({
|
||||
clientProps: clientProps satisfies BeforeFolderListClientProps,
|
||||
Component: collectionConfig.admin.components.beforeList,
|
||||
importMap: payload.importMap,
|
||||
serverProps: serverProps satisfies BeforeFolderListServerPropsOnly,
|
||||
})
|
||||
}
|
||||
|
||||
if (collectionConfig.admin.components?.beforeListTable) {
|
||||
result.BeforeFolderListTable = RenderServerComponent({
|
||||
clientProps: clientProps satisfies BeforeFolderListTableClientProps,
|
||||
Component: collectionConfig.admin.components.beforeListTable,
|
||||
importMap: payload.importMap,
|
||||
serverProps: serverProps satisfies BeforeFolderListTableServerPropsOnly,
|
||||
})
|
||||
}
|
||||
|
||||
if (collectionConfig.admin.components?.Description) {
|
||||
result.Description = RenderServerComponent({
|
||||
clientProps: {
|
||||
collectionSlug: collectionConfig.slug,
|
||||
description,
|
||||
} satisfies ViewDescriptionClientProps,
|
||||
Component: collectionConfig.admin.components.Description,
|
||||
importMap: payload.importMap,
|
||||
serverProps: serverProps satisfies ViewDescriptionServerPropsOnly,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -32,12 +32,9 @@ import { renderDocumentSlots } from './renderDocumentSlots.js'
|
||||
|
||||
export const generateMetadata: GenerateEditViewMetadata = async (args) => getMetaBySegment(args)
|
||||
|
||||
/**
|
||||
* This function is responsible for rendering
|
||||
* an Edit Document view on the server for both:
|
||||
* - default document edit views
|
||||
* - on-demand edit views within drawers
|
||||
*/
|
||||
// This function will be responsible for rendering an Edit Document view
|
||||
// it will be called on the server for Edit page views as well as
|
||||
// called on-demand from document drawers
|
||||
export const renderDocument = async ({
|
||||
disableActions,
|
||||
documentSubViewType,
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
import type {
|
||||
AdminViewServerProps,
|
||||
BuildCollectionFolderViewResult,
|
||||
FolderListViewServerPropsOnly,
|
||||
ListQuery,
|
||||
} from 'payload'
|
||||
|
||||
import { DefaultFolderView, FolderProvider, HydrateAuthProvider } from '@payloadcms/ui'
|
||||
import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerComponent'
|
||||
import { formatAdminURL } from '@payloadcms/ui/shared'
|
||||
import { redirect } from 'next/navigation.js'
|
||||
import { getFolderData } from 'payload'
|
||||
import React from 'react'
|
||||
|
||||
export type BuildFolderViewArgs = {
|
||||
customCellProps?: Record<string, any>
|
||||
disableBulkDelete?: boolean
|
||||
disableBulkEdit?: boolean
|
||||
enableRowSelections: boolean
|
||||
folderID?: number | string
|
||||
isInDrawer?: boolean
|
||||
overrideEntityVisibility?: boolean
|
||||
query: ListQuery
|
||||
} & AdminViewServerProps
|
||||
|
||||
export const buildFolderView = async (
|
||||
args: BuildFolderViewArgs,
|
||||
): Promise<BuildCollectionFolderViewResult> => {
|
||||
const {
|
||||
disableBulkDelete,
|
||||
disableBulkEdit,
|
||||
enableRowSelections,
|
||||
folderID,
|
||||
initPageResult,
|
||||
isInDrawer,
|
||||
params,
|
||||
query: queryFromArgs,
|
||||
searchParams,
|
||||
} = args
|
||||
|
||||
const {
|
||||
locale: fullLocale,
|
||||
permissions,
|
||||
req: {
|
||||
i18n,
|
||||
payload,
|
||||
payload: { config },
|
||||
query: queryFromReq,
|
||||
user,
|
||||
},
|
||||
visibleEntities,
|
||||
} = initPageResult
|
||||
|
||||
const allFolderCollectionSlugs = Object.keys(config?.folders?.collections || {})
|
||||
|
||||
const collections = allFolderCollectionSlugs.filter(
|
||||
(collectionSlug) =>
|
||||
permissions?.collections?.[collectionSlug]?.read &&
|
||||
visibleEntities.collections.includes(collectionSlug),
|
||||
)
|
||||
|
||||
if (!collections.length) {
|
||||
throw new Error('not-found')
|
||||
}
|
||||
|
||||
const query = queryFromArgs || queryFromReq
|
||||
// get relationTo filter from query params
|
||||
const selectedCollectionSlugs: string[] =
|
||||
Array.isArray(query?.relationTo) && query.relationTo.length
|
||||
? query.relationTo
|
||||
: [...allFolderCollectionSlugs, config.folders.slug]
|
||||
|
||||
const {
|
||||
routes: { admin: adminRoute },
|
||||
} = config
|
||||
|
||||
const { breadcrumbs, documents, subfolders } = await getFolderData({
|
||||
folderID,
|
||||
payload: initPageResult.req.payload,
|
||||
search: query?.search as string,
|
||||
user: initPageResult.req.user,
|
||||
})
|
||||
|
||||
const resolvedFolderID = breadcrumbs[breadcrumbs.length - 1]?.id
|
||||
|
||||
if (
|
||||
!isInDrawer &&
|
||||
((resolvedFolderID && folderID && folderID !== String(resolvedFolderID)) ||
|
||||
(folderID && !resolvedFolderID))
|
||||
) {
|
||||
return redirect(
|
||||
formatAdminURL({
|
||||
adminRoute,
|
||||
path: config.admin.routes.folders,
|
||||
serverURL: config.serverURL,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const serverProps: Omit<FolderListViewServerPropsOnly, 'collectionConfig' | 'listPreferences'> = {
|
||||
documents,
|
||||
i18n,
|
||||
locale: fullLocale,
|
||||
params,
|
||||
payload,
|
||||
permissions,
|
||||
searchParams,
|
||||
subfolders,
|
||||
user,
|
||||
}
|
||||
|
||||
// const folderViewSlots = renderFolderViewSlots({
|
||||
// clientProps: {
|
||||
// },
|
||||
// description: staticDescription,
|
||||
// payload,
|
||||
// serverProps,
|
||||
// })
|
||||
|
||||
// documents cannot be created without a parent folder in this view
|
||||
const hasCreatePermissionCollectionSlugs = folderID
|
||||
? [config.folders.slug, ...allFolderCollectionSlugs]
|
||||
: [config.folders.slug]
|
||||
|
||||
return {
|
||||
View: (
|
||||
<FolderProvider
|
||||
breadcrumbs={breadcrumbs}
|
||||
documents={documents}
|
||||
filteredCollectionSlugs={selectedCollectionSlugs}
|
||||
folderID={folderID}
|
||||
subfolders={subfolders}
|
||||
>
|
||||
<HydrateAuthProvider permissions={permissions} />
|
||||
{RenderServerComponent({
|
||||
clientProps: {
|
||||
// ...folderViewSlots,
|
||||
disableBulkDelete,
|
||||
disableBulkEdit,
|
||||
enableRowSelections,
|
||||
hasCreatePermissionCollectionSlugs,
|
||||
selectedCollectionSlugs,
|
||||
},
|
||||
// Component:config.folders?.components?.views?.list?.Component,
|
||||
Fallback: DefaultFolderView,
|
||||
importMap: payload.importMap,
|
||||
serverProps,
|
||||
})}
|
||||
</FolderProvider>
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import type React from 'react'
|
||||
|
||||
import { notFound } from 'next/navigation.js'
|
||||
|
||||
import type { BuildFolderViewArgs } from './buildView.js'
|
||||
|
||||
import { buildFolderView } from './buildView.js'
|
||||
|
||||
export const FolderView: React.FC<BuildFolderViewArgs> = async (args) => {
|
||||
try {
|
||||
const { View } = await buildFolderView(args)
|
||||
return View
|
||||
} catch (error) {
|
||||
if (error.message === 'not-found') {
|
||||
notFound()
|
||||
} else {
|
||||
console.error(error) // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import type { GenerateViewMetadata } from '../Root/index.js'
|
||||
|
||||
import { generateMetadata } from '../../utilities/meta.js'
|
||||
|
||||
export const generateBrowseByFolderMetadata = async (
|
||||
args: Parameters<GenerateViewMetadata>[0],
|
||||
): Promise<Metadata> => {
|
||||
const { config, i18n } = args
|
||||
|
||||
const title: string = i18n.t('folder:browseByFolder')
|
||||
const description: string = ''
|
||||
const keywords: string = ''
|
||||
|
||||
return generateMetadata({
|
||||
...(config.admin.meta || {}),
|
||||
description,
|
||||
keywords,
|
||||
serverURL: config.serverURL,
|
||||
title,
|
||||
})
|
||||
}
|
||||
@@ -40,12 +40,6 @@ type RenderListViewArgs = {
|
||||
redirectAfterDuplicate?: boolean
|
||||
} & AdminViewServerProps
|
||||
|
||||
/**
|
||||
* This function is responsible for rendering
|
||||
* the list view on the server for both:
|
||||
* - default list view
|
||||
* - list view within drawers
|
||||
*/
|
||||
export const renderListView = async (
|
||||
args: RenderListViewArgs,
|
||||
): Promise<{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { AdminViewConfig, SanitizedConfig } from 'payload'
|
||||
|
||||
import type { ViewFromConfig } from './getRouteData.js'
|
||||
import type { ViewFromConfig } from './getViewFromConfig.js'
|
||||
|
||||
import { isPathMatchingRoute } from './isPathMatchingRoute.js'
|
||||
|
||||
|
||||
@@ -14,11 +14,9 @@ import { formatAdminURL } from 'payload/shared'
|
||||
import type { initPage } from '../../utilities/initPage/index.js'
|
||||
|
||||
import { Account } from '../Account/index.js'
|
||||
import { CollectionFolderView } from '../CollectionFolders/index.js'
|
||||
import { CreateFirstUserView } from '../CreateFirstUser/index.js'
|
||||
import { Dashboard } from '../Dashboard/index.js'
|
||||
import { Document as DocumentView } from '../Document/index.js'
|
||||
import { FolderView } from '../Folders/index.js'
|
||||
import { forgotPasswordBaseClass, ForgotPasswordView } from '../ForgotPassword/index.js'
|
||||
import { ListView } from '../List/index.js'
|
||||
import { loginBaseClass, LoginView } from '../Login/index.js'
|
||||
@@ -33,7 +31,6 @@ import { isPathMatchingRoute } from './isPathMatchingRoute.js'
|
||||
|
||||
const baseClasses = {
|
||||
account: 'account',
|
||||
folders: 'folders',
|
||||
forgot: forgotPasswordBaseClass,
|
||||
login: loginBaseClass,
|
||||
reset: resetPasswordBaseClass,
|
||||
@@ -52,7 +49,6 @@ export type ViewFromConfig = {
|
||||
const oneSegmentViews: OneSegmentViews = {
|
||||
account: Account,
|
||||
createFirstUser: CreateFirstUserView,
|
||||
folders: FolderView,
|
||||
forgot: ForgotPasswordView,
|
||||
inactivity: LogoutInactivity,
|
||||
login: LoginView,
|
||||
@@ -60,7 +56,7 @@ const oneSegmentViews: OneSegmentViews = {
|
||||
unauthorized: UnauthorizedView,
|
||||
}
|
||||
|
||||
type GetRouteDataArgs = {
|
||||
type GetViewFromConfigArgs = {
|
||||
adminRoute: string
|
||||
config: SanitizedConfig
|
||||
currentRoute: string
|
||||
@@ -71,10 +67,9 @@ type GetRouteDataArgs = {
|
||||
segments: string[]
|
||||
}
|
||||
|
||||
type GetRouteDataResult = {
|
||||
type GetViewFromConfigResult = {
|
||||
DefaultView: ViewFromConfig
|
||||
documentSubViewType?: DocumentSubViewTypes
|
||||
folderID?: string
|
||||
initPageOptions: Parameters<typeof initPage>[0]
|
||||
serverProps: ServerPropsFromView
|
||||
templateClassName: string
|
||||
@@ -82,20 +77,19 @@ type GetRouteDataResult = {
|
||||
viewType?: ViewTypes
|
||||
}
|
||||
|
||||
export const getRouteData = ({
|
||||
export const getViewFromConfig = ({
|
||||
adminRoute,
|
||||
config,
|
||||
currentRoute,
|
||||
importMap,
|
||||
searchParams,
|
||||
segments,
|
||||
}: GetRouteDataArgs): GetRouteDataResult => {
|
||||
}: GetViewFromConfigArgs): GetViewFromConfigResult => {
|
||||
let ViewToRender: ViewFromConfig = null
|
||||
let templateClassName: string
|
||||
let templateType: 'default' | 'minimal' | undefined
|
||||
let documentSubViewType: DocumentSubViewTypes
|
||||
let viewType: ViewTypes
|
||||
let folderID: string
|
||||
|
||||
const initPageOptions: Parameters<typeof initPage>[0] = {
|
||||
config,
|
||||
@@ -110,7 +104,6 @@ export const getRouteData = ({
|
||||
const isCollection = segmentOne === 'collections'
|
||||
let matchedCollection: SanitizedConfig['collections'][number] = undefined
|
||||
let matchedGlobal: SanitizedConfig['globals'][number] = undefined
|
||||
const isFolderViewEnabled = config.folders?.enabled
|
||||
|
||||
const serverProps: ServerPropsFromView = {
|
||||
viewActions: config?.admin?.components?.actions || [],
|
||||
@@ -160,7 +153,6 @@ export const getRouteData = ({
|
||||
if (oneSegmentViews[viewKey]) {
|
||||
// --> /account
|
||||
// --> /create-first-user
|
||||
// --> /folders
|
||||
// --> /forgot
|
||||
// --> /login
|
||||
// --> /logout
|
||||
@@ -174,11 +166,6 @@ export const getRouteData = ({
|
||||
templateClassName = baseClasses[viewKey]
|
||||
templateType = 'minimal'
|
||||
|
||||
if (isFolderViewEnabled && viewKey === 'folders') {
|
||||
templateType = 'default'
|
||||
viewType = 'folders'
|
||||
}
|
||||
|
||||
if (viewKey === 'account') {
|
||||
templateType = 'default'
|
||||
viewType = 'account'
|
||||
@@ -195,18 +182,9 @@ export const getRouteData = ({
|
||||
templateClassName = baseClasses[segmentTwo]
|
||||
templateType = 'minimal'
|
||||
viewType = 'reset'
|
||||
} else if (`/${segmentOne}` === config.admin.routes.folders) {
|
||||
if (isFolderViewEnabled) {
|
||||
// --> /folders/:folderID
|
||||
ViewToRender = {
|
||||
Component: oneSegmentViews.folders,
|
||||
}
|
||||
templateClassName = baseClasses.folders
|
||||
templateType = 'default'
|
||||
viewType = 'folders'
|
||||
folderID = segmentTwo
|
||||
}
|
||||
} else if (isCollection && matchedCollection) {
|
||||
}
|
||||
|
||||
if (isCollection && matchedCollection) {
|
||||
// --> /collections/:collectionSlug
|
||||
|
||||
ViewToRender = {
|
||||
@@ -251,49 +229,31 @@ export const getRouteData = ({
|
||||
templateType = 'minimal'
|
||||
viewType = 'verify'
|
||||
} else if (isCollection && matchedCollection) {
|
||||
if (segmentThree === 'folders') {
|
||||
if (
|
||||
isFolderViewEnabled &&
|
||||
Object.keys(config.folders.collections).includes(matchedCollection.slug)
|
||||
) {
|
||||
// Collection Folder Views
|
||||
// --> /collections/:collectionSlug/folders
|
||||
// --> /collections/:collectionSlug/folders/:folderID
|
||||
ViewToRender = {
|
||||
Component: CollectionFolderView,
|
||||
}
|
||||
// Custom Views
|
||||
// --> /collections/:collectionSlug/:id
|
||||
// --> /collections/:collectionSlug/:id/api
|
||||
// --> /collections/:collectionSlug/:id/preview
|
||||
// --> /collections/:collectionSlug/:id/versions
|
||||
// --> /collections/:collectionSlug/:id/versions/:versionID
|
||||
|
||||
templateClassName = `collection-folders`
|
||||
templateType = 'default'
|
||||
viewType = 'collection-folders'
|
||||
folderID = segmentFour
|
||||
}
|
||||
} else {
|
||||
// Collection Edit Views
|
||||
// --> /collections/:collectionSlug/:id
|
||||
// --> /collections/:collectionSlug/:id/api
|
||||
// --> /collections/:collectionSlug/:id/preview
|
||||
// --> /collections/:collectionSlug/:id/versions
|
||||
// --> /collections/:collectionSlug/:id/versions/:versionID
|
||||
ViewToRender = {
|
||||
Component: DocumentView,
|
||||
}
|
||||
|
||||
templateClassName = `collection-default-edit`
|
||||
templateType = 'default'
|
||||
|
||||
const viewInfo = getDocumentViewInfo([segmentFour, segmentFive])
|
||||
viewType = viewInfo.viewType
|
||||
documentSubViewType = viewInfo.documentSubViewType
|
||||
|
||||
attachViewActions({
|
||||
collectionOrGlobal: matchedCollection,
|
||||
serverProps,
|
||||
viewKeyArg: documentSubViewType,
|
||||
})
|
||||
ViewToRender = {
|
||||
Component: DocumentView,
|
||||
}
|
||||
|
||||
templateClassName = `collection-default-edit`
|
||||
templateType = 'default'
|
||||
|
||||
const viewInfo = getDocumentViewInfo([segmentFour, segmentFive])
|
||||
viewType = viewInfo.viewType
|
||||
documentSubViewType = viewInfo.documentSubViewType
|
||||
|
||||
attachViewActions({
|
||||
collectionOrGlobal: matchedCollection,
|
||||
serverProps,
|
||||
viewKeyArg: documentSubViewType,
|
||||
})
|
||||
} else if (isGlobal && matchedGlobal) {
|
||||
// Global Edit Views
|
||||
// Custom Views
|
||||
// --> /globals/:globalSlug/versions
|
||||
// --> /globals/:globalSlug/preview
|
||||
// --> /globals/:globalSlug/versions/:versionID
|
||||
@@ -328,7 +288,6 @@ export const getRouteData = ({
|
||||
return {
|
||||
DefaultView: ViewToRender,
|
||||
documentSubViewType,
|
||||
folderID,
|
||||
initPageOptions,
|
||||
serverProps,
|
||||
templateClassName,
|
||||
@@ -11,12 +11,12 @@ import { RenderServerComponent } from '@payloadcms/ui/elements/RenderServerCompo
|
||||
import { getClientConfig } from '@payloadcms/ui/utilities/getClientConfig'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import { formatAdminURL } from 'payload/shared'
|
||||
import React from 'react'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { DefaultTemplate } from '../../templates/Default/index.js'
|
||||
import { MinimalTemplate } from '../../templates/Minimal/index.js'
|
||||
import { initPage } from '../../utilities/initPage/index.js'
|
||||
import { getRouteData } from './getRouteData.js'
|
||||
import { getViewFromConfig } from './getViewFromConfig.js'
|
||||
|
||||
export type GenerateViewMetadata = (args: {
|
||||
config: SanitizedConfig
|
||||
@@ -64,13 +64,12 @@ export const RootPage = async ({
|
||||
const {
|
||||
DefaultView,
|
||||
documentSubViewType,
|
||||
folderID,
|
||||
initPageOptions,
|
||||
serverProps,
|
||||
templateClassName,
|
||||
templateType,
|
||||
viewType,
|
||||
} = getRouteData({
|
||||
} = getViewFromConfig({
|
||||
adminRoute,
|
||||
config,
|
||||
currentRoute,
|
||||
@@ -90,10 +89,6 @@ export const RootPage = async ({
|
||||
})
|
||||
?.then((doc) => !!doc))
|
||||
|
||||
/**
|
||||
* This function is responsible for handling the case where the view is not found.
|
||||
* The current route did not match any default views or custom route views.
|
||||
*/
|
||||
if (!DefaultView?.Component && !DefaultView?.payloadComponent) {
|
||||
if (initPageResult?.req?.user) {
|
||||
notFound()
|
||||
@@ -146,7 +141,6 @@ export const RootPage = async ({
|
||||
...serverProps,
|
||||
clientConfig,
|
||||
docID: initPageResult?.docID,
|
||||
folderID,
|
||||
i18n: initPageResult?.req.i18n,
|
||||
importMap,
|
||||
initPageResult,
|
||||
@@ -157,8 +151,8 @@ export const RootPage = async ({
|
||||
})
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
{!templateType && <React.Fragment>{RenderedView}</React.Fragment>}
|
||||
<Fragment>
|
||||
{!templateType && <Fragment>{RenderedView}</Fragment>}
|
||||
{templateType === 'minimal' && (
|
||||
<MinimalTemplate className={templateClassName}>{RenderedView}</MinimalTemplate>
|
||||
)}
|
||||
@@ -188,6 +182,6 @@ export const RootPage = async ({
|
||||
{RenderedView}
|
||||
</DefaultTemplate>
|
||||
)}
|
||||
</React.Fragment>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,9 @@ import type { SanitizedConfig } from 'payload'
|
||||
|
||||
import { getNextRequestI18n } from '../../utilities/getNextRequestI18n.js'
|
||||
import { generateAccountViewMetadata } from '../Account/metadata.js'
|
||||
import { generateCollectionFolderMetadata } from '../CollectionFolders/metadata.js'
|
||||
import { generateCreateFirstUserViewMetadata } from '../CreateFirstUser/metadata.js'
|
||||
import { generateDashboardViewMetadata } from '../Dashboard/metadata.js'
|
||||
import { generateDocumentViewMetadata } from '../Document/metadata.js'
|
||||
import { generateBrowseByFolderMetadata } from '../Folders/metadata.js'
|
||||
import { generateForgotPasswordViewMetadata } from '../ForgotPassword/metadata.js'
|
||||
import { generateListViewMetadata } from '../List/metadata.js'
|
||||
import { generateLoginViewMetadata } from '../Login/metadata.js'
|
||||
@@ -20,7 +18,6 @@ import { getCustomViewByRoute } from './getCustomViewByRoute.js'
|
||||
|
||||
const oneSegmentMeta = {
|
||||
'create-first-user': generateCreateFirstUserViewMetadata,
|
||||
folders: generateBrowseByFolderMetadata,
|
||||
forgot: generateForgotPasswordViewMetadata,
|
||||
login: generateLoginViewMetadata,
|
||||
logout: generateUnauthorizedViewMetadata,
|
||||
@@ -48,7 +45,7 @@ export const generatePageMetadata = async ({
|
||||
const segments = Array.isArray(params.segments) ? params.segments : []
|
||||
|
||||
const currentRoute = `/${segments.join('/')}`
|
||||
const [segmentOne, segmentTwo, segmentThree] = segments
|
||||
const [segmentOne, segmentTwo] = segments
|
||||
|
||||
const isGlobal = segmentOne === 'globals'
|
||||
const isCollection = segmentOne === 'collections'
|
||||
@@ -78,7 +75,6 @@ export const generatePageMetadata = async ({
|
||||
if (oneSegmentMeta[segmentOne] && segmentOne !== 'account') {
|
||||
// --> /create-first-user
|
||||
// --> /forgot
|
||||
// --> /folders
|
||||
// --> /login
|
||||
// --> /logout
|
||||
// --> /logout-inactivity
|
||||
@@ -96,10 +92,8 @@ export const generatePageMetadata = async ({
|
||||
if (`/${segmentOne}` === config.admin.routes.reset) {
|
||||
// --> /reset/:token
|
||||
meta = await generateResetPasswordViewMetadata({ config, i18n })
|
||||
} else if (`/${segmentOne}` === config.admin.routes.folders) {
|
||||
// --> /folders/:folderID
|
||||
meta = await generateBrowseByFolderMetadata({ config, i18n })
|
||||
} else if (isCollection) {
|
||||
}
|
||||
if (isCollection) {
|
||||
// --> /collections/:collectionSlug
|
||||
meta = await generateListViewMetadata({ collectionConfig, config, i18n })
|
||||
} else if (isGlobal) {
|
||||
@@ -118,29 +112,15 @@ export const generatePageMetadata = async ({
|
||||
// --> /:collectionSlug/verify/:token
|
||||
meta = await generateVerifyViewMetadata({ config, i18n })
|
||||
} else if (isCollection) {
|
||||
if (segmentThree === 'folders') {
|
||||
if (Object.keys(config.folders.collections).includes(collectionConfig.slug)) {
|
||||
// Collection Folder Views
|
||||
// --> /collections/:collectionSlug/folders
|
||||
// --> /collections/:collectionSlug/folders/:id
|
||||
meta = await generateCollectionFolderMetadata({
|
||||
collectionConfig,
|
||||
config,
|
||||
i18n,
|
||||
params,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// Collection Document Views
|
||||
// --> /collections/:collectionSlug/:id
|
||||
// --> /collections/:collectionSlug/:id/preview
|
||||
// --> /collections/:collectionSlug/:id/versions
|
||||
// --> /collections/:collectionSlug/:id/versions/:version
|
||||
// --> /collections/:collectionSlug/:id/api
|
||||
meta = await generateDocumentViewMetadata({ collectionConfig, config, i18n, params })
|
||||
}
|
||||
// Custom Views
|
||||
// --> /collections/:collectionSlug/:id
|
||||
// --> /collections/:collectionSlug/:id/preview
|
||||
// --> /collections/:collectionSlug/:id/versions
|
||||
// --> /collections/:collectionSlug/:id/versions/:version
|
||||
// --> /collections/:collectionSlug/:id/api
|
||||
meta = await generateDocumentViewMetadata({ collectionConfig, config, i18n, params })
|
||||
} else if (isGlobal) {
|
||||
// Global Document Views
|
||||
// Custom Views
|
||||
// --> /globals/:globalSlug/versions
|
||||
// --> /globals/:globalSlug/versions/:version
|
||||
// --> /globals/:globalSlug/preview
|
||||
|
||||
@@ -18,11 +18,9 @@ export const renderPill = (data, latestVersion, currentLabel, previousLabel, pil
|
||||
return (
|
||||
<React.Fragment>
|
||||
{data?.id === latestVersion ? (
|
||||
<Pill pillStyle={pillStyle} size="small">
|
||||
{currentLabel}
|
||||
</Pill>
|
||||
<Pill pillStyle={pillStyle}>{currentLabel}</Pill>
|
||||
) : (
|
||||
<Pill size="small">{previousLabel}</Pill>
|
||||
<Pill>{previousLabel}</Pill>
|
||||
)}
|
||||
|
||||
</React.Fragment>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @param {import('next').NextConfig} nextConfig
|
||||
* @param {Object} [sortOnOptions] - Optional configuration options
|
||||
* @param {boolean} [sortOnOptions.devBundleServerPackages] - Whether to bundle server packages in development mode. @default true
|
||||
* @param {Object} [options] - Optional configuration options
|
||||
* @param {boolean} [options.devBundleServerPackages] - Whether to bundle server packages in development mode. @default true
|
||||
*
|
||||
* @returns {import('next').NextConfig}
|
||||
* */
|
||||
|
||||
@@ -53,7 +53,7 @@ export type ListQuery = {
|
||||
search?: string
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
} & Record<string, unknown>
|
||||
}
|
||||
|
||||
export type BuildTableStateArgs = {
|
||||
collectionSlug: string | string[]
|
||||
@@ -71,7 +71,3 @@ export type BuildTableStateArgs = {
|
||||
req: PayloadRequest
|
||||
tableAppearance?: 'condensed' | 'default'
|
||||
}
|
||||
|
||||
export type BuildCollectionFolderViewResult = {
|
||||
View: React.ReactNode
|
||||
}
|
||||
|
||||
@@ -563,7 +563,6 @@ export type DocumentSlots = {
|
||||
}
|
||||
|
||||
export type {
|
||||
BuildCollectionFolderViewResult,
|
||||
BuildTableStateArgs,
|
||||
DefaultServerFunctionArgs,
|
||||
ListQuery,
|
||||
@@ -619,26 +618,6 @@ export type {
|
||||
EditViewProps,
|
||||
} from './views/document.js'
|
||||
|
||||
export type {
|
||||
AfterFolderListClientProps,
|
||||
AfterFolderListServerProps,
|
||||
AfterFolderListServerPropsOnly,
|
||||
AfterFolderListTableClientProps,
|
||||
AfterFolderListTableServerProps,
|
||||
AfterFolderListTableServerPropsOnly,
|
||||
BeforeFolderListClientProps,
|
||||
BeforeFolderListServerProps,
|
||||
BeforeFolderListServerPropsOnly,
|
||||
BeforeFolderListTableClientProps,
|
||||
BeforeFolderListTableServerProps,
|
||||
BeforeFolderListTableServerPropsOnly,
|
||||
FolderListViewClientProps,
|
||||
FolderListViewServerProps,
|
||||
FolderListViewServerPropsOnly,
|
||||
FolderListViewSlots,
|
||||
FolderListViewSlotSharedClientProps,
|
||||
} from './views/folderList.js'
|
||||
|
||||
export type {
|
||||
AdminViewClientProps,
|
||||
/**
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { ServerProps } from '../../config/types.js'
|
||||
import type { FolderOrDocument } from '../../folders/types.js'
|
||||
import type { SanitizedCollectionConfig } from '../../index.js'
|
||||
export type FolderListViewSlots = {
|
||||
AfterFolderList?: React.ReactNode
|
||||
AfterFolderListTable?: React.ReactNode
|
||||
BeforeFolderList?: React.ReactNode
|
||||
BeforeFolderListTable?: React.ReactNode
|
||||
Description?: React.ReactNode
|
||||
listMenuItems?: React.ReactNode[]
|
||||
Table: React.ReactNode
|
||||
}
|
||||
|
||||
export type FolderListViewServerPropsOnly = {
|
||||
collectionConfig: SanitizedCollectionConfig
|
||||
documents: FolderOrDocument[]
|
||||
subfolders: FolderOrDocument[]
|
||||
} & ServerProps
|
||||
|
||||
export type FolderListViewServerProps = FolderListViewClientProps & FolderListViewServerPropsOnly
|
||||
|
||||
export type FolderListViewClientProps = {
|
||||
beforeActions?: React.ReactNode[]
|
||||
collectionSlug: SanitizedCollectionConfig['slug']
|
||||
disableBulkDelete?: boolean
|
||||
disableBulkEdit?: boolean
|
||||
enableRowSelections?: boolean
|
||||
hasCreatePermission: boolean
|
||||
newDocumentURL: string
|
||||
} & FolderListViewSlots
|
||||
|
||||
export type FolderListViewSlotSharedClientProps = {
|
||||
collectionSlug: SanitizedCollectionConfig['slug']
|
||||
hasCreatePermission: boolean
|
||||
newDocumentURL: string
|
||||
}
|
||||
|
||||
// BeforeFolderList
|
||||
export type BeforeFolderListClientProps = FolderListViewSlotSharedClientProps
|
||||
export type BeforeFolderListServerPropsOnly = {} & FolderListViewServerPropsOnly
|
||||
export type BeforeFolderListServerProps = BeforeFolderListClientProps &
|
||||
BeforeFolderListServerPropsOnly
|
||||
|
||||
// BeforeFolderListTable
|
||||
export type BeforeFolderListTableClientProps = FolderListViewSlotSharedClientProps
|
||||
export type BeforeFolderListTableServerPropsOnly = {} & FolderListViewServerPropsOnly
|
||||
export type BeforeFolderListTableServerProps = BeforeFolderListTableClientProps &
|
||||
BeforeFolderListTableServerPropsOnly
|
||||
|
||||
// AfterFolderList
|
||||
export type AfterFolderListClientProps = FolderListViewSlotSharedClientProps
|
||||
export type AfterFolderListServerPropsOnly = {} & FolderListViewServerPropsOnly
|
||||
export type AfterFolderListServerProps = AfterFolderListClientProps & AfterFolderListServerPropsOnly
|
||||
|
||||
// AfterFolderListTable
|
||||
export type AfterFolderListTableClientProps = FolderListViewSlotSharedClientProps
|
||||
export type AfterFolderListTableServerPropsOnly = {} & FolderListViewServerPropsOnly
|
||||
export type AfterFolderListTableServerProps = AfterFolderListTableClientProps &
|
||||
AfterFolderListTableServerPropsOnly
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ClientTranslationsObject } from '@payloadcms/translations'
|
||||
import type { ClientTranslationsObject, I18n } from '@payloadcms/translations'
|
||||
|
||||
import type { SanitizedPermissions } from '../../auth/index.js'
|
||||
import type { ImportMap } from '../../bin/generateImportMap/index.js'
|
||||
@@ -8,12 +8,13 @@ import type {
|
||||
CustomComponent,
|
||||
Locale,
|
||||
MetaConfig,
|
||||
Params,
|
||||
PayloadComponent,
|
||||
SanitizedConfig,
|
||||
ServerProps,
|
||||
} from '../../config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../../globals/config/types.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { Payload, PayloadRequest } from '../../types/index.js'
|
||||
import type { LanguageOptions } from '../LanguageOptions.js'
|
||||
import type { Data, StaticDescription } from '../types.js'
|
||||
import type { DocumentSubViewTypes } from './document.js'
|
||||
@@ -41,14 +42,9 @@ export type AdminViewServerPropsOnly = {
|
||||
* @todo remove `docID` here as it is already contained in `initPageResult`
|
||||
*/
|
||||
readonly docID?: number | string
|
||||
readonly folderID?: string
|
||||
readonly importMap: ImportMap
|
||||
readonly initialData?: Data
|
||||
readonly initPageResult: InitPageResult
|
||||
readonly params?: { [key: string]: string | string[] | undefined }
|
||||
readonly redirectAfterCreate?: boolean
|
||||
readonly redirectAfterDelete?: boolean
|
||||
readonly redirectAfterDuplicate?: boolean
|
||||
} & ServerProps
|
||||
|
||||
export type AdminViewServerProps = AdminViewClientProps & AdminViewServerPropsOnly
|
||||
@@ -82,10 +78,8 @@ export type InitPageResult = {
|
||||
*/
|
||||
export type ViewTypes =
|
||||
| 'account'
|
||||
| 'collection-folders'
|
||||
| 'dashboard'
|
||||
| 'document'
|
||||
| 'folders'
|
||||
| 'list'
|
||||
| 'reset'
|
||||
| 'verify'
|
||||
|
||||
@@ -38,7 +38,7 @@ export type ImportMap = {
|
||||
[path: UserImportPath]: any
|
||||
}
|
||||
|
||||
export type AddToImportMap = (payloadComponent?: PayloadComponent | PayloadComponent[]) => void
|
||||
export type AddToImportMap = (payloadComponent: PayloadComponent | PayloadComponent[]) => void
|
||||
|
||||
export async function generateImportMap(
|
||||
config: SanitizedConfig,
|
||||
|
||||
@@ -256,11 +256,6 @@ export type AfterForgotPasswordHook = (args: {
|
||||
context: RequestContext
|
||||
}) => any
|
||||
|
||||
export type EnableFoldersOptions = {
|
||||
// Displays the folder collection and parentFolder field in the document view
|
||||
debug?: boolean
|
||||
}
|
||||
|
||||
export type BaseListFilter = (args: {
|
||||
limit: number
|
||||
locale?: TypedLocale
|
||||
@@ -588,9 +583,8 @@ export type SanitizedJoins = {
|
||||
export interface SanitizedCollectionConfig
|
||||
extends Omit<
|
||||
DeepRequired<CollectionConfig>,
|
||||
'admin' | 'auth' | 'endpoints' | 'fields' | 'slug' | 'upload' | 'versions'
|
||||
'auth' | 'endpoints' | 'fields' | 'slug' | 'upload' | 'versions'
|
||||
> {
|
||||
admin: CollectionAdminOptions
|
||||
auth: Auth
|
||||
endpoints: Endpoint[] | false
|
||||
fields: Field[]
|
||||
|
||||
@@ -24,7 +24,6 @@ export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
||||
routes: {
|
||||
account: '/account',
|
||||
createFirstUser: '/create-first-user',
|
||||
folders: '/folders',
|
||||
forgot: '/forgot',
|
||||
inactivity: '/logout-inactivity',
|
||||
login: '/login',
|
||||
@@ -100,7 +99,6 @@ export const addDefaultsToConfig = (config: Config): Config => {
|
||||
routes: {
|
||||
account: '/account',
|
||||
createFirstUser: '/create-first-user',
|
||||
folders: '/folders',
|
||||
forgot: '/forgot',
|
||||
inactivity: '/logout-inactivity',
|
||||
login: '/login',
|
||||
@@ -111,13 +109,6 @@ export const addDefaultsToConfig = (config: Config): Config => {
|
||||
},
|
||||
}
|
||||
|
||||
config.folders = {
|
||||
collections: {},
|
||||
debug: false,
|
||||
enabled: false,
|
||||
...(config.folders || {}),
|
||||
}
|
||||
|
||||
config.bin = config.bin ?? []
|
||||
config.collections = config.collections ?? []
|
||||
config.cookiePrefix = config.cookiePrefix ?? 'payload'
|
||||
|
||||
@@ -18,7 +18,6 @@ import { sanitizeCollection } from '../collections/config/sanitize.js'
|
||||
import { migrationsCollection } from '../database/migrations/migrationsCollection.js'
|
||||
import { DuplicateCollection, InvalidConfiguration } from '../errors/index.js'
|
||||
import { defaultTimezones } from '../fields/baseFields/timezone/defaultTimezones.js'
|
||||
import { addFolderCollections } from '../folders/addFolderCollections.js'
|
||||
import { sanitizeGlobal } from '../globals/config/sanitize.js'
|
||||
import {
|
||||
baseBlockFields,
|
||||
@@ -191,8 +190,6 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
|
||||
const collectionSlugs = new Set<CollectionSlug>()
|
||||
|
||||
await addFolderCollections(config as unknown as Config)
|
||||
|
||||
const validRelationships = [
|
||||
...(config.collections.map((c) => c.slug) ?? []),
|
||||
jobsCollectionSlug,
|
||||
@@ -316,7 +313,6 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
for (const hook of Object.keys(hooks)) {
|
||||
const defaultAmount = hook === 'afterRead' || hook === 'beforeChange' ? 1 : 0
|
||||
if (hooks[hook]?.length > defaultAmount) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`The jobsCollectionOverrides function is returning a collection with an additional ${hook} hook defined. These hooks will not run unless the jobs.runHooks option is set to true. Setting this option to true will negatively impact performance.`,
|
||||
)
|
||||
@@ -378,15 +374,7 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
config.csrf.push(config.serverURL)
|
||||
}
|
||||
|
||||
const uploadAdapters = new Set<string>()
|
||||
// interact with all collections
|
||||
for (const collection of config.collections) {
|
||||
// deduped upload adapters
|
||||
if (collection.upload?.adapter) {
|
||||
uploadAdapters.add(collection.upload.adapter)
|
||||
}
|
||||
}
|
||||
|
||||
// Get deduped list of upload adapters
|
||||
if (!config.upload) {
|
||||
config.upload = { adapters: [] }
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import type { ErrorName } from '../errors/types.js'
|
||||
import type { GlobalConfig, Globals, SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||
import type {
|
||||
Block,
|
||||
CollectionSlug,
|
||||
FlattenedBlock,
|
||||
JobsConfig,
|
||||
Payload,
|
||||
@@ -852,11 +851,6 @@ export type Config = {
|
||||
* @default '/create-first-user'
|
||||
*/
|
||||
createFirstUser?: `/${string}`
|
||||
/** The route for folder view.
|
||||
*
|
||||
* @default '/folders'
|
||||
*/
|
||||
folders: `/${string}`
|
||||
/** The route for the forgot password page.
|
||||
*
|
||||
* @default '/forgot'
|
||||
@@ -983,44 +977,6 @@ export type Config = {
|
||||
email?: EmailAdapter | Promise<EmailAdapter>
|
||||
/** Custom REST endpoints */
|
||||
endpoints?: Endpoint[]
|
||||
/**
|
||||
* Options for folder view within the admin panel
|
||||
*/
|
||||
folders?: {
|
||||
/**
|
||||
* An array of functions to be ran when the folder collection is initialized
|
||||
* This allows plugins to modify the collection configuration
|
||||
*/
|
||||
collectionOverrides?: (({
|
||||
collection,
|
||||
}: {
|
||||
collection: CollectionConfig
|
||||
}) => CollectionConfig | Promise<CollectionConfig>)[]
|
||||
/**
|
||||
* Collections that you would like organize within folders
|
||||
*/
|
||||
collections: {
|
||||
[key: CollectionSlug]: any
|
||||
}
|
||||
/**
|
||||
* Ability to view hidden fields and collections related to folders
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
debug?: boolean
|
||||
/**
|
||||
* Enable folders in the admin panel
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
enabled?: boolean
|
||||
/**
|
||||
* Slug for the folder collection
|
||||
*
|
||||
* @default "_folders"
|
||||
*/
|
||||
slug?: string
|
||||
}
|
||||
/**
|
||||
* @see https://payloadcms.com/docs/configuration/globals#global-configs
|
||||
*/
|
||||
@@ -1164,6 +1120,7 @@ export type Config = {
|
||||
read?: QueryPresetConstraints
|
||||
update?: QueryPresetConstraints
|
||||
}
|
||||
hooks: CollectionConfig['hooks']
|
||||
labels?: CollectionConfig['labels']
|
||||
}
|
||||
/** Control the routing structure that Payload binds itself to. */
|
||||
|
||||
@@ -40,24 +40,11 @@ export { getFieldPaths } from '../fields/getFieldPaths.js'
|
||||
|
||||
export * from '../fields/validations.js'
|
||||
|
||||
export type {
|
||||
FolderBreadcrumb,
|
||||
FolderDocumentItemKey,
|
||||
FolderEnabledColection,
|
||||
FolderInterface,
|
||||
FolderOrDocument,
|
||||
GetFolderDataResult,
|
||||
Subfolder,
|
||||
} from '../folders/types.js'
|
||||
export { formatFolderOrDocumentItem } from '../folders/utils/formatFolderOrDocumentItem.js'
|
||||
|
||||
export { validOperators, validOperatorSet } from '../types/constants.js'
|
||||
|
||||
export { formatFilesize } from '../uploads/formatFilesize.js'
|
||||
|
||||
export { isImage } from '../uploads/isImage.js'
|
||||
export { combineWhereConstraints } from '../utilities/combineWhereConstraints.js'
|
||||
|
||||
export {
|
||||
deepCopyObject,
|
||||
deepCopyObjectComplex,
|
||||
@@ -72,7 +59,6 @@ export {
|
||||
deepMergeWithSourceArrays,
|
||||
} from '../utilities/deepMerge.js'
|
||||
|
||||
export { extractID } from '../utilities/extractID.js'
|
||||
export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON.js'
|
||||
export { flattenAllFields } from '../utilities/flattenAllFields.js'
|
||||
|
||||
@@ -125,5 +111,4 @@ export { validateWhereQuery } from '../utilities/validateWhereQuery.js'
|
||||
export { wait } from '../utilities/wait.js'
|
||||
export { default as wordBoundariesRegex } from '../utilities/wordBoundariesRegex.js'
|
||||
export { versionDefaults } from '../versions/defaults.js'
|
||||
|
||||
export { deepMergeSimple } from '@payloadcms/translations/utilities'
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import type { Config } from '../config/types.js'
|
||||
import type { CollectionSlug } from '../index.js'
|
||||
|
||||
import { foldersSlug, parentFolderFieldName } from './constants.js'
|
||||
import { createFolderCollection } from './createFolderCollection.js'
|
||||
|
||||
export async function addFolderCollections(config: NonNullable<Config>): Promise<void> {
|
||||
if (!config.collections) {
|
||||
return
|
||||
}
|
||||
|
||||
if (config.folders?.enabled) {
|
||||
const enabledCollectionSlugs: CollectionSlug[] = []
|
||||
const debug = Boolean(config.folders?.debug)
|
||||
config.folders.slug = config.folders.slug || foldersSlug
|
||||
|
||||
for (let i = 0; i < config.collections.length; i++) {
|
||||
const collection = config.collections[i]
|
||||
if (config.folders.collections[collection.slug]) {
|
||||
if (collection) {
|
||||
collection.fields.push({
|
||||
name: parentFolderFieldName,
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
allowCreate: false,
|
||||
allowEdit: false,
|
||||
components: {
|
||||
Cell: '@payloadcms/ui/rsc#FolderTableCell',
|
||||
Field: '@payloadcms/ui#FolderEditField',
|
||||
},
|
||||
},
|
||||
index: true,
|
||||
label: 'Folder',
|
||||
relationTo: config.folders.slug,
|
||||
})
|
||||
enabledCollectionSlugs.push(collection.slug)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (enabledCollectionSlugs.length) {
|
||||
let folderCollection = createFolderCollection({
|
||||
slug: config.folders.slug,
|
||||
collectionSlugs: enabledCollectionSlugs,
|
||||
debug,
|
||||
})
|
||||
|
||||
if (
|
||||
Array.isArray(config.folders.collectionOverrides) &&
|
||||
config.folders.collectionOverrides.length
|
||||
) {
|
||||
for (const override of config.folders.collectionOverrides) {
|
||||
folderCollection = await override({ collection: folderCollection })
|
||||
}
|
||||
}
|
||||
config.collections.push(folderCollection)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export const foldersSlug = '_folders'
|
||||
export const parentFolderFieldName = '_folder'
|
||||
@@ -1,73 +0,0 @@
|
||||
import type { CollectionConfig } from '../collections/config/types.js'
|
||||
|
||||
import { parentFolderFieldName } from './constants.js'
|
||||
import { populateFolderDataEndpoint } from './endpoints/populateFolderData.js'
|
||||
import { deleteSubfoldersAfterDelete } from './hooks/deleteSubfoldersAfterDelete.js'
|
||||
import { dissasociateAfterDelete } from './hooks/dissasociateAfterDelete.js'
|
||||
import { reparentChildFolder } from './hooks/reparentChildFolder.js'
|
||||
|
||||
type CreateFolderCollectionArgs = {
|
||||
collectionSlugs: string[]
|
||||
debug?: boolean
|
||||
slug: string
|
||||
}
|
||||
export const createFolderCollection = ({
|
||||
slug,
|
||||
collectionSlugs,
|
||||
debug,
|
||||
}: CreateFolderCollectionArgs): CollectionConfig => ({
|
||||
slug,
|
||||
admin: {
|
||||
hidden: !debug,
|
||||
useAsTitle: 'name',
|
||||
},
|
||||
endpoints: [populateFolderDataEndpoint],
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
index: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: parentFolderFieldName,
|
||||
type: 'relationship',
|
||||
admin: {
|
||||
hidden: !debug,
|
||||
},
|
||||
index: true,
|
||||
relationTo: slug,
|
||||
},
|
||||
{
|
||||
name: 'documentsAndFolders',
|
||||
type: 'join',
|
||||
admin: {
|
||||
hidden: !debug,
|
||||
},
|
||||
collection: [slug, ...collectionSlugs],
|
||||
hasMany: true,
|
||||
on: parentFolderFieldName,
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
afterChange: [
|
||||
reparentChildFolder({
|
||||
parentFolderFieldName,
|
||||
}),
|
||||
],
|
||||
afterDelete: [
|
||||
dissasociateAfterDelete({
|
||||
collectionSlugs,
|
||||
parentFolderFieldName,
|
||||
}),
|
||||
deleteSubfoldersAfterDelete({ folderSlug: slug, parentFolderFieldName }),
|
||||
],
|
||||
},
|
||||
labels: {
|
||||
plural: 'Folders',
|
||||
singular: 'Folder',
|
||||
},
|
||||
typescript: {
|
||||
interface: 'FolderInterface',
|
||||
},
|
||||
})
|
||||
@@ -1,45 +0,0 @@
|
||||
import httpStatus from 'http-status'
|
||||
|
||||
import type { Endpoint } from '../../index.js'
|
||||
|
||||
import { getFolderData } from '../utils/getFolderData.js'
|
||||
|
||||
export const populateFolderDataEndpoint: Endpoint = {
|
||||
handler: async (req) => {
|
||||
if (!req?.user) {
|
||||
return Response.json(
|
||||
{
|
||||
message: 'Unauthorized request.',
|
||||
},
|
||||
{
|
||||
status: httpStatus.UNAUTHORIZED,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const folderCollection = Boolean(req.payload.collections?.[req.payload.config.folders.slug])
|
||||
|
||||
if (!folderCollection) {
|
||||
return Response.json(
|
||||
{
|
||||
message: 'Folders are not configured',
|
||||
},
|
||||
{
|
||||
status: httpStatus.NOT_FOUND,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const data = await getFolderData({
|
||||
collectionSlug: req.searchParams?.get('collectionSlug') || undefined,
|
||||
folderID: req.searchParams?.get('folderID') || undefined,
|
||||
payload: req.payload,
|
||||
search: req.searchParams?.get('search') || undefined,
|
||||
user: req.user,
|
||||
})
|
||||
|
||||
return Response.json(data)
|
||||
},
|
||||
method: 'get',
|
||||
path: '/populate-folder-data',
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { CollectionAfterDeleteHook } from '../../index.js'
|
||||
|
||||
type Args = {
|
||||
folderSlug: string
|
||||
parentFolderFieldName: string
|
||||
}
|
||||
export const deleteSubfoldersAfterDelete = ({
|
||||
folderSlug,
|
||||
parentFolderFieldName,
|
||||
}: Args): CollectionAfterDeleteHook => {
|
||||
return async ({ id, req }) => {
|
||||
await req.payload.delete({
|
||||
collection: folderSlug,
|
||||
depth: 0,
|
||||
overrideAccess: false,
|
||||
req,
|
||||
select: { id: true },
|
||||
where: {
|
||||
[parentFolderFieldName]: {
|
||||
equals: id,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { CollectionAfterDeleteHook } from '../../index.js'
|
||||
|
||||
type Args = {
|
||||
collectionSlugs: string[]
|
||||
parentFolderFieldName: string
|
||||
}
|
||||
export const dissasociateAfterDelete = ({
|
||||
collectionSlugs,
|
||||
parentFolderFieldName,
|
||||
}: Args): CollectionAfterDeleteHook => {
|
||||
return async ({ id, req }) => {
|
||||
for (const collectionSlug of collectionSlugs) {
|
||||
await req.payload.update({
|
||||
collection: collectionSlug,
|
||||
data: {
|
||||
[parentFolderFieldName]: null,
|
||||
},
|
||||
depth: 0,
|
||||
overrideAccess: false,
|
||||
req,
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
where: {
|
||||
[parentFolderFieldName]: {
|
||||
equals: id,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
import type { CollectionAfterChangeHook, JsonObject, Payload, TypeWithID } from '../../index.js'
|
||||
|
||||
import { extractID } from '../../utilities/extractID.js'
|
||||
|
||||
type Args = {
|
||||
folderID: number | string
|
||||
parentFolderFieldName: string
|
||||
parentIDToFind: number | string
|
||||
payload: Payload
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a child folder belongs to a parent folder by
|
||||
* recursively checking upwards through the folder hierarchy.
|
||||
*/
|
||||
async function isChildOfFolder({
|
||||
folderID,
|
||||
parentFolderFieldName,
|
||||
parentIDToFind,
|
||||
payload,
|
||||
}: Args): Promise<boolean> {
|
||||
const parentFolder = (await payload.findByID({
|
||||
id: folderID,
|
||||
collection: payload.config.folders.slug,
|
||||
overrideAccess: false,
|
||||
select: {
|
||||
parentFolderFieldName: true,
|
||||
},
|
||||
})) as JsonObject & TypeWithID
|
||||
|
||||
const parentFolderID = parentFolder[parentFolderFieldName]
|
||||
? extractID(parentFolder[parentFolderFieldName])
|
||||
: undefined
|
||||
|
||||
if (!parentFolderID) {
|
||||
// made it to the root
|
||||
return false
|
||||
}
|
||||
|
||||
if (parentFolderID === parentIDToFind) {
|
||||
// found match, would be cyclic
|
||||
return true
|
||||
}
|
||||
|
||||
return isChildOfFolder({
|
||||
folderID: parentFolderID,
|
||||
parentFolderFieldName,
|
||||
parentIDToFind,
|
||||
payload,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* If a parent is moved into a child folder, we need to re-parent the child
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
→ F1
|
||||
→ F2
|
||||
→ F2A
|
||||
→ F3
|
||||
|
||||
Moving F1 → F2A becomes:
|
||||
|
||||
→ F2A
|
||||
→ F1
|
||||
→ F2
|
||||
→ F3
|
||||
```
|
||||
*/
|
||||
export const reparentChildFolder = ({
|
||||
parentFolderFieldName,
|
||||
}: {
|
||||
parentFolderFieldName: string
|
||||
}): CollectionAfterChangeHook => {
|
||||
return async ({ doc, previousDoc, req }) => {
|
||||
if (
|
||||
previousDoc[parentFolderFieldName] !== doc[parentFolderFieldName] &&
|
||||
doc[parentFolderFieldName]
|
||||
) {
|
||||
const newParentFolderID = extractID(doc[parentFolderFieldName])
|
||||
const isMovingToChild = newParentFolderID
|
||||
? await isChildOfFolder({
|
||||
folderID: newParentFolderID,
|
||||
parentFolderFieldName,
|
||||
parentIDToFind: doc.id,
|
||||
payload: req.payload,
|
||||
})
|
||||
: false
|
||||
|
||||
if (isMovingToChild) {
|
||||
// if the folder was moved into a child folder, the child folder needs
|
||||
// to be re-parented with the parent of the folder that was moved
|
||||
await req.payload.update({
|
||||
id: newParentFolderID,
|
||||
collection: req.payload.config.folders.slug,
|
||||
data: {
|
||||
[parentFolderFieldName]: previousDoc[parentFolderFieldName]
|
||||
? extractID(previousDoc[parentFolderFieldName])
|
||||
: null,
|
||||
},
|
||||
depth: 0,
|
||||
overrideAccess: false,
|
||||
req,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import type { TypeWithID } from '../collections/config/types.js'
|
||||
import type { CollectionSlug, SanitizedCollectionConfig } from '../index.js'
|
||||
import type { Document } from '../types/index.js'
|
||||
|
||||
export type FolderInterface = {
|
||||
_folder?: FolderInterface | (number | string | undefined)
|
||||
documentsAndFolders?: {
|
||||
docs: {
|
||||
relationTo: CollectionSlug
|
||||
value: Document
|
||||
}[]
|
||||
}
|
||||
name: string
|
||||
} & TypeWithID
|
||||
|
||||
export type FolderBreadcrumb = {
|
||||
id: null | number | string
|
||||
name: string
|
||||
}
|
||||
|
||||
export type Subfolder = {
|
||||
fileCount: number
|
||||
hasSubfolders: boolean
|
||||
id: number | string
|
||||
name: string
|
||||
subfolderCount: number
|
||||
}
|
||||
|
||||
export type FolderEnabledColection = {
|
||||
admin: {
|
||||
custom: {
|
||||
folderCollectionSlug: CollectionSlug
|
||||
}
|
||||
}
|
||||
slug: CollectionSlug
|
||||
} & SanitizedCollectionConfig
|
||||
|
||||
/**
|
||||
* `${relationTo}-${id}` is used as a key for the item
|
||||
*/
|
||||
export type FolderDocumentItemKey = `${string}-${number | string}`
|
||||
|
||||
/**
|
||||
* Needed for document card view for upload enabled collections
|
||||
*/
|
||||
type DocumentMediaData = {
|
||||
filename?: string
|
||||
mimeType?: string
|
||||
url?: string
|
||||
}
|
||||
/**
|
||||
* A generic structure for a folder or document item.
|
||||
*/
|
||||
export type FolderOrDocument = {
|
||||
itemKey: FolderDocumentItemKey
|
||||
relationTo: CollectionSlug
|
||||
value: {
|
||||
_folder?: number | string
|
||||
_folderOrDocumentTitle: string
|
||||
createdAt?: string
|
||||
id: number | string
|
||||
updatedAt?: string
|
||||
} & DocumentMediaData
|
||||
}
|
||||
|
||||
export type GetFolderDataResult = {
|
||||
breadcrumbs: FolderBreadcrumb[] | null
|
||||
documents: FolderOrDocument[]
|
||||
subfolders: FolderOrDocument[]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { CollectionSlug, Document } from '../../index.js'
|
||||
import type { FolderOrDocument } from '../types.js'
|
||||
|
||||
type Args = {
|
||||
isUpload: boolean
|
||||
relationTo: CollectionSlug
|
||||
useAsTitle?: string
|
||||
value: Document
|
||||
}
|
||||
export function formatFolderOrDocumentItem({
|
||||
isUpload,
|
||||
relationTo,
|
||||
useAsTitle,
|
||||
value,
|
||||
}: Args): FolderOrDocument {
|
||||
const itemValue: FolderOrDocument['value'] = {
|
||||
id: value?.id,
|
||||
_folder: value?._folder,
|
||||
_folderOrDocumentTitle: value[useAsTitle || 'id'],
|
||||
createdAt: value?.createdAt,
|
||||
updatedAt: value?.updatedAt,
|
||||
}
|
||||
|
||||
if (isUpload) {
|
||||
itemValue.filename = value.filename
|
||||
itemValue.mimeType = value.mimeType
|
||||
itemValue.url = value.url
|
||||
}
|
||||
|
||||
return {
|
||||
itemKey: `${relationTo}-${value.id}`,
|
||||
relationTo,
|
||||
value: itemValue,
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import type { PaginatedDocs } from '../../database/types.js'
|
||||
import type { User } from '../../index.js'
|
||||
import type { Payload } from '../../types/index.js'
|
||||
import type { FolderBreadcrumb, FolderInterface } from '../types.js'
|
||||
|
||||
import { parentFolderFieldName } from '../constants.js'
|
||||
type GetFolderBreadcrumbsArgs = {
|
||||
breadcrumbs?: FolderBreadcrumb[]
|
||||
folderID?: number | string
|
||||
payload: Payload
|
||||
user?: User
|
||||
}
|
||||
/**
|
||||
* Builds breadcrumbs up from child folder
|
||||
* all the way up to root folder
|
||||
*/
|
||||
export const getFolderBreadcrumbs = async ({
|
||||
breadcrumbs = [],
|
||||
folderID,
|
||||
payload,
|
||||
user,
|
||||
}: GetFolderBreadcrumbsArgs): Promise<FolderBreadcrumb[] | null> => {
|
||||
if (folderID) {
|
||||
const folderQuery = (await payload.find({
|
||||
collection: payload.config.folders.slug,
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
overrideAccess: false,
|
||||
select: {
|
||||
name: true,
|
||||
[parentFolderFieldName]: true,
|
||||
},
|
||||
user,
|
||||
where: {
|
||||
id: {
|
||||
equals: folderID,
|
||||
},
|
||||
},
|
||||
})) as PaginatedDocs<FolderInterface>
|
||||
|
||||
const folder = folderQuery.docs[0]
|
||||
|
||||
if (folder) {
|
||||
breadcrumbs.push({
|
||||
id: folder.id,
|
||||
name: folder.name,
|
||||
})
|
||||
if (folder[parentFolderFieldName]) {
|
||||
return getFolderBreadcrumbs({
|
||||
breadcrumbs,
|
||||
folderID:
|
||||
typeof folder[parentFolderFieldName] === 'number' ||
|
||||
typeof folder[parentFolderFieldName] === 'string'
|
||||
? folder[parentFolderFieldName]
|
||||
: folder[parentFolderFieldName].id,
|
||||
payload,
|
||||
user,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return breadcrumbs.reverse()
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import type { CollectionSlug, User } from '../../index.js'
|
||||
import type { Payload } from '../../types/index.js'
|
||||
import type { GetFolderDataResult } from '../types.js'
|
||||
|
||||
import { parseDocumentID } from '../../index.js'
|
||||
import { getFolderBreadcrumbs } from './getFolderBreadcrumbs.js'
|
||||
import { queryDocumentsAndFoldersFromJoin } from './getFoldersAndDocumentsFromJoin.js'
|
||||
import { getOrphanedDocs } from './getOrphanedDocs.js'
|
||||
|
||||
type Args = {
|
||||
/**
|
||||
* Specify to query documents from a specific collection
|
||||
* @default undefined
|
||||
* @example 'posts'
|
||||
*/
|
||||
collectionSlug?: CollectionSlug
|
||||
/**
|
||||
* The ID of the folder to query documents from
|
||||
* @default undefined
|
||||
*/
|
||||
folderID?: number | string
|
||||
/**
|
||||
* The locale to use for the document query
|
||||
* @default undefined
|
||||
*/
|
||||
payload: Payload
|
||||
/**
|
||||
* Search term to filter documents by - only applicable IF `collectionSlug` exists and NO `folderID` is provided
|
||||
*/
|
||||
search?: string
|
||||
/**
|
||||
* The user making the request
|
||||
* @default undefined
|
||||
*/
|
||||
user?: User
|
||||
}
|
||||
/**
|
||||
* Query for documents, subfolders and breadcrumbs for a given folder
|
||||
*/
|
||||
export const getFolderData = async ({
|
||||
collectionSlug,
|
||||
folderID: _folderID,
|
||||
payload,
|
||||
search,
|
||||
user,
|
||||
}: Args): Promise<GetFolderDataResult> => {
|
||||
const parentFolderID = parseDocumentID({
|
||||
id: _folderID,
|
||||
collectionSlug: payload.config.folders.slug,
|
||||
payload,
|
||||
})
|
||||
|
||||
const breadcrumbsPromise = getFolderBreadcrumbs({
|
||||
folderID: parentFolderID,
|
||||
payload,
|
||||
user,
|
||||
})
|
||||
|
||||
if (parentFolderID) {
|
||||
// subfolders and documents are queried together
|
||||
const documentAndSubfolderPromise = queryDocumentsAndFoldersFromJoin({
|
||||
collectionSlug,
|
||||
parentFolderID,
|
||||
payload,
|
||||
user,
|
||||
})
|
||||
const [breadcrumbs, documentsAndSubfolders] = await Promise.all([
|
||||
breadcrumbsPromise,
|
||||
documentAndSubfolderPromise,
|
||||
])
|
||||
|
||||
return {
|
||||
breadcrumbs,
|
||||
documents: documentsAndSubfolders.documents,
|
||||
subfolders: documentsAndSubfolders.subfolders,
|
||||
}
|
||||
} else {
|
||||
// subfolders and documents are queried separately
|
||||
const subfoldersPromise = getOrphanedDocs({
|
||||
collectionSlug: payload.config.folders.slug,
|
||||
payload,
|
||||
search,
|
||||
user,
|
||||
})
|
||||
const documentsPromise = collectionSlug
|
||||
? getOrphanedDocs({
|
||||
collectionSlug,
|
||||
payload,
|
||||
search,
|
||||
user,
|
||||
})
|
||||
: Promise.resolve([])
|
||||
const [breadcrumbs, subfolders, documents] = await Promise.all([
|
||||
breadcrumbsPromise,
|
||||
subfoldersPromise,
|
||||
documentsPromise,
|
||||
])
|
||||
return {
|
||||
breadcrumbs,
|
||||
documents,
|
||||
subfolders,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import type { User } from '../../auth/types.js'
|
||||
import type { PaginatedDocs } from '../../database/types.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { Document, Payload } from '../../types/index.js'
|
||||
import type { FolderOrDocument } from '../types.js'
|
||||
|
||||
import { formatFolderOrDocumentItem } from './formatFolderOrDocumentItem.js'
|
||||
|
||||
type QueryDocumentsAndFoldersResults = {
|
||||
documents: FolderOrDocument[]
|
||||
subfolders: FolderOrDocument[]
|
||||
}
|
||||
type QueryDocumentsAndFoldersArgs = {
|
||||
collectionSlug?: CollectionSlug
|
||||
parentFolderID: number | string
|
||||
payload: Payload
|
||||
user?: User
|
||||
}
|
||||
export async function queryDocumentsAndFoldersFromJoin({
|
||||
collectionSlug,
|
||||
parentFolderID,
|
||||
payload,
|
||||
user,
|
||||
}: QueryDocumentsAndFoldersArgs): Promise<QueryDocumentsAndFoldersResults> {
|
||||
const subfolderDoc = (await payload.find({
|
||||
collection: payload.config.folders.slug,
|
||||
joins: {
|
||||
documentsAndFolders: {
|
||||
limit: 100_000,
|
||||
sort: 'name',
|
||||
where: {
|
||||
relationTo: {
|
||||
in: [
|
||||
payload.config.folders.slug,
|
||||
...(collectionSlug
|
||||
? [collectionSlug]
|
||||
: Object.keys(payload.config.folders.collections)),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
limit: 1,
|
||||
// overrideAccess: false, // @todo: bug in core, throws "QueryError: The following paths cannot be queried: relationTo"
|
||||
user,
|
||||
where: {
|
||||
id: {
|
||||
equals: parentFolderID,
|
||||
},
|
||||
},
|
||||
})) as PaginatedDocs<Document>
|
||||
|
||||
const childrenDocs = subfolderDoc?.docs[0]?.documentsAndFolders?.docs || []
|
||||
|
||||
const results: QueryDocumentsAndFoldersResults = childrenDocs.reduce(
|
||||
(acc: QueryDocumentsAndFoldersResults, doc: Document) => {
|
||||
const { relationTo, value } = doc
|
||||
const item = formatFolderOrDocumentItem({
|
||||
isUpload: Boolean(payload.collections[relationTo].config.upload),
|
||||
relationTo,
|
||||
useAsTitle: payload.collections[relationTo].config.admin?.useAsTitle,
|
||||
value,
|
||||
})
|
||||
|
||||
if (relationTo === payload.config.folders.slug) {
|
||||
acc.subfolders.push(item)
|
||||
} else {
|
||||
acc.documents.push(item)
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
{
|
||||
documents: [],
|
||||
subfolders: [],
|
||||
},
|
||||
)
|
||||
|
||||
return results
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import type { CollectionSlug, Payload, User, Where } from '../../index.js'
|
||||
import type { FolderOrDocument } from '../types.js'
|
||||
|
||||
import { formatFolderOrDocumentItem } from './formatFolderOrDocumentItem.js'
|
||||
|
||||
type Args = {
|
||||
collectionSlug: CollectionSlug
|
||||
payload: Payload
|
||||
search?: string
|
||||
user?: User
|
||||
}
|
||||
export async function getOrphanedDocs({
|
||||
collectionSlug,
|
||||
payload,
|
||||
search,
|
||||
user,
|
||||
}: Args): Promise<FolderOrDocument[]> {
|
||||
let whereConstraints: Where = {
|
||||
or: [
|
||||
{
|
||||
_folder: {
|
||||
exists: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
_folder: {
|
||||
equals: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
if (collectionSlug && search && payload.collections[collectionSlug].config.admin?.useAsTitle) {
|
||||
whereConstraints = {
|
||||
[payload.collections[collectionSlug].config.admin.useAsTitle]: {
|
||||
like: search,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const orphanedFolders = await payload.find({
|
||||
collection: collectionSlug,
|
||||
limit: 0,
|
||||
// overrideAccess: false, // @todo: bug in core, throws "QueryError: The following paths cannot be queried: _folder"
|
||||
sort: payload.collections[collectionSlug].config.admin.useAsTitle,
|
||||
user,
|
||||
where: whereConstraints,
|
||||
})
|
||||
|
||||
return (
|
||||
orphanedFolders?.docs.map((doc) =>
|
||||
formatFolderOrDocumentItem({
|
||||
isUpload: Boolean(payload.collections[collectionSlug].config.upload),
|
||||
relationTo: collectionSlug,
|
||||
useAsTitle: payload.collections[collectionSlug].config.admin.useAsTitle,
|
||||
value: doc,
|
||||
}),
|
||||
) || []
|
||||
)
|
||||
}
|
||||
@@ -1371,7 +1371,6 @@ export type {
|
||||
UploadFieldValidation,
|
||||
UsernameFieldValidation,
|
||||
} from './fields/validations.js'
|
||||
export { getFolderData } from './folders/utils/getFolderData.js'
|
||||
export {
|
||||
type ClientGlobalConfig,
|
||||
createClientGlobalConfig,
|
||||
@@ -1379,6 +1378,7 @@ export {
|
||||
type ServerOnlyGlobalAdminProperties,
|
||||
type ServerOnlyGlobalProperties,
|
||||
} from './globals/config/client.js'
|
||||
|
||||
export type {
|
||||
AfterChangeHook as GlobalAfterChangeHook,
|
||||
AfterReadHook as GlobalAfterReadHook,
|
||||
@@ -1390,6 +1390,7 @@ export type {
|
||||
GlobalConfig,
|
||||
SanitizedGlobalConfig,
|
||||
} from './globals/config/types.js'
|
||||
|
||||
export { docAccessOperation as docAccessOperationGlobal } from './globals/operations/docAccess.js'
|
||||
|
||||
export { findOneOperation } from './globals/operations/findOne.js'
|
||||
@@ -1436,11 +1437,11 @@ export type {
|
||||
WorkflowTypes,
|
||||
} from './queues/config/types/workflowTypes.js'
|
||||
export { importHandlerPath } from './queues/operations/runJobs/runJob/importHandlerPath.js'
|
||||
|
||||
export { getLocalI18n } from './translations/getLocalI18n.js'
|
||||
export * from './types/index.js'
|
||||
export { getFileByPath } from './uploads/getFileByPath.js'
|
||||
export type * from './uploads/types.js'
|
||||
|
||||
export { addDataAndFileToRequest } from './utilities/addDataAndFileToRequest.js'
|
||||
|
||||
export { addLocalesToRequestFromData, sanitizeLocales } from './utilities/addLocalesToRequest.js'
|
||||
@@ -1498,7 +1499,6 @@ export { logError } from './utilities/logError.js'
|
||||
export { defaultLoggerOptions } from './utilities/logger.js'
|
||||
export { mapAsync } from './utilities/mapAsync.js'
|
||||
export { mergeHeaders } from './utilities/mergeHeaders.js'
|
||||
export { parseDocumentID } from './utilities/parseDocumentID.js'
|
||||
export { sanitizeFallbackLocale } from './utilities/sanitizeFallbackLocale.js'
|
||||
export { sanitizeJoinParams } from './utilities/sanitizeJoinParams.js'
|
||||
export { sanitizePopulateParam } from './utilities/sanitizePopulateParam.js'
|
||||
@@ -1515,7 +1515,6 @@ export { appendVersionToQueryKey } from './versions/drafts/appendVersionToQueryK
|
||||
export { getQueryDraftsSort } from './versions/drafts/getQueryDraftsSort.js'
|
||||
export { enforceMaxVersions } from './versions/enforceMaxVersions.js'
|
||||
export { getLatestCollectionVersion } from './versions/getLatestCollectionVersion.js'
|
||||
|
||||
export { getLatestGlobalVersion } from './versions/getLatestGlobalVersion.js'
|
||||
export { saveVersion } from './versions/saveVersion.js'
|
||||
export type { SchedulePublishTaskInput } from './versions/schedule/types.js'
|
||||
|
||||
@@ -125,7 +125,9 @@ export const getQueryPresetsConfig = (config: Config): CollectionConfig => ({
|
||||
},
|
||||
],
|
||||
hooks: {
|
||||
...(config.queryPresets?.hooks || {}),
|
||||
beforeValidate: [
|
||||
...(config.queryPresets?.hooks?.beforeValidate || []),
|
||||
({ data, operation, req }) => {
|
||||
// TODO: type this
|
||||
const typedData = data as any
|
||||
|
||||
@@ -43,6 +43,33 @@ export async function cropImage({
|
||||
sharpOptions.animated = true
|
||||
}
|
||||
|
||||
const { height: originalHeight, width: originalWidth } = dimensions
|
||||
const newWidth = Number(widthInPixels)
|
||||
const newHeight = Number(heightInPixels)
|
||||
|
||||
const dimensionsChanged = originalWidth !== newWidth || originalHeight !== newHeight
|
||||
|
||||
if (!dimensionsChanged) {
|
||||
let adjustedHeight = originalHeight
|
||||
|
||||
if (fileIsAnimatedType) {
|
||||
const animatedMetadata = await sharp(
|
||||
file.tempFilePath || file.data,
|
||||
sharpOptions,
|
||||
).metadata()
|
||||
adjustedHeight = animatedMetadata.pages ? animatedMetadata.height : originalHeight
|
||||
}
|
||||
|
||||
return {
|
||||
data: file.data,
|
||||
info: {
|
||||
height: adjustedHeight,
|
||||
size: file.size,
|
||||
width: originalWidth,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const formattedCropData = {
|
||||
height: Number(heightInPixels),
|
||||
left: percentToPixel(x, dimensions.width),
|
||||
|
||||
@@ -241,7 +241,7 @@ export const generateFileData = async <T>({
|
||||
})
|
||||
|
||||
// Apply resize after cropping to ensure it conforms to resizeOptions
|
||||
if (resizeOptions) {
|
||||
if (resizeOptions && !resizeOptions.withoutEnlargement) {
|
||||
const resizedAfterCrop = await sharp(croppedImage)
|
||||
.resize({
|
||||
fit: resizeOptions?.fit || 'cover',
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import type { Where } from '../types/index.js'
|
||||
|
||||
export function combineWhereConstraints(
|
||||
constraints: Array<undefined | Where>,
|
||||
as: 'and' | 'or' = 'and',
|
||||
): Where {
|
||||
if (constraints.length === 0) {
|
||||
return {}
|
||||
}
|
||||
|
||||
return {
|
||||
[as]: constraints.filter((constraint): constraint is Where => {
|
||||
if (constraint && typeof constraint === 'object' && Object.keys(constraint).length > 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
export const extractID = <IDType extends number | string>(
|
||||
objectOrID: { id: IDType } | IDType,
|
||||
): IDType => {
|
||||
if (typeof objectOrID === 'string' || typeof objectOrID === 'number') {
|
||||
return objectOrID
|
||||
}
|
||||
|
||||
return objectOrID.id
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { CollectionSlug, Payload } from '../index.js'
|
||||
|
||||
import { isNumber } from './isNumber.js'
|
||||
|
||||
type ParseDocumentIDArgs = {
|
||||
collectionSlug: CollectionSlug
|
||||
id?: number | string
|
||||
payload: Payload
|
||||
}
|
||||
|
||||
export function parseDocumentID({ id, collectionSlug, payload }: ParseDocumentIDArgs) {
|
||||
const idType = payload.collections[collectionSlug]?.customIDType ?? payload.db.defaultIDType
|
||||
|
||||
return id ? (idType === 'number' && isNumber(id) ? parseFloat(String(id)) : id) : undefined
|
||||
}
|
||||
@@ -42,6 +42,16 @@
|
||||
"import": "./src/exports/rsc.ts",
|
||||
"types": "./src/exports/rsc.ts",
|
||||
"default": "./src/exports/rsc.ts"
|
||||
},
|
||||
"./translations/languages/all": {
|
||||
"import": "./src/translations/index.ts",
|
||||
"types": "./src/translations/index.ts",
|
||||
"default": "./src/translations/index.ts"
|
||||
},
|
||||
"./translations/languages/*": {
|
||||
"import": "./src/translations/languages/*.ts",
|
||||
"types": "./src/translations/languages/*.ts",
|
||||
"default": "./src/translations/languages/*.ts"
|
||||
}
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
@@ -92,6 +102,16 @@
|
||||
"import": "./dist/exports/rsc.js",
|
||||
"types": "./dist/exports/rsc.d.ts",
|
||||
"default": "./dist/exports/rsc.js"
|
||||
},
|
||||
"./translations/languages/all": {
|
||||
"import": "./dist/translations/index.js",
|
||||
"types": "./dist/translations/index.d.ts",
|
||||
"default": "./dist/translations/index.js"
|
||||
},
|
||||
"./translations/languages/*": {
|
||||
"import": "./dist/translations/languages/*.js",
|
||||
"types": "./dist/translations/languages/*.d.ts",
|
||||
"default": "./dist/translations/languages/*.js"
|
||||
}
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
'use client'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { PopupList, useConfig, useDocumentDrawer, useTranslation } from '@payloadcms/ui'
|
||||
import {
|
||||
PopupList,
|
||||
Translation,
|
||||
useConfig,
|
||||
useDocumentDrawer,
|
||||
useTranslation,
|
||||
} from '@payloadcms/ui'
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import type {
|
||||
PluginImportExportTranslationKeys,
|
||||
PluginImportExportTranslations,
|
||||
} from '../../translations/index.js'
|
||||
|
||||
import { useImportExport } from '../ImportExportProvider/index.js'
|
||||
import './index.scss'
|
||||
|
||||
@@ -14,7 +25,10 @@ export const ExportListMenuItem: React.FC<{
|
||||
exportCollectionSlug: string
|
||||
}> = ({ collectionSlug, exportCollectionSlug }) => {
|
||||
const { getEntityConfig } = useConfig()
|
||||
const { i18n } = useTranslation()
|
||||
const { i18n, t } = useTranslation<
|
||||
PluginImportExportTranslations,
|
||||
PluginImportExportTranslationKeys
|
||||
>()
|
||||
const currentCollectionConfig = getEntityConfig({ collectionSlug })
|
||||
|
||||
const [DocumentDrawer, DocumentDrawerToggler] = useDocumentDrawer({
|
||||
@@ -30,7 +44,15 @@ export const ExportListMenuItem: React.FC<{
|
||||
return (
|
||||
<PopupList.Button className={baseClass}>
|
||||
<DocumentDrawerToggler>
|
||||
Export {getTranslation(currentCollectionConfig.labels.plural, i18n)}
|
||||
<Translation
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
i18nKey="plugin-import-export:exportDocumentLabel"
|
||||
t={t}
|
||||
variables={{
|
||||
label: getTranslation(currentCollectionConfig.labels.plural, i18n),
|
||||
}}
|
||||
/>
|
||||
</DocumentDrawerToggler>
|
||||
<DocumentDrawer />
|
||||
</PopupList.Button>
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
'use client'
|
||||
|
||||
import { Button, SaveButton, useConfig, useForm, useTranslation } from '@payloadcms/ui'
|
||||
import { Button, SaveButton, Translation, useConfig, useForm, useTranslation } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
import type {
|
||||
PluginImportExportTranslationKeys,
|
||||
PluginImportExportTranslations,
|
||||
} from '../../translations/index.js'
|
||||
|
||||
export const ExportSaveButton: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { t } = useTranslation<PluginImportExportTranslations, PluginImportExportTranslationKeys>()
|
||||
const {
|
||||
config: {
|
||||
routes: { api },
|
||||
@@ -65,7 +70,7 @@ export const ExportSaveButton: React.FC = () => {
|
||||
<React.Fragment>
|
||||
<SaveButton label={label}></SaveButton>
|
||||
<Button onClick={handleDownload} size="medium" type="button">
|
||||
Download
|
||||
<Translation i18nKey="upload:download" t={t} />
|
||||
</Button>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
@@ -83,11 +83,12 @@ export const FieldsToExport: SelectFieldClientComponent = (props) => {
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<FieldLabel label="Columns to Export" />
|
||||
<FieldLabel label={props.field.label} path={props.path} />
|
||||
<ReactSelect
|
||||
className={baseClass}
|
||||
disabled={props.readOnly}
|
||||
getOptionValue={(option) => String(option.value)}
|
||||
inputId={`field-${props.path.replace(/\./g, '__')}`}
|
||||
isClearable={true}
|
||||
isMulti={true}
|
||||
isSortable={true}
|
||||
|
||||
@@ -3,13 +3,18 @@ import type { Column } from '@payloadcms/ui'
|
||||
import type { ClientField, FieldAffectingDataClient } from 'payload'
|
||||
|
||||
import { getTranslation } from '@payloadcms/translations'
|
||||
import { Table, useConfig, useField, useTranslation } from '@payloadcms/ui'
|
||||
import { Table, Translation, useConfig, useField, useTranslation } from '@payloadcms/ui'
|
||||
import { fieldAffectsData } from 'payload/shared'
|
||||
import * as qs from 'qs-esm'
|
||||
import React from 'react'
|
||||
|
||||
import { useImportExport } from '../ImportExportProvider/index.js'
|
||||
import type {
|
||||
PluginImportExportTranslationKeys,
|
||||
PluginImportExportTranslations,
|
||||
} from '../../translations/index.js'
|
||||
|
||||
import './index.scss'
|
||||
import { useImportExport } from '../ImportExportProvider/index.js'
|
||||
|
||||
const baseClass = 'preview'
|
||||
|
||||
@@ -24,7 +29,10 @@ export const Preview = () => {
|
||||
const [dataToRender, setDataToRender] = React.useState<any[]>([])
|
||||
const [resultCount, setResultCount] = React.useState<any>('')
|
||||
const [columns, setColumns] = React.useState<Column[]>([])
|
||||
const { i18n } = useTranslation()
|
||||
const { i18n, t } = useTranslation<
|
||||
PluginImportExportTranslations,
|
||||
PluginImportExportTranslationKeys
|
||||
>()
|
||||
|
||||
const collectionSlug = typeof collection === 'string' && collection
|
||||
const collectionConfig = config.collections.find(
|
||||
@@ -102,8 +110,20 @@ export const Preview = () => {
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__header`}>
|
||||
<h3>Preview</h3>
|
||||
{resultCount && <span>{resultCount} total documents</span>}
|
||||
<h3>
|
||||
<Translation i18nKey="version:preview" t={t} />
|
||||
</h3>
|
||||
{resultCount && (
|
||||
<Translation
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
i18nKey="plugin-import-export:totalDocumentsCount"
|
||||
t={t}
|
||||
variables={{
|
||||
count: resultCount,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{dataToRender && <Table columns={columns} data={dataToRender} />}
|
||||
</div>
|
||||
|
||||
@@ -72,11 +72,12 @@ export const SortBy: SelectFieldClientComponent = (props) => {
|
||||
|
||||
return (
|
||||
<div className={baseClass} style={{ '--field-width': '33%' } as React.CSSProperties}>
|
||||
<FieldLabel label="Sort By" />
|
||||
<FieldLabel label={props.field.label} path={props.path} />
|
||||
<ReactSelect
|
||||
className={baseClass}
|
||||
disabled={props.readOnly}
|
||||
getOptionValue={(option) => String(option.value)}
|
||||
inputId={`field-${props.path.replace(/\./g, '__')}`}
|
||||
isClearable={true}
|
||||
isSortable={true}
|
||||
// @ts-expect-error react select option
|
||||
|
||||
@@ -12,10 +12,11 @@ export const getFields = (config: Config): Field[] => {
|
||||
width: '33%',
|
||||
},
|
||||
defaultValue: 'all',
|
||||
label: 'Locale',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-locale-label'),
|
||||
options: [
|
||||
{
|
||||
label: 'All Locales',
|
||||
label: ({ t }) => t('general:allLocales'),
|
||||
value: 'all',
|
||||
},
|
||||
...config.localization.locales.map((locale) => ({
|
||||
@@ -34,7 +35,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
defaultValue: () => getFilename(),
|
||||
label: 'File Name',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-name-label'),
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
@@ -46,7 +48,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
width: '33%',
|
||||
},
|
||||
defaultValue: 'csv',
|
||||
label: 'Export Format',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-format-label'),
|
||||
options: [
|
||||
{
|
||||
label: 'CSV',
|
||||
@@ -66,6 +69,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
placeholder: 'No limit',
|
||||
width: '33%',
|
||||
},
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-limit-label'),
|
||||
},
|
||||
{
|
||||
name: 'sort',
|
||||
@@ -75,6 +80,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
Field: '@payloadcms/plugin-import-export/rsc#SortBy',
|
||||
},
|
||||
},
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-sort-label'),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -98,14 +105,15 @@ export const getFields = (config: Config): Field[] => {
|
||||
width: '33%',
|
||||
},
|
||||
defaultValue: 'yes',
|
||||
label: 'Drafts',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-drafts-label'),
|
||||
options: [
|
||||
{
|
||||
label: 'Yes',
|
||||
label: ({ t }) => t('general:yes'),
|
||||
value: 'yes',
|
||||
},
|
||||
{
|
||||
label: 'No',
|
||||
label: ({ t }) => t('general:no'),
|
||||
value: 'no',
|
||||
},
|
||||
],
|
||||
@@ -113,6 +121,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
// {
|
||||
// name: 'depth',
|
||||
// type: 'number',
|
||||
// // @ts-expect-error - this is not correctly typed in plugins right now
|
||||
// label: ({ t }) => t('plugin-import-export:field-depth-label'),
|
||||
// admin: {
|
||||
// width: '33%',
|
||||
// },
|
||||
@@ -126,17 +136,22 @@ export const getFields = (config: Config): Field[] => {
|
||||
name: 'selectionToUse',
|
||||
type: 'radio',
|
||||
defaultValue: 'all',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-selectionToUse-label'),
|
||||
options: [
|
||||
{
|
||||
label: 'Use current selection',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:selectionToUse-currentSelection'),
|
||||
value: 'currentSelection',
|
||||
},
|
||||
{
|
||||
label: 'Use current filters',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:selectionToUse-currentFilters'),
|
||||
value: 'currentFilters',
|
||||
},
|
||||
{
|
||||
label: 'Use all documents',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:selectionToUse-allDocuments'),
|
||||
value: 'all',
|
||||
},
|
||||
],
|
||||
@@ -151,6 +166,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
},
|
||||
},
|
||||
hasMany: true,
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:field-fields-label'),
|
||||
},
|
||||
{
|
||||
name: 'collectionSlug',
|
||||
@@ -174,7 +191,8 @@ export const getFields = (config: Config): Field[] => {
|
||||
defaultValue: {},
|
||||
},
|
||||
],
|
||||
label: 'Export Options',
|
||||
// @ts-expect-error - this is not correctly typed in plugins right now
|
||||
label: ({ t }) => t('plugin-import-export:exportOptions'),
|
||||
},
|
||||
{
|
||||
name: 'preview',
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Config, JobsConfig } from 'payload'
|
||||
|
||||
import { deepMergeSimple } from 'payload'
|
||||
|
||||
import type { PluginDefaultTranslationsObject } from './translations/types.js'
|
||||
import type { ImportExportPluginConfig } from './types.js'
|
||||
|
||||
import { getCreateCollectionExportTask } from './export/getCreateExportCollectionTask.js'
|
||||
@@ -70,7 +71,23 @@ export const importExportPlugin =
|
||||
config.i18n = {}
|
||||
}
|
||||
|
||||
config.i18n.translations = deepMergeSimple(translations, config.i18n?.translations ?? {})
|
||||
// config.i18n.translations = deepMergeSimple(translations, config.i18n?.translations ?? {})
|
||||
|
||||
/**
|
||||
* Merge plugin translations
|
||||
*/
|
||||
const simplifiedTranslations = Object.entries(translations).reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = value.translations
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, PluginDefaultTranslationsObject>,
|
||||
)
|
||||
|
||||
config.i18n = {
|
||||
...config.i18n,
|
||||
translations: deepMergeSimple(simplifiedTranslations, config.i18n?.translations ?? {}),
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import type { GenericTranslationsObject } from '@payloadcms/translations'
|
||||
|
||||
export const en: GenericTranslationsObject = {
|
||||
$schema: './translation-schema.json',
|
||||
'plugin-seo': {
|
||||
export: 'Export',
|
||||
import: 'Import',
|
||||
},
|
||||
}
|
||||
@@ -1,10 +1,90 @@
|
||||
import type { GenericTranslationsObject, NestedKeysStripped } from '@payloadcms/translations'
|
||||
import type {
|
||||
GenericTranslationsObject,
|
||||
NestedKeysStripped,
|
||||
SupportedLanguages,
|
||||
} from '@payloadcms/translations'
|
||||
|
||||
import { en } from './en.js'
|
||||
import type { PluginDefaultTranslationsObject } from './types.js'
|
||||
|
||||
import { ar } from './languages/ar.js'
|
||||
import { az } from './languages/az.js'
|
||||
import { bg } from './languages/bg.js'
|
||||
import { ca } from './languages/ca.js'
|
||||
import { cs } from './languages/cs.js'
|
||||
import { da } from './languages/da.js'
|
||||
import { de } from './languages/de.js'
|
||||
import { en } from './languages/en.js'
|
||||
import { es } from './languages/es.js'
|
||||
import { et } from './languages/et.js'
|
||||
import { fa } from './languages/fa.js'
|
||||
import { fr } from './languages/fr.js'
|
||||
import { he } from './languages/he.js'
|
||||
import { hr } from './languages/hr.js'
|
||||
import { hu } from './languages/hu.js'
|
||||
import { hy } from './languages/hy.js'
|
||||
import { it } from './languages/it.js'
|
||||
import { ja } from './languages/ja.js'
|
||||
import { ko } from './languages/ko.js'
|
||||
import { lt } from './languages/lt.js'
|
||||
import { my } from './languages/my.js'
|
||||
import { nb } from './languages/nb.js'
|
||||
import { nl } from './languages/nl.js'
|
||||
import { pl } from './languages/pl.js'
|
||||
import { pt } from './languages/pt.js'
|
||||
import { ro } from './languages/ro.js'
|
||||
import { rs } from './languages/rs.js'
|
||||
import { rsLatin } from './languages/rsLatin.js'
|
||||
import { ru } from './languages/ru.js'
|
||||
import { sk } from './languages/sk.js'
|
||||
import { sl } from './languages/sl.js'
|
||||
import { sv } from './languages/sv.js'
|
||||
import { th } from './languages/th.js'
|
||||
import { tr } from './languages/tr.js'
|
||||
import { uk } from './languages/uk.js'
|
||||
import { vi } from './languages/vi.js'
|
||||
import { zh } from './languages/zh.js'
|
||||
import { zhTw } from './languages/zhTw.js'
|
||||
|
||||
export const translations = {
|
||||
ar,
|
||||
az,
|
||||
bg,
|
||||
ca,
|
||||
cs,
|
||||
da,
|
||||
de,
|
||||
en,
|
||||
}
|
||||
es,
|
||||
et,
|
||||
fa,
|
||||
fr,
|
||||
he,
|
||||
hr,
|
||||
hu,
|
||||
hy,
|
||||
it,
|
||||
ja,
|
||||
ko,
|
||||
lt,
|
||||
my,
|
||||
nb,
|
||||
nl,
|
||||
pl,
|
||||
pt,
|
||||
ro,
|
||||
rs,
|
||||
'rs-latin': rsLatin,
|
||||
ru,
|
||||
sk,
|
||||
sl,
|
||||
sv,
|
||||
th,
|
||||
tr,
|
||||
uk,
|
||||
vi,
|
||||
zh,
|
||||
'zh-TW': zhTw,
|
||||
} as SupportedLanguages<PluginDefaultTranslationsObject>
|
||||
|
||||
export type PluginImportExportTranslations = GenericTranslationsObject
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const arTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'جميع المواقع',
|
||||
exportDocumentLabel: 'تصدير {{label}}',
|
||||
exportOptions: 'خيارات التصدير',
|
||||
'field-depth-label': 'عمق',
|
||||
'field-drafts-label': 'تضمن المسودات',
|
||||
'field-fields-label': 'حقول',
|
||||
'field-format-label': 'تنسيق التصدير',
|
||||
'field-limit-label': 'حد',
|
||||
'field-locale-label': 'موقع',
|
||||
'field-name-label': 'اسم الملف',
|
||||
'field-selectionToUse-label': 'اختيار للاستخدام',
|
||||
'field-sort-label': 'ترتيب حسب',
|
||||
'selectionToUse-allDocuments': 'استخدم جميع الوثائق',
|
||||
'selectionToUse-currentFilters': 'استخدم الفلاتر الحالية',
|
||||
'selectionToUse-currentSelection': 'استخدم الاختيار الحالي',
|
||||
totalDocumentsCount: '{{count}} مستنداً إجمالياً',
|
||||
},
|
||||
}
|
||||
|
||||
export const ar: PluginLanguage = {
|
||||
dateFNSKey: 'ar',
|
||||
translations: arTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const azTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Bütün yerlər',
|
||||
exportDocumentLabel: '{{label}} ixrac edin',
|
||||
exportOptions: 'İxrac Variantları',
|
||||
'field-depth-label': 'Dərinlik',
|
||||
'field-drafts-label': 'Qaralamaları daxil etin',
|
||||
'field-fields-label': 'Sahələr',
|
||||
'field-format-label': 'İxrac Formatı',
|
||||
'field-limit-label': 'Hədd',
|
||||
'field-locale-label': 'Yerli',
|
||||
'field-name-label': 'Fayl adı',
|
||||
'field-selectionToUse-label': 'İstifadə etmək üçün seçim',
|
||||
'field-sort-label': 'Sırala',
|
||||
'selectionToUse-allDocuments': 'Bütün sənədlərdən istifadə edin',
|
||||
'selectionToUse-currentFilters': 'Cari filtrlərdən istifadə edin',
|
||||
'selectionToUse-currentSelection': 'Cari seçimi istifadə edin',
|
||||
totalDocumentsCount: '{{count}} ümumi sənəd',
|
||||
},
|
||||
}
|
||||
|
||||
export const az: PluginLanguage = {
|
||||
dateFNSKey: 'az',
|
||||
translations: azTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const bgTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Всички локации',
|
||||
exportDocumentLabel: 'Експортиране {{label}}',
|
||||
exportOptions: 'Опции за експортиране',
|
||||
'field-depth-label': 'Дълбочина',
|
||||
'field-drafts-label': 'Включете чернови',
|
||||
'field-fields-label': 'Полета',
|
||||
'field-format-label': 'Формат за експортиране',
|
||||
'field-limit-label': 'Лимит',
|
||||
'field-locale-label': 'Регион',
|
||||
'field-name-label': 'Име на файла',
|
||||
'field-selectionToUse-label': 'Избор за използване',
|
||||
'field-sort-label': 'Сортирай по',
|
||||
'selectionToUse-allDocuments': 'Използвайте всички документи',
|
||||
'selectionToUse-currentFilters': 'Използвайте текущите филтри',
|
||||
'selectionToUse-currentSelection': 'Използвайте текущия избор',
|
||||
totalDocumentsCount: '{{count}} общо документа',
|
||||
},
|
||||
}
|
||||
|
||||
export const bg: PluginLanguage = {
|
||||
dateFNSKey: 'bg',
|
||||
translations: bgTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const caTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Totes les localitzacions',
|
||||
exportDocumentLabel: 'Exporta {{label}}',
|
||||
exportOptions: "Opcions d'exportació",
|
||||
'field-depth-label': 'Profunditat',
|
||||
'field-drafts-label': 'Inclou esborranys',
|
||||
'field-fields-label': 'Camps',
|
||||
'field-format-label': "Format d'exportació",
|
||||
'field-limit-label': 'Límit',
|
||||
'field-locale-label': 'Local',
|
||||
'field-name-label': 'Nom del fitxer',
|
||||
'field-selectionToUse-label': 'Selecció per utilitzar',
|
||||
'field-sort-label': 'Ordena per',
|
||||
'selectionToUse-allDocuments': 'Utilitzeu tots els documents',
|
||||
'selectionToUse-currentFilters': 'Utilitza els filtres actuals',
|
||||
'selectionToUse-currentSelection': 'Utilitza la selecció actual',
|
||||
totalDocumentsCount: '{{count}} documents totals',
|
||||
},
|
||||
}
|
||||
|
||||
export const ca: PluginLanguage = {
|
||||
dateFNSKey: 'ca',
|
||||
translations: caTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const csTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Všechny lokalizace',
|
||||
exportDocumentLabel: 'Export {{label}}',
|
||||
exportOptions: 'Možnosti exportu',
|
||||
'field-depth-label': 'Hloubka',
|
||||
'field-drafts-label': 'Zahrnout návrhy',
|
||||
'field-fields-label': 'Pole',
|
||||
'field-format-label': 'Formát exportu',
|
||||
'field-limit-label': 'Limita',
|
||||
'field-locale-label': 'Místní',
|
||||
'field-name-label': 'Název souboru',
|
||||
'field-selectionToUse-label': 'Výběr k použití',
|
||||
'field-sort-label': 'Seřadit podle',
|
||||
'selectionToUse-allDocuments': 'Použijte všechny dokumenty',
|
||||
'selectionToUse-currentFilters': 'Použijte aktuální filtry',
|
||||
'selectionToUse-currentSelection': 'Použijte aktuální výběr',
|
||||
totalDocumentsCount: '{{count}} celkem dokumentů',
|
||||
},
|
||||
}
|
||||
|
||||
export const cs: PluginLanguage = {
|
||||
dateFNSKey: 'cs',
|
||||
translations: csTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const daTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Alle lokaliteter',
|
||||
exportDocumentLabel: 'Eksport {{label}}',
|
||||
exportOptions: 'Eksportmuligheder',
|
||||
'field-depth-label': 'Dybde',
|
||||
'field-drafts-label': 'Inkluder udkast',
|
||||
'field-fields-label': 'Felter',
|
||||
'field-format-label': 'Eksportformat',
|
||||
'field-limit-label': 'Begrænsning',
|
||||
'field-locale-label': 'Lokale',
|
||||
'field-name-label': 'Filnavn',
|
||||
'field-selectionToUse-label': 'Valg til brug',
|
||||
'field-sort-label': 'Sorter efter',
|
||||
'selectionToUse-allDocuments': 'Brug alle dokumenter',
|
||||
'selectionToUse-currentFilters': 'Brug nuværende filtre',
|
||||
'selectionToUse-currentSelection': 'Brug nuværende valg',
|
||||
totalDocumentsCount: '{{count}} samlede dokumenter',
|
||||
},
|
||||
}
|
||||
|
||||
export const da: PluginLanguage = {
|
||||
dateFNSKey: 'da',
|
||||
translations: daTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const deTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Alle Gebietsschemata',
|
||||
exportDocumentLabel: 'Export {{label}}',
|
||||
exportOptions: 'Exportoptionen',
|
||||
'field-depth-label': 'Tiefe',
|
||||
'field-drafts-label': 'Fügen Sie Entwürfe hinzu',
|
||||
'field-fields-label': 'Felder',
|
||||
'field-format-label': 'Exportformat',
|
||||
'field-limit-label': 'Grenze',
|
||||
'field-locale-label': 'Ort',
|
||||
'field-name-label': 'Dateiname',
|
||||
'field-selectionToUse-label': 'Auswahl zur Verwendung',
|
||||
'field-sort-label': 'Sortieren nach',
|
||||
'selectionToUse-allDocuments': 'Verwenden Sie alle Dokumente.',
|
||||
'selectionToUse-currentFilters': 'Verwenden Sie aktuelle Filter',
|
||||
'selectionToUse-currentSelection': 'Verwenden Sie die aktuelle Auswahl',
|
||||
totalDocumentsCount: '{{count}} gesamte Dokumente',
|
||||
},
|
||||
}
|
||||
|
||||
export const de: PluginLanguage = {
|
||||
dateFNSKey: 'de',
|
||||
translations: deTranslations,
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import type { PluginLanguage } from '../types.js'
|
||||
export const enTranslations = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'All locales',
|
||||
exportDocumentLabel: 'Export {{label}}',
|
||||
exportOptions: 'Export Options',
|
||||
'field-depth-label': 'Depth',
|
||||
'field-drafts-label': 'Include drafts',
|
||||
'field-fields-label': 'Fields',
|
||||
'field-format-label': 'Export Format',
|
||||
'field-limit-label': 'Limit',
|
||||
'field-locale-label': 'Locale',
|
||||
'field-name-label': 'File name',
|
||||
'field-selectionToUse-label': 'Selection to use',
|
||||
'field-sort-label': 'Sort by',
|
||||
'selectionToUse-allDocuments': 'Use all documents',
|
||||
'selectionToUse-currentFilters': 'Use current filters',
|
||||
'selectionToUse-currentSelection': 'Use current selection',
|
||||
totalDocumentsCount: '{{count}} total documents',
|
||||
},
|
||||
}
|
||||
|
||||
export const en: PluginLanguage = {
|
||||
dateFNSKey: 'en-US',
|
||||
translations: enTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const esTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Todas las ubicaciones',
|
||||
exportDocumentLabel: 'Exportar {{label}}',
|
||||
exportOptions: 'Opciones de Exportación',
|
||||
'field-depth-label': 'Profundidad',
|
||||
'field-drafts-label': 'Incluir borradores',
|
||||
'field-fields-label': 'Campos',
|
||||
'field-format-label': 'Formato de Exportación',
|
||||
'field-limit-label': 'Límite',
|
||||
'field-locale-label': 'Localidad',
|
||||
'field-name-label': 'Nombre del archivo',
|
||||
'field-selectionToUse-label': 'Selección para usar',
|
||||
'field-sort-label': 'Ordenar por',
|
||||
'selectionToUse-allDocuments': 'Utilice todos los documentos',
|
||||
'selectionToUse-currentFilters': 'Utilice los filtros actuales',
|
||||
'selectionToUse-currentSelection': 'Usar selección actual',
|
||||
totalDocumentsCount: '{{count}} documentos totales',
|
||||
},
|
||||
}
|
||||
|
||||
export const es: PluginLanguage = {
|
||||
dateFNSKey: 'es',
|
||||
translations: esTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const etTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Kõik kohalikud seaded',
|
||||
exportDocumentLabel: 'Ekspordi {{label}}',
|
||||
exportOptions: 'Ekspordi valikud',
|
||||
'field-depth-label': 'Sügavus',
|
||||
'field-drafts-label': 'Kaasa arvatud mustandid',
|
||||
'field-fields-label': 'Väljad',
|
||||
'field-format-label': 'Ekspordi formaat',
|
||||
'field-limit-label': 'Piirang',
|
||||
'field-locale-label': 'Lokaal',
|
||||
'field-name-label': 'Faili nimi',
|
||||
'field-selectionToUse-label': 'Valiku kasutamine',
|
||||
'field-sort-label': 'Sorteeri järgi',
|
||||
'selectionToUse-allDocuments': 'Kasutage kõiki dokumente',
|
||||
'selectionToUse-currentFilters': 'Kasuta praeguseid filtreid',
|
||||
'selectionToUse-currentSelection': 'Kasuta praegust valikut',
|
||||
totalDocumentsCount: '{{count}} dokumendi koguarv',
|
||||
},
|
||||
}
|
||||
|
||||
export const et: PluginLanguage = {
|
||||
dateFNSKey: 'et',
|
||||
translations: etTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const faTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'تمام مکان ها',
|
||||
exportDocumentLabel: 'صادر کردن {{label}}',
|
||||
exportOptions: 'گزینه های صادرات',
|
||||
'field-depth-label': 'عمق',
|
||||
'field-drafts-label': 'شامل پیش نویس ها',
|
||||
'field-fields-label': 'مزارع',
|
||||
'field-format-label': 'فرمت صادرات',
|
||||
'field-limit-label': 'محدودیت',
|
||||
'field-locale-label': 'محلی',
|
||||
'field-name-label': 'نام فایل',
|
||||
'field-selectionToUse-label': 'انتخاب برای استفاده',
|
||||
'field-sort-label': 'مرتب سازی بر اساس',
|
||||
'selectionToUse-allDocuments': 'از تمام مستندات استفاده کنید',
|
||||
'selectionToUse-currentFilters': 'از فیلترهای فعلی استفاده کنید',
|
||||
'selectionToUse-currentSelection': 'از انتخاب فعلی استفاده کنید',
|
||||
totalDocumentsCount: '{{count}} سند کل',
|
||||
},
|
||||
}
|
||||
|
||||
export const fa: PluginLanguage = {
|
||||
dateFNSKey: 'fa-IR',
|
||||
translations: faTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const frTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Tous les paramètres régionaux',
|
||||
exportDocumentLabel: 'Exporter {{label}}',
|
||||
exportOptions: "Options d'exportation",
|
||||
'field-depth-label': 'Profondeur',
|
||||
'field-drafts-label': 'Inclure les ébauches',
|
||||
'field-fields-label': 'Champs',
|
||||
'field-format-label': "Format d'exportation",
|
||||
'field-limit-label': 'Limite',
|
||||
'field-locale-label': 'Localisation',
|
||||
'field-name-label': 'Nom de fichier',
|
||||
'field-selectionToUse-label': 'Sélection à utiliser',
|
||||
'field-sort-label': 'Trier par',
|
||||
'selectionToUse-allDocuments': 'Utilisez tous les documents',
|
||||
'selectionToUse-currentFilters': 'Utilisez les filtres actuels',
|
||||
'selectionToUse-currentSelection': 'Utilisez la sélection actuelle',
|
||||
totalDocumentsCount: '{{count}} documents au total',
|
||||
},
|
||||
}
|
||||
|
||||
export const fr: PluginLanguage = {
|
||||
dateFNSKey: 'fr',
|
||||
translations: frTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const heTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'כל המיקומים',
|
||||
exportDocumentLabel: 'ייצוא {{label}}',
|
||||
exportOptions: 'אפשרויות ייצוא',
|
||||
'field-depth-label': 'עומק',
|
||||
'field-drafts-label': 'כלול טיוטות',
|
||||
'field-fields-label': 'שדות',
|
||||
'field-format-label': 'פורמט יצוא',
|
||||
'field-limit-label': 'הגבלה',
|
||||
'field-locale-label': 'מקום',
|
||||
'field-name-label': 'שם הקובץ',
|
||||
'field-selectionToUse-label': 'בחירה לשימוש',
|
||||
'field-sort-label': 'מיין לפי',
|
||||
'selectionToUse-allDocuments': 'השתמש בכל המסמכים',
|
||||
'selectionToUse-currentFilters': 'השתמש במסננים הנוכחיים',
|
||||
'selectionToUse-currentSelection': 'השתמש בבחירה הנוכחית',
|
||||
totalDocumentsCount: '{{count}} מסמכים כולל',
|
||||
},
|
||||
}
|
||||
|
||||
export const he: PluginLanguage = {
|
||||
dateFNSKey: 'he',
|
||||
translations: heTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const hrTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Sve lokalne postavke',
|
||||
exportDocumentLabel: 'Izvoz {{label}}',
|
||||
exportOptions: 'Opcije izvoza',
|
||||
'field-depth-label': 'Dubina',
|
||||
'field-drafts-label': 'Uključite nacrte',
|
||||
'field-fields-label': 'Polja',
|
||||
'field-format-label': 'Format izvoza',
|
||||
'field-limit-label': 'Ograničenje',
|
||||
'field-locale-label': 'Lokalitet',
|
||||
'field-name-label': 'Naziv datoteke',
|
||||
'field-selectionToUse-label': 'Odabir za upotrebu',
|
||||
'field-sort-label': 'Sortiraj po',
|
||||
'selectionToUse-allDocuments': 'Koristite sve dokumente',
|
||||
'selectionToUse-currentFilters': 'Koristite trenutne filtre',
|
||||
'selectionToUse-currentSelection': 'Koristite trenutni odabir',
|
||||
totalDocumentsCount: '{{count}} ukupno dokumenata',
|
||||
},
|
||||
}
|
||||
|
||||
export const hr: PluginLanguage = {
|
||||
dateFNSKey: 'hr',
|
||||
translations: hrTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const huTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Minden helyszín',
|
||||
exportDocumentLabel: '{{label}} exportálása',
|
||||
exportOptions: 'Exportálási lehetőségek',
|
||||
'field-depth-label': 'Mélység',
|
||||
'field-drafts-label': 'Tartalmazza a vázlatokat',
|
||||
'field-fields-label': 'Mezők',
|
||||
'field-format-label': 'Export formátum',
|
||||
'field-limit-label': 'Korlát',
|
||||
'field-locale-label': 'Helyszín',
|
||||
'field-name-label': 'Fájlnév',
|
||||
'field-selectionToUse-label': 'Használatra kiválasztva',
|
||||
'field-sort-label': 'Rendezés szerint',
|
||||
'selectionToUse-allDocuments': 'Használjon minden dokumentumot',
|
||||
'selectionToUse-currentFilters': 'Használja az aktuális szűrőket',
|
||||
'selectionToUse-currentSelection': 'Használja a jelenlegi kiválasztást',
|
||||
totalDocumentsCount: '{{count}} összes dokumentum',
|
||||
},
|
||||
}
|
||||
|
||||
export const hu: PluginLanguage = {
|
||||
dateFNSKey: 'hu',
|
||||
translations: huTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const hyTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Բոլոր տեղականությունները',
|
||||
exportDocumentLabel: 'Փոխարտադրել {{label}}',
|
||||
exportOptions: 'Արտահանման տարբերակներ',
|
||||
'field-depth-label': 'Խորություն',
|
||||
'field-drafts-label': 'Ներառեք սևագրեր',
|
||||
'field-fields-label': 'Դաշտեր',
|
||||
'field-format-label': 'Արտահանման ձևաչափ',
|
||||
'field-limit-label': 'Սահմանափակում',
|
||||
'field-locale-label': 'Լոկալ',
|
||||
'field-name-label': 'Ֆայլի անվանումը',
|
||||
'field-selectionToUse-label': 'Օգտագործման ընտրություն',
|
||||
'field-sort-label': 'Դասավորել ըստ',
|
||||
'selectionToUse-allDocuments': 'Օգտագործեք բոլոր փաստաթղթերը',
|
||||
'selectionToUse-currentFilters': 'Օգտագործեք ընթացիկ ֆիլտրերը',
|
||||
'selectionToUse-currentSelection': 'Օգտագործել ընթացիկ ընտրությունը',
|
||||
totalDocumentsCount: '{{count}} ընդհանուր փաստաթուղթեր',
|
||||
},
|
||||
}
|
||||
|
||||
export const hy: PluginLanguage = {
|
||||
dateFNSKey: 'hy-AM',
|
||||
translations: hyTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const itTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Tutte le località',
|
||||
exportDocumentLabel: 'Esporta {{label}}',
|
||||
exportOptions: 'Opzioni di Esportazione',
|
||||
'field-depth-label': 'Profondità',
|
||||
'field-drafts-label': 'Includi bozze',
|
||||
'field-fields-label': 'Campi',
|
||||
'field-format-label': 'Formato di Esportazione',
|
||||
'field-limit-label': 'Limite',
|
||||
'field-locale-label': 'Locale',
|
||||
'field-name-label': 'Nome del file',
|
||||
'field-selectionToUse-label': 'Selezione da utilizzare',
|
||||
'field-sort-label': 'Ordina per',
|
||||
'selectionToUse-allDocuments': 'Utilizza tutti i documenti',
|
||||
'selectionToUse-currentFilters': 'Utilizza i filtri correnti',
|
||||
'selectionToUse-currentSelection': 'Utilizza la selezione corrente',
|
||||
totalDocumentsCount: '{{count}} documenti totali',
|
||||
},
|
||||
}
|
||||
|
||||
export const it: PluginLanguage = {
|
||||
dateFNSKey: 'it',
|
||||
translations: itTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const jaTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'すべてのロケール',
|
||||
exportDocumentLabel: '{{label}}をエクスポートする',
|
||||
exportOptions: 'エクスポートオプション',
|
||||
'field-depth-label': '深さ',
|
||||
'field-drafts-label': 'ドラフトを含めます',
|
||||
'field-fields-label': 'フィールド',
|
||||
'field-format-label': 'エクスポート形式',
|
||||
'field-limit-label': '制限',
|
||||
'field-locale-label': 'ロケール',
|
||||
'field-name-label': 'ファイル名',
|
||||
'field-selectionToUse-label': '使用する選択',
|
||||
'field-sort-label': '並び替える',
|
||||
'selectionToUse-allDocuments': 'すべての文書を使用してください。',
|
||||
'selectionToUse-currentFilters': '現在のフィルターを使用してください',
|
||||
'selectionToUse-currentSelection': '現在の選択を使用する',
|
||||
totalDocumentsCount: '{{count}}合計の文書',
|
||||
},
|
||||
}
|
||||
|
||||
export const ja: PluginLanguage = {
|
||||
dateFNSKey: 'ja',
|
||||
translations: jaTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const koTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: '모든 지역 설정',
|
||||
exportDocumentLabel: '{{label}} 내보내기',
|
||||
exportOptions: '수출 옵션',
|
||||
'field-depth-label': '깊이',
|
||||
'field-drafts-label': '초안을 포함하십시오.',
|
||||
'field-fields-label': '필드',
|
||||
'field-format-label': '수출 형식',
|
||||
'field-limit-label': '한계',
|
||||
'field-locale-label': '지역',
|
||||
'field-name-label': '파일 이름',
|
||||
'field-selectionToUse-label': '사용할 선택',
|
||||
'field-sort-label': '정렬 방식',
|
||||
'selectionToUse-allDocuments': '모든 문서를 사용하십시오.',
|
||||
'selectionToUse-currentFilters': '현재 필터를 사용하십시오.',
|
||||
'selectionToUse-currentSelection': '현재 선택 항목을 사용하십시오.',
|
||||
totalDocumentsCount: '{{count}}개의 총 문서',
|
||||
},
|
||||
}
|
||||
|
||||
export const ko: PluginLanguage = {
|
||||
dateFNSKey: 'ko',
|
||||
translations: koTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const ltTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Visos vietovės',
|
||||
exportDocumentLabel: 'Eksportuoti {{label}}',
|
||||
exportOptions: 'Eksporto parinktys',
|
||||
'field-depth-label': 'Gylis',
|
||||
'field-drafts-label': 'Įtraukite juodraščius',
|
||||
'field-fields-label': 'Laukai',
|
||||
'field-format-label': 'Eksporto formatas',
|
||||
'field-limit-label': 'Ribos',
|
||||
'field-locale-label': 'Lokalė',
|
||||
'field-name-label': 'Failo pavadinimas',
|
||||
'field-selectionToUse-label': 'Naudojimo pasirinkimas',
|
||||
'field-sort-label': 'Rūšiuoti pagal',
|
||||
'selectionToUse-allDocuments': 'Naudokite visus dokumentus.',
|
||||
'selectionToUse-currentFilters': 'Naudoti esamus filtrus',
|
||||
'selectionToUse-currentSelection': 'Naudoti dabartinį pasirinkimą',
|
||||
totalDocumentsCount: '{{count}} viso dokumentų',
|
||||
},
|
||||
}
|
||||
|
||||
export const lt: PluginLanguage = {
|
||||
dateFNSKey: 'lt',
|
||||
translations: ltTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const lvTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Visas lokalitātes',
|
||||
exportDocumentLabel: 'Eksportēt {{label}}',
|
||||
exportOptions: 'Eksportēšanas opcijas',
|
||||
'field-depth-label': 'Dziļums',
|
||||
'field-drafts-label': 'Iekļaut melnrakstus',
|
||||
'field-fields-label': 'Lauki',
|
||||
'field-format-label': 'Eksporta formāts',
|
||||
'field-limit-label': 'Limits',
|
||||
'field-locale-label': 'Lokalizācija',
|
||||
'field-name-label': 'Faila nosaukums',
|
||||
'field-selectionToUse-label': 'Izvēles lietošana',
|
||||
'field-sort-label': 'Kārtot pēc',
|
||||
'selectionToUse-allDocuments': 'Izmantojiet visus dokumentus',
|
||||
'selectionToUse-currentFilters': 'Izmantot pašreizējos filtrus',
|
||||
'selectionToUse-currentSelection': 'Izmantot pašreizējo izvēli',
|
||||
totalDocumentsCount: '{{count}} kopā dokumenti',
|
||||
},
|
||||
}
|
||||
|
||||
export const lv: PluginLanguage = {
|
||||
dateFNSKey: 'lv',
|
||||
translations: lvTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const myTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'အားလုံးနေရာတွင်',
|
||||
exportDocumentLabel: 'Eksport {{label}}',
|
||||
exportOptions: 'Pilihan Eksport',
|
||||
'field-depth-label': 'အန္တိုင်း',
|
||||
'field-drafts-label': 'မူကြမ်းများပါဝင်ပါ',
|
||||
'field-fields-label': 'ကွင်းပျိုးရန်ကွက်များ',
|
||||
'field-format-label': 'တင်ပို့နည်းအစီအစဉ်',
|
||||
'field-limit-label': 'ကန့်သတ်ချက်',
|
||||
'field-locale-label': 'Tempatan',
|
||||
'field-name-label': 'ဖိုင်နာမည်',
|
||||
'field-selectionToUse-label': 'Pilihan untuk digunakan',
|
||||
'field-sort-label': 'စီမံအလိုက်',
|
||||
'selectionToUse-allDocuments': 'Gunakan semua dokumen',
|
||||
'selectionToUse-currentFilters': 'Gunakan penapis semasa',
|
||||
'selectionToUse-currentSelection': 'Gunakan pilihan semasa',
|
||||
totalDocumentsCount: '{{count}} keseluruhan dokumen',
|
||||
},
|
||||
}
|
||||
|
||||
export const my: PluginLanguage = {
|
||||
dateFNSKey: 'en-US',
|
||||
translations: myTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const nbTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Alle steder',
|
||||
exportDocumentLabel: 'Eksporter {{label}}',
|
||||
exportOptions: 'Eksportalternativer',
|
||||
'field-depth-label': 'Dybde',
|
||||
'field-drafts-label': 'Inkluder utkast',
|
||||
'field-fields-label': 'Felt',
|
||||
'field-format-label': 'Eksportformat',
|
||||
'field-limit-label': 'Begrensning',
|
||||
'field-locale-label': 'Lokal',
|
||||
'field-name-label': 'Filnavn',
|
||||
'field-selectionToUse-label': 'Valg til bruk',
|
||||
'field-sort-label': 'Sorter etter',
|
||||
'selectionToUse-allDocuments': 'Bruk alle dokumentene',
|
||||
'selectionToUse-currentFilters': 'Bruk gjeldende filtre',
|
||||
'selectionToUse-currentSelection': 'Bruk gjeldende utvalg',
|
||||
totalDocumentsCount: '{{count}} totalt dokumenter',
|
||||
},
|
||||
}
|
||||
|
||||
export const nb: PluginLanguage = {
|
||||
dateFNSKey: 'nb',
|
||||
translations: nbTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const nlTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Alle locaties',
|
||||
exportDocumentLabel: 'Exporteer {{label}}',
|
||||
exportOptions: 'Exportmogelijkheden',
|
||||
'field-depth-label': 'Diepte',
|
||||
'field-drafts-label': 'Voeg ontwerpen toe',
|
||||
'field-fields-label': 'Velden',
|
||||
'field-format-label': 'Exportformaat',
|
||||
'field-limit-label': 'Limiet',
|
||||
'field-locale-label': 'Lokale',
|
||||
'field-name-label': 'Bestandsnaam',
|
||||
'field-selectionToUse-label': 'Selectie om te gebruiken',
|
||||
'field-sort-label': 'Sorteer op',
|
||||
'selectionToUse-allDocuments': 'Gebruik alle documenten',
|
||||
'selectionToUse-currentFilters': 'Gebruik huidige filters',
|
||||
'selectionToUse-currentSelection': 'Gebruik huidige selectie',
|
||||
totalDocumentsCount: '{{count}} totaal aantal documenten',
|
||||
},
|
||||
}
|
||||
|
||||
export const nl: PluginLanguage = {
|
||||
dateFNSKey: 'nl',
|
||||
translations: nlTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const plTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Wszystkie lokalizacje',
|
||||
exportDocumentLabel: 'Eksportuj {{label}}',
|
||||
exportOptions: 'Opcje eksportu',
|
||||
'field-depth-label': 'Głębokość',
|
||||
'field-drafts-label': 'Dołącz szkice',
|
||||
'field-fields-label': 'Pola',
|
||||
'field-format-label': 'Format eksportu',
|
||||
'field-limit-label': 'Limit',
|
||||
'field-locale-label': 'Lokalizacja',
|
||||
'field-name-label': 'Nazwa pliku',
|
||||
'field-selectionToUse-label': 'Wybór do użycia',
|
||||
'field-sort-label': 'Sortuj według',
|
||||
'selectionToUse-allDocuments': 'Użyj wszystkich dokumentów.',
|
||||
'selectionToUse-currentFilters': 'Użyj aktualnych filtrów',
|
||||
'selectionToUse-currentSelection': 'Użyj aktualnego wyboru',
|
||||
totalDocumentsCount: '{{count}} łączna liczba dokumentów',
|
||||
},
|
||||
}
|
||||
|
||||
export const pl: PluginLanguage = {
|
||||
dateFNSKey: 'pl',
|
||||
translations: plTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const ptTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Todos os locais',
|
||||
exportDocumentLabel: 'Exportar {{label}}',
|
||||
exportOptions: 'Opções de Exportação',
|
||||
'field-depth-label': 'Profundidade',
|
||||
'field-drafts-label': 'Incluir rascunhos',
|
||||
'field-fields-label': 'Campos',
|
||||
'field-format-label': 'Formato de Exportação',
|
||||
'field-limit-label': 'Limite',
|
||||
'field-locale-label': 'Localização',
|
||||
'field-name-label': 'Nome do arquivo',
|
||||
'field-selectionToUse-label': 'Seleção para usar',
|
||||
'field-sort-label': 'Ordenar por',
|
||||
'selectionToUse-allDocuments': 'Use todos os documentos',
|
||||
'selectionToUse-currentFilters': 'Use os filtros atuais',
|
||||
'selectionToUse-currentSelection': 'Use a seleção atual',
|
||||
totalDocumentsCount: '{{count}} documentos totais',
|
||||
},
|
||||
}
|
||||
|
||||
export const pt: PluginLanguage = {
|
||||
dateFNSKey: 'pt',
|
||||
translations: ptTranslations,
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
for file in *.js; do
|
||||
mv -- "$file" "${file%.js}.ts"
|
||||
done
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const roTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Toate locațiile',
|
||||
exportDocumentLabel: 'Export {{label}}',
|
||||
exportOptions: 'Opțiuni de export',
|
||||
'field-depth-label': 'Adâncime',
|
||||
'field-drafts-label': 'Includează schițe',
|
||||
'field-fields-label': 'Campuri',
|
||||
'field-format-label': 'Format de export',
|
||||
'field-limit-label': 'Limită',
|
||||
'field-locale-label': 'Localizare',
|
||||
'field-name-label': 'Numele fișierului',
|
||||
'field-selectionToUse-label': 'Selectarea pentru utilizare',
|
||||
'field-sort-label': 'Sortează după',
|
||||
'selectionToUse-allDocuments': 'Utilizați toate documentele.',
|
||||
'selectionToUse-currentFilters': 'Utilizați filtrele curente',
|
||||
'selectionToUse-currentSelection': 'Utilizați selecția curentă',
|
||||
totalDocumentsCount: '{{count}} documente totale',
|
||||
},
|
||||
}
|
||||
|
||||
export const ro: PluginLanguage = {
|
||||
dateFNSKey: 'ro',
|
||||
translations: roTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const rsTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Sve lokacije',
|
||||
exportDocumentLabel: 'Извоз {{label}}',
|
||||
exportOptions: 'Опције извоза',
|
||||
'field-depth-label': 'Dubina',
|
||||
'field-drafts-label': 'Uključite nacrte',
|
||||
'field-fields-label': 'Polja',
|
||||
'field-format-label': 'Format izvoza',
|
||||
'field-limit-label': 'Ograničenje',
|
||||
'field-locale-label': 'Локалитет',
|
||||
'field-name-label': 'Ime datoteke',
|
||||
'field-selectionToUse-label': 'Izbor za upotrebu',
|
||||
'field-sort-label': 'Sortiraj po',
|
||||
'selectionToUse-allDocuments': 'Koristite sve dokumente',
|
||||
'selectionToUse-currentFilters': 'Koristite trenutne filtere',
|
||||
'selectionToUse-currentSelection': 'Koristite trenutni izbor',
|
||||
totalDocumentsCount: '{{count}} ukupno dokumenata',
|
||||
},
|
||||
}
|
||||
|
||||
export const rs: PluginLanguage = {
|
||||
dateFNSKey: 'rs',
|
||||
translations: rsTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const rsLatinTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Sve lokalne postavke',
|
||||
exportDocumentLabel: 'Izvoz {{label}}',
|
||||
exportOptions: 'Opcije izvoza',
|
||||
'field-depth-label': 'Dubina',
|
||||
'field-drafts-label': 'Uključite nacrte',
|
||||
'field-fields-label': 'Polja',
|
||||
'field-format-label': 'Format izvoza',
|
||||
'field-limit-label': 'Ograničenje',
|
||||
'field-locale-label': 'Lokalitet',
|
||||
'field-name-label': 'Ime datoteke',
|
||||
'field-selectionToUse-label': 'Izbor za upotrebu',
|
||||
'field-sort-label': 'Sortiraj po',
|
||||
'selectionToUse-allDocuments': 'Koristite sve dokumente',
|
||||
'selectionToUse-currentFilters': 'Koristite trenutne filtere',
|
||||
'selectionToUse-currentSelection': 'Koristi trenutni izbor',
|
||||
totalDocumentsCount: '{{count}} ukupno dokumenata',
|
||||
},
|
||||
}
|
||||
|
||||
export const rsLatin: PluginLanguage = {
|
||||
dateFNSKey: 'rs-Latin',
|
||||
translations: rsLatinTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const ruTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Все локали',
|
||||
exportDocumentLabel: 'Экспорт {{label}}',
|
||||
exportOptions: 'Опции экспорта',
|
||||
'field-depth-label': 'Глубина',
|
||||
'field-drafts-label': 'Включить черновики',
|
||||
'field-fields-label': 'Поля',
|
||||
'field-format-label': 'Формат экспорта',
|
||||
'field-limit-label': 'Лимит',
|
||||
'field-locale-label': 'Локаль',
|
||||
'field-name-label': 'Имя файла',
|
||||
'field-selectionToUse-label': 'Выбор использования',
|
||||
'field-sort-label': 'Сортировать по',
|
||||
'selectionToUse-allDocuments': 'Используйте все документы',
|
||||
'selectionToUse-currentFilters': 'Использовать текущие фильтры',
|
||||
'selectionToUse-currentSelection': 'Использовать текущий выбор',
|
||||
totalDocumentsCount: '{{count}} общее количество документов',
|
||||
},
|
||||
}
|
||||
|
||||
export const ru: PluginLanguage = {
|
||||
dateFNSKey: 'ru',
|
||||
translations: ruTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const skTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Všetky miestne nastavenia',
|
||||
exportDocumentLabel: 'Export {{label}}',
|
||||
exportOptions: 'Možnosti exportu',
|
||||
'field-depth-label': 'Hĺbka',
|
||||
'field-drafts-label': 'Zahrnúť návrhy',
|
||||
'field-fields-label': 'Polia',
|
||||
'field-format-label': 'Formát exportu',
|
||||
'field-limit-label': 'Limit',
|
||||
'field-locale-label': 'Lokalita',
|
||||
'field-name-label': 'Názov súboru',
|
||||
'field-selectionToUse-label': 'Výber na použitie',
|
||||
'field-sort-label': 'Triediť podľa',
|
||||
'selectionToUse-allDocuments': 'Použite všetky dokumenty',
|
||||
'selectionToUse-currentFilters': 'Použiť aktuálne filtre',
|
||||
'selectionToUse-currentSelection': 'Použiť aktuálny výber',
|
||||
totalDocumentsCount: '{{count}} celkový počet dokumentov',
|
||||
},
|
||||
}
|
||||
|
||||
export const sk: PluginLanguage = {
|
||||
dateFNSKey: 'sk',
|
||||
translations: skTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const slTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Vse lokacije',
|
||||
exportDocumentLabel: 'Izvozi {{label}}',
|
||||
exportOptions: 'Možnosti izvoza',
|
||||
'field-depth-label': 'Globina',
|
||||
'field-drafts-label': 'Vključi osnutke',
|
||||
'field-fields-label': 'Polja',
|
||||
'field-format-label': 'Format izvoza',
|
||||
'field-limit-label': 'Omejitev',
|
||||
'field-locale-label': 'Lokalno',
|
||||
'field-name-label': 'Ime datoteke',
|
||||
'field-selectionToUse-label': 'Izbor za uporabo',
|
||||
'field-sort-label': 'Razvrsti po',
|
||||
'selectionToUse-allDocuments': 'Uporabite vse dokumente',
|
||||
'selectionToUse-currentFilters': 'Uporabite trenutne filtre.',
|
||||
'selectionToUse-currentSelection': 'Uporabi trenutno izbiro',
|
||||
totalDocumentsCount: '{{count}} skupno dokumentov',
|
||||
},
|
||||
}
|
||||
|
||||
export const sl: PluginLanguage = {
|
||||
dateFNSKey: 'sl-SI',
|
||||
translations: slTranslations,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { PluginDefaultTranslationsObject, PluginLanguage } from '../types.js'
|
||||
|
||||
export const svTranslations: PluginDefaultTranslationsObject = {
|
||||
'plugin-import-export': {
|
||||
allLocales: 'Alla platser',
|
||||
exportDocumentLabel: 'Exportera {{label}}',
|
||||
exportOptions: 'Exportalternativ',
|
||||
'field-depth-label': 'Djup',
|
||||
'field-drafts-label': 'Inkludera utkast',
|
||||
'field-fields-label': 'Fält',
|
||||
'field-format-label': 'Exportformat',
|
||||
'field-limit-label': 'Begränsning',
|
||||
'field-locale-label': 'Lokal',
|
||||
'field-name-label': 'Filnamn',
|
||||
'field-selectionToUse-label': 'Val att använda',
|
||||
'field-sort-label': 'Sortera efter',
|
||||
'selectionToUse-allDocuments': 'Använd alla dokument',
|
||||
'selectionToUse-currentFilters': 'Använd aktuella filter',
|
||||
'selectionToUse-currentSelection': 'Använd nuvarande urval',
|
||||
totalDocumentsCount: '{{count}} totala dokument',
|
||||
},
|
||||
}
|
||||
|
||||
export const sv: PluginLanguage = {
|
||||
dateFNSKey: 'sv',
|
||||
translations: svTranslations,
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user