ui(support): compact tabbed support page with friendlier /support alias
This commit is contained in:
parent
a1b069da13
commit
f2787d79cf
3 changed files with 289 additions and 308 deletions
|
|
@ -194,6 +194,8 @@ export function AppRouter() {
|
|||
<Route path="/license" element={<LicensePage />} />
|
||||
<Route path="/privacy" element={<PrivacyPage />} />
|
||||
<Route path="/supporters" element={<SupportersPage />} />
|
||||
{/* Friendly alias: people naturally type /support. */}
|
||||
<Route path="/support" element={<Navigate to="/supporters" replace />} />
|
||||
<Route path="/donate" element={<DonatePage />} />
|
||||
{/* Marketplace */}
|
||||
<Route path="/marketplace-listings" element={<MarketplacePage />} />
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
import { useTranslation } from '@/contexts/language';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
/**
|
||||
* "Ways to support" — three purpose-first cards; each scrolls to its rail by id
|
||||
* (#support-lightning / #support-silent / #support-angor), so it works anywhere
|
||||
* those anchors exist (/supporters and /donate).
|
||||
*/
|
||||
export function WaysToSupport({ className }: { className?: string }) {
|
||||
export function WaysToSupport({
|
||||
className,
|
||||
active,
|
||||
onSelect,
|
||||
}: {
|
||||
className?: string;
|
||||
active?: string;
|
||||
onSelect?: (id: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const ways = [
|
||||
{ id: 'support-lightning', emoji: '⚡', title: t('walletUi.supporters.wayLightningTitle'), desc: t('walletUi.supporters.wayLightningDesc') },
|
||||
|
|
@ -13,16 +22,29 @@ export function WaysToSupport({ className }: { className?: string }) {
|
|||
{ id: 'support-angor', emoji: '⛓', title: t('walletUi.supporters.wayAngorTitle'), desc: t('walletUi.supporters.wayAngorDesc') },
|
||||
];
|
||||
return (
|
||||
<div className={`grid grid-cols-1 gap-3 sm:grid-cols-3 ${className ?? ''}`}>
|
||||
<div className={`grid grid-cols-3 gap-2 ${className ?? ''}`}>
|
||||
{ways.map((w) => (
|
||||
<button
|
||||
key={w.id}
|
||||
onClick={() => document.getElementById(w.id)?.scrollIntoView({ behavior: 'smooth', block: 'start' })}
|
||||
className="min-h-0 min-w-0 rounded-xl border border-white/10 bg-black/60 p-4 text-left transition-colors hover:border-[#ccff00]/40 hover:bg-white/5"
|
||||
type="button"
|
||||
aria-pressed={active === w.id}
|
||||
onClick={() => {
|
||||
if (onSelect) {
|
||||
onSelect(w.id);
|
||||
return;
|
||||
}
|
||||
document.getElementById(w.id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}}
|
||||
className={cn(
|
||||
'min-h-0 min-w-0 rounded-xl border p-2.5 text-left transition-colors sm:p-3',
|
||||
active === w.id
|
||||
? 'border-[#ccff00]/60 bg-[#ccff00]/10'
|
||||
: 'border-white/10 bg-black/60 hover:border-[#ccff00]/40 hover:bg-white/5',
|
||||
)}
|
||||
>
|
||||
<span className="text-2xl">{w.emoji}</span>
|
||||
<p className="mt-2 text-sm font-black uppercase text-white">{w.title}</p>
|
||||
<p className="mt-1 text-xs text-gray-400">{w.desc}</p>
|
||||
<span className="text-xl">{w.emoji}</span>
|
||||
<p className="mt-1 text-[10px] font-black uppercase leading-tight text-white sm:mt-2 sm:text-xs">{w.title}</p>
|
||||
<p className="mt-0.5 hidden text-[10px] leading-snug text-gray-400 sm:block">{w.desc}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { Input } from '@/components/ui/input';
|
|||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
|
||||
|
||||
// Only count support received from this point (the project's public launch era).
|
||||
|
|
@ -27,7 +28,7 @@ import { Button } from '@/components/ui/button';
|
|||
// platform zap is already ≥ 2026-01, so this doesn't currently trim the list —
|
||||
// the visible cap below is what keeps it short.
|
||||
const SUPPORTERS_SINCE = Math.floor(Date.UTC(2025, 11, 1) / 1000); // 2025-12-01
|
||||
const VISIBLE_LIMIT = 21;
|
||||
const VISIBLE_LIMIT = 12;
|
||||
|
||||
/** npub → hex pubkey (useAuthor queries relays by HEX authors). */
|
||||
function npubToHex(npub?: string): string | undefined {
|
||||
|
|
@ -261,15 +262,15 @@ function SectionHeader({
|
|||
color?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="mb-6 md:mb-8">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<Icon className="h-5 w-5 flex-shrink-0" style={{ color }} />
|
||||
<h2 className="text-2xl md:text-3xl font-black uppercase tracking-wide" style={{ color }}>
|
||||
<div className="mb-3 md:mb-4">
|
||||
<div className="mb-1.5 flex items-center gap-2.5">
|
||||
<Icon className="h-4 w-4 flex-shrink-0" style={{ color }} />
|
||||
<h2 className="text-lg font-black uppercase tracking-wide md:text-xl" style={{ color }}>
|
||||
{title}
|
||||
</h2>
|
||||
<div className="flex-1 h-px" style={{ background: `linear-gradient(to right, ${color}40, transparent)` }} />
|
||||
<div className="h-px flex-1" style={{ background: `linear-gradient(to right, ${color}40, transparent)` }} />
|
||||
</div>
|
||||
{subtitle && <p className="text-gray-400 text-sm ml-8">{subtitle}</p>}
|
||||
{subtitle && <p className="ml-7 text-xs text-gray-400">{subtitle}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -547,19 +548,6 @@ function SupporterRow({ rank, entry }: { rank: number; entry: SupporterEntry })
|
|||
);
|
||||
}
|
||||
|
||||
// ─── Placeholder card (empty slot) ───────────────────────────────────────────
|
||||
|
||||
function PlaceholderCard({ label }: { label: string }) {
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-3 p-5 rounded-xl border border-dashed border-white/10 text-center opacity-40">
|
||||
<div className="w-16 h-16 rounded-2xl border border-dashed border-white/20 flex items-center justify-center">
|
||||
<span className="text-2xl">?</span>
|
||||
</div>
|
||||
<p className="text-xs text-gray-600 font-medium">{label}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Main page ────────────────────────────────────────────────────────────────
|
||||
|
||||
export default function SupportersPage() {
|
||||
|
|
@ -571,6 +559,8 @@ export default function SupportersPage() {
|
|||
const totalUsd = useSatsToUsd(allSats);
|
||||
const { data: stats } = useGrowthStats();
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const [tab, setTab] = useState<'support' | 'backers' | 'community' | 'grants'>('support');
|
||||
const [rail, setRail] = useState<'support-lightning' | 'support-silent' | 'support-angor'>('support-lightning');
|
||||
|
||||
// One ranked wall: Angor backers + Lightning zaps to the project, merged by npub.
|
||||
const supporters = useMemo<SupporterEntry[]>(() => {
|
||||
|
|
@ -652,97 +642,106 @@ export default function SupportersPage() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="container max-w-4xl px-4 py-12 md:py-16 space-y-16">
|
||||
<div className="container max-w-4xl space-y-6 px-4 py-8 md:py-10">
|
||||
|
||||
{/* ── Hero ── */}
|
||||
<div className="text-center">
|
||||
<div className="text-6xl mb-4">⚡</div>
|
||||
<h1 className="text-5xl md:text-6xl font-black uppercase mb-3 leading-none">
|
||||
<h1 className="text-4xl font-black uppercase leading-none md:text-5xl">
|
||||
<span className="neon-red">{t('walletUi.supporters.our')}</span>{' '}
|
||||
<span className="neon-lime">{t('walletUi.supporters.community')}</span>
|
||||
</h1>
|
||||
<p className="text-gray-400 max-w-lg mx-auto text-base mb-8">
|
||||
<p className="mx-auto mt-2 max-w-xl text-sm text-gray-400">
|
||||
{t('walletUi.supporters.heroDesc')}
|
||||
</p>
|
||||
<div className="mt-5 flex flex-wrap items-center justify-center gap-2">
|
||||
<a
|
||||
href={ANGOR_PROJECT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-2 px-6 py-3 rounded-full bg-gradient-to-r from-[#ff0040] to-[#ff6600] text-white font-black uppercase text-sm hover:opacity-90 transition-all shadow-lg shadow-[#ff0040]/30"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-gradient-to-r from-[#ff0040] to-[#ff6600] px-5 py-2.5 text-xs font-black uppercase text-white shadow-lg shadow-[#ff0040]/25 transition-opacity hover:opacity-90"
|
||||
>
|
||||
<Zap className="h-4 w-4" />
|
||||
{t('walletUi.supporters.backAngor')}
|
||||
<ExternalLink className="h-3.5 w-3.5 opacity-70" />
|
||||
<ExternalLink className="h-3 w-3 opacity-70" />
|
||||
</a>
|
||||
<Link
|
||||
to="/donate"
|
||||
className="mt-5 inline-flex items-center gap-1.5 text-xs font-bold text-[#ccff00]/80 transition-colors hover:text-[#ccff00]"
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-white/15 px-4 py-2.5 text-xs font-bold text-[#ccff00]/80 transition-colors hover:border-[#ccff00]/40 hover:text-[#ccff00]"
|
||||
>
|
||||
{t('walletUi.supporters.donatePageLink')} →
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* ── 1. Support ── */}
|
||||
<section>
|
||||
<SectionHeader
|
||||
icon={Zap}
|
||||
title={t('walletUi.supporters.support')}
|
||||
subtitle={t('walletUi.supporters.supportDesc')}
|
||||
color="#f7931a"
|
||||
/>
|
||||
|
||||
<WaysToSupport className="mb-4" />
|
||||
|
||||
{/* Lightning rail — the in-app way to chip in (zap publicly or pay quietly). */}
|
||||
<div id="support-lightning" className="scroll-mt-24">
|
||||
<SupportSkateSpots className="mt-4" />
|
||||
<div className="mt-4 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-[11px] text-gray-500">
|
||||
<span>{supporters.length.toLocaleString()} {t('walletUi.supporters.backers')}</span>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>{allSats.toLocaleString()} sats</span>
|
||||
{typeof totalUsd === 'number' && (
|
||||
<>
|
||||
<span aria-hidden="true">·</span>
|
||||
<span>≈ ${totalUsd.toFixed(2)}</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Silent payments — private on-chain (and Lightning) via a BIP-353 name. */}
|
||||
<div id="support-silent" className="scroll-mt-24">
|
||||
<SilentPaymentsCard className="mt-4" />
|
||||
</div>
|
||||
<div id="support-angor" className="scroll-mt-24">
|
||||
<AngorProjectCard count={supporters.length} className="mt-4" />
|
||||
</div>
|
||||
</section>
|
||||
{/* ── Compact navigation ── */}
|
||||
<Tabs
|
||||
value={tab}
|
||||
onValueChange={(value) => setTab(value as 'support' | 'backers' | 'community' | 'grants')}
|
||||
>
|
||||
<TabsList className="grid h-auto w-full grid-cols-4 gap-1 rounded-2xl border border-white/10 bg-black/70 p-1">
|
||||
{[
|
||||
{ value: 'support', label: t('walletUi.supporters.support') },
|
||||
{ value: 'backers', label: t('walletUi.supporters.backers') },
|
||||
{ value: 'community', label: t('walletUi.supporters.community') },
|
||||
{ value: 'grants', label: t('walletUi.supporters.grantsSection') },
|
||||
].map((item) => (
|
||||
<TabsTrigger
|
||||
key={item.value}
|
||||
value={item.value}
|
||||
className="h-auto min-h-10 whitespace-normal rounded-xl px-1.5 py-2 text-center text-[9px] font-black uppercase leading-tight tracking-wide data-[state=active]:bg-[#ccff00] data-[state=active]:text-black data-[state=active]:shadow-none sm:px-2 sm:text-[11px]"
|
||||
>
|
||||
{item.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
</TabsList>
|
||||
|
||||
{/* ── 2. Bitcoin grantmakers ── */}
|
||||
<section>
|
||||
<SectionHeader
|
||||
icon={Handshake}
|
||||
title={t('walletUi.supporters.grantsSection')}
|
||||
subtitle={t('walletUi.supporters.grantsSectionDesc')}
|
||||
color="#f7931a"
|
||||
/>
|
||||
<GrantmakerForm />
|
||||
</section>
|
||||
{/* ── Support rails ── */}
|
||||
<TabsContent value="support" className="space-y-4 pt-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-black uppercase text-white">{t('walletUi.supporters.support')}</h2>
|
||||
<p className="mt-0.5 text-sm text-gray-400">{t('walletUi.supporters.donateDesc')}</p>
|
||||
</div>
|
||||
<WaysToSupport active={rail} onSelect={(id) => setRail(id as typeof rail)} />
|
||||
<div id={rail} className="scroll-mt-24">
|
||||
{rail === 'support-lightning' && <SupportSkateSpots />}
|
||||
{rail === 'support-silent' && <SilentPaymentsCard />}
|
||||
{rail === 'support-angor' && <AngorProjectCard count={supporters.length} />}
|
||||
</div>
|
||||
|
||||
{/* ── 3. What your support pays for (words only) ── */}
|
||||
<section>
|
||||
<section className="pt-1">
|
||||
<SectionHeader
|
||||
icon={Heart}
|
||||
title={t('walletUi.supporters.paysFor')}
|
||||
subtitle={t('walletUi.supporters.paysForDesc')}
|
||||
color="#22d3ee"
|
||||
/>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
|
||||
{PAYS_FOR.map(({ icon: Icon, key, desc }) => (
|
||||
<div
|
||||
key={key}
|
||||
className="flex items-start gap-3 rounded-xl border border-white/10 bg-black/60 px-4 py-3"
|
||||
>
|
||||
<Icon className="mt-0.5 h-4 w-4 flex-shrink-0 text-[#22d3ee]" />
|
||||
<div key={key} className="flex items-start gap-2 rounded-xl border border-white/10 bg-black/60 px-3 py-2">
|
||||
<Icon className="mt-0.5 h-3.5 w-3.5 flex-shrink-0 text-[#22d3ee]" />
|
||||
<div className="min-w-0">
|
||||
<span className="text-sm font-medium text-white">{t(key)}</span>
|
||||
<p className="text-[11px] text-gray-500">{t(desc)}</p>
|
||||
<span className="text-xs font-medium text-white">{t(key)}</span>
|
||||
<p className="text-[10px] leading-snug text-gray-500">{t(desc)}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</TabsContent>
|
||||
|
||||
{/* ── 3. Impact so far (what the community built) ── */}
|
||||
{/* ── Backers, impact and tiers ── */}
|
||||
<TabsContent value="backers" className="space-y-5 pt-4">
|
||||
{impact.length > 0 && (
|
||||
<section>
|
||||
<SectionHeader
|
||||
|
|
@ -751,16 +750,16 @@ export default function SupportersPage() {
|
|||
subtitle={t('walletUi.supporters.impactDesc')}
|
||||
color="#ccff00"
|
||||
/>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
||||
<div className="grid grid-cols-3 gap-2 sm:grid-cols-6">
|
||||
{impact.map(({ key, value }) => (
|
||||
<div key={key} className="rounded-xl border border-white/10 bg-black/60 px-4 py-3 text-center">
|
||||
<div className="text-2xl font-black text-[#ccff00]">{value.toLocaleString()}</div>
|
||||
<div className="text-xs text-gray-400">{t(key)}</div>
|
||||
<div key={key} className="rounded-xl border border-white/10 bg-black/60 px-2 py-2.5 text-center">
|
||||
<div className="text-lg font-black text-[#ccff00]">{value.toLocaleString()}</div>
|
||||
<div className="truncate text-[10px] text-gray-400">{t(key)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-4 text-center text-xs text-gray-500">
|
||||
<Link to="/stats" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">
|
||||
<p className="mt-2 text-center text-[11px] text-gray-500">
|
||||
<Link to="/stats" className="text-[#ccff00]/70 underline underline-offset-2 hover:text-[#ccff00]">
|
||||
{t('walletUi.supporters.impactSeeStats')}
|
||||
</Link>
|
||||
{' · '}
|
||||
|
|
@ -769,7 +768,6 @@ export default function SupportersPage() {
|
|||
</section>
|
||||
)}
|
||||
|
||||
{/* ── 4. Supporters (Angor + Lightning zaps) ── */}
|
||||
<section>
|
||||
<SectionHeader
|
||||
icon={Zap}
|
||||
|
|
@ -777,19 +775,19 @@ export default function SupportersPage() {
|
|||
subtitle={t('walletUi.supporters.backersDesc')}
|
||||
color="#22d3ee"
|
||||
/>
|
||||
|
||||
{supporters.length > 0 ? (
|
||||
<>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
{(showAll ? supporters : supporters.slice(0, VISIBLE_LIMIT)).map((entry, i) => (
|
||||
<SupporterRow key={entry.key} rank={i + 1} entry={entry} />
|
||||
))}
|
||||
</div>
|
||||
{supporters.length > VISIBLE_LIMIT && (
|
||||
<div className="mt-3 text-center">
|
||||
<div className="mt-2 text-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAll((v) => !v)}
|
||||
className="min-h-0 min-w-0 text-xs font-bold uppercase tracking-wide text-[#22d3ee]/80 transition-colors hover:text-[#22d3ee]"
|
||||
className="min-h-0 min-w-0 text-[11px] font-bold uppercase tracking-wide text-[#22d3ee]/80 transition-colors hover:text-[#22d3ee]"
|
||||
>
|
||||
{showAll
|
||||
? t('walletUi.supporters.showLess')
|
||||
|
|
@ -797,47 +795,62 @@ export default function SupportersPage() {
|
|||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="mt-4 text-center text-sm text-gray-400">
|
||||
<p className="mt-3 text-center text-xs text-gray-400">
|
||||
{t('walletUi.supporters.supportersTotals', {
|
||||
count: supporters.length,
|
||||
angor: BACKERS_TOTAL_SATS.toLocaleString(),
|
||||
zap: zapTotal.toLocaleString(),
|
||||
})}
|
||||
{typeof totalUsd === 'number' && (
|
||||
<span className="text-gray-500"> · ≈ ${totalUsd.toFixed(2)}</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="mt-2 text-center text-xs text-gray-600">
|
||||
{t('walletUi.supporters.quietNote')}
|
||||
{typeof totalUsd === 'number' && <span className="text-gray-500"> · ≈ ${totalUsd.toFixed(2)}</span>}
|
||||
</p>
|
||||
<p className="mt-1 text-center text-[11px] text-gray-600">{t('walletUi.supporters.quietNote')}</p>
|
||||
{latestAt > 0 && (
|
||||
<p className="mt-1 text-center text-[11px] text-gray-600">
|
||||
{t('walletUi.supporters.updatedAt', {
|
||||
when: formatDistanceToNow(new Date(latestAt * 1000), { addSuffix: true }),
|
||||
})}
|
||||
<p className="mt-0.5 text-center text-[10px] text-gray-600">
|
||||
{t('walletUi.supporters.updatedAt', { when: formatDistanceToNow(new Date(latestAt * 1000), { addSuffix: true }) })}
|
||||
</p>
|
||||
)}
|
||||
<TierLadder supporters={supporters} />
|
||||
</>
|
||||
) : (
|
||||
<div className="rounded-xl border border-dashed border-[#22d3ee]/20 p-8 text-center">
|
||||
<Zap className="h-10 w-10 mx-auto mb-3 text-[#22d3ee]/30" />
|
||||
<p className="text-gray-500 text-sm mb-1">{t('walletUi.supporters.beFirstBacker')}</p>
|
||||
<a
|
||||
href={ANGOR_PROJECT_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 text-xs font-bold text-[#22d3ee]/80 hover:text-[#22d3ee] transition-colors"
|
||||
>
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
{t('walletUi.supporters.backAngor')}
|
||||
<div className="rounded-xl border border-dashed border-[#22d3ee]/20 p-6 text-center">
|
||||
<Zap className="mx-auto mb-2 h-8 w-8 text-[#22d3ee]/30" />
|
||||
<p className="mb-1 text-sm text-gray-500">{t('walletUi.supporters.beFirstBacker')}</p>
|
||||
<a href={ANGOR_PROJECT_URL} target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1.5 text-xs font-bold text-[#22d3ee]/80 hover:text-[#22d3ee]">
|
||||
<ExternalLink className="h-3 w-3" /> {t('walletUi.supporters.backAngor')}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</TabsContent>
|
||||
|
||||
{/* ── Community, mission, collaborators, partners, friends ── */}
|
||||
<TabsContent value="community" className="space-y-6 pt-4">
|
||||
<section className="rounded-2xl border border-[#ff0040]/25 bg-black/70 p-5">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Heart className="h-5 w-5 text-[#ff0040]" />
|
||||
<h2 className="text-center text-lg font-black uppercase text-white">
|
||||
{t('walletUi.supporters.whyTitle1')}{' '}
|
||||
<span className="neon-red">{t('walletUi.supporters.whyTitle2')}</span>
|
||||
</h2>
|
||||
</div>
|
||||
<p className="mx-auto mt-2 max-w-2xl text-center text-xs leading-relaxed text-gray-400">
|
||||
{t('walletUi.supporters.whyDesc')}
|
||||
</p>
|
||||
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
|
||||
{[
|
||||
{ emoji: '🛹', title: t('walletUi.supporters.why1Title'), desc: t('walletUi.supporters.why1Desc') },
|
||||
{ emoji: '⚡', title: t('walletUi.supporters.why2Title'), desc: t('walletUi.supporters.why2Desc') },
|
||||
{ emoji: '🌍', title: t('walletUi.supporters.why3Title'), desc: t('walletUi.supporters.why3Desc') },
|
||||
].map(({ emoji, title, desc }) => (
|
||||
<div key={title} className="rounded-xl border border-white/10 bg-white/[0.03] p-3 text-center">
|
||||
<div className="text-2xl">{emoji}</div>
|
||||
<p className="mt-1 text-xs font-bold">{title}</p>
|
||||
<p className="mt-0.5 text-[11px] text-gray-500">{desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── 6. Collaborators ── */}
|
||||
<section>
|
||||
<SectionHeader
|
||||
icon={Users}
|
||||
|
|
@ -845,124 +858,68 @@ export default function SupportersPage() {
|
|||
subtitle={t('walletUi.supporters.collaboratorsDesc')}
|
||||
color="#ff0040"
|
||||
/>
|
||||
|
||||
{COLLABORATORS.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{COLLABORATORS.map((c) => (
|
||||
<CollaboratorCard key={c.name} collab={c} />
|
||||
))}
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{COLLABORATORS.map((c) => <CollaboratorCard key={c.name} collab={c} />)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-xl border border-dashed border-[#ff0040]/20 p-8 text-center">
|
||||
<Users className="h-10 w-10 mx-auto mb-3 text-[#ff0040]/30" />
|
||||
<p className="text-gray-500 text-sm mb-1">{t('walletUi.supporters.wantCollaborate')}</p>
|
||||
<p className="text-xs text-gray-600 mb-4">{t('walletUi.supporters.wantCollaborateDesc')}</p>
|
||||
<a
|
||||
href="https://nostrhub.io/naddr1qvzqqqrhnypzqg88e9f588nx7wyz4wt0ura23w3yd4hmztqrkr95utwyvgachjepqyd8wumn8ghj7emfwsh8x6rpddjhxur9v9ex2tnyd9uj7qq22d4kzar92dcx7arnhyhdwn"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 text-xs font-bold text-[#ff0040]/70 hover:text-[#ff0040] transition-colors"
|
||||
>
|
||||
<ExternalLink className="h-3 w-3" />
|
||||
{t('walletUi.supporters.viewNostrHub')}
|
||||
<div className="rounded-xl border border-dashed border-[#ff0040]/20 p-5 text-center">
|
||||
<Users className="mx-auto mb-2 h-8 w-8 text-[#ff0040]/30" />
|
||||
<p className="mb-1 text-sm text-gray-500">{t('walletUi.supporters.wantCollaborate')}</p>
|
||||
<p className="mb-3 text-xs text-gray-600">{t('walletUi.supporters.wantCollaborateDesc')}</p>
|
||||
<a href="https://nostrhub.io/naddr1qvzqqqrhnypzqg88e9f588nx7wyz4wt0ura23w3yd4hmztqrkr95utwyvgachjepqyd8wumn8ghj7emfwsh8x6rpddjhxur9v9ex2tnyd9uj7qq22d4kzar92dcx7arnhyhdwn" target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1.5 text-xs font-bold text-[#ff0040]/70 hover:text-[#ff0040]">
|
||||
<ExternalLink className="h-3 w-3" /> {t('walletUi.supporters.viewNostrHub')}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* ── 7. Partners ── */}
|
||||
<section>
|
||||
<SectionHeader
|
||||
icon={Handshake}
|
||||
title={t('walletUi.supporters.partners')}
|
||||
subtitle={t('walletUi.supporters.partnersDesc')}
|
||||
color="#ff6600"
|
||||
/>
|
||||
|
||||
<SectionHeader icon={Handshake} title={t('walletUi.supporters.partners')} subtitle={t('walletUi.supporters.partnersDesc')} color="#ff6600" />
|
||||
{PARTNERS.length > 0 ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{PARTNERS.map((p) => (
|
||||
<PartnerCard key={p.name} partner={p} />
|
||||
))}
|
||||
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
{PARTNERS.map((p) => <PartnerCard key={p.name} partner={p} />)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 mb-4">
|
||||
{Array.from({ length: 4 }).map((_, i) => (
|
||||
<PlaceholderCard key={i} label={t('walletUi.supporters.comingSoon')} />
|
||||
))}
|
||||
<div className="rounded-xl border border-dashed border-[#ff6600]/20 p-5 text-center text-sm text-gray-500">
|
||||
{t('walletUi.supporters.wantPartner')}
|
||||
</div>
|
||||
<div className="rounded-xl border border-dashed border-[#ff6600]/20 p-6 text-center">
|
||||
<p className="text-gray-500 text-sm">{t('walletUi.supporters.wantPartner')}</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* ── 8. Friends ── */}
|
||||
<section>
|
||||
<SectionHeader icon={Star} title={t('walletUi.supporters.friends')} subtitle={t('walletUi.supporters.friendsDesc')} color="#ccff00" />
|
||||
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
|
||||
{FRIENDS.map((f) => <FriendCard key={f.name} friend={f} />)}
|
||||
</div>
|
||||
<div className="mt-3 rounded-xl border border-dashed border-[#ccff00]/20 p-4 text-center">
|
||||
<p className="text-xs text-gray-500">{t('walletUi.supporters.wantListed')}</p>
|
||||
<p className="mt-0.5 text-[11px] text-gray-600">{t('walletUi.supporters.wantListedDesc')}</p>
|
||||
</div>
|
||||
</section>
|
||||
</TabsContent>
|
||||
|
||||
{/* ── Grantmakers ── */}
|
||||
<TabsContent value="grants" className="space-y-4 pt-4">
|
||||
<SectionHeader
|
||||
icon={Star}
|
||||
title={t('walletUi.supporters.friends')}
|
||||
subtitle={t('walletUi.supporters.friendsDesc')}
|
||||
color="#ccff00"
|
||||
icon={Handshake}
|
||||
title={t('walletUi.supporters.grantsSection')}
|
||||
subtitle={t('walletUi.supporters.grantsSectionDesc')}
|
||||
color="#f7931a"
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-3">
|
||||
{FRIENDS.map((f) => (
|
||||
<FriendCard key={f.name} friend={f} />
|
||||
))}
|
||||
{/* Fill remaining slots up to 5 with placeholders */}
|
||||
{FRIENDS.length < 5 && Array.from({ length: 5 - FRIENDS.length }).map((_, i) => (
|
||||
<PlaceholderCard key={`ph-${i}`} label={t('walletUi.supporters.comingSoon')} />
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 rounded-xl border border-dashed border-[#ccff00]/20 p-5 text-center">
|
||||
<p className="text-gray-500 text-sm mb-1">{t('walletUi.supporters.wantListed')}</p>
|
||||
<p className="text-xs text-gray-600">
|
||||
{t('walletUi.supporters.wantListedDesc')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── Mission ── */}
|
||||
<section>
|
||||
<div className="relative rounded-2xl overflow-hidden border-2 border-[#ff0040]/30 bg-black/80 p-8 md:p-10">
|
||||
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-[#ff0040] via-[#ff6600] to-[#ccff00]" />
|
||||
<div className="flex justify-center mb-5">
|
||||
<Heart className="h-10 w-10 text-[#ff0040]" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-black uppercase text-center mb-3">
|
||||
{t('walletUi.supporters.whyTitle1')} <span className="neon-red">{t('walletUi.supporters.whyTitle2')}</span>
|
||||
</h2>
|
||||
<p className="text-gray-400 text-sm text-center max-w-2xl mx-auto mb-6 leading-relaxed">
|
||||
{t('walletUi.supporters.whyDesc')}
|
||||
</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
||||
{[
|
||||
{ emoji: '🛹', title: t('walletUi.supporters.why1Title'), desc: t('walletUi.supporters.why1Desc') },
|
||||
{ emoji: '⚡', title: t('walletUi.supporters.why2Title'), desc: t('walletUi.supporters.why2Desc') },
|
||||
{ emoji: '🌍', title: t('walletUi.supporters.why3Title'), desc: t('walletUi.supporters.why3Desc') },
|
||||
].map(({ emoji, title, desc }) => (
|
||||
<div key={title} className="p-4 rounded-xl bg-white/5 border border-white/10 text-center">
|
||||
<div className="text-3xl mb-2">{emoji}</div>
|
||||
<p className="font-bold text-sm mb-1">{title}</p>
|
||||
<p className="text-xs text-gray-500">{desc}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<GrantmakerForm />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* ── Footer ── */}
|
||||
<div className="border-t border-gray-800 pt-8 text-center space-y-2 text-sm text-gray-500">
|
||||
<div className="border-t border-gray-800 pt-5 text-center text-xs text-gray-500">
|
||||
<p>{t('walletUi.supporters.thankYou')}</p>
|
||||
<p>
|
||||
<Link to="/license" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">{t('walletUi.supporters.license')}</Link>
|
||||
<p className="mt-1.5">
|
||||
<Link to="/license" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">{t('walletUi.supporters.license')}</Link>
|
||||
{' '}·{' '}
|
||||
<Link to="/about" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">{t('nav.about')}</Link>
|
||||
<Link to="/about" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">{t('nav.about')}</Link>
|
||||
{' '}·{' '}
|
||||
<Link to="/faq" className="text-[#ccff00]/70 hover:text-[#ccff00] transition-colors underline">{t('walletUi.supporters.faq')}</Link>
|
||||
<Link to="/faq" className="text-[#ccff00]/70 underline transition-colors hover:text-[#ccff00]">{t('walletUi.supporters.faq')}</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue