From 8ab14d79a1f463bb737b95d2de7fcff620b53464 Mon Sep 17 00:00:00 2001 From: igor_nadein Date: Sat, 28 Feb 2026 13:41:21 +0300 Subject: [PATCH] feat: Make thumbnail conditional on fullscreen mode - Changed Modal component to support render prop pattern (children as function) - Modal children can now receive isFullscreen state as parameter - Changed modal size from 'xl' to 'lg' for compact default view - Thumbnail now only shows when modal is in fullscreen mode - In normal mode: single column with metadata and 'Open file' button - In fullscreen mode: two columns with metadata (left) and thumbnail (right) - Provides progressive disclosure: compact view first, expanded view on demand --- frontend/src/app/documents/page.tsx | 595 +++++++++++++++------------ frontend/src/components/ui/Modal.tsx | 6 +- 2 files changed, 333 insertions(+), 268 deletions(-) diff --git a/frontend/src/app/documents/page.tsx b/frontend/src/app/documents/page.tsx index 727672cc..82dd3b16 100644 --- a/frontend/src/app/documents/page.tsx +++ b/frontend/src/app/documents/page.tsx @@ -658,301 +658,364 @@ export default function DocumentsPage() { isOpen={!!selectedDocument} onClose={() => setSelectedDocument(null)} title={selectedDocument?.title} - size="xl" + size="lg" showFullscreenToggle > - {selectedDocument && ( -
- {/* Left Column - Information */} -
- {/* Status Badge */} -
- -
+ {(isFullscreen) => + selectedDocument && ( +
+ {/* Left Column - Information */} +
+ {/* Status Badge */} +
+ +
- {/* Metadata Grid */} -
- {/* Author */} -
-
- -
-
-

Автор

-

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

-
-
- - {/* Created Date */} -
-
- -
-
-

Дата создания

-

- {formatDate(selectedDocument.uploaded_at || selectedDocument.created_at)} -

-
-
- - {/* File Info */} - {selectedDocument.file_name && ( -
-
- + {/* Metadata Grid */} +
+ {/* Author */} +
+
+ +
+
+

Автор

+

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

+
-
-

Файл

-

- {selectedDocument.file_name} -

+ + {/* Created Date */} +
+
+ +
+
+

Дата создания

+

+ {formatDate(selectedDocument.uploaded_at || selectedDocument.created_at)} +

+
-
- )} - {/* File Size */} - {selectedDocument.file_size && ( -
-
- -
-
-

Размер

-

- {(selectedDocument.file_size / 1024 / 1024).toFixed(2)} МБ -

-
-
- )} -
- - {/* Folder Path */} - {selectedDocument.folder_path && ( -
-
- - Расположение -
-

{selectedDocument.folder_path}

-
- )} - - {/* Description */} -
-

- - Описание -

-

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

-
- - {/* Recipients & Departments */} - {(selectedDocument.sent_to_all || - (selectedDocument.recipients && selectedDocument.recipients.length > 0) || - (selectedDocument.departments && selectedDocument.departments.length > 0)) && ( -
-

- - Получатели -

-
- {selectedDocument.sent_to_all && ( -
- - Отправлено всем сотрудникам + {/* File Info */} + {selectedDocument.file_name && ( +
+
+ +
+
+

Файл

+

+ {selectedDocument.file_name} +

+
)} - - {selectedDocument.departments && selectedDocument.departments.length > 0 && ( -
-
- - Отделы + + {/* File Size */} + {selectedDocument.file_size && ( +
+
+
-
- {selectedDocument.departments.map((dept) => ( - - {dept.name} +
+

Размер

+

+ {(selectedDocument.file_size / 1024 / 1024).toFixed(2)} МБ +

+
+
+ )} +
+ + {/* Folder Path */} + {selectedDocument.folder_path && ( +
+
+ + Расположение +
+

+ {selectedDocument.folder_path} +

+
+ )} + + {/* Description */} +
+

+ + Описание +

+

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

+
+ + {/* Recipients & Departments */} + {(selectedDocument.sent_to_all || + (selectedDocument.recipients && + selectedDocument.recipients.length > 0) || + (selectedDocument.departments && + selectedDocument.departments.length > 0)) && ( +
+

+ + Получатели +

+
+ {selectedDocument.sent_to_all && ( +
+ + + Отправлено всем сотрудникам - ))} -
-
- )} +
+ )} - {selectedDocument.recipients && selectedDocument.recipients.length > 0 && ( -
-
- - Конкретные получатели ({selectedDocument.recipients.length}) -
-
- {selectedDocument.recipients.slice(0, 10).map((recipient) => ( -
- {recipient.last_name} {recipient.first_name} -
- ))} - {selectedDocument.recipients.length > 10 && ( -
- ... и ещё {selectedDocument.recipients.length - 10} + {selectedDocument.departments && + selectedDocument.departments.length > 0 && ( +
+
+ + Отделы +
+
+ {selectedDocument.departments.map((dept) => ( + + {dept.name} + + ))} +
+
+ )} + + {selectedDocument.recipients && + selectedDocument.recipients.length > 0 && ( +
+
+ + Конкретные получатели ( + {selectedDocument.recipients.length}) +
+
+ {selectedDocument.recipients + .slice(0, 10) + .map((recipient) => ( +
+ {recipient.last_name} {recipient.first_name} +
+ ))} + {selectedDocument.recipients.length > 10 && ( +
+ ... и ещё{" "} + {selectedDocument.recipients.length - 10} +
+ )} +
)} -
- )} -
-
- )} +
+ )} - {/* Workflow Actions */} -
-

Действия

- { - loadDocuments(); - setSelectedDocument(null); - }} - /> -
+ {/* File Open Button - Show only when not fullscreen */} + {!isFullscreen && selectedDocument.file_url && ( +
+ +
+ )} - {/* Acknowledgement */} - {selectedDocument.acknowledgement_required && ( -
-
-

- Подтверждение прочтения + {/* Workflow Actions */} +
+

+ Действия

- + />
- { - loadDocuments(); - // Refresh selected document - apiClient.getDocument(selectedDocument.id).then(setSelectedDocument); - }} - /> -

- )} -
- {/* Right Column - Document Thumbnail */} -
-
- {selectedDocument.file_url && ( -
-
-

Превью документа

-
- - {/* Thumbnail Content */} -
- {(() => { - const fileExt = selectedDocument.file_name?.toLowerCase().split('.').pop() || ''; - const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'].includes(fileExt); - const isPDF = fileExt === 'pdf'; - - if (isImage) { - return ( - {selectedDocument.file_name { - // Fallback to icon on error - e.currentTarget.style.display = 'none'; - }} - /> - ); - } - - if (isPDF) { - return ( -
-