Consolidate company admin screens: health badge on list, tabbed detail page

Companies/Index:
- Added Health badge column (Healthy / At Risk / Critical / Never Active)
  with the numeric score in a tooltip; computed from the same signals as
  CompanyHealth/Index using the new shared CompanyHealthHelper

Companies/Details:
- Converted flat card layout to five tabs: Overview, Users, Subscription,
  Onboarding, Health; URL hash is preserved so the active tab survives
  page refresh and back navigation
- Subscription tab shows plan/status/dates with an expiry countdown and a
  "Manage Subscription & Features" button to the full Manage page
- Onboarding tab shows wizard completion, milestone progress bar, and
  first-activity dates (previously only on the standalone page)
- Health tab shows score gauge, risk badge, and individual risk signals
  with a link through to the full CompanyHealth dashboard
- JS moved to wwwroot/js/companies-details.js (avoids inline-script failures)

Infrastructure:
- Extracted ComputeHealth / ToRiskLevel / ChurnRisk to CompanyHealthHelper.cs
  (same Controllers namespace); CompanyHealthController delegates to it
- CompanyCountSummary extended with Jobs30Counts, Jobs90Counts, LastLoginDates
  (3 extra GROUP BY queries scoped to the current page IDs, not all companies)
- CompanyListDto gains HealthScore, HealthRisk, LastLoginDate

Navigation:
- Removed "Onboarding Progress" hub card from People & Activity; the data
  is now surfaced directly on the Companies/Details Onboarding tab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 22:22:14 -04:00
parent 786b78e502
commit 726bebdce9
10 changed files with 966 additions and 743 deletions
File diff suppressed because it is too large Load Diff
@@ -96,6 +96,7 @@
Plan <i class="bi @SortIcon("Plan")"></i>
</a>
</th>
<th>Health</th>
<th>Users</th>
<th>Setup Wizard</th>
<th>
@@ -138,6 +139,18 @@
<td>
<span class="badge @PlanBadge(company.SubscriptionPlan)">@PlanName(company.SubscriptionPlan)</span>
</td>
<td>
@{
var (hBadge, hLabel) = company.HealthRisk switch {
"Healthy" => ("bg-success-subtle text-success-emphasis border border-success-subtle", "Healthy"),
"AtRisk" => ("bg-warning-subtle text-warning-emphasis border border-warning-subtle", "At Risk"),
"Critical" => ("bg-danger-subtle text-danger-emphasis border border-danger-subtle", "Critical"),
"NeverActivated" => ("bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle", "Never Active"),
_ => ("bg-secondary-subtle text-secondary-emphasis border border-secondary-subtle", company.HealthRisk)
};
}
<span class="badge @hBadge" title="Score: @company.HealthScore">@hLabel</span>
</td>
<td>
<span class="badge bg-primary rounded-pill">@company.UserCount</span>
</td>