mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-18 21:44:08 +01:00
change to use the tannstack table
This commit is contained in:
parent
7b69c68056
commit
2b694a1b44
@ -18,7 +18,13 @@ import {
|
||||
CloudRain,
|
||||
Wind,
|
||||
} from "lucide-react";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
CardDescription,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
@ -26,7 +32,11 @@ import { Badge } from "@/components/ui/badge";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { ChatbotDialog } from "./chatbot-dialog";
|
||||
import { AnalyticsDialog } from "./analytics-dialog";
|
||||
import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/components/ui/hover-card";
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/components/ui/hover-card";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import type { Crop, CropAnalytics } from "@/types";
|
||||
import GoogleMapWithDrawing from "@/components/google-map-with-drawing";
|
||||
@ -37,7 +47,11 @@ interface CropDetailPageParams {
|
||||
cropId: string;
|
||||
}
|
||||
|
||||
export default function CropDetailPage({ params }: { params: Promise<CropDetailPageParams> }) {
|
||||
export default function CropDetailPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<CropDetailPageParams>;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [crop, setCrop] = useState<Crop | null>(null);
|
||||
const [analytics, setAnalytics] = useState<CropAnalytics | null>(null);
|
||||
@ -57,7 +71,9 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
|
||||
if (!crop || !analytics) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-background text-foreground">Loading...</div>
|
||||
<div className="min-h-screen flex items-center justify-center bg-background text-foreground">
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -87,7 +103,8 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
icon: ListCollapse,
|
||||
description: "View detailed information",
|
||||
onClick: () => console.log("Details clicked"),
|
||||
color: "bg-purple-50 dark:bg-purple-900 text-purple-600 dark:text-purple-300",
|
||||
color:
|
||||
"bg-purple-50 dark:bg-purple-900 text-purple-600 dark:text-purple-300",
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
@ -107,7 +124,8 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="gap-2 text-green-700 dark:text-green-300 hover:text-green-800 dark:hover:text-green-200 hover:bg-green-100/50 dark:hover:bg-green-800/50"
|
||||
onClick={() => router.back()}>
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4" /> Back to Farm
|
||||
</Button>
|
||||
<HoverCard>
|
||||
@ -126,7 +144,9 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
</Avatar>
|
||||
<div className="space-y-1">
|
||||
<h4 className="text-sm font-semibold">Growth Timeline</h4>
|
||||
<p className="text-sm text-muted-foreground">Planted on {crop.plantedDate.toLocaleDateString()}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Planted on {crop.plantedDate.toLocaleDateString()}
|
||||
</p>
|
||||
<div className="flex items-center pt-2">
|
||||
<Separator className="w-full" />
|
||||
<span className="mx-2 text-xs text-muted-foreground">
|
||||
@ -150,19 +170,28 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex flex-col items-end">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className={`${healthColors[analytics.plantHealth]} border`}>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className={`${healthColors[analytics.plantHealth]} border`}
|
||||
>
|
||||
Health Score: {crop.healthScore}%
|
||||
</Badge>
|
||||
<Badge variant="outline" className="bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-300">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-300"
|
||||
>
|
||||
Growing
|
||||
</Badge>
|
||||
</div>
|
||||
{crop.expectedHarvest ? (
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Expected harvest: {crop.expectedHarvest.toLocaleDateString()}
|
||||
Expected harvest:{" "}
|
||||
{crop.expectedHarvest.toLocaleDateString()}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground mt-1">Expected harvest date not available</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Expected harvest date not available
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -180,13 +209,18 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
key={action.title}
|
||||
variant="outline"
|
||||
className={`h-auto p-4 flex flex-col items-center gap-3 transition-all group ${action.color} hover:scale-105`}
|
||||
onClick={action.onClick}>
|
||||
<div className={`p-3 rounded-lg ${action.color} group-hover:scale-110 transition-transform`}>
|
||||
onClick={action.onClick}
|
||||
>
|
||||
<div
|
||||
className={`p-3 rounded-lg ${action.color} group-hover:scale-110 transition-transform`}
|
||||
>
|
||||
<action.icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="font-medium mb-1">{action.title}</div>
|
||||
<p className="text-xs text-muted-foreground">{action.description}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{action.description}
|
||||
</p>
|
||||
</div>
|
||||
</Button>
|
||||
))}
|
||||
@ -196,7 +230,9 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
<Card className="border-green-100 dark:border-green-700">
|
||||
<CardHeader>
|
||||
<CardTitle>Environmental Conditions</CardTitle>
|
||||
<CardDescription>Real-time monitoring of growing conditions</CardDescription>
|
||||
<CardDescription>
|
||||
Real-time monitoring of growing conditions
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-6">
|
||||
@ -247,15 +283,22 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
].map((metric) => (
|
||||
<Card
|
||||
key={metric.label}
|
||||
className="border-none shadow-none bg-gradient-to-br from-white to-gray-50/50 dark:from-slate-800 dark:to-slate-700/50">
|
||||
className="border-none shadow-none bg-gradient-to-br from-white to-gray-50/50 dark:from-slate-800 dark:to-slate-700/50"
|
||||
>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className={`p-2 rounded-lg ${metric.bg}`}>
|
||||
<metric.icon className={`h-4 w-4 ${metric.color}`} />
|
||||
<metric.icon
|
||||
className={`h-4 w-4 ${metric.color}`}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-muted-foreground">{metric.label}</p>
|
||||
<p className="text-2xl font-semibold tracking-tight">{metric.value}</p>
|
||||
<p className="text-sm font-medium text-muted-foreground">
|
||||
{metric.label}
|
||||
</p>
|
||||
<p className="text-2xl font-semibold tracking-tight">
|
||||
{metric.value}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@ -269,9 +312,14 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="font-medium">Growth Progress</span>
|
||||
<span className="text-muted-foreground">{analytics.growthProgress}%</span>
|
||||
<span className="text-muted-foreground">
|
||||
{analytics.growthProgress}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={analytics.growthProgress} className="h-2" />
|
||||
<Progress
|
||||
value={analytics.growthProgress}
|
||||
className="h-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Next Action Card */}
|
||||
@ -282,10 +330,15 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
<Timer className="h-4 w-4 text-green-600 dark:text-green-300" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium mb-1">Next Action Required</p>
|
||||
<p className="text-sm text-muted-foreground">{analytics.nextAction}</p>
|
||||
<p className="font-medium mb-1">
|
||||
Next Action Required
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{analytics.nextAction}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Due by {analytics.nextActionDue.toLocaleDateString()}
|
||||
Due by{" "}
|
||||
{analytics.nextActionDue.toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -337,9 +390,14 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
<div key={nutrient.name} className="space-y-2">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="font-medium">{nutrient.name}</span>
|
||||
<span className="text-muted-foreground">{nutrient.value}%</span>
|
||||
<span className="text-muted-foreground">
|
||||
{nutrient.value}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={nutrient.value} className={`h-2 ${nutrient.color}`} />
|
||||
<Progress
|
||||
value={nutrient.value}
|
||||
className={`h-2 ${nutrient.color}`}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -372,10 +430,14 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
][i]
|
||||
}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">2 hours ago</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
2 hours ago
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{i < 4 && <Separator className="my-4 dark:bg-slate-700" />}
|
||||
{i < 4 && (
|
||||
<Separator className="my-4 dark:bg-slate-700" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</ScrollArea>
|
||||
@ -385,8 +447,17 @@ export default function CropDetailPage({ params }: { params: Promise<CropDetailP
|
||||
</div>
|
||||
|
||||
{/* Dialogs */}
|
||||
<ChatbotDialog open={isChatOpen} onOpenChange={setIsChatOpen} cropName={crop.name} />
|
||||
<AnalyticsDialog open={isAnalyticsOpen} onOpenChange={setIsAnalyticsOpen} crop={crop} analytics={analytics} />
|
||||
<ChatbotDialog
|
||||
open={isChatOpen}
|
||||
onOpenChange={setIsChatOpen}
|
||||
cropName={crop.name}
|
||||
/>
|
||||
<AnalyticsDialog
|
||||
open={isAnalyticsOpen}
|
||||
onOpenChange={setIsAnalyticsOpen}
|
||||
crop={crop}
|
||||
analytics={analytics}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -399,9 +470,15 @@ function Activity({ icon }: { icon: number }) {
|
||||
const icons = [
|
||||
<Droplets key="0" className="h-4 w-4 text-blue-500 dark:text-blue-300" />,
|
||||
<Leaf key="1" className="h-4 w-4 text-green-500 dark:text-green-300" />,
|
||||
<LineChart key="2" className="h-4 w-4 text-purple-500 dark:text-purple-300" />,
|
||||
<LineChart
|
||||
key="2"
|
||||
className="h-4 w-4 text-purple-500 dark:text-purple-300"
|
||||
/>,
|
||||
<Sprout key="3" className="h-4 w-4 text-yellow-500 dark:text-yellow-300" />,
|
||||
<ThermometerSun key="4" className="h-4 w-4 text-orange-500 dark:text-orange-300" />,
|
||||
<ThermometerSun
|
||||
key="4"
|
||||
className="h-4 w-4 text-orange-500 dark:text-orange-300"
|
||||
/>,
|
||||
];
|
||||
return icons[icon];
|
||||
}
|
||||
|
||||
@ -1,19 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import {
|
||||
JSXElementConstructor,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
ReactPortal,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Calendar, ChevronDown, Plus, Search } from "lucide-react";
|
||||
import {
|
||||
useReactTable,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
getPaginationRowModel,
|
||||
flexRender,
|
||||
SortingState,
|
||||
PaginationState,
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@ -26,14 +32,8 @@ import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
} from "@/components/ui/pagination";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { Calendar as CalendarComponent } from "@/components/ui/calendar";
|
||||
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { fetchInventoryItems } from "@/api/inventory";
|
||||
@ -44,11 +44,14 @@ import { DeleteInventoryItem } from "./delete-inventory-item";
|
||||
export default function InventoryPage() {
|
||||
const [date, setDate] = useState<Date>();
|
||||
const [inventoryType, setInventoryType] = useState("all");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
// Fetch inventory items using react-query.
|
||||
const {
|
||||
data: inventoryItems,
|
||||
data: inventoryItems = [],
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
@ -57,155 +60,156 @@ export default function InventoryPage() {
|
||||
staleTime: 60 * 1000,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background items-center justify-center">
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError || !inventoryItems) {
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background items-center justify-center">
|
||||
Error loading inventory data.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Filter items based on selected type.
|
||||
const filteredItems =
|
||||
inventoryType === "all"
|
||||
? inventoryItems
|
||||
: inventoryItems.filter((item) =>
|
||||
inventoryType === "plantation"
|
||||
? item.type === "Plantation"
|
||||
: item.type === "Fertilizer",
|
||||
: inventoryItems.filter(
|
||||
(item) => item.type.toLowerCase() === inventoryType
|
||||
);
|
||||
|
||||
const columns = [
|
||||
{ accessorKey: "name", header: "Name" },
|
||||
{ accessorKey: "category", header: "Category" },
|
||||
{ accessorKey: "type", header: "Type" },
|
||||
{ accessorKey: "quantity", header: "Quantity" },
|
||||
{ accessorKey: "lastUpdated", header: "Last Updated" },
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: (info: {
|
||||
getValue: () =>
|
||||
| string
|
||||
| number
|
||||
| bigint
|
||||
| boolean
|
||||
| ReactElement<unknown, string | JSXElementConstructor<any>>
|
||||
| Iterable<ReactNode>
|
||||
| ReactPortal
|
||||
| Promise<
|
||||
| string
|
||||
| number
|
||||
| bigint
|
||||
| boolean
|
||||
| ReactPortal
|
||||
| ReactElement<unknown, string | JSXElementConstructor<any>>
|
||||
| Iterable<ReactNode>
|
||||
| null
|
||||
| undefined
|
||||
>
|
||||
| null
|
||||
| undefined;
|
||||
}) => <Badge>{info.getValue()}</Badge>,
|
||||
},
|
||||
{ accessorKey: "edit", header: "Edit", cell: () => <EditInventoryItem /> },
|
||||
{
|
||||
accessorKey: "delete",
|
||||
header: "Delete",
|
||||
cell: () => <DeleteInventoryItem />,
|
||||
},
|
||||
];
|
||||
|
||||
const table = useReactTable({
|
||||
data: filteredItems,
|
||||
columns,
|
||||
state: { sorting, pagination },
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
onPaginationChange: setPagination,
|
||||
});
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
Loading...
|
||||
</div>
|
||||
);
|
||||
if (isError)
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
Error loading inventory data.
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background">
|
||||
<div className="flex-1 flex flex-col">
|
||||
<main className="flex-1 p-6">
|
||||
<h1 className="text-2xl font-bold tracking-tight mb-6">Inventory</h1>
|
||||
|
||||
{/* Filters and search */}
|
||||
<div className="flex flex-col sm:flex-row gap-4 mb-6">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant={inventoryType === "all" ? "default" : "outline"}
|
||||
onClick={() => setInventoryType("all")}
|
||||
className="w-24"
|
||||
>
|
||||
All
|
||||
</Button>
|
||||
<Select value={inventoryType} onValueChange={setInventoryType}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue placeholder="Crop" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="all">All</SelectItem>
|
||||
<SelectItem value="plantation">Plantation</SelectItem>
|
||||
<SelectItem value="fertilizer">Fertilizer</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 gap-4">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" className="flex-1 justify-between">
|
||||
<div className="flex items-center">
|
||||
<Calendar className="mr-2 h-4 w-4" />
|
||||
{date ? date.toLocaleDateString() : "Time filter"}
|
||||
</div>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<CalendarComponent
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search Farms"
|
||||
className="pl-8"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AddInventoryItem />
|
||||
</div>
|
||||
<Button onClick={() => setInventoryType("all")} className="w-24">
|
||||
All
|
||||
</Button>
|
||||
<Input type="search" placeholder="Search the name" />
|
||||
<AddInventoryItem />
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<div className="border rounded-md">
|
||||
<h3 className="px-4 py-2 border-b font-medium">Table Fields</h3>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Name</TableHead>
|
||||
<TableHead>Category</TableHead>
|
||||
<TableHead>Type</TableHead>
|
||||
<TableHead className="text-right">Quantity</TableHead>
|
||||
<TableHead>Last Updated</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Edit</TableHead>
|
||||
<TableHead>Delete</TableHead>
|
||||
</TableRow>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead
|
||||
key={header.id}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{filteredItems.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={6}
|
||||
className="text-center py-8 text-muted-foreground"
|
||||
>
|
||||
No inventory items found
|
||||
</TableCell>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
) : (
|
||||
filteredItems.map((item) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell className="font-medium">{item.name}</TableCell>
|
||||
<TableCell>{item.category}</TableCell>
|
||||
<TableCell>{item.type}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
{item.quantity} {item.unit}
|
||||
</TableCell>
|
||||
<TableCell>{item.lastUpdated}</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant={
|
||||
item.status === "Low Stock"
|
||||
? "destructive"
|
||||
: "default"
|
||||
}
|
||||
>
|
||||
{item.status}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<EditInventoryItem />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<DeleteInventoryItem />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<Pagination className="mt-5">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<Button
|
||||
className="flex w-24"
|
||||
onClick={() =>
|
||||
setPagination((prev) => ({
|
||||
...prev,
|
||||
pageIndex: Math.max(0, prev.pageIndex - 1),
|
||||
}))
|
||||
}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
</PaginationItem>
|
||||
|
||||
<PaginationItem>
|
||||
<Button
|
||||
className="flex w-24"
|
||||
onClick={() =>
|
||||
setPagination((prev) => ({
|
||||
...prev,
|
||||
pageIndex: prev.pageIndex + 1,
|
||||
}))
|
||||
}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
"@react-oauth/google": "^0.12.1",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/react-query": "^5.66.0",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"axios": "^1.7.9",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
@ -71,6 +71,9 @@ dependencies:
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.66.0
|
||||
version: 5.67.3(react@19.0.0)
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.21.2
|
||||
version: 8.21.2(react-dom@19.0.0)(react@19.0.0)
|
||||
axios:
|
||||
specifier: ^1.7.9
|
||||
version: 1.8.3
|
||||
@ -1600,6 +1603,23 @@ packages:
|
||||
react: 19.0.0
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-table@8.21.2(react-dom@19.0.0)(react@19.0.0):
|
||||
resolution: {integrity: sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
react-dom: '>=16.8'
|
||||
dependencies:
|
||||
'@tanstack/table-core': 8.21.2
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
dev: false
|
||||
|
||||
/@tanstack/table-core@8.21.2:
|
||||
resolution: {integrity: sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/@types/d3-array@3.2.1:
|
||||
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
|
||||
dev: false
|
||||
|
||||
Loading…
Reference in New Issue
Block a user