"use client"; import { useState, useCallback } from "react"; // ============================================ // 타입 정의 // ============================================ // 거래처 유형 export type ClientType = "매입" | "매출" | "매입매출"; // 악성채권 진행상태 export type BadDebtProgress = "협의중" | "소송중" | "회수완료" | "대손처리" | ""; // 백엔드 API 응답 타입 (확장) export interface ClientApiResponse { id: number; tenant_id: number; client_group_id: number | null; client_code: string; name: string; contact_person: string | null; phone: string | null; email: string | null; address: string | null; business_no: string | null; business_type: string | null; business_item: string | null; is_active: boolean; created_at: string; updated_at: string; // 2차 추가 필드 (백엔드 완료 시 활성화) client_type?: ClientType; mobile?: string | null; fax?: string | null; manager_name?: string | null; manager_tel?: string | null; system_manager?: string | null; account_id?: string | null; account_password?: string | null; purchase_payment_day?: string | null; sales_payment_day?: string | null; tax_agreement?: boolean; tax_amount?: number | null; tax_start_date?: string | null; tax_end_date?: string | null; bad_debt?: boolean; bad_debt_amount?: number | null; bad_debt_receive_date?: string | null; bad_debt_end_date?: string | null; bad_debt_progress?: BadDebtProgress; memo?: string | null; } // 프론트엔드 타입 (확장) export interface Client { id: string; code: string; name: string; businessNo: string; representative: string; // contact_person phone: string; address: string; email: string; businessType: string; businessItem: string; registeredDate: string; status: "활성" | "비활성"; groupId: string | null; groupName?: string; // 2차 추가 필드 clientType: ClientType; mobile: string; fax: string; managerName: string; managerTel: string; systemManager: string; accountId: string; accountPassword: string; purchasePaymentDay: string; salesPaymentDay: string; taxAgreement: boolean; taxAmount: string; taxStartDate: string; taxEndDate: string; badDebt: boolean; badDebtAmount: string; badDebtReceiveDate: string; badDebtEndDate: string; badDebtProgress: BadDebtProgress; memo: string; } // 페이지네이션 정보 export interface PaginationInfo { currentPage: number; lastPage: number; total: number; perPage: number; } // 검색 파라미터 export interface ClientSearchParams { page?: number; size?: number; q?: string; onlyActive?: boolean; } // 생성/수정 요청 타입 (확장) export interface ClientFormData { clientCode?: string; name: string; businessNo: string; representative: string; phone: string; address: string; email: string; businessType: string; businessItem: string; groupId?: string | null; isActive: boolean; // 2차 추가 필드 clientType: ClientType; mobile: string; fax: string; managerName: string; managerTel: string; systemManager: string; accountId: string; accountPassword: string; purchasePaymentDay: string; salesPaymentDay: string; taxAgreement: boolean; taxAmount: string; taxStartDate: string; taxEndDate: string; badDebt: boolean; badDebtAmount: string; badDebtReceiveDate: string; badDebtEndDate: string; badDebtProgress: BadDebtProgress; memo: string; } // 폼 초기값 export const INITIAL_CLIENT_FORM: ClientFormData = { name: "", businessNo: "", representative: "", phone: "", address: "", email: "", businessType: "", businessItem: "", groupId: null, isActive: true, clientType: "매입", mobile: "", fax: "", managerName: "", managerTel: "", systemManager: "", accountId: "", accountPassword: "", purchasePaymentDay: "말일", salesPaymentDay: "말일", taxAgreement: false, taxAmount: "", taxStartDate: "", taxEndDate: "", badDebt: false, badDebtAmount: "", badDebtReceiveDate: "", badDebtEndDate: "", badDebtProgress: "", memo: "", }; // ============================================ // 데이터 변환 유틸리티 // ============================================ // 거래처 유형 매핑 (API → Frontend) const CLIENT_TYPE_MAP: Record = { 'PURCHASE': '매입', 'SALES': '매출', 'BOTH': '매입매출', '매입': '매입', '매출': '매출', '매입매출': '매입매출', }; // 거래처 유형 역매핑 (Frontend → API) const CLIENT_TYPE_REVERSE_MAP: Record = { '매입': 'PURCHASE', '매출': 'SALES', '매입매출': 'BOTH', }; // 거래처 유형 변환 (API → Frontend) function mapClientType(apiValue: string | undefined): ClientType { if (!apiValue) return '매입'; return CLIENT_TYPE_MAP[apiValue] || '매입'; } // API 응답 → 프론트엔드 타입 변환 export function transformClientFromApi(api: ClientApiResponse): Client { return { id: String(api.id), code: api.client_code, name: api.name, representative: api.contact_person || "", phone: api.phone || "", email: api.email || "", address: api.address || "", businessNo: api.business_no || "", businessType: api.business_type || "", businessItem: api.business_item || "", registeredDate: api.created_at ? api.created_at.split(" ")[0] : "", status: api.is_active ? "활성" : "비활성", groupId: api.client_group_id ? String(api.client_group_id) : null, // 2차 추가 필드 clientType: mapClientType(api.client_type), mobile: api.mobile || "", fax: api.fax || "", managerName: api.manager_name || "", managerTel: api.manager_tel || "", systemManager: api.system_manager || "", accountId: api.account_id || "", accountPassword: "", // 비밀번호는 조회 시 비움 purchasePaymentDay: api.purchase_payment_day || "말일", salesPaymentDay: api.sales_payment_day || "말일", taxAgreement: api.tax_agreement || false, taxAmount: api.tax_amount ? String(api.tax_amount) : "", taxStartDate: api.tax_start_date || "", taxEndDate: api.tax_end_date || "", badDebt: api.bad_debt || false, badDebtAmount: api.bad_debt_amount ? String(api.bad_debt_amount) : "", badDebtReceiveDate: api.bad_debt_receive_date || "", badDebtEndDate: api.bad_debt_end_date || "", badDebtProgress: api.bad_debt_progress || "", memo: api.memo || "", }; } // 프론트엔드 → API 요청 변환 (생성) export function transformClientToApiCreate(form: ClientFormData): Record { return { client_code: form.clientCode, name: form.name, contact_person: form.representative || null, phone: form.phone || null, email: form.email || null, address: form.address || null, business_no: form.businessNo || null, business_type: form.businessType || null, business_item: form.businessItem || null, client_group_id: form.groupId ? Number(form.groupId) : null, is_active: form.isActive, // 2차 추가 필드 client_type: CLIENT_TYPE_REVERSE_MAP[form.clientType] || form.clientType, mobile: form.mobile || null, fax: form.fax || null, manager_name: form.managerName || null, manager_tel: form.managerTel || null, system_manager: form.systemManager || null, account_id: form.accountId || null, account_password: form.accountPassword || null, purchase_payment_day: form.purchasePaymentDay || null, sales_payment_day: form.salesPaymentDay || null, tax_agreement: form.taxAgreement, tax_amount: form.taxAmount ? Number(form.taxAmount) : null, tax_start_date: form.taxStartDate || null, tax_end_date: form.taxEndDate || null, bad_debt: form.badDebt, bad_debt_amount: form.badDebtAmount ? Number(form.badDebtAmount) : null, bad_debt_receive_date: form.badDebtReceiveDate || null, bad_debt_end_date: form.badDebtEndDate || null, bad_debt_progress: form.badDebtProgress || null, memo: form.memo || null, }; } // 프론트엔드 → API 요청 변환 (수정) export function transformClientToApiUpdate(form: ClientFormData): Record { const data: Record = { name: form.name, contact_person: form.representative || null, phone: form.phone || null, email: form.email || null, address: form.address || null, business_no: form.businessNo || null, business_type: form.businessType || null, business_item: form.businessItem || null, client_group_id: form.groupId ? Number(form.groupId) : null, is_active: form.isActive, // 2차 추가 필드 client_type: CLIENT_TYPE_REVERSE_MAP[form.clientType] || form.clientType, mobile: form.mobile || null, fax: form.fax || null, manager_name: form.managerName || null, manager_tel: form.managerTel || null, system_manager: form.systemManager || null, account_id: form.accountId || null, purchase_payment_day: form.purchasePaymentDay || null, sales_payment_day: form.salesPaymentDay || null, tax_agreement: form.taxAgreement, tax_amount: form.taxAmount ? Number(form.taxAmount) : null, tax_start_date: form.taxStartDate || null, tax_end_date: form.taxEndDate || null, bad_debt: form.badDebt, bad_debt_amount: form.badDebtAmount ? Number(form.badDebtAmount) : null, bad_debt_receive_date: form.badDebtReceiveDate || null, bad_debt_end_date: form.badDebtEndDate || null, bad_debt_progress: form.badDebtProgress || null, memo: form.memo || null, }; // 비밀번호는 입력한 경우에만 전송 if (form.accountPassword) { data.account_password = form.accountPassword; } return data; } // Client → ClientFormData 변환 (수정 시 폼 초기화용) export function clientToFormData(client: Client): ClientFormData { return { clientCode: client.code, name: client.name, businessNo: client.businessNo, representative: client.representative, phone: client.phone, address: client.address, email: client.email, businessType: client.businessType, businessItem: client.businessItem, groupId: client.groupId, isActive: client.status === "활성", clientType: client.clientType, mobile: client.mobile, fax: client.fax, managerName: client.managerName, managerTel: client.managerTel, systemManager: client.systemManager, accountId: client.accountId, accountPassword: "", // 비밀번호는 비움 purchasePaymentDay: client.purchasePaymentDay, salesPaymentDay: client.salesPaymentDay, taxAgreement: client.taxAgreement, taxAmount: client.taxAmount, taxStartDate: client.taxStartDate, taxEndDate: client.taxEndDate, badDebt: client.badDebt, badDebtAmount: client.badDebtAmount, badDebtReceiveDate: client.badDebtReceiveDate, badDebtEndDate: client.badDebtEndDate, badDebtProgress: client.badDebtProgress, memo: client.memo, }; } // ============================================ // useClientList 훅 // ============================================ export function useClientList() { const [clients, setClients] = useState([]); const [pagination, setPagination] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // 목록 조회 const fetchClients = useCallback(async (params: ClientSearchParams = {}) => { setIsLoading(true); setError(null); try { const searchParams = new URLSearchParams(); if (params.page) searchParams.set("page", String(params.page)); if (params.size) searchParams.set("size", String(params.size)); if (params.q) searchParams.set("q", params.q); if (params.onlyActive !== undefined) { searchParams.set("only_active", params.onlyActive ? "1" : "0"); } const response = await fetch(`/api/proxy/clients?${searchParams.toString()}`); if (!response.ok) { throw new Error(`API 오류: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { const apiClients: ClientApiResponse[] = result.data.data || []; const transformedClients = apiClients.map(transformClientFromApi); setClients(transformedClients); setPagination({ currentPage: result.data.current_page || 1, lastPage: result.data.last_page || 1, total: result.data.total || 0, perPage: result.data.per_page || 20, }); } else { throw new Error(result.message || "데이터 조회 실패"); } } catch (err) { const errorMessage = err instanceof Error ? err.message : "알 수 없는 오류"; setError(errorMessage); setClients([]); setPagination(null); } finally { setIsLoading(false); } }, []); // 단건 조회 const fetchClient = useCallback(async (id: string): Promise => { try { const response = await fetch(`/api/proxy/clients/${id}`); if (!response.ok) { throw new Error(`API 오류: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { return transformClientFromApi(result.data); } return null; } catch (err) { console.error("거래처 조회 실패:", err); return null; } }, []); // 생성 const createClient = useCallback(async (formData: ClientFormData): Promise => { try { const response = await fetch("/api/proxy/clients", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(transformClientToApiCreate(formData)), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `API 오류: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { return transformClientFromApi(result.data); } return null; } catch (err) { console.error("거래처 생성 실패:", err); throw err; } }, []); // 수정 const updateClient = useCallback(async (id: string, formData: ClientFormData): Promise => { try { const response = await fetch(`/api/proxy/clients/${id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(transformClientToApiUpdate(formData)), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `API 오류: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { return transformClientFromApi(result.data); } return null; } catch (err) { console.error("거래처 수정 실패:", err); throw err; } }, []); // 삭제 const deleteClient = useCallback(async (id: string): Promise => { try { const response = await fetch(`/api/proxy/clients/${id}`, { method: "DELETE", }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `API 오류: ${response.status}`); } return true; } catch (err) { console.error("거래처 삭제 실패:", err); throw err; } }, []); // 활성/비활성 토글 const toggleClientStatus = useCallback(async (id: string): Promise => { try { const response = await fetch(`/api/proxy/clients/${id}/toggle`, { method: "PATCH", }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || `API 오류: ${response.status}`); } const result = await response.json(); if (result.success && result.data) { return transformClientFromApi(result.data); } return null; } catch (err) { console.error("거래처 상태 변경 실패:", err); throw err; } }, []); return { // 상태 clients, pagination, isLoading, error, // 액션 fetchClients, fetchClient, createClient, updateClient, deleteClient, toggleClientStatus, // 유틸리티 setClients, }; }