Files
chapter-organizer/WebApp/Services/TeamMeetingHistoryService.cs
poprhythm 84eaf338a9 Enhance TeamMeetingHistoryDialog to display team members' attendance
This commit updates the TeamMeetingHistoryDialog component to show the attendance status of team members during meetings. It replaces the previous status display with a list of team members, indicating their presence or absence using MudBlazor chips. Additionally, the TeamMeetingHistoryService is modified to include student data for accurate attendance tracking. These changes improve the clarity and usability of the meeting history dialog, enhancing the overall user experience in managing team meetings.
2026-01-27 20:10:40 -05:00

229 lines
7.9 KiB
C#

using Core.Entities;
using Core.Services;
using Data;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
namespace WebApp.Services;
public class TeamMeetingHistoryService : ITeamMeetingHistoryService
{
private readonly AppDbContext _context;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger<TeamMeetingHistoryService> _logger;
private readonly INotesService _notesService;
private readonly INoteNamingService _noteNamingService;
public TeamMeetingHistoryService(
AppDbContext context,
IHttpContextAccessor httpContextAccessor,
ILogger<TeamMeetingHistoryService> logger,
INotesService notesService,
INoteNamingService noteNamingService)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
_logger = logger;
_notesService = notesService;
_noteNamingService = noteNamingService;
}
private string? GetCurrentUserEmail()
{
var user = _httpContextAccessor.HttpContext?.User;
if (user == null)
return null;
return user.FindFirstValue(ClaimTypes.Email) ?? user.Identity?.Name;
}
public async Task<IEnumerable<TeamMeetingHistory>> GetMeetingHistoriesAsync(DateTime? startDate = null, DateTime? endDate = null)
{
var query = _context.TeamMeetingHistories
.AsNoTracking()
.Include(tmh => tmh.Teams)
.ThenInclude(t => t.Event)
.Include(tmh => tmh.Students)
.AsQueryable();
if (startDate.HasValue)
{
var start = startDate.Value.Date;
query = query.Where(tmh => tmh.MeetingDate >= start);
}
if (endDate.HasValue)
{
var end = endDate.Value.Date.AddDays(1); // Include the entire end date
query = query.Where(tmh => tmh.MeetingDate < end);
}
return await query
.OrderByDescending(tmh => tmh.MeetingDate)
.ToListAsync();
}
public async Task<TeamMeetingHistory?> GetMeetingHistoryAsync(int id)
{
return await _context.TeamMeetingHistories
.Include(tmh => tmh.Teams)
.ThenInclude(t => t.Event)
.Include(tmh => tmh.Teams)
.ThenInclude(t => t.Students)
.Include(tmh => tmh.Students)
.FirstOrDefaultAsync(tmh => tmh.Id == id);
}
public async Task<TeamMeetingHistory> CreateMeetingHistoryAsync(TeamMeetingHistory meetingHistory)
{
// Create a new meeting history entity to avoid tracking conflicts
var newMeetingHistory = new TeamMeetingHistory
{
MeetingDate = meetingHistory.MeetingDate.Date // Normalize to date only
};
// Attach teams by loading them from the database to avoid tracking conflicts
var teamIds = meetingHistory.Teams.Select(t => t.Id).ToList();
var teams = await _context.Teams
.Where(t => teamIds.Contains(t.Id))
.ToListAsync();
// Attach students by loading them from the database to avoid tracking conflicts
var studentIds = meetingHistory.Students.Select(s => s.Id).ToList();
var students = await _context.Students
.Where(s => studentIds.Contains(s.Id))
.ToListAsync();
// Attach teams and students to the new meeting history
newMeetingHistory.Teams = teams;
newMeetingHistory.Students = students;
_context.TeamMeetingHistories.Add(newMeetingHistory);
await _context.SaveChangesAsync();
_logger.LogInformation("Meeting history created: {MeetingHistoryId} for date {MeetingDate}",
newMeetingHistory.Id, newMeetingHistory.MeetingDate);
return newMeetingHistory;
}
public async Task<TeamMeetingHistory> UpdateMeetingHistoryAsync(TeamMeetingHistory meetingHistory)
{
var existingHistory = await _context.TeamMeetingHistories
.Include(tmh => tmh.Teams)
.Include(tmh => tmh.Students)
.FirstOrDefaultAsync(tmh => tmh.Id == meetingHistory.Id);
if (existingHistory == null)
{
throw new InvalidOperationException($"Meeting history with ID {meetingHistory.Id} not found.");
}
// Update properties
existingHistory.MeetingDate = meetingHistory.MeetingDate.Date; // Normalize to date only
// Update teams
existingHistory.Teams.Clear();
foreach (var team in meetingHistory.Teams)
{
var existingTeam = await _context.Teams.FindAsync(team.Id);
if (existingTeam != null)
{
existingHistory.Teams.Add(existingTeam);
}
}
// Update students
existingHistory.Students.Clear();
foreach (var student in meetingHistory.Students)
{
var existingStudent = await _context.Students.FindAsync(student.Id);
if (existingStudent != null)
{
existingHistory.Students.Add(existingStudent);
}
}
await _context.SaveChangesAsync();
_logger.LogInformation("Meeting history updated: {MeetingHistoryId} by {User}",
meetingHistory.Id, GetCurrentUserEmail());
return existingHistory;
}
public async Task DeleteMeetingHistoryAsync(int id)
{
var meetingHistory = await _context.TeamMeetingHistories
.FirstOrDefaultAsync(tmh => tmh.Id == id);
if (meetingHistory == null)
{
throw new InvalidOperationException($"Meeting history with ID {id} not found.");
}
_context.TeamMeetingHistories.Remove(meetingHistory);
await _context.SaveChangesAsync();
_logger.LogInformation("Meeting history deleted: {MeetingHistoryId}", id);
}
/// <summary>
/// Gets the meeting note for a specific meeting date by looking it up by title.
/// </summary>
/// <param name="meetingDate">The date of the meeting</param>
/// <returns>The note if found, null otherwise</returns>
public async Task<Note?> GetMeetingNoteAsync(DateTime meetingDate)
{
var noteTitle = _noteNamingService.GetMeetingNoteTitle(meetingDate);
var notes = await _notesService.GetNotesAsync(includeDeleted: false);
return notes.FirstOrDefault(n => n.Title == noteTitle);
}
public async Task<IEnumerable<Team>> GetTeamsForDateAsync(DateTime date)
{
var normalizedDate = date.Date;
return await _context.TeamMeetingHistories
.AsNoTracking()
.Where(tmh => tmh.MeetingDate == normalizedDate)
.SelectMany(tmh => tmh.Teams)
.Include(t => t.Event)
.Distinct()
.ToListAsync();
}
public async Task<IEnumerable<Student>> GetStudentsForDateAsync(DateTime date)
{
var normalizedDate = date.Date;
return await _context.TeamMeetingHistories
.AsNoTracking()
.Where(tmh => tmh.MeetingDate == normalizedDate)
.SelectMany(tmh => tmh.Students)
.Distinct()
.ToListAsync();
}
public async Task<IEnumerable<TeamMeetingHistory>> GetMeetingHistoriesForTeamAsync(int teamId)
{
return await _context.TeamMeetingHistories
.AsNoTracking()
.Include(tmh => tmh.Teams)
.ThenInclude(t => t.Event)
.Include(tmh => tmh.Teams)
.ThenInclude(t => t.Students)
.Include(tmh => tmh.Students)
.Where(tmh => tmh.Teams.Any(t => t.Id == teamId))
.OrderByDescending(tmh => tmh.MeetingDate)
.ThenByDescending(tmh => tmh.Id)
.ToListAsync();
}
public async Task<int> GetMeetingHistoryCountForTeamAsync(int teamId)
{
return await _context.TeamMeetingHistories
.AsNoTracking()
.Where(tmh => tmh.Teams.Any(t => t.Id == teamId))
.CountAsync();
}
}