Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 5 additions & 9 deletions frontend/app/asset-owner/plans/[planId]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { useState, useEffect } from "react";
import { useRouter, useParams } from "next/navigation";
import { plansAPI } from "@/app/lib/api/plans";
import type { Plan } from "@/app/lib/api/plans";
import { getPlan, useMockData } from "@/lib/api/dataSource";
import { EditInheritancePlanPanel } from "@/components/plans/EditInheritancePlanPanel";
Expand Down Expand Up @@ -32,14 +31,11 @@ export default function EditPlanPage() {
const handleClose = () => router.back();

const handleSaved = (updated: Plan) => {
const save = useMockData
? Promise.resolve(updated)
: plansAPI.updatePlan(planId, updated);
save.then((savedPlan) => {
if (useMockData) require("@/lib/mockStore").mockStore.updatePlan(planId, savedPlan);
setPlan(savedPlan);
router.back();
}).catch((err) => setError(err instanceof Error ? err.message : "Failed to save plan."));
// EditInheritancePlanPanel already persists the change via plansAPI.updatePlan
// and hands back the backend-confirmed plan — just sync local state here.
if (useMockData) require("@/lib/mockStore").mockStore.updatePlan(planId, updated);
setPlan(updated);
router.back();
};

if (loading) {
Expand Down
1 change: 0 additions & 1 deletion frontend/app/lib/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { PlansAPI } from "./plans";
export { PlansAPI } from "./plans";
export type {
Plan,
Beneficiary,
CreatePlanRequest,
UpdatePlanRequest,
ClaimPlanRequest,
Expand Down
25 changes: 25 additions & 0 deletions frontend/app/lib/api/inheritance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ export interface CreatePlanRequest {
is_active: boolean;
}

export interface UpdatePlanRequest {
/** Full replacement list of beneficiaries with allocations */
beneficiaries: PlanBeneficiaryRequest[];
/** Grace period in seconds before the plan becomes claimable */
grace_period?: number;
/** Whether this plan earns yield via AMM / lending pools */
earn_yield?: boolean;
/** Annualised yield rate in basis points (e.g. 500 = 5%) */
yield_rate_bps?: number;
}

export interface PingRequest {
/** Owner Stellar address */
owner: string;
Expand Down Expand Up @@ -203,6 +214,20 @@ export class InheritanceAPI {
return apiClient.get<PlanResponse[]>(endpoint, config);
}

/**
* Update an existing inheritance plan's beneficiaries, grace period, or
* yield settings.
*
* Requires signature auth (X-Public-Key + X-Signature headers).
*/
async updatePlan(
planId: string,
request: UpdatePlanRequest,
config?: RequestConfig
): Promise<PlanResponse> {
return apiClient.put<PlanResponse>(`/api/plans/${planId}`, request, config);
}

/**
* Send a liveness ping to keep the plan active.
*
Expand Down
77 changes: 42 additions & 35 deletions frontend/app/lib/api/plans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
*/

import { apiClient, ApiResponse, PaginatedResponse } from "./client";
import type { PlanBeneficiaryRequest, PlanResponse } from "./inheritance";

export type { PlanBeneficiaryRequest, PlanResponse };

export interface Plan {
id: string;
Expand Down Expand Up @@ -37,32 +40,41 @@ export interface Plan {
last_ping?: number;
}

export interface Beneficiary {
id?: string;
wallet_address: string;
name: string;
allocation_percentage: number;
}

/**
* Request DTOs below mirror the Axum backend's `Plan` and `UpdatePlanRequest`
* structs exactly (see backend/src/api.rs) so beneficiary allocations,
* wallet addresses, and fiat anchor info are never dropped on submission.
*/
export interface CreatePlanRequest {
title: string;
description?: string;
fee: number;
net_amount: number;
beneficiary_name?: string;
bank_account_number?: string;
bank_name?: string;
currency_preference: string;
two_fa_code: string;
/** Owner Stellar address */
owner: string;
/** Token contract address on Stellar */
token: string;
/** Amount to deposit (in token units) */
amount: number;
/** Full list of beneficiaries with allocations. Must sum to 10000 bps */
beneficiaries: PlanBeneficiaryRequest[];
/** Unix timestamp of the last liveness ping */
last_ping: number;
/** Grace period in seconds before the plan becomes claimable */
grace_period: number;
/** Whether this plan earns yield via AMM / lending pools */
earn_yield: boolean;
/** Annualised yield rate in basis points (e.g. 500 = 5%) */
yield_rate_bps: number;
/** Whether the plan is active immediately */
is_active: boolean;
}

export interface UpdatePlanRequest {
title?: string;
description?: string;
beneficiaries?: Beneficiary[];
inactivity_period_days?: number;
yield_harvesting_enabled?: boolean;
signed_transaction?: string;
/** Full replacement list of beneficiaries with allocations. Must sum to 10000 bps */
beneficiaries: PlanBeneficiaryRequest[];
/** Grace period in seconds before the plan becomes claimable */
grace_period?: number;
/** Whether this plan earns yield via AMM / lending pools */
earn_yield?: boolean;
/** Annualised yield rate in basis points (e.g. 500 = 5%) */
yield_rate_bps?: number;
}

export interface ClaimPlanRequest {
Expand All @@ -86,12 +98,8 @@ export class PlansAPI {
/**
* Create a new plan
*/
async createPlan(request: CreatePlanRequest): Promise<Plan> {
const response = await apiClient.post<ApiResponse<Plan>>(
"/api/plans",
request
);
return response.data!;
async createPlan(request: CreatePlanRequest): Promise<PlanResponse> {
return apiClient.post<PlanResponse>("/api/plans", request);
}

/**
Expand Down Expand Up @@ -151,14 +159,13 @@ export class PlansAPI {
}

/**
* Update an existing plan
* Update an existing plan's beneficiaries, grace period, or yield settings
*/
async updatePlan(planId: string, request: UpdatePlanRequest): Promise<Plan> {
const response = await apiClient.put<ApiResponse<Plan>>(
`/api/plans/${planId}`,
request
);
return response.data!;
async updatePlan(
planId: string,
request: UpdatePlanRequest
): Promise<PlanResponse> {
return apiClient.put<PlanResponse>(`/api/plans/${planId}`, request);
}

/**
Expand Down
Loading
Loading