Refactor: Add metadata and thumbnail scraping to External URL tab
This commit is contained in:
parent
c9ec1296a6
commit
e7f50a6667
@ -271,35 +271,58 @@ export const AssetViewerModal: React.FC<AssetViewerModalProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
) : asset.type === 'url' ? (
|
||||
<div className="text-center p-12 max-w-md space-y-4 flex flex-col justify-center items-center">
|
||||
<div className="w-16 h-16 rounded-2xl bg-ink-100 border border-ink-200 flex items-center justify-center text-ink-900 shadow-sm">
|
||||
<Globe className="w-8 h-8" />
|
||||
<div className="w-full h-full flex flex-col min-h-0 bg-ink-0">
|
||||
<div className="bg-ink-100 px-4 py-2 border-b border-ink-200 flex justify-between items-center text-xs text-ink-600 font-bold select-none shrink-0">
|
||||
<span className="flex items-center gap-1.5">
|
||||
<Globe className="w-4 h-4 text-ink-950" />
|
||||
<span className="font-sans">External Web Link Preview</span>
|
||||
</span>
|
||||
<span className="text-[10px] text-ink-400 font-mono font-normal truncate max-w-xs">{asset.url}</span>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-sm font-bold text-ink-900 font-sans">External Resource Portal</h4>
|
||||
<p className="text-xs text-ink-500 mt-2 leading-relaxed font-sans">
|
||||
This asset points to an external destination outside of the local CDN container.
|
||||
</p>
|
||||
<div className="bg-ink-100 border border-ink-200 rounded-xl px-4 py-2 text-xs font-mono text-ink-600 truncate mt-3 max-w-sm">
|
||||
{asset.url}
|
||||
<div className="flex-1 overflow-auto p-6 md:p-12 flex items-center justify-center bg-ink-50">
|
||||
<div className="bg-ink-0 border border-ink-200 rounded-2xl p-6 shadow-xl max-w-xl w-full flex flex-col items-center text-center space-y-5">
|
||||
{asset.thumbnailUrl ? (
|
||||
<div className="w-full h-48 border border-ink-100 rounded-xl overflow-hidden shadow-sm shrink-0">
|
||||
<img
|
||||
src={asset.thumbnailUrl}
|
||||
alt="Web Preview Banner"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-16 h-16 rounded-2xl bg-ink-50 border border-ink-200 flex items-center justify-center text-ink-900 shadow-sm shrink-0">
|
||||
<Globe className="w-8 h-8 text-ink-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="space-y-2 w-full">
|
||||
<h3 className="text-base font-extrabold text-ink-950 font-sans leading-snug">{asset.title}</h3>
|
||||
{asset.description ? (
|
||||
<p className="text-xs text-ink-500 font-sans leading-relaxed px-4">{asset.description}</p>
|
||||
) : (
|
||||
<p className="text-xs text-ink-400 font-sans italic leading-relaxed">No description provided for this external link.</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full pt-2 px-4">
|
||||
{(user?.role === 'ADMIN' || asset.isDownloadable || asset.downloadRequests?.[0]?.status === 'APPROVED') ? (
|
||||
<a
|
||||
href={asset.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 px-6 py-2.5 rounded-xl bg-ink-900 text-ink-0 text-xs font-bold hover:bg-ink-850 transition-all hover:scale-[1.02] active:scale-95 shadow-md w-full font-sans cursor-pointer"
|
||||
>
|
||||
<span>Open Website in New Tab</span>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
) : (
|
||||
<div className="bg-amber-50 border border-amber-200 text-amber-900 rounded-xl p-4 text-xs font-medium text-center font-sans">
|
||||
Access Restricted: You must request and receive approval from the Administrator to open this external link.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{(user?.role === 'ADMIN' || asset.isDownloadable || asset.downloadRequests?.[0]?.status === 'APPROVED') ? (
|
||||
<a
|
||||
href={asset.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center justify-center gap-2 px-6 py-2.5 rounded-lg bg-ink-900 text-ink-0 text-xs font-bold hover:bg-ink-800 transition-all shadow-sm w-full font-sans"
|
||||
>
|
||||
<span>Open Link in New Tab</span>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
) : (
|
||||
<div className="bg-amber-50 border border-amber-205 text-amber-900 rounded-xl p-4 text-xs text-center font-medium max-w-sm font-sans">
|
||||
Access Restricted: You must request and receive download approval from the Administrator to open this resource link.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : asset.type.includes('pdf') ? (
|
||||
<div className="w-full h-full flex flex-col min-h-0">
|
||||
|
||||
@ -95,6 +95,30 @@ export const UploadAssetModal: React.FC<UploadAssetModalProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const handleFetchUrlDetails = async () => {
|
||||
if (!uploadUrl) {
|
||||
error('URL Required', 'Please enter a URL first.');
|
||||
return;
|
||||
}
|
||||
if (!uploadUrl.startsWith('http')) {
|
||||
error('Invalid URL', 'Please enter a valid URL starting with http:// or https://');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsScraping(true);
|
||||
try {
|
||||
const data = await scrapeCaseStudy(uploadUrl);
|
||||
setUploadTitle(data.title);
|
||||
setThumbnailUrl(data.thumbnailUrl || '');
|
||||
success('Page Details Fetched', 'Title and banner image successfully retrieved.');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to scrape URL details', err);
|
||||
error('Failed to retrieve page details', err.response?.data?.error || 'Make sure the URL is accessible.');
|
||||
} finally {
|
||||
setIsScraping(false);
|
||||
}
|
||||
};
|
||||
|
||||
const formatBytes = (bytes: number, decimals = 2) => {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
@ -318,14 +342,29 @@ export const UploadAssetModal: React.FC<UploadAssetModalProps> = ({
|
||||
) : uploadTab === 'url' ? (
|
||||
<div>
|
||||
<label className="text-xs font-semibold text-ink-500 mb-1.5 block">External Asset URL</label>
|
||||
<input
|
||||
type="url"
|
||||
value={uploadUrl}
|
||||
onChange={(e) => setUploadUrl(e.target.value)}
|
||||
placeholder="https://example.com/partner-docs"
|
||||
required={uploadTab === 'url'}
|
||||
className="w-full bg-ink-50 border border-ink-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ink-900/10 text-ink-900 placeholder-ink-400"
|
||||
/>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="url"
|
||||
value={uploadUrl}
|
||||
onChange={(e) => setUploadUrl(e.target.value)}
|
||||
placeholder="https://example.com/partner-docs"
|
||||
required={uploadTab === 'url'}
|
||||
className="flex-1 bg-ink-50 border border-ink-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ink-900/10 text-ink-900 placeholder-ink-400"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleFetchUrlDetails}
|
||||
disabled={isScraping || !uploadUrl}
|
||||
variant="primary"
|
||||
size="sm"
|
||||
className="flex-shrink-0"
|
||||
>
|
||||
{isScraping ? 'Fetching...' : 'Fetch Details'}
|
||||
</Button>
|
||||
</div>
|
||||
<span className="text-[10px] text-ink-450 mt-1 block">
|
||||
Fetches title and thumbnail banner image from the target webpage automatically.
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user