diff --git a/WebApp/Components/Features/MeetingSchedule/History.razor b/WebApp/Components/Features/MeetingSchedule/History.razor index bafde1c..6d34354 100644 --- a/WebApp/Components/Features/MeetingSchedule/History.razor +++ b/WebApp/Components/Features/MeetingSchedule/History.razor @@ -9,6 +9,9 @@ @inject IDialogService DialogService @inject ISnackbar Snackbar @inject IConfiguration Configuration +@inject IMeetingScheduleDataService DataService +@inject IMeetingScheduleStateService StateService +@inject NavigationManager NavigationManager @implements IAsyncDisposable @@ -40,6 +43,7 @@ Breakpoint="Breakpoint.Sm"> + @foreach (var team in _allTeams) { @@ -51,6 +55,9 @@ Date + + Actions + @{ var teamIndex = 0; } @@ -77,6 +84,15 @@ @context.MeetingDate.ToString("MM/dd/yy") + + + + + @{ var rowTeamIndex = 0; } @@ -99,6 +115,7 @@ Times Met + @{ var footerTeamIndex = 0; } @@ -268,6 +285,56 @@ } } + private async Task LoadMeetingIntoPlanner(TeamMeetingHistory history) + { + if (_isDisposed) return; + + try + { + // Get all teams and students from database + var allTeams = await DataService.LoadTeamsAsync(); + var allStudents = await DataService.LoadStudentsAsync(); + + // Match teams from history to all teams by ID for reference equality + var historyTeamIds = history.Teams.Select(t => t.Id).ToHashSet(); + var scheduledTeams = allTeams.Where(t => historyTeamIds.Contains(t.Id)); + + // Calculate absent students (all students not in the meeting history's student list) + var presentStudentIds = history.Students.Select(s => s.Id).ToHashSet(); + var absentStudents = allStudents.Where(s => !presentStudentIds.Contains(s.Id)); + + // Save state to localStorage + await StateService.SaveScheduledTeamsAsync(scheduledTeams); + await StateService.SaveAbsentStudentsAsync(absentStudents); + // Clear extended teams and excluded students when loading from history + await StateService.SaveExtendedTeamsAsync([]); + await StateService.SaveExcludedStudentsAsync(new Dictionary<(int teamId, int timeSlotIndex, int studentId), bool>()); + + // Navigate to planner + NavigationManager.NavigateTo("/meeting-schedule"); + + if (!_isDisposed) + { + Snackbar.Add($"Loaded meeting from {history.MeetingDate:MM/dd/yyyy} into planner", Severity.Success); + } + } + catch (TaskCanceledException) + { + // Component was disposed, ignore + } + catch (JSDisconnectedException) + { + // JS connection lost, ignore + } + catch (Exception ex) + { + if (!_isDisposed) + { + Snackbar.Add($"Error loading meeting into planner: {ex.Message}", Severity.Error); + } + } + } + public async ValueTask DisposeAsync() { if (!_isDisposed) diff --git a/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor b/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor index 863cc64..8aec18f 100644 --- a/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor +++ b/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor @@ -11,6 +11,8 @@ @inject ISnackbar Snackbar @inject IDialogService DialogService @inject IMeetingScheduleDataService DataService +@inject IMeetingScheduleStateService StateService +@inject NavigationManager NavigationManager @implements IAsyncDisposable @@ -128,6 +130,13 @@ Delete + + Load into Planner + t.Id).ToHashSet(); + var scheduledTeams = allTeams.Where(t => historyTeamIds.Contains(t.Id)); + + // Calculate absent students (all students not in the meeting history's student list) + var presentStudentIds = _meetingHistory.Students.Select(s => s.Id).ToHashSet(); + var absentStudents = allStudents.Where(s => !presentStudentIds.Contains(s.Id)); + + // Save state to localStorage + await StateService.SaveScheduledTeamsAsync(scheduledTeams); + await StateService.SaveAbsentStudentsAsync(absentStudents); + // Clear extended teams and excluded students when loading from history + await StateService.SaveExtendedTeamsAsync([]); + await StateService.SaveExcludedStudentsAsync(new Dictionary<(int teamId, int timeSlotIndex, int studentId), bool>()); + + // Close dialog and navigate to planner + MudDialog.Close(); + NavigationManager.NavigateTo("/meeting-schedule"); + + if (!_isDisposed) + { + Snackbar.Add($"Loaded meeting from {_meetingHistory.MeetingDate:MM/dd/yyyy} into planner", Severity.Success); + } + } + catch (TaskCanceledException) + { + // Component was disposed, ignore + } + catch (JSDisconnectedException) + { + // JS connection lost, ignore + } + catch (Exception ex) + { + if (!_isDisposed) + { + Snackbar.Add($"Error loading meeting into planner: {ex.Message}", Severity.Error); + } + } + } + private void Close() { if (_isDisposed) return; diff --git a/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor b/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor index 0c15ef1..2a4bde2 100644 --- a/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor +++ b/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor @@ -11,6 +11,7 @@ @inject INotesService NotesService @inject INoteNamingService NoteNamingService @inject ISnackbar Snackbar +@inject IDialogService DialogService @implements IAsyncDisposable @@ -256,8 +257,42 @@ var existingMeetings = await TeamMeetingHistoryService.GetMeetingHistoriesAsync(dateOnly, dateOnly); if (existingMeetings.Any()) { - Snackbar.Add($"A meeting history already exists for {dateOnly:MM/dd/yyyy}. Only one meeting per day is allowed.", Severity.Warning); - return; + // Show confirmation dialog + var confirmResult = await DialogService.ShowMessageBox( + "Overwrite Meeting?", + "A meeting already exists for this date. Overwrite it?", + yesText: "Overwrite", + cancelText: "Cancel"); + + if (confirmResult != true) + { + return; // User cancelled + } + + // User confirmed, switch to edit mode + var existingMeeting = existingMeetings.First(); + _isEditMode = true; + MeetingHistoryId = existingMeeting.Id; + + // Load existing meeting data + var existingHistory = await TeamMeetingHistoryService.GetMeetingHistoryAsync(existingMeeting.Id); + if (existingHistory != null) + { + // Match teams by ID to ensure reference equality with MudToggleGroup + var selectedTeamIds = existingHistory.Teams.Select(t => t.Id).ToHashSet(); + _selectedTeams = AllTeams.Where(t => selectedTeamIds.Contains(t.Id)); + + // Match students by ID to ensure reference equality with MudToggleGroup + var selectedStudentIds = existingHistory.Students.Select(s => s.Id).ToHashSet(); + _selectedStudents = AllStudents.Where(s => selectedStudentIds.Contains(s.Id)); + + // Load existing note if available + var existingNote = await TeamMeetingHistoryService.GetMeetingNoteAsync(existingHistory.MeetingDate); + if (existingNote != null) + { + _noteContent = existingNote.Content ?? ""; + } + } } }