feat: improve Bitcoin grantmaker inquiry UX
Some checks are pending
Deploy to GitHub Pages / deploy (push) Waiting to run
Some checks are pending
Deploy to GitHub Pages / deploy (push) Waiting to run
This commit is contained in:
parent
9732743527
commit
1af3a5f6ef
3 changed files with 182 additions and 49 deletions
|
|
@ -6683,11 +6683,12 @@ walletUi: {
|
|||
our: 'OUR',
|
||||
community: 'COMMUNITY',
|
||||
heroDesc: 'SkateSpots is built by and for the community. Supporters, collaborators, partners and friends keeping this free and decentralized.',
|
||||
grantsTab: 'Grants',
|
||||
grantsSection: 'Bitcoin grantmakers',
|
||||
grantsSectionDesc: 'For organizations that issue grants to open-source Bitcoin projects.',
|
||||
grantsTitle: 'Open to sponsorships · Bitcoin grantmakers',
|
||||
grantsDisclaimer: 'Want to donate to this project? Fill the form. Not to KYC us — to KYC YOU.',
|
||||
grantsDisclaimerDesc: "This lane is only for grantmakers that KYC/KYB their grantees. If you're a skater who just wants to donate, use the Lightning, Silent Payments or Angor rails above — nobody needs your passport.",
|
||||
grantsDisclaimer: 'Grantmaker inquiry. This lane is for organizations that already KYC/KYB their grantees.',
|
||||
grantsDisclaimerDesc: "If you just want to donate, use the Lightning, Silent Payments or Angor rails above — no identity check.",
|
||||
grantsOrg: 'Organization',
|
||||
grantsContact: 'Contact (npub, email or handle)',
|
||||
grantsWebsite: 'Website',
|
||||
|
|
@ -6695,6 +6696,19 @@ walletUi: {
|
|||
grantsSize: 'Grant size',
|
||||
grantsKyc: 'KYC / KYB process',
|
||||
grantsFund: 'What would you like to fund?',
|
||||
grantsFundTitle: 'What a grant can fund',
|
||||
grantsMoreDetails: 'Add grant details',
|
||||
grantsOptional: 'Optional',
|
||||
grantsRequired: 'Organization, contact and confirmation are required.',
|
||||
grantsContactHint: 'Prefer an npub or NIP-05. Email or @handle is only reference text for the private DM.',
|
||||
grantsWebsiteInvalid: 'Enter a valid http(s) website URL.',
|
||||
grantsReview: 'Review message',
|
||||
grantsReviewTitle: 'Review your private message',
|
||||
grantsReviewDesc: 'Nothing has been sent. Check the message before opening the private DM.',
|
||||
grantsOpenDm: 'Open private DM',
|
||||
grantsCopy: 'Copy message',
|
||||
grantsCopied: 'Copied',
|
||||
grantsCopyFailed: 'Could not copy the message',
|
||||
grantsAck: 'I confirm we are a grantmaker that KYC/KYB our grantees.',
|
||||
grantsSend: 'Open grant conversation',
|
||||
grantsNoKycTitle: 'No KYC of SkateSpots',
|
||||
|
|
|
|||
|
|
@ -6683,11 +6683,12 @@ walletUi: {
|
|||
our: 'NUESTRA',
|
||||
community: 'COMUNIDAD',
|
||||
heroDesc: 'SkateSpots lo construye y lo mantiene la comunidad. Patrocinadores, colaboradores, socios y amigos que lo mantienen libre y descentralizado.',
|
||||
grantsTab: 'Ayudas',
|
||||
grantsSection: 'Grantmakers de Bitcoin',
|
||||
grantsSectionDesc: 'Para organizaciones que conceden ayudas a proyectos Bitcoin de código abierto.',
|
||||
grantsTitle: 'Abierto a patrocinios · grantmakers de Bitcoin',
|
||||
grantsDisclaimer: '¿Quieres donar a este proyecto? Rellena el formulario. No para hacernos KYC a nosotros, sino para hacerte KYC a TI.',
|
||||
grantsDisclaimerDesc: 'Este canal es solo para grantmakers que hacen KYC/KYB a sus beneficiarios. Si eres un skater que solo quiere donar, usa las vías de Lightning, Silent Payments o Angor de arriba: nadie necesita tu pasaporte.',
|
||||
grantsDisclaimer: 'Consulta para grantmakers. Este canal es para organizaciones que ya hacen KYC/KYB a sus beneficiarios.',
|
||||
grantsDisclaimerDesc: 'Si solo quieres donar, usa las vías de Lightning, Silent Payments o Angor de arriba: sin comprobación de identidad.',
|
||||
grantsOrg: 'Organización',
|
||||
grantsContact: 'Contacto (npub, email o usuario)',
|
||||
grantsWebsite: 'Sitio web',
|
||||
|
|
@ -6695,6 +6696,19 @@ walletUi: {
|
|||
grantsSize: 'Tamaño de la ayuda',
|
||||
grantsKyc: 'Proceso de KYC / KYB',
|
||||
grantsFund: '¿Qué te gustaría financiar?',
|
||||
grantsFundTitle: 'Qué puede financiar una ayuda',
|
||||
grantsMoreDetails: 'Añadir detalles de la ayuda',
|
||||
grantsOptional: 'Opcional',
|
||||
grantsRequired: 'La organización, el contacto y la confirmación son obligatorios.',
|
||||
grantsContactHint: 'Mejor un npub o NIP-05. El email o @usuario solo es texto de referencia para el DM privado.',
|
||||
grantsWebsiteInvalid: 'Introduce una URL http(s) válida.',
|
||||
grantsReview: 'Revisar mensaje',
|
||||
grantsReviewTitle: 'Revisa tu mensaje privado',
|
||||
grantsReviewDesc: 'Todavía no se ha enviado nada. Revisa el mensaje antes de abrir el DM privado.',
|
||||
grantsOpenDm: 'Abrir DM privado',
|
||||
grantsCopy: 'Copiar mensaje',
|
||||
grantsCopied: 'Copiado',
|
||||
grantsCopyFailed: 'No se pudo copiar el mensaje',
|
||||
grantsAck: 'Confirmo que somos un grantmaker que hace KYC/KYB a sus beneficiarios.',
|
||||
grantsSend: 'Abrir conversación de ayuda',
|
||||
grantsNoKycTitle: 'Sin KYC de SkateSpots',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useMemo, useState } from 'react';
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { ExternalLink, Zap, Heart, Users, Handshake, Building2, Star, ShieldCheck, Server, Globe, Bot, Code2, Briefcase, Megaphone, TrendingUp, Info, Lock, EyeOff } from 'lucide-react';
|
||||
import { ExternalLink, Zap, Heart, Users, Handshake, Building2, Star, ShieldCheck, Server, Globe, Bot, Code2, Briefcase, Megaphone, TrendingUp, Info, Lock, EyeOff, Copy, Check, ChevronDown } from 'lucide-react';
|
||||
import { SupportSkateSpots } from '@/components/SupportSkateSpots';
|
||||
import { SilentPaymentsCard } from '@/components/SilentPaymentsCard';
|
||||
import { AngorProjectCard, ANGOR_PROJECT_URL } from '@/components/AngorProjectCard';
|
||||
|
|
@ -20,7 +20,9 @@ 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 { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
|
||||
|
||||
// Only count support received from this point (the project's public launch era).
|
||||
|
|
@ -133,6 +135,7 @@ const PAYS_FOR = [
|
|||
function GrantmakerForm() {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const owner = PLATFORM_OWNER_PUBKEYS[0];
|
||||
const [org, setOrg] = useState('');
|
||||
const [contact, setContact] = useState('');
|
||||
|
|
@ -142,8 +145,13 @@ function GrantmakerForm() {
|
|||
const [kyc, setKyc] = useState('');
|
||||
const [fund, setFund] = useState('');
|
||||
const [ack, setAck] = useState(false);
|
||||
const [reviewOpen, setReviewOpen] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const canSend = !!org.trim() && !!contact.trim() && ack && !!owner;
|
||||
const websiteError = website.trim() && !/^https?:\/\/\S+$/i.test(website.trim())
|
||||
? t('walletUi.supporters.grantsWebsiteInvalid')
|
||||
: '';
|
||||
const canSend = !!org.trim() && !!contact.trim() && ack && !websiteError && !!owner;
|
||||
const message = [
|
||||
'Bitcoin grantmaker inquiry — SkateSpots',
|
||||
'',
|
||||
|
|
@ -158,6 +166,24 @@ function GrantmakerForm() {
|
|||
'I confirm this is a grantmaker inquiry and we KYC/KYB our grantees.',
|
||||
].filter(Boolean).join('\n');
|
||||
|
||||
const copyMessage = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(message);
|
||||
setCopied(true);
|
||||
toast({ title: t('walletUi.supporters.grantsCopied') });
|
||||
window.setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
toast({ title: t('walletUi.supporters.grantsCopyFailed'), variant: 'destructive' });
|
||||
}
|
||||
};
|
||||
|
||||
const openDm = () => {
|
||||
if (!canSend || !owner) return;
|
||||
navigate(`/messages/${nip19.npubEncode(owner)}?message=${encodeURIComponent(message)}`);
|
||||
};
|
||||
|
||||
const inputClass = 'border-[#f7931a]/25 bg-black/50 text-white placeholder:text-gray-500';
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border-2 border-[#f7931a]/30 bg-gradient-to-br from-[#f7931a]/10 via-black to-transparent p-4 sm:p-6">
|
||||
<div className="flex items-start gap-3">
|
||||
|
|
@ -167,41 +193,93 @@ function GrantmakerForm() {
|
|||
<div className="min-w-0">
|
||||
<h3 className="text-lg font-black uppercase text-white">{t('walletUi.supporters.grantsTitle')}</h3>
|
||||
<p className="mt-1 text-sm font-bold text-[#f7931a]">{t('walletUi.supporters.grantsDisclaimer')}</p>
|
||||
<p className="mt-1 text-xs leading-relaxed text-gray-500">{t('walletUi.supporters.grantsDisclaimerDesc')}</p>
|
||||
<p className="mt-1 text-xs leading-relaxed text-gray-400">{t('walletUi.supporters.grantsDisclaimerDesc')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<p className="mt-4 text-[11px] font-bold uppercase tracking-wide text-[#f7931a]">
|
||||
{t('walletUi.supporters.grantsRequired')}
|
||||
</p>
|
||||
|
||||
<div className="mt-2 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-org">{t('walletUi.supporters.grantsOrg')} *</Label>
|
||||
<Input id="grant-org" value={org} onChange={(e) => setOrg(e.target.value)} placeholder="e.g. OpenSats" />
|
||||
<Label htmlFor="grant-org" className="text-gray-200">{t('walletUi.supporters.grantsOrg')} *</Label>
|
||||
<Input id="grant-org" value={org} onChange={(e) => setOrg(e.target.value)} placeholder="e.g. OpenSats" className={inputClass} required aria-required="true" />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-contact">{t('walletUi.supporters.grantsContact')} *</Label>
|
||||
<Input id="grant-contact" value={contact} onChange={(e) => setContact(e.target.value)} placeholder="npub, email or @handle" />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-website">{t('walletUi.supporters.grantsWebsite')}</Label>
|
||||
<Input id="grant-website" value={website} onChange={(e) => setWebsite(e.target.value)} placeholder="https://…" />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-program">{t('walletUi.supporters.grantsProgram')}</Label>
|
||||
<Input id="grant-program" value={program} onChange={(e) => setProgram(e.target.value)} placeholder="e.g. Q1 2027 grants" />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-size">{t('walletUi.supporters.grantsSize')}</Label>
|
||||
<Input id="grant-size" value={grantSize} onChange={(e) => setGrantSize(e.target.value)} placeholder="e.g. 5–25M sats" />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-kyc">{t('walletUi.supporters.grantsKyc')}</Label>
|
||||
<Input id="grant-kyc" value={kyc} onChange={(e) => setKyc(e.target.value)} placeholder="e.g. KYB + grantee KYC" />
|
||||
<Label htmlFor="grant-contact" className="text-gray-200">{t('walletUi.supporters.grantsContact')} *</Label>
|
||||
<Input id="grant-contact" value={contact} onChange={(e) => setContact(e.target.value)} placeholder="npub / NIP-05 / email / @handle" className={inputClass} required aria-required="true" />
|
||||
<p className="text-[10px] leading-relaxed text-gray-500">{t('walletUi.supporters.grantsContactHint')}</p>
|
||||
</div>
|
||||
<div className="space-y-1.5 sm:col-span-2">
|
||||
<Label htmlFor="grant-fund">{t('walletUi.supporters.grantsFund')}</Label>
|
||||
<Textarea id="grant-fund" value={fund} onChange={(e) => setFund(e.target.value)} rows={3} placeholder="Relay, app development, translation, design, events…" />
|
||||
<Label htmlFor="grant-website" className="text-gray-200">{t('walletUi.supporters.grantsWebsite')}</Label>
|
||||
<Input
|
||||
id="grant-website"
|
||||
type="url"
|
||||
value={website}
|
||||
onChange={(e) => setWebsite(e.target.value)}
|
||||
placeholder="https://…"
|
||||
className={inputClass}
|
||||
aria-invalid={!!websiteError}
|
||||
aria-describedby={websiteError ? 'grant-website-error' : undefined}
|
||||
/>
|
||||
{websiteError && <p id="grant-website-error" role="alert" className="text-[10px] text-red-400">{websiteError}</p>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-4 rounded-xl border border-[#f7931a]/15 bg-black/25 p-3">
|
||||
<p className="text-[10px] font-black uppercase tracking-widest text-[#f7931a]">{t('walletUi.supporters.grantsFundTitle')}</p>
|
||||
<div className="mt-2 grid grid-cols-1 gap-2 sm:grid-cols-3">
|
||||
{PAYS_FOR.slice(0, 3).map(({ icon: Icon, key, desc }) => (
|
||||
<div key={key} className="flex items-start gap-2 text-xs text-gray-300">
|
||||
<Icon className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#f7931a]" />
|
||||
<div>
|
||||
<p className="font-bold text-white">{t(key)}</p>
|
||||
<p className="mt-0.5 text-[10px] leading-relaxed text-gray-500">{t(desc)}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label className="mt-4 flex cursor-pointer items-start gap-2.5 rounded-xl border border-[#f7931a]/20 bg-black/30 p-3 text-xs text-gray-300">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={ack}
|
||||
onChange={(e) => setAck(e.target.checked)}
|
||||
className="mt-0.5 h-5 w-5 shrink-0 accent-[#f7931a]"
|
||||
required
|
||||
aria-required="true"
|
||||
/>
|
||||
<span>{t('walletUi.supporters.grantsAck')} *</span>
|
||||
</label>
|
||||
|
||||
<details className="group mt-4 rounded-xl border border-white/10 bg-black/25">
|
||||
<summary className="flex min-h-11 cursor-pointer list-none items-center gap-2 px-3 py-2 text-xs font-bold text-gray-300">
|
||||
<ChevronDown className="h-4 w-4 text-[#f7931a] transition-transform group-open:rotate-180" />
|
||||
{t('walletUi.supporters.grantsMoreDetails')}
|
||||
<span className="ml-auto text-[10px] font-normal text-gray-500">{t('walletUi.supporters.grantsOptional')}</span>
|
||||
</summary>
|
||||
<div className="grid grid-cols-1 gap-3 border-t border-white/10 p-3 sm:grid-cols-2">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-program" className="text-gray-200">{t('walletUi.supporters.grantsProgram')}</Label>
|
||||
<Input id="grant-program" value={program} onChange={(e) => setProgram(e.target.value)} placeholder="e.g. Q1 2027 grants" className={inputClass} />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-size" className="text-gray-200">{t('walletUi.supporters.grantsSize')}</Label>
|
||||
<Input id="grant-size" value={grantSize} onChange={(e) => setGrantSize(e.target.value)} placeholder="e.g. 5–25M sats" className={inputClass} />
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="grant-kyc" className="text-gray-200">{t('walletUi.supporters.grantsKyc')}</Label>
|
||||
<Input id="grant-kyc" value={kyc} onChange={(e) => setKyc(e.target.value)} placeholder="e.g. KYB + grantee KYC" className={inputClass} />
|
||||
</div>
|
||||
<div className="space-y-1.5 sm:col-span-2">
|
||||
<Label htmlFor="grant-fund" className="text-gray-200">{t('walletUi.supporters.grantsFund')}</Label>
|
||||
<Textarea id="grant-fund" value={fund} onChange={(e) => setFund(e.target.value)} rows={3} placeholder="Relay, app development, translation, design, events…" className={inputClass} />
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
|
||||
{[
|
||||
{ icon: ShieldCheck, title: t('walletUi.supporters.grantsNoKycTitle'), body: t('walletUi.supporters.grantsNoKycDesc') },
|
||||
|
|
@ -212,37 +290,54 @@ function GrantmakerForm() {
|
|||
<Icon className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#f7931a]" />
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-bold text-white">{title}</p>
|
||||
<p className="mt-0.5 text-[10px] leading-relaxed text-gray-500">{body}</p>
|
||||
<p className="mt-0.5 text-[10px] leading-relaxed text-gray-400">{body}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="mt-3 rounded-xl border border-[#f7931a]/15 bg-[#f7931a]/[0.04] px-3 py-2 text-[11px] leading-relaxed text-gray-500">
|
||||
<p className="mt-3 rounded-xl border border-[#f7931a]/15 bg-[#f7931a]/[0.04] px-3 py-2 text-[11px] leading-relaxed text-gray-400">
|
||||
{t('walletUi.supporters.grantsEthos')}
|
||||
</p>
|
||||
|
||||
<label className="mt-4 flex cursor-pointer items-start gap-2.5 rounded-xl border border-[#f7931a]/20 bg-black/30 p-3 text-xs text-gray-400">
|
||||
<input type="checkbox" checked={ack} onChange={(e) => setAck(e.target.checked)} className="mt-0.5 h-4 w-4 accent-[#f7931a]" />
|
||||
<span>{t('walletUi.supporters.grantsAck')}</span>
|
||||
</label>
|
||||
|
||||
<div className="mt-4 flex flex-col gap-2 sm:flex-row sm:items-center">
|
||||
<Button
|
||||
type="button"
|
||||
disabled={!canSend}
|
||||
onClick={() => {
|
||||
if (!canSend || !owner) return;
|
||||
navigate(`/messages/${nip19.npubEncode(owner)}?message=${encodeURIComponent(message)}`);
|
||||
}}
|
||||
className="w-full justify-center bg-[#f7931a] font-bold text-black hover:bg-[#ffb347] sm:w-auto"
|
||||
onClick={() => setReviewOpen(true)}
|
||||
className="min-h-11 w-full justify-center bg-[#f7931a] font-bold text-black hover:bg-[#ffb347] disabled:opacity-50 sm:w-auto"
|
||||
>
|
||||
<Handshake className="mr-2 h-4 w-4" /> {t('walletUi.supporters.grantsSend')}
|
||||
<Handshake className="mr-2 h-4 w-4" /> {t('walletUi.supporters.grantsReview')}
|
||||
</Button>
|
||||
<p className="flex items-center gap-1.5 text-[11px] text-gray-600">
|
||||
<p className="flex items-center gap-1.5 text-[11px] text-gray-400" aria-live="polite">
|
||||
<Info className="h-3 w-3 shrink-0" />
|
||||
{t('walletUi.supporters.grantsNote')}
|
||||
{canSend ? t('walletUi.supporters.grantsNote') : t('walletUi.supporters.grantsRequired')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Dialog open={reviewOpen} onOpenChange={setReviewOpen}>
|
||||
<DialogContent className="max-h-[85dvh] max-w-lg overflow-y-auto border-[#f7931a]/30 bg-zinc-950">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white">{t('walletUi.supporters.grantsReviewTitle')}</DialogTitle>
|
||||
<DialogDescription>{t('walletUi.supporters.grantsReviewDesc')}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<pre className="max-h-[40dvh] overflow-y-auto whitespace-pre-wrap break-words rounded-xl border border-white/10 bg-black/40 p-3 text-xs leading-relaxed text-gray-300">
|
||||
{message}
|
||||
</pre>
|
||||
<DialogFooter className="flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<Button variant="outline" onClick={() => setReviewOpen(false)} className="border-white/15">
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button variant="outline" onClick={() => void copyMessage()} className="gap-2 border-white/15">
|
||||
{copied ? <Check className="h-4 w-4 text-[#ccff00]" /> : <Copy className="h-4 w-4" />}
|
||||
{copied ? t('walletUi.supporters.grantsCopied') : t('walletUi.supporters.grantsCopy')}
|
||||
</Button>
|
||||
<Button onClick={openDm} disabled={!canSend} className="gap-2 bg-[#f7931a] font-bold text-black hover:bg-[#ffb347]">
|
||||
<Handshake className="h-4 w-4" /> {t('walletUi.supporters.grantsOpenDm')}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -559,7 +654,17 @@ 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 [searchParams, setSearchParams] = useSearchParams();
|
||||
const tabParam = searchParams.get('tab');
|
||||
const tab: 'support' | 'backers' | 'community' | 'grants' =
|
||||
tabParam === 'backers' || tabParam === 'community' || tabParam === 'grants'
|
||||
? tabParam
|
||||
: 'support';
|
||||
const setTab = (value: 'support' | 'backers' | 'community' | 'grants') => {
|
||||
const next = new URLSearchParams(searchParams);
|
||||
next.set('tab', value);
|
||||
setSearchParams(next, { replace: true });
|
||||
};
|
||||
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.
|
||||
|
|
@ -694,12 +799,12 @@ export default function SupportersPage() {
|
|||
{ 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') },
|
||||
{ value: 'grants', label: t('walletUi.supporters.grantsTab') },
|
||||
].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]"
|
||||
className="h-auto min-h-11 whitespace-normal rounded-xl px-1.5 py-2 text-center text-[10px] 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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue