diff --git a/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx b/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx index 266ae09..d3b3a81 100644 --- a/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx +++ b/Channel-Frontend/src/features/assets/components/AssetViewerModal.tsx @@ -1,8 +1,7 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { Minimize2, Maximize2, Globe, ExternalLink, FileText, File, Download, Compass, Sparkles } from 'lucide-react'; import { axiosInstance } from '../../../services/axios'; -import { getFullAssetUrl } from '../../../utils/asset-url'; import type { Asset } from '../../../types/assets'; import type { User } from '../../../types/auth'; import Modal from '../../../components/ui/Modal'; diff --git a/Channel-Frontend/src/pages/AssetsPage.tsx b/Channel-Frontend/src/pages/AssetsPage.tsx index 338cbf7..4c0e037 100644 --- a/Channel-Frontend/src/pages/AssetsPage.tsx +++ b/Channel-Frontend/src/pages/AssetsPage.tsx @@ -1,10 +1,10 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useMemo } from "react"; import { useLocation } from "react-router-dom"; import { motion } from "framer-motion"; import type { Variants } from "framer-motion"; import { UploadCloud, Search, File, Share2, Folder, Sparkles, Filter, LayoutGrid, - List, ArrowUpDown, Shield, X + List, ArrowUpDown, Shield, X, ChevronDown, Check } from "lucide-react"; import { useAuthStore } from "../hooks/use-auth"; import { axiosInstance } from "../services/axios"; @@ -74,6 +74,7 @@ export const AssetsPage = () => { // Search, Sort & Filter State const [searchQuery, setSearchQuery] = useState(""); const [selectedCategory, setSelectedCategory] = useState("ALL"); + const [isSortOpen, setIsSortOpen] = useState(false); const [sortBy, setSortBy] = useState<'newest' | 'oldest' | 'title_asc' | 'title_desc' | 'type'>('newest'); const [filters, setFilters] = useState({}); @@ -301,13 +302,29 @@ export const AssetsPage = () => { } }; - // Client-side filtering fallback - const filteredAssets = assets.filter((asset) => { - if (selectedCategory === "RECOMMENDED") { - return recommendedAssets.some((r) => r.id === asset.id); + // Instant Client-side filtering & sorting + const filteredAssets = useMemo(() => { + let result = assets.filter((asset) => { + if (selectedCategory === "RECOMMENDED") { + return recommendedAssets.some((r) => r.id === asset.id); + } + return true; + }); + + const sorted = [...result]; + if (sortBy === 'oldest') { + sorted.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()); + } else if (sortBy === 'newest') { + sorted.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + } else if (sortBy === 'title_asc') { + sorted.sort((a, b) => a.title.localeCompare(b.title)); + } else if (sortBy === 'title_desc') { + sorted.sort((a, b) => b.title.localeCompare(a.title)); + } else if (sortBy === 'type') { + sorted.sort((a, b) => (a.type || '').localeCompare(b.type || '')); } - return true; - }); + return sorted; + }, [assets, selectedCategory, recommendedAssets, sortBy]); const activeFilterCount = (filters.verticalIds?.length || 0) + @@ -346,20 +363,55 @@ export const AssetsPage = () => { /> - {/* Sorting Dropdown */} -
- - + + + {sortBy === 'newest' && 'Newest First'} + {sortBy === 'oldest' && 'Oldest First'} + {sortBy === 'title_asc' && 'Title A - Z'} + {sortBy === 'title_desc' && 'Title Z - A'} + {sortBy === 'type' && 'Format / Type'} + + + + + {isSortOpen && ( + <> +
setIsSortOpen(false)} /> +
+ {[ + { id: 'newest', label: 'Newest First' }, + { id: 'oldest', label: 'Oldest First' }, + { id: 'title_asc', label: 'Title A - Z' }, + { id: 'title_desc', label: 'Title Z - A' }, + { id: 'type', label: 'Format / Type' }, + ].map((opt) => ( + + ))} +
+ + )}
{/* View Density Switcher */}