Funds you manage page and fix Trade on behalf links

This commit is contained in:
2026-03-18 22:33:01 -04:00
parent bfd891e977
commit 039e2662f7
3 changed files with 64 additions and 11 deletions
+38 -1
View File
@@ -5,7 +5,7 @@ import { notFound } from 'next/navigation'
import { formatCurrency, formatNumber, pnlColor, formatPnl } from '@/lib/utils' import { formatCurrency, formatNumber, pnlColor, formatPnl } from '@/lib/utils'
import { getBalanceTier } from '@/lib/pricing' import { getBalanceTier } from '@/lib/pricing'
import Link from 'next/link' import Link from 'next/link'
import { TrendingUp, TrendingDown, Coins } from 'lucide-react' import { TrendingUp, TrendingDown, Coins, Building2 } from 'lucide-react'
import ChangePasswordForm from './ChangePasswordForm' import ChangePasswordForm from './ChangePasswordForm'
import AccountSettingsForm from './AccountSettingsForm' import AccountSettingsForm from './AccountSettingsForm'
@@ -38,6 +38,12 @@ export default async function ProfilePage({ params }: Props) {
take: 30, take: 30,
include: { hashtag: { select: { tag: true, displayTag: true } } }, include: { hashtag: { select: { tag: true, displayTag: true } } },
}, },
managedFunds: {
orderBy: { addedAt: 'asc' },
select: {
fund: { select: { id: true, name: true, slug: true } },
},
},
}, },
}) })
@@ -174,6 +180,37 @@ export default async function ProfilePage({ params }: Props) {
</section> </section>
)} )}
{/* Funds managed — only shown to the profile owner */}
{isOwn && user.managedFunds.length > 0 && (
<section>
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
<Building2 className="h-5 w-5 text-indigo-400" />
Funds you manage
</h2>
<div className="bg-surface-card border border-surface-border rounded-xl overflow-hidden">
<div className="divide-y divide-surface-border">
{user.managedFunds.map(({ fund }) => (
<div key={fund.id} className="flex items-center justify-between px-4 py-3">
<Link
href={`/fund/${fund.slug}`}
className="font-medium hover:text-indigo-300 transition-colors flex items-center gap-2"
>
<Building2 className="h-3.5 w-3.5 text-indigo-400 shrink-0" />
{fund.name}
</Link>
<Link
href={`/stocks?fund=${fund.slug}`}
className="text-xs text-indigo-400 hover:text-indigo-300 transition-colors"
>
Trade as this fund
</Link>
</div>
))}
</div>
</div>
</section>
)}
{/* Trade history */} {/* Trade history */}
{user.trades.length > 0 && ( {user.trades.length > 0 && (
<section> <section>
+25 -9
View File
@@ -13,7 +13,7 @@ type SortField = 'price' | 'tag' | 'change' | 'updated'
type SortDir = 'asc' | 'desc' type SortDir = 'asc' | 'desc'
interface PageProps { interface PageProps {
searchParams: { page?: string; sort?: string; dir?: string; tab?: string } searchParams: { page?: string; sort?: string; dir?: string; tab?: string; fund?: string }
} }
function SortLink({ function SortLink({
@@ -22,19 +22,22 @@ function SortLink({
currentSort, currentSort,
currentDir, currentDir,
page, page,
fund,
}: { }: {
field: SortField field: SortField
label: string label: string
currentSort: SortField currentSort: SortField
currentDir: SortDir currentDir: SortDir
page: number page: number
fund?: string
}) { }) {
const isActive = currentSort === field const isActive = currentSort === field
const nextDir: SortDir = isActive && currentDir === 'desc' ? 'asc' : 'desc' const nextDir: SortDir = isActive && currentDir === 'desc' ? 'asc' : 'desc'
const Icon = isActive ? (currentDir === 'desc' ? ArrowDown : ArrowUp) : ArrowUpDown const Icon = isActive ? (currentDir === 'desc' ? ArrowDown : ArrowUp) : ArrowUpDown
const fundParam = fund ? `&fund=${encodeURIComponent(fund)}` : ''
return ( return (
<Link <Link
href={`/stocks?page=1&sort=${field}&dir=${nextDir}&tab=stocks`} href={`/stocks?page=1&sort=${field}&dir=${nextDir}&tab=stocks${fundParam}`}
className={`flex items-center gap-1 hover:text-slate-200 transition-colors select-none ${isActive ? 'text-indigo-400' : 'text-slate-400'}`} className={`flex items-center gap-1 hover:text-slate-200 transition-colors select-none ${isActive ? 'text-indigo-400' : 'text-slate-400'}`}
> >
{label} {label}
@@ -45,6 +48,8 @@ function SortLink({
export default async function StocksPage({ searchParams }: PageProps) { export default async function StocksPage({ searchParams }: PageProps) {
const tab = searchParams.tab === 'funds' ? 'funds' : 'stocks' const tab = searchParams.tab === 'funds' ? 'funds' : 'stocks'
const fund = searchParams.fund ? decodeURIComponent(searchParams.fund) : undefined
const fundParam = fund ? `&fund=${encodeURIComponent(fund)}` : ''
const page = Math.max(1, parseInt(searchParams.page ?? '1', 10)) const page = Math.max(1, parseInt(searchParams.page ?? '1', 10))
const sort = (['price', 'tag', 'change', 'updated'].includes(searchParams.sort ?? '') const sort = (['price', 'tag', 'change', 'updated'].includes(searchParams.sort ?? '')
? searchParams.sort ? searchParams.sort
@@ -224,20 +229,31 @@ export default async function StocksPage({ searchParams }: PageProps) {
</Link> </Link>
</div> </div>
{/* Fund mode banner */}
{fund && (
<div className="flex items-center justify-between gap-3 bg-indigo-500/10 border border-indigo-500/20 rounded-xl px-4 py-3 text-sm">
<div className="flex items-center gap-2">
<Building2 className="h-4 w-4 text-indigo-400 shrink-0" />
<span className="text-indigo-200">Trading as <span className="font-semibold">{fund}</span> click any hashtag to trade on behalf of this fund</span>
</div>
<Link href="/stocks" className="text-xs text-slate-400 hover:text-slate-200 shrink-0">Exit fund mode ×</Link>
</div>
)}
{tab === 'stocks' && (<> {tab === 'stocks' && (<>
<div className="bg-surface-card border border-surface-border rounded-xl overflow-hidden"> <div className="bg-surface-card border border-surface-border rounded-xl overflow-hidden">
{/* Column headers */} {/* Column headers */}
<div className="grid grid-cols-[2fr_1fr_1fr_1fr_1fr] gap-4 px-4 py-2.5 border-b border-surface-border text-xs font-medium"> <div className="grid grid-cols-[2fr_1fr_1fr_1fr_1fr] gap-4 px-4 py-2.5 border-b border-surface-border text-xs font-medium">
<SortLink field="tag" label="Hashtag" currentSort={sort} currentDir={dir} page={page} /> <SortLink field="tag" label="Hashtag" currentSort={sort} currentDir={dir} page={page} fund={fund} />
<div className="text-right"> <div className="text-right">
<SortLink field="price" label="Price" currentSort={sort} currentDir={dir} page={page} /> <SortLink field="price" label="Price" currentSort={sort} currentDir={dir} page={page} fund={fund} />
</div> </div>
<div className="text-right"> <div className="text-right">
<SortLink field="change" label="Change" currentSort={sort} currentDir={dir} page={page} /> <SortLink field="change" label="Change" currentSort={sort} currentDir={dir} page={page} fund={fund} />
</div> </div>
<div className="text-right hidden sm:block text-slate-400">Posts/hr</div> <div className="text-right hidden sm:block text-slate-400">Posts/hr</div>
<div className="text-right"> <div className="text-right">
<SortLink field="updated" label="Updated" currentSort={sort} currentDir={dir} page={page} /> <SortLink field="updated" label="Updated" currentSort={sort} currentDir={dir} page={page} fund={fund} />
</div> </div>
</div> </div>
@@ -262,7 +278,7 @@ export default async function StocksPage({ searchParams }: PageProps) {
{(page - 1) * PAGE_SIZE + i + 1} {(page - 1) * PAGE_SIZE + i + 1}
</span> </span>
<Link <Link
href={`/hashtag/${stock.tag}`} href={`/hashtag/${stock.tag}${fund ? `?fund=${encodeURIComponent(fund)}` : ''}`}
className="font-medium hover:text-indigo-300 transition-colors truncate" className="font-medium hover:text-indigo-300 transition-colors truncate"
> >
#{stock.displayTag} #{stock.displayTag}
@@ -311,7 +327,7 @@ export default async function StocksPage({ searchParams }: PageProps) {
<div className="flex items-center justify-center gap-2"> <div className="flex items-center justify-center gap-2">
{page > 1 && ( {page > 1 && (
<Link <Link
href={`/stocks?page=${page - 1}&sort=${sort}&dir=${dir}&tab=stocks`} href={`/stocks?page=${page - 1}&sort=${sort}&dir=${dir}&tab=stocks${fundParam}`}
className="px-3 py-1.5 text-sm bg-surface-card border border-surface-border rounded-lg hover:border-indigo-500/50 transition-colors" className="px-3 py-1.5 text-sm bg-surface-card border border-surface-border rounded-lg hover:border-indigo-500/50 transition-colors"
> >
Prev Prev
@@ -322,7 +338,7 @@ export default async function StocksPage({ searchParams }: PageProps) {
</span> </span>
{page < totalPages && ( {page < totalPages && (
<Link <Link
href={`/stocks?page=${page + 1}&sort=${sort}&dir=${dir}&tab=stocks`} href={`/stocks?page=${page + 1}&sort=${sort}&dir=${dir}&tab=stocks${fundParam}`}
className="px-3 py-1.5 text-sm bg-surface-card border border-surface-border rounded-lg hover:border-indigo-500/50 transition-colors" className="px-3 py-1.5 text-sm bg-surface-card border border-surface-border rounded-lg hover:border-indigo-500/50 transition-colors"
> >
Next Next
+1 -1
View File
File diff suppressed because one or more lines are too long