Flatten authenticated sidebar IA, move admin to the profile menu, right-align TopSearchBar actions, and replace placeholder dashboard panels with data from /api/user/stats and /api/user-cards. Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
4.4 KiB
JavaScript
154 lines
4.4 KiB
JavaScript
import Link from 'next/link';
|
|
import { VOCAB } from '../lib/collection-vocabulary.js';
|
|
|
|
/**
|
|
* Last-added card spotlight on /dashboard — real ownership data only.
|
|
* Market charts and watchlist ship in a follow-up convoy.
|
|
*/
|
|
export default function DashboardCardSpotlight({ card = null, loading = false }) {
|
|
if (loading) {
|
|
return (
|
|
<aside className="glass-panel rounded-2xl p-5 sm:p-6">
|
|
<div
|
|
className="aspect-[2.5/3.5] rounded-lg motion-essential animate-pulse mb-4"
|
|
style={{ backgroundColor: 'var(--bg-tertiary)' }}
|
|
aria-hidden="true"
|
|
/>
|
|
<div
|
|
className="h-4 rounded motion-essential animate-pulse mb-2"
|
|
style={{ backgroundColor: 'var(--bg-tertiary)' }}
|
|
/>
|
|
<div
|
|
className="h-4 rounded motion-essential animate-pulse w-2/3"
|
|
style={{ backgroundColor: 'var(--bg-tertiary)' }}
|
|
/>
|
|
</aside>
|
|
);
|
|
}
|
|
|
|
if (!card) {
|
|
return (
|
|
<aside className="glass-panel rounded-2xl p-5 sm:p-6">
|
|
<h2
|
|
className="text-lg font-bold mb-3"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
Latest card
|
|
</h2>
|
|
<p className="text-sm mb-4" style={{ color: 'var(--text-secondary)' }}>
|
|
Add cards to {VOCAB.MY_COLLECTION} to see details here.
|
|
</p>
|
|
<Link href="/scanner">
|
|
<span
|
|
className="text-sm font-medium hover:underline"
|
|
style={{ color: 'var(--accent-ember)' }}
|
|
>
|
|
Open scanner
|
|
</span>
|
|
</Link>
|
|
</aside>
|
|
);
|
|
}
|
|
|
|
const cardId = card.card_id ?? card.id;
|
|
const marketPrice = card.market_price ?? card.marketPrice;
|
|
const priceLabel =
|
|
marketPrice != null && !Number.isNaN(Number(marketPrice))
|
|
? `$${Number(marketPrice).toFixed(2)}`
|
|
: '—';
|
|
|
|
return (
|
|
<aside className="glass-panel rounded-2xl p-5 sm:p-6 space-y-4">
|
|
<header>
|
|
<h2
|
|
className="text-lg font-bold"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
Latest card
|
|
</h2>
|
|
</header>
|
|
|
|
<Link
|
|
href={`/card/${cardId}`}
|
|
className="card-grid-outer-glow aspect-[2.5/3.5] rounded-lg overflow-hidden block focus:outline-none focus:ring-2"
|
|
style={{
|
|
backgroundColor: 'var(--bg-tertiary)',
|
|
'--tw-ring-color': 'var(--accent-ember)',
|
|
}}
|
|
>
|
|
{card.image_url ? (
|
|
// eslint-disable-next-line @next/next/no-img-element
|
|
<img
|
|
src={card.image_url}
|
|
alt={card.name ?? 'Card'}
|
|
className="w-full h-full object-cover"
|
|
/>
|
|
) : (
|
|
<div
|
|
className="w-full h-full flex items-center justify-center p-4 text-center text-sm"
|
|
style={{ color: 'var(--text-secondary)' }}
|
|
>
|
|
{card.name ?? 'Card'}
|
|
</div>
|
|
)}
|
|
</Link>
|
|
|
|
<div>
|
|
<div
|
|
className="text-lg font-bold"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{card.name}
|
|
</div>
|
|
{card.set_name && (
|
|
<div className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
|
{card.set_name}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<dl className="grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
|
|
{card.rarity && (
|
|
<>
|
|
<dt style={{ color: 'var(--text-secondary)' }}>Rarity</dt>
|
|
<dd
|
|
className="font-semibold text-right"
|
|
style={{ color: 'var(--accent-ember)' }}
|
|
>
|
|
{card.rarity}
|
|
</dd>
|
|
</>
|
|
)}
|
|
{card.game && (
|
|
<>
|
|
<dt style={{ color: 'var(--text-secondary)' }}>Game</dt>
|
|
<dd
|
|
className="font-medium text-right"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{card.game}
|
|
</dd>
|
|
</>
|
|
)}
|
|
{card.quantity != null && (
|
|
<>
|
|
<dt style={{ color: 'var(--text-secondary)' }}>Owned</dt>
|
|
<dd
|
|
className="font-medium text-right"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{card.quantity}
|
|
</dd>
|
|
</>
|
|
)}
|
|
<dt style={{ color: 'var(--text-secondary)' }}>Market</dt>
|
|
<dd
|
|
className="font-semibold text-right"
|
|
style={{ color: 'var(--text-primary)' }}
|
|
>
|
|
{priceLabel}
|
|
</dd>
|
|
</dl>
|
|
</aside>
|
|
);
|
|
}
|