From d932ec28e342499ce04dfdb31a9643ec8a2e224d Mon Sep 17 00:00:00 2001
From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com>
Date: Wed, 10 Dec 2025 17:20:29 +0530
Subject: [PATCH] Show friendly message for non-previewable file previews
(#16372)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Closes #15771
---
.../files/components/AttachmentRow.tsx | 29 +++------
.../files/components/DocumentViewer.tsx | 61 +++++++++++++++++++
2 files changed, 69 insertions(+), 21 deletions(-)
diff --git a/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx b/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx
index efa575d5ce..c5d7061ac2 100644
--- a/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx
+++ b/packages/twenty-front/src/modules/activities/files/components/AttachmentRow.tsx
@@ -15,7 +15,6 @@ import styled from '@emotion/styled';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
-import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
import { FileIcon } from '@/file/components/FileIcon';
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
import { IconCalendar, OverflowingTextWithTooltip } from 'twenty-ui/display';
@@ -90,10 +89,6 @@ export const AttachmentRow = ({
const { name: originalFileName, extension: attachmentFileExtension } =
getFileNameAndExtension(attachment.name);
- const fileExtension =
- attachmentFileExtension?.toLowerCase().replace('.', '') ?? '';
- const isPreviewable = PREVIEWABLE_EXTENSIONS.includes(fileExtension);
-
const [attachmentFileName, setAttachmentFileName] =
useState(originalFileName);
@@ -180,22 +175,14 @@ export const AttachmentRow = ({
/>
) : (
- {isPreviewable ? (
-
-
-
- ) : (
-
-
-
- )}
+
+
+
)}
diff --git a/packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx b/packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx
index e761accfb6..f97b52d678 100644
--- a/packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx
+++ b/packages/twenty-front/src/modules/activities/files/components/DocumentViewer.tsx
@@ -1,5 +1,7 @@
import { PREVIEWABLE_EXTENSIONS } from '@/activities/files/const/previewable-extensions.const';
+import { downloadFile } from '@/activities/files/utils/downloadFile';
import { fetchCsvPreview } from '@/activities/files/utils/fetchCsvPreview';
+import { getFileType } from '@/activities/files/utils/getFileType';
import DocViewer, { DocViewerRenderers } from '@cyntler/react-doc-viewer';
import '@cyntler/react-doc-viewer/dist/index.css';
import { useTheme } from '@emotion/react';
@@ -7,6 +9,8 @@ import styled from '@emotion/styled';
import { Trans } from '@lingui/react/macro';
import { useEffect, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
+import { IconDownload } from 'twenty-ui/display';
+import { Button } from 'twenty-ui/input';
import { getFileNameAndExtension } from '~/utils/file/getFileNameAndExtension';
const StyledDocumentViewerContainer = styled.div`
@@ -37,6 +41,29 @@ const StyledDocumentViewerContainer = styled.div`
}
`;
+const StyledUnavailablePreviewContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ gap: ${({ theme }) => theme.spacing(4)};
+ padding: ${({ theme }) => theme.spacing(8)};
+ text-align: center;
+`;
+
+const StyledMessage = styled.div`
+ color: ${({ theme }) => theme.font.color.secondary};
+ font-size: ${({ theme }) => theme.font.size.lg};
+ max-width: 400px;
+`;
+
+const StyledTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.primary};
+ font-size: ${({ theme }) => theme.font.size.xl};
+ font-weight: ${({ theme }) => theme.font.weight.semiBold};
+`;
+
type DocumentViewerProps = {
documentName: string;
documentUrl: string;
@@ -77,6 +104,9 @@ export const DocumentViewer = ({
const { extension } = getFileNameAndExtension(documentName);
const fileExtension = extension?.toLowerCase().replace('.', '') ?? '';
+ const fileCategory = getFileType(documentName);
+ const isPreviewable = PREVIEWABLE_EXTENSIONS.includes(fileExtension);
+
const mimeType = PREVIEWABLE_EXTENSIONS.includes(fileExtension)
? MIME_TYPE_MAPPING[fileExtension]
: undefined;
@@ -89,6 +119,37 @@ export const DocumentViewer = ({
}
}, [documentUrl, fileExtension]);
+ if (!isPreviewable) {
+ return (
+
+
+
+ Preview Not Available
+
+
+ {fileCategory === 'ARCHIVE' ? (
+
+ Archive files cannot be previewed. Please download the file to
+ access its contents.
+
+ ) : (
+
+ This file type cannot be previewed. Please download the file to
+ view it.
+
+ )}
+
+
+
+ );
+ }
+
if (fileExtension === 'csv' && !isDefined(csvPreview))
return (