PageSourceSearch

https://wedraw.kr/assets/AppKitchenAI.rtnRHmWR.js

js wedraw.kr collected 2026-10-04 07:42:41 UTC 49,935 bytes, 613 lines download raw bytes

1import{j as e}from"./vendor-ui.BwPBmVwt.js";import{r as j,R as E}from"./vendor-react.kR8tqwKZ.js";import{C as L,O as z,E as D}from"./vendor-three.C5-HLXBy.js";import{a as v}from"./KitchenCabinetRenderer.10BUJEeA.js";import{H as R}from"./house.BgjiwUvE.js";import{B as M}from"./box.BoKUV7eQ.js";import{M as B}from"./maximize-2.CwPAzw95.js";import{C as N}from"./chevron-left.C8eB59fV.js";import{C as I}from"./chevron-right.BO4QQ722.js";import{A as T}from"./AIKitchenRenderer.B7lZWR7W.js";import{u as F}from"./index.86ltQqcV.js";import{L as q,a as k}from"./LabBanner.DdcZKo4g.js";import"./vendor-utils.D2q0l15H.js";import"./Evaluator.DqK84_8_.js";import"./materials.B5rqHQnW.js";import"./useDoorHingeAnimation.BXQ-fYs4.js";const W=({onComplete:x})=>{const[t,r]=j.useState("purpose"),[h,l]=j.useState(null),[i,m]=j.useState(null),[a,s]=j.useState({height:2200,width:2400,depth:600,width1:2e3,width2:1500,width3:1500}),{setSpaceConfig:d,generateLayout:f}=v(),u=o=>{l(o),r("shape")},c=o=>{m(o),r("dimensions")},p=(o,b)=>{const S=isNaN(b)?0:b;s(A=>({...A,[o]:S}))},g=async()=>{const o={purpose:h,shape:i,dimensions:a};d(o),await f(o),x()},n=()=>{switch(t){case"shape":r("purpose");break;case"dimensions":r("shape");break;case"confirm":r("dimensions");break}};return j.useEffect(()=>{i&&t==="dimensions"&&d({purpose:h,shape:i,dimensions:a})},[i,a,t,h,d]),e.jsx("div",{className:"absolute inset-0 pointer-events-none flex items-center justify-center",children:e.jsx("div",{className:"w-full max-w-4xl mx-auto p-6 pointer-events-auto",children:e.jsxs("div",{className:"bg-white/95 backdrop-blur-sm rounded-2xl shadow-2xl overflow-hidden max-h-[80vh] overflow-y-auto",children:[e.jsx("div",{className:"h-1 bg-gray-200",children:e.jsx("div",{className:"h-full bg-blue-500 transition-all duration-300",style:{width:`${t==="purpose"?25:t==="shape"?50:t==="dimensions"?75:100}%`}})}),e.jsxs("div",{className:"p-8",children:[t==="purpose"&&e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("h2",{className:"text-3xl font-bold text-gray-900 mb-2",children:"어떤 공간을 채워볼까요?"}),e.jsx("p",{className:"text-gray-600",children:"설계하고자 하는 주방 공간의 유형을 선택해주세요"})]}),e.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[e.jsx("button",{onClick:()=>u("full"),className:"group relative p-6 bg-gradient-to-br from-blue-50 to-blue-100 rounded-xl hover:shadow-lg transition-all hover:scale-105",children:e.jsxs("div",{className:"flex flex-col items-center space-y-4",children:[e.jsx(R,{className:"w-12 h-12 text-blue-600"}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-lg",children:"주방 전체"}),e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:"하부장, 상부장, 키큰장 모두"})]})]})}),e.jsx("button",{onClick:()=>u("base"),className:"group relative p-6 bg-gradient-to-br from-green-50 to-green-100 rounded-xl hover:shadow-lg transition-all hover:scale-105",children:e.jsxs("div",{className:"flex flex-col items-center space-y-4",children:[e.jsx(M,{className:"w-12 h-12 text-green-600"}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-lg",children:"하부장만"}),e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:"기존 주방에 하부장 추가/교체"})]})]})}),e.jsx("button",{onClick:()=>u("wall"),className:"group relative p-6 bg-gradient-to-br from-purple-50 to-purple-100 rounded-xl hover:shadow-lg transition-all hover:scale-105",children:e.jsxs("div",{className:"flex flex-col items-center space-y-4",children:[e.jsx(B,{className:"w-12 h-12 text-purple-600"}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-lg",children:"상부장만"}),e.jsx("p",{className:"text-sm text-gray-600 mt-1",children:"벽면 상부장 설치"})]})]})})]})]}),t==="shape"&&e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("h2",{className:"text-3xl font-bold text-gray-900 mb-2",children:"공간의 전체적인 모양을 선택해주세요"}),e.jsx("p",{className:"text-gray-600",children:"주방이 설치될 벽면의 형태를 선택하세요"})]}),e.jsxs("div",{className:"grid grid-cols-4 gap-4",children:[e.jsx("button",{onClick:()=>c("straight"),className:"group relative p-6 bg-white border-2 border-gray-200 rounded-xl hover:border-blue-500 hover:shadow-lg transition-all",children:e.jsxs("div",{className:"flex flex-col items-center space-y-3",children:[e.jsx("div",{className:"w-16 h-16 flex items-center justify-center",children:e.jsx("div",{className:"w-full h-1 bg-gray-800 rounded-full"})}),e.jsx("span",{className:"font-medium",children:"ㅡ자형"}),e.jsx("span",{className:"text-xs text-gray-500",children:"일자형"})]})}),e.jsx("button",{onClick:()=>c("L"),className:"group relative p-6 bg-white border-2 border-gray-200 rounded-xl hover:border-blue-500 hover:shadow-lg transition-all",children:e.jsxs("div",{className:"flex flex-col items-center space-y-3",children:[e.jsxs("div",{className:"w-16 h-16 flex items-end justify-start p-2",children:[e.jsx("div",{className:"w-12 h-1 bg-gray-800 rounded-full"}),e.jsx("div",{className:"w-1 h-10 bg-gray-800 rounded-full -ml-1"})]}),e.jsx("span",{className:"font-medium",children:"ㄱ자형"}),e.jsx("span",{className:"text-xs text-gray-500",children:"코너형"})]})}),e.jsx("button",{onClick:()=>c("U"),className:"group relative p-6 bg-white border-2 border-gray-200 rounded-xl hover:border-blue-500 hover:shadow-lg transition-all",children:e.jsxs("div",{className:"flex flex-col items-center space-y-3",children:[e.jsx("div",{className:"w-16 h-16 flex items-center justify-center p-2",children:e.jsx("div",{className:"w-12 h-10 border-2 border-gray-800 border-top-0 rounded-b"})}),e.jsx("span",{className:"font-medium",children:"ㄷ자형"}),e.jsx("span",{className:"text-xs text-gray-500",children:"3면형"})]})}),e.jsx("button",{onClick:()=>c("G"),className:"group relative p-6 bg-white border-2 border-gray-200 rounded-xl hover:border-blue-500 hover:shadow-lg transition-all",children:e.jsxs("div",{className:"flex flex-col items-center space-y-3",children:[e.jsx("div",{className:"w-16 h-16 flex items-center justify-center p-2",children:e.jsx("div",{className:"w-12 h-10 border-2 border-gray-800 rounded"})}),e.jsx("span",{className:"font-medium",children:"ㅁ자형"}),e.jsx("span",{className:"text-xs text-gray-500",children:"아일랜드형"})]})})]}),e.jsxs("button",{onClick:n,className:"flex items-center space-x-2 px-4 py-2 text-gray-600 hover:text-gray-900",children:[e.jsx(N,{className:"w-4 h-4"}),e.jsx("span",{children:"이전 단계로"})]})]}),t==="dimensions"&&e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("h2",{className:"text-3xl font-bold text-gray-900 mb-2",children:"공간의 치수를 입력해주세요"}),e.jsx("p",{className:"text-gray-600",children:"3D 뷰에서 실시간으로 확인하며 조정하세요"})]}),e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-4",children:[e.jsxs("div",{className:"bg-blue-50 rounded-lg p-4 border-2 border-blue-200",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"가로 (너비)"}),e.jsxs("div",{className:"flex flex-col space-y-2",children:[e.jsx("input",{type:"range",min:"600",max:"6000",step:"10",value:a.width,onChange:o=>p("width",parseInt(o.target.value)),className:"w-full"}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"number",value:a.width,onChange:o=>p("width",parseInt(o.target.value)||0),className:"w-full px-2 py-1 border rounded"}),e.jsx("span",{className:"text-sm text-gray-600",children:"mm"})]})]})]}),e.jsxs("div",{className:"bg-green-50 rounded-lg p-4 border-2 border-green-200",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"세로 (깊이)"}),e.jsxs("div",{className:"flex flex-col space-y-2",children:[e.jsx("input",{type:"range",min:"300",max:"800",step:"10",value:a.depth,onChange:o=>p("depth",parseInt(o.target.value)),className:"w-full"}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"number",value:a.depth,onChange:o=>p("depth",parseInt(o.target.value)||0),className:"w-full px-2 py-1 border rounded"}),e.jsx("span",{className:"text-sm text-gray-600",children:"mm"})]})]})]}),e.jsxs("div",{className:"bg-purple-50 rounded-lg p-4 border-2 border-purple-200",children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"높이"}),e.jsxs("div",{className:"flex flex-col space-y-2",children:[e.jsx("input",{type:"range",min:"300",max:"3000",step:"10",value:a.height,onChange:o=>p("height",parseInt(o.target.value)),className:"w-full"}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"number",value:a.height,onChange:o=>p("height",parseInt(o.target.value)||0),className:"w-full px-2 py-1 border rounded"}),e.jsx("span",{className:"text-sm text-gray-600",children:"mm"})]})]})]})]}),i==="L"&&e.jsxs("div",{className:"mt-4 p-4 bg-yellow-50 rounded-lg",children:[e.jsx("p",{className:"text-sm text-yellow-800",children:"L자형 주방의 경우 추가 벽면 길이 입력이 필요합니다."}),e.jsxs("div",{className:"mt-4 grid grid-cols-2 gap-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"첫 번째 ë²½ 길이"}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"number",value:a.width1,onChange:o=>p("width1",parseInt(o.target.value)||0),className:"w-full px-2 py-1 border rounded"}),e.jsx("span",{className:"text-sm text-gray-600",children:"mm"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium text-gray-700 mb-2",children:"두 번째 ë²½ 길이"}),e.jsxs("div",{className:"flex items-center space-x-2",children:[e.jsx("input",{type:"number",value:a.width2,onChange:o=>p("width2",parseInt(o.target.value)||0),className:"w-full px-2 py-1 border rounded"}),e.jsx("span",{className:"text-sm text-gray-600",children:"mm"})]})]})]})]})]}),e.jsxs("div",{className:"flex justify-between mt-6",children:[e.jsxs("button",{onClick:n,className:"flex items-center space-x-2 px-4 py-2 text-gray-600 hover:text-gray-900",children:[e.jsx(N,{className:"w-4 h-4"}),e.jsx("span",{children:"이전 단계로"})]}),e.jsxs("button",{onClick:()=>r("confirm"),className:"flex items-center space-x-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors",children:[e.jsx("span",{children:"다음 단계"}),e.jsx(I,{className:"w-4 h-4"})]})]})]}),t==="confirm"&&e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{className:"text-center",children:[e.jsx("h2",{className:"text-3xl font-bold text-gray-900 mb-2",children:"입력하신 정보를 확인해주세요"}),e.jsx("p",{className:"text-gray-600",children:"AI가 이 정보를 바탕으로 최적의 주방을 설계합니다"})]}),e.jsxs("div",{className:"bg-gray-50 rounded-lg p-6 space-y-3",children:[e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"공간 용도"}),e.jsx("span",{className:"font-medium",children:h==="full"?"주방 전체":h==="base"?"하부장만":"상부장만"})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"공간 형태"}),e.jsx("span",{className:"font-medium",children:i==="straight"?"ㅡ자형":i==="L"?"ㄱ자형":i==="U"?"ㄷ자형":"ㅁ자형"})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"가로 (너비)"}),e.jsxs("span",{className:"font-medium",children:[a.width,"mm"]})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"세로 (깊이)"}),e.jsxs("span",{className:"font-medium",children:[a.depth,"mm"]})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"높이"}),e.jsxs("span",{className:"font-medium",children:[a.height,"mm"]})]}),i==="L"&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"첫 번째 ë²½"}),e.jsxs("span",{className:"font-medium",children:[a.width1,"mm"]})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsx("span",{className:"text-gray-600",children:"두 번째 ë²½"}),e.jsxs("span",{className:"font-medium",children:[a.width2,"mm"]})]})]})]}),e.jsxs("div",{className:"flex justify-between",children:[e.jsxs("button",{onClick:n,className:"flex items-center space-x-2 px-4 py-2 text-gray-600 hover:text-gray-900",children:[e.jsx(N,{className:"w-4 h-4"}),e.jsx("span",{children:"이전 단계로"})]}),e.jsx("button",{onClick:g,className:"px-8 py-3 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-lg hover:from-blue-700 hover:to-blue-800 transition-all transform hover:scale-105 font-medium",children:"이 공간에 맞ê²
1Œ 주방 자동 설계하기"})]})]})]})]})})})},y={panels:{white_oak:{pricePerSqm:85e3,thickness:[15,18,20,25]},walnut:{pricePerSqm:12e4,thickness:[15,18,20,25]},white_glossy:{pricePerSqm:95e3,thickness:[15,18,20]},black_matte:{pricePerSqm:105e3,thickness:[15,18,20]},MDF:{pricePerSqm:45e3,thickness:[15,18,20,25]},plywood:{pricePerSqm:55e3,thickness:[12,15,18]}},hardware:{hinges:{softClose:15e3},rails:{fullExtension:65e3},handles:{bar:12e3}},accessories:{sinks:{standard_single:18e4},faucets:{standard:12e4}},labor:{delivery:5e4,deliveryPerKm:1e3,installation:2e5,installationPerModule:3e4}},H={tiers:[{minAmount:5e6,rate:.1},{minAmount:3e6,rate:.07},{minAmount:1e6,rate:.05},{minAmount:0,rate:0}]},O=.1,U={white_oak:1,walnut:1.2,white_glossy:1.1,black_matte:1.15,MDF:.6,plywood:.7};function w(x,t,r,h=18){const l=x*t/1e6,i=y.panels[r]?.pricePerSqm||y.panels.MDF.pricePerSqm,m=U[r]||1;return Math.round(l*i*m)}function G(x){return H.tiers.find(r=>x>=r.minAmount)?.rate||0}function K(x,t=0,r=1){const h=G(x),l=Math.round(x*h),i=x-l;let m=y.labor.delivery;t>30&&(m+=(t-30)*y.labor.deliveryPerKm);const a=y.labor.installation+(r-1)*y.labor.installationPerModule,s=i+m+a,d=Math.round(s*O),f=s+d;return{subtotal:x,discountRate:h,discountAmount:l,deliveryFee:m,installationFee:a,taxAmount:d,total:f}}class ${static generateModuleSVG(t,r,h){const{width:l,height:i}=r,m=.2,a=l*m,s=i*m,d=a+40,f=s+40;let u="";if(t.includes("DRAWER")){const c=h?.drawerCount||2,p=s/c;u=`
2        <rect x="20" y="20" width="${a}" height="${s}"
3              fill="none" stroke="#333" stroke-width="1.5"/>
4      `;for(let g=1;g<c;g++)u+=`
5          <line x1="20" y1="${20+p*g}"
6                x2="${20+a}" y2="${20+p*g}"
7                stroke="#666" stroke-width="1"/>
8        `;for(let g=0;g<c;g++){const n=20+p*g+p/2;u+=`
9          <line x1="${20+a*.4}" y1="${n}"
10                x2="${20+a*.6}" y2="${n}"
11                stroke="#999" stroke-width="2"/>
12        `}}else if(t.includes("SINK"))u=`
13        <rect x="20" y="20" width="${a}" height="${s}"
14              fill="none" stroke="#333" stroke-width="1.5"/>
15        <rect x="${20+a*.2}" y="${20+s*.2}"
16              width="${a*.6}" height="${s*.5}"
17              fill="#e0e0e0" stroke="#666" stroke-width="1" rx="5"/>
18        <circle cx="${20+a*.7}" cy="${20+s*.3}" r="8"
19                fill="none" stroke="#666" stroke-width="1"/>
20      `;else if(t.includes("DOOR")){const c=t.includes("SINGLE")?1:2;u=`
21        <rect x="20" y="20" width="${a}" height="${s}"
22              fill="none" stroke="#333" stroke-width="1.5"/>
23      `,c===2?(u+=`
24          <line x1="${20+a/2}" y1="20"
25                x2="${20+a/2}" y2="${20+s}"
26                stroke="#666" stroke-width="1"/>
27        `,u+=`
28          <circle cx="${20+a/2-10}" cy="${20+s/2}" r="3" fill="#999"/>
29          <circle cx="${20+a/2+10}" cy="${20+s/2}" r="3" fill="#999"/>
30        `):u+=`
31          <circle cx="${20+a*.9}" cy="${20+s/2}" r="3" fill="#999"/>
32        `}else if(t.includes("SHELF")||t==="wall"){u=`
33        <rect x="20" y="20" width="${a}" height="${s}"
34              fill="none" stroke="#333" stroke-width="1.5"/>
35      `;for(let c=1;c<3;c++)u+=`
36          <line x1="25" y1="${20+s*c/3}"
37                x2="${20+a-5}" y2="${20+s*c/3}"
38                stroke="#999" stroke-width="1" stroke-dasharray="2,2"/>
39        `}else t.includes("PANTRY")||t==="tall"?(u=`
40        <rect x="20" y="20" width="${a}" height="${s}"
41              fill="none" stroke="#333" stroke-width="1.5"/>
42      `,u+=`
43        <rect x="22" y="22" width="${a-4}" height="${s*.3}"
44              fill="none" stroke="#666" stroke-width="1"/>
45      `,u+=`
46        <rect x="22" y="${20+s*.35}" width="${a-4}" height="${s*.15}"
47              fill="none" stroke="#666" stroke-width="1"/>
48        <line x1="${20+a*.4}" y1="${20+s*.425}"
49              x2="${20+a*.6}" y2="${20+s*.425}"
50              stroke="#999" stroke-width="2"/>
51      `,u+=`
52        <rect x="22" y="${20+s*.55}" width="${a-4}" height="${s*.43}"
53              fill="none" stroke="#666" stroke-width="1"/>
54        <circle cx="${20+a*.9}" cy="${20+s*.75}" r="3" fill="#999"/>
55      `):u=`
56        <rect x="20" y="20" width="${a}" height="${s}"
57              fill="none" stroke="#333" stroke-width="1.5"/>
58      `;return`
59      <svg xmlns="http://www.w3.org/2000/svg"
60           width="${d}" height="${f}"
61           viewBox="0 0 ${d} ${f}">
62        ${u}
63      </svg>
64    `}static svgToDataURL(t){return`data:image/svg+xml,${encodeURIComponent(t).replace(/'/g,"%27").replace(/"/g,"%22")}`}}class _{static generateComponentSVG(t,r,h){let i="";switch(t){case"panel":i=`
65          <rect x="10" y="10" width="40" height="40"
66                fill="#f0f0f0" stroke="#333" stroke-width="1"/>
67          <line x1="10" y1="10" x2="15" y2="5"
68                stroke="#333" stroke-width="1"/>
69          <line x1="50" y1="10" x2="55" y2="5"
70                stroke="#333" stroke-width="1"/>
71          <line x1="15" y1="5" x2="55" y2="5"
72                stroke="#333" stroke-width="1"/>
73          <line x1="55" y1="5" x2="55" y2="45"
74                stroke="#333" stroke-width="1"/>
75          <line x1="50" y1="50" x2="55" y2="45"
76                stroke="#333" stroke-width="1"/>
77        `;break;case"door":i=`
78          <rect x="10" y="10" width="40" height="40"
79                fill="#e8e8e8" stroke="#333" stroke-width="1.5"/>
80          <rect x="12" y="12" width="36" height="36"
81                fill="none" stroke="#666" stroke-width="0.5"/>
82          <circle cx="42" cy="30" r="2" fill="#999"/>
83          <line x1="42" y1="25" x2="42" y2="35"
84                stroke="#666" stroke-width="0.5"/>
85        `;break;case"drawer":r.includes("전면판")?i=`
86            <rect x="10" y="20" width="40" height="20"
87                  fill="#e0e0e0" stroke="#333" stroke-width="1.5"/>
88            <line x1="20" y1="30" x2="40" y2="30"
89                  stroke="#999" stroke-width="2"/>
90          `:i=`
91            <path d="M10 25 L10 45 L50 45 L50 25"
92                  fill="none" stroke="#333" stroke-width="1.5"/>
93            <path d="M10 25 L15 20 L55 20 L50 25"
94                  fill="#f5f5f5" stroke="#333" stroke-width="1"/>
95            <path d="M50 25 L55 20 L55 40 L50 45"
96                  fill="#e8e8e8" stroke="#333" stroke-width="1"/>
97          `;break;case"hardware":r.includes("경첩")?i=`
98            <rect x="20" y="10" width="8" height="40"
99                  fill="#c0c0c0" stroke="#333" stroke-width="1"/>
100            <rect x="32" y="10" width="8" height="40"
101                  fill="#c0c0c0" stroke="#333" stroke-width="1"/>
102            <circle cx="30" cy="30" r="3" fill="#999"/>
103            <circle cx="24" cy="20" r="1.5" fill="#666"/>
104            <circle cx="36" cy="20" r="1.5" fill="#666"/>
105            <circle cx="24" cy="40" r="1.5" fill="#666"/>
106            <circle cx="36" cy="40" r="1.5" fill="#666"/>
107          `:r.includes("레일")?i=`
108            <rect x="10" y="25" width="40" height="4"
109                  fill="#b0b0b0" stroke="#333" stroke-width="1"/>
110            <rect x="10" y="31" width="40" height="4"
111                  fill="#b0b0b0" stroke="#333" stroke-width="1"/>
112            <circle cx="15" cy="30" r="2" fill="#666"/>
113            <circle cx="45" cy="30" r="2" fill="#666"/>
114          `:i=`
115            <rect x="15" y="28" width="30" height="4"
116                  fill="#a0a0a0" stroke="#333" stroke-width="1" rx="2"/>
117            <circle cx="20" cy="30" r="2" fill="#888"/>
118            <circle cx="40" cy="30" r="2" fill="#888"/>
119          `;break;case"accessory":r.includes("싱크")?i=`
120            <rect x="10" y="15" width="40" height="30"
121                  fill="#f8f8f8" stroke="#333" stroke-width="1.5" rx="4"/>
122            <rect x="15" y="20" width="30" height="20"
123                  fill="#e0e0e0" stroke="#666" stroke-width="1" rx="2"/>
124            <circle cx="40" cy="25" r="3"
125                    fill="none" stroke="#666" stroke-width="1"/>
126            <circle cx="40" cy="25" r="1" fill="#666"/>
127          `:r.includes("수전")?i=`
128            <rect x="28" y="40" width="4" height="10"
129                  fill="#c0c0c0" stroke="#333" stroke-width="1"/>
130            <path d="M30 40 L30 25 Q30 20 35 20 L40 20"
131                  fill="none" stroke="#999" stroke-width="2"/>
132            <circle cx="30" cy="45" r="5"
133                    fill="#d0d0d0" stroke="#333" stroke-width="1"/>
134            <circle cx="42" cy="20" r="2" fill="#999"/>
135          `:i=`
136            <rect x="5" y="25" width="50" height="8"
137                  fill="#f5f5f0" stroke="#333" stroke-width="1.5"/>
138            <path d="M5 25 L10 20 L60 20 L55 25"
139                  fill="#fafaf8" stroke="#333" stroke-width="1"/>
140            <path d="M55 25 L60 20 L60 28 L55 33"
141                  fill="#e8e8e6" stroke="#333" stroke-width="1"/>
142            <line x1="15" y1="22" x2="45" y2="22"
143                  stroke="#ccc" stroke-width="0.5" opacity="0.5"/>
144            <line x1="10" y1="28" x2="50" y2="28"
145                  stroke="#ccc" stroke-width="0.5" opacity="0.5"/>
146          `;break;default:i=`
147          <rect x="10" y="10" width="40" height="40"
148                fill="#f0f0f0" stroke="#333" stroke-width="1.5"/>
149        `}return`
150      <svg xmlns="http://www.w3.org/2000/svg"
151           width="60" height="60"
152           viewBox="0 0 60 60">
153        ${i}
154      </svg>
155    `}static svgToDataURL(t){return`data:image/svg+xml,${encodeURIComponent(t).replace(/'/g,"%27").replace(/"/g,"%22")}`}}class V{material="white_oak";constructor(t){t&&(this.material=t)}generateQuotationId(){const t=new Date,r=t.getFullYear(),h=String(t.getMonth()+1).padStart(2,"0"),l=String(t.getDate()).padStart(2,"0"),i=Math.floor(Math.random()*1e4).toString().padStart(4,"0");return`QT-${r}${h}${l}-${i}`}generateBOM(t){const r=[],{dimensions:h,config:l}=t,{width:i,height:m,depth:a}=h;if(t.type==="base"){const n={category:"accessory",name:"대리석 상판",material:"인조대리석",size:`${i} × ${a+20} × 40`,unit:"EA",unitPrice:25e4,quantity:1,amount:0};n.amount=n.unitPrice*n.quantity,r.push(n)}else if(t.type==="wall"||t.type==="tall"){const n={category:"panel",name:"상판",material:this.material,size:`${i} × ${a} × 18`,unit:"EA",unitPrice:w(i,a,this.material,18),quantity:1,amount:0};n.amount=n.unitPrice*n.quantity,r.push(n)}const s=t.type==="base"?m-18:m,d=t.type==="base"&&l.isHandleless||!1,f={category:"panel",name:d?"좌측판 (핸들리스)":"좌측판",material:this.material,size:`${s} × ${a} × 18`,unit:"EA",unitPrice:w(s,a,this.material,18)*(d?1.2:1),quantity:1,amount:0};f.amount=f.unitPrice*f.quantity,r.push(f);const u={category:"panel",name:d?"우측판 (핸들리스)":"우측판",material:this.material,size:`${s} × ${a} × 18`,unit:"EA",unitPrice:w(s,a,this.material,18)*(d?1.2:1),quantity:1,amount:0};u.amount=u.unitPrice*u.quantity,r.push(u);const c={category:"panel",name:"밑판",material:this.material,size:`${i-36} × ${a} × 18`,unit:"EA",unitPrice:w(i-36,a,this.material,18),quantity:1,amount:0};c.amount=c.unitPrice*c.quantity,r.push(c);const p={category:"panel",name:"후판",material:"MDF",size:`${i-36} × ${m-18} × 5`,unit:"EA",unitPrice:w(i-36,m-18,"MDF",5),quantity:1,amount:0};if(p.amount=p.unitPrice*p.quantity,r.push(p),l.doorType&&l.doorType!=="none"){const n=l.doorCount||2,o=l.doorType==="double"?i/2-2:i-4,b={category:"door",name:"문짝",material:this.material,size:`${o} × ${m-4} × 18`,unit:"EA",unitPrice:w(o,m-4,this.material,18),quantity:n,amount:0};b.amount=b.unitPrice*b.quantity,r.push(b)}if(l.hasDrawer&&l.drawerCount){const n=Math.floor((m-40)/l.drawerCount),o={category:"drawer",name:"서랍 전면판",material:this.material,size:`${i-4} × ${n-2} × 18`,unit:"EA",unitPrice:w(i-4,n-2,this.material,18),quantity:l.drawerCount,amount:0};o.amount=o.unitPrice*o.quantity,r.push(o);const b={category:"drawer",name:"서랍 박스",material:"plywood",size:`${i-100} × ${n-50} × ${a-100}`,unit:"SET",unitPrice:35e3,quantity:l.drawerCount,amount:0};b.amount=b.unitPrice*b.quantity,r.push(b)}if(l.doorCount&&l.doorCount>0){const n={category:"hardware",name:"경첩 (소프트클로즈)",material:"스테인리스",size:"",unit:"EA",unitPrice:y.hardware.hinges.softClose,quantity:l.doorCount*2,amount:0};n.amount=n.unitPrice*n.quantity,r.push(n)}if(l.hasDrawer&&l.drawerCount){const n={category:"hardware",name:"레일 (풀익스텐션)",material:"스테인리스",size:`${a-100}mm`,unit:"SET",unitPrice:y.hardware.rails.fullExtension,quantity:l.drawerCount,amount:0};n.amount=n.unitPrice*n.quantity,r.push(n)}const g=(l.doorCount||0)+(l.drawerCount||0);if(g>0&&!l.isHandleless){const n={category:"hardware",name:"손잡이",material:"알루미늄",size:"150mm",unit:"EA",unitPrice:y.hardware.handles.bar,quantity:g,amount:0};n.amount=n.unitPrice*n.quantity,r.push(n)}if(l.hasSink){const n={category:"accessory",name:"싱크볼",material:"스테인리스",size:"표준 싱글",unit:"EA",unitPrice:y.accessories.sinks.standard_single,quantity:1,amount:0};n.amount=n.unitPrice*n.quantity,r.push(n);const o={category:"accessory",name:"수전",material:"크롬",size:"표준형",unit:"EA",unitPrice:y.accessories.faucets.standard,quantity:1,amount:0};o.amount=o.unitPrice*o.quantity,r.push(o)}if(l.rowCount&&l.rowCount>1){const n=l.rowCount-1,o={category:"panel",name:"가로 선반",material:this.material,size:`${i-36} × ${a-20} × 18`,unit:"EA",unitPrice:w(i-36,a-20,this.material,18),quantity:n,amount:0};o.amount=o.unitPrice*o.quantity,r.push(o)}return r}generateQuotation(t){const r=[];let h=0;for(const m of t.modules){const a=this.generateBOM(m),s=a.reduce((d,f)=>d+f.amount,0);r.push({moduleId:m.id,moduleType:m.config.preset||m.type,position:m.position,dimensions:m.dimensions,components:a,subtotal:s}),h+=s}const l=K(h,30,t.modules.length);return{id:this.generateQuotationId(),createdAt:new Date,validUntil:new Date(Date.now()+720*60*60*1e3),customer:null,items:r,summary:{totalModules:t.modules.length,subtotal:l.subtotal,discountRate:l.discountRate,discountAmount:l.discountAmount,deliveryFee:l.deliveryFee,installationFee:l.installationFee,taxAmount:l.taxAmount,total:l.total},notes:["본 견적서는 발행일로부터 30일간 유효합니다.","부가세 10%가 포함된 금액입니다.","배송 및 설치 일정은 별도 협의가 필요합니다.","제작 기간은 결제 완료 후 약 2-3주 소요됩니다.","색상 및 재질은 실물과 다소 차이가 있을 수 있습니다."]}}formatToHTML(t){const r=i=>`${i.getFullYear()}.${String(i.getMonth()+1).padStart(2,"0")}.${String(i.getDate()).padStart(2,"0")}`,h=i=>
155`₩${i.toLocaleString()}`;return`
156      <!DOCTYPE html>
157      <html lang="ko">
158      <head>
159        <meta charset="UTF-8">
160        <meta name="viewport" content="width=device-width, initial-scale=1.0">
161        <title>견적서 - ${t.id}</title>
162        <style>
163          ${this.getPrintStyles()}
164        </style>
165      </head>
166      <body>
167        <div class="quotation-container">
168          <header>
169            <div class="company-info">
170              <h1>Topping</h1>
171              <p>맞춤형 주방 가구 전문</p>
172            </div>
173            <div class="quotation-title">
174              <h2>견 적 서</h2>
175              <p class="quotation-number">No. ${t.id}</p>
176            </div>
177          </header>
178
179          <section class="dates">
180            <div>발행일: ${r(t.createdAt)}</div>
181            <div>유효기간: ${r(t.validUntil)}까지</div>
182          </section>
183
184          ${t.customer?`
185          <section class="customer-info">
186            <h3>고객 정보</h3>
187            <table>
188              <tr><th>성함:</th><td>${t.customer.name}</td></tr>
189              <tr><th>연락처:</th><td>${t.customer.phone}</td></tr>
190              <tr><th>주소:</th><td>${t.customer.address}</td></tr>
191              ${t.customer.email?`<tr><th>이메일:</th><td>${t.customer.email}</td></tr>`:""}
192            </table>
193          </section>
194          `:`
195          <section class="customer-info">
196            <h3>고객 정보</h3>
197            <table>
198              <tr><th>성함:</th><td>_________________</td></tr>
199              <tr><th>연락처:</th><td>_________________</td></tr>
200              <tr><th>주소:</th><td>_________________________________</td></tr>
201            </table>
202          </section>
203          `}
204
205          <section class="summary">
206            <h3>요약</h3>
207            <table class="summary-table">
208              <tr><th>총 모듈 수:</th><td>${t.summary.totalModules}개</td></tr>
209              <tr><th>소계:</th><td>${h(t.summary.subtotal)}</td></tr>
210              ${t.summary.discountAmount>0?`
211              <tr><th>할인 (${(t.summary.discountRate*100).toFixed(0)}%):</th><td class="discount">-${h(t.summary.discountAmount)}</td></tr>
212              `:""}
213              <tr><th>배송비:</th><td>${h(t.summary.deliveryFee)}</td></tr>
214              <tr><th>설치비:</th><td>${h(t.summary.installationFee)}</td></tr>
215              <tr><th>부가세 (10%):</th><td>${h(t.summary.taxAmount)}</td></tr>
216              <tr class="total-row"><th>총액:</th><td class="total">${h(t.summary.total)}</td></tr>
217            </table>
218          </section>
219
220          <section class="details">
221            <h3>상세 내역</h3>
222            ${t.items.map((i,m)=>{const a=$.generateModuleSVG(i.moduleType,i.dimensions,{drawerCount:i.moduleType.includes("DRAWER_3")?3:i.moduleType.includes("DRAWER_2")?2:0}),s=$.svgToDataURL(a);return`
223              <div class="module-section">
224                <div class="module-header">
225                  <div class="module-info">
226                    <h4>Module ${m+1}: ${this.getModuleName(i.moduleType)}</h4>
227                    <p class="module-dims">${i.dimensions.width}×${i.dimensions.height}×${i.dimensions.depth}mm</p>
228                  </div>
229                  <div class="module-image">
230                    <img src="${s}" alt="${i.moduleType}" style="width: 150px; height: auto;"/>
231                  </div>
232                </div>
233                <table class="detail-table">
234                  <thead>
235                    <tr>
236                      <th width="60">이미지</th>
237                      <th>구분</th>
238                      <th>품명</th>
239                      <th>재질</th>
240                      <th>규격</th>
241                      <th>단위</th>
242                      <th>단가</th>
243                      <th>수량</th>
244                      <th>금액</th>
245                    </tr>
246                  </thead>
247                  <tbody>
248                    ${i.components.map(d=>{const f=_.generateComponentSVG(d.category,d.name,d.size);return`
249                      <tr>
250                        <td class="center">
251                          <img src="${_.svgToDataURL(f)}" alt="${d.name}" style="width: 40px; height: 40px;"/>
252                        </td>
253                        <td>${this.getCategoryName(d.category)}</td>
254                        <td>${d.name}</td>
255                        <td>${d.material}</td>
256                        <td>${d.size||"-"}</td>
257                        <td>${d.unit}</td>
258                        <td class="number">${h(d.unitPrice)}</td>
259                        <td class="center">${d.quantity}</td>
260                        <td class="number">${h(d.amount)}</td>
261                      </tr>
262                      `}).join("")}
263                  </tbody>
264                  <tfoot>
265                    <tr>
266                      <td colspan="8" class="right">소계:</td>
267                      <td class="number"><strong>${h(i.subtotal)}</strong></td>
268                    </tr>
269                  </tfoot>
270                </table>
271              </div>
272            `}).join("")}
273          </section>
274
275          <section class="notes">
276            <h3>참고사항</h3>
277            <ul>
278              ${t.notes.map(i=>`<li>${i}</li>`).join("")}
279            </ul>
280          </section>
281
282          <footer>
283            <div class="signature-area">
284              <div>
285                <p>위 내용과 같이 견적합니다.</p>
286                <p class="date">${r(t.createdAt)}</p>
287              </div>
288              <div class="signature">
289                <p>담당자: _________________</p>
290              </div>
291            </div>
292          </footer>
293        </div>
294      </body>
295      </html>
296    `}getModuleName(t){return{B_SINK:"싱크대 하부장",B_DOOR:"양문형 하부장",B_DRAWER_3:"3단 서랍장",B_DRAWER_2:"2단 서랍장",B_SINGLE_DOOR_RIGHT:"1문형 하부장",W_SHELF:"선반형 상부장",W_FLAP:"플랩형 상부장",T_PANTRY:"팬트리 키큰장",T_OVEN:"빌트인 오븐장",base:"하부장",wall:"상부장",tall:"키큰장"}[t]||t}getCategoryName(t){return{panel:"패널",door:"문짝",drawer:"서랍",hardware:"하드웨어",accessory:"액세서리"}[t]||t}getPrintStyles(){return`
297      * {
298        margin: 0;
299        padding: 0;
300        box-sizing: border-box;
301      }
302
303      body {
304        font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
305        line-height: 1.6;
306        color: #333;
307        background: white;
308      }
309
310      .quotation-container {
311        max-width: 210mm;
312        margin: 0 auto;
313        padding: 20mm;
314        background: white;
315      }
316
317      header {
318        display: flex;
319        justify-content: space-between;
320        align-items: flex-start;
321        margin-bottom: 30px;
322        padding-bottom: 20px;
323        border-bottom: 2px solid #333;
324      }
325
326      .company-info h1 {
327        font-size: 28px;
328        color: #2d3748;
329        margin-bottom: 5px;
330      }
331
332      .company-info p {
333        font-size: 14px;
334        color: #718096;
335      }
336
337      .quotation-title h2 {
338        font-size: 24px;
339        text-align: right;
340        margin-bottom: 5px;
341      }
342
343      .quotation-number {
344        font-size: 14px;
345        text-align: right;
346        color: #4a5568;
347      }
348
349      section {
350        margin-bottom: 30px;
351      }
352
353      h3 {
354        font-size: 18px;
355        margin-bottom: 15px;
356        color: #2d3748;
357        padding-bottom: 5px;
358        border-bottom: 1px solid #e2e8f0;
359      }
360
361      h4 {
362        font-size: 14px;
363        margin: 0 0 5px 0;
364        color: #2d3748;
365      }
366
367      .dates {
368        display: flex;
369        justify-content: space-between;
370        font-size: 14px;
371        color: #4a5568;
372      }
373
374      .customer-info table {
375        width: 100%;
376        font-size: 14px;
377      }
378
379      .customer-info th {
380        width: 100px;
381        text-align: left;
382        padding: 5px 10px;
383        color: #4a5568;
384      }
385
386      .customer-info td {
387        padding: 5px 10px;
388        border-bottom: 1px solid #e2e8f0;
389      }
390
391      .summary-table {
392        width: 100%;
393        font-size: 14px;
394      }
395
396      .summary-table th {
397        text-align: left;
398        padding: 8px;
399        color: #4a5568;
400      }
401
402      .summary-table td {
403        text-align: right;
404        padding: 8px;
405        font-weight: 500;
406      }
407
408      .summary-table .discount {
409        color: #dc2626;
410      }
411
412      .summary-table .total-row {
413        border-top: 2px solid #333;
414        font-size: 16px;
415        font-weight: bold;
416      }
417
418      .summary-table .total {
419        color: #059669;
420        font-size: 18px;
421      }
422
423      .module-section {
424        margin-bottom: 30px;
425        page-break-inside: avoid;
426      }
427
428      .module-header {
429        background: #f8f9fa;
430        padding: 10px 15px;
431        border-radius: 8px 8px 0 0;
432        border: 1px solid #e5e7eb;
433        border-bottom: none;
434      }
435
436      .module-dims {
437        margin: 0;
438        font-size: 12px;
439        color: #718096;
440      }
441
442      .detail-table {
443        width: 100%;
444        font-size: 12px;
445        border-collapse: collapse;
446        border: 1px solid #e5e7eb;
447      }
448
449      .detail-table thead {
450        background: #f3f4f6;
451      }
452
453      .detail-table th {
454        padding: 8px;
455        text-align: left;
456        border: 1px solid #e5e7eb;
457        font-weight: 600;
458      }
459
460      .detail-table td {
461        padding: 6px 8px;
462        border: 1px solid #e5e7eb;
463      }
464
465      .detail-table .number {
466        text-align: right;
467      }
468
469      .detail-table .center {
470        text-align: center;
471      }
472
473      .detail-table .right {
474        text-align: right;
475      }
476
477      .detail-table tfoot {
478        background: #f9fafb;
479        font-weight: 600;
480      }
481
482      .notes ul {
483        list-style: none;
484        padding-left: 0;
485      }
486
487      .notes li {
488        font-size: 13px;
489        color: #4a5568;
490        margin-bottom: 5px;
491        padding-left: 20px;
492        position: relative;
493      }
494
495      .notes li:before {
496        content: "•";
497        position: absolute;
498        left: 5px;
499      }
500
501      footer {
502        margin-top: 50px;
503        padding-top: 30px;
504        border-top: 1px solid #e2e8f0;
505      }
506
507      .signature-area {
508        display: flex;
509        justify-content: space-between;
510        align-items: flex-end;
511      }
512
513      .signature-area p {
514        font-size: 14px;
515        margin-bottom: 10px;
516      }
517
518      .signature-area .date {
519        margin-top: 20px;
520        color: #4a5568;
521      }
522
523      @media print {
524        .no-print {
525          display: none !important;
526        }
527
528        @page {
529          size: A4;
530          margin: 15mm;
531        }
532
533        body {
534          margin: 0;
535          print-color-adjust: exact;
536          -webkit-print-color-adjust: exact;
537        }
538
539        .quotation-container {
540          padding: 0;
541        }
542
543        .module-section {
544          page-break-inside: avoid;
545        }
546
547        header {
548          page-break-after: avoid;
549        }
550
551        thead {
552          display: table-header-group;
553        }
554
555        table {
556          page-break-inside: auto;
557        }
558
559        tr {
560          page-break-inside: avoid;
561          page-break-after: auto;
562        }
563
564        .summary-section,
565        .notes {
566          page-break-inside: avoid;
567        }
568      }
569    `}}const C=[{id:"B_SINK",label:"싱크대",icon:"🚰"},{id:"B_DOOR",label:"양문형",icon:"🚪"},{id:"B_DRAWER_3",label:"3단 서랍",icon:"🗄️"},{id:"B_DRAWER_2",label:"2단 서랍",icon:"🗄️"},{id:"B_SINGLE_DOOR_RIGHT",label:"1문형",icon:"🚪"}],P=[{id:"W_SHELF",label:"선반형",icon:"🗃"},{id:"W_FLAP",label:"플랩형",icon:"📦"}],Q=[{value:300,label:"300mm"},{value:400,label:"400mm"},{value:450,label:"450mm"},{value:500,label:"500mm"},{value:600,label:"600mm"},{value:800,label:"800mm"},{value:900,label:"900mm"}],Y=()=>{const{generatedLayout:x,selectedModuleId:t,selectModule:r,replaceModule:h,resizeModule:l,addModule:i,removeModule:m}=v(),a=x?.modules.find(s=>s.id===t);return x?e.jsx("div",{className:"flex flex-col h-full bg-white rounded-lg shadow-lg",children:e.jsxs("div",{className:"flex-1 overflow-y-auto p-6 space-y-6",children:[e.jsx("h2",{className:"text-xl font-bold text-gray-800",children:"가구 컨트롤러"}),a?e.jsxs("div",{className:"space-y-4",children:[e.jsxs("div",{className:"bg-green-50 p-4 rounded-lg",children:[e.jsx("h3",{className:"font-semibold text-green-900 mb-2",children:"선택된 가구"}),e.jsxs("div",{className:"text-sm text-green-700",children:[e.jsxs("p",{children:["ID: ",a.id]}),e.jsxs("p",{children:["타입: ",a.config.preset]}),e.jsxs("p",{children:["크기: ",a.dimensions.width," × ",a.dimensions.height," × ",a.dimensions.depth,"mm"]})]}),e.jsx("button",{onClick:()=>r(null),className:"mt-2 text-xs text-green-600 hover:text-green-800",children:"선택 해제"})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-gray-700 mb-2",children:"가구 교체"}),e.jsx("div",{className:"grid grid-cols-2 gap-2",children:(a.type==="base"?C:P).map(s=>e.jsxs("button",{onClick:()=>h(a.id,s.id),disabled:a.config.preset===s.id,className:`
570                    flex items-center justify-center p-3 rounded-lg border-2 transition-all
571                    ${a.config.preset===s.id?"bg-green-100 border-green-500 cursor-not-allowed":"bg-white border-gray-200 hover:border-green-400 hover:bg-green-50"}
572                  `,children:[e.jsx("span",{className:"mr-2 text-xl",children:s.icon}),e.jsx("span",{className:"text-sm font-medium",children:s.label})]},s.id))})]}),e.jsxs("div",{children:[e.jsx("h3",{className:"font-semibold text-gray-700 mb-2",children:"너비 조절"}),e.jsx("select",{value:a.dimensions.width,onChange:s=>l(a.id,{width:parseInt(s.target.value)}),className:"w-full p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500",children:Q.map(s=>e.jsx("option",{value:s.value,children:s.label},s.value))})]})]}):e.jsxs("div",{className:"text-center py-8 text-gray-500",children:[e.jsx("p",{className:"mb-2",children:"가구를 클릭하여 선택하세요"}),e.jsx("p",{className:"text-sm",children:"선택된 가구를 교체하거나 크기를 조절할 수 있습니다"})]}),e.jsxs("div",{className:"border-t pt-4",children:[e.jsx("h3",{className:"font-semibold text-gray-700 mb-3",children:"캐비닛 추가"}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium text-gray-600 mb-2",children:"하부장 추가"}),e.jsx("div",{className:"grid grid-cols-3 gap-2",children:C.slice(1).map(s=>e.jsxs("button",{onClick:()=>i("base",s.id,600),className:"flex flex-col items-center p-2 rounded-lg border border-gray-200 hover:border-green-400 hover:bg-green-50 transition-all",children:[e.jsx("span",{className:"text-lg",children:s.icon}),e.jsx("span",{className:"text-xs mt-1",children:s.label})]},s.id))})]}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-sm font-medium text-gray-600 mb-2",children:"상부장 추가"}),e.jsx("div",{className:"grid grid-cols-3 gap-2",children:P.map(s=>e.jsxs("button",{onClick:()=>i("wall",s.id,600),className:"flex flex-col items-center p-2 rounded-lg border border-gray-200 hover:border-blue-400 hover:bg-blue-50 transition-all",children:[e.jsx("span",{className:"text-lg",children:s.icon}),e.jsx("span",{className:"text-xs mt-1",children:s.label})]},s.id))})]})]})]}),e.jsxs("div",{className:"border-t pt-4",children:[e.jsx("h3",{className:"font-semibold text-gray-700 mb-2",children:"레이아웃 정보"}),e.jsxs("div",{className:"text-sm text-gray-600 space-y-1",children:[e.jsxs("p",{children:["하부장: ",x.modules.filter(s=>s.type==="base").length,"개"]}),e.jsxs("p",{children:["상부장: ",x.modules.filter(s=>s.type==="wall").length,"개"]}
572),e.jsxs("p",{children:["총 가격: ",x.totalPrice.toLocaleString(),"원"]})]})]}),e.jsxs("div",{className:"border-t pt-4",children:[e.jsx("h3",{className:"font-semibold text-gray-700 mb-2",children:"모듈 목록"}),e.jsx("div",{className:"space-y-2 max-h-48 overflow-y-auto",children:x.modules.map(s=>e.jsx("button",{onClick:()=>r(s.id),className:`
573                w-full text-left p-2 rounded-lg transition-all text-sm
574                ${s.id===t?"bg-green-100 border-2 border-green-500":"bg-gray-50 hover:bg-gray-100 border-2 border-transparent"}
575              `,children:e.jsxs("div",{className:"flex justify-between items-center",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-600",children:s.type==="base"?"하부":"상부"}),e.jsx("span",{className:"font-medium",children:s.config.preset})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-xs text-gray-500",children:[s.dimensions.width,"mm"]}),e.jsx("button",{onClick:d=>{d.stopPropagation(),m(s.id)},className:"text-red-500 hover:text-red-700 transition-colors",title:"제거",children:"❌"})]})]})},s.id))})]}),e.jsx("div",{className:"border-t pt-4",children:e.jsxs("button",{onClick:()=>{if(!x)return;const s=new V("white_oak"),d=x,f=s.generateQuotation(d),u=s.formatToHTML(f),c=window.open("","_blank","width=900,height=1200,scrollbars=yes,resizable=yes");if(c){c.document.write(u),c.document.close();const p=c.document.createElement("div");p.className="no-print",p.style.cssText=`
576                position: fixed;
577                top: 20px;
578                right: 20px;
579                display: flex;
580                gap: 10px;
581                z-index: 1000;
582                background: white;
583                padding: 10px;
584                border-radius: 8px;
585                box-shadow: 0 2px 10px rgba(0,0,0,0.1);
586              `;const g=c.document.createElement("button");g.textContent="🖨️ 인쇄",g.style.cssText=`
587                padding: 10px 20px;
588                background: #059669;
589                color: white;
590                border: none;
591                border-radius: 6px;
592                font-size: 14px;
593                font-weight: 600;
594                cursor: pointer;
595              `,g.onclick=()=>c.print();const n=c.document.createElement("button");n.textContent="📄 PDF 저장",n.style.cssText=`
596                padding: 10px 20px;
597                background: #3b82f6;
598                color: white;
599                border: none;
600                border-radius: 6px;
601                font-size: 14px;
602                font-weight: 600;
603                cursor: pointer;
604              `,n.onclick=()=>{alert('인쇄 창에서 "PDF로 저장"을 선택하세요.'),c.print()};const o=c.document.createElement("button");o.textContent="✕ 닫기",o.style.cssText=`
605                padding: 10px 20px;
606                background: #6b7280;
607                color: white;
608                border: none;
609                border-radius: 6px;
610                font-size: 14px;
611                font-weight: 600;
612                cursor: pointer;
613              `,o.onclick=()=>c.close(),p.appendChild(g),p.appendChild(n),p.appendChild(o),c.document.body.appendChild(p);const b=c.document.createElement("link");b.href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;600;700&display=swap",b.rel="stylesheet",c.document.head.appendChild(b)}},className:"w-full flex items-center justify-center gap-2 px-4 py-3 bg-green-600 text-white font-semibold rounded-lg hover:bg-green-700 transition-colors",children:[e.jsx("span",{children:"📄"}),e.jsx("span",{children:"견적서 보기"})]})}),e.jsx("div",{className:"border-t pt-4",children:e.jsx("button",{onClick:()=>{v.getState().reset(),window.dispatchEvent(new CustomEvent("resetWizard"))},className:"w-full px-4 py-2.5 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors text-sm font-medium",children:"처음부터 다시 설계"})})]})}):null},xe=()=>{const[x,t]=j.useState(!1),{spaceConfig:r,generatedLayout:h,generateLayout:l,isDoorsOpen:i,toggleDoors:m}=v(),a=F(d=>d.getContentPadding()),s=async()=>{t(!0),r&&await l(r)};return E.useEffect(()=>{const d=()=>{t(!1)};return window.addEventListener("resetWizard",d),()=>window.removeEventListener("resetWizard",d)},[]),e.jsxs(e.Fragment,{children:[e.jsx(q,{}),e.jsxs("div",{className:"relative w-full bg-gradient-to-br from-gray-50 to-gray-100",style:{height:`calc(100vh - ${a} - ${k}px)`,marginTop:`calc(${a} + ${k}
613px)`},children:[e.jsx("div",{className:"absolute inset-0",style:{width:x?"calc(100% - 384px)":"100%",marginLeft:"0"},children:e.jsxs(L,{shadows:!0,camera:{position:[3,2.5,5],fov:45},className:"w-full h-full",children:[e.jsx("ambientLight",{intensity:.4}),e.jsx("directionalLight",{position:[3,3,3],intensity:.6,castShadow:!0,"shadow-mapSize":[2048,2048]}),e.jsx("pointLight",{position:[-3,2,-3],intensity:.3}),e.jsx(z,{enablePan:!0,enableZoom:!0,enableRotate:!0,target:[0,.35,0],minDistance:1,maxDistance:20}),e.jsx(D,{preset:"apartment",background:!1}),e.jsx(T,{layout:h||{modules:[],totalPrice:0,recommendations:[]}})]})}),x&&e.jsx("div",{className:"absolute top-4 left-1/2 transform -translate-x-1/2 z-50",children:e.jsxs("button",{onClick:m,className:"group flex items-center gap-2 px-4 py-2.5 bg-white/90 backdrop-blur-sm rounded-lg shadow-md hover:shadow-lg transition-all duration-200 border border-gray-200/50",children:[e.jsx("div",{className:`transition-transform duration-300 ${i?"rotate-90":""}`,children:e.jsx("svg",{className:"w-4 h-4 text-gray-600",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:1.5,d:"M15 19l-7-7 7-7"})})}),e.jsx("span",{className:"text-sm font-medium text-gray-700",children:i?"모두 닫기":"모두 열기"}),e.jsxs("div",{className:"flex items-center gap-1 ml-1",children:[e.jsx("div",{className:"w-1.5 h-1.5 rounded-full bg-gray-400"}),e.jsx("div",{className:"w-1.5 h-1.5 rounded-full bg-gray-400"}),e.jsx("div",{className:"w-1.5 h-1.5 rounded-full bg-gray-400"})]})]})}),!x&&e.jsx(W,{onComplete:s}),x&&e.jsx("div",{className:"absolute top-0 right-0 w-96 h-full bg-white/50 backdrop-blur-sm border-l border-gray-200",children:e.jsx("div",{className:"h-full p-4",children:e.jsx(Y,{})})})]})]})};export{xe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.