feat(WEB): 견적 개소 복제 기능 추가 - 부호 자동 채번

- LocationListPanel에 복제 버튼(Copy 아이콘) 추가
- 복제 시 모든 개소 데이터(bomResult 포함) 복사
- 부호 자동 채번: 같은 접두어의 최대 번호 +1, 자릿수 유지
This commit is contained in:
2026-02-04 19:36:37 +09:00
parent 8250420d99
commit a7fb6c67d1
2 changed files with 54 additions and 1 deletions

View File

@@ -9,7 +9,7 @@
"use client";
import { useState, useCallback } from "react";
import { Plus, Upload, Download, Pencil, Trash2 } from "lucide-react";
import { Plus, Upload, Download, Pencil, Trash2, Copy } from "lucide-react";
import { toast } from "sonner";
import { Button } from "../ui/button";
@@ -77,6 +77,7 @@ interface LocationListPanelProps {
onSelectLocation: (id: string) => void;
onAddLocation: (location: Omit<LocationItem, "id">) => Promise<boolean>;
onDeleteLocation: (id: string) => void;
onCloneLocation?: (id: string) => void;
onUpdateLocation: (locationId: string, updates: Partial<LocationItem>) => void;
onExcelUpload: (locations: Omit<LocationItem, "id">[]) => void;
finishedGoods: FinishedGoods[];
@@ -94,6 +95,7 @@ export function LocationListPanel({
onSelectLocation,
onAddLocation,
onDeleteLocation,
onCloneLocation,
onUpdateLocation,
onExcelUpload,
finishedGoods,
@@ -523,6 +525,16 @@ export function LocationListPanel({
>
<Pencil className="h-4 w-4 text-gray-600" />
</button>
<button
onClick={(e) => {
e.stopPropagation();
onCloneLocation?.(loc.id);
}}
className="p-1 hover:bg-blue-100 rounded"
title="복제"
>
<Copy className="h-4 w-4 text-blue-500" />
</button>
<button
onClick={(e) => {
e.stopPropagation();

View File

@@ -500,6 +500,46 @@ export function QuoteRegistrationV2({
}
}, []);
// 개소 삭제
// 개소 복제 (부호 자동 채번)
const handleCloneLocation = useCallback((locationId: string) => {
const source = formData.locations.find((loc) => loc.id === locationId);
if (!source) return;
// 부호에서 접두어와 번호 분리 (예: "DS-01" → prefix="DS-", num=1)
const codeMatch = source.code.match(/^(.*?)(\d+)$/);
let newCode = source.code + "-copy";
if (codeMatch) {
const prefix = codeMatch[1]; // "DS-"
const numLength = codeMatch[2].length; // 2 (자릿수 보존)
// 같은 접두어를 가진 부호 중 최대 번호 찾기
let maxNum = 0;
formData.locations.forEach((loc) => {
const m = loc.code.match(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(\\d+)$`));
if (m) {
maxNum = Math.max(maxNum, parseInt(m[1], 10));
}
});
newCode = prefix + String(maxNum + 1).padStart(numLength, "0");
}
const clonedLocation: LocationItem = {
...source,
id: `loc-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
code: newCode,
};
setFormData((prev) => ({
...prev,
locations: [...prev.locations, clonedLocation],
}));
setSelectedLocationId(clonedLocation.id);
toast.success(`개소가 복제되었습니다. (${newCode})`);
}, [formData.locations]);
// 개소 삭제
const handleDeleteLocation = useCallback((locationId: string) => {
setFormData((prev) => ({
@@ -840,6 +880,7 @@ export function QuoteRegistrationV2({
onSelectLocation={setSelectedLocationId}
onAddLocation={handleAddLocation}
onDeleteLocation={handleDeleteLocation}
onCloneLocation={handleCloneLocation}
onUpdateLocation={handleUpdateLocation}
onExcelUpload={handleExcelUpload}
finishedGoods={finishedGoods}