feat: add MVC Razor admin UI for email templates
- GetAllEmailTemplatesQuery + handler (new GetAll use case) - IEmailTemplateRepository.GetAllAsync + EF implementation - AdminTemplatesController: Index, Create, Edit, Save, Delete - EmailTemplateEditViewModel with IsNew/PageTitle helpers - Views/_ViewStart, _ViewImports, Shared/_Layout (Bootstrap 5) - Shared/_EditorLayout (chained layout for all edit screens) - Views/AdminTemplates/Index (table with edit/delete actions) - Views/AdminTemplates/Edit (card form, readonly composite key on edit) - Program.cs: AddControllersWithViews, UseStaticFiles, MapDefaultControllerRoute Ref: IT-634 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
using System.Text.Json;
|
||||
using HrynCo.NotificationService.DAL.Abstract.Templates;
|
||||
using HrynCo.NotificationService.Services.EmailTemplates.Create;
|
||||
using HrynCo.NotificationService.Services.EmailTemplates.Delete;
|
||||
using HrynCo.NotificationService.Services.EmailTemplates.Get;
|
||||
using HrynCo.NotificationService.Services.EmailTemplates.GetAll;
|
||||
using HrynCo.NotificationService.Services.EmailTemplates.Update;
|
||||
using HrynCo.NotificationService.Web.Controllers.Admin.ViewModels;
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace HrynCo.NotificationService.Web.Controllers.Admin;
|
||||
|
||||
[Route("admin/templates")]
|
||||
public class AdminTemplatesController : Controller
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public AdminTemplatesController(IMediator mediator)
|
||||
{
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
// GET /admin/templates
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> Index(CancellationToken ct)
|
||||
{
|
||||
var result = await _mediator.Send(new GetAllEmailTemplatesQuery(), ct);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
ModelState.AddModelError("", result.Error?.Message ?? "Failed to load templates.");
|
||||
return View(Array.Empty<EmailTemplate>());
|
||||
}
|
||||
|
||||
return View(result.Result);
|
||||
}
|
||||
|
||||
// GET /admin/templates/create
|
||||
[HttpGet("create")]
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View("Edit", new EmailTemplateEditViewModel());
|
||||
}
|
||||
|
||||
// GET /admin/templates/{serviceName}/{key}/{languageCode}
|
||||
[HttpGet("{serviceName}/{key}/{languageCode}")]
|
||||
public async Task<IActionResult> Edit(string serviceName, string key, string languageCode, CancellationToken ct)
|
||||
{
|
||||
var result = await _mediator.Send(new GetEmailTemplateQuery(serviceName, key, languageCode), ct);
|
||||
if (!result.IsSuccess || result.Result is null)
|
||||
return NotFound();
|
||||
|
||||
var template = result.Result;
|
||||
var vm = new EmailTemplateEditViewModel
|
||||
{
|
||||
Id = template.Id,
|
||||
ServiceName = template.ServiceName,
|
||||
Key = template.Key,
|
||||
LanguageCode = template.LanguageCode,
|
||||
Subject = template.Subject,
|
||||
HtmlBody = template.HtmlBody,
|
||||
TextBody = template.TextBody,
|
||||
VariablesJson = JsonSerializer.Serialize(template.Variables)
|
||||
};
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
// POST /admin/templates/save
|
||||
[HttpPost("save")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Save(EmailTemplateEditViewModel model, CancellationToken ct)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return View("Edit", model);
|
||||
|
||||
List<EmailTemplateVariable> variables;
|
||||
try
|
||||
{
|
||||
variables = JsonSerializer.Deserialize<List<EmailTemplateVariable>>(model.VariablesJson,
|
||||
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
|
||||
?? [];
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.VariablesJson), "Invalid JSON format for variables.");
|
||||
return View("Edit", model);
|
||||
}
|
||||
|
||||
if (model.IsNew)
|
||||
{
|
||||
var command = new CreateEmailTemplateCommand(
|
||||
model.ServiceName,
|
||||
model.Key,
|
||||
model.LanguageCode,
|
||||
model.Subject,
|
||||
model.HtmlBody,
|
||||
model.TextBody,
|
||||
variables);
|
||||
|
||||
var result = await _mediator.Send(command, ct);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
ModelState.AddModelError("", result.Error?.Message ?? "Failed to create template.");
|
||||
return View("Edit", model);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var command = new UpdateEmailTemplateCommand(
|
||||
model.ServiceName,
|
||||
model.Key,
|
||||
model.LanguageCode,
|
||||
model.Subject,
|
||||
model.HtmlBody,
|
||||
model.TextBody,
|
||||
variables);
|
||||
|
||||
var result = await _mediator.Send(command, ct);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
ModelState.AddModelError("", result.Error?.Message ?? "Failed to update template.");
|
||||
return View("Edit", model);
|
||||
}
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// POST /admin/templates/{serviceName}/{key}/{languageCode}/delete
|
||||
[HttpPost("{serviceName}/{key}/{languageCode}/delete")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(string serviceName, string key, string languageCode, CancellationToken ct)
|
||||
{
|
||||
await _mediator.Send(new DeleteEmailTemplateCommand(serviceName, key, languageCode), ct);
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HrynCo.NotificationService.Web.Controllers.Admin.ViewModels;
|
||||
|
||||
public class EmailTemplateEditViewModel
|
||||
{
|
||||
public Guid? Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public string ServiceName { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string Key { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string LanguageCode { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string Subject { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string HtmlBody { get; set; } = "";
|
||||
|
||||
public string TextBody { get; set; } = "";
|
||||
|
||||
// JSON array: [{"name":"UserName","required":true}, ...]
|
||||
public string VariablesJson { get; set; } = "[]";
|
||||
|
||||
public bool IsNew => Id == null;
|
||||
public string PageTitle => IsNew ? "Create Email Template" : "Edit Email Template";
|
||||
}
|
||||
Reference in New Issue
Block a user