-
+
{/* 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
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
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 ? (

) : (
-
+
-
+
{token.name?.slice(0, 2)}
)}
-
-
-
- {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)}%
-
-

+
+ {/* Chevron */}
+
+
) : (
-
+
{isSearchingToken ? (
-
+
-
) : (
- <>
-
- Select token
-
-
-

-
- >
+
+ Select token
+
)}
+ {/* Chevron */}
+
+

+
)}
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"
>
-
+ TEST
+
+
+ Test Token
+
+
+
+
+ $
+ 100.00
+
+
- $
- 100.00
+ 0.05
+ %
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 ? (

) : (
-
+
-
+
{token.name?.slice(0, 2)}
)}
-
-
-
- {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)}%
-
-

+
+ {/* Chevron */}
+
+
) : (
-
+
Select token
-
-

+ {/* Chevron */}
+
+
)}
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"
>
-
+ TEST
+
+
+ Test Token
+
+
+
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:
,
},
];