summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/forms/stat.ts50
-rw-r--r--lib/pq/service.ts19
-rw-r--r--lib/rfq-last/service.ts1
-rw-r--r--lib/rfq-last/vendor/rfq-vendor-table.tsx10
-rw-r--r--lib/tbe-last/service.ts21
-rw-r--r--lib/tbe-last/vendor/tbe-table.tsx8
-rw-r--r--lib/vendor-document-list/ship/import-from-dolce-button.tsx188
7 files changed, 194 insertions, 103 deletions
diff --git a/lib/forms/stat.ts b/lib/forms/stat.ts
index 054f2462..f13bab61 100644
--- a/lib/forms/stat.ts
+++ b/lib/forms/stat.ts
@@ -218,39 +218,13 @@ export async function getVendorFormStatus(projectId?: number): Promise<VendorFor
-export async function getFormStatusByVendor(projectId: number, formCode: string): Promise<FormStatusByVendor[]> {
+export async function getFormStatusByVendor(projectId: number, contractItemId: number, formCode: string): Promise<FormStatusByVendor[]> {
try {
const session = await getServerSession(authOptions)
if (!session?.user?.id) {
throw new Error("인증이 필요합니다.")
}
- const vendorStatusList: FormStatusByVendor[] = []
- const vendorId = Number(session.user.companyId)
-
- const vendorContracts = await db
- .select({
- id: contracts.id,
- projectId: contracts.projectId
- })
- .from(contracts)
- .where(
- and(
- eq(contracts.vendorId, vendorId),
- eq(contracts.projectId, projectId)
- )
- )
-
- const contractIds = vendorContracts.map(v => v.id)
-
- const contractItemsList = await db
- .select({
- id: contractItems.id
- })
- .from(contractItems)
- .where(inArray(contractItems.contractId, contractIds))
-
- const contractItemIds = contractItemsList.map(v => v.id)
let vendorFormCount = 0
let vendorTagCount = 0
@@ -277,7 +251,7 @@ export async function getFormStatusByVendor(projectId: number, formCode: string)
.from(forms)
.where(
and(
- inArray(forms.contractItemId, contractItemIds),
+ eq(forms.contractItemId, contractItemId),
eq(forms.formCode, formCode)
)
)
@@ -294,28 +268,16 @@ export async function getFormStatusByVendor(projectId: number, formCode: string)
.from(formEntries)
.where(
and(
- inArray(formEntries.contractItemId, contractItemIds),
+ eq(formEntries.contractItemId, contractItemId),
eq(formEntries.formCode, formCode)
)
)
// 6. TAG별 편집 가능 필드 조회
- const editableFieldsByTag = new Map<string, string[]>()
-
- for (const contractItemId of contractItemIds) {
- const tagFields = await getEditableFieldsByTag(contractItemId, projectId)
-
- tagFields.forEach((fields, tagNo) => {
- if (!editableFieldsByTag.has(tagNo)) {
- editableFieldsByTag.set(tagNo, fields)
- } else {
- const existingFields = editableFieldsByTag.get(tagNo) || []
- const mergedFields = [...new Set([...existingFields, ...fields])]
- editableFieldsByTag.set(tagNo, mergedFields)
- }
- })
- }
+ const editableFieldsByTag = await getEditableFieldsByTag(contractItemId, projectId)
+ const vendorStatusList: VendorFormStatus[] = []
+
for (const entry of entriesList) {
const metaResult = await db
.select({
diff --git a/lib/pq/service.ts b/lib/pq/service.ts
index 67be5398..f58a1d4d 100644
--- a/lib/pq/service.ts
+++ b/lib/pq/service.ts
@@ -94,6 +94,10 @@ export async function getPQDataByVendorId(
projectId?: number
): Promise<PQGroupData[]> {
try {
+ // 파라미터 유효성 검증
+ if (isNaN(vendorId)) {
+ throw new Error("Invalid vendorId parameter");
+ }
// 기본 쿼리 구성
const selectObj = {
criteriaId: pqCriterias.id,
@@ -1531,6 +1535,7 @@ export async function getAllPQsByVendorId(vendorId: number) {
// 특정 PQ의 상세 정보 조회 (개별 PQ 페이지용)
export async function getPQById(pqSubmissionId: number, vendorId: number) {
try {
+
const pq = await db
.select({
id: vendorPQSubmissions.id,
@@ -1543,12 +1548,15 @@ export async function getPQById(pqSubmissionId: number, vendorId: number) {
approvedAt: vendorPQSubmissions.approvedAt,
rejectedAt: vendorPQSubmissions.rejectedAt,
rejectReason: vendorPQSubmissions.rejectReason,
-
+
// 벤더 정보 (추가)
vendorName: vendors.vendorName,
vendorCode: vendors.vendorCode,
vendorStatus: vendors.status,
-
+ vendorCountry: vendors.country,
+ vendorEmail: vendors.email,
+ vendorPhone: vendors.phone,
+
// 프로젝트 정보 (조인)
projectName: projects.name,
projectCode: projects.code,
@@ -1564,11 +1572,11 @@ export async function getPQById(pqSubmissionId: number, vendorId: number) {
)
.limit(1)
.then(rows => rows[0]);
-
+
if (!pq) {
throw new Error("PQ not found or access denied");
}
-
+
return pq;
} catch (error) {
console.error("Error fetching PQ by ID:", error);
@@ -4046,11 +4054,12 @@ export async function updatePqValidToAction(input: UpdatePqValidToInput) {
}
}
+
// SHI 참석자 총 인원수 계산 함수
export async function getTotalShiAttendees(shiAttendees: Record<string, unknown> | null): Promise<number> {
if (!shiAttendees) return 0
-
+
let total = 0
Object.entries(shiAttendees).forEach(([key, value]) => {
if (value && typeof value === 'object' && 'checked' in value && 'count' in value) {
diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts
index 78d2479a..f536a142 100644
--- a/lib/rfq-last/service.ts
+++ b/lib/rfq-last/service.ts
@@ -3292,6 +3292,7 @@ async function processSingleVendor({
currentUser,
designAttachments
});
+ console.log("tbeSession 생성 완료", tbeSession);
// 이메일 발송 처리 (사용자가 선택한 경우에만)
let emailSent = null;
if (hasToSendEmail) {
diff --git a/lib/rfq-last/vendor/rfq-vendor-table.tsx b/lib/rfq-last/vendor/rfq-vendor-table.tsx
index e5c1f51e..55549a6d 100644
--- a/lib/rfq-last/vendor/rfq-vendor-table.tsx
+++ b/lib/rfq-last/vendor/rfq-vendor-table.tsx
@@ -765,11 +765,11 @@ export function RfqVendorTable({
}
const statusConfig = {
- "진행중": { variant: "default", icon: <Clock className="h-3 w-3 mr-1" />, color: "text-blue-600" },
- "검토중": { variant: "secondary", icon: <Eye className="h-3 w-3 mr-1" />, color: "text-orange-600" },
- "보류": { variant: "outline", icon: <AlertCircle className="h-3 w-3 mr-1" />, color: "text-yellow-600" },
- "완료": { variant: "success", icon: <CheckCircle className="h-3 w-3 mr-1" />, color: "text-green-600" },
- "취소": { variant: "destructive", icon: <XCircle className="h-3 w-3 mr-1" />, color: "text-red-600" },
+ "진행중": { variant: "default", icon: <Clock className="h-3 w-3 mr-1" />},
+ "검토중": { variant: "secondary", icon: <Eye className="h-3 w-3 mr-1" /> },
+ "보류": { variant: "outline", icon: <AlertCircle className="h-3 w-3 mr-1" /> },
+ "완료": { variant: "success", icon: <CheckCircle className="h-3 w-3 mr-1" /> },
+ "취소": { variant: "destructive", icon: <XCircle className="h-3 w-3 mr-1" /> },
}[status] || { variant: "outline", icon: null, color: "text-gray-600" };
return (
diff --git a/lib/tbe-last/service.ts b/lib/tbe-last/service.ts
index da0a5a4c..346576e5 100644
--- a/lib/tbe-last/service.ts
+++ b/lib/tbe-last/service.ts
@@ -1,11 +1,11 @@
// lib/tbe-last/service.ts
'use server'
-import { revalidatePath, unstable_cache } from "next/cache";
+import { revalidatePath, revalidateTag, unstable_cache } from "next/cache";
import db from "@/db/db";
import { and, desc, asc, eq, sql, or, isNull, isNotNull, ne, inArray } from "drizzle-orm";
import { tbeLastView, tbeDocumentsView } from "@/db/schema";
-import { rfqPrItems } from "@/db/schema/rfqLast";
+import { rfqPrItems, rfqsLast } from "@/db/schema/rfqLast";
import {rfqLastDetails, rfqLastTbeDocumentReviews, rfqLastTbePdftronComments, rfqLastTbeVendorDocuments,rfqLastTbeSessions } from "@/db/schema";
import { filterColumns } from "@/lib/filter-columns";
import { GetTBELastSchema } from "./validations";
@@ -320,10 +320,22 @@ export async function updateTbeEvaluation(
// 상태 업데이트
if (data.status !== undefined) {
updateData.status = data.status
-
+
// 완료 상태로 변경 시 종료일 설정
if (data.status === "완료") {
updateData.actualEndDate = new Date()
+
+ // TBE 완료 시 연결된 RFQ 상태를 "TBE 완료"로 업데이트
+ if (currentTbeSession.rfqsLastId) {
+ await db
+ .update(rfqsLast)
+ .set({
+ status: "TBE 완료",
+ updatedBy: userId,
+ updatedAt: new Date()
+ })
+ .where(eq(rfqsLast.id, currentTbeSession.rfqsLastId))
+ }
}
}
@@ -337,10 +349,11 @@ export async function updateTbeEvaluation(
// 캐시 초기화
revalidateTag(`tbe-session-${tbeSessionId}`)
revalidateTag(`tbe-sessions`)
-
+
// RFQ 관련 캐시도 초기화
if (currentTbeSession.rfqsLastId) {
revalidateTag(`rfq-${currentTbeSession.rfqsLastId}`)
+ revalidateTag(`rfqs`)
}
return {
diff --git a/lib/tbe-last/vendor/tbe-table.tsx b/lib/tbe-last/vendor/tbe-table.tsx
index d7ee0a06..48242088 100644
--- a/lib/tbe-last/vendor/tbe-table.tsx
+++ b/lib/tbe-last/vendor/tbe-table.tsx
@@ -21,6 +21,7 @@ import { VendorQADialog } from "./vendor-comment-dialog"
import { VendorDocumentsSheet } from "./vendor-documents-sheet"
import { VendorPrItemsDialog } from "./vendor-pr-items-dialog"
import { getTBEforVendor } from "../vendor-tbe-service"
+import { VendorEvaluationViewDialog } from "./vendor-evaluation-view-dialog"
interface TbeVendorTableProps {
promises: Promise<[
@@ -217,6 +218,13 @@ export function TbeVendorTable({ promises }: TbeVendorTableProps) {
onOpenChange={setPrItemsOpen}
rfqId={selectedRfqId}
/>
+ {/* Evaluation View Dialog */}
+ <VendorEvaluationViewDialog
+ open={evaluationViewOpen}
+ onOpenChange={setEvaluationViewOpen}
+ selectedSession={selectedSession}
+ sessionDetail={sessionDetail}
+ />
</>
)
} \ No newline at end of file
diff --git a/lib/vendor-document-list/ship/import-from-dolce-button.tsx b/lib/vendor-document-list/ship/import-from-dolce-button.tsx
index fe7f55c7..76d66960 100644
--- a/lib/vendor-document-list/ship/import-from-dolce-button.tsx
+++ b/lib/vendor-document-list/ship/import-from-dolce-button.tsx
@@ -1,4 +1,4 @@
-// import-from-dolce-button.tsx - 최적화된 버전
+// import-from-dolce-button.tsx - 리비전/첨부파일 포함 버전
"use client"
import * as React from "react"
@@ -223,20 +223,30 @@ export function ImportFromDOLCEButton({
}
}, [debouncedProjectIds, fetchAllImportStatus])
-
-
- // 🔥 전체 통계 메모이제이션
+ // 🔥 전체 통계 메모이제이션 - 리비전과 첨부파일 추가
const totalStats = React.useMemo(() => {
const statuses = Array.from(importStatusMap.values())
return statuses.reduce((acc, status) => ({
availableDocuments: acc.availableDocuments + (status.availableDocuments || 0),
newDocuments: acc.newDocuments + (status.newDocuments || 0),
updatedDocuments: acc.updatedDocuments + (status.updatedDocuments || 0),
+ availableRevisions: acc.availableRevisions + (status.availableRevisions || 0),
+ newRevisions: acc.newRevisions + (status.newRevisions || 0),
+ updatedRevisions: acc.updatedRevisions + (status.updatedRevisions || 0),
+ availableAttachments: acc.availableAttachments + (status.availableAttachments || 0),
+ newAttachments: acc.newAttachments + (status.newAttachments || 0),
+ updatedAttachments: acc.updatedAttachments + (status.updatedAttachments || 0),
importEnabled: acc.importEnabled || status.importEnabled
}), {
availableDocuments: 0,
newDocuments: 0,
updatedDocuments: 0,
+ availableRevisions: 0,
+ newRevisions: 0,
+ updatedRevisions: 0,
+ availableAttachments: 0,
+ newAttachments: 0,
+ updatedAttachments: 0,
importEnabled: false
})
}, [importStatusMap])
@@ -347,7 +357,14 @@ export function ImportFromDOLCEButton({
}
}, [projectIds, fetchAllImportStatus, onImportComplete, t])
- // 🔥 상태 뱃지 메모이제이션
+ // 🔥 전체 변경 사항 계산
+ const totalChanges = React.useMemo(() => {
+ return totalStats.newDocuments + totalStats.updatedDocuments +
+ totalStats.newRevisions + totalStats.updatedRevisions +
+ totalStats.newAttachments + totalStats.updatedAttachments
+ }, [totalStats])
+
+ // 🔥 상태 뱃지 메모이제이션 - 리비전과 첨부파일 포함
const statusBadge = React.useMemo(() => {
if (loadingVendorProjects) {
return <Badge variant="secondary">{t('dolceImport.status.loadingProjectInfo')}</Badge>
@@ -365,7 +382,7 @@ export function ImportFromDOLCEButton({
return <Badge variant="secondary">{t('dolceImport.status.importDisabled')}</Badge>
}
- if (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) {
+ if (totalChanges > 0) {
return (
<Badge variant="samsung" className="gap-1">
<AlertTriangle className="w-3 h-3" />
@@ -380,10 +397,10 @@ export function ImportFromDOLCEButton({
{t('dolceImport.status.synchronized')}
</Badge>
)
- }, [loadingVendorProjects, statusLoading, importStatusMap.size, totalStats, projectIds.length, t])
+ }, [loadingVendorProjects, statusLoading, importStatusMap.size, totalStats.importEnabled, totalChanges, projectIds.length, t])
- const canImport = totalStats.importEnabled &&
- (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0)
+ // 🔥 가져오기 가능 여부 - 리비전과 첨부파일도 체크
+ const canImport = totalStats.importEnabled && totalChanges > 0
// 🔥 새로고침 핸들러 최적화
const handleRefresh = React.useCallback(() => {
@@ -391,24 +408,20 @@ export function ImportFromDOLCEButton({
fetchAllImportStatus()
}, [fetchAllImportStatus])
-
- // 🔥 자동 동기화 실행 (기존 useEffect들 다음에 추가)
- React.useEffect(() => {
+ // 🔥 자동 동기화 실행 (기존 useEffect들 다음에 추가)
+ React.useEffect(() => {
// 조건: 가져오기 가능하고, 동기화할 항목이 있고, 현재 진행중이 아닐 때
- if (canImport &&
- (totalStats.newDocuments > 0 || totalStats.updatedDocuments > 0) &&
- !isImporting &&
- !isDialogOpen) {
+ if (canImport && totalChanges > 0 && !isImporting && !isDialogOpen) {
// 상태 로딩이 완료된 후 잠깐 대기 (사용자가 상태를 확인할 수 있도록)
const timer = setTimeout(() => {
- console.log(`🔄 자동 동기화 시작: 새 문서 ${totalStats.newDocuments}개, 업데이트 ${totalStats.updatedDocuments}개`)
+ console.log(`🔄 자동 동기화 시작: ${totalChanges}개 항목`)
// 동기화 시작 알림
toast.info(
- '새로운 문서가 발견되어 자동 동기화를 시작합니다',
+ '새로운 변경사항이 발견되어 자동 동기화를 시작합니다',
{
- description: `새 문서 ${totalStats.newDocuments}개, 업데이트 ${totalStats.updatedDocuments}개`,
+ description: `총 ${totalChanges}개 항목 (문서/리비전/첨부파일)`,
duration: 3000
}
)
@@ -424,9 +437,8 @@ export function ImportFromDOLCEButton({
return () => clearTimeout(timer)
}
- }, [canImport, totalStats.newDocuments, totalStats.updatedDocuments, isImporting, isDialogOpen, handleImport])
+ }, [canImport, totalChanges, isImporting, isDialogOpen, handleImport])
-
// 로딩 중이거나 projectIds가 없으면 버튼을 표시하지 않음
if (projectIds.length === 0) {
return null
@@ -449,12 +461,12 @@ export function ImportFromDOLCEButton({
<Download className="w-4 h-4" />
)}
<span className="hidden sm:inline">{t('dolceImport.buttons.getList')}</span>
- {totalStats.newDocuments + totalStats.updatedDocuments > 0 && (
+ {totalChanges > 0 && (
<Badge
variant="samsung"
className="h-5 w-5 p-0 text-xs flex items-center justify-center"
>
- {totalStats.newDocuments + totalStats.updatedDocuments}
+ {totalChanges}
</Badge>
)}
</Button>
@@ -493,21 +505,75 @@ export function ImportFromDOLCEButton({
<div className="space-y-3">
<Separator />
- <div className="grid grid-cols-2 gap-4 text-sm">
- <div>
- <div className="text-muted-foreground">{t('dolceImport.labels.newDocuments')}</div>
- <div className="font-medium">{totalStats.newDocuments || 0}</div>
- </div>
- <div>
- <div className="text-muted-foreground">{t('dolceImport.labels.updates')}</div>
- <div className="font-medium">{totalStats.updatedDocuments || 0}</div>
+ {/* 문서 정보 */}
+ <div className="space-y-2">
+ <div className="font-medium text-sm">{t('dolceImport.labels.documents')}</div>
+ <div className="grid grid-cols-3 gap-3 text-sm">
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.total')}</div>
+ <div className="font-medium">{totalStats.availableDocuments || 0}</div>
+ </div>
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.new')}</div>
+ <div className="font-medium text-green-600">{totalStats.newDocuments || 0}</div>
+ </div>
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.updates')}</div>
+ <div className="font-medium text-blue-600">{totalStats.updatedDocuments || 0}</div>
+ </div>
</div>
</div>
- <div className="text-sm">
- <div className="text-muted-foreground">{t('dolceImport.labels.totalDocuments')}</div>
- <div className="font-medium">{totalStats.availableDocuments || 0}</div>
- </div>
+ {/* 리비전 정보 */}
+ {(totalStats.availableRevisions > 0 || totalStats.newRevisions > 0 || totalStats.updatedRevisions > 0) && (
+ <div className="space-y-2">
+ <div className="font-medium text-sm">{t('dolceImport.labels.revisions')}</div>
+ <div className="grid grid-cols-3 gap-3 text-sm">
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.total')}</div>
+ <div className="font-medium">{totalStats.availableRevisions || 0}</div>
+ </div>
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.new')}</div>
+ <div className="font-medium text-green-600">{totalStats.newRevisions || 0}</div>
+ </div>
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.updates')}</div>
+ <div className="font-medium text-blue-600">{totalStats.updatedRevisions || 0}</div>
+ </div>
+ </div>
+ </div>
+ )}
+
+ {/* 첨부파일 정보 */}
+ {(totalStats.availableAttachments > 0 || totalStats.newAttachments > 0 || totalStats.updatedAttachments > 0) && (
+ <div className="space-y-2">
+ <div className="font-medium text-sm">{t('dolceImport.labels.attachments')}</div>
+ <div className="grid grid-cols-3 gap-3 text-sm">
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.total')}</div>
+ <div className="font-medium">{totalStats.availableAttachments || 0}</div>
+ </div>
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.new')}</div>
+ <div className="font-medium text-green-600">{totalStats.newAttachments || 0}</div>
+ </div>
+ <div>
+ <div className="text-muted-foreground">{t('dolceImport.labels.updates')}</div>
+ <div className="font-medium text-blue-600">{totalStats.updatedAttachments || 0}</div>
+ </div>
+ </div>
+ </div>
+ )}
+
+ {/* 요약 */}
+ {totalChanges > 0 && (
+ <div className="bg-muted/50 rounded-lg p-3">
+ <div className="text-sm font-medium">
+ {t('dolceImport.labels.totalChanges')}: <span className="text-primary">{totalChanges}</span>
+ </div>
+ </div>
+ )}
{/* 각 프로젝트별 세부 정보 */}
{projectIds.length > 1 && (
@@ -522,11 +588,29 @@ export function ImportFromDOLCEButton({
<div key={projectId} className="text-xs">
<div className="font-medium">{t('dolceImport.labels.projectLabel', { projectId })}</div>
{status ? (
- <div className="text-muted-foreground">
- {t('dolceImport.descriptions.projectDetails', {
- newDocuments: status.newDocuments,
- updatedDocuments: status.updatedDocuments
- })}
+ <div className="text-muted-foreground space-y-1">
+ <div>
+ {t('dolceImport.descriptions.projectDocuments', {
+ newDocuments: status.newDocuments,
+ updatedDocuments: status.updatedDocuments
+ })}
+ </div>
+ {(status.newRevisions > 0 || status.updatedRevisions > 0) && (
+ <div>
+ {t('dolceImport.descriptions.projectRevisions', {
+ newRevisions: status.newRevisions,
+ updatedRevisions: status.updatedRevisions
+ })}
+ </div>
+ )}
+ {(status.newAttachments > 0 || status.updatedAttachments > 0) && (
+ <div>
+ {t('dolceImport.descriptions.projectAttachments', {
+ newAttachments: status.newAttachments,
+ updatedAttachments: status.updatedAttachments
+ })}
+ </div>
+ )}
</div>
) : (
<div className="text-destructive">{t('dolceImport.status.statusCheckFailed')}</div>
@@ -595,14 +679,28 @@ export function ImportFromDOLCEButton({
<div className="rounded-lg border p-4 space-y-3">
<div className="flex items-center justify-between text-sm">
<span>{t('dolceImport.labels.itemsToImport')}</span>
- <span className="font-medium">
- {totalStats.newDocuments + totalStats.updatedDocuments}
- </span>
+ <span className="font-medium">{totalChanges}</span>
+ </div>
+
+ <div className="space-y-1 text-xs text-muted-foreground">
+ {totalStats.newDocuments + totalStats.updatedDocuments > 0 && (
+ <div>
+ • {t('dolceImport.labels.documents')}: {totalStats.newDocuments} {t('dolceImport.labels.new')}, {totalStats.updatedDocuments} {t('dolceImport.labels.updates')}
+ </div>
+ )}
+ {totalStats.newRevisions + totalStats.updatedRevisions > 0 && (
+ <div>
+ • {t('dolceImport.labels.revisions')}: {totalStats.newRevisions} {t('dolceImport.labels.new')}, {totalStats.updatedRevisions} {t('dolceImport.labels.updates')}
+ </div>
+ )}
+ {totalStats.newAttachments + totalStats.updatedAttachments > 0 && (
+ <div>
+ • {t('dolceImport.labels.attachments')}: {totalStats.newAttachments} {t('dolceImport.labels.new')}, {totalStats.updatedAttachments} {t('dolceImport.labels.updates')}
+ </div>
+ )}
</div>
<div className="text-xs text-muted-foreground">
- {t('dolceImport.descriptions.includesNewAndUpdated')}
- <br />
{t('dolceImport.descriptions.b4DocumentsNote')}
{projectIds.length > 1 && (
<>