diff --git a/src/PowderCoating.Web/Controllers/CustomersController.cs b/src/PowderCoating.Web/Controllers/CustomersController.cs index 4e327d8..2da409c 100644 --- a/src/PowderCoating.Web/Controllers/CustomersController.cs +++ b/src/PowderCoating.Web/Controllers/CustomersController.cs @@ -877,6 +877,74 @@ public class CustomersController : Controller } } + /// + /// Displays a full-screen SMS consent form for the customer to read and agree to. + /// Staff opens this page on a tablet and hands it to the customer; no staff account + /// interaction is required — the page is scoped to the customer by ID only. + /// Redirects back to Details if the customer has already consented. + /// + // GET: Customers/SmsConsent/5 + public async Task SmsConsent(int id) + { + var customer = await _unitOfWork.Customers.GetByIdAsync(id); + if (customer == null) return NotFound(); + + if (customer.NotifyBySms) + { + this.ToastInfo("This customer has already given SMS consent."); + return RedirectToAction(nameof(Details), new { id }); + } + + var companyId = _tenantContext.GetCurrentCompanyId(); + if (companyId.HasValue) + { + var company = await _unitOfWork.Companies.GetByIdAsync(companyId.Value); + ViewBag.CompanyName = company?.CompanyName; + ViewBag.CompanyLogoUrl = !string.IsNullOrEmpty(company?.LogoFilePath) + ? Url.Action("Logo", "Kiosk") + : null; + } + + ViewBag.ShowInactivityTimer = false; + ViewBag.CustomerName = $"{customer.ContactFirstName} {customer.ContactLastName}".Trim(); + if (string.IsNullOrWhiteSpace(ViewBag.CustomerName as string) && !string.IsNullOrEmpty(customer.CompanyName)) + ViewBag.CustomerName = customer.CompanyName; + + return View(customer.Id); + } + + /// + /// Records the customer's SMS consent: sets NotifyBySms, SmsConsentedAt (UTC now), + /// and SmsConsentMethod = "InPerson". Called when the customer taps "I Agree" on the + /// consent form presented by staff. + /// + // POST: Customers/SmsConsent/5 + [HttpPost, ValidateAntiForgeryToken] + public async Task SmsConsent(int id, bool agreed) + { + var customer = await _unitOfWork.Customers.GetByIdAsync(id); + if (customer == null) return NotFound(); + + if (!agreed) + { + this.ToastError("Customer did not agree to SMS consent."); + return RedirectToAction(nameof(Details), new { id }); + } + + customer.NotifyBySms = true; + customer.SmsConsentedAt = DateTime.UtcNow; + customer.SmsConsentMethod = "InPerson"; + customer.SmsOptedOutAt = null; + + await _unitOfWork.Customers.UpdateAsync(customer); + await _unitOfWork.CompleteAsync(); + + _logger.LogInformation("SMS consent recorded for customer {CustomerId} via staff-presented form", id); + + this.ToastSuccess($"SMS consent recorded for {customer.ContactFirstName} {customer.ContactLastName}."); + return RedirectToAction(nameof(Details), new { id }); + } + /// /// Issues a standalone credit memo and increments the customer's CreditBalance. /// Restricted to CompanyAdmin because credits affect the financial ledger. The memo diff --git a/src/PowderCoating.Web/Views/Customers/Details.cshtml b/src/PowderCoating.Web/Views/Customers/Details.cshtml index c8db141..6281ff7 100644 --- a/src/PowderCoating.Web/Views/Customers/Details.cshtml +++ b/src/PowderCoating.Web/Views/Customers/Details.cshtml @@ -175,7 +175,8 @@ } @if (Model.NotifyBySms) { - + SMS on } @@ -184,6 +185,11 @@ SMS off + + Get SMS Consent + } diff --git a/src/PowderCoating.Web/Views/Customers/SmsConsent.cshtml b/src/PowderCoating.Web/Views/Customers/SmsConsent.cshtml new file mode 100644 index 0000000..f84de17 --- /dev/null +++ b/src/PowderCoating.Web/Views/Customers/SmsConsent.cshtml @@ -0,0 +1,45 @@ +@model int +@{ + Layout = "~/Views/Shared/_KioskLayout.cshtml"; + ViewData["Title"] = "SMS Consent"; + string customerName = ViewBag.CustomerName as string ?? "Customer"; +} + +
+

SMS Notifications

+

Please read the following and tap I Agree to opt in.

+ +
+ @Html.AntiForgeryToken() + + +
+ SMS Consent & Opt-In +

+ By tapping I Agree below, @customerName consents to receive + SMS text messages from @(ViewBag.CompanyName ?? "this shop") regarding order status + updates, pickup notifications, and other information related to your powder coating + services. +

+

+ Message frequency varies. Message and data rates may apply. + You may opt out at any time by replying STOP to any message. + Reply HELP for assistance. +

+

+ Your mobile number will not be shared with third parties or used for marketing + unrelated to your orders. +

+
+ +
+ + No Thanks + + +
+
+