be-components 7.7.2 → 7.7.4
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/lib/commonjs/Assets/fonts/Barlow-Bold.ttf +0 -0
- package/lib/commonjs/Assets/fonts/Barlow-Regular.ttf +0 -0
- package/lib/commonjs/Assets/fonts/Barlow-SemiBold.ttf +0 -0
- package/lib/commonjs/Assets/images/powered_by_be.webp +0 -0
- package/lib/commonjs/BetRouter/components/MyOpportunities.tsx.bak +440 -0
- package/lib/commonjs/BetRouter/types/ADMIN_PORTAL.md +863 -0
- package/lib/commonjs/BetRouter/types/ADMIN_PORTAL_SIMPLIFIED.md +1881 -0
- package/lib/commonjs/BetRouter/types/CREDENTIALS_EXAMPLE.md +350 -0
- package/lib/commonjs/BetRouter/types/LIQUIDITY_CLIENT_GUIDE.md +399 -0
- package/lib/commonjs/BetRouter/types/MARKET_LINKING_WORKFLOW.md +682 -0
- package/lib/commonjs/BetRouter/types/MARKET_LINKING_WORKFLOW_V2.md +627 -0
- package/lib/commonjs/BetRouter/types/README.md +249 -0
- package/lib/commonjs/Charts/README.md +310 -0
- package/lib/commonjs/NotificationManager/components/ScheduleNotification.js +1 -16
- package/lib/commonjs/NotificationManager/components/ScheduleNotification.js.map +1 -1
- package/lib/commonjs/NotificationManager/components/shared/GroupSelector.js +74 -4
- package/lib/commonjs/NotificationManager/components/shared/GroupSelector.js.map +1 -1
- package/lib/commonjs/NotificationManager/index.js +49 -5
- package/lib/commonjs/NotificationManager/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ApiOverrides/index.ts +2 -2
- package/src/NotificationManager/components/ScheduleNotification.tsx +1 -17
- package/src/NotificationManager/components/shared/GroupSelector.tsx +83 -6
- package/src/NotificationManager/index.tsx +52 -5
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
# Partner Credentials - Client Usage Guide
|
|
2
|
+
|
|
3
|
+
This guide shows how to build dynamic credential forms in your client based on partner types.
|
|
4
|
+
|
|
5
|
+
## 🎯 Usage in Client
|
|
6
|
+
|
|
7
|
+
### Example 1: Dynamic Form Builder
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import {
|
|
11
|
+
RouterPartnerProps,
|
|
12
|
+
KalshiCredentialProps,
|
|
13
|
+
BettorEdgeCredentialProps,
|
|
14
|
+
BetOpenlyCredentialProps,
|
|
15
|
+
getRequiredCredentialFields,
|
|
16
|
+
getCredentialFieldLabels,
|
|
17
|
+
getCredentialFieldTypes
|
|
18
|
+
} from './types/betrouter';
|
|
19
|
+
|
|
20
|
+
interface CredentialFormProps {
|
|
21
|
+
partner: RouterPartnerProps;
|
|
22
|
+
onSubmit: (credentials: any) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function CredentialForm({ partner, onSubmit }: CredentialFormProps) {
|
|
26
|
+
const fields = getRequiredCredentialFields(partner.name);
|
|
27
|
+
const labels = getCredentialFieldLabels(partner.name);
|
|
28
|
+
const types = getCredentialFieldTypes(partner.name);
|
|
29
|
+
|
|
30
|
+
const [formData, setFormData] = useState<Record<string, string>>({});
|
|
31
|
+
|
|
32
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
onSubmit(formData);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<form onSubmit={handleSubmit}>
|
|
39
|
+
<h2>Connect to {partner.name}</h2>
|
|
40
|
+
{fields.map(field => (
|
|
41
|
+
<div key={field}>
|
|
42
|
+
<label>{labels[field]}</label>
|
|
43
|
+
{types[field] === 'textarea' ? (
|
|
44
|
+
<textarea
|
|
45
|
+
value={formData[field] || ''}
|
|
46
|
+
onChange={(e) => setFormData({ ...formData, [field]: e.target.value })}
|
|
47
|
+
required
|
|
48
|
+
/>
|
|
49
|
+
) : (
|
|
50
|
+
<input
|
|
51
|
+
type={types[field]}
|
|
52
|
+
value={formData[field] || ''}
|
|
53
|
+
onChange={(e) => setFormData({ ...formData, [field]: e.target.value })}
|
|
54
|
+
required
|
|
55
|
+
/>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
))}
|
|
59
|
+
<button type="submit">Connect</button>
|
|
60
|
+
</form>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Example 2: Type-Safe Credential Update
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import {
|
|
69
|
+
ApiContracts,
|
|
70
|
+
ExtractRequestBody,
|
|
71
|
+
ExtractSuccessResponse,
|
|
72
|
+
KalshiCredentialProps,
|
|
73
|
+
BettorEdgeCredentialProps,
|
|
74
|
+
BetOpenlyCredentialProps
|
|
75
|
+
} from './types/betrouter';
|
|
76
|
+
|
|
77
|
+
type UpdateCredentialsBody = ExtractRequestBody<'POST /accounts/partner/credentials/update'>;
|
|
78
|
+
type UpdateCredentialsResponse = ExtractSuccessResponse<'POST /accounts/partner/credentials/update'>;
|
|
79
|
+
|
|
80
|
+
async function updatePartnerCredentials(
|
|
81
|
+
partnerId: string,
|
|
82
|
+
credentials: KalshiCredentialProps | BettorEdgeCredentialProps | BetOpenlyCredentialProps
|
|
83
|
+
): Promise<UpdateCredentialsResponse> {
|
|
84
|
+
const body: UpdateCredentialsBody = {
|
|
85
|
+
partner_id: partnerId,
|
|
86
|
+
credentials
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const response = await fetch('/api/accounts/partner/credentials/update', {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
headers: {
|
|
92
|
+
'Content-Type': 'application/json',
|
|
93
|
+
'Authorization': `Bearer ${token}`
|
|
94
|
+
},
|
|
95
|
+
body: JSON.stringify(body)
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return response.json();
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Example 3: Partner-Specific Forms
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { KalshiCredentialProps, BettorEdgeCredentialProps, BetOpenlyCredentialProps } from './types/betrouter';
|
|
106
|
+
|
|
107
|
+
// Kalshi Form
|
|
108
|
+
function KalshiCredentialsForm() {
|
|
109
|
+
const [credentials, setCredentials] = useState<KalshiCredentialProps>({
|
|
110
|
+
api_key: '',
|
|
111
|
+
private_key_pem: ''
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<form>
|
|
116
|
+
<input
|
|
117
|
+
type="text"
|
|
118
|
+
placeholder="API Key"
|
|
119
|
+
value={credentials.api_key}
|
|
120
|
+
onChange={(e) => setCredentials({ ...credentials, api_key: e.target.value })}
|
|
121
|
+
/>
|
|
122
|
+
<textarea
|
|
123
|
+
placeholder="Private Key (PEM format)"
|
|
124
|
+
value={credentials.private_key_pem}
|
|
125
|
+
onChange={(e) => setCredentials({ ...credentials, private_key_pem: e.target.value })}
|
|
126
|
+
/>
|
|
127
|
+
</form>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// BettorEdge Form
|
|
132
|
+
function BettorEdgeCredentialsForm() {
|
|
133
|
+
const [credentials, setCredentials] = useState<BettorEdgeCredentialProps>({
|
|
134
|
+
username: '',
|
|
135
|
+
password: '',
|
|
136
|
+
device_id: ''
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<form>
|
|
141
|
+
<input
|
|
142
|
+
type="text"
|
|
143
|
+
placeholder="Username"
|
|
144
|
+
value={credentials.username}
|
|
145
|
+
onChange={(e) => setCredentials({ ...credentials, username: e.target.value })}
|
|
146
|
+
/>
|
|
147
|
+
<input
|
|
148
|
+
type="password"
|
|
149
|
+
placeholder="Password"
|
|
150
|
+
value={credentials.password}
|
|
151
|
+
onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
|
|
152
|
+
/>
|
|
153
|
+
<input
|
|
154
|
+
type="text"
|
|
155
|
+
placeholder="Device ID"
|
|
156
|
+
value={credentials.device_id}
|
|
157
|
+
onChange={(e) => setCredentials({ ...credentials, device_id: e.target.value })}
|
|
158
|
+
/>
|
|
159
|
+
</form>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// BetOpenly Form
|
|
164
|
+
function BetOpenlyCredentialsForm() {
|
|
165
|
+
const [credentials, setCredentials] = useState<BetOpenlyCredentialProps>({
|
|
166
|
+
username: '',
|
|
167
|
+
password: ''
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<form>
|
|
172
|
+
<input
|
|
173
|
+
type="text"
|
|
174
|
+
placeholder="Username"
|
|
175
|
+
value={credentials.username}
|
|
176
|
+
onChange={(e) => setCredentials({ ...credentials, username: e.target.value })}
|
|
177
|
+
/>
|
|
178
|
+
<input
|
|
179
|
+
type="password"
|
|
180
|
+
placeholder="Password"
|
|
181
|
+
value={credentials.password}
|
|
182
|
+
onChange={(e) => setCredentials({ ...credentials, password: e.target.value })}
|
|
183
|
+
/>
|
|
184
|
+
</form>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Example 4: Complete Flow with React Query
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { useMutation } from '@tanstack/react-query';
|
|
193
|
+
import {
|
|
194
|
+
RouterPartnerProps,
|
|
195
|
+
KalshiCredentialProps,
|
|
196
|
+
BettorEdgeCredentialProps,
|
|
197
|
+
BetOpenlyCredentialProps,
|
|
198
|
+
ExtractRequestBody,
|
|
199
|
+
ExtractSuccessResponse
|
|
200
|
+
} from './types/betrouter';
|
|
201
|
+
|
|
202
|
+
type UpdateCredentialsBody = ExtractRequestBody<'POST /accounts/partner/credentials/update'>;
|
|
203
|
+
type UpdateCredentialsResponse = ExtractSuccessResponse<'POST /accounts/partner/credentials/update'>;
|
|
204
|
+
|
|
205
|
+
function useUpdateCredentials() {
|
|
206
|
+
return useMutation({
|
|
207
|
+
mutationFn: async (body: UpdateCredentialsBody): Promise<UpdateCredentialsResponse> => {
|
|
208
|
+
const response = await fetch('/api/accounts/partner/credentials/update', {
|
|
209
|
+
method: 'POST',
|
|
210
|
+
headers: {
|
|
211
|
+
'Content-Type': 'application/json',
|
|
212
|
+
'Authorization': `Bearer ${token}`
|
|
213
|
+
},
|
|
214
|
+
body: JSON.stringify(body)
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!response.ok) throw new Error('Failed to update credentials');
|
|
218
|
+
return response.json();
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function ConnectPartnerPage({ partner }: { partner: RouterPartnerProps }) {
|
|
224
|
+
const updateCredentials = useUpdateCredentials();
|
|
225
|
+
const [formData, setFormData] = useState<Record<string, string>>({});
|
|
226
|
+
|
|
227
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
|
|
230
|
+
// Type-safe credentials based on partner
|
|
231
|
+
let credentials: KalshiCredentialProps | BettorEdgeCredentialProps | BetOpenlyCredentialProps;
|
|
232
|
+
|
|
233
|
+
if (partner.name === 'Kalshi') {
|
|
234
|
+
credentials = {
|
|
235
|
+
api_key: formData.api_key,
|
|
236
|
+
private_key_pem: formData.private_key_pem
|
|
237
|
+
};
|
|
238
|
+
} else if (partner.name === 'BettorEdge') {
|
|
239
|
+
credentials = {
|
|
240
|
+
username: formData.username,
|
|
241
|
+
password: formData.password,
|
|
242
|
+
device_id: formData.device_id
|
|
243
|
+
};
|
|
244
|
+
} else if (partner.name === 'BetOpenly') {
|
|
245
|
+
credentials = {
|
|
246
|
+
username: formData.username,
|
|
247
|
+
password: formData.password
|
|
248
|
+
};
|
|
249
|
+
} else {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
await updateCredentials.mutateAsync({
|
|
254
|
+
partner_id: partner.partner_id,
|
|
255
|
+
credentials
|
|
256
|
+
});
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Use the helper functions for dynamic form rendering
|
|
260
|
+
// (see Example 1)
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## 📋 Available Helper Functions
|
|
265
|
+
|
|
266
|
+
### `getRequiredCredentialFields(partnerName: string): string[]`
|
|
267
|
+
Returns array of required field names for a partner.
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
getRequiredCredentialFields('Kalshi')
|
|
271
|
+
// Returns: ['api_key', 'private_key_pem']
|
|
272
|
+
|
|
273
|
+
getRequiredCredentialFields('BettorEdge')
|
|
274
|
+
// Returns: ['username', 'password', 'device_id']
|
|
275
|
+
|
|
276
|
+
getRequiredCredentialFields('BetOpenly')
|
|
277
|
+
// Returns: ['username', 'password']
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### `getCredentialFieldLabels(partnerName: string): Record<string, string>`
|
|
281
|
+
Returns human-readable labels for each field.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
getCredentialFieldLabels('Kalshi')
|
|
285
|
+
// Returns: { api_key: 'API Key', private_key_pem: 'Private Key (PEM format)' }
|
|
286
|
+
|
|
287
|
+
getCredentialFieldLabels('BetOpenly')
|
|
288
|
+
// Returns: { username: 'Username', password: 'Password' }
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `getCredentialFieldTypes(partnerName: string): Record<string, 'text' | 'password' | 'textarea'>`
|
|
292
|
+
Returns input types for each field.
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
getCredentialFieldTypes('Kalshi')
|
|
296
|
+
// Returns: { api_key: 'text', private_key_pem: 'textarea' }
|
|
297
|
+
|
|
298
|
+
getCredentialFieldTypes('BetOpenly')
|
|
299
|
+
// Returns: { username: 'text', password: 'password' }
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## 🔐 Type Guards
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { isKalshiCredentials, isBettorEdgeCredentials, isBetOpenlyCredentials } from './types/betrouter';
|
|
306
|
+
|
|
307
|
+
function processCredentials(credentials: any) {
|
|
308
|
+
if (isKalshiCredentials(credentials)) {
|
|
309
|
+
// TypeScript knows this is KalshiCredentialProps
|
|
310
|
+
console.log(credentials.api_key);
|
|
311
|
+
console.log(credentials.private_key_pem);
|
|
312
|
+
} else if (isBettorEdgeCredentials(credentials)) {
|
|
313
|
+
// TypeScript knows this is BettorEdgeCredentialProps
|
|
314
|
+
console.log(credentials.username);
|
|
315
|
+
console.log(credentials.password);
|
|
316
|
+
console.log(credentials.device_id);
|
|
317
|
+
} else if (isBetOpenlyCredentials(credentials)) {
|
|
318
|
+
// TypeScript knows this is BetOpenlyCredentialProps
|
|
319
|
+
console.log(credentials.username);
|
|
320
|
+
console.log(credentials.password);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## 🆕 Adding New Partner Credentials
|
|
326
|
+
|
|
327
|
+
When you add a new partner:
|
|
328
|
+
|
|
329
|
+
1. **Add the credential interface in `src/types/credentials.ts`:**
|
|
330
|
+
```typescript
|
|
331
|
+
export interface NewPartnerCredentialProps {
|
|
332
|
+
field1: string
|
|
333
|
+
field2: string
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
2. **Update the union type:**
|
|
338
|
+
```typescript
|
|
339
|
+
export type PartnerCredentialProps =
|
|
340
|
+
KalshiCredentialProps |
|
|
341
|
+
BettorEdgeCredentialProps |
|
|
342
|
+
BetOpenlyCredentialProps |
|
|
343
|
+
NewPartnerCredentialProps
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
3. **Update the helper functions** in the same file
|
|
347
|
+
|
|
348
|
+
4. **Copy to your client** - `cp -r ../betrouter/src/types ./src/types/betrouter`
|
|
349
|
+
|
|
350
|
+
5. **Your client automatically gets the new partner support!**
|