@@ -93,6 +125,7 @@
@foreach (var card in HomePageModel.TrustCards)
{
+
}
diff --git a/Components/Pages/admin/Admin.razor.cs b/Components/Pages/admin/Admin.razor.cs
index d21b581..1be3ab1 100644
--- a/Components/Pages/admin/Admin.razor.cs
+++ b/Components/Pages/admin/Admin.razor.cs
@@ -1,13 +1,17 @@
using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
namespace ApplianceRepair.Components.Pages.admin
{
- public partial class Admin(HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader, RepairRequestReader repairRequestReader) : ComponentBase
+ public partial class Admin(HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader, RepairRequestReader repairRequestReader, RepairRequestMediaReader repairRequestMediaReader) : ComponentBase
{
public HomePageModel? HomePageModel;
public BusinessInfoModel? BusinessInfo;
public List
? RepairRequests;
+ public List? SelectedRequestMedia;
+ public int SelectedRequestMediaImageIndex = 0;
+
private enum AdminTab { Home, Requests, BusinessInfo }
private AdminTab CurrentTab = AdminTab.Home;
@@ -28,6 +32,29 @@ namespace ApplianceRepair.Components.Pages.admin
});
}
+ private async void RefreshContentCards()
+ {
+ if (HomePageModel == null)
+ {
+ return;
+ }
+
+ var servicesList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Services)) ?? [];
+ var trustList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Trust)) ?? [];
+
+ HomePageModel.ServicesCards.Clear();
+ foreach (var card in servicesList)
+ {
+ HomePageModel.ServicesCards.Add(new ContentCardModel(card));
+ }
+
+ HomePageModel.TrustCards.Clear();
+ foreach (var card in trustList)
+ {
+ HomePageModel.TrustCards.Add(new ContentCardModel(card));
+ }
+ }
+
private async void RevertHomePageModel()
{
var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig;
@@ -69,6 +96,12 @@ namespace ApplianceRepair.Components.Pages.admin
await businessConfigReader.UpdateRecord(BusinessInfo);
}
+ private async Task DeleteContentCard(ContentCardModel card)
+ {
+ await contentCardReader.DeleteRecord(card);
+ RefreshContentCards();
+ }
+
private void AddServiceCard()
{
HomePageModel?.ServicesCards.Add(new ContentCardModel() {
@@ -93,5 +126,58 @@ namespace ApplianceRepair.Components.Pages.admin
Text = "Short Description"
});
}
+
+ private async Task ViewRequestImages(RepairRequestModel request)
+ {
+ if (!string.IsNullOrEmpty(request.RequestNumber))
+ {
+ SelectedRequestMedia = await repairRequestMediaReader.ReadAllByRequestNumber(request.RequestNumber);
+ SelectedRequestMediaImageIndex = 0;
+ await JS.InvokeVoidAsync("eval", $"document.getElementById('imageViewerModal').showModal()");
+ }
+ }
+
+ private async Task CloseImageViewer()
+ {
+ await JS.InvokeVoidAsync("eval", $"document.getElementById('imageViewerModal').close()");
+ SelectedRequestMedia = [];
+ }
+
+ private async Task ImageViewerModal_PrevImage()
+ {
+ if (SelectedRequestMedia == null) return;
+
+ SelectedRequestMediaImageIndex++;
+ if (SelectedRequestMediaImageIndex >= SelectedRequestMedia.Count())
+ {
+ SelectedRequestMediaImageIndex = 0;
+ }
+ }
+
+ private async Task ImageViewerModal_NextImage()
+ {
+ if (SelectedRequestMedia == null) return;
+ SelectedRequestMediaImageIndex--;
+ if (SelectedRequestMediaImageIndex < 0)
+ {
+ SelectedRequestMediaImageIndex = SelectedRequestMedia.Count() - 1;
+ }
+ }
+
+ private string GetWebPath(string fullPath = "")
+ {
+ if (string.IsNullOrEmpty(fullPath)) return "";
+
+ var marker = "wwwroot";
+ var index = fullPath.IndexOf(marker);
+
+ if (index != -1)
+ {
+ // Returns "/uploads/filename.jpg"
+ return fullPath.Substring(index + marker.Length).Replace('\\', '/');
+ }
+
+ return fullPath;
+ }
}
}
diff --git a/Components/Pages/admin/Admin.razor.css b/Components/Pages/admin/Admin.razor.css
index eddde49..486c4d0 100644
--- a/Components/Pages/admin/Admin.razor.css
+++ b/Components/Pages/admin/Admin.razor.css
@@ -5,6 +5,107 @@
font-family: 'Open Sans', sans-serif;
}
+/* Image Viewer Modal */
+.image-viewer-modal[open] {
+ border: none;
+ border-radius: 12px;
+ padding: 0;
+ max-width: 90vw;
+ width: 1000px;
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
+ background-color: #fff;
+ overflow: hidden;
+}
+
+.image-viewer-modal::backdrop {
+ background-color: rgba(0, 0, 0, 0.75);
+ backdrop-filter: blur(4px);
+}
+
+.image-viewer-modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem 1.5rem;
+ background-color: #f8f9fa;
+ border-bottom: 1px solid #e9ecef;
+ font-weight: 600;
+}
+
+.image-viewer-image-stage {
+ flex-grow: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 400px;
+ max-height: 70vh;
+ background-color: #000;
+ border-radius: 8px;
+ position: relative;
+ overflow: hidden;
+}
+
+.image-viewer-image-stage img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+ display: block;
+}
+
+.image-viewer-carousel-container {
+ display: flex;
+ align-items: center;
+ padding: 1.5rem;
+ gap: 1rem;
+ background-color: #f1f3f5;
+}
+
+.image-viewer-nav-btn {
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-radius: 50%;
+ width: 45px;
+ height: 45px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ font-size: 1.2rem;
+ transition: all 0.2s ease;
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ z-index: 10;
+}
+
+.image-viewer-nav-btn:hover {
+ background-color: #007bff;
+ color: white;
+ transform: scale(1.1);
+}
+
+.image-viewer-nav-btn:active {
+ transform: scale(0.95);
+}
+
+.image-viewer-image-counter {
+ position: absolute;
+ bottom: 10px;
+ background: rgba(0, 0, 0, 0.6);
+ color: white;
+ padding: 4px 12px;
+ border-radius: 20px;
+ font-size: 0.85rem;
+}
+
+.image-viewer-no-images {
+ width: 100%;
+ text-align: center;
+ padding: 3rem;
+ color: #6c757d;
+ font-style: italic;
+}
+/* Image viewer modal */
+
.admin-header {
text-align: center;
margin-bottom: 40px;
@@ -109,12 +210,12 @@ label {
border: 1px solid #e0e6ed;
border-radius: 12px;
padding: 20px;
-
transition: transform 0.2s ease;
-
display: flex;
flex-direction: column;
align-content: center;
+
+ position: relative;
}
.content-card:hover {
@@ -294,6 +395,17 @@ label {
animation: fadeIn 0.5s ease;
}
+.close-btn {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ background: none;
+ border: none;
+ font-size: 20px;
+ cursor: pointer;
+ line-height: 1;
+}
+
@keyframes fadeIn {
from {
opacity: 0;
diff --git a/wwwroot/uploads/exvwoomm.qgm.jpg b/Components/Pages/admin/Modal.razor
similarity index 100%
rename from wwwroot/uploads/exvwoomm.qgm.jpg
rename to Components/Pages/admin/Modal.razor
diff --git a/Migrations/20260424050555_InitialCreate.Designer.cs b/Migrations/20260424050555_InitialCreate.Designer.cs
deleted file mode 100644
index 7baa601..0000000
--- a/Migrations/20260424050555_InitialCreate.Designer.cs
+++ /dev/null
@@ -1,173 +0,0 @@
-//
-using System;
-using ApplianceRepair;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-#nullable disable
-
-namespace ApplianceRepair.Migrations
-{
- [DbContext(typeof(DatabaseContext))]
- [Migration("20260424050555_InitialCreate")]
- partial class InitialCreate
- {
- ///
- protected override void BuildTargetModel(ModelBuilder modelBuilder)
- {
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
-
- modelBuilder.Entity("ApplianceRepair.BusinessConfigRecord", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("CreatedAt")
- .HasColumnType("TEXT");
-
- b.Property("Name")
- .HasColumnType("TEXT");
-
- b.Property("PhoneNumber")
- .HasColumnType("TEXT");
-
- b.Property("SupportEmail")
- .HasColumnType("TEXT");
-
- b.Property("UpdatedAt")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.ToTable("BusinessConfig");
- });
-
- modelBuilder.Entity("ApplianceRepair.ContentCardRecord", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("BelongsToPage")
- .HasColumnType("TEXT");
-
- b.Property("CreatedAt")
- .HasColumnType("TEXT");
-
- b.Property("Group")
- .HasColumnType("TEXT");
-
- b.Property("Header")
- .HasColumnType("TEXT");
-
- b.Property("Text")
- .HasColumnType("TEXT");
-
- b.Property("UpdatedAt")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.ToTable("ContentCards");
- });
-
- modelBuilder.Entity("ApplianceRepair.HomePageRecord", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("BookHeaderText")
- .HasColumnType("TEXT");
-
- b.Property("CallHeaderText")
- .HasColumnType("TEXT");
-
- b.Property("CreatedAt")
- .HasColumnType("TEXT");
-
- b.Property("HeaderLine1")
- .HasColumnType("TEXT");
-
- b.Property("HeaderLine2")
- .HasColumnType("TEXT");
-
- b.Property("HeaderText")
- .HasColumnType("TEXT");
-
- b.Property("SecondaryHeaderText")
- .HasColumnType("TEXT");
-
- b.Property("UpdatedAt")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.ToTable("HomePage");
- });
-
- modelBuilder.Entity("ApplianceRepair.RepairRequestMediaRecord", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("CreatedAt")
- .HasColumnType("TEXT");
-
- b.Property("MediaPath")
- .HasColumnType("TEXT");
-
- b.Property("RequestNumber")
- .HasColumnType("TEXT");
-
- b.Property("UpdatedAt")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.ToTable("RepairRequestMedia");
- });
-
- modelBuilder.Entity("ApplianceRepair.RepairRequestRecord", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("Brand")
- .HasColumnType("TEXT");
-
- b.Property("CreatedAt")
- .HasColumnType("TEXT");
-
- b.Property("Name")
- .HasColumnType("TEXT");
-
- b.Property("Notes")
- .HasColumnType("TEXT");
-
- b.Property("Phone")
- .HasColumnType("TEXT");
-
- b.Property("RequestNumber")
- .HasColumnType("TEXT");
-
- b.Property("Type")
- .HasColumnType("TEXT");
-
- b.Property("UpdatedAt")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.ToTable("RepairRequests");
- });
-#pragma warning restore 612, 618
- }
- }
-}
diff --git a/Migrations/20260424050555_InitialCreate.cs b/Migrations/20260424050555_InitialCreate.cs
deleted file mode 100644
index 40d9bb7..0000000
--- a/Migrations/20260424050555_InitialCreate.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace ApplianceRepair.Migrations
-{
- ///
- public partial class InitialCreate : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "BusinessConfig",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- CreatedAt = table.Column(type: "TEXT", nullable: false),
- UpdatedAt = table.Column(type: "TEXT", nullable: false),
- Name = table.Column(type: "TEXT", nullable: true),
- PhoneNumber = table.Column(type: "TEXT", nullable: true),
- SupportEmail = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_BusinessConfig", x => x.Id);
- });
-
- migrationBuilder.CreateTable(
- name: "ContentCards",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- CreatedAt = table.Column(type: "TEXT", nullable: false),
- UpdatedAt = table.Column(type: "TEXT", nullable: false),
- BelongsToPage = table.Column(type: "TEXT", nullable: true),
- Group = table.Column(type: "TEXT", nullable: true),
- Header = table.Column(type: "TEXT", nullable: true),
- Text = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_ContentCards", x => x.Id);
- });
-
- migrationBuilder.CreateTable(
- name: "HomePage",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- CreatedAt = table.Column(type: "TEXT", nullable: false),
- UpdatedAt = table.Column(type: "TEXT", nullable: false),
- HeaderLine1 = table.Column(type: "TEXT", nullable: true),
- HeaderLine2 = table.Column(type: "TEXT", nullable: true),
- HeaderText = table.Column(type: "TEXT", nullable: true),
- CallHeaderText = table.Column(type: "TEXT", nullable: true),
- BookHeaderText = table.Column(type: "TEXT", nullable: true),
- SecondaryHeaderText = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_HomePage", x => x.Id);
- });
-
- migrationBuilder.CreateTable(
- name: "RepairRequestMedia",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- CreatedAt = table.Column(type: "TEXT", nullable: false),
- UpdatedAt = table.Column(type: "TEXT", nullable: false),
- RequestNumber = table.Column(type: "TEXT", nullable: true),
- MediaPath = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_RepairRequestMedia", x => x.Id);
- });
-
- migrationBuilder.CreateTable(
- name: "RepairRequests",
- columns: table => new
- {
- Id = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- CreatedAt = table.Column(type: "TEXT", nullable: false),
- UpdatedAt = table.Column(type: "TEXT", nullable: false),
- RequestNumber = table.Column(type: "TEXT", nullable: true),
- Type = table.Column(type: "TEXT", nullable: true),
- Brand = table.Column(type: "TEXT", nullable: true),
- Notes = table.Column(type: "TEXT", nullable: true),
- Name = table.Column(type: "TEXT", nullable: true),
- Phone = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_RepairRequests", x => x.Id);
- });
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropTable(
- name: "BusinessConfig");
-
- migrationBuilder.DropTable(
- name: "ContentCards");
-
- migrationBuilder.DropTable(
- name: "HomePage");
-
- migrationBuilder.DropTable(
- name: "RepairRequestMedia");
-
- migrationBuilder.DropTable(
- name: "RepairRequests");
- }
- }
-}
diff --git a/Migrations/20260502001727_InitialDeployment.Designer.cs b/Migrations/20260502001727_InitialDeployment.Designer.cs
new file mode 100644
index 0000000..c15aec2
--- /dev/null
+++ b/Migrations/20260502001727_InitialDeployment.Designer.cs
@@ -0,0 +1,421 @@
+//
+using System;
+using ApplianceRepair;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace ApplianceRepair.Migrations
+{
+ [DbContext(typeof(DatabaseContext))]
+ [Migration("20260502001727_InitialDeployment")]
+ partial class InitialDeployment
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
+
+ modelBuilder.Entity("ApplianceRepair.BusinessConfigRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("SupportEmail")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("BusinessConfig");
+ });
+
+ modelBuilder.Entity("ApplianceRepair.ContentCardRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("BelongsToPage")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Group")
+ .HasColumnType("TEXT");
+
+ b.Property("Header")
+ .HasColumnType("TEXT");
+
+ b.Property("Text")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("ContentCards");
+ });
+
+ modelBuilder.Entity("ApplianceRepair.HomePageRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("BookHeaderText")
+ .HasColumnType("TEXT");
+
+ b.Property("CallHeaderText")
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("HeaderLine1")
+ .HasColumnType("TEXT");
+
+ b.Property("HeaderLine2")
+ .HasColumnType("TEXT");
+
+ b.Property("HeaderText")
+ .HasColumnType("TEXT");
+
+ b.Property("SecondaryHeaderText")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("HomePage");
+ });
+
+ modelBuilder.Entity("ApplianceRepair.RepairRequestMediaRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("MediaPath")
+ .HasColumnType("TEXT");
+
+ b.Property("RequestNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("RepairRequestMedia");
+ });
+
+ modelBuilder.Entity("ApplianceRepair.RepairRequestRecord", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Brand")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("CreatedAt")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Notes")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Phone")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("RequestNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("RepairRequests");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordHash")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("INTEGER");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("TEXT");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderKey")
+ .HasColumnType("TEXT");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("LoginProvider")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ 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/Migrations/20260502001727_InitialDeployment.cs b/Migrations/20260502001727_InitialDeployment.cs
new file mode 100644
index 0000000..3c10dda
--- /dev/null
+++ b/Migrations/20260502001727_InitialDeployment.cs
@@ -0,0 +1,328 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ApplianceRepair.Migrations
+{
+ ///
+ public partial class InitialDeployment : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "AspNetRoles",
+ columns: table => new
+ {
+ Id = table.Column(type: "TEXT", nullable: false),
+ Name = table.Column(type: "TEXT", maxLength: 256, nullable: true),
+ NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true),
+ ConcurrencyStamp = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetRoles", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUsers",
+ columns: table => new
+ {
+ Id = table.Column(type: "TEXT", nullable: false),
+ UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true),
+ NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true),
+ Email = table.Column(type: "TEXT", maxLength: 256, nullable: true),
+ NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true),
+ EmailConfirmed = table.Column(type: "INTEGER", nullable: false),
+ PasswordHash = table.Column(type: "TEXT", nullable: true),
+ SecurityStamp = table.Column(type: "TEXT", nullable: true),
+ ConcurrencyStamp = table.Column(type: "TEXT", nullable: true),
+ PhoneNumber = table.Column(type: "TEXT", nullable: true),
+ PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false),
+ TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false),
+ LockoutEnd = table.Column(type: "TEXT", nullable: true),
+ LockoutEnabled = table.Column(type: "INTEGER", nullable: false),
+ AccessFailedCount = table.Column(type: "INTEGER", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUsers", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "BusinessConfig",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ CreatedAt = table.Column(type: "TEXT", nullable: false),
+ UpdatedAt = table.Column(type: "TEXT", nullable: false),
+ Name = table.Column(type: "TEXT", nullable: true),
+ PhoneNumber = table.Column(type: "TEXT", nullable: true),
+ SupportEmail = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_BusinessConfig", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "ContentCards",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ CreatedAt = table.Column(type: "TEXT", nullable: false),
+ UpdatedAt = table.Column(type: "TEXT", nullable: false),
+ BelongsToPage = table.Column(type: "TEXT", nullable: true),
+ Group = table.Column(type: "TEXT", nullable: true),
+ Header = table.Column(type: "TEXT", nullable: true),
+ Text = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_ContentCards", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "HomePage",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ CreatedAt = table.Column(type: "TEXT", nullable: false),
+ UpdatedAt = table.Column(type: "TEXT", nullable: false),
+ HeaderLine1 = table.Column(type: "TEXT", nullable: true),
+ HeaderLine2 = table.Column(type: "TEXT", nullable: true),
+ HeaderText = table.Column(type: "TEXT", nullable: true),
+ CallHeaderText = table.Column(type: "TEXT", nullable: true),
+ BookHeaderText = table.Column(type: "TEXT", nullable: true),
+ SecondaryHeaderText = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_HomePage", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "RepairRequestMedia",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ CreatedAt = table.Column(type: "TEXT", nullable: false),
+ UpdatedAt = table.Column(type: "TEXT", nullable: false),
+ RequestNumber = table.Column(type: "TEXT", nullable: true),
+ MediaPath = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_RepairRequestMedia", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "RepairRequests",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ CreatedAt = table.Column(type: "TEXT", nullable: false),
+ UpdatedAt = table.Column(type: "TEXT", nullable: false),
+ RequestNumber = table.Column(type: "TEXT", nullable: true),
+ Type = table.Column(type: "TEXT", nullable: false),
+ Brand = table.Column(type: "TEXT", nullable: false),
+ Notes = table.Column(type: "TEXT", nullable: false),
+ Name = table.Column(type: "TEXT", nullable: false),
+ Phone = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_RepairRequests", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetRoleClaims",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ RoleId = table.Column(type: "TEXT", nullable: false),
+ ClaimType = table.Column(type: "TEXT", nullable: true),
+ ClaimValue = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
+ table.ForeignKey(
+ name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
+ column: x => x.RoleId,
+ principalTable: "AspNetRoles",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserClaims",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ UserId = table.Column(type: "TEXT", nullable: false),
+ ClaimType = table.Column(type: "TEXT", nullable: true),
+ ClaimValue = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
+ table.ForeignKey(
+ name: "FK_AspNetUserClaims_AspNetUsers_UserId",
+ column: x => x.UserId,
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserLogins",
+ columns: table => new
+ {
+ LoginProvider = table.Column(type: "TEXT", nullable: false),
+ ProviderKey = table.Column(type: "TEXT", nullable: false),
+ ProviderDisplayName = table.Column(type: "TEXT", nullable: true),
+ UserId = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
+ table.ForeignKey(
+ name: "FK_AspNetUserLogins_AspNetUsers_UserId",
+ column: x => x.UserId,
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserRoles",
+ columns: table => new
+ {
+ UserId = table.Column(type: "TEXT", nullable: false),
+ RoleId = table.Column(type: "TEXT", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
+ table.ForeignKey(
+ name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
+ column: x => x.RoleId,
+ principalTable: "AspNetRoles",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_AspNetUserRoles_AspNetUsers_UserId",
+ column: x => x.UserId,
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "AspNetUserTokens",
+ columns: table => new
+ {
+ UserId = table.Column(type: "TEXT", nullable: false),
+ LoginProvider = table.Column(type: "TEXT", nullable: false),
+ Name = table.Column(type: "TEXT", nullable: false),
+ Value = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
+ table.ForeignKey(
+ name: "FK_AspNetUserTokens_AspNetUsers_UserId",
+ column: x => x.UserId,
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetRoleClaims_RoleId",
+ table: "AspNetRoleClaims",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "RoleNameIndex",
+ table: "AspNetRoles",
+ column: "NormalizedName",
+ unique: true);
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetUserClaims_UserId",
+ table: "AspNetUserClaims",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetUserLogins_UserId",
+ table: "AspNetUserLogins",
+ column: "UserId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_AspNetUserRoles_RoleId",
+ table: "AspNetUserRoles",
+ column: "RoleId");
+
+ migrationBuilder.CreateIndex(
+ name: "EmailIndex",
+ table: "AspNetUsers",
+ column: "NormalizedEmail");
+
+ migrationBuilder.CreateIndex(
+ name: "UserNameIndex",
+ table: "AspNetUsers",
+ column: "NormalizedUserName",
+ unique: true);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "AspNetRoleClaims");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserClaims");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserLogins");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserRoles");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUserTokens");
+
+ migrationBuilder.DropTable(
+ name: "BusinessConfig");
+
+ migrationBuilder.DropTable(
+ name: "ContentCards");
+
+ migrationBuilder.DropTable(
+ name: "HomePage");
+
+ migrationBuilder.DropTable(
+ name: "RepairRequestMedia");
+
+ migrationBuilder.DropTable(
+ name: "RepairRequests");
+
+ migrationBuilder.DropTable(
+ name: "AspNetRoles");
+
+ migrationBuilder.DropTable(
+ name: "AspNetUsers");
+ }
+ }
+}
diff --git a/Migrations/DatabaseContextModelSnapshot.cs b/Migrations/DatabaseContextModelSnapshot.cs
index a642903..549d731 100644
--- a/Migrations/DatabaseContextModelSnapshot.cs
+++ b/Migrations/DatabaseContextModelSnapshot.cs
@@ -137,24 +137,29 @@ namespace ApplianceRepair.Migrations
.HasColumnType("INTEGER");
b.Property("Brand")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("CreatedAt")
.HasColumnType("TEXT");
b.Property("Name")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("Notes")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("Phone")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("RequestNumber")
.HasColumnType("TEXT");
b.Property("Type")
+ .IsRequired()
.HasColumnType("TEXT");
b.Property("UpdatedAt")
@@ -164,6 +169,249 @@ namespace ApplianceRepair.Migrations
b.ToTable("RepairRequests");
});
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ClaimType")
+ .HasColumnType("TEXT");
+
+ b.Property("ClaimValue")
+ .HasColumnType("TEXT");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("TEXT");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("TEXT");
+
+ b.Property