Merge remote-tracking branch 'origin/master'
# Conflicts: # src/components/quotes/QuoteRegistration.tsx
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
/**
|
||||
* 수주 등록 컴포넌트
|
||||
*
|
||||
* IntegratedDetailTemplate 마이그레이션 (2026-01-20)
|
||||
* - 견적 불러오기 섹션
|
||||
* - 기본 정보 섹션
|
||||
* - 수주/배송 정보 섹션
|
||||
@@ -19,7 +20,6 @@ import { Textarea } from "@/components/ui/textarea";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import {
|
||||
@@ -44,12 +44,15 @@ import {
|
||||
Plus,
|
||||
Trash2,
|
||||
Info,
|
||||
MapPin,
|
||||
Truck,
|
||||
Package,
|
||||
MessageSquare,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
ResponsiveFormTemplate,
|
||||
FormSection,
|
||||
} from "@/components/templates/ResponsiveFormTemplate";
|
||||
import { IntegratedDetailTemplate } from "@/components/templates/IntegratedDetailTemplate";
|
||||
import { orderCreateConfig, orderEditConfig } from "./orderConfig";
|
||||
import { FormSection } from "@/components/organisms/FormSection";
|
||||
import { QuotationSelectDialog } from "./QuotationSelectDialog";
|
||||
import { type QuotationForSelect, type QuotationItem } from "./actions";
|
||||
import { ItemAddDialog, OrderItem } from "./ItemAddDialog";
|
||||
@@ -179,6 +182,9 @@ export function OrderRegistration({
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
const [fieldErrors, setFieldErrors] = useState<FieldErrors>({});
|
||||
|
||||
// Config 선택
|
||||
const config = isEditMode ? orderEditConfig : orderCreateConfig;
|
||||
|
||||
// 거래처 목록 조회
|
||||
const { clients, fetchClients, isLoading: isClientsLoading } = useClientList();
|
||||
|
||||
@@ -349,7 +355,7 @@ export function OrderRegistration({
|
||||
}, []);
|
||||
|
||||
// 저장 핸들러
|
||||
const handleSave = async () => {
|
||||
const handleSave = useCallback(async () => {
|
||||
// 유효성 검사
|
||||
const errors = validateForm();
|
||||
setFieldErrors(errors);
|
||||
@@ -366,21 +372,12 @@ export function OrderRegistration({
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
};
|
||||
}, [form, validateForm, onSave]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResponsiveFormTemplate
|
||||
title={isEditMode ? "수주 수정" : "수주 등록"}
|
||||
description="견적을 수주로 전환하거나 새 수주를 등록합니다"
|
||||
icon={FileText}
|
||||
onSave={handleSave}
|
||||
onCancel={onBack}
|
||||
saveLabel="저장"
|
||||
cancelLabel="취소"
|
||||
saveLoading={isSaving}
|
||||
saveDisabled={isSaving}
|
||||
>
|
||||
// 폼 콘텐츠 렌더링
|
||||
const renderFormContent = useCallback(
|
||||
() => (
|
||||
<div className="space-y-6 max-w-4xl">
|
||||
{/* Validation 에러 Alert */}
|
||||
{Object.keys(fieldErrors).length > 0 && (
|
||||
<Alert className="bg-red-50 border-red-200">
|
||||
@@ -411,13 +408,12 @@ export function OrderRegistration({
|
||||
)}
|
||||
|
||||
{/* 견적 불러오기 섹션 */}
|
||||
<FormSection title="견적 불러오기" icon={Search}>
|
||||
<FormSection
|
||||
title="견적 불러오기"
|
||||
description="확정된 견적을 선택하면 정보가 자동으로 채워집니다"
|
||||
icon={Search}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<Info className="h-4 w-4" />
|
||||
확정된 견적을 선택하면 정보가 자동으로 채워집니다
|
||||
</div>
|
||||
|
||||
{form.selectedQuotation ? (
|
||||
<div className="p-4 border rounded-lg bg-muted/30">
|
||||
<div className="flex items-start justify-between">
|
||||
@@ -463,7 +459,11 @@ export function OrderRegistration({
|
||||
</FormSection>
|
||||
|
||||
{/* 기본 정보 섹션 */}
|
||||
<FormSection title="기본 정보" icon={FileText}>
|
||||
<FormSection
|
||||
title="기본 정보"
|
||||
description="발주처 및 현장 정보를 입력하세요"
|
||||
icon={FileText}
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>
|
||||
@@ -544,7 +544,11 @@ export function OrderRegistration({
|
||||
</FormSection>
|
||||
|
||||
{/* 수주/배송 정보 섹션 */}
|
||||
<FormSection title="수주/배송 정보">
|
||||
<FormSection
|
||||
title="수주/배송 정보"
|
||||
description="출고 및 배송 정보를 입력하세요"
|
||||
icon={Truck}
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* 출고예정일 */}
|
||||
<div className="space-y-2">
|
||||
@@ -719,7 +723,11 @@ export function OrderRegistration({
|
||||
</FormSection>
|
||||
|
||||
{/* 수신처 주소 섹션 */}
|
||||
<FormSection title="수신처 주소">
|
||||
<FormSection
|
||||
title="수신처 주소"
|
||||
description="배송지 주소를 입력하세요"
|
||||
icon={MapPin}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
@@ -752,7 +760,11 @@ export function OrderRegistration({
|
||||
</FormSection>
|
||||
|
||||
{/* 비고 섹션 */}
|
||||
<FormSection title="비고">
|
||||
<FormSection
|
||||
title="비고"
|
||||
description="특이사항을 입력하세요"
|
||||
icon={MessageSquare}
|
||||
>
|
||||
<Textarea
|
||||
placeholder="특이사항을 입력하세요"
|
||||
value={form.remarks}
|
||||
@@ -764,7 +776,11 @@ export function OrderRegistration({
|
||||
</FormSection>
|
||||
|
||||
{/* 품목 내역 섹션 */}
|
||||
<FormSection title="품목 내역">
|
||||
<FormSection
|
||||
title="품목 내역"
|
||||
description="수주 품목을 관리하세요"
|
||||
icon={Package}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
{/* 품목 에러 메시지 */}
|
||||
{fieldErrors.items && (
|
||||
@@ -883,7 +899,33 @@ export function OrderRegistration({
|
||||
</div>
|
||||
</div>
|
||||
</FormSection>
|
||||
</ResponsiveFormTemplate>
|
||||
</div>
|
||||
),
|
||||
[
|
||||
form,
|
||||
fieldErrors,
|
||||
clients,
|
||||
isClientsLoading,
|
||||
openPostcode,
|
||||
clearFieldError,
|
||||
handleClearQuotation,
|
||||
handleQuantityChange,
|
||||
handleRemoveItem,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IntegratedDetailTemplate
|
||||
config={config}
|
||||
mode={isEditMode ? "edit" : "create"}
|
||||
isLoading={false}
|
||||
isSubmitting={isSaving}
|
||||
onBack={onBack}
|
||||
onCancel={onBack}
|
||||
onSubmit={handleSave}
|
||||
renderForm={renderFormContent}
|
||||
/>
|
||||
|
||||
{/* 견적 선택 팝업 */}
|
||||
<QuotationSelectDialog
|
||||
@@ -901,4 +943,4 @@ export function OrderRegistration({
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
38
src/components/orders/orderConfig.ts
Normal file
38
src/components/orders/orderConfig.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
'use client';
|
||||
|
||||
import { FileText } from 'lucide-react';
|
||||
import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types';
|
||||
|
||||
/**
|
||||
* 수주 등록 페이지 Config
|
||||
*/
|
||||
export const orderCreateConfig: DetailConfig = {
|
||||
title: '수주 등록',
|
||||
description: '견적을 수주로 전환하거나 새 수주를 등록합니다',
|
||||
icon: FileText,
|
||||
basePath: '/sales/order-management',
|
||||
fields: [],
|
||||
actions: {
|
||||
showBack: true,
|
||||
showSave: true,
|
||||
submitLabel: '저장',
|
||||
backLabel: '취소',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 수주 수정 페이지 Config
|
||||
*/
|
||||
export const orderEditConfig: DetailConfig = {
|
||||
title: '수주 수정',
|
||||
description: '수주 정보를 수정합니다',
|
||||
icon: FileText,
|
||||
basePath: '/sales/order-management',
|
||||
fields: [],
|
||||
actions: {
|
||||
showBack: true,
|
||||
showSave: true,
|
||||
submitLabel: '저장',
|
||||
backLabel: '취소',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user