feat: refactor inventory item deletion and editing logic for improved error handling and structure

This commit is contained in:
THIS ONE IS A LITTLE BIT TRICKY KRUB 2025-04-03 21:30:41 +07:00
parent f037bf766f
commit 63d38fb99d
6 changed files with 98 additions and 78 deletions

View File

@ -160,7 +160,6 @@ type GetInventoryItemOutput struct {
type DeleteInventoryItemInput struct { type DeleteInventoryItemInput struct {
Header string `header:"Authorization" required:"true" example:"Bearer token"` Header string `header:"Authorization" required:"true" example:"Bearer token"`
UserID string `header:"userId" required:"true" example:"user-uuid"`
ID string `path:"id"` ID string `path:"id"`
} }
@ -347,7 +346,8 @@ func (a *api) updateInventoryItemHandler(ctx context.Context, input *UpdateInven
} }
func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInventoryItemInput) (*DeleteInventoryItemOutput, error) { func (a *api) deleteInventoryItemHandler(ctx context.Context, input *DeleteInventoryItemInput) (*DeleteInventoryItemOutput, error) {
err := a.inventoryRepo.Delete(ctx, input.ID, input.UserID) userID, err := a.getUserIDFromHeader(input.Header)
err = a.inventoryRepo.Delete(ctx, input.ID, userID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -92,9 +92,32 @@ export async function deleteInventoryItem(id: string) {
try { try {
const response = await axiosInstance.delete("/inventory/" + id); const response = await axiosInstance.delete("/inventory/" + id);
return response.data; return response.data;
} catch (error) { } catch (error: unknown) {
console.error("Error while deleting Inventory Item! " + error); // Cast error to AxiosError to safely access response properties
throw new Error("Failed to deleting inventory item: " + error); if (error instanceof AxiosError && error.response) {
// Log the detailed error message
console.error("Error while deleting Inventory Item!");
console.error("Response Status:", error.response.status); // e.g., 422
console.error("Error Detail:", error.response.data?.detail); // Custom error message from backend
console.error("Full Error Response:", error.response.data); // Entire error object (including details)
// Throw a new error with a more specific message
throw new Error(
`Failed to delete inventory item: ${
error.response.data?.detail || error.message
}`
);
} else {
// Handle other errors (e.g., network errors or unknown errors)
console.error(
"Error while deleting Inventory Item, unknown error:",
error
);
throw new Error(
"Failed to delete inventory item: " +
(error instanceof Error ? error.message : "Unknown error")
);
}
} }
} }
export async function updateInventoryItem( export async function updateInventoryItem(

View File

@ -19,7 +19,6 @@ export function DeleteInventoryItem({ id }: { id: string }) {
mutationFn: deleteInventoryItem, mutationFn: deleteInventoryItem,
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["inventoryItems"] }); queryClient.invalidateQueries({ queryKey: ["inventoryItems"] });
setOpen(false); // Close dialog on success
}, },
onError: (error) => { onError: (error) => {
console.error("Failed to delete item:", error); console.error("Failed to delete item:", error);
@ -32,7 +31,7 @@ export function DeleteInventoryItem({ id }: { id: string }) {
return ( return (
<div> <div>
{/* trigger button for the confirmation dialog */} {/* delete confirmation dialog */}
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
@ -48,8 +47,6 @@ export function DeleteInventoryItem({ id }: { id: string }) {
Are you sure you want to delete this item? This action cannot be Are you sure you want to delete this item? This action cannot be
undone. undone.
</DialogDescription> </DialogDescription>
{/* footer with confirm and cancel buttons */}
<DialogFooter> <DialogFooter>
<Button <Button
className="bg-gray-500 hover:bg-gray-700 text-white" className="bg-gray-500 hover:bg-gray-700 text-white"

View File

@ -27,47 +27,40 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { InventoryStatus, InventoryItemCategory, HarvestUnits } from "@/types"; import {
InventoryStatus,
InventoryItemCategory,
HarvestUnits,
UpdateInventoryItemInput,
} from "@/types";
import { updateInventoryItem } from "@/api/inventory"; import { updateInventoryItem } from "@/api/inventory";
import type { UpdateInventoryItemInput } from "@/types";
export interface EditInventoryItemProps {
id: string;
name: string;
categoryId: number;
statusId: number;
unitId: number;
quantity: number;
fetchedInventoryStatus: InventoryStatus[];
fetchedInventoryCategory: InventoryItemCategory[];
fetchedHarvestUnits: HarvestUnits[];
}
export function EditInventoryItem({ export function EditInventoryItem({
id, item,
name,
categoryId,
statusId,
unitId,
quantity,
fetchedInventoryStatus, fetchedInventoryStatus,
fetchedInventoryCategory, fetchedInventoryCategory,
fetchedHarvestUnits, fetchedHarvestUnits,
}: EditInventoryItemProps) { }: {
item: UpdateInventoryItemInput;
fetchedInventoryStatus: InventoryStatus[];
fetchedInventoryCategory: InventoryItemCategory[];
fetchedHarvestUnits: HarvestUnits[];
}) {
console.table(item);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [itemName, setItemName] = useState(name); const [itemName, setItemName] = useState(item.name);
const [itemCategory, setItemCategory] = useState( const [itemCategory, setItemCategory] = useState(
fetchedInventoryCategory.find( fetchedInventoryCategory.find((x) => x.id === item.categoryId)?.name
(categoryItem) => categoryItem.id === categoryId
)?.name
); );
const [itemQuantity, setItemQuantity] = useState(quantity);
const [itemQuantity, setItemQuantity] = useState(item.quantity);
const [itemUnit, setItemUnit] = useState( const [itemUnit, setItemUnit] = useState(
fetchedHarvestUnits.find((harvestItem) => harvestItem.id === unitId)?.name fetchedHarvestUnits.find((x) => x.id === item.unitId)?.name
); );
const [itemStatus, setItemStatus] = useState( const [itemStatus, setItemStatus] = useState(
fetchedInventoryStatus.find((statusItem) => statusItem.id === statusId) fetchedInventoryStatus.find((x) => x.id === item.statusId)?.name
?.name
); );
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@ -75,7 +68,7 @@ export function EditInventoryItem({
const mutation = useMutation({ const mutation = useMutation({
mutationFn: (item: UpdateInventoryItemInput) => mutationFn: (item: UpdateInventoryItemInput) =>
updateInventoryItem(id, item), updateInventoryItem(item.id, item),
onSuccess: () => { onSuccess: () => {
// invalidate queries to refresh inventory data. // invalidate queries to refresh inventory data.
queryClient.invalidateQueries({ queryKey: ["inventoryItems"] }); queryClient.invalidateQueries({ queryKey: ["inventoryItems"] });
@ -108,26 +101,9 @@ export function EditInventoryItem({
); );
return; 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({ mutation.mutate({
name: itemName, name: itemName,
categoryId: categoryId: item.categoryId,
fetchedInventoryCategory.find(
(category) => category.name === itemCategory
)?.id ?? 0,
quantity: itemQuantity, quantity: itemQuantity,
unitId: unitId:
fetchedHarvestUnits.find((unit) => unit.name === itemUnit)?.id ?? 0, fetchedHarvestUnits.find((unit) => unit.name === itemUnit)?.id ?? 0,
@ -135,6 +111,7 @@ export function EditInventoryItem({
fetchedInventoryStatus.find((status) => status.name === itemStatus) fetchedInventoryStatus.find((status) => status.name === itemStatus)
?.id ?? 0, ?.id ?? 0,
dateAdded: new Date().toISOString(), dateAdded: new Date().toISOString(),
id: "",
}); });
}; };

View File

@ -40,11 +40,9 @@ import {
fetchInventoryCategory, fetchInventoryCategory,
} from "@/api/inventory"; } from "@/api/inventory";
import { AddInventoryItem } from "./add-inventory-item"; import { AddInventoryItem } from "./add-inventory-item";
import { import { EditInventoryItem } from "./edit-inventory-item";
EditInventoryItem,
EditInventoryItemProps,
} from "./edit-inventory-item";
import { DeleteInventoryItem } from "./delete-inventory-item"; import { DeleteInventoryItem } from "./delete-inventory-item";
import { InventoryItem } from "@/types";
export default function InventoryPage() { export default function InventoryPage() {
const [sorting, setSorting] = useState<SortingState>([]); const [sorting, setSorting] = useState<SortingState>([]);
@ -105,12 +103,9 @@ export default function InventoryPage() {
return inventoryItems return inventoryItems
.map((item) => ({ .map((item) => ({
...item, ...item,
status: item.status.name, status: { id: item.status.id, name: item.status.name },
category: item.category.name, category: { id: item.category.id, name: item.category.name },
categoryId: item.categoryId, unit: { id: item.unit.id, name: item.unit.name },
unit: item.unit.name,
unitId: item.unitId,
statusId: item.statusId,
fetchedInventoryStatus: inventoryStatus, fetchedInventoryStatus: inventoryStatus,
fetchedInventoryCategory: inventoryCategory, fetchedInventoryCategory: inventoryCategory,
fetchedHarvestUnits: harvestUnits, fetchedHarvestUnits: harvestUnits,
@ -132,15 +127,31 @@ export default function InventoryPage() {
const columns = [ const columns = [
{ accessorKey: "name", header: "Name" }, { accessorKey: "name", header: "Name" },
{ accessorKey: "category", header: "Category" }, {
{ accessorKey: "quantity", header: "Quantity" }, accessorKey: "category",
{ accessorKey: "unit", header: "Unit" }, header: "Category",
{ accessorKey: "lastUpdated", header: "Last Updated" }, cell: ({ row }: { row: { original: InventoryItem } }) =>
row.original.category.name,
},
{
accessorKey: "quantity",
header: "Quantity",
},
{
accessorKey: "unit",
header: "Unit",
cell: ({ row }: { row: { original: InventoryItem } }) =>
row.original.unit.name,
},
{
accessorKey: "lastUpdated",
header: "Last Updated",
},
{ {
accessorKey: "status", accessorKey: "status",
header: "Status", header: "Status",
cell: (info: { getValue: () => string }) => { cell: ({ row }: { row: { original: InventoryItem } }) => {
const status = info.getValue(); const status = row.original.status.name;
let statusClass = ""; let statusClass = "";
@ -166,11 +177,20 @@ export default function InventoryPage() {
{ {
accessorKey: "edit", accessorKey: "edit",
header: "Edit", header: "Edit",
cell: ({ row }: { row: { original: EditInventoryItemProps } }) => ( cell: ({ row }: { row: { original: InventoryItem } }) => (
<EditInventoryItem <EditInventoryItem
{...row.original} item={{
id: row.original.id,
name: row.original.name,
categoryId: row.original.categoryId,
quantity: row.original.quantity,
unitId: row.original.unitId,
dateAdded: row.original.dateAdded,
statusId: row.original.statusId,
}}
fetchedInventoryStatus={inventoryStatus} fetchedInventoryStatus={inventoryStatus}
fetchedInventoryCategory={inventoryCategory} fetchedInventoryCategory={inventoryCategory}
fetchedHarvestUnits={harvestUnits}
/> />
), ),
enableSorting: false, enableSorting: false,
@ -178,7 +198,7 @@ export default function InventoryPage() {
{ {
accessorKey: "delete", accessorKey: "delete",
header: "Delete", header: "Delete",
cell: ({ row }: { row: { original: EditInventoryItemProps } }) => ( cell: ({ row }: { row: { original: InventoryItem } }) => (
<DeleteInventoryItem id={row.original.id} /> <DeleteInventoryItem id={row.original.id} />
), ),
enableSorting: false, enableSorting: false,

View File

@ -160,9 +160,12 @@ export type CreateInventoryItemInput = {
statusId: number; statusId: number;
}; };
export type UpdateInventoryItemInput = CreateInventoryItemInput & {}; // export type UpdateInventoryItemInput = CreateInventoryItemInput & {};
// export type EditInventoryItemInput = CreateInventoryItemInput & { id: number };
// export type UpdateInventoryItemInput = Partial<CreateInventoryItemInput> & { id: string }; export type UpdateInventoryItemInput = Partial<CreateInventoryItemInput> & {
id: string;
};
export interface Blog { export interface Blog {
id: number; id: number;