feat(calendar): show recorded deadlines in agenda and month views

This commit is contained in:
cesnimda
2026-11-03 14:51:12 +01:00
parent 7e410e795d
commit 194e01ed7f
72 changed files with 2737 additions and 2 deletions
@@ -1,5 +1,6 @@
using System.Security.Cryptography;
using System.Text;
using System.Globalization;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
@@ -18,6 +19,34 @@ public sealed class ApplicationCalendarController(
IGmailOAuthService google,
IMicrosoftGraphOAuthService microsoft) : ControllerBase
{
[HttpGet("agenda")]
public async Task<ActionResult<List<CalendarAgendaItemDto>>> Agenda([FromQuery] DateOnly from, [FromQuery] DateOnly to, CancellationToken cancellationToken)
{
var owner = RequiredOwner();
if (from == default || to < from || to.DayNumber - from.DayNumber > 61 || to == DateOnly.MaxValue)
return BadRequest("Choose a calendar range of at most 62 days.");
var start = from.ToDateTime(TimeOnly.MinValue);
var end = to.AddDays(1).ToDateTime(TimeOnly.MinValue);
var jobs = await db.JobApplications.AsNoTracking()
.Where(job => job.OwnerUserId == owner && !job.IsDeleted)
.Where(job => (job.Deadline >= start && job.Deadline < end) || (job.FollowUpAt >= start && job.FollowUpAt < end))
.Select(job => new { job.Id, job.JobTitle, CompanyName = job.Company.OwnerUserId == owner ? job.Company.Name : null, job.Deadline, job.FollowUpAt })
.ToListAsync(cancellationToken);
var rows = new List<CalendarAgendaItemDto>();
foreach (var job in jobs)
{
Add("deadline", job.Deadline);
Add("follow-up", job.FollowUpAt);
void Add(string kind, DateTime? recorded)
{
if (recorded is not { } date || date < start || date >= end) return;
// Calendar grouping uses the stored calendar date; legacy timezone/precision is not reconstructed.
rows.Add(new(job.Id, job.JobTitle, job.CompanyName, kind, date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), date));
}
}
return Ok(rows.OrderBy(row => row.Date).ThenBy(row => row.Kind).ThenBy(row => row.JobApplicationId).ToList());
}
[HttpGet("status")]
public async Task<ActionResult<CalendarStatusDto>> Status(CancellationToken cancellationToken)
{
@@ -141,4 +170,5 @@ public sealed class ApplicationCalendarController(
public sealed record CalendarStatusDto(CalendarProviderStatusDto Google, CalendarProviderStatusDto Microsoft);
public sealed record CreateCalendarEventRequest(string? Provider, string? Kind);
public sealed record CalendarEventDto(string Provider, string? Id, string? WebUrl);
public sealed record CalendarAgendaItemDto(int JobApplicationId, string JobTitle, string? CompanyName, string Kind, string Date, DateTime RecordedAt);
}