diff --git a/barkmanAPI/Migrations/20250203160519_add-barcodes.Designer.cs b/barkmanAPI/Migrations/20250203160519_add-barcodes.Designer.cs new file mode 100644 index 0000000..5611d82 --- /dev/null +++ b/barkmanAPI/Migrations/20250203160519_add-barcodes.Designer.cs @@ -0,0 +1,108 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using barkmanapi; + +#nullable disable + +namespace barkmanapi.Migrations +{ + [DbContext(typeof(BarkContext))] + [Migration("20250203160519_add-barcodes")] + partial class addbarcodes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("barkmanapi.InventoryItems", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Barcode") + .HasColumnType("integer") + .HasColumnName("barcode"); + + b.Property("Brand") + .IsRequired() + .HasColumnType("text") + .HasColumnName("brand"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Notes") + .HasColumnType("text") + .HasColumnName("notes"); + + b.Property("RentalPrice") + .HasColumnType("real") + .HasColumnName("rental_price"); + + b.Property("ReplacementCost") + .HasColumnType("real") + .HasColumnName("replacement_cost"); + + b.Property("SerialNumber") + .HasColumnType("text") + .HasColumnName("serial_number"); + + b.Property("StatusId") + .HasColumnType("text") + .HasColumnName("status_id"); + + b.HasKey("Id") + .HasName("pk_inventory"); + + b.HasIndex("StatusId") + .HasDatabaseName("ix_inventory_status_id"); + + b.ToTable("inventory", (string)null); + }); + + modelBuilder.Entity("barkmanapi.ItemStatus", b => + { + b.Property("Id") + .HasColumnType("text") + .HasColumnName("id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_item_status"); + + b.ToTable("item_status", (string)null); + }); + + modelBuilder.Entity("barkmanapi.InventoryItems", b => + { + b.HasOne("barkmanapi.ItemStatus", "Status") + .WithMany() + .HasForeignKey("StatusId") + .HasConstraintName("fk_inventory_item_status_status_id"); + + b.Navigation("Status"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/barkmanAPI/Migrations/20250203160519_add-barcodes.cs b/barkmanAPI/Migrations/20250203160519_add-barcodes.cs new file mode 100644 index 0000000..15424dc --- /dev/null +++ b/barkmanAPI/Migrations/20250203160519_add-barcodes.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace barkmanapi.Migrations +{ + /// + public partial class addbarcodes : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "barcode", + table: "inventory", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "barcode", + table: "inventory"); + } + } +} diff --git a/barkmanAPI/Migrations/BarkContextModelSnapshot.cs b/barkmanAPI/Migrations/BarkContextModelSnapshot.cs index 10252f0..332a8b9 100644 --- a/barkmanAPI/Migrations/BarkContextModelSnapshot.cs +++ b/barkmanAPI/Migrations/BarkContextModelSnapshot.cs @@ -30,6 +30,10 @@ namespace barkmanapi.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Barcode") + .HasColumnType("integer") + .HasColumnName("barcode"); + b.Property("Brand") .IsRequired() .HasColumnType("text") diff --git a/barkmanAPI/Program.cs b/barkmanAPI/Program.cs index 5549237..5ede613 100644 --- a/barkmanAPI/Program.cs +++ b/barkmanAPI/Program.cs @@ -48,7 +48,7 @@ var itemStatusGroup = app.MapGroup(prefix: "/itemstatus") .WithDescription("Endpoints for managing item status"); inventoryGroup.MapGet("", async (BarkContext db) => - await db.Inventory.OrderBy(item => item.Id).ToListAsync()); + await db.Inventory.OrderBy(item => item.Barcode).ToListAsync()); inventoryGroup.MapGet("/{id}", async (int id, BarkContext db) => { @@ -73,9 +73,10 @@ inventoryGroup.MapPut("/{id}", async (int id, InventoryItems updatedItem, BarkCo } existingItem.Name = updatedItem.Name; + existingItem.Barcode = updatedItem.Barcode; existingItem.Brand = updatedItem.Brand; existingItem.SerialNumber = updatedItem.SerialNumber; - existingItem.Status = updatedItem.Status; + existingItem.StatusId = updatedItem.StatusId; existingItem.RentalPrice = updatedItem.RentalPrice; existingItem.ReplacementCost = updatedItem.ReplacementCost; existingItem.Notes = updatedItem.Notes; @@ -88,6 +89,7 @@ inventoryGroup.MapPost("", async (InventoryItems newItemInput, BarkContext db) = { var newItem = new InventoryItems(); newItem.Name = newItemInput.Name; + newItem.Barcode = newItemInput.Barcode; newItem.Brand = newItemInput.Brand; newItem.SerialNumber = newItemInput.SerialNumber; newItem.RentalPrice = newItemInput.RentalPrice; diff --git a/barkmanAPI/barkDbModel.cs b/barkmanAPI/barkDbModel.cs index 77cce27..1e283bb 100644 --- a/barkmanAPI/barkDbModel.cs +++ b/barkmanAPI/barkDbModel.cs @@ -10,7 +10,8 @@ public class BarkContext(DbContextOptions options) : DbContext(opti public class InventoryItems { - public int Id {get; set;} + public int Id {get; set;} + public int Barcode {get; set;} public string Name { get; set; } public string Brand { get; set; } public string? SerialNumber { get; set; } diff --git a/barkmanui/src/App.tsx b/barkmanui/src/App.tsx index b72505c..ac886f0 100644 --- a/barkmanui/src/App.tsx +++ b/barkmanui/src/App.tsx @@ -43,9 +43,9 @@ function App() { }/> }/> - }/> - }/> - }/> + }/> + }/> + }/> diff --git a/barkmanui/src/features/inventory/AddItem.tsx b/barkmanui/src/features/inventory/AddItem.tsx index 581e430..a335528 100644 --- a/barkmanui/src/features/inventory/AddItem.tsx +++ b/barkmanui/src/features/inventory/AddItem.tsx @@ -2,17 +2,23 @@ import {Button, Group, TextInput, NumberInput, Container, Title, Flex} from '@ma import {useForm} from '@mantine/form'; import {useMutation} from "@tanstack/react-query"; import {NewItem} from "./types.ts"; -import { useNavigate} from "react-router"; -import { IconX, IconCheck } from '@tabler/icons-react'; -import { notifications } from '@mantine/notifications'; +import {useNavigate} from "react-router"; +import {IconX, IconCheck} from '@tabler/icons-react'; +import {notifications} from '@mantine/notifications'; +import useInventoryList from "./hooks/useInventoryList.tsx"; +import {useEffect} from "react"; + function AddItem() { const navigate = useNavigate(); + const inventoryQuery = useInventoryList(); + const newItemForm = useForm({ mode: 'uncontrolled', initialValues: { + barcode: 0, name: "", brand: "", serialNumber: "", @@ -36,8 +42,8 @@ function AddItem() { if (result.ok) { notifications.show({ - icon: , - color:"teal", + icon: , + color: "teal", title: "All good!", message: "Item Created", position: 'top-center', @@ -48,8 +54,8 @@ function AddItem() { if (!result.ok) { notifications.show({ - icon: , - color:"red", + icon: , + color: "red", title: "Bummer!", message: "Something went wrong", position: 'top-center', @@ -57,9 +63,23 @@ function AddItem() { throw new Error('Failed to create inventory item'); } - } + }, + + }) + useEffect(() => { + if (inventoryQuery.data) { + const nextBarcode = inventoryQuery.data[inventoryQuery.data.length - 1].barcode + 1; + newItemForm.setValues({barcode: nextBarcode}); + } + },[inventoryQuery.data]); + + if (inventoryQuery.isPending) return 'Loading...' + if (inventoryQuery.error) return 'An error has occurred: ' + inventoryQuery.error.message + + + return ( <> @@ -74,7 +94,8 @@ function AddItem() { wrap="wrap"> Add Item - + ; function EditItem() { const params = useParams(); + const navigate = useNavigate(); const editItemForm = useForm({ mode: 'uncontrolled', initialValues: { + barcode: 0, name: "", brand: "", statusId: "", @@ -53,7 +57,26 @@ function EditItem() { } }); + if (result.ok) { + notifications.show({ + icon: , + color:"teal", + title: "All good!", + message: "Item Updated", + position: 'top-center', + }); + navigate("/inventory"); + + } + if (!result.ok) { + notifications.show({ + icon: , + color:"red", + title: "Bummer!", + message: "Something went wrong", + position: 'top-center', + }); throw new Error('Failed to update inventory item'); } @@ -90,7 +113,8 @@ function EditItem() {
{isFetching ? 'Updating...' : ''}
Edit Item - ID: {data.id} + => { - const response = await fetch( - import.meta.env.VITE_API_URL + '/inventory', - ) + const inventoryQuery = useInventoryList(); - if (!response.ok) throw new Error('Failed to fetch inventory ' + response.statusText) + if (inventoryQuery.isPending) return 'Loading...' - return await response.json() - }, - }); - - if (isPending) return 'Loading...' - - if (error) return 'An error has occurred: ' + error.message + if (inventoryQuery.error) return 'An error has occurred: ' + inventoryQuery.error.message return ( @@ -37,16 +26,13 @@ function InventoryList() {

ARFF ARFF BARK BARK

- Add Item + Add Item - -
{isFetching ? 'Updating...' : ''}
- - ID + Barcode Brand Item Status @@ -54,14 +40,14 @@ function InventoryList() { - {data.map((data) => ( + {inventoryQuery.data?.map((data) => ( - {data.id} + {data.barcode} {data.brand} {data.name} {data.statusId} - Details + Details ))} diff --git a/barkmanui/src/features/inventory/ItemDetail.tsx b/barkmanui/src/features/inventory/ItemDetail.tsx index 2d17c0b..b1e7a44 100644 --- a/barkmanui/src/features/inventory/ItemDetail.tsx +++ b/barkmanui/src/features/inventory/ItemDetail.tsx @@ -29,7 +29,7 @@ function ItemDetail() { Item Detail
{isFetching ? 'Updating...' : ''}
- ID: {data.id} + Barcode: {data.barcode} Brand: {data.brand} Name: {data.name} Status: {data.status.name} @@ -38,7 +38,7 @@ function ItemDetail() { Replacement Cost: ${data.replacementCost} Notes: {data.notes} - Edit + Edit
) diff --git a/barkmanui/src/features/inventory/hooks/useInventoryList.tsx b/barkmanui/src/features/inventory/hooks/useInventoryList.tsx new file mode 100644 index 0000000..6c1ab01 --- /dev/null +++ b/barkmanui/src/features/inventory/hooks/useInventoryList.tsx @@ -0,0 +1,17 @@ +import {useQuery} from "@tanstack/react-query"; +import {InventoryItem} from "../types.js"; + + const useInventoryList = () => useQuery({ + queryKey: ['inventory'], + queryFn: async (): Promise => { + const response = await fetch( + import.meta.env.VITE_API_URL + '/inventory', + ) + + if (!response.ok) throw new Error('Failed to fetch inventory ' + response.statusText) + + return await response.json() + }, + }); + +export default useInventoryList; \ No newline at end of file diff --git a/barkmanui/src/features/inventory/types.ts b/barkmanui/src/features/inventory/types.ts index 21d4246..4f1c7c4 100644 --- a/barkmanui/src/features/inventory/types.ts +++ b/barkmanui/src/features/inventory/types.ts @@ -1,5 +1,6 @@ export interface InventoryItem { id: number, + barcode: number, brand: string, name: string, status: {id: string; name: string}, @@ -11,6 +12,7 @@ export interface InventoryItem { } export interface NewItem { + barcode: number, brand: string, name: string, serialNumber: string,