summaryrefslogtreecommitdiff
path: root/lib/general-contracts_old/detail/general-contract-documents.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-11 09:22:58 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-11 09:22:58 +0000
commit535de26b9cf3242c04543d6891d78352b9593a60 (patch)
treecfb2a76a3442ad0ec1d2b2b75293658d93a45808 /lib/general-contracts_old/detail/general-contract-documents.tsx
parent88b737a71372353e47c466553273d88f5bf36834 (diff)
(최겸) 구매 수정사항 개발
Diffstat (limited to 'lib/general-contracts_old/detail/general-contract-documents.tsx')
-rw-r--r--lib/general-contracts_old/detail/general-contract-documents.tsx383
1 files changed, 0 insertions, 383 deletions
diff --git a/lib/general-contracts_old/detail/general-contract-documents.tsx b/lib/general-contracts_old/detail/general-contract-documents.tsx
deleted file mode 100644
index b0f20e7f..00000000
--- a/lib/general-contracts_old/detail/general-contract-documents.tsx
+++ /dev/null
@@ -1,383 +0,0 @@
-'use client'
-
-import React, { useState, useEffect } from 'react'
-import { Button } from '@/components/ui/button'
-import { Input } from '@/components/ui/input'
-import { Label } from '@/components/ui/label'
-import { Textarea } from '@/components/ui/textarea'
-import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
-import { Badge } from '@/components/ui/badge'
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
-import {
- Download,
- Trash2,
- FileText,
- LoaderIcon,
- Paperclip,
- MessageSquare
-} from 'lucide-react'
-import { toast } from 'sonner'
-import { useTransition } from 'react'
-import {
- uploadContractAttachment,
- getContractAttachments,
- getContractAttachmentForDownload,
- deleteContractAttachment
-} from '../service'
-import { downloadFile } from '@/lib/file-download'
-
-interface ContractDocument {
- id: number
- contractId: number
- documentName: string
- fileName: string
- filePath: string
- documentType?: string
- shiComment?: string | null
- vendorComment?: string | null
- uploadedAt: Date
- uploadedById: number
-}
-
-interface ContractDocumentsProps {
- contractId: number
- userId: string
- readOnly?: boolean
-}
-
-export function ContractDocuments({ contractId, userId, readOnly = false }: ContractDocumentsProps) {
- const [documents, setDocuments] = useState<ContractDocument[]>([])
- const [isLoading, setIsLoading] = useState(false)
- const [isPending, startTransition] = useTransition()
- const [editingComment, setEditingComment] = useState<{ id: number; type: 'shi' | 'vendor' } | null>(null)
- const [commentText, setCommentText] = useState('')
- const [selectedDocumentType, setSelectedDocumentType] = useState('')
-
- const loadDocuments = React.useCallback(async () => {
- setIsLoading(true)
- try {
- const documentList = await getContractAttachments(contractId)
- setDocuments(documentList as ContractDocument[])
- } catch (error) {
- console.error('Error loading documents:', error)
- toast.error('문서 목록을 불러오는 중 오류가 발생했습니다.')
- } finally {
- setIsLoading(false)
- }
- }, [contractId])
-
- useEffect(() => {
- loadDocuments()
- }, [loadDocuments])
-
- const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
- const file = event.target.files?.[0]
- if (!file) return
-
- if (!selectedDocumentType) {
- toast.error('문서 유형을 선택해주세요.')
- return
- }
-
- startTransition(async () => {
- try {
- // 본 계약문서 타입인 경우 기존 문서 확인
- if (selectedDocumentType === 'main') {
- const existingMainDoc = documents.find(doc => doc.documentType === 'main')
- if (existingMainDoc) {
- toast.info('기존 계약문서가 새롭게 업로드한 문서로 대체됩니다.')
- // 기존 본 계약문서 삭제
- await deleteContractAttachment(existingMainDoc.id, contractId)
- }
- }
-
- await uploadContractAttachment(contractId, file, userId, selectedDocumentType)
- toast.success('문서가 업로드되었습니다.')
- loadDocuments()
- // 파일 입력 초기화
- event.target.value = ''
- } catch (error) {
- console.error('Error uploading document:', error)
- toast.error('문서 업로드 중 오류가 발생했습니다.')
- }
- })
- }
-
- const handleDownload = async (document: ContractDocument) => {
- try {
- const fileData = await getContractAttachmentForDownload(document.id, contractId)
- downloadFile(fileData.attachment?.filePath || '', fileData.attachment?.fileName || '', {
- showToast: true
- })
- } catch (error) {
- console.error('Error downloading document:', error)
- toast.error('문서 다운로드 중 오류가 발생했습니다.')
- }
- }
-
- const handleDelete = async (documentId: number) => {
-
- startTransition(async () => {
- try {
- await deleteContractAttachment(documentId, contractId)
- toast.success('문서가 삭제되었습니다.')
- loadDocuments()
- } catch (error) {
- console.error('Error deleting document:', error)
- toast.error('문서 삭제 중 오류가 발생했습니다.')
- }
- })
- }
-
- const handleEditComment = (documentId: number, type: 'shi' | 'vendor', currentComment?: string) => {
- setEditingComment({ id: documentId, type })
- setCommentText(currentComment || '')
- }
-
- const handleSaveComment = async () => {
- if (!editingComment) return
-
- try {
- // TODO: API 호출로 댓글 저장
- toast.success('댓글이 저장되었습니다.')
- setEditingComment(null)
- setCommentText('')
- loadDocuments()
- } catch (error) {
- console.error('Error saving comment:', error)
- toast.error('댓글 저장 중 오류가 발생했습니다.')
- }
- }
-
- const getDocumentTypeLabel = (documentName: string) => {
- switch (documentName) {
- case 'specification': return '사양'
- case 'pricing': return '단가종류'
- case 'other': return '기타'
- default: return documentName
- }
- }
-
- const getDocumentTypeColor = (documentName: string) => {
- switch (documentName) {
- case 'specification': return 'bg-blue-100 text-blue-800'
- case 'pricing': return 'bg-green-100 text-green-800'
- case 'other': return 'bg-gray-100 text-gray-800'
- default: return 'bg-gray-100 text-gray-800'
- }
- }
-
- const groupedDocuments = documents.reduce((acc, doc) => {
- const type = doc.documentName
- if (!acc[type]) {
- acc[type] = []
- }
- acc[type].push(doc)
- return acc
- }, {} as Record<string, ContractDocument[]>)
-
- const documentTypes = [
- { value: 'specification', label: '사양' },
- { value: 'pricing', label: '단가종류' },
- { value: 'other', label: '기타' }
- ]
-
- return (
- <Card>
- <CardHeader>
- <CardTitle className="flex items-center gap-2">
- <Paperclip className="h-5 w-5" />
- 계약 첨부문서
- </CardTitle>
- </CardHeader>
- <CardContent className="space-y-6">
- {/* 파일 업로드 */}
- {!readOnly && (
- <div className="space-y-4">
- <div className="flex items-center gap-4">
- <Select value={selectedDocumentType} onValueChange={setSelectedDocumentType}>
- <SelectTrigger className="w-40">
- <SelectValue placeholder="문서 유형" />
- </SelectTrigger>
- <SelectContent>
- {documentTypes.map((type) => (
- <SelectItem key={type.value} value={type.value}>
- {type.label}
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- <Input
- type="file"
- onChange={handleFileUpload}
- disabled={isPending}
- className="flex-1"
- />
- </div>
- </div>
- )}
-
- {/* 문서 목록 */}
- {isLoading ? (
- <div className="flex items-center justify-center py-8">
- <LoaderIcon className="h-6 w-6 animate-spin" />
- <span className="ml-2">문서를 불러오는 중...</span>
- </div>
- ) : documents.length === 0 ? (
- <div className="text-center py-8 text-muted-foreground">
- <FileText className="h-12 w-12 mx-auto mb-4 opacity-50" />
- <p>업로드된 문서가 없습니다.</p>
- </div>
- ) : (
- <div className="space-y-6">
- {Object.entries(groupedDocuments).map(([type, docs]) => (
- <div key={type} className="space-y-3">
- <div className="flex items-center gap-2">
- <Badge className={getDocumentTypeColor(type)}>
- {getDocumentTypeLabel(type)}
- </Badge>
- <span className="text-sm text-muted-foreground">
- {docs.length}개 문서
- </span>
- </div>
-
- <div className="space-y-3">
- {docs.map((doc) => (
- <div key={doc.id} className="border rounded-lg p-4 space-y-3">
- <div className="flex items-center justify-between">
- <div className="flex items-center gap-3">
- <FileText className="h-5 w-5 text-muted-foreground" />
- <div>
- <p className="font-medium">{doc.fileName}</p>
- <p className="text-sm text-muted-foreground">
- 업로드: {new Date(doc.uploadedAt).toLocaleDateString()}
- </p>
- </div>
- </div>
-
- {!readOnly && (
- <div className="flex items-center gap-2">
- <Button
- variant="outline"
- size="sm"
- onClick={() => handleDownload(doc)}
- >
- <Download className="h-4 w-4" />
- </Button>
- <Button
- variant="outline"
- size="sm"
- onClick={() => handleDelete(doc.id)}
- className="text-red-600 hover:text-red-700"
- >
- <Trash2 className="h-4 w-4" />
- </Button>
- </div>
- )}
- </div>
-
- {/* 댓글 섹션 */}
- <div className="grid grid-cols-2 gap-4">
- {/* SHI 댓글 */}
- <div className="space-y-2">
- <div className="flex items-center justify-between">
- <Label className="text-sm font-medium">SHI 댓글</Label>
- {!readOnly && (
- <Button
- variant="ghost"
- size="sm"
- onClick={() => handleEditComment(doc.id, 'shi', doc.shiComment || '')}
- >
- <MessageSquare className="h-4 w-4" />
- </Button>
- )}
- </div>
- {editingComment?.id === doc.id && editingComment.type === 'shi' ? (
- <div className="space-y-2">
- <Textarea
- value={commentText}
- onChange={(e) => setCommentText(e.target.value)}
- placeholder="SHI 댓글을 입력하세요"
- rows={3}
- />
- <div className="flex gap-2">
- <Button size="sm" onClick={handleSaveComment}>
- 저장
- </Button>
- <Button
- size="sm"
- variant="outline"
- onClick={() => setEditingComment(null)}
- >
- 취소
- </Button>
- </div>
- </div>
- ) : (
- <div className="min-h-[60px] p-3 bg-gray-50 rounded border">
- {doc.shiComment ? (
- <p className="text-sm">{doc.shiComment}</p>
- ) : (
- <p className="text-sm text-muted-foreground">댓글이 없습니다.</p>
- )}
- </div>
- )}
- </div>
-
- {/* Vendor 댓글 */}
- <div className="space-y-2">
- <div className="flex items-center justify-between">
- <Label className="text-sm font-medium">Vendor 댓글</Label>
- {!readOnly && (
- <Button
- variant="ghost"
- size="sm"
- onClick={() => handleEditComment(doc.id, 'vendor', doc.vendorComment || '')}
- >
- <MessageSquare className="h-4 w-4" />
- </Button>
- )}
- </div>
- {editingComment?.id === doc.id && editingComment.type === 'vendor' ? (
- <div className="space-y-2">
- <Textarea
- value={commentText}
- onChange={(e) => setCommentText(e.target.value)}
- placeholder="Vendor 댓글을 입력하세요"
- rows={3}
- />
- <div className="flex gap-2">
- <Button size="sm" onClick={handleSaveComment}>
- 저장
- </Button>
- <Button
- size="sm"
- variant="outline"
- onClick={() => setEditingComment(null)}
- >
- 취소
- </Button>
- </div>
- </div>
- ) : (
- <div className="min-h-[60px] p-3 bg-gray-50 rounded border">
- {doc.vendorComment ? (
- <p className="text-sm">{doc.vendorComment}</p>
- ) : (
- <p className="text-sm text-muted-foreground">댓글이 없습니다.</p>
- )}
- </div>
- )}
- </div>
- </div>
- </div>
- ))}
- </div>
- </div>
- ))}
- </div>
- )}
- </CardContent>
- </Card>
- )
-}