using Management.Data; using Management.Security; using Microsoft.AspNetCore.Mvc; using System.Text.Json; namespace Management.Controllers.Admin; /// /// 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 /// [ApiController] [Route("api/admin/templates")] public sealed class AdminTemplatesController : AdminControllerBase { public AdminTemplatesController(SqlService sql, ClientContext client, ILogger log) : base(sql, client, log) { } [HttpPost("list")] public Task List([FromBody] JsonElement body, CancellationToken ct) => CallProc("spAdminTemplates", "list", body.ToString(), ct); [HttpGet("{templateId:int}")] public Task Get(int templateId, CancellationToken ct) => CallProc("spAdminTemplates", "get", new { templateId }, ct); [HttpPost] public Task 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 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 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 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; } }