diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index c7aabec..4324e43 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,253 +1,144 @@ -import { ActivityFeed } from "@/components/feed/activity-feed" -import { XPLeaderboard } from "@/components/gamification/xp-leaderboard" -import { Button } from "@/components/ui/button" -import { Card, CardContent } from "@/components/ui/card" import Link from "next/link" -import { Trophy, Music, MapPin, Calendar, ChevronRight, Star, Youtube, Route } from "lucide-react" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { getApiUrl } from "@/lib/api-config" -interface Show { +interface Vertical { id: number - slug?: string - date: string - venue?: { - id: number - name: string - slug?: string - city?: string - state?: string - } - tour?: { - id: number - name: string - slug?: string - } + name: string + slug: string + description: string | null } -interface Song { +interface Scene { id: number - title: string - slug?: string - performance_count?: number - avg_rating?: number + name: string + slug: string + description: string | null } -async function getRecentShows(): Promise { +async function getVerticals(): Promise { try { - const res = await fetch(`${getApiUrl()}/shows/recent?limit=8`, { - cache: 'no-store', - next: { revalidate: 60 } - }) + const res = await fetch(`${getApiUrl()}/verticals`, { next: { revalidate: 60 } }) if (!res.ok) return [] return res.json() - } catch (e) { - console.error('Failed to fetch recent shows:', e) + } catch { return [] } } -async function getTopSongs(): Promise { +async function getScenes(): Promise { try { - const res = await fetch(`${getApiUrl()}/stats/top-songs?limit=5`, { - cache: 'no-store', - next: { revalidate: 300 } - }) + const res = await fetch(`${getApiUrl()}/verticals/scenes`, { next: { revalidate: 60 } }) if (!res.ok) return [] return res.json() - } catch (e) { - console.error('Failed to fetch top songs:', e) + } catch { return [] } } - - -export default async function Home() { - const [recentShows, topSongs] = await Promise.all([ - getRecentShows(), - getTopSongs() - ]) +export default async function HomePage() { + const [verticals, scenes] = await Promise.all([getVerticals(), getScenes()]) return ( -
+
{/* Hero Section */} -
-

- Elmeg +
+

+ Fediversion

-

- A comprehensive community-driven archive for Goose history. -
- Discover shows, share ratings, and explore the music together. +

+ The unified platform for the entire jam scene. + One account, all your favorite bands.

-
- - - - - - +
+ +
- {/* Recent Shows */} -
-
-

- - Recent Shows -

- - View all shows - -
- {recentShows.length > 0 ? ( -
- {recentShows.map((show) => ( - - - -
- {new Date(show.date).toLocaleDateString('en-US', { - weekday: 'short', - year: 'numeric', - month: 'short', - day: 'numeric' - })} -
- {show.venue && ( -
- {show.venue.name} -
- )} - {show.venue?.city && ( -
- {show.venue.city}{show.venue.state ? `, ${show.venue.state}` : ''} -
- )} - {show.tour && ( -
- {show.tour.name} -
- )} -
-
+ {/* Scenes Section */} + {scenes.length > 0 && ( +
+

Browse by Scene

+
+ {scenes.map((scene) => ( + +
{scene.name}
+ {scene.description && ( +
+ {scene.description} +
+ )} ))}
- ) : ( - -

No shows yet. Check back soon!

-
- )} -
+
+ )} -
- {/* Top Songs */} -
-
-

- - Top Songs -

- - All songs - -
- - - {topSongs.length > 0 ? ( -
    - {topSongs.map((song, idx) => ( -
  • - - - {idx + 1} - -
    -
    {song.title}
    - {song.performance_count && ( -
    - {song.performance_count} performances -
    - )} -
    - -
  • - ))} -
- ) : ( -
- No songs yet + {/* Bands Grid */} +
+

Featured Bands

+
+ {verticals.map((vertical) => ( + + + + + {vertical.name} + + + {vertical.description && ( + {vertical.description} + )} + + +
+ + +
- )} -
-
-
- - {/* Activity Feed */} -
-
-

Recent Activity

- - View all - -
- -
- - {/* XP Leaderboard */} -
- -
-
- - {/* Quick Links */} -
- - -

Shows

-

Browse the complete archive

- - - -

Venues

-

Find your favorite spots

- - - -

Songs

-

Explore the catalog

- - - -

Top Performances

-

Highest rated jams

- - - -

Leaderboards

-

Top rated everything

- - - -

Tours

-

Browse by tour

- - - -

Videos

-

Watch full shows and songs

- + + + ))} +

-
+ + {/* Stats Section */} +
+

Join the Community

+

+ Track shows, rate performances, discover connections across bands. +

+
+
+
{verticals.length}
+
Bands
+
+
+
{scenes.length}
+
Scenes
+
+
+
1
+
Account
+
+
+
+
) } diff --git a/frontend/app/songs/[slug]/page.tsx b/frontend/app/songs/[slug]/page.tsx index 76f4f45..8d970b7 100644 --- a/frontend/app/songs/[slug]/page.tsx +++ b/frontend/app/songs/[slug]/page.tsx @@ -25,6 +25,19 @@ async function getSong(id: string) { } } +// Fetch cross-band versions of this song via SongCanon +async function getRelatedVersions(songId: number) { + try { + const res = await fetch(`${getApiUrl()}/canon/song/${songId}/related`, { + next: { revalidate: 60 } + }) + if (!res.ok) return [] + return res.json() + } catch { + return [] + } +} + // Get top rated performances for "Heady Version" leaderboard function getHeadyVersions(performances: any[]) { if (!performances || performances.length === 0) return [] @@ -45,6 +58,9 @@ export default async function SongDetailPage({ params }: { params: Promise<{ slu const headyVersions = getHeadyVersions(song.performances || []) const topPerformance = headyVersions[0] + // Fetch cross-band versions + const relatedVersions = await getRelatedVersions(song.id) + return (