mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-09-30 13:03:12 +00:00
- 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
89 lines
3.5 KiB
C#
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>";
|
|
}
|
|
}
|
|
}
|