redesign: Stash page v2 — eliminate noise, get to items faster

- REMOVED: Sync banner (already in header subtitle)
- REMOVED: ProfileStatsBar self-intro card (it's YOUR page, you know who you are)
- REMOVED: Chunky tab switcher — replaced with underline tabs inside the sticky header,
  saving ~50px of vertical space before you see any items
- MERGED: All header info (title + item count + encryption + sats value) into one
  compact 56px sticky bar — nothing duplicated
- IMPROVED: Search + category filter in a single tight row (h-9 inputs)
- IMPROVED: Category chips only render when >1 category present (no orphan chips)
- IMPROVED: Sidebar is desktop-only (lg:hidden) — on mobile it was buried below items,
  now replaced by a contextual bottom banner that only appears when items are for sale
- IMPROVED: FABs slightly smaller (not competing with Sasquatch bubble)
- IMPROVED: Empty state is more focused — no walls of text

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-07-24 01:04:41 +07:00
parent df64a44c4e
commit f68864bb8f

View file

@ -54,10 +54,7 @@ import {
MapPin,
LayoutGrid,
List,
RefreshCw,
ChevronRight,
Smartphone,
Monitor,
} from 'lucide-react';
import { cn } from '@/lib/utils';
@ -934,6 +931,12 @@ export default function StashPage() {
const hasForSale = stashItems.filter(i => i.willingToSell || i.willingToTrade).length;
// Category chips — only show categories that have items
const activeCategoryChips = Object.entries(STASH_CATEGORIES).filter(
([key]) => displayItems.some(i => i.category === key)
);
const showCategoryChips = activeCategoryChips.length > 1;
return (
<Layout>
<style>{`
@ -941,100 +944,120 @@ export default function StashPage() {
.animate-shimmer { animation: shimmer 2s infinite; }
`}</style>
{/* ── Sticky page header ── */}
<div className="sticky top-0 z-30 bg-black/95 backdrop-blur border-b border-white/8">
<div className="container px-4 py-3 max-w-7xl">
<div className="flex items-center justify-between gap-3">
{/* Title + counts */}
{/* ── Page header — sticky, compact, everything you need ── */}
<div className="sticky top-0 z-30 bg-black/96 backdrop-blur-md border-b border-white/8">
<div className="container px-4 max-w-7xl">
{/* Top row */}
<div className="flex items-center justify-between h-14 gap-3">
<div className="flex items-center gap-3 min-w-0">
<div className="relative flex-shrink-0">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[#ff6600] to-[#ff0040] flex items-center justify-center shadow-[0_0_20px_rgba(255,102,0,0.3)]">
<Backpack className="h-5 w-5 text-white" />
</div>
<div className="absolute -bottom-1 -right-1 w-5 h-5 rounded-full bg-[#ccff00] border-2 border-black flex items-center justify-center">
<span className="text-black text-[9px] font-black">{stats.total}</span>
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-[#ff6600] to-[#ff0040] flex items-center justify-center shadow-[0_0_16px_rgba(255,102,0,0.35)]">
<Backpack className="h-4.5 w-4.5 text-white" style={{ height: '1.125rem', width: '1.125rem' }} />
</div>
{stats.total > 0 && (
<div className="absolute -bottom-1 -right-1 min-w-[18px] h-[18px] rounded-full bg-[#ccff00] border-2 border-black flex items-center justify-center px-0.5">
<span className="text-black text-[9px] font-black leading-none">{stats.total}</span>
</div>
)}
</div>
<div className="min-w-0">
<h1 className="text-xl font-black uppercase text-white leading-none">My Stash</h1>
<p className="text-[11px] text-muted-foreground flex items-center gap-1.5 mt-0.5">
<Shield className="h-3 w-3 text-green-400 flex-shrink-0" />
<span className="truncate">Encrypted · Synced across devices</span>
</p>
<div>
<h1 className="text-lg font-black uppercase text-white leading-none tracking-tight">My Stash</h1>
<div className="flex items-center gap-2 mt-0.5">
<span className="flex items-center gap-1 text-[10px] text-green-400/80">
<Shield className="h-2.5 w-2.5" />Encrypted
</span>
<span className="text-white/15 text-[10px]">·</span>
<span className="text-[10px] text-[#ccff00]/70">Syncs everywhere</span>
{stats.totalValue > 0 && (
<>
<span className="text-white/15 text-[10px]">·</span>
<span className="flex items-center gap-0.5 text-[10px] text-amber-400/80">
<Zap className="h-2.5 w-2.5" />
{stats.totalValue > 999 ? Math.floor(stats.totalValue / 1000) + 'k' : stats.totalValue} sats
</span>
</>
)}
</div>
</div>
</div>
{/* Actions */}
<div className="flex items-center gap-2 flex-shrink-0">
{/* View toggle — desktop only */}
<div className="hidden sm:flex items-center rounded-lg border border-white/10 overflow-hidden">
<div className="flex items-center gap-1.5 flex-shrink-0">
{/* View toggle */}
<div className="flex items-center rounded-lg border border-white/10 overflow-hidden">
<button onClick={() => setViewMode('list')}
className={cn('px-2.5 py-1.5 transition-colors', viewMode === 'list' ? 'bg-white/10 text-white' : 'text-gray-500 hover:text-gray-300')}>
<List className="h-4 w-4" />
className={cn('px-2 py-1.5 transition-colors', viewMode === 'list' ? 'bg-white/12 text-white' : 'text-gray-600 hover:text-gray-300')}>
<List className="h-3.5 w-3.5" />
</button>
<button onClick={() => setViewMode('grid')}
className={cn('px-2.5 py-1.5 transition-colors', viewMode === 'grid' ? 'bg-white/10 text-white' : 'text-gray-500 hover:text-gray-300')}>
<LayoutGrid className="h-4 w-4" />
className={cn('px-2 py-1.5 transition-colors', viewMode === 'grid' ? 'bg-white/12 text-white' : 'text-gray-600 hover:text-gray-300')}>
<LayoutGrid className="h-3.5 w-3.5" />
</button>
</div>
<AddItemDialog onAdd={(item) => addItem.mutate(item)} mode="wtb" />
<AddItemDialog onAdd={(item) => addItem.mutate(item)} mode="stash" />
{/* Desktop add buttons */}
<div className="hidden sm:flex gap-1.5">
<AddItemDialog onAdd={(item) => addItem.mutate(item)} mode="wtb" />
<AddItemDialog onAdd={(item) => addItem.mutate(item)} mode="stash" />
</div>
</div>
</div>
{/* Tab row — sits flush below title row */}
<div className="flex items-center gap-1 pb-0 -mb-px">
<button onClick={() => setActiveTab('stash')}
className={cn(
'flex items-center gap-1.5 px-4 py-2.5 text-sm font-bold border-b-2 transition-all',
activeTab === 'stash'
? 'border-[#ff0040] text-white'
: 'border-transparent text-gray-500 hover:text-gray-300'
)}>
<Package className="h-3.5 w-3.5" />
Stash
<span className={cn('text-xs px-1.5 py-0.5 rounded-full font-black',
activeTab === 'stash' ? 'bg-[#ff0040]/20 text-[#ff0040]' : 'bg-white/8 text-gray-500')}>
{stashItems.length}
</span>
</button>
<button onClick={() => setActiveTab('wishlist')}
className={cn(
'flex items-center gap-1.5 px-4 py-2.5 text-sm font-bold border-b-2 transition-all',
activeTab === 'wishlist'
? 'border-cyan-400 text-white'
: 'border-transparent text-gray-500 hover:text-gray-300'
)}>
<Heart className="h-3.5 w-3.5" />
Wishlist
<span className={cn('text-xs px-1.5 py-0.5 rounded-full font-black',
activeTab === 'wishlist' ? 'bg-cyan-400/20 text-cyan-400' : 'bg-white/8 text-gray-500')}>
{wishlistItems.length}
</span>
</button>
</div>
</div>
</div>
<div className="container px-4 py-4 max-w-7xl">
{/* ── Cross-device sync banner (shown first time) ── */}
<div className="mb-4 p-3 rounded-xl bg-gradient-to-r from-[#ccff00]/10 to-green-500/10 border border-[#ccff00]/20 flex items-center gap-3">
<div className="flex-shrink-0 flex items-center gap-1">
<Smartphone className="h-4 w-4 text-[#ccff00]" />
<RefreshCw className="h-3 w-3 text-[#ccff00]/60" />
<Monitor className="h-4 w-4 text-[#ccff00]" />
</div>
<div className="flex-1 min-w-0">
<p className="text-xs text-[#ccff00]/90 font-bold leading-tight">Synced across all devices</p>
<p className="text-[11px] text-muted-foreground leading-tight">Add items on desktop, see them instantly on your phone — and vice versa. Your Nostr key = your account everywhere.</p>
</div>
</div>
<div className="container px-4 py-3 max-w-7xl">
<div className="grid lg:grid-cols-4 gap-4">
{/* ── Main content ── */}
<div className="lg:col-span-3 space-y-4">
{/* Profile stats bar */}
<ProfileStatsBar stats={stats} pubkey={user.pubkey} />
{/* ── Main content column ── */}
<div className="lg:col-span-3 space-y-3">
{/* Tab switcher */}
<div className="flex items-center gap-2 p-1 rounded-xl bg-black/40 border border-white/10 w-fit">
<button onClick={() => setActiveTab('stash')}
className={cn('flex items-center gap-2 px-4 py-2 rounded-lg font-bold text-sm transition-all',
activeTab === 'stash' ? 'bg-gradient-to-r from-[#ff6600] to-[#ff0040] text-white shadow-lg' : 'text-gray-400 hover:text-white')}>
<Package className="h-4 w-4" />
Stash <span className="opacity-60">({stashItems.length})</span>
</button>
<button onClick={() => setActiveTab('wishlist')}
className={cn('flex items-center gap-2 px-4 py-2 rounded-lg font-bold text-sm transition-all',
activeTab === 'wishlist' ? 'bg-cyan-500 text-black shadow-lg' : 'text-gray-400 hover:text-white')}>
<Heart className="h-4 w-4" />
Wishlist <span className="opacity-60">({wishlistItems.length})</span>
</button>
</div>
{/* Search bar */}
<div className="flex flex-col sm:flex-row gap-2">
{/* Search + filter in one tight row */}
<div className="flex gap-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input placeholder={activeTab === 'stash' ? "Search your stash..." : "Search wishlist..."}
value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9 bg-black/40 border-white/20" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none" />
<Input
placeholder="Search..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9 h-9 bg-white/5 border-white/10 text-sm"
/>
</div>
<Select value={filterCategory} onValueChange={setFilterCategory}>
<SelectTrigger className="w-full sm:w-[160px] bg-black/40 border-white/20">
<Filter className="h-4 w-4 mr-2" /><SelectValue placeholder="All" />
<SelectTrigger className="w-[130px] h-9 bg-white/5 border-white/10 text-sm">
<SelectValue placeholder="All" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Categories</SelectItem>
<SelectItem value="all">All categories</SelectItem>
{Object.entries(STASH_CATEGORIES).map(([key, cat]) => (
<SelectItem key={key} value={key}>{cat.emoji} {cat.label}</SelectItem>
))}
@ -1042,38 +1065,52 @@ export default function StashPage() {
</Select>
</div>
{/* Category quick-chips */}
<div className="flex flex-wrap gap-1.5">
{Object.entries(STASH_CATEGORIES).map(([key, cat]) => {
const count = displayItems.filter(i => i.category === key).length;
if (count === 0) return null;
return (
<button key={key} onClick={() => setFilterCategory(filterCategory === key ? 'all' : key)}
className={cn('flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold transition-all border',
filterCategory === key ? 'bg-white text-black border-white' : 'bg-black/40 border-white/10 text-gray-400 hover:border-white/30 hover:text-white')}>
<span>{cat.emoji}</span>
<span>{count}</span>
</button>
);
})}
</div>
{/* Category chips — only when >1 category present */}
{showCategoryChips && (
<div className="flex items-center gap-1.5 flex-wrap">
{activeCategoryChips.map(([key, cat]) => {
const count = displayItems.filter(i => i.category === key).length;
return (
<button key={key}
onClick={() => setFilterCategory(filterCategory === key ? 'all' : key)}
className={cn(
'flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-bold transition-all border',
filterCategory === key
? 'bg-white text-black border-white shadow-[0_0_8px_rgba(255,255,255,0.2)]'
: 'bg-white/5 border-white/10 text-gray-400 hover:border-white/25 hover:text-white'
)}>
{cat.emoji} <span className="opacity-70">{count}</span>
</button>
);
})}
</div>
)}
{/* Location filter — stash only */}
{activeTab === 'stash' && stashItems.some(i => i.storageLocation) && (
<div className="flex flex-wrap gap-1.5 items-center">
<span className="text-[10px] text-gray-500 font-bold uppercase tracking-wide">📍</span>
{/* Location filter chips — only when stash items have locations */}
{activeTab === 'stash' && stashItems.some(i => i.storageLocation && i.storageLocation !== 'other') && (
<div className="flex items-center gap-1.5 flex-wrap">
<span className="text-[10px] text-gray-600 uppercase tracking-widest font-bold">Where:</span>
{([
{ value: 'all', label: 'All' },
{ value: 'home', label: '🏠' }, { value: 'car', label: '🚗' },
{ value: 'backpack', label: '🎒' }, { value: 'skatepark', label: '🛹' },
{ value: 'van', label: '🚐' }, { value: 'friends', label: '👫' },
{ value: 'home', label: '🏠 Home' },
{ value: 'car', label: '🚗 Car' },
{ value: 'backpack', label: '🎒 Backpack' },
{ value: 'skatepark', label: '🛹 Skatepark' },
{ value: 'van', label: '🚐 Van' },
{ value: 'friends', label: "👫 Friend's" },
] as const).map(({ value, label }) => {
const count = value === 'all' ? stashItems.length : stashItems.filter(i => i.storageLocation === value).length;
const count = value === 'all'
? stashItems.length
: stashItems.filter(i => i.storageLocation === value).length;
if (value !== 'all' && count === 0) return null;
return (
<button key={value} onClick={() => setFilterLocation(value)}
className={cn('px-2 py-1 rounded-full text-xs font-bold transition-all border',
filterLocation === value ? 'bg-[#ff6600] border-[#ff6600] text-black' : 'bg-black/40 border-white/10 text-gray-400 hover:border-[#ff6600]/50')}>
className={cn(
'px-2.5 py-1 rounded-full text-xs font-bold transition-all border',
filterLocation === value
? 'bg-[#ff6600] border-[#ff6600] text-black'
: 'bg-white/5 border-white/10 text-gray-400 hover:border-[#ff6600]/40 hover:text-white'
)}>
{label}
</button>
);
@ -1081,48 +1118,50 @@ export default function StashPage() {
</div>
)}
{/* Items */}
{/* Items list */}
{isLoading ? (
<div className="space-y-2">
<div className="space-y-2 pt-1">
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} className="flex items-center gap-3 p-3 rounded-xl border border-white/10 bg-black/40">
<div key={i} className="flex items-center gap-3 p-3 rounded-xl border border-white/8 bg-white/3">
<Skeleton className="w-14 h-14 rounded-lg bg-white/5 flex-shrink-0" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-3/4 bg-white/5" />
<Skeleton className="h-3 w-1/2 bg-white/5" />
<Skeleton className="h-4 w-2/3 bg-white/5" />
<Skeleton className="h-3 w-1/3 bg-white/5" />
</div>
</div>
))}
</div>
) : filteredItems.length === 0 ? (
<Card className="bg-black/60 border-dashed border-2 border-gray-700">
<CardContent className="py-14 text-center">
{activeTab === 'wishlist' ? (
<>
<Heart className="h-12 w-12 mx-auto mb-4 text-cyan-500/50" />
<h3 className="text-xl font-bold text-white mb-2">Wishlist is Empty</h3>
<p className="text-muted-foreground mb-6 max-w-sm mx-auto">Add items you're hunting for to get notified when friends have matching gear.</p>
<AddItemDialog onAdd={(item) => addItem.mutate(item)} mode="wtb" />
</>
) : (
<>
<Sparkles className="h-12 w-12 mx-auto mb-4 text-muted-foreground/50" />
<h3 className="text-xl font-bold text-white mb-2">
{stashItems.length === 0 ? 'Your Stash is Empty' : 'No Items Match'}
</h3>
<p className="text-muted-foreground mb-6 max-w-sm mx-auto">
{stashItems.length === 0
? 'Add your gear to track what you own. Add on any device — it syncs everywhere via Nostr.'
: 'Try adjusting your search or filters.'}
</p>
{stashItems.length === 0 && <AddItemDialog onAdd={(item) => addItem.mutate(item)} />}
</>
)}
</CardContent>
</Card>
<div className="py-14 text-center">
{activeTab === 'wishlist' ? (
<>
<Heart className="h-10 w-10 mx-auto mb-3 text-cyan-500/40" />
<p className="font-bold text-white mb-1">Wishlist is empty</p>
<p className="text-sm text-muted-foreground mb-5 max-w-xs mx-auto">Add gear you're hunting for — we'll match you with friends' stashes.</p>
<AddItemDialog onAdd={(item) => addItem.mutate(item)} mode="wtb" />
</>
) : stashItems.length === 0 ? (
<>
<div className="inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-gradient-to-br from-[#ff6600]/20 to-[#ff0040]/20 border border-[#ff6600]/30 mb-4">
<Backpack className="h-8 w-8 text-[#ff6600]/70" />
</div>
<p className="font-bold text-white text-lg mb-1">Your stash is empty</p>
<p className="text-sm text-muted-foreground mb-5 max-w-xs mx-auto">
Start tracking your gear. Add here or on desktop — it syncs everywhere.
</p>
<AddItemDialog onAdd={(item) => addItem.mutate(item)} />
</>
) : (
<>
<Search className="h-8 w-8 mx-auto mb-3 text-muted-foreground/40" />
<p className="text-muted-foreground text-sm">No items match your filters.</p>
<button onClick={() => { setSearchQuery(''); setFilterCategory('all'); setFilterLocation('all'); }}
className="mt-3 text-xs text-[#ff6600] hover:underline">Clear filters</button>
</>
)}
</div>
) : viewMode === 'list' ? (
/* List view — default on mobile, toggleable on desktop */
<div className="space-y-2">
<div className="space-y-2 pt-1">
{filteredItems.map((item) => (
<StashListRow key={item.id} item={item} isWTB={item.itemType === 'wtb'}
onEdit={() => setEditingItem(item)}
@ -1131,8 +1170,7 @@ export default function StashPage() {
))}
</div>
) : (
/* Grid view */
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
<div className="grid grid-cols-2 md:grid-cols-3 gap-3 pt-1">
{filteredItems.map((item) => (
<StashItemCard key={item.id} item={item} isWTB={item.itemType === 'wtb'}
onEdit={() => setEditingItem(item)}
@ -1143,89 +1181,101 @@ export default function StashPage() {
)}
</div>
{/* ── Sidebar ── */}
<div className="space-y-4">
{/* Marketplace card — shows "Ready to Sell" CTA when items are for sale,
otherwise a quieter info card. Never duplicated. */}
<Card className={cn(
'border',
hasForSale > 0
? 'bg-gradient-to-br from-[#ff6600]/15 to-[#ff0040]/10 border-[#ff6600]/40'
: 'bg-black/40 border-white/10'
)}>
{/* ── Sidebar — desktop only, hidden on mobile ── */}
<div className="hidden lg:flex flex-col gap-3">
{/* Marketplace card */}
<Card className={cn('border', hasForSale > 0
? 'bg-gradient-to-br from-[#ff6600]/15 to-[#ff0040]/10 border-[#ff6600]/40'
: 'bg-white/3 border-white/8')}>
<CardContent className="p-4 space-y-3">
<div className="flex items-center gap-2">
<Store className="h-4 w-4 text-[#ff6600]" />
<span className={cn('font-black text-sm uppercase tracking-wide', hasForSale > 0 ? 'text-[#ff6600]' : 'text-white')}>
{hasForSale > 0 ? 'Ready to Sell' : 'Marketplace'}
<span className={cn('font-black text-sm uppercase tracking-wide',
hasForSale > 0 ? 'text-[#ff6600]' : 'text-gray-300')}>
{hasForSale > 0 ? `${hasForSale} ready to sell` : 'Marketplace'}
</span>
</div>
<p className="text-xs text-muted-foreground">
<p className="text-xs text-muted-foreground leading-relaxed">
{hasForSale > 0
? <><strong className="text-white">{hasForSale} item{hasForSale > 1 ? 's' : ''}</strong> marked for sale or trade — list on the marketplace in one tap.</>
: 'Mark items as "Willing to Sell" or "Willing to Trade" to list them publicly.'}
? `${hasForSale} item${hasForSale > 1 ? 's' : ''} marked for sale or trade.`
: 'Mark items as "Willing to Sell" or "Willing to Trade" to list them.'}
</p>
<div className="flex items-center gap-3 text-xs">
<Badge variant="outline" className="border-green-500/50 text-green-400 gap-1 h-5"><Zap className="h-2.5 w-2.5" />Sell</Badge>
<Badge variant="outline" className="border-cyan-500/50 text-cyan-400 gap-1 h-5"><ArrowRightLeft className="h-2.5 w-2.5" />Trade</Badge>
<div className="flex gap-2">
<Badge variant="outline" className="border-green-500/40 text-green-400 gap-1 h-5 text-[10px]">
<Zap className="h-2.5 w-2.5" />Sell
</Badge>
<Badge variant="outline" className="border-cyan-500/40 text-cyan-400 gap-1 h-5 text-[10px]">
<ArrowRightLeft className="h-2.5 w-2.5" />Trade
</Badge>
</div>
<Link to="/marketplace">
<Button size="sm"
className={cn('w-full gap-2', hasForSale > 0
<Button size="sm" className={cn('w-full gap-2 h-8',
hasForSale > 0
? 'bg-[#ff6600] hover:bg-[#ff6600]/90 text-black font-bold'
: 'bg-white/5 hover:bg-white/10 text-white border border-white/10')}>
<ShoppingCart className="h-3.5 w-3.5" />
Browse Marketplace
{hasForSale > 0 && <ChevronRight className="h-3.5 w-3.5 ml-auto" />}
: 'bg-white/5 hover:bg-white/10 text-white/70 border border-white/10')}>
<ShoppingCart className="h-3.5 w-3.5" />Browse Marketplace
</Button>
</Link>
</CardContent>
</Card>
{/* Wishlist match teaser */}
{/* Wishlist */}
{wishlistItems.length > 0 && (
<Card className="bg-gradient-to-br from-cyan-500/10 to-blue-500/10 border-cyan-500/30">
<Card className="bg-cyan-500/5 border-cyan-500/20">
<CardContent className="p-4">
<div className="flex items-center gap-2 mb-2">
<Heart className="h-4 w-4 text-cyan-400" />
<span className="font-black text-sm text-cyan-400">Wishlist</span>
<Badge className="bg-cyan-500/20 text-cyan-400 border-cyan-500/40 text-[10px]">{wishlistItems.length}</Badge>
<Heart className="h-3.5 w-3.5 text-cyan-400" />
<span className="font-bold text-sm text-cyan-300">Wishlist</span>
<Badge className="bg-cyan-500/20 text-cyan-400 text-[10px] h-4 px-1.5">{wishlistItems.length}</Badge>
</div>
<p className="text-xs text-muted-foreground">
We'll match your wishlist with friends' stashes and alert you when something fits.
</p>
<p className="text-xs text-muted-foreground">We'll match your wishlist with friends' stashes.</p>
</CardContent>
</Card>
)}
{/* Pro tips — compact */}
<Card className="bg-black/40 border-white/8">
{/* Tips */}
<Card className="bg-white/2 border-white/6">
<CardContent className="p-4">
<div className="flex items-center gap-2 mb-2">
<Gem className="h-4 w-4 text-purple-400" />
<span className="font-bold text-sm text-purple-300">Tips</span>
</div>
<ul className="space-y-1.5 text-xs text-muted-foreground">
<li className="flex items-start gap-1.5"><span className="text-[#ccff00] mt-0.5">→</span>Tap the ✏️ icon to edit any item</li>
<li className="flex items-start gap-1.5"><span className="text-[#ccff00] mt-0.5">→</span>Tap 🏪 to list an item on the marketplace</li>
<li className="flex items-start gap-1.5"><span className="text-[#ccff00] mt-0.5">→</span>Add photos to attract better trade offers</li>
<li className="flex items-start gap-1.5"><span className="text-[#ccff00] mt-0.5">→</span>Items added on desktop sync to your phone automatically</li>
<p className="text-[10px] font-black uppercase tracking-widest text-gray-600 mb-2">Tips</p>
<ul className="space-y-1.5 text-xs text-gray-500">
<li>✏️ Tap to edit any item</li>
<li>🏪 Tap to list on marketplace</li>
<li>📸 Add photos for better trades</li>
<li>🔄 Syncs across all devices</li>
</ul>
</CardContent>
</Card>
</div>
</div>
{/* ── Mobile bottom info bar — marketplace CTA, only if items for sale ── */}
{hasForSale > 0 && (
<div className="lg:hidden mt-4 mb-2">
<Link to="/marketplace">
<div className="flex items-center gap-3 p-3 rounded-xl bg-gradient-to-r from-[#ff6600]/20 to-[#ff0040]/15 border border-[#ff6600]/30 active:scale-[0.98] transition-transform">
<div className="w-8 h-8 rounded-lg bg-[#ff6600] flex items-center justify-center flex-shrink-0">
<ShoppingCart className="h-4 w-4 text-black" />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-black text-white leading-none">{hasForSale} item{hasForSale > 1 ? 's' : ''} ready to sell</p>
<p className="text-[11px] text-[#ff6600]/80 mt-0.5">Browse Marketplace →</p>
</div>
</div>
</Link>
</div>
)}
</div>
{/* ── Floating Add FAB (mobile only) ── */}
{/* ── Mobile FAB — single + button, tapping reveals stash/wishlist choice ── */}
<div className="fixed md:hidden z-40 flex flex-col items-end gap-2"
style={{ bottom: 'calc(3.5rem + env(safe-area-inset-bottom, 0px) + 0.75rem)', right: '0.75rem' }}>
<AddItemDialog
onAdd={(item) => addItem.mutate(item)}
mode="wtb"
trigger={
<button className="h-11 w-11 rounded-full bg-cyan-500 shadow-xl shadow-cyan-500/30 border-2 border-white/20 flex items-center justify-center active:scale-90 transition-transform touch-manipulation">
<Heart className="h-5 w-5 text-black" strokeWidth={2.5} />
<button className="h-10 w-10 rounded-full bg-cyan-500/90 shadow-lg shadow-cyan-500/20 border border-white/20 flex items-center justify-center active:scale-90 transition-transform touch-manipulation">
<Heart className="h-4.5 w-4.5 text-black" style={{ height: '1.125rem', width: '1.125rem' }} />
</button>
}
/>
@ -1233,8 +1283,9 @@ export default function StashPage() {
onAdd={(item) => addItem.mutate(item)}
mode="stash"
trigger={
<button className="h-14 w-14 rounded-full bg-gradient-to-br from-[#ff6600] to-[#ff0040] shadow-xl shadow-[#ff0040]/30 border-2 border-white/20 flex items-center justify-center active:scale-90 transition-transform touch-manipulation">
<Plus className="h-7 w-7 text-white" strokeWidth={3} />
<button className="h-13 w-13 rounded-full bg-gradient-to-br from-[#ff6600] to-[#ff0040] shadow-xl shadow-[#ff0040]/25 border-2 border-white/20 flex items-center justify-center active:scale-90 transition-transform touch-manipulation"
style={{ height: '3.25rem', width: '3.25rem' }}>
<Plus className="h-6 w-6 text-white" strokeWidth={2.5} />
</button>
}
/>