From 37611339fea096e47aaa42311a13a6313b4200db Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 2 Jun 2025 02:27:28 +0000 Subject: (대표님) 20250602 오전 작업사항 (코드프리징) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data-table.ts | 6 +- lib/forms/services.ts | 7 + lib/tags/service.ts | 18 +-- .../table/enhanced-documents-table.tsx | 48 +++--- .../table/stage-revision-expanded-content.tsx | 171 +++++++++++---------- 5 files changed, 137 insertions(+), 113 deletions(-) (limited to 'lib') diff --git a/lib/data-table.ts b/lib/data-table.ts index 4ad57d76..fd9309ab 100644 --- a/lib/data-table.ts +++ b/lib/data-table.ts @@ -20,9 +20,11 @@ import { FilterFn, Row } from "@tanstack/react-table" export function getCommonPinningStylesWithBorder({ column, withBorder = true, + isHeader = false, }: { column: Column withBorder?: boolean + isHeader?: boolean }): React.CSSProperties { const pinnedSide = column.getIsPinned() as "left" | "right" | false @@ -38,8 +40,8 @@ export function getCommonPinningStylesWithBorder({ /* ▒▒ 기타 스타일 ▒▒ */ width: column.getSize(), - // 불투명한 배경색 설정 - 테이블의 배경색과 동일하게 - background: pinnedSide ? "hsl(var(--background))" : "transparent", + // 헤더는 항상 불투명, 셀은 핀된 경우에만 불투명 + background: isHeader || pinnedSide ? "hsl(var(--background))" : "transparent", zIndex: pinnedSide ? 1 : 0, } diff --git a/lib/forms/services.ts b/lib/forms/services.ts index b6e479a2..27f2f5c2 100644 --- a/lib/forms/services.ts +++ b/lib/forms/services.ts @@ -307,6 +307,13 @@ export async function getFormData(formCode: string, contractItemId: number) { if (entry) { if (Array.isArray(entry.data)) { data = entry.data; + + data.sort((a, b) => { + const statusA = a.status || ''; + const statusB = b.status || ''; + return statusB.localeCompare(statusA); + }); + } else { console.warn("formEntries data was not an array. Using empty array."); } diff --git a/lib/tags/service.ts b/lib/tags/service.ts index 187aba39..e65ab65b 100644 --- a/lib/tags/service.ts +++ b/lib/tags/service.ts @@ -24,8 +24,8 @@ interface CreatedOrExistingForm { export async function getTags(input: GetTagsSchema, packagesId: number) { - return unstable_cache( - async () => { + // return unstable_cache( + // async () => { try { const offset = (input.page - 1) * input.perPage; @@ -79,13 +79,13 @@ export async function getTags(input: GetTagsSchema, packagesId: number) { // 에러 발생 시 디폴트 return { data: [], pageCount: 0 }; } - }, - [JSON.stringify(input), String(packagesId)], // 캐싱 키에 packagesId 추가 - { - revalidate: 3600, - tags: [`tags-${packagesId}`], // 패키지별 태그 사용 - } - )(); + // }, + // [JSON.stringify(input), String(packagesId)], // 캐싱 키에 packagesId 추가 + // { + // revalidate: 3600, + // tags: [`tags-${packagesId}`], // 패키지별 태그 사용 + // } + // )(); } export async function createTag( 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({ {/* 메인 테이블 - 가로스크롤 문제 해결을 위한 구조 개선 */}
- ( - // ✅ 확장된 내용을 별도 컨테이너로 분리하여 가로스크롤 영향 차단 -
- handleStageToggle(String(document.documentId), stageId)} - /> -
- )} - // 확장된 행에 대한 특별한 스타일링 - expandedRowClassName="!p-0" - > + ( +
+ handleStageToggle(String(document.documentId), stageId)} + /> +
+ )} + expandedRowClassName="!p-0" + // clickableColumns={[ + // 'docNumber', + // 'title', + // 'currentStageStatus', + // 'progressPercentage', + // ]} + excludeFromClick={[ + 'actions', + 'select' + ]} + > { 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>({}) const [expandedRevisions, setExpandedRevisions] = React.useState>(new Set()) - + // ✅ 문서 뷰어 상태 관리 const [viewerOpen, setViewerOpen] = React.useState(false) const [selectedRevisions, setSelectedRevisions] = React.useState([]) @@ -127,11 +127,11 @@ export const StageRevisionExpandedContent = ({ const viewer = React.useRef(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 = ({
) } - + return ( <>
e.stopPropagation()}> @@ -366,27 +366,28 @@ export const StageRevisionExpandedContent = ({ 새 리비전 업로드 */}
- +
{stagesWithRevisions.map((stage) => { const isExpanded = currentExpandedStages[stage.id] || false const revisions = stage.revisions || [] - + return (
- {/* 스테이지 헤더 */} -
+ {/* 스테이지 헤더 - 전체 영역 클릭 가능 */} +
{ + e.preventDefault() + e.stopPropagation() + handleStageToggle(stage.id) + }} + >
- - +
+
{stage.stageName}
@@ -417,7 +418,7 @@ export const StageRevisionExpandedContent = ({
- +
@@ -437,45 +438,51 @@ export const StageRevisionExpandedContent = ({
)}
- - {/* 스테이지 액션 메뉴 */} - - - - - - {onStageStatusUpdate && ( - <> - onStageStatusUpdate(stage.id, 'IN_PROGRESS')}> - 진행 시작 - - onStageStatusUpdate(stage.id, 'COMPLETED')}> - 완료 처리 - - - )} - onUploadRevision(documentData, stage.stageName)}> - 리비전 업로드 - - {/* ✅ 스테이지에 첨부파일이 있는 리비전이 있을 때만 문서 보기 버튼 표시 */} - {revisions.some(rev => rev.attachments && rev.attachments.length > 0) && ( - handleViewRevision(revisions.filter(rev => rev.attachments && rev.attachments.length > 0))}> - - 스테이지 문서 보기 + + {/* 스테이지 액션 메뉴 - 클릭 이벤트 전파 차단 */} +
{ + e.stopPropagation() // 액션 메뉴 클릭 시 스테이지 토글 방지 + }} + > + + + + + + {onStageStatusUpdate && ( + <> + onStageStatusUpdate(stage.id, 'IN_PROGRESS')}> + 진행 시작 + + onStageStatusUpdate(stage.id, 'COMPLETED')}> + 완료 처리 + + + )} + onUploadRevision(documentData, stage.stageName)}> + 리비전 업로드 - )} - - + {/* ✅ 스테이지에 첨부파일이 있는 리비전이 있을 때만 문서 보기 버튼 표시 */} + {revisions.some(rev => rev.attachments && rev.attachments.length > 0) && ( + handleViewRevision(revisions.filter(rev => rev.attachments && rev.attachments.length > 0))}> + + 스테이지 문서 보기 + + )} + + +
- + {/* 리비전 목록 - 테이블 형태 */} {isExpanded && (
@@ -499,29 +506,29 @@ export const StageRevisionExpandedContent = ({ {revisions.map((revision) => { const hasAttachments = revision.attachments && revision.attachments.length > 0 - + return ( {/* 리비전 */} - {revision.uploaderType ==="vendor"?"To SHI":"From SHI"} + {revision.uploaderType === "vendor" ? "To SHI" : "From SHI"} - + {revision.revision} - + {/* 상태 */} {getStatusText(revision.revisionStatus)} - + {/* 업로더 */}
@@ -529,20 +536,20 @@ export const StageRevisionExpandedContent = ({ {revision.uploaderName || '-'}
- {/* 제출일 */} + {/* 제출일 */} {revision.uploadedAt ? formatDate(revision.uploadedAt) : '-'} - + {/* 제출일 */} {revision.externalSentDate ? formatDate(revision.externalSentDate) : '-'} - + {/* 승인/반려일 */}
@@ -569,7 +576,7 @@ export const StageRevisionExpandedContent = ({ )}
- + {/* ✅ 첨부파일 - 클릭 시 다운로드, 별도 뷰어 버튼 */} {hasAttachments ? ( @@ -588,7 +595,7 @@ export const StageRevisionExpandedContent = ({ ))} {revision.attachments.length > 4 && ( - @@ -610,7 +617,7 @@ export const StageRevisionExpandedContent = ({ - )} - + {/* 액션 */}
@@ -647,7 +654,7 @@ export const StageRevisionExpandedContent = ({
- + {/* 코멘트 */} {revision.comment ? ( @@ -703,7 +710,7 @@ export const StageRevisionExpandedContent = ({ 문서 미리보기 - {selectedRevisions.length === 1 + {selectedRevisions.length === 1 ? `리비전 ${selectedRevisions[0]?.revision} 첨부파일` : `${selectedRevisions.length}개 리비전 첨부파일` } -- cgit v1.2.3