@@ -483,7 +483,62 @@ async def cancel_match_by_participant(
483483
484484 self ._set_match_status (match , "cancelled_by_participant" )
485485
486- # TODO: send particpant an email saying that the match has been cancelled
486+ # Send cancellation email to volunteer before deleting match
487+ try :
488+ # Load volunteer with their data before deleting match
489+ volunteer = (
490+ self .db .query (User )
491+ .options (joinedload (User .user_data ))
492+ .filter (User .id == match .volunteer_id )
493+ .first ()
494+ )
495+ participant = (
496+ self .db .query (User )
497+ .options (joinedload (User .user_data ))
498+ .filter (User .id == match .participant_id )
499+ .first ()
500+ )
501+
502+ if volunteer and participant and match .confirmed_time :
503+ ses_service = SESEmailService ()
504+ confirmed_time_utc = match .confirmed_time .start_time
505+
506+ # Get volunteer's timezone and language
507+ volunteer_tz = ZoneInfo ("America/Toronto" ) # Default to EST
508+ if volunteer .user_data and volunteer .user_data .timezone :
509+ tz_result = get_timezone_from_abbreviation (volunteer .user_data .timezone )
510+ if tz_result :
511+ volunteer_tz = tz_result
512+
513+ volunteer_language = volunteer .language .value if volunteer .language else "en"
514+
515+ # Convert time to volunteer's timezone
516+ volunteer_time = confirmed_time_utc .astimezone (volunteer_tz )
517+ volunteer_date = volunteer_time .strftime ("%B %d, %Y" )
518+ volunteer_time_str = volunteer_time .strftime ("%I:%M %p" )
519+ volunteer_tz_abbr = volunteer_time .strftime ("%Z" )
520+
521+ # Send to volunteer
522+ if volunteer .email :
523+ participant_name = (
524+ f"{ participant .first_name } { participant .last_name } "
525+ if participant .first_name and participant .last_name
526+ else participant .first_name or "The participant"
527+ )
528+ ses_service .send_participant_cancelled_email (
529+ to_email = volunteer .email ,
530+ participant_name = participant_name ,
531+ date = volunteer_date ,
532+ time = volunteer_time_str ,
533+ timezone = volunteer_tz_abbr ,
534+ first_name = volunteer .first_name ,
535+ dashboard_url = "http://localhost:3000/volunteer/dashboard" ,
536+ language = volunteer_language ,
537+ )
538+ except Exception as e :
539+ # Log error but don't fail the cancellation
540+ self .logger .error (f"Failed to send participant cancelled email for match { match_id } : { e } " )
541+
487542 # Soft-delete the match when cancelled (cleans up time blocks and sets deleted_at)
488543 self ._delete_match (match )
489544
@@ -519,6 +574,62 @@ async def cancel_match_by_volunteer(
519574 if acting_volunteer_id and match .volunteer_id != acting_volunteer_id :
520575 raise HTTPException (status_code = 403 , detail = "Cannot modify another volunteer's match" )
521576
577+ # Send cancellation email to participant before clearing confirmed time
578+ try :
579+ # Load participant and volunteer with their data before clearing time
580+ participant = (
581+ self .db .query (User )
582+ .options (joinedload (User .user_data ))
583+ .filter (User .id == match .participant_id )
584+ .first ()
585+ )
586+ volunteer = (
587+ self .db .query (User )
588+ .options (joinedload (User .user_data ))
589+ .filter (User .id == match .volunteer_id )
590+ .first ()
591+ )
592+
593+ if participant and volunteer and match .confirmed_time :
594+ ses_service = SESEmailService ()
595+ confirmed_time_utc = match .confirmed_time .start_time
596+
597+ # Get participant's timezone and language
598+ participant_tz = ZoneInfo ("America/Toronto" ) # Default to EST
599+ if participant .user_data and participant .user_data .timezone :
600+ tz_result = get_timezone_from_abbreviation (participant .user_data .timezone )
601+ if tz_result :
602+ participant_tz = tz_result
603+
604+ participant_language = participant .language .value if participant .language else "en"
605+
606+ # Convert time to participant's timezone
607+ participant_time = confirmed_time_utc .astimezone (participant_tz )
608+ participant_date = participant_time .strftime ("%B %d, %Y" )
609+ participant_time_str = participant_time .strftime ("%I:%M %p" )
610+ participant_tz_abbr = participant_time .strftime ("%Z" )
611+
612+ # Send to participant
613+ if participant .email :
614+ volunteer_name = (
615+ f"{ volunteer .first_name } { volunteer .last_name } "
616+ if volunteer .first_name and volunteer .last_name
617+ else volunteer .first_name or "Your volunteer"
618+ )
619+ ses_service .send_volunteer_cancelled_email (
620+ to_email = participant .email ,
621+ volunteer_name = volunteer_name ,
622+ date = participant_date ,
623+ time = participant_time_str ,
624+ timezone = participant_tz_abbr ,
625+ first_name = participant .first_name ,
626+ request_matches_url = "http://localhost:3000/participant/dashboard" ,
627+ language = participant_language ,
628+ )
629+ except Exception as e :
630+ # Log error but don't fail the cancellation
631+ self .logger .error (f"Failed to send volunteer cancelled email for match { match_id } : { e } " )
632+
522633 self ._clear_confirmed_time (match )
523634 self ._set_match_status (match , "cancelled_by_volunteer" )
524635
0 commit comments