- First-visit language auto-detect (Spanish devices land in es; stored choice wins). - Sasquatch AI replies in the app language (skate trick names kept English). - i18n safety net: scripts/check-i18n.mjs wired into npm test — fails on any t() key missing from en/es or any en/es key drift. - ~1,680 new keys (2,297 total) across mapUi / spotUi / marketUi / labUi and ~65 files wired to t(): Map, spots (detail/create/edit/chat), marketplace, trades, offers, escrow, shipping, The Lab, /become/* forms, coaches, skaters. - Policy: only skate TRICK names stay English; user-generated content untouched.
106 lines
4.3 KiB
TypeScript
106 lines
4.3 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 { useTranslation } from '@/contexts/language';
|
|
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 { t } = useTranslation();
|
|
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)}>
|
|
{t('labUi.proUpgrade.goPro')} <span className="text-gray-500 font-normal normal-case">{t('labUi.proUpgrade.optional')}</span>
|
|
</p>
|
|
<p className="text-xs text-gray-400 mt-0.5">
|
|
{t('labUi.proUpgrade.freeTo')} <strong className="text-amber-400">{t('labUi.proUpgrade.proBadge')}</strong> and
|
|
{' '}<strong className="text-amber-400">{t('labUi.proUpgrade.featuredPlacement')}</strong> {t('labUi.proUpgrade.atTop')}
|
|
</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" /> {t('labUi.proUpgrade.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" /> {t('labUi.proUpgrade.benefit1')}</li>
|
|
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 flex-shrink-0" /> {t('labUi.proUpgrade.benefit2')}</li>
|
|
<li className="flex items-center gap-2"><Check className="h-3.5 w-3.5 text-amber-400 flex-shrink-0" /> {t('labUi.proUpgrade.benefit3')}</li>
|
|
</ul>
|
|
)}
|
|
|
|
{/* Already active */}
|
|
{isActive && (
|
|
<p className="mt-2 text-xs text-gray-500">
|
|
{isPlatformOwner
|
|
? t('labUi.proUpgrade.platformOwner')
|
|
: t(daysRemaining === 1 ? 'labUi.proUpgrade.proActiveOne' : 'labUi.proUpgrade.proActiveMany', { count: daysRemaining })}
|
|
</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={t('labUi.proUpgrade.context')}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|