summaryrefslogtreecommitdiff
path: root/lib/avl/table/avl-vendor-add-and-modify-dialog.tsx
diff options
context:
space:
mode:
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.tsx386
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>