feat: 생산/품질/자재/출고/주문 관리 페이지 구현
- 생산관리: 대시보드, 작업지시, 작업실적, 작업자화면 - 품질관리: 검사관리 (리스트/등록/상세) - 자재관리: 입고관리, 재고현황 - 출고관리: 출하관리 (리스트/등록/상세/수정) - 주문관리: 수주관리, 생산의뢰 - 기존 컴포넌트 개선: CardTransactionInquiry, VendorDetail, QuoteRegistration - IntegratedListTemplateV2 개선 - 공통 컴포넌트 분석 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
import { Button } from "../ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Alert, AlertDescription } from "../ui/alert";
|
||||
import {
|
||||
FileText,
|
||||
Calculator,
|
||||
@@ -30,6 +31,19 @@ import {
|
||||
Sparkles,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
// 필드명 매핑
|
||||
const FIELD_NAME_MAP: Record<string, string> = {
|
||||
clientId: "발주처",
|
||||
productCategory: "제품 카테고리",
|
||||
productName: "제품명",
|
||||
openWidth: "오픈사이즈(W)",
|
||||
openHeight: "오픈사이즈(H)",
|
||||
guideRailType: "가이드레일 설치 유형",
|
||||
motorPower: "모터 전원",
|
||||
controller: "연동제어기",
|
||||
quantity: "수량",
|
||||
};
|
||||
import {
|
||||
ResponsiveFormTemplate,
|
||||
FormSection,
|
||||
@@ -226,10 +240,13 @@ export function QuoteRegistration({
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!validateForm()) {
|
||||
toast.error("입력 내용을 확인해주세요.");
|
||||
// 페이지 상단으로 스크롤
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 에러 초기화
|
||||
setErrors({});
|
||||
setIsSaving(true);
|
||||
try {
|
||||
await onSave(formData);
|
||||
@@ -353,6 +370,44 @@ export function QuoteRegistration({
|
||||
saveDisabled={isSaving || isLoading}
|
||||
maxWidth="2xl"
|
||||
>
|
||||
{/* Validation 에러 표시 */}
|
||||
{Object.keys(errors).length > 0 && (
|
||||
<Alert className="bg-red-50 border-red-200 mb-6">
|
||||
<AlertDescription className="text-red-900">
|
||||
<div className="flex items-start gap-2">
|
||||
<span className="text-lg">⚠️</span>
|
||||
<div className="flex-1">
|
||||
<strong className="block mb-2">
|
||||
입력 내용을 확인해주세요 ({Object.keys(errors).length}개 오류)
|
||||
</strong>
|
||||
<ul className="space-y-1 text-sm">
|
||||
{Object.entries(errors).map(([field, message]) => {
|
||||
// item-0-productCategory 형태의 키에서 필드명 추출
|
||||
const fieldParts = field.split("-");
|
||||
let fieldName = field;
|
||||
if (fieldParts.length === 3) {
|
||||
const itemIndex = parseInt(fieldParts[1]) + 1;
|
||||
const fieldKey = fieldParts[2];
|
||||
fieldName = `견적 ${itemIndex} - ${FIELD_NAME_MAP[fieldKey] || fieldKey}`;
|
||||
} else {
|
||||
fieldName = FIELD_NAME_MAP[field] || field;
|
||||
}
|
||||
return (
|
||||
<li key={field} className="flex items-start gap-1">
|
||||
<span>•</span>
|
||||
<span>
|
||||
<strong>{fieldName}</strong>: {message}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* 1. 기본 정보 */}
|
||||
<FormSection
|
||||
title="기본 정보"
|
||||
|
||||
Reference in New Issue
Block a user