mirror of
https://github.com/ForFarmTeam/ForFarm.git
synced 2025-12-18 21:44: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 { CalendarIcon } from "lucide-react";
|
||||
import { format } from "date-fns";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
@ -28,10 +29,47 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { createInventoryItem } from "@/api/inventory";
|
||||
import type { CreateInventoryItemInput } from "@/types";
|
||||
|
||||
export function AddInventoryItem() {
|
||||
const [date, setDate] = useState<Date>();
|
||||
const [date, setDate] = useState<Date | undefined>();
|
||||
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 (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
@ -48,13 +86,13 @@ export function AddInventoryItem() {
|
||||
<Label htmlFor="name" className="text-right">
|
||||
Name
|
||||
</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 className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="type" className="text-right">
|
||||
Type
|
||||
</Label>
|
||||
<Select>
|
||||
<Select value={itemType} onValueChange={setItemType}>
|
||||
<SelectTrigger className="col-span-3">
|
||||
<SelectValue placeholder="Select type" />
|
||||
</SelectTrigger>
|
||||
@ -71,19 +109,37 @@ export function AddInventoryItem() {
|
||||
<Label htmlFor="category" className="text-right">
|
||||
Category
|
||||
</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 className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="quantity" className="text-right">
|
||||
Quantity
|
||||
</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 className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="unit" className="text-right">
|
||||
Unit
|
||||
</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 className="grid grid-cols-4 items-center gap-4">
|
||||
<Label htmlFor="date" className="text-right">
|
||||
@ -105,7 +161,7 @@ export function AddInventoryItem() {
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="submit" onClick={() => setOpen(false)}>
|
||||
<Button type="submit" onClick={handleSave}>
|
||||
Save Item
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Calendar, ChevronDown, Plus, Search } from "lucide-react";
|
||||
|
||||
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 { Badge } from "@/components/ui/badge";
|
||||
|
||||
import { fetchInventoryItems } from "@/api/inventory";
|
||||
import { AddInventoryItem } from "./add-inventory-item";
|
||||
|
||||
export default function InventoryPage() {
|
||||
const [date, setDate] = useState<Date>();
|
||||
const [inventoryType, setInventoryType] = useState("all");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
// Sample inventory data
|
||||
const inventoryItems = [
|
||||
{
|
||||
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",
|
||||
},
|
||||
];
|
||||
// Fetch inventory items using react-query.
|
||||
const {
|
||||
data: inventoryItems,
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
queryKey: ["inventoryItems"],
|
||||
queryFn: fetchInventoryItems,
|
||||
staleTime: 60 * 1000,
|
||||
});
|
||||
|
||||
// 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 =
|
||||
inventoryType === "all"
|
||||
? inventoryItems
|
||||
@ -81,7 +52,6 @@ export default function InventoryPage() {
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background">
|
||||
{/* Main content */}
|
||||
<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>
|
||||
@ -130,9 +100,7 @@ export default function InventoryPage() {
|
||||
<Input type="search" placeholder="Search Farms" className="pl-8" />
|
||||
</div>
|
||||
|
||||
<Button>
|
||||
<Plus className="mr-2 h-4 w-4" /> Add
|
||||
</Button>
|
||||
<AddInventoryItem />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -58,3 +58,16 @@ export interface User {
|
||||
Avatar: string;
|
||||
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