VibeCoderzVibeCoderz
All Prompts
BroLabFanbase UI Preview

BroLabFanbase

Контейнер приложения BroLab Fanbase: лендинг, дашборды, оболочка. Основной элемент для управления фан-сообществом.

by TreiguaLive Preview

Prompt

# BroLabFanbase

You are given a task to integrate an existing React component in the codebase

~~~/README.md
# BroLab Fanbase

A minimal MVP platform for artists and fans.

## Features
- **Landing Page**: Editorial style, conversion focused.
- **Fan Dashboard**: Follow artists, view purchases.
- **Artist Dashboard**: Manage profile, links, events.
- **Public Hub**: Artist public page.

## Usage

```tsx
import BroLabFanbase from '@/sd-components/717d7811-1df6-403a-8a33-21a315b8c315';

export default function App() {
  return <BroLabFanbase />;
}
```
~~~

~~~/src/App.tsx
import BroLabFanbase from './Component';

export default function App() {
  return <BroLabFanbase />;
}
~~~

~~~/package.json
{
  "name": "brolab-fanbase",
  "description": "BroLab Fanbase MVP - Artist & Fan Platform",
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.28.0",
    "lucide-react": "^0.300.0",
    "framer-motion": "^10.16.4",
    "clsx": "^2.1.0",
    "tailwind-merge": "^2.2.0",
    "date-fns": "^3.0.0",
    "zod": "^3.22.4"
  }
}
~~~

~~~/src/lib/utils.ts
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
~~~

~~~/src/Component.tsx
import { HashRouter, Routes, Route } from 'react-router-dom';
import LandingPage from './pages/LandingPage';
import FanDashboard from './pages/dashboard/FanDashboard';
import ArtistDashboard from './pages/dashboard/ArtistDashboard';
import ArtistProfile from './pages/dashboard/ArtistProfile';
import PublicArtistHub from './pages/PublicArtistHub';

// Re-export specific pages for easy access if needed
export { LandingPage, FanDashboard, ArtistDashboard, PublicArtistHub };

// Main Component that includes Routing
export function BroLabFanbase() {
  return (
    <HashRouter>
      <Routes>
        <Route path="/" element={<LandingPage />} />
        
        {/* Fan Routes */}
        <Route path="/me" element={<FanDashboard />} />
        
        {/* Artist Routes */}
        <Route path="/dashboard" element={<ArtistDashboard />} />
        <Route path="/dashboard/profile" element={<ArtistProfile />} />
        
        {/* Public Routes */}
        <Route path="/:slug" element={<PublicArtistHub />} />
      </Routes>
    </HashRouter>
  );
}

// Default export for the component system
export default BroLabFanbase;
~~~

~~~/src/i18n/getLocale.ts
/**
 * Minimal i18n scaffolding
 * Detects locale from browser or defaults to EN.
 */
export function getLocale(): string {
  if (typeof navigator === 'undefined') return 'en';
  
  const languages = navigator.languages || [navigator.language];
  const preferred = languages[0].split('-')[0].toLowerCase();
  
  return ['en', 'fr'].includes(preferred) ? preferred : 'en';
}
~~~

~~~/src/i18n/strings/en.ts
export const strings = {
  landing: {
    hero: {
      title: "Your career isn't an algorithm.",
      subtitle: "Own your fanbase. Build a mobile-first artist hub.",
      cta_primary: "Join the beta",
      cta_secondary: "Watch 30s demo"
    },
    waitlist: {
      placeholder: "Enter your email",
      submit: "Join Waitlist",
      success: "You're on the list!"
    }
  },
  common: {
    signIn: "Sign In",
    signOut: "Sign Out",
    loading: "Loading..."
  }
};
~~~

~~~/src/pages/LandingPage.tsx
/**
 * Landing Page
 * Phase 1: High conversion, editorial style, mobile-first.
 */
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { motion } from 'framer-motion';
import { ArrowRight, Play, Check } from 'lucide-react';
import { cn } from '../lib/utils';

export default function LandingPage() {
  const [email, setEmail] = useState('');

  const handleWaitlistSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // TODO: Connect to Convex waitlist.submit
    alert('Added to waitlist: ' + email);
    setEmail('');
  };

  return (
    <div className="min-h-screen bg-background text-foreground flex flex-col font-sans">
      {/* Navbar */}
      <nav className="flex items-center justify-between p-6 max-w-7xl mx-auto w-full">
        <div className="font-serif font-bold text-2xl tracking-tight">BroLab Fanbase</div>
        <div className="flex items-center gap-4">
          <Link to="/sign-in" className="text-sm font-medium hover:text-primary/80 transition-colors">
            Sign In
          </Link>
          <Link 
            to="/dashboard" 
            className="hidden sm:flex bg-primary text-primary-foreground px-4 py-2 rounded-full text-sm font-medium hover:bg-primary/90 transition-colors"
          >
            Join Beta
          </Link>
        </div>
      </nav>

      {/* Hero Section */}
      <section className="flex-1 flex flex-col items-center justify-center text-center px-4 py-20 relative overflow-hidden">
        {/* Abstract Background Elements */}
        <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[800px] bg-accent/20 rounded-full blur-[120px] -z-10" />
        
        <motion.div 
          initial={{ opacity: 0, y: 20 }}
          animate={{ opacity: 1, y: 0 }}
          transition={{ duration: 0.8, ease: "easeOut" }}
          className="max-w-4xl mx-auto space-y-8"
        >
          <h1 className="text-5xl md:text-7xl lg:text-8xl font-serif font-bold tracking-tight leading-[1.1]">
            Your career isn’t <br />
            <span className="text-transparent bg-clip-text bg-gradient-to-r from-accent to-purple-400">
              an algorithm.
            </span>
          </h1>
          
          <p className="text-xl md:text-2xl text-muted-foreground max-w-2xl mx-auto font-light leading-relaxed">
            Own your fanbase. Build a mobile-first artist hub where fans follow your drops, never miss shows, and support you — all from one link.
          </p>

          <div className="flex flex-col sm:flex-row items-center justify-center gap-4 pt-8">
            <form onSubmit={handleWaitlistSubmit} className="flex w-full sm:w-auto gap-2">
              <input 
                type="email" 
                placeholder="Enter your email" 
                required
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                className="bg-secondary/50 border border-border px-4 py-3 rounded-full w-full sm:w-80 focus:outline-none focus:ring-2 focus:ring-accent transition-all"
              />
              <button 
                type="submit"
                className="bg-primary text-primary-foreground px-6 py-3 rounded-full font-medium hover:bg-primary/90 transition-all flex items-center gap-2 whitespace-nowrap"
              >
                Join Beta <ArrowRight className="w-4 h-4" />
              </button>
            </form>
          </div>

          <div className="pt-8 flex items-center justify-center gap-2 text-sm text-muted-foreground">
            <button className="flex items-center gap-2 hover:text-foreground transition-colors group">
              <div className="w-8 h-8 rounded-full border border-border flex items-center justify-center group-hover:border-accent transition-colors">
                <Play className="w-3 h-3 fill-current" />
              </div>
              Watch 30s demo
            </button>
            <span className="w-1 h-1 bg-border rounded-full mx-2" />
            <span>No credit card required</span>
          </div>
        </motion.div>
      </section>

      {/* Feature Grid (Minimal) */}
      <section className="py-24 px-4 bg-secondary/20">
        <div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-12">
          {[
            {
              title: "Direct to Fan",
              desc: "No algorithms hiding your posts. When you drop, your fans know immediately via their dashboard."
            },
            {
              title: "Global Commerce",
              desc: "Sell tickets, merch, and digital downloads directly. Keep more of what you earn."
            },
            {
              title: "Own Your Data",
              desc: "Get real emails and phone numbers. Build a portable asset that lasts your entire career."
            }
          ].map((feature, i) => (
            <div key={i} className="space-y-4">
              <div className="w-12 h-1 bg-accent mb-6" />
              <h3 className="text-2xl font-serif font-bold">{feature.title}</h3>
              <p className="text-muted-foreground leading-relaxed">
                {feature.desc}
              </p>
            </div>
          ))}
        </div>
      </section>

      {/* Footer */}
      <footer className="py-12 px-6 border-t border-border text-center text-sm text-muted-foreground">
        <div className="flex items-center justify-center gap-6 mb-8">
          <Link to="#" className="hover:text-foreground">Terms</Link>
          <Link to="#" className="hover:text-foreground">Privacy</Link>
          <Link to="#" className="hover:text-foreground">Contact</Link>
        </div>
        <p>&copy; {new Date().getFullYear()} BroLab Entertainment. All rights reserved.</p>
      </footer>
    </div>
  );
}
~~~

~~~/src/components/AppShell.tsx
/**
 * AppShell Component
 * Responsive layout shell with Desktop Sidebar and Mobile Top/Drawer navigation.
 */
import React, { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { cn } from '../lib/utils';
import { 
  Menu, 
  X, 
  Home, 
  User, 
  Music, 
  Calendar, 
  CreditCard, 
  Settings, 
  LogOut,
  LayoutDashboard,
  Heart
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';

type NavItem = {
  label: string;
  href: string;
  icon: React.ElementType;
};

interface AppShellProps {
  children: React.ReactNode;
  userType: 'fan' | 'artist';
}

export function AppShell({ children, userType }: AppShellProps) {
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
  const location = useLocation();

  const artistNav: NavItem[] = [
    { label: 'Overview', href: '/dashboard', icon: LayoutDashboard },
    { label: 'Profile & Bio', href: '/dashboard/profile', icon: User },
    { label: 'Links', href: '/dashboard/links', icon: Music },
    { label: 'Events', href: '/dashboard/events', icon: Calendar },
    { label: 'Billing', href: '/dashboard/billing', icon: CreditCard },
  ];

  const fanNav: NavItem[] = [
    { label: 'My Following', href: '/me', icon: Heart },
    { label: 'Purchases', href: '/me/purchases', icon: Music },
    { label: 'Billing', href: '/me/billing', icon: CreditCard },
  ];

  const navItems = userType === 'artist' ? artistNav : fanNav;

  return (
    <div className="min-h-screen bg-background text-foreground flex flex-col lg:flex-row">
      {/* Mobile Top Nav */}
      <div className="lg:hidden flex items-center justify-between p-4 border-b border-border bg-background/80 backdrop-blur-md sticky top-0 z-50">
        <Link to="/" className="font-serif font-bold text-xl tracking-tight">
          BroLab Fanbase
        </Link>
        <button 
          onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
          className="p-2 hover:bg-accent rounded-full transition-colors"
        >
          {isMobileMenuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
        </button>
      </div>

      {/* Mobile Drawer */}
      <AnimatePresence>
        {isMobileMenuOpen && (
          <motion.div 
            initial={{ opacity: 0, y: -20 }}
            animate={{ opacity: 1, y: 0 }}
            exit={{ opacity: 0, y: -20 }}
            className="lg:hidden fixed inset-0 top-[65px] bg-background z-40 p-6 flex flex-col gap-6"
          >
            <nav className="flex flex-col gap-2">
              {navItems.map((item) => (
                <Link
                  key={item.href}
                  to={item.href}
                  onClick={() => setIsMobileMenuOpen(false)}
                  className={cn(
                    "flex items-center gap-3 p-3 rounded-lg text-lg font-medium transition-colors",
                    location.pathname === item.href 
                      ? "bg-accent text-accent-foreground" 
                      : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
                  )}
                >
                  <item.icon className="w-5 h-5" />
                  {item.label}
                </Link>
              ))}
            </nav>
            
            <div className="mt-auto pt-6 border-t border-border">
              <div className="flex items-center gap-3 p-3 mb-2">
                <div className="w-10 h-10 rounded-full bg-accent flex items-center justify-center">
                  <User className="w-5 h-5" />
                </div>
                <div>
                  <p className="font-medium">User Name</p>
                  <p className="text-sm text-muted-foreground capitalize">{userType}</p>
                </div>
              </div>
              <button className="flex items-center gap-3 p-3 w-full text-left text-destructive hover:bg-destructive/10 rounded-lg transition-colors">
                <LogOut className="w-5 h-5" />
                Sign Out
              </button>
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* Desktop Sidebar */}
      <aside className="hidden lg:flex flex-col w-64 border-r border-border min-h-screen sticky top-0 bg-card">
        <div className="p-6">
          <Link to="/" className="font-serif font-bold text-xl tracking-tight block mb-8">
            BroLab Fanbase
          </Link>
          
          <nav className="flex flex-col gap-1">
            {navItems.map((item) => (
              <Link
                key={item.href}
                to={item.href}
                className={cn(
                  "flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors",
                  location.pathname === item.href 
                    ? "bg-accent text-accent-foreground" 
                    : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
                )}
              >
                <item.icon className="w-4 h-4" />
                {item.label}
              </Link>
            ))}
          </nav>
        </div>

        <div className="mt-auto p-4 border-t border-border">
          <div className="flex items-center gap-3 px-2 mb-4">
            <div className="w-8 h-8 rounded-full bg-accent flex items-center justify-center">
              <User className="w-4 h-4" />
            </div>
            <div className="overflow-hidden">
              <p className="font-medium text-sm truncate">User Name</p>
              <p className="text-xs text-muted-foreground capitalize">{userType}</p>
            </div>
          </div>
          <button className="flex items-center gap-3 px-2 py-1.5 w-full text-left text-xs text-muted-foreground hover:text-destructive transition-colors">
            <LogOut className="w-3 h-3" />
            Sign Out
          </button>
        </div>
      </aside>

      {/* Main Content */}
      <main className="flex-1 p-4 lg:p-8 overflow-y-auto">
        <div className="max-w-5xl mx-auto w-full">
          {children}
        </div>
      </main>
    </div>
  );
}
~~~

~~~/src/pages/PublicArtistHub.tsx
/**
 * Public Artist Hub
 * The main public page for an artist.
 * Route: /[artistSlug]
 */
import React, { useState } from 'react';
import { useParams, Link } from 'react-router-dom';
import { motion } from 'framer-motion';
import { 
  Play, 
  Calendar, 
  ShoppingBag, 
  Share2, 
  Instagram, 
  Youtube, 
  Twitter, 
  Music, 
  Heart 
} from 'lucide-react';
import { cn } from '../lib/utils';

// Mock Data
const ARTIST_DATA = {
  name: "The Midnight",
  slug: "themidnight",
  bio: "We are a synthwave band from Los Angeles. Our new album Heroes is out now.",
  avatarUrl: "https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80",
  coverUrl: "https://images.unsplash.com/photo-1514525253440-b393452e8d26?ixlib=rb-4.0.3&auto=format&fit=crop&w=2000&q=80",
  socials: [
    { icon: Instagram, url: "#" },
    { icon: Youtube, url: "#" },
    { icon: Twitter, url: "#" },
    { icon: Music, url: "#" },
  ],
  links: [
    { id: 1, title: "Listen to 'Heroes' on Spotify", url: "#", type: "music" },
    { id: 2, title: "Official Merch Store", url: "#", type: "shop" },
    { id: 3, title: "Watch 'Change Your Heart' Video", url: "#", type: "video" },
  ],
  events: [
    { id: 1, date: "2024-06-15", city: "Los Angeles, CA", venue: "The Greek Theatre", ticketUrl: "#" },
    { id: 2, date: "2024-06-20", city: "New York, NY", venue: "Terminal 5", ticketUrl: "#" },
    { id: 3, date: "2024-07-05", city: "London, UK", venue: "Brixton Academy", ticketUrl: "#" },
  ]
};

export default function PublicArtistHub() {
  const { slug } = useParams();
  const [activeTab, setActiveTab] = useState<'links' | 'events'>('links');
  const [isFollowing, setIsFollowing] = useState(false);

  // In a real app, fetch data based on slug. 
  // For demo, we just use the mock data regardless of slug.
  const artist = ARTIST_DATA;

  return (
    <div className="min-h-screen bg-background text-foreground font-sans pb-20">
      {/* Hero Section */}
      <div className="relative h-[60vh] w-full overflow-hidden">
        <div className="absolute inset-0 bg-black/40 z-10" />
        <div className="absolute inset-0 bg-gradient-to-t from-background via-transparent to-transparent z-20" />
        <img 
          src={artist.coverUrl} 
          alt="Cover" 
          className="w-full h-full object-cover"
        />
        
        {/* Navbar Overlay */}
        <div className="absolute top-0 left-0 right-0 z-50 p-6 flex justify-between items-center">
          <Link to="/" className="text-white/80 hover:text-white font-serif font-bold text-xl drop-shadow-md">
            BroLab Fanbase
          </Link>
          <button className="p-2 bg-white/10 backdrop-blur-md rounded-full hover:bg-white/20 transition-colors text-white">
            <Share2 className="w-5 h-5" />
          </button>
        </div>

        {/* Artist Info Overlay */}
        <div className="absolute bottom-0 left-0 right-0 z-30 p-6 md:p-12 flex flex-col items-center text-center">
          <motion.div 
            initial={{ opacity: 0, scale: 0.9 }}
            animate={{ opacity: 1, scale: 1 }}
            className="w-24 h-24 md:w-32 md:h-32 rounded-full border-4 border-background overflow-hidden mb-4 shadow-xl"
          >
            <img src={artist.avatarUrl} alt={artist.name} className="w-full h-full object-cover" />
          </motion.div>
          
          <motion.h1 
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ delay: 0.1 }}
            className="text-4xl md:text-6xl font-serif font-bold text-white mb-2 drop-shadow-lg"
          >
            {artist.name}
          </motion.h1>
          
          <motion.p 
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ delay: 0.2 }}
            className="text-white/80 max-w-lg mb-6 drop-shadow-md"
          >
            {artist.bio}
          </motion.p>

          <motion.div 
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ delay: 0.3 }}
            className="flex items-center gap-3"
          >
            <button 
              onClick={() => setIsFollowing(!isFollowing)}
              className={cn(
                "px-8 py-3 rounded-full font-medium transition-all flex items-center gap-2",
                isFollowing 
                  ? "bg-transparent border border-white/50 text-white hover:bg-white/10" 
                  : "bg-white text-black hover:bg-white/90"
              )}
            >
              {isFollowing ? (
                <>Following</>
              ) : (
                <>Follow <Heart className="w-4 h-4 fill-current" /></>
              )}
            </button>
            
            <div className="flex items-center gap-2 bg-black/30 backdrop-blur-md rounded-full px-4 py-2">
              {artist.socials.map((social, i) => (
                <a key={i} href={social.url} className="p-2 text-white/80 hover:text-white transition-colors">
                  <social.icon className="w-5 h-5" />
                </a>
              ))}
            </div>
          </motion.div>
        </div>
      </div>

      {/* Content Tabs */}
      <div className="max-w-3xl mx-auto px-4 mt-8">
        <div className="flex border-b border-border mb-8">
          <button 
            onClick={() => setActiveTab('links')}
            className={cn(
              "flex-1 pb-4 text-center font-medium transition-colors relative",
              activeTab === 'links' ? "text-foreground" : "text-muted-foreground hover:text-foreground"
            )}
          >
            Latest Drops
            {activeTab === 'links' && (
              <motion.div layoutId="tab" className="absolute bottom-0 left-0 right-0 h-0.5 bg-accent" />
            )}
          </button>
          <button 
            onClick={() => setActiveTab('events')}
            className={cn(
              "flex-1 pb-4 text-center font-medium transition-colors relative",
              activeTab === 'events' ? "text-foreground" : "text-muted-foreground hover:text-foreground"
            )}
          >
            Tour Dates
            {activeTab === 'events' && (
              <motion.div layoutId="tab" className="absolute bottom-0 left-0 right-0 h-0.5 bg-accent" />
            )}
          </button>
        </div>

        <div className="space-y-4">
          {activeTab === 'links' ? (
            <motion.div 
              initial={{ opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}
              className="space-y-4"
            >
              {artist.links.map((link) => (
                <a 
                  key={link.id}
                  href={link.url}
                  className="block group bg-card border border-border hover:border-accent p-4 rounded-xl transition-all hover:translate-y-[-2px] hover:shadow-lg"
                >
                  <div className="flex items-center justify-between">
                    <span className="font-medium text-lg">{link.title}</span>
                    <div className="w-8 h-8 rounded-full bg-secondary flex items-center justify-center group-hover:bg-accent group-hover:text-white transition-colors">
                      {link.type === 'shop' ? <ShoppingBag className="w-4 h-4" /> : <Play className="w-4 h-4 fill-current" />}
                    </div>
                  </div>
                </a>
              ))}
            </motion.div>
          ) : (
            <motion.div 
              initial={{ opacity: 0, y: 10 }}
              animate={{ opacity: 1, y: 0 }}
              className="space-y-4"
            >
              {artist.events.map((event) => (
                <div 
                  key={event.id}
                  className="flex items-center gap-4 bg-card border border-border p-4 rounded-xl"
                >
                  <div className="bg-secondary px-4 py-2 rounded-lg text-center min-w-[70px]">
                    <span className="block text-xs text-muted-foreground uppercase font-bold">Jun</span>
                    <span className="block text-xl font-bold">{event.date.split('-')[2]}</span>
                  </div>
                  <div className="flex-1">
                    <h3 className="font-bold text-lg">{event.city}</h3>
                    <p className="text-muted-foreground text-sm">{event.venue}</p>
                  </div>
                  <button className="bg-primary text-primary-foreground px-4 py-2 rounded-full text-sm font-medium hover:bg-primary/90 transition-colors">
                    Tickets
                  </button>
                </div>
              ))}
            </motion.div>
          )}
        </div>
      </div>
    </div>
  );
}
~~~

~~~/src/pages/dashboard/FanDashboard.tsx
import React from 'react';
import { AppShell } from '../../components/AppShell';
import { 
  Heart, 
  Music, 
  Calendar, 
  Share2, 
  MessageCircle, 
  Play, 
  MoreHorizontal,
  Ticket,
  ShoppingBag,
  ExternalLink,
  Users
} from 'lucide-react';
import { Link } from 'react-router-dom';

// --- Mock Data ---

type FeedItemType = 'release' | 'concert' | 'merch' | 'update';

interface FeedItem {
  id: string;
  type: FeedItemType;
  artist: {
    name: string;
    avatar: string;
    slug: string;
  };
  timestamp: string;
  content: {
    title: string;
    description: string;
    image?: string;
    meta?: string; // e.g., "Single", "Album", "Tour"
    link?: string;
  };
  stats: {
    likes: number;
    comments: number;
  };
}

const FEED_ITEMS: FeedItem[] = [
  {
    id: '1',
    type: 'release',
    artist: {
      name: 'The Midnight',
      avatar: 'https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?w=100&h=100&fit=crop',
      slug: 'the-midnight'
    },
    timestamp: '2 hours ago',
    content: {
      title: 'Heroes (Instrumental) - Out Now',
      description: 'The instrumental version of our latest album is finally here. Experience the synthwave journey without words.',
      image: 'https://images.unsplash.com/photo-1614613535308-eb5fbd3d2c17?w=800&q=80',
      meta: 'New Album',
      link: '#'
    },
    stats: { likes: 1240, comments: 85 }
  },
  {
    id: '2',
    type: 'concert',
    artist: {
      name: 'Gunship',
      avatar: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=100&h=100&fit=crop',
      slug: 'gunship'
    },
    timestamp: '5 hours ago',
    content: {
      title: 'World Tour 2024 Announced',
      description: 'We are hitting the road this summer! Check out the dates and grab your tickets before they are gone.',
      image: 'https://images.unsplash.com/photo-1459749411177-0473ef71607b?w=800&q=80',
      meta: 'Tour Announcement',
      link: '#'
    },
    stats: { likes: 3500, comments: 420 }
  },
  {
    id: '3',
    type: 'merch',
    artist: {
      name: 'FM-84',
      avatar: 'https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=100&h=100&fit=crop',
      slug: 'fm-84'
    },
    timestamp: '1 day ago',
    content: {
      title: 'Limited Edition Vinyl Restock',
      description: 'Atlas is back in stock. 180g colored vinyl. Limited to 500 copies worldwide.',
      image: 'https://images.unsplash.com/photo-1603048588665-791ca8aea617?w=800&q=80',
      meta: 'Merch Drop',
      link: '#'
    },
    stats: { likes: 890, comments: 120 }
  }
];

const SUGGESTED_ARTISTS = [
  { id: 1, name: 'Timecop1983', genre: 'Synthwave', image: 'https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?w=100&h=100&fit=crop' },
  { id: 2, name: 'Carpenter Brut', genre: 'Darksynth', image: 'https://images.unsplash.com/photo-1493225255756-d9584f8606e9?w=100&h=100&fit=crop' },
  { id: 3, name: 'Perturbator', genre: 'Cyberpunk', image: 'https://images.unsplash.com/photo-1514525253440-b393452e2729?w=100&h=100&fit=crop' },
];

// --- Sub-Components ---

function FeedCard({ item }: { item: FeedItem }) {
  return (
    <div className="bg-card border border-border/50 rounded-2xl overflow-hidden mb-6 transition-all hover:border-border hover:shadow-lg group">
      {/* Header */}
      <div className="p-4 flex items-center justify-between">
        <div className="flex items-center gap-3">
          <Link to={`/${item.artist.slug}`} className="relative">
            <div className="w-10 h-10 rounded-full overflow-hidden ring-2 ring-background group-hover:ring-accent transition-all">
              <img src={item.artist.avatar} alt={item.artist.name} className="w-full h-full object-cover" />
            </div>
            {/* Online/New Indicator could go here */}
          </Link>
          <div>
            <Link to={`/${item.artist.slug}`} className="font-bold hover:underline decoration-accent underline-offset-4">
              {item.artist.name}
            </Link>
            <div className="flex items-center gap-2 text-xs text-muted-foreground">
              <span>{item.timestamp}</span>
              <span>•</span>
              <span className="flex items-center gap-1">
                {item.type === 'release' && <Music className="w-3 h-3" />}
                {item.type === 'concert' && <Ticket className="w-3 h-3" />}
                {item.type === 'merch' && <ShoppingBag className="w-3 h-3" />}
                {item.type === 'update' && <MessageCircle className="w-3 h-3" />}
                <span className="uppercase font-medium tracking-wider">{item.content.meta || item.type}</span>
              </span>
            </div>
          </div>
        </div>
        <button className="text-muted-foreground hover:text-foreground">
          <MoreHorizontal className="w-5 h-5" />
        </button>
      </div>

      {/* Media Content */}
      <div className="relative aspect-video bg-muted overflow-hidden">
        {item.content.image ? (
          <img 
            src={item.content.image} 
            alt={item.content.title} 
            className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
          />
        ) : (
          <div className="w-full h-full flex items-center justify-center bg-secondary">
            <Music className="w-12 h-12 text-muted-foreground opacity-50" />
          </div>
        )}
        
        {/* Overlay Action Button (Play/View) */}
        <div className="absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
          {item.type === 'release' && (
            <button className="w-16 h-16 rounded-full bg-primary/90 text-primary-foreground flex items-center justify-center backdrop-blur-sm transform scale-90 group-hover:scale-100 transition-all hover:bg-primary">
              <Play className="w-8 h-8 ml-1" />
            </button>
          )}
        </div>
      </div>

      {/* Content Body */}
      <div className="p-5">
        <h3 className="text-xl font-serif font-bold mb-2 group-hover:text-accent transition-colors">
          {item.content.title}
        </h3>
        <p className="text-muted-foreground text-sm leading-relaxed mb-4">
          {item.content.description}
        </p>

        {/* Action Buttons */}
        <div className="flex items-center justify-between border-t border-border/50 pt-4 mt-2">
          <div className="flex items-center gap-4">
            <button className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-red-500 transition-colors">
              <Heart className="w-4 h-4" />
              <span>{item.stats.likes}</span>
            </button>
            <button className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-blue-400 transition-colors">
              <MessageCircle className="w-4 h-4" />
              <span>{item.stats.comments}</span>
            </button>
          </div>
          
          <div className="flex items-center gap-2">
            <button className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors">
              <Share2 className="w-4 h-4" />
            </button>
            
            {/* Primary Action */}
            {item.type === 'concert' ? (
              <button className="bg-foreground text-background px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-wide hover:bg-foreground/90 transition-colors flex items-center gap-2">
                <Ticket className="w-3 h-3" />
                Get Tickets
              </button>
            ) : item.type === 'merch' ? (
              <button className="bg-foreground text-background px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-wide hover:bg-foreground/90 transition-colors flex items-center gap-2">
                <ShoppingBag className="w-3 h-3" />
                Shop Now
              </button>
            ) : (
               <button className="bg-accent text-accent-foreground px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-wide hover:bg-accent/90 transition-colors flex items-center gap-2">
                <Play className="w-3 h-3" />
                Listen
              </button>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

function SuggestedArtistCard({ artist }: { artist: typeof SUGGESTED_ARTISTS[0] }) {
  return (
    <div className="flex items-center justify-between p-3 rounded-xl hover:bg-accent/5 transition-colors group">
      <div className="flex items-center gap-3">
        <div className="w-10 h-10 rounded-full bg-secondary overflow-hidden">
          <img src={artist.image} alt={artist.name} className="w-full h-full object-cover" />
        </div>
        <div>
          <h4 className="font-bold text-sm group-hover:text-accent transition-colors">{artist.name}</h4>
          <p className="text-xs text-muted-foreground">{artist.genre}</p>
        </div>
      </div>
      <button className="text-xs font-bold text-accent hover:text-accent-foreground border border-accent hover:bg-accent px-3 py-1 rounded-full transition-all">
        Follow
      </button>
    </div>
  );
}

// --- Main Page Component ---

export default function FanDashboard() {
  return (
    <AppShell userType="fan">
      <div className="grid grid-cols-1 lg:grid-cols-12 gap-8">
        
        {/* LEFT COLUMN: Main Feed (8 cols) */}
        <div className="lg:col-span-8 space-y-2">
          {/* Feed Header */}
          <div className="flex items-end justify-between mb-6">
            <div>
              <h1 className="text-3xl font-serif font-bold">Your Feed</h1>
              <p className="text-muted-foreground text-sm">Latest updates from your artists</p>
            </div>
            <div className="hidden sm:flex gap-2">
              <button className="px-3 py-1 text-xs font-medium bg-foreground text-background rounded-full">All</button>
              <button className="px-3 py-1 text-xs font-medium text-muted-foreground hover:bg-secondary rounded-full">Music</button>
              <button className="px-3 py-1 text-xs font-medium text-muted-foreground hover:bg-secondary rounded-full">Events</button>
            </div>
          </div>

          {/* Feed Items */}
          <div className="space-y-6">
            {FEED_ITEMS.map((item) => (
              <FeedCard key={item.id} item={item} />
            ))}
            
            {/* End of Feed */}
            <div className="py-8 text-center">
              <div className="inline-block p-3 rounded-full bg-secondary text-muted-foreground mb-3 animate-pulse">
                <MoreHorizontal className="w-6 h-6" />
              </div>
              <p className="text-sm text-muted-foreground">You are all caught up!</p>
              <button className="mt-4 text-accent text-sm font-medium hover:underline">Find more artists</button>
            </div>
          </div>
        </div>

        {/* RIGHT COLUMN: Sidebar (4 cols) */}
        <div className="lg:col-span-4 space-y-8">
          
          {/* My Stats Widget */}
          <div className="bg-card border border-border/50 rounded-2xl p-5 sticky top-24">
            <h2 className="font-serif font-bold text-lg mb-4 flex items-center gap-2">
              <Users className="w-5 h-5 text-accent" />
              My Community
            </h2>
            <div className="grid grid-cols-2 gap-4 mb-6">
              <div className="bg-secondary/30 p-4 rounded-xl text-center">
                <span className="block text-2xl font-bold font-mono">12</span>
                <span className="text-xs text-muted-foreground uppercase tracking-wider">Following</span>
              </div>
              <div className="bg-secondary/30 p-4 rounded-xl text-center">
                <span className="block text-2xl font-bold font-mono">5</span>
                <span className="text-xs text-muted-foreground uppercase tracking-wider">Events</span>
              </div>
            </div>
            
            <div className="space-y-1">
              <div className="flex justify-between text-sm py-2 border-b border-border/50">
                <span className="text-muted-foreground">Next Event</span>
                <span className="font-medium text-right">Gunship, London<br/><span className="text-xs text-muted-foreground">Aug 14, 2024</span></span>
              </div>
              <div className="flex justify-between text-sm py-2">
                <span className="text-muted-foreground">Membership</span>
                <span className="font-medium text-accent">Pro Fan</span>
              </div>
            </div>
          </div>

          {/* Suggested Artists Widget */}
          <div className="bg-card border border-border/50 rounded-2xl p-5">
            <div className="flex items-center justify-between mb-4">
              <h2 className="font-serif font-bold text-lg">Suggested Artists</h2>
              <button className="text-xs text-accent hover:underline">View All</button>
            </div>
            <div className="space-y-2">
              {SUGGESTED_ARTISTS.map(artist => (
                <SuggestedArtistCard key={artist.id} artist={artist} />
              ))}
            </div>
          </div>

          {/* Mini Player / Featured Track (Optional decoration) */}
          <div className="relative overflow-hidden rounded-2xl bg-gradient-to-br from-indigo-900 to-purple-900 p-6 text-white">
            <div className="absolute top-0 right-0 p-4 opacity-20">
              <Music className="w-24 h-24" />
            </div>
            <p className="text-xs font-medium text-indigo-200 mb-1 uppercase tracking-widest">Featured Track</p>
            <h3 className="text-xl font-serif font-bold mb-1">Endless Summer</h3>
            <p className="text-sm text-indigo-200 mb-4">The Midnight</p>
            <div className="flex items-center gap-3">
              <button className="w-10 h-10 rounded-full bg-white text-indigo-900 flex items-center justify-center hover:scale-105 transition-transform">
                <Play className="w-5 h-5 ml-1" />
              </button>
              <div className="h-1 flex-1 bg-indigo-950/50 rounded-full overflow-hidden">
                <div className="h-full w-1/3 bg-indigo-400 rounded-full"></div>
              </div>
            </div>
          </div>

        </div>
      </div>
    </AppShell>
  );
}
~~~

~~~/src/pages/dashboard/ArtistProfile.tsx
import React, { useState } from 'react';
import { AppShell } from '../../components/AppShell';
import { Camera, Save, Globe, Instagram, Youtube, Twitter, Music } from 'lucide-react';

export default function ArtistProfile() {
  const [isEditing, setIsEditing] = useState(false);

  return (
    <AppShell userType="artist">
      <div className="space-y-6">
        <div className="flex items-center justify-between border-b border-border pb-6">
          <div>
            <h1 className="text-2xl font-serif font-bold">Profile & Bio</h1>
            <p className="text-muted-foreground text-sm">Customize how you appear on your public hub.</p>
          </div>
          <button 
            className="bg-primary text-primary-foreground px-4 py-2 rounded-md text-sm font-medium flex items-center gap-2"
            onClick={() => setIsEditing(!isEditing)}
          >
            <Save className="w-4 h-4" />
            Save Changes
          </button>
        </div>

        <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
          {/* Left Column: Avatar & Basic Info */}
          <div className="space-y-6">
            <div className="bg-card border border-border p-6 rounded-xl space-y-4">
              <h3 className="font-medium text-sm text-muted-foreground uppercase tracking-wider">Profile Image</h3>
              
              <div className="relative group">
                <div className="aspect-square rounded-xl overflow-hidden bg-secondary">
                  <img 
                    src="https://images.unsplash.com/photo-1570295999919-56ceb5ecca61?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80" 
                    alt="Profile" 
                    className="w-full h-full object-cover"
                  />
                  <div className="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center cursor-pointer">
                    <Camera className="w-8 h-8 text-white" />
                  </div>
                </div>
              </div>

              <div className="space-y-2">
                <label className="text-sm font-medium">Image URL</label>
                <input 
                  type="text" 
                  defaultValue="https://images.unsplash.com/..." 
                  className="w-full bg-secondary/50 border border-border rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-accent"
                />
                <p className="text-xs text-muted-foreground">For MVP, paste a direct image link.</p>
              </div>
            </div>
          </div>

          {/* Right Column: Public Data & Socials */}
          <div className="lg:col-span-2 space-y-6">
            <div className="bg-card border border-border p-6 rounded-xl space-y-6">
              <h3 className="font-medium text-sm text-muted-foreground uppercase tracking-wider">Public Data</h3>
              
              <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
                <div className="space-y-2">
                  <label className="text-sm font-medium">Display Name</label>
                  <input 
                    type="text" 
                    defaultValue="The Midnight" 
                    className="w-full bg-secondary/50 border border-border rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-accent"
                  />
                </div>
                <div className="space-y-2">
                  <label className="text-sm font-medium">Unique Slug</label>
                  <div className="flex items-center">
                    <span className="bg-secondary border border-r-0 border-border rounded-l-md px-3 py-2 text-sm text-muted-foreground">fan.brolab/</span>
                    <input 
                      type="text" 
                      defaultValue="themidnight" 
                      className="flex-1 bg-secondary/50 border border-border rounded-r-md px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-accent"
                    />
                  </div>
                </div>
                <div className="md:col-span-2 space-y-2">
                  <label className="text-sm font-medium">Bio</label>
                  <textarea 
                    rows={4}
                    defaultValue="We are a synthwave band from Los Angeles. Our new album Heroes is out now."
                    className="w-full bg-secondary/50 border border-border rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-accent resize-none"
                  />
                </div>
              </div>
            </div>

            <div className="bg-card border border-border p-6 rounded-xl space-y-6">
              <h3 className="font-medium text-sm text-muted-foreground uppercase tracking-wider">Social Links</h3>
              <div className="space-y-4">
                {[
                  { icon: Instagram, label: "Instagram", placeholder: "instagram.com/..." },
                  { icon: Youtube, label: "YouTube", placeholder: "youtube.com/..." },
                  { icon: Twitter, label: "X (Twitter)", placeholder: "x.com/..." },
                  { icon: Music, label: "Spotify", placeholder: "open.spotify.com/..." },
                  { icon: Globe, label: "Website", placeholder: "yourwebsite.com" },
                ].map((social, i) => (
                  <div key={i} className="flex items-center gap-4 p-3 bg-secondary/20 rounded-lg border border-transparent hover:border-border transition-colors">
                    <social.icon className="w-5 h-5 text-muted-foreground" />
                    <div className="flex-1 space-y-1">
                      <label className="text-xs font-medium block">{social.label}</label>
                      <input 
                        type="text" 
                        placeholder={social.placeholder}
                        className="w-full bg-transparent border-none p-0 text-sm focus:ring-0 placeholder:text-muted-foreground/50"
                      />
                    </div>
                    <div className="relative inline-flex h-6 w-11 items-center rounded-full bg-secondary border border-border cursor-pointer">
                      <span className="translate-x-6 inline-block h-4 w-4 transform rounded-full bg-green-500 transition"/>
                    </div>
                  </div>
                ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    </AppShell>
  );
}
~~~

~~~/src/pages/dashboard/ArtistDashboard.tsx
import React from 'react';
import { AppShell } from '../../components/AppShell';
import { Share2, Users, DollarSign, Calendar, Plus } from 'lucide-react';
import { Link } from 'react-router-dom';

export default function ArtistDashboard() {
  return (
    <AppShell userType="artist">
      <div className="space-y-8">
        <div className="flex items-center justify-between">
          <div>
            <h1 className="text-3xl font-serif font-bold mb-2">Artist Dashboard</h1>
            <p className="text-muted-foreground">Manage your hub and connect with your fanbase.</p>
          </div>
          <Link 
            to="/artist-name" 
            target="_blank"
            className="flex items-center gap-2 text-sm font-medium text-accent hover:underline"
          >
            View Public Hub <Share2 className="w-4 h-4" />
          </Link>
        </div>

        {/* Stats Grid */}
        <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div className="bg-card border border-border p-6 rounded-xl">
            <div className="flex items-center justify-between mb-4">
              <span className="text-muted-foreground text-sm">Total Followers</span>
              <Users className="w-4 h-4 text-muted-foreground" />
            </div>
            <span className="text-3xl font-bold">1,234</span>
            <span className="text-xs text-green-500 ml-2">+12% this month</span>
          </div>
          <div className="bg-card border border-border p-6 rounded-xl">
            <div className="flex items-center justify-between mb-4">
              <span className="text-muted-foreground text-sm">Revenue (30d)</span>
              <DollarSign className="w-4 h-4 text-muted-foreground" />
            </div>
            <span className="text-3xl font-bold">$4,500</span>
            <span className="text-xs text-green-500 ml-2">+5% this month</span>
          </div>
          <div className="bg-card border border-border p-6 rounded-xl">
            <div className="flex items-center justify-between mb-4">
              <span className="text-muted-foreground text-sm">Upcoming Events</span>
              <Calendar className="w-4 h-4 text-muted-foreground" />
            </div>
            <span className="text-3xl font-bold">3</span>
          </div>
        </div>

        {/* Quick Actions */}
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          <div className="bg-card border border-border p-6 rounded-xl">
            <h3 className="font-bold mb-4">Setup Checklist</h3>
            <div className="space-y-3">
              {[
                { label: 'Complete your profile', done: true },
                { label: 'Add your first event', done: true },
                { label: 'Connect Stripe for payouts', done: false },
                { label: 'Post your first update', done: false },
              ].map((task, i) => (
                <div key={i} className="flex items-center gap-3">
                  <div className={`w-5 h-5 rounded-full flex items-center justify-center border ${task.done ? 'bg-green-500 border-green-500 text-white' : 'border-border'}`}>
                    {task.done && <Users className="w-3 h-3" />} 
                    {/* Using Users as checkmark placeholder since Check icon wasn't imported */}
                  </div>
                  <span className={task.done ? 'text-muted-foreground line-through' : ''}>{task.label}</span>
                </div>
              ))}
            </div>
          </div>

          <div className="bg-card border border-border p-6 rounded-xl flex flex-col items-center justify-center text-center space-y-4">
            <div className="w-12 h-12 rounded-full bg-accent/20 flex items-center justify-center text-accent">
              <Plus className="w-6 h-6" />
            </div>
            <h3 className="font-bold">Create New Content</h3>
            <p className="text-sm text-muted-foreground">Add a new event, product, or link to your hub.</p>
            <div className="flex gap-2">
              <button className="bg-secondary text-secondary-foreground hover:bg-secondary/80 px-4 py-2 rounded-md text-sm font-medium">Add Link</button>
              <button className="bg-accent text-accent-foreground hover:bg-accent/90 px-4 py-2 rounded-md text-sm font-medium">Add Event</button>
            </div>
          </div>
        </div>
      </div>
    </AppShell>
  );
}
~~~

Implementation Guidelines

1. Analyze the component structure, styling, animation implementations
2. Review the component's arguments and state
3. Think through what is the best place to adopt this component/style into the design we are doing
4. Then adopt the component/design to our current system

Help me integrate this into my design
All Prompts