summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-04 14:37:22 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-04 14:37:22 +0900
commit7827446e923e725db30ce2e849f5698d6bcfdeea (patch)
tree62b15c0e52eb578a9a8a843c730b18bc35399c96 /lib
parent02ee1692db190101956a0957504d7dd13c053cce (diff)
(김준회) bulk b4 upload 프로젝트 목록 문제(페이지네이션 내부 데이터로 한정되는 문제) 및 이모지 제거, change_logs 미반영 오류 처리
Diffstat (limited to 'lib')
-rw-r--r--lib/vendor-document-list/enhanced-document-service.ts62
-rw-r--r--lib/vendor-document-list/ship/bulk-b4-upload-dialog.tsx21
-rw-r--r--lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx34
-rw-r--r--lib/vendor-document-list/ship/enhanced-documents-table.tsx35
4 files changed, 112 insertions, 40 deletions
diff --git a/lib/vendor-document-list/enhanced-document-service.ts b/lib/vendor-document-list/enhanced-document-service.ts
index 6f9eda01..50073ee5 100644
--- a/lib/vendor-document-list/enhanced-document-service.ts
+++ b/lib/vendor-document-list/enhanced-document-service.ts
@@ -25,6 +25,7 @@ import { getServerSession } from "next-auth/next"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
import { countDocumentStagesOnly, selectDocumentStagesOnly } from "./repository"
import { saveFile } from "../file-stroage"
+import { syncService } from "./sync-service"
// 스키마 타입 정의
export interface GetEnhancedDocumentsSchema {
@@ -1579,6 +1580,11 @@ export async function getDocumentDetails(documentId: number) {
if (!projectId) {
return { success: false, error: "프로젝트를 선택해주세요" }
}
+
+ // vendorId 가져오기
+ const vendorId = session.user.companyId ? Number(session.user.companyId) : 0
+ const userId = session.user.id ? parseInt(session.user.id) : 0
+ const userName = session.user.name || "System"
const results: UploadResult[] = []
let successCount = 0
@@ -1705,6 +1711,32 @@ export async function getDocumentDetails(documentId: number) {
revisionStatus: 'SUBMITTED',
}).returning()
revisionId = newRevision.id
+
+ // ✅ change_logs에 revision 생성 기록
+ if (vendorId > 0) {
+ try {
+ await syncService.logChange(
+ vendorId,
+ 'revision',
+ revisionId,
+ 'CREATE',
+ {
+ issueStageId: targetStageId,
+ revision: fileInfo.revision,
+ uploaderType: "vendor",
+ uploaderName: session.user.name || "System",
+ revisionStatus: 'SUBMITTED',
+ },
+ null,
+ userId,
+ userName,
+ ['DOLCE']
+ )
+ } catch (logError) {
+ console.error('Failed to log revision change:', logError)
+ // 로그 실패는 업로드 실패로 처리하지 않음
+ }
+ }
}
// 파일 저장
@@ -1720,13 +1752,39 @@ export async function getDocumentDetails(documentId: number) {
}
// 첨부파일 정보 저장
- await db.insert(documentAttachments).values({
+ const [newAttachment] = await db.insert(documentAttachments).values({
revisionId,
fileName: fileInfo.file.name,
filePath: saveResult.publicPath!,
fileType: fileInfo.file.type,
fileSize: fileInfo.file.size,
- })
+ }).returning()
+
+ // ✅ change_logs에 attachment 생성 기록
+ if (vendorId > 0 && newAttachment?.id) {
+ try {
+ await syncService.logChange(
+ vendorId,
+ 'attachment',
+ newAttachment.id,
+ 'CREATE',
+ {
+ revisionId,
+ fileName: fileInfo.file.name,
+ filePath: saveResult.publicPath!,
+ fileType: fileInfo.file.type,
+ fileSize: fileInfo.file.size,
+ },
+ null,
+ userId,
+ userName,
+ ['DOLCE']
+ )
+ } catch (logError) {
+ console.error('Failed to log attachment change:', logError)
+ // 로그 실패는 업로드 실패로 처리하지 않음
+ }
+ }
results.push({
docNumber,
diff --git a/lib/vendor-document-list/ship/bulk-b4-upload-dialog.tsx b/lib/vendor-document-list/ship/bulk-b4-upload-dialog.tsx
index 43788c8a..3ff2f467 100644
--- a/lib/vendor-document-list/ship/bulk-b4-upload-dialog.tsx
+++ b/lib/vendor-document-list/ship/bulk-b4-upload-dialog.tsx
@@ -52,7 +52,6 @@ import {
FileText,
} from "lucide-react"
-import { SimplifiedDocumentsView } from "@/db/schema"
import { bulkUploadB4Documents } from "../enhanced-document-service"
// 파일명 파싱 유틸리티
@@ -94,10 +93,15 @@ const formSchema = z.object({
files: z.array(z.instanceof(File)).min(1, "Please select files"),
})
+export interface ProjectOption {
+ id: string
+ code: string
+}
+
interface BulkB4UploadDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
- allDocuments: SimplifiedDocumentsView[]
+ projectOptions: ProjectOption[]
}
interface ParsedFile {
@@ -110,8 +114,8 @@ interface ParsedFile {
export function BulkB4UploadDialog({
open,
- onOpenChange,
- allDocuments
+ onOpenChange,
+ projectOptions
}: BulkB4UploadDialogProps) {
const [isUploading, setIsUploading] = React.useState(false)
const [parsedFiles, setParsedFiles] = React.useState<ParsedFile[]>([])
@@ -121,15 +125,6 @@ export function BulkB4UploadDialog({
const [pendingProjectId, setPendingProjectId] = React.useState<string>("")
const router = useRouter()
- // 프로젝트 ID 추출
- const projectOptions = React.useMemo(() => {
- const projectIds = [...new Set(allDocuments.map(doc => doc.projectId).filter(Boolean))]
- return projectIds.map(id => ({
- id: String(id),
- code: allDocuments.find(doc => doc.projectId === id)?.projectCode || `Project ${id}`
- }))
- }, [allDocuments])
-
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
diff --git a/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx b/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx
index 94252db5..846eb5b2 100644
--- a/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx
+++ b/lib/vendor-document-list/ship/enhanced-doc-table-toolbar-actions.tsx
@@ -13,20 +13,28 @@ import { SendToSHIButton } from "./send-to-shi-button"
import { ImportFromDOLCEButton } from "./import-from-dolce-button"
import { BulkB4UploadDialog } from "./bulk-b4-upload-dialog"
+interface ProjectCodeStat {
+ code: string
+ count: number
+ projectId: number | null
+}
+
interface EnhancedDocTableToolbarActionsProps {
table: Table<SimplifiedDocumentsView>
projectType: "ship" | "plant"
b4: boolean
+ projectCodeStats?: ProjectCodeStat[]
}
export function EnhancedDocTableToolbarActions({
table,
projectType,
- b4
+ b4,
+ projectCodeStats = []
}: EnhancedDocTableToolbarActionsProps) {
const [bulkUploadDialogOpen, setBulkUploadDialogOpen] = React.useState(false)
- // 🔥 메모이제이션으로 불필요한 재계산 방지
+ // 메모이제이션으로 불필요한 재계산 방지
const allDocuments = React.useMemo(() => {
return table.getFilteredRowModel().rows.map(row => row.original)
}, [
@@ -35,20 +43,30 @@ export function EnhancedDocTableToolbarActions({
table.getState().globalFilter, // 전역 필터가 변경될 때만 재계산
])
- // 🔥 projectIds 메모이제이션 (ImportFromDOLCEButton에서 중복 계산 방지)
+ // projectIds 메모이제이션 (ImportFromDOLCEButton에서 중복 계산 방지)
const projectIds = React.useMemo(() => {
const uniqueIds = [...new Set(allDocuments.map(doc => doc.projectId).filter(Boolean))]
return uniqueIds.sort()
}, [allDocuments])
- // 🔥 핸들러들을 useCallback으로 메모이제이션
+ // 프로젝트 옵션 생성 (전체 프로젝트 목록)
+ const projectOptions = React.useMemo(() => {
+ return projectCodeStats
+ .filter(stat => stat.code !== 'Unknown' && stat.projectId)
+ .map(stat => ({
+ id: String(stat.projectId),
+ code: stat.code
+ }))
+ }, [projectCodeStats])
+
+ // 핸들러들을 useCallback으로 메모이제이션
const handleSyncComplete = React.useCallback(() => {
table.resetRowSelection()
}, [table])
const handleDocumentAdded = React.useCallback(() => {
table.resetRowSelection()
- // 🔥 강제 새로고침 대신 더 효율적인 방법 사용
+ // 강제 새로고침 대신 더 효율적인 방법 사용
setTimeout(() => {
// 상태 업데이트만으로 충분한 경우가 많음
window.location.reload()
@@ -62,7 +80,7 @@ export function EnhancedDocTableToolbarActions({
}, 500)
}, [table])
- // 🔥 Export 핸들러 메모이제이션
+ // Export 핸들러 메모이제이션
const handleExport = React.useCallback(() => {
exportTableToExcel(table, {
filename: "Document-list",
@@ -76,7 +94,7 @@ export function EnhancedDocTableToolbarActions({
{/* SHIP: DOLCE에서 목록 가져오기 */}
<ImportFromDOLCEButton
allDocuments={allDocuments}
- projectIds={projectIds} // 🔥 미리 계산된 projectIds 전달
+ projectIds={projectIds} // 미리 계산된 projectIds 전달
onImportComplete={handleImportComplete}
/>
@@ -117,7 +135,7 @@ export function EnhancedDocTableToolbarActions({
<BulkB4UploadDialog
open={bulkUploadDialogOpen}
onOpenChange={setBulkUploadDialogOpen}
- allDocuments={allDocuments}
+ projectOptions={projectOptions}
/>
)}
</>
diff --git a/lib/vendor-document-list/ship/enhanced-documents-table.tsx b/lib/vendor-document-list/ship/enhanced-documents-table.tsx
index d92f16dd..3eda0ffb 100644
--- a/lib/vendor-document-list/ship/enhanced-documents-table.tsx
+++ b/lib/vendor-document-list/ship/enhanced-documents-table.tsx
@@ -20,7 +20,7 @@ import { getSimplifiedDocumentColumns } from "./enhanced-doc-table-columns"
import { EnhancedDocTableToolbarActions } from "./enhanced-doc-table-toolbar-actions"
import { useQueryStates, parseAsString, parseAsStringEnum, parseAsInteger } from "nuqs"
-// 🔥 Project Code 필터를 위한 Select 컴포넌트 import 추가
+// Project Code 필터를 위한 Select 컴포넌트 import 추가
import {
Select,
SelectContent,
@@ -60,18 +60,18 @@ export function SimplifiedDocumentsTable({
allPromises,
onDataLoaded,
}: SimplifiedDocumentsTableProps) {
- // 🔥 React.use() 결과를 안전하게 처리
+ // React.use() 결과를 안전하게 처리
const promiseResults = React.use(allPromises)
const [documentResult, statsResult] = promiseResults
- // 🔥 데이터 구조분해를 메모이제이션
+ // 데이터 구조분해를 메모이제이션
const documentData = React.useMemo(() => documentResult as Awaited<ReturnType<typeof getUserVendorDocuments>>, [documentResult])
const statsData = React.useMemo(() => statsResult as Awaited<ReturnType<typeof getUserVendorDocumentStats>>, [statsResult])
const { data, pageCount, drawingKind } = documentData
const { primaryDrawingKind, projectCodeStats: serverProjectCodeStats, projectB4Stats: serverProjectB4Stats } = statsData
- // 🔥 URL searchParams를 통한 필터 상태 관리
+ // URL searchParams를 통한 필터 상태 관리
const [{ b4FilterType, projectCode, page }, setQueryStates] = useQueryStates(
{
b4FilterType: parseAsStringEnum<'all' | 'gtt_deliverable' | 'shi_input'>(['all', 'gtt_deliverable', 'shi_input']).withDefault('all'),
@@ -88,25 +88,25 @@ export function SimplifiedDocumentsTable({
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _ensurePageIsInState = page
- // 🔥 서버에서 받아온 프로젝트 코드 통계 사용 (필터링과 무관한 전체 통계)
+ // 서버에서 받아온 프로젝트 코드 통계 사용 (필터링과 무관한 전체 통계)
const projectCodeStats = React.useMemo(() => {
return serverProjectCodeStats || []
}, [serverProjectCodeStats])
- // 🔥 데이터 로드 콜백 (서버에서 이미 필터링되어 옴)
+ // 데이터 로드 콜백 (서버에서 이미 필터링되어 옴)
React.useEffect(() => {
if (data && onDataLoaded) {
onDataLoaded(data)
}
}, [data, onDataLoaded])
- // 🔥 컬럼 메모이제이션 최적화 (b4FilterType에 따라 동적 변경)
+ // 컬럼 메모이제이션 최적화 (b4FilterType에 따라 동적 변경)
const columns = React.useMemo(
() => getSimplifiedDocumentColumns({ b4FilterType }),
[b4FilterType]
)
- // 🔥 필터 필드들을 메모이제이션
+ // 필터 필드들을 메모이제이션
const advancedFilterFields: DataTableAdvancedFilterField<SimplifiedDocumentsView>[] = React.useMemo(() => [
{
id: "docNumber",
@@ -206,7 +206,7 @@ export function SimplifiedDocumentsTable({
},
], [])
- // 🔥 B4 전용 필드들 메모이제이션
+ // B4 전용 필드들 메모이제이션
const b4FilterFields: DataTableAdvancedFilterField<SimplifiedDocumentsView>[] = React.useMemo(() => [
{
id: "cGbn",
@@ -240,23 +240,23 @@ export function SimplifiedDocumentsTable({
},
], [])
- // 🔥 B4 문서 존재 여부 체크 메모이제이션
+ // B4 문서 존재 여부 체크 메모이제이션
const hasB4Documents = React.useMemo(() => {
return data.some((doc: SimplifiedDocumentsView) => doc.drawingKind === 'B4')
}, [data])
- // 🔥 최종 필터 필드 메모이제이션
+ // 최종 필터 필드 메모이제이션
const finalFilterFields = React.useMemo(() => {
return hasB4Documents ? [...advancedFilterFields, ...b4FilterFields] : advancedFilterFields
}, [hasB4Documents, advancedFilterFields, b4FilterFields])
- // 🔥 테이블 초기 상태 메모이제이션
+ // 테이블 초기 상태 메모이제이션
const tableInitialState = React.useMemo(() => ({
sorting: [{ id: "createdAt" as const, desc: true }],
columnPinning: { right: ["actions"] },
}), [])
- // 🔥 getRowId 함수 메모이제이션
+ // getRowId 함수 메모이제이션
const getRowId = React.useCallback((originalRow: SimplifiedDocumentsView) => String(originalRow.documentId), [])
const { table } = useDataTable({
@@ -272,17 +272,17 @@ export function SimplifiedDocumentsTable({
columnResizeMode: "onEnd",
})
- // 🔥 활성 drawingKind 메모이제이션
+ // 활성 drawingKind 메모이제이션
const activeDrawingKind = React.useMemo(() => {
return drawingKind || primaryDrawingKind
}, [drawingKind, primaryDrawingKind])
- // 🔥 kindInfo 메모이제이션
+ // kindInfo 메모이제이션
const kindInfo = React.useMemo(() => {
return activeDrawingKind ? DRAWING_KIND_INFO[activeDrawingKind] : null
}, [activeDrawingKind])
- // 🔥 B4 문서 통계 - 프로젝트 필터링에 따라 동적으로 계산 (서버에서 받아온 전체 통계 기반)
+ // B4 문서 통계 - 프로젝트 필터링에 따라 동적으로 계산 (서버에서 받아온 전체 통계 기반)
const b4Stats = React.useMemo(() => {
if (!hasB4Documents || !serverProjectB4Stats) return null
@@ -324,7 +324,7 @@ export function SimplifiedDocumentsTable({
</div>
)}
- {/* 🔥 필터 섹션 - Project Code 필터와 B4 필터를 함께 배치 */}
+ {/* 필터 섹션 - Project Code 필터와 B4 필터를 함께 배치 */}
<div className="space-y-3">
{/* Project Code 필터 드롭다운 */}
<div className="flex items-center gap-3 p-4 bg-muted/30 rounded-lg">
@@ -425,6 +425,7 @@ export function SimplifiedDocumentsTable({
table={table}
projectType="ship"
b4={hasB4Documents && b4FilterType === 'gtt_deliverable'}
+ projectCodeStats={projectCodeStats}
/>
</DataTableAdvancedToolbar>
</DataTable>