bunch o stuff
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
@page "/book"
|
@page "/book"
|
||||||
|
@rendermode InteractiveServer
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
|
||||||
<div class="container booking-page">
|
@if(!Complete)
|
||||||
|
{
|
||||||
|
<div class="container booking-page">
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
<h1>Request an Appointment</h1>
|
<h1>Request an Appointment</h1>
|
||||||
<p>Complete the form below and a technician will review your case.</p>
|
<p>Complete the form below and a technician will review your case.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EditForm Model="@Model" OnValidSubmit="HandleSubmit" class="booking-form-wrapper">
|
<EditForm FormName="RepairRequestForm" Model="Model" OnValidSubmit="HandleSubmit" On class="booking-form-wrapper">
|
||||||
<DataAnnotationsValidator />
|
<DataAnnotationsValidator />
|
||||||
|
|
||||||
<div class="form-grid">
|
<div class="form-grid">
|
||||||
@@ -24,6 +27,7 @@
|
|||||||
<option>Oven / Stove</option>
|
<option>Oven / Stove</option>
|
||||||
<option>Dryer</option>
|
<option>Dryer</option>
|
||||||
</InputSelect>
|
</InputSelect>
|
||||||
|
<ValidationMessage For="@(() => Model.Type)" class="text-danger" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
@@ -42,6 +46,7 @@
|
|||||||
<option>Kenmore</option>
|
<option>Kenmore</option>
|
||||||
<option>Other (please include in notes)</option>
|
<option>Other (please include in notes)</option>
|
||||||
</InputSelect>
|
</InputSelect>
|
||||||
|
<ValidationMessage For="@(() => Model.Brand)" class="text-danger" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
@@ -49,6 +54,7 @@
|
|||||||
<InputTextArea @bind-Value="Model.Notes"
|
<InputTextArea @bind-Value="Model.Notes"
|
||||||
placeholder="e.g. Making a clicking noise, not draining, error code F1E2..."
|
placeholder="e.g. Making a clicking noise, not draining, error code F1E2..."
|
||||||
class="custom-input textarea" />
|
class="custom-input textarea" />
|
||||||
|
<ValidationMessage For="@(() => Model.Notes)" class="text-danger" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -56,7 +62,7 @@
|
|||||||
<h3 class="form-heading">2. Photos & Contact</h3>
|
<h3 class="form-heading">2. Photos & Contact</h3>
|
||||||
|
|
||||||
<div class="upload-zone">
|
<div class="upload-zone">
|
||||||
<p><strong>Upload Photos/Video</strong></p>
|
<p><strong>Upload Photos/Video (10MB max)</strong></p>
|
||||||
<p class="small-text">Tip: A photo of the <u>model number sticker</u> helps us arrive with the right parts!</p>
|
<p class="small-text">Tip: A photo of the <u>model number sticker</u> helps us arrive with the right parts!</p>
|
||||||
<InputFile OnChange="HandleFiles" multiple class="file-input" id="file-upload" />
|
<InputFile OnChange="HandleFiles" multiple class="file-input" id="file-upload" />
|
||||||
<label for="file-upload" class="btn btn-secondary">Add Media</label>
|
<label for="file-upload" class="btn btn-secondary">Add Media</label>
|
||||||
@@ -70,11 +76,13 @@
|
|||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label>Full Name</label>
|
<label>Full Name</label>
|
||||||
<InputText @bind-Value="Model.Name" class="custom-input" />
|
<InputText @bind-Value="Model.Name" class="custom-input" />
|
||||||
|
<ValidationMessage For="@(() => Model.Name)" class="text-danger" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="field-group">
|
<div class="field-group">
|
||||||
<label>Phone Number</label>
|
<label>Phone Number</label>
|
||||||
<InputText @bind-Value="Model.Phone" class="custom-input" />
|
<InputText @bind-Value="Model.Phone" class="custom-input" />
|
||||||
|
<ValidationMessage For="@(() => Model.Phone)" class="text-danger" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,4 +91,18 @@
|
|||||||
<button type="submit" class="btn btn-primary btn-large">Submit Repair Request</button>
|
<button type="submit" class="btn btn-primary btn-large">Submit Repair Request</button>
|
||||||
</div>
|
</div>
|
||||||
</EditForm>
|
</EditForm>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="complete-container">
|
||||||
|
<div class="complete-content">
|
||||||
|
<h1 class="complete-heading">Thank You!</h1>
|
||||||
|
<p class="complete-subheading">We will be contacting you shortly.</p>
|
||||||
|
<p class="complete-subheading">Your request number is: @Model.RequestNumber</p>
|
||||||
|
<NavLink class="btn-home" href="" Match="NavLinkMatch.All">
|
||||||
|
<span class="home-icon">🏠</span> Back to Home
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -2,16 +2,70 @@
|
|||||||
|
|
||||||
namespace ApplianceRepair.Components.Pages
|
namespace ApplianceRepair.Components.Pages
|
||||||
{
|
{
|
||||||
public partial class Book()
|
public static class RequestNumberGenerator
|
||||||
{
|
{
|
||||||
private RepairRequestModel Model = new();
|
private static readonly char[] _chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789".ToCharArray();
|
||||||
private List<IBrowserFile> SelectedFiles = new();
|
|
||||||
|
public static string Generate(int length = 6)
|
||||||
|
{
|
||||||
|
var result = new char[length];
|
||||||
|
// Use Random.Shared in .NET 6+ for thread safety
|
||||||
|
for (int i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
result[i] = _chars[Random.Shared.Next(_chars.Length)];
|
||||||
|
}
|
||||||
|
return new string(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Book(RepairRequestReader repairRequestReader, RepairRequestMediaReader repairRequestMediaReader)
|
||||||
|
{
|
||||||
|
public RepairRequestModel Model = new();
|
||||||
|
public List<IBrowserFile> SelectedFiles = new();
|
||||||
|
public bool Complete = false;
|
||||||
|
|
||||||
private void HandleFiles(InputFileChangeEventArgs e) => SelectedFiles.AddRange(e.GetMultipleFiles());
|
private void HandleFiles(InputFileChangeEventArgs e) => SelectedFiles.AddRange(e.GetMultipleFiles());
|
||||||
|
|
||||||
private async Task HandleSubmit()
|
private async Task HandleSubmit()
|
||||||
{
|
{
|
||||||
// Logic to process the request
|
Model.RequestNumber = RequestNumberGenerator.Generate();
|
||||||
|
Model.CreatedAt = DateTime.Now;
|
||||||
|
Model.UpdatedAt = DateTime.Now;
|
||||||
|
await repairRequestReader.AddRecord(Model);
|
||||||
|
|
||||||
|
var imageUploadPath = Path.Combine(Environment.CurrentDirectory, "wwwroot", "uploads");
|
||||||
|
if (!Directory.Exists(imageUploadPath)) Directory.CreateDirectory(imageUploadPath);
|
||||||
|
|
||||||
|
foreach (var file in SelectedFiles)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var trustedFileName = Path.GetRandomFileName() + Path.GetExtension(file.Name);
|
||||||
|
var path = Path.Combine(imageUploadPath, trustedFileName);
|
||||||
|
|
||||||
|
using var stream = file.OpenReadStream(maxAllowedSize: 1024 * 1024 * 10);
|
||||||
|
using var fileStream = new FileStream(path, FileMode.Create);
|
||||||
|
|
||||||
|
await stream.CopyToAsync(fileStream);
|
||||||
|
|
||||||
|
var mediaRecord = new RepairRequestMediaRecord()
|
||||||
|
{
|
||||||
|
CreatedAt = DateTime.Now,
|
||||||
|
UpdatedAt = DateTime.Now,
|
||||||
|
RequestNumber = Model.RequestNumber,
|
||||||
|
MediaPath = path,
|
||||||
|
};
|
||||||
|
|
||||||
|
await repairRequestMediaReader.AddRecord(mediaRecord);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// probably need to show this to the user somehow, something prettier tho
|
||||||
|
Console.WriteLine($"File: {file.Name} Error: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Complete = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,61 @@
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Match your mobile responsiveness */
|
.complete-container {
|
||||||
|
min-height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.complete-content {
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.complete-heading {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.complete-subheading {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #555;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .btn-home {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 12px 24px;
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: background-color 0.2s ease, transform 0.1s ease;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .btn-home:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .btn-home:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-icon {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.form-grid {
|
.form-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@@ -102,4 +156,12 @@
|
|||||||
.booking-form-wrapper {
|
.booking-form-wrapper {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.complete-heading {
|
||||||
|
font-size: 1.8rem; /* Slightly smaller for small screens */
|
||||||
|
}
|
||||||
|
|
||||||
|
.complete-subheading {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace ApplianceRepair.Components.Pages
|
namespace ApplianceRepair.Components.Pages
|
||||||
{
|
{
|
||||||
public partial class Home(IMemoryCache cache, HomePageReader homePageReader, ContentCardReader contentCardReader)
|
public partial class Home(IMemoryCache cache, HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader)
|
||||||
{
|
{
|
||||||
private HomePageModel? Model;
|
private HomePageModel? Model;
|
||||||
|
|
||||||
@@ -10,7 +10,12 @@ namespace ApplianceRepair.Components.Pages
|
|||||||
{
|
{
|
||||||
if (!cache.TryGetValue(nameof(HomePageModel), out Model))
|
if (!cache.TryGetValue(nameof(HomePageModel), out Model))
|
||||||
{
|
{
|
||||||
Model = await homePageReader.ReadLatestRecordWithModel(contentCardReader) ?? Defaults.DefaultHomePageContent;
|
var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig;
|
||||||
|
var latestHomeRecord = await homePageReader.ReadLatestRecord() ?? Defaults.DefaultHomePageContent;
|
||||||
|
var servicesList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Services)) ?? [];
|
||||||
|
var trustList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Trust)) ?? [];
|
||||||
|
|
||||||
|
Model = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList);
|
||||||
|
|
||||||
var cacheOptions = new MemoryCacheEntryOptions()
|
var cacheOptions = new MemoryCacheEntryOptions()
|
||||||
.SetAbsoluteExpiration(TimeSpan.FromHours(24))
|
.SetAbsoluteExpiration(TimeSpan.FromHours(24))
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ else
|
|||||||
<h1>@Model.HeaderLine1 <br><span>@Model.HeaderLine2</span></h1>
|
<h1>@Model.HeaderLine1 <br><span>@Model.HeaderLine2</span></h1>
|
||||||
<p>@Model.HeaderText</p>
|
<p>@Model.HeaderText</p>
|
||||||
<div class="cta-group">
|
<div class="cta-group">
|
||||||
<a href="phone:@Model.FormattedPhoneNumber" class="btn btn-primary">Call for Same-Day Service</a>
|
<a href="@Model.PhoneNumberCallLink" class="btn btn-primary">Call for Same-Day Service</a>
|
||||||
<a href="/book" class="btn btn-secondary">Request an Appointment</a>
|
<a href="/book" class="btn btn-secondary">Request an Appointment</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,16 +21,22 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<header class="admin-header">
|
<header class="admin-header">
|
||||||
<div class="tab-container">
|
<div class="tab-container">
|
||||||
<button class="tab-btn @(currentTab == AdminTab.Home ? "active" : "")"
|
<button class="tab-btn @(CurrentTab == AdminTab.Home ? "active" : "")"
|
||||||
@onclick="() => currentTab = AdminTab.Home">
|
@onclick="() => CurrentTab = AdminTab.Home">
|
||||||
Home Page
|
Home Page
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-container">
|
||||||
|
<button class="tab-btn @(CurrentTab == AdminTab.BusinessInfo ? "active" : "")"
|
||||||
|
@onclick="() => CurrentTab = AdminTab.BusinessInfo">
|
||||||
|
Business Info
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@if (currentTab == AdminTab.Home)
|
@if (CurrentTab == AdminTab.Home)
|
||||||
{
|
{
|
||||||
<EditForm FormName="HomePageForm" Model="HomePageModel" OnValidSubmit="SaveHomePageModel" On class="admin-form home-page-form">
|
<EditForm FormName="HomePageForm" Model="HomePageModel" OnValidSubmit="SaveHomePageModel" On class="admin-form">
|
||||||
<DataAnnotationsValidator />
|
<DataAnnotationsValidator />
|
||||||
|
|
||||||
<div class="form-section text-center">
|
<div class="form-section text-center">
|
||||||
@@ -100,9 +106,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</EditForm>
|
</EditForm>
|
||||||
}
|
}
|
||||||
else
|
else if (CurrentTab == AdminTab.BusinessInfo)
|
||||||
{
|
{
|
||||||
<h1>Another page</h1>
|
<EditForm FormName="BusinessInfoForm" Model="BusinessInfo" OnValidSubmit="SaveBusinessInfo" On class="admin-form">
|
||||||
|
<DataAnnotationsValidator />
|
||||||
|
|
||||||
|
<div class="form-section text-center">
|
||||||
|
<h3><i class="icon">🏠</i> Business Info</h3>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label>Business Name</label>
|
||||||
|
<InputText @bind-Value="BusinessInfo.Name" class="form-input" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label>Phone Number</label>
|
||||||
|
<InputText @bind-Value="BusinessInfo.PhoneNumber" class="form-input" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<label>Support Email</label>
|
||||||
|
<InputText @bind-Value="BusinessInfo.SupportEmail" class="form-input" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="admin-footer">
|
||||||
|
<button type="submit" class="btn btn-save">Save</button>
|
||||||
|
<button type="button" class="btn btn-revert" @onclick="RevertBusinessInfo">Revert</button>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,39 +1,63 @@
|
|||||||
namespace ApplianceRepair.Components.Pages.admin
|
namespace ApplianceRepair.Components.Pages.admin
|
||||||
{
|
{
|
||||||
public partial class EditPages(HomePageReader homePageReader, ContentCardReader contentCardReader)
|
public partial class EditPages(HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader)
|
||||||
{
|
{
|
||||||
public HomePageModel? HomePageModel;
|
public HomePageModel? HomePageModel;
|
||||||
|
public BusinessInfoModel? BusinessInfo;
|
||||||
|
|
||||||
private enum AdminTab { Home, About }
|
private enum AdminTab { Home, About, BusinessInfo }
|
||||||
private AdminTab currentTab = AdminTab.Home;
|
private AdminTab CurrentTab = AdminTab.Home;
|
||||||
|
|
||||||
override
|
protected override async Task OnInitializedAsync()
|
||||||
protected async void OnInitialized()
|
|
||||||
{
|
{
|
||||||
HomePageModel = await homePageReader.ReadLatestRecordWithModel(contentCardReader) ?? Defaults.DefaultHomePageContent;
|
var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig;
|
||||||
|
var latestHomeRecord = await homePageReader.ReadLatestRecord() ?? Defaults.DefaultHomePageContent;
|
||||||
|
var servicesList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Services)) ?? [];
|
||||||
|
var trustList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Trust)) ?? [];
|
||||||
|
|
||||||
|
BusinessInfo = new BusinessInfoModel(businessConfig);
|
||||||
|
HomePageModel = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void RevertHomePageModel()
|
private async void RevertHomePageModel()
|
||||||
{
|
{
|
||||||
HomePageModel = await homePageReader.ReadLatestRecordWithModel(contentCardReader) ?? Defaults.DefaultHomePageContent;
|
var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig;
|
||||||
|
var latestHomeRecord = await homePageReader.ReadLatestRecord() ?? Defaults.DefaultHomePageContent;
|
||||||
|
var servicesList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Services)) ?? [];
|
||||||
|
var trustList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Trust)) ?? [];
|
||||||
|
|
||||||
|
BusinessInfo = new BusinessInfoModel(businessConfig);
|
||||||
|
HomePageModel = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RevertBusinessInfo()
|
||||||
|
{
|
||||||
|
var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig;
|
||||||
|
BusinessInfo = new BusinessInfoModel(businessConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void SaveHomePageModel()
|
private async void SaveHomePageModel()
|
||||||
{
|
{
|
||||||
HomePageModel.CreatedAt = DateTime.Now;
|
|
||||||
HomePageModel.UpdatedAt = DateTime.Now;
|
HomePageModel.UpdatedAt = DateTime.Now;
|
||||||
|
await homePageReader.UpdateRecord(HomePageModel);
|
||||||
|
|
||||||
foreach (var card in HomePageModel.ServicesCards)
|
foreach (var card in HomePageModel.ServicesCards)
|
||||||
{
|
{
|
||||||
|
card.UpdatedAt = DateTime.Now;
|
||||||
await contentCardReader.UpdateRecord(card);
|
await contentCardReader.UpdateRecord(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var card in HomePageModel.TrustCards)
|
foreach (var card in HomePageModel.TrustCards)
|
||||||
{
|
{
|
||||||
|
card.UpdatedAt = DateTime.Now;
|
||||||
await contentCardReader.UpdateRecord(card);
|
await contentCardReader.UpdateRecord(card);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await homePageReader.AddRecord(HomePageModel);
|
private async void SaveBusinessInfo()
|
||||||
|
{
|
||||||
|
BusinessInfo.UpdatedAt = DateTime.Now;
|
||||||
|
await businessConfigReader.UpdateRecord(BusinessInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddServiceCard()
|
private void AddServiceCard()
|
||||||
@@ -42,7 +66,7 @@
|
|||||||
CreatedAt = DateTime.Now,
|
CreatedAt = DateTime.Now,
|
||||||
UpdatedAt = DateTime.Now,
|
UpdatedAt = DateTime.Now,
|
||||||
BelongsToPage = HomePageModel.PageName,
|
BelongsToPage = HomePageModel.PageName,
|
||||||
Group = HomePageModel.ContentCardTypes.Service.ToString(),
|
Group = HomePageModel.ContentCardTypes.Services.ToString(),
|
||||||
Header = "Service Name",
|
Header = "Service Name",
|
||||||
Text = "Short Description"
|
Text = "Short Description"
|
||||||
});
|
});
|
||||||
|
|||||||
39
Data.cs
39
Data.cs
@@ -1,4 +1,6 @@
|
|||||||
namespace ApplianceRepair
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace ApplianceRepair
|
||||||
{
|
{
|
||||||
public class HomePageRecord
|
public class HomePageRecord
|
||||||
{
|
{
|
||||||
@@ -11,15 +13,10 @@
|
|||||||
|
|
||||||
public string? HeaderText { get; set; }
|
public string? HeaderText { get; set; }
|
||||||
|
|
||||||
public string? HeaderButton1Text { get; set; }
|
public string? CallHeaderText { get; set; }
|
||||||
public string? HeaderButton2Text { get; set; }
|
public string? BookHeaderText { get; set; }
|
||||||
|
|
||||||
public string? HeaderButton1Link { get; set; }
|
|
||||||
public string? HeaderButton2Link { get; set; }
|
|
||||||
|
|
||||||
public string? SecondaryHeaderText { get; set; }
|
public string? SecondaryHeaderText { get; set; }
|
||||||
|
|
||||||
public string? CopyrightText { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContentCardRecord
|
public class ContentCardRecord
|
||||||
@@ -47,10 +44,36 @@
|
|||||||
|
|
||||||
public class RepairRequestRecord
|
public class RepairRequestRecord
|
||||||
{
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
public string? RequestNumber { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "Appliance Type is required.")]
|
||||||
public string? Type { get; set; }
|
public string? Type { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "Appliance brand is required.")]
|
||||||
public string? Brand { get; set; }
|
public string? Brand { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "Description is required.")]
|
||||||
public string? Notes { get; set; }
|
public string? Notes { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "Full Name is required.")]
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "Phone number is required.")]
|
||||||
|
[Phone(ErrorMessage = "Please enter a valid phone number.")]
|
||||||
public string? Phone { get; set; }
|
public string? Phone { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RepairRequestMediaRecord
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public DateTime CreatedAt { get; set; }
|
||||||
|
public DateTime UpdatedAt { get; set; }
|
||||||
|
|
||||||
|
public string? RequestNumber { get; set; }
|
||||||
|
public string? MediaPath { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,5 +8,47 @@ namespace ApplianceRepair
|
|||||||
public DbSet<ContentCardRecord> ContentCards { get; set; }
|
public DbSet<ContentCardRecord> ContentCards { get; set; }
|
||||||
public DbSet<BusinessConfigRecord> BusinessConfig { get; set; }
|
public DbSet<BusinessConfigRecord> BusinessConfig { get; set; }
|
||||||
public DbSet<RepairRequestRecord> RepairRequests { get; set; }
|
public DbSet<RepairRequestRecord> RepairRequests { get; set; }
|
||||||
|
public DbSet<RepairRequestMediaRecord> RepairRequestMedia { get; set; }
|
||||||
|
|
||||||
|
// Seed the data
|
||||||
|
public static async Task Initialize(DatabaseContext context)
|
||||||
|
{
|
||||||
|
if (!context.BusinessConfig.Any())
|
||||||
|
{
|
||||||
|
var config = Defaults.DefaultBusinessConfig;
|
||||||
|
config.CreatedAt = DateTime.Now;
|
||||||
|
config.UpdatedAt = DateTime.Now;
|
||||||
|
await context.BusinessConfig.AddAsync(config);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.HomePage.Any())
|
||||||
|
{
|
||||||
|
var home = Defaults.DefaultHomePageContent;
|
||||||
|
home.CreatedAt = DateTime.Now;
|
||||||
|
home.UpdatedAt = DateTime.Now;
|
||||||
|
await context.HomePage.AddAsync(home);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
if (!context.ContentCards.Any())
|
||||||
|
{
|
||||||
|
foreach (var card in Defaults.DefaultHomePageServiceCards)
|
||||||
|
{
|
||||||
|
card.CreatedAt = DateTime.Now;
|
||||||
|
card.UpdatedAt = DateTime.Now;
|
||||||
|
await context.ContentCards.AddAsync(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var card in Defaults.DefaultHomePageTrustCards)
|
||||||
|
{
|
||||||
|
card.CreatedAt = DateTime.Now;
|
||||||
|
card.UpdatedAt = DateTime.Now;
|
||||||
|
await context.ContentCards.AddAsync(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
41
Defaults.cs
41
Defaults.cs
@@ -2,39 +2,36 @@
|
|||||||
{
|
{
|
||||||
public static class Defaults
|
public static class Defaults
|
||||||
{
|
{
|
||||||
public static readonly HomePageModel DefaultHomePageContent = new()
|
public static readonly HomePageRecord DefaultHomePageContent = new()
|
||||||
{
|
{
|
||||||
BusinessName = "Appliance Pro",
|
|
||||||
FormattedPhoneNumber = "(555) 012-3456",
|
|
||||||
PhoneNumberCallLink = "tel:5550123456",
|
|
||||||
|
|
||||||
HeaderLine1 = "Expert Appliance Repair,",
|
HeaderLine1 = "Expert Appliance Repair,",
|
||||||
HeaderLine2 = "Done Right Today.",
|
HeaderLine2 = "Done Right Today.",
|
||||||
|
|
||||||
HeaderText = "Fast, affordable repairs for all major brands. Serving the Greater Metro Area.",
|
HeaderText = "Fast, affordable repairs for all major brands. Serving the Greater Metro Area.",
|
||||||
|
|
||||||
HeaderButton1Text = "Call for Same-Day Service",
|
CallHeaderText = "Call for Same-Day Service",
|
||||||
HeaderButton1Link = "tel:5550123456",
|
BookHeaderText = "Book Online",
|
||||||
HeaderButton2Text = "Book Online",
|
|
||||||
HeaderButton2Link = "#booking",
|
|
||||||
|
|
||||||
SecondaryHeaderText = "What We Fix",
|
SecondaryHeaderText = "What We Fix",
|
||||||
|
};
|
||||||
|
|
||||||
ServicesCards =
|
public static readonly List<ContentCardRecord> DefaultHomePageServiceCards = [
|
||||||
[
|
new() { Header = "Refrigerators", Text = "Cooling issues, leaks, and compressor repairs.", BelongsToPage = "Home", Group = "Services" },
|
||||||
new() { Header = "Refrigerators", Text = "Cooling issues, leaks, and compressor repairs." },
|
new() { Header = "Washers & Dryers", Text = "Fixing drum issues, drainage, and heating elements.", BelongsToPage = "Home", Group = "Services" },
|
||||||
new() { Header = "Washers & Dryers", Text = "Fixing drum issues, drainage, and heating elements." },
|
new() { Header = "Ovens & Ranges", Text = "Electrical igniters, gas flow, and temperature control.", BelongsToPage = "Home", Group = "Services" }
|
||||||
new() { Header = "Ovens & Ranges", Text = "Electrical igniters, gas flow, and temperature control." }
|
];
|
||||||
],
|
|
||||||
|
|
||||||
TrustCards =
|
public static readonly List<ContentCardRecord> DefaultHomePageTrustCards = [
|
||||||
[
|
new() { Header = "90-Day Warranty", Text = "Quality parts and labor guaranteed.", BelongsToPage = "Home", Group = "Trust" },
|
||||||
new() { Header = "90-Day Warranty", Text = "Quality parts and labor guaranteed." },
|
new() { Header = "Certified Techs", Text = "Licensed, bonded, and background-checked.", BelongsToPage = "Home", Group = "Trust" },
|
||||||
new() { Header = "Certified Techs", Text = "Licensed, bonded, and background-checked." },
|
new() { Header = "Fair Pricing", Text = "No hidden fees or diagnostic surprises.", BelongsToPage = "Home", Group = "Trust" }
|
||||||
new() { Header = "Fair Pricing", Text = "No hidden fees or diagnostic surprises." }
|
];
|
||||||
],
|
|
||||||
|
|
||||||
CopyrightText = $"© {DateTime.Now.Year} Appliance Pro. All rights reserved."
|
public static readonly BusinessConfigRecord DefaultBusinessConfig = new()
|
||||||
|
{
|
||||||
|
Name = "Appliance Pro",
|
||||||
|
PhoneNumber = "5550123456",
|
||||||
|
SupportEmail = "appliance@pro.net"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
173
Migrations/20260424050555_InitialCreate.Designer.cs
generated
Normal file
173
Migrations/20260424050555_InitialCreate.Designer.cs
generated
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.BusinessConfigRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SupportEmail")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("BusinessConfig");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.ContentCardRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("BelongsToPage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Group")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Header")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Text")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ContentCards");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.HomePageRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("BookHeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CallHeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HeaderLine1")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HeaderLine2")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SecondaryHeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("HomePage");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.RepairRequestMediaRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MediaPath")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RequestNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RepairRequestMedia");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.RepairRequestRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Brand")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RequestNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RepairRequests");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
125
Migrations/20260424050555_InitialCreate.cs
Normal file
125
Migrations/20260424050555_InitialCreate.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ApplianceRepair.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class InitialCreate : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "BusinessConfig",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
SupportEmail = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_BusinessConfig", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ContentCards",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
BelongsToPage = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Group = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Header = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Text = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ContentCards", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "HomePage",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
HeaderLine1 = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
HeaderLine2 = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
HeaderText = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
CallHeaderText = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
BookHeaderText = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
SecondaryHeaderText = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_HomePage", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RepairRequestMedia",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
RequestNumber = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
MediaPath = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RepairRequestMedia", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RepairRequests",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
RequestNumber = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Type = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Brand = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Notes = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Phone = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RepairRequests", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
170
Migrations/DatabaseContextModelSnapshot.cs
Normal file
170
Migrations/DatabaseContextModelSnapshot.cs
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using ApplianceRepair;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ApplianceRepair.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DatabaseContext))]
|
||||||
|
partial class DatabaseContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "9.0.12");
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.BusinessConfigRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SupportEmail")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("BusinessConfig");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.ContentCardRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("BelongsToPage")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Group")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Header")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Text")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ContentCards");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.HomePageRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("BookHeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("CallHeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HeaderLine1")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HeaderLine2")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("HeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("SecondaryHeaderText")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("HomePage");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.RepairRequestMediaRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("MediaPath")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RequestNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RepairRequestMedia");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("ApplianceRepair.RepairRequestRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Brand")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Phone")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("RequestNumber")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("RepairRequests");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
Models.cs
108
Models.cs
@@ -22,17 +22,58 @@
|
|||||||
public class HomePageModel : HomePageRecord
|
public class HomePageModel : HomePageRecord
|
||||||
{
|
{
|
||||||
public static string PageName = "Home";
|
public static string PageName = "Home";
|
||||||
|
|
||||||
public enum ContentCardTypes
|
public enum ContentCardTypes
|
||||||
{
|
{
|
||||||
Service,
|
Services,
|
||||||
Trust,
|
Trust,
|
||||||
}
|
}
|
||||||
|
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
public string FormattedPhoneNumber { get; set; }
|
|
||||||
public string PhoneNumberCallLink { get; set; }
|
public string PhoneNumber { get; set; }
|
||||||
|
|
||||||
|
public string FormattedPhoneNumber
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(PhoneNumber))
|
||||||
|
{
|
||||||
|
return $"({PhoneNumber[0..3]})-{PhoneNumber[3..6]}-{PhoneNumber[6..10]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PhoneNumberCallLink
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(PhoneNumber))
|
||||||
|
{
|
||||||
|
return $"tel:{PhoneNumber}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CopyrightText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(BusinessName))
|
||||||
|
{
|
||||||
|
return $"© {DateTime.Now.Year} {BusinessName}. All rights reserved.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"© {DateTime.Now.Year} All rights reserved.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<ContentCardModel> ServicesCards { get; set; }
|
public List<ContentCardModel> ServicesCards { get; set; }
|
||||||
|
|
||||||
public List<ContentCardModel> TrustCards { get; set; }
|
public List<ContentCardModel> TrustCards { get; set; }
|
||||||
|
|
||||||
public HomePageModel()
|
public HomePageModel()
|
||||||
@@ -40,44 +81,57 @@
|
|||||||
HeaderLine1 = string.Empty;
|
HeaderLine1 = string.Empty;
|
||||||
HeaderLine2 = string.Empty;
|
HeaderLine2 = string.Empty;
|
||||||
HeaderText = string.Empty;
|
HeaderText = string.Empty;
|
||||||
HeaderButton1Text = string.Empty;
|
CallHeaderText = string.Empty;
|
||||||
HeaderButton1Link = string.Empty;
|
BookHeaderText = string.Empty;
|
||||||
HeaderButton2Text = string.Empty;
|
|
||||||
HeaderButton2Link = string.Empty;
|
|
||||||
SecondaryHeaderText = string.Empty;
|
SecondaryHeaderText = string.Empty;
|
||||||
CopyrightText = string.Empty;
|
|
||||||
|
|
||||||
BusinessName = "Appliance Pro";
|
BusinessName = string.Empty;
|
||||||
FormattedPhoneNumber = "(555) 555-5555";
|
PhoneNumber = string.Empty;
|
||||||
PhoneNumberCallLink = $"tel:{FormattedPhoneNumber}";
|
|
||||||
|
|
||||||
ServicesCards = [];
|
ServicesCards = [];
|
||||||
TrustCards = [];
|
TrustCards = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public HomePageModel(HomePageRecord record)
|
public HomePageModel(
|
||||||
|
HomePageRecord homePageRecord,
|
||||||
|
BusinessConfigRecord businessConfigRecord,
|
||||||
|
List<ContentCardRecord> serviceCards,
|
||||||
|
List<ContentCardRecord> trustCards)
|
||||||
{
|
{
|
||||||
HeaderLine1 = record.HeaderLine1;
|
HeaderLine1 = homePageRecord.HeaderLine1;
|
||||||
HeaderLine2 = record.HeaderLine2;
|
HeaderLine2 = homePageRecord.HeaderLine2;
|
||||||
HeaderText = record.HeaderText;
|
HeaderText = homePageRecord.HeaderText;
|
||||||
HeaderButton1Text = record.HeaderButton1Text;
|
CallHeaderText = homePageRecord.CallHeaderText;
|
||||||
HeaderButton1Link = record.HeaderButton1Link;
|
BookHeaderText = homePageRecord.BookHeaderText;
|
||||||
HeaderButton2Text = record.HeaderButton2Text;
|
SecondaryHeaderText = homePageRecord.SecondaryHeaderText;;
|
||||||
HeaderButton2Link = record.HeaderButton2Link;
|
|
||||||
SecondaryHeaderText = record.SecondaryHeaderText;
|
|
||||||
CopyrightText = record.CopyrightText;
|
|
||||||
|
|
||||||
BusinessName = "Appliance Pro";
|
BusinessName = businessConfigRecord.Name ?? "";
|
||||||
FormattedPhoneNumber = "(555) 555-5555";
|
PhoneNumber = businessConfigRecord.PhoneNumber ?? "";
|
||||||
PhoneNumberCallLink = $"tel:{FormattedPhoneNumber}";
|
|
||||||
|
|
||||||
ServicesCards = [];
|
ServicesCards = [];
|
||||||
TrustCards = [];
|
TrustCards = [];
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RepairRequestModel : RepairRequestRecord
|
foreach (var card in serviceCards)
|
||||||
{
|
{
|
||||||
|
ServicesCards.Add(new ContentCardModel(card));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var card in trustCards)
|
||||||
|
{
|
||||||
|
TrustCards.Add(new ContentCardModel(card));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RepairRequestModel : RepairRequestRecord { }
|
||||||
|
|
||||||
|
public class BusinessInfoModel : BusinessConfigRecord
|
||||||
|
{
|
||||||
|
public BusinessInfoModel(BusinessConfigRecord record)
|
||||||
|
{
|
||||||
|
Name = record.Name;
|
||||||
|
PhoneNumber = record.PhoneNumber;
|
||||||
|
SupportEmail = record.SupportEmail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
Program.cs
10
Program.cs
@@ -17,6 +17,8 @@ builder.Services.AddLogging();
|
|||||||
builder.Services.AddScoped<BusinessConfigReader>();
|
builder.Services.AddScoped<BusinessConfigReader>();
|
||||||
builder.Services.AddScoped<ContentCardReader>();
|
builder.Services.AddScoped<ContentCardReader>();
|
||||||
builder.Services.AddScoped<HomePageReader>();
|
builder.Services.AddScoped<HomePageReader>();
|
||||||
|
builder.Services.AddScoped<RepairRequestReader>();
|
||||||
|
builder.Services.AddScoped<RepairRequestMediaReader>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
@@ -25,12 +27,16 @@ using (var scope = app.Services.CreateScope())
|
|||||||
var services = scope.ServiceProvider;
|
var services = scope.ServiceProvider;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
services.GetRequiredService<DatabaseContext>().Database.EnsureCreated();
|
var context = services.GetRequiredService<DatabaseContext>();
|
||||||
|
|
||||||
|
await context.Database.MigrateAsync();
|
||||||
|
|
||||||
|
await DatabaseContext.Initialize(context);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||||
logger.LogError(ex, "An error occurred creating the DB.");
|
logger.LogError(ex, "An error occurred while migrating or seeding the database.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
110
Services.cs
110
Services.cs
@@ -10,37 +10,27 @@ namespace ApplianceRepair
|
|||||||
return records;
|
return records;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<HomePageModel?> ReadLatestRecordWithModel(ContentCardReader contentCardReader)
|
|
||||||
{
|
|
||||||
var record = await db.HomePage.OrderByDescending(page => page.Id).FirstOrDefaultAsync();
|
|
||||||
if (record == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new HomePageModel(record);
|
|
||||||
var pageName = HomePageModel.PageName;
|
|
||||||
|
|
||||||
var services = await contentCardReader.ReadAllByPageAndGroup(pageName, HomePageModel.ContentCardTypes.Service.ToString()) ?? [];
|
|
||||||
foreach (var card in services)
|
|
||||||
{
|
|
||||||
model.ServicesCards.Add(new ContentCardModel(card));
|
|
||||||
}
|
|
||||||
|
|
||||||
var trust = await contentCardReader.ReadAllByPageAndGroup(pageName, HomePageModel.ContentCardTypes.Trust.ToString()) ?? [];
|
|
||||||
foreach (var card in trust)
|
|
||||||
{
|
|
||||||
model.TrustCards.Add(new ContentCardModel(card));
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AddRecord(HomePageRecord record)
|
public async Task AddRecord(HomePageRecord record)
|
||||||
{
|
{
|
||||||
|
record.CreatedAt = DateTime.Now;
|
||||||
|
record.UpdatedAt = DateTime.Now;
|
||||||
await db.AddAsync(record);
|
await db.AddAsync(record);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdateRecord(HomePageRecord record)
|
||||||
|
{
|
||||||
|
var found = db.HomePage.Where((page) => page.Id == record.Id).FirstOrDefault();
|
||||||
|
if (found == null)
|
||||||
|
{
|
||||||
|
await AddRecord(record);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db.HomePage.Update(record);
|
||||||
|
}
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ContentCardReader(DatabaseContext db)
|
public class ContentCardReader(DatabaseContext db)
|
||||||
@@ -58,6 +48,7 @@ namespace ApplianceRepair
|
|||||||
public async Task AddRecord(ContentCardRecord record)
|
public async Task AddRecord(ContentCardRecord record)
|
||||||
{
|
{
|
||||||
await db.ContentCards.AddAsync(record);
|
await db.ContentCards.AddAsync(record);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateRecord(ContentCardRecord record)
|
public async Task UpdateRecord(ContentCardRecord record)
|
||||||
@@ -81,5 +72,72 @@ namespace ApplianceRepair
|
|||||||
{
|
{
|
||||||
return await db.BusinessConfig.OrderByDescending(page => page.Id).FirstOrDefaultAsync();
|
return await db.BusinessConfig.OrderByDescending(page => page.Id).FirstOrDefaultAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task AddRecord(BusinessConfigRecord record)
|
||||||
|
{
|
||||||
|
await db.BusinessConfig.AddAsync(record);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateRecord(BusinessConfigRecord record)
|
||||||
|
{
|
||||||
|
var found = db.BusinessConfig.Where((config) => config.Id == record.Id).FirstOrDefault();
|
||||||
|
if (found == null)
|
||||||
|
{
|
||||||
|
await AddRecord(record);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db.BusinessConfig.Update(record);
|
||||||
|
}
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RepairRequestReader(DatabaseContext db)
|
||||||
|
{
|
||||||
|
public async Task<RepairRequestRecord?> ReadByRequestNumber(string requestNumber)
|
||||||
|
{
|
||||||
|
return await db.RepairRequests.Where((record) => record.RequestNumber == requestNumber).FirstOrDefaultAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<RepairRequestRecord>> ReadAll()
|
||||||
|
{
|
||||||
|
return await db.RepairRequests.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddRecord(RepairRequestRecord record)
|
||||||
|
{
|
||||||
|
await db.RepairRequests.AddAsync(record);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateRecord(RepairRequestRecord record)
|
||||||
|
{
|
||||||
|
var found = db.RepairRequests.Where((config) => config.Id == record.Id).FirstOrDefault();
|
||||||
|
if (found == null)
|
||||||
|
{
|
||||||
|
await AddRecord(record);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db.RepairRequests.Update(record);
|
||||||
|
}
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RepairRequestMediaReader(DatabaseContext db)
|
||||||
|
{
|
||||||
|
public async Task<List<RepairRequestMediaRecord>> ReadAllByRequestNumber(string requestNumber)
|
||||||
|
{
|
||||||
|
return await db.RepairRequestMedia.Where((record) => record.RequestNumber == requestNumber).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task AddRecord(RepairRequestMediaRecord record)
|
||||||
|
{
|
||||||
|
await db.RepairRequestMedia.AddAsync(record);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
wwwroot/uploads/exvwoomm.qgm.jpg
Normal file
0
wwwroot/uploads/exvwoomm.qgm.jpg
Normal file
BIN
wwwroot/uploads/uefue0ou.esy.png
Normal file
BIN
wwwroot/uploads/uefue0ou.esy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
Reference in New Issue
Block a user