From 525ced2567383799e7e74ad5c330454825bbe529 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:49:29 +0000 Subject: [PATCH 1/2] Initial plan From ac753205dfd6e9fe16eba6fb4f79c4a52f7057fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 20:54:49 +0000 Subject: [PATCH 2/2] Add inventory management feature with model, migration, controller, and views Agent-Logs-Url: https://github.com/SpeakingInBits/MyCraftShop/sessions/a3371dae-ddef-49c8-86e9-dcd655a0d623 Co-authored-by: JoeProgrammer88 <7156063+JoeProgrammer88@users.noreply.github.com> --- .../Controllers/InventoryController.cs | 129 ++++++++ MyCraftShop/Data/ApplicationDbContext.cs | 2 + ...0260407205138_AddInventoryItem.Designer.cs | 312 ++++++++++++++++++ .../20260407205138_AddInventoryItem.cs | 38 +++ .../ApplicationDbContextModelSnapshot.cs | 112 ++++--- MyCraftShop/Models/InventoryItem.cs | 30 ++ MyCraftShop/Views/Inventory/Create.cshtml | 56 ++++ MyCraftShop/Views/Inventory/Index.cshtml | 153 +++++++++ MyCraftShop/Views/Inventory/_EditModal.cshtml | 45 +++ MyCraftShop/Views/Shared/_Layout.cshtml | 3 + 10 files changed, 841 insertions(+), 39 deletions(-) create mode 100644 MyCraftShop/Controllers/InventoryController.cs create mode 100644 MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.Designer.cs create mode 100644 MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.cs create mode 100644 MyCraftShop/Models/InventoryItem.cs create mode 100644 MyCraftShop/Views/Inventory/Create.cshtml create mode 100644 MyCraftShop/Views/Inventory/Index.cshtml create mode 100644 MyCraftShop/Views/Inventory/_EditModal.cshtml diff --git a/MyCraftShop/Controllers/InventoryController.cs b/MyCraftShop/Controllers/InventoryController.cs new file mode 100644 index 0000000..0d7192f --- /dev/null +++ b/MyCraftShop/Controllers/InventoryController.cs @@ -0,0 +1,129 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using MyCraftShop.Data; +using MyCraftShop.Models; + +namespace MyCraftShop.Controllers +{ + public class InventoryController : Controller + { + private readonly ApplicationDbContext _context; + private const int PageSize = 10; + + public InventoryController(ApplicationDbContext context) + { + _context = context; + } + + // GET: Inventory + public async Task Index(int page = 1) + { + var totalItems = await _context.InventoryItems.CountAsync(); + var totalPages = (int)Math.Ceiling(totalItems / (double)PageSize); + page = Math.Max(1, Math.Min(page, Math.Max(1, totalPages))); + + var items = await _context.InventoryItems + .OrderBy(i => i.Name) + .Skip((page - 1) * PageSize) + .Take(PageSize) + .ToListAsync(); + + ViewBag.CurrentPage = page; + ViewBag.TotalPages = totalPages; + + return View(items); + } + + // GET: Inventory/Create + public IActionResult Create() + { + return View(); + } + + // POST: Inventory/Create + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Create([Bind("Name,Description,CostPerItem,SalesPrice,Quantity")] InventoryItem inventoryItem) + { + if (ModelState.IsValid) + { + _context.Add(inventoryItem); + await _context.SaveChangesAsync(); + return RedirectToAction(nameof(Index)); + } + return View(inventoryItem); + } + + // GET: Inventory/Edit/5 + [HttpGet] + public async Task Edit(int? id) + { + if (id == null) + { + return NotFound(); + } + + var inventoryItem = await _context.InventoryItems.FindAsync(id); + if (inventoryItem == null) + { + return NotFound(); + } + + return PartialView("_EditModal", inventoryItem); + } + + // POST: Inventory/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Edit(int id, [Bind("Id,Name,Description,CostPerItem,SalesPrice,Quantity")] InventoryItem inventoryItem) + { + if (id != inventoryItem.Id) + { + return NotFound(); + } + + if (ModelState.IsValid) + { + try + { + _context.Update(inventoryItem); + await _context.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!InventoryItemExists(inventoryItem.Id)) + { + return NotFound(); + } + else + { + throw; + } + } + return RedirectToAction(nameof(Index)); + } + + return PartialView("_EditModal", inventoryItem); + } + + // POST: Inventory/Delete/5 + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Delete(int id) + { + var inventoryItem = await _context.InventoryItems.FindAsync(id); + if (inventoryItem != null) + { + _context.InventoryItems.Remove(inventoryItem); + await _context.SaveChangesAsync(); + } + + return RedirectToAction(nameof(Index)); + } + + private bool InventoryItemExists(int id) + { + return _context.InventoryItems.Any(e => e.Id == id); + } + } +} diff --git a/MyCraftShop/Data/ApplicationDbContext.cs b/MyCraftShop/Data/ApplicationDbContext.cs index bcb712a..4cc5bb6 100644 --- a/MyCraftShop/Data/ApplicationDbContext.cs +++ b/MyCraftShop/Data/ApplicationDbContext.cs @@ -1,9 +1,11 @@ using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; +using MyCraftShop.Models; namespace MyCraftShop.Data { public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) { + public DbSet InventoryItems { get; set; } } } diff --git a/MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.Designer.cs b/MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.Designer.cs new file mode 100644 index 0000000..6405882 --- /dev/null +++ b/MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.Designer.cs @@ -0,0 +1,312 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using MyCraftShop.Data; + +#nullable disable + +namespace MyCraftShop.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260407205138_AddInventoryItem")] + partial class AddInventoryItem + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("MyCraftShop.Models.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CostPerItem") + .HasColumnType("float"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Quantity") + .HasColumnType("float"); + + b.Property("SalesPrice") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("InventoryItems"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.cs b/MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.cs new file mode 100644 index 0000000..d697faa --- /dev/null +++ b/MyCraftShop/Data/Migrations/20260407205138_AddInventoryItem.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MyCraftShop.Data.Migrations +{ + /// + public partial class AddInventoryItem : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "InventoryItems", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Description = table.Column(type: "nvarchar(max)", nullable: true), + CostPerItem = table.Column(type: "float", nullable: false), + SalesPrice = table.Column(type: "float", nullable: true), + Quantity = table.Column(type: "float", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_InventoryItems", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "InventoryItems"); + } + } +} diff --git a/MyCraftShop/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/MyCraftShop/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 3a75e0d..e572cd6 100644 --- a/MyCraftShop/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/MyCraftShop/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,10 +1,12 @@ -// +// +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using MyCraftShop.Data; -using System; + +#nullable disable namespace MyCraftShop.Data.Migrations { @@ -15,9 +17,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "3.0.0") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -29,29 +32,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Name") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() - .HasName("RoleNameIndex") + .HasDatabaseName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); - b.ToTable("AspNetRoles"); + b.ToTable("AspNetRoles", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -67,7 +71,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RoleId"); - b.ToTable("AspNetRoleClaims"); + b.ToTable("AspNetRoleClaims", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => @@ -83,8 +87,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(max)"); b.Property("Email") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("EmailConfirmed") .HasColumnType("bit"); @@ -96,12 +100,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("NormalizedUserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); @@ -119,28 +123,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bit"); b.Property("UserName") - .HasColumnType("nvarchar(256)") - .HasMaxLength(256); + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); b.HasKey("Id"); b.HasIndex("NormalizedEmail") - .HasName("EmailIndex"); + .HasDatabaseName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() - .HasName("UserNameIndex") + .HasDatabaseName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); - b.ToTable("AspNetUsers"); + b.ToTable("AspNetUsers", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); @@ -156,18 +161,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("AspNetUserClaims"); + b.ToTable("AspNetUserClaims", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderKey") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); @@ -180,7 +185,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("UserId"); - b.ToTable("AspNetUserLogins"); + b.ToTable("AspNetUserLogins", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => @@ -195,7 +200,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RoleId"); - b.ToTable("AspNetUserRoles"); + b.ToTable("AspNetUserRoles", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => @@ -204,19 +209,48 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Name") - .HasColumnType("nvarchar(128)") - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.Property("Value") .HasColumnType("nvarchar(max)"); b.HasKey("UserId", "LoginProvider", "Name"); - b.ToTable("AspNetUserTokens"); + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("MyCraftShop.Models.InventoryItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CostPerItem") + .HasColumnType("float"); + + b.Property("Description") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Quantity") + .HasColumnType("float"); + + b.Property("SalesPrice") + .HasColumnType("float"); + + b.HasKey("Id"); + + b.ToTable("InventoryItems"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => diff --git a/MyCraftShop/Models/InventoryItem.cs b/MyCraftShop/Models/InventoryItem.cs new file mode 100644 index 0000000..9db8d3c --- /dev/null +++ b/MyCraftShop/Models/InventoryItem.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; + +namespace MyCraftShop.Models +{ + public class InventoryItem + { + public int Id { get; set; } + + [Required] + [Display(Name = "Item Name")] + public string Name { get; set; } = string.Empty; + + [Display(Name = "Description")] + public string? Description { get; set; } + + [Required] + [Display(Name = "Cost Per Item")] + [Range(0.01, double.MaxValue, ErrorMessage = "Cost per item must be greater than 0.")] + public double CostPerItem { get; set; } + + [Display(Name = "Sales Price")] + [Range(0.01, double.MaxValue, ErrorMessage = "Sales price must be a positive value.")] + public double? SalesPrice { get; set; } + + [Required] + [Display(Name = "Quantity")] + [Range(0, double.MaxValue, ErrorMessage = "Quantity must be 0 or greater.")] + public double Quantity { get; set; } + } +} diff --git a/MyCraftShop/Views/Inventory/Create.cshtml b/MyCraftShop/Views/Inventory/Create.cshtml new file mode 100644 index 0000000..9f5a9bd --- /dev/null +++ b/MyCraftShop/Views/Inventory/Create.cshtml @@ -0,0 +1,56 @@ +@model MyCraftShop.Models.InventoryItem + +@{ + ViewData["Title"] = "Create Inventory Item"; +} + +
+
+

Create Inventory Item

+
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
Optional. Leave blank if not applicable.
+
+ +
+ + + +
Decimals and percentages are supported (e.g., 0.5 for half).
+
+ +
+ + Back to List +
+
+
+
+ +@section Scripts { + @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} +} diff --git a/MyCraftShop/Views/Inventory/Index.cshtml b/MyCraftShop/Views/Inventory/Index.cshtml new file mode 100644 index 0000000..dced8b7 --- /dev/null +++ b/MyCraftShop/Views/Inventory/Index.cshtml @@ -0,0 +1,153 @@ +@model IEnumerable + +@{ + ViewData["Title"] = "Inventory Management"; + int currentPage = ViewBag.CurrentPage; + int totalPages = ViewBag.TotalPages; +} + +
+

Inventory Management

+ + Create New Item + +
+ +@if (!Model.Any()) +{ +
No inventory items found. Click Create New Item to add one.
+} +else +{ +
+ @foreach (var item in Model) + { +
+
+
+
@item.Name
+ @if (!string.IsNullOrEmpty(item.Description)) + { +

@item.Description

+ } +
    +
  • Cost Per Item: @item.CostPerItem.ToString("C")
  • +
  • Sales Price: @(item.SalesPrice.HasValue ? item.SalesPrice.Value.ToString("C") : "N/A")
  • +
  • Quantity: @item.Quantity.ToString("G")
  • +
+
+ +
+
+ } +
+ + @if (totalPages > 1) + { + + } +} + + + + + + + +@section Scripts { + +} diff --git a/MyCraftShop/Views/Inventory/_EditModal.cshtml b/MyCraftShop/Views/Inventory/_EditModal.cshtml new file mode 100644 index 0000000..a66eff3 --- /dev/null +++ b/MyCraftShop/Views/Inventory/_EditModal.cshtml @@ -0,0 +1,45 @@ +@model MyCraftShop.Models.InventoryItem + +
+ + +
diff --git a/MyCraftShop/Views/Shared/_Layout.cshtml b/MyCraftShop/Views/Shared/_Layout.cshtml index c117468..10444da 100644 --- a/MyCraftShop/Views/Shared/_Layout.cshtml +++ b/MyCraftShop/Views/Shared/_Layout.cshtml @@ -26,6 +26,9 @@ +