onairos 2.2.0 → 2.3.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.
@@ -1,16 +1,16 @@
1
1
  import React, { useState, useEffect } from 'react';
2
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
- ];
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
+ // ];
14
14
 
15
15
  const dataTypes = [
16
16
  {
@@ -37,7 +37,7 @@ const dataTypes = [
37
37
  description: 'User preferences, interests, settings and personal choices',
38
38
  icon: '⚙️',
39
39
  required: false,
40
- tooltip: 'Your likes, interests, content preferences, and behavioral patterns. Helps create a more personalized experience.',
40
+ tooltip: 'Your stated preferences and interests from connected platforms. Helps customize your experience.',
41
41
  privacyLink: 'https://onairos.uk/privacy#preferences-data'
42
42
  }
43
43
  ];
@@ -86,23 +86,24 @@ export default function DataRequest({
86
86
  preferences: false
87
87
  });
88
88
 
89
- const [connectorStates, setConnectorStates] = useState({});
89
+ // Remove connector states - not needed for data request
90
+ // const [connectorStates, setConnectorStates] = useState({});
90
91
  const [isSubmitting, setIsSubmitting] = useState(false);
91
92
  const [isLoadingApi, setIsLoadingApi] = useState(false);
92
93
  const [apiResponse, setApiResponse] = useState(null);
93
94
  const [apiError, setApiError] = useState(null);
94
95
 
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]);
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]);
106
107
 
107
108
  const handleDataToggle = (dataId) => {
108
109
  // Don't allow toggling required items
@@ -115,15 +116,7 @@ export default function DataRequest({
115
116
  }));
116
117
  };
117
118
 
118
- const handleConnectorToggle = (platformName) => {
119
- setConnectorStates(prev => ({
120
- ...prev,
121
- [platformName]: {
122
- ...prev[platformName],
123
- selected: !prev[platformName]?.selected
124
- }
125
- }));
126
- };
119
+ // handleConnectorToggle removed - connectors are handled in onboarding
127
120
 
128
121
  const handleRowClick = (dataId) => {
129
122
  const dataType = dataTypes.find(dt => dt.id === dataId);
@@ -155,9 +148,9 @@ export default function DataRequest({
155
148
  .map(([key]) => key);
156
149
 
157
150
  // Get selected connectors
158
- const selectedConnectors = Object.entries(connectorStates)
159
- .filter(([platform, state]) => state.selected && state.connected)
160
- .map(([platform]) => platform);
151
+ // const selectedConnectors = Object.entries(connectorStates)
152
+ // .filter(([platform, state]) => state.selected && state.connected)
153
+ // .map(([platform]) => platform);
161
154
 
162
155
  const mapDataTypesToConfirmations = (approvedData) => {
163
156
  const confirmations = [];
@@ -189,7 +182,7 @@ export default function DataRequest({
189
182
  userHash,
190
183
  appName,
191
184
  approvedData,
192
- selectedConnectors,
185
+ // selectedConnectors, // Removed as connectors are handled in onboarding
193
186
  apiUrl: apiEndpoint,
194
187
  testMode,
195
188
  timestamp: new Date().toISOString()
@@ -201,7 +194,7 @@ export default function DataRequest({
201
194
 
202
195
  const requestBody = testMode ? {
203
196
  approvedData,
204
- selectedConnectors,
197
+ // selectedConnectors, // Removed as connectors are handled in onboarding
205
198
  userEmail,
206
199
  appName,
207
200
  testMode,
@@ -211,7 +204,7 @@ export default function DataRequest({
211
204
  storage: "local",
212
205
  appId: appName,
213
206
  confirmations: confirmations,
214
- connectors: selectedConnectors,
207
+ // connectors: selectedConnectors, // Removed as connectors are handled in onboarding
215
208
  EncryptedUserPin: "pending_pin_integration",
216
209
  account: userEmail,
217
210
  proofMode: false,
@@ -297,8 +290,8 @@ export default function DataRequest({
297
290
  };
298
291
 
299
292
  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;
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
302
295
 
303
296
  return (
304
297
  <div className="w-full max-w-md mx-auto bg-white rounded-lg shadow-xl overflow-hidden" style={{ maxHeight: '90vh', height: 'auto' }}>
@@ -378,65 +371,10 @@ export default function DataRequest({
378
371
  </div>
379
372
  </div>
380
373
 
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
- )}
413
- </div>
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
- )}
434
- </div>
435
-
436
374
  {/* Selection Summary */}
437
375
  <div className="mb-3 sm:mb-4 p-2 sm:p-3 bg-green-50 border border-green-200 rounded-lg">
438
376
  <p className="text-green-800 text-xs sm:text-sm">
439
- ✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} and {selectedConnectorCount} connector{selectedConnectorCount > 1 ? 's' : ''} selected
377
+ ✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected
440
378
  </p>
441
379
  </div>
442
380
 
@@ -0,0 +1,398 @@
1
+ <template>
2
+ <div class="onairos-vue-wrapper">
3
+ <!-- Loading State -->
4
+ <div v-if="isLoading" class="onairos-loading">
5
+ <div class="onairos-spinner"></div>
6
+ <span>{{ loadingText }}</span>
7
+ </div>
8
+
9
+ <!-- Button State -->
10
+ <button
11
+ v-else
12
+ :class="buttonClasses"
13
+ :disabled="disabled"
14
+ @click="handleClick"
15
+ >
16
+ <slot name="icon" v-if="$slots.icon"></slot>
17
+ <span :class="textClasses">
18
+ <slot>{{ buttonText }}</slot>
19
+ </span>
20
+ </button>
21
+
22
+ <!-- Success Message -->
23
+ <div v-if="showSuccess" class="onairos-success">
24
+ ✅ {{ successMessage }}
25
+ </div>
26
+
27
+ <!-- Error Message -->
28
+ <div v-if="error" class="onairos-error">
29
+ ❌ {{ error }}
30
+ </div>
31
+ </div>
32
+ </template>
33
+
34
+ <script setup>
35
+ import { ref, computed, onMounted, defineEmits, defineProps } from 'vue';
36
+
37
+ // Props
38
+ const props = defineProps({
39
+ requestData: {
40
+ type: [Array, Object],
41
+ default: () => ['email', 'profile']
42
+ },
43
+ webpageName: {
44
+ type: String,
45
+ default: 'Laravel Vue App'
46
+ },
47
+ testMode: {
48
+ type: Boolean,
49
+ default: false
50
+ },
51
+ autoFetch: {
52
+ type: Boolean,
53
+ default: true
54
+ },
55
+ buttonType: {
56
+ type: String,
57
+ default: 'pill',
58
+ validator: (value) => ['pill', 'icon', 'rounded'].includes(value)
59
+ },
60
+ textColor: {
61
+ type: String,
62
+ default: 'white'
63
+ },
64
+ textLayout: {
65
+ type: String,
66
+ default: 'center',
67
+ validator: (value) => ['left', 'center', 'right'].includes(value)
68
+ },
69
+ disabled: {
70
+ type: Boolean,
71
+ default: false
72
+ },
73
+ customClass: {
74
+ type: String,
75
+ default: ''
76
+ },
77
+ loadingText: {
78
+ type: String,
79
+ default: 'Connecting...'
80
+ },
81
+ successMessage: {
82
+ type: String,
83
+ default: 'Successfully connected!'
84
+ },
85
+ size: {
86
+ type: String,
87
+ default: 'medium',
88
+ validator: (value) => ['small', 'medium', 'large'].includes(value)
89
+ }
90
+ });
91
+
92
+ // Emits
93
+ const emit = defineEmits(['complete', 'error', 'loading', 'click']);
94
+
95
+ // Reactive state
96
+ const isLoading = ref(false);
97
+ const showSuccess = ref(false);
98
+ const error = ref(null);
99
+
100
+ // Computed properties
101
+ const buttonText = computed(() => {
102
+ if (isLoading.value) return props.loadingText;
103
+ return 'Connect with Onairos';
104
+ });
105
+
106
+ const buttonClasses = computed(() => {
107
+ const baseClasses = ['onairos-vue-btn'];
108
+
109
+ // Button type
110
+ baseClasses.push(`onairos-btn-${props.buttonType}`);
111
+
112
+ // Size
113
+ baseClasses.push(`onairos-btn-${props.size}`);
114
+
115
+ // States
116
+ if (isLoading.value) baseClasses.push('onairos-btn-loading');
117
+ if (props.disabled) baseClasses.push('onairos-btn-disabled');
118
+
119
+ // Custom class
120
+ if (props.customClass) baseClasses.push(props.customClass);
121
+
122
+ return baseClasses.join(' ');
123
+ });
124
+
125
+ const textClasses = computed(() => {
126
+ return [
127
+ 'onairos-btn-text',
128
+ `onairos-text-${props.textLayout}`,
129
+ `onairos-text-color-${props.textColor}`
130
+ ].join(' ');
131
+ });
132
+
133
+ // Methods
134
+ async function handleClick() {
135
+ if (isLoading.value || props.disabled) return;
136
+
137
+ try {
138
+ isLoading.value = true;
139
+ error.value = null;
140
+ emit('loading', true);
141
+ emit('click');
142
+
143
+ // Simulate connection process
144
+ const result = await initializeOnairosConnection();
145
+
146
+ showSuccess.value = true;
147
+ setTimeout(() => {
148
+ showSuccess.value = false;
149
+ }, 3000);
150
+
151
+ emit('complete', result);
152
+ } catch (err) {
153
+ error.value = err.message || 'Connection failed';
154
+ emit('error', err);
155
+ } finally {
156
+ isLoading.value = false;
157
+ emit('loading', false);
158
+ }
159
+ }
160
+
161
+ async function initializeOnairosConnection() {
162
+ // Check if global Onairos is available
163
+ if (typeof window.createOnairosButton === 'function') {
164
+ return new Promise((resolve, reject) => {
165
+ // Use the global Blade helper
166
+ const config = {
167
+ requestData: props.requestData,
168
+ webpageName: props.webpageName,
169
+ testMode: props.testMode,
170
+ autoFetch: props.autoFetch,
171
+ onComplete: (result) => {
172
+ resolve(result);
173
+ },
174
+ onError: (err) => {
175
+ reject(err);
176
+ }
177
+ };
178
+
179
+ // Trigger the connection flow
180
+ const tempId = `temp-${Date.now()}`;
181
+ const tempElement = document.createElement('div');
182
+ tempElement.id = tempId;
183
+ tempElement.style.display = 'none';
184
+ document.body.appendChild(tempElement);
185
+
186
+ window.createOnairosButton(tempId, config);
187
+
188
+ // Trigger the button click programmatically
189
+ setTimeout(() => {
190
+ const btn = document.querySelector(`#${tempId}-btn`);
191
+ if (btn) btn.click();
192
+ }, 100);
193
+ });
194
+ } else {
195
+ // Fallback: dynamic import of OnairosButton
196
+ const { OnairosButton } = await import('onairos');
197
+
198
+ return new Promise((resolve, reject) => {
199
+ // Create a temporary React component mount
200
+ const config = {
201
+ requestData: props.requestData,
202
+ webpageName: props.webpageName,
203
+ testMode: props.testMode,
204
+ autoFetch: props.autoFetch,
205
+ onComplete: resolve,
206
+ onError: reject
207
+ };
208
+
209
+ // This would need React DOM integration
210
+ // For now, we'll resolve with a mock result
211
+ setTimeout(() => {
212
+ resolve({
213
+ success: true,
214
+ data: 'Mock connection successful',
215
+ timestamp: new Date().toISOString()
216
+ });
217
+ }, 2000);
218
+ });
219
+ }
220
+ }
221
+
222
+ // Lifecycle
223
+ onMounted(() => {
224
+ // Initialize any needed global configurations
225
+ if (typeof window.initializeOnairosForBlade === 'function') {
226
+ window.initializeOnairosForBlade({
227
+ testMode: props.testMode,
228
+ autoDetectMobile: true,
229
+ globalStyles: false // Vue component will handle its own styles
230
+ });
231
+ }
232
+ });
233
+ </script>
234
+
235
+ <style scoped>
236
+ .onairos-vue-wrapper {
237
+ display: inline-block;
238
+ position: relative;
239
+ }
240
+
241
+ .onairos-vue-btn {
242
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
243
+ border: none;
244
+ color: white;
245
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
246
+ font-weight: 600;
247
+ cursor: pointer;
248
+ transition: all 0.3s ease;
249
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
250
+ outline: none;
251
+ }
252
+
253
+ .onairos-vue-btn:hover:not(.onairos-btn-disabled) {
254
+ transform: translateY(-2px);
255
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
256
+ }
257
+
258
+ .onairos-vue-btn:active:not(.onairos-btn-disabled) {
259
+ transform: translateY(0);
260
+ }
261
+
262
+ /* Button Types */
263
+ .onairos-btn-pill {
264
+ border-radius: 25px;
265
+ }
266
+
267
+ .onairos-btn-rounded {
268
+ border-radius: 8px;
269
+ }
270
+
271
+ .onairos-btn-icon {
272
+ border-radius: 50%;
273
+ width: 50px;
274
+ height: 50px;
275
+ padding: 0;
276
+ display: flex;
277
+ align-items: center;
278
+ justify-content: center;
279
+ }
280
+
281
+ /* Sizes */
282
+ .onairos-btn-small {
283
+ padding: 8px 16px;
284
+ font-size: 12px;
285
+ }
286
+
287
+ .onairos-btn-medium {
288
+ padding: 12px 24px;
289
+ font-size: 14px;
290
+ }
291
+
292
+ .onairos-btn-large {
293
+ padding: 16px 32px;
294
+ font-size: 16px;
295
+ }
296
+
297
+ .onairos-btn-icon.onairos-btn-small {
298
+ width: 35px;
299
+ height: 35px;
300
+ }
301
+
302
+ .onairos-btn-icon.onairos-btn-large {
303
+ width: 60px;
304
+ height: 60px;
305
+ }
306
+
307
+ /* Text Layout */
308
+ .onairos-text-left {
309
+ text-align: left;
310
+ }
311
+
312
+ .onairos-text-center {
313
+ text-align: center;
314
+ }
315
+
316
+ .onairos-text-right {
317
+ text-align: right;
318
+ }
319
+
320
+ /* States */
321
+ .onairos-btn-loading {
322
+ opacity: 0.7;
323
+ cursor: not-allowed;
324
+ }
325
+
326
+ .onairos-btn-disabled {
327
+ opacity: 0.5;
328
+ cursor: not-allowed;
329
+ }
330
+
331
+ /* Loading Spinner */
332
+ .onairos-loading {
333
+ display: flex;
334
+ align-items: center;
335
+ gap: 8px;
336
+ padding: 12px 24px;
337
+ background: #f8f9fa;
338
+ border-radius: 25px;
339
+ border: 1px solid #e9ecef;
340
+ }
341
+
342
+ .onairos-spinner {
343
+ width: 16px;
344
+ height: 16px;
345
+ border: 2px solid #e9ecef;
346
+ border-top: 2px solid #667eea;
347
+ border-radius: 50%;
348
+ animation: onairos-spin 1s linear infinite;
349
+ }
350
+
351
+ @keyframes onairos-spin {
352
+ 0% { transform: rotate(0deg); }
353
+ 100% { transform: rotate(360deg); }
354
+ }
355
+
356
+ /* Success/Error Messages */
357
+ .onairos-success,
358
+ .onairos-error {
359
+ margin-top: 8px;
360
+ padding: 8px 12px;
361
+ border-radius: 4px;
362
+ font-size: 12px;
363
+ font-weight: 500;
364
+ }
365
+
366
+ .onairos-success {
367
+ background: #d4edda;
368
+ color: #155724;
369
+ border: 1px solid #c3e6cb;
370
+ }
371
+
372
+ .onairos-error {
373
+ background: #f8d7da;
374
+ color: #721c24;
375
+ border: 1px solid #f5c6cb;
376
+ }
377
+
378
+ /* Mobile Responsive */
379
+ @media (max-width: 768px) {
380
+ .onairos-btn-medium {
381
+ width: 100%;
382
+ padding: 15px 20px;
383
+ font-size: 16px;
384
+ }
385
+
386
+ .onairos-btn-small {
387
+ width: 100%;
388
+ padding: 12px 16px;
389
+ font-size: 14px;
390
+ }
391
+
392
+ .onairos-btn-large {
393
+ width: 100%;
394
+ padding: 18px 24px;
395
+ font-size: 18px;
396
+ }
397
+ }
398
+ </style>