From 8cc4b0dfcecf3eb231ac63e98bcacde1a91d9788 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sat, 21 Mar 2026 13:04:56 +0100 Subject: [PATCH] send test email --- .../Controllers/JobApplicationsController.cs | 9 +++- JobTrackerApi/Controllers/UsersController.cs | 31 ++++++++++- job-tracker-ui/src/pages/AdminUsersPage.tsx | 52 ++++++++++++++++++- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/JobTrackerApi/Controllers/JobApplicationsController.cs b/JobTrackerApi/Controllers/JobApplicationsController.cs index 22a50d1..94bb6ab 100644 --- a/JobTrackerApi/Controllers/JobApplicationsController.cs +++ b/JobTrackerApi/Controllers/JobApplicationsController.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using JobTrackerApi.Data; using JobTrackerApi.Models; @@ -1015,5 +1015,12 @@ namespace JobTrackerApi.Controllers return Ok(outList); } + + [HttpGet("summarizer-metrics")] + public async Task> GetSummarizerMetrics(CancellationToken cancellationToken) + { + var metrics = await _summarizer.GetMetricsAsync(cancellationToken); + return Ok(metrics); + } } } diff --git a/JobTrackerApi/Controllers/UsersController.cs b/JobTrackerApi/Controllers/UsersController.cs index 724df10..45c7983 100644 --- a/JobTrackerApi/Controllers/UsersController.cs +++ b/JobTrackerApi/Controllers/UsersController.cs @@ -1,9 +1,10 @@ -using JobTrackerApi.Models; +using JobTrackerApi.Models; using JobTrackerApi.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; +using System.Security.Claims; namespace JobTrackerApi.Controllers; @@ -113,6 +114,7 @@ public sealed class UsersController : ControllerBase return NoContent(); } + [HttpPost("{id}/send-password-reset")] public async Task SendPasswordReset([FromRoute] string id, CancellationToken cancellationToken) { @@ -139,5 +141,30 @@ public sealed class UsersController : ControllerBase return NoContent(); } -} + public sealed record SendTestEmailRequest(string? ToEmail, string? Subject, string? Message); + + [HttpPost("send-test-email")] + public async Task SendTestEmail([FromBody] SendTestEmailRequest? request, CancellationToken cancellationToken) + { + var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? User.FindFirstValue("sub"); + var currentUser = currentUserId is null ? null : await _users.FindByIdAsync(currentUserId); + + var toEmail = (request?.ToEmail ?? currentUser?.Email ?? "").Trim(); + if (string.IsNullOrWhiteSpace(toEmail)) return BadRequest("Recipient email is required."); + + var subject = string.IsNullOrWhiteSpace(request?.Subject) ? "Job Tracker test email" : request!.Subject!.Trim(); + var message = string.IsNullOrWhiteSpace(request?.Message) + ? "This is a test email from the Job Tracker admin panel.\n\nIf you received this, the SMTP configuration is working." + : request!.Message!.Trim(); + + await _email.SendAsync( + toEmail, + subject, + $"{message}\n\nSent at: {DateTimeOffset.UtcNow:u}", + cancellationToken + ); + + return NoContent(); + } +} diff --git a/job-tracker-ui/src/pages/AdminUsersPage.tsx b/job-tracker-ui/src/pages/AdminUsersPage.tsx index 97120f3..a3d7981 100644 --- a/job-tracker-ui/src/pages/AdminUsersPage.tsx +++ b/job-tracker-ui/src/pages/AdminUsersPage.tsx @@ -36,6 +36,11 @@ export default function AdminUsersPage() { const [newPassword, setNewPassword] = useState(""); const [newIsAdmin, setNewIsAdmin] = useState(false); + const [testEmailTo, setTestEmailTo] = useState(""); + const [testEmailSubject, setTestEmailSubject] = useState("Job Tracker SMTP test"); + const [testEmailMessage, setTestEmailMessage] = useState("This is a test email from the Job Tracker admin panel."); + const [sendingTestEmail, setSendingTestEmail] = useState(false); + async function load() { setLoading(true); try { @@ -50,7 +55,6 @@ export default function AdminUsersPage() { useEffect(() => { void load(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const canCreate = useMemo(() => newEmail.trim().length > 3 && newPassword.length >= 6, [newEmail, newPassword]); @@ -76,6 +80,23 @@ export default function AdminUsersPage() { } }; + const sendTestEmail = async () => { + setSendingTestEmail(true); + try { + await api.post("/users/send-test-email", { + toEmail: testEmailTo.trim() || null, + subject: testEmailSubject.trim() || null, + message: testEmailMessage.trim() || null, + }); + toast("Test email sent.", "success"); + } catch (e: any) { + const msg = e?.response?.data || e?.message || "Failed to send test email."; + toast(String(msg), "error"); + } finally { + setSendingTestEmail(false); + } + }; + const remove = async (u: UserDto) => { if (!window.confirm(`Delete user ${u.email || u.userName || u.id}?`)) return; try { @@ -94,6 +115,35 @@ export default function AdminUsersPage() { Admin-only user management. + + SMTP test email + + Send a quick delivery check using the configured SMTP settings. Leave the recipient blank to use your admin email. + + + setTestEmailTo(e.target.value)} + placeholder="Uses your admin email if left blank" + /> + setTestEmailSubject(e.target.value)} /> + setTestEmailMessage(e.target.value)} + sx={{ gridColumn: { xs: "1 / -1", md: "1 / -1" } }} + /> + + + + + + Create user