- 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>
59 lines
2 KiB
TypeScript
59 lines
2 KiB
TypeScript
import { Sparkles, BadgeCheck } from 'lucide-react';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { cn } from '@/lib/utils';
|
|
import { isFeaturedActive } from '@/lib/types';
|
|
import { useFeaturedVerification } from '@/hooks/useFeaturedVerification';
|
|
|
|
interface ProBadgeProps {
|
|
/** The `featuredUntil` unix-seconds timestamp from a parsed entity. */
|
|
featuredUntil?: number;
|
|
className?: string;
|
|
/** Compact = icon only (for dense card corners). */
|
|
compact?: boolean;
|
|
/**
|
|
* When set (an addressable `kind:pubkey:d` coordinate), the badge verifies the
|
|
* featured status against a NIP-57 zap receipt and shows a "Verified" state
|
|
* when found. Use on detail pages; omit in dense lists to avoid over-fetching.
|
|
*/
|
|
entityCoord?: string;
|
|
}
|
|
|
|
/**
|
|
* "Pro" badge shown on featured Lab profiles, events, and tours.
|
|
* Renders nothing if the entity isn't currently featured.
|
|
*/
|
|
export function ProBadge({ featuredUntil, className, compact = false, entityCoord }: ProBadgeProps) {
|
|
const { data: status } = useFeaturedVerification(
|
|
entityCoord,
|
|
entityCoord ? featuredUntil : undefined,
|
|
);
|
|
|
|
if (!isFeaturedActive(featuredUntil)) return null;
|
|
|
|
const verified = status === 'verified';
|
|
|
|
return (
|
|
<Badge
|
|
className={cn(
|
|
'gap-1 border-0 font-black uppercase tracking-wide shadow-sm',
|
|
verified
|
|
? 'bg-gradient-to-r from-emerald-400 to-green-500 text-black shadow-green-500/30'
|
|
: 'bg-gradient-to-r from-amber-400 to-orange-500 text-black shadow-orange-500/30',
|
|
compact ? 'px-1.5 py-0 text-[9px]' : 'text-[10px]',
|
|
className,
|
|
)}
|
|
title={
|
|
verified
|
|
? 'Verified Pro — payment confirmed on-chain via a Nostr zap receipt.'
|
|
: 'Pro listing — this profile supports SkateSpots and is featured.'
|
|
}
|
|
>
|
|
{verified ? (
|
|
<BadgeCheck className={compact ? 'h-2.5 w-2.5' : 'h-3 w-3'} />
|
|
) : (
|
|
<Sparkles className={compact ? 'h-2.5 w-2.5' : 'h-3 w-3'} />
|
|
)}
|
|
Pro
|
|
</Badge>
|
|
);
|
|
}
|