Files
AdPlatform-Server/Management/Controllers/Admin/AdminTemplatesController.cs
2026-03-14 13:50:09 -07:00

116 lines
4.7 KiB
C#

using Management.Data;
using Management.Security;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace Management.Controllers.Admin;
/// <summary>
/// Admin endpoints for allocation template management.
/// Table: dbo.tbAllocationTemplate
///
/// ENDPOINTS:
/// GET /api/admin/templates - List templates (filterable)
/// GET /api/admin/templates/{id} - Get template
/// POST /api/admin/templates - Create template
/// PUT /api/admin/templates/{id} - Update template
/// DELETE /api/admin/templates/{id} - Delete template
/// GET /api/admin/templates/categories - Distinct business categories
/// </summary>
[ApiController]
[Route("api/admin/templates")]
public sealed class AdminTemplatesController : AdminControllerBase
{
public AdminTemplatesController(SqlService sql, ClientContext client, ILogger<AdminTemplatesController> log)
: base(sql, client, log) { }
[HttpPost("list")]
public Task<IActionResult> List([FromBody] JsonElement body, CancellationToken ct)
=> CallProc("spAdminTemplates", "list", body.ToString(), ct);
[HttpGet("{templateId:int}")]
public Task<IActionResult> Get(int templateId, CancellationToken ct)
=> CallProc("spAdminTemplates", "get", new { templateId }, ct);
[HttpPost]
public Task<IActionResult> Create([FromBody] CreateTemplateRequest request, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(request?.ChannelType))
return Task.FromResult(ValidationError("channelType is required"));
if (string.IsNullOrWhiteSpace(request?.BusinessCategory))
return Task.FromResult(ValidationError("businessCategory is required"));
if (string.IsNullOrWhiteSpace(request?.Objective))
return Task.FromResult(ValidationError("objective is required"));
if (request.RecommendedPct is null or < 0 or > 100)
return Task.FromResult(ValidationError("recommendedPct must be between 0 and 100"));
Logger.LogWarning("[Admin] CreateTemplate | {Channel}/{Category}/{Objective} = {Pct}% | By={User}",
request.ChannelType, request.BusinessCategory, request.Objective, request.RecommendedPct, Client.Email);
return CallProc("spAdminTemplates", "create", new
{
channelType = request.ChannelType.Trim(),
businessCategory = request.BusinessCategory.Trim(),
objective = request.Objective.Trim(),
recommendedPct = request.RecommendedPct,
minBudgetRequired = request.MinBudgetRequired ?? 0m,
rationale = request.Rationale?.Trim()
}, ct);
}
[HttpPut("{templateId:int}")]
public Task<IActionResult> Update(int templateId, [FromBody] UpdateTemplateRequest request, CancellationToken ct)
{
if (request?.RecommendedPct is < 0 or > 100)
return Task.FromResult(ValidationError("recommendedPct must be between 0 and 100"));
Logger.LogWarning("[Admin] UpdateTemplate | Id={Id} | By={User}", templateId, Client.Email);
return CallProc("spAdminTemplates", "update", new
{
templateId,
channelType = request?.ChannelType?.Trim(),
businessCategory = request?.BusinessCategory?.Trim(),
objective = request?.Objective?.Trim(),
recommendedPct = request?.RecommendedPct,
minBudgetRequired = request?.MinBudgetRequired,
rationale = request?.Rationale?.Trim(),
isActive = request?.IsActive
}, ct);
}
[HttpDelete("{templateId:int}")]
public Task<IActionResult> Delete(int templateId, CancellationToken ct)
{
Logger.LogWarning("[Admin] DeleteTemplate | Id={Id} | By={User}", templateId, Client.Email);
return CallProc("spAdminTemplates", "delete", new { templateId }, ct);
}
[HttpGet("categories")]
public Task<IActionResult> Categories(CancellationToken ct)
=> CallProc("spAdminTemplates", "categories", new { }, ct);
}
// DTOs
public sealed class CreateTemplateRequest
{
public string? ChannelType { get; set; }
public string? BusinessCategory { get; set; }
public string? Objective { get; set; }
public decimal? RecommendedPct { get; set; }
public decimal? MinBudgetRequired { get; set; }
public string? Rationale { get; set; }
}
public sealed class UpdateTemplateRequest
{
public string? ChannelType { get; set; }
public string? BusinessCategory { get; set; }
public string? Objective { get; set; }
public decimal? RecommendedPct { get; set; }
public decimal? MinBudgetRequired { get; set; }
public string? Rationale { get; set; }
public bool? IsActive { get; set; }
}