Files
chapter-organizer/WebApp/Services/TeamMeetingHistoryService.cs
T
poprhythm 6bc4c2e7f2 Add TeamMeetingHistory entity, service, and UI components for meeting history management
This commit introduces the TeamMeetingHistory entity, including its configuration and database migrations. A new ITeamMeetingHistoryService interface and its implementation, TeamMeetingHistoryService, are added to handle CRUD operations for meeting histories. Additionally, UI components such as History.razor, MeetingHistoryDetailDialog, and SaveMeetingHistoryDialog are created to facilitate viewing and saving meeting histories. The integration of INoteNamingService enhances note management for meeting records, improving overall functionality and user experience in the application.
2026-01-19 22:02:59 -05:00

206 lines
7.1 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();
}
}