account reset options
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m20s

This commit is contained in:
2026-03-20 12:42:20 -04:00
parent bf14b039c6
commit ea1dca974c
9 changed files with 221 additions and 38 deletions
+1 -2
View File
@@ -359,9 +359,8 @@ The items below are planned improvements roughly ordered by user value. They are
### Other Ideas / Nice-to-Haves ### Other Ideas / Nice-to-Haves
- **Hedge funds**: group of players pool money into a shared portfolio, one designated fund manager places trades.
- **Email integration**: SMTP-based password reset and optional trade confirmation emails. - **Email integration**: SMTP-based password reset and optional trade confirmation emails.
- **Multi-instance support**: let users choose which Mastodon instance to pull data from per-hashtag, or aggregate across instances. - **Multi-instance support**: fallback to another instance if the primary instance is unavailable or throttles API calls.
- **Mobile-optimised trade panel**: the current layout works but a dedicated bottom-sheet on mobile would improve UX. - **Mobile-optimised trade panel**: the current layout works but a dedicated bottom-sheet on mobile would improve UX.
- **Price alerts**: users subscribe to a hashtag at a threshold price; a notification appears in the UI (or email if integrated) when it crosses that level. - **Price alerts**: users subscribe to a hashtag at a threshold price; a notification appears in the UI (or email if integrated) when it crosses that level.
- **Dark/light theme toggle**: currently dark-only. - **Dark/light theme toggle**: currently dark-only.
+3
View File
@@ -203,4 +203,7 @@ enum TradeType {
LOTTERY_WIN LOTTERY_WIN
LIQUIDATE_LONG LIQUIDATE_LONG
LIQUIDATE_SHORT LIQUIDATE_SHORT
DONATION // keepHistory reset: user was in the green — donated their portfolio
BANKRUPTCY // keepHistory reset: user was in the red — debts cleared
ACCOUNT_OPEN // keepHistory reset: new $2000 account opening entry
} }
+28 -5
View File
@@ -29,6 +29,7 @@ export function AdminUserActions({ user }: { user: UserData }) {
const [accountResetConfirm, setAccountResetConfirm] = useState('') const [accountResetConfirm, setAccountResetConfirm] = useState('')
const [accountResetError, setAccountResetError] = useState('') const [accountResetError, setAccountResetError] = useState('')
const [accountResetDone, setAccountResetDone] = useState(false) const [accountResetDone, setAccountResetDone] = useState(false)
const [resetKeepHistory, setResetKeepHistory] = useState(false)
async function handleSave() { async function handleSave() {
setLoading(true) setLoading(true)
@@ -110,7 +111,11 @@ export function AdminUserActions({ user }: { user: UserData }) {
} }
setLoading(true) setLoading(true)
setAccountResetError('') setAccountResetError('')
const res = await fetch(`/api/admin/users/${user.id}/reset`, { method: 'POST' }) const res = await fetch(`/api/admin/users/${user.id}/reset`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keepHistory: resetKeepHistory }),
})
const data = await res.json() const data = await res.json()
setLoading(false) setLoading(false)
if (!res.ok) { if (!res.ok) {
@@ -125,7 +130,7 @@ export function AdminUserActions({ user }: { user: UserData }) {
return ( return (
<> <>
<button <button
onClick={() => { setOpen(true); setResetUrl(null); setError(''); setDeleteConfirm(''); setDeleteError(''); setAccountResetConfirm(''); setAccountResetError(''); setAccountResetDone(false) }} onClick={() => { setOpen(true); setResetUrl(null); setError(''); setDeleteConfirm(''); setDeleteError(''); setAccountResetConfirm(''); setAccountResetError(''); setAccountResetDone(false); setResetKeepHistory(false) }}
className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors" className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors"
> >
Edit Edit
@@ -244,10 +249,28 @@ export function AdminUserActions({ user }: { user: UserData }) {
{/* Account reset */} {/* Account reset */}
<div className="border-t border-amber-500/20 pt-4"> <div className="border-t border-amber-500/20 pt-4">
<p className="text-sm text-amber-400 mb-1 font-medium">Reset account</p> <p className="text-sm text-amber-400 mb-1 font-medium">Reset account</p>
<p className="text-xs text-slate-500 mb-3"> <p className="text-xs text-slate-500 mb-2">
Closes all positions, forfeits fund investments, and resets the balance to $2,000. {resetKeepHistory
Trade history is preserved. ? 'Keeps trade history and adds DONATION/BANKRUPTCY + ACCOUNT OPEN bookmark entries. Balance resets to $2,000.'
: 'Permanently erases all trade history, positions, and fund investments, then resets the balance to $2,000.'}
</p> </p>
<div className="flex items-center justify-between mb-3">
<div>
<p className="text-xs text-slate-400">Keep trade history</p>
<p className="text-xs text-slate-500">Add reset bookmarks instead of erasing</p>
</div>
<button
type="button"
onClick={() => setResetKeepHistory((v) => !v)}
className={`relative inline-flex h-5 w-9 items-center rounded-full transition-colors ${
resetKeepHistory ? 'bg-amber-500' : 'bg-slate-600'
}`}
>
<span className={`inline-block h-3 w-3 transform rounded-full bg-white transition-transform ${
resetKeepHistory ? 'translate-x-5' : 'translate-x-1'
}`} />
</button>
</div>
{accountResetDone ? ( {accountResetDone ? (
<p className="text-xs text-emerald-400"> Account has been reset.</p> <p className="text-xs text-emerald-400"> Account has been reset.</p>
) : ( ) : (
@@ -7,13 +7,12 @@ const STARTING_BALANCE = 2000
/** /**
* POST /api/admin/users/[userId]/reset * POST /api/admin/users/[userId]/reset
* Body: { keepHistory?: boolean }
* *
* Admin-only. Resets a user's account: closes all positions, forfeits fund * Admin-only. Resets a user's account. See /api/user/me/reset for full docs.
* investments, and resets their cash balance to the default starting balance.
* Trade history is preserved.
*/ */
export async function POST( export async function POST(
_req: NextRequest, req: NextRequest,
{ params }: { params: { userId: string } }, { params }: { params: { userId: string } },
) { ) {
const session = await getServerSession(authOptions) const session = await getServerSession(authOptions)
@@ -21,11 +20,24 @@ export async function POST(
return NextResponse.json({ error: 'Forbidden' }, { status: 403 }) return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
} }
const body = await req.json().catch(() => ({}))
const keepHistory = body.keepHistory === true
const user = await prisma.user.findUnique({ const user = await prisma.user.findUnique({
where: { id: params.userId }, where: { id: params.userId },
select: { select: {
balance: true,
isFund: true, isFund: true,
fundInvestments: { select: { fundId: true, shares: true } }, fundInvestments: { select: { fundId: true, shares: true } },
positions: {
where: { shares: { gt: 0 } },
select: {
shares: true,
avgBuyPrice: true,
positionType: true,
hashtag: { select: { currentPrice: true } },
},
},
}, },
}) })
if (!user) return NextResponse.json({ error: 'User not found.' }, { status: 404 }) if (!user) return NextResponse.json({ error: 'User not found.' }, { status: 404 })
@@ -33,6 +45,15 @@ export async function POST(
return NextResponse.json({ error: 'Fund accounts cannot be reset this way.' }, { status: 400 }) return NextResponse.json({ error: 'Fund accounts cannot be reset this way.' }, { status: 400 })
} }
const portfolioValue = user.positions.reduce((sum, p) => {
const val =
p.positionType === 'LONG'
? p.shares * p.hashtag.currentPrice
: p.avgBuyPrice * p.shares - (p.hashtag.currentPrice - p.avgBuyPrice) * p.shares
return sum + val
}, 0)
const totalValue = user.balance + portfolioValue
// Forfeit all fund investments — decrement each fund's sharesOutstanding // Forfeit all fund investments — decrement each fund's sharesOutstanding
const fundUpdates = user.fundInvestments const fundUpdates = user.fundInvestments
.filter((inv) => inv.shares > 0) .filter((inv) => inv.shares > 0)
@@ -43,11 +64,35 @@ export async function POST(
}), }),
) )
const tradeOps = keepHistory
? [
totalValue >= STARTING_BALANCE
? prisma.trade.create({
data: { userId: params.userId, type: 'DONATION', shares: 0, price: 0, total: totalValue, profit: -totalValue },
})
: prisma.trade.create({
data: {
userId: params.userId,
type: 'BANKRUPTCY',
shares: 0,
price: 0,
total: STARTING_BALANCE - totalValue,
profit: STARTING_BALANCE - totalValue,
},
}),
prisma.trade.create({
data: { userId: params.userId, type: 'ACCOUNT_OPEN', shares: 0, price: 0, total: STARTING_BALANCE, profit: STARTING_BALANCE },
}),
]
: [prisma.trade.deleteMany({ where: { userId: params.userId } })]
await prisma.$transaction([ await prisma.$transaction([
...fundUpdates, ...fundUpdates,
prisma.fundInvestment.deleteMany({ where: { userId: params.userId } }), prisma.fundInvestment.deleteMany({ where: { userId: params.userId } }),
prisma.position.updateMany({ where: { userId: params.userId }, data: { shares: 0 } }), prisma.position.deleteMany({ where: { userId: params.userId } }),
prisma.userPortfolioHistory.deleteMany({ where: { userId: params.userId } }),
prisma.user.update({ where: { id: params.userId }, data: { balance: STARTING_BALANCE } }), prisma.user.update({ where: { id: params.userId }, data: { balance: STARTING_BALANCE } }),
...tradeOps,
]) ])
return NextResponse.json({ ok: true }) return NextResponse.json({ ok: true })
+56 -6
View File
@@ -1,4 +1,4 @@
import { NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth' import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth' import { authOptions } from '@/lib/auth'
import { prisma } from '@/lib/prisma' import { prisma } from '@/lib/prisma'
@@ -7,22 +7,39 @@ const STARTING_BALANCE = 2000
/** /**
* POST /api/user/me/reset * POST /api/user/me/reset
* Body: { keepHistory?: boolean }
* *
* Resets the current user's account: closes all positions, forfeits fund * Resets the current user's account. Deletes positions, fund investments, and
* investments, and resets the cash balance to the default starting balance. * portfolio history. When keepHistory is true the existing trade log is
* Trade history is preserved. * preserved and two bookmark entries are appended:
* • DONATION — user was in the green (totalValue ≥ $2k): records the value donated
* • BANKRUPTCY — user was in the red (totalValue < $2k): records the debt cleared
* • ACCOUNT_OPEN — always appended, records the fresh $2k grant
* When keepHistory is false all trades are also deleted.
*/ */
export async function POST() { export async function POST(req: NextRequest) {
const session = await getServerSession(authOptions) const session = await getServerSession(authOptions)
if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
const userId = session.user.id const userId = session.user.id
const body = await req.json().catch(() => ({}))
const keepHistory = body.keepHistory === true
const user = await prisma.user.findUnique({ const user = await prisma.user.findUnique({
where: { id: userId }, where: { id: userId },
select: { select: {
balance: true,
isFund: true, isFund: true,
fundInvestments: { select: { fundId: true, shares: true } }, fundInvestments: { select: { fundId: true, shares: true } },
positions: {
where: { shares: { gt: 0 } },
select: {
shares: true,
avgBuyPrice: true,
positionType: true,
hashtag: { select: { currentPrice: true } },
},
},
}, },
}) })
if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 }) if (!user) return NextResponse.json({ error: 'Not found' }, { status: 404 })
@@ -30,6 +47,15 @@ export async function POST() {
return NextResponse.json({ error: 'Fund accounts cannot be reset this way.' }, { status: 400 }) return NextResponse.json({ error: 'Fund accounts cannot be reset this way.' }, { status: 400 })
} }
const portfolioValue = user.positions.reduce((sum, p) => {
const val =
p.positionType === 'LONG'
? p.shares * p.hashtag.currentPrice
: p.avgBuyPrice * p.shares - (p.hashtag.currentPrice - p.avgBuyPrice) * p.shares
return sum + val
}, 0)
const totalValue = user.balance + portfolioValue
// Forfeit all fund investments — decrement each fund's sharesOutstanding // Forfeit all fund investments — decrement each fund's sharesOutstanding
const fundUpdates = user.fundInvestments const fundUpdates = user.fundInvestments
.filter((inv) => inv.shares > 0) .filter((inv) => inv.shares > 0)
@@ -40,11 +66,35 @@ export async function POST() {
}), }),
) )
const tradeOps = keepHistory
? [
totalValue >= STARTING_BALANCE
? prisma.trade.create({
data: { userId, type: 'DONATION', shares: 0, price: 0, total: totalValue, profit: -totalValue },
})
: prisma.trade.create({
data: {
userId,
type: 'BANKRUPTCY',
shares: 0,
price: 0,
total: STARTING_BALANCE - totalValue,
profit: STARTING_BALANCE - totalValue,
},
}),
prisma.trade.create({
data: { userId, type: 'ACCOUNT_OPEN', shares: 0, price: 0, total: STARTING_BALANCE, profit: STARTING_BALANCE },
}),
]
: [prisma.trade.deleteMany({ where: { userId } })]
await prisma.$transaction([ await prisma.$transaction([
...fundUpdates, ...fundUpdates,
prisma.fundInvestment.deleteMany({ where: { userId } }), prisma.fundInvestment.deleteMany({ where: { userId } }),
prisma.position.updateMany({ where: { userId }, data: { shares: 0 } }), prisma.position.deleteMany({ where: { userId } }),
prisma.userPortfolioHistory.deleteMany({ where: { userId } }),
prisma.user.update({ where: { id: userId }, data: { balance: STARTING_BALANCE } }), prisma.user.update({ where: { id: userId }, data: { balance: STARTING_BALANCE } }),
...tradeOps,
]) ])
return NextResponse.json({ ok: true }) return NextResponse.json({ ok: true })
+22 -4
View File
@@ -60,6 +60,7 @@ export default async function TradeHistoryPage({ searchParams }: PageProps) {
{trades.map((t) => { {trades.map((t) => {
const isLottery = t.type === 'LOTTERY_WIN' const isLottery = t.type === 'LOTTERY_WIN'
const isLiquidation = t.type === 'LIQUIDATE_LONG' || t.type === 'LIQUIDATE_SHORT' const isLiquidation = t.type === 'LIQUIDATE_LONG' || t.type === 'LIQUIDATE_SHORT'
const isSystemReset = t.type === 'DONATION' || t.type === 'BANKRUPTCY' || t.type === 'ACCOUNT_OPEN'
return ( return (
<div key={t.id} className="flex items-center justify-between px-4 py-3 text-sm"> <div key={t.id} className="flex items-center justify-between px-4 py-3 text-sm">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@@ -69,9 +70,13 @@ export default async function TradeHistoryPage({ searchParams }: PageProps) {
? 'bg-orange-500/15 text-orange-400' ? 'bg-orange-500/15 text-orange-400'
: isLottery : isLottery
? 'bg-amber-500/15 text-amber-400' ? 'bg-amber-500/15 text-amber-400'
: t.type.startsWith('BUY') : t.type === 'DONATION'
? 'bg-emerald-500/15 text-emerald-400' ? 'bg-purple-500/15 text-purple-400'
: 'bg-red-500/15 text-red-400' : t.type === 'ACCOUNT_OPEN'
? 'bg-emerald-500/15 text-emerald-400'
: t.type.startsWith('BUY')
? 'bg-emerald-500/15 text-emerald-400'
: 'bg-red-500/15 text-red-400'
}`} }`}
> >
{isLiquidation ? 'LIQUIDATED' : t.type.replace(/_/g, ' ')} {isLiquidation ? 'LIQUIDATED' : t.type.replace(/_/g, ' ')}
@@ -79,6 +84,14 @@ export default async function TradeHistoryPage({ searchParams }: PageProps) {
<div> <div>
{isLottery ? ( {isLottery ? (
<span className="text-amber-300">Lucky Dip</span> <span className="text-amber-300">Lucky Dip</span>
) : isSystemReset ? (
<span className="text-slate-300">
{t.type === 'DONATION'
? 'Account reset — donated'
: t.type === 'BANKRUPTCY'
? 'Bankruptcy declared'
: 'Account opened'}
</span>
) : ( ) : (
<Link <Link
href={`/hashtag/${t.hashtag!.tag}`} href={`/hashtag/${t.hashtag!.tag}`}
@@ -93,8 +106,13 @@ export default async function TradeHistoryPage({ searchParams }: PageProps) {
</div> </div>
</div> </div>
<div className="text-right"> <div className="text-right">
{isLottery ? ( {isLottery || t.type === 'ACCOUNT_OPEN' ? (
<p className="text-emerald-400 font-medium">{formatCurrency(t.profit)}</p> <p className="text-emerald-400 font-medium">{formatCurrency(t.profit)}</p>
) : isSystemReset ? (
<>
<p className="text-slate-500">{formatCurrency(t.total)}</p>
<p className={`text-xs ${pnlColor(t.profit)}`}>{formatPnl(t.profit)}</p>
</>
) : ( ) : (
<> <>
<p>{formatNumber(t.shares)} sh @ {formatCurrency(t.price)}</p> <p>{formatNumber(t.shares)} sh @ {formatCurrency(t.price)}</p>
@@ -11,6 +11,7 @@ interface Props {
export default function ResetAccountForm({ username }: Props) { export default function ResetAccountForm({ username }: Props) {
const router = useRouter() const router = useRouter()
const [open, setOpen] = useState(false) const [open, setOpen] = useState(false)
const [keepHistory, setKeepHistory] = useState(false)
const [confirm, setConfirm] = useState('') const [confirm, setConfirm] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [error, setError] = useState('') const [error, setError] = useState('')
@@ -25,7 +26,11 @@ export default function ResetAccountForm({ username }: Props) {
setLoading(true) setLoading(true)
setError('') setError('')
try { try {
const res = await fetch('/api/user/me/reset', { method: 'POST' }) const res = await fetch('/api/user/me/reset', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keepHistory }),
})
const data = await res.json() const data = await res.json()
if (!res.ok) { if (!res.ok) {
setError(data.error ?? 'Reset failed.') setError(data.error ?? 'Reset failed.')
@@ -53,14 +58,41 @@ export default function ResetAccountForm({ username }: Props) {
{open && ( {open && (
<form onSubmit={handleReset} className="mt-4 space-y-4 max-w-sm"> <form onSubmit={handleReset} className="mt-4 space-y-4 max-w-sm">
<p className="text-sm text-slate-400"> {keepHistory ? (
This will <span className="text-amber-400 font-medium">close all your open positions</span>, <p className="text-sm text-slate-400">
forfeit any fund investments, and reset your cash balance back to{' '} All positions and fund investments are closed. Your trade history is kept
<span className="text-white font-medium">$2,000</span>. Trade history is kept. and a <span className="text-purple-400 font-medium">DONATION</span> or{' '}
</p> <span className="text-red-400 font-medium">BANKRUPTCY</span> entry marks the
<p className="text-sm text-slate-400"> reset, followed by an{' '}
Think of it as going bankrupt and starting over this cannot be undone. <span className="text-emerald-400 font-medium">ACCOUNT OPEN</span>. Balance
</p> resets to <span className="text-white font-medium">$2,000</span>.
</p>
) : (
<p className="text-sm text-slate-400">
This will <span className="text-amber-400 font-medium">permanently erase</span> your entire
trade history, all positions, and fund investments, then reset your cash balance back to{' '}
<span className="text-white font-medium">$2,000</span>. A true clean slate.
</p>
)}
{/* Keep history toggle */}
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-slate-300">Keep trade history</p>
<p className="text-xs text-slate-500">Add reset bookmarks instead of erasing</p>
</div>
<button
type="button"
onClick={() => setKeepHistory((v) => !v)}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
keepHistory ? 'bg-amber-500' : 'bg-slate-600'
}`}
>
<span className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
keepHistory ? 'translate-x-6' : 'translate-x-1'
}`} />
</button>
</div>
<div> <div>
<label className="block text-xs text-slate-400 mb-1"> <label className="block text-xs text-slate-400 mb-1">
Type <span className="text-white font-mono">{username}</span> to confirm Type <span className="text-white font-mono">{username}</span> to confirm
+19 -6
View File
@@ -276,6 +276,7 @@ export default async function ProfilePage({ params }: Props) {
{user.trades.map((t) => { {user.trades.map((t) => {
const isLottery = t.type === 'LOTTERY_WIN' const isLottery = t.type === 'LOTTERY_WIN'
const isLiquidation = t.type === 'LIQUIDATE_LONG' || t.type === 'LIQUIDATE_SHORT' const isLiquidation = t.type === 'LIQUIDATE_LONG' || t.type === 'LIQUIDATE_SHORT'
const isSystemReset = t.type === 'DONATION' || t.type === 'BANKRUPTCY' || t.type === 'ACCOUNT_OPEN'
return ( return (
<div key={t.id} className="px-4 py-3 text-sm space-y-1.5"> <div key={t.id} className="px-4 py-3 text-sm space-y-1.5">
{/* Primary row: badge · hashtag/label · total */} {/* Primary row: badge · hashtag/label · total */}
@@ -286,15 +287,27 @@ export default async function ProfilePage({ params }: Props) {
? 'bg-orange-500/15 text-orange-400' ? 'bg-orange-500/15 text-orange-400'
: isLottery : isLottery
? 'bg-amber-500/15 text-amber-400' ? 'bg-amber-500/15 text-amber-400'
: t.type.startsWith('BUY') : t.type === 'DONATION'
? 'bg-emerald-500/15 text-emerald-400' ? 'bg-purple-500/15 text-purple-400'
: 'bg-red-500/15 text-red-400' : t.type === 'ACCOUNT_OPEN'
? 'bg-emerald-500/15 text-emerald-400'
: t.type.startsWith('BUY')
? 'bg-emerald-500/15 text-emerald-400'
: 'bg-red-500/15 text-red-400'
}`} }`}
> >
{isLiquidation ? 'LIQUIDATED' : t.type.replace(/_/g, ' ')} {isLiquidation ? 'LIQUIDATED' : t.type.replace(/_/g, ' ')}
</span> </span>
{isLottery ? ( {isLottery ? (
<span className="text-amber-300 font-medium flex-1 min-w-0">Lucky Dip</span> <span className="text-amber-300 font-medium flex-1 min-w-0">Lucky Dip</span>
) : isSystemReset ? (
<span className="text-slate-300 font-medium flex-1 min-w-0">
{t.type === 'DONATION'
? 'Account reset — donated'
: t.type === 'BANKRUPTCY'
? 'Bankruptcy declared'
: 'Account opened'}
</span>
) : ( ) : (
<Link <Link
href={`/hashtag/${t.hashtag!.tag}`} href={`/hashtag/${t.hashtag!.tag}`}
@@ -310,12 +323,12 @@ export default async function ProfilePage({ params }: Props) {
{/* Secondary row: time (left) · shares @ price (right) */} {/* Secondary row: time (left) · shares @ price (right) */}
<div className="flex items-center justify-between text-xs text-slate-500"> <div className="flex items-center justify-between text-xs text-slate-500">
<span>{formatDistanceToNow(t.createdAt, { addSuffix: true })}</span> <span>{formatDistanceToNow(t.createdAt, { addSuffix: true })}</span>
{!isLottery && ( {!isLottery && !isSystemReset && (
<span className="tabular-nums ml-3">{formatNumber(t.shares)} sh @ {formatCurrency(t.price)}</span> <span className="tabular-nums ml-3">{formatNumber(t.shares)} sh @ {formatCurrency(t.price)}</span>
)} )}
</div> </div>
{/* PnL: sell and liquidation trades */} {/* PnL: sell, liquidation, and reset trades */}
{(t.type === 'SELL_LONG' || t.type === 'SELL_SHORT' || isLiquidation) && ( {(t.type === 'SELL_LONG' || t.type === 'SELL_SHORT' || isLiquidation || t.type === 'DONATION' || t.type === 'BANKRUPTCY') && (
<div className={`text-xs text-right ${pnlColor(t.profit)}`}>{formatPnl(t.profit)}</div> <div className={`text-xs text-right ${pnlColor(t.profit)}`}>{formatPnl(t.profit)}</div>
)} )}
</div> </div>
+1 -1
View File
File diff suppressed because one or more lines are too long