fix(ux): Batch F part 3 — bounty ranks, Lab tab grid, contacts search
- Bounty leaderboards no longer slice to top 50 before computing the current user's rank (anyone outside the top 50 never saw their position), and each row no longer prints the count twice - Lab craft tabs: 4-up grid (11 tabs were laid out 5-across, leaving a lone 'Judges' orphan row); stale comment corrected - Contacts search placeholder now says it filters by npub (it never matched the displayed names)
This commit is contained in:
parent
a464913d6f
commit
760bc9b82c
3 changed files with 10 additions and 14 deletions
|
|
@ -100,8 +100,8 @@ function LeaderboardEntry({
|
||||||
<Badge className="bg-[#ff0040] text-white text-[10px] px-1.5 py-0">You</Badge>
|
<Badge className="bg-[#ff0040] text-white text-[10px] px-1.5 py-0">You</Badge>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500">
|
<p className="text-xs text-gray-500 capitalize">
|
||||||
{count} {getTypeLabel()}
|
{getTypeLabel()}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -153,8 +153,7 @@ export default function BountyLeaderboardPage() {
|
||||||
|
|
||||||
return Object.entries(countByPubkey)
|
return Object.entries(countByPubkey)
|
||||||
.map(([pubkey, count]) => ({ pubkey, count }))
|
.map(([pubkey, count]) => ({ pubkey, count }))
|
||||||
.sort((a, b) => b.count - a.count)
|
.sort((a, b) => b.count - a.count);
|
||||||
.slice(0, 50);
|
|
||||||
}, [shops]);
|
}, [shops]);
|
||||||
|
|
||||||
// Calculate spot leaderboard
|
// Calculate spot leaderboard
|
||||||
|
|
@ -168,8 +167,7 @@ export default function BountyLeaderboardPage() {
|
||||||
|
|
||||||
return Object.entries(countByPubkey)
|
return Object.entries(countByPubkey)
|
||||||
.map(([pubkey, count]) => ({ pubkey, count }))
|
.map(([pubkey, count]) => ({ pubkey, count }))
|
||||||
.sort((a, b) => b.count - a.count)
|
.sort((a, b) => b.count - a.count);
|
||||||
.slice(0, 50);
|
|
||||||
}, [spots]);
|
}, [spots]);
|
||||||
|
|
||||||
// Combined leaderboard (total contributions)
|
// Combined leaderboard (total contributions)
|
||||||
|
|
@ -190,8 +188,7 @@ export default function BountyLeaderboardPage() {
|
||||||
|
|
||||||
return Object.entries(combined)
|
return Object.entries(combined)
|
||||||
.map(([pubkey, data]) => ({ pubkey, ...data }))
|
.map(([pubkey, data]) => ({ pubkey, ...data }))
|
||||||
.sort((a, b) => b.total - a.total)
|
.sort((a, b) => b.total - a.total);
|
||||||
.slice(0, 50);
|
|
||||||
}, [shops, spots]);
|
}, [shops, spots]);
|
||||||
|
|
||||||
// Find user's rank
|
// Find user's rank
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ export default function ContactsPage() {
|
||||||
type="text"
|
type="text"
|
||||||
value={query}
|
value={query}
|
||||||
onChange={(e) => setQuery(e.target.value)}
|
onChange={(e) => setQuery(e.target.value)}
|
||||||
placeholder="Paste an npub to filter…"
|
placeholder="Filter by npub…"
|
||||||
className="h-10 w-full rounded-xl border border-white/10 bg-white/5 pl-9 pr-9 text-sm text-white placeholder:text-gray-600 focus:border-teal-500/50 focus:outline-none"
|
className="h-10 w-full rounded-xl border border-white/10 bg-white/5 pl-9 pr-9 text-sm text-white placeholder:text-gray-600 focus:border-teal-500/50 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
{query && (
|
{query && (
|
||||||
|
|
|
||||||
|
|
@ -448,11 +448,10 @@ function LabToolbar({
|
||||||
return (
|
return (
|
||||||
<div className="sticky top-14 md:top-16 z-30 bg-black/95 backdrop-blur border-b border-white/5 -mx-4 px-4 py-3 space-y-2.5">
|
<div className="sticky top-14 md:top-16 z-30 bg-black/95 backdrop-blur border-b border-white/5 -mx-4 px-4 py-3 space-y-2.5">
|
||||||
{/* Tab row — a wrapping grid so every craft is visible at a glance with
|
{/* Tab row — a wrapping grid so every craft is visible at a glance with
|
||||||
no horizontal scrolling. 10 crafts lay out as two tidy rows of five on
|
no horizontal scrolling. 11 crafts lay out 4-4-3 on phones, and flex
|
||||||
phones, and flex to a single line on wide screens. Icon-over-label
|
to a single line on wide screens. Icon-over-label stack keeps each tab
|
||||||
stack keeps each tab compact but tappable; a count badge floats in
|
compact but tappable; a count badge floats in the corner. */}
|
||||||
the corner. */}
|
<div className="grid grid-cols-4 gap-1.5 sm:gap-2 md:flex md:flex-wrap -mx-1 px-1">
|
||||||
<div className="grid grid-cols-5 gap-1.5 sm:gap-2 md:flex md:flex-wrap -mx-1 px-1">
|
|
||||||
{tabs.map(({ key, label, icon: Icon }) => {
|
{tabs.map(({ key, label, icon: Icon }) => {
|
||||||
const isActive = serviceType === key;
|
const isActive = serviceType === key;
|
||||||
const a = ACCENT[key];
|
const a = ACCENT[key];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue