dashboard upgrade
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
namespace SportsDivision.Application.DTOs;
|
||||
public class DashboardDto { public int TotalStudents { get; set; } public int MaleStudents { get; set; } public int FemaleStudents { get; set; } public int TotalSchools { get; set; } public int ActiveTournaments { get; set; } public int TotalRegistrations { get; set; } public int EventsInProgress { get; set; } public int EventsCompleted { get; set; } public List<SchoolPointsSummaryDto> TopSchools { get; set; } = new(); public List<RecentScoreDto> RecentScores { get; set; } = new(); }
|
||||
public class DashboardDto { public int TotalStudents { get; set; } public int MaleStudents { get; set; } public int FemaleStudents { get; set; } public int TotalSchools { get; set; } public int TotalEvents { get; set; } public int TotalScores { get; set; } public int ActiveTournaments { get; set; } public int DraftTournaments { get; set; } public int RegistrationTournaments { get; set; } public int CompletedTournaments { get; set; } public int ArchivedTournaments { get; set; } public int TotalRegistrations { get; set; } public int EventsInProgress { get; set; } public int EventsCompleted { get; set; } public List<SchoolPointsSummaryDto> TopSchools { get; set; } = new(); public List<RecentScoreDto> RecentScores { get; set; } = new(); }
|
||||
public class SchoolPointsSummaryDto { public int SchoolId { get; set; } public string SchoolName { get; set; } = string.Empty; public string? ShortName { get; set; } public int TotalPoints { get; set; } public int FirstPlaceCount { get; set; } public int SecondPlaceCount { get; set; } public int ThirdPlaceCount { get; set; } }
|
||||
public class RecentScoreDto { public string StudentName { get; set; } = string.Empty; public string EventName { get; set; } = string.Empty; public string EventLevelName { get; set; } = string.Empty; public decimal RawPerformance { get; set; } public int? Placement { get; set; } public DateTime RecordedAt { get; set; } }
|
||||
|
||||
@@ -24,19 +24,48 @@ public class DashboardService : IDashboardService
|
||||
MaleStudents = await _uow.Students.CountAsync(s => s.Sex == Sex.Male),
|
||||
FemaleStudents = await _uow.Students.CountAsync(s => s.Sex == Sex.Female),
|
||||
TotalSchools = await _uow.Schools.CountAsync(),
|
||||
ActiveTournaments = await _uow.Tournaments.CountAsync(t => t.Status == TournamentStatus.InProgress && !t.IsArchived)
|
||||
TotalEvents = await _uow.Events.CountAsync(),
|
||||
TotalScores = await _uow.Scores.CountAsync(),
|
||||
ActiveTournaments = await _uow.Tournaments.CountAsync(t => t.Status == TournamentStatus.InProgress && !t.IsArchived),
|
||||
DraftTournaments = await _uow.Tournaments.CountAsync(t => t.Status == TournamentStatus.Draft && !t.IsArchived),
|
||||
RegistrationTournaments = await _uow.Tournaments.CountAsync(t => t.Status == TournamentStatus.Registration && !t.IsArchived),
|
||||
CompletedTournaments = await _uow.Tournaments.CountAsync(t => t.Status == TournamentStatus.Completed && !t.IsArchived),
|
||||
ArchivedTournaments = await _uow.Tournaments.CountAsync(t => t.IsArchived)
|
||||
};
|
||||
|
||||
if (tournamentId.HasValue)
|
||||
{
|
||||
var tels = await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId.Value);
|
||||
var tels = (await _uow.TournamentEventLevels.GetByTournamentAsync(tournamentId.Value)).ToList();
|
||||
int regCount = 0;
|
||||
int scoredEvents = 0;
|
||||
var recent = new List<RecentScoreDto>();
|
||||
|
||||
foreach (var tel in tels)
|
||||
{
|
||||
var regs = await _uow.EventRegistrations.GetByTournamentEventLevelAsync(tel.TournamentEventLevelId);
|
||||
regCount += regs.Count();
|
||||
|
||||
bool hasResults = false;
|
||||
foreach (var reg in regs.Where(r => r.Score != null))
|
||||
{
|
||||
if (reg.Score!.Placement != null) hasResults = true;
|
||||
recent.Add(new RecentScoreDto
|
||||
{
|
||||
StudentName = reg.Student?.FullName ?? "Unknown",
|
||||
EventName = tel.Event?.Name ?? string.Empty,
|
||||
EventLevelName = tel.EventLevel?.Name ?? string.Empty,
|
||||
RawPerformance = reg.Score!.RawPerformance,
|
||||
Placement = reg.Score.Placement,
|
||||
RecordedAt = reg.Score.RecordedAt
|
||||
});
|
||||
}
|
||||
if (hasResults) scoredEvents++;
|
||||
}
|
||||
|
||||
dashboard.TotalRegistrations = regCount;
|
||||
dashboard.EventsCompleted = scoredEvents;
|
||||
dashboard.EventsInProgress = tels.Count - scoredEvents;
|
||||
dashboard.RecentScores = recent.OrderByDescending(r => r.RecordedAt).Take(8).ToList();
|
||||
|
||||
var standings = await _scoringService.GetSchoolStandingsAsync(tournamentId.Value);
|
||||
dashboard.TopSchools = standings.Take(10).ToList();
|
||||
|
||||
@@ -18,11 +18,25 @@ public class DashboardController : Controller
|
||||
|
||||
public async Task<IActionResult> Index(int? tournamentId)
|
||||
{
|
||||
var dashboard = await _dashboardService.GetDashboardAsync(tournamentId);
|
||||
// "Active" tournaments are the non-archived ones; archived tournaments are never shown here.
|
||||
var tournaments = (await _tournamentService.GetAllAsync(includeArchived: false)).ToList();
|
||||
|
||||
// Ignore a selection that points at an archived/unknown tournament.
|
||||
int? selectedId = tournamentId.HasValue && tournaments.Any(t => t.TournamentId == tournamentId.Value)
|
||||
? tournamentId
|
||||
: null;
|
||||
|
||||
// With exactly one active tournament, show it automatically — no need to choose.
|
||||
// With several, the user must pick one (selectedId stays null until they do).
|
||||
if (!selectedId.HasValue && tournaments.Count == 1)
|
||||
{
|
||||
selectedId = tournaments[0].TournamentId;
|
||||
}
|
||||
|
||||
var dashboard = await _dashboardService.GetDashboardAsync(selectedId);
|
||||
|
||||
var tournaments = await _tournamentService.GetAllAsync(includeArchived: false);
|
||||
ViewBag.Tournaments = tournaments;
|
||||
ViewBag.SelectedTournamentId = tournamentId;
|
||||
ViewBag.SelectedTournamentId = selectedId;
|
||||
|
||||
return View(dashboard);
|
||||
}
|
||||
|
||||
10
src/SportsDivision.Web/Controllers/HelpController.cs
Normal file
10
src/SportsDivision.Web/Controllers/HelpController.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace SportsDivision.Web.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class HelpController : Controller
|
||||
{
|
||||
public IActionResult Index() => View();
|
||||
}
|
||||
@@ -1,70 +1,171 @@
|
||||
@model DashboardDto
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
var tournaments = ViewBag.Tournaments as IEnumerable<TournamentDto>;
|
||||
var tournamentList = (ViewBag.Tournaments as IEnumerable<TournamentDto>)?.ToList() ?? new List<TournamentDto>();
|
||||
var selectedTournamentId = ViewBag.SelectedTournamentId as int?;
|
||||
var selectedTournament = tournamentList.FirstOrDefault(t => t.TournamentId == selectedTournamentId);
|
||||
}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Dashboard</h2>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
|
||||
<h2 class="mb-0">Dashboard</h2>
|
||||
@if (tournamentList.Count > 1)
|
||||
{
|
||||
<form method="get" class="d-flex align-items-center gap-2">
|
||||
<label class="form-label mb-0 text-nowrap">Tournament:</label>
|
||||
<select name="tournamentId" class="form-select form-select-sm" style="width:250px" onchange="this.form.submit()">
|
||||
<option value="">-- All --</option>
|
||||
@if (tournaments != null)
|
||||
{
|
||||
@foreach (var t in tournaments)
|
||||
<option value="">-- Select a tournament --</option>
|
||||
@foreach (var t in tournamentList)
|
||||
{
|
||||
<option value="@t.TournamentId" selected="@(selectedTournamentId == t.TournamentId)">@t.Name</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
</form>
|
||||
}
|
||||
else if (tournamentList.Count == 1)
|
||||
{
|
||||
<span class="badge bg-primary fs-6"><i class="bi bi-trophy me-1"></i>@tournamentList[0].Name</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* ---- Top-level counts (system-wide) ---- *@
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-primary text-white shadow-sm">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card bg-primary text-white shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Total Students</h6>
|
||||
<h2>@Model.TotalStudents</h2>
|
||||
<h6 class="card-title"><i class="bi bi-people me-1"></i>Total Students</h6>
|
||||
<h2 class="mb-0">@Model.TotalStudents</h2>
|
||||
<small>Male: @Model.MaleStudents | Female: @Model.FemaleStudents</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-success text-white shadow-sm">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card bg-success text-white shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Schools</h6>
|
||||
<h2>@Model.TotalSchools</h2>
|
||||
<h6 class="card-title"><i class="bi bi-building me-1"></i>Schools</h6>
|
||||
<h2 class="mb-0">@Model.TotalSchools</h2>
|
||||
<small>Participating institutions</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-info text-white shadow-sm">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card bg-info text-white shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Active Tournaments</h6>
|
||||
<h2>@Model.ActiveTournaments</h2>
|
||||
<h6 class="card-title"><i class="bi bi-calendar-event me-1"></i>Events</h6>
|
||||
<h2 class="mb-0">@Model.TotalEvents</h2>
|
||||
<small>Track, field & high jump</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card bg-warning text-dark shadow-sm">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="card bg-dark text-white shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Registrations</h6>
|
||||
<h2>@Model.TotalRegistrations</h2>
|
||||
<h6 class="card-title"><i class="bi bi-clipboard-check me-1"></i>Scores Recorded</h6>
|
||||
<h2 class="mb-0">@Model.TotalScores</h2>
|
||||
<small>Across all tournaments</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (Model.TopSchools.Any())
|
||||
{
|
||||
<div class="card shadow-sm">
|
||||
@* ---- Tournament status breakdown + quick actions ---- *@
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-7">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header" style="background-color:#003366; color:white;">
|
||||
<h5 class="mb-0">School Standings</h5>
|
||||
<h5 class="mb-0"><i class="bi bi-trophy me-1"></i>Tournaments by Status</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<div class="text-center">
|
||||
<div class="h3 mb-0">@Model.DraftTournaments</div>
|
||||
<span class="badge bg-secondary">Draft</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="h3 mb-0">@Model.RegistrationTournaments</div>
|
||||
<span class="badge bg-info">Registration</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="h3 mb-0">@Model.ActiveTournaments</div>
|
||||
<span class="badge bg-primary">In Progress</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="h3 mb-0">@Model.CompletedTournaments</div>
|
||||
<span class="badge bg-success">Completed</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="h3 mb-0">@Model.ArchivedTournaments</div>
|
||||
<span class="badge bg-dark">Archived</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<a asp-controller="Tournament" asp-action="Index" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-list-ul me-1"></i>Manage tournaments
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-dark text-white">
|
||||
<h5 class="mb-0"><i class="bi bi-lightning-charge me-1"></i>Quick Actions</h5>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-wrap gap-2 align-content-start">
|
||||
<a asp-controller="Tournament" asp-action="Create" class="btn btn-outline-primary btn-sm"><i class="bi bi-plus-circle me-1"></i>New Tournament</a>
|
||||
<a asp-controller="School" asp-action="Index" class="btn btn-outline-secondary btn-sm"><i class="bi bi-building me-1"></i>Schools</a>
|
||||
<a asp-controller="Student" asp-action="Index" class="btn btn-outline-secondary btn-sm"><i class="bi bi-people me-1"></i>Students</a>
|
||||
<a asp-controller="Report" asp-action="Index" class="btn btn-outline-success btn-sm"><i class="bi bi-file-earmark-bar-graph me-1"></i>Reports</a>
|
||||
@if (User.IsInRole("Admin"))
|
||||
{
|
||||
<a asp-controller="ScoringConfig" asp-action="Index" class="btn btn-outline-dark btn-sm"><i class="bi bi-sliders me-1"></i>Scoring Config</a>
|
||||
}
|
||||
<a asp-controller="Help" asp-action="Index" class="btn btn-outline-info btn-sm"><i class="bi bi-question-circle me-1"></i>Help</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (selectedTournamentId.HasValue)
|
||||
{
|
||||
@* ---- Selected-tournament detail ---- *@
|
||||
<h4 class="mb-3"><i class="bi bi-bullseye me-1"></i>@(selectedTournament?.Name ?? "Selected Tournament")</h4>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="card bg-warning text-dark shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title"><i class="bi bi-card-checklist me-1"></i>Registrations</h6>
|
||||
<h2 class="mb-0">@Model.TotalRegistrations</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="card bg-success text-white shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title"><i class="bi bi-check2-circle me-1"></i>Events Scored</h6>
|
||||
<h2 class="mb-0">@Model.EventsCompleted</h2>
|
||||
<small>Results calculated</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<div class="card bg-secondary text-white shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title"><i class="bi bi-hourglass-split me-1"></i>Events Pending</h6>
|
||||
<h2 class="mb-0">@Model.EventsInProgress</h2>
|
||||
<small>Awaiting results</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-lg-7">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header" style="background-color:#003366; color:white;">
|
||||
<h5 class="mb-0"><i class="bi bi-bar-chart-line me-1"></i>School Standings</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if (Model.TopSchools.Any())
|
||||
{
|
||||
<table class="table table-striped table-hover mb-0">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
@@ -91,6 +192,61 @@
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted p-3 mb-0">No points scored yet for this tournament.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header bg-dark text-white">
|
||||
<h5 class="mb-0"><i class="bi bi-activity me-1"></i>Recent Activity</h5>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if (Model.RecentScores.Any())
|
||||
{
|
||||
<ul class="list-group list-group-flush">
|
||||
@foreach (var s in Model.RecentScores)
|
||||
{
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div>
|
||||
<div class="fw-semibold">@s.StudentName</div>
|
||||
<small class="text-muted">@s.EventName — @s.EventLevelName · @s.RawPerformance.ToString("0.00")</small>
|
||||
</div>
|
||||
<div class="text-end text-nowrap">
|
||||
@if (s.Placement != null)
|
||||
{
|
||||
<span class="badge bg-warning text-dark">#@s.Placement</span>
|
||||
}
|
||||
<div><small class="text-muted">@s.RecordedAt.ToString("MMM d, HH:mm")</small></div>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted p-3 mb-0">No scores recorded yet for this tournament.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (tournamentList.Count > 1)
|
||||
{
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
You have multiple active tournaments. <strong>Select one above</strong> to see its registrations, school standings, and recent scoring activity.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-secondary">
|
||||
<i class="bi bi-info-circle me-1"></i>
|
||||
No active tournaments yet. <a asp-controller="Tournament" asp-action="Create">Create a tournament</a> to get started.
|
||||
</div>
|
||||
}
|
||||
|
||||
409
src/SportsDivision.Web/Views/Help/Index.cshtml
Normal file
409
src/SportsDivision.Web/Views/Help/Index.cshtml
Normal file
@@ -0,0 +1,409 @@
|
||||
@{
|
||||
ViewData["Title"] = "Help & User Guide";
|
||||
Layout = "_HelpLayout";
|
||||
}
|
||||
|
||||
<div class="help-hero">
|
||||
<h1 class="mb-1"><i class="bi bi-life-preserver me-2"></i>Help & User Guide</h1>
|
||||
<p class="mb-0 lead">Everything you need to run a track & field competition — tournaments, scoring, results, and reports. Use the search box to jump straight to an answer.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-search-bar">
|
||||
<div class="position-relative">
|
||||
<i class="bi bi-search help-search-icon"></i>
|
||||
<input id="helpSearch" type="search" class="form-control" autocomplete="off"
|
||||
placeholder="Search the help — e.g. 'archive', 'tie', 'scoring constants', 'advancement'…" />
|
||||
</div>
|
||||
<div id="helpCount" class="small text-muted mt-1"></div>
|
||||
</div>
|
||||
|
||||
<div id="helpNoResults" class="alert alert-warning help-noresults">
|
||||
<i class="bi bi-emoji-frown me-1"></i> No help topics matched your search. Try a different word.
|
||||
</div>
|
||||
|
||||
<div class="help-layout">
|
||||
<nav class="help-toc" id="helpToc" aria-label="Help contents">
|
||||
<div class="fw-semibold text-uppercase small text-muted mb-2">Contents</div>
|
||||
<a href="#getting-started">Getting started</a>
|
||||
<a href="#tournaments">Tournaments & statuses</a>
|
||||
<a href="#archiving">Archiving tournaments</a>
|
||||
<a href="#events">Events</a>
|
||||
<a href="#track">Track events</a>
|
||||
<a href="#field">Field events</a>
|
||||
<a href="#highjump">High jump</a>
|
||||
<a href="#wa-points">World Athletics points</a>
|
||||
<a href="#placement-points">Placement points</a>
|
||||
<a href="#schools-students">Schools & students</a>
|
||||
<a href="#reports">Reports & Excel</a>
|
||||
<a href="#pagination">Lists & pagination</a>
|
||||
<a href="#dashboard">Dashboard</a>
|
||||
<a href="#faq">FAQ / troubleshooting</a>
|
||||
</nav>
|
||||
|
||||
<div class="help-content">
|
||||
|
||||
@* ============ GETTING STARTED ============ *@
|
||||
<section class="help-section" id="getting-started">
|
||||
<h2><i class="bi bi-compass me-1"></i>Getting started</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-signpost-2 me-1"></i>What this system does</h3>
|
||||
<p>The Dominica Sports Division system manages school track & field competitions end to end: it holds your <strong>schools</strong> and <strong>students</strong>, lets you build <strong>tournaments</strong> made up of <strong>events</strong> at different age/sex <strong>levels</strong>, captures results for track, field and high jump, converts them to points, and produces <strong>standings and reports</strong>.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-diagram-3 me-1"></i>The big picture — how a competition flows</h3>
|
||||
<ol>
|
||||
<li>Add your <strong>Schools</strong> and their <strong>Students</strong>.</li>
|
||||
<li>Create a <strong>Tournament</strong> and add the <strong>events</strong> you'll run (each at one or more age/sex levels).</li>
|
||||
<li>Open registration and <strong>register students</strong> into events.</li>
|
||||
<li>Run each event and record results in <strong>Track Events</strong>, <strong>Field Events</strong> or <strong>High Jump</strong>.</li>
|
||||
<li>Calculate points/placements; view <strong>school standings</strong> on the Dashboard and run <strong>Reports</strong>.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-search me-1"></i>Using this help page</h3>
|
||||
<p>Type any word into the search box at the top — topics that don't match are hidden as you type. Clear the box to see everything again. On wide screens the left-hand contents menu jumps you to a section.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ TOURNAMENTS ============ *@
|
||||
<section class="help-section" id="tournaments">
|
||||
<h2><i class="bi bi-trophy me-1"></i>Tournaments & statuses</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-arrow-right-circle me-1"></i>The tournament lifecycle</h3>
|
||||
<p>A tournament moves through four statuses, in order:</p>
|
||||
<ul>
|
||||
<li><span class="badge bg-secondary">Draft</span> — you're still setting it up (adding events).</li>
|
||||
<li><span class="badge bg-info">Registration</span> — students can be registered into events.</li>
|
||||
<li><span class="badge bg-primary">In Progress</span> — the competition is running and results are being recorded.</li>
|
||||
<li><span class="badge bg-success">Completed</span> — all events are finished and scored.</li>
|
||||
</ul>
|
||||
<p>Open a tournament's <strong>Details</strong> page and use the buttons in the top-right (<em>Open Registration → Start Tournament → Complete</em>) to move it forward one step at a time.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-arrow-counterclockwise me-1"></i>Reverting a status set by mistake</h3>
|
||||
<p>Every forward step can be undone. On the Details page you'll also see backward buttons: <strong>Back to Draft</strong>, <strong>Back to Registration</strong>, and — if a tournament was marked Completed by mistake — <strong>Reopen Tournament</strong> (which returns it to In Progress). Nothing is lost; the status simply changes back.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-check2-square me-1"></i>Why "Complete" asks for confirmation</h3>
|
||||
<p>Marking a tournament <strong>Completed</strong> pops up a confirmation because it signals the competition is over. If you click it by accident, just confirm you didn't mean to (or use <strong>Reopen Tournament</strong> afterwards to set it back to In Progress).</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ ARCHIVING ============ *@
|
||||
<section class="help-section" id="archiving">
|
||||
<h2><i class="bi bi-archive me-1"></i>Archiving tournaments</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-box-seam me-1"></i>What archiving does</h3>
|
||||
<p>Archiving <strong>hides</strong> an old tournament from the default list to reduce clutter — it is <strong>not</strong> deleted. All its events, registrations and results are kept and can be restored at any time with <strong>Unarchive</strong>.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-shield-check me-1"></i>Only completed tournaments can be archived</h3>
|
||||
<p>The <strong>Archive</strong> button only appears once a tournament's status is <span class="badge bg-success">Completed</span>. This prevents archiving something that's still in draft, registration, or mid-competition. If you need to archive an active one, finish it (or it isn't ready to archive yet).</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-eye me-1"></i>Where do archived tournaments go? / "I can't see my tournament"</h3>
|
||||
<p>Archived tournaments are hidden on the Tournaments page by default. To see them, tick the <strong>Show archived</strong> switch near the top of the list — they reappear with an <span class="badge bg-dark">Archived</span> badge. A small counter on the switch tells you how many are currently hidden. Untick it to hide them again.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-arrow-clockwise me-1"></i>Restoring an archived tournament</h3>
|
||||
<p>Turn on <strong>Show archived</strong>, find the tournament, and click <strong>Unarchive</strong> (on the list row or its Details page). It returns to the normal list immediately, keeping whatever status it had.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ EVENTS ============ *@
|
||||
<section class="help-section" id="events">
|
||||
<h2><i class="bi bi-calendar-event me-1"></i>Events</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-tags me-1"></i>The three event categories</h3>
|
||||
<ul>
|
||||
<li><strong>Track</strong> — races timed in seconds (lower is better): 80 m, 100 m, 200 m, … 5000 m, hurdles, relays.</li>
|
||||
<li><strong>Field</strong> — distance/height events measured in metres (higher is better): Long Jump, Triple Jump, Shot Put, Discus, Javelin, Cricket Ball.</li>
|
||||
<li><strong>High Jump</strong> — its own category because it uses a rising bar with multiple attempts, scored differently from other field events.</li>
|
||||
</ul>
|
||||
<p>The <strong>Events</strong> page lists every event grouped by category. Each event can be offered at several <strong>levels</strong> (age/sex divisions) within a tournament.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-plus-square me-1"></i>Adding events to a tournament</h3>
|
||||
<p>On a tournament's <strong>Details</strong> page, use <strong>Add Event Level</strong> to pick an event and a level (e.g. "100 m" for "Under 16 Boys"). Each event+level combination is where registrations and scoring happen.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ TRACK ============ *@
|
||||
<section class="help-section" id="track">
|
||||
<h2><i class="bi bi-stopwatch me-1"></i>Track events</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-list-ol me-1"></i>How a track event flows</h3>
|
||||
<ol>
|
||||
<li><strong>Create rounds</strong> — a preliminary <em>Heat</em> round and a <em>Final</em> (add a Semi-Final too for big fields).</li>
|
||||
<li><strong>Seed heats</strong> — athletes are split into heats of at most <strong>8</strong> lanes.</li>
|
||||
<li><strong>Record times</strong> for each lane, per heat tab.</li>
|
||||
<li><strong>Calculate Advancement</strong> — marks who progresses (see "Top N + M FL" below).</li>
|
||||
<li><strong>Send Advancers to Next Round</strong> — copies the qualifiers into the next round.</li>
|
||||
<li>Record the final's times, then <strong>Calculate Final Standings</strong> to award places & points.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-input-cursor me-1"></i>What does the "3, 3, 2" dropdown (Add Round) mean?</h3>
|
||||
<p>When you add a round you set its <strong>advancement rule</strong>: <strong>Advance Top N</strong> per heat + <strong>M Fastest Losers</strong>. For example "Top 3 + 2 FL" means the first 3 in each heat qualify automatically, plus the 2 fastest remaining athletes across all heats. For a <strong>Final</strong> round these are not used (a final awards points instead of advancing anyone), so the advance fields are hidden.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-question-circle me-1"></i>What does "Top 0 + 0 FL" mean, and does it update?</h3>
|
||||
<p>That's just a round whose advancement rule is still set to zero — nobody is set to advance yet. It updates the moment you give the round real advancement numbers (e.g. Top 3 + 2 FL) when you create or edit it. On a <strong>Final</strong> round it stays at zero on purpose, because a final doesn't advance anyone — it's the last round.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-trophy me-1"></i>Heats never exceed 8 — and points come from the final</h3>
|
||||
<p>Seeding always caps a heat at <strong>8 lanes</strong>; with more athletes the system makes additional heats. <strong>Points and placements are awarded only in the final round</strong> via <strong>Calculate Final Standings</strong> — the finishers are ranked fastest-first and given placement points (10, 8, 6, 5…) and World Athletics points for their time.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-arrow-repeat me-1"></i>Re-running the calculation</h3>
|
||||
<p>If you correct a time after calculating, just click <strong>Calculate Final Standings</strong> again — it recomputes places and points from the current times. The "Back" button on a heat page returns you to that specific event's page.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ FIELD ============ *@
|
||||
<section class="help-section" id="field">
|
||||
<h2><i class="bi bi-rulers me-1"></i>Field events</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-pencil-square me-1"></i>Recording and scoring a field event</h3>
|
||||
<ol>
|
||||
<li>Enter each athlete's best <strong>mark in metres</strong> and Save.</li>
|
||||
<li>Click <strong>Calculate Points</strong> — converts each mark to World Athletics points.</li>
|
||||
<li>Click <strong>Calculate Placements</strong> — ranks athletes by points and awards placement points (10, 8, 6…).</li>
|
||||
</ol>
|
||||
<p>Field events are measured in <strong>metres</strong> (e.g. a 6.42 m long jump), where a bigger number is better.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ HIGH JUMP ============ *@
|
||||
<section class="help-section" id="highjump">
|
||||
<h2><i class="bi bi-arrow-up-circle me-1"></i>High jump</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-bar-chart-steps me-1"></i>Recording a high jump competition</h3>
|
||||
<ol>
|
||||
<li><strong>Add the bar heights</strong> (e.g. 1.40, 1.45, 1.50 …) in ascending order.</li>
|
||||
<li>For each athlete at each bar, record attempts: <span class="text-success fw-bold">O</span> = cleared, <span class="text-danger fw-bold">X</span> = failed, <span class="text-muted fw-bold">–</span> = passed. Three failures in a row eliminates an athlete.</li>
|
||||
<li>Click <strong>Calculate Results</strong> to rank everyone and award points.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-table me-1"></i>Where are the points? Do I need to click a button?</h3>
|
||||
<p>The grid of O/X/– only records attempts. The <strong>Results</strong> table (placement, best height, points) appears <strong>after you click "Calculate Results"</strong>. That button takes everyone's clearances, ranks them, and writes their placement and points. Until then the Results section shows a prompt. It's safe to re-run any time after fixing an attempt.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-shuffle me-1"></i>How ties are broken (countback)</h3>
|
||||
<p>Athletes are ranked by World Athletics high-jump countback:</p>
|
||||
<ol>
|
||||
<li><strong>Highest bar cleared</strong> wins.</li>
|
||||
<li>If tied, <strong>fewest misses at that top bar</strong> wins.</li>
|
||||
<li>If still tied, <strong>fewest total misses</strong> across the whole competition wins.</li>
|
||||
</ol>
|
||||
<p>So yes — earlier bars are consulted, but only as the second tie-breaker, after comparing misses at the final height.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-people me-1"></i>Genuine ties & jump-offs</h3>
|
||||
<p>If two athletes are identical on all three countback measures they are a true tie: they <strong>share the same placement</strong>, and the points for the positions they occupy are <strong>pooled and split equally</strong> (e.g. a tie for 1st & 2nd gives each (10+8)÷2 = 9). A tie for <strong>1st place</strong> can't be settled by countback, so the system shows a <strong>jump-off required</strong> warning naming the athletes; arrange a jump-off and adjust as needed.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ WA POINTS ============ *@
|
||||
<section class="help-section" id="wa-points">
|
||||
<h2><i class="bi bi-calculator me-1"></i>World Athletics points (Scoring Constants)</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-question-octagon me-1"></i>What are the Scoring Constants for?</h3>
|
||||
<p>They convert any performance — a time, a distance, a height — onto a single common <strong>points scale</strong>, so results in completely different events can be compared and added together fairly (the same idea behind the decathlon). Each event has its own three constants — <strong>A, B and C</strong> — editable on the <strong>Scoring Config</strong> page.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-function me-1"></i>The two formulas</h3>
|
||||
<p>The shape depends on whether lower or higher is better:</p>
|
||||
<p><span class="help-formula">Track (time): Points = A × (B − T)^C</span> — T is the time in seconds.</p>
|
||||
<p><span class="help-formula">Field & High Jump: Points = A × (P − B)^C</span> — P is the mark in metres.</p>
|
||||
<p>The result is rounded down to a whole number, and a performance worse than B scores <strong>0</strong>.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-sliders me-1"></i>What A, B and C each mean</h3>
|
||||
<ul>
|
||||
<li><strong>B — baseline/cutoff:</strong> the threshold worth ~0 points. Track: the slowest time that still scores. Field/Jump: the smallest mark that still scores.</li>
|
||||
<li><strong>A — scale factor:</strong> sets the overall magnitude — how many points each unit of margin above the baseline is worth.</li>
|
||||
<li><strong>C — curve shape:</strong> the exponent. C > 1 means better performances earn disproportionately more points (rewards excellence).</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-123 me-1"></i>Worked example — High Jump (A = 585.63, B = 0.75, C = 1.42)</h3>
|
||||
<p>Using <code>Points = 585.63 × (Height − 0.75)^1.42</code>:</p>
|
||||
<ul>
|
||||
<li>1.60 m → 585.63 × (0.85)^1.42 = <strong>464</strong></li>
|
||||
<li>1.55 m → 585.63 × (0.80)^1.42 = <strong>426</strong></li>
|
||||
<li>1.50 m → 585.63 × (0.75)^1.42 = <strong>389</strong></li>
|
||||
</ul>
|
||||
<p>B = 0.75 means a "jump" of 0.75 m or lower scores 0. For comparison, 100 m uses <code>26 × (18 − T)^1.81</code>, so a 100 m slower than 18 s scores 0.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-exclamation-triangle me-1"></i>Why does an event show 0 points?</h3>
|
||||
<p>If an event's <strong>Points</strong> column is 0 for everyone, it usually has <strong>no scoring constants configured</strong>. Add A/B/C for it on the <strong>Scoring Config</strong> page and recalculate. (Placement points still work even without constants.)</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ PLACEMENT POINTS ============ *@
|
||||
<section class="help-section" id="placement-points">
|
||||
<h2><i class="bi bi-award me-1"></i>Placement points</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-distribute-vertical me-1"></i>Placement points vs World Athletics points</h3>
|
||||
<p>These are two different columns that reward different things:</p>
|
||||
<ul>
|
||||
<li><strong>Points (World Athletics):</strong> rewards the <em>quality of the performance</em> itself — a great mark scores high regardless of who else competed.</li>
|
||||
<li><strong>Placement points:</strong> rewards <em>finishing position</em> — by default 10 / 8 / 6 / 5 / 4 / 3 / 2 / 1 for 1st–8th — and these are what feed the <strong>school standings</strong>.</li>
|
||||
</ul>
|
||||
<p>An athlete earns both. You can change the placement values on the <strong>Scoring Config</strong> page.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ SCHOOLS & STUDENTS ============ *@
|
||||
<section class="help-section" id="schools-students">
|
||||
<h2><i class="bi bi-people me-1"></i>Schools & students</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-building me-1"></i>Managing schools</h3>
|
||||
<p>The <strong>Schools</strong> page lists every institution with its zone, level and student count. Add or edit schools here; each student belongs to one school, and school standings are tallied from their students' placement points.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-person-plus me-1"></i>Managing students & eligibility</h3>
|
||||
<p>Add students under <strong>Students</strong>, each with a date of birth and sex. Age/sex determine which event <strong>levels</strong> they're eligible for. A tournament event level can <strong>waive the age restriction</strong> if needed (toggled on the tournament's Details page).</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ REPORTS ============ *@
|
||||
<section class="help-section" id="reports">
|
||||
<h2><i class="bi bi-file-earmark-bar-graph me-1"></i>Reports & Excel export</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-table me-1"></i>Available reports</h3>
|
||||
<p>The <strong>Reports</strong> section includes Popular Events, Registration by Gender, Event/School breakdowns, Students by School, Scores by Event, and Student Points. Most can be filtered by tournament, zone, school or event.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-file-earmark-excel me-1"></i>Exporting to Excel</h3>
|
||||
<p>Every report has an <strong>Export to Excel</strong> button that downloads the current report as an <code>.xlsx</code> spreadsheet for printing or sharing.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ PAGINATION ============ *@
|
||||
<section class="help-section" id="pagination">
|
||||
<h2><i class="bi bi-card-list me-1"></i>Lists & pagination</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-arrow-left-right me-1"></i>Page size & navigation</h3>
|
||||
<p>Long lists (students, schools, tournaments, etc.) are paginated. Use the <strong>Show … per page</strong> dropdown to display 20, 50, 100, 500 or <strong>All</strong> items, and the page numbers to move between pages. Your filters are kept as you page.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ DASHBOARD ============ *@
|
||||
<section class="help-section" id="dashboard">
|
||||
<h2><i class="bi bi-speedometer2 me-1"></i>Dashboard</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-grid-1x2 me-1"></i>What the Dashboard shows</h3>
|
||||
<p>The Dashboard summarises the system at a glance: totals for students, schools, events and recorded scores; a breakdown of tournaments by status; and <strong>Quick Actions</strong> shortcuts. Pick a tournament from the selector to see its registrations, how many events are scored vs pending, the live <strong>school standings</strong>, and a <strong>recent activity</strong> feed of the latest results.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@* ============ FAQ ============ *@
|
||||
<section class="help-section" id="faq">
|
||||
<h2><i class="bi bi-patch-question me-1"></i>FAQ / troubleshooting</h2>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-dash-circle me-1"></i>An event's points are all zero</h3>
|
||||
<p>The event likely has no <strong>scoring constants</strong>. Configure A/B/C on the Scoring Config page, then recalculate. Placement points are unaffected.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-eye-slash me-1"></i>A tournament disappeared from the list</h3>
|
||||
<p>It was probably <strong>archived</strong>. Tick <strong>Show archived</strong> on the Tournaments page to reveal it, then <strong>Unarchive</strong> if you want it back in the normal list.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-x-octagon me-1"></i>High jump shows no results</h3>
|
||||
<p>Click <strong>Calculate Results</strong> on the High Jump page — the Results table only fills in after you run it.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-trophy me-1"></i>Track event has no points</h3>
|
||||
<p>Track points are awarded only in the <strong>final</strong> round. Make sure you've created a Final round, recorded its times, and clicked <strong>Calculate Final Standings</strong>.</p>
|
||||
</div>
|
||||
|
||||
<div class="help-entry">
|
||||
<h3><i class="bi bi-lock me-1"></i>I can't archive a tournament</h3>
|
||||
<p>Only <span class="badge bg-success">Completed</span> tournaments can be archived — the button appears once a tournament is marked Completed.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
(function () {
|
||||
var search = document.getElementById('helpSearch');
|
||||
var entries = Array.prototype.slice.call(document.querySelectorAll('.help-entry'));
|
||||
var sections = Array.prototype.slice.call(document.querySelectorAll('.help-section'));
|
||||
var noResults = document.getElementById('helpNoResults');
|
||||
var toc = document.getElementById('helpToc');
|
||||
var countEl = document.getElementById('helpCount');
|
||||
|
||||
// Precompute searchable text for each entry (heading + body).
|
||||
entries.forEach(function (e) { e._text = (e.textContent || '').toLowerCase(); });
|
||||
|
||||
function applyFilter() {
|
||||
var q = search.value.trim().toLowerCase();
|
||||
var visible = 0;
|
||||
entries.forEach(function (e) {
|
||||
var match = q === '' || e._text.indexOf(q) !== -1;
|
||||
e.style.display = match ? '' : 'none';
|
||||
if (match) visible++;
|
||||
});
|
||||
// Hide a section header when none of its entries match.
|
||||
sections.forEach(function (s) {
|
||||
var es = s.querySelectorAll('.help-entry');
|
||||
var anyVisible = false;
|
||||
for (var i = 0; i < es.length; i++) { if (es[i].style.display !== 'none') { anyVisible = true; break; } }
|
||||
s.style.display = (!anyVisible && q !== '') ? 'none' : '';
|
||||
});
|
||||
noResults.style.display = (visible === 0 && q !== '') ? 'block' : 'none';
|
||||
if (toc) toc.style.visibility = q !== '' ? 'hidden' : 'visible';
|
||||
countEl.textContent = q !== '' ? (visible + ' topic' + (visible === 1 ? '' : 's') + ' found') : '';
|
||||
}
|
||||
|
||||
search.addEventListener('input', applyFilter);
|
||||
// Pressing Escape clears the search.
|
||||
search.addEventListener('keydown', function (e) { if (e.key === 'Escape') { search.value = ''; applyFilter(); } });
|
||||
})();
|
||||
</script>
|
||||
}
|
||||
106
src/SportsDivision.Web/Views/Shared/_HelpLayout.cshtml
Normal file
106
src/SportsDivision.Web/Views/Shared/_HelpLayout.cshtml
Normal file
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - Sports Division Help</title>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
|
||||
<style>
|
||||
:root {
|
||||
--help-primary: #003366;
|
||||
--help-accent: #FFD700;
|
||||
}
|
||||
body {
|
||||
background-color: #f4f6f9;
|
||||
color: #212529;
|
||||
}
|
||||
.help-topbar {
|
||||
background-color: var(--help-primary);
|
||||
color: #fff;
|
||||
padding: .6rem 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1030;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,.15);
|
||||
}
|
||||
.help-topbar .brand { font-weight: 600; font-size: 1.1rem; }
|
||||
.help-topbar .brand i { color: var(--help-accent); }
|
||||
.help-topbar a.back-link { color: #fff; text-decoration: none; }
|
||||
.help-topbar a.back-link:hover { color: var(--help-accent); }
|
||||
|
||||
.help-wrap { max-width: 1100px; margin: 0 auto; padding: 1.5rem 1rem 4rem; }
|
||||
|
||||
.help-hero {
|
||||
background: linear-gradient(135deg, var(--help-primary), #004080);
|
||||
color: #fff;
|
||||
border-radius: .75rem;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.help-search-bar {
|
||||
position: sticky;
|
||||
top: 56px;
|
||||
z-index: 1020;
|
||||
background: #f4f6f9;
|
||||
padding: .75rem 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.help-search-bar input {
|
||||
font-size: 1.05rem;
|
||||
padding: .65rem 1rem .65rem 2.6rem;
|
||||
}
|
||||
.help-search-icon {
|
||||
position: absolute; left: .9rem; top: 50%; transform: translateY(-50%);
|
||||
color: #6c757d; pointer-events: none;
|
||||
}
|
||||
.help-layout { display: grid; grid-template-columns: 240px 1fr; gap: 1.5rem; }
|
||||
@@media (max-width: 800px) { .help-layout { grid-template-columns: 1fr; } .help-toc { display: none; } }
|
||||
|
||||
.help-toc {
|
||||
position: sticky; top: 130px; align-self: start;
|
||||
max-height: calc(100vh - 150px); overflow-y: auto;
|
||||
}
|
||||
.help-toc a {
|
||||
display: block; padding: .3rem .5rem; color: #495057; text-decoration: none;
|
||||
border-left: 3px solid transparent; font-size: .92rem; border-radius: 0 .25rem .25rem 0;
|
||||
}
|
||||
.help-toc a:hover { background: #e9ecef; border-left-color: var(--help-accent); color: var(--help-primary); }
|
||||
|
||||
.help-section { scroll-margin-top: 130px; margin-bottom: 2rem; }
|
||||
.help-section > h2 {
|
||||
color: var(--help-primary); border-bottom: 2px solid var(--help-accent);
|
||||
padding-bottom: .35rem; margin-bottom: 1rem; font-size: 1.45rem;
|
||||
}
|
||||
.help-entry {
|
||||
background: #fff; border: 1px solid #e3e6ea; border-radius: .5rem;
|
||||
padding: 1rem 1.25rem; margin-bottom: .85rem; box-shadow: 0 1px 2px rgba(0,0,0,.04);
|
||||
}
|
||||
.help-entry h3 { font-size: 1.08rem; margin-bottom: .5rem; color: #003366; }
|
||||
.help-entry h3 i { color: var(--help-accent); }
|
||||
.help-entry p:last-child, .help-entry ul:last-child, .help-entry ol:last-child { margin-bottom: 0; }
|
||||
.help-entry code, .help-formula {
|
||||
background: #eef2f7; border-radius: .25rem; padding: .1rem .35rem; font-size: .92em;
|
||||
}
|
||||
.help-formula { display: inline-block; padding: .4rem .7rem; margin: .25rem 0; font-weight: 600; }
|
||||
mark { background: var(--help-accent); padding: 0 .1rem; }
|
||||
.help-noresults { display: none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="help-topbar">
|
||||
<span class="brand"><i class="bi bi-life-preserver"></i> Sports Division Help</span>
|
||||
<a class="back-link" asp-controller="Dashboard" asp-action="Index"><i class="bi bi-arrow-left-circle me-1"></i>Back to App</a>
|
||||
</div>
|
||||
|
||||
<div class="help-wrap">
|
||||
@RenderBody()
|
||||
</div>
|
||||
|
||||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,6 +29,12 @@
|
||||
<span class="d-none d-sm-inline">@displayName</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
<a class="dropdown-item" asp-controller="Help" asp-action="Index">
|
||||
<i class="bi bi-question-circle me-2"></i>Help & User Guide
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider" /></li>
|
||||
<li>
|
||||
<form asp-controller="Account" asp-action="Logout" method="post">
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
Reference in New Issue
Block a user