SkateSpots/src/components/LabProUpgrade.tsx
shakespeare.diy e48a5e6bc5 Add duration tiers + layered NIP-57 zap verification, extend featured/Pro to tours
- Add 4 duration tiers (7d/30d/90d/365d) with per-day price discrimination to usePlatformFee
- PlatformFeePayment: tier selector UI + attempt NIP-57 zap request (a/p tags), graceful fallback to plain LNURL invoice
- Add getZapProviderPubkey + getZapInvoiceFromLightningAddress helpers
- Add useFeaturedVerification hook (verified/claimed/none) querying kind 9735 zap receipts
- ProBadge: optional entityCoord to show green Verified state on detail pages
- Wire entityCoord through all 5 Become* Lab pages + CoachDetailPage
- Add featuredTour fee type; FeaturedUpgrade generalized for events + tours
- CreateTourPage: featured tag on publish + FeaturedUpgrade UI
- TourCard/TourDetailPage: render ProBadge; sort featured tours first
- Document tiers + zap-receipt verification scheme in NIP.md

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
2026-07-24 16:18:15 +07:00

104 lines
4.2 KiB
TypeScript

import { Sparkles, Check } from 'lucide-react';
import { Switch } from '@/components/ui/switch';
import { PlatformFeePayment } from '@/components/PlatformFeePayment';
import { usePlatformFee } from '@/hooks/usePlatformFee';
import { cn } from '@/lib/utils';
interface LabProUpgradeProps {
/** Whether the user opted in to Pro. */
enabled: boolean;
onEnabledChange: (enabled: boolean) => void;
/** Accent color class for the entity (e.g. "text-orange-400"). */
accentText?: string;
/** Addressable coordinate `kind:pubkey:d` for zap-receipt verification. */
entityCoord?: string;
className?: string;
}
/**
* Opt-in "Go Pro" upgrade for Lab profiles.
*
* Free profiles publish without this. Toggling it on reveals the Lightning fee
* payment; once paid (or for platform owners), the profile is published with a
* `featured` tag that unlocks the Pro badge + featured placement in The Lab.
*
* There is NO hard paywall — a basic profile is always free.
*/
export function LabProUpgrade({
enabled,
onEnabledChange,
accentText = 'text-amber-400',
entityCoord,
className,
}: LabProUpgradeProps) {
const { isActive, isPlatformOwner, daysRemaining } = usePlatformFee('labProfile');
return (
<div
className={cn(
'rounded-2xl border border-amber-500/30 bg-gradient-to-br from-amber-500/8 to-black/40 overflow-hidden',
className,
)}
>
<div className="p-4">
<div className="flex items-start justify-between gap-3">
<div className="flex items-start gap-3 min-w-0">
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-amber-400 to-orange-500 flex items-center justify-center flex-shrink-0 shadow-lg shadow-amber-500/25">
<Sparkles className="h-4 w-4 text-black" />
</div>
<div className="min-w-0">
<p className={cn('font-black uppercase tracking-wide text-sm', accentText)}>
Go Pro <span className="text-gray-500 font-normal normal-case">· optional</span>
</p>
<p className="text-xs text-gray-400 mt-0.5">
Free to list. Pro adds a <strong className="text-amber-400">Pro badge</strong> and
{' '}<strong className="text-amber-400">featured placement</strong> at the top of The Lab.
</p>
</div>
</div>
{isActive ? (
<span className="flex items-center gap-1 text-[11px] font-bold text-green-400 flex-shrink-0">
<Check className="h-3.5 w-3.5" /> Active
</span>
) : (
<Switch
checked={enabled}
onCheckedChange={onEnabledChange}
className="flex-shrink-0 data-[state=checked]:bg-amber-500"
/>
)}
</div>
{/* Benefits list */}
{!isActive && (
<ul className="mt-3 grid gap-1.5 text-xs text-gray-400">
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 flex-shrink-0" /> Gold "Pro" badge on your profile &amp; cards</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 flex-shrink-0" /> Featured first when skaters browse The Lab</li>
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 flex-shrink-0" /> Supports SkateSpots — no ads, no middlemen</li>
</ul>
)}
{/* Already active */}
{isActive && (
<p className="mt-2 text-xs text-gray-500">
{isPlatformOwner
? 'Platform owner — Pro is always on.'
: `Pro active · ${daysRemaining} day${daysRemaining !== 1 ? 's' : ''} remaining. Your profile will be featured.`}
</p>
)}
</div>
{/* Payment appears once opted in and not already active */}
{enabled && !isActive && (
<div className="px-4 pb-4">
<PlatformFeePayment
type="labProfile"
entityCoord={entityCoord}
onSuccess={() => { /* hook auto-updates isActive */ }}
context="Your profile stays live for free after Pro expires — you just lose the badge & boost."
/>
</div>
)}
</div>
);
}