Files
Hy2026/backend/Controllers/IdeasController.cs
T
Rafał Tyński 127ca92f63 feat(ideas): add idea creator, grant calls and applications
Implement the idea creator and application generator: idea status workflow, social canvas card, grant call windows with 409 when closed, and an LLM-backed idea assistant with offline fallback.
2026-10-04 03:13:45 +02:00

187 lines
5.1 KiB
C#

using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using TarninaHub.Application.Abstractions;
using TarninaHub.Application.Dtos;
using TarninaHub.Application.Mappings;
using TarninaHub.Application.Security;
using TarninaHub.Domain.Rules;
namespace TarninaHub.Controllers;
[ApiController]
[ApiVersion("1.0")]
[Route("api/ideas")]
[Authorize]
public class IdeasController : ControllerBase
{
private readonly IIdeaService _ideaService;
private readonly IIdeaAssistant _ideaAssistant;
public IdeasController(IIdeaService ideaService, IIdeaAssistant ideaAssistant)
{
_ideaService = ideaService;
_ideaAssistant = ideaAssistant;
}
[HttpPost]
public async Task<ActionResult<IdeaResponse>> Create(
CreateIdeaRequest request,
CancellationToken cancellationToken)
{
var userId = User.GetUserId();
if (string.IsNullOrWhiteSpace(userId))
{
return Unauthorized();
}
var created = await _ideaService.CreateAsync(userId, request, cancellationToken);
return CreatedAtAction(nameof(GetById), new { ideaId = created.Id }, created);
}
[HttpGet]
public async Task<ActionResult<IReadOnlyList<IdeaResponse>>> ListMine(CancellationToken cancellationToken)
{
var userId = User.GetUserId();
if (string.IsNullOrWhiteSpace(userId))
{
return Unauthorized();
}
var ideas = await _ideaService.ListForAuthorAsync(userId, cancellationToken);
return Ok(ideas);
}
[HttpGet("{ideaId}")]
public async Task<ActionResult<IdeaResponse>> GetById(string ideaId, CancellationToken cancellationToken)
{
var idea = await _ideaService.GetAsync(ideaId, cancellationToken);
if (idea is null)
{
return NotFound();
}
if (!IsOwner(idea) && !IsStaff())
{
return Forbid();
}
return Ok(idea);
}
[HttpPut("{ideaId}")]
public async Task<ActionResult<IdeaResponse>> Update(
string ideaId,
UpdateIdeaRequest request,
CancellationToken cancellationToken)
{
var current = await _ideaService.GetAsync(ideaId, cancellationToken);
if (current is null)
{
return NotFound();
}
if (!IsOwner(current))
{
return Forbid();
}
if (!string.IsNullOrWhiteSpace(request.Status))
{
var from = IdeaStatusWire.Parse(current.Status);
var to = IdeaStatusWire.Parse(request.Status);
if (to is null || from is null || !IdeaWorkflow.CanTransition(from.Value, to.Value))
{
ModelState.AddModelError(
nameof(request.Status),
$"Transition from '{current.Status}' to '{request.Status}' is not allowed.");
return ValidationProblem(ModelState);
}
}
var updated = await _ideaService.UpdateAsync(ideaId, request, cancellationToken);
return updated is null ? NotFound() : Ok(updated);
}
[HttpGet("{ideaId}/card")]
public async Task<ActionResult<IdeaCardResponse>> GetCard(string ideaId, CancellationToken cancellationToken)
{
var idea = await _ideaService.GetAsync(ideaId, cancellationToken);
if (idea is null)
{
return NotFound();
}
if (!IsOwner(idea) && !IsStaff())
{
return Forbid();
}
var card = await _ideaService.GetCardAsync(ideaId, cancellationToken);
return card is null ? NotFound() : Ok(card);
}
[HttpPut("{ideaId}/card")]
public async Task<ActionResult<IdeaCardResponse>> UpdateCard(
string ideaId,
IdeaCardRequest request,
CancellationToken cancellationToken)
{
var idea = await _ideaService.GetAsync(ideaId, cancellationToken);
if (idea is null)
{
return NotFound();
}
if (!IsOwner(idea))
{
return Forbid();
}
var card = await _ideaService.UpdateCardAsync(ideaId, request, cancellationToken);
return card is null ? NotFound() : Ok(card);
}
[HttpPost("{ideaId}/assist")]
public async Task<ActionResult<IdeaAssistResponse>> Assist(
string ideaId,
IdeaAssistRequest request,
CancellationToken cancellationToken)
{
var idea = await _ideaService.GetAsync(ideaId, cancellationToken);
if (idea is null)
{
return NotFound();
}
if (!IsOwner(idea) && !IsStaff())
{
return Forbid();
}
var assist = await _ideaAssistant.AssistAsync(ideaId, request, cancellationToken);
return assist is null ? NotFound() : Ok(assist);
}
private bool IsOwner(IdeaResponse idea) =>
string.Equals(idea.AuthorId, User.GetUserId(), StringComparison.Ordinal);
private bool IsStaff() =>
User.IsInRole(RoleNames.Operator) || User.IsInRole(RoleNames.Manager) || User.IsInRole(RoleNames.Admin);
}