feat(admin-ui): visual schema builder для создания/редактирования справочников
Новый функционал — заводить свои справочники через UI без code deploy:
API:
- CreateDictionaryRequest type в client.ts
- useCreateDictionary / useUpdateDictionary mutations с invalidate
['dictionaries'] и ['dictionary', name]
Компоненты (src/components/schema/):
- types.ts: PropertyDef + buildSchemaJson() (Draft-07) + parseSchemaJson()
для двусторонней конвертации между UI state и schema_json
- PropertyEditor: name + kind select + required + description + type-specific
опции (minLength/maxLength/pattern для string; minimum/maximum для number;
enum values; uniqueItems для array). Up/Down/Trash icon buttons
- SchemaBuilder: список property cards + кнопка «добавить» + EmptyState
- DictionaryEditorDialog: модалка с 3 табами:
* Метаданные (name/displayName/description/scope/bundle/version/locales)
* Поля (SchemaBuilder)
* JSON (readonly preview сгенерированной schema_json)
Поддерживаемые типы полей: string, integer, number, boolean, enum,
localized (i18n object с patternProperties), date (format=date),
datetime (format=date-time), array_string (с опц. enum + uniqueItems).
Wiring:
- /dictionaries: PageHeader actions = «+ Создать справочник» → Dialog в режиме create
→ onSuccess navigate на /dictionaries/{name}
- /dictionaries/$name: PageHeader actions расширен «Схема» (secondary) +
«Создать запись» (primary). Dialog в edit-режиме prefilled из
detailQuery.data + parseSchemaJson()
i18n: 30+ новых ключей в schema.* / scope.* (RU/EN)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
apiClient,
|
||||
type CreateDictionaryRequest,
|
||||
type CreateRecordRequest,
|
||||
type DictionaryDetail,
|
||||
type RecordResponse,
|
||||
} from './client'
|
||||
|
||||
@@ -10,6 +12,39 @@ const idempotencyKey = () =>
|
||||
? crypto.randomUUID()
|
||||
: `${Date.now()}-${Math.random().toString(36).slice(2)}`
|
||||
|
||||
export const useCreateDictionary = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (req: CreateDictionaryRequest): Promise<DictionaryDetail> => {
|
||||
const { data } = await apiClient.post<DictionaryDetail>('/dictionaries', req)
|
||||
return data
|
||||
},
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['dictionaries'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useUpdateDictionary = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async (params: {
|
||||
name: string
|
||||
payload: CreateDictionaryRequest
|
||||
}): Promise<DictionaryDetail> => {
|
||||
const { data } = await apiClient.put<DictionaryDetail>(
|
||||
`/dictionaries/${params.name}`,
|
||||
params.payload,
|
||||
)
|
||||
return data
|
||||
},
|
||||
onSuccess: (_, params) => {
|
||||
qc.invalidateQueries({ queryKey: ['dictionaries'] })
|
||||
qc.invalidateQueries({ queryKey: ['dictionary', params.name] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useCreateRecord = (dictionaryName: string) => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
|
||||
Reference in New Issue
Block a user