diff --git a/frontend/api/inventory.ts b/frontend/api/inventory.ts index e1fb1d2..ae9c465 100644 --- a/frontend/api/inventory.ts +++ b/frontend/api/inventory.ts @@ -3,6 +3,8 @@ import type { InventoryItem, InventoryItemStatus, InventoryItemCategory, + CreateInventoryItemInput, + UpdateInventoryItemInput, } from "@/types"; /** @@ -39,9 +41,11 @@ export async function fetchInventoryCategory(): Promise< export async function fetchInventoryItems(): Promise { try { - const response = await axiosInstance.get("/api/inventory"); + const response = await axiosInstance.get("/inventory"); return response.data; } catch (error) { + // console.error("Error while fetching inventory items! " + error); + // throw error; // Fallback dummy data return [ { @@ -93,25 +97,42 @@ export async function fetchInventoryItems(): Promise { } } -/** - * 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 + item: Omit ): Promise { - // Simulate network delay - await new Promise((resolve) => setTimeout(resolve, 500)); try { const response = await axiosInstance.post( - "/api/inventory", + "/inventory", item ); return response.data; } catch (error) { - console.error("Error while creating Inventory Item!" + error); + console.error("Error while creating Inventory Item! " + error); throw new Error("Failed to create inventory item: " + error); } } + +export async function deleteInventoryItem(id: string) { + try { + const response = await axiosInstance.delete("/inventory/" + id); + return response.data; + } catch (error) { + console.error("Error while deleting Inventory Item! " + error); + throw new Error("Failed to deleting inventory item: " + error); + } +} +export async function updateInventoryItem( + id: string, + item: UpdateInventoryItemInput +) { + try { + const response = await axiosInstance.put( + "/inventory/" + id, + item + ); + return response.data; + } catch (error) { + console.error("Error while updating Inventory Item! " + error); + throw new Error("Failed to updating inventory item: " + error); + } +} diff --git a/frontend/app/(sidebar)/inventory/add-inventory-item.tsx b/frontend/app/(sidebar)/inventory/add-inventory-item.tsx index 067b441..76a5522 100644 --- a/frontend/app/(sidebar)/inventory/add-inventory-item.tsx +++ b/frontend/app/(sidebar)/inventory/add-inventory-item.tsx @@ -60,23 +60,13 @@ export function AddInventoryItem({ const [itemUnit, setItemUnit] = useState(""); const [itemStatus, setItemStatus] = useState(""); - // const { - // data: inventoryItems = [], - // isLoading: isItemLoading, - // isError: isItemError, - // } = useQuery({ - // queryKey: ["inventoryItems"], - // queryFn: fetchInventoryItems, - // staleTime: 60 * 1000, - // }); - const mutation = useMutation({ mutationFn: (item: CreateInventoryItemInput) => createInventoryItem(item), onSuccess: () => { - // Invalidate queries to refresh inventory data. + // invalidate queries to refresh inventory data. const queryClient = useQueryClient(); queryClient.invalidateQueries({ queryKey: ["inventoryItems"] }); - // Reset form fields and close dialog. + // reset form fields and close dialog. setItemName(""); setItemCategory(""); setItemQuantity(0); @@ -86,9 +76,27 @@ export function AddInventoryItem({ }, }); + const inputStates = [itemName, itemCategory, itemUnit, itemStatus, date]; + const isInputValid = inputStates.every((input) => input); + const handleSave = () => { - // Basic validation (you can extend this as needed) - if (!itemName || !itemCategory || !itemUnit) return; + if (!isInputValid) { + console.error("All fields are required"); + return; + } + + const newItem: CreateInventoryItemInput = { + name: itemName, + categoryId: + inventoryCategory.find((item) => item.name === itemCategory)?.id || 0, + quantity: itemQuantity, + unitId: harvestUnits.find((item) => item.name === itemUnit)?.id || 0, + statusId: + inventoryStatus.find((item) => item.name === itemStatus)?.id || 0, + lastUpdated: date ? date.toISOString() : new Date().toISOString(), + }; + console.table(newItem); + mutation.mutate(newItem); }; return ( @@ -163,7 +171,7 @@ export function AddInventoryItem({ id="quantity" type="number" className="col-span-3" - value={itemQuantity} + value={itemQuantity === 0 ? "" : itemQuantity} onChange={(e) => setItemQuantity(Number(e.target.value))} /> diff --git a/frontend/app/(sidebar)/inventory/delete-inventory-item.tsx b/frontend/app/(sidebar)/inventory/delete-inventory-item.tsx index 56163eb..677c095 100644 --- a/frontend/app/(sidebar)/inventory/delete-inventory-item.tsx +++ b/frontend/app/(sidebar)/inventory/delete-inventory-item.tsx @@ -8,13 +8,26 @@ import { DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { deleteInventoryItem } from "@/api/inventory"; export function DeleteInventoryItem({ id }: { id: string }) { const [open, setOpen] = useState(false); + const queryClient = useQueryClient(); + + const { mutate: deleteItem, status } = useMutation({ + mutationFn: deleteInventoryItem, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["inventoryItems"] }); + setOpen(false); // Close dialog on success + }, + onError: (error) => { + console.error("Failed to delete item:", error); + }, + }); const handleDelete = () => { - console.log(`Item with ID ${id} deleted.`); - setOpen(false); + deleteItem(id.toString()); }; return ( @@ -47,8 +60,9 @@ export function DeleteInventoryItem({ id }: { id: string }) { diff --git a/frontend/app/(sidebar)/inventory/edit-inventory-item.tsx b/frontend/app/(sidebar)/inventory/edit-inventory-item.tsx index 6ee2603..0740170 100644 --- a/frontend/app/(sidebar)/inventory/edit-inventory-item.tsx +++ b/frontend/app/(sidebar)/inventory/edit-inventory-item.tsx @@ -32,8 +32,8 @@ import { InventoryItemCategory, HarvestUnits, } from "@/types"; -// import { updateInventoryItem } from "@/api/inventory"; -// import type { UpdateInventoryItemInput } from "@/types"; +import { updateInventoryItem } from "@/api/inventory"; +import type { UpdateInventoryItemInput } from "@/types"; export interface EditInventoryItemProps { id: string; @@ -76,35 +76,73 @@ export function EditInventoryItem({ (statusItem) => statusItem.id.toString() === statusId )?.name ); + const [error, setError] = useState(null); - // const queryClient = useQueryClient(); + const queryClient = useQueryClient(); - // const mutation = useMutation({ - // mutationFn: (item: UpdateInventoryItemInput) => UpdateInventoryItem(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 mutation = useMutation({ + mutationFn: (item: UpdateInventoryItemInput) => + updateInventoryItem(id, item), + onSuccess: () => { + // invalidate queries to refresh inventory data. + queryClient.invalidateQueries({ queryKey: ["inventoryItems"] }); + // reset form fields and close dialog. + setItemName(""); + setItemCategory(""); + setItemQuantity(0); + setItemUnit(""); + setOpen(false); + setItemStatus(""); + }, + }); + // send edit request const handleEdit = () => { - // // 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, - // }); + if (!itemName || !itemCategory || !itemUnit) { + setError("All fields are required. Please fill in missing details."); + return; + } + + const category = fetchedInventoryCategory.find( + (c) => c.name === itemCategory + ); + const unit = fetchedHarvestUnits.find((u) => u.name === itemUnit); + const status = fetchedInventoryStatus.find((s) => s.name === itemStatus); + + if (!category || !unit || !status) { + setError( + "Invalid category, unit, or status. Please select a valid option." + ); + return; + } + // console.table({ + // name: itemName, + // categoryId: + // fetchedInventoryCategory.find( + // (category) => category.name === itemCategory + // )?.id ?? 0, + // quantity: itemQuantity, + // unitId: + // fetchedHarvestUnits.find((unit) => unit.name === itemUnit)?.id ?? 0, + // statusId: + // fetchedInventoryStatus.find((status) => status.name === itemStatus) + // ?.id ?? 0, + // lastUpdated: new Date().toISOString(), + // }); + mutation.mutate({ + name: itemName, + categoryId: + fetchedInventoryCategory.find( + (category) => category.name === itemCategory + )?.id ?? 0, + quantity: itemQuantity, + unitId: + fetchedHarvestUnits.find((unit) => unit.name === itemUnit)?.id ?? 0, + statusId: + fetchedInventoryStatus.find((status) => status.name === itemStatus) + ?.id ?? 0, + lastUpdated: new Date().toISOString(), + }); }; return ( @@ -205,6 +243,7 @@ export function EditInventoryItem({ + {error &&

{error}

} diff --git a/frontend/app/(sidebar)/inventory/page.tsx b/frontend/app/(sidebar)/inventory/page.tsx index 9765528..4b0668a 100644 --- a/frontend/app/(sidebar)/inventory/page.tsx +++ b/frontend/app/(sidebar)/inventory/page.tsx @@ -22,11 +22,13 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; +import { TriangleAlertIcon } from "lucide-react"; import { Pagination, PaginationContent, PaginationItem, } from "@/components/ui/pagination"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Search } from "lucide-react"; import { FaSort, FaSortUp, FaSortDown } from "react-icons/fa"; import { fetchHarvestUnits } from "@/api/harvest"; @@ -92,6 +94,8 @@ export default function InventoryPage() { ////////////////////////////// // console.table(inventoryItems); // console.table(inventoryStatus); + // console.table(harvestUnits); + const [searchTerm, setSearchTerm] = useState(""); const filteredItems = useMemo(() => { return inventoryItems @@ -195,6 +199,22 @@ export default function InventoryPage() { const isLoading = loadingStates.some((loading) => loading); const isError = errorStates.some((error) => error); + if (inventoryItems.length === 0) { + return ( +
+ +
+ + No Inventory Data + + You currently have no inventory items. Add a new item to get + started! + +
+
+
+ ); + } if (isLoading) return (
diff --git a/frontend/types.ts b/frontend/types.ts index ea753e7..4be8579 100644 --- a/frontend/types.ts +++ b/frontend/types.ts @@ -82,10 +82,16 @@ export type HarvestUnits = { name: string; }; -export type CreateInventoryItemInput = Omit< - InventoryItem, - "id" | "lastUpdated" | "status" ->; +export type CreateInventoryItemInput = { + name: string; + categoryId: number; + quantity: number; + unitId: number; + lastUpdated: string; + statusId: number; +}; + +export type UpdateInventoryItemInput = CreateInventoryItemInput & {}; export interface Blog { id: number;