onairos 2.0.1 → 2.0.3
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/iframe.bundle.js +1 -1
- package/dist/iframe.bundle.js.map +1 -1
- package/dist/onairos.bundle.js +1 -7
- package/dist/onairos.bundle.js.map +1 -1
- package/dist/onairos.esm.js +2 -2
- package/dist/onairos.esm.js.map +1 -1
- package/dist/static/ca60d8c79124e56d8c1c.png +0 -0
- package/package.json +7 -2
- package/src/iframe/DataRequestPage.jsx +105 -67
- package/src/iframe/components/Box.jsx +31 -44
- package/src/iframe/components/IndividualConnection.jsx +61 -28
- package/src/iframe/dataRequestHandler.js +7 -1
- package/src/iframe/icons/onairos_logo.png +0 -0
- package/src/onairosButton.jsx +302 -300
- package/webpack.config.js +10 -1
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onairos",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@anthropic-ai/sdk": "^0.24.3",
|
|
6
6
|
"@google/generative-ai": "^0.15.0",
|
|
@@ -95,7 +95,12 @@
|
|
|
95
95
|
"react": "^18.2.0",
|
|
96
96
|
"react-dom": "^18.2.0",
|
|
97
97
|
"react-native": ">=0.69.0",
|
|
98
|
-
"tailwindcss": "^3.3.5"
|
|
98
|
+
"tailwindcss": "^3.3.5",
|
|
99
|
+
"ajv": "^8.12.0",
|
|
100
|
+
"@anthropic-ai/sdk": "^0.24.3",
|
|
101
|
+
"@google/generative-ai": "^0.15.0",
|
|
102
|
+
"@pinecone-database/pinecone": "^2.2.2",
|
|
103
|
+
"openai": "^4.52.7"
|
|
99
104
|
},
|
|
100
105
|
"description": "The Onairos Library is a collection of functions that enable Applications to connect and communicate data with Onairos Identities via User Authorization. Integration for developers is designed to be seamless, simple and effective for all applications",
|
|
101
106
|
"main": "dist/onairos.bundle.js",
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import UniversalOnboarding from '../components/UniversalOnboarding.js';
|
|
3
3
|
import IndividualConnection from './components/IndividualConnection';
|
|
4
|
+
import onairosLogo from './icons/onairos_logo.png';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* DataRequestPage Component
|
|
7
8
|
* Displays different data requests and handles user interactions
|
|
8
9
|
*/
|
|
9
|
-
const DataRequestPage = ({ requestData = {}, dataRequester = 'App', proofMode = false, domain = '' }) => {
|
|
10
|
+
const DataRequestPage = ({ requestData = {}, dataRequester = 'App', proofMode = false, domain = '', appIcon = '' }) => {
|
|
10
11
|
const [loading, setLoading] = useState(true);
|
|
11
12
|
const [activeModels, setActiveModels] = useState([]);
|
|
12
13
|
const [granted, setGranted] = useState(0);
|
|
13
14
|
const [allowSubmit, setAllowSubmit] = useState(false);
|
|
14
|
-
const [
|
|
15
|
-
const [traits, setTraits] = useState(false);
|
|
15
|
+
const [userConnections, setUserConnections] = useState(['instagram', 'youtube', 'email']);
|
|
16
16
|
const [selectedRequests, setSelectedRequests] = useState({});
|
|
17
17
|
const selectedConnections = useRef([]);
|
|
18
18
|
const userSub = useRef(null);
|
|
@@ -36,7 +36,7 @@ const DataRequestPage = ({ requestData = {}, dataRequester = 'App', proofMode =
|
|
|
36
36
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
37
37
|
|
|
38
38
|
// Sample active models - this would come from your backend
|
|
39
|
-
setActiveModels(['
|
|
39
|
+
setActiveModels(['Profile', 'User Memories']);
|
|
40
40
|
setLoading(false);
|
|
41
41
|
} catch (error) {
|
|
42
42
|
console.error('Error loading data:', error);
|
|
@@ -151,90 +151,128 @@ const DataRequestPage = ({ requestData = {}, dataRequester = 'App', proofMode =
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
return (
|
|
154
|
-
<div className="min-h-screen bg-gray-
|
|
154
|
+
<div className="min-h-screen bg-gray-100">
|
|
155
155
|
{loading ? (
|
|
156
156
|
<div className="flex items-center justify-center min-h-screen">
|
|
157
157
|
<div className="animate-spin h-8 w-8 border-2 border-blue-600 rounded-full border-t-transparent"></div>
|
|
158
158
|
</div>
|
|
159
|
-
) :
|
|
159
|
+
) : activeModels.length === 0 ? (
|
|
160
160
|
<UniversalOnboarding appIcon="https://onairos.sirv.com/Images/OnairosBlack.png" appName={dataRequester}/>
|
|
161
161
|
) : (
|
|
162
|
-
<div className="max-w-md mx-auto p-
|
|
163
|
-
<header className="
|
|
164
|
-
<
|
|
162
|
+
<div className="max-w-md mx-auto p-6 space-y-4">
|
|
163
|
+
<header className="bg-white p-6 rounded-xl shadow-md">
|
|
164
|
+
<div className="flex items-center justify-between mb-6">
|
|
165
|
+
<div className="flex items-center space-x-2">
|
|
166
|
+
<img src={onairosLogo} alt="Onairos Logo" className="w-8 h-8" />
|
|
167
|
+
<div className="text-gray-400 mx-2">→</div>
|
|
168
|
+
{appIcon ? (
|
|
169
|
+
<img src={appIcon} alt={`${dataRequester} Logo`} className="w-8 h-8 rounded-full" />
|
|
170
|
+
) : (
|
|
171
|
+
<div className="w-8 h-8 bg-gray-200 rounded-full flex items-center justify-center">
|
|
172
|
+
<span className="text-gray-600 text-xs font-bold">{dataRequester.charAt(0)}</span>
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
</div>
|
|
176
|
+
<h2 className="text-lg font-bold text-gray-800">{dataRequester}</h2>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<h1 className="text-xl font-bold text-gray-800 mb-4">Data Access Request</h1>
|
|
180
|
+
<p className="text-gray-600 mb-6">Select the data you want to share with {dataRequester}</p>
|
|
181
|
+
|
|
165
182
|
<div className="flex items-center justify-between gap-4">
|
|
166
183
|
<button
|
|
167
184
|
onClick={rejectDataRequest}
|
|
168
|
-
className="border w-full border
|
|
185
|
+
className="border w-full border border-gray-300 hover:bg-gray-50 text-gray-700 font-medium py-3 px-4 rounded-lg transition-colors"
|
|
169
186
|
>
|
|
170
|
-
|
|
187
|
+
Decline
|
|
171
188
|
</button>
|
|
172
189
|
<button
|
|
173
190
|
disabled={!allowSubmit}
|
|
174
191
|
onClick={sendDataRequest}
|
|
175
|
-
className="w-full bg-
|
|
192
|
+
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
176
193
|
>
|
|
177
|
-
|
|
194
|
+
Approve {granted > 0 && `(${granted})`}
|
|
178
195
|
</button>
|
|
179
196
|
</div>
|
|
180
197
|
</header>
|
|
181
198
|
|
|
182
|
-
<div className="space-y-
|
|
183
|
-
{
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
199
|
+
<div className="space-y-3">
|
|
200
|
+
{/* Only show Profile and User Memories */}
|
|
201
|
+
{['Profile', 'User Memories'].map((dataType, index) => {
|
|
202
|
+
const key = dataType.toLowerCase().replace(' ', '_');
|
|
203
|
+
const product = {
|
|
204
|
+
type: dataType,
|
|
205
|
+
descriptions: dataType === 'Profile' ?
|
|
206
|
+
'Basic profile information and preferences' :
|
|
207
|
+
'Your personal context and memory data',
|
|
208
|
+
reward: dataType === 'Profile' ?
|
|
209
|
+
'Personalized experience' :
|
|
210
|
+
'Contextual understanding of your preferences'
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<IndividualConnection
|
|
215
|
+
key={key}
|
|
216
|
+
active={true}
|
|
217
|
+
title={product.type}
|
|
218
|
+
id={product}
|
|
219
|
+
number={index}
|
|
220
|
+
descriptions={product.descriptions}
|
|
221
|
+
rewards={product.reward}
|
|
222
|
+
size={key}
|
|
223
|
+
changeGranted={changeGranted}
|
|
224
|
+
onSelectionChange={(isSelected) =>
|
|
225
|
+
handleConnectionSelection(dataRequester, key, index, product.type, product.reward, isSelected)
|
|
226
|
+
}
|
|
227
|
+
/>
|
|
228
|
+
);
|
|
229
|
+
})}
|
|
230
|
+
|
|
231
|
+
{/* User Connections Section */}
|
|
232
|
+
<div className="bg-white p-4 rounded-xl shadow-sm mt-4">
|
|
233
|
+
<h3 className="text-sm font-semibold text-gray-700 mb-3">Your Connected Services</h3>
|
|
234
|
+
<div className="flex items-center space-x-3">
|
|
235
|
+
{userConnections.map((connection, index) => {
|
|
236
|
+
const getConnectionIcon = (type) => {
|
|
237
|
+
switch(type) {
|
|
238
|
+
case 'instagram':
|
|
239
|
+
return (
|
|
240
|
+
<div className="w-10 h-10 rounded-full bg-gradient-to-tr from-purple-500 via-pink-500 to-yellow-500 flex items-center justify-center">
|
|
241
|
+
<span className="text-white text-xs">IG</span>
|
|
242
|
+
</div>
|
|
243
|
+
);
|
|
244
|
+
case 'youtube':
|
|
245
|
+
return (
|
|
246
|
+
<div className="w-10 h-10 rounded-full bg-red-600 flex items-center justify-center">
|
|
247
|
+
<span className="text-white text-xs">YT</span>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
case 'email':
|
|
251
|
+
return (
|
|
252
|
+
<div className="w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center">
|
|
253
|
+
<span className="text-white text-xs">@</span>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
default:
|
|
257
|
+
return (
|
|
258
|
+
<div className="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
|
|
259
|
+
<span className="text-gray-600 text-xs">{type.charAt(0).toUpperCase()}</span>
|
|
260
|
+
</div>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
218
265
|
return (
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
size={key}
|
|
228
|
-
changeGranted={changeGranted}
|
|
229
|
-
onSelectionChange={(isSelected) =>
|
|
230
|
-
handleConnectionSelection(dataRequester, key, index, product.type, product.reward, isSelected)
|
|
231
|
-
}
|
|
232
|
-
/>
|
|
233
|
-
)
|
|
234
|
-
})
|
|
235
|
-
)}
|
|
266
|
+
<div key={index} className="flex flex-col items-center">
|
|
267
|
+
{getConnectionIcon(connection)}
|
|
268
|
+
<span className="text-xs text-gray-600 mt-1">{connection}</span>
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
})}
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
236
274
|
</div>
|
|
237
|
-
</div>
|
|
275
|
+
</div>
|
|
238
276
|
)}
|
|
239
277
|
</div>
|
|
240
278
|
);
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import Sentiment from '../icons/Sentiment.png';
|
|
3
|
-
import Avatar from '../icons/Avatar.png';
|
|
4
|
-
import Avatar2 from '../icons/Avatar2.png';
|
|
5
|
-
import Trait from '../icons/Trait.png';
|
|
6
|
-
import { Checkbox } from "@/components/ui/checkbox";
|
|
7
|
-
import { Label } from "@/components/ui/label";
|
|
8
2
|
|
|
9
3
|
/**
|
|
10
4
|
* Box Component
|
|
11
|
-
* Displays a checkbox item for data access requests
|
|
5
|
+
* Displays a checkbox item for data access requests
|
|
12
6
|
*/
|
|
13
7
|
const Box = (props) => {
|
|
14
|
-
const
|
|
8
|
+
const handleChange = (e) => {
|
|
15
9
|
const checked = e.target.checked;
|
|
16
10
|
console.log(`Checkbox ${props.title} is now: ${checked ? 'checked' : 'unchecked'}`);
|
|
17
11
|
if (checked) {
|
|
@@ -21,46 +15,39 @@ const Box = (props) => {
|
|
|
21
15
|
props.setSelected(false);
|
|
22
16
|
props.changeGranted(-1);
|
|
23
17
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const Insight = (props.title === "Avatar") ? 'Avatar' : (props.title === "Traits") ? 'Personality Traits' : 'Persona';
|
|
27
|
-
|
|
28
|
-
const getIcon = () => {
|
|
29
|
-
switch (props.title) {
|
|
30
|
-
case "Traits":
|
|
31
|
-
return <img src={Trait || "/placeholder.svg"} alt="Traits" className="w-5 h-5" />;
|
|
32
|
-
case "Avatar":
|
|
33
|
-
return <img src={Avatar2 || "/placeholder.svg"} alt="Avatar" className="w-5 h-5" />;
|
|
34
|
-
default:
|
|
35
|
-
return <img src={Sentiment || "/placeholder.svg"} alt="Interest" className="w-5 h-5" />;
|
|
36
|
-
}
|
|
18
|
+
|
|
19
|
+
props.onSelectionChange(checked);
|
|
37
20
|
};
|
|
38
21
|
|
|
39
22
|
return (
|
|
40
|
-
<div className="
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
23
|
+
<div className="relative inline-flex items-center">
|
|
24
|
+
<input
|
|
25
|
+
type="checkbox"
|
|
26
|
+
id={`request-${props.number}`}
|
|
27
|
+
disabled={!props.active}
|
|
28
|
+
onChange={handleChange}
|
|
29
|
+
className={`
|
|
30
|
+
appearance-none w-5 h-5 border rounded
|
|
31
|
+
${!props.active ? 'border-gray-300 bg-gray-100 cursor-not-allowed' : 'border-blue-500 cursor-pointer'}
|
|
32
|
+
checked:bg-blue-600 checked:border-blue-600
|
|
33
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500/30
|
|
34
|
+
transition-colors
|
|
35
|
+
`}
|
|
36
|
+
/>
|
|
37
|
+
<svg
|
|
38
|
+
className="absolute left-0.5 top-0.5 w-4 h-4 text-white pointer-events-none opacity-0 peer-checked:opacity-100"
|
|
39
|
+
fill="none"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
viewBox="0 0 24 24"
|
|
42
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
43
|
+
>
|
|
44
|
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="3" d="M5 13l4 4L19 7"></path>
|
|
45
|
+
</svg>
|
|
46
|
+
|
|
60
47
|
{!props.active && (
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
</
|
|
48
|
+
<span className="ml-2 text-xs text-red-500 font-medium">
|
|
49
|
+
Not available
|
|
50
|
+
</span>
|
|
64
51
|
)}
|
|
65
52
|
</div>
|
|
66
53
|
);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import Box from './Box';
|
|
3
|
-
import { Card } from "@/components/ui/card";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* IndividualConnection Component
|
|
@@ -14,41 +13,75 @@ function IndividualConnection(props) {
|
|
|
14
13
|
props.onSelectionChange(isSelected);
|
|
15
14
|
};
|
|
16
15
|
|
|
17
|
-
//
|
|
18
|
-
const
|
|
16
|
+
// Get icon based on data type
|
|
17
|
+
const getDataTypeIcon = () => {
|
|
18
|
+
switch(props.title) {
|
|
19
|
+
case "Profile":
|
|
20
|
+
return (
|
|
21
|
+
<div className="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center">
|
|
22
|
+
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-blue-600" viewBox="0 0 20 20" fill="currentColor">
|
|
23
|
+
<path fillRule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clipRule="evenodd" />
|
|
24
|
+
</svg>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
case "User Memories":
|
|
28
|
+
return (
|
|
29
|
+
<div className="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center">
|
|
30
|
+
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-purple-600" viewBox="0 0 20 20" fill="currentColor">
|
|
31
|
+
<path d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z" />
|
|
32
|
+
</svg>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
default:
|
|
36
|
+
return (
|
|
37
|
+
<div className="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center">
|
|
38
|
+
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-gray-600" viewBox="0 0 20 20" fill="currentColor">
|
|
39
|
+
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
|
|
40
|
+
</svg>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
19
45
|
|
|
20
46
|
return (
|
|
21
|
-
<
|
|
22
|
-
<div className="p-
|
|
23
|
-
<div className="flex items-start
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<div className="flex flex-col space-y-1">
|
|
38
|
-
<span className="text-xs font-semibold text-black/70">Intent</span>
|
|
39
|
-
<span className="text-sm text-black">{props.descriptions}</span>
|
|
47
|
+
<div className="bg-white rounded-xl shadow-sm hover:shadow-md transition-all overflow-hidden">
|
|
48
|
+
<div className="p-5">
|
|
49
|
+
<div className="flex items-start space-x-4">
|
|
50
|
+
{getDataTypeIcon()}
|
|
51
|
+
<div className="flex-1">
|
|
52
|
+
<div className="flex items-center justify-between">
|
|
53
|
+
<h3 className="font-medium text-gray-800">{props.title}</h3>
|
|
54
|
+
<Box
|
|
55
|
+
active={props.active}
|
|
56
|
+
onSelectionChange={handleSelectionChange}
|
|
57
|
+
changeGranted={props.changeGranted}
|
|
58
|
+
setSelected={setSelected}
|
|
59
|
+
number={props.number + 1}
|
|
60
|
+
type={"Test"}
|
|
61
|
+
title={props.title}
|
|
62
|
+
/>
|
|
40
63
|
</div>
|
|
64
|
+
|
|
65
|
+
{props.descriptions && (
|
|
66
|
+
<div className="mt-2">
|
|
67
|
+
<p className="text-sm text-gray-600">{props.descriptions}</p>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
41
70
|
</div>
|
|
42
|
-
|
|
43
|
-
|
|
71
|
+
</div>
|
|
72
|
+
|
|
44
73
|
{props.rewards && (
|
|
45
|
-
<div className="
|
|
46
|
-
<
|
|
47
|
-
|
|
74
|
+
<div className="mt-3 pt-3 border-t border-gray-100">
|
|
75
|
+
<div className="flex items-center">
|
|
76
|
+
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-yellow-500 mr-2" viewBox="0 0 20 20" fill="currentColor">
|
|
77
|
+
<path fillRule="evenodd" d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zM12 2a1 1 0 01.967.744L14.146 7.2 17.5 9.134a1 1 0 010 1.732l-3.354 1.935-1.18 4.455a1 1 0 01-1.933 0L9.854 12.8 6.5 10.866a1 1 0 010-1.732l3.354-1.935 1.18-4.455A1 1 0 0112 2z" clipRule="evenodd" />
|
|
78
|
+
</svg>
|
|
79
|
+
<span className="text-xs text-gray-500">Benefit: <span className="text-gray-700">{props.rewards}</span></span>
|
|
80
|
+
</div>
|
|
48
81
|
</div>
|
|
49
82
|
)}
|
|
50
83
|
</div>
|
|
51
|
-
</
|
|
84
|
+
</div>
|
|
52
85
|
);
|
|
53
86
|
}
|
|
54
87
|
|
|
@@ -38,7 +38,7 @@ function getPopupUrl() {
|
|
|
38
38
|
* Creates popup content dynamically as fallback
|
|
39
39
|
*/
|
|
40
40
|
function createDynamicPopupContent(data) {
|
|
41
|
-
const { requestData = [], webpageName = 'App', userData = {}, autoFetch = true } = data;
|
|
41
|
+
const { requestData = [], webpageName = 'App', userData = {}, autoFetch = true, appIcon = null } = data;
|
|
42
42
|
|
|
43
43
|
const defaultDataTypes = [
|
|
44
44
|
{ id: 'email', name: 'Email Address', description: 'Your email for account identification', icon: '📧' },
|
|
@@ -74,6 +74,12 @@ function createDynamicPopupContent(data) {
|
|
|
74
74
|
<div class="max-w-md mx-auto bg-white rounded-lg shadow-lg">
|
|
75
75
|
<div class="p-6">
|
|
76
76
|
<div class="text-center mb-6">
|
|
77
|
+
<div class="flex items-center justify-center space-x-2 mb-4">
|
|
78
|
+
<img src="https://onairos.sirv.com/Images/OnairosBlack.png" alt="Onairos Logo" class="w-8 h-8">
|
|
79
|
+
<div class="text-gray-400 mx-2">→</div>
|
|
80
|
+
${appIcon ? `<img src="${appIcon}" alt="${webpageName} Logo" class="w-8 h-8 rounded-full">` :
|
|
81
|
+
`<div class="w-8 h-8 bg-gray-200 rounded-full flex items-center justify-center"><span class="text-gray-600 text-xs font-bold">${webpageName.charAt(0)}</span></div>`}
|
|
82
|
+
</div>
|
|
77
83
|
<h2 class="text-2xl font-bold text-gray-900 mb-2">Data Request</h2>
|
|
78
84
|
<p class="text-gray-600">${webpageName} is requesting access to your data</p>
|
|
79
85
|
</div>
|
|
Binary file
|