рефакторинг
This commit is contained in:
198
src/entitiy/template/api/templateApiService.ts
Normal file
198
src/entitiy/template/api/templateApiService.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
export interface CreateTemplateInput {
|
||||
name: string
|
||||
description?: string
|
||||
elements: Record<string, any>
|
||||
}
|
||||
|
||||
export interface CreateTemplateOutput {
|
||||
id: UUID
|
||||
}
|
||||
|
||||
export interface GetTemplatesOutput {
|
||||
templates: ApiTemplate[]
|
||||
}
|
||||
|
||||
export interface GetTemplateOutput {
|
||||
template: ApiTemplate | null
|
||||
}
|
||||
|
||||
export interface PatchTemplateInput {
|
||||
name?: string | null
|
||||
description?: string | null
|
||||
elements?: Record<string, any> | null
|
||||
}
|
||||
|
||||
export interface PatchTemplateOutput {
|
||||
id: string
|
||||
}
|
||||
|
||||
export interface ApiTemplate {
|
||||
id: string
|
||||
name: string
|
||||
description?: string | null
|
||||
elements: Record<string, any>
|
||||
created_at: string
|
||||
updated_at: string
|
||||
deleted_at?: string | null
|
||||
}
|
||||
|
||||
// Импортируем типы для преобразования
|
||||
import { getDefaultLayoutSettings, migrateTemplateElement } from '@/lib/utils'
|
||||
import { Template, TemplateElement } from '@/type/template'
|
||||
|
||||
// Утилитарные функции для преобразования данных
|
||||
|
||||
// Преобразование API шаблона в внутренний формат
|
||||
export function apiTemplateToTemplate(apiTemplate: ApiTemplate): Template {
|
||||
// Преобразуем elements из Record<string, any> в TemplateElement[]
|
||||
const elements: TemplateElement[] = Object.values(apiTemplate.elements || {})
|
||||
.map(el => migrateTemplateElement(el))
|
||||
.sort((a, b) => (a.order || 0) - (b.order || 0))
|
||||
|
||||
return {
|
||||
id: apiTemplate.id,
|
||||
name: apiTemplate.name,
|
||||
description: apiTemplate.description || undefined,
|
||||
elements,
|
||||
layoutSettings: getDefaultLayoutSettings(),
|
||||
createdAt: new Date(apiTemplate.created_at),
|
||||
updatedAt: new Date(apiTemplate.updated_at),
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразование внутреннего шаблона в API формат для создания
|
||||
export function templateToCreateInput(
|
||||
template: Omit<Template, 'id' | 'createdAt' | 'updatedAt'>
|
||||
): CreateTemplateInput {
|
||||
// Преобразуем элементы в Record<string, any>
|
||||
const elements: Record<string, any> = {}
|
||||
template.elements.forEach((element, index) => {
|
||||
elements[element.id] = element
|
||||
})
|
||||
|
||||
return {
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
elements,
|
||||
}
|
||||
}
|
||||
|
||||
// Преобразование внутреннего шаблона в API формат для обновления
|
||||
export function templateToPatchInput(template: Template): PatchTemplateInput {
|
||||
// Преобразуем элементы в Record<string, any>
|
||||
const elements: Record<string, any> = {}
|
||||
template.elements.forEach((element, index) => {
|
||||
elements[element.id] = element
|
||||
})
|
||||
|
||||
return {
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
elements,
|
||||
}
|
||||
}
|
||||
|
||||
// API создание шаблона
|
||||
export async function createTemplateApi(
|
||||
template: CreateTemplateInput
|
||||
): Promise<CreateTemplateOutput> {
|
||||
try {
|
||||
const response = await fetch('/api/templates/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(template),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const result: CreateTemplateOutput = await response.json()
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Ошибка при создании шаблона:', error)
|
||||
throw new Error('Ошибка при создании шаблона на сервере')
|
||||
}
|
||||
}
|
||||
|
||||
// API получение всех шаблонов
|
||||
export async function getTemplatesApi(): Promise<GetTemplatesOutput> {
|
||||
try {
|
||||
const response = await fetch('/api/templates/', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const result: GetTemplatesOutput = await response.json()
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении шаблонов:', error)
|
||||
throw new Error('Ошибка при получении шаблонов с сервера')
|
||||
}
|
||||
}
|
||||
|
||||
// API получение конкретного шаблона
|
||||
export async function getTemplateApi(
|
||||
templateId: UUID
|
||||
): Promise<GetTemplateOutput> {
|
||||
try {
|
||||
const response = await fetch(`/api/templates/${templateId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const result: GetTemplateOutput = await response.json()
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Ошибка при получении шаблона:', error)
|
||||
throw new Error('Ошибка при получении шаблона с сервера')
|
||||
}
|
||||
}
|
||||
|
||||
// API обновление шаблона
|
||||
export async function patchTemplateApi(
|
||||
templateId: string,
|
||||
template: PatchTemplateInput
|
||||
): Promise<PatchTemplateOutput> {
|
||||
try {
|
||||
const response = await fetch(`/api/templates/${templateId}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(template),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const result: PatchTemplateOutput = await response.json()
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('Ошибка при обновлении шаблона:', error)
|
||||
throw new Error('Ошибка при обновлении шаблона на сервере')
|
||||
}
|
||||
}
|
||||
|
||||
// Алиас для удобства использования
|
||||
export async function updateTemplate(
|
||||
template: Template
|
||||
): Promise<PatchTemplateOutput> {
|
||||
const patchInput = templateToPatchInput(template)
|
||||
return patchTemplateApi(template.id, patchInput)
|
||||
}
|
||||
143
src/entitiy/template/model/TemplateContext.tsx
Normal file
143
src/entitiy/template/model/TemplateContext.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import {
|
||||
apiTemplateToTemplate,
|
||||
createTemplateApi,
|
||||
getTemplateApi,
|
||||
getTemplatesApi,
|
||||
patchTemplateApi,
|
||||
templateToCreateInput,
|
||||
templateToPatchInput,
|
||||
} from '@/entitiy/template/api/templateApiService'
|
||||
import { Template } from '@/type/template'
|
||||
import {
|
||||
QueryClient,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
} from '@tanstack/react-query'
|
||||
import React, { createContext, ReactNode, useContext, useMemo } from 'react'
|
||||
|
||||
export const queryClient = new QueryClient()
|
||||
|
||||
interface TemplateContextType {
|
||||
templates: Template[]
|
||||
isLoading: boolean
|
||||
error: Error | null
|
||||
refetchTemplates: () => void
|
||||
addTemplate: (
|
||||
data: Omit<Template, 'id' | 'createdAt' | 'updatedAt'>
|
||||
) => Promise<Template>
|
||||
updateTemplate: (template: Template) => Promise<Template>
|
||||
deleteTemplate: (id: UUID) => Promise<string>
|
||||
}
|
||||
|
||||
const TemplateContext = createContext<TemplateContextType | undefined>(
|
||||
undefined
|
||||
)
|
||||
|
||||
export const TemplateProvider: React.FC<{ children: ReactNode }> = ({
|
||||
children,
|
||||
}) => {
|
||||
const client = useQueryClient()
|
||||
|
||||
const {
|
||||
data: templates = [],
|
||||
status: listStatus,
|
||||
error: errorList,
|
||||
refetch,
|
||||
} = useQuery<Template[], Error>({
|
||||
queryKey: ['templates'],
|
||||
queryFn: async () => {
|
||||
const { templates: apiList } = await getTemplatesApi()
|
||||
return apiList.map(apiTemplateToTemplate)
|
||||
},
|
||||
})
|
||||
|
||||
const createMutation = useMutation({
|
||||
mutationFn: async (
|
||||
newTemplate: Omit<Template, 'id' | 'createdAt' | 'updatedAt'>
|
||||
) => {
|
||||
const input = templateToCreateInput(newTemplate)
|
||||
const { id } = await createTemplateApi(input)
|
||||
const { template: apiTpl } = await getTemplateApi(id)
|
||||
if (!apiTpl) throw new Error('Template not found')
|
||||
return apiTemplateToTemplate(apiTpl)
|
||||
},
|
||||
onSuccess: () => {
|
||||
client.invalidateQueries({ queryKey: ['templates'] })
|
||||
},
|
||||
})
|
||||
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: async (template: Template) => {
|
||||
const input = templateToPatchInput(template)
|
||||
await patchTemplateApi(template.id, input)
|
||||
return template
|
||||
},
|
||||
onSuccess: updated => {
|
||||
client.setQueryData<Template[]>(
|
||||
['templates'],
|
||||
old =>
|
||||
old?.map((t: Template) => (t.id === updated.id ? updated : t)) ?? []
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: async (id: string) => {
|
||||
// TODO: deleteTemplateApi
|
||||
return id
|
||||
},
|
||||
onSuccess: id => {
|
||||
client.setQueryData<Template[]>(
|
||||
['templates'],
|
||||
old => old?.filter((t: Template) => t.id !== id) ?? []
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
templates,
|
||||
isLoading:
|
||||
listStatus === 'pending' ||
|
||||
createMutation.status === 'pending' ||
|
||||
updateMutation.status === 'pending' ||
|
||||
deleteMutation.status === 'pending',
|
||||
error:
|
||||
(errorList as Error) ||
|
||||
(createMutation.error as Error) ||
|
||||
(updateMutation.error as Error) ||
|
||||
(deleteMutation.error as Error) ||
|
||||
null,
|
||||
refetchTemplates: refetch,
|
||||
addTemplate: createMutation.mutateAsync,
|
||||
updateTemplate: updateMutation.mutateAsync,
|
||||
deleteTemplate: deleteMutation.mutateAsync,
|
||||
}),
|
||||
[
|
||||
templates,
|
||||
listStatus,
|
||||
errorList,
|
||||
createMutation.status,
|
||||
createMutation.error,
|
||||
updateMutation.status,
|
||||
updateMutation.error,
|
||||
deleteMutation.status,
|
||||
deleteMutation.error,
|
||||
refetch,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<TemplateContext.Provider value={value}>
|
||||
{children}
|
||||
</TemplateContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useTemplateContext = (): TemplateContextType => {
|
||||
const ctx = useContext(TemplateContext)
|
||||
if (!ctx)
|
||||
throw new Error('useTemplateContext must be used within TemplateProvider')
|
||||
return ctx
|
||||
}
|
||||
Reference in New Issue
Block a user