|
|
|
|
@@ -15,7 +15,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
|
|
|
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
|
|
|
|
// API: /work-instruction/*
|
|
|
|
|
import {
|
|
|
|
|
getWorkInstructionList, previewWorkInstructionNo, saveWorkInstruction, deleteWorkInstructions,
|
|
|
|
|
getWorkInstructionList, previewWorkInstructionNo, saveWorkInstruction, deleteWorkInstructions, getBomBaseQtyMap,
|
|
|
|
|
getWIItemSource, getWISalesOrderSource, getWIProductionPlanSource, getEquipmentList, getEmployeeList,
|
|
|
|
|
getRoutingVersions, RoutingVersionData,
|
|
|
|
|
} from "@/lib/api/workInstruction";
|
|
|
|
|
@@ -65,6 +65,31 @@ interface SelectedItem {
|
|
|
|
|
equipmentIds?: string[];
|
|
|
|
|
workTeams?: string[];
|
|
|
|
|
workers?: string[];
|
|
|
|
|
// 기준수(BOM 0레벨 base_qty) / 배치수(자동) / 배분(균등|순차)
|
|
|
|
|
baseQty?: number | null;
|
|
|
|
|
splitMode?: "even" | "sequential";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 배치수 산출: baseQty>0 && qty>baseQty면 ceil(qty/baseQty), 아니면 1
|
|
|
|
|
function calcBatchCount(qty: number, baseQty: number | null | undefined): number {
|
|
|
|
|
const b = Number(baseQty || 0);
|
|
|
|
|
if (!Number.isFinite(b) || b <= 0) return 1;
|
|
|
|
|
if (!Number.isFinite(qty) || qty <= 0) return 1;
|
|
|
|
|
return qty > b ? Math.ceil(qty / b) : 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 분할 산출: batchCount<=1이면 [qty], 아니면 mode 따라 분할
|
|
|
|
|
function splitQty(qty: number, baseQty: number, batchCount: number, mode: "even" | "sequential"): number[] {
|
|
|
|
|
if (batchCount <= 1) return [qty];
|
|
|
|
|
if (mode === "sequential") {
|
|
|
|
|
const head = Array(batchCount - 1).fill(baseQty);
|
|
|
|
|
const tail = qty - baseQty * (batchCount - 1);
|
|
|
|
|
return [...head, tail];
|
|
|
|
|
}
|
|
|
|
|
// even: 앞은 floor, 마지막이 잔여 흡수
|
|
|
|
|
const base = Math.floor(qty / batchCount);
|
|
|
|
|
const remainder = qty - base * (batchCount - 1);
|
|
|
|
|
return [...Array(batchCount - 1).fill(base), remainder];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 공용 다중선택 Popover 컴포넌트 (설비/작업조/작업자에 재사용)
|
|
|
|
|
@@ -334,6 +359,19 @@ export default function WorkInstructionPage() {
|
|
|
|
|
}).catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BOM 기준수 일괄 조회 (품목별 base_qty 매핑)
|
|
|
|
|
if (uniqueItemCodes.length > 0) {
|
|
|
|
|
getBomBaseQtyMap(uniqueItemCodes).then(r => {
|
|
|
|
|
if (r.success && r.data) {
|
|
|
|
|
setConfirmItems(prev => prev.map(it => ({
|
|
|
|
|
...it,
|
|
|
|
|
baseQty: r.data[it.itemCode] ?? null,
|
|
|
|
|
splitMode: it.splitMode || "even",
|
|
|
|
|
})));
|
|
|
|
|
}
|
|
|
|
|
}).catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setIsRegModalOpen(false); setIsConfirmModalOpen(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -364,13 +402,26 @@ export default function WorkInstructionPage() {
|
|
|
|
|
const headerEquipment = first?.equipmentIds?.[0] || "";
|
|
|
|
|
const headerWorkTeam = first?.workTeams?.[0] || "";
|
|
|
|
|
const headerWorker = first?.workers?.[0] || "";
|
|
|
|
|
// 배치수≥2인 품목은 splitMode에 따라 N건으로 펼친다.
|
|
|
|
|
const expandedItems: Array<typeof confirmItems[number] & { _qty: number }> = [];
|
|
|
|
|
for (const i of confirmItems) {
|
|
|
|
|
const qty = Number(i.qty || 0);
|
|
|
|
|
const baseQty = Number(i.baseQty || 0);
|
|
|
|
|
const batchCount = calcBatchCount(qty, i.baseQty);
|
|
|
|
|
if (batchCount > 1 && baseQty > 0) {
|
|
|
|
|
const parts = splitQty(qty, baseQty, batchCount, i.splitMode || "even");
|
|
|
|
|
for (const p of parts) expandedItems.push({ ...i, _qty: p });
|
|
|
|
|
} else {
|
|
|
|
|
expandedItems.push({ ...i, _qty: qty });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const payload = {
|
|
|
|
|
status: confirmStatus,
|
|
|
|
|
startDate: headerStart, endDate: headerEnd,
|
|
|
|
|
equipmentId: headerEquipment, workTeam: headerWorkTeam, worker: headerWorker,
|
|
|
|
|
routing: confirmRouting || null,
|
|
|
|
|
items: confirmItems.map(i => ({
|
|
|
|
|
itemNumber: i.itemCode, itemCode: i.itemCode, qty: String(i.qty), remark: i.remark,
|
|
|
|
|
items: expandedItems.map(i => ({
|
|
|
|
|
itemNumber: i.itemCode, itemCode: i.itemCode, qty: String(i._qty), remark: i.remark,
|
|
|
|
|
sourceTable: i.sourceTable, sourceId: String(i.sourceId), partCode: i.itemCode,
|
|
|
|
|
routing: i.routing || null,
|
|
|
|
|
// 품목별 일정/설비/작업조/작업자 (옵션 A — 다중값 쉼표 구분)
|
|
|
|
|
@@ -764,7 +815,7 @@ export default function WorkInstructionPage() {
|
|
|
|
|
|
|
|
|
|
{/* ── 2단계: 확인 모달 ── */}
|
|
|
|
|
<Dialog open={isConfirmModalOpen} onOpenChange={setIsConfirmModalOpen}>
|
|
|
|
|
<DialogContent className="max-w-[95vw] sm:max-w-[1500px] w-[95vw] max-h-[90vh] flex flex-col overflow-hidden">
|
|
|
|
|
<DialogContent className="max-w-[98vw] sm:max-w-[1900px] w-[98vw] max-h-[92vh] flex flex-col overflow-hidden">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>작업지시 적용 확인</DialogTitle>
|
|
|
|
|
<DialogDescription>기본 정보를 입력하고 '최종 적용' 버튼을 눌러주세요.</DialogDescription>
|
|
|
|
|
@@ -785,32 +836,57 @@ export default function WorkInstructionPage() {
|
|
|
|
|
<div className="border rounded-lg p-5">
|
|
|
|
|
<h4 className="text-[13px] font-bold pb-2 border-b text-foreground mb-3">품목 목록</h4>
|
|
|
|
|
<div className="overflow-auto">
|
|
|
|
|
<Table className="min-w-[1500px]">
|
|
|
|
|
<Table className="min-w-[2200px]">
|
|
|
|
|
<TableHeader className="sticky top-0 z-10">
|
|
|
|
|
<TableRow className="bg-muted hover:bg-muted">
|
|
|
|
|
<TableHead className="w-[50px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">순번</TableHead>
|
|
|
|
|
<TableHead className="w-[110px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
|
|
|
|
<TableHead className="w-[140px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
|
|
|
|
<TableHead className="w-[80px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">규격</TableHead>
|
|
|
|
|
<TableHead className="w-[80px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">수량</TableHead>
|
|
|
|
|
<TableHead className="w-[150px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">라우팅</TableHead>
|
|
|
|
|
<TableHead className="w-[130px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">시작일</TableHead>
|
|
|
|
|
<TableHead className="w-[130px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">완료예정일</TableHead>
|
|
|
|
|
<TableHead className="w-[150px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">설비</TableHead>
|
|
|
|
|
<TableHead className="w-[120px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업조</TableHead>
|
|
|
|
|
<TableHead className="w-[150px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업자</TableHead>
|
|
|
|
|
<TableHead className="w-[140px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">비고</TableHead>
|
|
|
|
|
<TableHead className="sticky left-0 z-20 bg-muted w-[50px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">순번</TableHead>
|
|
|
|
|
<TableHead className="sticky left-[50px] z-20 bg-muted w-[130px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
|
|
|
|
<TableHead className="sticky left-[180px] z-20 bg-muted w-[180px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
|
|
|
|
<TableHead className="sticky left-[360px] z-20 bg-muted w-[100px] border-r text-[11px] font-bold uppercase tracking-wide text-muted-foreground">규격</TableHead>
|
|
|
|
|
<TableHead className="w-[120px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">수량</TableHead>
|
|
|
|
|
<TableHead className="w-[100px] text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">기준수</TableHead>
|
|
|
|
|
<TableHead className="w-[80px] text-center text-[11px] font-bold uppercase tracking-wide text-muted-foreground">배치수</TableHead>
|
|
|
|
|
<TableHead className="w-[140px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">배분</TableHead>
|
|
|
|
|
<TableHead className="w-[200px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">라우팅</TableHead>
|
|
|
|
|
<TableHead className="w-[220px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">시작일</TableHead>
|
|
|
|
|
<TableHead className="w-[220px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">완료예정일</TableHead>
|
|
|
|
|
<TableHead className="w-[200px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">설비</TableHead>
|
|
|
|
|
<TableHead className="w-[160px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업조</TableHead>
|
|
|
|
|
<TableHead className="w-[200px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업자</TableHead>
|
|
|
|
|
<TableHead className="w-[180px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">비고</TableHead>
|
|
|
|
|
<TableHead className="w-[40px]" />
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
<TableBody>
|
|
|
|
|
{confirmItems.map((item, idx) => (
|
|
|
|
|
<TableRow key={idx}>
|
|
|
|
|
<TableCell className="text-[13px] text-center">{idx + 1}</TableCell>
|
|
|
|
|
<TableCell className="text-[13px] font-medium">{item.itemCode}</TableCell>
|
|
|
|
|
<TableCell className="text-sm truncate max-w-[140px]" title={item.itemName || item.itemCode}>{item.itemName || item.itemCode}</TableCell>
|
|
|
|
|
<TableCell className="text-[13px]">{item.spec || "-"}</TableCell>
|
|
|
|
|
<TableCell><Input type="number" className="h-7 text-[13px] w-20" value={item.qty} onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, qty: Number(e.target.value) } : it))} /></TableCell>
|
|
|
|
|
{confirmItems.map((item, idx) => {
|
|
|
|
|
const batchCount = calcBatchCount(Number(item.qty || 0), item.baseQty);
|
|
|
|
|
const splitDisabled = batchCount <= 1 || !item.baseQty;
|
|
|
|
|
return (
|
|
|
|
|
<TableRow key={idx} className="bg-background">
|
|
|
|
|
<TableCell className="sticky left-0 z-10 bg-background text-[13px] text-center">{idx + 1}</TableCell>
|
|
|
|
|
<TableCell className="sticky left-[50px] z-10 bg-background text-[13px] font-medium">{item.itemCode}</TableCell>
|
|
|
|
|
<TableCell className="sticky left-[180px] z-10 bg-background text-sm truncate max-w-[180px]" title={item.itemName || item.itemCode}>{item.itemName || item.itemCode}</TableCell>
|
|
|
|
|
<TableCell className="sticky left-[360px] z-10 bg-background border-r text-[13px]">{item.spec || "-"}</TableCell>
|
|
|
|
|
<TableCell><Input type="number" className="h-9 text-sm w-full min-w-[100px]" value={item.qty} onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, qty: Number(e.target.value) } : it))} /></TableCell>
|
|
|
|
|
<TableCell className="text-right text-sm text-muted-foreground">
|
|
|
|
|
{item.baseQty != null && item.baseQty > 0 ? Number(item.baseQty).toLocaleString() : "-"}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell className={cn("text-center text-sm font-semibold", batchCount > 1 ? "text-primary" : "text-muted-foreground")}>
|
|
|
|
|
{item.baseQty != null && item.baseQty > 0 ? batchCount : "-"}
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Select
|
|
|
|
|
value={item.splitMode || "even"}
|
|
|
|
|
onValueChange={v => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, splitMode: v as "even" | "sequential" } : it))}
|
|
|
|
|
disabled={splitDisabled}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="h-9 text-sm"><SelectValue /></SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="even">균등분할</SelectItem>
|
|
|
|
|
<SelectItem value="sequential">순차분할</SelectItem>
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Select
|
|
|
|
|
value={nv(item.routing || "")}
|
|
|
|
|
@@ -819,7 +895,7 @@ export default function WorkInstructionPage() {
|
|
|
|
|
setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, routing: val } : it));
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="h-7 text-xs"><SelectValue placeholder="라우팅" /></SelectTrigger>
|
|
|
|
|
<SelectTrigger className="h-9 text-sm"><SelectValue placeholder="라우팅" /></SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="none">선택 안 함</SelectItem>
|
|
|
|
|
{(item.routingOptions || []).map((rv: RoutingVersionData) => (
|
|
|
|
|
@@ -831,10 +907,10 @@ export default function WorkInstructionPage() {
|
|
|
|
|
</Select>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Input type="date" className="h-7 text-[13px]" value={item.startDate || ""} onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, startDate: e.target.value } : it))} />
|
|
|
|
|
<Input type="date" className="h-9 text-sm w-full min-w-[200px]" value={item.startDate || ""} onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, startDate: e.target.value } : it))} />
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Input type="date" className="h-7 text-[13px]" value={item.endDate || ""} onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, endDate: e.target.value } : it))} />
|
|
|
|
|
<Input type="date" className="h-9 text-sm w-full min-w-[200px]" value={item.endDate || ""} onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, endDate: e.target.value } : it))} />
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<MultiSelectPopover
|
|
|
|
|
@@ -864,10 +940,11 @@ export default function WorkInstructionPage() {
|
|
|
|
|
emptyMessage="사원을 찾을 수 없어요"
|
|
|
|
|
/>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell><Input className="h-7 text-[13px]" value={item.remark} placeholder="비고" onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, remark: e.target.value } : it))} /></TableCell>
|
|
|
|
|
<TableCell><Input className="h-9 text-sm w-full min-w-[160px]" value={item.remark} placeholder="비고" onChange={e => setConfirmItems(prev => prev.map((it, i) => i === idx ? { ...it, remark: e.target.value } : it))} /></TableCell>
|
|
|
|
|
<TableCell><Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => setConfirmItems(prev => prev.filter((_, i) => i !== idx))}><X className="w-3 h-3 text-destructive" /></Button></TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
))}
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</TableBody>
|
|
|
|
|
</Table>
|
|
|
|
|
</div>
|
|
|
|
|
@@ -884,7 +961,7 @@ export default function WorkInstructionPage() {
|
|
|
|
|
|
|
|
|
|
{/* ── 수정 모달 ── */}
|
|
|
|
|
<Dialog open={isEditModalOpen} onOpenChange={(v) => { if (!v && wsModalOpen) return; setIsEditModalOpen(v); }}>
|
|
|
|
|
<DialogContent className="max-w-[95vw] sm:max-w-[1500px] w-[95vw] max-h-[90vh] flex flex-col overflow-hidden">
|
|
|
|
|
<DialogContent className="max-w-[98vw] sm:max-w-[1900px] w-[98vw] max-h-[92vh] flex flex-col overflow-hidden">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>{`작업지시 관리 - ${editOrder?.work_instruction_no || ""}`}</DialogTitle>
|
|
|
|
|
<DialogDescription>품목을 추가/삭제하고 정보를 수정해주세요.</DialogDescription>
|
|
|
|
|
@@ -907,22 +984,22 @@ export default function WorkInstructionPage() {
|
|
|
|
|
<span className="text-[11px] font-semibold text-primary bg-primary/8 border border-primary/15 px-2 py-0.5 rounded-full font-mono">{editItems.length}건</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="overflow-auto">
|
|
|
|
|
<Table className="min-w-[1500px]">
|
|
|
|
|
<Table className="min-w-[2200px]">
|
|
|
|
|
<TableHeader className="sticky top-0 z-10">
|
|
|
|
|
<TableRow className="bg-muted hover:bg-muted">
|
|
|
|
|
<TableHead className="w-[50px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">순번</TableHead>
|
|
|
|
|
<TableHead className="w-[110px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
|
|
|
|
<TableHead className="w-[140px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
|
|
|
|
<TableHead className="w-[80px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">규격</TableHead>
|
|
|
|
|
<TableHead className="w-[80px] text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">수량</TableHead>
|
|
|
|
|
<TableHead className="w-[150px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">라우팅</TableHead>
|
|
|
|
|
<TableHead className="w-[90px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">공정작업기준</TableHead>
|
|
|
|
|
<TableHead className="w-[130px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">시작일</TableHead>
|
|
|
|
|
<TableHead className="w-[130px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">완료예정일</TableHead>
|
|
|
|
|
<TableHead className="w-[150px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">설비</TableHead>
|
|
|
|
|
<TableHead className="w-[120px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업조</TableHead>
|
|
|
|
|
<TableHead className="w-[150px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업자</TableHead>
|
|
|
|
|
<TableHead className="w-[140px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">비고</TableHead>
|
|
|
|
|
<TableHead className="sticky left-0 z-20 bg-muted w-[50px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">순번</TableHead>
|
|
|
|
|
<TableHead className="sticky left-[50px] z-20 bg-muted w-[130px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목코드</TableHead>
|
|
|
|
|
<TableHead className="sticky left-[180px] z-20 bg-muted w-[180px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">품목명</TableHead>
|
|
|
|
|
<TableHead className="sticky left-[360px] z-20 bg-muted w-[100px] border-r text-[11px] font-bold uppercase tracking-wide text-muted-foreground">규격</TableHead>
|
|
|
|
|
<TableHead className="w-[120px] text-right text-[11px] font-bold uppercase tracking-wide text-muted-foreground">수량</TableHead>
|
|
|
|
|
<TableHead className="w-[200px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">라우팅</TableHead>
|
|
|
|
|
<TableHead className="w-[110px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">공정작업기준</TableHead>
|
|
|
|
|
<TableHead className="w-[220px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">시작일</TableHead>
|
|
|
|
|
<TableHead className="w-[220px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">완료예정일</TableHead>
|
|
|
|
|
<TableHead className="w-[200px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">설비</TableHead>
|
|
|
|
|
<TableHead className="w-[160px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업조</TableHead>
|
|
|
|
|
<TableHead className="w-[200px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">작업자</TableHead>
|
|
|
|
|
<TableHead className="w-[180px] text-[11px] font-bold uppercase tracking-wide text-muted-foreground">비고</TableHead>
|
|
|
|
|
<TableHead className="w-[40px]" />
|
|
|
|
|
</TableRow>
|
|
|
|
|
</TableHeader>
|
|
|
|
|
@@ -930,12 +1007,12 @@ export default function WorkInstructionPage() {
|
|
|
|
|
{editItems.length === 0 ? (
|
|
|
|
|
<TableRow><TableCell colSpan={14} className="text-center py-8 text-sm text-muted-foreground">등록된 품목이 없어요</TableCell></TableRow>
|
|
|
|
|
) : editItems.map((item, idx) => (
|
|
|
|
|
<TableRow key={idx}>
|
|
|
|
|
<TableCell className="text-[13px] text-center">{idx + 1}</TableCell>
|
|
|
|
|
<TableCell className="text-[13px] font-medium">{item.itemCode}</TableCell>
|
|
|
|
|
<TableCell className="text-sm truncate max-w-[140px]" title={item.itemName}>{item.itemName || "-"}</TableCell>
|
|
|
|
|
<TableCell className="text-[13px] truncate" title={item.spec}>{item.spec || "-"}</TableCell>
|
|
|
|
|
<TableCell className="text-right"><Input type="number" className="h-7 text-[13px] w-20 ml-auto" value={item.qty} onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, qty: Number(e.target.value) } : it))} /></TableCell>
|
|
|
|
|
<TableRow key={idx} className="bg-background">
|
|
|
|
|
<TableCell className="sticky left-0 z-10 bg-background text-[13px] text-center">{idx + 1}</TableCell>
|
|
|
|
|
<TableCell className="sticky left-[50px] z-10 bg-background text-[13px] font-medium">{item.itemCode}</TableCell>
|
|
|
|
|
<TableCell className="sticky left-[180px] z-10 bg-background text-sm truncate max-w-[180px]" title={item.itemName}>{item.itemName || "-"}</TableCell>
|
|
|
|
|
<TableCell className="sticky left-[360px] z-10 bg-background border-r text-[13px] truncate" title={item.spec}>{item.spec || "-"}</TableCell>
|
|
|
|
|
<TableCell className="text-right"><Input type="number" className="h-9 text-sm w-full min-w-[100px] ml-auto" value={item.qty} onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, qty: Number(e.target.value) } : it))} /></TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Select
|
|
|
|
|
value={nv(item.routing || "")}
|
|
|
|
|
@@ -944,7 +1021,7 @@ export default function WorkInstructionPage() {
|
|
|
|
|
setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, routing: val } : it));
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<SelectTrigger className="h-7 text-xs"><SelectValue placeholder="라우팅" /></SelectTrigger>
|
|
|
|
|
<SelectTrigger className="h-9 text-sm"><SelectValue placeholder="라우팅" /></SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
<SelectItem value="none">선택 안 함</SelectItem>
|
|
|
|
|
{(item.routingOptions || []).map((rv: RoutingVersionData) => (
|
|
|
|
|
@@ -959,7 +1036,7 @@ export default function WorkInstructionPage() {
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="h-7 text-xs"
|
|
|
|
|
className="h-9 text-sm"
|
|
|
|
|
disabled={!item.routing}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (!editOrder || !item.routing) return;
|
|
|
|
|
@@ -973,14 +1050,14 @@ export default function WorkInstructionPage() {
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ClipboardCheck className="w-3 h-3 mr-1" /> 수정
|
|
|
|
|
<ClipboardCheck className="w-3.5 h-3.5 mr-1" /> 수정
|
|
|
|
|
</Button>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Input type="date" className="h-7 text-[13px]" value={item.startDate || ""} onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, startDate: e.target.value } : it))} />
|
|
|
|
|
<Input type="date" className="h-9 text-sm w-full min-w-[200px]" value={item.startDate || ""} onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, startDate: e.target.value } : it))} />
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<Input type="date" className="h-7 text-[13px]" value={item.endDate || ""} onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, endDate: e.target.value } : it))} />
|
|
|
|
|
<Input type="date" className="h-9 text-sm w-full min-w-[200px]" value={item.endDate || ""} onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, endDate: e.target.value } : it))} />
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell>
|
|
|
|
|
<MultiSelectPopover
|
|
|
|
|
@@ -1010,7 +1087,7 @@ export default function WorkInstructionPage() {
|
|
|
|
|
emptyMessage="사원을 찾을 수 없어요"
|
|
|
|
|
/>
|
|
|
|
|
</TableCell>
|
|
|
|
|
<TableCell><Input className="h-7 text-[13px]" value={item.remark} placeholder="비고" onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, remark: e.target.value } : it))} /></TableCell>
|
|
|
|
|
<TableCell><Input className="h-9 text-sm w-full min-w-[160px]" value={item.remark} placeholder="비고" onChange={e => setEditItems(prev => prev.map((it, i) => i === idx ? { ...it, remark: e.target.value } : it))} /></TableCell>
|
|
|
|
|
<TableCell><Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => setEditItems(prev => prev.filter((_, i) => i !== idx))}><X className="w-3 h-3 text-destructive" /></Button></TableCell>
|
|
|
|
|
</TableRow>
|
|
|
|
|
))}
|
|
|
|
|
|