edited admin page, added request viewer

This commit is contained in:
slinky55
2026-04-25 21:25:53 -05:00
parent a1fddb3513
commit 03ca74d85a
5 changed files with 234 additions and 20 deletions

View File

@@ -1,28 +1,37 @@
using Microsoft.Extensions.Caching.Memory; using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Caching.Memory;
namespace ApplianceRepair.Components.Pages namespace ApplianceRepair.Components.Pages
{ {
public partial class Home(IMemoryCache cache, HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader) public partial class Home(IMemoryCache cache, HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader) : ComponentBase
{ {
private HomePageModel? Model; private HomePageModel? Model;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
if (!cache.TryGetValue(nameof(HomePageModel), out Model)) // TODO: Figure out a better cache system, needs to be marked dirty if admin makes a config change
{ //if (!cache.TryGetValue(nameof(HomePageModel), out Model))
//{
// 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()
// .SetAbsoluteExpiration(TimeSpan.FromHours(24))
// .SetSlidingExpiration(TimeSpan.FromHours(2));
// cache.Set(nameof(HomePageModel), Model, cacheOptions);
//}
var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig; var businessConfig = await businessConfigReader.ReadLatestRecord() ?? Defaults.DefaultBusinessConfig;
var latestHomeRecord = await homePageReader.ReadLatestRecord() ?? Defaults.DefaultHomePageContent; var latestHomeRecord = await homePageReader.ReadLatestRecord() ?? Defaults.DefaultHomePageContent;
var servicesList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Services)) ?? []; var servicesList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Services)) ?? [];
var trustList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Trust)) ?? []; var trustList = await contentCardReader.ReadAllByPageAndGroup(HomePageModel.PageName, nameof(HomePageModel.ContentCardTypes.Trust)) ?? [];
Model = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList); Model = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList);
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromHours(24))
.SetSlidingExpiration(TimeSpan.FromHours(2));
cache.Set(nameof(HomePageModel), Model, cacheOptions);
}
} }
} }
} }

View File

@@ -1,10 +1,12 @@
@page "/admin/editpages" @page "/admin"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@rendermode InteractiveServer @rendermode InteractiveServer
<div class="admin-wrapper"> <div class="admin-wrapper">
<div class="container"> <div class="container">
<header class="admin-header"> <header class="admin-header">
<h1>Page Content Management</h1> <h1>Admin Panel</h1>
<p>Update your business details and home page content below.</p> <p>Update your business details and home page content below.</p>
</header> </header>
@@ -32,6 +34,12 @@
Business Info Business Info
</button> </button>
</div> </div>
<div class="tab-container">
<button class="tab-btn @(CurrentTab == AdminTab.Requests ? "active" : "")"
@onclick="() => CurrentTab = AdminTab.Requests">
Requests
</button>
</div>
</header> </header>
@if (CurrentTab == AdminTab.Home) @if (CurrentTab == AdminTab.Home)
@@ -136,6 +144,57 @@
</div> </div>
</EditForm> </EditForm>
} }
else if (CurrentTab == AdminTab.Requests)
{
<div class="form-section">
<h3><i class="icon">📋</i> Service Requests</h3>
@if (RepairRequests == null || !RepairRequests.Any())
{
<p class="text-muted">No service requests found.</p>
}
else
{
<div class="requests-list">
@foreach (var request in RepairRequests)
{
<div class="content-card request-card">
<div class="request-header">
<span class="request-id">@request.RequestNumber</span>
<span class="request-date">@request.CreatedAt.ToString("MMM dd, yyyy")</span>
</div>
<div class="request-body">
<div class="info-row">
<span class="label">Customer:</span>
<span class="value">@request.Name</span>
</div>
<div class="info-row">
<span class="label">Phone:</span>
<span class="value">@request.FormattedPhoneNumber</span>
<a href="tel:@request.Phone" class="phone-link">
<i class="icon">📞</i>
</a>
</div>
<div class="info-row">
<span class="label">Appliance:</span>
<span class="value"><strong>@request.Brand</strong> @request.Type</span>
</div>
<div class="info-notes">
<span class="label">Issue Notes:</span>
<p>@request.Notes</p>
</div>
</div>
<div class="request-actions">
<button class="btn-small btn-view" @onclick="() => {}">View Images</button>
</div>
</div>
}
</div>
}
</div>
}
</div> </div>
</div> </div>
} }

View File

@@ -1,11 +1,14 @@
namespace ApplianceRepair.Components.Pages.admin using Microsoft.AspNetCore.Components;
namespace ApplianceRepair.Components.Pages.admin
{ {
public partial class EditPages(HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader) public partial class Admin(HomePageReader homePageReader, ContentCardReader contentCardReader, BusinessConfigReader businessConfigReader, RepairRequestReader repairRequestReader) : ComponentBase
{ {
public HomePageModel? HomePageModel; public HomePageModel? HomePageModel;
public BusinessInfoModel? BusinessInfo; public BusinessInfoModel? BusinessInfo;
public List<RepairRequestModel>? RepairRequests;
private enum AdminTab { Home, About, BusinessInfo } private enum AdminTab { Home, Requests, BusinessInfo }
private AdminTab CurrentTab = AdminTab.Home; private AdminTab CurrentTab = AdminTab.Home;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
@@ -17,6 +20,12 @@
BusinessInfo = new BusinessInfoModel(businessConfig); BusinessInfo = new BusinessInfoModel(businessConfig);
HomePageModel = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList); HomePageModel = new HomePageModel(latestHomeRecord, businessConfig, servicesList, trustList);
RepairRequests = [];
(await repairRequestReader.ReadAll()).ForEach((record) =>
{
RepairRequests.Add(new RepairRequestModel(record));
});
} }
private async void RevertHomePageModel() private async void RevertHomePageModel()

View File

@@ -164,6 +164,75 @@ label {
line-height: 1.5; line-height: 1.5;
} }
.requests-list {
display: flex;
flex-direction: column;
gap: 1.5rem;
padding-top: 1rem;
}
.request-card {
display: grid;
grid-template-columns: 150px 1fr 180px;
align-items: center;
text-align: left;
padding: 20px;
}
.request-id {
font-weight: 700;
color: #2a5298;
font-size: 1.1rem;
}
.request-date {
font-size: 0.85rem;
color: #888;
}
.request-body {
border-left: 1px solid #eee;
border-right: 1px solid #eee;
padding: 0 20px;
}
.info-row {
margin-bottom: 5px;
font-size: 0.9rem;
}
.info-row .label {
display: inline;
text-transform: none;
margin: 0;
color: #666;
font-weight: 600;
}
.info-notes {
margin-top: 10px;
font-size: 0.85rem;
font-style: italic;
color: #444;
}
.phone-link {
color: #2a5298;
text-decoration: none;
font-weight: 600;
transition: color 0.2s;
}
.phone-link:hover {
color: #4CAF50; /* Changes to green on hover to signify 'Call' */
text-decoration: underline;
}
.request-actions {
display: flex;
justify-content: flex-end;
}
.btn-save { .btn-save {
background: #4CAF50; background: #4CAF50;
color: white; color: white;
@@ -182,6 +251,23 @@ label {
background: #43a047; background: #43a047;
} }
.btn-small {
padding: 8px 20px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
border: 1px solid #2a5298;
background: transparent;
color: #2a5298;
transition: all 0.2s ease;
}
.btn-small:hover {
background: #2a5298;
color: white;
}
.btn-revert { .btn-revert {
background: transparent; background: transparent;
color: #666; color: #666;
@@ -237,4 +323,16 @@ label {
justify-content: center; justify-content: center;
row-gap: 2rem; row-gap: 2rem;
} }
.request-card {
grid-template-columns: 1fr;
gap: 15px;
}
.request-body {
border: none;
padding: 10px 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
} }

View File

@@ -123,7 +123,46 @@
} }
} }
public class RepairRequestModel : RepairRequestRecord { } public class RepairRequestModel : RepairRequestRecord
{
public string FormattedPhoneNumber
{
get
{
if (!string.IsNullOrEmpty(Phone))
{
return $"({Phone[0..3]})-{Phone[3..6]}-{Phone[6..10]}";
}
return "";
}
}
public string PhoneNumberCallLink
{
get
{
if (!string.IsNullOrEmpty(Phone))
{
return $"tel:{Phone}";
}
return "";
}
}
public RepairRequestModel() { }
public RepairRequestModel(RepairRequestRecord record)
{
RequestNumber = record.RequestNumber;
Type = record.Type;
Brand = record.Brand;
Notes = record.Notes;
Phone = record.Phone;
Name = record.Name;
}
}
public class BusinessInfoModel : BusinessConfigRecord public class BusinessInfoModel : BusinessConfigRecord
{ {