feat(WEB): 견적 개소 복제 기능 추가 - 부호 자동 채번
- LocationListPanel에 복제 버튼(Copy 아이콘) 추가 - 복제 시 모든 개소 데이터(bomResult 포함) 복사 - 부호 자동 채번: 같은 접두어의 최대 번호 +1, 자릿수 유지
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useCallback } from "react";
|
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 { toast } from "sonner";
|
||||||
|
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
@@ -77,6 +77,7 @@ interface LocationListPanelProps {
|
|||||||
onSelectLocation: (id: string) => void;
|
onSelectLocation: (id: string) => void;
|
||||||
onAddLocation: (location: Omit<LocationItem, "id">) => Promise<boolean>;
|
onAddLocation: (location: Omit<LocationItem, "id">) => Promise<boolean>;
|
||||||
onDeleteLocation: (id: string) => void;
|
onDeleteLocation: (id: string) => void;
|
||||||
|
onCloneLocation?: (id: string) => void;
|
||||||
onUpdateLocation: (locationId: string, updates: Partial<LocationItem>) => void;
|
onUpdateLocation: (locationId: string, updates: Partial<LocationItem>) => void;
|
||||||
onExcelUpload: (locations: Omit<LocationItem, "id">[]) => void;
|
onExcelUpload: (locations: Omit<LocationItem, "id">[]) => void;
|
||||||
finishedGoods: FinishedGoods[];
|
finishedGoods: FinishedGoods[];
|
||||||
@@ -94,6 +95,7 @@ export function LocationListPanel({
|
|||||||
onSelectLocation,
|
onSelectLocation,
|
||||||
onAddLocation,
|
onAddLocation,
|
||||||
onDeleteLocation,
|
onDeleteLocation,
|
||||||
|
onCloneLocation,
|
||||||
onUpdateLocation,
|
onUpdateLocation,
|
||||||
onExcelUpload,
|
onExcelUpload,
|
||||||
finishedGoods,
|
finishedGoods,
|
||||||
@@ -523,6 +525,16 @@ export function LocationListPanel({
|
|||||||
>
|
>
|
||||||
<Pencil className="h-4 w-4 text-gray-600" />
|
<Pencil className="h-4 w-4 text-gray-600" />
|
||||||
</button>
|
</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
|
<button
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@@ -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) => {
|
const handleDeleteLocation = useCallback((locationId: string) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
@@ -840,6 +880,7 @@ export function QuoteRegistrationV2({
|
|||||||
onSelectLocation={setSelectedLocationId}
|
onSelectLocation={setSelectedLocationId}
|
||||||
onAddLocation={handleAddLocation}
|
onAddLocation={handleAddLocation}
|
||||||
onDeleteLocation={handleDeleteLocation}
|
onDeleteLocation={handleDeleteLocation}
|
||||||
|
onCloneLocation={handleCloneLocation}
|
||||||
onUpdateLocation={handleUpdateLocation}
|
onUpdateLocation={handleUpdateLocation}
|
||||||
onExcelUpload={handleExcelUpload}
|
onExcelUpload={handleExcelUpload}
|
||||||
finishedGoods={finishedGoods}
|
finishedGoods={finishedGoods}
|
||||||
|
|||||||
Reference in New Issue
Block a user