onairos 2.1.13 → 2.2.1
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 +189 -110
- package/src/onairosButton.jsx +1 -0
- package/src/overlay/CheckBox.jsx +83 -23
- package/src/overlay/IndividualConnection.js +145 -15
- package/src/overlay/IndividualConnectionNew.jsx +101 -22
- package/test-enhanced-data-request.html +271 -0
|
@@ -1,66 +1,136 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// Remove the platform connectors - these should be handled in onboarding
|
|
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 stated preferences and interests from connected platforms. Helps customize your 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
|
+
// Remove connector states - not needed for data request
|
|
90
|
+
// const [connectorStates, setConnectorStates] = useState({});
|
|
39
91
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
40
92
|
const [isLoadingApi, setIsLoadingApi] = useState(false);
|
|
41
93
|
const [apiResponse, setApiResponse] = useState(null);
|
|
42
94
|
const [apiError, setApiError] = useState(null);
|
|
43
95
|
|
|
96
|
+
// Remove connector initialization - not needed
|
|
97
|
+
// useEffect(() => {
|
|
98
|
+
// const initialStates = {};
|
|
99
|
+
// platformConnectors.forEach(platform => {
|
|
100
|
+
// initialStates[platform.name] = {
|
|
101
|
+
// connected: connectedAccounts[platform.name] || false,
|
|
102
|
+
// selected: false
|
|
103
|
+
// };
|
|
104
|
+
// });
|
|
105
|
+
// setConnectorStates(initialStates);
|
|
106
|
+
// }, [connectedAccounts]);
|
|
107
|
+
|
|
44
108
|
const handleDataToggle = (dataId) => {
|
|
109
|
+
// Don't allow toggling required items
|
|
110
|
+
const dataType = dataTypes.find(dt => dt.id === dataId);
|
|
111
|
+
if (dataType?.required) return;
|
|
112
|
+
|
|
45
113
|
setSelectedData(prev => ({
|
|
46
114
|
...prev,
|
|
47
115
|
[dataId]: !prev[dataId]
|
|
48
116
|
}));
|
|
49
117
|
};
|
|
50
118
|
|
|
119
|
+
// handleConnectorToggle removed - connectors are handled in onboarding
|
|
120
|
+
|
|
51
121
|
const handleRowClick = (dataId) => {
|
|
52
|
-
|
|
122
|
+
const dataType = dataTypes.find(dt => dt.id === dataId);
|
|
123
|
+
if (dataType?.required) return; // Don't allow clicking required items
|
|
53
124
|
handleDataToggle(dataId);
|
|
54
125
|
};
|
|
55
126
|
|
|
56
127
|
const generateUserHash = (email) => {
|
|
57
|
-
// Simple hash function for user identification
|
|
58
128
|
let hash = 0;
|
|
59
129
|
const str = email + Date.now().toString();
|
|
60
130
|
for (let i = 0; i < str.length; i++) {
|
|
61
131
|
const char = str.charCodeAt(i);
|
|
62
132
|
hash = ((hash << 5) - hash) + char;
|
|
63
|
-
hash = hash & hash;
|
|
133
|
+
hash = hash & hash;
|
|
64
134
|
}
|
|
65
135
|
return `user_${Math.abs(hash).toString(36)}`;
|
|
66
136
|
};
|
|
@@ -70,7 +140,6 @@ export default function DataRequest({
|
|
|
70
140
|
setApiError(null);
|
|
71
141
|
|
|
72
142
|
try {
|
|
73
|
-
// Create a unique user hash for this request
|
|
74
143
|
const userHash = generateUserHash(userEmail);
|
|
75
144
|
|
|
76
145
|
// Get selected data types
|
|
@@ -78,16 +147,19 @@ export default function DataRequest({
|
|
|
78
147
|
.filter(([key, value]) => value)
|
|
79
148
|
.map(([key]) => key);
|
|
80
149
|
|
|
81
|
-
//
|
|
150
|
+
// Get selected connectors
|
|
151
|
+
// const selectedConnectors = Object.entries(connectorStates)
|
|
152
|
+
// .filter(([platform, state]) => state.selected && state.connected)
|
|
153
|
+
// .map(([platform]) => platform);
|
|
154
|
+
|
|
82
155
|
const mapDataTypesToConfirmations = (approvedData) => {
|
|
83
156
|
const confirmations = [];
|
|
84
157
|
const currentDate = new Date().toISOString();
|
|
85
158
|
|
|
86
|
-
// Map frontend types to backend types according to API expectations
|
|
87
159
|
const dataTypeMapping = {
|
|
88
|
-
'basic': 'Medium',
|
|
89
|
-
'personality': 'Large',
|
|
90
|
-
'preferences': 'Traits'
|
|
160
|
+
'basic': 'Medium',
|
|
161
|
+
'personality': 'Large',
|
|
162
|
+
'preferences': 'Traits'
|
|
91
163
|
};
|
|
92
164
|
|
|
93
165
|
approvedData.forEach(dataType => {
|
|
@@ -102,46 +174,43 @@ export default function DataRequest({
|
|
|
102
174
|
return confirmations;
|
|
103
175
|
};
|
|
104
176
|
|
|
105
|
-
// Determine API endpoint based on test mode
|
|
106
177
|
const apiEndpoint = testMode
|
|
107
178
|
? 'https://api2.onairos.uk/inferenceTest'
|
|
108
179
|
: 'https://api2.onairos.uk/getAPIurlMobile';
|
|
109
180
|
|
|
110
|
-
// Prepare the base result
|
|
111
181
|
const baseResult = {
|
|
112
182
|
userHash,
|
|
113
183
|
appName,
|
|
114
184
|
approvedData,
|
|
185
|
+
// selectedConnectors, // Removed as connectors are handled in onboarding
|
|
115
186
|
apiUrl: apiEndpoint,
|
|
116
187
|
testMode,
|
|
117
188
|
timestamp: new Date().toISOString()
|
|
118
189
|
};
|
|
119
190
|
|
|
120
191
|
if (autoFetch) {
|
|
121
|
-
// Auto mode true: make API request and return results
|
|
122
192
|
try {
|
|
123
193
|
const confirmations = mapDataTypesToConfirmations(approvedData);
|
|
124
194
|
|
|
125
|
-
// Format request according to backend expectations
|
|
126
195
|
const requestBody = testMode ? {
|
|
127
|
-
// Test mode: simple format for testing
|
|
128
196
|
approvedData,
|
|
197
|
+
// selectedConnectors, // Removed as connectors are handled in onboarding
|
|
129
198
|
userEmail,
|
|
130
199
|
appName,
|
|
131
200
|
testMode,
|
|
132
201
|
timestamp: new Date().toISOString()
|
|
133
202
|
} : {
|
|
134
|
-
// Live mode: proper Info format for backend
|
|
135
203
|
Info: {
|
|
136
204
|
storage: "local",
|
|
137
205
|
appId: appName,
|
|
138
206
|
confirmations: confirmations,
|
|
139
|
-
|
|
207
|
+
// connectors: selectedConnectors, // Removed as connectors are handled in onboarding
|
|
208
|
+
EncryptedUserPin: "pending_pin_integration",
|
|
140
209
|
account: userEmail,
|
|
141
210
|
proofMode: false,
|
|
142
211
|
Domain: window.location.hostname,
|
|
143
|
-
web3Type: "standard",
|
|
144
|
-
OthentSub: null
|
|
212
|
+
web3Type: "standard",
|
|
213
|
+
OthentSub: null
|
|
145
214
|
}
|
|
146
215
|
};
|
|
147
216
|
|
|
@@ -149,52 +218,55 @@ export default function DataRequest({
|
|
|
149
218
|
method: 'POST',
|
|
150
219
|
headers: {
|
|
151
220
|
'Content-Type': 'application/json',
|
|
221
|
+
'x-api-key': 'onairos_web_sdk_live_key_2024'
|
|
152
222
|
},
|
|
153
223
|
body: JSON.stringify(requestBody)
|
|
154
224
|
});
|
|
155
225
|
|
|
156
226
|
if (!response.ok) {
|
|
157
|
-
throw new Error(`API
|
|
227
|
+
throw new Error(`API request failed: ${response.status}`);
|
|
158
228
|
}
|
|
159
229
|
|
|
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 {
|
|
230
|
+
const data = await response.json();
|
|
231
|
+
setApiResponse(data);
|
|
232
|
+
|
|
233
|
+
const result = {
|
|
175
234
|
...baseResult,
|
|
176
|
-
apiResponse:
|
|
235
|
+
apiResponse: data,
|
|
177
236
|
success: true
|
|
178
237
|
};
|
|
238
|
+
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
onComplete(result);
|
|
241
|
+
}, 1500);
|
|
242
|
+
|
|
243
|
+
return result;
|
|
244
|
+
|
|
179
245
|
} catch (error) {
|
|
180
|
-
|
|
181
|
-
|
|
246
|
+
console.error('API request failed:', error);
|
|
247
|
+
setApiError(`API request failed: ${error.message}`);
|
|
248
|
+
|
|
249
|
+
const result = {
|
|
182
250
|
...baseResult,
|
|
183
|
-
|
|
251
|
+
error: error.message,
|
|
184
252
|
success: false
|
|
185
253
|
};
|
|
254
|
+
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
onComplete(result);
|
|
257
|
+
}, 2000);
|
|
258
|
+
|
|
259
|
+
return result;
|
|
186
260
|
}
|
|
187
261
|
} else {
|
|
188
|
-
|
|
189
|
-
return
|
|
190
|
-
...baseResult,
|
|
191
|
-
success: true,
|
|
192
|
-
message: 'Data request approved. Use the provided API URL to fetch user data.'
|
|
193
|
-
};
|
|
262
|
+
onComplete(baseResult);
|
|
263
|
+
return baseResult;
|
|
194
264
|
}
|
|
265
|
+
|
|
195
266
|
} catch (error) {
|
|
196
|
-
|
|
197
|
-
|
|
267
|
+
console.error('Data processing failed:', error);
|
|
268
|
+
setApiError(`Processing failed: ${error.message}`);
|
|
269
|
+
throw error;
|
|
198
270
|
} finally {
|
|
199
271
|
setIsLoadingApi(false);
|
|
200
272
|
}
|
|
@@ -218,6 +290,8 @@ export default function DataRequest({
|
|
|
218
290
|
};
|
|
219
291
|
|
|
220
292
|
const selectedCount = Object.values(selectedData).filter(Boolean).length;
|
|
293
|
+
// const selectedConnectorCount = Object.values(connectorStates).filter(state => state.selected).length; // Removed as connectors are handled in onboarding
|
|
294
|
+
// const connectedCount = Object.values(connectorStates).filter(state => state.connected).length; // Removed as connectors are handled in onboarding
|
|
221
295
|
|
|
222
296
|
return (
|
|
223
297
|
<div className="w-full max-w-md mx-auto bg-white rounded-lg shadow-xl overflow-hidden" style={{ maxHeight: '90vh', height: 'auto' }}>
|
|
@@ -225,7 +299,7 @@ export default function DataRequest({
|
|
|
225
299
|
<div className="text-center mb-4 sm:mb-6">
|
|
226
300
|
<h2 className="text-lg sm:text-xl font-bold text-gray-900 mb-2">Data Request</h2>
|
|
227
301
|
<p className="text-gray-600 text-xs sm:text-sm">
|
|
228
|
-
Select the data types
|
|
302
|
+
Select the data types and connections to share with <span className="font-medium">{appName}</span>
|
|
229
303
|
</p>
|
|
230
304
|
</div>
|
|
231
305
|
|
|
@@ -237,65 +311,70 @@ export default function DataRequest({
|
|
|
237
311
|
</div>
|
|
238
312
|
|
|
239
313
|
{/* Data Types Selection */}
|
|
240
|
-
<div className="
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
314
|
+
<div className="mb-6">
|
|
315
|
+
<h3 className="text-md font-semibold text-gray-900 mb-3">Data Types</h3>
|
|
316
|
+
<div className="space-y-2 sm:space-y-3">
|
|
317
|
+
{dataTypes.map((dataType) => {
|
|
318
|
+
const isSelected = selectedData[dataType.id] || false;
|
|
319
|
+
const isRequired = dataType.required;
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<div
|
|
323
|
+
key={dataType.id}
|
|
324
|
+
className={`flex items-center justify-between p-3 sm:p-4 border rounded-lg transition-colors ${
|
|
325
|
+
isRequired
|
|
326
|
+
? 'bg-gray-100 border-gray-300 cursor-not-allowed'
|
|
327
|
+
: 'hover:bg-gray-50 cursor-pointer border-gray-200'
|
|
328
|
+
}`}
|
|
329
|
+
onClick={() => handleRowClick(dataType.id)}
|
|
330
|
+
>
|
|
331
|
+
<div className="flex items-center space-x-3">
|
|
332
|
+
<div className="text-xl sm:text-2xl">
|
|
333
|
+
{dataType.icon}
|
|
334
|
+
</div>
|
|
335
|
+
<div>
|
|
336
|
+
<h4 className="font-medium text-gray-900 text-sm sm:text-base">
|
|
337
|
+
<Tooltip content={dataType.tooltip} privacyLink={dataType.privacyLink}>
|
|
338
|
+
{dataType.name}
|
|
339
|
+
</Tooltip>
|
|
340
|
+
{isRequired && <span className="text-gray-500 ml-1 text-xs">(Required)</span>}
|
|
341
|
+
</h4>
|
|
342
|
+
<p className="text-xs sm:text-sm text-gray-500">{dataType.description}</p>
|
|
343
|
+
</div>
|
|
258
344
|
</div>
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
) : (
|
|
274
|
-
<button
|
|
275
|
-
onClick={(e) => {
|
|
276
|
-
e.stopPropagation();
|
|
277
|
-
handleDataToggle(dataType.id);
|
|
278
|
-
}}
|
|
279
|
-
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 ${
|
|
280
|
-
isSelected ? 'bg-blue-600' : 'bg-gray-200'
|
|
281
|
-
}`}
|
|
282
|
-
>
|
|
283
|
-
<span
|
|
284
|
-
className={`inline-block h-3 sm:h-4 w-3 sm:w-4 transform rounded-full bg-white transition-transform ${
|
|
285
|
-
isSelected ? 'translate-x-5 sm:translate-x-6' : 'translate-x-1'
|
|
345
|
+
|
|
346
|
+
{/* Toggle Switch or Required Badge */}
|
|
347
|
+
{isRequired ? (
|
|
348
|
+
<div className="px-2 py-1 bg-gray-400 text-white text-xs rounded-full">
|
|
349
|
+
Required
|
|
350
|
+
</div>
|
|
351
|
+
) : (
|
|
352
|
+
<button
|
|
353
|
+
onClick={(e) => {
|
|
354
|
+
e.stopPropagation();
|
|
355
|
+
handleDataToggle(dataType.id);
|
|
356
|
+
}}
|
|
357
|
+
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 ${
|
|
358
|
+
isSelected ? 'bg-blue-600' : 'bg-gray-200'
|
|
286
359
|
}`}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
360
|
+
>
|
|
361
|
+
<span
|
|
362
|
+
className={`inline-block h-3 sm:h-4 w-3 sm:w-4 transform rounded-full bg-white transition-transform ${
|
|
363
|
+
isSelected ? 'translate-x-5 sm:translate-x-6' : 'translate-x-1'
|
|
364
|
+
}`}
|
|
365
|
+
/>
|
|
366
|
+
</button>
|
|
367
|
+
)}
|
|
368
|
+
</div>
|
|
369
|
+
);
|
|
370
|
+
})}
|
|
371
|
+
</div>
|
|
293
372
|
</div>
|
|
294
373
|
|
|
295
374
|
{/* Selection Summary */}
|
|
296
375
|
<div className="mb-3 sm:mb-4 p-2 sm:p-3 bg-green-50 border border-green-200 rounded-lg">
|
|
297
376
|
<p className="text-green-800 text-xs sm:text-sm">
|
|
298
|
-
✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected
|
|
377
|
+
✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected
|
|
299
378
|
</p>
|
|
300
379
|
</div>
|
|
301
380
|
|
|
@@ -323,12 +402,12 @@ export default function DataRequest({
|
|
|
323
402
|
: 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
|
324
403
|
}`}
|
|
325
404
|
>
|
|
326
|
-
{isSubmitting ? 'Processing...' : `Share
|
|
405
|
+
{isSubmitting ? 'Processing...' : `Share Selected Data`}
|
|
327
406
|
</button>
|
|
328
407
|
|
|
329
408
|
<button
|
|
330
409
|
type="button"
|
|
331
|
-
onClick={() => onComplete({ selectedData: {}, cancelled: true })}
|
|
410
|
+
onClick={() => onComplete({ selectedData: {}, selectedConnectors: [], cancelled: true })}
|
|
332
411
|
className="w-full py-2 text-gray-500 hover:text-gray-700 text-xs sm:text-sm"
|
|
333
412
|
>
|
|
334
413
|
Cancel
|
package/src/onairosButton.jsx
CHANGED
package/src/overlay/CheckBox.jsx
CHANGED
|
@@ -8,46 +8,106 @@ function Box({
|
|
|
8
8
|
setSelected,
|
|
9
9
|
number,
|
|
10
10
|
type,
|
|
11
|
-
title
|
|
11
|
+
title
|
|
12
|
+
}) {
|
|
12
13
|
const [isChecked, setIsChecked] = useState(false);
|
|
13
14
|
|
|
14
15
|
const handleCheckboxChange = (newState) => {
|
|
15
16
|
setIsChecked(newState);
|
|
16
|
-
console.log("This data request is active
|
|
17
|
-
console.log("With new Checked state now being:
|
|
17
|
+
console.log("This data request is active:", active);
|
|
18
|
+
console.log("With new Checked state now being:", newState);
|
|
19
|
+
|
|
18
20
|
// Update the counter
|
|
19
21
|
if (newState) {
|
|
20
22
|
changeGranted(1);
|
|
21
23
|
} else {
|
|
22
24
|
changeGranted(-1);
|
|
23
25
|
}
|
|
24
|
-
|
|
26
|
+
|
|
27
|
+
// Call parent callback if provided
|
|
28
|
+
if (onSelectionChange) {
|
|
29
|
+
onSelectionChange(newState);
|
|
30
|
+
}
|
|
25
31
|
};
|
|
26
32
|
|
|
27
|
-
return (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
return (
|
|
34
|
+
<div className="group">
|
|
35
|
+
<div className="relative">
|
|
36
|
+
{/* Custom styled checkbox with clear visual feedback */}
|
|
37
|
+
<label className="flex items-center cursor-pointer">
|
|
38
|
+
<div className="relative">
|
|
39
|
+
<input
|
|
40
|
+
type="checkbox"
|
|
41
|
+
checked={isChecked}
|
|
42
|
+
disabled={!active}
|
|
43
|
+
onChange={(e) => handleCheckboxChange(e.target.checked)}
|
|
44
|
+
className="sr-only" // Hide default checkbox
|
|
45
|
+
/>
|
|
46
|
+
|
|
47
|
+
{/* Custom checkbox appearance */}
|
|
48
|
+
<div className={`
|
|
49
|
+
w-6 h-6 border-2 rounded-md flex items-center justify-center transition-all duration-200
|
|
50
|
+
${!active
|
|
51
|
+
? 'border-gray-300 bg-gray-100 cursor-not-allowed opacity-50'
|
|
52
|
+
: isChecked
|
|
53
|
+
? 'border-green-500 bg-green-500 shadow-md transform scale-105'
|
|
54
|
+
: 'border-gray-400 bg-white hover:border-green-400 hover:bg-green-50'
|
|
55
|
+
}
|
|
56
|
+
`}>
|
|
57
|
+
{/* Checkmark icon */}
|
|
58
|
+
{isChecked && (
|
|
59
|
+
<svg
|
|
60
|
+
className="w-4 h-4 text-white animate-pulse"
|
|
61
|
+
fill="currentColor"
|
|
62
|
+
viewBox="0 0 20 20"
|
|
63
|
+
>
|
|
64
|
+
<path
|
|
65
|
+
fillRule="evenodd"
|
|
66
|
+
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"
|
|
67
|
+
clipRule="evenodd"
|
|
68
|
+
/>
|
|
69
|
+
</svg>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Selection status text */}
|
|
75
|
+
<span className={`ml-3 text-sm font-medium transition-colors duration-200 ${
|
|
76
|
+
!active
|
|
77
|
+
? 'text-gray-400'
|
|
78
|
+
: isChecked
|
|
79
|
+
? 'text-green-600 font-semibold'
|
|
80
|
+
: 'text-gray-700'
|
|
81
|
+
}`}>
|
|
82
|
+
{!active
|
|
83
|
+
? 'Not available'
|
|
84
|
+
: isChecked
|
|
85
|
+
? '✓ Selected'
|
|
86
|
+
: 'Click to select'
|
|
87
|
+
}
|
|
88
|
+
</span>
|
|
89
|
+
</label>
|
|
90
|
+
|
|
91
|
+
{/* Selection indicator badge */}
|
|
92
|
+
{isChecked && active && (
|
|
93
|
+
<div className="absolute -top-2 -right-2 w-4 h-4 bg-green-500 rounded-full flex items-center justify-center animate-bounce">
|
|
94
|
+
<svg className="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
95
|
+
<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" />
|
|
96
|
+
</svg>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
42
100
|
</div>
|
|
43
|
-
|
|
44
|
-
);
|
|
101
|
+
);
|
|
45
102
|
}
|
|
46
103
|
|
|
47
104
|
Box.propTypes = {
|
|
48
105
|
active: PropTypes.bool.isRequired,
|
|
49
|
-
onSelectionChange: PropTypes.func
|
|
50
|
-
|
|
106
|
+
onSelectionChange: PropTypes.func,
|
|
107
|
+
changeGranted: PropTypes.func.isRequired,
|
|
108
|
+
setSelected: PropTypes.func,
|
|
109
|
+
number: PropTypes.number,
|
|
110
|
+
type: PropTypes.string,
|
|
51
111
|
title: PropTypes.string.isRequired
|
|
52
112
|
};
|
|
53
113
|
|