feat(frontend,email): Archera Insurance CTA in modals + education pages + email mentions (closes #314)#336
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (12)
✅ Files skipped from review due to trivial changes (4)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughAdded a complete Archera Insurance integration: a frontend module rendering CTAs and a two-page education overlay, injected into purchase and plan modals, wired into app deeplinks, with HTML/CSS, backend NotificationData/template additions, and frontend + backend tests. ChangesArchera Insurance Education Overlay Integration
Sequence DiagramsequenceDiagram
participant User
participant Modal as Purchase/Plan Modal
participant Archera as Archera Module
participant Overlay as Archera Overlay
User->>Modal: Open purchase or plan modal
Modal->>Archera: renderArcheraCTA()
Archera-->>Modal: CTA element appended to modal
User->>Modal: Click CTA button
Modal->>Archera: openArcheraPage('what-is-archera')
Archera->>Overlay: Populate `#archera-page-container` with Page A
Overlay-->>User: Show Page A content
User->>Overlay: Click cross-link to "how-it-works"
Overlay->>Archera: openArcheraPage('how-it-works')
Archera->>Overlay: Clear & render Page B
Overlay-->>User: Show Page B steps
User->>Overlay: Click Back
Overlay->>Archera: closeArcheraPage()
Archera->>Overlay: Hide and clear container
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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.
|
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