summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-06-02 02:27:28 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-06-02 02:27:28 +0000
commit37611339fea096e47aaa42311a13a6313b4200db (patch)
treedd9c7dba27a3db2aebd18bf2087c6a30987aa957 /lib/vendor-document-list
parentbac0228d21b7195065e9cddcc327ae33659c7bcc (diff)
(대표님) 20250602 오전 작업사항 (코드프리징)
Diffstat (limited to 'lib/vendor-document-list')
-rw-r--r--lib/vendor-document-list/table/enhanced-documents-table.tsx48
-rw-r--r--lib/vendor-document-list/table/stage-revision-expanded-content.tsx171
2 files changed, 117 insertions, 102 deletions
diff --git a/lib/vendor-document-list/table/enhanced-documents-table.tsx b/lib/vendor-document-list/table/enhanced-documents-table.tsx
index 14c52455..f840a10c 100644
--- a/lib/vendor-document-list/table/enhanced-documents-table.tsx
+++ b/lib/vendor-document-list/table/enhanced-documents-table.tsx
@@ -468,26 +468,34 @@ export function EnhancedDocumentsTable({
{/* 메인 테이블 - 가로스크롤 문제 해결을 위한 구조 개선 */}
<div className="space-y-4">
<div className="rounded-md border bg-white overflow-hidden">
- <ExpandableDataTable
- table={table}
- expandable={true}
- expandedRows={expandedRows}
- setExpandedRows={setExpandedRows}
- renderExpandedContent={(document) => (
- // ✅ 확장된 내용을 별도 컨테이너로 분리하여 가로스크롤 영향 차단
- <div className="">
- <StageRevisionExpandedContent
- document={document}
- onUploadRevision={handleUploadRevision}
- projectType={projectType}
- expandedStages={expandedStages[String(document.documentId)] || {}}
- onStageToggle={(stageId) => handleStageToggle(String(document.documentId), stageId)}
- />
- </div>
- )}
- // 확장된 행에 대한 특별한 스타일링
- expandedRowClassName="!p-0"
- >
+ <ExpandableDataTable
+ table={table}
+ expandable={true}
+ expandedRows={expandedRows}
+ setExpandedRows={setExpandedRows}
+ renderExpandedContent={(document) => (
+ <div className="">
+ <StageRevisionExpandedContent
+ document={document}
+ onUploadRevision={handleUploadRevision}
+ projectType={projectType}
+ expandedStages={expandedStages[String(document.documentId)] || {}}
+ onStageToggle={(stageId) => handleStageToggle(String(document.documentId), stageId)}
+ />
+ </div>
+ )}
+ expandedRowClassName="!p-0"
+ // clickableColumns={[
+ // 'docNumber',
+ // 'title',
+ // 'currentStageStatus',
+ // 'progressPercentage',
+ // ]}
+ excludeFromClick={[
+ 'actions',
+ 'select'
+ ]}
+ >
<DataTableAdvancedToolbar
table={table}
filterFields={advancedFilterFields}
diff --git a/lib/vendor-document-list/table/stage-revision-expanded-content.tsx b/lib/vendor-document-list/table/stage-revision-expanded-content.tsx
index d9d53cc9..a4de03b7 100644
--- a/lib/vendor-document-list/table/stage-revision-expanded-content.tsx
+++ b/lib/vendor-document-list/table/stage-revision-expanded-content.tsx
@@ -13,7 +13,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
-import {
+import {
Table,
TableBody,
TableCell,
@@ -21,7 +21,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table"
-import {
+import {
FileText,
User,
Calendar,
@@ -86,7 +86,7 @@ const getPriorityText = (priority: string) => {
const getFileIconColor = (fileName: string) => {
const ext = fileName.split('.').pop()?.toLowerCase()
- switch(ext) {
+ switch (ext) {
case 'pdf': return 'text-red-500'
case 'doc': case 'docx': return 'text-blue-500'
case 'xls': case 'xlsx': return 'text-green-500'
@@ -105,7 +105,7 @@ interface StageRevisionExpandedContentProps {
onStageToggle?: (stageId: number) => void
}
-export const StageRevisionExpandedContent = ({
+export const StageRevisionExpandedContent = ({
document: documentData,
onUploadRevision,
onStageStatusUpdate,
@@ -117,7 +117,7 @@ export const StageRevisionExpandedContent = ({
// 로컬 상태 관리
const [localExpandedStages, setLocalExpandedStages] = React.useState<Record<number, boolean>>({})
const [expandedRevisions, setExpandedRevisions] = React.useState<Set<number>>(new Set())
-
+
// ✅ 문서 뷰어 상태 관리
const [viewerOpen, setViewerOpen] = React.useState(false)
const [selectedRevisions, setSelectedRevisions] = React.useState<any[]>([])
@@ -127,11 +127,11 @@ export const StageRevisionExpandedContent = ({
const viewer = React.useRef<HTMLDivElement>(null)
const initialized = React.useRef(false)
const isCancelled = React.useRef(false)
-
+
// 상위에서 관리하는지 로컬에서 관리하는지 결정
const isExternallyManaged = onStageToggle !== undefined
const currentExpandedStages = isExternallyManaged ? expandedStages : localExpandedStages
-
+
const handleStageToggle = React.useCallback((stageId: number) => {
if (isExternallyManaged && onStageToggle) {
onStageToggle(stageId)
@@ -157,7 +157,7 @@ export const StageRevisionExpandedContent = ({
// ✅ PDF 뷰어 정리 함수
const cleanupHtmlStyle = React.useCallback(() => {
- const htmlElement = window.document.documentElement
+ const htmlElement = window.document.documentElement
const originalStyle = htmlElement.getAttribute("style") || ""
const colorSchemeStyle = originalStyle
.split(";")
@@ -185,12 +185,12 @@ export const StageRevisionExpandedContent = ({
console.log(attachment)
try {
// ID를 우선으로 사용, 없으면 filePath 사용
- const queryParam = attachment.id
+ const queryParam = attachment.id
? `id=${encodeURIComponent(attachment.id)}`
: `path=${encodeURIComponent(attachment.filePath)}`
-
+
const response = await fetch(`/api/document-download?${queryParam}`)
-
+
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || '파일 다운로드에 실패했습니다.')
@@ -205,7 +205,7 @@ export const StageRevisionExpandedContent = ({
link.click()
window.document.body.removeChild(link)
window.URL.revokeObjectURL(url)
-
+
console.log('✅ 파일 다운로드 완료:', attachment.fileName)
} catch (error) {
console.error('❌ 파일 다운로드 오류:', error)
@@ -312,7 +312,7 @@ export const StageRevisionExpandedContent = ({
const handleCloseViewer = React.useCallback(async () => {
if (!fileSetLoading) {
isCancelled.current = true
-
+
if (instance) {
try {
await instance.UI.dispose()
@@ -342,7 +342,7 @@ export const StageRevisionExpandedContent = ({
</div>
)
}
-
+
return (
<>
<div className="w-full max-w-none bg-gray-50" onClick={(e) => e.stopPropagation()}>
@@ -366,27 +366,28 @@ export const StageRevisionExpandedContent = ({
새 리비전 업로드
</Button> */}
</div>
-
+
<ScrollArea className="h-[400px] w-full">
<div className="space-y-3 pr-4">
{stagesWithRevisions.map((stage) => {
const isExpanded = currentExpandedStages[stage.id] || false
const revisions = stage.revisions || []
-
+
return (
<div key={stage.id} className="bg-white rounded border shadow-sm overflow-hidden">
- {/* 스테이지 헤더 */}
- <div className="py-2 px-3 bg-gray-50 border-b">
+ {/* 스테이지 헤더 - 전체 영역 클릭 가능 */}
+ <div
+ className="py-2 px-3 bg-gray-50 border-b cursor-pointer hover:bg-gray-100 transition-colors"
+ onClick={(e) => {
+ e.preventDefault()
+ e.stopPropagation()
+ handleStageToggle(stage.id)
+ }}
+ >
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
- <button
- className="flex items-center gap-2 hover:bg-gray-100 p-1 rounded transition-colors"
- onClick={(e) => {
- e.preventDefault()
- e.stopPropagation()
- handleStageToggle(stage.id)
- }}
- >
+ {/* 버튼 영역 - 이제 시각적 표시만 담당 */}
+ <div className="flex items-center gap-2">
<div className="flex items-center gap-2">
<div className="w-6 h-6 rounded-full bg-white border-2 border-gray-300 flex items-center justify-center text-xs font-medium">
{stage.stageOrder || 1}
@@ -394,17 +395,17 @@ export const StageRevisionExpandedContent = ({
<div className={cn(
"w-2 h-2 rounded-full",
stage.stageStatus === 'COMPLETED' ? 'bg-green-500' :
- stage.stageStatus === 'IN_PROGRESS' ? 'bg-blue-500' :
- stage.stageStatus === 'SUBMITTED' ? 'bg-purple-500' :
- 'bg-gray-300'
+ stage.stageStatus === 'IN_PROGRESS' ? 'bg-blue-500' :
+ stage.stageStatus === 'SUBMITTED' ? 'bg-purple-500' :
+ 'bg-gray-300'
)} />
- {isExpanded ?
- <ChevronDown className="w-3 h-3 text-gray-500" /> :
+ {isExpanded ?
+ <ChevronDown className="w-3 h-3 text-gray-500" /> :
<ChevronRight className="w-3 h-3 text-gray-500" />
}
</div>
- </button>
-
+ </div>
+
<div className="flex-1">
<div className="flex items-center gap-2">
<div className="font-medium text-sm">{stage.stageName}</div>
@@ -417,7 +418,7 @@ export const StageRevisionExpandedContent = ({
</div>
</div>
</div>
-
+
<div className="flex items-center gap-4">
<div className="grid grid-cols-2 gap-2 text-xs">
<div>
@@ -437,45 +438,51 @@ export const StageRevisionExpandedContent = ({
</div>
)}
</div>
-
- {/* 스테이지 액션 메뉴 */}
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button
- variant="ghost"
- size="sm"
- className="h-7 w-7 p-0"
- >
- <MoreHorizontal className="h-3 w-3" />
- </Button>
- </DropdownMenuTrigger>
- <DropdownMenuContent align="end">
- {onStageStatusUpdate && (
- <>
- <DropdownMenuItem onClick={() => onStageStatusUpdate(stage.id, 'IN_PROGRESS')}>
- 진행 시작
- </DropdownMenuItem>
- <DropdownMenuItem onClick={() => onStageStatusUpdate(stage.id, 'COMPLETED')}>
- 완료 처리
- </DropdownMenuItem>
- </>
- )}
- <DropdownMenuItem onClick={() => onUploadRevision(documentData, stage.stageName)}>
- 리비전 업로드
- </DropdownMenuItem>
- {/* ✅ 스테이지에 첨부파일이 있는 리비전이 있을 때만 문서 보기 버튼 표시 */}
- {revisions.some(rev => rev.attachments && rev.attachments.length > 0) && (
- <DropdownMenuItem onClick={() => handleViewRevision(revisions.filter(rev => rev.attachments && rev.attachments.length > 0))}>
- <Eye className="w-3 h-3 mr-1" />
- 스테이지 문서 보기
+
+ {/* 스테이지 액션 메뉴 - 클릭 이벤트 전파 차단 */}
+ <div
+ onClick={(e) => {
+ e.stopPropagation() // 액션 메뉴 클릭 시 스테이지 토글 방지
+ }}
+ >
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-7 w-7 p-0"
+ >
+ <MoreHorizontal className="h-3 w-3" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ {onStageStatusUpdate && (
+ <>
+ <DropdownMenuItem onClick={() => onStageStatusUpdate(stage.id, 'IN_PROGRESS')}>
+ 진행 시작
+ </DropdownMenuItem>
+ <DropdownMenuItem onClick={() => onStageStatusUpdate(stage.id, 'COMPLETED')}>
+ 완료 처리
+ </DropdownMenuItem>
+ </>
+ )}
+ <DropdownMenuItem onClick={() => onUploadRevision(documentData, stage.stageName)}>
+ 리비전 업로드
</DropdownMenuItem>
- )}
- </DropdownMenuContent>
- </DropdownMenu>
+ {/* ✅ 스테이지에 첨부파일이 있는 리비전이 있을 때만 문서 보기 버튼 표시 */}
+ {revisions.some(rev => rev.attachments && rev.attachments.length > 0) && (
+ <DropdownMenuItem onClick={() => handleViewRevision(revisions.filter(rev => rev.attachments && rev.attachments.length > 0))}>
+ <Eye className="w-3 h-3 mr-1" />
+ 스테이지 문서 보기
+ </DropdownMenuItem>
+ )}
+ </DropdownMenuContent>
+ </DropdownMenu>
+ </div>
</div>
</div>
</div>
-
+
{/* 리비전 목록 - 테이블 형태 */}
{isExpanded && (
<div className="max-h-72 overflow-y-auto">
@@ -499,29 +506,29 @@ export const StageRevisionExpandedContent = ({
<TableBody>
{revisions.map((revision) => {
const hasAttachments = revision.attachments && revision.attachments.length > 0
-
+
return (
<TableRow key={revision.id} className="hover:bg-gray-50 h-10">
{/* 리비전 */}
<TableCell className="py-1 px-2">
<span className="text-xs font-semibold">
- {revision.uploaderType ==="vendor"?"To SHI":"From SHI"}
+ {revision.uploaderType === "vendor" ? "To SHI" : "From SHI"}
</span>
</TableCell>
-
+
<TableCell className="py-1 px-2">
<span className="font-mono text-xs font-semibold bg-gray-100 px-1.5 py-0.5 rounded">
{revision.revision}
</span>
</TableCell>
-
+
{/* 상태 */}
<TableCell className="py-1 px-2">
<Badge className={cn("text-xs px-1.5 py-0.5", getStatusColor(revision.revisionStatus))}>
{getStatusText(revision.revisionStatus)}
</Badge>
</TableCell>
-
+
{/* 업로더 */}
<TableCell className="py-1 px-2">
<div className="flex items-center gap-1">
@@ -529,20 +536,20 @@ export const StageRevisionExpandedContent = ({
<span className="text-xs truncate max-w-[60px]">{revision.uploaderName || '-'}</span>
</div>
</TableCell>
- {/* 제출일 */}
+ {/* 제출일 */}
<TableCell className="py-1 px-2">
<span className="text-xs text-gray-600">
{revision.uploadedAt ? formatDate(revision.uploadedAt) : '-'}
</span>
</TableCell>
-
+
{/* 제출일 */}
<TableCell className="py-1 px-2">
<span className="text-xs text-gray-600">
{revision.externalSentDate ? formatDate(revision.externalSentDate) : '-'}
</span>
</TableCell>
-
+
{/* 승인/반려일 */}
<TableCell className="py-1 px-2">
<div className="text-xs text-gray-600">
@@ -569,7 +576,7 @@ export const StageRevisionExpandedContent = ({
)}
</div>
</TableCell>
-
+
{/* ✅ 첨부파일 - 클릭 시 다운로드, 별도 뷰어 버튼 */}
<TableCell className="py-1 px-2">
{hasAttachments ? (
@@ -588,7 +595,7 @@ export const StageRevisionExpandedContent = ({
</Button>
))}
{revision.attachments.length > 4 && (
- <span
+ <span
className="text-xs text-gray-500 ml-0.5"
title={`총 ${revision.attachments.length}개 파일`}
>
@@ -610,7 +617,7 @@ export const StageRevisionExpandedContent = ({
<span className="text-gray-400 text-xs">-</span>
)}
</TableCell>
-
+
{/* 액션 */}
<TableCell className="py-1 px-2">
<div className="flex gap-0.5">
@@ -647,7 +654,7 @@ export const StageRevisionExpandedContent = ({
</Button>
</div>
</TableCell>
-
+
{/* 코멘트 */}
<TableCell className="py-1 px-2">
{revision.comment ? (
@@ -703,7 +710,7 @@ export const StageRevisionExpandedContent = ({
<DialogHeader className="h-[38px]">
<DialogTitle>문서 미리보기</DialogTitle>
<DialogDescription>
- {selectedRevisions.length === 1
+ {selectedRevisions.length === 1
? `리비전 ${selectedRevisions[0]?.revision} 첨부파일`
: `${selectedRevisions.length}개 리비전 첨부파일`
}