@@ -18,6 +18,26 @@ import {
1818 formatEstTime ,
1919} from "@/utils/dateEst" ;
2020import { EmailNotifications } from "@/server/email/EmailAction" ;
21+ import { emitToast } from "@/server/notifications/emitToast" ;
22+
23+ // Runs an email send alongside the toast emit rather than before it, so a
24+ // slow/failing email provider can't delay real-time toast delivery.
25+ async function sendEmailSafely ( label : string , send : ( ) => Promise < void > ) {
26+ try {
27+ await send ( ) ;
28+ } catch ( error ) {
29+ console . error ( `Failed to send ${ label } email` , error ) ;
30+ }
31+ }
32+
33+ // Fires email + toast without blocking the caller. Both already swallow
34+ // their own errors (sendEmailSafely, emitToast) — this must never make the
35+ // route-status API response wait on a slow/broken notification provider.
36+ function dispatchNotifications ( tasks : Promise < void > [ ] ) {
37+ Promise . all ( tasks ) . catch ( ( error ) => {
38+ console . error ( "Notification dispatch failed" , error ) ;
39+ } ) ;
40+ }
2141
2242export async function createRoute ( data : CreateRouteInput ) {
2343 await connectMongoDB ( ) ;
@@ -163,14 +183,22 @@ export async function completeRoute(routeId: string) {
163183 }
164184 ) . settings ?. notifications ?. rideCompleted
165185 ) {
166- await EmailNotifications . rideCompleted (
167- studentUser . email ,
168- studentUser . preferredName ??
169- `${ studentUser . firstName } ${ studentUser . lastName } ` ,
170- {
171- rideId : route . _id . toString ( ) ,
172- } ,
173- ) ;
186+ dispatchNotifications ( [
187+ sendEmailSafely ( "rideCompleted" , ( ) =>
188+ EmailNotifications . rideCompleted (
189+ studentUser . email ,
190+ studentUser . preferredName ??
191+ `${ studentUser . firstName } ${ studentUser . lastName } ` ,
192+ {
193+ rideId : route . _id . toString ( ) ,
194+ } ,
195+ ) ,
196+ ) ,
197+ emitToast ( studentUser . _id . toString ( ) , {
198+ type : "success" ,
199+ message : "Your ride has been completed. Thank you for riding with us!" ,
200+ } ) ,
201+ ] ) ;
174202 }
175203
176204 await archiveChatlogForRoute ( routeId ) ;
@@ -199,12 +227,23 @@ export async function cancelRoute(routeId: string, status?: string) {
199227 }
200228 ) . settings ?. notifications ?. rideCancelled
201229 ) {
202- await EmailNotifications . rideCancelled (
203- studentUser . email ,
204- studentUser . preferredName ??
205- `${ studentUser . firstName } ${ studentUser . lastName } ` ,
206- { rideId : route . _id . toString ( ) , reason : `Ride status: ${ route . status } ` } ,
207- ) ;
230+ dispatchNotifications ( [
231+ sendEmailSafely ( "rideCancelled" , ( ) =>
232+ EmailNotifications . rideCancelled (
233+ studentUser . email ,
234+ studentUser . preferredName ??
235+ `${ studentUser . firstName } ${ studentUser . lastName } ` ,
236+ {
237+ rideId : route . _id . toString ( ) ,
238+ reason : `Ride status: ${ route . status } ` ,
239+ } ,
240+ ) ,
241+ ) ,
242+ emitToast ( studentUser . _id . toString ( ) , {
243+ type : "error" ,
244+ message : "Your ride has been cancelled." ,
245+ } ) ,
246+ ] ) ;
208247 }
209248
210249 if ( route . driver ?. _id ) {
@@ -217,15 +256,23 @@ export async function cancelRoute(routeId: string, status?: string) {
217256 }
218257 ) . settings ?. notifications ?. rideCancelled
219258 ) {
220- await EmailNotifications . rideCancelled (
221- driverUser . email ,
222- driverUser . preferredName ??
223- `${ driverUser . firstName } ${ driverUser . lastName } ` ,
224- {
225- rideId : route . _id . toString ( ) ,
226- reason : `Ride status: ${ route . status } ` ,
227- } ,
228- ) ;
259+ dispatchNotifications ( [
260+ sendEmailSafely ( "rideCancelled" , ( ) =>
261+ EmailNotifications . rideCancelled (
262+ driverUser . email ,
263+ driverUser . preferredName ??
264+ `${ driverUser . firstName } ${ driverUser . lastName } ` ,
265+ {
266+ rideId : route . _id . toString ( ) ,
267+ reason : `Ride status: ${ route . status } ` ,
268+ } ,
269+ ) ,
270+ ) ,
271+ emitToast ( driverUser . _id . toString ( ) , {
272+ type : "error" ,
273+ message : "Your ride has been cancelled." ,
274+ } ) ,
275+ ] ) ;
229276 }
230277 }
231278
@@ -252,18 +299,26 @@ export async function startRoute(routeId: string) {
252299 }
253300 ) . settings ?. notifications ?. driverEnRoute
254301 ) {
255- await EmailNotifications . driverEnRoute (
256- studentUser . email ,
257- studentUser . preferredName ??
258- `${ studentUser . firstName } ${ studentUser . lastName } ` ,
259- {
260- name : route . driver
261- ? `${ route . driver . firstName } ${ route . driver . lastName } `
262- : "Driver" ,
263- eta : formatEstTime ( route . scheduledPickupTime ) ,
264- vehicle : route . vehicle ?. licensePlate ?? "Assigned vehicle" ,
265- } ,
266- ) ;
302+ dispatchNotifications ( [
303+ sendEmailSafely ( "driverEnRoute" , ( ) =>
304+ EmailNotifications . driverEnRoute (
305+ studentUser . email ,
306+ studentUser . preferredName ??
307+ `${ studentUser . firstName } ${ studentUser . lastName } ` ,
308+ {
309+ name : route . driver
310+ ? `${ route . driver . firstName } ${ route . driver . lastName } `
311+ : "Driver" ,
312+ eta : formatEstTime ( route . scheduledPickupTime ) ,
313+ vehicle : route . vehicle ?. licensePlate ?? "Assigned vehicle" ,
314+ } ,
315+ ) ,
316+ ) ,
317+ emitToast ( studentUser . _id . toString ( ) , {
318+ type : "info" ,
319+ message : "Your driver is on the way!" ,
320+ } ) ,
321+ ] ) ;
267322 }
268323
269324 return route . toObject ( ) ;
@@ -377,32 +432,48 @@ export async function scheduleRoute(
377432 }
378433 ) . settings ?. notifications ?. driverAssigned
379434 ) {
380- await EmailNotifications . driverAssigned (
381- studentUser . email ,
382- studentUser . preferredName ??
383- `${ studentUser . firstName } ${ studentUser . lastName } ` ,
384- {
385- name : `${ driver . firstName } ${ driver . lastName } ` ,
386- vehicle : vehicle . licensePlate ,
387- } ,
388- ) ;
435+ dispatchNotifications ( [
436+ sendEmailSafely ( "driverAssigned" , ( ) =>
437+ EmailNotifications . driverAssigned (
438+ studentUser . email ,
439+ studentUser . preferredName ??
440+ `${ studentUser . firstName } ${ studentUser . lastName } ` ,
441+ {
442+ name : `${ driver . firstName } ${ driver . lastName } ` ,
443+ vehicle : vehicle . licensePlate ,
444+ } ,
445+ ) ,
446+ ) ,
447+ emitToast ( studentUser . _id . toString ( ) , {
448+ type : "success" ,
449+ message : `Your driver ${ driver . firstName } ${ driver . lastName } has been assigned.` ,
450+ } ) ,
451+ ] ) ;
389452 }
390453
391454 if (
392455 driver &&
393456 ( driver as { settings ?: { notifications ?: { rideAssigned ?: boolean } } } )
394457 . settings ?. notifications ?. rideAssigned
395458 ) {
396- await EmailNotifications . rideAssigned (
397- driver . email ,
398- driver . preferredName ?? `${ driver . firstName } ${ driver . lastName } ` ,
399- {
400- rideId : route . _id . toString ( ) ,
401- pickup : route . pickupLocation . toString ( ) ,
402- dropoff : route . dropoffLocation . toString ( ) ,
403- time : `${ formatEstDate ( route . scheduledPickupTime ) } ${ formatEstTime ( route . scheduledPickupTime ) } ` ,
404- } ,
405- ) ;
459+ dispatchNotifications ( [
460+ sendEmailSafely ( "rideAssigned" , ( ) =>
461+ EmailNotifications . rideAssigned (
462+ driver . email ,
463+ driver . preferredName ?? `${ driver . firstName } ${ driver . lastName } ` ,
464+ {
465+ rideId : route . _id . toString ( ) ,
466+ pickup : route . pickupLocation . toString ( ) ,
467+ dropoff : route . dropoffLocation . toString ( ) ,
468+ time : `${ formatEstDate ( route . scheduledPickupTime ) } ${ formatEstTime ( route . scheduledPickupTime ) } ` ,
469+ } ,
470+ ) ,
471+ ) ,
472+ emitToast ( driver . _id . toString ( ) , {
473+ type : "info" ,
474+ message : "You have been assigned a new ride." ,
475+ } ) ,
476+ ] ) ;
406477 }
407478
408479 // Upsert (not create) so this is safe even if a chat record already
0 commit comments