feat: add dummy api for inventory

This commit is contained in:
Sosokker 2025-03-07 02:50:13 +07:00
parent ddf784f87b
commit 02d219a502
4 changed files with 199 additions and 65 deletions

97
frontend/api/inventory.ts Normal file
View 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",
};
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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">;