123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
import type {
|
||||
|
|
CollectionConfig,
|
|||
|
|
SelectFieldManyValidation,
|
|||
|
|
TextFieldSingleValidation,
|
|||
|
|
} from 'payload'
|
|||
|
|
import { select } from 'payload/shared'
|
|||
|
|
|
|||
|
|
import { DEFAULT_LOCALE, LOCALES } from '@/i18n/locales'
|
|||
|
|
|
|||
|
|
import { anyRole, managers, owners } from '../access/roles'
|
|||
|
|
import { localeOptions } from '../fields/locales'
|
|||
|
|
import { groups, label, message } from '../i18n'
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Языки сайта — языки его контента. Домен показывает любую их часть.
|
|||
|
|
* Английский обязателен: на него откатывается непереведённый контент.
|
|||
|
|
*/
|
|||
|
|
const validateLocales: SelectFieldManyValidation = async (value, args) => {
|
|||
|
|
const base = select(value, args)
|
|||
|
|
if (base !== true) return base
|
|||
|
|
|
|||
|
|
const locales = value ?? []
|
|||
|
|
if (!locales.includes(DEFAULT_LOCALE)) {
|
|||
|
|
return message(args.req, {
|
|||
|
|
en: 'English is required: untranslated content falls back to it',
|
|||
|
|
ru: 'Английский обязателен: на него откатывается непереведённый контент',
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (args.id) {
|
|||
|
|
const { docs } = await args.req.payload.find({
|
|||
|
|
collection: 'domains',
|
|||
|
|
where: { site: { equals: args.id } },
|
|||
|
|
depth: 0,
|
|||
|
|
pagination: false,
|
|||
|
|
overrideAccess: true,
|
|||
|
|
req: args.req,
|
|||
|
|
})
|
|||
|
|
const used = new Set(docs.flatMap((domain) => domain.locales ?? []))
|
|||
|
|
const missing = [...used].filter((locale) => !locales.includes(locale))
|
|||
|
|
if (missing.length > 0) {
|
|||
|
|
return message(args.req, {
|
|||
|
|
en: `These languages are enabled on the site's domains, remove them there first: ${missing.join(', ')}`,
|
|||
|
|
ru: `Эти языки включены на доменах сайта, сначала уберите их там: ${missing.join(', ')}`,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const validateOrderPrefix: TextFieldSingleValidation = (value, { req }) =>
|
|||
|
|
typeof value === 'string' && /^[A-Z]{2,4}$/.test(value)
|
|||
|
|
? true
|
|||
|
|
: message(req, {
|
|||
|
|
en: '2–4 capital Latin letters, e.g. VX',
|
|||
|
|
ru: '2–4 заглавные латинские буквы, например VX',
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
export const Sites: CollectionConfig = {
|
|||
|
|
slug: 'sites',
|
|||
|
|
labels: {
|
|||
|
|
singular: label('Site', 'Сайт'),
|
|||
|
|
plural: label('Sites', 'Сайты'),
|
|||
|
|
},
|
|||
|
|
admin: {
|
|||
|
|
group: groups.settings,
|
|||
|
|
useAsTitle: 'name',
|
|||
|
|
defaultColumns: ['name', 'code', 'orderPrefix', 'locales'],
|
|||
|
|
},
|
|||
|
|
access: {
|
|||
|
|
read: anyRole,
|
|||
|
|
create: managers,
|
|||
|
|
update: managers,
|
|||
|
|
// вместе с сайтом плагин удаляет все его документы
|
|||
|
|
delete: owners,
|
|||
|
|
},
|
|||
|
|
fields: [
|
|||
|
|
{
|
|||
|
|
name: 'code',
|
|||
|
|
type: 'select',
|
|||
|
|
required: true,
|
|||
|
|
unique: true,
|
|||
|
|
options: [
|
|||
|
|
{ value: 'dedicated', label: label('Veilix (dedicated)', 'Вейликс (dedicated)') },
|
|||
|
|
{ value: 'mass', label: label('VeilixVPN (mass)', 'ВейликсВПН (mass)') },
|
|||
|
|
],
|
|||
|
|
label: label('Brand', 'Бренд'),
|
|||
|
|
// по коду выбираются тема и страницы бренда, после создания не меняется
|
|||
|
|
access: { update: () => false },
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'name',
|
|||
|
|
type: 'text',
|
|||
|
|
required: true,
|
|||
|
|
localized: true,
|
|||
|
|
label: label('Name', 'Название'),
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'orderPrefix',
|
|||
|
|
type: 'text',
|
|||
|
|
required: true,
|
|||
|
|
unique: true,
|
|||
|
|
label: label('Order number prefix', 'Префикс номеров'),
|
|||
|
|
validate: validateOrderPrefix,
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: 'locales',
|
|||
|
|
type: 'select',
|
|||
|
|
hasMany: true,
|
|||
|
|
required: true,
|
|||
|
|
options: localeOptions,
|
|||
|
|
defaultValue: [...LOCALES],
|
|||
|
|
label: label('Content languages', 'Языки контента'),
|
|||
|
|
validate: validateLocales,
|
|||
|
|
admin: {
|
|||
|
|
description: label(
|
|||
|
|
'Each domain of the site shows some of these languages.',
|
|||
|
|
'Каждый домен сайта показывает часть этих языков.',
|
|||
|
|
),
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|