Fix notification bell, SMS consent kiosk flow, and button alignment

Notification bell:
- Bell now polls /InAppNotifications/Recent every 60s as a SignalR fallback
- Bell dropdown refresh on open so count is always current when staff looks at it

SMS consent → kiosk flow:
- Staff clicks "Get SMS Consent" on Customer Details → AJAX POST to
  /Kiosk/PushSmsConsent stores customer in IMemoryCache (10 min TTL)
- Kiosk PollSession returns smsConsentPending + customerId so tablet navigates
  to /Kiosk/SmsConsent/{customerId} automatically
- Customer reads TCPA consent on tablet, taps I Agree or No Thanks
- On agree: NotifyBySms/SmsConsentedAt/SmsConsentMethod set; in-app notification
  fires; cache cleared; tablet returns to Welcome
- Removed Customers/SmsConsent (staff-browser version); moved view to Kiosk/

Button alignment:
- kiosk.css: added display:flex + align-items:center + justify-content:center to
  all kiosk body buttons so content is centred vertically in tall button outlines

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 23:13:57 -04:00
parent b69ff6db3a
commit e1256503be
8 changed files with 168 additions and 77 deletions
@@ -60,6 +60,14 @@ body.kiosk-body {
width: 100%;
}
/* Vertically centre content in any tall kiosk button (covers <a> and <button>) */
.kiosk-body .btn,
.kiosk-btn {
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Suppress all hover effects on touch screens */
@media (hover: none) {
.kiosk-body .btn:hover { filter: none; opacity: 1; }
@@ -0,0 +1,19 @@
"use strict";
async function pushSmsConsent(customerId) {
const tok = document.querySelector('input[name="__RequestVerificationToken"]')?.value ?? '';
try {
const res = await fetch(`/Kiosk/PushSmsConsent?customerId=${customerId}`, {
method: 'POST',
headers: { 'RequestVerificationToken': tok }
});
const data = await res.json();
if (data.success) {
toastr.success('Consent form sent to the kiosk tablet — hand it to the customer.', 'Sent to Kiosk');
} else {
toastr.warning(data.message || 'Could not send consent to kiosk.');
}
} catch {
toastr.error('An error occurred. Please try again.');
}
}
@@ -26,6 +26,12 @@
if (!res.ok) throw new Error("HTTP " + res.status);
const data = await res.json();
setStatus("#16a34a", "Ready");
if (data.smsConsentPending && data.customerId) {
active = false;
setStatus("#2563eb", "Loading consent…");
window.location.href = `/Kiosk/SmsConsent/${data.customerId}`;
return;
}
if (data.hasSession && data.sessionToken) {
active = false;
setStatus("#2563eb", "Starting…");