SkateSpots/src/components/SponsorSlot.tsx
shakespeare.diy e29d35f1e4 fix: restore full skate-spots source tree from misplaced src/src/ copy
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>
2026-06-15 22:18:28 +07:00

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 };