feat: add fetchInventoryCategory API and integrate into inventory management

This commit is contained in:
THIS ONE IS A LITTLE BIT TRICKY KRUB 2025-04-01 18:39:25 +07:00
parent e7bdb5d6a1
commit ba8f754d8d
4 changed files with 87 additions and 53 deletions

View File

@ -3,14 +3,15 @@ import type {
InventoryItem, InventoryItem,
CreateInventoryItemInput, CreateInventoryItemInput,
InventoryItemStatus, InventoryItemStatus,
InventoryItemCategory,
} from "@/types"; } from "@/types";
/** /**
* Simulates an API call to fetch inventory items. * Simulates an API call to fetch inventory items.
* Waits for a simulated delay and then attempts an axios GET request. * Waits for a simulated delay and then attempts an axios GET request.
* If the request fails, returns fallback dummy data. * If the request fails, returns fallback dummy data.
* *
* *
*/ */
export async function fetchInventoryStatus(): Promise<InventoryItemStatus[]> { export async function fetchInventoryStatus(): Promise<InventoryItemStatus[]> {
try { try {
@ -23,6 +24,19 @@ export async function fetchInventoryStatus(): Promise<InventoryItemStatus[]> {
return []; return [];
} }
} }
export async function fetchInventoryCategory(): Promise<
InventoryItemCategory[]
> {
try {
const response = await axiosInstance.get<InventoryItemCategory[]>(
"/inventory/category"
);
return response.data;
} catch (error) {
console.error("Error fetching inventory status:", error);
return [];
}
}
export async function fetchInventoryItems(): Promise<InventoryItem[]> { export async function fetchInventoryItems(): Promise<InventoryItem[]> {
try { try {
@ -34,52 +48,47 @@ export async function fetchInventoryItems(): Promise<InventoryItem[]> {
{ {
id: 1, id: 1,
name: "Tomato Seeds", name: "Tomato Seeds",
category: "Seeds", category: "1",
type: "Plantation",
quantity: 500, quantity: 500,
unit: "packets", unit: "packets",
lastUpdated: "2023-03-01", lastUpdated: "2023-03-01",
status: "In Stock", status: "1",
}, },
{ {
id: 2, id: 2,
name: "NPK Fertilizer", name: "NPK Fertilizer",
category: "Fertilizer", category: "3",
type: "Fertilizer",
quantity: 200, quantity: 200,
unit: "kg", unit: "kg",
lastUpdated: "2023-03-05", lastUpdated: "2023-03-05",
status: "Low Stock", status: "2",
}, },
{ {
id: 3, id: 3,
name: "Corn Seeds", name: "Corn Seeds",
category: "Seeds", category: "1",
type: "Plantation",
quantity: 300, quantity: 300,
unit: "packets", unit: "packets",
lastUpdated: "2023-03-10", lastUpdated: "2023-03-10",
status: "In Stock", status: "3",
}, },
{ {
id: 4, id: 4,
name: "Organic Compost", name: "Organic Compost",
category: "Fertilizer", category: "3",
type: "Fertilizer",
quantity: 150, quantity: 150,
unit: "kg", unit: "kg",
lastUpdated: "2023-03-15", lastUpdated: "2023-03-15",
status: "Out Of Stock", status: "1",
}, },
{ {
id: 5, id: 5,
name: "Wheat Seeds", name: "Wheat Seeds",
category: "Seeds", category: "1",
type: "Plantation",
quantity: 250, quantity: 250,
unit: "packets", unit: "packets",
lastUpdated: "2023-03-20", lastUpdated: "2023-03-20",
status: "In Stock", status: "2",
}, },
]; ];
} }
@ -108,7 +117,6 @@ export async function createInventoryItem(
id: Math.floor(Math.random() * 1000), id: Math.floor(Math.random() * 1000),
name: item.name, name: item.name,
category: item.category, category: item.category,
type: item.type,
quantity: item.quantity, quantity: item.quantity,
unit: item.unit, unit: item.unit,
lastUpdated: new Date().toISOString(), lastUpdated: new Date().toISOString(),

View File

@ -33,6 +33,7 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { InventoryItemStatus, InventoryItemCategory } from "@/types";
// import { updateInventoryItem } from "@/api/inventory"; // import { updateInventoryItem } from "@/api/inventory";
// import type { UpdateInventoryItemInput } from "@/types"; // import type { UpdateInventoryItemInput } from "@/types";
@ -41,9 +42,10 @@ export interface EditInventoryItemProps {
name: string; name: string;
category: string; category: string;
status: string; status: string;
type: string;
unit: string; unit: string;
quantity: number; quantity: number;
fetchedInventoryStatus: InventoryItemStatus[];
fetchedInventoryCategory: InventoryItemCategory[];
} }
export function EditInventoryItem({ export function EditInventoryItem({
@ -51,13 +53,13 @@ export function EditInventoryItem({
name, name,
category, category,
status, status,
type,
unit, unit,
quantity, quantity,
fetchedInventoryStatus,
fetchedInventoryCategory,
}: EditInventoryItemProps) { }: EditInventoryItemProps) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [itemName, setItemName] = useState(name); const [itemName, setItemName] = useState(name);
const [itemType, setItemType] = useState(type);
const [itemCategory, setItemCategory] = useState(category); const [itemCategory, setItemCategory] = useState(category);
const [itemQuantity, setItemQuantity] = useState(quantity); const [itemQuantity, setItemQuantity] = useState(quantity);
const [itemUnit, setItemUnit] = useState(unit); const [itemUnit, setItemUnit] = useState(unit);
@ -119,17 +121,20 @@ export function EditInventoryItem({
</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 Category
</Label> </Label>
<Select value={itemType.toLowerCase()} onValueChange={setItemType}> <Select value={itemCategory} onValueChange={setItemCategory}>
<SelectTrigger className="col-span-3"> <SelectTrigger className="col-span-3">
<SelectValue placeholder="Select type" /> <SelectValue placeholder="Select type" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectLabel>Type</SelectLabel> <SelectLabel>Category</SelectLabel>
<SelectItem value="plantation">Plantation</SelectItem> {fetchedInventoryCategory.map((categoryItem, _) => (
<SelectItem value="fertilizer">Fertilizer</SelectItem> <SelectItem key={categoryItem.id} value={categoryItem.name}>
{categoryItem.name}
</SelectItem>
))}
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
@ -138,35 +143,22 @@ export function EditInventoryItem({
<Label htmlFor="type" className="text-right"> <Label htmlFor="type" className="text-right">
Status Status
</Label> </Label>
<Select <Select value={itemStatus} onValueChange={setItemStatus}>
value={itemStatus.toLowerCase()}
onValueChange={setItemStatus}
>
<SelectTrigger className="col-span-3"> <SelectTrigger className="col-span-3">
<SelectValue placeholder="Select status" /> <SelectValue placeholder="Select status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectGroup> <SelectGroup>
<SelectLabel>Status</SelectLabel> <SelectLabel>Status</SelectLabel>
<SelectItem value="in stock">In Stock</SelectItem> {fetchedInventoryStatus.map((statusItem, _) => (
<SelectItem value="low stock">Low Stock</SelectItem> <SelectItem key={statusItem.id} value={statusItem.name}>
<SelectItem value="out of stock">Out Of Stock</SelectItem> {statusItem.name}
</SelectItem>
))}
</SelectGroup> </SelectGroup>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="category" className="text-right">
Category
</Label>
<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"> <div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="quantity" className="text-right"> <Label htmlFor="quantity" className="text-right">
Quantity Quantity

View File

@ -31,7 +31,11 @@ import { Search } from "lucide-react";
import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa"; import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { fetchInventoryItems, fetchInventoryStatus } from "@/api/inventory"; import {
fetchInventoryItems,
fetchInventoryStatus,
fetchInventoryCategory,
} from "@/api/inventory";
import { AddInventoryItem } from "./add-inventory-item"; import { AddInventoryItem } from "./add-inventory-item";
import { import {
EditInventoryItem, EditInventoryItem,
@ -65,14 +69,33 @@ export default function InventoryPage() {
queryFn: fetchInventoryStatus, queryFn: fetchInventoryStatus,
staleTime: 60 * 1000, staleTime: 60 * 1000,
}); });
const {
data: inventoryCategory = [],
isLoading: isLoadingCategory,
isError: isErrorCategory,
} = useQuery({
queryKey: ["inventoryCategory"],
queryFn: fetchInventoryCategory,
staleTime: 60 * 1000,
});
// console.table(inventoryItems); // console.table(inventoryItems);
console.table(inventoryStatus); // console.table(inventoryStatus);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const filteredItems = useMemo(() => { const filteredItems = useMemo(() => {
return inventoryItems return inventoryItems
.map((item) => ({ .map((item) => ({
...item, ...item,
id: String(item.id), // Convert `id` to string here status:
inventoryStatus.find(
(statusItem) => statusItem.id.toString() === item.status
)?.name || "",
category:
inventoryCategory.find(
(categoryItem) => categoryItem.id.toString() === item.category
)?.name || "",
fetchedInventoryStatus: inventoryStatus,
fetchedInventoryCategory: inventoryCategory,
id: String(item.id),
})) }))
.filter((item) => .filter((item) =>
item.name.toLowerCase().includes(searchTerm.toLowerCase()) item.name.toLowerCase().includes(searchTerm.toLowerCase())
@ -110,7 +133,11 @@ export default function InventoryPage() {
accessorKey: "edit", accessorKey: "edit",
header: "Edit", header: "Edit",
cell: ({ row }: { row: { original: EditInventoryItemProps } }) => ( cell: ({ row }: { row: { original: EditInventoryItemProps } }) => (
<EditInventoryItem {...row.original} /> <EditInventoryItem
{...row.original}
fetchedInventoryStatus={inventoryStatus}
fetchedInventoryCategory={inventoryCategory}
/>
), ),
enableSorting: false, enableSorting: false,
}, },
@ -133,13 +160,13 @@ export default function InventoryPage() {
onPaginationChange: setPagination, onPaginationChange: setPagination,
}); });
if (isItemLoading || isLoadingStatus) if (isItemLoading || isLoadingStatus || isLoadingCategory)
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className="flex min-h-screen items-center justify-center">
Loading... Loading...
</div> </div>
); );
if (isItemError || isErrorStatus) if (isItemError || isErrorStatus || isErrorCategory)
return ( return (
<div className="flex min-h-screen items-center justify-center"> <div className="flex min-h-screen items-center justify-center">
Error loading inventory data. Error loading inventory data.

View File

@ -63,7 +63,6 @@ export type InventoryItem = {
id: number; id: number;
name: string; name: string;
category: string; category: string;
type: string;
quantity: number; quantity: number;
unit: string; unit: string;
lastUpdated: string; lastUpdated: string;
@ -74,7 +73,15 @@ export type InventoryItemStatus = {
name: string; name: string;
}; };
export type CreateInventoryItemInput = Omit<InventoryItem, "id" | "lastUpdated" | "status">; export type InventoryItemCategory = {
id: number;
name: string;
};
export type CreateInventoryItemInput = Omit<
InventoryItem,
"id" | "lastUpdated" | "status"
>;
export interface Blog { export interface Blog {
id: number; id: number;