shoc-backend/Api.SeaHavenIndustries/Controllers/DispatchPublicController.cs
Adam Moussa c482fa0558 Add vendor accept via email link
- DispatchPublicController with public GET /api/dispatch/accept/{token}
- Updates dispatch status to Acknowledged with timestamp
- Returns styled HTML confirmation page
- Dispatch email now includes green Accept Dispatch button
- Logs vendor acceptance to audit trail
2026-04-17 15:39:50 -04:00

89 lines
3.5 KiB
C#

using Data.SeaHavenIndustries;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace Api.SeaHavenIndustries.Controllers
{
[ApiController]
[Route("api/dispatch")]
public class DispatchPublicController : Controller
{
private readonly ApplicationDbContext _db;
public DispatchPublicController(ApplicationDbContext db)
{
_db = db;
}
[HttpGet("accept/{token}")]
public async Task<IActionResult> Accept(string token)
{
var dispatch = await _db.Dispatches
.Include(d => d.Vendor)
.FirstOrDefaultAsync(d => d.AcceptToken == token);
if (dispatch == null)
{
return Content(BuildHtml("Dispatch Not Found", "This dispatch link is invalid or has expired.", "#dc3545"), "text/html");
}
if (dispatch.Status != "Sent")
{
var alreadyMsg = dispatch.AcknowledgedAt.HasValue
? $"This dispatch was already accepted on {dispatch.AcknowledgedAt.Value:MMMM dd, yyyy 'at' h:mm tt} UTC."
: "This dispatch has already been processed.";
return Content(BuildHtml("Already Accepted", alreadyMsg, "#ffc107"), "text/html");
}
dispatch.Status = "Acknowledged";
dispatch.AcknowledgedAt = DateTime.UtcNow;
_db.WorkOrderAuditLogs.Add(new WorkOrderAuditLog
{
WorkOrderId = dispatch.WorkOrderId ?? 0,
FieldName = $"Dispatch {dispatch.DispatchNumber} Status",
OldValue = "Sent",
NewValue = "Acknowledged",
Action = "vendor_accept",
CreatedAt = DateTime.UtcNow
});
await _db.SaveChangesAsync();
var vendorName = dispatch.Vendor?.CompanyName ?? "Vendor";
return Content(BuildHtml(
"Dispatch Accepted",
$"<strong>{vendorName}</strong> has accepted dispatch <strong>{dispatch.DispatchNumber}</strong>.<br/><br/>Thank you for confirming. The dispatcher has been notified.",
"#28a745"
), "text/html");
}
private static string BuildHtml(string title, string message, string color)
{
return $@"<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'/>
<meta name='viewport' content='width=device-width, initial-scale=1'/>
<title>{title} - Sea Haven Industries</title>
<style>
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #f5f5f5; }}
.card {{ background: white; border-radius: 12px; padding: 40px; max-width: 480px; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.1); }}
.icon {{ width: 60px; height: 60px; border-radius: 50%; background: {color}; color: white; font-size: 28px; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; }}
h1 {{ font-size: 22px; color: #333; margin: 0 0 12px; }}
p {{ font-size: 15px; color: #666; line-height: 1.6; margin: 0; }}
.footer {{ margin-top: 30px; font-size: 12px; color: #999; }}
</style>
</head>
<body>
<div class='card'>
<div class='icon'>✓</div>
<h1>{title}</h1>
<p>{message}</p>
<div class='footer'>Sea Haven Industries</div>
</div>
</body>
</html>";
}
}
}