Files
chapter-organizer/WebApp/Components/Features/MeetingSchedule/ScheduledTeamsList.razor
T
poprhythmandCursor 3f50d6e635 feat: add a Tools page printer for note merge and print presets
Chapter officers can merge a markdown note onto filtered students, teams, or events and save the recipe. Extra student-note columns are additional fields (any ## … fields heading) so they work as print tokens whether imported or typed by hand.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 15:38:43 -04:00

132 lines
5.6 KiB
Plaintext

@using Core.Calculation
@using WebApp.Models
@using Core.Utility
<MudStack>
<MudText Typo="Typo.h6">@TimeSlotName</MudText>
@foreach (var team in Teams.OrderByEventFormatFirst().ThenBy(e => e.ToString()))
{
var scheduledTeamIds = ScheduledTeams.Select(t => t.Id).ToHashSet();
var removed = !scheduledTeamIds.Contains(team.Id);
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center" Class="d-flex align-center">
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
<MudIconButton Icon="@Icons.Material.Filled.Clear"
Size="Size.Small"
Class="@(removed ? "" : "d-none")"
OnClick="@(() => OnToggleTeam.InvokeAsync(team))"
aria-label="Restore team" />
@{
var teamMembers = TeamStudentNameFormatter.FormatStudentList(
team,
new TeamStudentNameFormatter.FormatOptions
{
Ordering = TeamStudentNameFormatter.OrderingStyle.None
});
}
<MudTooltip Text="@teamMembers">
<div @onclick="@(() => OnToggleTeam.InvokeAsync(team))" style="cursor: pointer; display: inline-block;">
<MudChip T="string"
Size="Size.Small"
Color="Color.Default"
Variant="@AppIcons.TeamChipVariant()">
@team
</MudChip>
</div>
</MudTooltip>
</MudStack>
<MudStack Row="true" Spacing="1" Wrap="Wrap.Wrap" AlignItems="AlignItems.Center">
@{
var nonExcludedStudentCount = GetNonExcludedStudentCount(team);
}
@foreach (var student in team.Students)
{
var overlap = StudentHasOverlaps(student);
var chipColor = overlap ? Color.Warning : Color.Default;
var isExcluded = IsStudentExcluded(team.Id, TimeSlotIndex, student.Id);
var formattedName = TeamStudentNameFormatter.FormatStudentName(
student,
team,
new TeamStudentNameFormatter.FormatOptions
{
MarkOverlaps = true,
HasOverlaps = StudentHasOverlaps,
MarkAbsent = true,
AbsentStudents = AbsentStudents.ToList()
});
@if (nonExcludedStudentCount > 1)
{
<InteractiveChip Size="Size.Small"
Color="@chipColor"
Variant="@AppIcons.StudentChipVariant()"
Style="@(isExcluded ? "opacity: 0.5;" : "")">
<ChildContent>
<span>@formattedName</span>
</ChildContent>
<ControlContent>
<MudIconButton Icon="@Icons.Material.Filled.Close"
Size="Size.Small"
Color="Color.Error"
Variant="Variant.Text"
OnClick="@(() => OnToggleStudentExclusion.InvokeAsync((team.Id, TimeSlotIndex, student.Id)))"
Style="padding: 0; min-width: 16px; width: 16px; height: 16px; margin-left: 4px;" />
</ControlContent>
</InteractiveChip>
}
else
{
<MudChip T="string"
Size="Size.Small"
Color="@chipColor"
Variant="@AppIcons.StudentChipVariant()"
Style="@(isExcluded ? "opacity: 0.5;" : "")">
<span>@formattedName</span>
</MudChip>
}
}
</MudStack>
</MudStack>
}
</MudStack>
@code {
[Parameter]
public string TimeSlotName { get; set; } = string.Empty;
[Parameter]
public int TimeSlotIndex { get; set; }
[Parameter]
public IEnumerable<Team> Teams { get; set; } = [];
[Parameter]
public IEnumerable<Team> ScheduledTeams { get; set; } = [];
[Parameter]
public IEnumerable<Student> AbsentStudents { get; set; } = [];
[Parameter]
public Func<Student, bool> StudentHasOverlaps { get; set; } = null!;
[Parameter]
public Dictionary<(int teamId, int timeSlotIndex, int studentId), bool> ExcludedStudents { get; set; } = new();
[Parameter]
public EventCallback<Team> OnToggleTeam { get; set; }
[Parameter]
public EventCallback<(int teamId, int timeSlotIndex, int studentId)> OnToggleStudentExclusion { get; set; }
private bool IsStudentExcluded(int teamId, int timeSlotIndex, int studentId)
{
var key = (teamId, timeSlotIndex, studentId);
return ExcludedStudents.ContainsKey(key) && ExcludedStudents[key];
}
private int GetNonExcludedStudentCount(Team team)
{
return team.Students.Count(s => !IsStudentExcluded(team.Id, TimeSlotIndex, s.Id));
}
}