Booking email overrides now use ContentBlockEditor
Rewrote the per-event email override section in the Bookings admin page to use
the same ContentBlockEditor component used by site-level email templates
Removed the old markdown editor with override/revert buttons
Removed the disabled booking-default-preview showing the raw default text
Preview toggle button redesigned with sunrise colour scheme
ContentBlockEditor gained new inputs to support this reuse:
omitAllowed — controls whether the "Leave out" tab appears
stateQueryParamKey — persists the active tab choice in URL query params
frameless — removes the heading, description, and accordion wrapper for
an inline editing mode
blockDefaults — when a block is in DEFAULT state, renders the default
content in a disabled tiptap editor so the admin can see what would be sent
MailNotificationTemplateEditor now merges booking block defaults from
DEFAULT_BOOKING_EMAIL_BLOCKS into the discovered defaults so the disabled
preview works for booking email types
Booking merge fields preserved in markdown link URLs
booking-template-resolver.ts now uses renderMarkdownPreservingTokens instead of
renderMarkdownToHtml when resolving booking email body content (default, site
override, and per-event override)
Merge field placeholders like {{params.bookingMergeFields.EVENT_LINK}}
inside markdown link URLs are no longer percent-encoded during HTML rendering
messages.ts template substitution now runs 3 passes (was 1) to resolve
parameters that might contain other parameter references
Booking merge field values are now included in the test params for the
welcome-to-the-group template spec, confirming they resolve correctly
Booking reminder email uses correct NGX domain link
booking-reminder-job.ts passes null instead of event.groupEvent?.url for the
event link parameter, so publicEventLink() constructs the group's own domain
URL rather than the Ramblers national URL
Walk description diff in publish status
Added LCS-based word diff that compares the transformed website description
against the Ramblers-stored description in toPublishStatus()
Words only in the Ramblers version are shown with red strikethrough
Words only in the website version are shown in green
Output is trusted HTML rendered via [innerHTML] so coloured spans display
RamblersEventSummaryResponse interface and the list-events.ts mapper now
include the description field
CSV export contraction expansion and character cleanup
replaceSpecialCharacters now:
Decodes HTML entities ' / ' back to apostrophes
Expands 30+ common contractions via lookup map (don't -> do not, etc.)
Strips apostrophes on unrecognised words (possessive forms)
Converts & to and (avoids double-encoding by Ramblers CSV import)
walkLeader() no longer calls replaceSpecialCharacters so names like
"O'Grady" preserve their apostrophe in the CSV export
Styling: disabled tiptap editor state
Added .tiptap-editor-shell-disabled and child classes for a visually distinct
disabled appearance
Activated via [class.tiptap-editor-shell-disabled]="!editable" on the shell
Code quality
Removed unused BrevoButtonComponent imports from
mail-notification-template-editor.ts and notification-config-selector.ts
Added EMAIL_STATE to the StoredValue enum for content block editor state
persistence
Test changes
Added booking merge field fixture (ATTENDEE_NAME, EVENT_TITLE, EVENT_DATE,
EVENT_LINK, ATTENDEE_LIST, PLACES_COUNT) to messages.spec.ts params
Added spec confirming booking merge fields in markdown link URLs are
preserved during template rendering
Updated resolveBookingBody spec to expect HTML rendering and preserved
merge field hrefs instead of raw DEFAULT_BOOKING_EMAIL_BLOCKS value
template-diff.ts returns booking block defaults as raw trimmed markdown
(not HTML)
Ramblers Walks Manager E2E
DRY: combined disableCookieBannerPermanently, forceDismissCookieBanners,
and cookieBannerIfVisible into a single dismissCookieBanners() method
Updated all call sites (ClickWhenReady, Login, Start, UploadWalks) to use
the combined method
Further fixes
Section-toggle tabs and Preview button now sit on the same row (removed
flex-wrap from the container)
Preview now refreshes when switching email type tabs while preview is
active (made onEmailTypeTabChange async with await Promise.resolve()
so Angular change detection settles the view before calling preview)
Batch-loaded events in loadEvents() using a single all() call with the
ids parameter instead of N+1 individual queryById() calls — cuts
group-event/all requests from N (one per unique event referenced in
bookings) to 1