diff --git a/src/components/dev/DevFillContext.tsx b/src/components/dev/DevFillContext.tsx
index a64c3304..d3ff2ad0 100644
--- a/src/components/dev/DevFillContext.tsx
+++ b/src/components/dev/DevFillContext.tsx
@@ -16,7 +16,7 @@ import React, { createContext, useContext, useState, useCallback, useEffect, Rea
// 지원하는 페이지 타입
export type DevFillPageType =
- | 'quote' | 'order' | 'workOrder' | 'workOrderComplete' | 'shipment' // 판매/생산 플로우
+ | 'quote' | 'quoteV2' | 'order' | 'workOrder' | 'workOrderComplete' | 'shipment' // 판매/생산 플로우
| 'deposit' | 'withdrawal' | 'purchaseApproval' | 'cardTransaction' // 회계 플로우
| 'client'; // 기준정보
diff --git a/src/components/dev/DevToolbar.tsx b/src/components/dev/DevToolbar.tsx
index fe24db78..a742d92f 100644
--- a/src/components/dev/DevToolbar.tsx
+++ b/src/components/dev/DevToolbar.tsx
@@ -43,6 +43,8 @@ import { useDevFillContext, type DevFillPageType } from './DevFillContext';
// 페이지 경로와 타입 매핑 (pathname만으로 매칭되는 패턴)
const PAGE_PATTERNS: { pattern: RegExp; type: DevFillPageType; label: string }[] = [
// 판매/생산 플로우
+ { pattern: /\/quote-management\/test-new/, type: 'quoteV2', label: '견적V2' },
+ { pattern: /\/quote-management\/test\/\d+/, type: 'quoteV2', label: '견적V2' },
{ pattern: /\/quote-management\/new/, type: 'quote', label: '견적' },
{ pattern: /\/quote-management\/\d+\/edit/, type: 'quote', label: '견적' },
{ pattern: /\/order-management-sales\/new/, type: 'order', label: '수주' },
@@ -67,6 +69,7 @@ const MODE_NEW_PAGES: { pattern: RegExp; type: DevFillPageType; label: string }[
// 플로우 단계 정의
const FLOW_STEPS: { type: DevFillPageType; label: string; icon: typeof FileText; path: string }[] = [
+ { type: 'quoteV2', label: '견적V2', icon: FileText, path: '/sales/quote-management/test-new' },
{ type: 'quote', label: '견적', icon: FileText, path: '/sales/quote-management/new' },
{ type: 'order', label: '수주', icon: ClipboardList, path: '/sales/order-management-sales/new' },
{ type: 'workOrder', label: '작업지시', icon: Wrench, path: '/production/work-orders/create' },
@@ -192,6 +195,24 @@ export function DevToolbar() {
{flowData.workOrderId && ` → 작업#${flowData.workOrderId}`}
)}
+ {/* 축소 상태에서 채우기 버튼 */}
+ {!isExpanded && activePage && hasRegisteredForm(activePage) && (
+
+ )}
{hasFlowData && (
diff --git a/src/components/quotes/QuoteRegistrationV2.tsx b/src/components/quotes/QuoteRegistrationV2.tsx
index 719e8e1a..975912ce 100644
--- a/src/components/quotes/QuoteRegistrationV2.tsx
+++ b/src/components/quotes/QuoteRegistrationV2.tsx
@@ -43,7 +43,8 @@ import {
} from "./actions";
import { getClients } from "../accounting/VendorManagement/actions";
import { isNextRedirectError } from "@/lib/utils/redirect-error";
-import { useAuth } from "@/contexts/AuthContext";
+// 실제 로그인 사용자 정보는 localStorage('user')에 저장됨 (LoginPage.tsx 참조)
+import { useDevFill } from "@/components/dev/useDevFill";
import type { Vendor } from "../accounting/VendorManagement";
import type { BomMaterial, CalculationResults } from "./types";
@@ -156,22 +157,12 @@ export function QuoteRegistrationV2({
isLoading = false,
hideHeader = false,
}: QuoteRegistrationV2Props) {
- // ---------------------------------------------------------------------------
- // 인증 정보
- // ---------------------------------------------------------------------------
- const { currentUser } = useAuth();
-
// ---------------------------------------------------------------------------
// 상태
// ---------------------------------------------------------------------------
- const [formData, setFormData] = useState(() => {
- const data = initialData || INITIAL_FORM_DATA;
- // create 모드에서 writer가 비어있으면 현재 사용자명으로 설정
- if (mode === "create" && !data.writer && currentUser?.name) {
- return { ...data, writer: currentUser.name };
- }
- return data;
- });
+ const [formData, setFormData] = useState(
+ initialData || INITIAL_FORM_DATA
+ );
const [selectedLocationId, setSelectedLocationId] = useState(null);
const [isSaving, setIsSaving] = useState(false);
const [isCalculating, setIsCalculating] = useState(false);
@@ -184,6 +175,79 @@ export function QuoteRegistrationV2({
const [isLoadingClients, setIsLoadingClients] = useState(false);
const [isLoadingProducts, setIsLoadingProducts] = useState(false);
+ // ---------------------------------------------------------------------------
+ // DevFill (개발/테스트용 자동 채우기)
+ // ---------------------------------------------------------------------------
+ useDevFill("quoteV2", useCallback(() => {
+ // 랜덤 개소 생성 함수
+ const createRandomLocation = (index: number): LocationItem => {
+ const floors = ["B2", "B1", "1F", "2F", "3F", "4F", "5F", "R"];
+ const codePrefix = ["SD", "FSS", "FD", "SS", "DS"];
+ const guideRailTypes = ["wall", "floor"];
+ const motorPowers = ["single", "three"];
+ const controllers = ["basic", "smart", "premium"];
+
+ const randomFloor = floors[Math.floor(Math.random() * floors.length)];
+ const randomPrefix = codePrefix[Math.floor(Math.random() * codePrefix.length)];
+ const randomWidth = Math.floor(Math.random() * 4000) + 2000; // 2000~6000
+ const randomHeight = Math.floor(Math.random() * 3000) + 2000; // 2000~5000
+ const randomProduct = finishedGoods[Math.floor(Math.random() * finishedGoods.length)];
+
+ return {
+ id: `loc-${Date.now()}-${index}`,
+ floor: randomFloor,
+ code: `${randomPrefix}-${String(index + 1).padStart(2, "0")}`,
+ openWidth: randomWidth,
+ openHeight: randomHeight,
+ productCode: randomProduct?.item_code || "FG-001",
+ productName: randomProduct?.item_name || "방화셔터",
+ quantity: Math.floor(Math.random() * 3) + 1, // 1~3
+ guideRailType: guideRailTypes[Math.floor(Math.random() * guideRailTypes.length)],
+ motorPower: motorPowers[Math.floor(Math.random() * motorPowers.length)],
+ controller: controllers[Math.floor(Math.random() * controllers.length)],
+ wingSize: [50, 60, 70][Math.floor(Math.random() * 3)],
+ inspectionFee: [50000, 60000, 70000][Math.floor(Math.random() * 3)],
+ };
+ };
+
+ // 1~5개 랜덤 개소 생성
+ const locationCount = Math.floor(Math.random() * 5) + 1;
+ const testLocations: LocationItem[] = [];
+ for (let i = 0; i < locationCount; i++) {
+ testLocations.push(createRandomLocation(i));
+ }
+
+ // 로그인 사용자 정보 가져오기
+ let writerName = "";
+ try {
+ const userStr = localStorage.getItem("user");
+ if (userStr) {
+ const user = JSON.parse(userStr);
+ writerName = user?.name || "";
+ }
+ } catch (e) {
+ console.error("[DevFill] 사용자 정보 로드 실패:", e);
+ }
+
+ const testData: QuoteFormDataV2 = {
+ registrationDate: new Date().toISOString().split("T")[0],
+ writer: writerName,
+ clientId: clients[0]?.id?.toString() || "",
+ clientName: clients[0]?.company_name || "테스트 거래처",
+ siteName: "테스트 현장",
+ manager: "홍길동",
+ contact: "010-1234-5678",
+ dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split("T")[0],
+ remarks: "[DevFill] 테스트 견적입니다.",
+ status: "draft",
+ locations: testLocations,
+ };
+
+ setFormData(testData);
+ setSelectedLocationId(testLocations[0].id);
+ toast.success(`[DevFill] 테스트 데이터가 채워졌습니다. (${locationCount}개 개소)`);
+ }, [clients, finishedGoods]));
+
// ---------------------------------------------------------------------------
// 계산된 값
// ---------------------------------------------------------------------------
@@ -211,13 +275,24 @@ export function QuoteRegistrationV2({
}, [formData.locations]);
// ---------------------------------------------------------------------------
- // 작성자 자동 설정 (create 모드에서 currentUser 로드 시)
+ // 작성자 자동 설정 (create 모드에서 로그인 사용자 정보 로드)
// ---------------------------------------------------------------------------
useEffect(() => {
- if (mode === "create" && !formData.writer && currentUser?.name) {
- setFormData((prev) => ({ ...prev, writer: currentUser.name }));
+ if (mode === "create" && !formData.writer) {
+ // 실제 로그인 사용자 정보는 localStorage('user')에 저장됨
+ try {
+ const userStr = localStorage.getItem("user");
+ if (userStr) {
+ const user = JSON.parse(userStr);
+ if (user?.name) {
+ setFormData((prev) => ({ ...prev, writer: user.name }));
+ }
+ }
+ } catch (e) {
+ console.error("[QuoteRegistrationV2] 사용자 정보 로드 실패:", e);
+ }
}
- }, [mode, currentUser?.name, formData.writer]);
+ }, [mode, formData.writer]);
// ---------------------------------------------------------------------------
// 초기 데이터 로드