228 lines
6.7 KiB
TypeScript
228 lines
6.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
import { apiClient } from "@/lib/api/client";
|
|
import { SubDataLookupConfig, SubDataState } from "@/types/repeater";
|
|
|
|
const LOG_PREFIX = {
|
|
INFO: "[SubDataLookup]",
|
|
DEBUG: "[SubDataLookup]",
|
|
WARN: "[SubDataLookup]",
|
|
ERROR: "[SubDataLookup]",
|
|
};
|
|
|
|
export interface UseSubDataLookupProps {
|
|
config: SubDataLookupConfig;
|
|
linkValue: string | number | null; // 상위 항목의 연결 값 (예: item_code)
|
|
itemIndex: number; // 상위 항목 인덱스
|
|
enabled?: boolean; // 기능 활성화 여부
|
|
}
|
|
|
|
export interface UseSubDataLookupReturn {
|
|
data: any[]; // 조회된 하위 데이터
|
|
isLoading: boolean; // 로딩 상태
|
|
error: string | null; // 에러 메시지
|
|
selectedItem: any | null; // 선택된 하위 항목
|
|
setSelectedItem: (item: any | null) => void; // 선택 항목 설정
|
|
isInputEnabled: boolean; // 조건부 입력 활성화 여부
|
|
maxValue: number | null; // 최대 입력 가능 값
|
|
isExpanded: boolean; // 확장 상태
|
|
setIsExpanded: (expanded: boolean) => void; // 확장 상태 설정
|
|
refetch: () => void; // 데이터 재조회
|
|
getSelectionSummary: () => string; // 선택 요약 텍스트
|
|
}
|
|
|
|
/**
|
|
* 하위 데이터 조회 훅
|
|
* 품목 선택 시 재고/단가 등 관련 데이터를 조회하고 관리
|
|
*/
|
|
export function useSubDataLookup(props: UseSubDataLookupProps): UseSubDataLookupReturn {
|
|
const { config, linkValue, itemIndex, enabled = true } = props;
|
|
|
|
// 상태
|
|
const [data, setData] = useState<any[]>([]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [selectedItem, setSelectedItem] = useState<any | null>(null);
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
|
|
// 이전 linkValue 추적 (중복 호출 방지)
|
|
const prevLinkValueRef = useRef<string | number | null>(null);
|
|
|
|
// 데이터 조회 함수
|
|
const fetchData = useCallback(async () => {
|
|
// 비활성화 또는 linkValue 없으면 스킵
|
|
if (!enabled || !config?.enabled || !linkValue) {
|
|
console.log(`${LOG_PREFIX.DEBUG} 조회 스킵:`, {
|
|
enabled,
|
|
configEnabled: config?.enabled,
|
|
linkValue,
|
|
itemIndex,
|
|
});
|
|
setData([]);
|
|
setSelectedItem(null);
|
|
return;
|
|
}
|
|
|
|
const { tableName, linkColumn, additionalFilters } = config.lookup;
|
|
|
|
if (!tableName || !linkColumn) {
|
|
console.warn(`${LOG_PREFIX.WARN} 필수 설정 누락:`, { tableName, linkColumn });
|
|
return;
|
|
}
|
|
|
|
console.log(`${LOG_PREFIX.INFO} 하위 데이터 조회 시작:`, {
|
|
tableName,
|
|
linkColumn,
|
|
linkValue,
|
|
itemIndex,
|
|
});
|
|
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// 검색 조건 구성 - 정확한 값 매칭을 위해 equals 연산자 사용
|
|
const searchCondition: Record<string, any> = {
|
|
[linkColumn]: { value: linkValue, operator: "equals" },
|
|
...additionalFilters,
|
|
};
|
|
|
|
console.log(`${LOG_PREFIX.DEBUG} API 요청 조건:`, {
|
|
tableName,
|
|
linkColumn,
|
|
linkValue,
|
|
searchCondition,
|
|
});
|
|
|
|
const response = await apiClient.post(`/table-management/tables/${tableName}/data`, {
|
|
page: 1,
|
|
size: 100,
|
|
search: searchCondition,
|
|
autoFilter: { enabled: true },
|
|
});
|
|
|
|
if (response.data?.success) {
|
|
const items = response.data?.data?.data || response.data?.data || [];
|
|
console.log(`${LOG_PREFIX.DEBUG} API 응답:`, {
|
|
dataCount: items.length,
|
|
firstItem: items[0],
|
|
tableName,
|
|
});
|
|
setData(items);
|
|
} else {
|
|
console.warn(`${LOG_PREFIX.WARN} API 응답 실패:`, response.data);
|
|
setData([]);
|
|
setError("데이터 조회에 실패했습니다");
|
|
}
|
|
} catch (err: any) {
|
|
console.error(`${LOG_PREFIX.ERROR} 하위 데이터 조회 실패:`, {
|
|
error: err.message,
|
|
config,
|
|
linkValue,
|
|
});
|
|
setError(err.message || "데이터 조회 중 오류가 발생했습니다");
|
|
setData([]);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
}, [enabled, config, linkValue, itemIndex]);
|
|
|
|
// linkValue 변경 시 데이터 조회
|
|
useEffect(() => {
|
|
// 같은 값이면 스킵
|
|
if (prevLinkValueRef.current === linkValue) {
|
|
return;
|
|
}
|
|
prevLinkValueRef.current = linkValue;
|
|
|
|
// linkValue가 없으면 초기화
|
|
if (!linkValue) {
|
|
setData([]);
|
|
setSelectedItem(null);
|
|
setIsExpanded(false);
|
|
return;
|
|
}
|
|
|
|
fetchData();
|
|
}, [linkValue, fetchData]);
|
|
|
|
// 조건부 입력 활성화 여부 계산
|
|
const isInputEnabled = useCallback((): boolean => {
|
|
if (!config?.enabled || !selectedItem) {
|
|
return false;
|
|
}
|
|
|
|
const { requiredFields, requiredMode = "all" } = config.selection;
|
|
|
|
if (!requiredFields || requiredFields.length === 0) {
|
|
// 필수 필드가 없으면 선택만 하면 활성화
|
|
return true;
|
|
}
|
|
|
|
// 선택된 항목에서 필수 필드 값 확인
|
|
if (requiredMode === "any") {
|
|
// 하나라도 있으면 OK
|
|
return requiredFields.some((field) => {
|
|
const value = selectedItem[field];
|
|
return value !== undefined && value !== null && value !== "";
|
|
});
|
|
} else {
|
|
// 모두 있어야 OK
|
|
return requiredFields.every((field) => {
|
|
const value = selectedItem[field];
|
|
return value !== undefined && value !== null && value !== "";
|
|
});
|
|
}
|
|
}, [config, selectedItem]);
|
|
|
|
// 최대값 계산
|
|
const getMaxValue = useCallback((): number | null => {
|
|
if (!config?.enabled || !selectedItem) {
|
|
return null;
|
|
}
|
|
|
|
const { maxValueField } = config.conditionalInput;
|
|
if (!maxValueField) {
|
|
return null;
|
|
}
|
|
|
|
const maxValue = selectedItem[maxValueField];
|
|
return typeof maxValue === "number" ? maxValue : parseFloat(maxValue) || null;
|
|
}, [config, selectedItem]);
|
|
|
|
// 선택 요약 텍스트 생성
|
|
const getSelectionSummary = useCallback((): string => {
|
|
if (!selectedItem) {
|
|
return "선택 안됨";
|
|
}
|
|
|
|
const { displayColumns, columnLabels } = config.lookup;
|
|
const parts: string[] = [];
|
|
|
|
displayColumns.forEach((col) => {
|
|
const value = selectedItem[col];
|
|
if (value !== undefined && value !== null && value !== "") {
|
|
const label = columnLabels?.[col] || col;
|
|
parts.push(`${label}: ${value}`);
|
|
}
|
|
});
|
|
|
|
return parts.length > 0 ? parts.join(", ") : "선택됨";
|
|
}, [selectedItem, config?.lookup]);
|
|
|
|
return {
|
|
data,
|
|
isLoading,
|
|
error,
|
|
selectedItem,
|
|
setSelectedItem,
|
|
isInputEnabled: isInputEnabled(),
|
|
maxValue: getMaxValue(),
|
|
isExpanded,
|
|
setIsExpanded,
|
|
refetch: fetchData,
|
|
getSelectionSummary,
|
|
};
|
|
}
|