"use client"; import { useState } from "react"; import dynamic from "next/dynamic"; import { Modal } from "@/components/ui/Modal"; import type { Document } from "@/types/api"; import { FileText, User, Calendar, HardDrive, FolderOpen, Users, Building2, Tag, Clock, CheckCircle, Eye, Download, ChevronRight, Activity, Maximize2, Minimize2, Info, Edit, MessageSquare, Link2, ScrollText, } from "lucide-react"; import { DocumentAcknowledgeButton, DocumentAcknowledgement } from "./DocumentAcknowledgement"; import { DocumentComments } from "./DocumentComments"; import { DocumentRelated } from "./DocumentRelated"; import { DocumentTaskLinks } from "./DocumentTaskLinks"; const DocumentPreviewPane = dynamic( () => import("./DocumentPreview").then((mod) => mod.DocumentPreviewPane), { ssr: false, loading: () => (

Загружаем предпросмотр...

), }, ); interface DocumentDetailModalProps { document: Document | null; isOpen: boolean; initialTab?: TabType; onClose: () => void; onUpdate?: () => void; onEditMetadata?: () => void; onViewReport?: () => void; onNavigateToRelated?: (docId: number) => void; } type TabType = "preview" | "info" | "activity" | "acknowledgements" | "comments" | "related"; export function DocumentDetailModal({ document, isOpen, initialTab, onClose, onUpdate, onEditMetadata, onViewReport, onNavigateToRelated, }: DocumentDetailModalProps) { const [activeTabState, setActiveTabState] = useState<{ documentId: number | null; value: TabType }>({ documentId: null, value: "preview", }); const [fullscreenState, setFullscreenState] = useState<{ documentId: number | null; value: boolean }>({ documentId: null, value: false, }); const documentId = document?.id ?? null; const documentHasFile = Boolean(document?.file_url); const documentRequiresAcknowledgement = Boolean(document?.acknowledgement_required); const acknowledgementRequiredForUser = Boolean( document?.acknowledgement_required_for_user ?? document?.acknowledgement_required, ); const showPersistentAcknowledgeAction = acknowledgementRequiredForUser && !document?.is_acknowledged; if (!document) return null; const formatDate = (date: string) => { return new Date(date).toLocaleDateString("ru-RU", { day: "numeric", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit", }); }; const formatFileSize = (bytes: number) => { if (bytes < 1024) return `${bytes} Б`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} КБ`; return `${(bytes / 1024 / 1024).toFixed(2)} МБ`; }; const tabs = [ ...(documentHasFile ? [{ id: "preview" as TabType, label: "Предпросмотр", icon: Eye }] : []), { id: "info" as TabType, label: "Информация", icon: Info }, { id: "activity" as TabType, label: "Активность", icon: Activity }, ...(documentRequiresAcknowledgement ? [{ id: "acknowledgements" as TabType, label: "Ознакомления", icon: CheckCircle }] : []), { id: "comments" as TabType, label: "Комментарии", icon: MessageSquare }, { id: "related" as TabType, label: "Связанные", icon: Link2 }, ]; const defaultTab: TabType = initialTab && tabs.some((tab) => tab.id === initialTab) ? initialTab : documentHasFile ? "preview" : "info"; const activeTab: TabType = activeTabState.documentId === documentId ? activeTabState.value : defaultTab; const currentActiveTab = tabs.some((tab) => tab.id === activeTab) ? activeTab : defaultTab; const desktopActiveTab: Exclude = currentActiveTab === "preview" ? "info" : currentActiveTab; const isFullscreen = fullscreenState.documentId === documentId ? fullscreenState.value : false; const detailTabs = tabs.filter((tab) => tab.id !== "preview"); const setDocumentActiveTab = (value: TabType) => { setActiveTabState({ documentId, value }); }; const setDocumentFullscreen = (value: boolean) => { setFullscreenState({ documentId, value }); }; const renderPreview = () => { if (!document.file_url) { return (

Файл не прикреплен

); } return ( ); }; return (
{/* Header with Title and Actions */}
{/* Title Row */}

{document.title}

{/* Quick Actions */}
{onEditMetadata && ( )} {document.file_url && ( <> Скачать )}
{/* Metadata Row - Always visible */}
{document.is_regulation && (
Регламент
)} {document.file_name && (
{document.file_name} {document.file_size && ( ({formatFileSize(document.file_size)}) )}
)}
{formatDate(document.uploaded_at || document.created_at)}
{document.uploaded_by && (
{document.uploaded_by.last_name} {document.uploaded_by.first_name}
)}
{/* Mobile Tabs Navigation */}
{/* Content Area */}
{/* Desktop Layout: Side-by-side */}
{/* Preview Panel (Left) */} {documentHasFile && (
{renderPreview()}
)} {/* Info Panel (Right) */} {!(documentHasFile && isFullscreen) && (
{/* Desktop Tabs */}
{/* Desktop Tab Content */}
{desktopActiveTab === "info" && (
{/* Description */}

Описание

{document.description || "Описание отсутствует"}

{/* Author & Dates */}

Информация

Автор

{document.uploaded_by ? `${document.uploaded_by.last_name} ${document.uploaded_by.first_name}` : "Не указан"}

Создан

{formatDate(document.uploaded_at || document.created_at)}

{document.modified_at && (

Изменен

{formatDate(document.modified_at)}

)}
{/* Folder Location */} {document.folder_path && (

Расположение

{document.folder_path.split(" / ").map((folder, index, arr) => (
{folder} {index < arr.length - 1 && ( )}
))}
)} {/* Tags */} {document.tags && document.tags.length > 0 && (

Теги

{document.tags.map((tag) => ( {tag.name} ))}
)} {/* Recipients */} {(document.sent_to_all || (document.departments && document.departments.length > 0) || (document.recipients && document.recipients.length > 0)) && (

Получатели

{document.sent_to_all && (
Все сотрудники
)} {document.departments && document.departments.length > 0 && (
Отделы ({document.departments.length})
{document.departments.map((dept) => (
{dept.name}
))}
)} {document.recipients && document.recipients.length > 0 && (
Получатели ({document.recipients.length})
{document.recipients.slice(0, 5).map((recipient) => (
{recipient.last_name} {recipient.first_name}
))} {document.recipients.length > 5 && (
... и ещё {document.recipients.length - 5}
)}
)}
)}
)} {desktopActiveTab === "activity" && (

История активности

Создан

{document.uploaded_by ? `${document.uploaded_by.last_name} ${document.uploaded_by.first_name}` : "Система"}{" "} • {formatDate(document.uploaded_at || document.created_at)}

История изменений будет доступна после интеграции с django-reversion API

)} {desktopActiveTab === "acknowledgements" && (
{document.acknowledgement_required ? ( <> {onViewReport && (

Подтверждение прочтения

)} onUpdate?.()} showAction={false} /> ) : (

Для этого документа не требуется подтверждение ознакомления

)}
)} {desktopActiveTab === "comments" && (
onUpdate?.()} />
)} {desktopActiveTab === "related" && (
onNavigateToRelated?.(docId)} />
)}
)}
{/* Mobile Layout: Tabs */}
{currentActiveTab === "preview" && (
{renderPreview()}
)} {currentActiveTab === "info" && (
{/* Description */}

Описание

{document.description || "Описание отсутствует"}

{/* Author & Dates */}

Информация

Автор

{document.uploaded_by ? `${document.uploaded_by.last_name} ${document.uploaded_by.first_name}` : "Не указан"}

Создан

{formatDate(document.uploaded_at || document.created_at)}

{document.modified_at && (

Изменен

{formatDate(document.modified_at)}

)}
{/* Folder Location */} {document.folder_path && (

Расположение

{document.folder_path.split(" / ").map((folder, index, arr) => (
{folder} {index < arr.length - 1 && ( )}
))}
)} {/* Tags */} {document.tags && document.tags.length > 0 && (

Теги

{document.tags.map((tag) => ( {tag.name} ))}
)} {/* Recipients */} {(document.sent_to_all || (document.departments && document.departments.length > 0) || (document.recipients && document.recipients.length > 0)) && (

Получатели

{document.sent_to_all && (
Все сотрудники
)} {document.departments && document.departments.length > 0 && (
Отделы ({document.departments.length})
{document.departments.map((dept) => (
{dept.name}
))}
)} {document.recipients && document.recipients.length > 0 && (
Получатели ({document.recipients.length})
{document.recipients.slice(0, 5).map((recipient) => (
{recipient.last_name} {recipient.first_name}
))} {document.recipients.length > 5 && (
... и ещё {document.recipients.length - 5}
)}
)}
)}
)} {currentActiveTab === "activity" && (

История активности

Создан

{document.uploaded_by ? `${document.uploaded_by.last_name} ${document.uploaded_by.first_name}` : "Система"}{" "} • {formatDate(document.uploaded_at || document.created_at)}

История изменений будет доступна после интеграции с django-reversion API

)} {currentActiveTab === "acknowledgements" && (
{document.acknowledgement_required ? ( <> {onViewReport && (

Подтверждение прочтения

)} onUpdate?.()} showAction={false} /> ) : (

Для этого документа не требуется подтверждение ознакомления

)}
)} {currentActiveTab === "comments" && (
onUpdate?.()} />
)} {currentActiveTab === "related" && (
onNavigateToRelated?.(docId)} />
)}
{showPersistentAcknowledgeAction && (
onUpdate?.()} className="pointer-events-auto w-full shadow-[0_12px_32px_rgba(0,0,0,0.35)] sm:w-auto sm:min-w-[220px]" />
)}
); }