diff --git a/src/apps/insights/components/PnLDetailModal.tsx b/src/apps/insights/components/PnLDetailModal.tsx index d7509af0..14a7aa83 100644 --- a/src/apps/insights/components/PnLDetailModal.tsx +++ b/src/apps/insights/components/PnLDetailModal.tsx @@ -19,8 +19,6 @@ interface PnLDetailModalProps { } export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailModalProps) => { - if (!view) return null; - const isMobile = useIsMobile(); const filteredSignals = useMemo(() => { @@ -31,7 +29,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM case 'open': return signals.filter(s => s.status === 'active'); case 'closed': - return signals.filter(s => + return signals.filter(s => ['completed', 'stopped', 'closed'].includes(s.status || 'active') ); default: @@ -45,7 +43,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM const openSignals = filteredSignals .filter(s => typeof s.profit_loss_percent === 'number') .sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime()); - + let cumulative = 0; const data = openSignals.map(s => { cumulative += s.profit_loss_percent || 0; @@ -73,7 +71,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM const dateB = b.closed_at || b.last_price_update || b.created_at; return new Date(dateA).getTime() - new Date(dateB).getTime(); }); - + let cumulative = 0; return closedSignals.map(s => { cumulative += s.realized_pnl_percent || 0; @@ -106,25 +104,25 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM const getDescriptionData = () => { const count = filteredSignals.length; - const winCount = filteredSignals.filter(s => - view === 'open' - ? (s.profit_loss_percent || 0) > 0 + const winCount = filteredSignals.filter(s => + view === 'open' + ? (s.profit_loss_percent || 0) > 0 : (s.realized_pnl_percent || 0) > 0 ).length; const winRate = count > 0 ? ((winCount / count) * 100).toFixed(1) : '0.0'; - + return { count, winRate }; }; const CustomTooltip = ({ active, payload }: any) => { if (!active || !payload?.length) return null; - + const p = payload[0]?.payload; if (!p) return null; - + const dateText = p.isNow ? 'Now' : format(new Date(p.ts), 'MMM dd, yyyy HH:mm'); const val = typeof payload[0].value === 'number' ? payload[0].value : 0; - + return (

{dateText}

@@ -147,11 +145,11 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM - - + + - - `${value.toFixed(1)}%`} @@ -187,7 +185,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM {/* Trades Table */}

Trade Breakdown

- + {/* Trade Timeline - only for closed trades */} {view === 'closed' && filteredSignals.length > 0 && (
@@ -201,7 +199,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM }) .map(signal => { const { events, latestStop } = normalizeTrailingHistory(signal); - + return (
{signal.ticker}
@@ -220,7 +218,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM
); } - + if (event.type === 'tp_hit') { return (
@@ -249,7 +247,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM
); } - + if (event.type === 'stop_loss_hit') { return (
@@ -261,7 +259,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM
); } - + if (event.type === 'closed') { return (
@@ -275,7 +273,7 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM
); } - + return null; })}
@@ -313,96 +311,95 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM ) : ( filteredSignals .sort((a, b) => { - const dateA = view === 'open' - ? new Date(a.created_at) + const dateA = view === 'open' + ? new Date(a.created_at) : new Date(a.closed_at || a.last_price_update || a.created_at); - const dateB = view === 'open' - ? new Date(b.created_at) + const dateB = view === 'open' + ? new Date(b.created_at) : new Date(b.closed_at || b.last_price_update || b.created_at); return dateB.getTime() - dateA.getTime(); }) .map(signal => { - const pnl = view === 'open' - ? (signal.profit_loss_percent || 0) + const pnl = view === 'open' + ? (signal.profit_loss_percent || 0) : (signal.realized_pnl_percent || 0); const isPositivePnL = pnl >= 0; - + // Count TP hits const tpHits = [ signal.tp1_hit, signal.tp2_hit, signal.tp3_hit, ].filter(Boolean).length; - + return ( {signal.ticker} ${formatPrice(signal.entry_price, signal.ticker)} - ${view === 'open' + ${view === 'open' ? formatPrice(signal.current_price || signal.entry_price, signal.ticker) - : signal.stop_loss_hit + : signal.stop_loss_hit ? formatPrice(getEffectiveStopLoss(signal), signal.ticker) : formatPrice(signal.current_price || signal.entry_price, signal.ticker) } - -
- - {isPositivePnL ? '+' : ''}{pnl.toFixed(2)}% - - {view === 'closed' && signal.status === 'closed' && ( -
- How calculated? -
- {signal.tp1_hit && ( -
TP1: {( - signal.order_side === 'buy' - ? (((signal.tp1 - signal.entry_price) / signal.entry_price) * 100 * 0.3333) - : (((signal.entry_price - signal.tp1) / signal.entry_price) * 100 * 0.3333) - ).toFixed(2)}% (33.33%)
- )} - {signal.tp2_hit && ( -
TP2: {( - signal.order_side === 'buy' - ? (((signal.tp2 - signal.entry_price) / signal.entry_price) * 100 * 0.3333) - : (((signal.entry_price - signal.tp2) / signal.entry_price) * 100 * 0.3333) - ).toFixed(2)}% (33.33%)
- )} - {signal.tp3_hit && ( -
TP3: {( - signal.order_side === 'buy' - ? (((signal.tp3 - signal.entry_price) / signal.entry_price) * 100 * 0.3333) - : (((signal.entry_price - signal.tp3) / signal.entry_price) * 100 * 0.3333) - ).toFixed(2)}% (33.33%)
- )} - {(() => { - const tpCount = [signal.tp1_hit, signal.tp2_hit, signal.tp3_hit].filter(Boolean).length; - const remaining = 1 - (tpCount * 0.3333); - if (remaining > 0) { - const exitPrice = signal.stop_loss_hit ? getEffectiveStopLoss(signal) : (signal.current_price || signal.entry_price); - const remainingPnL = signal.order_side === 'buy' - ? ((exitPrice - signal.entry_price) / signal.entry_price) * 100 * remaining - : ((signal.entry_price - exitPrice) / signal.entry_price) * 100 * remaining; - return
Remaining: {remainingPnL.toFixed(2)}% ({(remaining * 100).toFixed(0)}%)
; - } - return null; - })()} -
-
- )} -
-
- + +
+ + {isPositivePnL ? '+' : ''}{pnl.toFixed(2)}% + + {view === 'closed' && signal.status === 'closed' && ( +
+ How calculated? +
+ {signal.tp1_hit && ( +
TP1: {( + signal.order_side === 'buy' + ? (((signal.tp1 - signal.entry_price) / signal.entry_price) * 100 * 0.3333) + : (((signal.entry_price - signal.tp1) / signal.entry_price) * 100 * 0.3333) + ).toFixed(2)}% (33.33%)
+ )} + {signal.tp2_hit && ( +
TP2: {( + signal.order_side === 'buy' + ? (((signal.tp2 - signal.entry_price) / signal.entry_price) * 100 * 0.3333) + : (((signal.entry_price - signal.tp2) / signal.entry_price) * 100 * 0.3333) + ).toFixed(2)}% (33.33%)
+ )} + {signal.tp3_hit && ( +
TP3: {( + signal.order_side === 'buy' + ? (((signal.tp3 - signal.entry_price) / signal.entry_price) * 100 * 0.3333) + : (((signal.entry_price - signal.tp3) / signal.entry_price) * 100 * 0.3333) + ).toFixed(2)}% (33.33%)
+ )} + {(() => { + const tpCount = [signal.tp1_hit, signal.tp2_hit, signal.tp3_hit].filter(Boolean).length; + const remaining = 1 - (tpCount * 0.3333); + if (remaining > 0) { + const exitPrice = signal.stop_loss_hit ? getEffectiveStopLoss(signal) : (signal.current_price || signal.entry_price); + const remainingPnL = signal.order_side === 'buy' + ? ((exitPrice - signal.entry_price) / signal.entry_price) * 100 * remaining + : ((signal.entry_price - exitPrice) / signal.entry_price) * 100 * remaining; + return
Remaining: {remainingPnL.toFixed(2)}% ({(remaining * 100).toFixed(0)}%)
; + } + return null; + })()} +
+
+ )} +
+
+
{[1, 2, 3].map(i => (
{i}
@@ -410,13 +407,13 @@ export const PnLDetailModal = ({ open, onOpenChange, view, signals }: PnLDetailM
- {signal.status?.toUpperCase()} diff --git a/src/apps/pulse/components/App/tests/__snapshots__/AppWrapper.test.tsx.snap b/src/apps/pulse/components/App/tests/__snapshots__/AppWrapper.test.tsx.snap index c8db30ce..8de4fe84 100644 --- a/src/apps/pulse/components/App/tests/__snapshots__/AppWrapper.test.tsx.snap +++ b/src/apps/pulse/components/App/tests/__snapshots__/AppWrapper.test.tsx.snap @@ -136,19 +136,19 @@ exports[` > renders correctly and matches snapshot 1`] = ` type="button" >
Select token
arrow-down
diff --git a/src/apps/pulse/components/App/tests/__snapshots__/HomeScreen.test.tsx.snap b/src/apps/pulse/components/App/tests/__snapshots__/HomeScreen.test.tsx.snap index 665f2df3..38b947a4 100644 --- a/src/apps/pulse/components/App/tests/__snapshots__/HomeScreen.test.tsx.snap +++ b/src/apps/pulse/components/App/tests/__snapshots__/HomeScreen.test.tsx.snap @@ -136,19 +136,19 @@ exports[` > renders correctly and matches snapshot 1`] = ` type="button" >
Select token
arrow-down
diff --git a/src/apps/pulse/components/Buy/Buy.tsx b/src/apps/pulse/components/Buy/Buy.tsx index c8edb6bc..9e56be2c 100644 --- a/src/apps/pulse/components/Buy/Buy.tsx +++ b/src/apps/pulse/components/Buy/Buy.tsx @@ -642,74 +642,103 @@ export default function Buy(props: BuyProps) { > {token ? (
-
+ {/* Logo */} +
{token.logo ? ( Main ) : ( -
+
- + {token.name?.slice(0, 2)}
)} Chain Logo
-
-
-

- {token.symbol} -

- {token.symbol.length + token.name.length <= 13 && ( -

- {token.name} -

+ + {/* Top Row: Symbol and Name */} +
+

+ {token.symbol} +

+

+ {token.name} +

+
+ + {/* Bottom Row: Price and Change */} +
+

+ ${token.usdValue} +

+ +
+ {/* Triangle Indicator */} + {token.dailyPriceChange !== 0 && ( +
= 0 + ? 'border-b-[6px] border-b-[#5CFF93]' + : 'border-t-[6px] border-t-[#FF366C]' + } opacity-50`} + /> )} -
-
-

- ${token.usdValue} + +

= 0 + ? 'text-[#5CFF93]' + : 'text-[#FF366C]' + }`} + > + {Math.abs(token.dailyPriceChange).toFixed(2)}%

-
- arrow-down + + {/* Chevron */} +
+ arrow-down
) : ( -
+
{isSearchingToken ? ( -
+
-
+
Searching...
) : ( - <> -
- Select token -
-
- arrow-down -
- +
+ Select token +
)} + {/* Chevron */} +
+ arrow-down +
)} diff --git a/src/apps/pulse/components/Buy/tests/__snapshots__/Buy.test.tsx.snap b/src/apps/pulse/components/Buy/tests/__snapshots__/Buy.test.tsx.snap index e4093c75..aa7a9e8c 100644 --- a/src/apps/pulse/components/Buy/tests/__snapshots__/Buy.test.tsx.snap +++ b/src/apps/pulse/components/Buy/tests/__snapshots__/Buy.test.tsx.snap @@ -17,52 +17,66 @@ exports[` > renders correctly and matches snapshot 1`] = ` type="button" >
Main Chain Logo
-
-

- TEST -

-
+ TEST +

+

+ Test Token +

+
+
+

+ $ + 100.00 +

+

- $ - 100.00 + 0.05 + %

arrow-down
diff --git a/src/apps/pulse/components/Search/PortfolioTokenList.tsx b/src/apps/pulse/components/Search/PortfolioTokenList.tsx index a3787d7b..02d6b0d3 100644 --- a/src/apps/pulse/components/Search/PortfolioTokenList.tsx +++ b/src/apps/pulse/components/Search/PortfolioTokenList.tsx @@ -315,7 +315,7 @@ const PortfolioTokenList = (props: PortfolioTokenListProps) => { onClick={() => handleSort('price')} data-testid="pulse-portfolio-header-token" > -

+

Token/Price

{ onClick={() => handleSort('balance')} data-testid="pulse-portfolio-header-balance" > -

+

Balance

{ onClick={() => handleSort('pnl')} data-testid="pulse-portfolio-header-pnl" > -

+

Unrealized PnL/%

> renders correctly and matches snapshot 1`] = ` onClick={[Function]} >

Token/Price

@@ -68,7 +68,7 @@ exports[` > renders correctly and matches snapshot 1`] = ` onClick={[Function]} >

Balance

@@ -119,7 +119,7 @@ exports[` > renders correctly and matches snapshot 1`] = ` onClick={[Function]} >

Unrealized PnL/%

diff --git a/src/apps/pulse/components/Sell/Sell.tsx b/src/apps/pulse/components/Sell/Sell.tsx index ebd1f725..fca1ad37 100644 --- a/src/apps/pulse/components/Sell/Sell.tsx +++ b/src/apps/pulse/components/Sell/Sell.tsx @@ -301,79 +301,105 @@ const Sell = (props: SellProps) => { > {token ? (
-
+ {/* Logo */} +
{token.logo ? ( Main ) : ( -
+
- + {token.name?.slice(0, 2)}
)} Chain Logo
-
-
-

- {token.symbol} -

- {token.name && - token.symbol.length + token.name.length <= 13 && ( -

- {token.name} -

- )} -
-
+ + {/* Top Row: Symbol and Name */} +
+

+ {token.symbol} +

+

+ {token.name} +

+
+ + {/* Bottom Row: Price and Change */} +
+

+ ${formatExponentialSmallNumber(token.usdValue)} +

+ +
+ {/* Triangle Indicator */} + {token.dailyPriceChange !== 0 && ( +
= 0 + ? 'border-b-[6px] border-b-[#5CFF93]' + : 'border-t-[6px] border-t-[#FF366C]' + } opacity-50`} + /> + )} +

= 0 + ? 'text-[#5CFF93]' + : 'text-[#FF366C]' + }`} > - ${formatExponentialSmallNumber(token.usdValue)} + {Math.abs(token.dailyPriceChange).toFixed(2)}%

-
- arrow-down + + {/* Chevron */} +
+ arrow-down
) : ( -
+
Select token
-
- arrow-down + {/* Chevron */} +
+ arrow-down
)} diff --git a/src/apps/pulse/components/Sell/tests/__snapshots__/Sell.test.tsx.snap b/src/apps/pulse/components/Sell/tests/__snapshots__/Sell.test.tsx.snap index 3f866945..0ba99cea 100644 --- a/src/apps/pulse/components/Sell/tests/__snapshots__/Sell.test.tsx.snap +++ b/src/apps/pulse/components/Sell/tests/__snapshots__/Sell.test.tsx.snap @@ -18,57 +18,69 @@ exports[` > renders correctly and matches snapshot 1`] = ` type="button" >
Main Chain Logo
-
-

- TEST -

-
+ TEST +

+

+ Test Token +

+
+
+

+ $ + 100 +

+
arrow-down
diff --git a/src/containers/Main.tsx b/src/containers/Main.tsx index ed3e1a83..4bfc56c3 100644 --- a/src/containers/Main.tsx +++ b/src/containers/Main.tsx @@ -929,7 +929,7 @@ const AuthLayout = () => { }, { path: '*', - element: , + element: , }, ];