From 6d694efeb075086a69d88eb07725800aa86cc629 Mon Sep 17 00:00:00 2001 From: kenilkb Date: Thu, 16 Jul 2026 09:58:39 +0530 Subject: [PATCH] Feat: Add client-side dynamic thumbnail generators for DOCX and Spreadsheet assets --- .../features/assets/components/AssetCard.tsx | 72 +++++++++++++++ .../assets/components/DocxThumbnail.tsx | 68 ++++++++++++++ .../components/SpreadsheetThumbnail.tsx | 92 +++++++++++++++++++ 3 files changed, 232 insertions(+) create mode 100644 Channel-Frontend/src/features/assets/components/DocxThumbnail.tsx create mode 100644 Channel-Frontend/src/features/assets/components/SpreadsheetThumbnail.tsx diff --git a/Channel-Frontend/src/features/assets/components/AssetCard.tsx b/Channel-Frontend/src/features/assets/components/AssetCard.tsx index fb66bc3..1b8b60b 100644 --- a/Channel-Frontend/src/features/assets/components/AssetCard.tsx +++ b/Channel-Frontend/src/features/assets/components/AssetCard.tsx @@ -19,6 +19,8 @@ import type { Asset } from '../../../types/assets'; import type { User } from '../../../types/auth'; import { axiosInstance } from '../../../services/axios'; import { PdfThumbnail } from './PdfThumbnail'; +import { DocxThumbnail } from './DocxThumbnail'; +import { SpreadsheetThumbnail } from './SpreadsheetThumbnail'; const getFriendlyHostname = (urlStr: string) => { try { @@ -259,6 +261,76 @@ export const AssetCard: React.FC = ({ } /> + ) : isWord ? ( + +
+
+ PARTNER ASSET LIBRARY + DOCX +
+
+

+ {asset.title} +

+
+
+
+
+
+
+
+ PAGE 1 OF 1 + CONFIDENTIAL +
+
+ } + /> + ) : isSpreadsheet ? ( + +
+ Spreadsheet Editor + XLSX +
+
+
+
+
A
+
B
+
C
+
D
+
+
+
+
1
+
+ {asset.title} +
+
+
+
2
+
Category
+
Date
+
Size
+
Status
+
+
+
+
+
+ Sheet1 + Sheet2 +
+ READY +
+
+ } + /> ) : asset.type === 'url' ? (
{/* Browser Header Bar */} diff --git a/Channel-Frontend/src/features/assets/components/DocxThumbnail.tsx b/Channel-Frontend/src/features/assets/components/DocxThumbnail.tsx new file mode 100644 index 0000000..c68f026 --- /dev/null +++ b/Channel-Frontend/src/features/assets/components/DocxThumbnail.tsx @@ -0,0 +1,68 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { axiosInstance } from '../../../services/axios'; + +interface DocxThumbnailProps { + url: string; + fallback: React.ReactNode; +} + +export const DocxThumbnail: React.FC = ({ url, fallback }) => { + const containerRef = useRef(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + let isMounted = true; + + axiosInstance.get(url, { responseType: 'arraybuffer' }) + .then(async (res) => { + if (!isMounted) return; + try { + const { renderAsync } = await import('docx-preview'); + if (containerRef.current) { + containerRef.current.innerHTML = ''; + await renderAsync(res.data, containerRef.current); + setLoading(false); + } + } catch (err) { + console.error('[DocxThumbnail] Render error:', err); + setError(true); + setLoading(false); + } + }) + .catch(err => { + console.error('[DocxThumbnail] Fetch error:', err); + if (isMounted) { + setError(true); + setLoading(false); + } + }); + + return () => { + isMounted = false; + }; + }, [url]); + + if (error) return <>{fallback}; + + return ( +
+ {loading && ( +
+
+
+ )} +
+
+ ); +}; diff --git a/Channel-Frontend/src/features/assets/components/SpreadsheetThumbnail.tsx b/Channel-Frontend/src/features/assets/components/SpreadsheetThumbnail.tsx new file mode 100644 index 0000000..a675a66 --- /dev/null +++ b/Channel-Frontend/src/features/assets/components/SpreadsheetThumbnail.tsx @@ -0,0 +1,92 @@ +import React, { useEffect, useState } from 'react'; +import { axiosInstance } from '../../../services/axios'; +import * as XLSX from 'xlsx'; + +interface SpreadsheetThumbnailProps { + url: string; + fallback: React.ReactNode; +} + +export const SpreadsheetThumbnail: React.FC = ({ url, fallback }) => { + const [html, setHtml] = useState(''); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + let isMounted = true; + + axiosInstance.get(url, { responseType: 'arraybuffer' }) + .then(res => { + if (!isMounted) return; + try { + const workbook = XLSX.read(res.data, { type: 'array' }); + const sheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[sheetName]; + const sheetHtml = XLSX.utils.sheet_to_html(worksheet, { header: '', footer: '' }); + + if (isMounted) { + setHtml(sheetHtml); + setLoading(false); + } + } catch (err) { + console.error('[SpreadsheetThumbnail] Render error:', err); + if (isMounted) { + setError(true); + setLoading(false); + } + } + }) + .catch(err => { + console.error('[SpreadsheetThumbnail] Fetch error:', err); + if (isMounted) { + setError(true); + setLoading(false); + } + }); + + return () => { + isMounted = false; + }; + }, [url]); + + if (error) return <>{fallback}; + + return ( +
+ {loading && ( +
+
+
+ )} +
+ +
+
+
+ ); +};