fix(WEB): 자재투입 모달 UX 개선 - 선택 유지/중복투입 차단/버튼 UI

- 자재 투입 후 전체 새로고침 제거, 로컬 오버라이드로 현재 수주 선택 유지
- 자동선택 useEffect에 현재 선택 유효 가드 추가
- API remainingRequiredQty 활용하여 이미 충족된 품목 추가 선택 차단
- 기투입 수량 표시 및 '투입 완료' 뱃지 표시
- 체크박스 → 버튼 형태(선택/선택됨)로 변경
- 수량 소수점 불필요 자릿수 제거 (parseFloat 래핑)
This commit is contained in:
2026-02-12 20:07:31 +09:00
parent cbb38d48b9
commit 14af77ca65
7 changed files with 770 additions and 181 deletions

View File

@@ -11,10 +11,13 @@
* - 양식 미매핑 시 processType 폴백
*/
import { useState, useEffect } from 'react';
import { Loader2 } from 'lucide-react';
import { useState, useEffect, useCallback } from 'react';
import { Loader2, Save } from 'lucide-react';
import { DocumentViewer } from '@/components/document-system';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
import { getWorkOrderById, getMaterialInputLots } from '../WorkOrders/actions';
import { saveWorkLog } from './actions';
import type { MaterialInputLot } from '../WorkOrders/actions';
import type { WorkOrder, ProcessType } from '../WorkOrders/types';
import { WorkLogContent } from './WorkLogContent';
@@ -58,6 +61,7 @@ export function WorkLogModal({
const [order, setOrder] = useState<WorkOrder | null>(null);
const [materialLots, setMaterialLots] = useState<MaterialInputLot[]>([]);
const [isLoading, setIsLoading] = useState(false);
const [isSaving, setIsSaving] = useState(false);
const [error, setError] = useState<string | null>(null);
// 목업 WorkOrder 생성
@@ -136,6 +140,38 @@ export function WorkLogModal({
}
}, [open, workOrderId, processType]);
// 저장 핸들러
const handleSave = useCallback(async () => {
if (!workOrderId || !order) return;
setIsSaving(true);
try {
// 현재 아이템 데이터를 table_data로 변환
const tableData = (order.items || []).map((item) => ({
id: item.id,
item_name: item.productName,
specification: item.specification || item.floorCode,
quantity: item.quantity,
unit: item.unit || 'EA',
}));
const result = await saveWorkLog(workOrderId, {
table_data: tableData,
title: workLogTemplateName || '작업일지',
});
if (result.success) {
toast.success('작업일지가 저장되었습니다.');
} else {
toast.error(result.error || '저장에 실패했습니다.');
}
} catch {
toast.error('저장 중 오류가 발생했습니다.');
} finally {
setIsSaving(false);
}
}, [workOrderId, order, workLogTemplateName]);
if (!workOrderId) return null;
// 로딩/에러 상태는 DocumentViewer 내부에서 처리
@@ -183,6 +219,17 @@ export function WorkLogModal({
// 양식명으로 문서 제목 결정
const documentTitle = workLogTemplateName || '작업일지';
const toolbarExtra = (
<Button onClick={handleSave} disabled={isSaving} size="sm">
{isSaving ? (
<Loader2 className="w-4 h-4 mr-1.5 animate-spin" />
) : (
<Save className="w-4 h-4 mr-1.5" />
)}
{isSaving ? '저장 중...' : '저장'}
</Button>
);
return (
<DocumentViewer
title={documentTitle}
@@ -190,6 +237,7 @@ export function WorkLogModal({
preset="inspection"
open={open}
onOpenChange={onOpenChange}
toolbarExtra={toolbarExtra}
>
{isLoading ? (
<div className="flex items-center justify-center h-64 bg-white">