diff --git a/Channel-Frontend/src/features/assets/components/AssetCard.tsx b/Channel-Frontend/src/features/assets/components/AssetCard.tsx index 84277de..3963b3f 100644 --- a/Channel-Frontend/src/features/assets/components/AssetCard.tsx +++ b/Channel-Frontend/src/features/assets/components/AssetCard.tsx @@ -17,6 +17,7 @@ import { } from 'lucide-react'; import type { Asset } from '../../../types/assets'; import type { User } from '../../../types/auth'; +import { axiosInstance } from '../../../services/axios'; interface AssetCardProps { asset: Asset; @@ -69,102 +70,229 @@ export const AssetCard: React.FC = ({ return File; }; - const isRenderable = (type: string) => { - return type === 'url' || type.includes('pdf') || type.includes('image') || type.includes('png') || type.includes('jpg'); + const isRenderable = (type: string, url: string) => { + const isOffice = type.includes('word') || type.includes('presentation') || type.includes('sheet') || + url.toLowerCase().endsWith('.docx') || url.toLowerCase().endsWith('.doc') || + url.toLowerCase().endsWith('.pptx') || url.toLowerCase().endsWith('.ppt') || + url.toLowerCase().endsWith('.xlsx') || url.toLowerCase().endsWith('.xls'); + return type === 'url' || type.includes('pdf') || type.includes('image') || type.includes('png') || type.includes('jpg') || isOffice; + }; + + const getFullAssetUrl = (url: string) => { + let resolvedUrl = url; + if (!url.startsWith('http')) { + const backendBase = axiosInstance.defaults.baseURL || '/api/v1'; + const relativeHost = backendBase.replace('/api/v1', ''); + resolvedUrl = relativeHost.startsWith('/') || relativeHost === '' + ? `${window.location.origin}${relativeHost}${url}` + : `${relativeHost}${url}`; + } + + const currentOrigin = window.location.origin; + const isCurrentOriginLocal = currentOrigin.includes('localhost') || currentOrigin.includes('127.0.0.1'); + if (!isCurrentOriginLocal && (resolvedUrl.includes('localhost') || resolvedUrl.includes('127.0.0.1'))) { + resolvedUrl = resolvedUrl.replace(/https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?/, currentOrigin); + } + + return resolvedUrl; }; const Icon = getAssetIcon(asset.type); + const isImage = asset.type.includes('image') || asset.type.includes('png') || asset.type.includes('jpg') || asset.url.match(/\.(png|jpe?g|gif|svg|webp)$/i); + const isPdf = asset.type.includes('pdf') || asset.url.toLowerCase().endsWith('.pdf'); + const isWord = asset.type.includes('word') || asset.url.toLowerCase().endsWith('.docx') || asset.url.toLowerCase().endsWith('.doc'); + const isPresentation = asset.type.includes('presentation') || asset.url.toLowerCase().endsWith('.pptx') || asset.url.toLowerCase().endsWith('.ppt'); + const isSpreadsheet = asset.type.includes('sheet') || asset.url.toLowerCase().endsWith('.xlsx') || asset.url.toLowerCase().endsWith('.xls') || asset.url.toLowerCase().endsWith('.csv'); + return (
-
-
+ {/* Visual Thumbnail Area */} +
+ {/* Absolute overlays for controls */} +
{user?.role === 'ADMIN' && onToggleSelect && ( onToggleSelect(asset.id)} - className="w-3.5 h-3.5 rounded border-ink-300 text-ink-900 focus:ring-ink-900/10 cursor-pointer mr-1" + className="w-3.5 h-3.5 rounded border-ink-300 bg-ink-0 text-ink-900 focus:ring-ink-900/10 cursor-pointer shadow-sm" /> )} -
- -
- -
- {isRenderable(asset.type) && ( + +
+ {isRenderable(asset.type, asset.url) && ( )} - - - {isMenuOpen && ( - <> -
setActiveMenuId(null)} /> -
- - {user?.role === 'ADMIN' && ( - <> - - - - - )} +
+ + + {isMenuOpen && ( + <> +
setActiveMenuId(null)} /> +
+ + {user?.role === 'ADMIN' && ( + <> + + + + + )} +
+ + )} +
+
+ + {/* Thumbnail Preview Area Content */} + {isImage ? ( + {asset.title} { + e.currentTarget.style.display = 'none'; + const placeholder = document.getElementById(`fallback-${asset.id}`); + if (placeholder) placeholder.style.display = 'flex'; + }} + /> + ) : null} + + {/* Fallbacks / simulated thumbnails */} +
+ {isPdf ? ( +
+
+
+ PDF +
+
+
+
+
+
+
+ PDF + +
- +
+ ) : isPresentation ? ( +
+
+
+
+
+
+
+
+ SLIDE + PPTX +
+
+
+ ) : isWord ? ( +
+
+
+ DOC +
+
+
+
+
+
+
+ WORD + +
+
+
+ ) : isSpreadsheet ? ( +
+
+
+
+
+
+
+
+
+
+
+ SHEET + XLSX +
+
+
+ ) : ( +
+
+ +
+ {asset.type === 'url' ? 'LINK' : 'FILE'} +
+
+
)}
- +
@@ -241,3 +369,4 @@ export const AssetCard: React.FC = ({
); }; + diff --git a/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx b/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx index 2e0344b..363215b 100644 --- a/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx +++ b/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx @@ -26,14 +26,30 @@ export const AssetViewerModal: React.FC = ({ const [isLoadingText, setIsLoadingText] = useState(false); const [textPreviewContent, setTextPreviewContent] = useState(''); - const isLocalUrl = (url: string) => { - return url.includes('localhost') || url.includes('127.0.0.1'); + const getFullAssetUrl = (url: string) => { + let resolvedUrl = url; + if (!url.startsWith('http')) { + const backendBase = axiosInstance.defaults.baseURL || '/api/v1'; + const relativeHost = backendBase.replace('/api/v1', ''); + resolvedUrl = relativeHost.startsWith('/') || relativeHost === '' + ? `${window.location.origin}${relativeHost}${url}` + : `${relativeHost}${url}`; + } + + // If current origin is a public domain (like ngrok) but the resolved URL is local, + // rewrite the local URL host to match the public origin so that external viewers can fetch it. + const currentOrigin = window.location.origin; + const isCurrentOriginLocal = currentOrigin.includes('localhost') || currentOrigin.includes('127.0.0.1'); + if (!isCurrentOriginLocal && (resolvedUrl.includes('localhost') || resolvedUrl.includes('127.0.0.1'))) { + resolvedUrl = resolvedUrl.replace(/https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?/, currentOrigin); + } + + return resolvedUrl; }; - const getFullAssetUrl = (url: string) => { - return url.startsWith('http') - ? url - : `${axiosInstance.defaults.baseURL?.replace('/api/v1', '')}${url}`; + const isLocalUrl = (url: string) => { + const resolved = getFullAssetUrl(url); + return resolved.includes('localhost') || resolved.includes('127.0.0.1'); }; const getGithubRawUrl = (url: string) => {