summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/page.tsx39
-rw-r--r--app/[lng]/evcp/(evcp)/vendors/[id]/info/basic/vendor-basic-info.tsx735
-rw-r--r--app/[lng]/evcp/(evcp)/vendors/[id]/info/layout.tsx4
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`,
},