Show stage pill next to profit target on dashboards

State API now returns stage = 1 + number of withdrawals. Both the main
dashboard and the account detail page show a small Stage N pill next to
the profit target value.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-04-26 03:02:02 -05:00
co-authored by Claude Opus 4.6
parent 009f7471c2
commit 536aad27ef
4 changed files with 21 additions and 4 deletions
+10 -3
View File
@@ -1,6 +1,6 @@
'use client';
import { useEffect, useState } from 'react';
import { useEffect, useState, type ReactNode } from 'react';
import { useParams } from 'next/navigation';
import Link from 'next/link';
import {
@@ -47,7 +47,7 @@ function PerfRow({ label, value, badge, positive }: { label: string; value: stri
);
}
function ObjRow({ passed, label, value }: { passed: boolean; label: string; value: string }) {
function ObjRow({ passed, label, value }: { passed: boolean; label: ReactNode; value: string }) {
return (
<div className="flex items-center justify-between py-3 border-b border-slate-100 last:border-0">
<div className="flex items-center gap-2.5">
@@ -378,7 +378,14 @@ export default function AccountPage() {
<h2 className="text-slate-900 font-bold text-base mb-1">Objectives</h2>
<ObjRow
passed={profitPassed}
label="Profit Target"
label={
<span className="inline-flex items-center gap-1.5">
Profit Target
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-indigo-100 text-indigo-700">
Stage {account.stage}
</span>
</span>
}
value={cfg ? `$${fmt(totalProfit)} of $${fmt(account.effectiveProfitTarget ?? cfg.profitTarget)}` : '—'}
/>
<ObjRow
+3
View File
@@ -36,6 +36,8 @@ export async function GET() {
const isDead = autoLiqThreshold > 0 && cash.amount <= autoLiqThreshold;
const allFundTxns = client.fundTransactions?.[acc.id] ?? [];
const priorProfit = client.priorProfit?.[acc.id] ?? 0;
// Stage = 1 + number of withdrawals (negative fund transactions)
const stage = 1 + allFundTxns.filter((f) => f.amount < 0).length;
// Hide initial funding (amount === accountSize) from display
const displayFundTxns = cfg
? allFundTxns.filter((f) => f.amount !== cfg.account_size)
@@ -88,6 +90,7 @@ export async function GET() {
autoLiqThreshold,
totalProfit,
targetHit,
stage,
dailyTarget,
effectiveProfitTarget,
dailyPnL,
+6 -1
View File
@@ -135,7 +135,12 @@ function AccountRow({ account, firm, hideDead, privacy }: { account: AccountStat
</td>
<td className="px-4 py-3 text-slate-600 tabular-nums">
<div className="flex flex-col">
<span>${(account.effectiveProfitTarget ?? cfg?.profitTarget ?? 0).toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>
<div className="flex items-center gap-1.5">
<span>${(account.effectiveProfitTarget ?? cfg?.profitTarget ?? 0).toLocaleString(undefined, { maximumFractionDigits: 2 })}</span>
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-indigo-100 text-indigo-700">
Stage {account.stage}
</span>
</div>
{account.dailyTarget && (
<span className="text-xs text-slate-400">${fmt(account.dailyTarget.amount)} today</span>
)}
+2
View File
@@ -34,6 +34,8 @@ export interface AccountState {
totalProfit: number;
/** True when today's realizedPnL has met or exceeded the computed daily target */
targetHit: boolean;
/** Current stage = 1 + number of withdrawals (negative fund transactions). Stage 1 = fresh account. */
stage: number;
/** Next trading day's target, computed server-side. null when account is dead or config is missing. */
dailyTarget: { amount: number; path: 'first_day' | 'normal_day' | 'reduced_day' } | null;
/** Actual profit target to hit — max(stage profit target, consistency realTarget = maxDay/consistency). */