Chapter officers can merge a markdown note onto filtered students, teams, or events and save the recipe. Extra student-note columns are additional fields (any ## … fields heading) so they work as print tokens whether imported or typed by hand. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
3.5 KiB
C#
98 lines
3.5 KiB
C#
using Core.Entities;
|
|
using Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace WebApp.Services;
|
|
|
|
public class PrintPresetService : IPrintPresetService
|
|
{
|
|
private readonly AppDbContext _context;
|
|
private readonly ILogger<PrintPresetService> _logger;
|
|
|
|
public PrintPresetService(AppDbContext context, ILogger<PrintPresetService> logger)
|
|
{
|
|
_context = context;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<IReadOnlyList<PrintPreset>> GetAllAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
return await _context.PrintPresets
|
|
.AsNoTracking()
|
|
.Include(p => p.Note)
|
|
.OrderBy(p => p.Name)
|
|
.ToListAsync(cancellationToken);
|
|
}
|
|
|
|
public async Task<PrintPreset?> GetAsync(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
return await _context.PrintPresets
|
|
.AsNoTracking()
|
|
.Include(p => p.Note)
|
|
.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
|
|
}
|
|
|
|
public async Task<bool> NameExistsAsync(
|
|
string name,
|
|
int? excludeId = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var trimmed = name.Trim();
|
|
var query = _context.PrintPresets.AsNoTracking().Where(p => p.Name == trimmed);
|
|
if (excludeId.HasValue)
|
|
query = query.Where(p => p.Id != excludeId.Value);
|
|
return await query.AnyAsync(cancellationToken);
|
|
}
|
|
|
|
public async Task<PrintPreset> CreateAsync(PrintPreset preset, CancellationToken cancellationToken = default)
|
|
{
|
|
preset.Name = preset.Name.Trim();
|
|
preset.UpdatedAt = DateTime.UtcNow;
|
|
preset.Note = null!;
|
|
|
|
if (await NameExistsAsync(preset.Name, null, cancellationToken))
|
|
throw new InvalidOperationException($"A print preset named '{preset.Name}' already exists.");
|
|
|
|
_context.PrintPresets.Add(preset);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
_logger.LogInformation("Print preset created: {PresetId} {Name}", preset.Id, preset.Name);
|
|
return preset;
|
|
}
|
|
|
|
public async Task<PrintPreset> UpdateAsync(PrintPreset preset, CancellationToken cancellationToken = default)
|
|
{
|
|
var existing = await _context.PrintPresets
|
|
.FirstOrDefaultAsync(p => p.Id == preset.Id, cancellationToken);
|
|
|
|
if (existing is null)
|
|
throw new InvalidOperationException($"Print preset {preset.Id} was not found.");
|
|
|
|
var name = preset.Name.Trim();
|
|
if (await NameExistsAsync(name, preset.Id, cancellationToken))
|
|
throw new InvalidOperationException($"A print preset named '{name}' already exists.");
|
|
|
|
existing.Name = name;
|
|
existing.NoteId = preset.NoteId;
|
|
existing.EntityType = preset.EntityType;
|
|
existing.FiltersJson = preset.FiltersJson;
|
|
existing.UpdatedAt = DateTime.UtcNow;
|
|
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
_logger.LogInformation("Print preset updated: {PresetId} {Name}", existing.Id, existing.Name);
|
|
return existing;
|
|
}
|
|
|
|
public async Task DeleteAsync(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
var existing = await _context.PrintPresets
|
|
.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
|
|
|
|
if (existing is null)
|
|
return;
|
|
|
|
_context.PrintPresets.Remove(existing);
|
|
await _context.SaveChangesAsync(cancellationToken);
|
|
_logger.LogInformation("Print preset deleted: {PresetId} {Name}", id, existing.Name);
|
|
}
|
|
}
|