diff --git a/frontend/app/(main)/COMPANY_7/logistics/warehouse/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/warehouse/page.tsx index 544ab0c4..b0df815f 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/warehouse/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/warehouse/page.tsx @@ -55,6 +55,7 @@ import { Layers, Info, Eye, + GripVertical, } from "lucide-react"; import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; @@ -149,7 +150,6 @@ export default function WarehouseManagementPage() { // 모달: 랙 구조 일괄 등록 const [rackModalOpen, setRackModalOpen] = useState(false); - const [rackFloor, setRackFloor] = useState(""); const [rackZone, setRackZone] = useState(""); const [rackConditions, setRackConditions] = useState< { id: string; startRow: number; endRow: number; levels: number }[] @@ -162,6 +162,10 @@ export default function WarehouseManagementPage() { const [rackZoneLabel, setRackZoneLabel] = useState("구역"); const [rackRowLabel, setRackRowLabel] = useState("열"); const [rackLevelLabel, setRackLevelLabel] = useState("단"); + // 위치명 세그먼트 순서 (드래그로 변경 가능) + const [rackSegmentOrder, setRackSegmentOrder] = useState<("zone" | "row" | "level")[]>(["zone", "row", "level"]); + const [draggedSegment, setDraggedSegment] = useState(null); + const [dragOverSegment, setDragOverSegment] = useState(null); // 카테고리 옵션 const [categoryOptions, setCategoryOptions] = useState< @@ -199,7 +203,7 @@ export default function WarehouseManagementPage() { setCategoryOptions(whOpts); const locOpts: Record = {}; - for (const col of ["location_type", "status", "floor", "zone"]) { + for (const col of ["location_type", "status", "zone"]) { try { const res = await apiClient.get( `/table-categories/${LOCATION_TABLE}/${col}/values` @@ -512,7 +516,7 @@ export default function WarehouseManagementPage() { warehouse_code: locationForm.warehouse_code || selectedWarehouse?.warehouse_code || "", location_code: finalLocationCode, location_name: locationForm.location_name?.trim(), - floor: locationForm.floor || "", + floor: "", zone: locationForm.zone || "", row_num: locationForm.row_num || "", level_num: locationForm.level_num || "", @@ -570,13 +574,16 @@ export default function WarehouseManagementPage() { // ─── 랙 구조 일괄 등록 ─── const openRackModal = () => { - setRackFloor(""); setRackZone(""); setRackConditions([]); setRackLocationType(""); setRackStatus(""); setRackPreview([]); setRackSaving(false); + setRackSegmentOrder(["zone", "row", "level"]); + setRackZoneLabel("구역"); + setRackRowLabel("열"); + setRackLevelLabel("단"); setRackModalOpen(true); }; @@ -601,8 +608,8 @@ export default function WarehouseManagementPage() { }; const generateRackPreview = () => { - if (!rackFloor.trim() || !rackZone.trim()) { - toast.error("층과 구역을 입력해주세요"); + if (!rackZone.trim()) { + toast.error("구역을 선택해주세요"); return; } if (rackConditions.length === 0) { @@ -623,11 +630,8 @@ export default function WarehouseManagementPage() { const whCode = selectedWarehouse?.warehouse_code || ""; // 카테고리 코드→라벨 변환 (셀렉트에서 코드가 저장되므로) - const floorOpts = locationCategoryOptions["floor"] || []; const zoneOpts = locationCategoryOptions["zone"] || []; - const floorLabel = floorOpts.find(o => o.code === rackFloor)?.label || rackFloor.trim(); const zoneLabel = zoneOpts.find(o => o.code === rackZone)?.label || rackZone.trim(); - const floorCode = floorLabel.replace(/층$/, ""); const zoneCode = zoneLabel.replace(/구역$/, ""); // 기존 위치코드 Set (중복 체크용) @@ -640,7 +644,14 @@ export default function WarehouseManagementPage() { for (let row = cond.startRow; row <= cond.endRow; row++) { for (let level = 1; level <= cond.levels; level++) { const rowStr = String(row).padStart(2, "0"); - const locationCode = `${whCode}-${floorCode}${zoneCode}-${rowStr}-${level}`; + // 세그먼트 순서에 따라 위치코드/위치명 조립 + const segCodeMap: Record = { zone: zoneCode, row: rowStr, level: String(level) }; + const segNameMap: Record = { + zone: `${zoneCode}${rackZoneLabel}`, + row: `${rowStr}${rackRowLabel}`, + level: `${level}${rackLevelLabel}`, + }; + const locationCode = `${whCode}-${rackSegmentOrder.map(s => segCodeMap[s]).join("-")}`; // 미리보기 내부 중복 제거 if (seen.has(locationCode)) continue; seen.add(locationCode); @@ -649,12 +660,12 @@ export default function WarehouseManagementPage() { duplicates.push(locationCode); continue; } - const locationName = `${zoneCode}${rackZoneLabel}-${rowStr}${rackRowLabel}-${level}${rackLevelLabel}`; + const locationName = rackSegmentOrder.map(s => segNameMap[s]).join("-"); items.push({ location_code: locationCode, location_name: locationName, warehouse_code: whCode, - floor: floorLabel, + floor: "", zone: zoneLabel, row_num: String(row), level_num: String(level), @@ -924,10 +935,9 @@ export default function WarehouseManagementPage() { # 위치코드 위치명 - - 구역 - + 구역 + 유형 상태 @@ -960,10 +970,9 @@ export default function WarehouseManagementPage() { {loc.location_name} - {loc.floor} - {loc.zone} - {loc.row_num} + {loc.zone} {loc.level_num} + {loc.row_num} - {/* 층 */} -
- - - setLocationForm((prev) => ({ ...prev, floor: e.target.value })) - } - placeholder="층을 입력해주세요" - /> -
{/* 구역 */}
@@ -1296,7 +1294,7 @@ export default function WarehouseManagementPage() {

📍 기본 정보

-
+
-
- - {(locationCategoryOptions["floor"] || []).length > 0 ? ( - - ) : ( - setRackFloor(e.target.value)} - placeholder="예: B1, 1F, 2F" - /> - )} -
- {/* 위치명 형식 — 구역/열/단 뒤에 붙일 표현만 자유 입력 */} + {/* 위치명 형식 — 드래그로 세그먼트 순서 변경 가능 */}
-
- A - setRackZoneLabel(e.target.value)} - placeholder="구역" - className="h-8 w-20 text-xs" - /> - - 01 - setRackRowLabel(e.target.value)} - placeholder="열" - className="h-8 w-20 text-xs" - /> - - 1 - setRackLevelLabel(e.target.value)} - placeholder="단" - className="h-8 w-20 text-xs" - /> +

세그먼트를 드래그하여 순서를 변경할 수 있습니다

+
+ {rackSegmentOrder.map((seg, idx) => { + const config: Record void; placeholder: string; name: string }> = { + zone: { example: "A", label: rackZoneLabel, setLabel: setRackZoneLabel, placeholder: "구역", name: "구역" }, + row: { example: "01", label: rackRowLabel, setLabel: setRackRowLabel, placeholder: "열", name: "열" }, + level: { example: "1", label: rackLevelLabel, setLabel: setRackLevelLabel, placeholder: "단", name: "단" }, + }; + const c = config[seg]; + return ( + + {idx > 0 && -} +
{ + setDraggedSegment(seg); + e.dataTransfer.effectAllowed = "move"; + }} + onDragOver={(e) => { + e.preventDefault(); + e.dataTransfer.dropEffect = "move"; + setDragOverSegment(seg); + }} + onDragLeave={() => setDragOverSegment(null)} + onDrop={(e) => { + e.preventDefault(); + if (!draggedSegment || draggedSegment === seg) { + setDraggedSegment(null); + setDragOverSegment(null); + return; + } + setRackSegmentOrder((prev) => { + const next = [...prev]; + const fromIdx = next.indexOf(draggedSegment as any); + const toIdx = next.indexOf(seg as any); + next.splice(fromIdx, 1); + next.splice(toIdx, 0, draggedSegment as any); + return next; + }); + setDraggedSegment(null); + setDragOverSegment(null); + }} + onDragEnd={() => { setDraggedSegment(null); setDragOverSegment(null); }} + className={cn( + "flex items-center gap-1 rounded-md border px-2 py-1.5 cursor-grab active:cursor-grabbing transition-all", + draggedSegment === seg && "opacity-50 scale-95", + dragOverSegment === seg && draggedSegment !== seg && "ring-2 ring-primary border-primary bg-primary/5", + "bg-card hover:bg-accent/50" + )} + > + + {c.example} + c.setLabel(e.target.value)} + placeholder={c.placeholder} + className="h-6 w-14 text-[11px] px-1.5" + onClick={(e) => e.stopPropagation()} + onMouseDown={(e) => e.stopPropagation()} + draggable={false} + /> +
+
+ ); + })}

- 예시: A{rackZoneLabel}-01{rackRowLabel}-1{rackLevelLabel} - {" "}— 구역/열/단 번호는 자동 계산되고, 뒤에 붙는 명칭만 수정할 수 있습니다. + 예시: + {rackSegmentOrder.map((seg) => { + const ex: Record = { zone: `A${rackZoneLabel}`, row: `01${rackRowLabel}`, level: `1${rackLevelLabel}` }; + return ex[seg]; + }).join("-")} + + {" "}— 번호는 자동 계산되고, 뒤에 붙는 명칭만 수정할 수 있습니다.

@@ -1590,10 +1611,9 @@ export default function WarehouseManagementPage() { No 위치코드 위치명 - 구역 - + 유형 비고 @@ -1606,10 +1626,9 @@ export default function WarehouseManagementPage() { {item.location_code} {item.location_name} - {item.floor} {item.zone} - {item.row_num} {item.level_num} + {item.row_num} {resolveCategory(locationCategoryOptions, "location_type", item.location_type) || "-"}