Change filter sort to client side for vendors/cats
This commit is contained in:
@@ -45,7 +45,9 @@ export function Categories() {
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ["categories"],
|
||||
queryFn: async () => {
|
||||
const response = await fetch(`${config.apiUrl}/categories`);
|
||||
const response = await fetch(`${config.apiUrl}/categories`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) throw new Error("Failed to fetch categories");
|
||||
return response.json();
|
||||
},
|
||||
@@ -109,9 +111,11 @@ export function Categories() {
|
||||
}, [data?.categories, filters, sortColumn, sortDirection]);
|
||||
|
||||
// Calculate pagination
|
||||
const totalPages = Math.ceil(filteredData.length / 50);
|
||||
const paginatedData = useMemo(() => {
|
||||
const startIndex = (page - 1) * 50;
|
||||
return filteredData.slice(startIndex, startIndex + 50);
|
||||
const start = (page - 1) * 50;
|
||||
const end = start + 50;
|
||||
return filteredData.slice(start, end);
|
||||
}, [filteredData, page]);
|
||||
|
||||
// Calculate stats from filtered data
|
||||
@@ -327,7 +331,7 @@ export function Categories() {
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{filteredData.length > 0 && (
|
||||
{totalPages > 1 && (
|
||||
<motion.div
|
||||
layout="position"
|
||||
transition={{ duration: 0.15 }}
|
||||
@@ -345,7 +349,7 @@ export function Categories() {
|
||||
aria-disabled={page === 1}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{Array.from({ length: Math.ceil(filteredData.length / 50) }, (_, i) => (
|
||||
{Array.from({ length: totalPages }, (_, i) => (
|
||||
<PaginationItem key={i + 1}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
@@ -364,9 +368,9 @@ export function Categories() {
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (page < Math.ceil(filteredData.length / 50)) setPage(p => p + 1);
|
||||
if (page < totalPages) setPage(p => p + 1);
|
||||
}}
|
||||
aria-disabled={page >= Math.ceil(filteredData.length / 50)}
|
||||
aria-disabled={page >= totalPages}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination";
|
||||
import { motion } from "motion/react";
|
||||
import { motion } from "framer-motion";
|
||||
import config from "../config";
|
||||
|
||||
interface Vendor {
|
||||
@@ -28,6 +28,8 @@ interface VendorFilters {
|
||||
performance: string;
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE = 50;
|
||||
|
||||
export function Vendors() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [sortColumn, setSortColumn] = useState<keyof Vendor>("name");
|
||||
@@ -37,24 +39,11 @@ export function Vendors() {
|
||||
status: "all",
|
||||
performance: "all",
|
||||
});
|
||||
const [] = useState({
|
||||
column: 'name',
|
||||
direction: 'asc'
|
||||
});
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ["vendors", page, filters, sortColumn, sortDirection],
|
||||
queryKey: ["vendors"],
|
||||
queryFn: async () => {
|
||||
const params = new URLSearchParams({
|
||||
page: page.toString(),
|
||||
limit: '50',
|
||||
search: filters.search,
|
||||
status: filters.status,
|
||||
performance: filters.performance,
|
||||
sortColumn,
|
||||
sortDirection
|
||||
});
|
||||
const response = await fetch(`${config.apiUrl}/vendors?${params}`, {
|
||||
const response = await fetch(`${config.apiUrl}/vendors`, {
|
||||
credentials: 'include'
|
||||
});
|
||||
if (!response.ok) throw new Error("Failed to fetch vendors");
|
||||
@@ -115,16 +104,12 @@ export function Vendors() {
|
||||
}, [data?.vendors, filters, sortColumn, sortDirection]);
|
||||
|
||||
// Calculate pagination
|
||||
const totalPages = Math.ceil(filteredData.length / ITEMS_PER_PAGE);
|
||||
const paginatedData = useMemo(() => {
|
||||
if (!data?.vendors) return [];
|
||||
return data.vendors;
|
||||
}, [data?.vendors]);
|
||||
|
||||
// Calculate stats from filtered data
|
||||
const stats = useMemo(() => {
|
||||
if (!data?.stats) return null;
|
||||
return data.stats;
|
||||
}, [data?.stats]);
|
||||
const start = (page - 1) * ITEMS_PER_PAGE;
|
||||
const end = start + ITEMS_PER_PAGE;
|
||||
return filteredData.slice(start, end);
|
||||
}, [filteredData, page]);
|
||||
|
||||
const handleSort = (column: keyof Vendor) => {
|
||||
setSortDirection(prev => {
|
||||
@@ -147,7 +132,7 @@ export function Vendors() {
|
||||
transition={{
|
||||
layout: {
|
||||
duration: 0.15,
|
||||
ease: [0.4, 0, 0.2, 1] // Material Design easing
|
||||
ease: [0.4, 0, 0.2, 1]
|
||||
}
|
||||
}}
|
||||
className="container mx-auto py-6 space-y-4"
|
||||
@@ -173,9 +158,9 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">Total Vendors</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{stats?.totalVendors ?? "..."}</div>
|
||||
<div className="text-2xl font-bold">{data?.stats?.totalVendors ?? "..."}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{stats?.activeVendors ?? "..."} active
|
||||
{data?.stats?.activeVendors ?? "..."} active
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -186,10 +171,10 @@ export function Vendors() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
${typeof stats?.totalSpend === 'number' ? stats.totalSpend.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 }) : "..."}
|
||||
${typeof data?.stats?.totalSpend === 'number' ? data.stats.totalSpend.toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 }) : "..."}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Avg unit cost: ${typeof stats?.avgUnitCost === 'number' ? stats.avgUnitCost.toFixed(2) : "..."}
|
||||
Avg unit cost: ${typeof data?.stats?.avgUnitCost === 'number' ? data.stats.avgUnitCost.toFixed(2) : "..."}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -199,9 +184,9 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">Performance</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{typeof stats?.avgFillRate === 'number' ? stats.avgFillRate.toFixed(1) : "..."}%</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgFillRate === 'number' ? data.stats.avgFillRate.toFixed(1) : "..."}%</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Fill rate / {typeof stats?.avgOnTimeDelivery === 'number' ? stats.avgOnTimeDelivery.toFixed(1) : "..."}% on-time
|
||||
Fill rate / {typeof data?.stats?.avgOnTimeDelivery === 'number' ? data.stats.avgOnTimeDelivery.toFixed(1) : "..."}% on-time
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -211,7 +196,7 @@ export function Vendors() {
|
||||
<CardTitle className="text-sm font-medium">Lead Time</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{typeof stats?.avgLeadTime === 'number' ? stats.avgLeadTime.toFixed(1) : "..."} days</div>
|
||||
<div className="text-2xl font-bold">{typeof data?.stats?.avgLeadTime === 'number' ? data.stats.avgLeadTime.toFixed(1) : "..."} days</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Average delivery time
|
||||
</p>
|
||||
@@ -312,7 +297,7 @@ export function Vendors() {
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{data?.pagination && data.pagination.total > 0 && (
|
||||
{totalPages > 1 && (
|
||||
<motion.div
|
||||
layout="position"
|
||||
transition={{ duration: 0.15 }}
|
||||
@@ -330,7 +315,7 @@ export function Vendors() {
|
||||
aria-disabled={page === 1}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{Array.from({ length: data.pagination.pages }, (_, i) => (
|
||||
{Array.from({ length: totalPages }, (_, i) => (
|
||||
<PaginationItem key={i + 1}>
|
||||
<PaginationLink
|
||||
href="#"
|
||||
@@ -349,9 +334,9 @@ export function Vendors() {
|
||||
href="#"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (page < data.pagination.pages) setPage(p => p + 1);
|
||||
if (page < totalPages) setPage(p => p + 1);
|
||||
}}
|
||||
aria-disabled={page >= data.pagination.pages}
|
||||
aria-disabled={page >= totalPages}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
|
||||
Reference in New Issue
Block a user