Implement TeamMeetingToggleSelector component and extend team management functionality
This commit introduces the TeamMeetingToggleSelector component, which allows for the selection and management of teams within the meeting schedule. The Index.razor component has been updated to utilize this new selector, enhancing the user interface for managing scheduled and extended teams. Additionally, new methods for saving and loading extended teams have been added, improving the overall functionality and user experience in team scheduling. These changes contribute to better organization and management of team events in the application.
This commit is contained in:
@@ -91,11 +91,13 @@
|
|||||||
Label="Search for absent students"
|
Label="Search for absent students"
|
||||||
ShowFullName="true"/>
|
ShowFullName="true"/>
|
||||||
<MudDivider Class="my-4"/>
|
<MudDivider Class="my-4"/>
|
||||||
<TeamToggleSelector Teams="@_teams"
|
<TeamMeetingToggleSelector Teams="@_teams"
|
||||||
SelectedTeams="_scheduledTeams"
|
SelectedTeams="_scheduledTeams"
|
||||||
SelectedTeamsChanged="OnScheduledTeamsChanged"
|
SelectedTeamsChanged="OnScheduledTeamsChanged"
|
||||||
Title="Scheduled Teams"
|
ExtendedTeams="_extendedTeams"
|
||||||
ShowEventAttributes="true" />
|
ExtendedTeamsChanged="OnExtendedTeamsChanged"
|
||||||
|
Title="Scheduled Teams"
|
||||||
|
ShowEventAttributes="false" />
|
||||||
</MudStack>
|
</MudStack>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
|
||||||
@@ -112,6 +114,7 @@
|
|||||||
private IEnumerable<Team> _scheduledTeams = [];
|
private IEnumerable<Team> _scheduledTeams = [];
|
||||||
private IEnumerable<Student> _absentStudents = [];
|
private IEnumerable<Student> _absentStudents = [];
|
||||||
private IEnumerable<Team> _possibleAdditions = [];
|
private IEnumerable<Team> _possibleAdditions = [];
|
||||||
|
private IEnumerable<Team> _extendedTeams = [];
|
||||||
|
|
||||||
private async Task OnScheduledTeamsChanged(IEnumerable<Team> teams)
|
private async Task OnScheduledTeamsChanged(IEnumerable<Team> teams)
|
||||||
{
|
{
|
||||||
@@ -131,6 +134,12 @@
|
|||||||
await SaveTimeSlotCount();
|
await SaveTimeSlotCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task OnExtendedTeamsChanged(IEnumerable<Team> teams)
|
||||||
|
{
|
||||||
|
_extendedTeams = teams;
|
||||||
|
await SaveExtendedTeams();
|
||||||
|
}
|
||||||
|
|
||||||
private async void AddRegionals()
|
private async void AddRegionals()
|
||||||
{
|
{
|
||||||
_scheduledTeams
|
_scheduledTeams
|
||||||
@@ -240,6 +249,7 @@
|
|||||||
await LoadScheduledTeams();
|
await LoadScheduledTeams();
|
||||||
await LoadAbsentStudents();
|
await LoadAbsentStudents();
|
||||||
await LoadTimeSlotCount();
|
await LoadTimeSlotCount();
|
||||||
|
await LoadExtendedTeams();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveScheduledTeams()
|
private async Task SaveScheduledTeams()
|
||||||
@@ -286,6 +296,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task SaveExtendedTeams()
|
||||||
|
{
|
||||||
|
var teamIds = _extendedTeams.Select(t => t.Id).ToArray();
|
||||||
|
await LocalStorage.SetIntArrayAsync("MeetingSchedule_ExtendedTeams", teamIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadExtendedTeams()
|
||||||
|
{
|
||||||
|
var teamIds = await LocalStorage.GetIntArrayAsync("MeetingSchedule_ExtendedTeams");
|
||||||
|
if (teamIds.Length > 0)
|
||||||
|
{
|
||||||
|
_extendedTeams = _teams.Where(t => teamIds.Contains(t.Id)).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<TableData<TeamScheduleTimeSlot>> SolveSchedule(TableState arg1, CancellationToken arg2)
|
private async Task<TableData<TeamScheduleTimeSlot>> SolveSchedule(TableState arg1, CancellationToken arg2)
|
||||||
{
|
{
|
||||||
_isSolving = true;
|
_isSolving = true;
|
||||||
@@ -312,9 +337,21 @@
|
|||||||
// Update parameters with absent student names
|
// Update parameters with absent student names
|
||||||
_parameters.AbsentStudents = _absentStudents.Select(s => s.FirstNameLastName).ToArray();
|
_parameters.AbsentStudents = _absentStudents.Select(s => s.FirstNameLastName).ToArray();
|
||||||
|
|
||||||
|
// Update parameters with extended team event names
|
||||||
|
_parameters.ExtendedTeams = _extendedTeams
|
||||||
|
.Where(t => t.Event != null)
|
||||||
|
.Select(t => t.Event!.Name)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
var teamScheduler = new TeamScheduler(_scheduledTeams, _parameters.TimeSlots, availableStudents);
|
var teamScheduler = new TeamScheduler(_scheduledTeams, _parameters.TimeSlots, availableStudents);
|
||||||
_solution = teamScheduler.Solve();
|
_solution = teamScheduler.Solve();
|
||||||
|
|
||||||
|
// Post-process: extend teams to next consecutive time slot
|
||||||
|
if (_extendedTeams.Any())
|
||||||
|
{
|
||||||
|
ExtendTeamsInSolution(_solution, _extendedTeams, availableStudents);
|
||||||
|
}
|
||||||
|
|
||||||
// Try recommendation strategies in priority order
|
// Try recommendation strategies in priority order
|
||||||
var scheduler = new UnassignedStudentScheduler(_teams, _solution.TimeSlots);
|
var scheduler = new UnassignedStudentScheduler(_teams, _solution.TimeSlots);
|
||||||
UnassignedScheduleStrategy[] strategies =
|
UnassignedScheduleStrategy[] strategies =
|
||||||
@@ -340,6 +377,57 @@
|
|||||||
_solutionData.ReloadServerData();
|
_solutionData.ReloadServerData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExtendTeamsInSolution(TeamSchedulerSolution solution, IEnumerable<Team> extendedTeams, Student[] allStudents)
|
||||||
|
{
|
||||||
|
if (solution.TimeSlots == null || !solution.TimeSlots.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var extendedTeamsList = extendedTeams.ToList();
|
||||||
|
if (!extendedTeamsList.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var extendedTeamIds = extendedTeamsList.Select(t => t.Id).ToHashSet();
|
||||||
|
|
||||||
|
// Find which time slot each extended team is in
|
||||||
|
for (int slotIndex = 0; slotIndex < solution.TimeSlots.Length; slotIndex++)
|
||||||
|
{
|
||||||
|
var currentSlot = solution.TimeSlots[slotIndex];
|
||||||
|
var teamsToExtend = currentSlot.Teams.Where(t => extendedTeamIds.Contains(t.Id)).ToList();
|
||||||
|
|
||||||
|
if (!teamsToExtend.Any())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if there's a next time slot
|
||||||
|
if (slotIndex + 1 >= solution.TimeSlots.Length)
|
||||||
|
{
|
||||||
|
// Cannot extend - this is the last time slot
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextSlot = solution.TimeSlots[slotIndex + 1];
|
||||||
|
|
||||||
|
// Add extended teams to the next time slot
|
||||||
|
var nextSlotTeamsList = nextSlot.Teams.ToList();
|
||||||
|
var nextSlotTeamIds = nextSlotTeamsList.Select(t => t.Id).ToHashSet();
|
||||||
|
|
||||||
|
foreach (var team in teamsToExtend)
|
||||||
|
{
|
||||||
|
if (!nextSlotTeamIds.Contains(team.Id))
|
||||||
|
{
|
||||||
|
nextSlotTeamsList.Add(team);
|
||||||
|
nextSlotTeamIds.Add(team.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the next slot with the extended teams
|
||||||
|
nextSlot.Teams = nextSlotTeamsList.ToArray();
|
||||||
|
|
||||||
|
// Recalculate overlaps and unscheduled students for the next slot
|
||||||
|
nextSlot.StudentOverlaps = TeamSchedulerSolution.GetStudentTeamOverlaps(nextSlot.Teams);
|
||||||
|
nextSlot.UnscheduledStudents = TeamSchedulerSolution.GetStudentsNotInTimSlot(nextSlot.Teams, allStudents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async Task CopyToClipboard()
|
async Task CopyToClipboard()
|
||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
@using WebApp.Models
|
||||||
|
@using Core.Utility
|
||||||
|
|
||||||
|
@if (Title != null)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.h4">@Title</MudText>
|
||||||
|
}
|
||||||
|
|
||||||
|
<MudToggleGroup T="Team"
|
||||||
|
SelectionMode="SelectionMode.MultiSelection"
|
||||||
|
Values="@SelectedTeams"
|
||||||
|
ValuesChanged="@OnSelectedTeamsChanged"
|
||||||
|
Vertical="true"
|
||||||
|
CheckMark>
|
||||||
|
@foreach (var team in Teams.OrderByEventFormatFirst().ThenBy(e => e.Event.Name))
|
||||||
|
{
|
||||||
|
<MudToggleItem Value="@team" Style="font-size: .75rem;">
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between; gap: 4px; width: 100%;">
|
||||||
|
<MudTooltip Text="@TeamStudentNameFormatter.FormatStudentList(
|
||||||
|
team,
|
||||||
|
new TeamStudentNameFormatter.FormatOptions
|
||||||
|
{
|
||||||
|
CaptainIndicator = TeamStudentNameFormatter.CaptainIndicatorStyle.Captain,
|
||||||
|
Ordering = TeamStudentNameFormatter.OrderingStyle.None
|
||||||
|
})">
|
||||||
|
<span class="ellipsis" style="flex: 1; min-width: 0;">@team.ToString()</span>
|
||||||
|
</MudTooltip>
|
||||||
|
@if (IsSelected(team))
|
||||||
|
{
|
||||||
|
var isExtended = IsExtended(team);
|
||||||
|
<MudTooltip Text="@(isExtended ? "Remove from extended teams" : "Extend to 2 time slots")">
|
||||||
|
<MudIconButton Icon="@(isExtended ? Icons.Material.Filled.AddCircle : Icons.Material.Filled.Add)"
|
||||||
|
Size="Size.Small"
|
||||||
|
Color="@(isExtended ? Color.Primary : Color.Default)"
|
||||||
|
Variant="@(isExtended ? Variant.Filled : Variant.Text)"
|
||||||
|
OnClick="@(() => ToggleExtended(team))"
|
||||||
|
Style="margin-left: 4px; padding: 2px;" />
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
@if (ShowEventAttributes)
|
||||||
|
{
|
||||||
|
<EventAttributes EventDefinition="@team.Event"></EventAttributes>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</MudToggleItem>
|
||||||
|
}
|
||||||
|
</MudToggleGroup>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public IEnumerable<Team> Teams { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public IEnumerable<Team> SelectedTeams { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<IEnumerable<Team>> SelectedTeamsChanged { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public IEnumerable<Team> ExtendedTeams { get; set; } = [];
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<IEnumerable<Team>> ExtendedTeamsChanged { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string? Title { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowEventAttributes { get; set; } = true;
|
||||||
|
|
||||||
|
private bool IsSelected(Team team)
|
||||||
|
{
|
||||||
|
var selectedTeamIds = SelectedTeams.Select(t => t.Id).ToHashSet();
|
||||||
|
return selectedTeamIds.Contains(team.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsExtended(Team team)
|
||||||
|
{
|
||||||
|
var extendedTeamIds = ExtendedTeams.Select(t => t.Id).ToHashSet();
|
||||||
|
return extendedTeamIds.Contains(team.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSelectedTeamsChanged(IEnumerable<Team> value)
|
||||||
|
{
|
||||||
|
SelectedTeams = value;
|
||||||
|
await SelectedTeamsChanged.InvokeAsync(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ToggleExtended(Team team)
|
||||||
|
{
|
||||||
|
var extendedTeamIds = ExtendedTeams.Select(t => t.Id).ToHashSet();
|
||||||
|
IEnumerable<Team> newExtendedTeams;
|
||||||
|
|
||||||
|
if (extendedTeamIds.Contains(team.Id))
|
||||||
|
{
|
||||||
|
// Remove from extended teams
|
||||||
|
newExtendedTeams = ExtendedTeams.Where(t => t.Id != team.Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add to extended teams
|
||||||
|
newExtendedTeams = ExtendedTeams.Concat([team]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtendedTeams = newExtendedTeams;
|
||||||
|
await ExtendedTeamsChanged.InvokeAsync(newExtendedTeams);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user