feat: merge event rankings and attribute badges into the page printer

Interview notes can print each student's ranked events and a shared attribute legend from the same catalog as the ranking index, without leftover table markup or collapsed answer space.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-30 23:03:10 -04:00
co-authored by Cursor
parent 3f50d6e635
commit 3712dba974
19 changed files with 817 additions and 118 deletions
+24 -91
View File
@@ -1,4 +1,5 @@
using Core.Entities;
using Core.Printing;
using MudBlazor;
namespace WebApp.Models
@@ -14,50 +15,26 @@ namespace WebApp.Models
public static string Captain = Icons.Material.Filled.Star;
public static string Registration = Icons.Material.Filled.AppRegistration;
public static string EventCalendar = Icons.Material.Filled.Event;
public static string LevelOfEffortIcon(int? loe)
{
return loe switch
public static string LevelOfEffortIcon(int? loe) =>
loe switch
{
1 => "○",
2 => "◐",
3 => "⬤",
1 => EventAttributeMarks.LevelOfEffort1,
2 => EventAttributeMarks.LevelOfEffort2,
3 => EventAttributeMarks.LevelOfEffort3,
_ => Icons.Material.Filled.QuestionMark
};
}
/*https://unicodeplus.com/search*/
public static string OnSiteActivity = "ⓐ";
public static string RegionalEvent = "ⓡ";
public static string IndividualEvent = "ⓘ";
public static string PresubmissionEvent = "ⓟ";
public static string PresentationEvent = "";
public static string OnSiteActivity => EventAttributeMarks.OnSite;
public static string RegionalEvent => EventAttributeMarks.Regional;
public static string IndividualEvent => EventAttributeMarks.Individual;
public static string PresubmissionEvent => EventAttributeMarks.Presubmission;
public static string PresentationEvent => "";
// Tooltip mapping for icon unicode characters
public static Dictionary<string, string> IconTooltips => new()
{
{ OnSiteActivity, "On-Site Activity" },
{ RegionalEvent, "Regional Event" },
{ IndividualEvent, "Individual Event" },
{ PresubmissionEvent, "Presubmission" },
{ PresentationEvent, "Presentation/Interview" },
{ "○", "Level of Effort: 1" },
{ "◐", "Level of Effort: 2" },
{ "●", "Level of Effort: 3" }
};
public static IReadOnlyDictionary<string, string> IconTooltips { get; } =
EventAttributeMarks.LegendItems.ToDictionary(m => m.Symbol, m => m.Label, StringComparer.Ordinal);
// Color mapping for icon unicode characters
public static Dictionary<string, string> IconColors => new()
{
{ OnSiteActivity, "#ff9800" }, // Orange
{ RegionalEvent, "#2196f3" }, // Blue
{ IndividualEvent, "#9c27b0" }, // Purple
{ PresubmissionEvent, "#4caf50" }, // Green
{ PresentationEvent, "#f44336" }, // Red
{ "○", "#757575" }, // Gray
{ "◐", "#616161" }, // Darker Gray
{ "●", "#424242" } // Even Darker Gray
};
public static IReadOnlyDictionary<string, string> IconColors { get; } =
EventAttributeMarks.LegendItems.ToDictionary(m => m.Symbol, m => m.Color, StringComparer.Ordinal);
public static string EventEffort(EventDefinition eventDefinition)
{
@@ -96,63 +73,19 @@ namespace WebApp.Models
};
}
public static string RankedEventColor(int rank)
{
return rank switch
{
1 => "#dd7e6b",
2 => "#ea9999",
3 => "#f9cb9c",
4 => "#ffe599",
5 => "#fff2cc",
6 => "#fffaea",
7 => "#fffefa",
8 => "#fffefc",
9 => "#fffffd",
10 => "#fffffe",
_ => "#ddd"
};
}
public static string RankedEventColor(int rank) => EventRankLegend.ColorHex(rank);
public static string GetOrdinal(int num)
{
if (num <= 0) return num.ToString();
switch (num % 100)
{
case 11:
case 12:
case 13:
return num + "th";
}
switch (num % 10)
{
case 1:
return num + "st";
case 2:
return num + "nd";
case 3:
return num + "rd";
default:
return num + "th";
}
}
public static string GetOrdinal(int num) => EventRankLegend.Ordinal(num);
public static string GetOrdinalSuperscript(int number)
{
var suffix = number switch
{
11 or 12 or 13 => "th",
_ => (number % 10) switch
{
1 => "st",
2 => "nd",
3 => "rd",
_ => "th"
}
};
return $"{number}<sup>{suffix}</sup>";
var ordinal = EventRankLegend.Ordinal(number);
var suffixAt = 0;
while (suffixAt < ordinal.Length && (char.IsDigit(ordinal[suffixAt]) || ordinal[suffixAt] == '-'))
suffixAt++;
return suffixAt is 0 || suffixAt == ordinal.Length
? ordinal
: $"{ordinal[..suffixAt]}<sup>{ordinal[suffixAt..]}</sup>";
}
/// <summary>