@pitvox/partner-react 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @pitvox/partner-react
2
2
 
3
- React SDK for PitVox partner websites. Provides hooks, components, and complete leaderboard + competition experiences for sim racing communities affiliated with [PitVox](https://pitvox.com).
3
+ React SDK for PitVox partner websites. Provides hooks, styled components, and a driver dashboard for sim racing communities affiliated with [PitVox](https://pitvox.com).
4
+
5
+ **Hooks-first**: Use the data hooks with any UI framework (Tailwind, DaisyUI, Shadcn, Preline, etc.). The styled `pvx-*` components are optional building blocks for quick starts.
4
6
 
5
7
  ## Installation
6
8
 
@@ -10,10 +12,8 @@ npm install @pitvox/partner-react
10
12
 
11
13
  ### Peer dependencies
12
14
 
13
- The SDK requires these in your project:
14
-
15
15
  ```bash
16
- npm install react react-dom @tanstack/react-query react-router-dom
16
+ npm install react react-dom @tanstack/react-query
17
17
  ```
18
18
 
19
19
  ## Quick start
@@ -25,17 +25,12 @@ import { PitVoxPartnerProvider } from '@pitvox/partner-react'
25
25
 
26
26
  function App() {
27
27
  return (
28
- <BrowserRouter>
29
- <PitVoxPartnerProvider
30
- partnerSlug="your-slug"
31
- cdnUrl="https://cdn.pitvox.com"
32
- apiUrl="https://api.pitvox.com"
33
- apiKey="your-api-key"
34
- getSteamId={() => currentUser?.steamId ?? null}
35
- >
36
- {/* your app */}
37
- </PitVoxPartnerProvider>
38
- </BrowserRouter>
28
+ <PitVoxPartnerProvider
29
+ partnerSlug="your-slug"
30
+ getSteamId={() => currentUser?.steamId ?? null}
31
+ >
32
+ {/* your app */}
33
+ </PitVoxPartnerProvider>
39
34
  )
40
35
  }
41
36
  ```
@@ -44,48 +39,7 @@ function App() {
44
39
 
45
40
  ## Leaderboards
46
41
 
47
- ### Drop-in (recommended)
48
-
49
- One component gives you a complete 4-layer leaderboard with game tabs, version selector, tag filtering, sortable columns, sector highlighting, and drill-down navigation:
50
-
51
- ```jsx
52
- import { LeaderboardExplorer } from '@pitvox/partner-react'
53
- import '@pitvox/partner-react/styles.css'
54
-
55
- function LeaderboardsPage() {
56
- return <LeaderboardExplorer />
57
- }
58
- ```
59
-
60
- The `LeaderboardExplorer` manages all state via URL search parameters (`?game=evo&track=...&car=...&driver=...`), so deep-linking and browser back/forward work out of the box.
61
-
62
- **Props:**
63
-
64
- | Prop | Type | Default | Description |
65
- |------|------|---------|-------------|
66
- | `className` | `string` | — | Additional class on root container |
67
- | `defaultGame` | `'evo' \| 'acc'` | `'evo'` | Default game tab |
68
- | `title` | `string` | `'Leaderboards'` | Page heading |
69
-
70
- ### Layer components
71
-
72
- For more control, use the individual layer components directly. Each handles its own sorting, filtering, and highlighting logic:
73
-
74
- ```jsx
75
- import { TracksTable, CarsTable, DriversTable, LapHistoryTable } from '@pitvox/partner-react'
76
- import '@pitvox/partner-react/styles.css'
77
- ```
78
-
79
- - **`<TracksTable>`** — Layer 1: all tracks with record holders, tag filtering
80
- - **`<CarsTable>`** — Layer 2: cars for a selected track, tag filtering
81
- - **`<DriversTable>`** — Layer 3: drivers for a car with sectors (S1/S2/S3), tyre, fuel
82
- - **`<LapHistoryTable>`** — Layer 4: driver's lap history with validity and personal best highlighting
83
-
84
- You're responsible for wiring up the navigation between layers and managing URL state.
85
-
86
- ### Hooks only
87
-
88
- For fully custom UIs, use the data hooks directly:
42
+ ### Hooks
89
43
 
90
44
  ```jsx
91
45
  import {
@@ -99,12 +53,11 @@ import {
99
53
 
100
54
  **`useLeaderboardIndex(options?)`** — Fetch all tracks with record holders.
101
55
  - `options.game` — Filter by game (`'evo'` | `'acc'`)
102
- - `options.gameVersion` Filter by EVO version
103
- - Returns `{ data: Track[], isLoading, partner, generatedAt, totalLaps, totalUsers, versions }`
56
+ - Returns `{ data: Track[], isLoading, generatedAt, totalLaps, totalUsers, versions }`
104
57
 
105
58
  **`useTrackLeaderboard(trackId, layout?, options?)`** — Fetch track entries.
106
- - Without `options.carId`: returns best lap per car (Layer 2)
107
- - With `options.carId`: returns all drivers for that car (Layer 3)
59
+ - Without `options.carId`: returns best lap per car (car-level)
60
+ - With `options.carId`: returns all drivers for that car (driver-level)
108
61
  - Returns `{ data: Entry[], isLoading, error }`
109
62
 
110
63
  **`useDriverLaps(userId, trackId, layout, carId, options?)`** — Fetch a driver's lap history.
@@ -115,58 +68,25 @@ import {
115
68
 
116
69
  **`useCarMetadata()`** — Returns `{ tags: string[], cars: Record<string, { tags }> }` for tag filtering.
117
70
 
118
- ## Competitions
119
-
120
- ### Drop-in (recommended)
121
-
122
- One component gives you a complete competition experience with card grid, championship standings, round-by-round results with session tabs, and entry lists:
123
-
124
- ```jsx
125
- import { CompetitionExplorer } from '@pitvox/partner-react'
126
- import '@pitvox/partner-react/styles.css'
127
-
128
- function CompetitionsPage() {
129
- return <CompetitionExplorer />
130
- }
131
- ```
132
-
133
- The `CompetitionExplorer` manages all state via URL search parameters (`?competition=abc123&tab=standings&round=2`), so deep-linking and browser back/forward work out of the box.
134
-
135
- **Props:**
136
-
137
- | Prop | Type | Default | Description |
138
- |------|------|---------|-------------|
139
- | `className` | `string` | — | Additional class on root container |
140
- | `title` | `string` | `'Competitions'` | Page heading |
141
-
142
- **Features:**
143
- - Competition card grid with poster images, type badges, format/car pills, schedule summary, registration status
144
- - Championship standings with position rank badges, nation flags, wins/podiums columns, per-round cells with dropped round styling
145
- - Round results with session tabs (Practice/Qualifying/Race), best lap with sector hover tooltip, podium highlighting
146
- - Entry list with driver avatars
147
- - Horizontal schedule strip with next-round highlighting
148
- - Breadcrumb navigation between list and detail views
149
-
150
- ### Layer components
71
+ ### Styled components
151
72
 
152
- For more control, use the individual layer components directly:
73
+ For building leaderboard pages with the SDK's `pvx-*` styles:
153
74
 
154
75
  ```jsx
155
- import { CompetitionCards, StandingsTable, RoundResults, EntryList } from '@pitvox/partner-react'
76
+ import { TracksTable, CarsTable, DriversTable, LapHistoryTable } from '@pitvox/partner-react'
156
77
  import '@pitvox/partner-react/styles.css'
157
78
  ```
158
79
 
159
- - **`<CompetitionCards>`** — Competition card grid with posters, badges, schedule, and registration status
160
- - **`<StandingsTable>`** — Championship standings with per-round breakdowns, podium highlights, dropped rounds
161
- - **`<RoundResults>`** — Round results with session tab bar, driver/car detail, best sector highlighting
162
- - **`<EntryList>`** — Registered drivers grid with avatars
163
- - **`<RegisterButton>`** — Register/withdraw toggle (render prop or default button)
80
+ - **`<TracksTable>`** — All tracks with record holders, tag filtering, sorting
81
+ - **`<CarsTable>`** — Cars for a selected track with tag filtering
82
+ - **`<DriversTable>`** — Drivers for a car with sectors (S1/S2/S3), tyre, fuel
83
+ - **`<LapHistoryTable>`** — Driver's lap history with validity and personal best highlighting
164
84
 
165
- You're responsible for wiring up navigation between views and managing state.
85
+ You compose these into your own page layout and wire up navigation between layers. See the [partner templates](https://github.com/meekysoft/pitvox-partner-template) for a complete example using React Router.
166
86
 
167
- ### Hooks only
87
+ ## Competitions
168
88
 
169
- For fully custom UIs, use the data hooks directly:
89
+ ### Hooks
170
90
 
171
91
  ```jsx
172
92
  import {
@@ -190,13 +110,38 @@ import {
190
110
  **`useCompetitionAllRounds(competitionId, roundNumbers)`** — Fetch multiple round results in parallel.
191
111
 
192
112
  **`useCompetitionEntryList(competitionId)`** — Registered drivers.
113
+
114
+ ### Styled components
115
+
116
+ ```jsx
117
+ import {
118
+ CompetitionCards, StandingsTable, RoundResults, RoundSessionResults,
119
+ EntryList, RegisterButton, RegistrationPanel,
120
+ } from '@pitvox/partner-react'
121
+ import '@pitvox/partner-react/styles.css'
122
+ ```
123
+
124
+ - **`<CompetitionCards>`** — Card grid with posters, type badges, schedule, registration status
125
+ - **`<StandingsTable>`** — Championship standings with per-round breakdowns, podium highlights
126
+ - **`<RoundResults>`** — Standalone round results (fetches data, renders header + sessions)
127
+ - **`<RoundSessionResults>`** — Session tabs + results table (data-prop driven, no fetch)
128
+ - **`<EntryList>`** — Registered drivers grid with avatars
129
+ - **`<RegisterButton>`** — Register/withdraw toggle (render prop or default button)
130
+ - **`<RegistrationPanel>`** — Registration form + entry list with unregister
131
+
132
+ ### Shared utilities
133
+
134
+ Useful when composing competition pages:
135
+
136
+ ```jsx
137
+ import { TypeBadge, InfoPill, PODIUM_MEDALS, CompLoadingState, CompEmptyState } from '@pitvox/partner-react'
193
138
  ```
194
139
 
195
140
  ## Driver Dashboard
196
141
 
197
- ### Drop-in (recommended)
142
+ ### Drop-in composite
198
143
 
199
- One component gives you a complete driver dashboard with profile, stats cards, and current records:
144
+ The `DriverDashboard` is a self-contained component with no routing dependency:
200
145
 
201
146
  ```jsx
202
147
  import { DriverDashboard } from '@pitvox/partner-react'
@@ -213,8 +158,6 @@ function DashboardPage() {
213
158
  }
214
159
  ```
215
160
 
216
- **Props:**
217
-
218
161
  | Prop | Type | Default | Description |
219
162
  |------|------|---------|-------------|
220
163
  | `steamId` | `string` | — | Driver's Steam ID (required) |
@@ -222,50 +165,62 @@ function DashboardPage() {
222
165
  | `memberSince` | `string` | — | ISO date for "Racing since" display |
223
166
  | `className` | `string` | — | Additional class on root container |
224
167
 
225
- **Displays:**
226
- - Driver profile card (avatar, name, racing since)
227
- - Stats cards: total laps (with track breakdown tooltip), cars used (with car breakdown tooltip), driver rating
228
- - Current records list sorted by most recent, with track, car, lap time, and game badge
229
-
230
168
  ### Layer components
231
169
 
232
- For more control, use the individual components directly:
233
-
234
170
  ```jsx
235
171
  import { DriverProfile, StatsCards, RecordsTable } from '@pitvox/partner-react'
236
- import '@pitvox/partner-react/styles.css'
237
172
  ```
238
173
 
239
- - **`<DriverProfile>`** — Avatar, driver name, and "Racing since" date
240
- - **`<StatsCards>`** — Stats cards with icons and hover breakdown tooltips (accepts `stats` from `useDriverStats` and `rating` from `useDriverRating`)
241
- - **`<RecordsTable>`** — Compact records list sorted by most recent, with expand/collapse
242
-
243
- ### Hooks only
244
-
245
- For fully custom UIs, use the data hooks directly:
174
+ ### Hooks
246
175
 
247
176
  ```jsx
248
177
  import { useDriverStats, useDriverRating } from '@pitvox/partner-react'
249
178
  ```
250
179
 
251
- **`useDriverStats(steamId)`** — Fetch driver stats, records, and ranking from CDN.
252
- - Shares the same query cache as `useDriverLaps` (same CDN file)
253
- - Returns `{ data: { driverName, lapCount, trackBreakdown, carBreakdown, recordsHeld, currentRecords, bestRanking, bestRankingTrackId, bestRankingLayout, bestRankingCarId }, isLoading, error }`
180
+ **`useDriverStats(steamId)`** — Driver stats, records, and ranking from CDN.
254
181
 
255
- **`useDriverRating(steamId)`** — Fetch driver's rating from the partner ratings file.
256
- - Returns `{ data: { rating, rank, totalDrivers, comboCount, distinctCars, combos }, isLoading, error }`
182
+ **`useDriverRating(steamId)`** — Driver's rating from the partner ratings file.
257
183
 
258
184
  ## Registration
259
185
 
186
+ The SDK supports two registration modes, determined by whether you provide callbacks to the provider.
187
+
188
+ ### Basic mode (default)
189
+
190
+ No configuration needed. Registration components render links to pitvox.com where users register with Steam.
191
+
192
+ ### Power mode
193
+
194
+ For partners with a backend (e.g. Amplify Lambda proxying to pitvox-api), provide callbacks:
195
+
196
+ ```jsx
197
+ <PitVoxPartnerProvider
198
+ partnerSlug="your-slug"
199
+ getSteamId={() => user?.steamId ?? null}
200
+ onRegister={async (competitionId, driverData) => {
201
+ await fetch('/api/register', { method: 'POST', body: JSON.stringify({ competitionId, ...driverData }) })
202
+ }}
203
+ onWithdraw={async (competitionId, steamId) => {
204
+ await fetch('/api/withdraw', { method: 'POST', body: JSON.stringify({ competitionId, steamId }) })
205
+ }}
206
+ >
207
+ ```
208
+
209
+ ### Registration hooks
210
+
260
211
  ```jsx
261
- import { useRegistrationStatus, useRegister, useWithdraw } from '@pitvox/partner-react'
212
+ import { useRegistrationStatus, useRegister, useWithdraw, useRegistrationMode, useRegistrationUrl } from '@pitvox/partner-react'
262
213
  ```
263
214
 
264
- **`useRegistrationStatus(competitionId)`** — Check if current user (via `getSteamId`) is registered. Returns `{ data: { isRegistered, entryList } }`.
215
+ **`useRegistrationStatus(competitionId)`** — Check if current user is registered.
216
+
217
+ **`useRegister(competitionId)`** — Mutation delegating to `onRegister` callback.
218
+
219
+ **`useWithdraw(competitionId)`** — Mutation delegating to `onWithdraw` callback.
265
220
 
266
- **`useRegister(competitionId)`** — Returns a mutation. Call `mutate(driverData)` with `{ steamId, displayName, ... }`.
221
+ **`useRegistrationMode()`** — Returns `{ isPowerMode, isBasicMode }`.
267
222
 
268
- **`useWithdraw(competitionId)`** — Returns a mutation. Call `mutate()` to unregister.
223
+ **`useRegistrationUrl(competitionId)`** — Returns pitvox.com registration URL for basic mode.
269
224
 
270
225
  ## Formatting utilities
271
226
 
@@ -288,36 +243,25 @@ The default stylesheet uses CSS custom properties. Override them to match your b
288
243
 
289
244
  ```css
290
245
  :root {
291
- --pvx-accent: #e11d48; /* your brand colour */
292
- --pvx-bg-card: #1a1a2e; /* card background */
293
- --pvx-sector-best: #22d3ee; /* best sector highlight */
294
- --pvx-rank-gold: #fbbf24; /* podium colours */
246
+ --pvx-accent: #e11d48;
247
+ --pvx-bg-card: #1a1a2e;
248
+ --pvx-sector-best: #22d3ee;
249
+ --pvx-rank-gold: #fbbf24;
295
250
  }
296
251
  ```
297
252
 
298
- All classes are prefixed with `pvx-` to avoid collisions with your existing styles. See `styles.css` for the full list of variables and classes.
253
+ All classes are prefixed with `pvx-` to avoid collisions. See `styles.css` for the full list of variables.
299
254
 
300
- ## CDN data paths
255
+ ## Partner templates
301
256
 
302
- The SDK reads pre-computed JSON from the PitVox CDN. No API key is needed for read-only leaderboard and competition data.
257
+ For a complete working site using this SDK, see:
303
258
 
304
- | Data | Path |
305
- |------|------|
306
- | Leaderboard index | `leaderboards/partners/{slug}/index.json` |
307
- | Track entries | `leaderboards/partners/{slug}/tracks/{trackId}/{layout}.json` |
308
- | Driver laps | `laps/partners/{slug}/{userId}.json` |
309
- | User display names | `users/index.json` |
310
- | Car metadata | `cars/index.json` |
311
- | Competition index | `competitions/index.json` |
312
- | Competition config | `competitions/{slug}/{id}/config.json` |
313
- | Standings | `competitions/{slug}/{id}/standings.json` |
314
- | Round results | `competitions/{slug}/{id}/rounds/{n}.json` |
315
- | Entry list | `competitions/{slug}/{id}/entrylist.json` |
316
- | Driver ratings | `leaderboards/partners/{slug}/ratings.json` |
259
+ - **[pitvox-partner-template](https://github.com/meekysoft/pitvox-partner-template)** Basic template (no auth)
260
+ - **[pitvox-partner-template-amplify](https://github.com/meekysoft/pitvox-partner-template-amplify)** — AWS Amplify template with Steam auth and in-app registration
317
261
 
318
- ## Local development
262
+ The templates demonstrate how to compose SDK hooks and components into full pages with routing.
319
263
 
320
- To develop against a local version of the SDK:
264
+ ## Local development
321
265
 
322
266
  ```bash
323
267
  # In the SDK repo
@@ -333,7 +277,7 @@ Add `resolve.dedupe` to your Vite config to avoid duplicate React instances:
333
277
  // vite.config.js
334
278
  export default defineConfig({
335
279
  resolve: {
336
- dedupe: ['react', 'react-dom', 'react-router-dom', '@tanstack/react-query'],
280
+ dedupe: ['react', 'react-dom', '@tanstack/react-query'],
337
281
  },
338
282
  })
339
283
  ```