diff --git a/barkmanAPI/Migrations/20250122011527_postyFirst.Designer.cs b/barkmanAPI/Migrations/20250124040847_status.Designer.cs similarity index 63% rename from barkmanAPI/Migrations/20250122011527_postyFirst.Designer.cs rename to barkmanAPI/Migrations/20250124040847_status.Designer.cs index 50a6aa3..d183c10 100644 --- a/barkmanAPI/Migrations/20250122011527_postyFirst.Designer.cs +++ b/barkmanAPI/Migrations/20250124040847_status.Designer.cs @@ -11,8 +11,8 @@ using barkmanapi; namespace barkmanapi.Migrations { [DbContext(typeof(BarkContext))] - [Migration("20250122011527_postyFirst")] - partial class postyFirst + [Migration("20250124040847_status")] + partial class status { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -59,15 +59,50 @@ namespace barkmanapi.Migrations .HasColumnType("text") .HasColumnName("serial_number"); - b.Property("Status") + b.Property("StatusId") .HasColumnType("text") - .HasColumnName("status"); + .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("Items") + .HasForeignKey("StatusId") + .HasConstraintName("fk_inventory_item_status_status_id"); + + b.Navigation("Status"); + }); + + modelBuilder.Entity("barkmanapi.ItemStatus", b => + { + b.Navigation("Items"); + }); #pragma warning restore 612, 618 } } diff --git a/barkmanAPI/Migrations/20250122011527_postyFirst.cs b/barkmanAPI/Migrations/20250124040847_status.cs similarity index 58% rename from barkmanAPI/Migrations/20250122011527_postyFirst.cs rename to barkmanAPI/Migrations/20250124040847_status.cs index 599a405..ca4e049 100644 --- a/barkmanAPI/Migrations/20250122011527_postyFirst.cs +++ b/barkmanAPI/Migrations/20250124040847_status.cs @@ -6,11 +6,23 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace barkmanapi.Migrations { /// - public partial class postyFirst : Migration + public partial class status : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { + migrationBuilder.CreateTable( + name: "item_status", + columns: table => new + { + id = table.Column(type: "text", nullable: false), + name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("pk_item_status", x => x.id); + }); + migrationBuilder.CreateTable( name: "inventory", columns: table => new @@ -20,7 +32,7 @@ namespace barkmanapi.Migrations name = table.Column(type: "text", nullable: false), brand = table.Column(type: "text", nullable: false), serial_number = table.Column(type: "text", nullable: true), - status = table.Column(type: "text", nullable: true), + status_id = table.Column(type: "text", nullable: true), rental_price = table.Column(type: "real", nullable: true), replacement_cost = table.Column(type: "real", nullable: true), notes = table.Column(type: "text", nullable: true) @@ -28,7 +40,17 @@ namespace barkmanapi.Migrations constraints: table => { table.PrimaryKey("pk_inventory", x => x.id); + table.ForeignKey( + name: "fk_inventory_item_status_status_id", + column: x => x.status_id, + principalTable: "item_status", + principalColumn: "id"); }); + + migrationBuilder.CreateIndex( + name: "ix_inventory_status_id", + table: "inventory", + column: "status_id"); } /// @@ -36,6 +58,9 @@ namespace barkmanapi.Migrations { migrationBuilder.DropTable( name: "inventory"); + + migrationBuilder.DropTable( + name: "item_status"); } } } diff --git a/barkmanAPI/Migrations/BarkContextModelSnapshot.cs b/barkmanAPI/Migrations/BarkContextModelSnapshot.cs index ab7aae2..10252f0 100644 --- a/barkmanAPI/Migrations/BarkContextModelSnapshot.cs +++ b/barkmanAPI/Migrations/BarkContextModelSnapshot.cs @@ -56,15 +56,45 @@ namespace barkmanapi.Migrations .HasColumnType("text") .HasColumnName("serial_number"); - b.Property("Status") + b.Property("StatusId") .HasColumnType("text") - .HasColumnName("status"); + .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/Program.cs b/barkmanAPI/Program.cs index 6c46e45..a98a7fd 100644 --- a/barkmanAPI/Program.cs +++ b/barkmanAPI/Program.cs @@ -8,56 +8,70 @@ var allowSpecificOrigins = "_AllowSpecificOrigins"; builder.Services.AddDbContext(); builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(c => +builder.Services.AddOpenApi("document", c => { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "BarkMan API", Description = "BARK BARK WOOF WOOF", Version = "v1" }); + c.AddDocumentTransformer((doc, _, _) => + { + doc.Info.Version = "v1"; + doc.Info.Title = "BarkMan API"; + doc.Info.Description = "BARK BARK WOOF WOOF ARF"; + return Task.CompletedTask; + }); }); builder.Services.AddCors(options => { options.AddPolicy(name: allowSpecificOrigins, - policy => + policy => { - policy.WithOrigins("https://barkdev.ts.drewr.io", "http://localhost:5173").AllowAnyMethod().AllowAnyHeader(); + policy.WithOrigins("https://barkdev.ts.drewr.io", "http://localhost:5173").AllowAnyMethod() + .AllowAnyHeader(); }); }); -builder.Services.AddDbContext(opt => +builder.Services.AddDbContext(opt => opt.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")).UseSnakeCaseNamingConvention()); var app = builder.Build(); if (!app.Environment.IsProduction()) { - app.UseSwagger(); - app.UseSwaggerUI(c => - { - c.SwaggerEndpoint("/swagger/v1/swagger.json", "Bark Inventory API V1"); - }); + app.MapOpenApi(); + app.UseSwaggerUI(c => { c.SwaggerEndpoint("/openapi/document.json", "Bark Productions API V1"); }); } -app.MapGet("/", () => "Hello World!"); +var inventoryGroup = app.MapGroup(prefix: "/inventory") + .WithTags("Inventory") + .WithDescription("Endpoints for managing inventory items"); -app.MapGet("/inventory", async (BarkContext db) => - await db.Inventory.OrderBy(item=>item.Id).ToListAsync()); +var itemStatusGroup = app.MapGroup(prefix: "/itemstatus") + .WithTags("Item Status") + .WithDescription("Endpoints for managing item status"); -app.MapGet("/inventory/{id}", async (int id, BarkContext db) => +inventoryGroup.MapGet("", async (BarkContext db) => + await db.Inventory.OrderBy(item => item.Id).ToListAsync()); + +inventoryGroup.MapGet("/{id}", async (int id, BarkContext db) => { - var item = await db.Inventory.FindAsync(id); + var item = await db.Inventory + .Include(item => item.Status) + .FirstOrDefaultAsync(i => i.Id == id); + if (item == null) { return Results.NotFound(new { Message = "Inventory item not found" }); } + return Results.Ok(item); }); -app.MapPut("/inventory/{id}", async (int id, InventoryItems updatedItem, BarkContext db) => +inventoryGroup.MapPut("/{id}", async (int id, InventoryItems updatedItem, BarkContext db) => { var existingItem = await db.Inventory.FindAsync(id); if (existingItem == null) { return Results.NotFound(new { Message = "Inventory item not found" }); } - + existingItem.Name = updatedItem.Name; existingItem.Brand = updatedItem.Brand; existingItem.SerialNumber = updatedItem.SerialNumber; @@ -68,27 +82,78 @@ app.MapPut("/inventory/{id}", async (int id, InventoryItems updatedItem, BarkCon await db.SaveChangesAsync(); return Results.Ok(existingItem); - }); +}); -app.MapPost("/inventory", async (InventoryItems newItem, BarkContext db) => +inventoryGroup.MapPost("", async (InventoryItems newItem, BarkContext db) => { db.Inventory.Add(newItem); await db.SaveChangesAsync(); return Results.Created($"/inventory/{newItem.Id}", newItem); }); -app.MapDelete("/inventory/{id}", async (int id, BarkContext db) => +inventoryGroup.MapDelete("/{id}", async (int id, BarkContext db) => { var item = await db.Inventory.FindAsync(id); - if (item == null) { + if (item == null) + { return Results.NotFound(new { Message = "Inventory item not found" }); } + db.Inventory.Remove(item); await db.SaveChangesAsync(); return Results.Ok(new { Message = "Inventory item deleted successfully" }); }); +itemStatusGroup.MapGet("/", async (BarkContext db) => + await db.ItemStatus.ToListAsync()); + +itemStatusGroup.MapPost("/", async (ItemStatus newItemStatus, BarkContext db) => +{ + db.ItemStatus.Add(newItemStatus); + await db.SaveChangesAsync(); + return Results.Created($"/itemstatus/{newItemStatus.Id}", newItemStatus); +}); + +itemStatusGroup.MapGet("/{id}", async (string id, BarkContext db) => +{ + var itemStatus = await db.ItemStatus.FindAsync(id); + if (itemStatus == null) + { + return Results.NotFound(new { Message = "Item Status not found" }); + } + + return Results.Ok(itemStatus); +}); + +itemStatusGroup.MapPut("/{id}", async (string id, ItemStatus updatedStatus, BarkContext db) => +{ + var existingStatus = await db.ItemStatus.FindAsync(id); + if (existingStatus == null) + { + return Results.NotFound(new { Message = "Inventory item not found" }); + } + + existingStatus.Id = updatedStatus.Id; + existingStatus.Name = updatedStatus.Name; + + await db.SaveChangesAsync(); + return Results.Ok(existingStatus); +}); + +itemStatusGroup.MapDelete("/{id}", async (string id, BarkContext db) => +{ + var itemStatus = await db.ItemStatus.FindAsync(id); + if (itemStatus == null) + { + return Results.NotFound(new { Message = "Item status not found" }); + } + + db.ItemStatus.Remove(itemStatus); + await db.SaveChangesAsync(); + return Results.Ok(new { Message = "Item status deleted successfully" }); +}); + using (var serviceScope = app.Services.CreateScope()) { var dbContext = serviceScope.ServiceProvider.GetRequiredService(); @@ -97,4 +162,4 @@ using (var serviceScope = app.Services.CreateScope()) app.UseCors(allowSpecificOrigins); -app.Run(); +app.Run(); \ No newline at end of file diff --git a/barkmanAPI/barkDbModel.cs b/barkmanAPI/barkDbModel.cs index 88b538c..77cce27 100644 --- a/barkmanAPI/barkDbModel.cs +++ b/barkmanAPI/barkDbModel.cs @@ -5,6 +5,7 @@ namespace barkmanapi; public class BarkContext(DbContextOptions options) : DbContext(options) { public DbSet Inventory { get; set; } + public DbSet ItemStatus { get; set; } } public class InventoryItems @@ -13,8 +14,15 @@ public class InventoryItems public string Name { get; set; } public string Brand { get; set; } public string? SerialNumber { get; set; } - public string? Status { get; set; } + public ItemStatus Status { get; set; } + public string? StatusId { get; set; } public float? RentalPrice { get; set; } public float? ReplacementCost { get; set; } public string? Notes { get; set; } +} + +public class ItemStatus +{ + public string Id { get; set; } + public string Name { get; set; } } \ No newline at end of file diff --git a/barkmanAPI/barkmanapi.csproj b/barkmanAPI/barkmanapi.csproj index fa775f1..0cb694f 100644 --- a/barkmanAPI/barkmanapi.csproj +++ b/barkmanAPI/barkmanapi.csproj @@ -19,4 +19,8 @@ + + + + diff --git a/barkmanui/src/features/inventory/EditItem.tsx b/barkmanui/src/features/inventory/EditItem.tsx index 97fb7e7..5c34c6e 100644 --- a/barkmanui/src/features/inventory/EditItem.tsx +++ b/barkmanui/src/features/inventory/EditItem.tsx @@ -15,7 +15,8 @@ function EditItem() { initialValues: { name: "", brand: "", - status: "", + statusId: "", + status: {name: "", id: ""}, serialNumber: "", rentalPrice: 0, replacementCost: 0, @@ -94,8 +95,8 @@ function EditItem() { placeholder="Brand" {...editItemForm.getInputProps('brand')}/> - + {data.id} {data.brand} {data.name} - {data.status} + {data.statusId} Details diff --git a/barkmanui/src/features/inventory/ItemDetail.tsx b/barkmanui/src/features/inventory/ItemDetail.tsx index 81c7e00..2d17c0b 100644 --- a/barkmanui/src/features/inventory/ItemDetail.tsx +++ b/barkmanui/src/features/inventory/ItemDetail.tsx @@ -32,7 +32,7 @@ function ItemDetail() { ID: {data.id} Brand: {data.brand} Name: {data.name} - Status: {data.status} + Status: {data.status.name} Serial Number: {data.serialNumber} Rental Price: ${data.rentalPrice} Replacement Cost: ${data.replacementCost} diff --git a/barkmanui/src/features/inventory/types.ts b/barkmanui/src/features/inventory/types.ts index 62b5908..627572b 100644 --- a/barkmanui/src/features/inventory/types.ts +++ b/barkmanui/src/features/inventory/types.ts @@ -2,7 +2,8 @@ export interface InventoryItem { id: number, brand: string, name: string, - status: string, + status: {id: string; name: string}, + statusId: string; serialNumber: string, rentalPrice: number, replacementCost: number,