Files
jobtrackingapp/JobTrackerApi/Controllers/InterviewPrepController.cs
T

87 lines
3.6 KiB
C#

using JobTrackerApi.Models;
using JobTrackerApi.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace JobTrackerApi.Controllers;
// Phase 5.5 — Interview preparation and follow-up.
//
// Prep content is the user's; AI suggestions come from the existing /api/jobapplications/{id}/ai
// routes and only reach here once the user accepts one (POST with source "ai"). Communication stays
// entirely on the existing Correspondence routes — there is no messaging endpoint here.
// docs/architecture/overview.md.
[ApiController]
[Route("api/jobapplications/{jobId:int}")]
[Authorize(AuthenticationSchemes = "local")]
public sealed class InterviewPrepController : ControllerBase
{
public sealed record SetFollowUpRequest(DateTime? FollowUpAt, string? NextAction);
private readonly UserManager<ApplicationUser> _users;
private readonly IInterviewPrepService _prep;
public InterviewPrepController(UserManager<ApplicationUser> users, IInterviewPrepService prep)
{
_users = users;
_prep = prep;
}
[HttpGet("interview-prep")]
public async Task<ActionResult<InterviewPrepBoardDto>> Get(int jobId, CancellationToken ct)
{
var userId = await CurrentUserIdAsync();
if (userId is null) return Unauthorized();
var result = await _prep.GetAsync(userId, jobId, ct);
return result is null ? NotFound() : Ok(result);
}
[HttpPost("interview-prep")]
public async Task<ActionResult<InterviewPrepItemDto>> Add(int jobId, [FromBody] InterviewPrepInput input, CancellationToken ct)
{
var userId = await CurrentUserIdAsync();
if (userId is null) return Unauthorized();
if (string.IsNullOrWhiteSpace(input?.Title)) return BadRequest("Title is required.");
var created = await _prep.AddAsync(userId, jobId, input, ct);
return created is null ? NotFound() : Ok(created);
}
[HttpPatch("interview-prep/{itemId:int}")]
public async Task<ActionResult<InterviewPrepItemDto>> Update(int jobId, int itemId, [FromBody] InterviewPrepInput input, CancellationToken ct)
{
var userId = await CurrentUserIdAsync();
if (userId is null) return Unauthorized();
var updated = await _prep.UpdateAsync(userId, jobId, itemId, input, ct);
return updated is null ? NotFound() : Ok(updated);
}
[HttpDelete("interview-prep/{itemId:int}")]
public async Task<IActionResult> Delete(int jobId, int itemId, CancellationToken ct)
{
var userId = await CurrentUserIdAsync();
if (userId is null) return Unauthorized();
return await _prep.DeleteAsync(userId, jobId, itemId, ct) ? NoContent() : NotFound();
}
[HttpGet("follow-up")]
public async Task<ActionResult<FollowUpDto>> GetFollowUp(int jobId, CancellationToken ct)
{
var userId = await CurrentUserIdAsync();
if (userId is null) return Unauthorized();
var result = await _prep.GetFollowUpAsync(userId, jobId, ct);
return result is null ? NotFound() : Ok(result);
}
[HttpPut("follow-up")]
public async Task<ActionResult<FollowUpDto>> SetFollowUp(int jobId, [FromBody] SetFollowUpRequest request, CancellationToken ct)
{
var userId = await CurrentUserIdAsync();
if (userId is null) return Unauthorized();
var result = await _prep.SetFollowUpAsync(userId, jobId, request?.FollowUpAt, request?.NextAction, ct);
return result is null ? NotFound() : Ok(result);
}
private async Task<string?> CurrentUserIdAsync() => (await _users.GetUserAsync(User))?.Id;
}