feat(WEB): 입찰/계약/주문관리 기능 추가 및 견적 상세 리팩토링
- 입찰관리: 목록/상세/수정 페이지 및 목업 데이터 - 계약관리: 목록/상세/수정 페이지 구현 - 주문관리: 수주/발주 목록 및 상세 페이지 구현 - 견적 상세 폼: 섹션별 분리 및 hooks/utils 리팩토링 - 품목관리, 카테고리관리, 단가관리 기능 추가 - 현장설명회/협력업체 폼 개선 - 프린트 유틸리티 공통화 (print-utils.ts) - 문서 모달 공통 컴포넌트 정리 - IntegratedListTemplateV2, StatCards 개선 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
import { CategoryManagement } from '@/components/business/juil/category-management';
|
||||
|
||||
export default function CategoriesPage() {
|
||||
return <CategoryManagement />;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ItemDetailClient } from '@/components/business/juil/item-management';
|
||||
|
||||
interface ItemDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
searchParams: Promise<{ mode?: string }>;
|
||||
}
|
||||
|
||||
export default async function ItemDetailPage({ params, searchParams }: ItemDetailPageProps) {
|
||||
const { id } = await params;
|
||||
const { mode } = await searchParams;
|
||||
const isEditMode = mode === 'edit';
|
||||
|
||||
return <ItemDetailClient itemId={id} isEditMode={isEditMode} />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ItemDetailClient } from '@/components/business/juil/item-management';
|
||||
|
||||
export default function ItemNewPage() {
|
||||
return <ItemDetailClient isNewMode />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ItemManagementClient } from '@/components/business/juil/item-management';
|
||||
|
||||
export default function ItemManagementPage() {
|
||||
return <ItemManagementClient />;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { LaborDetailClient } from '@/components/business/juil/labor-management';
|
||||
|
||||
interface LaborDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
searchParams: Promise<{ mode?: string }>;
|
||||
}
|
||||
|
||||
export default async function LaborDetailPage({ params, searchParams }: LaborDetailPageProps) {
|
||||
const { id } = await params;
|
||||
const { mode } = await searchParams;
|
||||
const isEditMode = mode === 'edit';
|
||||
|
||||
return <LaborDetailClient laborId={id} isEditMode={isEditMode} />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { LaborDetailClient } from '@/components/business/juil/labor-management';
|
||||
|
||||
export default function LaborNewPage() {
|
||||
return <LaborDetailClient isNewMode />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { LaborManagementClient } from '@/components/business/juil/labor-management';
|
||||
|
||||
export default function LaborManagementPage() {
|
||||
return <LaborManagementClient />;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import PricingDetailClient from '@/components/business/juil/pricing-management/PricingDetailClient';
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function PricingEditPage({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
return <PricingDetailClient id={id} mode="edit" />;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import PricingDetailClient from '@/components/business/juil/pricing-management/PricingDetailClient';
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function PricingDetailPage({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
return <PricingDetailClient id={id} mode="view" />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import PricingDetailClient from '@/components/business/juil/pricing-management/PricingDetailClient';
|
||||
|
||||
export default function PricingNewPage() {
|
||||
return <PricingDetailClient mode="create" />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import PricingListClient from '@/components/business/juil/pricing-management/PricingListClient';
|
||||
|
||||
export default function PricingPage() {
|
||||
return <PricingListClient />;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { OrderDetailForm } from '@/components/business/juil/order-management';
|
||||
import { getOrderDetailFull } from '@/components/business/juil/order-management/actions';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
interface OrderEditPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function OrderEditPage({ params }: OrderEditPageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
const result = await getOrderDetailFull(id);
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return <OrderDetailForm mode="edit" orderId={id} initialData={result.data} />;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { OrderDetailForm } from '@/components/business/juil/order-management';
|
||||
import { getOrderDetailFull } from '@/components/business/juil/order-management/actions';
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
interface OrderDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function OrderDetailPage({ params }: OrderDetailPageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
const result = await getOrderDetailFull(id);
|
||||
|
||||
if (!result.success || !result.data) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return <OrderDetailForm mode="view" orderId={id} initialData={result.data} />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { OrderManagementListClient } from '@/components/business/juil/order-management';
|
||||
|
||||
export default function OrderManagementPage() {
|
||||
return <OrderManagementListClient />;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import SiteDetailForm from '@/components/business/juil/site-management/SiteDetailForm';
|
||||
|
||||
// 목업 데이터
|
||||
const MOCK_SITE = {
|
||||
id: '1',
|
||||
siteCode: '123-12-12345',
|
||||
partnerId: '1',
|
||||
partnerName: '거래처명',
|
||||
siteName: '현장명',
|
||||
address: '',
|
||||
status: 'active' as const,
|
||||
createdAt: '2025-09-01T00:00:00Z',
|
||||
updatedAt: '2025-09-01T00:00:00Z',
|
||||
};
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function SiteEditPage({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
// TODO: API에서 현장 정보 조회
|
||||
const site = { ...MOCK_SITE, id };
|
||||
|
||||
return <SiteDetailForm site={site} mode="edit" />;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import SiteDetailForm from '@/components/business/juil/site-management/SiteDetailForm';
|
||||
|
||||
// 목업 데이터
|
||||
const MOCK_SITE = {
|
||||
id: '1',
|
||||
siteCode: '123-12-12345',
|
||||
partnerId: '1',
|
||||
partnerName: '거래처명',
|
||||
siteName: '현장명',
|
||||
address: '',
|
||||
status: 'active' as const,
|
||||
createdAt: '2025-09-01T00:00:00Z',
|
||||
updatedAt: '2025-09-01T00:00:00Z',
|
||||
};
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function SiteDetailPage({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
// TODO: API에서 현장 정보 조회
|
||||
const site = { ...MOCK_SITE, id };
|
||||
|
||||
return <SiteDetailForm site={site} mode="view" />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { SiteManagementListClient } from '@/components/business/juil/site-management';
|
||||
|
||||
export default function SiteManagementPage() {
|
||||
return <SiteManagementListClient />;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import StructureReviewDetailForm from '@/components/business/juil/structure-review/StructureReviewDetailForm';
|
||||
|
||||
// 목업 데이터
|
||||
const MOCK_REVIEW = {
|
||||
id: '1',
|
||||
reviewNumber: '123123',
|
||||
partnerId: '1',
|
||||
partnerName: '거래처명A',
|
||||
siteId: '1',
|
||||
siteName: '현장A',
|
||||
requestDate: '2025-12-12',
|
||||
reviewCompany: '회사명',
|
||||
reviewerName: '홍길동',
|
||||
reviewDate: '2025-12-15',
|
||||
completionDate: null,
|
||||
status: 'pending' as const,
|
||||
createdAt: '2025-12-01T00:00:00Z',
|
||||
updatedAt: '2025-12-01T00:00:00Z',
|
||||
};
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function StructureReviewEditPage({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
// TODO: API에서 구조검토 정보 조회
|
||||
const review = { ...MOCK_REVIEW, id };
|
||||
|
||||
return <StructureReviewDetailForm review={review} mode="edit" />;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import StructureReviewDetailForm from '@/components/business/juil/structure-review/StructureReviewDetailForm';
|
||||
|
||||
// 목업 데이터
|
||||
const MOCK_REVIEW = {
|
||||
id: '1',
|
||||
reviewNumber: '123123',
|
||||
partnerId: '1',
|
||||
partnerName: '거래처명A',
|
||||
siteId: '1',
|
||||
siteName: '현장A',
|
||||
requestDate: '2025-12-12',
|
||||
reviewCompany: '회사명',
|
||||
reviewerName: '홍길동',
|
||||
reviewDate: '2025-12-15',
|
||||
completionDate: null,
|
||||
status: 'pending' as const,
|
||||
createdAt: '2025-12-01T00:00:00Z',
|
||||
updatedAt: '2025-12-01T00:00:00Z',
|
||||
};
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function StructureReviewDetailPage({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
// TODO: API에서 구조검토 정보 조회
|
||||
const review = { ...MOCK_REVIEW, id };
|
||||
|
||||
return <StructureReviewDetailForm review={review} mode="view" />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import StructureReviewListClient from '@/components/business/juil/structure-review/StructureReviewListClient';
|
||||
|
||||
export default function StructureReviewListPage() {
|
||||
return <StructureReviewListClient />;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BiddingDetailForm, getBiddingDetail } from '@/components/business/juil/bidding';
|
||||
|
||||
interface BiddingEditPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function BiddingEditPage({ params }: BiddingEditPageProps) {
|
||||
const { id } = await params;
|
||||
const result = await getBiddingDetail(id);
|
||||
|
||||
return (
|
||||
<BiddingDetailForm
|
||||
mode="edit"
|
||||
biddingId={id}
|
||||
initialData={result.data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { BiddingDetailForm, getBiddingDetail } from '@/components/business/juil/bidding';
|
||||
|
||||
interface BiddingDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function BiddingDetailPage({ params }: BiddingDetailPageProps) {
|
||||
const { id } = await params;
|
||||
const result = await getBiddingDetail(id);
|
||||
|
||||
return (
|
||||
<BiddingDetailForm
|
||||
mode="view"
|
||||
biddingId={id}
|
||||
initialData={result.data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
import { EstimateDetailForm } from '@/components/business/juil/estimates';
|
||||
import type { EstimateDetail } from '@/components/business/juil/estimates';
|
||||
|
||||
interface EstimateEditPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
// 목업 데이터 - 추후 API 연동
|
||||
async function getEstimateDetail(id: string): Promise<EstimateDetail | null> {
|
||||
// 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 async function EstimateEditPage({ params }: EstimateEditPageProps) {
|
||||
const { id } = await params;
|
||||
const detail = await getEstimateDetail(id);
|
||||
|
||||
return (
|
||||
<EstimateDetailForm
|
||||
mode="edit"
|
||||
estimateId={id}
|
||||
initialData={detail || undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
import { EstimateDetailForm } from '@/components/business/juil/estimates';
|
||||
import type { EstimateDetail } from '@/components/business/juil/estimates';
|
||||
|
||||
interface EstimateDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
// 목업 데이터 - 추후 API 연동
|
||||
async function getEstimateDetail(id: string): Promise<EstimateDetail | null> {
|
||||
// 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 async function EstimateDetailPage({ params }: EstimateDetailPageProps) {
|
||||
const { id } = await params;
|
||||
const detail = await getEstimateDetail(id);
|
||||
|
||||
return (
|
||||
<EstimateDetailForm
|
||||
mode="view"
|
||||
estimateId={id}
|
||||
initialData={detail || undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { BiddingListClient } from '@/components/business/juil/bidding';
|
||||
|
||||
export default function BiddingPage() {
|
||||
return <BiddingListClient />;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import ContractDetailForm from '@/components/business/juil/contract/ContractDetailForm';
|
||||
import { getContractDetail } from '@/components/business/juil/contract';
|
||||
|
||||
interface ContractEditPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function ContractEditPage({ params }: ContractEditPageProps) {
|
||||
const { id } = await params;
|
||||
const result = await getContractDetail(id);
|
||||
|
||||
return (
|
||||
<ContractDetailForm
|
||||
mode="edit"
|
||||
contractId={id}
|
||||
initialData={result.success ? result.data : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import ContractDetailForm from '@/components/business/juil/contract/ContractDetailForm';
|
||||
import { getContractDetail } from '@/components/business/juil/contract';
|
||||
|
||||
interface ContractDetailPageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function ContractDetailPage({ params }: ContractDetailPageProps) {
|
||||
const { id } = await params;
|
||||
const result = await getContractDetail(id);
|
||||
|
||||
return (
|
||||
<ContractDetailForm
|
||||
mode="view"
|
||||
contractId={id}
|
||||
initialData={result.success ? result.data : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { HandoverReportDetailForm, getHandoverReportDetail } from '@/components/business/juil/handover-report';
|
||||
|
||||
interface HandoverReportEditPageProps {
|
||||
params: Promise<{
|
||||
id: string;
|
||||
locale: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default async function HandoverReportEditPage({ params }: HandoverReportEditPageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
// 서버에서 상세 데이터 조회
|
||||
const result = await getHandoverReportDetail(id);
|
||||
|
||||
return (
|
||||
<HandoverReportDetailForm
|
||||
mode="edit"
|
||||
reportId={id}
|
||||
initialData={result.data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { HandoverReportDetailForm, getHandoverReportDetail } from '@/components/business/juil/handover-report';
|
||||
|
||||
interface HandoverReportDetailPageProps {
|
||||
params: Promise<{
|
||||
id: string;
|
||||
locale: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export default async function HandoverReportDetailPage({ params }: HandoverReportDetailPageProps) {
|
||||
const { id } = await params;
|
||||
|
||||
// 서버에서 상세 데이터 조회
|
||||
const result = await getHandoverReportDetail(id);
|
||||
|
||||
return (
|
||||
<HandoverReportDetailForm
|
||||
mode="view"
|
||||
reportId={id}
|
||||
initialData={result.data}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { HandoverReportListClient } from '@/components/business/juil/handover-report';
|
||||
|
||||
export default function HandoverReportPage() {
|
||||
return <HandoverReportListClient />;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ContractListClient } from '@/components/business/juil/contract';
|
||||
|
||||
export default function ContractPage() {
|
||||
return <ContractListClient />;
|
||||
}
|
||||
@@ -453,4 +453,93 @@ html {
|
||||
|
||||
[data-slot="sheet-overlay"][data-state="closed"] {
|
||||
animation: fadeOut 200ms ease-out forwards;
|
||||
}
|
||||
|
||||
/* ==========================================
|
||||
Print Styles - 인쇄 시 문서만 출력
|
||||
========================================== */
|
||||
@media print {
|
||||
/* A4 용지 설정 */
|
||||
@page {
|
||||
size: A4 portrait;
|
||||
margin: 10mm;
|
||||
}
|
||||
|
||||
/* 배경색 유지 */
|
||||
* {
|
||||
-webkit-print-color-adjust: exact !important;
|
||||
print-color-adjust: exact !important;
|
||||
color-adjust: exact !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
인쇄 스타일 (JavaScript printArea 사용 시 기본값)
|
||||
======================================== */
|
||||
|
||||
/* 기본 설정 - printArea 유틸리티가 새 창에서 인쇄하므로 간단하게 유지 */
|
||||
html, body {
|
||||
background: white !important;
|
||||
}
|
||||
|
||||
/* print-hidden 클래스 숨김 */
|
||||
.print-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
테이블 & 페이지 설정
|
||||
======================================== */
|
||||
|
||||
/* 페이지 나눔 방지 */
|
||||
table, figure, .page-break-avoid {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* 인쇄용 테이블 스타일 */
|
||||
.print-area table {
|
||||
border-collapse: collapse !important;
|
||||
}
|
||||
|
||||
.print-area th,
|
||||
.print-area td {
|
||||
border: 1px solid #000 !important;
|
||||
}
|
||||
|
||||
/* print-area 내부 문서 wrapper - transform 제거 */
|
||||
.print-area > div {
|
||||
max-width: none !important;
|
||||
width: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
box-shadow: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
/* 실제 문서 컨테이너 - A4에 맞게 조정 */
|
||||
.print-area > div > div {
|
||||
width: 100% !important;
|
||||
max-width: 190mm !important;
|
||||
min-height: auto !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 5mm !important;
|
||||
box-shadow: none !important;
|
||||
font-size: 10pt !important;
|
||||
}
|
||||
|
||||
/* 테이블 폰트 크기 축소 */
|
||||
.print-area table {
|
||||
font-size: 9pt !important;
|
||||
}
|
||||
|
||||
.print-area .text-xs {
|
||||
font-size: 8pt !important;
|
||||
}
|
||||
|
||||
.print-area .text-sm {
|
||||
font-size: 9pt !important;
|
||||
}
|
||||
|
||||
.print-area .text-3xl {
|
||||
font-size: 18pt !important;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user