diff options
Diffstat (limited to 'app')
3 files changed, 778 insertions, 0 deletions
diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx new file mode 100644 index 00000000..6b058b37 --- /dev/null +++ b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx @@ -0,0 +1,39 @@ +import { vendorMdgService } from "@/lib/vendors/mdg-service" +import { VendorBasicInfo } from "./vendor-basic-info" + +interface VendorBasicPageProps { + params: { + lng: string + // 협력업체 ID: 여기서는 Oracle의 벤더 코드(VNDRCD)를 사용 + id: string + } +} + +export default async function VendorBasicPage(props: VendorBasicPageProps) { + const resolvedParams = await props.params + const vendorId = resolvedParams.id + + // Oracle에서 벤더 상세 정보 조회 (ID로 조회) + const vendorDetails = await vendorMdgService.getVendorDetailsByVendorId(vendorId) + + if (!vendorDetails) { + return ( + <div className="space-y-6"> + <div className="text-center py-12"> + <h3 className="text-lg font-medium text-gray-900 mb-2"> + 벤더 정보를 찾을 수 없습니다 + </h3> + <p className="text-gray-500"> + 벤더 ID: {vendorId} + </p> + </div> + </div> + ) + } + + return ( + <div className="space-y-6"> + <VendorBasicInfo vendorDetails={vendorDetails} /> + </div> + ) +}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/vendor-basic-info.tsx b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/vendor-basic-info.tsx new file mode 100644 index 00000000..16f75bcb --- /dev/null +++ b/app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/vendor-basic-info.tsx @@ -0,0 +1,735 @@ +"use client" + +import { useState, useTransition, useMemo } from "react" +import { useParams } from "next/navigation" +import { toast } from "sonner" +import { Separator } from "@/components/ui/separator" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Textarea } from "@/components/ui/textarea" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { AddressDisplay } from "@/components/ui/text-utils" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + Phone, + Mail, + Calendar, + CheckCircle, + XCircle, + AlertCircle, + Edit, + Save, + X, + Building2, + Eye +} from "lucide-react" +import { updateMdgVendorBasicInfo } from "@/lib/vendors/mdg-actions" + +// 구매조직별 정보 타입 +interface PurchasingOrgInfo { + PUR_ORG_CD: string + PUR_ORD_CUR: string | null + SPLY_COND: string | null + DL_COND_1: string | null + DL_COND_2: string | null + GR_BSE_INVC_VR: string | null + ORD_CNFM_REQ_ORDR: string | null + CNFM_CTL_KEY: string | null + PUR_HOLD_ORDR: string | null + DEL_ORDR: string | null + AT_PUR_ORD_ORDR: string | null + SALE_CHRGR_NM: string | null + VNDR_TELNO: string | null + PUR_HOLD_DT: string | null + PUR_HOLD_CAUS: string | null +} + +interface VendorDetails { + VNDRCD: string + VNDRNM_1: string | null + VNDRNM_2: string | null + VNDRNM_ABRV_1: string | null + CO_VLM: string | null + BIZR_NO: string | null + CO_REG_NO: string | null + REPR_NM: string | null + REP_TEL_NO: string | null + REPR_RESNO: string | null + REPRESENTATIVE_EMAIL: string | null + BIZTP: string | null + BIZCON: string | null + NTN_CD: string | null + REG_DT: string | null + ADR_1: string | null + ADR_2: string | null + POSTAL_CODE: string | null + ADDR_DETAIL_1: string | null + PREVIOUS_VENDOR_CODE: string | null + PRTNR_GB: string | null + PURCHASING_ORGS: PurchasingOrgInfo[] + DEL_ORDR: string | null + PUR_HOLD_ORDR: string | null +} + +interface VendorBasicInfoProps { + vendorDetails: VendorDetails +} + +export function VendorBasicInfo({ vendorDetails }: VendorBasicInfoProps) { + const params = useParams() + const vendorId = params.id as string + const [isEditing, setIsEditing] = useState(false) + const [editData, setEditData] = useState(vendorDetails) + const [isPending, startTransition] = useTransition() + const [showConfirmDialog, setShowConfirmDialog] = useState(false) + const [selectedPurchasingOrg, setSelectedPurchasingOrg] = useState<string>(() => { + // 구매조직이 1개면 자동 선택, 여러개면 첫 번째 선택, 없으면 'none' + if (vendorDetails.PURCHASING_ORGS.length === 1) { + return vendorDetails.PURCHASING_ORGS[0].PUR_ORG_CD + } else if (vendorDetails.PURCHASING_ORGS.length > 1) { + return vendorDetails.PURCHASING_ORGS[0].PUR_ORG_CD + } + return 'none' + }) + const [showAllOrgs, setShowAllOrgs] = useState(false) + + // 변경사항 감지 + const changes = useMemo(() => { + const changedFields: Array<{ label: string; before: string; after: string }> = [] + + const fieldLabels: Record<string, string> = { + VNDRNM_1: "업체명", + VNDRNM_2: "영문명", + VNDRNM_ABRV_1: "업체약어", + BIZR_NO: "사업자번호", + CO_REG_NO: "법인등록번호", + CO_VLM: "기업규모", + REPR_NM: "대표자명", + REP_TEL_NO: "대표자 전화번호", + REPR_RESNO: "대표자 생년월일", + REPRESENTATIVE_EMAIL: "대표자 이메일", + BIZTP: "사업유형", + BIZCON: "산업유형", + NTN_CD: "국가코드", + ADR_1: "주소", + ADR_2: "영문주소", + POSTAL_CODE: "우편번호", + ADDR_DETAIL_1: "상세주소" + } + + Object.keys(fieldLabels).forEach(field => { + const originalValue = vendorDetails[field as keyof VendorDetails] as string || '' + const editedValue = editData[field as keyof VendorDetails] as string || '' + + if (originalValue !== editedValue) { + changedFields.push({ + label: fieldLabels[field], + before: originalValue || '(없음)', + after: editedValue || '(없음)' + }) + } + }) + + return changedFields + }, [vendorDetails, editData]) + + // 선택된 구매조직 정보 + const currentPurchasingOrg = vendorDetails.PURCHASING_ORGS.find( + org => org.PUR_ORG_CD === selectedPurchasingOrg + ) + + // 상태에 따른 뱃지 스타일 결정 + const getStatusBadge = (status: string | null) => { + if (!status || status === 'N') { + return <Badge variant="default" className="bg-green-100 text-green-800"><CheckCircle className="w-3 h-3 mr-1" />활성</Badge> + } + return <Badge variant="destructive"><XCircle className="w-3 h-3 mr-1" />비활성</Badge> + } + + const handleEditStart = () => { + setIsEditing(true) + } + + const handleEditCancel = () => { + setIsEditing(false) + setEditData(vendorDetails) + } + + const handleEditSave = () => { + if (isPending) return + + // 변경사항이 없으면 바로 편집 모드 종료 + if (changes.length === 0) { + setIsEditing(false) + toast.info("변경된 내용이 없습니다.") + return + } + + // 변경사항이 있으면 확인 Dialog 표시 + setShowConfirmDialog(true) + } + + const handleConfirmSave = () => { + setShowConfirmDialog(false) + + startTransition(async () => { + try { + const result = await updateMdgVendorBasicInfo({ + vendorId, + updateData: { + VNDRNM_1: editData.VNDRNM_1, + VNDRNM_2: editData.VNDRNM_2, + VNDRNM_ABRV_1: editData.VNDRNM_ABRV_1, + BIZR_NO: editData.BIZR_NO, + CO_REG_NO: editData.CO_REG_NO, + CO_VLM: editData.CO_VLM, + REPR_NM: editData.REPR_NM, + REP_TEL_NO: editData.REP_TEL_NO, + REPR_RESNO: editData.REPR_RESNO, + REPRESENTATIVE_EMAIL: editData.REPRESENTATIVE_EMAIL, + BIZTP: editData.BIZTP, + BIZCON: editData.BIZCON, + NTN_CD: editData.NTN_CD, + ADR_1: editData.ADR_1, + ADR_2: editData.ADR_2, + POSTAL_CODE: editData.POSTAL_CODE, + ADDR_DETAIL_1: editData.ADDR_DETAIL_1, + } + }) + + if (result.success) { + toast.success(result.message || "벤더 정보가 성공적으로 업데이트되었습니다.") + setIsEditing(false) + // 필요한 경우 페이지 리로드 또는 데이터 갱신 + window.location.reload() + } else { + toast.error(result.error || "벤더 정보 업데이트에 실패했습니다.") + } + } catch (error) { + console.error('벤더 정보 업데이트 중 오류:', error) + toast.error("벤더 정보 업데이트 중 오류가 발생했습니다.") + } + }) + } + + const handleInputChange = (field: keyof VendorDetails, value: string) => { + setEditData(prev => ({ + ...prev, + [field]: value + })) + } + + const renderField = ( + label: string, + value: string | null, + field?: keyof VendorDetails, + isTextarea = false, + isMono = false + ) => { + if (isEditing && field) { + return ( + <div> + <label className="text-sm font-medium text-muted-foreground">{label}</label> + {isTextarea ? ( + <Textarea + value={editData[field] as string || ''} + onChange={(e) => handleInputChange(field, e.target.value)} + className="mt-1" + /> + ) : ( + <Input + value={editData[field] as string || ''} + onChange={(e) => handleInputChange(field, e.target.value)} + className={`mt-1 ${isMono ? 'font-mono' : ''}`} + /> + )} + </div> + ) + } + + return ( + <div> + <label className="text-sm font-medium text-muted-foreground">{label}</label> + <p className={`text-sm ${isMono ? 'font-mono' : ''} break-words ${isTextarea ? 'whitespace-pre-wrap' : ''}`}> + {value || '-'} + </p> + </div> + ) + } + + // 구매조직별 정보 필드 렌더링 + const renderPurchasingOrgField = ( + label: string, + value: string | null | undefined, + isBadge = false, + badgeType?: 'status' | 'confirm' | 'hold' + ) => { + if (isBadge) { + let badgeContent + switch (badgeType) { + case 'status': + badgeContent = value === 'X' ? ( + <Badge variant="outline" className="text-xs bg-green-50 text-green-700">활성</Badge> + ) : ( + <Badge variant="secondary" className="text-xs">비활성</Badge> + ) + break + case 'confirm': + badgeContent = value === 'X' ? ( + <Badge variant="outline" className="text-xs bg-blue-50 text-blue-700">요청</Badge> + ) : ( + <Badge variant="secondary" className="text-xs">미요청</Badge> + ) + break + case 'hold': + badgeContent = value ? ( + <Badge variant="destructive" className="text-xs"> + <AlertCircle className="w-3 h-3 mr-1" />정지 + </Badge> + ) : ( + <Badge variant="outline" className="text-xs bg-green-50 text-green-700"> + <CheckCircle className="w-3 h-3 mr-1" />정상 + </Badge> + ) + break + default: + badgeContent = <Badge variant="outline">{value || '-'}</Badge> + } + + return ( + <div> + <label className="text-sm font-medium text-muted-foreground">{label}</label> + <p className="text-sm">{badgeContent}</p> + </div> + ) + } + + return ( + <div> + <label className="text-sm font-medium text-muted-foreground">{label}</label> + <p className="text-sm break-words">{value || '-'}</p> + </div> + ) + } + + // 구매조직 정보 카드 컴포넌트 + const PurchasingOrgCard = ({ org }: { org: PurchasingOrgInfo }) => ( + <Card key={org.PUR_ORG_CD} className="border-l-4 border-l-blue-500"> + <CardHeader className="pb-3"> + <CardTitle className="text-lg flex items-center gap-2"> + 구매조직: {org.PUR_ORG_CD} + </CardTitle> + </CardHeader> + <CardContent> + <div className="grid grid-cols-3 gap-4"> + {renderPurchasingOrgField("오더통화", org.PUR_ORD_CUR)} + <div> + <label className="text-sm font-medium text-muted-foreground">내외자구분</label> + <p className="text-sm"> + {editData.PRTNR_GB ? ( + <Badge variant="outline" className="text-xs"> + {editData.PRTNR_GB === '1' ? '사내' : editData.PRTNR_GB === '2' ? '사외' : editData.PRTNR_GB} + </Badge> + ) : '-'} + </p> + </div> + {renderPurchasingOrgField("인도조건", org.DL_COND_1)} + {renderPurchasingOrgField("GR송장검증", org.GR_BSE_INVC_VR, true, 'status')} + {renderPurchasingOrgField("P/O 확인요청", org.ORD_CNFM_REQ_ORDR, true, 'confirm')} + {renderPurchasingOrgField("확정제어", org.CNFM_CTL_KEY)} + {renderPurchasingOrgField("지급조건", org.SPLY_COND)} + {renderPurchasingOrgField("거래정지", org.PUR_HOLD_ORDR, true, 'hold')} + {renderPurchasingOrgField("삭제상태", org.DEL_ORDR)} + {renderPurchasingOrgField("영업담당자", org.SALE_CHRGR_NM)} + {renderPurchasingOrgField("전화번호", org.VNDR_TELNO)} + {renderPurchasingOrgField("보류일자", org.PUR_HOLD_DT)} + </div> + {org.PUR_HOLD_CAUS && ( + <div className="mt-4"> + {renderPurchasingOrgField("보류사유", org.PUR_HOLD_CAUS)} + </div> + )} + </CardContent> + </Card> + ) + + return ( + <> + {/* 헤더 */} + <div className="flex items-center justify-between"> + <div className="min-w-0 flex-1"> + <h3 className="text-2xl font-bold tracking-tight break-words"> + {editData.VNDRNM_1 || '업체명 없음'} + </h3> + <p className="text-muted-foreground"> + 벤더 코드: {editData.VNDRCD} + </p> + </div> + <div className="flex items-center space-x-4"> + {/* 상태 배지 */} + <div className="flex items-center space-x-2"> + {getStatusBadge(editData.DEL_ORDR)} + </div> + + {/* 액션 버튼들 */} + <div className="flex items-center space-x-2"> + {isEditing ? ( + <> + <Button onClick={handleEditSave} size="sm" disabled={showConfirmDialog}> + <Save className="w-4 h-4 mr-2" /> + 저장 + </Button> + <Button onClick={handleEditCancel} variant="outline" size="sm" disabled={showConfirmDialog || isPending}> + <X className="w-4 h-4 mr-2" /> + 취소 + </Button> + </> + ) : ( + <> + <Button onClick={handleEditStart} variant="outline" size="sm"> + <Edit className="w-4 h-4 mr-2" /> + 수정 + </Button> + </> + )} + </div> + </div> + </div> + + <Separator /> + + <div className="grid gap-6 md:grid-cols-2"> + {/* 기본 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2"> + 기본 정보 + </CardTitle> + <CardDescription> + 업체의 기본적인 정보입니다. + </CardDescription> + </CardHeader> + <CardContent className="space-y-4"> + <div className="grid grid-cols-1 gap-4"> + {renderField("업체명", editData.VNDRNM_1, "VNDRNM_1")} + {renderField("영문명", editData.VNDRNM_2, "VNDRNM_2")} + <div className="grid grid-cols-2 gap-4"> + {renderField("업체약어", editData.VNDRNM_ABRV_1, "VNDRNM_ABRV_1")} + {renderField("기업규모", editData.CO_VLM, "CO_VLM")} + </div> + <div className="grid grid-cols-2 gap-4"> + {renderField("사업자번호", editData.BIZR_NO, "BIZR_NO", false, true)} + {renderField("법인등록번호", editData.CO_REG_NO, "CO_REG_NO", false, true)} + </div> + </div> + </CardContent> + </Card> + + {/* 대표자 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2"> + 대표자 정보 + </CardTitle> + </CardHeader> + <CardContent className="space-y-4"> + <div className="grid grid-cols-1 gap-4"> + {renderField("대표자명", editData.REPR_NM, "REPR_NM")} + + <div> + <label className="text-sm font-medium text-muted-foreground">대표자 전화번호</label> + {isEditing ? ( + <Input + value={editData.REP_TEL_NO || ''} + onChange={(e) => handleInputChange('REP_TEL_NO', e.target.value)} + className="mt-1 font-mono" + /> + ) : ( + <p className="text-sm flex items-center gap-1"> + {editData.REP_TEL_NO ? ( + <> + <Phone className="w-3 h-3" /> + <span className="font-mono">{editData.REP_TEL_NO}</span> + </> + ) : '-'} + </p> + )} + </div> + + <div> + <label className="text-sm font-medium text-muted-foreground">대표자 생년월일</label> + {isEditing ? ( + <Input + value={editData.REPR_RESNO || ''} + onChange={(e) => handleInputChange('REPR_RESNO', e.target.value)} + className="mt-1 font-mono" + /> + ) : ( + <p className="text-sm flex items-center gap-1"> + {editData.REPR_RESNO ? ( + <> + <Calendar className="w-3 h-3" /> + <span className="font-mono">{editData.REPR_RESNO}</span> + </> + ) : '-'} + </p> + )} + </div> + + <div> + <label className="text-sm font-medium text-muted-foreground">대표자 이메일</label> + {isEditing ? ( + <Input + type="email" + value={editData.REPRESENTATIVE_EMAIL || ''} + onChange={(e) => handleInputChange('REPRESENTATIVE_EMAIL', e.target.value)} + className="mt-1" + /> + ) : ( + <p className="text-sm flex items-center gap-1"> + {editData.REPRESENTATIVE_EMAIL ? ( + <> + <Mail className="w-3 h-3 flex-shrink-0" /> + <span className="break-all">{editData.REPRESENTATIVE_EMAIL}</span> + </> + ) : '-'} + </p> + )} + </div> + </div> + </CardContent> + </Card> + + {/* 사업 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2"> + 사업 정보 + </CardTitle> + </CardHeader> + <CardContent className="space-y-4"> + <div className="space-y-4"> + {renderField("사업유형", editData.BIZTP, "BIZTP", true)} + {renderField("산업유형", editData.BIZCON, "BIZCON", true)} + <div className="grid grid-cols-2 gap-4"> + {renderField("국가코드", editData.NTN_CD, "NTN_CD")} + {renderField("등록일자", editData.REG_DT, "REG_DT", false, true)} + </div> + </div> + </CardContent> + </Card> + + {/* 주소 정보 */} + <Card> + <CardHeader> + <CardTitle className="flex items-center gap-2"> + 주소 정보 + </CardTitle> + </CardHeader> + <CardContent className="space-y-4"> + <div className="space-y-4"> + {isEditing ? ( + <div className="space-y-4"> + {renderField("주소", editData.ADR_1, "ADR_1")} + {renderField("영문주소", editData.ADR_2, "ADR_2")} + {renderField("우편번호", editData.POSTAL_CODE, "POSTAL_CODE")} + {renderField("상세주소", editData.ADDR_DETAIL_1, "ADDR_DETAIL_1")} + </div> + ) : ( + <div> + <label className="text-sm font-medium text-muted-foreground mb-2 block">주소</label> + <AddressDisplay + address={editData.ADR_1} + addressEng={editData.ADR_2} + postalCode={editData.POSTAL_CODE} + addressDetail={editData.ADDR_DETAIL_1} + /> + </div> + )} + </div> + </CardContent> + </Card> + + {/* 구매조직 정보 */} + <Card className="md:col-span-2"> + <CardHeader> + <CardTitle className="flex items-center gap-2"> + 구매조직 정보 + {vendorDetails.PURCHASING_ORGS.length > 0 && ( + <Badge variant="secondary" className="ml-2"> + {vendorDetails.PURCHASING_ORGS.length}개 조직 + </Badge> + )} + </CardTitle> + <CardDescription> + 구매조직에 따른 상세 정보입니다. + </CardDescription> + </CardHeader> + <CardContent className="space-y-4"> + {vendorDetails.PURCHASING_ORGS.length === 0 ? ( + <div className="text-center py-8 text-muted-foreground"> + <Building2 className="w-12 h-12 mx-auto mb-4 opacity-50" /> + <p>구매조직 정보가 없습니다.</p> + </div> + ) : vendorDetails.PURCHASING_ORGS.length === 1 ? ( + // 구매조직이 1개인 경우 + <div className="grid grid-cols-3 gap-4"> + {renderPurchasingOrgField("구매조직", currentPurchasingOrg?.PUR_ORG_CD)} + {renderPurchasingOrgField("오더통화", currentPurchasingOrg?.PUR_ORD_CUR)} + <div> + <label className="text-sm font-medium text-muted-foreground">내외자구분</label> + <p className="text-sm"> + {editData.PRTNR_GB ? ( + <Badge variant="outline" className="text-xs"> + {editData.PRTNR_GB === '1' ? '사내' : editData.PRTNR_GB === '2' ? '사외' : editData.PRTNR_GB} + </Badge> + ) : '-'} + </p> + </div> + {renderPurchasingOrgField("인도조건", currentPurchasingOrg?.DL_COND_1)} + {renderPurchasingOrgField("GR송장검증", currentPurchasingOrg?.GR_BSE_INVC_VR, true, 'status')} + {renderPurchasingOrgField("P/O 확인요청", currentPurchasingOrg?.ORD_CNFM_REQ_ORDR, true, 'confirm')} + {renderPurchasingOrgField("확정제어", currentPurchasingOrg?.CNFM_CTL_KEY)} + {renderPurchasingOrgField("지급조건", currentPurchasingOrg?.SPLY_COND)} + {renderPurchasingOrgField("거래정지", currentPurchasingOrg?.PUR_HOLD_ORDR, true, 'hold')} + {renderPurchasingOrgField("이전업체코드", editData.PREVIOUS_VENDOR_CODE)} + </div> + ) : ( + // 구매조직이 여러개인 경우 + <div className="space-y-4"> + <div className="flex items-center gap-4"> + <div className="flex-1"> + <label className="text-sm font-medium text-muted-foreground">구매조직 선택</label> + <Select value={selectedPurchasingOrg} onValueChange={setSelectedPurchasingOrg}> + <SelectTrigger className="mt-1"> + <SelectValue placeholder="구매조직을 선택하세요" /> + </SelectTrigger> + <SelectContent> + {vendorDetails.PURCHASING_ORGS.map((org) => ( + <SelectItem key={org.PUR_ORG_CD} value={org.PUR_ORG_CD}> + {org.PUR_ORG_CD} - {org.SALE_CHRGR_NM || '담당자 미지정'} + </SelectItem> + ))} + </SelectContent> + </Select> + </div> + <div className="pt-6"> + <Button + variant={showAllOrgs ? "default" : "outline"} + onClick={() => setShowAllOrgs(!showAllOrgs)} + size="sm" + > + <Eye className="w-4 h-4 mr-2" /> + {showAllOrgs ? '선택 보기' : '전체 보기'} + </Button> + </div> + </div> + + {showAllOrgs ? ( + // 전체 구매조직 정보 표시 + <div className="space-y-4"> + {vendorDetails.PURCHASING_ORGS.map((org) => ( + <PurchasingOrgCard key={org.PUR_ORG_CD} org={org} /> + ))} + </div> + ) : ( + // 선택된 구매조직 정보만 표시 + currentPurchasingOrg && ( + <div className="grid grid-cols-3 gap-4"> + {renderPurchasingOrgField("구매조직", currentPurchasingOrg.PUR_ORG_CD)} + {renderPurchasingOrgField("오더통화", currentPurchasingOrg.PUR_ORD_CUR)} + <div> + <label className="text-sm font-medium text-muted-foreground">내외자구분</label> + <p className="text-sm"> + {editData.PRTNR_GB ? ( + <Badge variant="outline" className="text-xs"> + {editData.PRTNR_GB === '1' ? '사내' : editData.PRTNR_GB === '2' ? '사외' : editData.PRTNR_GB} + </Badge> + ) : '-'} + </p> + </div> + {renderPurchasingOrgField("인도조건", currentPurchasingOrg.DL_COND_1)} + {renderPurchasingOrgField("GR송장검증", currentPurchasingOrg.GR_BSE_INVC_VR, true, 'status')} + {renderPurchasingOrgField("P/O 확인요청", currentPurchasingOrg.ORD_CNFM_REQ_ORDR, true, 'confirm')} + {renderPurchasingOrgField("확정제어", currentPurchasingOrg.CNFM_CTL_KEY)} + {renderPurchasingOrgField("지급조건", currentPurchasingOrg.SPLY_COND)} + {renderPurchasingOrgField("거래정지", currentPurchasingOrg.PUR_HOLD_ORDR, true, 'hold')} + {renderPurchasingOrgField("이전업체코드", editData.PREVIOUS_VENDOR_CODE)} + </div> + ) + )} + </div> + )} + </CardContent> + </Card> + </div> + + {/* 변경사항 확인 Dialog */} + <Dialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}> + <DialogContent className="sm:max-w-[600px]"> + <DialogHeader> + <DialogTitle className="flex items-center gap-2"> + <AlertCircle className="w-5 h-5 text-amber-500" /> + 변경사항 확인 + </DialogTitle> + <DialogDescription> + 다음 정보가 변경됩니다. 저장하시겠습니까? + </DialogDescription> + </DialogHeader> + + <div className="max-h-[400px] overflow-y-auto"> + <div className="space-y-4"> + {changes.map((change, index) => ( + <div key={index} className="border rounded-lg p-4 space-y-2"> + <div className="font-medium text-sm text-muted-foreground"> + {change.label} + </div> + <div className="grid grid-cols-1 gap-2"> + <div className="flex items-start gap-2"> + <span className="text-xs bg-red-100 text-red-700 px-2 py-1 rounded font-mono">이전</span> + <span className="text-sm break-words flex-1 line-through text-muted-foreground"> + {change.before} + </span> + </div> + <div className="flex items-start gap-2"> + <span className="text-xs bg-green-100 text-green-700 px-2 py-1 rounded font-mono">변경</span> + <span className="text-sm break-words flex-1 font-medium"> + {change.after} + </span> + </div> + </div> + </div> + ))} + </div> + </div> + + <DialogFooter> + <Button + variant="outline" + onClick={() => setShowConfirmDialog(false)} + disabled={isPending} + > + 취소 + </Button> + <Button + onClick={handleConfirmSave} + disabled={isPending} + className="bg-blue-600 hover:bg-blue-700" + > + {isPending ? "저장 중..." : `${changes.length}개 항목 저장`} + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + </> + ) +}
\ No newline at end of file diff --git a/app/[lng]/evcp/(evcp)/vendors/[id]/info/layout.tsx b/app/[lng]/evcp/(evcp)/vendors/[id]/info/layout.tsx index 7e2cd4f6..7826a7c0 100644 --- a/app/[lng]/evcp/(evcp)/vendors/[id]/info/layout.tsx +++ b/app/[lng]/evcp/(evcp)/vendors/[id]/info/layout.tsx @@ -35,6 +35,10 @@ export default async function SettingsLayout({ href: `/${lng}/evcp/vendors/${id}/info`, }, { + title: "기본정보", + href: `/${lng}/evcp/vendors/${id}/info/basic`, + }, + { title: "공급품목(패키지)", href: `/${lng}/evcp/vendors/${id}/info/items`, }, |
