Funds you manage page and fix Trade on behalf links
This commit is contained in:
@@ -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
@@ -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 →
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user