315 lines
8.5 KiB
TypeScript
315 lines
8.5 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback } from "react";
|
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
|
// 버튼 액션 데이터 인터페이스
|
|
export interface ButtonActionStandard {
|
|
action_type: string;
|
|
action_name: string;
|
|
action_name_eng?: string;
|
|
description?: string;
|
|
category: string;
|
|
default_text?: string;
|
|
default_text_eng?: string;
|
|
default_icon?: string;
|
|
default_color?: string;
|
|
default_variant?: string;
|
|
confirmation_required: boolean;
|
|
confirmation_message?: string;
|
|
validation_rules?: any;
|
|
action_config?: any;
|
|
sort_order?: number;
|
|
is_active: string;
|
|
created_date?: string;
|
|
created_by?: string;
|
|
updated_date?: string;
|
|
updated_by?: string;
|
|
}
|
|
|
|
// 버튼 액션 생성/수정 데이터
|
|
export interface ButtonActionFormData {
|
|
action_type: string;
|
|
action_name: string;
|
|
action_name_eng?: string;
|
|
description?: string;
|
|
category: string;
|
|
default_text?: string;
|
|
default_text_eng?: string;
|
|
default_icon?: string;
|
|
default_color?: string;
|
|
default_variant?: string;
|
|
confirmation_required?: boolean;
|
|
confirmation_message?: string;
|
|
validation_rules?: any;
|
|
action_config?: any;
|
|
sort_order?: number;
|
|
is_active?: string;
|
|
}
|
|
|
|
// API 응답 인터페이스
|
|
interface ApiResponse<T> {
|
|
success: boolean;
|
|
data?: T;
|
|
message?: string;
|
|
error?: string;
|
|
}
|
|
|
|
// 쿼리 파라미터 인터페이스
|
|
interface ButtonActionQueryParams {
|
|
active?: string;
|
|
category?: string;
|
|
search?: string;
|
|
}
|
|
|
|
/**
|
|
* 버튼 액션 관리 훅
|
|
*/
|
|
export const useButtonActions = (params?: ButtonActionQueryParams) => {
|
|
const queryClient = useQueryClient();
|
|
|
|
// 버튼 액션 목록 조회
|
|
const {
|
|
data: buttonActions,
|
|
isLoading,
|
|
error,
|
|
refetch,
|
|
} = useQuery({
|
|
queryKey: ["buttonActions", params],
|
|
queryFn: async (): Promise<ButtonActionStandard[]> => {
|
|
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);
|
|
|
|
const response = await fetch(
|
|
`/api/admin/button-actions?${searchParams.toString()}`,
|
|
{
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
const result: ApiResponse<ButtonActionStandard[]> = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to fetch button actions");
|
|
}
|
|
|
|
return result.data || [];
|
|
},
|
|
staleTime: 5 * 60 * 1000, // 5분간 캐시 유지
|
|
cacheTime: 10 * 60 * 1000, // 10분간 메모리 보관
|
|
});
|
|
|
|
// 버튼 액션 생성
|
|
const createButtonActionMutation = useMutation({
|
|
mutationFn: async (
|
|
data: ButtonActionFormData
|
|
): Promise<ButtonActionStandard> => {
|
|
const response = await fetch("/api/admin/button-actions", {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(
|
|
errorData.message || `HTTP error! status: ${response.status}`
|
|
);
|
|
}
|
|
|
|
const result: ApiResponse<ButtonActionStandard> = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to create button action");
|
|
}
|
|
|
|
return result.data!;
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["buttonActions"] });
|
|
},
|
|
});
|
|
|
|
// 버튼 액션 수정
|
|
const updateButtonActionMutation = useMutation({
|
|
mutationFn: async ({
|
|
actionType,
|
|
data,
|
|
}: {
|
|
actionType: string;
|
|
data: Partial<ButtonActionFormData>;
|
|
}): Promise<ButtonActionStandard> => {
|
|
const response = await fetch(`/api/admin/button-actions/${actionType}`, {
|
|
method: "PUT",
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(data),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(
|
|
errorData.message || `HTTP error! status: ${response.status}`
|
|
);
|
|
}
|
|
|
|
const result: ApiResponse<ButtonActionStandard> = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to update button action");
|
|
}
|
|
|
|
return result.data!;
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["buttonActions"] });
|
|
},
|
|
});
|
|
|
|
// 버튼 액션 삭제
|
|
const deleteButtonActionMutation = useMutation({
|
|
mutationFn: async (actionType: string): Promise<void> => {
|
|
const response = await fetch(`/api/admin/button-actions/${actionType}`, {
|
|
method: "DELETE",
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(
|
|
errorData.message || `HTTP error! status: ${response.status}`
|
|
);
|
|
}
|
|
|
|
const result: ApiResponse<void> = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to delete button action");
|
|
}
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["buttonActions"] });
|
|
},
|
|
});
|
|
|
|
// 정렬 순서 업데이트
|
|
const updateSortOrderMutation = useMutation({
|
|
mutationFn: async (
|
|
buttonActions: { action_type: string; sort_order: number }[]
|
|
): Promise<void> => {
|
|
const response = await fetch(
|
|
"/api/admin/button-actions/sort-order/bulk",
|
|
{
|
|
method: "PUT",
|
|
headers: {
|
|
Authorization: `Bearer ${localStorage.getItem("token")}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ buttonActions }),
|
|
}
|
|
);
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json();
|
|
throw new Error(
|
|
errorData.message || `HTTP error! status: ${response.status}`
|
|
);
|
|
}
|
|
|
|
const result: ApiResponse<void> = await response.json();
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || "Failed to update sort order");
|
|
}
|
|
},
|
|
onSuccess: () => {
|
|
// 목록 새로고침
|
|
queryClient.invalidateQueries({ queryKey: ["buttonActions"] });
|
|
},
|
|
});
|
|
|
|
// 편의 메서드들
|
|
const createButtonAction = useCallback(
|
|
(data: ButtonActionFormData) => {
|
|
return createButtonActionMutation.mutateAsync(data);
|
|
},
|
|
[createButtonActionMutation]
|
|
);
|
|
|
|
const updateButtonAction = useCallback(
|
|
(actionType: string, data: Partial<ButtonActionFormData>) => {
|
|
return updateButtonActionMutation.mutateAsync({ actionType, data });
|
|
},
|
|
[updateButtonActionMutation]
|
|
);
|
|
|
|
const deleteButtonAction = useCallback(
|
|
(actionType: string) => {
|
|
return deleteButtonActionMutation.mutateAsync(actionType);
|
|
},
|
|
[deleteButtonActionMutation]
|
|
);
|
|
|
|
const updateSortOrder = useCallback(
|
|
(buttonActions: { action_type: string; sort_order: number }[]) => {
|
|
return updateSortOrderMutation.mutateAsync(buttonActions);
|
|
},
|
|
[updateSortOrderMutation]
|
|
);
|
|
|
|
return {
|
|
// 데이터
|
|
buttonActions: buttonActions || [],
|
|
|
|
// 로딩 상태
|
|
isLoading,
|
|
isCreating: createButtonActionMutation.isPending,
|
|
isUpdating: updateButtonActionMutation.isPending,
|
|
isDeleting: deleteButtonActionMutation.isPending,
|
|
isSortingUpdating: updateSortOrderMutation.isPending,
|
|
|
|
// 에러
|
|
error,
|
|
createError: createButtonActionMutation.error,
|
|
updateError: updateButtonActionMutation.error,
|
|
deleteError: deleteButtonActionMutation.error,
|
|
sortError: updateSortOrderMutation.error,
|
|
|
|
// 액션
|
|
createButtonAction,
|
|
updateButtonAction,
|
|
deleteButtonAction,
|
|
updateSortOrder,
|
|
refetch,
|
|
|
|
// 상태 초기화
|
|
resetCreateError: createButtonActionMutation.reset,
|
|
resetUpdateError: updateButtonActionMutation.reset,
|
|
resetDeleteError: deleteButtonActionMutation.reset,
|
|
resetSortError: updateSortOrderMutation.reset,
|
|
};
|
|
};
|
|
|
|
|