diff --git a/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor b/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor index d127d3d..863cc64 100644 --- a/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor +++ b/WebApp/Components/Features/MeetingSchedule/MeetingHistoryDetailDialog.razor @@ -10,6 +10,7 @@ @inject INoteNamingService NoteNamingService @inject ISnackbar Snackbar @inject IDialogService DialogService +@inject IMeetingScheduleDataService DataService @implements IAsyncDisposable @@ -25,7 +26,28 @@ else { - Meeting Details + @* Navigation Header *@ + + + + + + + @_meetingHistory.MeetingDate.ToString("MM/dd/yyyy") + + + + + + @@ -106,6 +128,13 @@ Delete + + Edit + Close } else @@ -127,9 +156,13 @@ private Note? _meetingNote; private bool _isLoading = true; private bool _isDeleting = false; + private Team[] _allTeams = []; + private Student[] _allStudents = []; private CancellationTokenSource? _cancellationTokenSource; private bool _isDisposed = false; private bool IsActionDisabled => _isLoading || _isDeleting; + private TeamMeetingHistory? _previousMeetingHistory; + private TeamMeetingHistory? _nextMeetingHistory; protected override void OnInitialized() { @@ -138,6 +171,9 @@ protected override async Task OnInitializedAsync() { + // Load all teams and students for the edit dialog + _allTeams = await DataService.LoadTeamsAsync(); + _allStudents = await DataService.LoadStudentsAsync(); await LoadMeetingHistory(); } @@ -153,6 +189,9 @@ if (_meetingHistory != null) { _meetingNote = await TeamMeetingHistoryService.GetMeetingNoteAsync(_meetingHistory.MeetingDate); + + // Load all meeting histories to find previous/next + await LoadNavigationMeetings(); } } catch (TaskCanceledException) @@ -180,6 +219,91 @@ } } + private async Task LoadNavigationMeetings() + { + if (_isDisposed || _meetingHistory == null) return; + + try + { + // Get all meeting histories ordered by date + var allMeetings = (await TeamMeetingHistoryService.GetMeetingHistoriesAsync()) + .OrderBy(m => m.MeetingDate) + .ThenBy(m => m.Id) + .ToList(); + + var currentIndex = allMeetings.FindIndex(m => m.Id == _meetingHistory.Id); + + if (currentIndex >= 0) + { + _previousMeetingHistory = currentIndex > 0 ? allMeetings[currentIndex - 1] : null; + _nextMeetingHistory = currentIndex < allMeetings.Count - 1 ? allMeetings[currentIndex + 1] : null; + } + else + { + _previousMeetingHistory = null; + _nextMeetingHistory = null; + } + } + catch (TaskCanceledException) + { + // Component was disposed, ignore + } + catch (JSDisconnectedException) + { + // JS connection lost, ignore + } + catch (Exception ex) + { + if (!_isDisposed) + { + // Log error but don't show snackbar - navigation is not critical + System.Diagnostics.Debug.WriteLine($"Error loading navigation meetings: {ex.Message}"); + } + } + } + + private async Task NavigateToPrevious() + { + if (_previousMeetingHistory == null || _isDisposed) return; + + // Close current dialog and open new one with previous meeting ID + var parameters = new DialogParameters + { + ["MeetingHistoryId"] = _previousMeetingHistory.Id + }; + + var options = new DialogOptions + { + MaxWidth = MaxWidth.Medium, + FullWidth = true, + CloseButton = true + }; + + MudDialog.Close(); + await DialogService.ShowAsync("Meeting History", parameters, options); + } + + private async Task NavigateToNext() + { + if (_nextMeetingHistory == null || _isDisposed) return; + + // Close current dialog and open new one with next meeting ID + var parameters = new DialogParameters + { + ["MeetingHistoryId"] = _nextMeetingHistory.Id + }; + + var options = new DialogOptions + { + MaxWidth = MaxWidth.Medium, + FullWidth = true, + CloseButton = true + }; + + MudDialog.Close(); + await DialogService.ShowAsync("Meeting History", parameters, options); + } + private async Task ConfirmDelete() { if (_isDisposed || _isDeleting || _meetingHistory == null) return; @@ -255,6 +379,36 @@ await LoadMeetingHistory(); } + private async Task OpenEditDialog() + { + if (_meetingHistory == null || _isDisposed) return; + + var parameters = new DialogParameters + { + ["MeetingHistoryId"] = _meetingHistory.Id, + ["AllTeams"] = _allTeams, + ["AllStudents"] = _allStudents, + ["ScheduledTeams"] = new List(), // Not used when editing + ["AbsentStudents"] = new List() // Not used when editing + }; + + var options = new DialogOptions + { + MaxWidth = MaxWidth.Medium, + FullWidth = true, + CloseButton = true + }; + + var dialog = await DialogService.ShowAsync("Edit Meeting History", parameters, options); + var result = await dialog.Result; + + // Refresh meeting history if dialog was saved + if (!result.Canceled && !_isDisposed) + { + await LoadMeetingHistory(); + } + } + private List GetAllStudentsFromTeams() { if (_meetingHistory == null) diff --git a/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor b/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor index 8a1fb3d..0c15ef1 100644 --- a/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor +++ b/WebApp/Components/Features/MeetingSchedule/SaveMeetingHistoryDialog.razor @@ -4,6 +4,8 @@ @using Core.Services @using WebApp.Services @using WebApp.Components.Shared.Components +@using WebApp.Components.Features.Teams.Components +@using WebApp.Components.Features.Students.Components @using WebApp.Models @inject ITeamMeetingHistoryService TeamMeetingHistoryService @inject INotesService NotesService @@ -20,49 +22,32 @@ else { - Save Meeting History + @(_isEditMode ? "Edit Meeting History" : "Save Meeting History") + Variant="Variant.Outlined" + Disabled="@_isEditMode" /> - Teams That Met - - @foreach (var team in AllTeams.OrderByEventFormatFirst().ThenBy(e => e.ToString())) - { - - @team.ToString() - - } - + - Students Present - - @foreach (var student in AllStudents.OrderBy(s => s.FirstName)) - { - - @student.FirstNameLastName - - } - + @@ -121,13 +106,17 @@ [Parameter] public IEnumerable AllStudents { get; set; } = []; + [Parameter] + public int? MeetingHistoryId { get; set; } + private DateTime? _meetingDate = DateTime.Today; - private List _selectedTeams = []; - private List _selectedStudents = []; + private IEnumerable _selectedTeams = []; + private IEnumerable _selectedStudents = []; private string _noteTitle = ""; private string _noteContent = ""; private bool _isLoading = true; private bool _isSaving = false; + private bool _isEditMode = false; private CancellationTokenSource? _cancellationTokenSource; private bool _isDisposed = false; @@ -147,12 +136,41 @@ try { - // Initialize selected teams from scheduled teams - _selectedTeams = ScheduledTeams.ToList(); + // If editing, load existing meeting history + if (MeetingHistoryId.HasValue) + { + _isEditMode = true; + var existingHistory = await TeamMeetingHistoryService.GetMeetingHistoryAsync(MeetingHistoryId.Value); + + if (existingHistory != null) + { + _meetingDate = existingHistory.MeetingDate; + + // 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 ?? ""; + } + } + } + else + { + // Initialize selected teams from scheduled teams + _selectedTeams = ScheduledTeams; - // Initialize selected students (all students except absent ones) - var absentStudentIds = AbsentStudents.Select(s => s.Id).ToHashSet(); - _selectedStudents = AllStudents.Where(s => !absentStudentIds.Contains(s.Id)).ToList(); + // Initialize selected students (all students except absent ones) + var absentStudentIds = AbsentStudents.Select(s => s.Id).ToHashSet(); + _selectedStudents = AllStudents.Where(s => !absentStudentIds.Contains(s.Id)); + } // Generate default note title if meeting date is set if (_meetingDate.HasValue) @@ -208,13 +226,13 @@ private void OnTeamsChanged(IEnumerable teams) { - _selectedTeams = teams.ToList(); + _selectedTeams = teams; StateHasChanged(); } private void OnStudentsChanged(IEnumerable students) { - _selectedStudents = students.ToList(); + _selectedStudents = students; StateHasChanged(); } @@ -228,6 +246,21 @@ return; } + // Check if a meeting history already exists for this date (only when creating new, not editing) + if (!_isEditMode) + { + var dateOnly = _meetingDate.Value.Date; + // GetMeetingHistoriesAsync: startDate >= date, endDate < (endDate.Date + 1 day) + // To get meetings for a single day, pass dateOnly as startDate and dateOnly as endDate + // This becomes: MeetingDate >= dateOnly AND MeetingDate < (dateOnly + 1 day) = just that day + 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; + } + } + try { _isSaving = true; @@ -262,19 +295,52 @@ } } - // Create meeting history - var meetingHistory = new TeamMeetingHistory + // Create or update meeting history + TeamMeetingHistory meetingHistory; + if (_isEditMode && MeetingHistoryId.HasValue) { - MeetingDate = _meetingDate.Value, - Teams = _selectedTeams, - Students = _selectedStudents - }; + // Update existing meeting history + var existingHistory = await TeamMeetingHistoryService.GetMeetingHistoryAsync(MeetingHistoryId.Value); + if (existingHistory == null) + { + Snackbar.Add("Meeting history not found", Severity.Error); + _isSaving = false; + StateHasChanged(); + return; + } + + meetingHistory = existingHistory; + meetingHistory.MeetingDate = _meetingDate.Value; + meetingHistory.Teams = _selectedTeams.ToList(); + meetingHistory.Students = _selectedStudents.ToList(); + + await TeamMeetingHistoryService.UpdateMeetingHistoryAsync(meetingHistory); + + if (!_isDisposed) + { + Snackbar.Add($"Meeting history updated for {_meetingDate.Value:MM/dd/yyyy}", Severity.Success); + } + } + else + { + // Create new meeting history + meetingHistory = new TeamMeetingHistory + { + MeetingDate = _meetingDate.Value, + Teams = _selectedTeams.ToList(), + Students = _selectedStudents.ToList() + }; - await TeamMeetingHistoryService.CreateMeetingHistoryAsync(meetingHistory); + await TeamMeetingHistoryService.CreateMeetingHistoryAsync(meetingHistory); + + if (!_isDisposed) + { + Snackbar.Add($"Meeting history saved for {_meetingDate.Value:MM/dd/yyyy}", Severity.Success); + } + } if (!_isDisposed) { - Snackbar.Add($"Meeting history saved for {_meetingDate.Value:MM/dd/yyyy}", Severity.Success); MudDialog.Close(DialogResult.Ok(true)); } }