import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import type { Variants } from 'framer-motion'; import { UploadCloud, Search, File, CheckCircle } from 'lucide-react'; import { useAuthStore } from '../hooks/use-auth'; import { axiosInstance } from '../services/axios'; import { getAssets, getOrganizations, deleteAsset, requestDownload, approveDownloadRequest, rejectDownloadRequest, downloadAssetFile } from '../services/assets-api'; // Subcomponents import { AssetCard } from '../features/assets/components/AssetCard'; import { UploadAssetModal } from '../features/assets/components/UploadAssetModal'; import { EditAssetModal } from '../features/assets/components/EditAssetModal'; import { ShareAssetModal } from '../features/assets/components/ShareAssetModal'; import { AssetDetailsModal } from '../features/assets/components/AssetDetailsModal'; import { AssetViewerModal } from '../features/assets/components/AssetViewerModal'; import { DownloadRequestsModal } from '../features/assets/components/DownloadRequestsModal'; import { PageHeader } from '../components/ui/PageHeader'; // Type Definitions import type { Asset, Organization } from '../types/assets'; const containerVariants: Variants = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { staggerChildren: 0.05 } } }; const itemVariants: Variants = { hidden: { opacity: 0, y: 15, scale: 0.98 }, show: { opacity: 1, y: 0, scale: 1, transition: { type: 'spring', stiffness: 350, damping: 25 } } }; export const AssetsPage = () => { const user = useAuthStore((state) => state.user); const [assets, setAssets] = useState([]); const [organizations, setOrganizations] = useState([]); const [loading, setLoading] = useState(true); // Search & Filter const [searchQuery, setSearchQuery] = useState(''); const [selectedCategory, setSelectedCategory] = useState('ALL'); // Modal Control States const [isUploadOpen, setIsUploadOpen] = useState(false); const [isEditOpen, setIsEditOpen] = useState(false); const [isShareOpen, setIsShareOpen] = useState(false); const [isDetailsOpen, setIsDetailsOpen] = useState(false); const [isViewerOpen, setIsViewerOpen] = useState(false); const [isRequestsOpen, setIsRequestsOpen] = useState(false); const [activeAsset, setActiveAsset] = useState(null); const [activeMenuId, setActiveMenuId] = useState(null); useEffect(() => { fetchData(); }, []); const fetchData = async () => { setLoading(true); try { const assetsData = await getAssets(); setAssets(assetsData); if (user?.role === 'ADMIN') { const orgsData = await getOrganizations(); setOrganizations(orgsData); } } catch (err) { console.error('Failed to fetch assets data', err); } finally { setLoading(false); } }; const openEditModal = (asset: Asset) => { setActiveAsset(asset); setIsEditOpen(true); }; const openShareModal = (asset: Asset) => { setActiveAsset(asset); setIsShareOpen(true); }; const openViewerModal = (asset: Asset) => { setActiveAsset(asset); setIsViewerOpen(true); }; const openDetailsModal = (asset: Asset) => { setActiveAsset(asset); setIsDetailsOpen(true); }; const handleDeleteAsset = async (id: string) => { if (!window.confirm('Are you sure you want to permanently delete this asset?')) return; try { await deleteAsset(id); await fetchData(); } catch (err) { console.error('Failed to delete asset', err); } }; const handleRequestDownload = async (asset: Asset) => { try { await requestDownload(asset.id); await fetchData(); } catch (err) { console.error('Failed to request download access', err); } }; const handleApproveRequest = async (assetId: string, requestId: string) => { try { await approveDownloadRequest(assetId, requestId); await fetchData(); } catch (err) { console.error('Failed to approve request', err); } }; const handleRejectRequest = async (assetId: string, requestId: string) => { try { await rejectDownloadRequest(assetId, requestId); await fetchData(); } catch (err) { console.error('Failed to reject request', err); } }; const handleDownload = async (asset: Asset) => { try { await downloadAssetFile(asset.id); const downloadUrl = asset.url.startsWith('http') ? asset.url : `${axiosInstance.defaults.baseURL?.replace('/api/v1', '')}${asset.url}`; const a = document.createElement('a'); a.href = downloadUrl; a.download = asset.title; a.target = '_blank'; document.body.appendChild(a); a.click(); document.body.removeChild(a); setAssets(prev => prev.map(item => item.id === asset.id ? { ...item, downloadsCount: item.downloadsCount + 1 } : item )); } catch (err) { console.error('Failed to process download', err); } }; const CATEGORIES = ['ALL', 'Marketing', 'Presentations', 'Branding', 'Resources', 'Technical']; const filteredAssets = assets.filter(asset => { const query = searchQuery.toLowerCase().trim(); const matchesSearch = !query || asset.title.toLowerCase().includes(query) || (asset.description && asset.description.toLowerCase().includes(query)) || (asset.categoryId && asset.categoryId.toLowerCase().includes(query)) || (asset.subcategory && asset.subcategory.toLowerCase().includes(query)) || (asset.githubUrl && asset.githubUrl.toLowerCase().includes(query)) || asset.type.toLowerCase().includes(query) || asset.tags.some(tag => tag.toLowerCase().includes(query)); const matchesCategory = selectedCategory === 'ALL' || asset.categoryId === selectedCategory; return matchesSearch && matchesCategory; }); const pendingRequestsCount = assets.reduce((acc, asset) => { return acc + (asset.downloadRequests?.filter(r => r.status === 'PENDING').length || 0); }, 0); return ( Global CDN Active } actions={ <> {user?.role === 'ADMIN' && pendingRequestsCount > 0 && ( )} {user?.role === 'ADMIN' && ( )} } /> {/* Action Bar */}
setSearchQuery(e.target.value)} className="w-full bg-ink-0 border border-ink-200 rounded-lg py-1.5 pl-9 pr-4 text-xs font-semibold text-ink-900 placeholder-ink-400 outline-none transition-all focus:border-ink-900/50 focus:ring-2 ring-ink-900/10 shadow-sm hover:border-ink-300" placeholder="Search by title, desc, tag, category, URL..." />
{CATEGORIES.map((cat) => ( ))}
{/* Grid Content */} {loading ? (
) : filteredAssets.length === 0 ? (

No assets found

There are no assets matching your criteria.

) : ( {filteredAssets.map((asset) => ( ))} )} {/* Modals Container */} setIsUploadOpen(false)} onSuccess={fetchData} /> { setIsEditOpen(false); setActiveAsset(null); }} asset={activeAsset} onSuccess={fetchData} /> { setIsShareOpen(false); setActiveAsset(null); }} asset={activeAsset} organizations={organizations} onSuccess={fetchData} /> { setIsDetailsOpen(false); setActiveAsset(null); }} asset={activeAsset} userRole={user?.role} /> { setIsViewerOpen(false); setActiveAsset(null); }} asset={activeAsset} user={user} onDownload={handleDownload} /> setIsRequestsOpen(false)} assets={assets} onApprove={handleApproveRequest} onReject={handleRejectRequest} /> ); }; export default AssetsPage;