Merge pull request 'feat: add filtering by ServiceName and Key in email templates query and UI' (#11) from development into main

Reviewed-on: #11
This commit was merged in pull request #11.
This commit is contained in:
2026-05-17 11:43:55 +03:00
7 changed files with 56 additions and 10 deletions
@@ -4,10 +4,10 @@ namespace HrynCo.NotificationService.DAL.Abstract.Repositories;
public interface IEmailTemplateRepository
{
Task<IReadOnlyList<EmailTemplate>> GetAllAsync(CancellationToken ct = default);
Task<IReadOnlyList<EmailTemplate>> GetAllAsync(string? serviceName = null, string? key = null, CancellationToken ct = default);
Task<IReadOnlyList<EmailTemplate>> GetByServiceAsync(string serviceName, CancellationToken ct = default);
Task<EmailTemplate?> GetAsync(string serviceName, string key, string languageCode, CancellationToken ct = default);
Task AddAsync(EmailTemplate template, CancellationToken ct = default);
Task UpdateAsync(EmailTemplate template, CancellationToken ct = default);
Task DeleteAsync(EmailTemplate template, CancellationToken ct = default);
}
}
@@ -13,9 +13,21 @@ internal sealed class EmailTemplateRepository
{
}
public async Task<IReadOnlyList<EmailTemplate>> GetAllAsync(CancellationToken ct = default)
public async Task<IReadOnlyList<EmailTemplate>> GetAllAsync(string? serviceName = null, string? key = null, CancellationToken ct = default)
{
List<EmailTemplateEntity> entities = await EfRepository.Get()
IQueryable<EmailTemplateEntity> query = EfRepository.Get();
if (!string.IsNullOrWhiteSpace(serviceName))
{
query = query.Where(x => x.ServiceName == serviceName);
}
if (!string.IsNullOrWhiteSpace(key))
{
query = query.Where(x => x.Key == key);
}
List<EmailTemplateEntity> entities = await query
.OrderBy(x => x.ServiceName).ThenBy(x => x.Key)
.AsNoTracking()
.ToListAsync(ct);
@@ -22,7 +22,7 @@ internal sealed class GetAllEmailTemplatesHandler
protected override async Task<ServiceResult<IReadOnlyList<EmailTemplate>>> DoOnHandle(
GetAllEmailTemplatesQuery request, CancellationToken cancellationToken)
{
var templates = await _templates.GetAllAsync(cancellationToken);
var templates = await _templates.GetAllAsync(request.ServiceName, request.Key, cancellationToken);
return Success(templates);
}
}
@@ -4,4 +4,5 @@ using HrynCo.NotificationService.Services.Core;
namespace HrynCo.NotificationService.Services.EmailTemplates.GetAll;
public sealed record GetAllEmailTemplatesQuery : IRequest<ServiceResult<IReadOnlyList<EmailTemplate>>>;
public sealed record GetAllEmailTemplatesQuery(string? ServiceName = null, string? Key = null)
: IRequest<ServiceResult<IReadOnlyList<EmailTemplate>>>;
@@ -23,9 +23,12 @@ public class AdminTemplatesController : Controller
// GET /admin/templates
[HttpGet("")]
public async Task<IActionResult> Index(CancellationToken ct)
public async Task<IActionResult> Index([FromQuery] string? serviceName, [FromQuery] string? key, CancellationToken ct)
{
var result = await _mediator.Send(new GetAllEmailTemplatesQuery(), ct);
ViewData["ServiceNameFilter"] = serviceName;
ViewData["KeyFilter"] = key;
var result = await _mediator.Send(new GetAllEmailTemplatesQuery(serviceName, key), ct);
if (!result.IsSuccess)
{
ModelState.AddModelError("", result.Error?.Message ?? "Failed to load templates.");
@@ -1,6 +1,7 @@
using HrynCo.NotificationService.Web.Infrastructure;
using HrynCo.NotificationService.Services.EmailTemplates.Create;
using HrynCo.NotificationService.Services.EmailTemplates.Delete;
using HrynCo.NotificationService.Services.EmailTemplates.GetAll;
using HrynCo.NotificationService.Services.EmailTemplates.Get;
using HrynCo.NotificationService.Services.EmailTemplates.GetByService;
using HrynCo.NotificationService.Services.EmailTemplates.Update;
@@ -15,9 +16,9 @@ public sealed class EmailTemplatesController : ApiControllerBase
public EmailTemplatesController(IMediator mediator) : base(mediator) { }
[HttpGet]
public async Task<IActionResult> GetAll([FromQuery] string serviceName, CancellationToken cancellationToken)
public async Task<IActionResult> GetAll([FromQuery] string? serviceName, [FromQuery] string? key, CancellationToken cancellationToken)
{
var result = await Mediator.Send(new GetEmailTemplatesQuery(serviceName), cancellationToken);
var result = await Mediator.Send(new GetAllEmailTemplatesQuery(serviceName, key), cancellationToken);
return FromServiceResult(result);
}
@@ -2,6 +2,8 @@
@model IReadOnlyList<EmailTemplate>
@{
ViewData["Title"] = "Email Templates";
var serviceNameFilter = ViewData["ServiceNameFilter"] as string ?? string.Empty;
var keyFilter = ViewData["KeyFilter"] as string ?? string.Empty;
}
<div class="page-header">
@@ -11,6 +13,33 @@
</a>
</div>
<div class="card shadow-sm mb-3">
<div class="card-body">
<form method="get" action="/admin/templates" class="row g-2 align-items-end">
<div class="col-12 col-md-5">
<label class="form-label fw-semibold" for="serviceName">Service Name</label>
<input id="serviceName"
name="serviceName"
value="@serviceNameFilter"
class="form-control"
placeholder="Filter by service name" />
</div>
<div class="col-12 col-md-5">
<label class="form-label fw-semibold" for="key">Key</label>
<input id="key"
name="key"
value="@keyFilter"
class="form-control"
placeholder="Filter by key" />
</div>
<div class="col-12 col-md-2 d-flex gap-2">
<button type="submit" class="btn btn-primary w-100">Filter</button>
<a href="/admin/templates" class="btn btn-outline-secondary w-100">Clear</a>
</div>
</form>
</div>
</div>
@if (!ViewData.ModelState.IsValid)
{
<div class="alert alert-danger">