Skip to content

feat(api): status-driven registration emails (receipt + waiting list)#1656

Merged
losolio merged 1 commit into
mainfrom
feat/registration-notifications
Jul 1, 2026
Merged

feat(api): status-driven registration emails (receipt + waiting list)#1656
losolio merged 1 commit into
mainfrom
feat/registration-notifications

Conversation

@losolio

@losolio losolio commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Why

Reported bug: an admin putting a participant on the waiting list still sent a "you're registered" confirmation. Root cause — the email was a single per-event "welcome letter" fired on create, gated only by the event's status, not the registration's actual status.

What

Registration emails are now status-driven — the email is chosen by the registration's status and sent on status transitions (create + status change), idempotent (only on an actual change):

Status Email
Verified Receipt — a copy of the registration and its orders
WaitingList "You're on the waiting list"
other none

So placing someone on the waiting list now sends the waiting-list email, not a confirmation.

Rendered with the existing DocComposer (Fluid/Liquid) engine used by certificate emails — HTML only, no PDF. Templates are neutral in tone ("Påmelding", not over-confirming), omit the "Eventuras" signature, and mention that you can log in to see up-to-date info (no links).

Changes

  • DocComposer: register nested model types recursively so templates can render structured data (order lines). Additive — certificates unaffected.
  • RegistrationEmailRenderer + models + embedded .liquid templates (nb/en, fallback to nb): registration-receipt, registration-waitlist.
  • RegistrationNotificationService (status → email) + DI; wired into CreateRegistrationAsync and UpdateRegistrationAsync (on status change).
  • Removed the internal SendWelcomeLetter option and the EventInfo.WelcomeLetter auto-send. NewRegistrationDto.SendWelcomeLetter is kept as an [Obsolete] no-op (logs a warning if a client still sends false); to be removed in the next API version.
  • Admin-adding a participant to a waiting-list event already defaults the registration to WaitingList (so a single waiting-list email; no double send).

Tests

dotnet build clean, 67/67 in Eventuras.Services.Tests:

  • RegistrationEmailRendererTest — receipt renders subject/body/nested order lines, waiting-list message, locale fallback.
  • RegistrationNotificationServiceTest — WaitingList → waiting-list email (not confirmation), Verified → receipt, no-op on unchanged/non-notifiable status.
  • CreateRegistrationAsync_SetsWaitingList_WhenEventIsWaitingList.

Deferred

The one remaining double-email case: an event not on the waiting list where an admin manually waitlists a single participant via the status dropdown (receipt at create + waiting-list at patch). The clean fix is event-driven notifications / an atomic "add to waiting list" — intentionally out of scope here (see docs/spec/registration-notifications.md).

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 1, 2026 22:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements status-driven registration emails in the API so that participants receive the correct message based on the registration’s actual status (e.g., WaitingList vs Verified), addressing the bug where admin waitlisting still triggered a “you’re registered” confirmation.

Changes:

  • Introduces RegistrationNotificationService + RegistrationEmailRenderer to render and send receipt (Verified) and waiting-list (WaitingList) emails using the existing DocComposer (Fluid/Liquid) pipeline.
  • Wires notifications into CreateRegistrationAsync (create) and UpdateRegistrationAsync (status transitions), and removes the old welcome-letter auto-send path and RegistrationOptions.SendWelcomeLetter.
  • Adds embedded Liquid templates (nb/en), expands DocComposer model-type registration for nested models, and adds targeted unit tests.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
docs/spec/registration-notifications.md Design/spec documenting the new status-driven notification behavior and rationale.
apps/api/tests/Eventuras.Services.Tests/Registrations/RegistrationNotificationServiceTest.cs Tests for transition-to-email mapping and idempotency behavior.
apps/api/tests/Eventuras.Services.Tests/Registrations/RegistrationManagementServiceTests.cs Updates tests for removed welcome-letter option and adds waiting-list defaulting test.
apps/api/tests/Eventuras.Services.Tests/Registrations/RegistrationEmailRendererTest.cs Tests template rendering (receipt/waitlist), nested line rendering, and locale fallback.
apps/api/src/Eventuras.WebApi/Controllers/v3/Registrations/RegistrationsController.cs Logs deprecated sendWelcomeLetter=false usage and stops passing it into service options.
apps/api/src/Eventuras.WebApi/Controllers/v3/Registrations/NewRegistrationDto.cs Deprecates SendWelcomeLetter field as ignored for backwards compatibility.
apps/api/src/Eventuras.Services/Registrations/Templates/registration-waitlist.nb.liquid New Bokmål waiting-list HTML email template.
apps/api/src/Eventuras.Services/Registrations/Templates/registration-waitlist.en.liquid New English waiting-list HTML email template.
apps/api/src/Eventuras.Services/Registrations/Templates/registration-receipt.nb.liquid New Bokmål receipt HTML email template (includes orders).
apps/api/src/Eventuras.Services/Registrations/Templates/registration-receipt.en.liquid New English receipt HTML email template (includes orders).
apps/api/src/Eventuras.Services/Registrations/RegistrationServiceCollectionExtensions.cs Registers renderer + notification service in DI.
apps/api/src/Eventuras.Services/Registrations/RegistrationOptions.cs Removes SendWelcomeLetter option from internal service options.
apps/api/src/Eventuras.Services/Registrations/RegistrationManagementService.cs Replaces welcome-letter send with status-driven notifications on create/update.
apps/api/src/Eventuras.Services/Registrations/Notifications/RegistrationNotificationService.cs New status-transition email sender assembling receipt/waitlist models and sending via IEmailSender.
apps/api/src/Eventuras.Services/Registrations/Notifications/RegistrationEmailRenderer.cs New renderer using DocComposer to produce subject + HTML/text body from embedded templates.
apps/api/src/Eventuras.Services/Registrations/Notifications/RegistrationEmailModels.cs Email model records used as template input (including nested order lines).
apps/api/src/Eventuras.Services/Eventuras.Services.csproj Embeds the Liquid templates and adds DocComposer project reference.
apps/api/src/Eventuras.Libs.DocComposer/FluidDocumentComposer.cs Registers nested model types recursively to allow template access to nested structures.
.changeset/registration-status-emails.md Changeset documenting the API behavior change and deprecation.

Comment on lines +36 to +46
private static string NormalizeLocale(string? locale)
{
var lang = (locale ?? DefaultLocale).Split('-')[0].ToLowerInvariant();
return lang switch
{
"nb" or "no" => "nb",
"nn" => "nn",
"en" => "en",
_ => DefaultLocale
};
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call — fixed. NormalizeLocale now passes unknown languages through (only mapping nonb), so the composer tries that locale's template and falls back to nb if missing. Matches the certificate renderer and lets future *.sv.liquid etc. be picked up.

services.AddTransient<IRegistrationAccessControlService, RegistrationAccessControlService>();
services.AddTransient<IRegistrationManagementService, RegistrationManagementService>();

// Email renderer is stateless and caches parsed templates -> singleton.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct — FluidDocumentComposer re-parses per call; it only caches registered model types. Fixed the comment (singleton still reuses the composer + its registered-type cache). A real parsed-template cache can come later if it matters.

@losolio losolio force-pushed the feat/registration-notifications branch from 6f684ae to 4259874 Compare July 1, 2026 22:13
Replace the single "welcome letter" with an email chosen by the registration's
status, rendered from Liquid templates via the existing DocComposer engine
(HTML, no PDF):
- Verified    -> receipt with a copy of the registration and its orders
- WaitingList -> "you're on the waiting list" email
- other statuses -> no email

Sent on status transitions (create + status change) and idempotent (only on an
actual change), so putting a participant on the waiting list sends the
waiting-list email instead of a confirmation (fixes the reported bug).

- DocComposer: register nested model types recursively so templates can render
  structured data (order lines).
- RegistrationEmailRenderer + models + nb/en templates (fallback to nb).
- RegistrationNotificationService (status -> email) + DI.
- Remove the internal SendWelcomeLetter option and the EventInfo.WelcomeLetter
  auto-send. NewRegistrationDto.SendWelcomeLetter is kept as an [Obsolete] no-op
  (logs a warning when a client still sends it); to be removed in the next API version.
- Tests: renderer, notification service (incl. waiting-list regression) and
  admin-add on a waiting-list event defaults to WaitingList.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Copilot AI review requested due to automatic review settings July 1, 2026 22:17
@losolio losolio force-pushed the feat/registration-notifications branch from 4259874 to eb5f6c2 Compare July 1, 2026 22:17
@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Comment on lines +46 to +48
- Templates as embedded resources per locale (`nb`, `nn`, `en`):
- `registration-receipt.<locale>.liquid`
- `registration-waitlist.<locale>.liquid`
@losolio losolio added this pull request to the merge queue Jul 1, 2026
Merged via the queue into main with commit bc521c0 Jul 1, 2026
24 checks passed
@losolio losolio deleted the feat/registration-notifications branch July 1, 2026 22:40
@github-project-automation github-project-automation Bot moved this from 🆕 New to ✅ Done in Eventuras backlog Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants