The entire skate-spots codebase was accidentally copied into src/src/ and public/public/ instead of src/ and public/ directly. This left the real src/ directory as blank MKStack template stubs, explaining why the landing page showed the default template instead of SkateSpots. Changes made: - Copied all files from src/src/ up to src/ (App.tsx, AppRouter.tsx, main.tsx, all pages, components, hooks, contexts, lib, i18n, test) - Copied all files from public/public/ up to public/ (images, manifest, etc.) - Deleted src/src/ and public/public/ nested directories - Removed HelloWorld.tsx template stub from pages/ - Retained the previously fixed index.css (Tailwind v3 directives) Full skate-spots feature set is now active: - All 30+ routes (map, events, spots, coaches, marketplace, wallet, etc.) - NWCProvider, LanguageProvider, PWAInstallPrompt, AmberCallbackHandler - Skate community relays (goskatespots.nostr1.com, wotr.relatr.xyz, etc.) - Outfit Variable font, Amber pre-check in main.tsx - All hooks (useSkateSpots, useCoaches, useZaps, etc.) - i18n support (en/es) Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
160 lines
4.3 KiB
TypeScript
160 lines
4.3 KiB
TypeScript
/**
|
|
* SponsorSlot - Invisible placeholder for future sponsors
|
|
*
|
|
* This component renders nothing when no sponsors are provided,
|
|
* but can be "activated" by passing sponsor data.
|
|
*
|
|
* Sizes:
|
|
* - banner: Full-width horizontal banner (homepage, tour pages)
|
|
* - card: Square/rectangular card (sidebar, grid layouts)
|
|
* - inline: Small inline mention (challenge cards, footer)
|
|
*
|
|
* Usage:
|
|
* <SponsorSlot size="banner" /> // Invisible placeholder
|
|
* <SponsorSlot size="banner" sponsors={[{name: "Vans", logo: "...", url: "..."}]} />
|
|
*/
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
export interface Sponsor {
|
|
name: string;
|
|
logo?: string;
|
|
url?: string;
|
|
tier?: 'title' | 'gold' | 'silver' | 'challenge' | 'partner';
|
|
}
|
|
|
|
interface SponsorSlotProps {
|
|
size: 'banner' | 'card' | 'inline';
|
|
sponsors?: Sponsor[];
|
|
title?: string; // e.g., "Powered by", "Sponsored by", "Partners"
|
|
className?: string;
|
|
// For challenge sponsors
|
|
challengeId?: string;
|
|
}
|
|
|
|
export function SponsorSlot({
|
|
size,
|
|
sponsors,
|
|
title = 'Partners',
|
|
className,
|
|
}: SponsorSlotProps) {
|
|
// If no sponsors, render nothing (invisible placeholder)
|
|
if (!sponsors || sponsors.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// Banner style - full width, horizontal layout
|
|
if (size === 'banner') {
|
|
return (
|
|
<div className={cn(
|
|
'w-full py-6 border-t border-b border-gray-800 bg-black/50',
|
|
className
|
|
)}>
|
|
<div className="container px-4">
|
|
<p className="text-xs text-gray-500 uppercase tracking-wider mb-4 text-center">
|
|
{title}
|
|
</p>
|
|
<div className="flex items-center justify-center gap-8 flex-wrap">
|
|
{sponsors.map((sponsor) => (
|
|
<SponsorItem key={sponsor.name} sponsor={sponsor} size="banner" />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Card style - for sidebar or grid layouts
|
|
if (size === 'card') {
|
|
return (
|
|
<div className={cn(
|
|
'p-4 rounded-lg border border-gray-800 bg-black/50',
|
|
className
|
|
)}>
|
|
<p className="text-xs text-gray-500 uppercase tracking-wider mb-3">
|
|
{title}
|
|
</p>
|
|
<div className="flex flex-col gap-3">
|
|
{sponsors.map((sponsor) => (
|
|
<SponsorItem key={sponsor.name} sponsor={sponsor} size="card" />
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Inline style - small, compact
|
|
if (size === 'inline') {
|
|
return (
|
|
<div className={cn(
|
|
'flex items-center gap-2 text-xs text-gray-500',
|
|
className
|
|
)}>
|
|
<span>{title}:</span>
|
|
{sponsors.map((sponsor, idx) => (
|
|
<span key={sponsor.name}>
|
|
<SponsorItem sponsor={sponsor} size="inline" />
|
|
{idx < sponsors.length - 1 && ', '}
|
|
</span>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Individual sponsor display
|
|
function SponsorItem({ sponsor, size }: { sponsor: Sponsor; size: 'banner' | 'card' | 'inline' }) {
|
|
const content = (
|
|
<div className={cn(
|
|
'flex items-center gap-2 transition-opacity hover:opacity-80',
|
|
size === 'banner' && 'flex-col',
|
|
size === 'card' && 'flex-row',
|
|
size === 'inline' && 'inline-flex',
|
|
)}>
|
|
{sponsor.logo && (
|
|
<img
|
|
src={sponsor.logo}
|
|
alt={sponsor.name}
|
|
className={cn(
|
|
'object-contain',
|
|
size === 'banner' && 'h-10 max-w-[120px]',
|
|
size === 'card' && 'h-6 max-w-[80px]',
|
|
size === 'inline' && 'h-4 max-w-[60px]',
|
|
)}
|
|
/>
|
|
)}
|
|
<span className={cn(
|
|
'font-medium',
|
|
size === 'banner' && 'text-sm text-gray-400',
|
|
size === 'card' && 'text-sm text-gray-300',
|
|
size === 'inline' && 'text-xs text-gray-400',
|
|
!sponsor.logo && 'text-gray-300', // More visible if no logo
|
|
)}>
|
|
{sponsor.name}
|
|
</span>
|
|
{sponsor.tier === 'title' && size !== 'inline' && (
|
|
<span className="text-[10px] text-yellow-500 uppercase">Title Sponsor</span>
|
|
)}
|
|
</div>
|
|
);
|
|
|
|
if (sponsor.url) {
|
|
return (
|
|
<a
|
|
href={sponsor.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="block"
|
|
>
|
|
{content}
|
|
</a>
|
|
);
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
// Export types for use in tour schema
|
|
export type { Sponsor as TourSponsor };
|