✨ Event Calendar
This commit is contained in:
		| @@ -257,7 +257,7 @@ public class AccountController( | |||||||
|             .OrderByDescending(s => s.CreatedAt) |             .OrderByDescending(s => s.CreatedAt) | ||||||
|             .FirstOrDefaultAsync(); |             .FirstOrDefaultAsync(); | ||||||
|         if (status is null) return NotFound(); |         if (status is null) return NotFound(); | ||||||
|          |  | ||||||
|         await events.ClearStatus(currentUser, status); |         await events.ClearStatus(currentUser, status); | ||||||
|         return NoContent(); |         return NoContent(); | ||||||
|     } |     } | ||||||
| @@ -268,31 +268,31 @@ public class AccountController( | |||||||
|     { |     { | ||||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||||
|         var userId = currentUser.Id; |         var userId = currentUser.Id; | ||||||
|          |  | ||||||
|         var today = SystemClock.Instance.GetCurrentInstant().InUtc().Date; |         var today = SystemClock.Instance.GetCurrentInstant().InUtc().Date; | ||||||
|         var localTime = new TimeOnly(0, 0); |         var localTime = new TimeOnly(0, 0); | ||||||
|         var startOfDay = today.ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant(); |         var startOfDay = today.ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant(); | ||||||
|         var endOfDay = today.PlusDays(1).ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant(); |         var endOfDay = today.PlusDays(1).ToDateOnly().ToDateTime(localTime).ToUniversalTime().ToInstant(); | ||||||
|          |  | ||||||
|         var result = await db.AccountCheckInResults |         var result = await db.AccountCheckInResults | ||||||
|             .Where(x => x.AccountId == userId) |             .Where(x => x.AccountId == userId) | ||||||
|             .Where(x => x.CreatedAt >= startOfDay && x.CreatedAt < endOfDay) |             .Where(x => x.CreatedAt >= startOfDay && x.CreatedAt < endOfDay) | ||||||
|             .OrderByDescending(x => x.CreatedAt) |             .OrderByDescending(x => x.CreatedAt) | ||||||
|             .FirstOrDefaultAsync(); |             .FirstOrDefaultAsync(); | ||||||
|      |  | ||||||
|         return result is null ? NotFound() : Ok(result); |         return result is null ? NotFound() : Ok(result); | ||||||
|     } |     } | ||||||
|      |  | ||||||
|     [HttpPost("me/check-in")] |     [HttpPost("me/check-in")] | ||||||
|     [Authorize] |     [Authorize] | ||||||
|     public async Task<ActionResult<CheckInResult>> DoCheckIn([FromBody] string? captchaToken) |     public async Task<ActionResult<CheckInResult>> DoCheckIn([FromBody] string? captchaToken) | ||||||
|     { |     { | ||||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||||
|          |  | ||||||
|         var isAvailable = await events.CheckInDailyIsAvailable(currentUser); |         var isAvailable = await events.CheckInDailyIsAvailable(currentUser); | ||||||
|         if (!isAvailable) |         if (!isAvailable) | ||||||
|             return BadRequest("Check-in is not available for today."); |             return BadRequest("Check-in is not available for today."); | ||||||
|      |  | ||||||
|         var needsCaptcha = events.CheckInDailyDoAskCaptcha(currentUser); |         var needsCaptcha = events.CheckInDailyDoAskCaptcha(currentUser); | ||||||
|         return needsCaptcha switch |         return needsCaptcha switch | ||||||
|         { |         { | ||||||
| @@ -303,6 +303,23 @@ public class AccountController( | |||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     [HttpGet("me/calendar")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<ActionResult<List<DailyEventResponse>>> GetEventCalendar([FromQuery] int? month, [FromQuery] int? year) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var currentDate = SystemClock.Instance.GetCurrentInstant().InUtc().Date; | ||||||
|  |         month ??= currentDate.Month; | ||||||
|  |         year ??= currentDate.Year; | ||||||
|  |  | ||||||
|  |         if (month is < 1 or > 12) return BadRequest("Invalid month."); | ||||||
|  |         if (year < 1) return BadRequest("Invalid year."); | ||||||
|  |  | ||||||
|  |         var calendar = await events.GetEventCalendar(currentUser, month.Value, year.Value); | ||||||
|  |         return Ok(calendar); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     [HttpGet("search")] |     [HttpGet("search")] | ||||||
|     public async Task<List<Account>> Search([FromQuery] string query, [FromQuery] int take = 20) |     public async Task<List<Account>> Search([FromQuery] string query, [FromQuery] int take = 20) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -167,4 +167,45 @@ public class AccountEventService( | |||||||
|  |  | ||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | public async Task<List<DailyEventResponse>> GetEventCalendar(Account user, int month, int year = 0) | ||||||
|  | { | ||||||
|  |     if (year == 0) | ||||||
|  |         year = SystemClock.Instance.GetCurrentInstant().InUtc().Date.Year; | ||||||
|  |  | ||||||
|  |     // Create start and end dates for the specified month | ||||||
|  |     var startOfMonth = new LocalDate(year, month, 1).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant(); | ||||||
|  |     var endOfMonth = startOfMonth.Plus(Duration.FromDays(DateTime.DaysInMonth(year, month))); | ||||||
|  |  | ||||||
|  |     var statuses = await db.AccountStatuses | ||||||
|  |         .Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth) | ||||||
|  |         .OrderBy(x => x.CreatedAt) | ||||||
|  |         .ToListAsync(); | ||||||
|  |          | ||||||
|  |     var checkIn = await db.AccountCheckInResults | ||||||
|  |         .Where(x => x.AccountId == user.Id && x.CreatedAt >= startOfMonth && x.CreatedAt < endOfMonth) | ||||||
|  |         .ToListAsync(); | ||||||
|  |  | ||||||
|  |     var dates = Enumerable.Range(1, DateTime.DaysInMonth(year, month)) | ||||||
|  |         .Select(day => new LocalDate(year, month, day).AtStartOfDayInZone(DateTimeZone.Utc).ToInstant()) | ||||||
|  |         .ToList(); | ||||||
|  |  | ||||||
|  |     var statusesByDate = statuses | ||||||
|  |         .GroupBy(s => s.CreatedAt.InUtc().Date) | ||||||
|  |         .ToDictionary(g => g.Key, g => g.ToList()); | ||||||
|  |  | ||||||
|  |     var checkInByDate = checkIn | ||||||
|  |         .ToDictionary(c => c.CreatedAt.InUtc().Date); | ||||||
|  |  | ||||||
|  |     return dates.Select(date => | ||||||
|  |     { | ||||||
|  |         var utcDate = date.InUtc().Date; | ||||||
|  |         return new DailyEventResponse | ||||||
|  |         { | ||||||
|  |             Date = date, | ||||||
|  |             CheckInResult = checkInByDate.GetValueOrDefault(utcDate), | ||||||
|  |             Statuses = statusesByDate.GetValueOrDefault(utcDate, new List<Status>()) | ||||||
|  |         }; | ||||||
|  |     }).ToList(); | ||||||
|  | } | ||||||
| } | } | ||||||
| @@ -49,4 +49,14 @@ public class FortuneTip | |||||||
|     public bool IsPositive { get; set; } |     public bool IsPositive { get; set; } | ||||||
|     public string Title { get; set; } = null!; |     public string Title { get; set; } = null!; | ||||||
|     public string Content { get; set; } = null!; |     public string Content { get; set; } = null!; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// <summary> | ||||||
|  | /// This method should not be mapped. Used to generate the daily event calendar. | ||||||
|  | /// </summary> | ||||||
|  | public class DailyEventResponse | ||||||
|  | { | ||||||
|  |     public Instant Date { get; set; } | ||||||
|  |     public CheckInResult? CheckInResult { get; set; } | ||||||
|  |     public ICollection<Status> Statuses { get; set; } = new List<Status>(); | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user