@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 +99 -155
- package/dist/index.cjs +1 -1
- package/dist/index.js +920 -984
- package/dist/styles.css +423 -0
- package/package.json +3 -4
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
|
|
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
|
|
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
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
###
|
|
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
|
-
- `
|
|
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 (
|
|
107
|
-
- With `options.carId`: returns all drivers for that car (
|
|
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
|
-
|
|
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
|
|
73
|
+
For building leaderboard pages with the SDK's `pvx-*` styles:
|
|
153
74
|
|
|
154
75
|
```jsx
|
|
155
|
-
import {
|
|
76
|
+
import { TracksTable, CarsTable, DriversTable, LapHistoryTable } from '@pitvox/partner-react'
|
|
156
77
|
import '@pitvox/partner-react/styles.css'
|
|
157
78
|
```
|
|
158
79
|
|
|
159
|
-
- **`<
|
|
160
|
-
- **`<
|
|
161
|
-
- **`<
|
|
162
|
-
- **`<
|
|
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
|
|
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
|
-
|
|
87
|
+
## Competitions
|
|
168
88
|
|
|
169
|
-
|
|
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
|
|
142
|
+
### Drop-in composite
|
|
198
143
|
|
|
199
|
-
|
|
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
|
-
|
|
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)`** —
|
|
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)`** —
|
|
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
|
|
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
|
-
**`
|
|
221
|
+
**`useRegistrationMode()`** — Returns `{ isPowerMode, isBasicMode }`.
|
|
267
222
|
|
|
268
|
-
**`
|
|
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;
|
|
292
|
-
--pvx-bg-card: #1a1a2e;
|
|
293
|
-
--pvx-sector-best: #22d3ee;
|
|
294
|
-
--pvx-rank-gold: #fbbf24;
|
|
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
|
|
253
|
+
All classes are prefixed with `pvx-` to avoid collisions. See `styles.css` for the full list of variables.
|
|
299
254
|
|
|
300
|
-
##
|
|
255
|
+
## Partner templates
|
|
301
256
|
|
|
302
|
-
|
|
257
|
+
For a complete working site using this SDK, see:
|
|
303
258
|
|
|
304
|
-
|
|
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
|
-
|
|
262
|
+
The templates demonstrate how to compose SDK hooks and components into full pages with routing.
|
|
319
263
|
|
|
320
|
-
|
|
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', '
|
|
280
|
+
dedupe: ['react', 'react-dom', '@tanstack/react-query'],
|
|
337
281
|
},
|
|
338
282
|
})
|
|
339
283
|
```
|