diff --git a/src/app/[locale]/(protected)/construction/project/bidding/estimates/[id]/edit/page.tsx b/src/app/[locale]/(protected)/construction/project/bidding/estimates/[id]/edit/page.tsx index 311890aa..815b3c85 100644 --- a/src/app/[locale]/(protected)/construction/project/bidding/estimates/[id]/edit/page.tsx +++ b/src/app/[locale]/(protected)/construction/project/bidding/estimates/[id]/edit/page.tsx @@ -3,202 +3,36 @@ import { use, useEffect, useState } from 'react'; import { EstimateDetailForm } from '@/components/business/construction/estimates'; import type { EstimateDetail } from '@/components/business/construction/estimates'; +import { getEstimateDetail } from '@/components/business/construction/estimates/actions'; interface EstimateEditPageProps { params: Promise<{ id: string }>; } -// 목업 데이터 - 추후 API 연동 -function getEstimateDetail(id: string): EstimateDetail { - // TODO: 실제 API 연동 - const mockData: EstimateDetail = { - id, - estimateCode: '123123', - partnerId: '1', - partnerName: '회사명', - projectName: '현장명', - estimatorId: 'hong', - estimatorName: '이름', - itemCount: 21, - estimateAmount: 1420000, - completedDate: null, - bidDate: '2025-12-12', - status: 'pending', - createdAt: '2025-12-01', - updatedAt: '2025-12-01', - createdBy: 'hong', - siteBriefing: { - briefingCode: '123123', - partnerName: '회사명', - companyName: '회사명', - briefingDate: '2025-12-12', - attendee: '이름', - }, - bidInfo: { - projectName: '현장명', - bidDate: '2025-12-12', - siteCount: 21, - constructionPeriod: '2026-01-01 ~ 2026-12-10', - constructionStartDate: '2026-01-01', - constructionEndDate: '2026-12-10', - vatType: 'excluded', - workReport: '업무 보고 내용', - documents: [ - { - id: '1', - fileName: 'abc.zip', - fileUrl: '#', - fileSize: 1024000, - }, - ], - }, - summaryItems: [ - { - id: '1', - name: '서터 심창측공사', - quantity: 1, - unit: '식', - materialCost: 78540000, - laborCost: 15410000, - totalCost: 93950000, - remarks: '', - }, - ], - expenseItems: [ - { - id: '1', - name: 'public_1', - amount: 10000, - }, - ], - priceAdjustments: [ - { - id: '1', - category: '배합비', - unitPrice: 10000, - coating: 10000, - batting: 10000, - boxReinforce: 10500, - painting: 10500, - total: 51000, - }, - { - id: '2', - category: '재단비', - unitPrice: 1375, - coating: 0, - batting: 0, - boxReinforce: 0, - painting: 0, - total: 1375, - }, - { - id: '3', - category: '판매단가', - unitPrice: 0, - coating: 10000, - batting: 10000, - boxReinforce: 10500, - painting: 10500, - total: 41000, - }, - { - id: '4', - category: '조립단가', - unitPrice: 10300, - coating: 10300, - batting: 10300, - boxReinforce: 10500, - painting: 10200, - total: 51600, - }, - ], - detailItems: [ - { - id: '1', - no: 1, - name: 'FS530외/주차', - material: 'screen', - width: 2350, - height: 2500, - quantity: 1, - box: 1, - assembly: 0, - coating: 0, - batting: 0, - mounting: 0, - fitting: 0, - controller: 0, - widthConstruction: 0, - heightConstruction: 0, - materialCost: 1420000, - laborCost: 510000, - quantityPrice: 1930000, - expenseQuantity: 5500, - expenseTotal: 5500, - totalCost: 1930000, - otherCost: 0, - marginCost: 0, - totalPrice: 1930000, - unitPrice: 1420000, - expense: 0, - marginRate: 0, - unitQuantity: 1, - expenseResult: 0, - marginActual: 0, - }, - { - id: '2', - no: 2, - name: 'FS530외/주차', - material: 'screen', - width: 7500, - height: 2500, - quantity: 1, - box: 1, - assembly: 0, - coating: 0, - batting: 0, - mounting: 0, - fitting: 0, - controller: 0, - widthConstruction: 0, - heightConstruction: 0, - materialCost: 4720000, - laborCost: 780000, - quantityPrice: 5500000, - expenseQuantity: 5500, - expenseTotal: 5500, - totalCost: 5500000, - otherCost: 0, - marginCost: 0, - totalPrice: 5500000, - unitPrice: 4720000, - expense: 0, - marginRate: 0, - unitQuantity: 1, - expenseResult: 0, - marginActual: 0, - }, - ], - approval: { - approvers: [], - references: [], - }, - }; - - return mockData; -} - export default function EstimateEditPage({ params }: EstimateEditPageProps) { const { id } = use(params); const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { - const detail = getEstimateDetail(id); - setData(detail); - setIsLoading(false); + async function fetchData() { + try { + setIsLoading(true); + setError(null); + const result = await getEstimateDetail(id); + if (result.success && result.data) { + setData(result.data); + } else { + setError(result.error || '견적 정보를 불러오는데 실패했습니다.'); + } + } catch (err) { + setError(err instanceof Error ? err.message : '오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } + } + fetchData(); }, [id]); if (isLoading) { @@ -209,6 +43,14 @@ export default function EstimateEditPage({ params }: EstimateEditPageProps) { ); } + if (error) { + return ( +
+
{error}
+
+ ); + } + return ( ; } -// 목업 데이터 - 추후 API 연동 -function getEstimateDetail(id: string): EstimateDetail { - // TODO: 실제 API 연동 - const mockData: EstimateDetail = { - id, - estimateCode: '123123', - partnerId: '1', - partnerName: '회사명', - projectName: '현장명', - estimatorId: 'hong', - estimatorName: '이름', - itemCount: 21, - estimateAmount: 1420000, - completedDate: null, - bidDate: '2025-12-12', - status: 'pending', - createdAt: '2025-12-01', - updatedAt: '2025-12-01', - createdBy: 'hong', - siteBriefing: { - briefingCode: '123123', - partnerName: '회사명', - companyName: '회사명', - briefingDate: '2025-12-12', - attendee: '이름', - }, - bidInfo: { - projectName: '현장명', - bidDate: '2025-12-12', - siteCount: 21, - constructionPeriod: '2026-01-01 ~ 2026-12-10', - constructionStartDate: '2026-01-01', - constructionEndDate: '2026-12-10', - vatType: 'excluded', - workReport: '업무 보고 내용', - documents: [ - { - id: '1', - fileName: 'abc.zip', - fileUrl: '#', - fileSize: 1024000, - }, - ], - }, - summaryItems: [ - { - id: '1', - name: '서터 심창측공사', - quantity: 1, - unit: '식', - materialCost: 78540000, - laborCost: 15410000, - totalCost: 93950000, - remarks: '', - }, - ], - expenseItems: [ - { - id: '1', - name: 'public_1', - amount: 10000, - }, - ], - priceAdjustments: [ - { - id: '1', - category: '배합비', - unitPrice: 10000, - coating: 10000, - batting: 10000, - boxReinforce: 10500, - painting: 10500, - total: 51000, - }, - { - id: '2', - category: '재단비', - unitPrice: 1375, - coating: 0, - batting: 0, - boxReinforce: 0, - painting: 0, - total: 1375, - }, - { - id: '3', - category: '판매단가', - unitPrice: 0, - coating: 10000, - batting: 10000, - boxReinforce: 10500, - painting: 10500, - total: 41000, - }, - { - id: '4', - category: '조립단가', - unitPrice: 10300, - coating: 10300, - batting: 10300, - boxReinforce: 10500, - painting: 10200, - total: 51600, - }, - ], - detailItems: [ - { - id: '1', - no: 1, - name: 'FS530외/주차', - material: 'screen', - width: 2350, - height: 2500, - quantity: 1, - box: 1, - assembly: 0, - coating: 0, - batting: 0, - mounting: 0, - fitting: 0, - controller: 0, - widthConstruction: 0, - heightConstruction: 0, - materialCost: 1420000, - laborCost: 510000, - quantityPrice: 1930000, - expenseQuantity: 5500, - expenseTotal: 5500, - totalCost: 1930000, - otherCost: 0, - marginCost: 0, - totalPrice: 1930000, - unitPrice: 1420000, - expense: 0, - marginRate: 0, - unitQuantity: 1, - expenseResult: 0, - marginActual: 0, - }, - { - id: '2', - no: 2, - name: 'FS530외/주차', - material: 'screen', - width: 7500, - height: 2500, - quantity: 1, - box: 1, - assembly: 0, - coating: 0, - batting: 0, - mounting: 0, - fitting: 0, - controller: 0, - widthConstruction: 0, - heightConstruction: 0, - materialCost: 4720000, - laborCost: 780000, - quantityPrice: 5500000, - expenseQuantity: 5500, - expenseTotal: 5500, - totalCost: 5500000, - otherCost: 0, - marginCost: 0, - totalPrice: 5500000, - unitPrice: 4720000, - expense: 0, - marginRate: 0, - unitQuantity: 1, - expenseResult: 0, - marginActual: 0, - }, - ], - approval: { - approvers: [], - references: [], - }, - }; - - return mockData; -} - export default function EstimateDetailPage({ params }: EstimateDetailPageProps) { const { id } = use(params); const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); useEffect(() => { - const detail = getEstimateDetail(id); - setData(detail); - setIsLoading(false); + async function fetchData() { + try { + setIsLoading(true); + setError(null); + const result = await getEstimateDetail(id); + if (result.success && result.data) { + setData(result.data); + } else { + setError(result.error || '견적 정보를 불러오는데 실패했습니다.'); + } + } catch (err) { + setError(err instanceof Error ? err.message : '오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } + } + fetchData(); }, [id]); if (isLoading) { @@ -209,6 +43,14 @@ export default function EstimateDetailPage({ params }: EstimateDetailPageProps) ); } + if (error) { + return ( +
+
{error}
+
+ ); + } + return ( | null; + attendee_count: number; + site_count: number; + construction_start_date: string | null; + construction_end_date: string | null; + vat_type: string; partner?: { id: number; name: string; @@ -162,26 +175,38 @@ function mapQuoteStatusToEstimateStatus( */ function transformQuoteToEstimateDetail(apiData: ApiQuote): EstimateDetail { const base = transformQuoteToEstimate(apiData); + const sb = apiData.site_briefing; - const siteBriefing: SiteBriefingInfo = apiData.site_briefing + // 참석자 정보 변환 + const attendeeNames = sb?.attendees + ? sb.attendees.map((a) => a.name).join(', ') + : ''; + + // 공사기간 문자열 생성 + const constructionPeriod = + sb?.construction_start_date && sb?.construction_end_date + ? `${sb.construction_start_date} ~ ${sb.construction_end_date}` + : ''; + + const siteBriefing: SiteBriefingInfo = sb ? { - briefingCode: apiData.site_briefing.briefing_code || '', - partnerName: apiData.site_briefing.partner?.name || '', - companyName: '', - briefingDate: apiData.site_briefing.briefing_date || '', - attendee: '', + briefingCode: sb.briefing_code || '', + partnerName: sb.partner?.name || '', + companyName: sb.partner?.name || '', + briefingDate: sb.briefing_date || '', + attendee: attendeeNames, } : { briefingCode: '', partnerName: '', companyName: '', briefingDate: '', attendee: '' }; const bidInfo: BidInfo = { - projectName: apiData.site_name || '', - bidDate: apiData.registration_date || '', - siteCount: 0, - constructionPeriod: '', - constructionStartDate: '', - constructionEndDate: '', - vatType: 'excluded', - workReport: '', + projectName: sb?.title || apiData.site_name || '', + bidDate: sb?.bid_date || apiData.registration_date || '', + siteCount: sb?.site_count || 0, + constructionPeriod, + constructionStartDate: sb?.construction_start_date || '', + constructionEndDate: sb?.construction_end_date || '', + vatType: (sb?.vat_type as 'excluded' | 'included') || 'excluded', + workReport: sb?.description || '', documents: [], };