feat(admin-ui): record CRUD with json-schema-driven form
POST/PUT/DELETE формы для записей справочников. Schema-driven рендер полей из definition.schemaJson — i18n-aware (x-localized → input на каждый supportedLocale), enum → select, type → text/number/checkbox/array. Валидация через ajv (Draft-07, match backend networknt validator). - new: api/mutations.ts с Idempotency-Key (crypto.randomUUID) и query invalidation - new: SchemaDrivenForm — react-hook-form + ajv compile per-schema, errors через setError по ajv instancePath - new: Modal (Esc + backdrop click) и confirm dialog для close-операции - nginx: regex location ^/api/v1/dictionaries/[^/]+$ → writer (admin schema fetch) - queries: dictionaryDetailQuery возвращает full def с schemaJson - i18n: RU/EN ключи для CRUD (create/edit/close, validFrom/validTo, confirm)
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
import axios from 'axios'
|
||||
|
||||
/**
|
||||
* Centralized axios instance. JWT injection вешается через interceptor:
|
||||
* когда @nstart/auth интегрируется, добавляем Authorization: Bearer ...
|
||||
*/
|
||||
export const apiClient = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE ?? '/api/v1',
|
||||
timeout: 10_000,
|
||||
@@ -12,7 +8,6 @@ export const apiClient = axios.create({
|
||||
apiClient.interceptors.request.use((config) => {
|
||||
const lang = (typeof navigator !== 'undefined' && navigator.language) || 'ru-RU'
|
||||
config.headers['Accept-Language'] = `${lang},ru-RU;q=0.9,en-US;q=0.8`
|
||||
// JWT placeholder
|
||||
const token = localStorage.getItem('ordinis.token')
|
||||
if (token) config.headers['Authorization'] = `Bearer ${token}`
|
||||
return config
|
||||
@@ -28,12 +23,14 @@ apiClient.interceptors.response.use(
|
||||
},
|
||||
)
|
||||
|
||||
export type DataScope = 'PUBLIC' | 'INTERNAL' | 'RESTRICTED'
|
||||
|
||||
export type DictionaryDefinition = {
|
||||
id: string
|
||||
name: string
|
||||
displayName?: string
|
||||
description?: string
|
||||
scope: 'PUBLIC' | 'INTERNAL' | 'RESTRICTED'
|
||||
scope: DataScope
|
||||
schemaVersion: string
|
||||
bundle: string
|
||||
supportedLocales: string[]
|
||||
@@ -42,12 +39,59 @@ export type DictionaryDefinition = {
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type JsonSchema = {
|
||||
type?: string
|
||||
title?: string
|
||||
description?: string
|
||||
properties?: Record<string, JsonSchema>
|
||||
required?: string[]
|
||||
enum?: unknown[]
|
||||
format?: string
|
||||
pattern?: string
|
||||
minLength?: number
|
||||
maxLength?: number
|
||||
minimum?: number
|
||||
maximum?: number
|
||||
items?: JsonSchema
|
||||
additionalProperties?: boolean | JsonSchema
|
||||
['x-localized']?: boolean
|
||||
default?: unknown
|
||||
}
|
||||
|
||||
export type DictionaryDetail = DictionaryDefinition & {
|
||||
schemaJson: JsonSchema
|
||||
}
|
||||
|
||||
export type FlattenedRecord = {
|
||||
id: string
|
||||
businessKey: string
|
||||
data: Record<string, unknown>
|
||||
dataScope: 'PUBLIC' | 'INTERNAL' | 'RESTRICTED'
|
||||
dataScope: DataScope
|
||||
validFrom: string
|
||||
validTo: string
|
||||
_meta?: { locale?: string }
|
||||
}
|
||||
|
||||
export type RecordResponse = {
|
||||
id: string
|
||||
dictionaryId: string
|
||||
businessKey: string
|
||||
data: Record<string, unknown>
|
||||
geometryWkt?: string
|
||||
dataScope: DataScope
|
||||
validFrom: string
|
||||
validTo: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
createdBy?: string
|
||||
updatedBy?: string
|
||||
version: number
|
||||
}
|
||||
|
||||
export type CreateRecordRequest = {
|
||||
businessKey: string
|
||||
data: Record<string, unknown>
|
||||
geometryWkt?: string
|
||||
validFrom?: string
|
||||
validTo?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user