feat(WEB): 작업지시 수정 페이지 및 생산 관리 기능 개선
신규 기능: - 작업지시 수정 페이지 추가 (/production/work-orders/[id]/edit) - WorkOrderEdit 컴포넌트 신규 생성 - bulk-actions.ts 일괄 작업 유틸리티 추가 - toast-utils.ts 알림 유틸리티 추가 기능 개선: - ProductionDashboard 대시보드 액션 및 표시 개선 - WorkOrderCreate 생성 화면 개선 - WorkResultList 작업 결과 목록 타입 및 표시 개선 - EstimateDetailForm 견적 폼 개선 - QuoteRegistration 견적 등록 개선 - client-management-sales-admin 거래처 관리 개선 - error-handler.ts 에러 처리 개선
This commit is contained in:
@@ -178,6 +178,9 @@ export function QuoteRegistration({
|
||||
const [isLoadingProducts, setIsLoadingProducts] = useState(false);
|
||||
const [isCalculating, setIsCalculating] = useState(false);
|
||||
|
||||
// 카테고리별 완제품 캐시 (API 재호출 최소화)
|
||||
const [categoryProductsCache, setCategoryProductsCache] = useState<Record<string, FinishedGoods[]>>({});
|
||||
|
||||
// 거래처 목록 상태 (API에서 로드)
|
||||
const [clients, setClients] = useState<Vendor[]>([]);
|
||||
const [isLoadingClients, setIsLoadingClients] = useState(false);
|
||||
@@ -203,14 +206,20 @@ export function QuoteRegistration({
|
||||
}, 0);
|
||||
}, [calculationResults, formData.items]);
|
||||
|
||||
// 컴포넌트 마운트 시 완제품 목록 로드
|
||||
// 컴포넌트 마운트 시 완제품 목록 로드 (초기 로드는 size 제한 없이 - 카테고리별 호출로 대체됨)
|
||||
useEffect(() => {
|
||||
const loadFinishedGoods = async () => {
|
||||
const loadInitialProducts = async () => {
|
||||
setIsLoadingProducts(true);
|
||||
try {
|
||||
// 초기에는 ALL 카테고리로 로드 (size 제한 내에서)
|
||||
const result = await getFinishedGoods();
|
||||
if (result.success) {
|
||||
setFinishedGoods(result.data);
|
||||
// 캐시에도 저장
|
||||
setCategoryProductsCache(prev => ({
|
||||
...prev,
|
||||
"ALL": result.data
|
||||
}));
|
||||
} else {
|
||||
toast.error(`완제품 목록 로드 실패: ${result.error}`);
|
||||
}
|
||||
@@ -221,7 +230,7 @@ export function QuoteRegistration({
|
||||
setIsLoadingProducts(false);
|
||||
}
|
||||
};
|
||||
loadFinishedGoods();
|
||||
loadInitialProducts();
|
||||
}, []);
|
||||
|
||||
// 컴포넌트 마운트 시 거래처 목록 로드
|
||||
@@ -280,12 +289,41 @@ export function QuoteRegistration({
|
||||
}
|
||||
}, [editingQuote]);
|
||||
|
||||
// 카테고리별 완제품 필터링
|
||||
// 카테고리별 완제품 로드 (API 호출)
|
||||
const loadProductsByCategory = async (category: string) => {
|
||||
// 이미 캐시에 있으면 스킵
|
||||
if (categoryProductsCache[category]) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoadingProducts(true);
|
||||
try {
|
||||
// 카테고리가 ALL이면 전체, 아니면 해당 카테고리만 조회
|
||||
const result = await getFinishedGoods(category === "ALL" ? undefined : category);
|
||||
if (result.success) {
|
||||
setCategoryProductsCache(prev => ({
|
||||
...prev,
|
||||
[category]: result.data
|
||||
}));
|
||||
} else {
|
||||
toast.error(`완제품 목록 로드 실패: ${result.error}`);
|
||||
}
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
toast.error("완제품 목록을 불러오는데 실패했습니다.");
|
||||
} finally {
|
||||
setIsLoadingProducts(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 카테고리별 완제품 조회 (캐시 기반)
|
||||
const getFilteredProducts = (category: string) => {
|
||||
if (!category || category === "ALL") {
|
||||
return finishedGoods; // 전체 선택 시 모든 완제품
|
||||
// 전체 선택 시 캐시된 ALL 데이터 또는 초기 finishedGoods
|
||||
return categoryProductsCache["ALL"] || finishedGoods;
|
||||
}
|
||||
return finishedGoods.filter(fg => fg.item_category === category);
|
||||
// 카테고리별 캐시 반환
|
||||
return categoryProductsCache[category] || [];
|
||||
};
|
||||
|
||||
// 유효성 검사
|
||||
@@ -395,9 +433,11 @@ export function QuoteRegistration({
|
||||
const newItems = [...formData.items];
|
||||
newItems[index] = { ...newItems[index], [field]: value };
|
||||
|
||||
// 제품 카테고리 변경 시 제품명 초기화
|
||||
if (field === "productCategory") {
|
||||
// 제품 카테고리 변경 시 제품명 초기화 및 해당 카테고리 제품 로드
|
||||
if (field === "productCategory" && typeof value === "string") {
|
||||
newItems[index].productName = "";
|
||||
// 해당 카테고리 제품 목록 API 호출 (캐시 없으면)
|
||||
loadProductsByCategory(value);
|
||||
}
|
||||
|
||||
setFormData({ ...formData, items: newItems });
|
||||
|
||||
@@ -810,7 +810,7 @@ export async function getFinishedGoods(category?: string): Promise<{
|
||||
if (category) {
|
||||
searchParams.set('item_category', category);
|
||||
}
|
||||
searchParams.set('size', '1000'); // 전체 조회
|
||||
searchParams.set('size', '5000'); // 전체 조회 (테넌트별 FG 품목 수에 따라 조정)
|
||||
|
||||
const url = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/items?${searchParams.toString()}`;
|
||||
|
||||
@@ -863,8 +863,8 @@ export async function getFinishedGoods(category?: string): Promise<{
|
||||
success: true,
|
||||
data: items.map((item: Record<string, unknown>) => ({
|
||||
id: item.id,
|
||||
item_code: item.code as string, // Item 모델은 'code' 필드 사용
|
||||
item_name: item.name as string, // Item 모델은 'name' 필드 사용
|
||||
item_code: (item.item_code || item.code) as string, // API가 code → item_code로 변환
|
||||
item_name: item.name as string,
|
||||
item_category: (item.item_category as string) || '',
|
||||
specification: item.specification as string | undefined,
|
||||
unit: item.unit as string | undefined,
|
||||
|
||||
Reference in New Issue
Block a user