+ );
+}
diff --git a/src/features/targets/components/TargetRow.tsx b/src/features/targets/components/TargetRow.tsx
index cdc8459..bc0b0a7 100644
--- a/src/features/targets/components/TargetRow.tsx
+++ b/src/features/targets/components/TargetRow.tsx
@@ -32,6 +32,7 @@ interface Props {
nowSecs: number;
onTopUp: (goal: TargetGoal) => void;
onWithdraw: (goal: TargetGoal) => void;
+ onViewPlan: (goal: TargetGoal) => void;
}
/* public/dashboard/icons/target-02.svg, inlined so stroke follows currentColor
@@ -99,16 +100,16 @@ export default function TargetRow({
nowSecs,
onTopUp,
onWithdraw,
+ onViewPlan,
}: Props) {
const status = targetStatus(goal, nowSecs);
if (status === "withdrawn") return ;
const pct = progressPct(goal);
- const matured = status === "matured";
-
- // A goal reaches its target before the deadline still forfeits 1% on
- // withdrawal — the forfeit keys off `end_date`, not how full the goal is — so
- // that case reads "Withdraw early" rather than "Withdraw Now".
+ // "Finished" = deadline passed or target fully funded. Topping up a finished
+ // goal makes no sense, so it swaps Top Up for Withdraw (early ⇒ 1% forfeit,
+ // hence the red button).
+ const finished = status === "matured";
const early = isEarlyWithdrawal(goal, nowSecs);
return (
@@ -161,38 +162,31 @@ export default function TargetRow({
-
- {matured ? (
+
+ {finished ? (
onWithdraw(goal)}
- title={
- early
- ? `The contract charges the 1% forfeit until ${formatDateShort(goal.end_date)}.`
- : undefined
- }
- className="w-full sm:w-auto"
+ className="max-sm:flex-1"
>
{early ? "Withdraw early" : "Withdraw Now"}
) : (
- <>
- onTopUp(goal)}
- className="w-full sm:w-auto"
- >
- Top Up
-
- onWithdraw(goal)}
- className="w-full sm:w-auto"
- >
- Withdraw early
-
- >
+ onTopUp(goal)}
+ className="max-sm:flex-1"
+ >
+ Top Up
+
)}
+
);
diff --git a/src/pages/dashboard/Locked.tsx b/src/pages/dashboard/Locked.tsx
index 017a4e0..28f1612 100644
--- a/src/pages/dashboard/Locked.tsx
+++ b/src/pages/dashboard/Locked.tsx
@@ -7,6 +7,7 @@ import { lockStatus } from "../../features/locked/lib/lockMath";
import LockRow from "../../features/locked/components/LockRow";
import ProjectedGrowth from "../../features/locked/components/ProjectedGrowth";
import UnlockModal from "../../features/locked/components/UnlockModal";
+import LockPlanModal from "../../features/locked/components/LockPlanModal";
import DashButton from "../../shared/dash/DashButton";
import Spinner from "../../shared/dash/Spinner";
import FilterPills from "../../shared/dash/FilterPills";
@@ -25,6 +26,7 @@ export default function Locked() {
const [filter, setFilter] = useState("All");
const [page, setPage] = useState(1);
const [unlockTarget, setUnlockTarget] = useState(null);
+ const [planTarget, setPlanTarget] = useState(null);
// Recompute maturity on a timer: a lock can cross its end_date while the page
// is open, and nothing else would re-render the row.
@@ -152,6 +154,7 @@ export default function Locked() {
lock={l}
nowSecs={nowSecs}
onUnlock={setUnlockTarget}
+ onViewPlan={setPlanTarget}
/>
))}
@@ -175,6 +178,10 @@ export default function Locked() {
onCancel={() => setUnlockTarget(null)}
/>
)}
+
+ {planTarget && (
+ setPlanTarget(null)} />
+ )}
);
}
diff --git a/src/pages/dashboard/Targets.tsx b/src/pages/dashboard/Targets.tsx
index b5f090e..d3d5d81 100644
--- a/src/pages/dashboard/Targets.tsx
+++ b/src/pages/dashboard/Targets.tsx
@@ -14,6 +14,7 @@ import TargetRow from "../../features/targets/components/TargetRow";
import GoalProjection from "../../features/targets/components/GoalProjection";
import TopUpModal from "../../features/targets/components/TopUpModal";
import WithdrawModal from "../../features/targets/components/WithdrawModal";
+import GoalPlanModal from "../../features/targets/components/GoalPlanModal";
import DashButton from "../../shared/dash/DashButton";
import Spinner from "../../shared/dash/Spinner";
import FilterPills from "../../shared/dash/FilterPills";
@@ -53,6 +54,7 @@ export default function Targets() {
const [page, setPage] = useState(1);
const [topUpTarget, setTopUpTarget] = useState(null);
const [withdrawTarget, setWithdrawTarget] = useState(null);
+ const [planTarget, setPlanTarget] = useState(null);
const [goalYield, setGoalYield] = useState(null);
// A goal can cross its deadline while the page is open; nothing else would
@@ -205,6 +207,7 @@ export default function Targets() {
nowSecs={nowSecs}
onTopUp={setTopUpTarget}
onWithdraw={openWithdraw}
+ onViewPlan={setPlanTarget}
/>
))}
@@ -231,6 +234,18 @@ export default function Targets() {
/>
)}
+ {planTarget && (
+ {
+ setPlanTarget(null);
+ openWithdraw(g);
+ }}
+ onClose={() => setPlanTarget(null)}
+ />
+ )}
+
{withdrawTarget && (