fix: restore reviews detail layout and leaudit data wiring

This commit is contained in:
wren
2026-05-06 17:31:48 +08:00
parent 63bf3f56ce
commit 796ce90e32
8 changed files with 1652 additions and 607 deletions
+278 -148
View File
@@ -45,8 +45,7 @@ import { PdfPreviewTest } from "~/components/reviews/previewComponents/PdfPrevie
import { DocxPreviewTest } from "~/components/reviews/previewComponents/DocxPreviewTest";
import { ComparePreview } from "~/components/reviews/previewComponents/ComparePreview";
import { type ReviewPoint } from '~/components/reviews';
import { messageService } from "~/components/ui/MessageModal";
import { type ReviewPoint, type PdfBboxHighlight } from '~/components/reviews';
import { loadingBarService } from "~/components/ui/LoadingBar";
/**
@@ -155,6 +154,8 @@ interface ReviewData {
aiAnalysis: AnalysisData;
}
type PreviewKind = 'pdf' | 'docx';
type PreviewDocument = {
path?: string;
attachments?: Array<{
@@ -163,10 +164,14 @@ type PreviewDocument = {
}>;
};
interface DefaultPreviewTarget {
page?: number;
highlightValue?: string;
bboxHighlight?: PdfBboxHighlight;
}
function resolvePreviewPath(document: PreviewDocument | null | undefined): string {
if (document?.path) {
return document.path;
}
if (document?.path) return document.path;
const primaryAttachment = Array.isArray(document?.attachments)
? document.attachments.find((item) => item?.fileRole === 'primary' && item?.ossUrl)
@@ -181,37 +186,124 @@ function resolvePreviewExtension(document: PreviewDocument | null | undefined):
return typeof suffix === 'string' ? suffix.toLowerCase() : '';
}
function buildReviewData(document: any, reviewPoints: ReviewPoint[], statistics: Statistics, reviewInfo: ReviewInfo): ReviewData | null {
if (!document) {
return null;
}
function isValidQuad(value: unknown): value is [number, number, number, number] {
return Array.isArray(value) && value.length === 4 && value.every(item => typeof item === 'number' && Number.isFinite(item));
}
const mockData = getMockReviewData();
const typeValue = document.type ?? document.type_id;
function hasNonZeroQuad(value: [number, number, number, number]): boolean {
return value.some(item => item !== 0);
}
function getReviewPointContentText(value: unknown): string | undefined {
if (value == null) return undefined;
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
const text = String(value).trim();
return text || undefined;
}
if (typeof value === 'object' && value && 'value' in value) {
return getReviewPointContentText((value as { value?: unknown }).value);
}
return undefined;
}
function getReviewPointFieldPage(point: ReviewPoint, fieldKey: string, rawValue: unknown): number | undefined {
const contentPage = point.contentPage?.[fieldKey];
const normalizedContentPage = Number(contentPage);
if (Number.isFinite(normalizedContentPage) && normalizedContentPage > 0) return normalizedContentPage;
const inlinePage = typeof rawValue === 'object' && rawValue && 'page' in rawValue
? Number((rawValue as { page?: unknown }).page)
: NaN;
if (Number.isFinite(inlinePage) && inlinePage > 0) return inlinePage;
const pageNum = point.fieldPositions?.[fieldKey]?.page_num;
if (typeof pageNum === 'number' && Number.isFinite(pageNum) && pageNum >= 0) return pageNum + 1;
return undefined;
}
function getReviewPointFieldBbox(point: ReviewPoint, fieldKey: string, page?: number): PdfBboxHighlight | undefined {
const fieldPosition = point.fieldPositions?.[fieldKey];
if (!fieldPosition || !isValidQuad(fieldPosition.bbox) || !isValidQuad(fieldPosition.page_box)) return undefined;
if (!hasNonZeroQuad(fieldPosition.bbox) || !hasNonZeroQuad(fieldPosition.page_box)) return undefined;
return {
fileInfo: {
fileName: document.name || "未知文件名",
path: document.path || "未知路径",
contractNumber: document.documentNumber || document.document_number || "未知编号",
fileSize: document.size ? formatFileSize(document.size) : document.file_size ? formatFileSize(document.file_size) : "未知大小",
fileFormat: document.fileType ? document.fileType.toUpperCase() : "未知格式",
pageCount: document.pageCount || document.page_count || 0,
uploadTime: document.uploadTime || document.created_at || "未知时间",
uploadUser: document.uploadUser || "未知用户",
auditStatus: document.auditStatus || 0,
legalBasis: document.legalBasis || {},
fileType: typeValue !== undefined && typeValue !== null ? String(typeValue) : ""
},
contractInfo: mockData.contractInfo,
reviewInfo,
statistics,
fileContent: mockData.fileContent,
reviewPoints,
aiAnalysis: mockData.aiAnalysis,
fieldKey,
bbox: [...fieldPosition.bbox],
pageBox: [...fieldPosition.page_box],
pageNum: fieldPosition.page_num,
page,
confidence: fieldPosition.confidence,
matchMethod: fieldPosition.match_method,
};
}
function resolveDefaultPreviewTarget(point: ReviewPoint, previewKind: PreviewKind): DefaultPreviewTarget {
let firstPageCandidate: DefaultPreviewTarget | undefined;
let firstPageWithBboxCandidate: DefaultPreviewTarget | undefined;
let firstPageWithTextCandidate: DefaultPreviewTarget | undefined;
for (const [fieldKey, rawValue] of Object.entries(point.content || {})) {
const page = getReviewPointFieldPage(point, fieldKey, rawValue);
if (!page) continue;
const highlightValue = getReviewPointContentText(rawValue);
const bboxHighlight = getReviewPointFieldBbox(point, fieldKey, page);
const candidate: DefaultPreviewTarget = { page, highlightValue, bboxHighlight };
if (!firstPageCandidate) firstPageCandidate = candidate;
if (!firstPageWithBboxCandidate && bboxHighlight) firstPageWithBboxCandidate = candidate;
if (!firstPageWithTextCandidate && highlightValue) firstPageWithTextCandidate = candidate;
}
if (previewKind === 'pdf') {
return firstPageWithBboxCandidate || firstPageCandidate || {};
}
return firstPageWithTextCandidate || firstPageCandidate || {};
}
interface ReviewsTestLoaderSuccess {
previousRoute: string;
document: any;
reviewPoints: ReviewPoint[];
reviewInfo: ReviewInfo;
statistics: Statistics;
comparison_document: any;
userInfo: { sub: string; nick_name: string } | null;
frontendJWT: string | null;
flowType: 'legacy' | 'leaudit';
detailMode: 'legacy' | 'leaudit';
}
interface ReviewsTestLoaderError {
result: false;
message: string;
previousRoute: string;
}
type ReviewsTestLoaderData = ReviewsTestLoaderSuccess | ReviewsTestLoaderError;
const EMPTY_STATISTICS: Statistics = {
total: 0,
success: 0,
warning: 0,
error: 0,
notApplicable: 0,
score: 0,
};
const EMPTY_REVIEW_INFO: ReviewInfo = {
reviewTime: '',
reviewModel: '',
ruleGroup: '',
result: '',
issueCount: 0,
};
function isReviewsTestLoaderError(data: ReviewsTestLoaderData): data is ReviewsTestLoaderError {
return 'result' in data && data.result === false;
}
export const meta: MetaFunction = () => {
return [
@@ -233,49 +325,53 @@ export const handle = {
noPadding: true
};
export async function loader({ request }: LoaderFunctionArgs) {
export async function loader({ request }: LoaderFunctionArgs): Promise<Response> {
try {
const url = new URL(request.url);
const id = url.searchParams.get('id') || undefined;
const id = url.searchParams.get('id') || '';
const previousRoute = url.searchParams.get('previousRoute') || '';
// console.log("[Reviews Loader] 开始加载,id:", id, "previousRoute:", previousRoute);
if (!id) {
console.error("[Reviews Loader] 文件ID不能为空");
return Response.json({ result: false, message: '文件ID不能为空' });
return Response.json({ result: false, message: '文件ID不能为空', previousRoute });
}
// 获取用户会话信息
const { getUserSession } = await import("~/api/login/auth.server");
const { userInfo, frontendJWT } = await getUserSession(request);
// reviewsTest 正式链路统一只走新后端聚合接口,避免旧统一接口 404 噪音。
const reviewData = await getReviewPoints_fromApi(id, request);
if ('error' in reviewData && reviewData.error) {
console.error("[Reviews Loader] 获取评查点数据错误:", reviewData.error);
return Response.json({ result: false, message: reviewData.error });
return Response.json({
result: false,
message: reviewData.error,
previousRoute,
});
}
return Response.json({
previousRoute: previousRoute,
previousRoute,
document: reviewData.document,
reviewPoints: reviewData.data,
reviewInfo: reviewData.reviewInfo,
statistics: reviewData.stats,
statistics: { ...reviewData.stats, notApplicable: reviewData.stats?.notApplicable ?? 0 },
comparison_document: reviewData.comparison_document,
userInfo,
frontendJWT,
flowType: 'legacy',
scoredResults: null,
scoredSummary: null
userInfo:
userInfo?.sub && userInfo?.nick_name
? { sub: userInfo.sub, nick_name: userInfo.nick_name }
: null,
frontendJWT: frontendJWT ?? null,
flowType: 'leaudit',
detailMode: 'leaudit',
});
} catch (error) {
console.error('[Reviews Loader] 获取评查数据失败:', error);
console.error('[Reviews Loader] 错误堆栈:', error instanceof Error ? error.stack : '无堆栈信息');
return Response.json({ result: false, message: `获取评查数据失败: ${error instanceof Error ? error.message : '未知错误'}` });
console.error('[reviewsTest loader] Failed to load review data:', error);
return Response.json({
result: false,
message: `获取评查数据失败: ${error instanceof Error ? error.message : '未知错误'}`,
previousRoute: '',
});
}
}
// 添加 action 函数处理需要用户认证的操作
export async function action({ request }: ActionFunctionArgs) {
try {
const formData = await request.formData();
@@ -346,36 +442,27 @@ export async function action({ request }: ActionFunctionArgs) {
export default function ReviewDetails() {
const navigate = useNavigate();
const loaderData = useLoaderData<typeof loader>();
const normalizedLoaderData =
loaderData &&
typeof loaderData === 'object' &&
'reviewPoints' in loaderData &&
loaderData.reviewPoints &&
typeof loaderData.reviewPoints === 'object' &&
'data' in loaderData.reviewPoints &&
'document' in loaderData.reviewPoints
? {
...loaderData,
document: (loaderData.reviewPoints as any).document,
reviewPoints: (loaderData.reviewPoints as any).data,
reviewInfo: (loaderData.reviewPoints as any).reviewInfo,
statistics: (loaderData.reviewPoints as any).stats,
comparison_document: (loaderData.reviewPoints as any).comparison_document,
scoring_proposals: (loaderData.reviewPoints as any).scoring_proposals ?? [],
}
: loaderData;
const loaderData = useLoaderData<ReviewsTestLoaderData>();
const fetcher = useFetcher();
const { document, reviewPoints, statistics, reviewInfo, comparison_document, frontendJWT } = normalizedLoaderData as any;
const fallbackReviewData = buildReviewData(document, reviewPoints, statistics, reviewInfo);
const isLoaderError = isReviewsTestLoaderError(loaderData);
const successLoaderData = isLoaderError ? null : loaderData;
const document = successLoaderData?.document ?? null;
const reviewPoints = successLoaderData?.reviewPoints ?? [];
const statistics = successLoaderData?.statistics ?? EMPTY_STATISTICS;
const reviewInfo = successLoaderData?.reviewInfo ?? EMPTY_REVIEW_INFO;
const comparison_document = successLoaderData?.comparison_document ?? null;
const detailMode = successLoaderData?.detailMode ?? 'legacy';
const currentUserInfo = successLoaderData?.userInfo ?? null;
const frontendJWT = successLoaderData?.frontendJWT ?? null;
const [isLoading, setIsLoading] = useState(false); // 已经通过loader加载了数据,不需要再显示加载状态
const [rightActiveTab, setRightActiveTab] = useState<'result' | 'fields' | 'info'>('result');
const [reviewData, setReviewData] = useState<ReviewData | null>(fallbackReviewData);
const [reviewData, setReviewData] = useState<ReviewData | null>(null);
const [activeReviewPointResultId, setActiveReviewPointResultId] = useState<string | number | null>(null);
const [targetPage, setTargetPage] = useState<number | undefined>(undefined);
const [charPositions, setCharPositions] = useState<Array<{ box: number[][], char: string, score: number }> | undefined>(undefined);
const [bboxHighlight, setBboxHighlight] = useState<PdfBboxHighlight | undefined>(undefined);
const [highlightValue, setHighlightValue] = useState<string | undefined>(undefined);
const [pendingUpdate, setPendingUpdate] = useState<{
reviewPointResultId: string;
@@ -416,32 +503,14 @@ export default function ReviewDetails() {
// console.log('评查信息:', reviewInfo);
// console.log('比对文档:', comparison_document);
// console.log('用户信息:', loaderData.userInfo);
// console.log('JWT Token (前20位):', frontendJWT?.substring(0, 20) + '...');
// console.groupEnd();
// }
// }, [loaderData, document, reviewPoints, statistics, reviewInfo, comparison_document, frontendJWT]);
// }, [loaderData, document, reviewPoints, statistics, reviewInfo, comparison_document]);
// loader 数据加载出错
useEffect(()=>{
loadingBarService.hide();
// console.log('[Reviews Component] useEffect检查loaderData:', {
// hasResultKey: Object.keys(loaderData).find(key => key === 'result'),
// resultValue: loaderData.result,
// willNavigateBack: Object.keys(loaderData).find(key => key === 'result') && !loaderData.result
// });
if(Object.keys(loaderData).find(key => key === 'result') && !loaderData.result){
messageService.show({
title: '错误',
message: loaderData.message,
type: 'error',
confirmText: '确定',
cancelText: '',
onConfirm: () => {
navigate(-1);
}
})
}
},[loaderData, navigate]);
},[loaderData]);
// 当文档 ID 变化时,清空高亮相关的状态
@@ -450,17 +519,49 @@ export default function ReviewDetails() {
setActiveReviewPointResultId(null);
setTargetPage(undefined);
setCharPositions(undefined);
setBboxHighlight(undefined);
setHighlightValue(undefined);
}
}, [document?.id]);
// 使用 loader 数据同步本地评查页状态,避免首屏空白。
// 模拟获取评查数据
useEffect(() => {
setReviewData(buildReviewData(document, reviewPoints, statistics, reviewInfo));
if (!document) return;
// 构建文件信息对象
const fileInfo = {
fileName: document.name || "未知文件名",
path: document.path || "未知路径",
contractNumber: document.documentNumber || document.document_number || "未知编号",
fileSize: document.size ? formatFileSize(document.size) : document.file_size ? formatFileSize(document.file_size) : "未知大小",
// 文件格式类型
fileFormat: document.fileType ? document.fileType.toUpperCase() : "未知格式",
pageCount: document.pageCount || document.page_count || 0,
uploadTime: document.uploadTime || document.created_at || "未知时间",
uploadUser: document.uploadUser || "未知用户",
auditStatus: document.auditStatus || 0,
legalBasis: document.legalBasis || {},
// 文件类型(1:合同,2:卷宗。。。)
fileType: document.type || document.type_id ? document.type_id.toString() : ''
};
// 创建包含真实文档数据的评查数据对象
const reviewDataObj: ReviewData = {
// 使用真实文件信息
fileInfo: fileInfo,
// 其他字段暂时使用默认值
contractInfo: getMockReviewData().contractInfo,
reviewInfo: reviewInfo,
statistics: statistics,
fileContent: getMockReviewData().fileContent,
reviewPoints: reviewPoints,
aiAnalysis: getMockReviewData().aiAnalysis,
};
setReviewData(reviewDataObj);
setIsLoading(false);
}, [document, reviewPoints, statistics, reviewInfo]);
const effectiveReviewData = reviewData ?? fallbackReviewData;
const handleTabChange = (tabKey: 'result' | 'fields' | 'info') => {
setRightActiveTab(tabKey);
@@ -468,47 +569,36 @@ export default function ReviewDetails() {
// 从左栏选择评查点
const handleRuleSelect = (id: string | number) => {
setActiveReviewPointResultId(id);
setRightActiveTab('result');
// 查找评查点并尝试跳转到其页面
const point = effectiveReviewData?.reviewPoints.find(p => p.id === id);
const point = reviewData?.reviewPoints.find(p => p.id === id);
if (point) {
console.log('跳转到评查点页面:', point);
const page = getFirstPageFromPoint(point);
if (page) setTargetPage(page);
else setTargetPage(undefined);
setCharPositions(undefined);
setHighlightValue(undefined);
console.log('选择的评查点:', point);
const previewKind: PreviewKind = previewExtension === 'docx' ? 'docx' : 'pdf';
const defaultTarget = resolveDefaultPreviewTarget(point, previewKind);
handleReviewPointSelect(
id,
defaultTarget.page,
undefined,
defaultTarget.highlightValue,
previewKind === 'pdf' ? defaultTarget.bboxHighlight : undefined,
);
return;
}
handleReviewPointSelect(id);
};
// 从评查点中提取第一个有效页码
const getFirstPageFromPoint = (point: ReviewPoint): number | undefined => {
if (point.content) {
for (const data of Object.values(point.content)) {
const page = (data as any)?.page;
if (page && Number(page) > 0) return Number(page);
}
}
if (point.contentPage) {
for (const page of Object.values(point.contentPage)) {
if (page && Number(page) > 0) return Number(page);
}
}
return undefined;
};
// 下载文件
const handleDownloadFile = async () => {
try {
const downloadUrl = `/api/pdf-proxy?path=${encodeURIComponent(document?.path || '')}`;
const downloadUrl = `/api/pdf-proxy?path=${encodeURIComponent(previewPath)}`;
const response = await axios.get(downloadUrl, { responseType: 'blob' });
const blobUrl = URL.createObjectURL(response.data);
const a = window.document.createElement('a');
a.style.display = 'none';
a.href = blobUrl;
a.download = decodeURIComponent(document?.path?.split('/').pop() || 'document');
a.download = decodeURIComponent(previewPath.split('/').pop() || 'document');
window.document.body.appendChild(a);
a.click();
setTimeout(() => {
@@ -521,17 +611,19 @@ export default function ReviewDetails() {
}
};
const handleReviewPointSelect = (reviewPointId: string | number, page?: number, charPos?: Array<{ box: number[][], char: string, score: number }>, value?: string) => {
const handleReviewPointSelect = (reviewPointId: string | number, page?: number, charPos?: Array<{ box: number[][], char: string, score: number }>, value?: string, nextBboxHighlight?: PdfBboxHighlight) => {
// 如果点击的是相同的评查点,但有page参数,先重置targetPage以确保useEffect能够触发
if (reviewPointId === activeReviewPointResultId && page) {
setTargetPage(undefined);
setCharPositions(undefined);
setBboxHighlight(undefined);
setHighlightValue(undefined);
// 使用setTimeout确保状态更新后再设置新的targetPage、charPositions和highlightValue
setTimeout(() => {
setActiveReviewPointResultId(reviewPointId);
setTargetPage(page);
setCharPositions(charPos);
setBboxHighlight(nextBboxHighlight);
setHighlightValue(value);
}, 0);
} else {
@@ -539,10 +631,16 @@ export default function ReviewDetails() {
setActiveReviewPointResultId(reviewPointId);
setTargetPage(page);
setCharPositions(charPos);
setBboxHighlight(nextBboxHighlight);
setHighlightValue(value);
}
};
// 处理AI建议替换(保留空实现,右栏详情卡片中DOCX替换需要)
const handleAiSuggestionReplace = (_searchText: string, _replaceText: string, _pageNumber: number) => {
// PDF 文件不支持替换,暂不实现
};
// 刷新评审数据
// async function refreshReviewData(documentId: string) {
// // 设置加载状态
@@ -779,7 +877,7 @@ export default function ReviewDetails() {
};
// 获取当前激活的评查点对象
const activeReviewPoint = effectiveReviewData?.reviewPoints.find(p => p.id === activeReviewPointResultId) || null;
const activeReviewPoint = reviewData?.reviewPoints.find(p => p.id === activeReviewPointResultId) || null;
// ── 模板上传相关函数 ──
const handleOpenReuploadModal = () => {
@@ -821,7 +919,7 @@ export default function ReviewDetails() {
selectedTemplateFiles[0],
(document as any).id,
(comparison_document as any)?.comparisonId,
frontendJWT || undefined
frontendJWT || undefined,
);
if (uploadResult.error) throw new Error(uploadResult.error);
toastService.success('模板文件上传成功,即将返回上一页...');
@@ -833,14 +931,6 @@ export default function ReviewDetails() {
} finally { setIsUploading(false); }
};
const formatFileSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
// ── 结构比对全页面视图 ──
if (showComparison) {
return (
@@ -849,7 +939,7 @@ export default function ReviewDetails() {
<button type="button" className="flex items-center gap-1 text-slate-600 hover:text-slate-900 text-[12.5px]" onClick={() => setShowComparison(false)}>
<i className="ri-arrow-left-line" />
</button>
<span className="font-medium text-sm text-slate-800 truncate">{effectiveReviewData?.fileInfo?.fileName}</span>
<span className="font-medium text-sm text-slate-800 truncate">{reviewData?.fileInfo?.fileName}</span>
</header>
<div className="flex-1 min-h-0 overflow-auto">
<Comparison comparison_document={comparison_document} />
@@ -858,6 +948,44 @@ export default function ReviewDetails() {
);
}
if (isLoaderError) {
return (
<div className="flex items-center justify-center min-h-screen bg-slate-50 px-6">
<div className="w-full max-w-xl rounded-xl border border-slate-200 bg-white shadow-sm p-8">
<div className="flex items-start gap-3">
<div className="w-10 h-10 rounded-full bg-amber-50 text-amber-600 flex items-center justify-center shrink-0">
<i className="ri-error-warning-line text-xl" />
</div>
<div className="min-w-0 flex-1">
<h1 className="text-lg font-semibold text-slate-900"></h1>
<p className="mt-2 text-sm text-slate-600 leading-6 break-words">
{loaderData.message || '文档不存在、当前账号无权限访问,或评查数据尚未准备完成。'}
</p>
<div className="mt-5 flex items-center gap-3">
<button
type="button"
className="inline-flex items-center gap-2 rounded-md bg-[#00684a] px-4 py-2 text-sm font-medium text-white hover:bg-[#00543c]"
onClick={() => navigate(getReturnUrl())}
>
<i className="ri-arrow-left-line" />
</button>
<button
type="button"
className="inline-flex items-center gap-2 rounded-md border border-slate-200 px-4 py-2 text-sm text-slate-600 hover:bg-slate-50"
onClick={() => window.location.reload()}
>
<i className="ri-refresh-line" />
</button>
</div>
</div>
</div>
</div>
</div>
);
}
return (
<div className="flex flex-col h-screen overflow-hidden">
{isLoading ? (
@@ -865,14 +993,14 @@ export default function ReviewDetails() {
<div className="loading-spinner"></div>
<span className="ml-3">...</span>
</div>
) : effectiveReviewData ? (
) : reviewData ? (
<main className="flex-1 min-h-0 grid grid-cols-[22%,1fr,30%] p-2">
{/* 左栏:规则目录 */}
<RulesDirectory
reviewPoints={effectiveReviewData.reviewPoints}
statistics={effectiveReviewData.statistics}
reviewPoints={reviewData.reviewPoints}
statistics={reviewData.statistics}
activeReviewPointResultId={activeReviewPointResultId}
fileName={effectiveReviewData.fileInfo.fileName}
fileName={reviewData.fileInfo.fileName}
onRuleSelect={handleRuleSelect}
onBack={() => navigate(getReturnUrl())}
/>
@@ -886,18 +1014,19 @@ export default function ReviewDetails() {
targetPage={targetPage}
charPositions={charPositions}
activeReviewPointResultId={activeReviewPointResultId}
reviewPoints={effectiveReviewData.reviewPoints}
reviewPoints={reviewData.reviewPoints}
highlightValue={highlightValue}
aiSuggestionReplace={aiSuggestionReplace}
userInfo={(normalizedLoaderData as any)?.userInfo}
userInfo={currentUserInfo || undefined}
/>
) : (
<PdfPreviewTest
filePath={previewPath}
targetPage={targetPage}
charPositions={charPositions}
bboxHighlight={bboxHighlight}
activeReviewPointResultId={activeReviewPointResultId}
reviewPoints={effectiveReviewData.reviewPoints}
reviewPoints={reviewData.reviewPoints}
/>
)}
</section>
@@ -907,15 +1036,16 @@ export default function ReviewDetails() {
activeTab={rightActiveTab}
onTabChange={handleTabChange}
activeReviewPoint={activeReviewPoint}
reviewPoints={effectiveReviewData.reviewPoints}
fileInfo={effectiveReviewData.fileInfo}
reviewInfo={effectiveReviewData.reviewInfo}
reviewPoints={reviewData.reviewPoints}
detailMode={detailMode}
fileInfo={reviewData.fileInfo}
reviewInfo={reviewData.reviewInfo}
onReviewPointSelect={handleReviewPointSelect}
onStatusChange={handleReviewPointStatusChange}
onConfirmResults={handleConfirmResults}
onDownload={handleDownloadFile}
auditStatus={document?.auditStatus}
fileFormat={effectiveReviewData.fileInfo.fileFormat}
fileFormat={reviewData.fileInfo.fileFormat}
onUploadTemplate={handleOpenReuploadModal}
onComparison={() => setShowComparison(true)}
showComparisonButton={showComparisonButton}
@@ -939,7 +1069,7 @@ export default function ReviewDetails() {
</div>
<div className="flex-1 min-h-0">
<ComparePreview
doc1Path={document?.path || ''}
doc1Path={previewPath}
doc2Path={comparison_document?.template_contract_path || ''}
/>
</div>