@pitvox/partner-react 0.1.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/LICENSE +21 -0
- package/README.md +279 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +1514 -0
- package/dist/styles.css +1139 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MeekySoft
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# @pitvox/partner-react
|
|
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).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @pitvox/partner-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Peer dependencies
|
|
12
|
+
|
|
13
|
+
The SDK requires these in your project:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install react react-dom @tanstack/react-query react-router-dom
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Wrap your app with the provider:
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import { PitVoxPartnerProvider } from '@pitvox/partner-react'
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
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>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> The provider auto-creates a `QueryClient` if your app doesn't already have one. If you use React Query elsewhere, wrap with your own `QueryClientProvider` first and the SDK will share it.
|
|
44
|
+
|
|
45
|
+
## Leaderboards
|
|
46
|
+
|
|
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:
|
|
89
|
+
|
|
90
|
+
```jsx
|
|
91
|
+
import {
|
|
92
|
+
useLeaderboardIndex,
|
|
93
|
+
useTrackLeaderboard,
|
|
94
|
+
useDriverLaps,
|
|
95
|
+
useUserLookup,
|
|
96
|
+
useCarMetadata,
|
|
97
|
+
} from '@pitvox/partner-react'
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**`useLeaderboardIndex(options?)`** — Fetch all tracks with record holders.
|
|
101
|
+
- `options.game` — Filter by game (`'evo'` | `'acc'`)
|
|
102
|
+
- `options.gameVersion` — Filter by EVO version
|
|
103
|
+
- Returns `{ data: Track[], isLoading, partner, generatedAt, totalLaps, totalUsers, versions }`
|
|
104
|
+
|
|
105
|
+
**`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)
|
|
108
|
+
- Returns `{ data: Entry[], isLoading, error }`
|
|
109
|
+
|
|
110
|
+
**`useDriverLaps(userId, trackId, layout, carId, options?)`** — Fetch a driver's lap history.
|
|
111
|
+
- `options.showInvalid` — Include invalid laps (default `false`)
|
|
112
|
+
- Returns `{ data: Lap[], isLoading, driverName }`
|
|
113
|
+
|
|
114
|
+
**`useUserLookup()`** — Returns a lookup function: `(userId, fallback?) => { displayName, avatarUrl, affiliations }`
|
|
115
|
+
|
|
116
|
+
**`useCarMetadata()`** — Returns `{ tags: string[], cars: Record<string, { tags }> }` for tag filtering.
|
|
117
|
+
|
|
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
|
|
151
|
+
|
|
152
|
+
For more control, use the individual layer components directly:
|
|
153
|
+
|
|
154
|
+
```jsx
|
|
155
|
+
import { CompetitionCards, StandingsTable, RoundResults, EntryList } from '@pitvox/partner-react'
|
|
156
|
+
import '@pitvox/partner-react/styles.css'
|
|
157
|
+
```
|
|
158
|
+
|
|
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)
|
|
164
|
+
|
|
165
|
+
You're responsible for wiring up navigation between views and managing state.
|
|
166
|
+
|
|
167
|
+
### Hooks only
|
|
168
|
+
|
|
169
|
+
For fully custom UIs, use the data hooks directly:
|
|
170
|
+
|
|
171
|
+
```jsx
|
|
172
|
+
import {
|
|
173
|
+
useCompetitions,
|
|
174
|
+
useCompetitionConfig,
|
|
175
|
+
useCompetitionStandings,
|
|
176
|
+
useCompetitionRound,
|
|
177
|
+
useCompetitionAllRounds,
|
|
178
|
+
useCompetitionEntryList,
|
|
179
|
+
} from '@pitvox/partner-react'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**`useCompetitions()`** — All competitions for this partner.
|
|
183
|
+
|
|
184
|
+
**`useCompetitionConfig(competitionId)`** — Single competition config (name, rounds, countingRounds, etc.).
|
|
185
|
+
|
|
186
|
+
**`useCompetitionStandings(competitionId)`** — Championship standings with per-round breakdowns.
|
|
187
|
+
|
|
188
|
+
**`useCompetitionRound(competitionId, roundNumber)`** — Single round results with session data.
|
|
189
|
+
|
|
190
|
+
**`useCompetitionAllRounds(competitionId, roundNumbers)`** — Fetch multiple round results in parallel.
|
|
191
|
+
|
|
192
|
+
**`useCompetitionEntryList(competitionId)`** — Registered drivers.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Registration
|
|
196
|
+
|
|
197
|
+
```jsx
|
|
198
|
+
import { useRegistrationStatus, useRegister, useWithdraw } from '@pitvox/partner-react'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**`useRegistrationStatus(competitionId)`** — Check if current user (via `getSteamId`) is registered. Returns `{ data: { isRegistered, entryList } }`.
|
|
202
|
+
|
|
203
|
+
**`useRegister(competitionId)`** — Returns a mutation. Call `mutate(driverData)` with `{ steamId, displayName, ... }`.
|
|
204
|
+
|
|
205
|
+
**`useWithdraw(competitionId)`** — Returns a mutation. Call `mutate()` to unregister.
|
|
206
|
+
|
|
207
|
+
## Formatting utilities
|
|
208
|
+
|
|
209
|
+
```jsx
|
|
210
|
+
import {
|
|
211
|
+
formatLapTime, // 92365 → "1:32.365"
|
|
212
|
+
formatSectorTime, // 34567 → "34.567"
|
|
213
|
+
formatCarName, // "ks_ferrari_296_gt3" → "Ferrari 296 Gt3"
|
|
214
|
+
formatTrackName, // "donington_park", "national" → "Donington Park National"
|
|
215
|
+
formatDate, // ISO string → "27 Feb 2024"
|
|
216
|
+
formatRelativeTime, // ISO string → "2h ago"
|
|
217
|
+
formatDelta, // 542 → "+0.542"
|
|
218
|
+
formatTyreCompound, // "SR" → "Soft Race"
|
|
219
|
+
} from '@pitvox/partner-react'
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Theming
|
|
223
|
+
|
|
224
|
+
The default stylesheet uses CSS custom properties. Override them to match your brand:
|
|
225
|
+
|
|
226
|
+
```css
|
|
227
|
+
:root {
|
|
228
|
+
--pvx-accent: #e11d48; /* your brand colour */
|
|
229
|
+
--pvx-bg-card: #1a1a2e; /* card background */
|
|
230
|
+
--pvx-sector-best: #22d3ee; /* best sector highlight */
|
|
231
|
+
--pvx-rank-gold: #fbbf24; /* podium colours */
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
All classes are prefixed with `pvx-` to avoid collisions with your existing styles. See `styles.css` for the full list of variables and classes.
|
|
236
|
+
|
|
237
|
+
## CDN data paths
|
|
238
|
+
|
|
239
|
+
The SDK reads pre-computed JSON from the PitVox CDN. No API key is needed for read-only leaderboard and competition data.
|
|
240
|
+
|
|
241
|
+
| Data | Path |
|
|
242
|
+
|------|------|
|
|
243
|
+
| Leaderboard index | `leaderboards/partners/{slug}/index.json` |
|
|
244
|
+
| Track entries | `leaderboards/partners/{slug}/tracks/{trackId}/{layout}.json` |
|
|
245
|
+
| Driver laps | `laps/partners/{slug}/{userId}.json` |
|
|
246
|
+
| User display names | `users/index.json` |
|
|
247
|
+
| Car metadata | `cars/index.json` |
|
|
248
|
+
| Competition index | `competitions/index.json` |
|
|
249
|
+
| Competition config | `competitions/{slug}/{id}/config.json` |
|
|
250
|
+
| Standings | `competitions/{slug}/{id}/standings.json` |
|
|
251
|
+
| Round results | `competitions/{slug}/{id}/rounds/{n}.json` |
|
|
252
|
+
| Entry list | `competitions/{slug}/{id}/entrylist.json` |
|
|
253
|
+
|
|
254
|
+
## Local development
|
|
255
|
+
|
|
256
|
+
To develop against a local version of the SDK:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
# In the SDK repo
|
|
260
|
+
npm link
|
|
261
|
+
|
|
262
|
+
# In your app
|
|
263
|
+
npm link @pitvox/partner-react
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Add `resolve.dedupe` to your Vite config to avoid duplicate React instances:
|
|
267
|
+
|
|
268
|
+
```js
|
|
269
|
+
// vite.config.js
|
|
270
|
+
export default defineConfig({
|
|
271
|
+
resolve: {
|
|
272
|
+
dedupe: ['react', 'react-dom', 'react-router-dom', '@tanstack/react-query'],
|
|
273
|
+
},
|
|
274
|
+
})
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## License
|
|
278
|
+
|
|
279
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),b=require("react"),w=require("@tanstack/react-query"),te=require("react-router-dom"),O=b.createContext(null),qe="https://cdn.pitvox.com",_e="https://api.pitvox.com";let K=null;function Ve({partnerSlug:s,apiKey:t,cdnUrl:r=qe,apiUrl:n=_e,getSteamId:a,children:i}){const l=b.useMemo(()=>({partnerSlug:s,apiKey:t,cdnUrl:r.replace(/\/$/,""),apiUrl:n.replace(/\/$/,""),getSteamId:a||(()=>null)}),[s,t,r,n,a]);let o=!1;try{w.useQueryClient(),o=!0}catch{o=!1}return o?e.jsx(O.Provider,{value:l,children:i}):(K||(K=new w.QueryClient({defaultOptions:{queries:{staleTime:6e4}}})),e.jsx(w.QueryClientProvider,{client:K,children:e.jsx(O.Provider,{value:l,children:i})}))}function S(){const s=b.useContext(O);if(!s)throw new Error("usePitVox must be used within a <PitVoxPartnerProvider>");return s}async function R(s,t){const r=await fetch(`${s}/${t}`);if(!r.ok){if(r.status===404||r.status===403)return null;throw new Error(`CDN fetch failed: ${r.status}`)}return r.json()}function E(s){if(!s&&s!==0)return"-";const t=Math.floor(s/1e3),r=Math.floor(t/60),n=t%60,a=s%1e3;return`${r}:${String(n).padStart(2,"0")}.${String(a).padStart(3,"0")}`}function I(s){return!s&&s!==0?"-":(s/1e3).toFixed(3)}function $(s){if(!s)return"";let t=s;return t.startsWith("ks_")&&(t=t.slice(3)),t.split("_").map(r=>r.charAt(0).toUpperCase()+r.slice(1)).join(" ")}function Q(s,t,r){const n=s.split("_").map(l=>l.charAt(0).toUpperCase()+l.slice(1)).join(" "),a=r==="acc"||!r&&(t==null?void 0:t.toLowerCase())==="track config";if(!t||t==="default"||a)return n;const i=t.split("_").map(l=>l.charAt(0).toUpperCase()+l.slice(1)).join(" ");return`${n} ${i}`}function A(s){if(!s)return"-";try{return new Date(s).toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"})}catch{return"-"}}function Ke(s){if(!s)return"";const t=Date.now()-new Date(s).getTime(),r=Math.floor(t/1e3);if(r<60)return"Just now";const n=Math.floor(r/60);if(n<60)return`${n}m ago`;const a=Math.floor(n/60);if(a<24)return`${a}h ago`;const i=Math.floor(a/24);return i<30?`${i}d ago`:A(s)}function Oe(s){return s==null?"":`${s>=0?"+":"-"}${(Math.abs(s)/1e3).toFixed(3)}`}function H(s){return s?{E:"Eco",RD:"Road",SC:"SuperCar",RV:"Race Vintage",SM:"Semislick",ST:"Street",HR:"Hard Race",MR:"Medium Race",SR:"Soft Race",S:"Soft"}[s]||s:"Unknown"}function re(s={}){var o,m,c,h,f,v;const{cdnUrl:t,partnerSlug:r}=S(),{game:n,gameVersion:a}=s,i=w.useQuery({queryKey:["pitvox","leaderboards",r,"index"],queryFn:()=>R(t,`leaderboards/partners/${r}/index.json`),staleTime:3e4,refetchInterval:3e4}),l=b.useMemo(()=>{var j;if(!((j=i.data)!=null&&j.tracks))return[];let x=i.data.tracks;return n&&(x=x.filter(d=>d.game===n)),a&&(x=x.filter(d=>d.gameVersion===a)),x.map(d=>({id:d.trackId,layout:d.layout,game:d.game,gameVersion:d.gameVersion,displayName:Q(d.trackId,d.layout,d.game),driverCount:d.driverCount||0,carCount:d.carCount||0,record:d.recordHolder?{visibleId:d.recordHolder.steamId||d.recordHolder.userId,carId:d.recordHolder.carId,carDisplayName:$(d.recordHolder.carId),lapTimeMs:d.recordHolder.lapTimeMs,timestamp:d.recordHolder.recordedAt}:null,recordByTag:d.recordByTag||null}))},[(o=i.data)==null?void 0:o.tracks,n,a]);return{...i,data:l,partner:((m=i.data)==null?void 0:m.partner)||null,generatedAt:(c=i.data)==null?void 0:c.generatedAt,totalLaps:((h=i.data)==null?void 0:h.totalLaps)||0,totalUsers:((f=i.data)==null?void 0:f.totalUsers)||0,versions:((v=i.data)==null?void 0:v.versions)||{}}}function G(s,t,r={}){const{cdnUrl:n,partnerSlug:a}=S(),{carId:i,game:l,gameVersion:o}=r,m=t||"default",c=o?`leaderboards/partners/${a}/v/${o}/tracks/${s}/${m}.json`:`leaderboards/partners/${a}/tracks/${s}/${m}.json`,{data:h,isLoading:f,error:v}=w.useQuery({queryKey:["pitvox","leaderboards",a,"track",s,m,o],queryFn:()=>R(n,c),enabled:!!s,staleTime:3e4,refetchInterval:3e4});return{data:b.useMemo(()=>{if(!(h!=null&&h.entries))return[];let j=h.entries;if(l&&(j=j.filter(d=>d.game===l)),!i){const d=new Map,u=new Map;for(const p of j){const N=p.steamId||p.userId;u.has(p.carId)||u.set(p.carId,new Set),u.get(p.carId).add(N),(!d.has(p.carId)||p.lapTimeMs<d.get(p.carId).lapTimeMs)&&d.set(p.carId,p)}return Array.from(d.values()).map(p=>{var N;return{...p,driverCount:((N=u.get(p.carId))==null?void 0:N.size)||0}}).sort((p,N)=>p.lapTimeMs-N.lapTimeMs)}return j.filter(d=>d.carId===i).sort((d,u)=>d.lapTimeMs-u.lapTimeMs)},[h==null?void 0:h.entries,i,l]),isLoading:f,error:v}}function ne(s,t,r,n,a={}){var v,x;const{cdnUrl:i,partnerSlug:l}=S(),{showInvalid:o=!1,game:m,gameVersion:c}=a,h=w.useQuery({queryKey:["pitvox","laps",l,s],queryFn:()=>R(i,`laps/partners/${l}/${s}.json`),enabled:!!s,staleTime:3e4,refetchInterval:3e4}),f=b.useMemo(()=>{var j;return(j=h.data)!=null&&j.laps?h.data.laps.filter(d=>{if(d.trackId!==t)return!1;if(r){if(d.trackLayout!==r)return!1}else if(d.trackLayout&&d.trackLayout!=="default")return!1;return!(d.carId!==n||m&&d.game!==m||c&&d.gameVersion!==c||!o&&!d.isValid)}).sort((d,u)=>d.lapTimeMs-u.lapTimeMs):[]},[(v=h.data)==null?void 0:v.laps,t,r,n,m,c,o]);return{...h,data:f,driverName:((x=h.data)==null?void 0:x.driverName)||"Driver"}}function ae(){const{cdnUrl:s}=S(),{data:t}=w.useQuery({queryKey:["pitvox","users","index"],queryFn:()=>R(s,"users/index.json"),staleTime:5*6e4,gcTime:30*6e4}),r=(t==null?void 0:t.users)||{};return(n,a)=>{const i=r[n];return{displayName:(i==null?void 0:i.displayName)||a||n||"Unknown",avatarUrl:(i==null?void 0:i.avatarUrl)||null,affiliations:(i==null?void 0:i.affiliations)||[]}}}function ie(){const{cdnUrl:s}=S(),{data:t}=w.useQuery({queryKey:["pitvox","cars","index"],queryFn:()=>R(s,"cars/index.json"),staleTime:5*6e4,gcTime:30*6e4});return{tags:(t==null?void 0:t.tags)||[],cars:(t==null?void 0:t.cars)||{}}}function Qe(s,t){return G(s,null,{carId:t})}function oe(){const{cdnUrl:s,partnerSlug:t}=S();return w.useQuery({queryKey:["pitvox","competitions",t],queryFn:async()=>{const r=await R(s,"competitions/index.json");return r!=null&&r.competitions?r.competitions.filter(n=>n.partnerSlug===t):[]},staleTime:6e4})}function W(s){const{cdnUrl:t,partnerSlug:r}=S();return w.useQuery({queryKey:["pitvox","competition",r,s,"config"],queryFn:()=>R(t,`competitions/${r}/${s}/config.json`),enabled:!!s,staleTime:6e4})}function le(s){const{cdnUrl:t,partnerSlug:r}=S();return w.useQuery({queryKey:["pitvox","competition",r,s,"standings"],queryFn:()=>R(t,`competitions/${r}/${s}/standings.json`),enabled:!!s,staleTime:6e4})}function ce(s,t){const{cdnUrl:r,partnerSlug:n}=S();return w.useQuery({queryKey:["pitvox","competition",n,s,"round",t],queryFn:()=>R(r,`competitions/${n}/${s}/rounds/${t}.json`),enabled:!!s&&t!=null,staleTime:6e4})}function He(s,t=[]){const{cdnUrl:r,partnerSlug:n}=S();return w.useQuery({queryKey:["pitvox","competition",n,s,"allRounds",t],queryFn:async()=>(await Promise.all(t.map(i=>R(r,`competitions/${n}/${s}/rounds/${i}.json`).catch(()=>null)))).filter(Boolean),enabled:!!s&&t.length>0,staleTime:6e4})}function de(s){const{cdnUrl:t,partnerSlug:r}=S();return w.useQuery({queryKey:["pitvox","competition",r,s,"entrylist"],queryFn:()=>R(t,`competitions/${r}/${s}/entrylist.json`),enabled:!!s,staleTime:6e4})}async function pe(s,t,r,n={}){const a=await fetch(`${s}${r}`,{...n,headers:{"Content-Type":"application/json","X-Partner-Key":t,...n.headers}});if(!a.ok){const i=await a.json().catch(()=>({}));throw new Error(i.detail||`API request failed: ${a.status}`)}return a.json()}function Ge(s,t,r,n){return pe(s,t,`/api/v1/competitions/${r}/register`,{method:"POST",body:JSON.stringify(n)})}function We(s,t,r,n){return pe(s,t,`/api/v1/competitions/${r}/register/${n}`,{method:"DELETE"})}function xe(s){const{cdnUrl:t,partnerSlug:r,getSteamId:n}=S(),a=n();return w.useQuery({queryKey:["pitvox","registration",r,s,a],queryFn:async()=>{var m;const i=await fetch(`${t}/competitions/${r}/${s}/entrylist.json`);if(!i.ok)return{isRegistered:!1,entryList:null};const l=await i.json();return{isRegistered:!!a&&((m=l.drivers)==null?void 0:m.some(c=>c.steamId===a)),entryList:l}},enabled:!!s,staleTime:6e4})}function ue(s){const{apiUrl:t,apiKey:r,partnerSlug:n}=S(),a=w.useQueryClient();return w.useMutation({mutationFn:i=>Ge(t,r,s,i),onSuccess:()=>{a.invalidateQueries({queryKey:["pitvox","registration",n,s]}),a.invalidateQueries({queryKey:["pitvox","competition",n,s,"entrylist"]})}})}function me(s){const{apiUrl:t,apiKey:r,partnerSlug:n,getSteamId:a}=S(),i=w.useQueryClient();return w.useMutation({mutationFn:l=>{const o=l||a();if(!o)throw new Error("No Steam ID available");return We(t,r,s,o)},onSuccess:()=>{i.invalidateQueries({queryKey:["pitvox","registration",n,s]}),i.invalidateQueries({queryKey:["pitvox","competition",n,s,"entrylist"]})}})}const ze={formula:"Formula",gt3:"GT3",gt2:"GT2",gt4:"GT4",cup:"Cup",prototype:"Prototype",rally:"Rally",hypercar:"Hypercar",supercar:"Supercar",sports_car:"Sports Car",hot_hatch:"Hot Hatch",lightweight:"Lightweight","1960s":"1960s","1970s":"1970s","1980s":"1980s","1990s":"1990s","2000s":"2000s","2010s":"2010s","2020s":"2020s"},ve=[{id:"class",tags:["formula","gt3","gt2","gt4","cup","prototype","rally"]},{id:"tier",tags:["hypercar","supercar","sports_car","hot_hatch","lightweight"]},{id:"era",tags:["1960s","1970s","1980s","1990s","2000s","2010s","2020s"]}];function Je(s){for(const t of ve)if(t.tags.includes(s))return t.id;return"other"}function he(s,t){if(t.size===0)return!0;const r={};for(const n of t){const a=Je(n);r[a]||(r[a]=[]),r[a].push(n)}for(const n of Object.values(r))if(!n.some(a=>s.includes(a)))return!1;return!0}function je(){const[s,t]=b.useState(new Set),r=b.useCallback(a=>{t(i=>{const l=new Set(i);return l.has(a)?l.delete(a):l.add(a),l})},[]),n=b.useCallback(()=>t(new Set),[]);return{activeTags:s,toggle:r,clear:n}}function z(s){const[t,r]=b.useState(s),n=b.useCallback(a=>{r(i=>i.key===a?{key:a,dir:i.dir==="asc"?"desc":"asc"}:{key:a,dir:a.includes("timestamp")||a.includes("Date")?"desc":"asc"})},[]);return[t,n]}function J(s,t,r){return[...s].sort((n,a)=>{const i=r(n,t.key),l=r(a,t.key);if(i==null&&l==null)return 0;if(i==null)return 1;if(l==null)return-1;const o=typeof i=="string"?i.localeCompare(l):i-l;return t.dir==="desc"?-o:o})}function fe(s,t=!1){const r=t?s==null?void 0:s.filter(n=>n.isValid):s;return r!=null&&r.length?{s1:Math.min(...r.map(n=>n.sector1Ms).filter(Boolean)),s2:Math.min(...r.map(n=>n.sector2Ms).filter(Boolean)),s3:Math.min(...r.map(n=>n.sector3Ms).filter(Boolean))}:null}function Ze({active:s,dir:t}){return s?t==="asc"?e.jsx("svg",{className:"pvx-sort-icon",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4.5 15.75l7.5-7.5 7.5 7.5"})}):e.jsx("svg",{className:"pvx-sort-icon",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M19.5 8.25l-7.5 7.5-7.5-7.5"})}):e.jsx("svg",{className:"pvx-sort-icon pvx-sort-icon--inactive",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8 9l4-4 4 4M16 15l-4 4-4-4"})})}function L({label:s,sortKey:t,config:r,onSort:n,className:a=""}){return e.jsx("th",{className:`pvx-th pvx-th--sortable ${a}`,onClick:()=>n(t),children:e.jsxs("span",{className:"pvx-th-inner",children:[s,e.jsx(Ze,{active:r.key===t,dir:r.dir})]})})}function Z({userId:s,getUserDisplay:t}){const{displayName:r,avatarUrl:n}=t(s);return e.jsxs("span",{className:"pvx-driver",children:[n?e.jsx("img",{src:n,alt:"",className:"pvx-driver-avatar"}):e.jsx("span",{className:"pvx-driver-avatar pvx-driver-avatar--placeholder"}),e.jsx("span",{className:"pvx-driver-name",children:r})]})}function ge({rank:s,podium:t=!1}){const r=t?s===1?"pvx-rank pvx-rank--gold":s===2?"pvx-rank pvx-rank--silver":s===3?"pvx-rank pvx-rank--bronze":"pvx-rank":"pvx-rank";return e.jsx("span",{className:r,children:s})}function Ne({availableTags:s,activeTags:t,onToggle:r,onClear:n}){if(!s||s.length<2)return null;const a=ve.map(i=>({id:i.id,tags:i.tags.filter(l=>s.includes(l))})).filter(i=>i.tags.length>0);return e.jsxs("div",{className:"pvx-tag-bar",children:[e.jsx("button",{onClick:n,className:`pvx-tag ${t.size===0?"pvx-tag--active":""}`,children:"All"}),a.map((i,l)=>e.jsxs("span",{className:"contents",children:[l>0&&e.jsx("span",{className:"pvx-tag-separator"}),i.tags.map(o=>e.jsx("button",{onClick:()=>r(o),className:`pvx-tag ${t.has(o)?"pvx-tag--active":""}`,children:ze[o]||o},o))]},i.id))]})}function X({segments:s}){return e.jsx("nav",{className:"pvx-breadcrumb","aria-label":"Breadcrumb",children:e.jsx("ol",{className:"pvx-breadcrumb-list",children:s.map((t,r)=>{const n=r===s.length-1;return e.jsxs("li",{className:"pvx-breadcrumb-item",children:[r>0&&e.jsx("svg",{className:"pvx-breadcrumb-chevron",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8.25 4.5l7.5 7.5-7.5 7.5"})}),!n&&t.onClick?e.jsx("button",{onClick:t.onClick,className:"pvx-breadcrumb-link",children:t.label}):e.jsx("span",{className:"pvx-breadcrumb-current",children:t.label})]},t.key)})})})}function q({message:s}){return e.jsxs("div",{className:"pvx-empty",children:[e.jsx("svg",{className:"pvx-empty-icon",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M16.5 18.75h-9m9 0a3 3 0 013 3h-15a3 3 0 013-3m9 0v-4.5A3.375 3.375 0 0013.125 10.875h-2.25A3.375 3.375 0 007.5 14.25v4.5"})}),e.jsx("p",{children:s})]})}function _(){return e.jsx("div",{className:"pvx-loading",children:"Loading..."})}function Xe(){return e.jsx("svg",{className:"pvx-icon pvx-icon--valid",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M4.5 12.75l6 6 9-13.5"})})}function Ye(){return e.jsx("svg",{className:"pvx-icon pvx-icon--invalid",fill:"none",viewBox:"0 0 24 24",strokeWidth:2,stroke:"currentColor",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M6 18L18 6M6 6l12 12"})})}function be({tracks:s,isLoading:t,carMetadata:r,getUserDisplay:n,onTrackSelect:a}){const[i,l]=z({key:"record.timestamp",dir:"desc"}),{activeTags:o,toggle:m,clear:c}=je(),h=b.useMemo(()=>{var j;if(!((j=r==null?void 0:r.tags)!=null&&j.length))return[];const x=new Set;for(const d of s||[])d.recordByTag&&Object.keys(d.recordByTag).forEach(u=>x.add(u));return r.tags.filter(d=>x.has(d))},[s,r]),f=b.useMemo(()=>s?o.size===0?s:s.map(x=>{var u,p;if(!x.recordByTag)return null;let j=null;const d=new Set;for(const N of Object.values(x.recordByTag)){if(d.has(N.carId))continue;d.add(N.carId);const y=((p=(u=r==null?void 0:r.cars)==null?void 0:u[N.carId])==null?void 0:p.tags)||["sports_car"];he(y,o)&&(!j||N.lapTimeMs<j.lapTimeMs)&&(j=N)}return j?{...x,record:{visibleId:j.steamId||j.identifier,carId:j.carId,carDisplayName:$(j.carId),lapTimeMs:j.lapTimeMs,timestamp:j.recordedAt}}:null}).filter(Boolean):[],[s,o,r]),v=b.useMemo(()=>J(f,i,(x,j)=>{var d,u;switch(j){case"displayName":return x.displayName;case"record.lapTimeMs":return(d=x.record)==null?void 0:d.lapTimeMs;case"driverCount":return x.driverCount||0;case"carCount":return x.carCount||0;case"record.timestamp":default:return(u=x.record)!=null&&u.timestamp?new Date(x.record.timestamp).getTime():0}}),[f,i]);return t?e.jsx(_,{}):s!=null&&s.length?e.jsxs("div",{className:"pvx-card",children:[e.jsx("div",{className:"pvx-card-header",children:e.jsx("h2",{className:"pvx-card-title",children:"Track Records"})}),e.jsx(Ne,{availableTags:h,activeTags:o,onToggle:m,onClear:c}),e.jsx("div",{className:"pvx-table-scroll",children:e.jsxs("table",{className:"pvx-table",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"pvx-thead-row",children:[e.jsx(L,{label:"Track",sortKey:"displayName",config:i,onSort:l}),e.jsx("th",{className:"pvx-th",children:"Record Holder"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-lg",children:"Car"}),e.jsx(L,{label:"Lap Time",sortKey:"record.lapTimeMs",config:i,onSort:l}),e.jsx(L,{label:"Drivers",sortKey:"driverCount",config:i,onSort:l,className:"pvx-hidden-below-md"}),e.jsx(L,{label:"Cars",sortKey:"carCount",config:i,onSort:l,className:"pvx-hidden-below-lg"}),e.jsx(L,{label:"Date",sortKey:"record.timestamp",config:i,onSort:l,className:"pvx-hidden-below-xl"})]})}),e.jsx("tbody",{className:"pvx-tbody",children:v.map(x=>{var j,d,u;return e.jsxs("tr",{className:"pvx-row pvx-row--clickable",onClick:()=>a(x.id,x.layout),children:[e.jsx("td",{className:"pvx-td pvx-td--primary",children:x.displayName}),e.jsx("td",{className:"pvx-td",children:(j=x.record)!=null&&j.visibleId?e.jsx(Z,{userId:x.record.visibleId,getUserDisplay:n}):"-"}),e.jsx("td",{className:"pvx-td pvx-hidden-below-lg",children:((d=x.record)==null?void 0:d.carDisplayName)||"-"}),e.jsx("td",{className:"pvx-td pvx-td--primary pvx-td--mono",children:x.record?E(x.record.lapTimeMs):"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-md",children:x.driverCount||"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-lg",children:x.carCount||"-"}),e.jsx("td",{className:"pvx-td pvx-td--muted pvx-hidden-below-xl",children:(u=x.record)!=null&&u.timestamp?A(x.record.timestamp):"-"})]},`${x.id}|${x.layout||""}`)})})]})})]}):e.jsx(q,{message:"No lap times recorded yet."})}function ye({entries:s,isLoading:t,track:r,carMetadata:n,getUserDisplay:a,onCarSelect:i,onNavigate:l}){const[o,m]=z({key:"lapTimeMs",dir:"asc"}),{activeTags:c,toggle:h,clear:f}=je(),v=b.useMemo(()=>{var p,N,y;if(!s||!((p=n==null?void 0:n.tags)!=null&&p.length))return[];const u=new Set;for(const k of s)(((y=(N=n.cars)==null?void 0:N[k.carId])==null?void 0:y.tags)||["sports_car"]).forEach(T=>u.add(T));return n.tags.filter(k=>u.has(k))},[s,n]),x=b.useMemo(()=>s?c.size===0?s:s.filter(u=>{var N,y;const p=((y=(N=n==null?void 0:n.cars)==null?void 0:N[u.carId])==null?void 0:y.tags)||["sports_car"];return he(p,c)}):[],[s,c,n]),j=b.useMemo(()=>J(x,o,(u,p)=>{switch(p){case"carId":return $(u.carId);case"driverCount":return u.driverCount||0;case"lapTimeMs":default:return u.lapTimeMs}}),[x,o]),d=[{key:"tracks",label:"Tracks",onClick:()=>l("tracks")},{key:"track",label:r.displayName}];return t?e.jsx(_,{}):e.jsxs("div",{className:"pvx-card",children:[e.jsx("div",{className:"pvx-card-header",children:e.jsx(X,{segments:d})}),e.jsx(Ne,{availableTags:v,activeTags:c,onToggle:h,onClear:f}),s!=null&&s.length?e.jsx("div",{className:"pvx-table-scroll",children:e.jsxs("table",{className:"pvx-table",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"pvx-thead-row",children:[e.jsx("th",{className:"pvx-th pvx-th--narrow",children:"#"}),e.jsx(L,{label:"Car",sortKey:"carId",config:o,onSort:m}),e.jsx("th",{className:"pvx-th",children:"Record Holder"}),e.jsx(L,{label:"Lap Time",sortKey:"lapTimeMs",config:o,onSort:m}),e.jsx(L,{label:"Drivers",sortKey:"driverCount",config:o,onSort:m,className:"pvx-hidden-below-md"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-lg",children:"Date"})]})}),e.jsx("tbody",{className:"pvx-tbody",children:j.map((u,p)=>e.jsxs("tr",{className:"pvx-row pvx-row--clickable",onClick:()=>i(u.carId),children:[e.jsx("td",{className:"pvx-td",children:e.jsx(ge,{rank:p+1,podium:!0})}),e.jsx("td",{className:"pvx-td pvx-td--primary",children:$(u.carId)}),e.jsx("td",{className:"pvx-td",children:e.jsx(Z,{userId:u.steamId||u.userId,getUserDisplay:a})}),e.jsx("td",{className:"pvx-td pvx-td--primary pvx-td--mono",children:E(u.lapTimeMs)}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-md",children:u.driverCount||"-"}),e.jsx("td",{className:"pvx-td pvx-td--muted pvx-hidden-below-lg",children:u.recordedAt?A(u.recordedAt):"-"})]},u.carId))})]})}):e.jsx(q,{message:"No lap times for this track yet."})]})}function Ce({entries:s,isLoading:t,track:r,carId:n,getUserDisplay:a,onDriverSelect:i,onNavigate:l}){const[o,m]=z({key:"lapTimeMs",dir:"asc"}),c=b.useMemo(()=>fe(s),[s]),h=b.useMemo(()=>J(s||[],o,(v,x)=>{switch(x){case"userId":return a(v.steamId||v.userId).displayName;case"lapCount":return v.lapCount||0;case"lapTimeMs":default:return v.lapTimeMs}}),[s,o,a]),f=[{key:"tracks",label:"Tracks",onClick:()=>l("tracks")},{key:"track",label:r.displayName,onClick:()=>l("track")},{key:"car",label:$(n)}];return t?e.jsx(_,{}):e.jsxs("div",{className:"pvx-card",children:[e.jsx("div",{className:"pvx-card-header",children:e.jsx(X,{segments:f})}),s!=null&&s.length?e.jsx("div",{className:"pvx-table-scroll",children:e.jsxs("table",{className:"pvx-table",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"pvx-thead-row",children:[e.jsx("th",{className:"pvx-th pvx-th--narrow",children:"#"}),e.jsx(L,{label:"Driver",sortKey:"userId",config:o,onSort:m}),e.jsx(L,{label:"Lap Time",sortKey:"lapTimeMs",config:o,onSort:m}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"S1"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"S2"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"S3"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-lg",children:"Tyre"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-xl",children:"Fuel"}),e.jsx(L,{label:"Laps",sortKey:"lapCount",config:o,onSort:m,className:"pvx-hidden-below-lg"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-xl",children:"Date"})]})}),e.jsx("tbody",{className:"pvx-tbody",children:h.map((v,x)=>{const j=x+1,d=v.steamId||v.userId,u=c&&v.sector1Ms===c.s1,p=c&&v.sector2Ms===c.s2,N=c&&v.sector3Ms===c.s3;return e.jsxs("tr",{className:`pvx-row pvx-row--clickable ${j<=3?"pvx-row--podium":""}`,onClick:()=>i(d),children:[e.jsx("td",{className:"pvx-td",children:e.jsx(ge,{rank:j,podium:!0})}),e.jsx("td",{className:"pvx-td pvx-td--primary",children:e.jsx(Z,{userId:d,getUserDisplay:a})}),e.jsx("td",{className:"pvx-td pvx-td--primary pvx-td--mono",children:E(v.lapTimeMs)}),e.jsx("td",{className:`pvx-td pvx-td--mono pvx-td--sector pvx-hidden-below-sm ${u?"pvx-td--best-sector":""}`,children:I(v.sector1Ms)}),e.jsx("td",{className:`pvx-td pvx-td--mono pvx-td--sector pvx-hidden-below-sm ${p?"pvx-td--best-sector":""}`,children:I(v.sector2Ms)}),e.jsx("td",{className:`pvx-td pvx-td--mono pvx-td--sector pvx-hidden-below-sm ${N?"pvx-td--best-sector":""}`,children:I(v.sector3Ms)}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-lg",title:H(v.tyreCompound),children:v.tyreCompound||"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-xl",children:v.startingFuelL?`${v.startingFuelL}L`:"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-lg",children:v.lapCount||"-"}),e.jsx("td",{className:"pvx-td pvx-td--muted pvx-hidden-below-xl",children:v.recordedAt?A(v.recordedAt):"-"})]},d)})})]})}):e.jsx(q,{message:"No lap times for this car yet."})]})}function we({userId:s,track:t,carId:r,game:n,gameVersion:a,showInvalid:i,getUserDisplay:l,onToggleInvalid:o,onNavigate:m}){const{data:c,driverName:h,isLoading:f}=ne(s,t.id,t.layout,r,{showInvalid:i,game:n,gameVersion:a}),{displayName:v,avatarUrl:x}=l(s,h),j=b.useMemo(()=>fe(c,!0),[c]),d=b.useMemo(()=>{const p=(c==null?void 0:c.filter(N=>N.isValid))||[];return p.length?Math.min(...p.map(N=>N.lapTimeMs)):null},[c]),u=[{key:"tracks",label:"Tracks",onClick:()=>m("tracks")},{key:"track",label:t.displayName,onClick:()=>m("track")},{key:"car",label:$(r),onClick:()=>m("car")},{key:"driver",label:v}];return f?e.jsx(_,{}):e.jsxs("div",{className:"pvx-card",children:[e.jsxs("div",{className:"pvx-card-header pvx-card-header--split",children:[e.jsxs("div",{className:"pvx-card-header-left",children:[e.jsx(X,{segments:u}),x?e.jsx("img",{src:x,alt:"",className:"pvx-driver-avatar pvx-driver-avatar--lg"}):e.jsx("span",{className:"pvx-driver-avatar pvx-driver-avatar--lg pvx-driver-avatar--placeholder"})]}),e.jsxs("label",{className:"pvx-checkbox-label",children:[e.jsx("input",{type:"checkbox",checked:i,onChange:o,className:"pvx-checkbox"}),e.jsx("span",{children:"Show invalid laps"})]})]}),c!=null&&c.length?e.jsx("div",{className:"pvx-table-scroll",children:e.jsxs("table",{className:"pvx-table",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"pvx-thead-row",children:[e.jsx("th",{className:"pvx-th pvx-th--narrow",children:"#"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-md",children:"Lap"}),e.jsx("th",{className:"pvx-th",children:"Lap Time"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"S1"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"S2"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"S3"}),e.jsx("th",{className:"pvx-th pvx-th--narrow",children:"Valid"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-lg",children:"Tyre"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-lg",children:"Fuel"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-xl",children:"Date"})]})}),e.jsx("tbody",{className:"pvx-tbody",children:c.map((p,N)=>{const y=p.isValid&&p.lapTimeMs===d,k=j&&p.isValid&&p.sector1Ms===j.s1,U=j&&p.isValid&&p.sector2Ms===j.s2,T=j&&p.isValid&&p.sector3Ms===j.s3;let P="pvx-row";return p.isValid||(P+=" pvx-row--invalid"),y&&(P+=" pvx-row--personal-best"),e.jsxs("tr",{className:P,children:[e.jsx("td",{className:"pvx-td",children:e.jsx("span",{className:`pvx-rank ${y?"pvx-rank--gold":""}`,children:N+1})}),e.jsx("td",{className:"pvx-td pvx-td--muted pvx-hidden-below-md",children:p.lapNumber||"-"}),e.jsx("td",{className:"pvx-td pvx-td--primary pvx-td--mono",children:E(p.lapTimeMs)}),e.jsx("td",{className:`pvx-td pvx-td--mono pvx-td--sector pvx-hidden-below-sm ${k?"pvx-td--best-sector":""}`,children:I(p.sector1Ms)}),e.jsx("td",{className:`pvx-td pvx-td--mono pvx-td--sector pvx-hidden-below-sm ${U?"pvx-td--best-sector":""}`,children:I(p.sector2Ms)}),e.jsx("td",{className:`pvx-td pvx-td--mono pvx-td--sector pvx-hidden-below-sm ${T?"pvx-td--best-sector":""}`,children:I(p.sector3Ms)}),e.jsx("td",{className:"pvx-td",title:p.isValid?void 0:p.invalidReason||"Invalid",children:p.isValid?e.jsx(Xe,{}):e.jsx(Ye,{})}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-lg",title:H(p.tyreCompound),children:p.tyreCompound||"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-lg",children:p.startingFuelL?`${p.startingFuelL}L`:"-"}),e.jsx("td",{className:"pvx-td pvx-td--muted pvx-hidden-below-xl",children:A(p.timestamp)})]},p.id)})})]})}):e.jsx(q,{message:i?"No laps recorded for this combination.":'No valid laps. Try enabling "Show invalid laps".'})]})}function es({className:s,defaultGame:t="evo",title:r="Leaderboards"}){const[n,a]=te.useSearchParams(),i=ae(),l=ie(),o=n.get("game")||t,m=n.get("version"),c=n.get("track"),h=n.get("car"),f=n.get("driver"),v=n.get("invalid")==="true",{data:x,isLoading:j,generatedAt:d,totalLaps:u,totalUsers:p,versions:N}=re({game:o}),y=N==null?void 0:N[o],k=m||(y==null?void 0:y.default)||null,U=b.useMemo(()=>!x||!k?x||[]:x.filter(g=>g.gameVersion===k),[x,k]),T=b.useMemo(()=>{if(!c)return null;const[g,C]=c.split("|");return{id:g,layout:C||null,displayName:Q(g,C,o)}},[c,o]),{data:P,isLoading:Y}=G(T==null?void 0:T.id,T==null?void 0:T.layout,{carId:h,game:o,gameVersion:k});function D(g){a(g)}function Ie(g){D({game:g})}function Pe(g){const C={game:o};g&&g!==(y==null?void 0:y.default)&&(C.version=g),D(C)}function Be(g,C){const ee={game:o,track:C?`${g}|${C}`:g};m&&(ee.version=m),D(ee)}function Ae(g){const C={game:o,track:c};m&&(C.version=m),g&&(C.car=g),D(C)}function Ee(g){const C={game:o,track:c,car:h,driver:g};m&&(C.version=m),D(C)}function V(g){const C={game:o};m&&(C.version=m),(g==="track"||g==="car")&&(C.track=c),g==="car"&&(C.car=h),D(C)}function Fe(){const g={game:o,track:c,car:h,driver:f};m&&(g.version=m),v||(g.invalid="true"),D(g)}const Ue=()=>T&&h&&f?e.jsx(we,{userId:f,track:T,carId:h,game:o,gameVersion:k,showInvalid:v,getUserDisplay:i,onToggleInvalid:Fe,onNavigate:V}):T&&h?e.jsx(Ce,{entries:P||[],isLoading:Y,track:T,carId:h,getUserDisplay:i,onDriverSelect:Ee,onNavigate:V}):T?e.jsx(ye,{entries:P||[],isLoading:Y,track:T,carMetadata:l,getUserDisplay:i,onCarSelect:Ae,onNavigate:V}):e.jsx(be,{tracks:U,isLoading:j,carMetadata:l,getUserDisplay:i,onTrackSelect:Be});return e.jsxs("div",{className:`pvx-leaderboard-explorer ${s||""}`,children:[e.jsxs("div",{className:"pvx-explorer-header",children:[e.jsx("h1",{className:"pvx-explorer-title",children:r}),e.jsxs("div",{className:"pvx-explorer-stats",children:[u>0&&e.jsxs("span",{children:[u.toLocaleString()," laps"]}),u>0&&p>0&&e.jsx("span",{className:"pvx-explorer-stats-sep",children:"|"}),p>0&&e.jsxs("span",{children:[p.toLocaleString()," drivers"]})]})]}),e.jsxs("div",{className:"pvx-explorer-controls",children:[e.jsx("div",{className:"pvx-game-tabs",children:["evo","acc"].map(g=>e.jsx("button",{onClick:()=>Ie(g),className:`pvx-game-tab ${o===g?"pvx-game-tab--active":""}`,children:g==="evo"?"AC EVO":"ACC"},g))}),y&&e.jsx("select",{value:k||"",onChange:g=>Pe(g.target.value),className:"pvx-version-select",children:y.available.slice().reverse().map(g=>e.jsxs("option",{value:g,children:["v",g,g===y.default?" (Latest)":""]},g))})]}),Ue(),d&&e.jsxs("p",{className:"pvx-data-timestamp",children:["Data updated: ",new Date(d).toLocaleString()]})]})}const ss={GBR:"🇬🇧",DEU:"🇩🇪",NLD:"🇳🇱",SWE:"🇸🇪",ESP:"🇪🇸",ZAF:"🇿🇦",FRA:"🇫🇷",ITA:"🇮🇹",USA:"🇺🇸",AUS:"🇦🇺",BRA:"🇧🇷",JPN:"🇯🇵",CAN:"🇨🇦",POL:"🇵🇱",AUT:"🇦🇹",BEL:"🇧🇪",PRT:"🇵🇹",NOR:"🇳🇴",DNK:"🇩🇰",FIN:"🇫🇮",IRL:"🇮🇪",CHE:"🇨🇭",NZL:"🇳🇿",MEX:"🇲🇽",ARG:"🇦🇷",CZE:"🇨🇿",HUN:"🇭🇺",RUS:"🇷🇺",TUR:"🇹🇷",KOR:"🇰🇷"},ts={championship:"Championship",series:"Series",event:"Event"},se=["PRACTICE","QUALIFYING","RACE"];function Te({position:s}){const t=s===1?"pvx-rank pvx-rank--gold":s===2?"pvx-rank pvx-rank--silver":s===3?"pvx-rank pvx-rank--bronze":"pvx-rank";return e.jsx("span",{className:t,children:s})}function Se({nation:s}){const t=s&&ss[s];return t?e.jsx("span",{className:"pvx-nation-flag",title:s,children:t}):null}function rs({sessions:s,activeSession:t,onSelect:r}){if(!s||s.length<=1)return null;const n=[...s].sort((a,i)=>se.indexOf(a.type)-se.indexOf(i.type));return e.jsx("div",{className:"pvx-session-tabs",children:n.map(a=>e.jsx("button",{onClick:()=>r(a.type),className:`pvx-session-tab ${t===a.type?"pvx-session-tab--active":""}`,children:a.type},a.type))})}function ke({type:s}){return e.jsx("span",{className:`pvx-comp-badge pvx-comp-badge--${s}`,children:ts[s]||s})}function B({children:s,variant:t="default"}){return e.jsx("span",{className:`pvx-info-pill ${t!=="default"?`pvx-info-pill--${t}`:""}`,children:s})}function Le(s){if(!s)return"TBD";const t=new Date(s);if(isNaN(t))return"TBD";const r=t.toLocaleDateString("en-GB",{weekday:"short",day:"numeric",month:"short"}),n=t.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"});return`${r} • ${n}`}function ns(s){return s.reduce((t,r)=>{var n;return(n=r.splits)!=null&&n.length?t?t.map((a,i)=>{const l=r.splits[i];return l&&l<a?l:a}):[...r.splits]:t},null)}function F({message:s="Loading..."}){return e.jsx("div",{className:"pvx-loading",children:s})}function M({message:s="No data available."}){return e.jsx("div",{className:"pvx-empty",children:e.jsx("p",{children:s})})}function Re({competitions:s,isLoading:t,onSelect:r,className:n}){return t?e.jsx(F,{message:"Loading competitions..."}):s!=null&&s.length?e.jsx("div",{className:`pvx-comp-grid ${n||""}`,children:s.map(a=>e.jsx(as,{comp:a,onSelect:r},a.id))}):e.jsx(M,{message:"No competitions available."})}function as({comp:s,onSelect:t}){var j,d,u;const{cdnUrl:r}=S(),n=s.posterCdnPath?`${r}/${s.posterCdnPath}`:null,a=s.registration,i=(a==null?void 0:a.currentCount)||0,l=a==null?void 0:a.maxParticipants,o=l&&i>=l,m=(a==null?void 0:a.deadline)&&new Date(a.deadline)<new Date,c=(a==null?void 0:a.isOpen)&&!m&&!o,h=new Date,f=(j=s.rounds)==null?void 0:j.find(p=>p.startTime&&new Date(p.startTime)>=h),v=((d=s.rounds)==null?void 0:d.length)||0,x=((u=s.rounds)==null?void 0:u.filter(p=>p.isFinalized).length)||0;return e.jsxs("div",{className:"pvx-comp-card",onClick:()=>t(s.id),role:"button",tabIndex:0,onKeyDown:p=>{(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),t(s.id))},children:[e.jsx("div",{className:"pvx-comp-card-poster",children:n?e.jsx("img",{src:n,alt:s.name,className:"pvx-comp-card-poster-img"}):e.jsx("div",{className:"pvx-comp-card-poster-placeholder",children:e.jsx(is,{})})}),e.jsxs("div",{className:"pvx-comp-card-body",children:[e.jsx("h3",{className:"pvx-comp-card-title",children:s.name}),s.description&&e.jsx("p",{className:"pvx-comp-card-desc",children:s.description}),e.jsxs("div",{className:"pvx-comp-card-pills",children:[e.jsx(ke,{type:s.type}),s.game&&e.jsx(B,{children:s.game.toUpperCase()}),s.formatDescription&&s.formatDescription.split(", ").map((p,N)=>e.jsx(B,{variant:"format",children:p},N))]}),s.carsDescription&&e.jsx("div",{className:"pvx-comp-card-pills",children:s.carsDescription.split(", ").map((p,N)=>e.jsx(B,{children:p},N))}),e.jsxs("div",{className:"pvx-comp-card-schedule",children:[f?e.jsxs("span",{className:"pvx-comp-card-schedule-next",children:[e.jsx("span",{className:"pvx-comp-card-schedule-label",children:"Next:"})," ","R",f.roundNumber," ",f.track||"TBD"," — ",Le(f.startTime)]}):v>0?e.jsxs("span",{className:"pvx-comp-card-schedule-next",children:[x,"/",v," rounds completed"]}):null,v>0&&e.jsxs("span",{className:"pvx-comp-card-round-count",children:[v," round",v!==1?"s":""]})]}),a&&e.jsxs("div",{className:"pvx-comp-card-reg",children:[e.jsx("span",{className:`pvx-comp-reg-badge ${c?"pvx-comp-reg-badge--open":o?"pvx-comp-reg-badge--full":"pvx-comp-reg-badge--closed"}`,children:c?"Registration Open":o?"Full":"Registration Closed"}),e.jsxs("span",{className:"pvx-comp-reg-count",children:[i,"/",l||"∞"," drivers"]})]})]})]})}function is(){return e.jsxs("svg",{className:"pvx-comp-trophy-icon",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("path",{d:"M6 9H4.5a2.5 2.5 0 0 1 0-5H6"}),e.jsx("path",{d:"M18 9h1.5a2.5 2.5 0 0 0 0-5H18"}),e.jsx("path",{d:"M4 22h16"}),e.jsx("path",{d:"M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"}),e.jsx("path",{d:"M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"}),e.jsx("path",{d:"M18 2H6v7a6 6 0 0 0 12 0V2Z"})]})}function $e({competitionId:s,className:t}){var o,m;const{data:r,isLoading:n}=le(s),{data:a,isLoading:i}=W(s);if(n||i)return e.jsx(F,{message:"Loading standings..."});if(!((o=r==null?void 0:r.standings)!=null&&o.length))return e.jsx(M,{message:"No standings data yet. Results will appear once rounds are finalised."});const l=((m=a==null?void 0:a.rounds)==null?void 0:m.filter(c=>c.isFinalized))||[];return e.jsxs("div",{className:`pvx-card ${t||""}`,children:[e.jsx("div",{className:"pvx-card-header--split",children:e.jsxs("div",{className:"pvx-card-header-left",children:[e.jsx("h3",{className:"pvx-card-title",children:"Championship Standings"}),e.jsxs("span",{className:"pvx-standings-subtitle",children:["After ",r.roundsCompleted," round",r.roundsCompleted!==1?"s":"",r.countingRounds>0&&` (best ${r.countingRounds} count)`]})]})}),e.jsx("div",{className:"pvx-table-scroll",children:e.jsxs("table",{className:"pvx-table",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"pvx-thead-row",children:[e.jsx("th",{className:"pvx-th pvx-th--narrow",children:"Pos"}),e.jsx("th",{className:"pvx-th",children:"Driver"}),e.jsx("th",{className:"pvx-th pvx-th--center pvx-hidden-below-sm",children:"W"}),e.jsx("th",{className:"pvx-th pvx-th--center pvx-hidden-below-sm",children:"Pod"}),e.jsx("th",{className:"pvx-th pvx-th--center",children:"Points"}),l.map(c=>e.jsxs("th",{className:"pvx-th pvx-th--center pvx-hidden-below-md",title:c.track||`Round ${c.roundNumber}`,children:["R",c.roundNumber]},c.roundNumber))]})}),e.jsx("tbody",{className:"pvx-tbody",children:r.standings.map(c=>e.jsx(os,{driver:c,finalizedRounds:l},c.driverId||c.driverName))})]})})]})}function os({driver:s,finalizedRounds:t}){var l,o,m;const r=s.position<=3,n=new Map(((l=s.roundResults)==null?void 0:l.map(c=>[c.roundNumber,c]))||[]),a=((o=s.roundResults)==null?void 0:o.filter(c=>c.position===1).length)||0,i=((m=s.roundResults)==null?void 0:m.filter(c=>c.position<=3).length)||0;return e.jsxs("tr",{className:`pvx-row ${r?"pvx-row--podium":""}`,children:[e.jsx("td",{className:"pvx-td",children:e.jsx(Te,{position:s.position})}),e.jsxs("td",{className:"pvx-td pvx-td--primary",children:[e.jsx(Se,{nation:s.nation}),s.driverName]}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-sm",children:a||"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-hidden-below-sm",children:i||"-"}),e.jsx("td",{className:"pvx-td pvx-td--center pvx-standings-total",children:s.totalPoints}),t.map(c=>{const h=n.get(c.roundNumber),f=h==null?void 0:h.dropped;return e.jsx("td",{className:`pvx-td pvx-td--center pvx-hidden-below-md ${!f&&(h==null?void 0:h.position)<=3?"pvx-standings-cell--podium":""}`,children:h?e.jsxs("div",{className:`pvx-standings-round-cell ${f?"pvx-standings-round-cell--dropped":""}`,children:[e.jsxs("span",{className:"pvx-standings-round-pos",children:["P",h.position]}),e.jsx("span",{className:"pvx-standings-round-pts",children:h.points})]}):e.jsx("span",{className:"pvx-td--muted",children:"-"})},c.roundNumber)})]})}function Me({competitionId:s,roundNumber:t,className:r}){var h,f;const{data:n,isLoading:a}=ce(s,t),[i,l]=b.useState(null);if(a)return e.jsx(F,{message:"Loading results..."});if(!n)return e.jsx(M,{message:"No results for this round."});const o=n.sessions||[];if(!o.length)return e.jsx(M,{message:"No session data for this round."});const m=i||((h=o.find(v=>v.type==="RACE"))==null?void 0:h.type)||((f=o[0])==null?void 0:f.type),c=o.find(v=>v.type===m)||o[0];return e.jsxs("div",{className:`pvx-round-results ${r||""}`,children:[e.jsxs("div",{className:"pvx-round-results-header",children:[e.jsxs("div",{children:[e.jsxs("h4",{className:"pvx-round-results-title",children:["Round ",n.roundNumber,n.track?`: ${n.track}`:""]}),n.startTime&&e.jsx("p",{className:"pvx-round-results-date",children:new Date(n.startTime).toLocaleDateString("en-GB",{day:"numeric",month:"long",year:"numeric"})})]}),e.jsx(ls,{sessions:o})]}),e.jsx(rs,{sessions:o,activeSession:m,onSelect:l}),e.jsx(cs,{session:c})]})}function ls({sessions:s}){var a;const t=s.find(i=>i.type==="RACE"),r=(a=t==null?void 0:t.results)==null?void 0:a.filter(i=>i.position<=3).sort((i,l)=>i.position-l.position);if(!(r!=null&&r.length))return null;const n=["🥇","🥈","🥉"];return e.jsx("div",{className:"pvx-round-podium-summary",children:r.map(i=>e.jsxs("span",{className:"pvx-round-podium-item",children:[e.jsx("span",{children:n[i.position-1]}),e.jsx("span",{children:i.driverName})]},i.driverId))})}function cs({session:s}){var n;const t=s.type==="RACE";if(!((n=s.results)!=null&&n.length))return e.jsx(M,{message:`No results for ${s.type}.`});const r=ns(s.results);return e.jsx("div",{className:"pvx-table-scroll",children:e.jsxs("table",{className:"pvx-table",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"pvx-thead-row",children:[e.jsx("th",{className:"pvx-th pvx-th--narrow",children:"Pos"}),e.jsx("th",{className:"pvx-th",children:"Driver"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"Car"}),e.jsx("th",{className:"pvx-th",children:"Best Lap"}),t&&e.jsxs(e.Fragment,{children:[e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"Laps"}),e.jsx("th",{className:"pvx-th pvx-hidden-below-sm",children:"Time / Gap"}),e.jsx("th",{className:"pvx-th pvx-th--center",children:"Points"})]})]})}),e.jsx("tbody",{className:"pvx-tbody",children:s.results.map((a,i)=>e.jsx(ds,{result:a,isRace:t,fastestSplits:r,rowIndex:i},a.driverId||i))})]})})}function ds({result:s,isRace:t,fastestSplits:r,rowIndex:n}){const a=s.position<=3;return e.jsxs("tr",{className:`pvx-row ${a?"pvx-row--podium":""}`,children:[e.jsx("td",{className:"pvx-td",children:e.jsx(Te,{position:s.position})}),e.jsxs("td",{className:"pvx-td pvx-td--primary",children:[e.jsx(Se,{nation:s.nation}),s.driverName,s.carNumber!=null&&e.jsxs("span",{className:"pvx-car-number",children:["#",s.carNumber]}),s.penalty&&e.jsx("span",{className:"pvx-penalty",children:s.penalty})]}),e.jsx("td",{className:"pvx-td pvx-hidden-below-sm",children:$(s.carId)}),e.jsx("td",{className:"pvx-td pvx-td--mono",children:e.jsx(ps,{bestLap:s.bestLapFormatted,hasBestLap:s.hasBestLap,splits:s.splits,fastestSplits:r,showAbove:n<=1})}),t&&e.jsxs(e.Fragment,{children:[e.jsx("td",{className:"pvx-td pvx-hidden-below-sm",children:s.lapsCompleted}),e.jsx("td",{className:"pvx-td pvx-td--mono pvx-td--muted pvx-hidden-below-sm",children:s.position===1?s.totalTime||"-":s.gap||"-"}),e.jsxs("td",{className:"pvx-td pvx-td--center",children:[e.jsx("span",{className:"pvx-session-points",children:s.points}),s.pointsOverride!==0&&s.pointsOverride&&e.jsxs("span",{className:"pvx-points-override",children:["(",s.pointsOverride>0?"+":"",s.pointsOverride,")"]})]})]})]})}function ps({bestLap:s,hasBestLap:t,splits:r,fastestSplits:n,showAbove:a}){return s?e.jsxs("span",{className:`pvx-best-lap-cell ${t?"pvx-best-lap-cell--fastest":""}`,children:[e.jsx("span",{className:"pvx-best-lap-time",children:s}),(r==null?void 0:r.length)>0&&e.jsx("span",{className:"pvx-splits-tooltip-anchor",children:e.jsx("span",{className:`pvx-splits-tooltip ${a?"":"pvx-splits-tooltip--above"}`,children:r.map((i,l)=>{const o=n&&i===n[l];return e.jsxs("span",{className:"pvx-splits-tooltip-row",children:[e.jsxs("span",{className:"pvx-splits-tooltip-label",children:["S",l+1]}),e.jsx("span",{className:o?"pvx-splits-tooltip-best":"",children:i})]},l)})})})]}):e.jsx("span",{className:"pvx-td--muted",children:"-"})}function De({competitionId:s,className:t}){const{data:r,isLoading:n}=de(s);if(n)return e.jsx(F,{message:"Loading drivers..."});const a=(r==null?void 0:r.drivers)||(r==null?void 0:r.entries)||[];return a.length?e.jsx("div",{className:`pvx-entry-grid ${t||""}`,children:a.map(i=>{var l,o;return e.jsxs("div",{className:"pvx-entry-card",children:[i.avatarUrl?e.jsx("img",{src:i.avatarUrl,alt:"",className:"pvx-entry-avatar"}):e.jsx("div",{className:"pvx-entry-avatar pvx-entry-avatar--placeholder",children:((o=(l=i.displayName)==null?void 0:l.charAt(0))==null?void 0:o.toUpperCase())||"?"}),e.jsx("span",{className:"pvx-entry-name",children:i.displayName})]},i.steamId||i.driverId)})}):e.jsx(M,{message:"No drivers registered yet."})}const xs=[{id:"standings",label:"Standings"},{id:"rounds",label:"Rounds"},{id:"drivers",label:"Drivers"}];function us({className:s,title:t="Competitions"}){const[r,n]=te.useSearchParams(),a=r.get("competition"),i=r.get("tab")||"standings",l=r.get("round")?Number(r.get("round")):null,{data:o,isLoading:m}=oe();function c(j){n({competition:j})}function h(){n({})}function f(j){n({competition:a,tab:j})}function v(j){n({competition:a,tab:"rounds",round:String(j)})}function x(){n({competition:a,tab:"rounds"})}return a?e.jsx("div",{className:`pvx-comp-explorer ${s||""}`,children:e.jsx(ms,{competitionId:a,activeTab:i,selectedRound:l,onBack:h,onTabChange:f,onSelectRound:v,onDeselectRound:x})}):e.jsxs("div",{className:`pvx-comp-explorer ${s||""}`,children:[e.jsxs("div",{className:"pvx-explorer-header",children:[e.jsx("h2",{className:"pvx-explorer-title",children:t}),!m&&(o==null?void 0:o.length)>0&&e.jsx("div",{className:"pvx-explorer-stats",children:e.jsxs("span",{children:[o.length," competition",o.length!==1?"s":""]})})]}),e.jsx(Re,{competitions:o,isLoading:m,onSelect:c})]})}function ms({competitionId:s,activeTab:t,selectedRound:r,onBack:n,onTabChange:a,onSelectRound:i,onDeselectRound:l}){var j,d;const{data:o,isLoading:m}=W(s);if(m)return e.jsx(F,{message:"Loading competition..."});if(!o)return e.jsxs("div",{children:[e.jsx(M,{message:"Competition not found."}),e.jsx("div",{className:"pvx-comp-back-link-wrap",children:e.jsx("button",{onClick:n,className:"pvx-comp-back-link",children:"← Back to competitions"})})]});const c=o.type==="championship",h=((j=o.rounds)==null?void 0:j.filter(u=>u.isFinalized))||[],f=o.rounds||[],v=xs.filter(u=>!(u.id==="standings"&&!c||u.id==="rounds"&&h.length===0)),x=v.find(u=>u.id===t)?t:((d=v[0])==null?void 0:d.id)||"standings";return e.jsxs(e.Fragment,{children:[e.jsx("button",{onClick:n,className:"pvx-comp-back-link",children:"← Competitions"}),e.jsxs("div",{className:"pvx-comp-detail-header",children:[e.jsx("h2",{className:"pvx-explorer-title",children:o.name}),o.description&&e.jsx("p",{className:"pvx-comp-detail-desc",children:o.description}),e.jsxs("div",{className:"pvx-comp-detail-meta",children:[e.jsx(ke,{type:o.type}),o.game&&e.jsx(B,{children:o.game.toUpperCase()}),o.countingRounds>0&&e.jsxs(B,{variant:"format",children:["Best ",o.countingRounds," of ",f.length," rounds count"]}),f.length>0&&e.jsxs(B,{children:[f.length," round",f.length!==1?"s":""]})]})]}),f.length>0&&e.jsx(vs,{rounds:f}),v.length>1&&e.jsx("div",{className:"pvx-comp-tabs",children:v.map(u=>e.jsx("button",{onClick:()=>a(u.id),className:`pvx-comp-tab ${x===u.id?"pvx-comp-tab--active":""}`,children:u.label},u.id))}),x==="standings"&&e.jsx($e,{competitionId:s}),x==="rounds"&&e.jsx(hs,{competitionId:s,finalizedRounds:h,selectedRound:r,onSelectRound:i,onDeselectRound:l}),x==="drivers"&&e.jsx(De,{competitionId:s})]})}function vs({rounds:s}){const t=new Date,r=s.find(n=>n.startTime&&new Date(n.startTime)>=t);return e.jsx("div",{className:"pvx-comp-schedule",children:e.jsx("div",{className:"pvx-comp-schedule-list",children:s.map(n=>{const a=n===r,i=n.startTime&&new Date(n.startTime)<t;return e.jsxs("div",{className:`pvx-comp-schedule-item ${a?"pvx-comp-schedule-item--next":""} ${i?"pvx-comp-schedule-item--past":""}`,children:[e.jsxs("span",{className:"pvx-comp-schedule-round",children:["R",n.roundNumber]}),e.jsx("span",{className:"pvx-comp-schedule-track",children:n.track||"TBD"}),e.jsx("span",{className:"pvx-comp-schedule-date",children:Le(n.startTime)}),n.isFinalized&&e.jsx("span",{className:"pvx-comp-schedule-badge",children:"Results"})]},n.roundNumber)})})})}function hs({competitionId:s,finalizedRounds:t,selectedRound:r,onSelectRound:n,onDeselectRound:a}){return t.length?e.jsxs("div",{className:"pvx-comp-rounds",children:[e.jsx("div",{className:"pvx-comp-round-pills",children:t.map(i=>e.jsxs("button",{onClick:()=>r===i.roundNumber?a():n(i.roundNumber),className:`pvx-comp-round-pill ${r===i.roundNumber?"pvx-comp-round-pill--active":""}`,children:[e.jsxs("span",{className:"pvx-comp-round-pill-num",children:["R",i.roundNumber]}),i.track&&e.jsx("span",{className:"pvx-comp-round-pill-track",children:i.track})]},i.roundNumber))}),r?e.jsx(Me,{competitionId:s,roundNumber:r}):e.jsx("div",{className:"pvx-empty",children:e.jsx("p",{children:"Select a round to view results."})})]}):e.jsx(M,{message:"No finalised rounds yet."})}function js({competitionId:s,driverData:t,className:r,children:n}){const{data:a,isLoading:i}=xe(s),l=ue(s),o=me(s),m=(a==null?void 0:a.isRegistered)||!1,c=i||l.isPending||o.isPending,h=l.error||o.error||null,f=()=>{t&&l.mutate(t)},v=()=>{o.mutate()};return typeof n=="function"?n({isRegistered:m,isLoading:c,register:f,withdraw:v,error:h}):e.jsx("button",{className:r,onClick:m?v:f,disabled:c,children:c?"Loading...":m?"Withdraw":"Register"})}exports.CarsTable=ye;exports.CompetitionCards=Re;exports.CompetitionExplorer=us;exports.DriversTable=Ce;exports.EntryList=De;exports.LapHistoryTable=we;exports.LeaderboardExplorer=es;exports.PitVoxPartnerProvider=Ve;exports.RegisterButton=js;exports.RoundResults=Me;exports.StandingsTable=$e;exports.TracksTable=be;exports.formatCarName=$;exports.formatDate=A;exports.formatDelta=Oe;exports.formatLapTime=E;exports.formatRelativeTime=Ke;exports.formatSectorTime=I;exports.formatTrackName=Q;exports.formatTyreCompound=H;exports.useCarLeaderboard=Qe;exports.useCarMetadata=ie;exports.useCompetitionAllRounds=He;exports.useCompetitionConfig=W;exports.useCompetitionEntryList=de;exports.useCompetitionRound=ce;exports.useCompetitionStandings=le;exports.useCompetitions=oe;exports.useDriverLaps=ne;exports.useLeaderboardIndex=re;exports.usePitVox=S;exports.useRegister=ue;exports.useRegistrationStatus=xe;exports.useTrackLeaderboard=G;exports.useUserLookup=ae;exports.useWithdraw=me;
|