Skip to content

fix(frontend): map full beneficiary list and enforce bps allocation validation - #1053

Merged
ONEONUORA merged 2 commits into
Fracverse:masterfrom
anonfedora:fix/beneficiary-allocation-validation-1031
Aug 24, 2026
Merged

fix(frontend): map full beneficiary list and enforce bps allocation validation#1053
ONEONUORA merged 2 commits into
Fracverse:masterfrom
anonfedora:fix/beneficiary-allocation-validation-1031

Conversation

@anonfedora

Copy link
Copy Markdown
Contributor

Summary

CreateInheritancePlanPanel and EditInheritancePlanPanel only sent beneficiaries[0]?.name to plansAPI.createPlan, silently discarding every other beneficiary's wallet address, allocation percentage, and fiat off-ramp config on submission.

Digging further, the problem went deeper than the beneficiary array: plansAPI.createPlan/updatePlan sent a request shape (title, fee, net_amount, bank_account_number, bank_name, currency_preference, two_fa_code) that never matched the backend's real Plan/UpdatePlanRequest contract (see backend/src/api.rs, and the MSW mock explicitly commented "matches the Axum POST /api/plans signature"), which actually requires owner, token, amount, beneficiaries: [{address, name, allocation_bps, fiat_anchor_info}], grace_period, earn_yield, yield_rate_bps. Beneficiaries were never going to survive submission regardless of how the array was built, and inheritance.ts was missing an updatePlan() method entirely.

This PR aligns the frontend with the real backend contract end-to-end and fixes the beneficiary data loss.

Changes

  • app/lib/api/inheritance.ts — added UpdatePlanRequest + updatePlan(), matching the backend's real UpdatePlanRequest (beneficiaries, grace_period?, earn_yield?, yield_rate_bps?).
  • app/lib/api/plans.ts — rewrote CreatePlanRequest/UpdatePlanRequest to match the backend's actual Plan/UpdatePlanRequest structs, reusing PlanBeneficiaryRequest; createPlan/updatePlan now return the real unwrapped PlanResponse instead of assuming an {status, data} envelope that the backend never sends.
  • components/plans/BeneficiaryAllocationRow.tsx (new, shared by both panels) — beneficiary row UI plus:
    • validateBeneficiaryDrafts — strict basis-point validation (allocations must total exactly 10,000 bps), Stellar StrKey checksum address validation, duplicate wallet-address detection, and fiat-field completeness checks, each with a specific error message.
    • beneficiaryDraftToRequest — builds the fiat_anchor_info JSON payload (name/currency/bank/account/daily_limit) the backend already parses on payout (parse_fiat_anchor_info).
  • CreateInheritancePlanPanel.tsx:
    • Maps the full beneficiary list into PlanBeneficiaryRequest[] (address, name, allocation_bps, fiat_anchor_info) instead of dropping everything but the first name.
    • Adds a destination-asset (XLM/USDC/custom contract) field, since the backend requires owner/token and the panel previously collected neither.
    • Adds per-beneficiary fiat off-ramp fields (bank, account, currency, daily limit).
  • EditInheritancePlanPanel.tsx:
    • Seeds beneficiary drafts from the plan's real beneficiaries array (wallet address, bps, parsed fiat info) instead of only a beneficiary name.
    • Sends the full beneficiary list plus real grace_period/earn_yield/yield_rate_bps on save, and merges the backend's response back into the UI's Plan shape.
    • Save button gating: allocation-total validity still gates the button (unchanged UX), while full field-level validation (name/address/fiat) now runs on save with clear inline + banner messaging.
  • Updated MSW mocks (tests/mocks/handlers.ts) and two test files to match the corrected, real backend contract.

Test plan

  • npm test — all 202 tests pass (npx vitest --run)
  • npx tsc --noEmit — no new type errors (one pre-existing, unrelated error in tests/components/InactivityTimerCard.test.tsx predates this branch)
  • npx eslint on all touched files — 0 errors (only pre-existing-style any warnings)
  • npm run build (next build) — compiles and type-checks cleanly, matching CI's Frontend CI workflow gate
  • Manual QA in a running app against a live backend (not exercised in this session — no backend/DB was running)

Closes #1031

🤖 Generated with Claude Code

@anonfedora
anonfedora force-pushed the fix/beneficiary-allocation-validation-1031 branch from 5fc68f1 to bbf1d2d Compare August 23, 2026 20:59
…alidation

CreateInheritancePlanPanel and EditInheritancePlanPanel only transmitted
beneficiaries[0]?.name to the backend, silently dropping every other
beneficiary's wallet address, allocation, and fiat off-ramp config.
Separately, plansAPI.createPlan/updatePlan sent a request shape (title,
fee, net_amount, bank_account_number, two_fa_code, ...) that never
matched the real backend's Plan/UpdatePlanRequest contract at all.

- plans.ts / inheritance.ts: CreatePlanRequest/UpdatePlanRequest now
  mirror the backend's actual Plan/UpdatePlanRequest structs (owner,
  token, amount, beneficiaries: PlanBeneficiaryRequest[], grace_period,
  earn_yield, yield_rate_bps); added the missing updatePlan() to
  inheritance.ts.
- New BeneficiaryAllocationRow component centralizes beneficiary row UI,
  strict basis-point validation (must total exactly 10,000 bps), Stellar
  StrKey address validation, duplicate-address detection, and fiat
  anchor/daily-limit encoding shared by both panels.
- Both panels now build and submit the full beneficiary list, surface
  clear per-field and total-allocation error messages, and correctly
  round-trip the backend's PlanResponse back into the UI.
- Updated MSW mocks/tests to match the corrected contract.

Closes Fracverse#1031
…ate plan update call

Rebased fix/beneficiary-allocation-validation-1031 onto upstream/master
to pick up Fracverse#1049 (wire frontend plans to the live API) and Fracverse#1052
(wallet-signed pings), which landed after this branch was created.

Fracverse#1049 changed EditPlanPage.handleSaved to persist the panel's callback
value via plansAPI.updatePlan(planId, updated) a second time, passing
the full Plan object as if it were an UpdatePlanRequest. That no longer
type-checks now that UpdatePlanRequest is a proper DTO, and it was
always redundant: EditInheritancePlanPanel already calls
plansAPI.updatePlan itself and hands the backend-confirmed plan back
via onSaved. The page now just syncs local state with that result.
@anonfedora
anonfedora force-pushed the fix/beneficiary-allocation-validation-1031 branch from bbf1d2d to 28e6051 Compare August 23, 2026 21:05

@ONEONUORA ONEONUORA 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.

Great job @anonfedora

@ONEONUORA
ONEONUORA merged commit 70ae4e3 into Fracverse:master Aug 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

frontend: Implement Multi-Beneficiary Form Controls & Allocation Validation in Plan Creation & Editing

2 participants