From 2d9a5b52b7b9651536049877cd05c80b6462fd6b Mon Sep 17 00:00:00 2001 From: warringtond Date: Sat, 27 Jun 2026 14:19:17 -0400 Subject: [PATCH] dashboard upgrade --- .../DTOs/DashboardDto.cs | 2 +- .../Services/DashboardService.cs | 33 +- .../Controllers/DashboardController.cs | 20 +- .../Controllers/HelpController.cs | 10 + .../Views/Dashboard/Index.cshtml | 274 +++++++++--- .../Views/Help/Index.cshtml | 409 ++++++++++++++++++ .../Views/Shared/_HelpLayout.cshtml | 106 +++++ .../Views/Shared/_LoginPartial.cshtml | 6 + 8 files changed, 795 insertions(+), 65 deletions(-) create mode 100644 src/SportsDivision.Web/Controllers/HelpController.cs create mode 100644 src/SportsDivision.Web/Views/Help/Index.cshtml create mode 100644 src/SportsDivision.Web/Views/Shared/_HelpLayout.cshtml diff --git a/src/SportsDivision.Application/DTOs/DashboardDto.cs b/src/SportsDivision.Application/DTOs/DashboardDto.cs index ba88617..01bb6c1 100644 --- a/src/SportsDivision.Application/DTOs/DashboardDto.cs +++ b/src/SportsDivision.Application/DTOs/DashboardDto.cs @@ -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 TopSchools { get; set; } = new(); public List 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 TopSchools { get; set; } = new(); public List 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; } } diff --git a/src/SportsDivision.Application/Services/DashboardService.cs b/src/SportsDivision.Application/Services/DashboardService.cs index f5c236a..abda842 100644 --- a/src/SportsDivision.Application/Services/DashboardService.cs +++ b/src/SportsDivision.Application/Services/DashboardService.cs @@ -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(); + 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(); diff --git a/src/SportsDivision.Web/Controllers/DashboardController.cs b/src/SportsDivision.Web/Controllers/DashboardController.cs index a3de849..e63e43f 100644 --- a/src/SportsDivision.Web/Controllers/DashboardController.cs +++ b/src/SportsDivision.Web/Controllers/DashboardController.cs @@ -18,11 +18,25 @@ public class DashboardController : Controller public async Task 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); } diff --git a/src/SportsDivision.Web/Controllers/HelpController.cs b/src/SportsDivision.Web/Controllers/HelpController.cs new file mode 100644 index 0000000..eb9f298 --- /dev/null +++ b/src/SportsDivision.Web/Controllers/HelpController.cs @@ -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(); +} diff --git a/src/SportsDivision.Web/Views/Dashboard/Index.cshtml b/src/SportsDivision.Web/Views/Dashboard/Index.cshtml index d3430d8..4837c2f 100644 --- a/src/SportsDivision.Web/Views/Dashboard/Index.cshtml +++ b/src/SportsDivision.Web/Views/Dashboard/Index.cshtml @@ -1,96 +1,252 @@ @model DashboardDto @{ ViewData["Title"] = "Dashboard"; - var tournaments = ViewBag.Tournaments as IEnumerable; + var tournamentList = (ViewBag.Tournaments as IEnumerable)?.ToList() ?? new List(); var selectedTournamentId = ViewBag.SelectedTournamentId as int?; + var selectedTournament = tournamentList.FirstOrDefault(t => t.TournamentId == selectedTournamentId); } -
-

Dashboard

-
- - + + @foreach (var t in tournamentList) { } - } - -
+ + + } + else if (tournamentList.Count == 1) + { + @tournamentList[0].Name + }
+@* ---- Top-level counts (system-wide) ---- *@
-
-
+
+
-
Total Students
-

@Model.TotalStudents

+
Total Students
+

@Model.TotalStudents

Male: @Model.MaleStudents | Female: @Model.FemaleStudents
-
-
+
+
-
Schools
-

@Model.TotalSchools

+
Schools
+

@Model.TotalSchools

+ Participating institutions
-
-
+
+
-
Active Tournaments
-

@Model.ActiveTournaments

+
Events
+

@Model.TotalEvents

+ Track, field & high jump
-
-
+
+
-
Registrations
-

@Model.TotalRegistrations

+
Scores Recorded
+

@Model.TotalScores

+ Across all tournaments
-@if (Model.TopSchools.Any()) -{ -
-
-
School Standings
+@* ---- Tournament status breakdown + quick actions ---- *@ +
+
+
+
+
Tournaments by Status
+
+
+
+
+
@Model.DraftTournaments
+ Draft +
+
+
@Model.RegistrationTournaments
+ Registration +
+
+
@Model.ActiveTournaments
+ In Progress +
+
+
@Model.CompletedTournaments
+ Completed +
+
+
@Model.ArchivedTournaments
+ Archived +
+
+
+ + Manage tournaments + +
-
- - - - - - - - - - - - - @for (int i = 0; i < Model.TopSchools.Count; i++) + +
+
+
+
Quick Actions
+
+
+ New Tournament + Schools + Students + Reports + @if (User.IsInRole("Admin")) + { + Scoring Config + } + Help +
+
+
+ + +@if (selectedTournamentId.HasValue) +{ + @* ---- Selected-tournament detail ---- *@ +

@(selectedTournament?.Name ?? "Selected Tournament")

+
+
+
+
+
Registrations
+

@Model.TotalRegistrations

+
+
+
+
+
+
+
Events Scored
+

@Model.EventsCompleted

+ Results calculated +
+
+
+
+
+
+
Events Pending
+

@Model.EventsInProgress

+ Awaiting results +
+
+
+
+ +
+
+
+
+
School Standings
+
+
+ @if (Model.TopSchools.Any()) { - var school = Model.TopSchools[i]; -
- - - - - - - +
#SchoolPoints1st2nd3rd
@(i + 1)@school.SchoolName @(school.ShortName != null ? $"({school.ShortName})" : "")@school.TotalPoints@school.FirstPlaceCount@school.SecondPlaceCount@school.ThirdPlaceCount
+ + + + + + + + + + + + @for (int i = 0; i < Model.TopSchools.Count; i++) + { + var school = Model.TopSchools[i]; + + + + + + + + + } + +
#SchoolPoints1st2nd3rd
@(i + 1)@school.SchoolName @(school.ShortName != null ? $"({school.ShortName})" : "")@school.TotalPoints@school.FirstPlaceCount@school.SecondPlaceCount@school.ThirdPlaceCount
} - - + else + { +

No points scored yet for this tournament.

+ } +
+
+
+
+
+
+
Recent Activity
+
+
+ @if (Model.RecentScores.Any()) + { +
    + @foreach (var s in Model.RecentScores) + { +
  • +
    +
    @s.StudentName
    + @s.EventName — @s.EventLevelName · @s.RawPerformance.ToString("0.00") +
    +
    + @if (s.Placement != null) + { + #@s.Placement + } +
    @s.RecordedAt.ToString("MMM d, HH:mm")
    +
    +
  • + } +
+ } + else + { +

No scores recorded yet for this tournament.

+ } +
+
} +else if (tournamentList.Count > 1) +{ +
+ + You have multiple active tournaments. Select one above to see its registrations, school standings, and recent scoring activity. +
+} +else +{ +
+ + No active tournaments yet. Create a tournament to get started. +
+} diff --git a/src/SportsDivision.Web/Views/Help/Index.cshtml b/src/SportsDivision.Web/Views/Help/Index.cshtml new file mode 100644 index 0000000..4b2461b --- /dev/null +++ b/src/SportsDivision.Web/Views/Help/Index.cshtml @@ -0,0 +1,409 @@ +@{ + ViewData["Title"] = "Help & User Guide"; + Layout = "_HelpLayout"; +} + +
+

Help & User Guide

+

Everything you need to run a track & field competition — tournaments, scoring, results, and reports. Use the search box to jump straight to an answer.

+
+ + + +
+ No help topics matched your search. Try a different word. +
+ +
+ + +
+ + @* ============ GETTING STARTED ============ *@ +
+

Getting started

+ +
+

What this system does

+

The Dominica Sports Division system manages school track & field competitions end to end: it holds your schools and students, lets you build tournaments made up of events at different age/sex levels, captures results for track, field and high jump, converts them to points, and produces standings and reports.

+
+ +
+

The big picture — how a competition flows

+
    +
  1. Add your Schools and their Students.
  2. +
  3. Create a Tournament and add the events you'll run (each at one or more age/sex levels).
  4. +
  5. Open registration and register students into events.
  6. +
  7. Run each event and record results in Track Events, Field Events or High Jump.
  8. +
  9. Calculate points/placements; view school standings on the Dashboard and run Reports.
  10. +
+
+ +
+

Using this help page

+

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.

+
+
+ + @* ============ TOURNAMENTS ============ *@ +
+

Tournaments & statuses

+ +
+

The tournament lifecycle

+

A tournament moves through four statuses, in order:

+
    +
  • Draft — you're still setting it up (adding events).
  • +
  • Registration — students can be registered into events.
  • +
  • In Progress — the competition is running and results are being recorded.
  • +
  • Completed — all events are finished and scored.
  • +
+

Open a tournament's Details page and use the buttons in the top-right (Open Registration → Start Tournament → Complete) to move it forward one step at a time.

+
+ +
+

Reverting a status set by mistake

+

Every forward step can be undone. On the Details page you'll also see backward buttons: Back to Draft, Back to Registration, and — if a tournament was marked Completed by mistake — Reopen Tournament (which returns it to In Progress). Nothing is lost; the status simply changes back.

+
+ +
+

Why "Complete" asks for confirmation

+

Marking a tournament Completed 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 Reopen Tournament afterwards to set it back to In Progress).

+
+
+ + @* ============ ARCHIVING ============ *@ +
+

Archiving tournaments

+ +
+

What archiving does

+

Archiving hides an old tournament from the default list to reduce clutter — it is not deleted. All its events, registrations and results are kept and can be restored at any time with Unarchive.

+
+ +
+

Only completed tournaments can be archived

+

The Archive button only appears once a tournament's status is Completed. 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).

+
+ +
+

Where do archived tournaments go? / "I can't see my tournament"

+

Archived tournaments are hidden on the Tournaments page by default. To see them, tick the Show archived switch near the top of the list — they reappear with an Archived badge. A small counter on the switch tells you how many are currently hidden. Untick it to hide them again.

+
+ +
+

Restoring an archived tournament

+

Turn on Show archived, find the tournament, and click Unarchive (on the list row or its Details page). It returns to the normal list immediately, keeping whatever status it had.

+
+
+ + @* ============ EVENTS ============ *@ +
+

Events

+ +
+

The three event categories

+
    +
  • Track — races timed in seconds (lower is better): 80 m, 100 m, 200 m, … 5000 m, hurdles, relays.
  • +
  • Field — distance/height events measured in metres (higher is better): Long Jump, Triple Jump, Shot Put, Discus, Javelin, Cricket Ball.
  • +
  • High Jump — its own category because it uses a rising bar with multiple attempts, scored differently from other field events.
  • +
+

The Events page lists every event grouped by category. Each event can be offered at several levels (age/sex divisions) within a tournament.

+
+ +
+

Adding events to a tournament

+

On a tournament's Details page, use Add Event Level 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.

+
+
+ + @* ============ TRACK ============ *@ +
+

Track events

+ +
+

How a track event flows

+
    +
  1. Create rounds — a preliminary Heat round and a Final (add a Semi-Final too for big fields).
  2. +
  3. Seed heats — athletes are split into heats of at most 8 lanes.
  4. +
  5. Record times for each lane, per heat tab.
  6. +
  7. Calculate Advancement — marks who progresses (see "Top N + M FL" below).
  8. +
  9. Send Advancers to Next Round — copies the qualifiers into the next round.
  10. +
  11. Record the final's times, then Calculate Final Standings to award places & points.
  12. +
+
+ +
+

What does the "3, 3, 2" dropdown (Add Round) mean?

+

When you add a round you set its advancement rule: Advance Top N per heat + M Fastest Losers. 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 Final round these are not used (a final awards points instead of advancing anyone), so the advance fields are hidden.

+
+ +
+

What does "Top 0 + 0 FL" mean, and does it update?

+

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 Final round it stays at zero on purpose, because a final doesn't advance anyone — it's the last round.

+
+ +
+

Heats never exceed 8 — and points come from the final

+

Seeding always caps a heat at 8 lanes; with more athletes the system makes additional heats. Points and placements are awarded only in the final round via Calculate Final Standings — the finishers are ranked fastest-first and given placement points (10, 8, 6, 5…) and World Athletics points for their time.

+
+ +
+

Re-running the calculation

+

If you correct a time after calculating, just click Calculate Final Standings 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.

+
+
+ + @* ============ FIELD ============ *@ +
+

Field events

+ +
+

Recording and scoring a field event

+
    +
  1. Enter each athlete's best mark in metres and Save.
  2. +
  3. Click Calculate Points — converts each mark to World Athletics points.
  4. +
  5. Click Calculate Placements — ranks athletes by points and awards placement points (10, 8, 6…).
  6. +
+

Field events are measured in metres (e.g. a 6.42 m long jump), where a bigger number is better.

+
+
+ + @* ============ HIGH JUMP ============ *@ +
+

High jump

+ +
+

Recording a high jump competition

+
    +
  1. Add the bar heights (e.g. 1.40, 1.45, 1.50 …) in ascending order.
  2. +
  3. For each athlete at each bar, record attempts: O = cleared, X = failed, = passed. Three failures in a row eliminates an athlete.
  4. +
  5. Click Calculate Results to rank everyone and award points.
  6. +
+
+ +
+

Where are the points? Do I need to click a button?

+

The grid of O/X/– only records attempts. The Results table (placement, best height, points) appears after you click "Calculate Results". 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.

+
+ +
+

How ties are broken (countback)

+

Athletes are ranked by World Athletics high-jump countback:

+
    +
  1. Highest bar cleared wins.
  2. +
  3. If tied, fewest misses at that top bar wins.
  4. +
  5. If still tied, fewest total misses across the whole competition wins.
  6. +
+

So yes — earlier bars are consulted, but only as the second tie-breaker, after comparing misses at the final height.

+
+ +
+

Genuine ties & jump-offs

+

If two athletes are identical on all three countback measures they are a true tie: they share the same placement, and the points for the positions they occupy are pooled and split equally (e.g. a tie for 1st & 2nd gives each (10+8)÷2 = 9). A tie for 1st place can't be settled by countback, so the system shows a jump-off required warning naming the athletes; arrange a jump-off and adjust as needed.

+
+
+ + @* ============ WA POINTS ============ *@ +
+

World Athletics points (Scoring Constants)

+ +
+

What are the Scoring Constants for?

+

They convert any performance — a time, a distance, a height — onto a single common points scale, 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 — A, B and C — editable on the Scoring Config page.

+
+ +
+

The two formulas

+

The shape depends on whether lower or higher is better:

+

Track (time): Points = A × (B − T)^C  — T is the time in seconds.

+

Field & High Jump: Points = A × (P − B)^C  — P is the mark in metres.

+

The result is rounded down to a whole number, and a performance worse than B scores 0.

+
+ +
+

What A, B and C each mean

+
    +
  • B — baseline/cutoff: the threshold worth ~0 points. Track: the slowest time that still scores. Field/Jump: the smallest mark that still scores.
  • +
  • A — scale factor: sets the overall magnitude — how many points each unit of margin above the baseline is worth.
  • +
  • C — curve shape: the exponent. C > 1 means better performances earn disproportionately more points (rewards excellence).
  • +
+
+ +
+

Worked example — High Jump (A = 585.63, B = 0.75, C = 1.42)

+

Using Points = 585.63 × (Height − 0.75)^1.42:

+
    +
  • 1.60 m → 585.63 × (0.85)^1.42 = 464
  • +
  • 1.55 m → 585.63 × (0.80)^1.42 = 426
  • +
  • 1.50 m → 585.63 × (0.75)^1.42 = 389
  • +
+

B = 0.75 means a "jump" of 0.75 m or lower scores 0. For comparison, 100 m uses 26 × (18 − T)^1.81, so a 100 m slower than 18 s scores 0.

+
+ +
+

Why does an event show 0 points?

+

If an event's Points column is 0 for everyone, it usually has no scoring constants configured. Add A/B/C for it on the Scoring Config page and recalculate. (Placement points still work even without constants.)

+
+
+ + @* ============ PLACEMENT POINTS ============ *@ +
+

Placement points

+ +
+

Placement points vs World Athletics points

+

These are two different columns that reward different things:

+
    +
  • Points (World Athletics): rewards the quality of the performance itself — a great mark scores high regardless of who else competed.
  • +
  • Placement points: rewards finishing position — by default 10 / 8 / 6 / 5 / 4 / 3 / 2 / 1 for 1st–8th — and these are what feed the school standings.
  • +
+

An athlete earns both. You can change the placement values on the Scoring Config page.

+
+
+ + @* ============ SCHOOLS & STUDENTS ============ *@ +
+

Schools & students

+ +
+

Managing schools

+

The Schools 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.

+
+ +
+

Managing students & eligibility

+

Add students under Students, each with a date of birth and sex. Age/sex determine which event levels they're eligible for. A tournament event level can waive the age restriction if needed (toggled on the tournament's Details page).

+
+
+ + @* ============ REPORTS ============ *@ +
+

Reports & Excel export

+ +
+

Available reports

+

The Reports 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.

+
+ +
+

Exporting to Excel

+

Every report has an Export to Excel button that downloads the current report as an .xlsx spreadsheet for printing or sharing.

+
+
+ + @* ============ PAGINATION ============ *@ +
+

Lists & pagination

+ +
+

Page size & navigation

+

Long lists (students, schools, tournaments, etc.) are paginated. Use the Show … per page dropdown to display 20, 50, 100, 500 or All items, and the page numbers to move between pages. Your filters are kept as you page.

+
+
+ + @* ============ DASHBOARD ============ *@ +
+

Dashboard

+ +
+

What the Dashboard shows

+

The Dashboard summarises the system at a glance: totals for students, schools, events and recorded scores; a breakdown of tournaments by status; and Quick Actions shortcuts. Pick a tournament from the selector to see its registrations, how many events are scored vs pending, the live school standings, and a recent activity feed of the latest results.

+
+
+ + @* ============ FAQ ============ *@ +
+

FAQ / troubleshooting

+ +
+

An event's points are all zero

+

The event likely has no scoring constants. Configure A/B/C on the Scoring Config page, then recalculate. Placement points are unaffected.

+
+ +
+

A tournament disappeared from the list

+

It was probably archived. Tick Show archived on the Tournaments page to reveal it, then Unarchive if you want it back in the normal list.

+
+ +
+

High jump shows no results

+

Click Calculate Results on the High Jump page — the Results table only fills in after you run it.

+
+ +
+

Track event has no points

+

Track points are awarded only in the final round. Make sure you've created a Final round, recorded its times, and clicked Calculate Final Standings.

+
+ +
+

I can't archive a tournament

+

Only Completed tournaments can be archived — the button appears once a tournament is marked Completed.

+
+
+ +
+
+ +@section Scripts { + +} diff --git a/src/SportsDivision.Web/Views/Shared/_HelpLayout.cshtml b/src/SportsDivision.Web/Views/Shared/_HelpLayout.cshtml new file mode 100644 index 0000000..f7247de --- /dev/null +++ b/src/SportsDivision.Web/Views/Shared/_HelpLayout.cshtml @@ -0,0 +1,106 @@ + + + + + + @ViewData["Title"] - Sports Division Help + + + + + +
+ Sports Division Help + Back to App +
+ +
+ @RenderBody() +
+ + + @await RenderSectionAsync("Scripts", required: false) + + diff --git a/src/SportsDivision.Web/Views/Shared/_LoginPartial.cshtml b/src/SportsDivision.Web/Views/Shared/_LoginPartial.cshtml index 8d2548b..9f78267 100644 --- a/src/SportsDivision.Web/Views/Shared/_LoginPartial.cshtml +++ b/src/SportsDivision.Web/Views/Shared/_LoginPartial.cshtml @@ -29,6 +29,12 @@ @displayName