onairos 4.0.21 → 4.0.23

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.
Files changed (50) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +397 -397
  3. package/dist/10.js +1 -1
  4. package/dist/123.js +1 -1
  5. package/dist/129.js +1 -1
  6. package/dist/18.js +1 -1
  7. package/dist/185.js +1 -1
  8. package/dist/22.js +1 -1
  9. package/dist/225.js +1 -1
  10. package/dist/241.js +1 -1
  11. package/dist/31.js +1 -1
  12. package/dist/336.js +1 -1
  13. package/dist/342.js +1 -1
  14. package/dist/342.js.map +1 -1
  15. package/dist/360.js +1 -1
  16. package/dist/421.js +1 -1
  17. package/dist/433.js +1 -1
  18. package/dist/502.js +1 -1
  19. package/dist/540.js +1 -1
  20. package/dist/540.js.map +1 -1
  21. package/dist/575.js +1 -1
  22. package/dist/592.js +1 -1
  23. package/dist/659.js +1 -1
  24. package/dist/672.js +1 -1
  25. package/dist/672.js.map +1 -1
  26. package/dist/845.js +1 -1
  27. package/dist/911.js +1 -1
  28. package/dist/947.js +1 -1
  29. package/dist/961.js +1 -1
  30. package/dist/961.js.map +1 -1
  31. package/dist/991.js +1 -1
  32. package/dist/999.js +1 -1
  33. package/dist/data_request_iframe.html +11 -11
  34. package/dist/data_request_popup.html +320 -320
  35. package/dist/iframe.bundle.js +1 -1
  36. package/dist/iframe.bundle.js.map +1 -1
  37. package/dist/oauth-callback.html +137 -206
  38. package/dist/onairos-laravel.js +1 -1
  39. package/dist/onairos-laravel.js.map +1 -1
  40. package/dist/onairos.bundle.js +1 -1
  41. package/dist/onairos.bundle.js.map +1 -1
  42. package/dist/onairos.esm.js +1 -1
  43. package/dist/onairos.esm.js.map +1 -1
  44. package/dist/src_onairosButton_jsx.js +1 -1
  45. package/dist/static/8c5b220bf6f482881a90.png +1 -1
  46. package/dist/vendors-node_modules_lottie-react_build_index_umd_js-node_modules_lucide-react_dist_esm_icons-20f774.js +1 -1
  47. package/dist/vendors-node_modules_react-dom_index_js.js +1 -1
  48. package/dist/vendors-node_modules_react_index_js.js +1 -1
  49. package/llm.txt +333 -333
  50. package/package.json +171 -167
@@ -1,321 +1,321 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Onairos Data Request</title>
7
- <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
8
- <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
9
- <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
10
- <script src="https://cdn.tailwindcss.com"></script>
11
- <style>
12
- body {
13
- margin: 0;
14
- padding: 0;
15
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
16
- background-color: #f9fafb;
17
- }
18
- .popup-container {
19
- width: 100%;
20
- height: 100vh;
21
- overflow-y: auto;
22
- background-color: white;
23
- }
24
- </style>
25
- </head>
26
- <body>
27
- <div id="root" class="popup-container"></div>
28
-
29
- <script type="text/babel">
30
- const { useState, useEffect } = React;
31
-
32
- const defaultDataTypes = [
33
- { id: 'email', name: 'Email Address', description: 'Your email for account identification', icon: '📧' },
34
- { id: 'profile', name: 'Profile Information', description: 'Basic profile data and preferences', icon: '👤' },
35
- { id: 'social', name: 'Social Connections', description: 'Connected social media accounts', icon: '🌐' },
36
- { id: 'activity', name: 'Activity Data', description: 'Usage patterns and interactions', icon: '📊' },
37
- { id: 'preferences', name: 'User Preferences', description: 'Settings and customization choices', icon: '⚙️' }
38
- ];
39
-
40
- function DataRequestPopup() {
41
- const [selectedData, setSelectedData] = useState({});
42
- const [isSubmitting, setIsSubmitting] = useState(false);
43
- const [isLoadingApi, setIsLoadingApi] = useState(false);
44
- const [apiResponse, setApiResponse] = useState(null);
45
- const [apiError, setApiError] = useState(null);
46
- const [requestData, setRequestData] = useState([]);
47
- const [appName, setAppName] = useState('App');
48
- const [userEmail, setUserEmail] = useState('');
49
- const [autoFetch, setAutoFetch] = useState(true);
50
-
51
- // Listen for data from parent window
52
- useEffect(() => {
53
- const handleMessage = (event) => {
54
- if (event.data && event.data.type === 'dataRequest') {
55
- setRequestData(event.data.requestData || []);
56
- setAppName(event.data.webpageName || 'App');
57
- setUserEmail(event.data.userData?.email || '');
58
- setAutoFetch(event.data.autoFetch !== false);
59
-
60
- // Send acknowledgment
61
- window.parent.postMessage({ action: 'dataReceived' }, '*');
62
- }
63
- };
64
-
65
- window.addEventListener('message', handleMessage);
66
- return () => window.removeEventListener('message', handleMessage);
67
- }, []);
68
-
69
- // Use provided requestData or default data types
70
- const dataTypes = Array.isArray(requestData) && requestData.length > 0
71
- ? requestData.map(id => defaultDataTypes.find(dt => dt.id === id) || { id, name: id, description: `${id} data`, icon: '📋' })
72
- : defaultDataTypes;
73
-
74
- const handleDataToggle = (dataId) => {
75
- setSelectedData(prev => ({
76
- ...prev,
77
- [dataId]: !prev[dataId]
78
- }));
79
- };
80
-
81
- const makeApiCall = async (approvedData) => {
82
- try {
83
- setIsLoadingApi(true);
84
- setApiError(null);
85
-
86
- const response = await fetch('https://api2.onairos.uk/inferenceTest', {
87
- method: 'POST',
88
- headers: {
89
- 'Content-Type': 'application/json',
90
- },
91
- body: JSON.stringify({
92
- approvedData,
93
- userEmail,
94
- appName,
95
- timestamp: new Date().toISOString()
96
- })
97
- });
98
-
99
- if (!response.ok) {
100
- throw new Error(`API call failed with status: ${response.status}`);
101
- }
102
-
103
- const data = await response.json();
104
- setApiResponse(data);
105
- return data;
106
- } catch (error) {
107
- console.error('API call error:', error);
108
- setApiError(error.message);
109
- throw error;
110
- } finally {
111
- setIsLoadingApi(false);
112
- }
113
- };
114
-
115
- const handleApprove = async () => {
116
- if (isSubmitting) return;
117
-
118
- setIsSubmitting(true);
119
-
120
- try {
121
- const approved = Object.entries(selectedData)
122
- .filter(([_, isSelected]) => isSelected)
123
- .map(([dataId]) => dataId);
124
-
125
- if (approved.length === 0) {
126
- alert('Please select at least one data type to continue.');
127
- setIsSubmitting(false);
128
- return;
129
- }
130
-
131
- const baseResult = {
132
- type: 'dataRequestComplete',
133
- source: 'onairosPopup',
134
- approved: approved,
135
- timestamp: new Date().toISOString(),
136
- userEmail: userEmail,
137
- appName: appName
138
- };
139
-
140
- let finalResult = baseResult;
141
-
142
- // If autoFetch is enabled, make API call automatically
143
- if (autoFetch) {
144
- try {
145
- const apiData = await makeApiCall(approved);
146
- finalResult = {
147
- ...baseResult,
148
- apiResponse: apiData,
149
- apiUrl: 'https://api2.onairos.uk/inferenceTest'
150
- };
151
- } catch (apiError) {
152
- finalResult = {
153
- ...baseResult,
154
- apiError: apiError.message,
155
- apiUrl: 'https://api2.onairos.uk/inferenceTest'
156
- };
157
- }
158
- }
159
-
160
- // Send result to parent window
161
- window.parent.postMessage(finalResult, '*');
162
-
163
- // Close popup after a brief delay
164
- setTimeout(() => {
165
- window.close();
166
- }, 1000);
167
-
168
- } catch (error) {
169
- console.error('Error in handleApprove:', error);
170
- setApiError('Failed to process request');
171
- } finally {
172
- setIsSubmitting(false);
173
- }
174
- };
175
-
176
- const handleReject = () => {
177
- window.parent.postMessage({
178
- type: 'dataRequestComplete',
179
- source: 'onairosPopup',
180
- approved: false,
181
- dataTypes: [],
182
- timestamp: new Date().toISOString(),
183
- userEmail: userEmail,
184
- appName: appName
185
- }, '*');
186
-
187
- window.close();
188
- };
189
-
190
- const selectedCount = Object.values(selectedData).filter(Boolean).length;
191
-
192
- return (
193
- <div className="p-6 max-w-md mx-auto">
194
- {/* Header */}
195
- <div className="text-center mb-6">
196
- <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
197
- <svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
198
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
199
- </svg>
200
- </div>
201
- <h2 className="text-xl font-semibold text-gray-900 mb-2">Data Access Request</h2>
202
- <p className="text-gray-600 text-sm">
203
- <span className="font-medium">{appName}</span> would like to access some of your data.
204
- {autoFetch && " Your data will be processed automatically after approval."}
205
- </p>
206
- </div>
207
-
208
- {/* Data Types Selection */}
209
- <div className="space-y-3 mb-6">
210
- {dataTypes.map((dataType) => {
211
- const isSelected = selectedData[dataType.id] || false;
212
-
213
- return (
214
- <div
215
- key={dataType.id}
216
- className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50 transition-colors"
217
- >
218
- <div className="flex items-center space-x-3">
219
- <div className="text-2xl">
220
- {dataType.icon}
221
- </div>
222
- <div>
223
- <h3 className="font-medium text-gray-900">{dataType.name}</h3>
224
- <p className="text-sm text-gray-500">{dataType.description}</p>
225
- </div>
226
- </div>
227
-
228
- {/* Toggle Switch */}
229
- <button
230
- onClick={() => handleDataToggle(dataType.id)}
231
- className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
232
- isSelected ? 'bg-blue-600' : 'bg-gray-200'
233
- }`}
234
- >
235
- <span
236
- className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
237
- isSelected ? 'translate-x-6' : 'translate-x-1'
238
- }`}
239
- />
240
- </button>
241
- </div>
242
- );
243
- })}
244
- </div>
245
-
246
- {/* Selection Summary */}
247
- {selectedCount > 0 && (
248
- <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
249
- <p className="text-green-800 text-sm">
250
- ✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected for sharing
251
- </p>
252
- </div>
253
- )}
254
-
255
- {/* API Status */}
256
- {autoFetch && isLoadingApi && (
257
- <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
258
- <div className="flex items-center space-x-2">
259
- <div className="animate-spin h-4 w-4 border-2 border-blue-600 rounded-full border-t-transparent"></div>
260
- <p className="text-blue-800 text-sm">Processing your data...</p>
261
- </div>
262
- </div>
263
- )}
264
-
265
- {autoFetch && apiResponse && (
266
- <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
267
- <p className="text-green-800 text-sm">
268
- ✅ Data processed successfully
269
- </p>
270
- </div>
271
- )}
272
-
273
- {autoFetch && apiError && (
274
- <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
275
- <p className="text-red-800 text-sm">
276
- ❌ Error processing data: {apiError}
277
- </p>
278
- </div>
279
- )}
280
-
281
- {/* Action Buttons */}
282
- <div className="flex space-x-3">
283
- <button
284
- onClick={handleReject}
285
- disabled={isSubmitting || isLoadingApi}
286
- className="flex-1 px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
287
- >
288
- Deny
289
- </button>
290
- <button
291
- onClick={handleApprove}
292
- disabled={isSubmitting || isLoadingApi || selectedCount === 0}
293
- className="flex-1 px-4 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
294
- >
295
- {isSubmitting || isLoadingApi ? (
296
- <>
297
- <div className="animate-spin h-4 w-4 border-2 border-white rounded-full border-t-transparent mr-2"></div>
298
- {autoFetch ? 'Processing...' : 'Approving...'}
299
- </>
300
- ) : (
301
- autoFetch ? 'Approve & Process' : 'Approve'
302
- )}
303
- </button>
304
- </div>
305
-
306
- {/* Auto-fetch info */}
307
- {autoFetch && (
308
- <div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
309
- <p className="text-blue-800 text-xs">
310
- 🔄 Auto-fetch enabled: Your approved data will be automatically processed and sent to {appName}.
311
- </p>
312
- </div>
313
- )}
314
- </div>
315
- );
316
- }
317
-
318
- ReactDOM.render(<DataRequestPopup />, document.getElementById('root'));
319
- </script>
320
- </body>
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Onairos Data Request</title>
7
+ <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
8
+ <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
9
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <style>
12
+ body {
13
+ margin: 0;
14
+ padding: 0;
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
16
+ background-color: #f9fafb;
17
+ }
18
+ .popup-container {
19
+ width: 100%;
20
+ height: 100vh;
21
+ overflow-y: auto;
22
+ background-color: white;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body>
27
+ <div id="root" class="popup-container"></div>
28
+
29
+ <script type="text/babel">
30
+ const { useState, useEffect } = React;
31
+
32
+ const defaultDataTypes = [
33
+ { id: 'email', name: 'Email Address', description: 'Your email for account identification', icon: '📧' },
34
+ { id: 'profile', name: 'Profile Information', description: 'Basic profile data and preferences', icon: '👤' },
35
+ { id: 'social', name: 'Social Connections', description: 'Connected social media accounts', icon: '🌐' },
36
+ { id: 'activity', name: 'Activity Data', description: 'Usage patterns and interactions', icon: '📊' },
37
+ { id: 'preferences', name: 'User Preferences', description: 'Settings and customization choices', icon: '⚙️' }
38
+ ];
39
+
40
+ function DataRequestPopup() {
41
+ const [selectedData, setSelectedData] = useState({});
42
+ const [isSubmitting, setIsSubmitting] = useState(false);
43
+ const [isLoadingApi, setIsLoadingApi] = useState(false);
44
+ const [apiResponse, setApiResponse] = useState(null);
45
+ const [apiError, setApiError] = useState(null);
46
+ const [requestData, setRequestData] = useState([]);
47
+ const [appName, setAppName] = useState('App');
48
+ const [userEmail, setUserEmail] = useState('');
49
+ const [autoFetch, setAutoFetch] = useState(true);
50
+
51
+ // Listen for data from parent window
52
+ useEffect(() => {
53
+ const handleMessage = (event) => {
54
+ if (event.data && event.data.type === 'dataRequest') {
55
+ setRequestData(event.data.requestData || []);
56
+ setAppName(event.data.webpageName || 'App');
57
+ setUserEmail(event.data.userData?.email || '');
58
+ setAutoFetch(event.data.autoFetch !== false);
59
+
60
+ // Send acknowledgment
61
+ window.parent.postMessage({ action: 'dataReceived' }, '*');
62
+ }
63
+ };
64
+
65
+ window.addEventListener('message', handleMessage);
66
+ return () => window.removeEventListener('message', handleMessage);
67
+ }, []);
68
+
69
+ // Use provided requestData or default data types
70
+ const dataTypes = Array.isArray(requestData) && requestData.length > 0
71
+ ? requestData.map(id => defaultDataTypes.find(dt => dt.id === id) || { id, name: id, description: `${id} data`, icon: '📋' })
72
+ : defaultDataTypes;
73
+
74
+ const handleDataToggle = (dataId) => {
75
+ setSelectedData(prev => ({
76
+ ...prev,
77
+ [dataId]: !prev[dataId]
78
+ }));
79
+ };
80
+
81
+ const makeApiCall = async (approvedData) => {
82
+ try {
83
+ setIsLoadingApi(true);
84
+ setApiError(null);
85
+
86
+ const response = await fetch('https://api2.onairos.uk/inferenceTest', {
87
+ method: 'POST',
88
+ headers: {
89
+ 'Content-Type': 'application/json',
90
+ },
91
+ body: JSON.stringify({
92
+ approvedData,
93
+ userEmail,
94
+ appName,
95
+ timestamp: new Date().toISOString()
96
+ })
97
+ });
98
+
99
+ if (!response.ok) {
100
+ throw new Error(`API call failed with status: ${response.status}`);
101
+ }
102
+
103
+ const data = await response.json();
104
+ setApiResponse(data);
105
+ return data;
106
+ } catch (error) {
107
+ console.error('API call error:', error);
108
+ setApiError(error.message);
109
+ throw error;
110
+ } finally {
111
+ setIsLoadingApi(false);
112
+ }
113
+ };
114
+
115
+ const handleApprove = async () => {
116
+ if (isSubmitting) return;
117
+
118
+ setIsSubmitting(true);
119
+
120
+ try {
121
+ const approved = Object.entries(selectedData)
122
+ .filter(([_, isSelected]) => isSelected)
123
+ .map(([dataId]) => dataId);
124
+
125
+ if (approved.length === 0) {
126
+ alert('Please select at least one data type to continue.');
127
+ setIsSubmitting(false);
128
+ return;
129
+ }
130
+
131
+ const baseResult = {
132
+ type: 'dataRequestComplete',
133
+ source: 'onairosPopup',
134
+ approved: approved,
135
+ timestamp: new Date().toISOString(),
136
+ userEmail: userEmail,
137
+ appName: appName
138
+ };
139
+
140
+ let finalResult = baseResult;
141
+
142
+ // If autoFetch is enabled, make API call automatically
143
+ if (autoFetch) {
144
+ try {
145
+ const apiData = await makeApiCall(approved);
146
+ finalResult = {
147
+ ...baseResult,
148
+ apiResponse: apiData,
149
+ apiUrl: 'https://api2.onairos.uk/inferenceTest'
150
+ };
151
+ } catch (apiError) {
152
+ finalResult = {
153
+ ...baseResult,
154
+ apiError: apiError.message,
155
+ apiUrl: 'https://api2.onairos.uk/inferenceTest'
156
+ };
157
+ }
158
+ }
159
+
160
+ // Send result to parent window
161
+ window.parent.postMessage(finalResult, '*');
162
+
163
+ // Close popup after a brief delay
164
+ setTimeout(() => {
165
+ window.close();
166
+ }, 1000);
167
+
168
+ } catch (error) {
169
+ console.error('Error in handleApprove:', error);
170
+ setApiError('Failed to process request');
171
+ } finally {
172
+ setIsSubmitting(false);
173
+ }
174
+ };
175
+
176
+ const handleReject = () => {
177
+ window.parent.postMessage({
178
+ type: 'dataRequestComplete',
179
+ source: 'onairosPopup',
180
+ approved: false,
181
+ dataTypes: [],
182
+ timestamp: new Date().toISOString(),
183
+ userEmail: userEmail,
184
+ appName: appName
185
+ }, '*');
186
+
187
+ window.close();
188
+ };
189
+
190
+ const selectedCount = Object.values(selectedData).filter(Boolean).length;
191
+
192
+ return (
193
+ <div className="p-6 max-w-md mx-auto">
194
+ {/* Header */}
195
+ <div className="text-center mb-6">
196
+ <div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
197
+ <svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
198
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
199
+ </svg>
200
+ </div>
201
+ <h2 className="text-xl font-semibold text-gray-900 mb-2">Data Access Request</h2>
202
+ <p className="text-gray-600 text-sm">
203
+ <span className="font-medium">{appName}</span> would like to access some of your data.
204
+ {autoFetch && " Your data will be processed automatically after approval."}
205
+ </p>
206
+ </div>
207
+
208
+ {/* Data Types Selection */}
209
+ <div className="space-y-3 mb-6">
210
+ {dataTypes.map((dataType) => {
211
+ const isSelected = selectedData[dataType.id] || false;
212
+
213
+ return (
214
+ <div
215
+ key={dataType.id}
216
+ className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50 transition-colors"
217
+ >
218
+ <div className="flex items-center space-x-3">
219
+ <div className="text-2xl">
220
+ {dataType.icon}
221
+ </div>
222
+ <div>
223
+ <h3 className="font-medium text-gray-900">{dataType.name}</h3>
224
+ <p className="text-sm text-gray-500">{dataType.description}</p>
225
+ </div>
226
+ </div>
227
+
228
+ {/* Toggle Switch */}
229
+ <button
230
+ onClick={() => handleDataToggle(dataType.id)}
231
+ className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
232
+ isSelected ? 'bg-blue-600' : 'bg-gray-200'
233
+ }`}
234
+ >
235
+ <span
236
+ className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
237
+ isSelected ? 'translate-x-6' : 'translate-x-1'
238
+ }`}
239
+ />
240
+ </button>
241
+ </div>
242
+ );
243
+ })}
244
+ </div>
245
+
246
+ {/* Selection Summary */}
247
+ {selectedCount > 0 && (
248
+ <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
249
+ <p className="text-green-800 text-sm">
250
+ ✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected for sharing
251
+ </p>
252
+ </div>
253
+ )}
254
+
255
+ {/* API Status */}
256
+ {autoFetch && isLoadingApi && (
257
+ <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
258
+ <div className="flex items-center space-x-2">
259
+ <div className="animate-spin h-4 w-4 border-2 border-blue-600 rounded-full border-t-transparent"></div>
260
+ <p className="text-blue-800 text-sm">Processing your data...</p>
261
+ </div>
262
+ </div>
263
+ )}
264
+
265
+ {autoFetch && apiResponse && (
266
+ <div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
267
+ <p className="text-green-800 text-sm">
268
+ ✅ Data processed successfully
269
+ </p>
270
+ </div>
271
+ )}
272
+
273
+ {autoFetch && apiError && (
274
+ <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
275
+ <p className="text-red-800 text-sm">
276
+ ❌ Error processing data: {apiError}
277
+ </p>
278
+ </div>
279
+ )}
280
+
281
+ {/* Action Buttons */}
282
+ <div className="flex space-x-3">
283
+ <button
284
+ onClick={handleReject}
285
+ disabled={isSubmitting || isLoadingApi}
286
+ className="flex-1 px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
287
+ >
288
+ Deny
289
+ </button>
290
+ <button
291
+ onClick={handleApprove}
292
+ disabled={isSubmitting || isLoadingApi || selectedCount === 0}
293
+ className="flex-1 px-4 py-2 text-white bg-blue-600 hover:bg-blue-700 rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center"
294
+ >
295
+ {isSubmitting || isLoadingApi ? (
296
+ <>
297
+ <div className="animate-spin h-4 w-4 border-2 border-white rounded-full border-t-transparent mr-2"></div>
298
+ {autoFetch ? 'Processing...' : 'Approving...'}
299
+ </>
300
+ ) : (
301
+ autoFetch ? 'Approve & Process' : 'Approve'
302
+ )}
303
+ </button>
304
+ </div>
305
+
306
+ {/* Auto-fetch info */}
307
+ {autoFetch && (
308
+ <div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
309
+ <p className="text-blue-800 text-xs">
310
+ 🔄 Auto-fetch enabled: Your approved data will be automatically processed and sent to {appName}.
311
+ </p>
312
+ </div>
313
+ )}
314
+ </div>
315
+ );
316
+ }
317
+
318
+ ReactDOM.render(<DataRequestPopup />, document.getElementById('root'));
319
+ </script>
320
+ </body>
321
321
  </html>