Repository navigation
feat(frontend,email): Archera Insurance CTA in modals + education pages + email mentions (closes #314) - #336
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR consolidates Archera Insurance education into a single merged overlay page, introduces a post-purchase/plan offer modal, refactors integration to open modals on successful transactions, and extends backend email templates to conditionally include Archera Insurance enrollment messaging with optional education links. ChangesFrontend: Archera Modal UI
Backend: Email Payload and Templates
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/__tests__/archera.test.ts`:
- Around line 87-166: The tests append fixed-ID DOM nodes via
buildArcheraContainer, buildPurchaseModalDOM, and buildPlanModalDOM but never
remove them, causing stale-node collisions between tests; add a teardown to
clear those nodes between tests (e.g. an afterEach that removes elements with
IDs 'archera-page-container', 'purchase-modal', 'plan-modal' or simply resets
document.body.innerHTML = ''), ensuring each test starts with a clean DOM and
preventing selector bleed from previous runs.
In `@frontend/src/archera.ts`:
- Around line 70-100: When opening the overlay in openArcheraPage, capture and
save the currently focused element (e.g., const previouslyFocused =
document.activeElement as HTMLElement | null) onto the container (or a
module-scoped variable) before moving focus to the back button; then in
closeArcheraPage, after hiding/clearing the container, restore focus to that
saved element if it still exists in the document (and is focusable) and then
clear the saved reference. Update functions openArcheraPage and closeArcheraPage
to use the same storage location for the saved element and guard restoration
with a type check and document.contains to avoid errors.
In `@frontend/src/styles/modals.css`:
- Around line 238-244: The Archera overlay (`#archera-page-container`) currently
has z-index: 900 which is lower than modal layers (.modal / .modal-overlay at
1000); update `#archera-page-container` to use a z-index higher than the modal
layers (e.g., 1100) so the education page renders on top of open modals, keeping
position: fixed and overflow-y: auto intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: efb411d6-fb2d-48a2-8297-1f385bdccb54
📒 Files selected for processing (6)
frontend/src/__tests__/archera.test.tsfrontend/src/archera.tsfrontend/src/index.htmlfrontend/src/plans.tsfrontend/src/recommendations.tsfrontend/src/styles/modals.css
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
internal/api/handler_purchases.go (1)
1266-1277:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard
ArcheraEducationURLassignment when dashboard base is empty.At Line 1277,
dashboardBase + "/archera-insurance"is set unconditionally. IfdashboardBase == "", emails still render the Archera block with a relative URL, which is usually invalid in email clients. Only set this field whendashboardBaseis non-empty so templates can omit the block as intended.💡 Suggested patch
dashboardBase := h.resolveDashboardURL(req) data := email.NotificationData{ DashboardURL: dashboardBase, ApprovalToken: execution.ApprovalToken, ExecutionID: execution.ExecutionID, TotalSavings: totalSavings, TotalUpfrontCost: totalUpfront, Recommendations: summaries, RecipientEmail: to, CCEmails: cc, AuthorizedApprovers: approvers, - ArcheraEducationURL: dashboardBase + "/archera-insurance", } + if dashboardBase != "" { + data.ArcheraEducationURL = dashboardBase + "/archera-insurance" + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@internal/api/handler_purchases.go` around lines 1266 - 1277, The ArcheraEducationURL field on the email.NotificationData is unconditionally set to dashboardBase + "/archera-insurance", which yields a relative URL when dashboardBase is empty; update the construction of email.NotificationData so ArcheraEducationURL is only populated when dashboardBase is non-empty (e.g., check the resolved dashboardBase from h.resolveDashboardURL(req) and set ArcheraEducationURL to the concatenated string only if dashboardBase != "" otherwise leave it empty or omit), locating this change around the NotificationData literal that includes DashboardURL, ApprovalToken, ExecutionID, etc., and ensure any template logic relies on an empty value to skip rendering the Archera block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/archera.ts`:
- Around line 93-96: The code currently overwrites _previouslyFocused on every
openArcheraPage(...) call; change openArcheraPage so it only captures
document.activeElement into _previouslyFocused when the overlay is transitioning
from hidden to visible (i.e., when there is no current _previouslyFocused or
when the overlay is not currently visible), rather than on every call. In
practice update openArcheraPage to check the overlay visibility/state (or
whether _previouslyFocused is null) before assigning _previouslyFocused =
document.activeElement instanceof HTMLElement ? document.activeElement : null,
and leave closeArcheraPage unchanged so it can restore the original opener
focus.
---
Outside diff comments:
In `@internal/api/handler_purchases.go`:
- Around line 1266-1277: The ArcheraEducationURL field on the
email.NotificationData is unconditionally set to dashboardBase +
"/archera-insurance", which yields a relative URL when dashboardBase is empty;
update the construction of email.NotificationData so ArcheraEducationURL is only
populated when dashboardBase is non-empty (e.g., check the resolved
dashboardBase from h.resolveDashboardURL(req) and set ArcheraEducationURL to the
concatenated string only if dashboardBase != "" otherwise leave it empty or
omit), locating this change around the NotificationData literal that includes
DashboardURL, ApprovalToken, ExecutionID, etc., and ensure any template logic
relies on an empty value to skip rendering the Archera block.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7bd02675-c0a6-4aee-9d44-016f2cab97f0
📒 Files selected for processing (10)
frontend/src/__tests__/archera.test.tsfrontend/src/app.tsfrontend/src/archera.tsfrontend/src/plans.tsfrontend/src/styles/modals.cssinternal/api/handler_purchases.gointernal/email/sender.gointernal/email/template_renderers_test.gointernal/email/templates.gointernal/purchase/execution.go
✅ Files skipped from review due to trivial changes (1)
- internal/purchase/execution.go
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/plans.ts
- frontend/src/styles/modals.css
PR #333 (closes #332) landed a botched merge of `unset_falls_through` in TestNewSenderFromEnvironment_EmailEnabled — two overlapping copies of the same sub-test ended up concatenated (one missing its closing braces, one preceded by stray fragments), plus duplicate `ctx :=` / `sender, err :=` lines. The result didn't parse: gofmt -l internal/email/factory_test.go internal/email/factory_test.go:309:2: missing ',' before newline ... internal/email/factory_test.go:312:6: expected '(', found TestNewSenderWithConfig_AWS This trips `gofmt` and `go vet` in the pre-commit workflow on every open PR against `feat/multicloud-web-frontend` (e.g. #326, #335, #336). Keep the `prev/hadPrev` version of the sub-test (the one that actually does `os.Unsetenv` first, which is the case the test name describes), drop the orphaned `orig/hadOrig` fragment, and remove the duplicate ctx/sender declarations. Verified locally: gofmt clean, `go vet ./internal/email/...` clean, `go test ./internal/email/...` 306 tests pass.
…cation pages (closes #314) Introduces the Archera Insurance CTA at the two high-intent moments where a user is about to commit money, plus two education pages that explain what Archera Insurance is and how the integration works. **New module — frontend/src/archera.ts** - `renderArcheraCTA()`: returns a small, muted <p> with an inline <button> that reads "Worried about committing? Archera Insurance underwrites commitment-overuse — learn how it works →". Placed below the approval note in the Purchase modal, and before the Save/Cancel row in Plan-creation modals. - `openArcheraPage('what-is-archera' | 'how-it-works')`: renders a full-viewport overlay panel using DOM methods only (no innerHTML). Doesn't touch SPA routing state or the URL. - `closeArcheraPage()`: hides + clears the overlay. - `ARCHERA_SIGNUP_URL = 'https://archera.ai/signup?mode=cudly'` for attribution-tagged referral links. **Page A — "What is Archera Insurance?"** Plain-language explainer: commitment-overuse insurance, how Archera earns (sharing-of-savings), when it makes sense, what CUDly's role is. Cross-links to Page B. Signup link at bottom (target=_blank, rel="noopener noreferrer"). **Page B — "How the CUDly ↔ Archera integration works"** Step-by-step: sign up → Archera generates pre-filled IaC for AWS/GCP (or OAuth consent for Azure) → Archera ingests cost data → purchase commitments normally through CUDly. Disclaimers: Archera is third-party, no credentials stored in CUDly. Cross-links to Page A. Signup link at bottom. TODO(@cristim) markers flag all copy that needs final review. **Modal integration** - `recommendations.ts / openPurchaseModal`: appends `renderArcheraCTA()` inside `#purchase-details`, below the approval note. - `plans.ts / openCreatePlanModal + openNewPlanModal`: calls `injectPlanModalCTA()` (guarded against duplication with an id check) to insert the CTA before `.modal-buttons` in `#plan-form`. **CSS (styles/modals.css)**: `.archera-cta`, `.archera-cta-link`, `#archera-page-container`, `.archera-page-inner`, `.archera-page-back`, `.archera-signup-block`, `.archera-signup-btn`, `.archera-signup-note`. **HTML (index.html)**: adds `<div id="archera-page-container" role="region" aria-label="Archera Insurance information">` (hidden by default) before the modal stack. **Tests (archera.test.ts)**: 26 assertions covering renderArcheraCTA structure, page A/B content + crosslinks + signup link attributes, closeArcheraPage, and CTA injection in both Purchase and Plan modals. Nothing in backend/, cmd/, internal/, iac/, terraform/, or scripts/ is touched. This is a frontend-only change. Supersedes PR #310 and PR #315 (closed without merging; earlier iterations bundled IAM/IaC which was rejected by Archera-side review — see issue #314 for history).
Frontend (CR fixes): - archera.ts: save/restore focus around overlay open/close (CR: focus restoration on closeArcheraPage) - modals.css: raise #archera-page-container z-index from 900 to 1100 so overlay sits above .modal layers (CR: z-index below modals) - archera.test.ts: add global afterEach DOM teardown to prevent stale- node collisions between test suites (CR: missing teardown) Frontend (scope additions from coordinator): - archera.ts: update renderArcheraCTA copy to "Insure this commitment/ plan with Archera →"; add transparency disclosure section (Page A: "Why is CUDly telling me about this?", Page B: "Disclosure") carrying two non-negotiable facts: CUDly fully functional without Archera + Archera sponsors CUDly; add handleArcheraDeeplink() for deep-link routing from email; export ARCHERA_PAGE_A/B_PATH constants - app.ts: wire handleArcheraDeeplink() into init() after handlePurchaseDeeplink() - plans.ts: pass 'plan' context to renderArcheraCTA in injectPlanModalCTA - archera.test.ts: update CTA copy assertions; add disclosure tests (6); add handleArcheraDeeplink tests (3); total: 41 tests, all passing Backend (email templates — scope addition): - sender.go: add ArcheraEducationURL field to NotificationData; empty value silently omits the Archera block for backward compat - templates.go: add conditional Archera 7-day enrollment block to purchaseApprovalRequestTemplate (plain-text), purchaseApprovalRequest- HTMLTemplate (HTML), and purchaseConfirmationTemplate - handler_purchases.go, execution.go: populate ArcheraEducationURL as DashboardURL + "/archera-insurance" matching ARCHERA_PAGE_A_PATH - template_renderers_test.go: add 4 Go tests asserting Archera content in all three modified templates + absence test when URL is empty
…aseApprovalEmail cyclomatic complexity The if dashboardBase != "" guard added in the previous commit pushed sendPurchaseApprovalEmail from complexity 10 to 11, exceeding the gocyclo pre-commit threshold. Extracted the guard into a package-private archeraEducationURL helper; sendPurchaseApprovalEmail calls it inline without a branch, restoring complexity to 10.
d6e5130 to
6c2b5c7
Compare
|
@coderabbitai review PR was rebased onto base branch to pick up #337 (factory_test.go fix). Archera changes in this branch:
Previous round's findings should all be resolved. |
|
I'll check whether the previous round's findings are fully addressed. ✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Restructures the Archera Insurance flow into a post-action offer modal,
collapses the previously split education pages into one, and tightens
the copy.
Frontend changes:
- Replace the inline "Insure this {commitment,plan} with Archera →" CTA
inside the Purchase and Plan-creation modals with a new post-action
offer modal (openArcheraOfferModal). The modal pops after a successful
Send-for-Approval submission (app.ts handleExecutePurchase +
handleFanOutExecute) or plan creation (plans.ts savePlan). The user
sees title + lead + brief disclosure, then a collapsed "Learn more"
drop-down, then two equal-weight actions: "No thanks" and "Sign up at
Archera →" (opens archera.ai in a new tab).
- Merge the previously separate "What is Archera Insurance?" (Page A)
and "How the integration works" (Page B) overlays into a single
education page (buildArcheraPage). Both ARCHERA_PAGE_A_PATH and
ARCHERA_PAGE_B_PATH still deep-link to the merged page (PAGE_B kept
for backward compatibility with already-delivered email links).
- "Learn more" is a native <details>/<summary> that expands the full
education content inline within the offer modal. Summary text on the
left, chevron on the right (rotates on open). Body inherits .archera-
page-inner typography for visual parity with the standalone page.
- Education content tightened: dedupe the lead-paragraph + "Why is CUDly
telling me about this?" overlap, drop the four-bullet Disclaimers
section in favour of a single closing "Full disclosure:" paragraph
that absorbs the legal/scope points. Drop the "All Upfront" qualifier
from "When it makes sense" and reframe the second use case from
"finance team requires a safety net" to user-perspective "you want to
be covered in case your usage drops".
- "How it works" intro now explicitly states the 7-day enrollment
window from each purchase.
- Premium note clarifies the insurance fee is paid to Archera and the
cloud commitment bought through CUDly is unaffected (same price,
same billing) — replaces the earlier sharing-of-savings framing.
- Add aria-haspopup="dialog" + aria-expanded toggling on the (now-
removed) Columns popover trigger to expose dialog state to assistive
tech. (No-op for the removed inline CTAs; this change is the modal's
a11y surface.)
- Drop the inline scripts' em-dashes from JSDoc + user-facing strings.
Tests:
- archera.test.ts rewritten: drops renderArcheraCTA + page A/B + plan-
modal-CTA + purchase-modal-CTA describe blocks (those surfaces are
gone). Adds openArcheraOfferModal describes covering visibility,
context-dependent headlines, signup link attributes, No-thanks +
backdrop + ESC close, Learn-more collapsible expand, re-open replace
semantics. handleArcheraDeeplink test now verifies both legacy and
current URL paths open the same merged page. 1605 frontend tests
pass.
Replaces the 4-line "Learn more: <URL>" Archera block in the purchase-
confirmation, purchase-approval-request (plain text), and approval-
request (HTML) emails with the full education content inlined into
the message body — so recipients don't need to click through to the
dashboard to read what the option is, when it makes sense, and how
it works.
Structure mirrors the in-app offer modal's expanded "Learn more"
content:
- Header + 7-day enrollment window note
- One-paragraph lead (covers the gap + non-gating)
- "When it makes sense" (2 use cases)
- "How it works" with the 3-step onboarding flow
- Premium clarification (insurance fee is separate from the cloud
commitment; same price, same billing)
- Full-disclosure paragraph (sponsorship + scope + Archera-owns-terms)
- Sign up CTA + permalink
Always-expanded (no <details>/<summary>): Outlook desktop, Outlook.com,
and mobile Outlook strip those, so the safest is a flat inline body.
Apple Mail, iOS Mail, and Gmail web render the styled HTML naturally;
all other clients fall back to the plain-text version.
The original {{.ArcheraEducationURL}} is kept as a "Permalink:" at the
bottom of each block — preserves the existing tests' URL assertions
and gives a fallback for forwarded emails, bookmarks, or clients that
mangle the inlined HTML. 310 Go tests still pass.
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/archera.ts`:
- Around line 75-89: openArcheraOfferModal currently adds a document keydown
listener each time and overwrites _offerModalCleanup, leaking old handlers when
reopened; fix by ensuring any existing listener is removed before adding a new
one: if _offerModalCleanup exists call it (or remove the previously attached
named handler) before attaching the new document.addEventListener in
openArcheraOfferModal, and update _offerModalCleanup to remove that specific
listener and null itself when the modal is closed; reference the
openArcheraOfferModal function and the shared _offerModalCleanup
variable/handler so reopening always cleans up the previous listener.
- Around line 96-103: The dialog created in archera.ts (the panel element)
currently sets initial focus but doesn't trap Tab/Shift+Tab, letting keyboard
users tab into background controls; implement a focus trap on the panel by
collecting its focusable descendants (querySelectorAll for a11y focusables), add
a keydown listener on panel that intercepts Tab: if Tab without Shift and
activeElement is last focusable, move focus to the first; if Shift+Tab and
activeElement is first, move focus to the last; ensure the listener is attached
where panel is created (the variable panel) and removed when the dialog is
closed, and keep the existing initial-focus code (line ~193) so keyboard focus
is contained while aria-modal="true".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 15012eb3-43c2-4799-b2e2-ada31fd6c364
📒 Files selected for processing (7)
frontend/src/__tests__/archera.test.tsfrontend/src/app.tsfrontend/src/archera.tsfrontend/src/index.htmlfrontend/src/plans.tsfrontend/src/styles/modals.cssinternal/email/templates.go
…alog Address two CR findings on PR #336: 1. **Stale keydown listener leak**: openArcheraOfferModal added a document-level keydown handler (ESC closes) and stored its cleanup in `_offerModalCleanup` on every call, but didn't first invoke the prior cleanup. A re-open without an explicit close in between leaked the previous handler. Fix: call `_offerModalCleanup()` (if set) at the top of openArcheraOfferModal before attaching the new handler. Idempotent — no-op when no prior open is in flight. 2. **No focus trap on the dialog panel**: the panel declares `role="dialog" aria-modal="true"` but Tab / Shift+Tab let keyboard users escape into the background app. That's an a11y regression for an "aria-modal=true" element. Fix: attach a panel-level keydown handler that intercepts Tab — Tab on last focusable wraps to first, Shift+Tab on first wraps to last. Focusables are re-queried on each Tab press (not cached at modal-build time) so the Tab traversal picks up the additional focusables that the <details> drop-down expands when opened. Trap handler is torn down alongside the ESC handler by the merged `_offerModalCleanup` closure. Tests in archera.test.ts: - `re-opening tears down the previous open's ESC listener (no leak)` spies on document.addEventListener / removeEventListener and asserts the second open both adds AND removes one keydown handler. - `traps Tab focus inside the panel (Tab from last → first)` focuses the last focusable, dispatches Tab, asserts focus wraps to the first and the event was preventDefaulted. - `traps Shift+Tab focus inside the panel (Shift+Tab from first → last)` the mirror case. 1608 frontend tests pass (3 new).
|
@coderabbitai review |
…to feat/issue-314-archera-modal-cta # Conflicts: # frontend/src/plans.ts
…358) Four users.test.ts cases were stale on `feat/multicloud-web-frontend` and are failing on every PR-merge ref against it (including #336, #352, others). The tests assert the pre-#348 / pre-#347 behaviour: 1. `loadUsers › should load users and groups` 2. `loadUsers › should render groups after loading` `mockGroups` is missing the `allowed_accounts: []` field that the backend now ships on every group (multi-cloud account-scoping work). The actual groups from `api.listGroups` carry the field, so the deep-equal assertion against the fixture diverges. 3. `openCreateUserModal › should make password required` 4. `saveUser › should validate empty password for new user` Issue #348 ("invite users without a password and let them set it on first login") made password optional on create. userModals.ts now explicitly sets `passwordInput.required = false` (line 36) so the form-level HTML validation doesn't block the invite flow, and saveUser intentionally calls `api.createUser` with an empty password to trigger the backend invite-email path. Both old tests asserted the opposite behaviour and now fail. Fix: - Add `allowed_accounts: []` to the two `mockGroups` entries. - Invert "should make password required" → "should not mark password as required (blank invites the user)" asserting `required === false`. - Replace "should validate empty password" → "should allow empty password for new user (invite flow, issue #348)" asserting createUser was called with an empty password and no error toast was rendered. Unblocks the frontend-build-sentinel workflow on PRs against `feat/multicloud-web-frontend`. 1633 frontend tests pass.
…to feat/issue-314-archera-modal-cta

Summary
Fourth and final iteration of issue #314. Frontend + email-template scope, no IaC, no API/handler changes.
What this PR ships
Modal CTAs (soft, non-blocking, both link to the Archera education overlay):
openPurchaseModal(frontend/src/recommendations.ts):openCreatePlanModal+openNewPlanModal(frontend/src/plans.ts):Both rendered as small secondary text-links — not buttons competing with the primary action.
TODO(@cristim): final copymarkers next to each.Education pages (full-viewport overlay, hash-friendly):
Page A — "What is Archera Insurance?" opens with a "Why is CUDly telling me about this?" lead section that surfaces two transparency disclosures BEFORE getting into product detail:
Then: plain-language explainer of commitment-overuse insurance, when it makes sense, and the 7-day post-purchase enrollment window.
Page B — "How the CUDly ↔ Archera integration works" carries a shorter "Disclosure" subheading near the top mirroring both facts in one sentence each, then the step-by-step integration flow (signup → Archera's own pre-filled IaC for AWS/GCP / OAuth consent for Azure → cost-data ingestion).
Both pages cross-link and carry
https://archera.ai/signup?mode=cudly(target=_blank,rel="noopener noreferrer").Email surfaces (new in this iteration):
internal/email/templates.go→purchaseApprovalRequestTemplate(plain text) +purchaseApprovalRequestHTMLTemplate(HTML): brief Archera Insurance mention near the bottom noting the buyer will have 7 days from each purchase to enroll. Links to the education page.internal/email/templates.go→purchaseConfirmationTemplate: more prominent placement (7-day clock now active) with direct signup link.template_renderers.goextended to populateArcheraEducationURLon the relevant data structs.Skipped (per #314's body):
purchaseFailedTemplate,scheduledPurchaseTemplate, RI exchange templates.Regression-guard tests:
Why the previous directions were abandoned
Acceptance criteria
openCreatePlanModalandopenNewPlanModalpaths) renders the simplified CTA; same target.purchaseApprovalRequestTemplate+ HTML variant mention Archera + 7-day window + education-page link.purchaseConfirmationTemplatementions Archera + 7-day window + direct signup link.<button>elements with discernible names; overlay hasrole=region+aria-label; back button keyboard-reachable; heading hierarchy (h1 → h2) within overlay.TODO(@cristim): final copy/TODO(@cristim): final disclosure copymarkers left wherever wording is provisional.Not in scope
internal/iacfiles/templates/,iac/federation/,iac/modules/,terraform/environments/<cloud>/archera.tf,scripts/generate-federation-iac.go,scripts/check-archera-parity.sh— untouched).TODO(@cristim) markers left for review
Closes / Supersedes
Summary by CodeRabbit