refactor: WorkerScreen 컴포넌트 기획 디자인 적용
- WorkCard: 헤더 박스(품목명+수량), 뱃지 영역, 담당자 정보, 버튼 레이아웃 개선 - ProcessDetailSection: 자재 투입 섹션, 공정 단계 뱃지, 검사 요청 AlertDialog 추가 - MaterialInputModal: FIFO 순위 설명, 테이블 형태 자재 목록, 중복 닫기 버튼 제거 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,18 +3,16 @@
|
||||
/**
|
||||
* 자재투입 모달
|
||||
*
|
||||
* - FIFO 순위 표시
|
||||
* - 자재 테이블 (BOM 기준)
|
||||
* - 투입 등록 기능
|
||||
* 기획 화면에 맞춘 레이아웃:
|
||||
* - FIFO 순위 설명 (1 최우선, 2 차선, 3+ 대기)
|
||||
* - ① 자재 선택 (BOM 기준) 테이블
|
||||
* - 취소 / 투입 등록 버튼 (전체 너비)
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Package } from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
@@ -64,13 +62,9 @@ interface MaterialInputModalProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
order: WorkOrder | null;
|
||||
/** 전량완료 흐름에서 사용 - 투입 등록/취소 후 완료 처리 */
|
||||
onComplete?: () => void;
|
||||
/** 전량완료 흐름 여부 (취소 시에도 완료 처리) */
|
||||
isCompletionFlow?: boolean;
|
||||
/** 자재 투입 저장 콜백 */
|
||||
onSaveMaterials?: (orderId: string, materials: MaterialInput[]) => void;
|
||||
/** 이미 투입된 자재 목록 */
|
||||
savedMaterials?: MaterialInput[];
|
||||
}
|
||||
|
||||
@@ -81,14 +75,10 @@ export function MaterialInputModal({
|
||||
onComplete,
|
||||
isCompletionFlow = false,
|
||||
onSaveMaterials,
|
||||
savedMaterials = [],
|
||||
}: MaterialInputModalProps) {
|
||||
const [selectedMaterials, setSelectedMaterials] = useState<Set<string>>(new Set());
|
||||
const [materials] = useState<MaterialInput[]>(MOCK_MATERIALS);
|
||||
|
||||
// 이미 투입된 자재가 있으면 선택 상태로 초기화
|
||||
const hasSavedMaterials = savedMaterials.length > 0;
|
||||
|
||||
const handleToggleMaterial = (materialId: string) => {
|
||||
setSelectedMaterials((prev) => {
|
||||
const next = new Set(prev);
|
||||
@@ -101,144 +91,153 @@ export function MaterialInputModal({
|
||||
});
|
||||
};
|
||||
|
||||
// 자재 투입 등록
|
||||
// 투입 등록
|
||||
const handleSubmit = () => {
|
||||
if (!order) return;
|
||||
|
||||
// 선택된 자재 정보 추출
|
||||
const selectedMaterialList = materials.filter((m) => selectedMaterials.has(m.id));
|
||||
console.log('[자재투입] 저장:', order.id, selectedMaterialList);
|
||||
|
||||
// 자재 저장 콜백
|
||||
if (onSaveMaterials) {
|
||||
onSaveMaterials(order.id, selectedMaterialList);
|
||||
}
|
||||
|
||||
setSelectedMaterials(new Set());
|
||||
onOpenChange(false);
|
||||
resetAndClose();
|
||||
|
||||
// 전량완료 흐름이면 완료 처리
|
||||
if (isCompletionFlow && onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
};
|
||||
|
||||
// 건너뛰기 (자재 없이 완료) - 전량완료 흐름에서만 사용
|
||||
const handleSkip = () => {
|
||||
setSelectedMaterials(new Set());
|
||||
onOpenChange(false);
|
||||
// 전량완료 흐름이면 완료 처리
|
||||
if (onComplete) {
|
||||
onComplete();
|
||||
}
|
||||
};
|
||||
|
||||
// 취소 (모달만 닫기)
|
||||
// 취소
|
||||
const handleCancel = () => {
|
||||
setSelectedMaterials(new Set());
|
||||
onOpenChange(false);
|
||||
resetAndClose();
|
||||
};
|
||||
|
||||
const getFifoRankBadge = (rank: number) => {
|
||||
const colors = {
|
||||
1: 'bg-red-100 text-red-800',
|
||||
2: 'bg-orange-100 text-orange-800',
|
||||
3: 'bg-gray-100 text-gray-800',
|
||||
};
|
||||
const labels = {
|
||||
1: '최우선',
|
||||
2: '차선',
|
||||
3: '대기',
|
||||
};
|
||||
return (
|
||||
<Badge className={colors[rank as 1 | 2 | 3] || colors[3]}>
|
||||
{rank}위 ({labels[rank as 1 | 2 | 3] || labels[3]})
|
||||
</Badge>
|
||||
);
|
||||
const resetAndClose = () => {
|
||||
setSelectedMaterials(new Set());
|
||||
onOpenChange(false);
|
||||
};
|
||||
|
||||
if (!order) return null;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Package className="h-5 w-5" />
|
||||
투입자재 등록
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
작업지시 {order.orderNo}에 투입할 자재를 선택하세요.
|
||||
</DialogDescription>
|
||||
<DialogContent className="max-w-2xl p-0 gap-0">
|
||||
{/* 헤더 */}
|
||||
<DialogHeader className="p-6 pb-4">
|
||||
<DialogTitle className="text-xl font-semibold">투입자재 등록</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* FIFO 순위 안내 */}
|
||||
<div className="flex items-center gap-4 text-sm text-muted-foreground">
|
||||
<span>FIFO 순위:</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Badge className="bg-red-100 text-red-800">1</Badge> 최우선
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Badge className="bg-orange-100 text-orange-800">2</Badge> 차선
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<Badge className="bg-gray-100 text-gray-800">3+</Badge> 대기
|
||||
</span>
|
||||
<div className="px-6 pb-6 space-y-6">
|
||||
{/* FIFO 순위 설명 */}
|
||||
<div className="flex items-center gap-4 p-4 bg-gray-50 rounded-lg">
|
||||
<span className="text-sm font-medium text-gray-700">FIFO 순위:</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Badge className="bg-gray-900 hover:bg-gray-900 text-white rounded-full w-6 h-6 flex items-center justify-center p-0 text-xs">
|
||||
1
|
||||
</Badge>
|
||||
<span className="text-sm text-gray-600">최우선</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Badge className="bg-gray-900 hover:bg-gray-900 text-white rounded-full w-6 h-6 flex items-center justify-center p-0 text-xs">
|
||||
2
|
||||
</Badge>
|
||||
<span className="text-sm text-gray-600">차선</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Badge className="bg-gray-900 hover:bg-gray-900 text-white rounded-full w-6 h-6 flex items-center justify-center p-0 text-xs">
|
||||
3+
|
||||
</Badge>
|
||||
<span className="text-sm text-gray-600">대기</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 자재 테이블 */}
|
||||
{materials.length === 0 ? (
|
||||
<div className="py-8 text-center text-muted-foreground border rounded-lg">
|
||||
이 공정에 배정된 자재가 없습니다.
|
||||
</div>
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-12">선택</TableHead>
|
||||
<TableHead>자재코드</TableHead>
|
||||
<TableHead>자재명</TableHead>
|
||||
<TableHead>단위</TableHead>
|
||||
<TableHead className="text-right">현재고</TableHead>
|
||||
<TableHead>FIFO</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{materials.map((material) => (
|
||||
<TableRow key={material.id}>
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
checked={selectedMaterials.has(material.id)}
|
||||
onCheckedChange={() => handleToggleMaterial(material.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">{material.materialCode}</TableCell>
|
||||
<TableCell>{material.materialName}</TableCell>
|
||||
<TableCell>{material.unit}</TableCell>
|
||||
<TableCell className="text-right">{material.currentStock.toLocaleString()}</TableCell>
|
||||
<TableCell>{getFifoRankBadge(material.fifoRank)}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
</div>
|
||||
{/* 자재 선택 섹션 */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-3">
|
||||
① 자재 선택 (BOM 기준)
|
||||
</h3>
|
||||
|
||||
<DialogFooter className="flex-col sm:flex-row gap-2">
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
취소
|
||||
</Button>
|
||||
{isCompletionFlow && (
|
||||
<Button variant="secondary" onClick={handleSkip}>
|
||||
건너뛰기
|
||||
{materials.length === 0 ? (
|
||||
<div className="border rounded-lg">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="bg-gray-50">
|
||||
<TableHead className="text-center">자재코드</TableHead>
|
||||
<TableHead className="text-center">자재명</TableHead>
|
||||
<TableHead className="text-center">단위</TableHead>
|
||||
<TableHead className="text-center">현재고</TableHead>
|
||||
<TableHead className="text-center">선택</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} className="text-center py-12 text-gray-500">
|
||||
이 공정에 배정된 자재가 없습니다.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="bg-gray-50">
|
||||
<TableHead className="text-center font-medium">자재코드</TableHead>
|
||||
<TableHead className="text-center font-medium">자재명</TableHead>
|
||||
<TableHead className="text-center font-medium">단위</TableHead>
|
||||
<TableHead className="text-center font-medium">현재고</TableHead>
|
||||
<TableHead className="text-center font-medium">선택</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{materials.map((material) => (
|
||||
<TableRow key={material.id}>
|
||||
<TableCell className="text-center font-medium">
|
||||
{material.materialCode}
|
||||
</TableCell>
|
||||
<TableCell className="text-center">{material.materialName}</TableCell>
|
||||
<TableCell className="text-center">{material.unit}</TableCell>
|
||||
<TableCell className="text-center">
|
||||
{material.currentStock.toLocaleString()}
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Checkbox
|
||||
checked={selectedMaterials.has(material.id)}
|
||||
onCheckedChange={() => handleToggleMaterial(material.id)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 버튼 영역 */}
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleCancel}
|
||||
className="flex-1 py-6 text-base font-medium"
|
||||
>
|
||||
취소
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={handleSubmit} disabled={selectedMaterials.size === 0}>
|
||||
투입 등록
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={selectedMaterials.size === 0}
|
||||
className="flex-1 py-6 text-base font-medium bg-gray-400 hover:bg-gray-500 disabled:bg-gray-300"
|
||||
>
|
||||
투입 등록
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user