From fb31fa7eb33dc9974a1ae18d2a3638a569d3ac39 Mon Sep 17 00:00:00 2001 From: Scott Pouliot Date: Tue, 12 May 2026 21:19:52 -0400 Subject: [PATCH] PR 5: Platform Settings UI redesign MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Section headers now show a group-specific icon, colored icon tile, and a one-line description explaining what each group controls (General, Notifications, Subscriptions, Quotes, Data Retention) - Each setting now displays UpdatedAt and UpdatedBy metadata below the current value so operators can see when and by whom a setting was last changed - Edit modal now uses type-appropriate inputs: number (with min=0, step=1) for *Days keys, email for *Email keys, url for BaseUrl, text otherwise; each type shows a contextual hint - Key name shown in monospace below the label on desktop for operator reference - Added SuccessMessage TempData alert at the top of the page - No backend or DB changes — view-only redesign Co-Authored-By: Claude Sonnet 4.6 --- .../Views/PlatformSettings/Index.cshtml | 323 ++++++++++++------ 1 file changed, 216 insertions(+), 107 deletions(-) diff --git a/src/PowderCoating.Web/Views/PlatformSettings/Index.cshtml b/src/PowderCoating.Web/Views/PlatformSettings/Index.cshtml index 911e785..8eab2fb 100644 --- a/src/PowderCoating.Web/Views/PlatformSettings/Index.cshtml +++ b/src/PowderCoating.Web/Views/PlatformSettings/Index.cshtml @@ -5,147 +5,241 @@ ViewData["Title"] = "Platform Settings"; ViewData["PageIcon"] = "bi-gear-wide-connected"; var groups = Model.GroupBy(s => s.GroupName ?? "General").OrderBy(g => g.Key); + + static string GroupIcon(string group) => group switch + { + "Notifications" => "bi-bell", + "Subscriptions" => "bi-credit-card", + "Quotes" => "bi-file-earmark-text", + "Data Retention" => "bi-clock-history", + _ => "bi-globe" + }; + + static string GroupDescription(string group) => group switch + { + "Notifications" => "Controls where platform event alerts (signups, bug reports, billing events) are sent.", + "Subscriptions" => "Trial and billing defaults applied to new tenant companies at registration.", + "Quotes" => "Default validity windows and token expiry for customer-facing quote links.", + "Data Retention" => "How long audit and webhook records are kept before the nightly purge removes them.", + _ => "Core platform configuration values used across features and email links." + }; + + static string InputType(string key) => key switch + { + var k when k.Contains("Email", StringComparison.OrdinalIgnoreCase) => "email", + var k when k.Contains("Url", StringComparison.OrdinalIgnoreCase) => "url", + var k when k.Contains("Days", StringComparison.OrdinalIgnoreCase) => "number", + _ => "text" + }; + + static string InputHint(string key) => key switch + { + var k when k.Contains("Email", StringComparison.OrdinalIgnoreCase) => "e.g. admin@example.com", + var k when k.Contains("Url", StringComparison.OrdinalIgnoreCase) => "e.g. https://app.yourdomain.com", + var k when k.Contains("Days", StringComparison.OrdinalIgnoreCase) => "Enter a whole number of days", + _ => "" + }; } @section Styles { } -
+
-@foreach (var group in groups) -{ -
-
-
- @group.Key -
+ @if (TempData["SuccessMessage"] != null) + { + -
-
- - - - - - - - - - @foreach (var s in group) - { - - - - - - } - -
SettingValueActions
-
@(s.Label ?? s.Key)
- @if (!string.IsNullOrWhiteSpace(s.Description)) - { - @s.Description - } -
- @if (string.IsNullOrWhiteSpace(s.Value)) - { - Not set - } - else - { - @s.Value - } - - -
+ } + + @foreach (var group in groups) + { + var groupKey = group.Key; + var icon = GroupIcon(groupKey); + var desc = GroupDescription(groupKey); + +
+
+
+
+ +
+
+
@groupKey
+

@desc

+
+
- -
-
- @foreach (var s in group) - { -
-
-
-
-
@(s.Label ?? s.Key)
- @if (!string.IsNullOrWhiteSpace(s.Description)) - { - @s.Description - } -
-
-
-
- Value - +
+
+ + + + + + + + + + + + + + + @foreach (var s in group) + { + + + + + + } + +
SettingCurrent ValueActions
+
@(s.Label ?? s.Key)
+ @if (!string.IsNullOrWhiteSpace(s.Description)) + { + @s.Description + } +
@s.Key
+
@if (string.IsNullOrWhiteSpace(s.Value)) { Not set } else { - @s.Value + @s.Value } - + @if (s.UpdatedAt.HasValue) + { +
+ Updated @s.UpdatedAt.Value.ToString("MMM d, yyyy") + @if (!string.IsNullOrWhiteSpace(s.UpdatedBy)) + { + by @s.UpdatedBy + } +
+ } +
+ +
+
+ + @* Mobile card view *@ +
+
+ @foreach (var s in group) + { +
+
+
+
+
@(s.Label ?? s.Key)
+ @if (!string.IsNullOrWhiteSpace(s.Description)) + { + @s.Description + } +
-
- Key - @s.Key +
+
+ Value + + @if (string.IsNullOrWhiteSpace(s.Value)) + { + Not set + } + else + { + @s.Value + } + +
+ @if (s.UpdatedAt.HasValue) + { +
+ Updated + + @s.UpdatedAt.Value.ToString("MMM d, yyyy") + @if (!string.IsNullOrWhiteSpace(s.UpdatedBy)) { by @s.UpdatedBy } + +
+ } +
+ Key + @s.Key +
+
+
- -
- } + } +
-
-} + } -@if (!Model.Any()) -{ -
-
- -

No platform settings found. Run a database migration to seed defaults.

+ @if (!Model.Any()) + { +
+
+ +

No platform settings found. Run a database migration to seed defaults.

+
-
-} + } + +
-