1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
"use client"
import * as React from "react"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { ScrollArea } from "@/components/ui/scroll-area"
import { FileTextIcon, MessageSquareIcon } from "lucide-react"
import { VendorPO } from "./types"
interface VendorPONoteDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
po: VendorPO | null
}
interface PONoteItem {
itemNo: string
description: string
remark: string
}
export function VendorPONoteDialog({
open,
onOpenChange,
po,
}: VendorPONoteDialogProps) {
// 계약서 내용 및 노트 데이터를 추출하는 함수
const extractContent = React.useCallback(() => {
if (!po) return { contractContent: null, remarks: null, itemNotes: [] }
const contractContent = po.contractContent || null // contracts.contractContent (ZMM_NOTE에서 추출)
const remarks = po.remarks || null // contracts.remarks (추가 비고)
const itemNotes: PONoteItem[] = []
// items 배열에서 remark이 있는 항목들 추출
if (po.items && po.items.length > 0) {
po.items.forEach((item, index) => {
if (item.remark && item.remark.trim()) {
itemNotes.push({
itemNo: item.itemNo || `Item ${index + 1}`,
description: item.itemDescription || item.materialSpec || '',
remark: item.remark,
})
}
})
}
return { contractContent, remarks, itemNotes }
}, [po])
const { contractContent, remarks, itemNotes } = extractContent()
const hasAnyContent = contractContent || remarks || itemNotes.length > 0
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px] max-h-[80vh]">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<FileTextIcon className="h-5 w-5" />
계약서 내용 - {po?.contractNo}
</DialogTitle>
<DialogDescription>
{po?.contractName} 계약의 계약서 내용 및 관련 노트입니다.
</DialogDescription>
</DialogHeader>
<ScrollArea className="max-h-[60vh] pr-4">
<div className="space-y-6">
{/* 계약서 내용 (메인) */}
{contractContent && (
<div className="space-y-3">
<div className="flex items-center gap-2">
<FileTextIcon className="h-4 w-4 text-blue-600" />
<h3 className="text-sm font-semibold text-blue-600">
계약서 내용
</h3>
</div>
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<p className="text-sm text-gray-700 whitespace-pre-wrap leading-relaxed">
{contractContent}
</p>
</div>
</div>
)}
{/* 구분선 */}
{contractContent && (remarks || itemNotes.length > 0) && <Separator />}
{/* 계약 비고 */}
{remarks && (
<div className="space-y-3">
<div className="flex items-center gap-2">
<MessageSquareIcon className="h-4 w-4 text-orange-600" />
<h3 className="text-sm font-semibold text-orange-600">
계약 비고
</h3>
</div>
<div className="bg-orange-50 border border-orange-200 rounded-lg p-4">
<p className="text-sm text-gray-700 whitespace-pre-wrap">
{remarks}
</p>
</div>
</div>
)}
{/* 구분선 */}
{(contractContent || remarks) && itemNotes.length > 0 && <Separator />}
{/* 아이템별 노트들 */}
{itemNotes.length > 0 && (
<div className="space-y-4">
<div className="flex items-center gap-2">
<FileTextIcon className="h-4 w-4 text-green-600" />
<h3 className="text-sm font-semibold text-green-600">
품목별 노트 ({itemNotes.length}개)
</h3>
</div>
<div className="space-y-3">
{itemNotes.map((item, index) => (
<div key={index} className="bg-green-50 border border-green-200 rounded-lg p-4">
<div className="flex items-start gap-3">
<Badge variant="outline" className="text-green-700 border-green-300">
{item.itemNo}
</Badge>
<div className="flex-1 min-w-0">
{item.description && (
<p className="text-sm font-medium text-gray-900 mb-1 truncate" title={item.description}>
{item.description}
</p>
)}
<p className="text-sm text-gray-700 whitespace-pre-wrap">
{item.remark}
</p>
</div>
</div>
</div>
))}
</div>
</div>
)}
{/* 내용이 없는 경우 */}
{!hasAnyContent && (
<div className="text-center py-8">
<FileTextIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
<p className="text-gray-500">등록된 계약서 내용이 없습니다.</p>
</div>
)}
</div>
</ScrollArea>
</DialogContent>
</Dialog>
)
}
|