377 lines
10 KiB
TypeScript
377 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback } from "react";
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
import { apiClient } from "@/lib/api/client";
|
|
|
|
// 템플릿 데이터 인터페이스
|
|
export interface TemplateStandard {
|
|
template_code: string;
|
|
template_name: string;
|
|
template_name_eng?: string;
|
|
description?: string;
|
|
category: string;
|
|
icon_name?: string;
|
|
default_size?: {
|
|
width: number;
|
|
height: number;
|
|
};
|
|
layout_config: any; // 템플릿의 컴포넌트 구조
|
|
preview_image?: string;
|
|
sort_order?: number;
|
|
is_active: string;
|
|
is_public?: string;
|
|
company_code: string;
|
|
created_date?: string;
|
|
created_by?: string;
|
|
updated_date?: string;
|
|
updated_by?: string;
|
|
}
|
|
|
|
// 템플릿 생성/수정 데이터
|
|
export interface TemplateFormData {
|
|
template_code: string;
|
|
template_name: string;
|
|
template_name_eng?: string;
|
|
description?: string;
|
|
category: string;
|
|
icon_name?: string;
|
|
default_size?: {
|
|
width: number;
|
|
height: number;
|
|
};
|
|
layout_config: any;
|
|
preview_image?: string;
|
|
sort_order?: number;
|
|
is_active?: string;
|
|
is_public?: string;
|
|
}
|
|
|
|
// API 응답 인터페이스
|
|
interface ApiResponse<T> {
|
|
success: boolean;
|
|
data?: T;
|
|
message?: string;
|
|
error?: string;
|
|
pagination?: {
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
totalPages: number;
|
|
};
|
|
}
|
|
|
|
// 쿼리 파라미터 인터페이스
|
|
interface TemplateQueryParams {
|
|
active?: string;
|
|
category?: string;
|
|
search?: string;
|
|
company_code?: string;
|
|
is_public?: string;
|
|
page?: number;
|
|
limit?: number;
|
|
}
|
|
|
|
/**
|
|
* 템플릿 관리 훅
|
|
*/
|
|
export const useTemplates = (params?: TemplateQueryParams) => {
|
|
const queryClient = useQueryClient();
|
|
|
|
// 템플릿 목록 조회
|
|
const {
|
|
data: templatesData,
|
|
isLoading,
|
|
error,
|
|
refetch,
|
|
} = useQuery({
|
|
queryKey: ["templates", params],
|
|
queryFn: async (): Promise<{ templates: TemplateStandard[]; pagination: any }> => {
|
|
const searchParams = new URLSearchParams();
|
|
|
|
if (params?.active) searchParams.append("active", params.active);
|
|
if (params?.category) searchParams.append("category", params.category);
|
|
if (params?.search) searchParams.append("search", params.search);
|
|
if (params?.company_code) searchParams.append("company_code", params.company_code);
|
|
if (params?.is_public) searchParams.append("is_public", params.is_public);
|
|
if (params?.page) searchParams.append("page", params.page.toString());
|
|
if (params?.limit) searchParams.append("limit", params.limit.toString());
|
|
|
|
const response = await apiClient.get(`/admin/template-standards?${searchParams.toString()}`);
|
|
|
|
const result: ApiResponse<TemplateStandard[]> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to fetch templates");
|
|
}
|
|
|
|
return {
|
|
templates: result.data || [],
|
|
pagination: result.pagination,
|
|
};
|
|
},
|
|
staleTime: 5 * 60 * 1000, // 5분간 캐시 유지
|
|
cacheTime: 10 * 60 * 1000, // 10분간 메모리 보관
|
|
});
|
|
|
|
const templates = templatesData?.templates || [];
|
|
const pagination = templatesData?.pagination;
|
|
|
|
// 템플릿 상세 조회
|
|
const getTemplate = useCallback(async (templateCode: string) => {
|
|
const response = await apiClient.get(`/admin/template-standards/${templateCode}`);
|
|
const result: ApiResponse<TemplateStandard> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to fetch template");
|
|
}
|
|
|
|
return result.data!;
|
|
}, []);
|
|
|
|
// 템플릿 생성
|
|
const createTemplateMutation = useMutation({
|
|
mutationFn: async (data: TemplateFormData): Promise<TemplateStandard> => {
|
|
const response = await apiClient.post("/admin/template-standards", data);
|
|
|
|
const result: ApiResponse<TemplateStandard> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to create template");
|
|
}
|
|
|
|
return result.data!;
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
|
},
|
|
});
|
|
|
|
// 템플릿 수정
|
|
const updateTemplateMutation = useMutation({
|
|
mutationFn: async ({
|
|
templateCode,
|
|
data,
|
|
}: {
|
|
templateCode: string;
|
|
data: Partial<TemplateFormData>;
|
|
}): Promise<TemplateStandard> => {
|
|
const response = await apiClient.put(`/admin/template-standards/${templateCode}`, data);
|
|
|
|
const result: ApiResponse<TemplateStandard> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to update template");
|
|
}
|
|
|
|
return result.data!;
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
|
},
|
|
});
|
|
|
|
// 템플릿 삭제
|
|
const deleteTemplateMutation = useMutation({
|
|
mutationFn: async (templateCode: string): Promise<void> => {
|
|
const response = await apiClient.delete(`/admin/template-standards/${templateCode}`);
|
|
|
|
const result: ApiResponse<void> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to delete template");
|
|
}
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
|
},
|
|
});
|
|
|
|
// 정렬 순서 업데이트
|
|
const updateSortOrderMutation = useMutation({
|
|
mutationFn: async (templates: { template_code: string; sort_order: number }[]): Promise<void> => {
|
|
const response = await apiClient.put("/admin/template-standards/sort-order/bulk", { templates });
|
|
|
|
const result: ApiResponse<void> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to update sort order");
|
|
}
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
|
},
|
|
});
|
|
|
|
// 템플릿 복제
|
|
const duplicateTemplateMutation = useMutation({
|
|
mutationFn: async ({
|
|
templateCode,
|
|
newTemplateCode,
|
|
newTemplateName,
|
|
}: {
|
|
templateCode: string;
|
|
newTemplateCode: string;
|
|
newTemplateName: string;
|
|
}): Promise<TemplateStandard> => {
|
|
const response = await apiClient.post(`/admin/template-standards/${templateCode}/duplicate`, {
|
|
new_template_code: newTemplateCode,
|
|
new_template_name: newTemplateName,
|
|
});
|
|
|
|
const result: ApiResponse<TemplateStandard> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to duplicate template");
|
|
}
|
|
|
|
return result.data!;
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
|
},
|
|
});
|
|
|
|
// 템플릿 가져오기
|
|
const importTemplateMutation = useMutation({
|
|
mutationFn: async (templateData: any): Promise<TemplateStandard> => {
|
|
const response = await apiClient.post("/admin/template-standards/import", templateData);
|
|
|
|
const result: ApiResponse<TemplateStandard> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to import template");
|
|
}
|
|
|
|
return result.data!;
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
|
},
|
|
});
|
|
|
|
// 템플릿 내보내기
|
|
const exportTemplate = useCallback(async (templateCode: string) => {
|
|
const response = await apiClient.get(`/admin/template-standards/${templateCode}/export`);
|
|
const result: ApiResponse<any> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to export template");
|
|
}
|
|
|
|
return result.data;
|
|
}, []);
|
|
|
|
// 카테고리 목록 조회
|
|
const {
|
|
data: categories,
|
|
isLoading: categoriesLoading,
|
|
error: categoriesError,
|
|
} = useQuery({
|
|
queryKey: ["template-categories"],
|
|
queryFn: async (): Promise<string[]> => {
|
|
const response = await apiClient.get("/admin/template-standards/categories");
|
|
|
|
const result: ApiResponse<string[]> = response.data;
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to fetch categories");
|
|
}
|
|
|
|
return result.data || [];
|
|
},
|
|
staleTime: 10 * 60 * 1000, // 10분간 캐시 유지
|
|
});
|
|
|
|
// 편의 메서드들
|
|
const createTemplate = useCallback(
|
|
(data: TemplateFormData) => {
|
|
return createTemplateMutation.mutateAsync(data);
|
|
},
|
|
[createTemplateMutation],
|
|
);
|
|
|
|
const updateTemplate = useCallback(
|
|
(templateCode: string, data: Partial<TemplateFormData>) => {
|
|
return updateTemplateMutation.mutateAsync({ templateCode, data });
|
|
},
|
|
[updateTemplateMutation],
|
|
);
|
|
|
|
const deleteTemplate = useCallback(
|
|
(templateCode: string) => {
|
|
return deleteTemplateMutation.mutateAsync(templateCode);
|
|
},
|
|
[deleteTemplateMutation],
|
|
);
|
|
|
|
const updateSortOrder = useCallback(
|
|
(templates: { template_code: string; sort_order: number }[]) => {
|
|
return updateSortOrderMutation.mutateAsync(templates);
|
|
},
|
|
[updateSortOrderMutation],
|
|
);
|
|
|
|
const duplicateTemplate = useCallback(
|
|
(templateCode: string, newTemplateCode: string, newTemplateName: string) => {
|
|
return duplicateTemplateMutation.mutateAsync({
|
|
templateCode,
|
|
newTemplateCode,
|
|
newTemplateName,
|
|
});
|
|
},
|
|
[duplicateTemplateMutation],
|
|
);
|
|
|
|
const importTemplate = useCallback(
|
|
(templateData: any) => {
|
|
return importTemplateMutation.mutateAsync(templateData);
|
|
},
|
|
[importTemplateMutation],
|
|
);
|
|
|
|
return {
|
|
// 데이터
|
|
templates,
|
|
pagination,
|
|
categories: categories || [],
|
|
|
|
// 로딩 상태
|
|
isLoading,
|
|
categoriesLoading,
|
|
isCreating: createTemplateMutation.isPending,
|
|
isUpdating: updateTemplateMutation.isPending,
|
|
isDeleting: deleteTemplateMutation.isPending,
|
|
isDuplicating: duplicateTemplateMutation.isPending,
|
|
isImporting: importTemplateMutation.isPending,
|
|
isSortOrderUpdating: updateSortOrderMutation.isPending,
|
|
|
|
// 에러 상태
|
|
error,
|
|
categoriesError,
|
|
createError: createTemplateMutation.error,
|
|
updateError: updateTemplateMutation.error,
|
|
deleteError: deleteTemplateMutation.error,
|
|
duplicateError: duplicateTemplateMutation.error,
|
|
importError: importTemplateMutation.error,
|
|
sortOrderError: updateSortOrderMutation.error,
|
|
|
|
// 메서드
|
|
getTemplate,
|
|
createTemplate,
|
|
updateTemplate,
|
|
deleteTemplate,
|
|
updateSortOrder,
|
|
duplicateTemplate,
|
|
importTemplate,
|
|
exportTemplate,
|
|
refetch,
|
|
};
|
|
};
|