From d8686c03526d004c5aa9924d36fccb90051da843 Mon Sep 17 00:00:00 2001 From: "Zimin A.N." Date: Wed, 6 May 2026 21:41:21 +0300 Subject: [PATCH] =?UTF-8?q?feat(schema):=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=BE=20=D1=88=D0=B0=D0=B1=D0=BB=D0=BE=D0=BD=D1=83=20?= =?UTF-8?q?=D1=81=D1=83=D1=89=D0=B5=D1=81=D1=82=D0=B2=D1=83=D1=8E=D1=89?= =?UTF-8?q?=D0=B5=D0=B3=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Для семейств похожих словарей (satellite_type / consumer_type / processing_level — все 'code+name+description' с таким же scope) admin копировал JSON руками или дублировал поля в Поля-табе. Это ~3 минуты на каждый. Теперь в create-mode dictionary editor сверху появился блок 'Создать по шаблону': SingleSelect списка существующих словарей. На выбор fetch'ится detail и заполняются: - description, scope, bundle - supportedLocales / defaultLocale - properties (через parseSchemaJson — round-trip preserved для FK, enum, localized, opaque kinds) - idSource Не трогаем: - name (admin вводит уникальный имя — это валидация на 422) - displayName (специфичен для нового словаря) - schemaVersion (resets на 1.0.0) Чисто client-side: queryClient.fetchQuery(dictionaryDetailQuery(name)) — TanStack Query сам кеширует результат и для users 'Изменить схему' позже. Не требует backend изменений. Edit-mode не показывает шаблон — там initial уже имеет schema существующего словаря. --- .../schema/DictionaryEditorDialog.tsx | 60 +++++++++++++++++++ ordinis-admin-ui/src/i18n.ts | 6 ++ 2 files changed, 66 insertions(+) diff --git a/ordinis-admin-ui/src/components/schema/DictionaryEditorDialog.tsx b/ordinis-admin-ui/src/components/schema/DictionaryEditorDialog.tsx index d8466a9..4f231de 100644 --- a/ordinis-admin-ui/src/components/schema/DictionaryEditorDialog.tsx +++ b/ordinis-admin-ui/src/components/schema/DictionaryEditorDialog.tsx @@ -1,5 +1,6 @@ import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useQueryClient } from '@tanstack/react-query' import axios from 'axios' import { Alert, @@ -15,6 +16,7 @@ import { type TabItem, } from '@nstart/ui' import { useCreateDictionary, useUpdateDictionary } from '@/api/mutations' +import { dictionaryDetailQuery, useDictionaries } from '@/api/queries' import type { CreateDictionaryRequest, DataScope, DictionaryDetail } from '@/api/client' import { SchemaBuilder } from './SchemaBuilder' import { @@ -73,6 +75,44 @@ export const DictionaryEditorDialog = ({ open, mode, onClose, onSuccess }: Props const [idSource, setIdSource] = useState(parsed.idSource ?? '') const [nameError, setNameError] = useState(null) + // Template (только в create-mode): admin выбирает существующий dict, + // его schema + locale + scope копируются. Имя остаётся пустым (admin + // вводит уникальное), version reset на 1.0.0. + const queryClient = useQueryClient() + const dictsQuery = useDictionaries() + const [loadingTemplate, setLoadingTemplate] = useState(false) + const templateOptions: SelectOption[] = useMemo(() => { + if (isEdit) return [] + const list = dictsQuery.data ?? [] + return list + .map((d) => ({ + id: d.name, + label: d.displayName ? `${d.name} — ${d.displayName}` : d.name, + })) + .sort((a, b) => a.label.localeCompare(b.label, 'ru')) + }, [dictsQuery.data, isEdit]) + + const handleApplyTemplate = async (templateName: string) => { + if (!templateName) return + setLoadingTemplate(true) + try { + const detail = await queryClient.fetchQuery(dictionaryDetailQuery(templateName)) + const tplParsed = parseSchemaJson(detail.schemaJson) + // Не трогаем name — admin должен ввести уникальное. + setDisplayName('') + setDescription(detail.description ?? '') + setScope(detail.scope) + setBundle(detail.bundle ?? 'cuod') + setSchemaVersion('1.0.0') + setSupportedLocales(detail.supportedLocales ?? ['ru-RU']) + setDefaultLocale(detail.defaultLocale ?? 'ru-RU') + setProperties(tplParsed.properties) + setIdSource(tplParsed.idSource ?? '') + } finally { + setLoadingTemplate(false) + } + } + const schemaJson = useMemo( () => buildSchemaJson(properties, idSource || undefined), [properties, idSource], @@ -147,6 +187,26 @@ export const DictionaryEditorDialog = ({ open, mode, onClose, onSuccess }: Props setActiveTab(id as EditorTab)} />
+ {!isEdit && ( +
+ { + void handleApplyTemplate(id) + }} + placeholder={ + dictsQuery.isLoading || loadingTemplate + ? '…' + : t('schema.template.placeholder') + } + disabled={dictsQuery.isLoading || loadingTemplate} + /> +
+ )} +