15 — Components

Club Directory

Filterable club directory with conference grouping, crest logos, record stats, and points. Data-source agnostic — drop in a static array or wire to Airtable with no component changes.

1 — ClubDirectory — Live Component

Fully interactive — filter by conference. Auto-sorted by points (W×3 + D×1) descending. 10 clubs across NC and SC.

10CLUBS· sorted by points
Charlotte FC crest
Charlotte FC
Charlotte, NC
EASTMarcus Webb
12
W
3
D
2
L
39
PTS
Greensboro FC crest
Greensboro FC
Greensboro, NC
WESTTom Bradley
11
W
2
D
4
L
35
PTS
Durham United crest
Durham United
Durham, NC
EASTSarah Chen
10
W
4
D
3
L
34
PTS
Raleigh Athletic crest
Raleigh Athletic
Raleigh, NC
EASTJames Okafor
9
W
5
D
3
L
32
PTS
Winston-Salem SC crest
Winston-Salem SC
Winston-Salem, NC
WESTDiana Moore
9
W
3
D
5
L
30
PTS
Triangle FC crest
Triangle FC
Cary, NC
EASTLisa Park
8
W
3
D
6
L
27
PTS
Asheville FC crest
Asheville FC
Asheville, NC
WESTPatrick Hughes
7
W
6
D
4
L
27
PTS
Coastal SC crest
Coastal SC
Wilmington, NC
EASTCarlos Rivera
6
W
4
D
7
L
22
PTS
Columbia United crest
Columbia United
Columbia, SC
WESTAmara Johnson
5
W
4
D
8
L
19
PTS
Charleston FC crest
Charleston FC
Charleston, SC
WESTRobert Kim
4
W
5
D
8
L
17
PTS
tsx
import { ClubDirectory } from "@/components/cpsl/clubs"
import { CLUBS } from "@/lib/clubs"

// Static data — swap for an Airtable fetch with no component changes:
// const clubs = await fetchClubsFromAirtable()

<ClubDirectory clubs={CLUBS} onClubClick={(club) => router.push(`/clubs/${club.id}`)} />

2 — ClubCard — All Variants

Conference determines the left accent colour and badge — blue for East, crimson for West. Pass onClick to enable hover state and pointer cursor.

East Conference
Charlotte FC crest
Charlotte FC
Charlotte, NC
EASTMarcus Webb
12
W
3
D
2
L
39
PTS
Durham United crest
Durham United
Durham, NC
EASTSarah Chen
10
W
4
D
3
L
34
PTS
West Conference
Greensboro FC crest
Greensboro FC
Greensboro, NC
WESTTom Bradley
11
W
2
D
4
L
35
PTS
Winston-Salem SC crest
Winston-Salem SC
Winston-Salem, NC
WESTDiana Moore
9
W
3
D
5
L
30
PTS
tsx
import { ClubCard } from "@/components/cpsl/clubs"

<ClubCard
  club={{
    id: "charlotte-fc",
    name: "Charlotte FC",
    location: "Charlotte, NC",
    logoSlug: "charlotte-fc",   // → /public/logos/charlotte-fc.svg
    conference: "East",
    record: { wins: 12, draws: 3, losses: 2 },
    director: "Marcus Webb",
  }}
  onClick={() => router.push("/clubs/charlotte-fc")}
/>

3 — Component API

Club (data shape)
interface Club {
  id: string
  name: string
  location: string
  logoSlug: string   // /public/logos/{slug}.svg
  conference: "East" | "West"
  record: {
    wins: number
    draws: number
    losses: number
  }
  director: string
}
ClubDirectory props
<ClubDirectory
  clubs={Club[]}            // any Club array
  onClubClick={(club) => {}} // optional
/>

// Built-in:
//   Filter: ALL / EAST / WEST
//   Sort:   pts desc (W×3 + D×1)
ClubCard props
<ClubCard
  club={Club}
  onClick={() => {}}  // enables hover + pointer
/>
Logo convention
// Logos live in /public/logos/
// Named after club.logoSlug:
//   "charlotte-fc"  → /logos/charlotte-fc.svg
//   "durham-united" → /logos/durham-united.svg
//
// Airtable: store logoSlug as a text field.
// Keep SVGs in /public/logos/ alongside code.
// Never store logos as Airtable attachments
// (URLs expire every few hours).