mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-19 14:04:08 +01:00
feat: add dummy api for inventory
This commit is contained in:
parent
ddf784f87b
commit
02d219a502
97
frontend/api/inventory.ts
Normal file
97
frontend/api/inventory.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import axiosInstance from "./config";
|
||||||
|
import type { InventoryItem, CreateInventoryItemInput } from "@/types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates an API call to fetch inventory items.
|
||||||
|
* Waits for a simulated delay and then attempts an axios GET request.
|
||||||
|
* If the request fails, returns fallback dummy data.
|
||||||
|
*/
|
||||||
|
export async function fetchInventoryItems(): Promise<InventoryItem[]> {
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.get<InventoryItem[]>("/api/inventory");
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback dummy data
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "Tomato Seeds",
|
||||||
|
category: "Seeds",
|
||||||
|
type: "Plantation",
|
||||||
|
quantity: 500,
|
||||||
|
unit: "packets",
|
||||||
|
lastUpdated: "2023-03-01",
|
||||||
|
status: "In Stock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "NPK Fertilizer",
|
||||||
|
category: "Fertilizer",
|
||||||
|
type: "Fertilizer",
|
||||||
|
quantity: 200,
|
||||||
|
unit: "kg",
|
||||||
|
lastUpdated: "2023-03-05",
|
||||||
|
status: "Low Stock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "Corn Seeds",
|
||||||
|
category: "Seeds",
|
||||||
|
type: "Plantation",
|
||||||
|
quantity: 300,
|
||||||
|
unit: "packets",
|
||||||
|
lastUpdated: "2023-03-10",
|
||||||
|
status: "In Stock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "Organic Compost",
|
||||||
|
category: "Fertilizer",
|
||||||
|
type: "Fertilizer",
|
||||||
|
quantity: 150,
|
||||||
|
unit: "kg",
|
||||||
|
lastUpdated: "2023-03-15",
|
||||||
|
status: "In Stock",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "Wheat Seeds",
|
||||||
|
category: "Seeds",
|
||||||
|
type: "Plantation",
|
||||||
|
quantity: 250,
|
||||||
|
unit: "packets",
|
||||||
|
lastUpdated: "2023-03-20",
|
||||||
|
status: "In Stock",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates creating a new inventory item.
|
||||||
|
* Uses axios POST and if unavailable, returns a simulated response.
|
||||||
|
*
|
||||||
|
* Note: The function accepts all fields except id, lastUpdated, and status.
|
||||||
|
*/
|
||||||
|
export async function createInventoryItem(
|
||||||
|
item: Omit<InventoryItem, "id" | "lastUpdated" | "status">
|
||||||
|
): Promise<InventoryItem> {
|
||||||
|
// Simulate network delay
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||||
|
try {
|
||||||
|
const response = await axiosInstance.post<InventoryItem>("/api/inventory", item);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
// Simulate successful creation if API endpoint is not available
|
||||||
|
return {
|
||||||
|
id: Math.floor(Math.random() * 1000),
|
||||||
|
name: item.name,
|
||||||
|
category: item.category,
|
||||||
|
type: item.type,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unit: item.unit,
|
||||||
|
lastUpdated: new Date().toISOString(),
|
||||||
|
status: "In Stock",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { CalendarIcon } from "lucide-react";
|
import { CalendarIcon } from "lucide-react";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Calendar } from "@/components/ui/calendar";
|
import { Calendar } from "@/components/ui/calendar";
|
||||||
@ -28,10 +29,47 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { createInventoryItem } from "@/api/inventory";
|
||||||
|
import type { CreateInventoryItemInput } from "@/types";
|
||||||
|
|
||||||
export function AddInventoryItem() {
|
export function AddInventoryItem() {
|
||||||
const [date, setDate] = useState<Date>();
|
const [date, setDate] = useState<Date | undefined>();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
const [itemName, setItemName] = useState("");
|
||||||
|
const [itemType, setItemType] = useState("");
|
||||||
|
const [itemCategory, setItemCategory] = useState("");
|
||||||
|
const [itemQuantity, setItemQuantity] = useState(0);
|
||||||
|
const [itemUnit, setItemUnit] = useState("");
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const mutation = useMutation({
|
||||||
|
mutationFn: (item: CreateInventoryItemInput) => createInventoryItem(item),
|
||||||
|
onSuccess: () => {
|
||||||
|
// Invalidate queries to refresh inventory data.
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["inventoryItems"] });
|
||||||
|
// Reset form fields and close dialog.
|
||||||
|
setItemName("");
|
||||||
|
setItemType("");
|
||||||
|
setItemCategory("");
|
||||||
|
setItemQuantity(0);
|
||||||
|
setItemUnit("");
|
||||||
|
setDate(undefined);
|
||||||
|
setOpen(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
// Basic validation (you can extend this as needed)
|
||||||
|
if (!itemName || !itemType || !itemCategory || !itemUnit) return;
|
||||||
|
mutation.mutate({
|
||||||
|
name: itemName,
|
||||||
|
type: itemType,
|
||||||
|
category: itemCategory,
|
||||||
|
quantity: itemQuantity,
|
||||||
|
unit: itemUnit,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
@ -48,13 +86,13 @@ export function AddInventoryItem() {
|
|||||||
<Label htmlFor="name" className="text-right">
|
<Label htmlFor="name" className="text-right">
|
||||||
Name
|
Name
|
||||||
</Label>
|
</Label>
|
||||||
<Input id="name" className="col-span-3" />
|
<Input id="name" className="col-span-3" value={itemName} onChange={(e) => setItemName(e.target.value)} />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="type" className="text-right">
|
<Label htmlFor="type" className="text-right">
|
||||||
Type
|
Type
|
||||||
</Label>
|
</Label>
|
||||||
<Select>
|
<Select value={itemType} onValueChange={setItemType}>
|
||||||
<SelectTrigger className="col-span-3">
|
<SelectTrigger className="col-span-3">
|
||||||
<SelectValue placeholder="Select type" />
|
<SelectValue placeholder="Select type" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
@ -71,19 +109,37 @@ export function AddInventoryItem() {
|
|||||||
<Label htmlFor="category" className="text-right">
|
<Label htmlFor="category" className="text-right">
|
||||||
Category
|
Category
|
||||||
</Label>
|
</Label>
|
||||||
<Input id="category" className="col-span-3" placeholder="e.g., Seeds, Organic" />
|
<Input
|
||||||
|
id="category"
|
||||||
|
className="col-span-3"
|
||||||
|
placeholder="e.g., Seeds, Organic"
|
||||||
|
value={itemCategory}
|
||||||
|
onChange={(e) => setItemCategory(e.target.value)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="quantity" className="text-right">
|
<Label htmlFor="quantity" className="text-right">
|
||||||
Quantity
|
Quantity
|
||||||
</Label>
|
</Label>
|
||||||
<Input id="quantity" type="number" className="col-span-3" />
|
<Input
|
||||||
|
id="quantity"
|
||||||
|
type="number"
|
||||||
|
className="col-span-3"
|
||||||
|
value={itemQuantity}
|
||||||
|
onChange={(e) => setItemQuantity(Number(e.target.value))}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="unit" className="text-right">
|
<Label htmlFor="unit" className="text-right">
|
||||||
Unit
|
Unit
|
||||||
</Label>
|
</Label>
|
||||||
<Input id="unit" className="col-span-3" placeholder="e.g., kg, packets" />
|
<Input
|
||||||
|
id="unit"
|
||||||
|
className="col-span-3"
|
||||||
|
placeholder="e.g., kg, packets"
|
||||||
|
value={itemUnit}
|
||||||
|
onChange={(e) => setItemUnit(e.target.value)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<Label htmlFor="date" className="text-right">
|
<Label htmlFor="date" className="text-right">
|
||||||
@ -105,7 +161,7 @@ export function AddInventoryItem() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button type="submit" onClick={() => setOpen(false)}>
|
<Button type="submit" onClick={handleSave}>
|
||||||
Save Item
|
Save Item
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { Calendar, ChevronDown, Plus, Search } from "lucide-react";
|
import { Calendar, ChevronDown, Plus, Search } from "lucide-react";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@ -12,66 +13,36 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover
|
|||||||
import { Calendar as CalendarComponent } from "@/components/ui/calendar";
|
import { Calendar as CalendarComponent } from "@/components/ui/calendar";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
import { fetchInventoryItems } from "@/api/inventory";
|
||||||
|
import { AddInventoryItem } from "./add-inventory-item";
|
||||||
|
|
||||||
export default function InventoryPage() {
|
export default function InventoryPage() {
|
||||||
const [date, setDate] = useState<Date>();
|
const [date, setDate] = useState<Date>();
|
||||||
const [inventoryType, setInventoryType] = useState("all");
|
const [inventoryType, setInventoryType] = useState("all");
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
// Sample inventory data
|
// Fetch inventory items using react-query.
|
||||||
const inventoryItems = [
|
const {
|
||||||
{
|
data: inventoryItems,
|
||||||
id: 1,
|
isLoading,
|
||||||
name: "Tomato Seeds",
|
isError,
|
||||||
category: "Seeds",
|
} = useQuery({
|
||||||
type: "Plantation",
|
queryKey: ["inventoryItems"],
|
||||||
quantity: 500,
|
queryFn: fetchInventoryItems,
|
||||||
unit: "packets",
|
staleTime: 60 * 1000,
|
||||||
lastUpdated: "2023-03-01",
|
});
|
||||||
status: "In Stock",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: "NPK Fertilizer",
|
|
||||||
category: "Fertilizer",
|
|
||||||
type: "Fertilizer",
|
|
||||||
quantity: 200,
|
|
||||||
unit: "kg",
|
|
||||||
lastUpdated: "2023-03-05",
|
|
||||||
status: "Low Stock",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: "Corn Seeds",
|
|
||||||
category: "Seeds",
|
|
||||||
type: "Plantation",
|
|
||||||
quantity: 300,
|
|
||||||
unit: "packets",
|
|
||||||
lastUpdated: "2023-03-10",
|
|
||||||
status: "In Stock",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: "Organic Compost",
|
|
||||||
category: "Fertilizer",
|
|
||||||
type: "Fertilizer",
|
|
||||||
quantity: 150,
|
|
||||||
unit: "kg",
|
|
||||||
lastUpdated: "2023-03-15",
|
|
||||||
status: "In Stock",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: "Wheat Seeds",
|
|
||||||
category: "Seeds",
|
|
||||||
type: "Plantation",
|
|
||||||
quantity: 250,
|
|
||||||
unit: "packets",
|
|
||||||
lastUpdated: "2023-03-20",
|
|
||||||
status: "In Stock",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Filter items based on selected type
|
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 =
|
const filteredItems =
|
||||||
inventoryType === "all"
|
inventoryType === "all"
|
||||||
? inventoryItems
|
? inventoryItems
|
||||||
@ -81,7 +52,6 @@ export default function InventoryPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen bg-background">
|
<div className="flex min-h-screen bg-background">
|
||||||
{/* Main content */}
|
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
<main className="flex-1 p-6">
|
<main className="flex-1 p-6">
|
||||||
<h1 className="text-2xl font-bold tracking-tight mb-6">Inventory</h1>
|
<h1 className="text-2xl font-bold tracking-tight mb-6">Inventory</h1>
|
||||||
@ -130,9 +100,7 @@ export default function InventoryPage() {
|
|||||||
<Input type="search" placeholder="Search Farms" className="pl-8" />
|
<Input type="search" placeholder="Search Farms" className="pl-8" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button>
|
<AddInventoryItem />
|
||||||
<Plus className="mr-2 h-4 w-4" /> Add
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -58,3 +58,16 @@ export interface User {
|
|||||||
Avatar: string;
|
Avatar: string;
|
||||||
IsActive: boolean;
|
IsActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type InventoryItem = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
category: string;
|
||||||
|
type: string;
|
||||||
|
quantity: number;
|
||||||
|
unit: string;
|
||||||
|
lastUpdated: string;
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CreateInventoryItemInput = Omit<InventoryItem, "id" | "lastUpdated" | "status">;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user