Initial commit

This commit is contained in:
2026-04-23 21:38:24 -04:00
commit 63e12a9636
1762 changed files with 1672620 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,276 @@
@using PowderCoating.Application.DTOs.Dashboard
@using PowderCoating.Core.Enums
@model SuperAdminDashboardViewModel
@{
ViewData["Title"] = "Platform Dashboard";
ViewData["PageIcon"] = "bi-shield-check";
// Badge color by position in sorted plan distribution (1st=secondary, 2nd=primary, 3rd=info, 4th+=success)
var planBadgeColors = Model.PlanDistribution.Keys
.Select((k, i) => (k, i))
.ToDictionary(x => x.k, x => x.i switch {
0 => "bg-secondary",
1 => "bg-primary",
2 => "bg-info",
_ => "bg-success"
});
string PlanBadge(int plan) => planBadgeColors.TryGetValue(plan, out var c) ? c : "bg-secondary";
string StatusBadge(SubscriptionStatus status) => status switch {
SubscriptionStatus.Active => "bg-success-subtle text-success",
SubscriptionStatus.GracePeriod => "bg-warning-subtle text-warning",
SubscriptionStatus.Expired => "bg-danger-subtle text-danger",
SubscriptionStatus.Canceled => "bg-secondary-subtle text-secondary",
_ => "bg-secondary-subtle text-secondary"
};
}
<!-- Platform Overview Header -->
<div class="d-flex justify-content-end mb-4">
<span class="badge bg-warning text-dark fs-6 px-3 py-2">
<i class="bi bi-shield-fill me-1"></i>Super Admin Mode
</span>
</div>
<!-- Platform Stats Row -->
<div class="row g-3 mb-4">
<div class="col-6 col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center gap-3">
<div class="rounded-3 p-3" style="background: rgba(79,70,229,0.1);">
<i class="bi bi-building fs-4" style="color: #4f46e5;"></i>
</div>
<div>
<div class="fs-2 fw-bold lh-1">@Model.TotalCompanies</div>
<div class="small text-muted">Total Companies</div>
<div class="small mt-1">
<span class="text-success">@Model.ActiveCompanies active</span>
@if (Model.InactiveCompanies > 0)
{
<span class="text-danger ms-1">· @Model.InactiveCompanies inactive</span>
}
</div>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center gap-3">
<div class="rounded-3 p-3" style="background: rgba(16,185,129,0.1);">
<i class="bi bi-people fs-4" style="color: #10b981;"></i>
</div>
<div>
<div class="fs-2 fw-bold lh-1">@Model.TotalUsers</div>
<div class="small text-muted">Total Users</div>
<div class="small text-muted mt-1">across all companies</div>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center gap-3">
<div class="rounded-3 p-3" style="background: rgba(245,158,11,0.1);">
<i class="bi bi-check-circle fs-4" style="color: #f59e0b;"></i>
</div>
<div>
<div class="fs-2 fw-bold lh-1">@Model.ActiveSubscriptions</div>
<div class="small text-muted">Active Subscriptions</div>
<div class="small mt-1">
@if (Model.GracePeriodCount > 0)
{
<span class="text-warning">@Model.GracePeriodCount grace period</span>
}
@if (Model.ExpiredCount > 0)
{
<span class="text-danger @(Model.GracePeriodCount > 0 ? "ms-1" : "")">@Model.ExpiredCount expired</span>
}
@if (Model.GracePeriodCount == 0 && Model.ExpiredCount == 0)
{
<span class="text-success">all healthy</span>
}
</div>
</div>
</div>
</div>
</div>
<div class="col-6 col-md-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body d-flex align-items-center gap-3">
<div class="rounded-3 p-3" style="background: rgba(139,92,246,0.1);">
<i class="bi bi-layers fs-4" style="color: #8b5cf6;"></i>
</div>
<div>
<div class="small text-muted mb-2">Plan Distribution</div>
<div class="d-flex flex-column gap-1">
@foreach (var kv in Model.PlanDistribution)
{
<div class="d-flex align-items-center gap-2">
<span class="badge @PlanBadge(kv.Key)" style="min-width:60px;">@kv.Value.DisplayName</span>
<span class="fw-semibold">@kv.Value.Count</span>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-4">
<!-- Companies Needing Attention -->
<div class="col-lg-7">
<div class="card border-0 shadow-sm h-100">
<div class="card-header d-flex align-items-center gap-2">
<i class="bi bi-exclamation-triangle text-warning"></i>
<span>Subscriptions Needing Attention</span>
@if (Model.CompanyAlerts.Any())
{
<span class="badge bg-warning text-dark ms-auto">@Model.CompanyAlerts.Count</span>
}
</div>
@if (!Model.CompanyAlerts.Any())
{
<div class="card-body text-center py-5">
<i class="bi bi-check-circle-fill text-success fs-1 mb-3 d-block"></i>
<p class="text-muted mb-0">All subscriptions are in good standing.</p>
</div>
}
else
{
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Company</th>
<th>Plan</th>
<th>Status</th>
<th>Expired</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var alert in Model.CompanyAlerts)
{
var manageUrl = Url.Action("Manage", "SubscriptionManagement", new { id = alert.Id });
<tr style="cursor:pointer" onclick="window.location='@manageUrl'">
<td>
<div class="fw-semibold">@alert.CompanyName</div>
@if (!alert.IsActive)
{
<small class="badge bg-danger-subtle text-danger">Inactive</small>
}
</td>
<td>
<span class="badge @PlanBadge(alert.Plan)">@alert.PlanDisplayName</span>
</td>
<td>
<span class="badge @StatusBadge(alert.Status)">@alert.Status</span>
</td>
<td>
@if (alert.SubscriptionEndDate.HasValue)
{
<span class="text-danger small">@alert.SubscriptionEndDate.Value.ToString("MMM d, yyyy")</span>
<br />
<small class="text-muted">@alert.DaysOverdue day@(alert.DaysOverdue == 1 ? "" : "s") ago</small>
}
</td>
<td class="text-end" onclick="event.stopPropagation()">
<a asp-controller="SubscriptionManagement" asp-action="Manage" asp-route-id="@alert.Id"
class="btn btn-sm btn-outline-secondary">
<i class="bi bi-pencil"></i>
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
</div>
</div>
<!-- Recently Added Companies -->
<div class="col-lg-5">
<div class="card border-0 shadow-sm h-100">
<div class="card-header d-flex align-items-center gap-2">
<i class="bi bi-building-add text-primary"></i>
<span>Recently Added Companies</span>
</div>
@if (!Model.RecentCompanies.Any())
{
<div class="card-body text-center py-5">
<p class="text-muted mb-0">No companies yet.</p>
</div>
}
else
{
<div class="list-group list-group-flush">
@foreach (var company in Model.RecentCompanies)
{
<a asp-controller="Companies" asp-action="Details" asp-route-id="@company.Id"
class="list-group-item list-group-item-action d-flex align-items-center gap-3 py-3 px-4">
<div class="rounded-circle d-flex align-items-center justify-content-center flex-shrink-0"
style="width:36px;height:36px;background:rgba(79,70,229,0.12);">
<i class="bi bi-building text-primary small"></i>
</div>
<div class="flex-grow-1 overflow-hidden">
<div class="fw-semibold text-truncate">@company.CompanyName</div>
<div class="small text-muted">@company.CreatedAt.ToString("MMM d, yyyy")</div>
</div>
<div class="d-flex flex-column align-items-end gap-1">
<span class="badge @PlanBadge(company.Plan)">@company.PlanDisplayName</span>
@if (!company.IsActive)
{
<span class="badge bg-danger-subtle text-danger">Inactive</span>
}
</div>
</a>
}
</div>
<div class="card-footer text-center py-2">
<a asp-controller="Companies" asp-action="Index" class="small text-muted">
View all companies <i class="bi bi-arrow-right ms-1"></i>
</a>
</div>
}
</div>
</div>
</div>
<!-- Quick Links Row -->
<div class="row g-3 mt-2">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body py-3">
<div class="d-flex flex-wrap gap-2 align-items-center">
<span class="text-muted small me-2">Quick Actions:</span>
<a asp-controller="Companies" asp-action="Create" class="btn btn-sm btn-outline-primary">
<i class="bi bi-plus-circle me-1"></i>New Company
</a>
<a asp-controller="PlatformUsers" asp-action="Index" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-people me-1"></i>Platform Users
</a>
<a asp-controller="PlatformSubscription" asp-action="Index" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-layers me-1"></i>Subscription Plans
</a>
<a asp-controller="SystemInfo" asp-action="Index" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-cpu me-1"></i>System Info
</a>
@if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")))
{
<a asp-controller="Diagnostics" asp-action="ViewLogs" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-file-text me-1"></i>View Logs
</a>
}
<a asp-controller="SeedData" asp-action="Index" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-database-fill-gear me-1"></i>Seed Data
</a>
</div>
</div>
</div>
</div>
</div>