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>
132 lines
5.6 KiB
Plaintext
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));
|
|
}
|
|
}
|