Change filter sort to client side for vendors/cats

This commit is contained in:
2025-01-17 00:59:11 -05:00
parent 2235121761
commit 88d1189da0
4 changed files with 41 additions and 180 deletions

View File

@@ -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>

View File

@@ -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>