diff options
| author | joonhoekim <26rote@gmail.com> | 2025-09-22 18:59:13 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-09-22 18:59:13 +0900 |
| commit | ba35e67845f935c8ce0151c9ef1fefa0b0510faf (patch) | |
| tree | d05eb27fab2acc54a839b2590c89e860d58fb747 /lib/avl/table/avl-vendor-add-and-modify-dialog.tsx | |
| parent | e4bd037d158513e45373ad9e1ef13f71af12162a (diff) | |
(김준회) AVL 피드백 반영 (이진용 프로 건)
Diffstat (limited to 'lib/avl/table/avl-vendor-add-and-modify-dialog.tsx')
| -rw-r--r-- | lib/avl/table/avl-vendor-add-and-modify-dialog.tsx | 386 |
1 files changed, 232 insertions, 154 deletions
diff --git a/lib/avl/table/avl-vendor-add-and-modify-dialog.tsx b/lib/avl/table/avl-vendor-add-and-modify-dialog.tsx index 174982e4..4f0eb404 100644 --- a/lib/avl/table/avl-vendor-add-and-modify-dialog.tsx +++ b/lib/avl/table/avl-vendor-add-and-modify-dialog.tsx @@ -8,7 +8,6 @@ import { DialogFooter, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" @@ -24,6 +23,14 @@ import { } from "@/components/ui/select" import { toast } from "sonner" import type { AvlVendorInfoInput, AvlDetailItem } from "../types" +import { EngineeringDisciplineSelector, type DisciplineCode } from "@/components/common/discipline" +import { MaterialGroupSelectorDialogSingle } from "@/components/common/material/material-group-selector-dialog-single" +import type { MaterialSearchItem } from "@/lib/material/material-group-service" +import { VendorSelectorDialogSingle } from "@/components/common/vendor" +import type { VendorSearchItem } from "@/components/common/vendor" +import { PlaceOfShippingSelector } from "@/components/common/selectors/place-of-shipping" +import { VendorTierSelector } from "@/components/common/selectors/vendor-tier" +import { DatePicker } from "@/components/ui/date-picker" interface AvlVendorAddAndModifyDialogProps { open: boolean @@ -58,6 +65,16 @@ export function AvlVendorAddAndModifyDialog({ initialHtDivision, initialProjectCode }: AvlVendorAddAndModifyDialogProps) { + // 설계공종 선택 상태 + const [selectedDiscipline, setSelectedDiscipline] = React.useState<DisciplineCode | undefined>(undefined) + // 자재그룹 선택 상태 + const [selectedMaterialGroup, setSelectedMaterialGroup] = React.useState<MaterialSearchItem | null>(null) + // 벤더 선택 상태 + const [selectedVendor, setSelectedVendor] = React.useState<VendorSearchItem | null>(null) + // 날짜 상태 (Date 객체로 관리) + const [quoteReceivedDate, setQuoteReceivedDate] = React.useState<Date | undefined>(undefined) + const [recentQuoteDate, setRecentQuoteDate] = React.useState<Date | undefined>(undefined) + const [recentOrderDate, setRecentOrderDate] = React.useState<Date | undefined>(undefined) const [formData, setFormData] = React.useState<Omit<AvlVendorInfoInput, 'avlListId'>>({ // 공통 기본 설정 isTemplate: isTemplate, @@ -137,9 +154,50 @@ export function AvlVendorAddAndModifyDialog({ remarks: "" }) - // 수정 모드일 때 폼 데이터 초기화 + // 수정 모드일 때 설계공종 선택 상태 및 폼 데이터 초기화 React.useEffect(() => { if (editingItem) { + // 설계공종 선택 상태 초기화 + if (editingItem.disciplineCode && editingItem.disciplineName) { + setSelectedDiscipline({ + CD: editingItem.disciplineCode, + USR_DF_CHAR_18: editingItem.disciplineName + }) + } else { + setSelectedDiscipline(undefined) + } + + // 자재그룹 선택 상태 초기화 + if (editingItem.materialGroupCode && editingItem.materialGroupName) { + setSelectedMaterialGroup({ + materialGroupCode: editingItem.materialGroupCode, + materialGroupDescription: editingItem.materialGroupName, + displayText: `${editingItem.materialGroupCode} - ${editingItem.materialGroupName}` + }) + } else { + setSelectedMaterialGroup(null) + } + + // 벤더 선택 상태 초기화 (기존 데이터가 있으면 가상의 벤더 객체 생성) + if (editingItem.vendorCode || editingItem.vendorName) { + setSelectedVendor({ + id: -1, // 임시 ID (실제 벤더 ID는 알 수 없음) + vendorName: editingItem.vendorName || "", + vendorCode: editingItem.vendorCode || null, + status: "UNKNOWN", // 상태 정보 없음 + displayText: editingItem.vendorCode + ? `${editingItem.vendorName} (${editingItem.vendorCode})` + : editingItem.vendorName || "" + }) + } else { + setSelectedVendor(null) + } + + // 날짜 상태 초기화 + setQuoteReceivedDate(editingItem.quoteReceivedDate ? new Date(editingItem.quoteReceivedDate) : undefined) + setRecentQuoteDate(editingItem.recentQuoteDate ? new Date(editingItem.recentQuoteDate) : undefined) + setRecentOrderDate(editingItem.recentOrderDate ? new Date(editingItem.recentOrderDate) : undefined) + setFormData({ // 공통 기본 설정 isTemplate: editingItem.isTemplate ?? isTemplate, @@ -224,6 +282,17 @@ export function AvlVendorAddAndModifyDialog({ // 다이얼로그가 열릴 때 초기값 재설정 (수정 모드가 아닐 때만) React.useEffect(() => { if (open && !editingItem) { + // 설계공종 선택 상태 초기화 + setSelectedDiscipline(undefined) + // 자재그룹 선택 상태 초기화 + setSelectedMaterialGroup(null) + // 벤더 선택 상태 초기화 + setSelectedVendor(null) + // 날짜 상태 초기화 + setQuoteReceivedDate(undefined) + setRecentQuoteDate(undefined) + setRecentOrderDate(undefined) + setFormData(prev => ({ ...prev, isTemplate: isTemplate, @@ -236,6 +305,40 @@ export function AvlVendorAddAndModifyDialog({ } }, [open, editingItem, isTemplate, initialProjectCode, initialConstructionSector, initialShipType, initialAvlKind, initialHtDivision]) + // 설계공종 선택 핸들러 + const handleDisciplineSelect = React.useCallback((discipline: DisciplineCode) => { + setSelectedDiscipline(discipline) + setFormData(prev => ({ + ...prev, + disciplineCode: discipline.CD, + disciplineName: discipline.USR_DF_CHAR_18 + })) + }, []) + + // 자재그룹 선택 핸들러 + const handleMaterialGroupSelect = React.useCallback((materialGroup: MaterialSearchItem | null) => { + setSelectedMaterialGroup(materialGroup) + setFormData(prev => ({ + ...prev, + materialGroupCode: materialGroup?.materialGroupCode || "", + materialGroupName: materialGroup?.materialGroupDescription || "" + })) + }, []) + + // 벤더 선택 핸들러 (선택기에서 벤더를 선택했을 때 Input 필드에 자동 입력) + const handleVendorSelect = React.useCallback((vendor: VendorSearchItem | null) => { + setSelectedVendor(vendor) + if (vendor) { + setFormData(prev => ({ + ...prev, + vendorCode: vendor.vendorCode || "", + vendorName: vendor.vendorName || "", + // AVL 등재업체명도 기본적으로 벤더명으로 설정 (사용자가 수정 가능) + avlVendorName: vendor.vendorName || "" + })) + } + }, []) + const handleSubmit = async () => { // 공통 필수 필드 검증 if (!formData.disciplineName || !formData.materialNameCustomerSide) { @@ -259,15 +362,34 @@ export function AvlVendorAddAndModifyDialog({ } try { + // 날짜 필드들을 문자열로 변환 + const formatDate = (date: Date | undefined): string => { + if (!date) return "" + return date.toISOString().split('T')[0] // YYYY-MM-DD 형식 + } + + const submissionData = { + ...formData, + quoteReceivedDate: formatDate(quoteReceivedDate), + recentQuoteDate: formatDate(recentQuoteDate), + recentOrderDate: formatDate(recentOrderDate), + } + if (editingItem && onUpdateItem) { // 수정 모드 - await onUpdateItem(editingItem.id, formData) + await onUpdateItem(editingItem.id, submissionData) } else { // 추가 모드 - await onAddItem(formData) + await onAddItem(submissionData) } - // 폼 초기화 + // 폼 및 선택 상태 초기화 + setSelectedDiscipline(undefined) + setSelectedMaterialGroup(null) + setSelectedVendor(null) + setQuoteReceivedDate(undefined) + setRecentQuoteDate(undefined) + setRecentOrderDate(undefined) setFormData({ isTemplate: isTemplate, projectCode: initialProjectCode || "", @@ -313,12 +435,18 @@ export function AvlVendorAddAndModifyDialog({ } as Omit<AvlVendorInfoInput, 'avlListId'>) onOpenChange(false) - } catch (error) { + } catch { // 에러 처리는 호출하는 쪽에서 담당 } } const handleCancel = () => { + setSelectedDiscipline(undefined) + setSelectedMaterialGroup(null) + setSelectedVendor(null) + setQuoteReceivedDate(undefined) + setRecentQuoteDate(undefined) + setRecentOrderDate(undefined) setFormData({ isTemplate: isTemplate, projectCode: initialProjectCode || "", @@ -365,31 +493,6 @@ export function AvlVendorAddAndModifyDialog({ onOpenChange(false) } - // 선종 옵션들 (공사부문에 따라 다름) - const getShipTypeOptions = (constructionSector: string) => { - if (constructionSector === "조선") { - return [ - { value: "A-max", label: "A-max" }, - { value: "S-max", label: "S-max" }, - { value: "VLCC", label: "VLCC" }, - { value: "LNGC", label: "LNGC" }, - { value: "CONT", label: "CONT" }, - ] - } else if (constructionSector === "해양") { - return [ - { value: "FPSO", label: "FPSO" }, - { value: "FLNG", label: "FLNG" }, - { value: "FPU", label: "FPU" }, - { value: "Platform", label: "Platform" }, - { value: "WTIV", label: "WTIV" }, - { value: "GOM", label: "GOM" }, - ] - } else { - return [] - } - } - - const shipTypeOptions = getShipTypeOptions(formData.constructionSector) return ( <Dialog open={open} onOpenChange={onOpenChange}> @@ -419,6 +522,8 @@ export function AvlVendorAddAndModifyDialog({ value={formData.projectCode} onChange={(e) => setFormData(prev => ({ ...prev, projectCode: e.target.value }))} placeholder="프로젝트 코드를 입력하세요" + readOnly + className="bg-muted" /> </div> </div> @@ -430,82 +535,39 @@ export function AvlVendorAddAndModifyDialog({ <div className="grid grid-cols-2 gap-4"> <div className="space-y-2"> <Label htmlFor="constructionSector">공사부문 *</Label> - <Select + <Input value={formData.constructionSector} - onValueChange={(value) => { - setFormData(prev => ({ - ...prev, - constructionSector: value, - shipType: "" // 공사부문 변경 시 선종 초기화 - })) - }} - > - <SelectTrigger> - <SelectValue placeholder="공사부문을 선택하세요" /> - </SelectTrigger> - <SelectContent> - <SelectItem value="조선">조선</SelectItem> - <SelectItem value="해양">해양</SelectItem> - </SelectContent> - </Select> + readOnly + className="bg-muted" + placeholder="공사부문이 설정되지 않았습니다" + /> </div> <div className="space-y-2"> <Label htmlFor="shipType">선종 *</Label> - <Select + <Input value={formData.shipType} - onValueChange={(value) => - setFormData(prev => ({ ...prev, shipType: value })) - } - disabled={!formData.constructionSector} - > - <SelectTrigger> - <SelectValue placeholder="선종을 선택하세요" /> - </SelectTrigger> - <SelectContent> - {shipTypeOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> + readOnly + className="bg-muted" + placeholder="선종이 설정되지 않았습니다" + /> </div> <div className="space-y-2"> <Label htmlFor="avlKind">AVL종류 *</Label> - <Select + <Input value={formData.avlKind} - onValueChange={(value) => - setFormData(prev => ({ ...prev, avlKind: value })) - } - > - <SelectTrigger> - <SelectValue placeholder="AVL종류를 선택하세요" /> - </SelectTrigger> - <SelectContent> - <SelectItem value="Nearshore">Nearshore</SelectItem> - <SelectItem value="Offshore">Offshore</SelectItem> - <SelectItem value="IOC">IOC</SelectItem> - <SelectItem value="NOC">NOC</SelectItem> - </SelectContent> - </Select> + readOnly + className="bg-muted" + placeholder="AVL종류가 설정되지 않았습니다" + /> </div> <div className="space-y-2"> <Label htmlFor="htDivision">H/T 구분 *</Label> - <Select + <Input value={formData.htDivision} - onValueChange={(value) => - setFormData(prev => ({ ...prev, htDivision: value })) - } - > - <SelectTrigger> - <SelectValue placeholder="H/T 구분을 선택하세요" /> - </SelectTrigger> - <SelectContent> - <SelectItem value="공통">공통</SelectItem> - <SelectItem value="H">Hull (H)</SelectItem> - <SelectItem value="T">Topside (T)</SelectItem> - </SelectContent> - </Select> + readOnly + className="bg-muted" + placeholder="H/T 구분이 설정되지 않았습니다" + /> </div> </div> </div> @@ -533,23 +595,19 @@ export function AvlVendorAddAndModifyDialog({ </SelectContent> </Select> </div> - <div className="space-y-2"> - <Label htmlFor="disciplineCode">설계공종코드</Label> - <Input - id="disciplineCode" - value={formData.disciplineCode} - onChange={(e) => setFormData(prev => ({ ...prev, disciplineCode: e.target.value }))} - placeholder="설계공종코드를 입력하세요" - /> - </div> <div className="space-y-2 col-span-2"> - <Label htmlFor="disciplineName">설계공종명 *</Label> - <Input - id="disciplineName" - value={formData.disciplineName} - onChange={(e) => setFormData(prev => ({ ...prev, disciplineName: e.target.value }))} - placeholder="설계공종명을 입력하세요" - /> + <Label htmlFor="discipline">설계공종 *</Label> + <EngineeringDisciplineSelector + selectedDiscipline={selectedDiscipline} + onDisciplineSelect={handleDisciplineSelect} + placeholder="설계공종을 선택하세요" + className="h-9" + /> + <div className="text-xs text-muted-foreground"> + {selectedDiscipline && ( + <span>선택됨: [{selectedDiscipline.CD}] {selectedDiscipline.USR_DF_CHAR_18}</span> + )} + </div> </div> <div className="space-y-2 col-span-2"> <Label htmlFor="materialNameCustomerSide">고객사 AVL 자재명 *</Label> @@ -591,31 +649,51 @@ export function AvlVendorAddAndModifyDialog({ {/* 자재그룹 정보 */} <div className="space-y-4"> <h4 className="text-sm font-semibold text-muted-foreground border-b pb-2">자재그룹 정보</h4> - <div className="grid grid-cols-2 gap-4"> - <div className="space-y-2"> - <Label htmlFor="materialGroupCode">자재그룹 코드</Label> - <Input - id="materialGroupCode" - value={formData.materialGroupCode} - onChange={(e) => setFormData(prev => ({ ...prev, materialGroupCode: e.target.value }))} - placeholder="자재그룹 코드를 입력하세요" - /> - </div> + <div className="space-y-4"> <div className="space-y-2"> - <Label htmlFor="materialGroupName">자재그룹 명</Label> - <Input - id="materialGroupName" - value={formData.materialGroupName} - onChange={(e) => setFormData(prev => ({ ...prev, materialGroupName: e.target.value }))} - placeholder="자재그룹 명을 입력하세요" - /> + {/* <Label htmlFor="materialGroup">자재그룹 선택</Label> */} + <MaterialGroupSelectorDialogSingle + triggerLabel="자재그룹 선택" + selectedMaterial={selectedMaterialGroup} + onMaterialSelect={handleMaterialGroupSelect} + placeholder="자재그룹을 검색하세요..." + title="자재그룹 선택" + description="AVL에 등록할 자재그룹을 선택해주세요." + triggerVariant="outline" + /> + <div className="text-xs text-muted-foreground"> + {selectedMaterialGroup && ( + <> + <span>자재그룹코드: {selectedMaterialGroup.materialGroupCode}</span> + <br /> + <span>자재그룹명: {selectedMaterialGroup.materialGroupDescription}</span> + </> + )} + </div> </div> </div> </div> {/* 협력업체 정보 */} <div className="space-y-4"> - <h4 className="text-sm font-semibold text-muted-foreground border-b pb-2">협력업체 정보</h4> + <div className="flex items-center justify-between border-b pb-2"> + <h4 className="text-sm font-semibold text-muted-foreground">협력업체 정보</h4> + <VendorSelectorDialogSingle + triggerLabel="협력업체 로드" + selectedVendor={selectedVendor} + onVendorSelect={handleVendorSelect} + title="협력업체 선택" + description="등록된 협력업체 목록에서 선택하여 정보를 자동으로 입력할 수 있습니다." + triggerVariant="outline" + statusFilter="ACTIVE" + /> + </div> + {selectedVendor && ( + <div className="text-xs text-muted-foreground bg-blue-50 p-2 rounded"> + <span>선택된 협력업체: [{selectedVendor.vendorCode || '-'}] {selectedVendor.vendorName}</span> + <span className="ml-2 text-blue-600">({selectedVendor.status})</span> + </div> + )} <div className="grid grid-cols-2 gap-4"> <div className="space-y-2"> <Label htmlFor="vendorCode">협력업체 코드</Label> @@ -646,11 +724,11 @@ export function AvlVendorAddAndModifyDialog({ </div> <div className="space-y-2"> <Label htmlFor="tier">등급 (Tier)</Label> - <Input - id="tier" + <VendorTierSelector value={formData.tier} - onChange={(e) => setFormData(prev => ({ ...prev, tier: e.target.value }))} - placeholder="등급을 입력하세요" + onValueChange={(value) => setFormData(prev => ({ ...prev, tier: value }))} + placeholder="등급을 선택하세요" + className="h-9" /> </div> </div> @@ -698,11 +776,11 @@ export function AvlVendorAddAndModifyDialog({ </div> <div className="space-y-2"> <Label htmlFor="manufacturingLocation">제작/선적지 (국가)</Label> - <Input - id="manufacturingLocation" + <PlaceOfShippingSelector value={formData.manufacturingLocation} - onChange={(e) => setFormData(prev => ({ ...prev, manufacturingLocation: e.target.value }))} - placeholder="제작/선적지를 입력하세요" + onValueChange={(value) => setFormData(prev => ({ ...prev, manufacturingLocation: value }))} + placeholder="제작/선적지를 선택하세요" + className="h-9" /> </div> </div> @@ -862,12 +940,12 @@ export function AvlVendorAddAndModifyDialog({ /> </div> <div className="space-y-2"> - <Label htmlFor="quoteReceivedDate">견적접수일 (YYYY-MM-DD)</Label> - <Input - id="quoteReceivedDate" - value={formData.quoteReceivedDate} - onChange={(e) => setFormData(prev => ({ ...prev, quoteReceivedDate: e.target.value }))} - placeholder="YYYY-MM-DD" + <Label htmlFor="quoteReceivedDate">견적접수일</Label> + <DatePicker + date={quoteReceivedDate} + onSelect={setQuoteReceivedDate} + placeholder="견적접수일 선택" + className="h-9" /> </div> </div> @@ -887,12 +965,12 @@ export function AvlVendorAddAndModifyDialog({ /> </div> <div className="space-y-2"> - <Label htmlFor="recentQuoteDate">최근견적일 (YYYY-MM-DD)</Label> - <Input - id="recentQuoteDate" - value={formData.recentQuoteDate} - onChange={(e) => setFormData(prev => ({ ...prev, recentQuoteDate: e.target.value }))} - placeholder="YYYY-MM-DD" + <Label htmlFor="recentQuoteDate">최근견적일</Label> + <DatePicker + date={recentQuoteDate} + onSelect={setRecentQuoteDate} + placeholder="최근견적일 선택" + className="h-9" /> </div> <div className="space-y-2"> @@ -905,12 +983,12 @@ export function AvlVendorAddAndModifyDialog({ /> </div> <div className="space-y-2"> - <Label htmlFor="recentOrderDate">최근발주일 (YYYY-MM-DD)</Label> - <Input - id="recentOrderDate" - value={formData.recentOrderDate} - onChange={(e) => setFormData(prev => ({ ...prev, recentOrderDate: e.target.value }))} - placeholder="YYYY-MM-DD" + <Label htmlFor="recentOrderDate">최근발주일</Label> + <DatePicker + date={recentOrderDate} + onSelect={setRecentOrderDate} + placeholder="최근발주일 선택" + className="h-9" /> </div> </div> |
