feat: duplicate doc moved from frontend to backend concern (#5342)
BREAKING CHANGE: collection.admin.hooks.beforeDuplicate removed and instead should be handled using field beforeDuplicate hooks which take the full field hook arguments. * feat: duplicate doc moved from frontend to backend concern * feat: default beforeDuplicate hook functions on unique fields * docs: beforeDuplicate field hook * test: duplicate doc local api * chore: fix build errors * chore: add access.create call to duplicate operation * chore: perfectionist reorder imports
This commit is contained in:
@@ -167,53 +167,6 @@ those three fields plus the ID field.
|
|||||||
so your admin queries can remain performant.
|
so your admin queries can remain performant.
|
||||||
</Banner>
|
</Banner>
|
||||||
|
|
||||||
### Admin Hooks
|
|
||||||
|
|
||||||
In addition to collection hooks themselves, Payload provides for admin UI-specific hooks that you can leverage.
|
|
||||||
|
|
||||||
**`beforeDuplicate`**
|
|
||||||
|
|
||||||
The `beforeDuplicate` hook is an async function that accepts an object containing the data to duplicate, as well as the
|
|
||||||
locale of the doc to duplicate. Within this hook, you can modify the data to be duplicated, which is useful in cases
|
|
||||||
where you have unique fields that need to be incremented or similar, as well as if you want to automatically modify a
|
|
||||||
document's `title`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { BeforeDuplicate, CollectionConfig } from 'payload/types'
|
|
||||||
// Your auto-generated Page type
|
|
||||||
import { Page } from '../payload-types.ts'
|
|
||||||
|
|
||||||
const beforeDuplicate: BeforeDuplicate<Page> = ({ data }) => {
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
title: `${data.title} Copy`,
|
|
||||||
uniqueField: data.uniqueField ? `${data.uniqueField}-copy` : '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Page: CollectionConfig = {
|
|
||||||
slug: 'pages',
|
|
||||||
admin: {
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
type: 'text',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'uniqueField',
|
|
||||||
type: 'text',
|
|
||||||
unique: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### TypeScript
|
### TypeScript
|
||||||
|
|
||||||
You can import collection types as follows:
|
You can import collection types as follows:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ functionalities to be easily reusable across your projects.
|
|||||||
|
|
||||||
- [beforeValidate](#beforevalidate)
|
- [beforeValidate](#beforevalidate)
|
||||||
- [beforeChange](#beforechange)
|
- [beforeChange](#beforechange)
|
||||||
|
- beforeDuplicate(#beforeduplicate)
|
||||||
- [afterChange](#afterchange)
|
- [afterChange](#afterchange)
|
||||||
- [afterRead](#afterread)
|
- [afterRead](#afterread)
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ const ExampleField: Field = {
|
|||||||
hooks: {
|
hooks: {
|
||||||
beforeValidate: [(args) => {...}],
|
beforeValidate: [(args) => {...}],
|
||||||
beforeChange: [(args) => {...}],
|
beforeChange: [(args) => {...}],
|
||||||
|
beforeDuplicate: [(args) => {...}],
|
||||||
afterChange: [(args) => {...}],
|
afterChange: [(args) => {...}],
|
||||||
afterRead: [(args) => {...}],
|
afterRead: [(args) => {...}],
|
||||||
}
|
}
|
||||||
@@ -217,6 +219,27 @@ Here, the `afterRead` hook for the `dateField` is used to format the date into a
|
|||||||
using `toLocaleDateString()`. This hook modifies the way the date is presented to the user, making it more
|
using `toLocaleDateString()`. This hook modifies the way the date is presented to the user, making it more
|
||||||
user-friendly.
|
user-friendly.
|
||||||
|
|
||||||
|
### beforeDuplicate
|
||||||
|
|
||||||
|
The `beforeDuplicate` field hook is only called when duplicating a document. It may be used when documents having the
|
||||||
|
exact same properties may cause issue. This gives you a way to avoid duplicate names on `unique`, `required` fields or
|
||||||
|
to unset values by returning `null`. This is called immediately after `defaultValue` and before validation occurs.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { Field } from 'payload/types'
|
||||||
|
|
||||||
|
const numberField: Field = {
|
||||||
|
name: 'number',
|
||||||
|
type: 'number',
|
||||||
|
hooks: {
|
||||||
|
// increment existing value by 1
|
||||||
|
beforeDuplicate: [({ value }) => {
|
||||||
|
return (value ?? 0) + 1
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## TypeScript
|
## TypeScript
|
||||||
|
|
||||||
Payload exports a type for field hooks which can be accessed and used as follows:
|
Payload exports a type for field hooks which can be accessed and used as follows:
|
||||||
|
|||||||
45
packages/graphql/src/resolvers/collections/duplicate.ts
Normal file
45
packages/graphql/src/resolvers/collections/duplicate.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import type { GeneratedTypes } from 'payload'
|
||||||
|
import type { PayloadRequest } from 'payload/types'
|
||||||
|
import type { Collection } from 'payload/types'
|
||||||
|
|
||||||
|
import { duplicateOperation } from 'payload/operations'
|
||||||
|
import { isolateObjectProperty } from 'payload/utilities'
|
||||||
|
|
||||||
|
import type { Context } from '../types.js'
|
||||||
|
|
||||||
|
export type Resolver<T> = (
|
||||||
|
_: unknown,
|
||||||
|
args: {
|
||||||
|
draft: boolean
|
||||||
|
fallbackLocale?: string
|
||||||
|
id: string
|
||||||
|
locale?: string
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
req: PayloadRequest
|
||||||
|
},
|
||||||
|
) => Promise<T>
|
||||||
|
|
||||||
|
export default function duplicateResolver<T extends keyof GeneratedTypes['collections']>(
|
||||||
|
collection: Collection,
|
||||||
|
): Resolver<GeneratedTypes['collections'][T]> {
|
||||||
|
return async function resolver(_, args, context: Context) {
|
||||||
|
const { req } = context
|
||||||
|
const locale = req.locale
|
||||||
|
const fallbackLocale = req.fallbackLocale
|
||||||
|
req.locale = args.locale || locale
|
||||||
|
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
id: args.id,
|
||||||
|
collection,
|
||||||
|
depth: 0,
|
||||||
|
draft: args.draft,
|
||||||
|
req: isolateObjectProperty(req, 'transactionID'),
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await duplicateOperation(options)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ import verifyEmail from '../resolvers/auth/verifyEmail.js'
|
|||||||
import createResolver from '../resolvers/collections/create.js'
|
import createResolver from '../resolvers/collections/create.js'
|
||||||
import getDeleteResolver from '../resolvers/collections/delete.js'
|
import getDeleteResolver from '../resolvers/collections/delete.js'
|
||||||
import { docAccessResolver } from '../resolvers/collections/docAccess.js'
|
import { docAccessResolver } from '../resolvers/collections/docAccess.js'
|
||||||
|
import duplicateResolver from '../resolvers/collections/duplicate.js'
|
||||||
import findResolver from '../resolvers/collections/find.js'
|
import findResolver from '../resolvers/collections/find.js'
|
||||||
import findByIDResolver from '../resolvers/collections/findByID.js'
|
import findByIDResolver from '../resolvers/collections/findByID.js'
|
||||||
import findVersionByIDResolver from '../resolvers/collections/findVersionByID.js'
|
import findVersionByIDResolver from '../resolvers/collections/findVersionByID.js'
|
||||||
@@ -237,6 +238,14 @@ function initCollectionsGraphQL({ config, graphqlResult }: InitCollectionsGraphQ
|
|||||||
resolve: getDeleteResolver(collection),
|
resolve: getDeleteResolver(collection),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
graphqlResult.Mutation.fields[`duplicate${singularName}`] = {
|
||||||
|
type: collection.graphQL.type,
|
||||||
|
args: {
|
||||||
|
id: { type: new GraphQLNonNull(idType) },
|
||||||
|
},
|
||||||
|
resolve: duplicateResolver(collection),
|
||||||
|
}
|
||||||
|
|
||||||
if (collectionConfig.versions) {
|
if (collectionConfig.versions) {
|
||||||
const versionIDType = config.db.defaultIDType === 'text' ? GraphQLString : GraphQLInt
|
const versionIDType = config.db.defaultIDType === 'text' ? GraphQLString : GraphQLInt
|
||||||
const versionCollectionFields: Field[] = [
|
const versionCollectionFields: Field[] = [
|
||||||
|
|||||||
33
packages/next/src/routes/rest/collections/duplicate.ts
Normal file
33
packages/next/src/routes/rest/collections/duplicate.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { getTranslation } from '@payloadcms/translations'
|
||||||
|
import httpStatus from 'http-status'
|
||||||
|
import { duplicateOperation } from 'payload/operations'
|
||||||
|
import { isNumber } from 'payload/utilities'
|
||||||
|
|
||||||
|
import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||||
|
|
||||||
|
export const duplicate: CollectionRouteHandlerWithID = async ({ id, collection, req }) => {
|
||||||
|
const { searchParams } = req
|
||||||
|
const depth = searchParams.get('depth')
|
||||||
|
// draft defaults to true, unless explicitly set requested as false to prevent the newly duplicated document from being published
|
||||||
|
const draft = searchParams.get('draft') !== 'false'
|
||||||
|
|
||||||
|
const doc = await duplicateOperation({
|
||||||
|
id,
|
||||||
|
collection,
|
||||||
|
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||||
|
draft,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = req.t('general:successfullyDuplicated', {label: getTranslation(collection.config.labels.singular, req.i18n)})
|
||||||
|
|
||||||
|
return Response.json(
|
||||||
|
{
|
||||||
|
doc,
|
||||||
|
message,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: httpStatus.OK,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -44,6 +44,7 @@ import { findVersionByID as findVersionByIdGlobal } from './globals/findVersionB
|
|||||||
import { findVersions as findVersionsGlobal } from './globals/findVersions.js'
|
import { findVersions as findVersionsGlobal } from './globals/findVersions.js'
|
||||||
import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion.js'
|
import { restoreVersion as restoreVersionGlobal } from './globals/restoreVersion.js'
|
||||||
import { update as updateGlobal } from './globals/update.js'
|
import { update as updateGlobal } from './globals/update.js'
|
||||||
|
import { duplicate } from './collections/duplicate.js'
|
||||||
|
|
||||||
const endpoints = {
|
const endpoints = {
|
||||||
collection: {
|
collection: {
|
||||||
@@ -71,6 +72,7 @@ const endpoints = {
|
|||||||
'doc-access-by-id': docAccess,
|
'doc-access-by-id': docAccess,
|
||||||
'doc-verify-by-id': verifyEmail,
|
'doc-verify-by-id': verifyEmail,
|
||||||
'doc-versions-by-id': restoreVersion,
|
'doc-versions-by-id': restoreVersion,
|
||||||
|
duplicate,
|
||||||
'first-register': registerFirstUser,
|
'first-register': registerFirstUser,
|
||||||
'forgot-password': forgotPassword,
|
'forgot-password': forgotPassword,
|
||||||
login,
|
login,
|
||||||
@@ -356,6 +358,9 @@ export const POST =
|
|||||||
res = await (
|
res = await (
|
||||||
endpoints.collection.POST[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID
|
endpoints.collection.POST[`doc-${slug2}-by-id`] as CollectionRouteHandlerWithID
|
||||||
)({ id: slug3, collection, req })
|
)({ id: slug3, collection, req })
|
||||||
|
} else if (slug3 === 'duplicate') {
|
||||||
|
// /:collection/:id/duplicate
|
||||||
|
res = await endpoints.collection.POST.duplicate({ id: slug2, collection, req })
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,9 @@ const sanitizeCollection = (
|
|||||||
if (sanitized.upload) {
|
if (sanitized.upload) {
|
||||||
if (sanitized.upload === true) sanitized.upload = {}
|
if (sanitized.upload === true) sanitized.upload = {}
|
||||||
|
|
||||||
|
// disable duplicate for uploads by default
|
||||||
|
sanitized.admin.disableDuplicate = sanitized.admin.disableDuplicate || true
|
||||||
|
|
||||||
sanitized.upload.staticDir = sanitized.upload.staticDir || sanitized.slug
|
sanitized.upload.staticDir = sanitized.upload.staticDir || sanitized.slug
|
||||||
sanitized.admin.useAsTitle =
|
sanitized.admin.useAsTitle =
|
||||||
sanitized.admin.useAsTitle && sanitized.admin.useAsTitle !== 'id'
|
sanitized.admin.useAsTitle && sanitized.admin.useAsTitle !== 'id'
|
||||||
@@ -136,6 +139,9 @@ const sanitizeCollection = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disable duplicate for auth enabled collections by default
|
||||||
|
sanitized.admin.disableDuplicate = sanitized.admin.disableDuplicate || true
|
||||||
|
|
||||||
if (!sanitized.auth.strategies) {
|
if (!sanitized.auth.strategies) {
|
||||||
sanitized.auth.strategies = []
|
sanitized.auth.strategies = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import joi from 'joi'
|
import joi from 'joi'
|
||||||
|
|
||||||
import { endpointsSchema } from '../../config/schema.js'
|
import { endpointsSchema } from '../../config/schema.js'
|
||||||
import {
|
import { componentSchema, customViewSchema, livePreviewSchema, } from '../../config/shared/componentSchema.js'
|
||||||
componentSchema,
|
|
||||||
customViewSchema,
|
|
||||||
livePreviewSchema,
|
|
||||||
} from '../../config/shared/componentSchema.js'
|
|
||||||
|
|
||||||
const strategyBaseSchema = joi.object().keys({
|
const strategyBaseSchema = joi.object().keys({
|
||||||
logout: joi.boolean(),
|
logout: joi.boolean(),
|
||||||
@@ -65,9 +61,6 @@ const collectionSchema = joi.object().keys({
|
|||||||
group: joi.alternatives().try(joi.string(), joi.object().pattern(joi.string(), [joi.string()])),
|
group: joi.alternatives().try(joi.string(), joi.object().pattern(joi.string(), [joi.string()])),
|
||||||
hidden: joi.alternatives().try(joi.boolean(), joi.func()),
|
hidden: joi.alternatives().try(joi.boolean(), joi.func()),
|
||||||
hideAPIURL: joi.bool(),
|
hideAPIURL: joi.bool(),
|
||||||
hooks: joi.object({
|
|
||||||
beforeDuplicate: joi.func(),
|
|
||||||
}),
|
|
||||||
listSearchableFields: joi.array().items(joi.string()),
|
listSearchableFields: joi.array().items(joi.string()),
|
||||||
livePreview: joi.object(livePreviewSchema),
|
livePreview: joi.object(livePreviewSchema),
|
||||||
pagination: joi.object({
|
pagination: joi.object({
|
||||||
|
|||||||
@@ -195,15 +195,6 @@ export type AfterForgotPasswordHook = (args: {
|
|||||||
context: RequestContext
|
context: RequestContext
|
||||||
}) => any
|
}) => any
|
||||||
|
|
||||||
type BeforeDuplicateArgs<T> = {
|
|
||||||
/** The collection which this hook is being run on */
|
|
||||||
collection: SanitizedCollectionConfig
|
|
||||||
data: T
|
|
||||||
locale?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BeforeDuplicate<T = any> = (args: BeforeDuplicateArgs<T>) => Promise<T> | T
|
|
||||||
|
|
||||||
export type CollectionAdminOptions = {
|
export type CollectionAdminOptions = {
|
||||||
/**
|
/**
|
||||||
* Custom admin components
|
* Custom admin components
|
||||||
@@ -275,12 +266,6 @@ export type CollectionAdminOptions = {
|
|||||||
* Hide the API URL within the Edit view
|
* Hide the API URL within the Edit view
|
||||||
*/
|
*/
|
||||||
hideAPIURL?: boolean
|
hideAPIURL?: boolean
|
||||||
hooks?: {
|
|
||||||
/**
|
|
||||||
* Function that allows you to modify a document's data before it is duplicated
|
|
||||||
*/
|
|
||||||
beforeDuplicate?: BeforeDuplicate
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Additional fields to be searched via the full text search
|
* Additional fields to be searched via the full text search
|
||||||
*/
|
*/
|
||||||
|
|||||||
366
packages/payload/src/collections/operations/duplicate.ts
Normal file
366
packages/payload/src/collections/operations/duplicate.ts
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
import type { DeepPartial } from 'ts-essentials'
|
||||||
|
|
||||||
|
import httpStatus from 'http-status'
|
||||||
|
|
||||||
|
import type { FindOneArgs } from '../../database/types.js'
|
||||||
|
import type { GeneratedTypes } from '../../index.js'
|
||||||
|
import type { PayloadRequest } from '../../types/index.js'
|
||||||
|
import type { Collection } from '../config/types.js'
|
||||||
|
|
||||||
|
import executeAccess from '../../auth/executeAccess.js'
|
||||||
|
import { hasWhereAccessResult } from '../../auth/types.js'
|
||||||
|
import { combineQueries } from '../../database/combineQueries.js'
|
||||||
|
import { APIError, Forbidden, NotFound } from '../../errors/index.js'
|
||||||
|
import { afterChange } from '../../fields/hooks/afterChange/index.js'
|
||||||
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
|
import { beforeChange } from '../../fields/hooks/beforeChange/index.js'
|
||||||
|
import { beforeValidate } from '../../fields/hooks/beforeValidate/index.js'
|
||||||
|
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||||
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
|
||||||
|
import { saveVersion } from '../../versions/saveVersion.js'
|
||||||
|
import { buildAfterOperation } from './utils.js'
|
||||||
|
|
||||||
|
export type Arguments = {
|
||||||
|
collection: Collection
|
||||||
|
depth?: number
|
||||||
|
draft?: boolean
|
||||||
|
id: number | string
|
||||||
|
overrideAccess?: boolean
|
||||||
|
req: PayloadRequest
|
||||||
|
showHiddenFields?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const duplicateOperation = async <TSlug extends keyof GeneratedTypes['collections']>(
|
||||||
|
incomingArgs: Arguments,
|
||||||
|
): Promise<GeneratedTypes['collections'][TSlug]> => {
|
||||||
|
let args = incomingArgs
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shouldCommit = await initTransaction(args.req)
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// beforeOperation - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
args =
|
||||||
|
(await hook({
|
||||||
|
args,
|
||||||
|
collection: args.collection.config,
|
||||||
|
context: args.req.context,
|
||||||
|
operation: 'update',
|
||||||
|
req: args.req,
|
||||||
|
})) || args
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
collection: { config: collectionConfig },
|
||||||
|
depth,
|
||||||
|
draft: draftArg = true,
|
||||||
|
overrideAccess,
|
||||||
|
req: {
|
||||||
|
fallbackLocale,
|
||||||
|
payload: { config },
|
||||||
|
payload,
|
||||||
|
},
|
||||||
|
req,
|
||||||
|
showHiddenFields,
|
||||||
|
} = args
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
throw new APIError('Missing ID of document to duplicate.', httpStatus.BAD_REQUEST)
|
||||||
|
}
|
||||||
|
const shouldSaveDraft = Boolean(draftArg && collectionConfig.versions.drafts)
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Read Access
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
const accessResults = !overrideAccess
|
||||||
|
? await executeAccess({ id, req }, collectionConfig.access.read)
|
||||||
|
: true
|
||||||
|
const hasWherePolicy = hasWhereAccessResult(accessResults)
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Retrieve document
|
||||||
|
// /////////////////////////////////////
|
||||||
|
const findOneArgs: FindOneArgs = {
|
||||||
|
collection: collectionConfig.slug,
|
||||||
|
locale: req.locale,
|
||||||
|
req,
|
||||||
|
where: combineQueries({ id: { equals: id } }, accessResults),
|
||||||
|
}
|
||||||
|
|
||||||
|
const docWithLocales = await getLatestCollectionVersion({
|
||||||
|
id,
|
||||||
|
config: collectionConfig,
|
||||||
|
payload,
|
||||||
|
query: findOneArgs,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!docWithLocales && !hasWherePolicy) throw new NotFound(req.t)
|
||||||
|
if (!docWithLocales && hasWherePolicy) throw new Forbidden(req.t)
|
||||||
|
|
||||||
|
// remove the createdAt timestamp and rely on the db to default it
|
||||||
|
delete docWithLocales.createdAt
|
||||||
|
|
||||||
|
// for version enabled collections, override the current status with draft, unless draft is explicitly set to false
|
||||||
|
if (shouldSaveDraft) {
|
||||||
|
docWithLocales._status = 'draft'
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Iterate locales of document and call the db create or update functions
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
let locales = [undefined]
|
||||||
|
let versionDoc
|
||||||
|
|
||||||
|
if (config.localization) {
|
||||||
|
locales = config.localization.locales.map(({ code }) => code)
|
||||||
|
}
|
||||||
|
|
||||||
|
await locales.reduce(async (previousPromise, locale: string | undefined, i) => {
|
||||||
|
await previousPromise
|
||||||
|
const operation = i === 0 ? 'create' : 'update'
|
||||||
|
|
||||||
|
const originalDoc = await afterRead({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
depth: 0,
|
||||||
|
doc: docWithLocales,
|
||||||
|
fallbackLocale: null,
|
||||||
|
global: null,
|
||||||
|
locale,
|
||||||
|
overrideAccess: true,
|
||||||
|
req,
|
||||||
|
showHiddenFields: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Create Access
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
if (operation === 'create' && !overrideAccess) {
|
||||||
|
await executeAccess({ data: originalDoc, req }, collectionConfig.access.create)
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// beforeValidate - Fields
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
let data = await beforeValidate<DeepPartial<GeneratedTypes['collections'][TSlug]>>({
|
||||||
|
id,
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
data: originalDoc,
|
||||||
|
doc: originalDoc,
|
||||||
|
duplicate: true,
|
||||||
|
global: null,
|
||||||
|
operation,
|
||||||
|
overrideAccess,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// beforeValidate - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
data =
|
||||||
|
(await hook({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
data,
|
||||||
|
operation,
|
||||||
|
originalDoc,
|
||||||
|
req,
|
||||||
|
})) || data
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// beforeChange - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
data =
|
||||||
|
(await hook({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
data,
|
||||||
|
operation,
|
||||||
|
originalDoc,
|
||||||
|
req,
|
||||||
|
})) || data
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// beforeChange - Fields
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
const result = await beforeChange<GeneratedTypes['collections'][TSlug]>({
|
||||||
|
id,
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
data,
|
||||||
|
doc: originalDoc,
|
||||||
|
docWithLocales,
|
||||||
|
global: null,
|
||||||
|
operation,
|
||||||
|
req,
|
||||||
|
skipValidation: shouldSaveDraft,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Handle potential password update
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
// const dataToUpdate: Record<string, unknown> = { ...result }
|
||||||
|
|
||||||
|
// if (shouldSavePassword && typeof password === 'string') {
|
||||||
|
// const { hash, salt } = await generatePasswordSaltHash({ password })
|
||||||
|
// dataToUpdate.salt = salt
|
||||||
|
// dataToUpdate.hash = hash
|
||||||
|
// delete dataToUpdate.password
|
||||||
|
// delete data.password
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Create / Update
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
if (i === 0) {
|
||||||
|
versionDoc = await payload.db.create({
|
||||||
|
collection: collectionConfig.slug,
|
||||||
|
data: result,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
versionDoc = await req.payload.db.updateOne({
|
||||||
|
id: versionDoc.id,
|
||||||
|
collection: collectionConfig.slug,
|
||||||
|
data: result,
|
||||||
|
locale,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Create version
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
let result = versionDoc
|
||||||
|
if (collectionConfig.versions) {
|
||||||
|
result = await saveVersion({
|
||||||
|
id: versionDoc.id,
|
||||||
|
collection: collectionConfig,
|
||||||
|
docWithLocales: {
|
||||||
|
...versionDoc,
|
||||||
|
createdAt: docWithLocales.createdAt,
|
||||||
|
},
|
||||||
|
draft: shouldSaveDraft,
|
||||||
|
payload,
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// afterRead - Fields
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
result = await afterRead({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
depth,
|
||||||
|
doc: versionDoc,
|
||||||
|
fallbackLocale,
|
||||||
|
global: null,
|
||||||
|
locale: req.locale,
|
||||||
|
overrideAccess,
|
||||||
|
req,
|
||||||
|
showHiddenFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// afterRead - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
result =
|
||||||
|
(await hook({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
doc: result,
|
||||||
|
req,
|
||||||
|
})) || result
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// afterChange - Fields
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
result = await afterChange<GeneratedTypes['collections'][TSlug]>({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
data: versionDoc,
|
||||||
|
doc: result,
|
||||||
|
global: null,
|
||||||
|
operation: 'create',
|
||||||
|
previousDoc: {},
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// afterChange - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
result =
|
||||||
|
(await hook({
|
||||||
|
collection: collectionConfig,
|
||||||
|
context: req.context,
|
||||||
|
doc: result,
|
||||||
|
operation: 'create',
|
||||||
|
previousDoc: {},
|
||||||
|
req,
|
||||||
|
})) || result
|
||||||
|
}, Promise.resolve())
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// afterOperation - Collection
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
result = await buildAfterOperation<GeneratedTypes['collections'][TSlug]>({
|
||||||
|
args,
|
||||||
|
collection: collectionConfig,
|
||||||
|
operation: 'create',
|
||||||
|
result,
|
||||||
|
})
|
||||||
|
|
||||||
|
// /////////////////////////////////////
|
||||||
|
// Return results
|
||||||
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
if (shouldCommit) await commitTransaction(req)
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (error: unknown) {
|
||||||
|
await killTransaction(args.req)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import type { GeneratedTypes } from '../../..//index.js'
|
||||||
|
import type { Payload } from '../../../index.js'
|
||||||
|
import type { Document, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||||
|
|
||||||
|
import { APIError } from '../../../errors/index.js'
|
||||||
|
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||||
|
import { duplicateOperation } from '../duplicate.js'
|
||||||
|
|
||||||
|
export type Options<TSlug extends keyof GeneratedTypes['collections']> = {
|
||||||
|
collection: TSlug
|
||||||
|
/**
|
||||||
|
* context, which will then be passed to req.context, which can be read by hooks
|
||||||
|
*/
|
||||||
|
context?: RequestContext
|
||||||
|
depth?: number
|
||||||
|
draft?: boolean
|
||||||
|
fallbackLocale?: string
|
||||||
|
id: number | string
|
||||||
|
locale?: string
|
||||||
|
overrideAccess?: boolean
|
||||||
|
req?: PayloadRequest
|
||||||
|
showHiddenFields?: boolean
|
||||||
|
user?: Document
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function duplicate<TSlug extends keyof GeneratedTypes['collections']>(
|
||||||
|
payload: Payload,
|
||||||
|
options: Options<TSlug>,
|
||||||
|
): Promise<GeneratedTypes['collections'][TSlug]> {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
collection: collectionSlug,
|
||||||
|
depth,
|
||||||
|
draft,
|
||||||
|
overrideAccess = true,
|
||||||
|
showHiddenFields,
|
||||||
|
} = options
|
||||||
|
const collection = payload.collections[collectionSlug]
|
||||||
|
|
||||||
|
if (!collection) {
|
||||||
|
throw new APIError(
|
||||||
|
`The collection with slug ${String(collectionSlug)} can't be found. Duplicate Operation.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = await createLocalReq(options, payload)
|
||||||
|
|
||||||
|
return duplicateOperation<TSlug>({
|
||||||
|
id,
|
||||||
|
collection,
|
||||||
|
depth,
|
||||||
|
draft,
|
||||||
|
overrideAccess,
|
||||||
|
req,
|
||||||
|
showHiddenFields,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import auth from '../../../auth/operations/local/index.js'
|
import auth from '../../../auth/operations/local/index.js'
|
||||||
import create from './create.js'
|
import create from './create.js'
|
||||||
import deleteLocal from './delete.js'
|
import deleteLocal from './delete.js'
|
||||||
|
import { duplicate } from './duplicate.js'
|
||||||
import find from './find.js'
|
import find from './find.js'
|
||||||
import findByID from './findByID.js'
|
import findByID from './findByID.js'
|
||||||
import findVersionByID from './findVersionByID.js'
|
import findVersionByID from './findVersionByID.js'
|
||||||
@@ -12,6 +13,7 @@ export default {
|
|||||||
auth,
|
auth,
|
||||||
create,
|
create,
|
||||||
deleteLocal,
|
deleteLocal,
|
||||||
|
duplicate,
|
||||||
find,
|
find,
|
||||||
findByID,
|
findByID,
|
||||||
findVersionByID,
|
findVersionByID,
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export type ServerOnlyCollectionProperties = keyof Pick<
|
|||||||
|
|
||||||
export type ServerOnlyCollectionAdminProperties = keyof Pick<
|
export type ServerOnlyCollectionAdminProperties = keyof Pick<
|
||||||
SanitizedCollectionConfig['admin'],
|
SanitizedCollectionConfig['admin'],
|
||||||
'components' | 'hidden' | 'hooks' | 'preview'
|
'components' | 'hidden' | 'preview'
|
||||||
>
|
>
|
||||||
|
|
||||||
export type ServerOnlyGlobalProperties = keyof Pick<
|
export type ServerOnlyGlobalProperties = keyof Pick<
|
||||||
@@ -193,7 +193,6 @@ const sanitizeCollections = (
|
|||||||
'components',
|
'components',
|
||||||
'hidden',
|
'hidden',
|
||||||
'preview',
|
'preview',
|
||||||
'hooks',
|
|
||||||
// `livePreview` is handled separately
|
// `livePreview` is handled separately
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export { createOperation } from '../collections/operations/create.js'
|
|||||||
export { deleteOperation } from '../collections/operations/delete.js'
|
export { deleteOperation } from '../collections/operations/delete.js'
|
||||||
export { deleteByIDOperation } from '../collections/operations/deleteByID.js'
|
export { deleteByIDOperation } from '../collections/operations/deleteByID.js'
|
||||||
export { docAccessOperation } from '../collections/operations/docAccess.js'
|
export { docAccessOperation } from '../collections/operations/docAccess.js'
|
||||||
|
export { duplicateOperation } from '../collections/operations/duplicate.js'
|
||||||
export { findOperation } from '../collections/operations/find.js'
|
export { findOperation } from '../collections/operations/find.js'
|
||||||
export { findByIDOperation } from '../collections/operations/findByID.js'
|
export { findByIDOperation } from '../collections/operations/findByID.js'
|
||||||
export { findVersionByIDOperation } from '../collections/operations/findVersionByID.js'
|
export { findVersionByIDOperation } from '../collections/operations/findVersionByID.js'
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export type {
|
|||||||
AfterReadHook as CollectionAfterReadHook,
|
AfterReadHook as CollectionAfterReadHook,
|
||||||
BeforeChangeHook as CollectionBeforeChangeHook,
|
BeforeChangeHook as CollectionBeforeChangeHook,
|
||||||
BeforeDeleteHook as CollectionBeforeDeleteHook,
|
BeforeDeleteHook as CollectionBeforeDeleteHook,
|
||||||
BeforeDuplicate,
|
|
||||||
BeforeLoginHook as CollectionBeforeLoginHook,
|
BeforeLoginHook as CollectionBeforeLoginHook,
|
||||||
BeforeOperationHook as CollectionBeforeOperationHook,
|
BeforeOperationHook as CollectionBeforeOperationHook,
|
||||||
BeforeReadHook as CollectionBeforeReadHook,
|
BeforeReadHook as CollectionBeforeReadHook,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { formatLabels, toWords } from '../../utilities/formatLabels.js'
|
import { formatLabels, toWords } from '../../utilities/formatLabels.js'
|
||||||
import { baseBlockFields } from '../baseFields/baseBlockFields.js'
|
import { baseBlockFields } from '../baseFields/baseBlockFields.js'
|
||||||
import { baseIDField } from '../baseFields/baseIDField.js'
|
import { baseIDField } from '../baseFields/baseIDField.js'
|
||||||
|
import { setDefaultBeforeDuplicate } from '../setDefaultBeforeDuplicate.js'
|
||||||
import validations from '../validations.js'
|
import validations from '../validations.js'
|
||||||
import { fieldAffectsData, tabHasName } from './types.js'
|
import { fieldAffectsData, tabHasName } from './types.js'
|
||||||
|
|
||||||
@@ -129,6 +130,8 @@ export const sanitizeFields = ({
|
|||||||
|
|
||||||
if (!field.hooks) field.hooks = {}
|
if (!field.hooks) field.hooks = {}
|
||||||
if (!field.access) field.access = {}
|
if (!field.access) field.access = {}
|
||||||
|
|
||||||
|
setDefaultBeforeDuplicate(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!field.admin) {
|
if (!field.admin) {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export const baseField = joi
|
|||||||
afterChange: joi.array().items(joi.func()).default([]),
|
afterChange: joi.array().items(joi.func()).default([]),
|
||||||
afterRead: joi.array().items(joi.func()).default([]),
|
afterRead: joi.array().items(joi.func()).default([]),
|
||||||
beforeChange: joi.array().items(joi.func()).default([]),
|
beforeChange: joi.array().items(joi.func()).default([]),
|
||||||
|
beforeDuplicate: joi.array().items(joi.func()).default([]),
|
||||||
beforeValidate: joi.array().items(joi.func()).default([]),
|
beforeValidate: joi.array().items(joi.func()).default([]),
|
||||||
})
|
})
|
||||||
.default(),
|
.default(),
|
||||||
|
|||||||
@@ -183,6 +183,10 @@ export interface FieldBase {
|
|||||||
afterChange?: FieldHook[]
|
afterChange?: FieldHook[]
|
||||||
afterRead?: FieldHook[]
|
afterRead?: FieldHook[]
|
||||||
beforeChange?: FieldHook[]
|
beforeChange?: FieldHook[]
|
||||||
|
/**
|
||||||
|
* Runs before a document is duplicated to prevent errors in unique fields or return null to use defaultValue.
|
||||||
|
*/
|
||||||
|
beforeDuplicate?: FieldHook[]
|
||||||
beforeValidate?: FieldHook[]
|
beforeValidate?: FieldHook[]
|
||||||
}
|
}
|
||||||
index?: boolean
|
index?: boolean
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Args<T> = {
|
|||||||
context: RequestContext
|
context: RequestContext
|
||||||
data: Record<string, unknown> | T
|
data: Record<string, unknown> | T
|
||||||
doc?: Record<string, unknown> | T
|
doc?: Record<string, unknown> | T
|
||||||
|
duplicate?: boolean
|
||||||
global: SanitizedGlobalConfig | null
|
global: SanitizedGlobalConfig | null
|
||||||
id?: number | string
|
id?: number | string
|
||||||
operation: 'create' | 'update'
|
operation: 'create' | 'update'
|
||||||
@@ -23,6 +24,7 @@ export const beforeValidate = async <T extends Record<string, unknown>>({
|
|||||||
context,
|
context,
|
||||||
data: incomingData,
|
data: incomingData,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate = false,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
overrideAccess,
|
overrideAccess,
|
||||||
@@ -36,6 +38,7 @@ export const beforeValidate = async <T extends Record<string, unknown>>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: collection?.fields || global?.fields,
|
fields: collection?.fields || global?.fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type Args<T> = {
|
|||||||
context: RequestContext
|
context: RequestContext
|
||||||
data: T
|
data: T
|
||||||
doc: T
|
doc: T
|
||||||
|
duplicate: boolean
|
||||||
field: Field | TabAsField
|
field: Field | TabAsField
|
||||||
global: SanitizedGlobalConfig | null
|
global: SanitizedGlobalConfig | null
|
||||||
id?: number | string
|
id?: number | string
|
||||||
@@ -38,6 +39,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
field,
|
field,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -259,6 +261,32 @@ export const promise = async <T>({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute beforeDuplicate hook
|
||||||
|
if (duplicate && field.hooks?.beforeDuplicate) {
|
||||||
|
if (field.hooks?.beforeDuplicate) {
|
||||||
|
await field.hooks.beforeDuplicate.reduce(async (priorHook, currentHook) => {
|
||||||
|
await priorHook
|
||||||
|
|
||||||
|
const hookedValue = await currentHook({
|
||||||
|
collection,
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
field,
|
||||||
|
global,
|
||||||
|
operation,
|
||||||
|
originalDoc: doc,
|
||||||
|
req,
|
||||||
|
siblingData,
|
||||||
|
value: siblingData[field.name],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (hookedValue !== undefined) {
|
||||||
|
siblingData[field.name] = hookedValue
|
||||||
|
}
|
||||||
|
}, Promise.resolve())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traverse subfields
|
// Traverse subfields
|
||||||
@@ -276,6 +304,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: field.fields,
|
fields: field.fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -301,6 +330,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: field.fields,
|
fields: field.fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -336,6 +366,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: block.fields,
|
fields: block.fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -361,6 +392,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: field.fields,
|
fields: field.fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -393,6 +425,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: field.fields,
|
fields: field.fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -412,6 +445,7 @@ export const promise = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ type Args<T> = {
|
|||||||
context: RequestContext
|
context: RequestContext
|
||||||
data: T
|
data: T
|
||||||
doc: T
|
doc: T
|
||||||
|
duplicate: boolean
|
||||||
fields: (Field | TabAsField)[]
|
fields: (Field | TabAsField)[]
|
||||||
global: SanitizedGlobalConfig | null
|
global: SanitizedGlobalConfig | null
|
||||||
id?: number | string
|
id?: number | string
|
||||||
@@ -26,6 +27,7 @@ export const traverseFields = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
fields,
|
fields,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
@@ -43,6 +45,7 @@ export const traverseFields = async <T>({
|
|||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
doc,
|
doc,
|
||||||
|
duplicate,
|
||||||
field,
|
field,
|
||||||
global,
|
global,
|
||||||
operation,
|
operation,
|
||||||
|
|||||||
33
packages/payload/src/fields/setDefaultBeforeDuplicate.ts
Normal file
33
packages/payload/src/fields/setDefaultBeforeDuplicate.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { extractTranslations } from '../translations/extractTranslations.js'
|
||||||
|
|
||||||
|
const copyTranslations = extractTranslations(['general:copy'])
|
||||||
|
|
||||||
|
// default beforeDuplicate hook for required and unique fields
|
||||||
|
import type { FieldAffectingData, FieldHook } from './config/types.js'
|
||||||
|
|
||||||
|
const unique: FieldHook = ({ value }) => (typeof value === 'string' ? `${value} - Copy` : undefined)
|
||||||
|
const localizedUnique: FieldHook = ({ req, value}) =>
|
||||||
|
(value ? `${value} - ${copyTranslations?.[req.locale]?.['general:copy'] ?? 'Copy'}` : undefined)
|
||||||
|
const uniqueRequired: FieldHook = ({ value }) => (`${value} - Copy`)
|
||||||
|
const localizedUniqueRequired: FieldHook = ({ req, value }) => (`${value} - ${copyTranslations?.[req.locale]?.['general:copy'] ?? 'Copy'}`)
|
||||||
|
|
||||||
|
export const setDefaultBeforeDuplicate = (field: FieldAffectingData) => {
|
||||||
|
if (
|
||||||
|
(('required' in field && field.required) || field.unique) &&
|
||||||
|
(!field.hooks?.beforeDuplicate || Array.isArray(field.hooks.beforeDuplicate) && field.hooks.beforeDuplicate.length === 0)
|
||||||
|
) {
|
||||||
|
if ((field.type === 'text' || field.type === 'textarea') && field.required && field.unique) {
|
||||||
|
field.hooks.beforeDuplicate = [
|
||||||
|
field.localized
|
||||||
|
? localizedUniqueRequired
|
||||||
|
: uniqueRequired
|
||||||
|
]
|
||||||
|
} else if (field.unique) {
|
||||||
|
field.hooks.beforeDuplicate = [
|
||||||
|
field.localized
|
||||||
|
? localizedUnique
|
||||||
|
: unique
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import type { Options as FindByIDOptions } from './collections/operations/local/
|
|||||||
import type { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID.js'
|
import type { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID.js'
|
||||||
import type { Options as FindVersionsOptions } from './collections/operations/local/findVersions.js'
|
import type { Options as FindVersionsOptions } from './collections/operations/local/findVersions.js'
|
||||||
import type { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion.js'
|
import type { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion.js'
|
||||||
|
import type { Options as DuplicateOptions } from './collections/operations/local/duplicate.js'
|
||||||
import type {
|
import type {
|
||||||
ByIDOptions as UpdateByIDOptions,
|
ByIDOptions as UpdateByIDOptions,
|
||||||
ManyOptions as UpdateManyOptions,
|
ManyOptions as UpdateManyOptions,
|
||||||
@@ -411,6 +412,13 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
|||||||
const { update } = localOperations
|
const { update } = localOperations
|
||||||
return update<T>(this, options)
|
return update<T>(this, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
duplicate = async <T extends keyof TGeneratedTypes['collections']>(
|
||||||
|
options: DuplicateOptions<T>,
|
||||||
|
): Promise<TGeneratedTypes['collections'][T]> => {
|
||||||
|
const { duplicate } = localOperations
|
||||||
|
return duplicate<T>(this, options)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialized = new BasePayload()
|
const initialized = new BasePayload()
|
||||||
|
|||||||
@@ -38,12 +38,14 @@ export default {
|
|||||||
textToDisplay: 'النصّ الذي تريد إظهاره',
|
textToDisplay: 'النصّ الذي تريد إظهاره',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'نسخ',
|
||||||
createdAt: 'تمّ الإنشاء في',
|
createdAt: 'تمّ الإنشاء في',
|
||||||
deletedCountSuccessfully: 'تمّ حذف {{count}} {{label}} بنجاح.',
|
deletedCountSuccessfully: 'تمّ حذف {{count}} {{label}} بنجاح.',
|
||||||
deletedSuccessfully: 'تمّ الحذف بنجاح.',
|
deletedSuccessfully: 'تمّ الحذف بنجاح.',
|
||||||
email: 'البريد الإلكتروني',
|
email: 'البريد الإلكتروني',
|
||||||
notFound: 'غير موجود',
|
notFound: 'غير موجود',
|
||||||
successfullyCreated: '{{label}} تم إنشاؤها بنجاح.',
|
successfullyCreated: '{{label}} تم إنشاؤها بنجاح.',
|
||||||
|
successfullyDuplicated: '{{label}} تم استنساخها بنجاح.',
|
||||||
thisLanguage: 'العربية',
|
thisLanguage: 'العربية',
|
||||||
updatedAt: 'تم التحديث في',
|
updatedAt: 'تم التحديث في',
|
||||||
updatedCountSuccessfully: 'تم تحديث {{count}} {{label}} بنجاح.',
|
updatedCountSuccessfully: 'تم تحديث {{count}} {{label}} بنجاح.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Göstəriləcək mətn',
|
textToDisplay: 'Göstəriləcək mətn',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopyala',
|
||||||
createdAt: 'Yaradıldığı tarix',
|
createdAt: 'Yaradıldığı tarix',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} uğurla silindi.',
|
deletedCountSuccessfully: '{{count}} {{label}} uğurla silindi.',
|
||||||
deletedSuccessfully: 'Uğurla silindi.',
|
deletedSuccessfully: 'Uğurla silindi.',
|
||||||
email: 'Elektron poçt',
|
email: 'Elektron poçt',
|
||||||
notFound: 'Tapılmadı',
|
notFound: 'Tapılmadı',
|
||||||
successfullyCreated: '{{label}} uğurla yaradıldı.',
|
successfullyCreated: '{{label}} uğurla yaradıldı.',
|
||||||
|
successfullyDuplicated: '{{label}} uğurla dublikatlandı.',
|
||||||
thisLanguage: 'Azərbaycan dili',
|
thisLanguage: 'Azərbaycan dili',
|
||||||
updatedAt: 'Yeniləndiyi tarix',
|
updatedAt: 'Yeniləndiyi tarix',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} uğurla yeniləndi.',
|
updatedCountSuccessfully: '{{count}} {{label}} uğurla yeniləndi.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Текст към дисплей',
|
textToDisplay: 'Текст към дисплей',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Копирай',
|
||||||
createdAt: 'Създаден на',
|
createdAt: 'Създаден на',
|
||||||
deletedCountSuccessfully: 'Изтрити {{count}} {{label}} успешно.',
|
deletedCountSuccessfully: 'Изтрити {{count}} {{label}} успешно.',
|
||||||
deletedSuccessfully: 'Изтрито успешно.',
|
deletedSuccessfully: 'Изтрито успешно.',
|
||||||
email: 'Имейл',
|
email: 'Имейл',
|
||||||
notFound: 'Няма открит',
|
notFound: 'Няма открит',
|
||||||
successfullyCreated: '{{label}} успешно създаден.',
|
successfullyCreated: '{{label}} успешно създаден.',
|
||||||
|
successfullyDuplicated: '{{label}} успешно дупликиран.',
|
||||||
thisLanguage: 'Български',
|
thisLanguage: 'Български',
|
||||||
updatedAt: 'Обновен на',
|
updatedAt: 'Обновен на',
|
||||||
updatedCountSuccessfully: 'Обновени {{count}} {{label}} успешно.',
|
updatedCountSuccessfully: 'Обновени {{count}} {{label}} успешно.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Text k zobrazení',
|
textToDisplay: 'Text k zobrazení',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopírovat',
|
||||||
createdAt: 'Vytvořeno v',
|
createdAt: 'Vytvořeno v',
|
||||||
deletedCountSuccessfully: 'Úspěšně smazáno {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Úspěšně smazáno {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Úspěšně odstraněno.',
|
deletedSuccessfully: 'Úspěšně odstraněno.',
|
||||||
email: 'E-mail',
|
email: 'E-mail',
|
||||||
notFound: 'Nenalezeno',
|
notFound: 'Nenalezeno',
|
||||||
successfullyCreated: '{{label}} úspěšně vytvořeno.',
|
successfullyCreated: '{{label}} úspěšně vytvořeno.',
|
||||||
|
successfullyDuplicated: '{{label}} úspěšně duplikováno.',
|
||||||
thisLanguage: 'Čeština',
|
thisLanguage: 'Čeština',
|
||||||
updatedAt: 'Aktualizováno v',
|
updatedAt: 'Aktualizováno v',
|
||||||
updatedCountSuccessfully: 'Úspěšně aktualizováno {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Úspěšně aktualizováno {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Angezeigter Text',
|
textToDisplay: 'Angezeigter Text',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopieren',
|
||||||
createdAt: 'Erstellt am',
|
createdAt: 'Erstellt am',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} erfolgreich gelöscht.',
|
deletedCountSuccessfully: '{{count}} {{label}} erfolgreich gelöscht.',
|
||||||
deletedSuccessfully: 'Erfolgreich gelöscht.',
|
deletedSuccessfully: 'Erfolgreich gelöscht.',
|
||||||
email: 'E-Mail',
|
email: 'E-Mail',
|
||||||
notFound: 'Nicht gefunden',
|
notFound: 'Nicht gefunden',
|
||||||
successfullyCreated: '{{label}} erfolgreich erstellt.',
|
successfullyCreated: '{{label}} erfolgreich erstellt.',
|
||||||
|
successfullyDuplicated: '{{label}} wurde erfolgreich dupliziert.',
|
||||||
thisLanguage: 'Deutsch',
|
thisLanguage: 'Deutsch',
|
||||||
updatedAt: 'Aktualisiert am',
|
updatedAt: 'Aktualisiert am',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} erfolgreich aktualisiert.',
|
updatedCountSuccessfully: '{{count}} {{label}} erfolgreich aktualisiert.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Text to display',
|
textToDisplay: 'Text to display',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Copy',
|
||||||
createdAt: 'Created At',
|
createdAt: 'Created At',
|
||||||
deletedCountSuccessfully: 'Deleted {{count}} {{label}} successfully.',
|
deletedCountSuccessfully: 'Deleted {{count}} {{label}} successfully.',
|
||||||
deletedSuccessfully: 'Deleted successfully.',
|
deletedSuccessfully: 'Deleted successfully.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Not Found',
|
notFound: 'Not Found',
|
||||||
successfullyCreated: '{{label}} successfully created.',
|
successfullyCreated: '{{label}} successfully created.',
|
||||||
|
successfullyDuplicated: '{{label}} successfully duplicated.',
|
||||||
thisLanguage: 'English',
|
thisLanguage: 'English',
|
||||||
updatedAt: 'Updated At',
|
updatedAt: 'Updated At',
|
||||||
updatedCountSuccessfully: 'Updated {{count}} {{label}} successfully.',
|
updatedCountSuccessfully: 'Updated {{count}} {{label}} successfully.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Texto a mostrar',
|
textToDisplay: 'Texto a mostrar',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Copiar',
|
||||||
createdAt: 'Fecha de creación',
|
createdAt: 'Fecha de creación',
|
||||||
deletedCountSuccessfully: 'Se eliminó {{count}} {{label}} con éxito.',
|
deletedCountSuccessfully: 'Se eliminó {{count}} {{label}} con éxito.',
|
||||||
deletedSuccessfully: 'Borrado exitosamente.',
|
deletedSuccessfully: 'Borrado exitosamente.',
|
||||||
email: 'Correo electrónico',
|
email: 'Correo electrónico',
|
||||||
notFound: 'No encontrado',
|
notFound: 'No encontrado',
|
||||||
successfullyCreated: '{{label}} creado correctamente.',
|
successfullyCreated: '{{label}} creado correctamente.',
|
||||||
|
successfullyDuplicated: '{{label}} duplicado correctamente.',
|
||||||
thisLanguage: 'Español',
|
thisLanguage: 'Español',
|
||||||
updatedAt: 'Fecha de modificado',
|
updatedAt: 'Fecha de modificado',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} actualizado con éxito.',
|
updatedCountSuccessfully: '{{count}} {{label}} actualizado con éxito.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'متن برای نمایش',
|
textToDisplay: 'متن برای نمایش',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'رونوشت',
|
||||||
createdAt: 'ساخته شده در',
|
createdAt: 'ساخته شده در',
|
||||||
deletedCountSuccessfully: 'تعداد {{count}} {{label}} با موفقیت پاک گردید.',
|
deletedCountSuccessfully: 'تعداد {{count}} {{label}} با موفقیت پاک گردید.',
|
||||||
deletedSuccessfully: 'با موفقیت حذف شد.',
|
deletedSuccessfully: 'با موفقیت حذف شد.',
|
||||||
email: 'رایانامه',
|
email: 'رایانامه',
|
||||||
notFound: 'یافت نشد',
|
notFound: 'یافت نشد',
|
||||||
successfullyCreated: '{{label}} با موفقیت ساخته شد.',
|
successfullyCreated: '{{label}} با موفقیت ساخته شد.',
|
||||||
|
successfullyDuplicated: '{{label}} با موفقیت رونوشت شد.',
|
||||||
thisLanguage: 'فارسی',
|
thisLanguage: 'فارسی',
|
||||||
updatedAt: 'بروز شده در',
|
updatedAt: 'بروز شده در',
|
||||||
updatedCountSuccessfully: 'تعداد {{count}} با عنوان {{label}} با موفقیت بروزرسانی شدند.',
|
updatedCountSuccessfully: 'تعداد {{count}} با عنوان {{label}} با موفقیت بروزرسانی شدند.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Texte à afficher',
|
textToDisplay: 'Texte à afficher',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Copie',
|
||||||
createdAt: 'Créé(e) à',
|
createdAt: 'Créé(e) à',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} supprimé avec succès.',
|
deletedCountSuccessfully: '{{count}} {{label}} supprimé avec succès.',
|
||||||
deletedSuccessfully: 'Supprimé(e) avec succès.',
|
deletedSuccessfully: 'Supprimé(e) avec succès.',
|
||||||
email: 'E-mail',
|
email: 'E-mail',
|
||||||
notFound: 'Pas trouvé',
|
notFound: 'Pas trouvé',
|
||||||
successfullyCreated: '{{label}} créé(e) avec succès.',
|
successfullyCreated: '{{label}} créé(e) avec succès.',
|
||||||
|
successfullyDuplicated: '{{label}} dupliqué(e) avec succès.',
|
||||||
thisLanguage: 'Français',
|
thisLanguage: 'Français',
|
||||||
updatedAt: 'Modifié le',
|
updatedAt: 'Modifié le',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} mis à jour avec succès.',
|
updatedCountSuccessfully: '{{count}} {{label}} mis à jour avec succès.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Tekst za prikaz',
|
textToDisplay: 'Tekst za prikaz',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopiraj',
|
||||||
createdAt: 'Kreirano u',
|
createdAt: 'Kreirano u',
|
||||||
deletedCountSuccessfully: 'Uspješno izbrisano {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Uspješno izbrisano {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Uspješno obrisano.',
|
deletedSuccessfully: 'Uspješno obrisano.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Nije pronađeno',
|
notFound: 'Nije pronađeno',
|
||||||
successfullyCreated: '{{label}} uspješno kreirano.',
|
successfullyCreated: '{{label}} uspješno kreirano.',
|
||||||
|
successfullyDuplicated: '{{label}} uspješno duplicirano.',
|
||||||
thisLanguage: 'Hrvatski',
|
thisLanguage: 'Hrvatski',
|
||||||
updatedAt: 'Ažurirano u',
|
updatedAt: 'Ažurirano u',
|
||||||
updatedCountSuccessfully: 'Uspješno ažurirano {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Uspješno ažurirano {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Megjelenítendő szöveg',
|
textToDisplay: 'Megjelenítendő szöveg',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Másolás',
|
||||||
createdAt: 'Létrehozva:',
|
createdAt: 'Létrehozva:',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} sikeresen törölve.',
|
deletedCountSuccessfully: '{{count}} {{label}} sikeresen törölve.',
|
||||||
deletedSuccessfully: 'Sikeresen törölve.',
|
deletedSuccessfully: 'Sikeresen törölve.',
|
||||||
email: 'E-mail',
|
email: 'E-mail',
|
||||||
notFound: 'Nem található',
|
notFound: 'Nem található',
|
||||||
successfullyCreated: '{{label}} sikeresen létrehozva.',
|
successfullyCreated: '{{label}} sikeresen létrehozva.',
|
||||||
|
successfullyDuplicated: '{{label}} sikeresen duplikálódott.',
|
||||||
thisLanguage: 'Magyar',
|
thisLanguage: 'Magyar',
|
||||||
updatedAt: 'Frissítve:',
|
updatedAt: 'Frissítve:',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} sikeresen frissítve.',
|
updatedCountSuccessfully: '{{count}} {{label}} sikeresen frissítve.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Testo da visualizzare',
|
textToDisplay: 'Testo da visualizzare',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Copia',
|
||||||
createdAt: 'Creato il',
|
createdAt: 'Creato il',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} eliminato con successo.',
|
deletedCountSuccessfully: '{{count}} {{label}} eliminato con successo.',
|
||||||
deletedSuccessfully: 'Eliminato con successo.',
|
deletedSuccessfully: 'Eliminato con successo.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Non Trovato',
|
notFound: 'Non Trovato',
|
||||||
successfullyCreated: '{{label}} creato con successo.',
|
successfullyCreated: '{{label}} creato con successo.',
|
||||||
|
successfullyDuplicated: '{{label}} duplicato con successo.',
|
||||||
thisLanguage: 'Italiano',
|
thisLanguage: 'Italiano',
|
||||||
updatedAt: 'Aggiornato il',
|
updatedAt: 'Aggiornato il',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} aggiornato con successo.',
|
updatedCountSuccessfully: '{{count}} {{label}} aggiornato con successo.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: '表示するテキスト',
|
textToDisplay: '表示するテキスト',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'コピー',
|
||||||
createdAt: '作成日',
|
createdAt: '作成日',
|
||||||
deletedCountSuccessfully: '{{count}}つの{{label}}を正常に削除しました。',
|
deletedCountSuccessfully: '{{count}}つの{{label}}を正常に削除しました。',
|
||||||
deletedSuccessfully: '正常に削除されました。',
|
deletedSuccessfully: '正常に削除されました。',
|
||||||
email: 'メールアドレス',
|
email: 'メールアドレス',
|
||||||
notFound: 'Not Found',
|
notFound: 'Not Found',
|
||||||
successfullyCreated: '{{label}} が作成されました。',
|
successfullyCreated: '{{label}} が作成されました。',
|
||||||
|
successfullyDuplicated: '{{label}} が複製されました。',
|
||||||
thisLanguage: 'Japanese',
|
thisLanguage: 'Japanese',
|
||||||
updatedAt: '更新日',
|
updatedAt: '更新日',
|
||||||
updatedCountSuccessfully: '{{count}}つの{{label}}を正常に更新しました。',
|
updatedCountSuccessfully: '{{count}}つの{{label}}を正常に更新しました。',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: '표시할 텍스트',
|
textToDisplay: '표시할 텍스트',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: '복사',
|
||||||
createdAt: '생성 일시',
|
createdAt: '생성 일시',
|
||||||
deletedCountSuccessfully: '{{count}}개의 {{label}}를 삭제했습니다.',
|
deletedCountSuccessfully: '{{count}}개의 {{label}}를 삭제했습니다.',
|
||||||
deletedSuccessfully: '삭제되었습니다.',
|
deletedSuccessfully: '삭제되었습니다.',
|
||||||
email: '이메일',
|
email: '이메일',
|
||||||
notFound: '찾을 수 없음',
|
notFound: '찾을 수 없음',
|
||||||
successfullyCreated: '{{label}}이(가) 생성되었습니다.',
|
successfullyCreated: '{{label}}이(가) 생성되었습니다.',
|
||||||
|
successfullyDuplicated: '{{label}}이(가) 복제되었습니다.',
|
||||||
thisLanguage: '한국어',
|
thisLanguage: '한국어',
|
||||||
updatedAt: '업데이트 일시',
|
updatedAt: '업데이트 일시',
|
||||||
updatedCountSuccessfully: '{{count}}개의 {{label}}을(를) 업데이트했습니다.',
|
updatedCountSuccessfully: '{{count}}개의 {{label}}을(를) 업데이트했습니다.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'ပြသရန် စာသား',
|
textToDisplay: 'ပြသရန် စာသား',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'ကူးယူမည်။',
|
||||||
createdAt: 'ဖန်တီးခဲ့သည့်အချိန်',
|
createdAt: 'ဖန်တီးခဲ့သည့်အချိန်',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} ကို အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။',
|
deletedCountSuccessfully: '{{count}} {{label}} ကို အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။',
|
||||||
deletedSuccessfully: 'အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။',
|
deletedSuccessfully: 'အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။',
|
||||||
email: 'အီးမေးလ်',
|
email: 'အီးမေးလ်',
|
||||||
notFound: 'ဘာမှ မရှိတော့ဘူး။',
|
notFound: 'ဘာမှ မရှိတော့ဘူး။',
|
||||||
successfullyCreated: '{{label}} အောင်မြင်စွာဖန်တီးခဲ့သည်။',
|
successfullyCreated: '{{label}} အောင်မြင်စွာဖန်တီးခဲ့သည်။',
|
||||||
|
successfullyDuplicated: '{{label}} အောင်မြင်စွာ ပုံတူပွားခဲ့သည်။',
|
||||||
thisLanguage: 'မြန်မာစာ',
|
thisLanguage: 'မြန်မာစာ',
|
||||||
updatedAt: 'ပြင်ဆင်ခဲ့သည့်အချိန်',
|
updatedAt: 'ပြင်ဆင်ခဲ့သည့်အချိန်',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} ကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ခဲ့သည်။',
|
updatedCountSuccessfully: '{{count}} {{label}} ကို အောင်မြင်စွာ အပ်ဒိတ်လုပ်ခဲ့သည်။',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Tekst som skal vises',
|
textToDisplay: 'Tekst som skal vises',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopiér',
|
||||||
createdAt: 'Opprettet',
|
createdAt: 'Opprettet',
|
||||||
deletedCountSuccessfully: 'Slettet {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Slettet {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Slettet.',
|
deletedSuccessfully: 'Slettet.',
|
||||||
email: 'E-post',
|
email: 'E-post',
|
||||||
notFound: 'Ikke funnet',
|
notFound: 'Ikke funnet',
|
||||||
successfullyCreated: '{{label}} ble opprettet.',
|
successfullyCreated: '{{label}} ble opprettet.',
|
||||||
|
successfullyDuplicated: '{{label}} ble duplisert.',
|
||||||
thisLanguage: 'Norsk',
|
thisLanguage: 'Norsk',
|
||||||
updatedAt: 'Oppdatert',
|
updatedAt: 'Oppdatert',
|
||||||
updatedCountSuccessfully: 'Oppdaterte {{count}} {{label}} vellykket.',
|
updatedCountSuccessfully: 'Oppdaterte {{count}} {{label}} vellykket.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Tekst om weer te geven',
|
textToDisplay: 'Tekst om weer te geven',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopiëren',
|
||||||
createdAt: 'Aangemaakt op',
|
createdAt: 'Aangemaakt op',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} succesvol verwijderd.',
|
deletedCountSuccessfully: '{{count}} {{label}} succesvol verwijderd.',
|
||||||
deletedSuccessfully: 'Succesvol verwijderd.',
|
deletedSuccessfully: 'Succesvol verwijderd.',
|
||||||
email: 'E-mail',
|
email: 'E-mail',
|
||||||
notFound: 'Niet gevonden',
|
notFound: 'Niet gevonden',
|
||||||
successfullyCreated: '{{label}} succesvol aangemaakt.',
|
successfullyCreated: '{{label}} succesvol aangemaakt.',
|
||||||
|
successfullyDuplicated: '{{label}} succesvol gedupliceerd.',
|
||||||
thisLanguage: 'Nederlands',
|
thisLanguage: 'Nederlands',
|
||||||
updatedAt: 'Aangepast op',
|
updatedAt: 'Aangepast op',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} succesvol bijgewerkt.',
|
updatedCountSuccessfully: '{{count}} {{label}} succesvol bijgewerkt.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Tekst do wyświetlenia',
|
textToDisplay: 'Tekst do wyświetlenia',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Skopiuj',
|
||||||
createdAt: 'Data utworzenia',
|
createdAt: 'Data utworzenia',
|
||||||
deletedCountSuccessfully: 'Pomyślnie usunięto {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Pomyślnie usunięto {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Pomyślnie usunięto.',
|
deletedSuccessfully: 'Pomyślnie usunięto.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Nie znaleziono',
|
notFound: 'Nie znaleziono',
|
||||||
successfullyCreated: 'Pomyślnie utworzono {{label}}.',
|
successfullyCreated: 'Pomyślnie utworzono {{label}}.',
|
||||||
|
successfullyDuplicated: 'Pomyślnie zduplikowano {{label}}',
|
||||||
thisLanguage: 'Polski',
|
thisLanguage: 'Polski',
|
||||||
updatedAt: 'Data edycji',
|
updatedAt: 'Data edycji',
|
||||||
updatedCountSuccessfully: 'Pomyślnie zaktualizowano {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Pomyślnie zaktualizowano {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Texto a ser exibido',
|
textToDisplay: 'Texto a ser exibido',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Copiar',
|
||||||
createdAt: 'Criado Em',
|
createdAt: 'Criado Em',
|
||||||
deletedCountSuccessfully: 'Excluído {{count}} {{label}} com sucesso.',
|
deletedCountSuccessfully: 'Excluído {{count}} {{label}} com sucesso.',
|
||||||
deletedSuccessfully: 'Apagado com sucesso.',
|
deletedSuccessfully: 'Apagado com sucesso.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Não Encontrado',
|
notFound: 'Não Encontrado',
|
||||||
successfullyCreated: '{{label}} criado com sucesso.',
|
successfullyCreated: '{{label}} criado com sucesso.',
|
||||||
|
successfullyDuplicated: '{{label}} duplicado com sucesso.',
|
||||||
thisLanguage: 'Português',
|
thisLanguage: 'Português',
|
||||||
updatedAt: 'Atualizado Em',
|
updatedAt: 'Atualizado Em',
|
||||||
updatedCountSuccessfully: 'Atualizado {{count}} {{label}} com sucesso.',
|
updatedCountSuccessfully: 'Atualizado {{count}} {{label}} com sucesso.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Text de afișat',
|
textToDisplay: 'Text de afișat',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Copiați',
|
||||||
createdAt: 'Creat la',
|
createdAt: 'Creat la',
|
||||||
deletedCountSuccessfully: 'Șterse cu succes {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Șterse cu succes {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Șters cu succes.',
|
deletedSuccessfully: 'Șters cu succes.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Nu a fost găsit',
|
notFound: 'Nu a fost găsit',
|
||||||
successfullyCreated: '{{label}} creat(ă) cu succes.',
|
successfullyCreated: '{{label}} creat(ă) cu succes.',
|
||||||
|
successfullyDuplicated: '{{label}} duplicat(ă) cu succes.',
|
||||||
thisLanguage: 'Română',
|
thisLanguage: 'Română',
|
||||||
updatedAt: 'Actualizat la',
|
updatedAt: 'Actualizat la',
|
||||||
updatedCountSuccessfully: 'Actualizate {{count}} {{label}} cu succes.',
|
updatedCountSuccessfully: 'Actualizate {{count}} {{label}} cu succes.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Tekst za prikaz',
|
textToDisplay: 'Tekst za prikaz',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopiraj',
|
||||||
createdAt: 'Kreirano u',
|
createdAt: 'Kreirano u',
|
||||||
deletedCountSuccessfully: 'Uspešno izbrisano {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Uspešno izbrisano {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Uspešno izbrisano.',
|
deletedSuccessfully: 'Uspešno izbrisano.',
|
||||||
email: 'E-pošta',
|
email: 'E-pošta',
|
||||||
notFound: 'Nije pronađeno',
|
notFound: 'Nije pronađeno',
|
||||||
successfullyCreated: '{{label}} uspešno kreirano.',
|
successfullyCreated: '{{label}} uspešno kreirano.',
|
||||||
|
successfullyDuplicated: '{{label}} uspešno duplicirano.',
|
||||||
thisLanguage: 'Srpski (latinica)',
|
thisLanguage: 'Srpski (latinica)',
|
||||||
updatedAt: 'Ažurirano u',
|
updatedAt: 'Ažurirano u',
|
||||||
updatedCountSuccessfully: 'Uspešno ažurirano {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Uspešno ažurirano {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Текст за приказ',
|
textToDisplay: 'Текст за приказ',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Копирај',
|
||||||
createdAt: 'Креирано у',
|
createdAt: 'Креирано у',
|
||||||
deletedCountSuccessfully: 'Успешно избрисано {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Успешно избрисано {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Успешно избрисано.',
|
deletedSuccessfully: 'Успешно избрисано.',
|
||||||
email: 'Е-пошта',
|
email: 'Е-пошта',
|
||||||
notFound: 'Није пронађено',
|
notFound: 'Није пронађено',
|
||||||
successfullyCreated: '{{label}} успешно креирано.',
|
successfullyCreated: '{{label}} успешно креирано.',
|
||||||
|
successfullyDuplicated: '{{label}} успешно дуплицирано.',
|
||||||
thisLanguage: 'Српски (ћирилица)',
|
thisLanguage: 'Српски (ћирилица)',
|
||||||
updatedAt: 'Ажурирано у',
|
updatedAt: 'Ажурирано у',
|
||||||
updatedCountSuccessfully: 'Успешно ажурирано {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Успешно ажурирано {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Текст для отображения',
|
textToDisplay: 'Текст для отображения',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Скопировать',
|
||||||
createdAt: 'Дата создания',
|
createdAt: 'Дата создания',
|
||||||
deletedCountSuccessfully: 'Удалено {{count}} {{label}} успешно.',
|
deletedCountSuccessfully: 'Удалено {{count}} {{label}} успешно.',
|
||||||
deletedSuccessfully: 'Удален успешно.',
|
deletedSuccessfully: 'Удален успешно.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Не найдено',
|
notFound: 'Не найдено',
|
||||||
successfullyCreated: '{{label}} успешно создан.',
|
successfullyCreated: '{{label}} успешно создан.',
|
||||||
|
successfullyDuplicated: '{{label}} успешно продублирован.',
|
||||||
thisLanguage: 'Русский',
|
thisLanguage: 'Русский',
|
||||||
updatedAt: 'Дата правки',
|
updatedAt: 'Дата правки',
|
||||||
updatedCountSuccessfully: 'Обновлено {{count}} {{label}} успешно.',
|
updatedCountSuccessfully: 'Обновлено {{count}} {{label}} успешно.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Text att visa',
|
textToDisplay: 'Text att visa',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopiera',
|
||||||
createdAt: 'Skapad Vid',
|
createdAt: 'Skapad Vid',
|
||||||
deletedCountSuccessfully: 'Raderade {{count}} {{label}} framgångsrikt.',
|
deletedCountSuccessfully: 'Raderade {{count}} {{label}} framgångsrikt.',
|
||||||
deletedSuccessfully: 'Togs bort framgångsrikt.',
|
deletedSuccessfully: 'Togs bort framgångsrikt.',
|
||||||
email: 'E-post',
|
email: 'E-post',
|
||||||
notFound: 'Hittades inte',
|
notFound: 'Hittades inte',
|
||||||
successfullyCreated: '{{label}} skapades framgångsrikt.',
|
successfullyCreated: '{{label}} skapades framgångsrikt.',
|
||||||
|
successfullyDuplicated: '{{label}} duplicerades framgångsrikt.',
|
||||||
thisLanguage: 'Svenska',
|
thisLanguage: 'Svenska',
|
||||||
updatedAt: 'Uppdaterades Vid',
|
updatedAt: 'Uppdaterades Vid',
|
||||||
updatedCountSuccessfully: 'Uppdaterade {{count}} {{label}} framgångsrikt.',
|
updatedCountSuccessfully: 'Uppdaterade {{count}} {{label}} framgångsrikt.',
|
||||||
|
|||||||
@@ -38,12 +38,14 @@ export default {
|
|||||||
textToDisplay: 'ข้อความสำหรับแสดงผล',
|
textToDisplay: 'ข้อความสำหรับแสดงผล',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'คัดลอก',
|
||||||
createdAt: 'สร้างเมื่อ',
|
createdAt: 'สร้างเมื่อ',
|
||||||
deletedCountSuccessfully: 'Deleted {{count}} {{label}} successfully.',
|
deletedCountSuccessfully: 'Deleted {{count}} {{label}} successfully.',
|
||||||
deletedSuccessfully: 'ลบสำเร็จ',
|
deletedSuccessfully: 'ลบสำเร็จ',
|
||||||
email: 'อีเมล',
|
email: 'อีเมล',
|
||||||
notFound: 'ไม่พบ',
|
notFound: 'ไม่พบ',
|
||||||
successfullyCreated: 'สร้าง {{label}} สำเร็จ',
|
successfullyCreated: 'สร้าง {{label}} สำเร็จ',
|
||||||
|
successfullyDuplicated: 'สำเนา {{label}} สำเร็จ',
|
||||||
thisLanguage: 'ไทย',
|
thisLanguage: 'ไทย',
|
||||||
updatedAt: 'แก้ไขเมื่อ',
|
updatedAt: 'แก้ไขเมื่อ',
|
||||||
updatedCountSuccessfully: 'อัปเดต {{count}} {{label}} เรียบร้อยแล้ว',
|
updatedCountSuccessfully: 'อัปเดต {{count}} {{label}} เรียบร้อยแล้ว',
|
||||||
|
|||||||
@@ -40,12 +40,14 @@ export default {
|
|||||||
textToDisplay: 'Görüntülenecek metin',
|
textToDisplay: 'Görüntülenecek metin',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Kopyala',
|
||||||
createdAt: 'Oluşturma tarihi',
|
createdAt: 'Oluşturma tarihi',
|
||||||
deletedCountSuccessfully: '{{count}} {{label}} başarıyla silindi.',
|
deletedCountSuccessfully: '{{count}} {{label}} başarıyla silindi.',
|
||||||
deletedSuccessfully: 'Başarıyla silindi.',
|
deletedSuccessfully: 'Başarıyla silindi.',
|
||||||
email: 'E-posta',
|
email: 'E-posta',
|
||||||
notFound: 'Bulunamadı',
|
notFound: 'Bulunamadı',
|
||||||
successfullyCreated: '{{label}} başarıyla oluşturuldu.',
|
successfullyCreated: '{{label}} başarıyla oluşturuldu.',
|
||||||
|
successfullyDuplicated: '{{label}} başarıyla kopyalandı.',
|
||||||
thisLanguage: 'Türkçe',
|
thisLanguage: 'Türkçe',
|
||||||
updatedAt: 'Güncellenme tarihi',
|
updatedAt: 'Güncellenme tarihi',
|
||||||
updatedCountSuccessfully: '{{count}} {{label}} başarıyla güncellendi.',
|
updatedCountSuccessfully: '{{count}} {{label}} başarıyla güncellendi.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Текст для відображення',
|
textToDisplay: 'Текст для відображення',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Скопіювати',
|
||||||
createdAt: 'Дата створення',
|
createdAt: 'Дата створення',
|
||||||
deletedCountSuccessfully: 'Успішно видалено {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Успішно видалено {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Успішно видалено.',
|
deletedSuccessfully: 'Успішно видалено.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Не знайдено',
|
notFound: 'Не знайдено',
|
||||||
successfullyCreated: '{{label}} успішно створено.',
|
successfullyCreated: '{{label}} успішно створено.',
|
||||||
|
successfullyDuplicated: '{{label}} успішно продубльовано.',
|
||||||
thisLanguage: 'Українська',
|
thisLanguage: 'Українська',
|
||||||
updatedAt: 'Змінено',
|
updatedAt: 'Змінено',
|
||||||
updatedCountSuccessfully: 'Успішно оновлено {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Успішно оновлено {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -39,12 +39,14 @@ export default {
|
|||||||
textToDisplay: 'Văn bản để hiển thị',
|
textToDisplay: 'Văn bản để hiển thị',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: 'Sao chép',
|
||||||
createdAt: 'Ngày tạo',
|
createdAt: 'Ngày tạo',
|
||||||
deletedCountSuccessfully: 'Đã xóa thành công {{count}} {{label}}.',
|
deletedCountSuccessfully: 'Đã xóa thành công {{count}} {{label}}.',
|
||||||
deletedSuccessfully: 'Đã xoá thành công.',
|
deletedSuccessfully: 'Đã xoá thành công.',
|
||||||
email: 'Email',
|
email: 'Email',
|
||||||
notFound: 'Không tìm thấy',
|
notFound: 'Không tìm thấy',
|
||||||
successfullyCreated: '{{label}} đã được tạo thành công.',
|
successfullyCreated: '{{label}} đã được tạo thành công.',
|
||||||
|
successfullyDuplicated: '{{label}} đã được sao chép thành công.',
|
||||||
thisLanguage: 'Vietnamese (Tiếng Việt)',
|
thisLanguage: 'Vietnamese (Tiếng Việt)',
|
||||||
updatedAt: 'Ngày cập nhật',
|
updatedAt: 'Ngày cập nhật',
|
||||||
updatedCountSuccessfully: 'Đã cập nhật thành công {{count}} {{label}}.',
|
updatedCountSuccessfully: 'Đã cập nhật thành công {{count}} {{label}}.',
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ export default {
|
|||||||
textToDisplay: '要顯示的文字',
|
textToDisplay: '要顯示的文字',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: '複製',
|
||||||
createdAt: '建立於',
|
createdAt: '建立於',
|
||||||
deletedCountSuccessfully: '已成功刪除 {{count}} 個 {{label}}。',
|
deletedCountSuccessfully: '已成功刪除 {{count}} 個 {{label}}。',
|
||||||
deletedSuccessfully: '已成功刪除。',
|
deletedSuccessfully: '已成功刪除。',
|
||||||
email: '電子郵件',
|
email: '電子郵件',
|
||||||
notFound: '未找到',
|
notFound: '未找到',
|
||||||
successfullyCreated: '成功建立{{label}}',
|
successfullyCreated: '成功建立{{label}}',
|
||||||
|
successfullyDuplicated: '成功複製{{label}}',
|
||||||
thisLanguage: '中文 (繁體)',
|
thisLanguage: '中文 (繁體)',
|
||||||
updatedAt: '更新於',
|
updatedAt: '更新於',
|
||||||
updatedCountSuccessfully: '已成功更新 {{count}} 個 {{label}}。',
|
updatedCountSuccessfully: '已成功更新 {{count}} 個 {{label}}。',
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ export default {
|
|||||||
textToDisplay: '要显示的文本',
|
textToDisplay: '要显示的文本',
|
||||||
},
|
},
|
||||||
general: {
|
general: {
|
||||||
|
copy: '复制',
|
||||||
createdAt: '创建于',
|
createdAt: '创建于',
|
||||||
deletedCountSuccessfully: '已成功删除 {{count}} {{label}}。',
|
deletedCountSuccessfully: '已成功删除 {{count}} {{label}}。',
|
||||||
deletedSuccessfully: '已成功删除。',
|
deletedSuccessfully: '已成功删除。',
|
||||||
email: '电子邮件',
|
email: '电子邮件',
|
||||||
notFound: '未找到',
|
notFound: '未找到',
|
||||||
successfullyCreated: '成功创建{{label}}',
|
successfullyCreated: '成功创建{{label}}',
|
||||||
|
successfullyDuplicated: '成功复制{{label}}',
|
||||||
thisLanguage: '中文 (简体)',
|
thisLanguage: '中文 (简体)',
|
||||||
updatedAt: '更新于',
|
updatedAt: '更新于',
|
||||||
updatedCountSuccessfully: '已成功更新 {{count}} {{label}}。',
|
updatedCountSuccessfully: '已成功更新 {{count}} {{label}}。',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'تمت الإرسال بنجاح.',
|
submissionSuccessful: 'تمت الإرسال بنجاح.',
|
||||||
submit: 'إرسال',
|
submit: 'إرسال',
|
||||||
successfullyCreated: '{{label}} تم إنشاؤها بنجاح.',
|
successfullyCreated: '{{label}} تم إنشاؤها بنجاح.',
|
||||||
successfullyDuplicated: '{{label}} تم استنساخها بنجاح.',
|
|
||||||
thisLanguage: 'العربية',
|
thisLanguage: 'العربية',
|
||||||
titleDeleted: 'تم حذف {{label}} "{{title}}" بنجاح.',
|
titleDeleted: 'تم حذف {{label}} "{{title}}" بنجاح.',
|
||||||
unauthorized: 'غير مصرح به',
|
unauthorized: 'غير مصرح به',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Təqdimat uğurlu oldu.',
|
submissionSuccessful: 'Təqdimat uğurlu oldu.',
|
||||||
submit: 'Təqdim et',
|
submit: 'Təqdim et',
|
||||||
successfullyCreated: '{{label}} uğurla yaradıldı.',
|
successfullyCreated: '{{label}} uğurla yaradıldı.',
|
||||||
successfullyDuplicated: '{{label}} uğurla dublikatlandı.',
|
|
||||||
thisLanguage: 'Azərbaycan dili',
|
thisLanguage: 'Azərbaycan dili',
|
||||||
titleDeleted: '{{label}} "{{title}}" uğurla silindi.',
|
titleDeleted: '{{label}} "{{title}}" uğurla silindi.',
|
||||||
unauthorized: 'İcazəsiz',
|
unauthorized: 'İcazəsiz',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'Успешно подаване.',
|
submissionSuccessful: 'Успешно подаване.',
|
||||||
submit: 'Подай',
|
submit: 'Подай',
|
||||||
successfullyCreated: '{{label}} успешно създаден.',
|
successfullyCreated: '{{label}} успешно създаден.',
|
||||||
successfullyDuplicated: '{{label}} успешно дупликиран.',
|
|
||||||
thisLanguage: 'Български',
|
thisLanguage: 'Български',
|
||||||
titleDeleted: '{{label}} "{{title}}" успешно изтрит.',
|
titleDeleted: '{{label}} "{{title}}" успешно изтрит.',
|
||||||
unauthorized: 'Неавторизиран',
|
unauthorized: 'Неавторизиран',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'Odeslání úspěšné.',
|
submissionSuccessful: 'Odeslání úspěšné.',
|
||||||
submit: 'Odeslat',
|
submit: 'Odeslat',
|
||||||
successfullyCreated: '{{label}} úspěšně vytvořeno.',
|
successfullyCreated: '{{label}} úspěšně vytvořeno.',
|
||||||
successfullyDuplicated: '{{label}} úspěšně duplikováno.',
|
|
||||||
thisLanguage: 'Čeština',
|
thisLanguage: 'Čeština',
|
||||||
titleDeleted: '{{label}} "{{title}}" úspěšně smazáno.',
|
titleDeleted: '{{label}} "{{title}}" úspěšně smazáno.',
|
||||||
unauthorized: 'Neoprávněný',
|
unauthorized: 'Neoprávněný',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Einrichung erfolgreich.',
|
submissionSuccessful: 'Einrichung erfolgreich.',
|
||||||
submit: 'Senden',
|
submit: 'Senden',
|
||||||
successfullyCreated: '{{label}} erfolgreich erstellt.',
|
successfullyCreated: '{{label}} erfolgreich erstellt.',
|
||||||
successfullyDuplicated: '{{label}} wurde erfolgreich dupliziert.',
|
|
||||||
thisLanguage: 'Deutsch',
|
thisLanguage: 'Deutsch',
|
||||||
titleDeleted: '{{label}} {{title}} wurde erfolgreich gelöscht.',
|
titleDeleted: '{{label}} {{title}} wurde erfolgreich gelöscht.',
|
||||||
unauthorized: 'Nicht autorisiert',
|
unauthorized: 'Nicht autorisiert',
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ export default {
|
|||||||
deletingTitle:
|
deletingTitle:
|
||||||
'There was an error while deleting {{title}}. Please check your connection and try again.',
|
'There was an error while deleting {{title}}. Please check your connection and try again.',
|
||||||
loadingDocument: 'There was a problem loading the document with ID of {{id}}.',
|
loadingDocument: 'There was a problem loading the document with ID of {{id}}.',
|
||||||
localesNotSaved_one: 'The following locale could not be saved:',
|
|
||||||
localesNotSaved_other: 'The following locales could not be saved:',
|
|
||||||
noMatchedField: 'No matched field found for "{{label}}"',
|
noMatchedField: 'No matched field found for "{{label}}"',
|
||||||
notAllowedToAccessPage: 'You are not allowed to access this page.',
|
notAllowedToAccessPage: 'You are not allowed to access this page.',
|
||||||
previewing: 'There was a problem previewing this document.',
|
previewing: 'There was a problem previewing this document.',
|
||||||
@@ -191,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Submission Successful.',
|
submissionSuccessful: 'Submission Successful.',
|
||||||
submit: 'Submit',
|
submit: 'Submit',
|
||||||
successfullyCreated: '{{label}} successfully created.',
|
successfullyCreated: '{{label}} successfully created.',
|
||||||
successfullyDuplicated: '{{label}} successfully duplicated.',
|
|
||||||
thisLanguage: 'English',
|
thisLanguage: 'English',
|
||||||
titleDeleted: '{{label}} "{{title}}" successfully deleted.',
|
titleDeleted: '{{label}} "{{title}}" successfully deleted.',
|
||||||
unauthorized: 'Unauthorized',
|
unauthorized: 'Unauthorized',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Envío realizado correctamente.',
|
submissionSuccessful: 'Envío realizado correctamente.',
|
||||||
submit: 'Enviar',
|
submit: 'Enviar',
|
||||||
successfullyCreated: '{{label}} creado correctamente.',
|
successfullyCreated: '{{label}} creado correctamente.',
|
||||||
successfullyDuplicated: '{{label}} duplicado correctamente.',
|
|
||||||
thisLanguage: 'Español',
|
thisLanguage: 'Español',
|
||||||
titleDeleted: '{{label}} {{title}} eliminado correctamente.',
|
titleDeleted: '{{label}} {{title}} eliminado correctamente.',
|
||||||
unauthorized: 'No autorizado',
|
unauthorized: 'No autorizado',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'با موفقیت ثبت شد.',
|
submissionSuccessful: 'با موفقیت ثبت شد.',
|
||||||
submit: 'فرستادن',
|
submit: 'فرستادن',
|
||||||
successfullyCreated: '{{label}} با موفقیت ساخته شد.',
|
successfullyCreated: '{{label}} با موفقیت ساخته شد.',
|
||||||
successfullyDuplicated: '{{label}} با موفقیت رونوشت شد.',
|
|
||||||
thisLanguage: 'فارسی',
|
thisLanguage: 'فارسی',
|
||||||
titleDeleted: '{{label}} "{{title}}" با موفقیت پاک شد.',
|
titleDeleted: '{{label}} "{{title}}" با موفقیت پاک شد.',
|
||||||
unauthorized: 'غیرمجاز',
|
unauthorized: 'غیرمجاز',
|
||||||
|
|||||||
@@ -191,7 +191,6 @@ export default {
|
|||||||
submissionSuccessful: 'Soumission réussie.',
|
submissionSuccessful: 'Soumission réussie.',
|
||||||
submit: 'Soumettre',
|
submit: 'Soumettre',
|
||||||
successfullyCreated: '{{label}} créé(e) avec succès.',
|
successfullyCreated: '{{label}} créé(e) avec succès.',
|
||||||
successfullyDuplicated: '{{label}} dupliqué(e) avec succès.',
|
|
||||||
thisLanguage: 'Français',
|
thisLanguage: 'Français',
|
||||||
titleDeleted: '{{label}} "{{title}}" supprimé(e) avec succès.',
|
titleDeleted: '{{label}} "{{title}}" supprimé(e) avec succès.',
|
||||||
unauthorized: 'Non autorisé',
|
unauthorized: 'Non autorisé',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'Uspješno slanje',
|
submissionSuccessful: 'Uspješno slanje',
|
||||||
submit: 'Podnesi',
|
submit: 'Podnesi',
|
||||||
successfullyCreated: '{{label}} uspješno kreirano.',
|
successfullyCreated: '{{label}} uspješno kreirano.',
|
||||||
successfullyDuplicated: '{{label}} uspješno duplicirano.',
|
|
||||||
thisLanguage: 'Hrvatski',
|
thisLanguage: 'Hrvatski',
|
||||||
titleDeleted: '{{label}} "{{title}}" uspješno obrisano.',
|
titleDeleted: '{{label}} "{{title}}" uspješno obrisano.',
|
||||||
unauthorized: 'Neovlašteno',
|
unauthorized: 'Neovlašteno',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Beküldés sikeres.',
|
submissionSuccessful: 'Beküldés sikeres.',
|
||||||
submit: 'Beküldés',
|
submit: 'Beküldés',
|
||||||
successfullyCreated: '{{label}} sikeresen létrehozva.',
|
successfullyCreated: '{{label}} sikeresen létrehozva.',
|
||||||
successfullyDuplicated: '{{label}} sikeresen duplikálódott.',
|
|
||||||
thisLanguage: 'Magyar',
|
thisLanguage: 'Magyar',
|
||||||
titleDeleted: '{{label}} "{{title}}" sikeresen törölve.',
|
titleDeleted: '{{label}} "{{title}}" sikeresen törölve.',
|
||||||
unauthorized: 'Jogosulatlan',
|
unauthorized: 'Jogosulatlan',
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ export default {
|
|||||||
submissionSuccessful: 'Invio riuscito.',
|
submissionSuccessful: 'Invio riuscito.',
|
||||||
submit: 'Invia',
|
submit: 'Invia',
|
||||||
successfullyCreated: '{{label}} creato con successo.',
|
successfullyCreated: '{{label}} creato con successo.',
|
||||||
successfullyDuplicated: '{{label}} duplicato con successo.',
|
|
||||||
thisLanguage: 'Italiano',
|
thisLanguage: 'Italiano',
|
||||||
titleDeleted: '{{label}} {{title}} eliminato con successo.',
|
titleDeleted: '{{label}} {{title}} eliminato con successo.',
|
||||||
unauthorized: 'Non autorizzato',
|
unauthorized: 'Non autorizzato',
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ export default {
|
|||||||
deletingTitle:
|
deletingTitle:
|
||||||
'{{title}} を削除する際にエラーが発生しました。接続を確認してからもう一度お試しください。',
|
'{{title}} を削除する際にエラーが発生しました。接続を確認してからもう一度お試しください。',
|
||||||
loadingDocument: 'IDが {{id}} のデータを読み込む際に問題が発生しました。',
|
loadingDocument: 'IDが {{id}} のデータを読み込む際に問題が発生しました。',
|
||||||
localesNotSaved_one: '次のロケールは保存できませんでした:',
|
|
||||||
localesNotSaved_other: '次のロケールは保存できませんでした:',
|
|
||||||
noMatchedField: '"{{label}}" に該当するフィールドがありません。',
|
noMatchedField: '"{{label}}" に該当するフィールドがありません。',
|
||||||
notAllowedToAccessPage: 'この画面へのアクセスは許可されていません。',
|
notAllowedToAccessPage: 'この画面へのアクセスは許可されていません。',
|
||||||
previewing: 'このデータをプレビューする際に問題が発生しました。',
|
previewing: 'このデータをプレビューする際に問題が発生しました。',
|
||||||
@@ -190,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: '送信が成功しました。',
|
submissionSuccessful: '送信が成功しました。',
|
||||||
submit: '送信',
|
submit: '送信',
|
||||||
successfullyCreated: '{{label}} が作成されました。',
|
successfullyCreated: '{{label}} が作成されました。',
|
||||||
successfullyDuplicated: '{{label}} が複製されました。',
|
|
||||||
thisLanguage: 'Japanese',
|
thisLanguage: 'Japanese',
|
||||||
titleDeleted: '{{label}} "{{title}}" が削除されました。',
|
titleDeleted: '{{label}} "{{title}}" が削除されました。',
|
||||||
unauthorized: '未認証',
|
unauthorized: '未認証',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: '제출이 완료되었습니다.',
|
submissionSuccessful: '제출이 완료되었습니다.',
|
||||||
submit: '제출',
|
submit: '제출',
|
||||||
successfullyCreated: '{{label}}이(가) 생성되었습니다.',
|
successfullyCreated: '{{label}}이(가) 생성되었습니다.',
|
||||||
successfullyDuplicated: '{{label}}이(가) 복제되었습니다.',
|
|
||||||
thisLanguage: '한국어',
|
thisLanguage: '한국어',
|
||||||
titleDeleted: '{{label}} "{{title}}"을(를) 삭제했습니다.',
|
titleDeleted: '{{label}} "{{title}}"을(를) 삭제했습니다.',
|
||||||
unauthorized: '권한 없음',
|
unauthorized: '권한 없음',
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ export default {
|
|||||||
submissionSuccessful: 'သိမ်းဆည်းမှု အောင်မြင်ပါသည်။',
|
submissionSuccessful: 'သိမ်းဆည်းမှု အောင်မြင်ပါသည်။',
|
||||||
submit: 'သိမ်းဆည်းမည်။',
|
submit: 'သိမ်းဆည်းမည်။',
|
||||||
successfullyCreated: '{{label}} အောင်မြင်စွာဖန်တီးခဲ့သည်။',
|
successfullyCreated: '{{label}} အောင်မြင်စွာဖန်တီးခဲ့သည်။',
|
||||||
successfullyDuplicated: '{{label}} အောင်မြင်စွာ ပုံတူပွားခဲ့သည်။',
|
|
||||||
thisLanguage: 'မြန်မာစာ',
|
thisLanguage: 'မြန်မာစာ',
|
||||||
titleDeleted: '{{label}} {{title}} အောင်မြင်စွာ ဖျက်သိမ်းခဲ့သည်။',
|
titleDeleted: '{{label}} {{title}} အောင်မြင်စွာ ဖျက်သိမ်းခဲ့သည်။',
|
||||||
unauthorized: 'အခွင့်မရှိပါ။',
|
unauthorized: 'အခွင့်မရှိပါ။',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Innsending vellykket.',
|
submissionSuccessful: 'Innsending vellykket.',
|
||||||
submit: 'Send inn',
|
submit: 'Send inn',
|
||||||
successfullyCreated: '{{label}} ble opprettet.',
|
successfullyCreated: '{{label}} ble opprettet.',
|
||||||
successfullyDuplicated: '{{label}} ble duplisert.',
|
|
||||||
thisLanguage: 'Norsk',
|
thisLanguage: 'Norsk',
|
||||||
titleDeleted: '{{label}} "{{title}}" ble slettet.',
|
titleDeleted: '{{label}} "{{title}}" ble slettet.',
|
||||||
unauthorized: 'Ikke autorisert',
|
unauthorized: 'Ikke autorisert',
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ export default {
|
|||||||
submissionSuccessful: 'Indiening succesvol.',
|
submissionSuccessful: 'Indiening succesvol.',
|
||||||
submit: 'Indienen',
|
submit: 'Indienen',
|
||||||
successfullyCreated: '{{label}} succesvol aangemaakt.',
|
successfullyCreated: '{{label}} succesvol aangemaakt.',
|
||||||
successfullyDuplicated: '{{label}} succesvol gedupliceerd.',
|
|
||||||
thisLanguage: 'Nederlands',
|
thisLanguage: 'Nederlands',
|
||||||
titleDeleted: '{{label}} "{{title}}" succesvol verwijderd.',
|
titleDeleted: '{{label}} "{{title}}" succesvol verwijderd.',
|
||||||
unauthorized: 'Onbevoegd',
|
unauthorized: 'Onbevoegd',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Zgłoszenie zakończone powodzeniem.',
|
submissionSuccessful: 'Zgłoszenie zakończone powodzeniem.',
|
||||||
submit: 'Zatwierdź',
|
submit: 'Zatwierdź',
|
||||||
successfullyCreated: 'Pomyślnie utworzono {{label}}.',
|
successfullyCreated: 'Pomyślnie utworzono {{label}}.',
|
||||||
successfullyDuplicated: 'Pomyślnie zduplikowano {{label}}',
|
|
||||||
thisLanguage: 'Polski',
|
thisLanguage: 'Polski',
|
||||||
titleDeleted: 'Pomyślnie usunięto {{label}} {{title}}',
|
titleDeleted: 'Pomyślnie usunięto {{label}} {{title}}',
|
||||||
unauthorized: 'Brak autoryzacji',
|
unauthorized: 'Brak autoryzacji',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Envio bem-sucedido.',
|
submissionSuccessful: 'Envio bem-sucedido.',
|
||||||
submit: 'Enviar',
|
submit: 'Enviar',
|
||||||
successfullyCreated: '{{label}} criado com sucesso.',
|
successfullyCreated: '{{label}} criado com sucesso.',
|
||||||
successfullyDuplicated: '{{label}} duplicado com sucesso.',
|
|
||||||
thisLanguage: 'Português',
|
thisLanguage: 'Português',
|
||||||
titleDeleted: '{{label}} {{title}} excluído com sucesso.',
|
titleDeleted: '{{label}} {{title}} excluído com sucesso.',
|
||||||
unauthorized: 'Não autorizado',
|
unauthorized: 'Não autorizado',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Trimitere cu succes.',
|
submissionSuccessful: 'Trimitere cu succes.',
|
||||||
submit: 'Trimite',
|
submit: 'Trimite',
|
||||||
successfullyCreated: '{{label}} creat(ă) cu succes.',
|
successfullyCreated: '{{label}} creat(ă) cu succes.',
|
||||||
successfullyDuplicated: '{{label}} duplicat(ă) cu succes.',
|
|
||||||
thisLanguage: 'Română',
|
thisLanguage: 'Română',
|
||||||
titleDeleted: '{{label}} "{{title}}" șters cu succes.',
|
titleDeleted: '{{label}} "{{title}}" șters cu succes.',
|
||||||
unauthorized: 'neautorizat(ă)',
|
unauthorized: 'neautorizat(ă)',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'Uspešno slanje',
|
submissionSuccessful: 'Uspešno slanje',
|
||||||
submit: 'Potvrdi',
|
submit: 'Potvrdi',
|
||||||
successfullyCreated: '{{label}} uspešno kreirano.',
|
successfullyCreated: '{{label}} uspešno kreirano.',
|
||||||
successfullyDuplicated: '{{label}} uspešno duplicirano.',
|
|
||||||
thisLanguage: 'Srpski (latinica)',
|
thisLanguage: 'Srpski (latinica)',
|
||||||
titleDeleted: '{{label}} "{{title}}" uspešno obrisano.',
|
titleDeleted: '{{label}} "{{title}}" uspešno obrisano.',
|
||||||
unauthorized: 'Niste autorizovani',
|
unauthorized: 'Niste autorizovani',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'Успешно слање',
|
submissionSuccessful: 'Успешно слање',
|
||||||
submit: 'Потврди',
|
submit: 'Потврди',
|
||||||
successfullyCreated: '{{label}} успешно креирано.',
|
successfullyCreated: '{{label}} успешно креирано.',
|
||||||
successfullyDuplicated: '{{label}} успешно дуплицирано.',
|
|
||||||
thisLanguage: 'Српски (ћирилица)',
|
thisLanguage: 'Српски (ћирилица)',
|
||||||
titleDeleted: '{{label}} "{{title}}" успешно обрисано.',
|
titleDeleted: '{{label}} "{{title}}" успешно обрисано.',
|
||||||
unauthorized: 'Нисте ауторизовани',
|
unauthorized: 'Нисте ауторизовани',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Успешно отправлено.',
|
submissionSuccessful: 'Успешно отправлено.',
|
||||||
submit: 'Отправить',
|
submit: 'Отправить',
|
||||||
successfullyCreated: '{{label}} успешно создан.',
|
successfullyCreated: '{{label}} успешно создан.',
|
||||||
successfullyDuplicated: '{{label}} успешно продублирован.',
|
|
||||||
thisLanguage: 'Русский',
|
thisLanguage: 'Русский',
|
||||||
titleDeleted: '{{label}} {{title}} успешно удалено.',
|
titleDeleted: '{{label}} {{title}} успешно удалено.',
|
||||||
unauthorized: 'Нет доступа',
|
unauthorized: 'Нет доступа',
|
||||||
|
|||||||
@@ -189,7 +189,6 @@ export default {
|
|||||||
submissionSuccessful: 'Inlämningen Lyckades.',
|
submissionSuccessful: 'Inlämningen Lyckades.',
|
||||||
submit: 'Lämna in',
|
submit: 'Lämna in',
|
||||||
successfullyCreated: '{{label}} skapades framgångsrikt.',
|
successfullyCreated: '{{label}} skapades framgångsrikt.',
|
||||||
successfullyDuplicated: '{{label}} duplicerades framgångsrikt.',
|
|
||||||
thisLanguage: 'Svenska',
|
thisLanguage: 'Svenska',
|
||||||
titleDeleted: '{{label}} "{{title}}" togs bort framgångsrikt.',
|
titleDeleted: '{{label}} "{{title}}" togs bort framgångsrikt.',
|
||||||
unauthorized: 'Obehörig',
|
unauthorized: 'Obehörig',
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ export default {
|
|||||||
submissionSuccessful: 'ส่งสำเร็จ',
|
submissionSuccessful: 'ส่งสำเร็จ',
|
||||||
submit: 'ส่ง',
|
submit: 'ส่ง',
|
||||||
successfullyCreated: 'สร้าง {{label}} สำเร็จ',
|
successfullyCreated: 'สร้าง {{label}} สำเร็จ',
|
||||||
successfullyDuplicated: 'สำเนา {{label}} สำเร็จ',
|
|
||||||
thisLanguage: 'ไทย',
|
thisLanguage: 'ไทย',
|
||||||
titleDeleted: 'ลบ {{label}} "{{title}}" สำเร็จ',
|
titleDeleted: 'ลบ {{label}} "{{title}}" สำเร็จ',
|
||||||
unauthorized: 'ไม่ได้รับอนุญาต',
|
unauthorized: 'ไม่ได้รับอนุญาต',
|
||||||
|
|||||||
@@ -190,7 +190,6 @@ export default {
|
|||||||
submissionSuccessful: 'Gönderme başarılı',
|
submissionSuccessful: 'Gönderme başarılı',
|
||||||
submit: 'Gönder',
|
submit: 'Gönder',
|
||||||
successfullyCreated: '{{label}} başarıyla oluşturuldu.',
|
successfullyCreated: '{{label}} başarıyla oluşturuldu.',
|
||||||
successfullyDuplicated: '{{label}} başarıyla kopyalandı.',
|
|
||||||
thisLanguage: 'Türkçe',
|
thisLanguage: 'Türkçe',
|
||||||
titleDeleted: '{{label}} {{title}} başarıyla silindi.',
|
titleDeleted: '{{label}} {{title}} başarıyla silindi.',
|
||||||
unauthorized: 'Yetkisiz',
|
unauthorized: 'Yetkisiz',
|
||||||
|
|||||||
@@ -188,7 +188,6 @@ export default {
|
|||||||
submissionSuccessful: 'Успішно відправлено.',
|
submissionSuccessful: 'Успішно відправлено.',
|
||||||
submit: 'Відправити',
|
submit: 'Відправити',
|
||||||
successfullyCreated: '{{label}} успішно створено.',
|
successfullyCreated: '{{label}} успішно створено.',
|
||||||
successfullyDuplicated: '{{label}} успішно продубльовано.',
|
|
||||||
thisLanguage: 'Українська',
|
thisLanguage: 'Українська',
|
||||||
titleDeleted: '{{label}} "{{title}}" успішно видалено.',
|
titleDeleted: '{{label}} "{{title}}" успішно видалено.',
|
||||||
unauthorized: 'Немає доступу',
|
unauthorized: 'Немає доступу',
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ export default {
|
|||||||
submissionSuccessful: 'Gửi thành công.',
|
submissionSuccessful: 'Gửi thành công.',
|
||||||
submit: 'Gửi',
|
submit: 'Gửi',
|
||||||
successfullyCreated: '{{label}} đã được tạo thành công.',
|
successfullyCreated: '{{label}} đã được tạo thành công.',
|
||||||
successfullyDuplicated: '{{label}} đã được sao chép thành công.',
|
|
||||||
thisLanguage: 'Vietnamese (Tiếng Việt)',
|
thisLanguage: 'Vietnamese (Tiếng Việt)',
|
||||||
titleDeleted: '{{label}} {{title}} đã được xóa thành công.',
|
titleDeleted: '{{label}} {{title}} đã được xóa thành công.',
|
||||||
unauthorized: 'Không có quyền truy cập.',
|
unauthorized: 'Không có quyền truy cập.',
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ export default {
|
|||||||
correctInvalidFields: '請更正無效區塊。',
|
correctInvalidFields: '請更正無效區塊。',
|
||||||
deletingTitle: '刪除{{title}}時出現了錯誤。請檢查您的網路連線並重試。',
|
deletingTitle: '刪除{{title}}時出現了錯誤。請檢查您的網路連線並重試。',
|
||||||
loadingDocument: '加載ID為{{id}}的文件時出現了問題。',
|
loadingDocument: '加載ID為{{id}}的文件時出現了問題。',
|
||||||
localesNotSaved_one: '這個語言環境無法被儲存:',
|
|
||||||
localesNotSaved_other: '以下的語言環境無法被儲存:',
|
|
||||||
noMatchedField: '找不到與"{{label}}"匹配的字串',
|
noMatchedField: '找不到與"{{label}}"匹配的字串',
|
||||||
notAllowedToAccessPage: '您沒有權限訪問此頁面。',
|
notAllowedToAccessPage: '您沒有權限訪問此頁面。',
|
||||||
previewing: '預覽文件時出現了問題。',
|
previewing: '預覽文件時出現了問題。',
|
||||||
@@ -188,7 +186,6 @@ export default {
|
|||||||
submissionSuccessful: '成功送出。',
|
submissionSuccessful: '成功送出。',
|
||||||
submit: '送出',
|
submit: '送出',
|
||||||
successfullyCreated: '成功建立{{label}}',
|
successfullyCreated: '成功建立{{label}}',
|
||||||
successfullyDuplicated: '成功複製{{label}}',
|
|
||||||
thisLanguage: '中文 (繁體)',
|
thisLanguage: '中文 (繁體)',
|
||||||
titleDeleted: '{{label}} "{{title}}"已被成功刪除。',
|
titleDeleted: '{{label}} "{{title}}"已被成功刪除。',
|
||||||
unauthorized: '未經授權',
|
unauthorized: '未經授權',
|
||||||
|
|||||||
@@ -186,7 +186,6 @@ export default {
|
|||||||
submissionSuccessful: '提交成功。',
|
submissionSuccessful: '提交成功。',
|
||||||
submit: '提交',
|
submit: '提交',
|
||||||
successfullyCreated: '成功创建{{label}}',
|
successfullyCreated: '成功创建{{label}}',
|
||||||
successfullyDuplicated: '成功复制{{label}}',
|
|
||||||
thisLanguage: '中文 (简体)',
|
thisLanguage: '中文 (简体)',
|
||||||
titleDeleted: '{{label}} "{{title}}"已被成功删除。',
|
titleDeleted: '{{label}} "{{title}}"已被成功删除。',
|
||||||
unauthorized: '未经授权',
|
unauthorized: '未经授权',
|
||||||
|
|||||||
@@ -77,8 +77,6 @@ export default {
|
|||||||
invalidFileType: 'Invalid file type',
|
invalidFileType: 'Invalid file type',
|
||||||
invalidFileTypeValue: 'Invalid file type: {{value}}',
|
invalidFileTypeValue: 'Invalid file type: {{value}}',
|
||||||
loadingDocument: 'There was a problem loading the document with ID of {{id}}.',
|
loadingDocument: 'There was a problem loading the document with ID of {{id}}.',
|
||||||
localesNotSaved_one: 'The following locale could not be saved:',
|
|
||||||
localesNotSaved_other: 'The following locales could not be saved:',
|
|
||||||
missingEmail: 'Missing email.',
|
missingEmail: 'Missing email.',
|
||||||
missingIDOfDocument: 'Missing ID of document to update.',
|
missingIDOfDocument: 'Missing ID of document to update.',
|
||||||
missingIDOfVersion: 'Missing ID of version.',
|
missingIDOfVersion: 'Missing ID of version.',
|
||||||
|
|||||||
@@ -78,8 +78,6 @@ export default {
|
|||||||
invalidFileType: '無効なファイル形式',
|
invalidFileType: '無効なファイル形式',
|
||||||
invalidFileTypeValue: '無効なファイル形式: {{value}}',
|
invalidFileTypeValue: '無効なファイル形式: {{value}}',
|
||||||
loadingDocument: 'IDが {{id}} のデータを読み込む際に問題が発生しました。',
|
loadingDocument: 'IDが {{id}} のデータを読み込む際に問題が発生しました。',
|
||||||
localesNotSaved_one: '次のロケールは保存できませんでした:',
|
|
||||||
localesNotSaved_other: '次のロケールは保存できませんでした:',
|
|
||||||
missingEmail: 'メールアドレスが不足しています。',
|
missingEmail: 'メールアドレスが不足しています。',
|
||||||
missingIDOfDocument: '更新するデータのIDが不足しています。',
|
missingIDOfDocument: '更新するデータのIDが不足しています。',
|
||||||
missingIDOfVersion: 'バージョンIDが不足しています。',
|
missingIDOfVersion: 'バージョンIDが不足しています。',
|
||||||
|
|||||||
@@ -72,8 +72,6 @@ export default {
|
|||||||
invalidFileType: '無效的文件類型',
|
invalidFileType: '無效的文件類型',
|
||||||
invalidFileTypeValue: '無效的文件類型: {{value}}',
|
invalidFileTypeValue: '無效的文件類型: {{value}}',
|
||||||
loadingDocument: '加載ID為{{id}}的文件時出現了問題。',
|
loadingDocument: '加載ID為{{id}}的文件時出現了問題。',
|
||||||
localesNotSaved_one: '這個語言環境無法被儲存:',
|
|
||||||
localesNotSaved_other: '以下的語言環境無法被儲存:',
|
|
||||||
missingEmail: '缺少電子郵件。',
|
missingEmail: '缺少電子郵件。',
|
||||||
missingIDOfDocument: '缺少需要更新的文檔的ID。',
|
missingIDOfDocument: '缺少需要更新的文檔的ID。',
|
||||||
missingIDOfVersion: '缺少版本的ID。',
|
missingIDOfVersion: '缺少版本的ID。',
|
||||||
|
|||||||
@@ -30,12 +30,14 @@ const serverTranslationKeys = [
|
|||||||
'fields:chooseDocumentToLink',
|
'fields:chooseDocumentToLink',
|
||||||
'fields:openInNewTab',
|
'fields:openInNewTab',
|
||||||
|
|
||||||
|
'general:copy',
|
||||||
'general:createdAt',
|
'general:createdAt',
|
||||||
'general:deletedCountSuccessfully',
|
'general:deletedCountSuccessfully',
|
||||||
'general:deletedSuccessfully',
|
'general:deletedSuccessfully',
|
||||||
'general:email',
|
'general:email',
|
||||||
'general:notFound',
|
'general:notFound',
|
||||||
'general:successfullyCreated',
|
'general:successfullyCreated',
|
||||||
|
'general:successfullyDuplicated',
|
||||||
'general:thisLanguage',
|
'general:thisLanguage',
|
||||||
'general:user',
|
'general:user',
|
||||||
'general:users',
|
'general:users',
|
||||||
@@ -130,7 +132,6 @@ const clientTranslationKeys = [
|
|||||||
'error:correctInvalidFields',
|
'error:correctInvalidFields',
|
||||||
'error:deletingTitle',
|
'error:deletingTitle',
|
||||||
'error:loadingDocument',
|
'error:loadingDocument',
|
||||||
'error:localesNotSaved',
|
|
||||||
'error:noMatchedField',
|
'error:noMatchedField',
|
||||||
'error:notAllowedToAccessPage',
|
'error:notAllowedToAccessPage',
|
||||||
'error:previewing',
|
'error:previewing',
|
||||||
@@ -266,7 +267,6 @@ const clientTranslationKeys = [
|
|||||||
'general:submit',
|
'general:submit',
|
||||||
'general:successfullyCreated',
|
'general:successfullyCreated',
|
||||||
'general:successfullyDeleted',
|
'general:successfullyDeleted',
|
||||||
'general:successfullyDuplicated',
|
|
||||||
'general:thisLanguage',
|
'general:thisLanguage',
|
||||||
'general:titleDeleted',
|
'general:titleDeleted',
|
||||||
'general:unauthorized',
|
'general:unauthorized',
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import { toast } from 'react-toastify'
|
|||||||
|
|
||||||
import type { Props } from './types.js'
|
import type { Props } from './types.js'
|
||||||
|
|
||||||
// import { requests } from '../../../api'
|
|
||||||
import { useForm, useFormModified } from '../../forms/Form/context.js'
|
import { useForm, useFormModified } from '../../forms/Form/context.js'
|
||||||
import { useConfig } from '../../providers/Config/index.js'
|
import { useConfig } from '../../providers/Config/index.js'
|
||||||
import { useTranslation } from '../../providers/Translation/index.js'
|
import { useTranslation } from '../../providers/Translation/index.js'
|
||||||
import { MinimalTemplate } from '../../templates/Minimal/index.js'
|
import { MinimalTemplate } from '../../templates/Minimal/index.js'
|
||||||
|
import { requests } from '../../utilities/api.js'
|
||||||
import { Button } from '../Button/index.js'
|
import { Button } from '../Button/index.js'
|
||||||
import * as PopupList from '../Popup/PopupButtonList/index.js'
|
import * as PopupList from '../Popup/PopupButtonList/index.js'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
@@ -21,12 +21,11 @@ const baseClass = 'duplicate'
|
|||||||
const Duplicate: React.FC<Props> = ({ id, slug, singularLabel }) => {
|
const Duplicate: React.FC<Props> = ({ id, slug, singularLabel }) => {
|
||||||
const { Modal, useModal } = facelessUIImport
|
const { Modal, useModal } = facelessUIImport
|
||||||
|
|
||||||
const { push } = useRouter()
|
const router = useRouter()
|
||||||
const modified = useFormModified()
|
const modified = useFormModified()
|
||||||
const { toggleModal } = useModal()
|
const { toggleModal } = useModal()
|
||||||
const { setModified } = useForm()
|
const { setModified } = useForm()
|
||||||
const {
|
const {
|
||||||
localization,
|
|
||||||
routes: { api },
|
routes: { api },
|
||||||
serverURL,
|
serverURL,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
@@ -46,134 +45,35 @@ const Duplicate: React.FC<Props> = ({ id, slug, singularLabel }) => {
|
|||||||
toggleModal(modalSlug)
|
toggleModal(modalSlug)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
await requests.post(`${serverURL}${api}/${slug}/${id}/duplicate`, {
|
||||||
const saveDocument = async ({
|
body: JSON.stringify({}),
|
||||||
id,
|
headers: {
|
||||||
duplicateID = '',
|
'Accept-Language': i18n.language,
|
||||||
locale = '',
|
'Content-Type': 'application/json',
|
||||||
}): Promise<null | string> => {
|
'credentials': 'include',
|
||||||
const data = null
|
},
|
||||||
|
}).then(async (res) => {
|
||||||
// const response = await requests.get(`${serverURL}${api}/${slug}/${id}`, {
|
const { doc, message } = await res.json()
|
||||||
// headers: {
|
if (res.status < 400) {
|
||||||
// 'Accept-Language': i18n.language,
|
|
||||||
// },
|
|
||||||
// params: {
|
|
||||||
// depth: 0,
|
|
||||||
// draft: true,
|
|
||||||
// 'fallback-locale': 'none',
|
|
||||||
// locale,
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
|
|
||||||
// let data = await response.json()
|
|
||||||
|
|
||||||
// TODO: convert this into a server action
|
|
||||||
// if (typeof collection.admin.hooks?.beforeDuplicate === 'function') {
|
|
||||||
// data = await collection.admin.hooks.beforeDuplicate({
|
|
||||||
// collection,
|
|
||||||
// data,
|
|
||||||
// locale,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (!duplicateID) {
|
|
||||||
if ('createdAt' in data) delete data.createdAt
|
|
||||||
if ('updatedAt' in data) delete data.updatedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
// const result = await requests[duplicateID ? 'patch' : 'post'](
|
|
||||||
// `${serverURL}${api}/${slug}/${duplicateID}?locale=${locale}&fallback-locale=none`,
|
|
||||||
// {
|
|
||||||
// body: JSON.stringify(data),
|
|
||||||
// headers: {
|
|
||||||
// 'Accept-Language': i18n.language,
|
|
||||||
// 'Content-Type': 'application/json',
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
|
|
||||||
// const json = await result.json()
|
|
||||||
|
|
||||||
// if (result.status === 201 || result.status === 200) {
|
|
||||||
// return json.doc.id
|
|
||||||
// }
|
|
||||||
|
|
||||||
// only show the error if this is the initial request failing
|
|
||||||
if (!duplicateID) {
|
|
||||||
// json.errors.forEach((error) => toast.error(error.message))
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
let duplicateID: string
|
|
||||||
let abort = false
|
|
||||||
const localeErrors = []
|
|
||||||
|
|
||||||
if (localization) {
|
|
||||||
await localization.localeCodes.reduce(async (priorLocalePatch, locale) => {
|
|
||||||
await priorLocalePatch
|
|
||||||
if (abort) return
|
|
||||||
const localeResult = await saveDocument({
|
|
||||||
id,
|
|
||||||
duplicateID,
|
|
||||||
locale,
|
|
||||||
})
|
|
||||||
duplicateID = localeResult || duplicateID
|
|
||||||
if (duplicateID && !localeResult) {
|
|
||||||
localeErrors.push(locale)
|
|
||||||
}
|
|
||||||
if (!duplicateID) {
|
|
||||||
abort = true
|
|
||||||
}
|
|
||||||
}, Promise.resolve())
|
|
||||||
} else {
|
|
||||||
duplicateID = await saveDocument({ id })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!duplicateID) {
|
|
||||||
// document was not saved, error toast was displayed
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
toast.success(
|
toast.success(
|
||||||
|
message ||
|
||||||
t('general:successfullyDuplicated', { label: getTranslation(singularLabel, i18n) }),
|
t('general:successfullyDuplicated', { label: getTranslation(singularLabel, i18n) }),
|
||||||
{
|
{
|
||||||
autoClose: 3000,
|
autoClose: 3000,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
setModified(false)
|
||||||
if (localeErrors.length > 0) {
|
router.push(`${admin}/collections/${slug}/${doc.id}`)
|
||||||
|
} else {
|
||||||
toast.error(
|
toast.error(
|
||||||
`
|
message ||
|
||||||
${t('error:localesNotSaved', { count: localeErrors.length })}
|
t('error:unspecific', { label: getTranslation(singularLabel, i18n) }),
|
||||||
${localeErrors.join(', ')}
|
|
||||||
`,
|
|
||||||
{ autoClose: 5000 },
|
{ autoClose: 5000 },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
setModified(false)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
push(`${admin}/collections/${slug}/${duplicateID}`)
|
|
||||||
}, 10)
|
|
||||||
},
|
},
|
||||||
[
|
[modified, serverURL, api, slug, id, i18n, toggleModal, modalSlug, t, singularLabel, setModified, router, admin],
|
||||||
modified,
|
|
||||||
localization,
|
|
||||||
t,
|
|
||||||
i18n,
|
|
||||||
setModified,
|
|
||||||
toggleModal,
|
|
||||||
modalSlug,
|
|
||||||
serverURL,
|
|
||||||
api,
|
|
||||||
slug,
|
|
||||||
id,
|
|
||||||
push,
|
|
||||||
admin,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const confirm = useCallback(async () => {
|
const confirm = useCallback(async () => {
|
||||||
|
|||||||
@@ -1,31 +1,10 @@
|
|||||||
import type { BeforeDuplicate, CollectionConfig } from 'payload/types'
|
import type {
|
||||||
import type { IndexedField } from '../../payload-types.js'
|
CollectionConfig} from 'payload/types'
|
||||||
|
|
||||||
import { indexedFieldsSlug } from '../../slugs.js'
|
import { indexedFieldsSlug } from '../../slugs.js'
|
||||||
|
|
||||||
const beforeDuplicate: BeforeDuplicate<IndexedField> = ({ data }) => {
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
collapsibleLocalizedUnique: data.collapsibleLocalizedUnique
|
|
||||||
? `${data.collapsibleLocalizedUnique}-copy`
|
|
||||||
: '',
|
|
||||||
collapsibleTextUnique: data.collapsibleTextUnique ? `${data.collapsibleTextUnique}-copy` : '',
|
|
||||||
group: {
|
|
||||||
...(data.group || {}),
|
|
||||||
localizedUnique: data.group?.localizedUnique ? `${data.group?.localizedUnique}-copy` : '',
|
|
||||||
},
|
|
||||||
uniqueText: data.uniqueText ? `${data.uniqueText}-copy` : '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const IndexedFields: CollectionConfig = {
|
const IndexedFields: CollectionConfig = {
|
||||||
slug: indexedFieldsSlug,
|
slug: indexedFieldsSlug,
|
||||||
// used to assert that versions also get indexes
|
|
||||||
admin: {
|
|
||||||
hooks: {
|
|
||||||
beforeDuplicate,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'text',
|
name: 'text',
|
||||||
|
|||||||
@@ -619,6 +619,23 @@ describe('Fields', () => {
|
|||||||
|
|
||||||
expect(result.id).toBeDefined()
|
expect(result.id).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should duplicate with unique fields', async () => {
|
||||||
|
const data = {
|
||||||
|
text: 'a',
|
||||||
|
}
|
||||||
|
const doc = await payload.create({
|
||||||
|
collection: 'indexed-fields',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
const result = await payload.duplicate({
|
||||||
|
collection: 'indexed-fields',
|
||||||
|
id: doc.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.id).not.toEqual(doc.id)
|
||||||
|
expect(result.uniqueRequiredText).toStrictEqual('uniqueRequired - Copy')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('array', () => {
|
describe('array', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user