"use client"; import { useState } from "react"; import useSWR from "swr"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle, CardDescription, } from "@/components/ui/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { ClipboardCheck, Download, Search, Package, } from "lucide-react"; import { useTableSort } from "@/hooks/use-table-sort"; import { SortableTableHead } from "@/components/sortable-table-head"; import type { Customer, Product, Component } from "@/lib/types"; const fetcher = (url: string) => fetch(url).then((r) => r.json()); export default function StockTakePage() { const { data: customers = [] } = useSWR("/api/customers", fetcher); const { data: products = [] } = useSWR("/api/products", fetcher); const [source, setSource] = useState("all"); const [selectedProducts, setSelectedProducts] = useState>(new Set()); const [queryKey, setQueryKey] = useState(null); const { data: results, isLoading } = useSWR( queryKey, fetcher ); const { sorted, sort, toggleSort } = useTableSort(results ?? []); function handleGenerate() { const params = new URLSearchParams(); if (source !== "all") params.set("source", source); if (selectedProducts.size > 0) { params.set("productIds", Array.from(selectedProducts).join(",")); } setQueryKey(`/api/stock-take?${params.toString()}`); } function toggleProduct(id: number) { setSelectedProducts((prev) => { const next = new Set(prev); if (next.has(id)) { next.delete(id); } else { next.add(id); } return next; }); } function handleExportCSV() { if (!sorted || sorted.length === 0) return; const headers = ["Part Number", "Name", "Shelf Location", "Current Stock"]; const rows = sorted.map((c) => [ c.part_number, c.name, c.shelf_location || "", String(c.stock_quantity ?? 0), ]); const csvContent = [ headers.join(","), ...rows.map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(",") ), ].join("\n"); const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; const date = new Date().toISOString().slice(0, 10); link.download = `stock-take-${date}.csv`; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } const hasResults = results && results.length > 0; return (

Stock Take

Select components by source and product to generate a stock take list

{/* Filters */} Filters Choose which components to include in your stock take {/* Source Filter */}
{/* Product Filter */}

Select products to only include components from their BOM. Leave empty for all components.

{products.length === 0 ? (

No products available

) : (
{products.map((p) => ( ))}
)}
{selectedProducts.size > 0 && ( )}
{/* Results */} {queryKey && (
Stock Take Results {isLoading ? "Loading..." : `${sorted.length} component${sorted.length !== 1 ? "s" : ""} found`}
{hasResults && ( )}
Source {isLoading ? ( Loading components... ) : sorted.length === 0 ? ( No components match the selected filters ) : ( sorted.map((comp) => ( {comp.part_number} {comp.name} {comp.category_name ? ( {comp.category_name} ) : ( - )} {comp.customer_supplied ? ( {comp.customer_name || "Customer"} ) : ( Standard )} {comp.shelf_location || "-"} {comp.stock_quantity ?? 0} )) )}
)}
); }