onairos 2.1.12 → 2.2.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/dist/onairos.bundle.js +1 -1
- package/dist/onairos.bundle.js.map +1 -1
- package/dist/onairos.esm.js +1 -1
- package/dist/onairos.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DataRequest.js +250 -109
- package/src/onairosButton.jsx +5 -10
- package/test-enhanced-data-request.html +271 -0
|
@@ -1,66 +1,143 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// Platform connectors with correct icons
|
|
4
|
+
const platformConnectors = [
|
|
5
|
+
{ name: 'YouTube', icon: '📺', color: 'bg-red-500', connector: 'youtube' },
|
|
6
|
+
{ name: 'LinkedIn', icon: '💼', color: 'bg-blue-700', connector: 'linkedin' },
|
|
7
|
+
{ name: 'Reddit', icon: '🔥', color: 'bg-orange-500', connector: 'reddit' },
|
|
8
|
+
{ name: 'Pinterest', icon: '📌', color: 'bg-red-600', connector: 'pinterest' },
|
|
9
|
+
{ name: 'Instagram', icon: '📷', color: 'bg-pink-500', connector: 'instagram' },
|
|
10
|
+
{ name: 'GitHub', icon: '⚡', color: 'bg-gray-800', connector: 'github' },
|
|
11
|
+
{ name: 'Facebook', icon: '👥', color: 'bg-blue-600', connector: 'facebook' },
|
|
12
|
+
{ name: 'Gmail', icon: '✉️', color: 'bg-red-400', connector: 'gmail' }
|
|
13
|
+
];
|
|
2
14
|
|
|
3
15
|
const dataTypes = [
|
|
4
16
|
{
|
|
5
17
|
id: 'basic',
|
|
6
18
|
name: 'Basic Info',
|
|
7
|
-
description: 'Essential profile information and
|
|
19
|
+
description: 'Essential profile information, account details, and basic demographics',
|
|
8
20
|
icon: '👤',
|
|
9
|
-
required:
|
|
21
|
+
required: true, // Auto-selected and non-deselectable
|
|
22
|
+
tooltip: 'Includes name, email, basic profile information. This data is essential for personalization and is always included.',
|
|
23
|
+
privacyLink: 'https://onairos.uk/privacy#basic-info'
|
|
10
24
|
},
|
|
11
25
|
{
|
|
12
26
|
id: 'personality',
|
|
13
27
|
name: 'Personality',
|
|
14
|
-
description: 'Personality traits, behavioral patterns and insights',
|
|
28
|
+
description: 'Personality traits, behavioral patterns and psychological insights',
|
|
15
29
|
icon: '🧠',
|
|
16
|
-
required: false
|
|
30
|
+
required: false,
|
|
31
|
+
tooltip: 'AI-analyzed personality traits based on your social media activity and interactions. Used to improve content recommendations.',
|
|
32
|
+
privacyLink: 'https://onairos.uk/privacy#personality-data'
|
|
17
33
|
},
|
|
18
34
|
{
|
|
19
35
|
id: 'preferences',
|
|
20
36
|
name: 'Preferences',
|
|
21
|
-
description: 'User preferences, settings and choices',
|
|
37
|
+
description: 'User preferences, interests, settings and personal choices',
|
|
22
38
|
icon: '⚙️',
|
|
23
|
-
required: false
|
|
39
|
+
required: false,
|
|
40
|
+
tooltip: 'Your likes, interests, content preferences, and behavioral patterns. Helps create a more personalized experience.',
|
|
41
|
+
privacyLink: 'https://onairos.uk/privacy#preferences-data'
|
|
24
42
|
}
|
|
25
43
|
];
|
|
26
44
|
|
|
45
|
+
// Tooltip component
|
|
46
|
+
const Tooltip = ({ children, content, privacyLink }) => {
|
|
47
|
+
const [showTooltip, setShowTooltip] = useState(false);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="relative inline-block">
|
|
51
|
+
<span
|
|
52
|
+
onMouseEnter={() => setShowTooltip(true)}
|
|
53
|
+
onMouseLeave={() => setShowTooltip(false)}
|
|
54
|
+
className="border-b border-dotted border-gray-400 cursor-help"
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</span>
|
|
58
|
+
{showTooltip && (
|
|
59
|
+
<div className="absolute z-50 w-64 p-3 mt-2 text-sm bg-white border border-gray-200 rounded-lg shadow-lg left-0">
|
|
60
|
+
<p className="mb-2 text-gray-700">{content}</p>
|
|
61
|
+
<a
|
|
62
|
+
href={privacyLink}
|
|
63
|
+
target="_blank"
|
|
64
|
+
rel="noopener noreferrer"
|
|
65
|
+
className="text-blue-600 hover:text-blue-800 text-xs font-medium"
|
|
66
|
+
>
|
|
67
|
+
Learn more about privacy →
|
|
68
|
+
</a>
|
|
69
|
+
</div>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
27
75
|
export default function DataRequest({
|
|
28
76
|
onComplete,
|
|
29
77
|
userEmail,
|
|
30
78
|
appName = 'App',
|
|
31
79
|
autoFetch = false,
|
|
32
|
-
testMode = false
|
|
80
|
+
testMode = false,
|
|
81
|
+
connectedAccounts = {} // Connected platforms from onboarding
|
|
33
82
|
}) {
|
|
34
83
|
const [selectedData, setSelectedData] = useState({
|
|
35
|
-
basic:
|
|
84
|
+
basic: true, // Auto-selected and required
|
|
36
85
|
personality: false,
|
|
37
86
|
preferences: false
|
|
38
87
|
});
|
|
88
|
+
|
|
89
|
+
const [connectorStates, setConnectorStates] = useState({});
|
|
39
90
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
40
91
|
const [isLoadingApi, setIsLoadingApi] = useState(false);
|
|
41
92
|
const [apiResponse, setApiResponse] = useState(null);
|
|
42
93
|
const [apiError, setApiError] = useState(null);
|
|
43
94
|
|
|
95
|
+
// Initialize connector states based on connected accounts
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const initialStates = {};
|
|
98
|
+
platformConnectors.forEach(platform => {
|
|
99
|
+
initialStates[platform.name] = {
|
|
100
|
+
connected: connectedAccounts[platform.name] || false,
|
|
101
|
+
selected: false
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
setConnectorStates(initialStates);
|
|
105
|
+
}, [connectedAccounts]);
|
|
106
|
+
|
|
44
107
|
const handleDataToggle = (dataId) => {
|
|
108
|
+
// Don't allow toggling required items
|
|
109
|
+
const dataType = dataTypes.find(dt => dt.id === dataId);
|
|
110
|
+
if (dataType?.required) return;
|
|
111
|
+
|
|
45
112
|
setSelectedData(prev => ({
|
|
46
113
|
...prev,
|
|
47
114
|
[dataId]: !prev[dataId]
|
|
48
115
|
}));
|
|
49
116
|
};
|
|
50
117
|
|
|
118
|
+
const handleConnectorToggle = (platformName) => {
|
|
119
|
+
setConnectorStates(prev => ({
|
|
120
|
+
...prev,
|
|
121
|
+
[platformName]: {
|
|
122
|
+
...prev[platformName],
|
|
123
|
+
selected: !prev[platformName]?.selected
|
|
124
|
+
}
|
|
125
|
+
}));
|
|
126
|
+
};
|
|
127
|
+
|
|
51
128
|
const handleRowClick = (dataId) => {
|
|
52
|
-
|
|
129
|
+
const dataType = dataTypes.find(dt => dt.id === dataId);
|
|
130
|
+
if (dataType?.required) return; // Don't allow clicking required items
|
|
53
131
|
handleDataToggle(dataId);
|
|
54
132
|
};
|
|
55
133
|
|
|
56
134
|
const generateUserHash = (email) => {
|
|
57
|
-
// Simple hash function for user identification
|
|
58
135
|
let hash = 0;
|
|
59
136
|
const str = email + Date.now().toString();
|
|
60
137
|
for (let i = 0; i < str.length; i++) {
|
|
61
138
|
const char = str.charCodeAt(i);
|
|
62
139
|
hash = ((hash << 5) - hash) + char;
|
|
63
|
-
hash = hash & hash;
|
|
140
|
+
hash = hash & hash;
|
|
64
141
|
}
|
|
65
142
|
return `user_${Math.abs(hash).toString(36)}`;
|
|
66
143
|
};
|
|
@@ -70,7 +147,6 @@ export default function DataRequest({
|
|
|
70
147
|
setApiError(null);
|
|
71
148
|
|
|
72
149
|
try {
|
|
73
|
-
// Create a unique user hash for this request
|
|
74
150
|
const userHash = generateUserHash(userEmail);
|
|
75
151
|
|
|
76
152
|
// Get selected data types
|
|
@@ -78,16 +154,19 @@ export default function DataRequest({
|
|
|
78
154
|
.filter(([key, value]) => value)
|
|
79
155
|
.map(([key]) => key);
|
|
80
156
|
|
|
81
|
-
//
|
|
157
|
+
// Get selected connectors
|
|
158
|
+
const selectedConnectors = Object.entries(connectorStates)
|
|
159
|
+
.filter(([platform, state]) => state.selected && state.connected)
|
|
160
|
+
.map(([platform]) => platform);
|
|
161
|
+
|
|
82
162
|
const mapDataTypesToConfirmations = (approvedData) => {
|
|
83
163
|
const confirmations = [];
|
|
84
164
|
const currentDate = new Date().toISOString();
|
|
85
165
|
|
|
86
|
-
// Map frontend types to backend types according to API expectations
|
|
87
166
|
const dataTypeMapping = {
|
|
88
|
-
'basic': 'Medium',
|
|
89
|
-
'personality': 'Large',
|
|
90
|
-
'preferences': 'Traits'
|
|
167
|
+
'basic': 'Medium',
|
|
168
|
+
'personality': 'Large',
|
|
169
|
+
'preferences': 'Traits'
|
|
91
170
|
};
|
|
92
171
|
|
|
93
172
|
approvedData.forEach(dataType => {
|
|
@@ -102,46 +181,43 @@ export default function DataRequest({
|
|
|
102
181
|
return confirmations;
|
|
103
182
|
};
|
|
104
183
|
|
|
105
|
-
// Determine API endpoint based on test mode
|
|
106
184
|
const apiEndpoint = testMode
|
|
107
185
|
? 'https://api2.onairos.uk/inferenceTest'
|
|
108
186
|
: 'https://api2.onairos.uk/getAPIurlMobile';
|
|
109
187
|
|
|
110
|
-
// Prepare the base result
|
|
111
188
|
const baseResult = {
|
|
112
189
|
userHash,
|
|
113
190
|
appName,
|
|
114
191
|
approvedData,
|
|
192
|
+
selectedConnectors,
|
|
115
193
|
apiUrl: apiEndpoint,
|
|
116
194
|
testMode,
|
|
117
195
|
timestamp: new Date().toISOString()
|
|
118
196
|
};
|
|
119
197
|
|
|
120
198
|
if (autoFetch) {
|
|
121
|
-
// Auto mode true: make API request and return results
|
|
122
199
|
try {
|
|
123
200
|
const confirmations = mapDataTypesToConfirmations(approvedData);
|
|
124
201
|
|
|
125
|
-
// Format request according to backend expectations
|
|
126
202
|
const requestBody = testMode ? {
|
|
127
|
-
// Test mode: simple format for testing
|
|
128
203
|
approvedData,
|
|
204
|
+
selectedConnectors,
|
|
129
205
|
userEmail,
|
|
130
206
|
appName,
|
|
131
207
|
testMode,
|
|
132
208
|
timestamp: new Date().toISOString()
|
|
133
209
|
} : {
|
|
134
|
-
// Live mode: proper Info format for backend
|
|
135
210
|
Info: {
|
|
136
211
|
storage: "local",
|
|
137
212
|
appId: appName,
|
|
138
213
|
confirmations: confirmations,
|
|
139
|
-
|
|
214
|
+
connectors: selectedConnectors,
|
|
215
|
+
EncryptedUserPin: "pending_pin_integration",
|
|
140
216
|
account: userEmail,
|
|
141
217
|
proofMode: false,
|
|
142
218
|
Domain: window.location.hostname,
|
|
143
|
-
web3Type: "standard",
|
|
144
|
-
OthentSub: null
|
|
219
|
+
web3Type: "standard",
|
|
220
|
+
OthentSub: null
|
|
145
221
|
}
|
|
146
222
|
};
|
|
147
223
|
|
|
@@ -149,52 +225,55 @@ export default function DataRequest({
|
|
|
149
225
|
method: 'POST',
|
|
150
226
|
headers: {
|
|
151
227
|
'Content-Type': 'application/json',
|
|
228
|
+
'x-api-key': 'onairos_web_sdk_live_key_2024'
|
|
152
229
|
},
|
|
153
230
|
body: JSON.stringify(requestBody)
|
|
154
231
|
});
|
|
155
232
|
|
|
156
233
|
if (!response.ok) {
|
|
157
|
-
throw new Error(`API
|
|
234
|
+
throw new Error(`API request failed: ${response.status}`);
|
|
158
235
|
}
|
|
159
236
|
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (testMode && apiData) {
|
|
165
|
-
formattedData = {
|
|
166
|
-
InferenceResult: {
|
|
167
|
-
output: apiData.croppedInference || apiData.output || apiData.inference,
|
|
168
|
-
traits: apiData.traitResult || apiData.traits || apiData.personalityData
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
setApiResponse(formattedData);
|
|
174
|
-
return {
|
|
237
|
+
const data = await response.json();
|
|
238
|
+
setApiResponse(data);
|
|
239
|
+
|
|
240
|
+
const result = {
|
|
175
241
|
...baseResult,
|
|
176
|
-
apiResponse:
|
|
242
|
+
apiResponse: data,
|
|
177
243
|
success: true
|
|
178
244
|
};
|
|
245
|
+
|
|
246
|
+
setTimeout(() => {
|
|
247
|
+
onComplete(result);
|
|
248
|
+
}, 1500);
|
|
249
|
+
|
|
250
|
+
return result;
|
|
251
|
+
|
|
179
252
|
} catch (error) {
|
|
180
|
-
|
|
181
|
-
|
|
253
|
+
console.error('API request failed:', error);
|
|
254
|
+
setApiError(`API request failed: ${error.message}`);
|
|
255
|
+
|
|
256
|
+
const result = {
|
|
182
257
|
...baseResult,
|
|
183
|
-
|
|
258
|
+
error: error.message,
|
|
184
259
|
success: false
|
|
185
260
|
};
|
|
261
|
+
|
|
262
|
+
setTimeout(() => {
|
|
263
|
+
onComplete(result);
|
|
264
|
+
}, 2000);
|
|
265
|
+
|
|
266
|
+
return result;
|
|
186
267
|
}
|
|
187
268
|
} else {
|
|
188
|
-
|
|
189
|
-
return
|
|
190
|
-
...baseResult,
|
|
191
|
-
success: true,
|
|
192
|
-
message: 'Data request approved. Use the provided API URL to fetch user data.'
|
|
193
|
-
};
|
|
269
|
+
onComplete(baseResult);
|
|
270
|
+
return baseResult;
|
|
194
271
|
}
|
|
272
|
+
|
|
195
273
|
} catch (error) {
|
|
196
|
-
|
|
197
|
-
|
|
274
|
+
console.error('Data processing failed:', error);
|
|
275
|
+
setApiError(`Processing failed: ${error.message}`);
|
|
276
|
+
throw error;
|
|
198
277
|
} finally {
|
|
199
278
|
setIsLoadingApi(false);
|
|
200
279
|
}
|
|
@@ -218,6 +297,8 @@ export default function DataRequest({
|
|
|
218
297
|
};
|
|
219
298
|
|
|
220
299
|
const selectedCount = Object.values(selectedData).filter(Boolean).length;
|
|
300
|
+
const selectedConnectorCount = Object.values(connectorStates).filter(state => state.selected).length;
|
|
301
|
+
const connectedCount = Object.values(connectorStates).filter(state => state.connected).length;
|
|
221
302
|
|
|
222
303
|
return (
|
|
223
304
|
<div className="w-full max-w-md mx-auto bg-white rounded-lg shadow-xl overflow-hidden" style={{ maxHeight: '90vh', height: 'auto' }}>
|
|
@@ -225,7 +306,7 @@ export default function DataRequest({
|
|
|
225
306
|
<div className="text-center mb-4 sm:mb-6">
|
|
226
307
|
<h2 className="text-lg sm:text-xl font-bold text-gray-900 mb-2">Data Request</h2>
|
|
227
308
|
<p className="text-gray-600 text-xs sm:text-sm">
|
|
228
|
-
Select the data types
|
|
309
|
+
Select the data types and connections to share with <span className="font-medium">{appName}</span>
|
|
229
310
|
</p>
|
|
230
311
|
</div>
|
|
231
312
|
|
|
@@ -237,65 +318,125 @@ export default function DataRequest({
|
|
|
237
318
|
</div>
|
|
238
319
|
|
|
239
320
|
{/* Data Types Selection */}
|
|
240
|
-
<div className="
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
321
|
+
<div className="mb-6">
|
|
322
|
+
<h3 className="text-md font-semibold text-gray-900 mb-3">Data Types</h3>
|
|
323
|
+
<div className="space-y-2 sm:space-y-3">
|
|
324
|
+
{dataTypes.map((dataType) => {
|
|
325
|
+
const isSelected = selectedData[dataType.id] || false;
|
|
326
|
+
const isRequired = dataType.required;
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<div
|
|
330
|
+
key={dataType.id}
|
|
331
|
+
className={`flex items-center justify-between p-3 sm:p-4 border rounded-lg transition-colors ${
|
|
332
|
+
isRequired
|
|
333
|
+
? 'bg-gray-100 border-gray-300 cursor-not-allowed'
|
|
334
|
+
: 'hover:bg-gray-50 cursor-pointer border-gray-200'
|
|
335
|
+
}`}
|
|
336
|
+
onClick={() => handleRowClick(dataType.id)}
|
|
337
|
+
>
|
|
338
|
+
<div className="flex items-center space-x-3">
|
|
339
|
+
<div className="text-xl sm:text-2xl">
|
|
340
|
+
{dataType.icon}
|
|
341
|
+
</div>
|
|
342
|
+
<div>
|
|
343
|
+
<h4 className="font-medium text-gray-900 text-sm sm:text-base">
|
|
344
|
+
<Tooltip content={dataType.tooltip} privacyLink={dataType.privacyLink}>
|
|
345
|
+
{dataType.name}
|
|
346
|
+
</Tooltip>
|
|
347
|
+
{isRequired && <span className="text-gray-500 ml-1 text-xs">(Required)</span>}
|
|
348
|
+
</h4>
|
|
349
|
+
<p className="text-xs sm:text-sm text-gray-500">{dataType.description}</p>
|
|
350
|
+
</div>
|
|
265
351
|
</div>
|
|
352
|
+
|
|
353
|
+
{/* Toggle Switch or Required Badge */}
|
|
354
|
+
{isRequired ? (
|
|
355
|
+
<div className="px-2 py-1 bg-gray-400 text-white text-xs rounded-full">
|
|
356
|
+
Required
|
|
357
|
+
</div>
|
|
358
|
+
) : (
|
|
359
|
+
<button
|
|
360
|
+
onClick={(e) => {
|
|
361
|
+
e.stopPropagation();
|
|
362
|
+
handleDataToggle(dataType.id);
|
|
363
|
+
}}
|
|
364
|
+
className={`relative inline-flex h-5 sm:h-6 w-9 sm:w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
|
|
365
|
+
isSelected ? 'bg-blue-600' : 'bg-gray-200'
|
|
366
|
+
}`}
|
|
367
|
+
>
|
|
368
|
+
<span
|
|
369
|
+
className={`inline-block h-3 sm:h-4 w-3 sm:w-4 transform rounded-full bg-white transition-transform ${
|
|
370
|
+
isSelected ? 'translate-x-5 sm:translate-x-6' : 'translate-x-1'
|
|
371
|
+
}`}
|
|
372
|
+
/>
|
|
373
|
+
</button>
|
|
374
|
+
)}
|
|
266
375
|
</div>
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
376
|
+
);
|
|
377
|
+
})}
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
|
|
381
|
+
{/* Platform Connectors */}
|
|
382
|
+
<div className="mb-6">
|
|
383
|
+
<h3 className="text-md font-semibold text-gray-900 mb-3">Data Connectors</h3>
|
|
384
|
+
<p className="text-xs text-gray-600 mb-3">Select connected platforms to include their data</p>
|
|
385
|
+
|
|
386
|
+
<div className="grid grid-cols-2 gap-2">
|
|
387
|
+
{platformConnectors.map((platform) => {
|
|
388
|
+
const connectorState = connectorStates[platform.name];
|
|
389
|
+
const isConnected = connectorState?.connected || false;
|
|
390
|
+
const isSelected = connectorState?.selected || false;
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
<div
|
|
394
|
+
key={platform.name}
|
|
395
|
+
className={`p-2 border rounded-lg transition-all duration-200 cursor-pointer ${
|
|
396
|
+
!isConnected ? 'opacity-50 cursor-not-allowed border-gray-200 bg-gray-50' :
|
|
397
|
+
isSelected ? 'border-green-400 bg-green-50' :
|
|
398
|
+
'border-gray-200 bg-white hover:border-gray-300'
|
|
399
|
+
}`}
|
|
400
|
+
onClick={() => isConnected && handleConnectorToggle(platform.name)}
|
|
401
|
+
>
|
|
402
|
+
<div className={`w-6 h-6 rounded ${platform.color} flex items-center justify-center text-white text-sm mb-2 mx-auto relative`}>
|
|
403
|
+
{platform.icon}
|
|
404
|
+
|
|
405
|
+
{/* Connection Status Indicator */}
|
|
406
|
+
{isConnected && isSelected && (
|
|
407
|
+
<div className="absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full flex items-center justify-center">
|
|
408
|
+
<svg className="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
409
|
+
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
|
410
|
+
</svg>
|
|
411
|
+
</div>
|
|
412
|
+
)}
|
|
272
413
|
</div>
|
|
273
|
-
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
>
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
414
|
+
|
|
415
|
+
<div className="text-center">
|
|
416
|
+
<h4 className="font-medium text-gray-900 text-xs">{platform.name}</h4>
|
|
417
|
+
<p className={`text-xs ${
|
|
418
|
+
isConnected ? (isSelected ? 'text-green-600' : 'text-blue-600') : 'text-gray-400'
|
|
419
|
+
}`}>
|
|
420
|
+
{!isConnected ? 'Not connected' :
|
|
421
|
+
isSelected ? 'Selected' : 'Tap to select'}
|
|
422
|
+
</p>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
);
|
|
426
|
+
})}
|
|
427
|
+
</div>
|
|
428
|
+
|
|
429
|
+
{connectedCount === 0 && (
|
|
430
|
+
<p className="text-xs text-yellow-600 mt-2 text-center">
|
|
431
|
+
⚠️ No platforms connected. Connect platforms in onboarding to select data sources.
|
|
432
|
+
</p>
|
|
433
|
+
)}
|
|
293
434
|
</div>
|
|
294
435
|
|
|
295
436
|
{/* Selection Summary */}
|
|
296
437
|
<div className="mb-3 sm:mb-4 p-2 sm:p-3 bg-green-50 border border-green-200 rounded-lg">
|
|
297
438
|
<p className="text-green-800 text-xs sm:text-sm">
|
|
298
|
-
✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''}
|
|
439
|
+
✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} and {selectedConnectorCount} connector{selectedConnectorCount > 1 ? 's' : ''} selected
|
|
299
440
|
</p>
|
|
300
441
|
</div>
|
|
301
442
|
|
|
@@ -323,12 +464,12 @@ export default function DataRequest({
|
|
|
323
464
|
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
|
324
465
|
}`}
|
|
325
466
|
>
|
|
326
|
-
{isSubmitting ? 'Processing...' : `Share
|
|
467
|
+
{isSubmitting ? 'Processing...' : `Share Selected Data`}
|
|
327
468
|
</button>
|
|
328
469
|
|
|
329
470
|
<button
|
|
330
471
|
type="button"
|
|
331
|
-
onClick={() => onComplete({ selectedData: {}, cancelled: true })}
|
|
472
|
+
onClick={() => onComplete({ selectedData: {}, selectedConnectors: [], cancelled: true })}
|
|
332
473
|
className="w-full py-2 text-gray-500 hover:text-gray-700 text-xs sm:text-sm"
|
|
333
474
|
>
|
|
334
475
|
Cancel
|
package/src/onairosButton.jsx
CHANGED
|
@@ -25,7 +25,7 @@ export function OnairosButton({
|
|
|
25
25
|
}) {
|
|
26
26
|
|
|
27
27
|
const [showOverlay, setShowOverlay] = useState(false);
|
|
28
|
-
const [currentFlow, setCurrentFlow] = useState('email'); // 'email' | 'onboarding' | 'pin' | '
|
|
28
|
+
const [currentFlow, setCurrentFlow] = useState('email'); // 'email' | 'onboarding' | 'pin' | 'dataRequest' (training is within onboarding)
|
|
29
29
|
const [userData, setUserData] = useState(null);
|
|
30
30
|
const [error, setError] = useState(null);
|
|
31
31
|
|
|
@@ -100,17 +100,11 @@ export function OnairosButton({
|
|
|
100
100
|
|
|
101
101
|
// Flow decision logic
|
|
102
102
|
if (isNewUser) {
|
|
103
|
-
console.log('🚀 New user detected → Starting onboarding flow');
|
|
103
|
+
console.log('🚀 New user detected → Starting onboarding flow (includes training)');
|
|
104
104
|
setCurrentFlow('onboarding');
|
|
105
105
|
} else {
|
|
106
|
-
console.log('👋
|
|
107
|
-
|
|
108
|
-
console.log('📚 Training enabled → Going to training flow');
|
|
109
|
-
setCurrentFlow('training');
|
|
110
|
-
} else {
|
|
111
|
-
console.log('📋 Training disabled → Going to data request flow');
|
|
112
|
-
setCurrentFlow('dataRequest');
|
|
113
|
-
}
|
|
106
|
+
console.log('👋 Existing user detected → Going directly to data request');
|
|
107
|
+
setCurrentFlow('dataRequest');
|
|
114
108
|
}
|
|
115
109
|
};
|
|
116
110
|
|
|
@@ -230,6 +224,7 @@ export function OnairosButton({
|
|
|
230
224
|
autoFetch={autoFetch}
|
|
231
225
|
testMode={testMode}
|
|
232
226
|
appIcon={appIcon}
|
|
227
|
+
connectedAccounts={userData?.connectedAccounts || {}}
|
|
233
228
|
/>
|
|
234
229
|
);
|
|
235
230
|
|