diff --git a/frontend/app/(sidebar)/farms/[farmId]/crops/[cropId]/page.tsx b/frontend/app/(sidebar)/farms/[farmId]/crops/[cropId]/page.tsx index 0b767fd..4c444ab 100644 --- a/frontend/app/(sidebar)/farms/[farmId]/crops/[cropId]/page.tsx +++ b/frontend/app/(sidebar)/farms/[farmId]/crops/[cropId]/page.tsx @@ -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 }) { +export default function CropDetailPage({ + params, +}: { + params: Promise; +}) { const router = useRouter(); const [crop, setCrop] = useState(null); const [analytics, setAnalytics] = useState(null); @@ -57,7 +71,9 @@ export default function CropDetailPage({ params }: { params: PromiseLoading... +
+ Loading... +
); } @@ -87,7 +103,8 @@ export default function CropDetailPage({ params }: { params: Promise 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 router.back()}> + onClick={() => router.back()} + > Back to Farm @@ -126,7 +144,9 @@ export default function CropDetailPage({ params }: { params: Promise

Growth Timeline

-

Planted on {crop.plantedDate.toLocaleDateString()}

+

+ Planted on {crop.plantedDate.toLocaleDateString()} +

@@ -150,19 +170,28 @@ export default function CropDetailPage({ params }: { params: Promise
- + Health Score: {crop.healthScore}% - + Growing
{crop.expectedHarvest ? (

- Expected harvest: {crop.expectedHarvest.toLocaleDateString()} + Expected harvest:{" "} + {crop.expectedHarvest.toLocaleDateString()}

) : ( -

Expected harvest date not available

+

+ Expected harvest date not available +

)}
@@ -180,13 +209,18 @@ export default function CropDetailPage({ params }: { params: Promise -
+ onClick={action.onClick} + > +
{action.title}
-

{action.description}

+

+ {action.description} +

))} @@ -196,7 +230,9 @@ export default function CropDetailPage({ params }: { params: Promise Environmental Conditions - Real-time monitoring of growing conditions + + Real-time monitoring of growing conditions +
@@ -247,15 +283,22 @@ export default function CropDetailPage({ params }: { params: Promise ( + className="border-none shadow-none bg-gradient-to-br from-white to-gray-50/50 dark:from-slate-800 dark:to-slate-700/50" + >
- +
-

{metric.label}

-

{metric.value}

+

+ {metric.label} +

+

+ {metric.value} +

@@ -269,9 +312,14 @@ export default function CropDetailPage({ params }: { params: Promise
Growth Progress - {analytics.growthProgress}% + + {analytics.growthProgress}% +
- +
{/* Next Action Card */} @@ -282,10 +330,15 @@ export default function CropDetailPage({ params }: { params: Promise
-

Next Action Required

-

{analytics.nextAction}

+

+ Next Action Required +

+

+ {analytics.nextAction} +

- Due by {analytics.nextActionDue.toLocaleDateString()} + Due by{" "} + {analytics.nextActionDue.toLocaleDateString()}

@@ -337,9 +390,14 @@ export default function CropDetailPage({ params }: { params: Promise
{nutrient.name} - {nutrient.value}% + + {nutrient.value}% +
- + ))} @@ -372,10 +430,14 @@ export default function CropDetailPage({ params }: { params: Promise -

2 hours ago

+

+ 2 hours ago +

- {i < 4 && } + {i < 4 && ( + + )} ))} @@ -385,8 +447,17 @@ export default function CropDetailPage({ params }: { params: Promise {/* Dialogs */} - - + + ); @@ -399,9 +470,15 @@ function Activity({ icon }: { icon: number }) { const icons = [ , , - , + , , - , + , ]; return icons[icon]; } diff --git a/frontend/app/(sidebar)/inventory/page.tsx b/frontend/app/(sidebar)/inventory/page.tsx index 3a9437e..9b02fc9 100644 --- a/frontend/app/(sidebar)/inventory/page.tsx +++ b/frontend/app/(sidebar)/inventory/page.tsx @@ -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(); const [inventoryType, setInventoryType] = useState("all"); - const [currentPage, setCurrentPage] = useState(1); + const [sorting, setSorting] = useState([]); + const [pagination, setPagination] = useState({ + 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 ( -
- Loading... -
- ); - } - - if (isError || !inventoryItems) { - return ( -
- Error loading inventory data. -
- ); - } - - // 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> + | Iterable + | ReactPortal + | Promise< + | string + | number + | bigint + | boolean + | ReactPortal + | ReactElement> + | Iterable + | null + | undefined + > + | null + | undefined; + }) => {info.getValue()}, + }, + { accessorKey: "edit", header: "Edit", cell: () => }, + { + accessorKey: "delete", + header: "Delete", + cell: () => , + }, + ]; + + const table = useReactTable({ + data: filteredItems, + columns, + state: { sorting, pagination }, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onSortingChange: setSorting, + onPaginationChange: setPagination, + }); + + if (isLoading) + return ( +
+ Loading... +
+ ); + if (isError) + return ( +
+ Error loading inventory data. +
+ ); + return (

Inventory

- - {/* Filters and search */}
-
- - -
- -
- - - - - - - - - -
- - -
- - -
+ + +
- - {/* Table */}
-

Table Fields

- - Name - Category - Type - Quantity - Last Updated - Status - Edit - Delete - + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ))} + + ))} - {filteredItems.length === 0 ? ( - - - No inventory items found - + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} - ) : ( - filteredItems.map((item) => ( - - {item.name} - {item.category} - {item.type} - - {item.quantity} {item.unit} - - {item.lastUpdated} - - - {item.status} - - - - - - - - - - )) - )} + ))}
+ + + + + + + + + + +
diff --git a/frontend/package.json b/frontend/package.json index 4da49cc..6dd6b31 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 7635441..f09642c 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -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