onairos
Version:
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 seamless, simple and effective for all applications. LLM SDK capabiliti
321 lines (285 loc) • 15.8 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Onairos Data Request</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
background-color: #f9fafb;
}
.popup-container {
width: 100%;
height: 100vh;
overflow-y: auto;
background-color: white;
}
</style>
</head>
<body>
<div id="root" class="popup-container"></div>
<script type="text/babel">
const { useState, useEffect } = React;
const defaultDataTypes = [
{ id: 'email', name: 'Email Address', description: 'Your email for account identification', icon: '📧' },
{ id: 'profile', name: 'Profile Information', description: 'Basic profile data and preferences', icon: '👤' },
{ id: 'social', name: 'Social Connections', description: 'Connected social media accounts', icon: '🌐' },
{ id: 'activity', name: 'Activity Data', description: 'Usage patterns and interactions', icon: '📊' },
{ id: 'preferences', name: 'User Preferences', description: 'Settings and customization choices', icon: '⚙️' }
];
function DataRequestPopup() {
const [selectedData, setSelectedData] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isLoadingApi, setIsLoadingApi] = useState(false);
const [apiResponse, setApiResponse] = useState(null);
const [apiError, setApiError] = useState(null);
const [requestData, setRequestData] = useState([]);
const [appName, setAppName] = useState('App');
const [userEmail, setUserEmail] = useState('');
const [autoFetch, setAutoFetch] = useState(true);
// Listen for data from parent window
useEffect(() => {
const handleMessage = (event) => {
if (event.data && event.data.type === 'dataRequest') {
setRequestData(event.data.requestData || []);
setAppName(event.data.webpageName || 'App');
setUserEmail(event.data.userData?.email || '');
setAutoFetch(event.data.autoFetch !== false);
// Send acknowledgment
window.parent.postMessage({ action: 'dataReceived' }, '*');
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
// Use provided requestData or default data types
const dataTypes = Array.isArray(requestData) && requestData.length > 0
? requestData.map(id => defaultDataTypes.find(dt => dt.id === id) || { id, name: id, description: `${id} data`, icon: '📋' })
: defaultDataTypes;
const handleDataToggle = (dataId) => {
setSelectedData(prev => ({
...prev,
[dataId]: !prev[dataId]
}));
};
const makeApiCall = async (approvedData) => {
try {
setIsLoadingApi(true);
setApiError(null);
const response = await fetch('https://api2.onairos.uk/inferenceTest', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
approvedData,
userEmail,
appName,
timestamp: new Date().toISOString()
})
});
if (!response.ok) {
throw new Error(`API call failed with status: ${response.status}`);
}
const data = await response.json();
setApiResponse(data);
return data;
} catch (error) {
console.error('API call error:', error);
setApiError(error.message);
throw error;
} finally {
setIsLoadingApi(false);
}
};
const handleApprove = async () => {
if (isSubmitting) return;
setIsSubmitting(true);
try {
const approved = Object.entries(selectedData)
.filter(([_, isSelected]) => isSelected)
.map(([dataId]) => dataId);
if (approved.length === 0) {
alert('Please select at least one data type to continue.');
setIsSubmitting(false);
return;
}
const baseResult = {
type: 'dataRequestComplete',
source: 'onairosPopup',
approved: approved,
timestamp: new Date().toISOString(),
userEmail: userEmail,
appName: appName
};
let finalResult = baseResult;
// If autoFetch is enabled, make API call automatically
if (autoFetch) {
try {
const apiData = await makeApiCall(approved);
finalResult = {
...baseResult,
apiResponse: apiData,
apiUrl: 'https://api2.onairos.uk/inferenceTest'
};
} catch (apiError) {
finalResult = {
...baseResult,
apiError: apiError.message,
apiUrl: 'https://api2.onairos.uk/inferenceTest'
};
}
}
// Send result to parent window
window.parent.postMessage(finalResult, '*');
// Close popup after a brief delay
setTimeout(() => {
window.close();
}, 1000);
} catch (error) {
console.error('Error in handleApprove:', error);
setApiError('Failed to process request');
} finally {
setIsSubmitting(false);
}
};
const handleReject = () => {
window.parent.postMessage({
type: 'dataRequestComplete',
source: 'onairosPopup',
approved: false,
dataTypes: [],
timestamp: new Date().toISOString(),
userEmail: userEmail,
appName: appName
}, '*');
window.close();
};
const selectedCount = Object.values(selectedData).filter(Boolean).length;
return (
<div className="p-6 max-w-md mx-auto">
{/* Header */}
<div className="text-center mb-6">
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<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" />
</svg>
</div>
<h2 className="text-xl font-semibold text-gray-900 mb-2">Data Access Request</h2>
<p className="text-gray-600 text-sm">
<span className="font-medium">{appName}</span> would like to access some of your data.
{autoFetch && " Your data will be processed automatically after approval."}
</p>
</div>
{/* Data Types Selection */}
<div className="space-y-3 mb-6">
{dataTypes.map((dataType) => {
const isSelected = selectedData[dataType.id] || false;
return (
<div
key={dataType.id}
className="flex items-center justify-between p-4 border rounded-lg hover:bg-gray-50 transition-colors"
>
<div className="flex items-center space-x-3">
<div className="text-2xl">
{dataType.icon}
</div>
<div>
<h3 className="font-medium text-gray-900">{dataType.name}</h3>
<p className="text-sm text-gray-500">{dataType.description}</p>
</div>
</div>
{/* Toggle Switch */}
<button
onClick={() => handleDataToggle(dataType.id)}
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 ${
isSelected ? 'bg-blue-600' : 'bg-gray-200'
}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
isSelected ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
</div>
);
})}
</div>
{/* Selection Summary */}
{selectedCount > 0 && (
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
<p className="text-green-800 text-sm">
✅ {selectedCount} data type{selectedCount > 1 ? 's' : ''} selected for sharing
</p>
</div>
)}
{/* API Status */}
{autoFetch && isLoadingApi && (
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<div className="flex items-center space-x-2">
<div className="animate-spin h-4 w-4 border-2 border-blue-600 rounded-full border-t-transparent"></div>
<p className="text-blue-800 text-sm">Processing your data...</p>
</div>
</div>
)}
{autoFetch && apiResponse && (
<div className="mb-4 p-3 bg-green-50 border border-green-200 rounded-lg">
<p className="text-green-800 text-sm">
✅ Data processed successfully
</p>
</div>
)}
{autoFetch && apiError && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg">
<p className="text-red-800 text-sm">
❌ Error processing data: {apiError}
</p>
</div>
)}
{/* Action Buttons */}
<div className="flex space-x-3">
<button
onClick={handleReject}
disabled={isSubmitting || isLoadingApi}
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"
>
Deny
</button>
<button
onClick={handleApprove}
disabled={isSubmitting || isLoadingApi || selectedCount === 0}
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"
>
{isSubmitting || isLoadingApi ? (
<>
<div className="animate-spin h-4 w-4 border-2 border-white rounded-full border-t-transparent mr-2"></div>
{autoFetch ? 'Processing...' : 'Approving...'}
</>
) : (
autoFetch ? 'Approve & Process' : 'Approve'
)}
</button>
</div>
{/* Auto-fetch info */}
{autoFetch && (
<div className="mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
<p className="text-blue-800 text-xs">
🔄 Auto-fetch enabled: Your approved data will be automatically processed and sent to {appName}.
</p>
</div>
)}
</div>
);
}
ReactDOM.render(<DataRequestPopup />, document.getElementById('root'));
</script>
</body>
</html>