@pitvox/partner-react 0.3.0 → 0.5.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 +127 -165
- package/dist/index.cjs +1 -1
- package/dist/index.js +1412 -1218
- package/dist/styles.css +614 -18
- 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,14 +25,12 @@ import { PitVoxPartnerProvider } from '@pitvox/partner-react'
|
|
|
25
25
|
|
|
26
26
|
function App() {
|
|
27
27
|
return (
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</PitVoxPartnerProvider>
|
|
35
|
-
</BrowserRouter>
|
|
28
|
+
<PitVoxPartnerProvider
|
|
29
|
+
partnerSlug="your-slug"
|
|
30
|
+
getSteamId={() => currentUser?.steamId ?? null}
|
|
31
|
+
>
|
|
32
|
+
{/* your app */}
|
|
33
|
+
</PitVoxPartnerProvider>
|
|
36
34
|
)
|
|
37
35
|
}
|
|
38
36
|
```
|
|
@@ -41,48 +39,7 @@ function App() {
|
|
|
41
39
|
|
|
42
40
|
## Leaderboards
|
|
43
41
|
|
|
44
|
-
###
|
|
45
|
-
|
|
46
|
-
One component gives you a complete 4-layer leaderboard with game tabs, version selector, tag filtering, sortable columns, sector highlighting, and drill-down navigation:
|
|
47
|
-
|
|
48
|
-
```jsx
|
|
49
|
-
import { LeaderboardExplorer } from '@pitvox/partner-react'
|
|
50
|
-
import '@pitvox/partner-react/styles.css'
|
|
51
|
-
|
|
52
|
-
function LeaderboardsPage() {
|
|
53
|
-
return <LeaderboardExplorer />
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
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.
|
|
58
|
-
|
|
59
|
-
**Props:**
|
|
60
|
-
|
|
61
|
-
| Prop | Type | Default | Description |
|
|
62
|
-
|------|------|---------|-------------|
|
|
63
|
-
| `className` | `string` | — | Additional class on root container |
|
|
64
|
-
| `defaultGame` | `'evo' \| 'acc'` | `'evo'` | Default game tab |
|
|
65
|
-
| `title` | `string` | `'Leaderboards'` | Page heading |
|
|
66
|
-
|
|
67
|
-
### Layer components
|
|
68
|
-
|
|
69
|
-
For more control, use the individual layer components directly. Each handles its own sorting, filtering, and highlighting logic:
|
|
70
|
-
|
|
71
|
-
```jsx
|
|
72
|
-
import { TracksTable, CarsTable, DriversTable, LapHistoryTable } from '@pitvox/partner-react'
|
|
73
|
-
import '@pitvox/partner-react/styles.css'
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
- **`<TracksTable>`** — Layer 1: all tracks with record holders, tag filtering
|
|
77
|
-
- **`<CarsTable>`** — Layer 2: cars for a selected track, tag filtering
|
|
78
|
-
- **`<DriversTable>`** — Layer 3: drivers for a car with sectors (S1/S2/S3), tyre, fuel
|
|
79
|
-
- **`<LapHistoryTable>`** — Layer 4: driver's lap history with validity and personal best highlighting
|
|
80
|
-
|
|
81
|
-
You're responsible for wiring up the navigation between layers and managing URL state.
|
|
82
|
-
|
|
83
|
-
### Hooks only
|
|
84
|
-
|
|
85
|
-
For fully custom UIs, use the data hooks directly:
|
|
42
|
+
### Hooks
|
|
86
43
|
|
|
87
44
|
```jsx
|
|
88
45
|
import {
|
|
@@ -96,12 +53,11 @@ import {
|
|
|
96
53
|
|
|
97
54
|
**`useLeaderboardIndex(options?)`** — Fetch all tracks with record holders.
|
|
98
55
|
- `options.game` — Filter by game (`'evo'` | `'acc'`)
|
|
99
|
-
- `
|
|
100
|
-
- Returns `{ data: Track[], isLoading, partner, generatedAt, totalLaps, totalUsers, versions }`
|
|
56
|
+
- Returns `{ data: Track[], isLoading, generatedAt, totalLaps, totalUsers, versions }`
|
|
101
57
|
|
|
102
58
|
**`useTrackLeaderboard(trackId, layout?, options?)`** — Fetch track entries.
|
|
103
|
-
- Without `options.carId`: returns best lap per car (
|
|
104
|
-
- 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)
|
|
105
61
|
- Returns `{ data: Entry[], isLoading, error }`
|
|
106
62
|
|
|
107
63
|
**`useDriverLaps(userId, trackId, layout, carId, options?)`** — Fetch a driver's lap history.
|
|
@@ -112,58 +68,25 @@ import {
|
|
|
112
68
|
|
|
113
69
|
**`useCarMetadata()`** — Returns `{ tags: string[], cars: Record<string, { tags }> }` for tag filtering.
|
|
114
70
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
### Drop-in (recommended)
|
|
118
|
-
|
|
119
|
-
One component gives you a complete competition experience with card grid, championship standings, round-by-round results with session tabs, and entry lists:
|
|
120
|
-
|
|
121
|
-
```jsx
|
|
122
|
-
import { CompetitionExplorer } from '@pitvox/partner-react'
|
|
123
|
-
import '@pitvox/partner-react/styles.css'
|
|
124
|
-
|
|
125
|
-
function CompetitionsPage() {
|
|
126
|
-
return <CompetitionExplorer />
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
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.
|
|
131
|
-
|
|
132
|
-
**Props:**
|
|
133
|
-
|
|
134
|
-
| Prop | Type | Default | Description |
|
|
135
|
-
|------|------|---------|-------------|
|
|
136
|
-
| `className` | `string` | — | Additional class on root container |
|
|
137
|
-
| `title` | `string` | `'Competitions'` | Page heading |
|
|
138
|
-
|
|
139
|
-
**Features:**
|
|
140
|
-
- Competition card grid with poster images, type badges, format/car pills, schedule summary, registration status
|
|
141
|
-
- Championship standings with position rank badges, nation flags, wins/podiums columns, per-round cells with dropped round styling
|
|
142
|
-
- Round results with session tabs (Practice/Qualifying/Race), best lap with sector hover tooltip, podium highlighting
|
|
143
|
-
- Entry list with driver avatars
|
|
144
|
-
- Horizontal schedule strip with next-round highlighting
|
|
145
|
-
- Breadcrumb navigation between list and detail views
|
|
146
|
-
|
|
147
|
-
### Layer components
|
|
71
|
+
### Styled components
|
|
148
72
|
|
|
149
|
-
For
|
|
73
|
+
For building leaderboard pages with the SDK's `pvx-*` styles:
|
|
150
74
|
|
|
151
75
|
```jsx
|
|
152
|
-
import {
|
|
76
|
+
import { TracksTable, CarsTable, DriversTable, LapHistoryTable } from '@pitvox/partner-react'
|
|
153
77
|
import '@pitvox/partner-react/styles.css'
|
|
154
78
|
```
|
|
155
79
|
|
|
156
|
-
- **`<
|
|
157
|
-
- **`<
|
|
158
|
-
- **`<
|
|
159
|
-
- **`<
|
|
160
|
-
- **`<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
|
|
161
84
|
|
|
162
|
-
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.
|
|
163
86
|
|
|
164
|
-
|
|
87
|
+
## Competitions
|
|
165
88
|
|
|
166
|
-
|
|
89
|
+
### Hooks
|
|
167
90
|
|
|
168
91
|
```jsx
|
|
169
92
|
import {
|
|
@@ -187,13 +110,38 @@ import {
|
|
|
187
110
|
**`useCompetitionAllRounds(competitionId, roundNumbers)`** — Fetch multiple round results in parallel.
|
|
188
111
|
|
|
189
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'
|
|
190
138
|
```
|
|
191
139
|
|
|
192
140
|
## Driver Dashboard
|
|
193
141
|
|
|
194
|
-
### Drop-in
|
|
142
|
+
### Drop-in composite
|
|
195
143
|
|
|
196
|
-
|
|
144
|
+
The `DriverDashboard` is a self-contained component with no routing dependency:
|
|
197
145
|
|
|
198
146
|
```jsx
|
|
199
147
|
import { DriverDashboard } from '@pitvox/partner-react'
|
|
@@ -210,8 +158,6 @@ function DashboardPage() {
|
|
|
210
158
|
}
|
|
211
159
|
```
|
|
212
160
|
|
|
213
|
-
**Props:**
|
|
214
|
-
|
|
215
161
|
| Prop | Type | Default | Description |
|
|
216
162
|
|------|------|---------|-------------|
|
|
217
163
|
| `steamId` | `string` | — | Driver's Steam ID (required) |
|
|
@@ -219,50 +165,84 @@ function DashboardPage() {
|
|
|
219
165
|
| `memberSince` | `string` | — | ISO date for "Racing since" display |
|
|
220
166
|
| `className` | `string` | — | Additional class on root container |
|
|
221
167
|
|
|
222
|
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
225
|
-
- Current records list sorted by most recent, with track, car, lap time, and game badge
|
|
168
|
+
The dashboard automatically includes:
|
|
169
|
+
- **Upcoming Events** — competition rounds the driver is registered for (CDN-based, always available)
|
|
170
|
+
- **Notifications** — only when `onFetchNotifications` is provided to the provider (see [Notifications](#notifications))
|
|
226
171
|
|
|
227
172
|
### Layer components
|
|
228
173
|
|
|
229
|
-
|
|
174
|
+
```jsx
|
|
175
|
+
import { DriverProfile, StatsCards, RecordsTable, UpcomingEvents, NotificationsCard } from '@pitvox/partner-react'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- **`<UpcomingEvents>`** — Upcoming competition rounds card (accepts `events` array from `useUpcomingEvents()`)
|
|
179
|
+
- **`<NotificationsCard>`** — Notifications list with read/unread state (accepts `notifications`, `unreadCount`, `onMarkRead`, `onMarkAllRead`)
|
|
180
|
+
|
|
181
|
+
### Hooks
|
|
230
182
|
|
|
231
183
|
```jsx
|
|
232
|
-
import {
|
|
233
|
-
import '@pitvox/partner-react/styles.css'
|
|
184
|
+
import { useDriverStats, useDriverRating, useUpcomingEvents } from '@pitvox/partner-react'
|
|
234
185
|
```
|
|
235
186
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
187
|
+
**`useDriverStats(steamId)`** — Driver stats, records, and ranking from CDN.
|
|
188
|
+
|
|
189
|
+
**`useDriverRating(steamId)`** — Driver's rating from the partner ratings file.
|
|
190
|
+
|
|
191
|
+
**`useUpcomingEvents()`** — Upcoming competition rounds the current user is registered for (CDN-based).
|
|
239
192
|
|
|
240
|
-
|
|
193
|
+
## Notifications
|
|
241
194
|
|
|
242
|
-
|
|
195
|
+
Notifications require a backend to proxy requests to pitvox-api (keeping the API key server-side). Provide callbacks to the provider:
|
|
243
196
|
|
|
244
197
|
```jsx
|
|
245
|
-
|
|
198
|
+
<PitVoxPartnerProvider
|
|
199
|
+
partnerSlug="your-slug"
|
|
200
|
+
getSteamId={() => user?.steamId ?? null}
|
|
201
|
+
onFetchNotifications={async (params) => {
|
|
202
|
+
const res = await fetch(`/api/notifications?limit=${params.limit || 20}`)
|
|
203
|
+
return res.json() // { notifications: [...], unreadCount: number }
|
|
204
|
+
}}
|
|
205
|
+
onMarkNotificationRead={async (id) => {
|
|
206
|
+
await fetch(`/api/notifications/${id}/read`, { method: 'PATCH' })
|
|
207
|
+
}}
|
|
208
|
+
onMarkAllNotificationsRead={async () => {
|
|
209
|
+
await fetch('/api/notifications/read-all', { method: 'PATCH' })
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
When no callbacks are provided, notification hooks return disabled/empty state and `DriverDashboard` hides the notifications section.
|
|
215
|
+
|
|
216
|
+
### Hooks
|
|
217
|
+
|
|
218
|
+
```jsx
|
|
219
|
+
import {
|
|
220
|
+
useNotifications, useUnreadCount, useMarkNotificationRead,
|
|
221
|
+
useMarkAllNotificationsRead, useNotificationsEnabled,
|
|
222
|
+
} from '@pitvox/partner-react'
|
|
246
223
|
```
|
|
247
224
|
|
|
248
|
-
**`
|
|
249
|
-
|
|
250
|
-
|
|
225
|
+
**`useNotifications(options?)`** — Fetch notifications (polls every 30s). Returns `{ data: { notifications, unreadCount }, isLoading }`.
|
|
226
|
+
|
|
227
|
+
**`useUnreadCount()`** — Unread count for navbar badges. Returns `{ count, isLoading }`.
|
|
228
|
+
|
|
229
|
+
**`useMarkNotificationRead()`** — Mutation to mark a notification as read.
|
|
230
|
+
|
|
231
|
+
**`useMarkAllNotificationsRead()`** — Mutation to mark all as read.
|
|
251
232
|
|
|
252
|
-
**`
|
|
253
|
-
- Returns `{ data: { rating, rank, totalDrivers, comboCount, distinctCars, combos }, isLoading, error }`
|
|
233
|
+
**`useNotificationsEnabled()`** — Returns `boolean` — whether notification callbacks are provided.
|
|
254
234
|
|
|
255
235
|
## Registration
|
|
256
236
|
|
|
257
|
-
The SDK supports two registration modes, determined
|
|
237
|
+
The SDK supports two registration modes, determined by whether you provide callbacks to the provider.
|
|
258
238
|
|
|
259
239
|
### Basic mode (default)
|
|
260
240
|
|
|
261
|
-
No configuration needed.
|
|
241
|
+
No configuration needed. Registration components render links to pitvox.com where users register with Steam.
|
|
262
242
|
|
|
263
243
|
### Power mode
|
|
264
244
|
|
|
265
|
-
For partners with a backend (e.g.
|
|
245
|
+
For partners with a backend (e.g. Amplify Lambda proxying to pitvox-api), provide callbacks:
|
|
266
246
|
|
|
267
247
|
```jsx
|
|
268
248
|
<PitVoxPartnerProvider
|
|
@@ -277,29 +257,21 @@ For partners with a backend (e.g. an Amplify Lambda that proxies to pitvox-api),
|
|
|
277
257
|
>
|
|
278
258
|
```
|
|
279
259
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
### Hooks
|
|
260
|
+
### Registration hooks
|
|
283
261
|
|
|
284
262
|
```jsx
|
|
285
|
-
import {
|
|
286
|
-
useRegistrationStatus,
|
|
287
|
-
useRegister,
|
|
288
|
-
useWithdraw,
|
|
289
|
-
useRegistrationMode,
|
|
290
|
-
useRegistrationUrl,
|
|
291
|
-
} from '@pitvox/partner-react'
|
|
263
|
+
import { useRegistrationStatus, useRegister, useWithdraw, useRegistrationMode, useRegistrationUrl } from '@pitvox/partner-react'
|
|
292
264
|
```
|
|
293
265
|
|
|
294
|
-
**`useRegistrationStatus(competitionId)`** — Check if current user
|
|
266
|
+
**`useRegistrationStatus(competitionId)`** — Check if current user is registered.
|
|
295
267
|
|
|
296
|
-
**`useRegister(competitionId)`** —
|
|
268
|
+
**`useRegister(competitionId)`** — Mutation delegating to `onRegister` callback.
|
|
297
269
|
|
|
298
|
-
**`useWithdraw(competitionId)`** —
|
|
270
|
+
**`useWithdraw(competitionId)`** — Mutation delegating to `onWithdraw` callback.
|
|
299
271
|
|
|
300
|
-
**`useRegistrationMode()`** — Returns `{ isPowerMode, isBasicMode }
|
|
272
|
+
**`useRegistrationMode()`** — Returns `{ isPowerMode, isBasicMode }`.
|
|
301
273
|
|
|
302
|
-
**`useRegistrationUrl(competitionId)`** — Returns
|
|
274
|
+
**`useRegistrationUrl(competitionId)`** — Returns pitvox.com registration URL for basic mode.
|
|
303
275
|
|
|
304
276
|
## Formatting utilities
|
|
305
277
|
|
|
@@ -312,7 +284,8 @@ import {
|
|
|
312
284
|
formatDate, // ISO string → "27 Feb 2024"
|
|
313
285
|
formatRelativeTime, // ISO string → "2h ago"
|
|
314
286
|
formatDelta, // 542 → "+0.542"
|
|
315
|
-
formatTyreCompound,
|
|
287
|
+
formatTyreCompound, // "SR" → "Soft Race"
|
|
288
|
+
formatNotificationMessage, // notification → "X beat your record on Track — Car"
|
|
316
289
|
} from '@pitvox/partner-react'
|
|
317
290
|
```
|
|
318
291
|
|
|
@@ -322,36 +295,25 @@ The default stylesheet uses CSS custom properties. Override them to match your b
|
|
|
322
295
|
|
|
323
296
|
```css
|
|
324
297
|
:root {
|
|
325
|
-
--pvx-accent: #e11d48;
|
|
326
|
-
--pvx-bg-card: #1a1a2e;
|
|
327
|
-
--pvx-sector-best: #22d3ee;
|
|
328
|
-
--pvx-rank-gold: #fbbf24;
|
|
298
|
+
--pvx-accent: #e11d48;
|
|
299
|
+
--pvx-bg-card: #1a1a2e;
|
|
300
|
+
--pvx-sector-best: #22d3ee;
|
|
301
|
+
--pvx-rank-gold: #fbbf24;
|
|
329
302
|
}
|
|
330
303
|
```
|
|
331
304
|
|
|
332
|
-
All classes are prefixed with `pvx-` to avoid collisions
|
|
305
|
+
All classes are prefixed with `pvx-` to avoid collisions. See `styles.css` for the full list of variables.
|
|
333
306
|
|
|
334
|
-
##
|
|
307
|
+
## Partner templates
|
|
335
308
|
|
|
336
|
-
|
|
309
|
+
For a complete working site using this SDK, see:
|
|
337
310
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
| Leaderboard index | `leaderboards/partners/{slug}/index.json` |
|
|
341
|
-
| Track entries | `leaderboards/partners/{slug}/tracks/{trackId}/{layout}.json` |
|
|
342
|
-
| Driver laps | `laps/partners/{slug}/{userId}.json` |
|
|
343
|
-
| User display names | `users/index.json` |
|
|
344
|
-
| Car metadata | `cars/index.json` |
|
|
345
|
-
| Competition index | `competitions/index.json` |
|
|
346
|
-
| Competition config | `competitions/{slug}/{id}/config.json` |
|
|
347
|
-
| Standings | `competitions/{slug}/{id}/standings.json` |
|
|
348
|
-
| Round results | `competitions/{slug}/{id}/rounds/{n}.json` |
|
|
349
|
-
| Entry list | `competitions/{slug}/{id}/entrylist.json` |
|
|
350
|
-
| Driver ratings | `leaderboards/partners/{slug}/ratings.json` |
|
|
311
|
+
- **[pitvox-partner-template](https://github.com/meekysoft/pitvox-partner-template)** — Basic template (no auth)
|
|
312
|
+
- **[pitvox-partner-template-amplify](https://github.com/meekysoft/pitvox-partner-template-amplify)** — AWS Amplify template with Steam auth and in-app registration
|
|
351
313
|
|
|
352
|
-
|
|
314
|
+
The templates demonstrate how to compose SDK hooks and components into full pages with routing.
|
|
353
315
|
|
|
354
|
-
|
|
316
|
+
## Local development
|
|
355
317
|
|
|
356
318
|
```bash
|
|
357
319
|
# In the SDK repo
|
|
@@ -367,7 +329,7 @@ Add `resolve.dedupe` to your Vite config to avoid duplicate React instances:
|
|
|
367
329
|
// vite.config.js
|
|
368
330
|
export default defineConfig({
|
|
369
331
|
resolve: {
|
|
370
|
-
dedupe: ['react', 'react-dom', '
|
|
332
|
+
dedupe: ['react', 'react-dom', '@tanstack/react-query'],
|
|
371
333
|
},
|
|
372
334
|
})
|
|
373
335
|
```
|