Deployable_3
This commit is contained in:
parent
d79b691167
commit
7136741686
@ -1,8 +1,7 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Minimize2, Maximize2, Globe, ExternalLink, FileText, File, Download, Compass, Sparkles } from 'lucide-react';
|
import { Minimize2, Maximize2, Globe, ExternalLink, FileText, File, Download, Compass, Sparkles } from 'lucide-react';
|
||||||
import { axiosInstance } from '../../../services/axios';
|
import { axiosInstance } from '../../../services/axios';
|
||||||
import { getFullAssetUrl } from '../../../utils/asset-url';
|
|
||||||
import type { Asset } from '../../../types/assets';
|
import type { Asset } from '../../../types/assets';
|
||||||
import type { User } from '../../../types/auth';
|
import type { User } from '../../../types/auth';
|
||||||
import Modal from '../../../components/ui/Modal';
|
import Modal from '../../../components/ui/Modal';
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
import { useLocation } from "react-router-dom";
|
import { useLocation } from "react-router-dom";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import type { Variants } from "framer-motion";
|
import type { Variants } from "framer-motion";
|
||||||
import {
|
import {
|
||||||
UploadCloud, Search, File, Share2, Folder, Sparkles, Filter, LayoutGrid,
|
UploadCloud, Search, File, Share2, Folder, Sparkles, Filter, LayoutGrid,
|
||||||
List, ArrowUpDown, Shield, X
|
List, ArrowUpDown, Shield, X, ChevronDown, Check
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useAuthStore } from "../hooks/use-auth";
|
import { useAuthStore } from "../hooks/use-auth";
|
||||||
import { axiosInstance } from "../services/axios";
|
import { axiosInstance } from "../services/axios";
|
||||||
@ -74,6 +74,7 @@ export const AssetsPage = () => {
|
|||||||
// Search, Sort & Filter State
|
// Search, Sort & Filter State
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string>("ALL");
|
const [selectedCategory, setSelectedCategory] = useState<string>("ALL");
|
||||||
|
const [isSortOpen, setIsSortOpen] = useState(false);
|
||||||
const [sortBy, setSortBy] = useState<'newest' | 'oldest' | 'title_asc' | 'title_desc' | 'type'>('newest');
|
const [sortBy, setSortBy] = useState<'newest' | 'oldest' | 'title_asc' | 'title_desc' | 'type'>('newest');
|
||||||
const [filters, setFilters] = useState<AssetQueryFilters>({});
|
const [filters, setFilters] = useState<AssetQueryFilters>({});
|
||||||
|
|
||||||
@ -301,13 +302,29 @@ export const AssetsPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Client-side filtering fallback
|
// Instant Client-side filtering & sorting
|
||||||
const filteredAssets = assets.filter((asset) => {
|
const filteredAssets = useMemo(() => {
|
||||||
if (selectedCategory === "RECOMMENDED") {
|
let result = assets.filter((asset) => {
|
||||||
return recommendedAssets.some((r) => r.id === asset.id);
|
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 =
|
const activeFilterCount =
|
||||||
(filters.verticalIds?.length || 0) +
|
(filters.verticalIds?.length || 0) +
|
||||||
@ -346,20 +363,55 @@ export const AssetsPage = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sorting Dropdown */}
|
{/* Premium Custom Sorting Dropdown */}
|
||||||
<div className="flex items-center gap-1.5 bg-slate-50 dark:bg-slate-800 px-2.5 py-1.5 rounded-lg border border-slate-200 dark:border-slate-700 shrink-0">
|
<div className="relative shrink-0">
|
||||||
<ArrowUpDown className="w-3.5 h-3.5 text-slate-400" />
|
<button
|
||||||
<select
|
type="button"
|
||||||
value={sortBy}
|
onClick={() => setIsSortOpen(!isSortOpen)}
|
||||||
onChange={(e) => setSortBy(e.target.value as any)}
|
className="flex items-center gap-2 bg-slate-50 dark:bg-slate-800 px-3 py-2 rounded-lg border border-slate-200 dark:border-slate-700 text-xs font-bold text-slate-800 dark:text-slate-200 hover:border-slate-400 dark:hover:border-slate-600 transition-all cursor-pointer shadow-xs"
|
||||||
className="bg-transparent text-xs font-semibold text-slate-700 dark:text-slate-300 outline-none cursor-pointer"
|
|
||||||
>
|
>
|
||||||
<option value="newest">Newest First</option>
|
<ArrowUpDown className="w-3.5 h-3.5 text-amber-500" />
|
||||||
<option value="oldest">Oldest First</option>
|
<span>
|
||||||
<option value="title_asc">Title A-Z</option>
|
{sortBy === 'newest' && 'Newest First'}
|
||||||
<option value="title_desc">Title Z-A</option>
|
{sortBy === 'oldest' && 'Oldest First'}
|
||||||
<option value="type">Format / Type</option>
|
{sortBy === 'title_asc' && 'Title A - Z'}
|
||||||
</select>
|
{sortBy === 'title_desc' && 'Title Z - A'}
|
||||||
|
{sortBy === 'type' && 'Format / Type'}
|
||||||
|
</span>
|
||||||
|
<ChevronDown className={`w-3.5 h-3.5 text-slate-400 transition-transform duration-200 ${isSortOpen ? 'rotate-180' : ''}`} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{isSortOpen && (
|
||||||
|
<>
|
||||||
|
<div className="fixed inset-0 z-20" onClick={() => setIsSortOpen(false)} />
|
||||||
|
<div className="absolute right-0 mt-1.5 w-44 bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-xl z-30 py-1 overflow-hidden backdrop-blur-md">
|
||||||
|
{[
|
||||||
|
{ 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) => (
|
||||||
|
<button
|
||||||
|
key={opt.id}
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
setSortBy(opt.id as any);
|
||||||
|
setIsSortOpen(false);
|
||||||
|
}}
|
||||||
|
className={`w-full text-left px-3.5 py-2 text-xs font-semibold flex items-center justify-between transition-colors cursor-pointer ${
|
||||||
|
sortBy === opt.id
|
||||||
|
? 'bg-amber-500/10 text-amber-500 font-bold dark:bg-amber-500/20'
|
||||||
|
: 'text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-800'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span>{opt.label}</span>
|
||||||
|
{sortBy === opt.id && <Check className="w-3.5 h-3.5 text-amber-500" />}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* View Density Switcher */}
|
{/* View Density Switcher */}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user