Implement step 17 modules: threads/messages, notifications with automation (submission.created, idea.created, grant_call.opened), innovation test feedback, admin panel (submissions, verify, metrics, grant call status), Middleman adaptation and partnerships. Split multi-type files into one type per file and replace object-valued dictionaries with string-valued maps in data contracts (migrations 0006/0007).
99 lines
3.1 KiB
C#
99 lines
3.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.Security;
|
|
|
|
namespace TarninaHub.Controllers;
|
|
|
|
[ApiController]
|
|
[ApiVersion("1.0")]
|
|
[Route("api/admin")]
|
|
[Authorize(Policy = Policies.OperatorOrAdmin)]
|
|
public class AdminController : ControllerBase
|
|
{
|
|
private readonly IAdminService _admin;
|
|
private readonly IGrantCallService _grantCalls;
|
|
private readonly IIdeaService _ideas;
|
|
private readonly INotificationService _notifications;
|
|
|
|
public AdminController(
|
|
IAdminService admin,
|
|
IGrantCallService grantCalls,
|
|
IIdeaService ideas,
|
|
INotificationService notifications)
|
|
{
|
|
_admin = admin;
|
|
_grantCalls = grantCalls;
|
|
_ideas = ideas;
|
|
_notifications = notifications;
|
|
}
|
|
|
|
[HttpGet("submissions")]
|
|
public async Task<ActionResult<IReadOnlyList<SubmissionResponse>>> ListSubmissions(
|
|
[FromQuery] string? status,
|
|
[FromQuery] string? challengeId,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var submissions = await _admin.ListSubmissionsAsync(status, challengeId, cancellationToken);
|
|
|
|
return Ok(submissions);
|
|
}
|
|
|
|
[HttpPut("knowledge-items/{knowledgeItemId}/verify")]
|
|
public async Task<ActionResult<KnowledgeItemResponse>> VerifyKnowledgeItem(
|
|
string knowledgeItemId,
|
|
VerifyKnowledgeItemRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var item = await _admin.VerifyKnowledgeItemAsync(knowledgeItemId, request, cancellationToken);
|
|
|
|
return item is null ? NotFound() : Ok(item);
|
|
}
|
|
|
|
[HttpGet("metrics")]
|
|
public async Task<ActionResult<AdminMetricsResponse>> Metrics(CancellationToken cancellationToken)
|
|
{
|
|
var metrics = await _admin.GetMetricsAsync(cancellationToken);
|
|
|
|
return Ok(metrics);
|
|
}
|
|
|
|
[HttpPut("grant-calls/{grantCallId}/status")]
|
|
public async Task<ActionResult<GrantCallResponse>> UpdateGrantCallStatus(
|
|
string grantCallId,
|
|
UpdateGrantCallStatusRequest request,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
var updated = await _grantCalls.UpdateStatusAsync(grantCallId, request.Status, cancellationToken);
|
|
|
|
if (updated is null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
if (string.Equals(request.Status, "open", StringComparison.Ordinal))
|
|
{
|
|
await NotifyIdeaAuthorsAsync(updated, cancellationToken);
|
|
}
|
|
|
|
return Ok(updated);
|
|
}
|
|
|
|
private async Task NotifyIdeaAuthorsAsync(GrantCallResponse call, CancellationToken cancellationToken)
|
|
{
|
|
var authorIds = await _ideas.ListDistinctAuthorIdsAsync(cancellationToken);
|
|
var payload = new Dictionary<string, string?>
|
|
{
|
|
["grantCallId"] = call.Id,
|
|
["name"] = call.Name,
|
|
};
|
|
|
|
foreach (var authorId in authorIds)
|
|
{
|
|
await _notifications.NotifyUserAsync(authorId, "grant_call.opened", payload, cancellationToken);
|
|
}
|
|
}
|
|
}
|