Update session pricing and demo mode

- Updated demo mode: now includes basic protection features, still stronger than many competing messengers.
- Adjusted pricing for Basic and Premium sessions to better reflect security levels.
- Added restrictions to Basic session and enhanced Premium session to deliver maximum protection.
This commit is contained in:
lockbitchat
2025-08-17 02:22:55 -04:00
parent 2040228892
commit adb1844392
5 changed files with 884 additions and 375 deletions

View File

@@ -6,11 +6,13 @@ const EnhancedMinimalHeader = ({
isConnected,
securityLevel,
sessionManager,
sessionTimeLeft
sessionTimeLeft,
webrtcManager
}) => {
const [currentTimeLeft, setCurrentTimeLeft] = React.useState(sessionTimeLeft || 0);
const [hasActiveSession, setHasActiveSession] = React.useState(false);
const [sessionType, setSessionType] = React.useState('unknown');
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
React.useEffect(() => {
const updateSessionInfo = () => {
@@ -22,17 +24,122 @@ const EnhancedMinimalHeader = ({
setHasActiveSession(isActive);
setCurrentTimeLeft(timeLeft);
setSessionType(currentSession?.type || 'unknown');
}
};
updateSessionInfo();
const interval = setInterval(updateSessionInfo, 1000);
return () => clearInterval(interval);
}, [sessionManager]);
React.useEffect(() => {
const updateSecurityStatus = () => {
try {
const activeWebrtcManager = webrtcManager || window.webrtcManager;
const activeSessionManager = sessionManager || window.sessionManager;
if (activeWebrtcManager && activeWebrtcManager.getSecurityStatus) {
const securityStatus = activeWebrtcManager.getSecurityStatus();
const sessionInfo = activeSessionManager ? activeSessionManager.getSessionInfo() : null;
if (window.DEBUG_MODE) {
console.log('🔍 Header security update:', {
hasWebrtcManager: !!activeWebrtcManager,
hasSessionManager: !!activeSessionManager,
securityStatus: securityStatus,
sessionInfo: sessionInfo
});
}
const realLevel = calculateRealSecurityLevel(securityStatus, sessionInfo);
setRealSecurityLevel(realLevel);
if (window.DEBUG_MODE) {
console.log('🔍 Calculated real security level:', realLevel);
}
}
} catch (error) {
console.warn('⚠️ Error updating security status:', error);
}
};
updateSecurityStatus();
const interval = setInterval(updateSecurityStatus, 3000);
return () => clearInterval(interval);
}, [webrtcManager, sessionManager]);
const calculateRealSecurityLevel = (securityStatus, sessionInfo) => {
if (!securityStatus) {
return {
level: 'Unknown',
score: 0,
color: 'red',
details: 'Security status not available'
};
}
const activeFeatures = securityStatus.activeFeaturesNames || [];
const totalFeatures = securityStatus.totalFeatures || 12;
const sessionType = sessionInfo?.type || securityStatus.sessionType || 'unknown';
const securityLevel = securityStatus.securityLevel || 'basic';
const stage = securityStatus.stage || 1;
let finalScore = securityStatus.score || 0;
let level = 'Basic';
let color = 'red';
// score от crypto utils
if (finalScore > 0) {
if (finalScore >= 90) {
level = 'Maximum';
color = 'green';
} else if (finalScore >= 60) {
level = 'Enhanced';
color = sessionType === 'demo' ? 'yellow' : 'green';
} else if (finalScore >= 30) {
level = 'Basic';
color = 'yellow';
} else {
level = 'Low';
color = 'red';
}
} else {
const baseScores = {
'basic': 30,
'enhanced': 65,
'maximum': 90
};
const featureScore = totalFeatures > 0 ? Math.min(40, (activeFeatures.length / totalFeatures) * 40) : 0;
finalScore = Math.min(100, (baseScores[securityLevel] || 30) + featureScore);
if (sessionType === 'demo') {
level = 'Basic';
color = finalScore >= 40 ? 'yellow' : 'red';
} else if (securityLevel === 'enhanced') {
level = 'Enhanced';
color = finalScore >= 70 ? 'green' : 'yellow';
} else if (securityLevel === 'maximum') {
level = 'Maximum';
color = 'green';
} else {
level = 'Basic';
color = finalScore >= 50 ? 'yellow' : 'red';
}
}
return {
level: level,
score: Math.round(finalScore),
color: color,
details: `${activeFeatures.length}/${totalFeatures} security features active`,
activeFeatures: activeFeatures,
sessionType: sessionType,
stage: stage,
securityLevel: securityLevel
};
};
React.useEffect(() => {
if (sessionManager?.hasActiveSession()) {
setCurrentTimeLeft(sessionManager.getTimeLeft());
@@ -43,14 +150,33 @@ const EnhancedMinimalHeader = ({
}, [sessionManager, sessionTimeLeft]);
const handleSecurityClick = () => {
if (securityLevel?.verificationResults) {
const currentSecurity = realSecurityLevel || securityLevel;
if (!currentSecurity) {
alert('Security information not available');
return;
}
if (currentSecurity.activeFeatures) {
const activeList = currentSecurity.activeFeatures.map(feature =>
`${feature.replace('has', '').replace(/([A-Z])/g, ' $1').trim()}`
).join('\n');
const message = `Security Level: ${currentSecurity.level} (${currentSecurity.score}%)\n` +
`Session Type: ${currentSecurity.sessionType}\n` +
`Stage: ${currentSecurity.stage}\n\n` +
`Active Security Features:\n${activeList || 'No features detected'}\n\n` +
`${currentSecurity.details || 'No additional details'}`;
alert(message);
} else if (currentSecurity.verificationResults) {
alert('Security check details:\n\n' +
Object.entries(securityLevel.verificationResults)
Object.entries(currentSecurity.verificationResults)
.map(([key, result]) => `${key}: ${result.passed ? '✅' : '❌'} ${result.details}`)
.join('\n')
);
} else if (securityLevel) {
alert(`Security Level: ${securityLevel.level}\nScore: ${securityLevel.score}%\nDetails: ${securityLevel.details || 'No additional details available'}`);
} else {
alert(`Security Level: ${currentSecurity.level}\nScore: ${currentSecurity.score}%\nDetails: ${currentSecurity.details || 'No additional details available'}`);
}
};
@@ -58,7 +184,6 @@ const EnhancedMinimalHeader = ({
React.useEffect(() => {
const handleForceUpdate = (event) => {
if (sessionManager) {
const isActive = sessionManager.hasActiveSession();
const timeLeft = sessionManager.getTimeLeft();
@@ -128,6 +253,7 @@ const EnhancedMinimalHeader = ({
};
const config = getStatusConfig();
const displaySecurityLevel = realSecurityLevel || securityLevel;
return React.createElement('header', {
className: 'header-minimal sticky top-0 z-50'
@@ -163,7 +289,7 @@ const EnhancedMinimalHeader = ({
React.createElement('p', {
key: 'subtitle',
className: 'text-xs sm:text-sm text-muted hidden sm:block'
}, 'End-to-end freedom. v4.0.03.00')
}, 'End-to-end freedom. v4.1.1')
])
]),
@@ -180,24 +306,24 @@ const EnhancedMinimalHeader = ({
sessionManager: sessionManager
}),
// Security Level Indicator
securityLevel && React.createElement('div', {
// Security Level Indicator
displaySecurityLevel && React.createElement('div', {
key: 'security-level',
className: 'hidden md:flex items-center space-x-2 cursor-pointer hover:opacity-80 transition-opacity duration-200',
onClick: handleSecurityClick,
title: `${securityLevel.level} (${securityLevel.score}%) - Click for details`
title: `${displaySecurityLevel.level} (${displaySecurityLevel.score}%) - ${displaySecurityLevel.details || 'Click for details'}`
}, [
React.createElement('div', {
key: 'security-icon',
className: `w-6 h-6 rounded-full flex items-center justify-center ${
securityLevel.color === 'green' ? 'bg-green-500/20' :
securityLevel.color === 'yellow' ? 'bg-yellow-500/20' : 'bg-red-500/20'
displaySecurityLevel.color === 'green' ? 'bg-green-500/20' :
displaySecurityLevel.color === 'yellow' ? 'bg-yellow-500/20' : 'bg-red-500/20'
}`
}, [
React.createElement('i', {
className: `fas fa-shield-alt text-xs ${
securityLevel.color === 'green' ? 'text-green-400' :
securityLevel.color === 'yellow' ? 'text-yellow-400' : 'text-red-400'
displaySecurityLevel.color === 'green' ? 'text-green-400' :
displaySecurityLevel.color === 'yellow' ? 'text-yellow-400' : 'text-red-400'
}`
})
]),
@@ -208,11 +334,11 @@ const EnhancedMinimalHeader = ({
React.createElement('div', {
key: 'security-level-text',
className: 'text-xs font-medium text-primary'
}, `${securityLevel.level} (${securityLevel.score}%)`),
securityLevel.details && React.createElement('div', {
}, `${displaySecurityLevel.level} (${displaySecurityLevel.score}%)`),
React.createElement('div', {
key: 'security-details',
className: 'text-xs text-muted mt-1 hidden lg:block'
}, securityLevel.details),
}, displaySecurityLevel.details || `Stage ${displaySecurityLevel.stage || 1}`),
React.createElement('div', {
key: 'security-progress',
className: 'w-16 h-1 bg-gray-600 rounded-full overflow-hidden'
@@ -220,33 +346,33 @@ const EnhancedMinimalHeader = ({
React.createElement('div', {
key: 'progress-bar',
className: `h-full transition-all duration-500 ${
securityLevel.color === 'green' ? 'bg-green-400' :
securityLevel.color === 'yellow' ? 'bg-yellow-400' : 'bg-red-400'
displaySecurityLevel.color === 'green' ? 'bg-green-400' :
displaySecurityLevel.color === 'yellow' ? 'bg-yellow-400' : 'bg-red-400'
}`,
style: { width: `${securityLevel.score}%` }
style: { width: `${displaySecurityLevel.score}%` }
})
])
])
]),
// Mobile Security Indicator
securityLevel && React.createElement('div', {
displaySecurityLevel && React.createElement('div', {
key: 'mobile-security',
className: 'md:hidden flex items-center'
}, [
React.createElement('div', {
key: 'mobile-security-icon',
className: `w-8 h-8 rounded-full flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity duration-200 ${
securityLevel.color === 'green' ? 'bg-green-500/20' :
securityLevel.color === 'yellow' ? 'bg-yellow-500/20' : 'bg-red-500/20'
displaySecurityLevel.color === 'green' ? 'bg-green-500/20' :
displaySecurityLevel.color === 'yellow' ? 'bg-yellow-500/20' : 'bg-red-500/20'
}`,
title: `${securityLevel.level} (${securityLevel.score}%) - Click for details`,
title: `${displaySecurityLevel.level} (${displaySecurityLevel.score}%) - Click for details`,
onClick: handleSecurityClick
}, [
React.createElement('i', {
className: `fas fa-shield-alt text-sm ${
securityLevel.color === 'green' ? 'text-green-400' :
securityLevel.color === 'yellow' ? 'text-yellow-400' : 'bg-red-400'
displaySecurityLevel.color === 'green' ? 'text-green-400' :
displaySecurityLevel.color === 'yellow' ? 'text-yellow-400' : 'text-red-400'
}`
})
])
@@ -288,4 +414,4 @@ const EnhancedMinimalHeader = ({
window.EnhancedMinimalHeader = EnhancedMinimalHeader;
console.log('✅ EnhancedMinimalHeader loaded with timer fixes');
console.log('✅ EnhancedMinimalHeader v4.1.1 loaded with real security status integration');

View File

@@ -2,20 +2,20 @@ const React = window.React;
const { useState, useEffect, useRef } = React;
const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) => {
const [step, setStep] = useState('select');
const [selectedType, setSelectedType] = useState(null);
const [invoice, setInvoice] = useState(null);
const [paymentStatus, setPaymentStatus] = useState('pending');
const [error, setError] = useState('');
const [paymentMethod, setPaymentMethod] = useState('webln');
const [preimageInput, setPreimageInput] = useState('');
const [isProcessing, setIsProcessing] = useState(false);
const [qrCodeUrl, setQrCodeUrl] = useState('');
const [paymentTimer, setPaymentTimer] = useState(null);
const [timeLeft, setTimeLeft] = useState(0);
const pollInterval = useRef(null);
const [step, setStep] = React.useState('select');
const [selectedType, setSelectedType] = React.useState(null);
const [invoice, setInvoice] = React.useState(null);
const [paymentStatus, setPaymentStatus] = React.useState('pending');
const [error, setError] = React.useState('');
const [paymentMethod, setPaymentMethod] = React.useState('webln');
const [preimageInput, setPreimageInput] = React.useState('');
const [isProcessing, setIsProcessing] = React.useState(false);
const [qrCodeUrl, setQrCodeUrl] = React.useState('');
const [paymentTimer, setPaymentTimer] = React.useState(null);
const [timeLeft, setTimeLeft] = React.useState(0);
const pollInterval = React.useRef(null);
useEffect(() => {
React.useEffect(() => {
if (!isOpen) {
resetModal();
if (pollInterval.current) {
@@ -45,7 +45,6 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
setError('');
if (type === 'demo') {
// Создаем demo сессию
try {
if (!sessionManager || !sessionManager.createDemoSession) {
throw new Error('Demo session manager not available');
@@ -64,7 +63,8 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
createdAt: Date.now(),
isDemo: true,
preimage: demoSession.preimage,
warning: demoSession.warning
warning: demoSession.warning,
securityLevel: 'Basic'
});
setPaymentStatus('demo');
} catch (error) {
@@ -83,7 +83,7 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
setError('');
try {
console.log(`Creating real Lightning invoice for ${type} session...`);
console.log(`Creating Lightning invoice for ${type} session...`);
if (!sessionManager) {
throw new Error('Session manager not initialized');
@@ -95,6 +95,8 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
throw new Error('Failed to create Lightning invoice');
}
createdInvoice.securityLevel = sessionManager.getSecurityLevelForSession(type);
setInvoice(createdInvoice);
setPaymentStatus('created');
@@ -205,8 +207,8 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
}
const dummyPreimages = ['1'.repeat(64), 'a'.repeat(64), 'f'.repeat(64), '0'.repeat(64)];
if (dummyPreimages.includes(trimmedPreimage) && selectedType !== 'free') {
setError('The entered preimage is invalid. Please use the actual preimage from the payment..');
if (dummyPreimages.includes(trimmedPreimage) && selectedType !== 'demo') {
setError('The entered preimage is invalid. Please use the actual preimage from the payment.');
return;
}
@@ -233,7 +235,6 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
throw new Error('Demo preimage not available');
}
// Для demo сессий используем специальную логику верификации
const isValid = await sessionManager.verifyPayment(invoice.preimage, invoice.paymentHash);
if (isValid && isValid.verified) {
@@ -243,7 +244,8 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
paymentHash: invoice.paymentHash,
amount: 0,
isDemo: true,
warning: invoice.warning
warning: invoice.warning,
securityLevel: 'basic'
});
setTimeout(() => {
@@ -265,7 +267,6 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
let isValid;
if (selectedType === 'demo') {
// Demo сессии уже обработаны в handleDemoSession
return;
} else {
isValid = await sessionManager.verifyPayment(preimage, invoice.paymentHash);
@@ -283,7 +284,8 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
type: selectedType,
preimage,
paymentHash: invoice.paymentHash,
amount: invoice.amount
amount: invoice.amount,
securityLevel: invoice.securityLevel || (selectedType === 'basic' ? 'enhanced' : 'maximum')
});
setTimeout(() => {
@@ -313,11 +315,19 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
const getSecurityBadgeColor = (level) => {
switch (level?.toLowerCase()) {
case 'basic': return 'bg-blue-500/20 text-blue-300 border-blue-500/30';
case 'enhanced': return 'bg-orange-500/20 text-orange-300 border-orange-500/30';
case 'maximum': return 'bg-green-500/20 text-green-300 border-green-500/30';
default: return 'bg-gray-500/20 text-gray-300 border-gray-500/30';
}
};
const pricing = sessionManager?.sessionPrices || {
demo: { sats: 0, hours: 0.1, usd: 0.00 },
basic: { sats: 500, hours: 1, usd: 0.20 },
premium: { sats: 1000, hours: 4, usd: 0.40 },
extended: { sats: 2000, hours: 24, usd: 0.80 }
basic: { sats: 5000, hours: 1, usd: 2.00 },
premium: { sats: 20000, hours: 6, usd: 8.00 }
};
if (!isOpen) return null;
@@ -347,7 +357,8 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
step === 'select' && window.SessionTypeSelector && React.createElement(window.SessionTypeSelector, {
key: 'selector',
onSelectType: handleSelectType,
onCancel: onClose
onCancel: onClose,
sessionManager: sessionManager
}),
step === 'payment' && React.createElement('div', {
@@ -361,16 +372,22 @@ const PaymentModal = ({ isOpen, onClose, sessionManager, onSessionPurchased }) =
React.createElement('h3', {
key: 'session-title',
className: 'text-lg font-semibold text-orange-400 mb-2'
}, `${selectedType.charAt(0).toUpperCase() + selectedType.slice(1)} session`),
}, [
`${selectedType.charAt(0).toUpperCase() + selectedType.slice(1)} session`,
invoice?.securityLevel && React.createElement('span', {
key: 'security-badge',
className: `text-xs px-2 py-1 rounded-full border ${getSecurityBadgeColor(invoice.securityLevel)}`
}, invoice.securityLevel.toUpperCase())
]),
React.createElement('div', {
key: 'session-details',
className: 'text-sm text-secondary'
}, [
React.createElement('div', { key: 'amount' }, `${pricing[selectedType].sats} sat for ${pricing[selectedType].hours}ч`),
React.createElement('div', { key: 'amount' }, `${pricing[selectedType].sats} sat for ${pricing[selectedType].hours}h`),
pricing[selectedType].usd > 0 && React.createElement('div', {
key: 'usd',
className: 'text-gray-400'
}, `$${pricing[selectedType].usd} USD`)
}, `${pricing[selectedType].usd} USD`)
])
]),

View File

@@ -10,8 +10,8 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
try {
const info = sessionManager.getDemoSessionInfo();
if (window.DEBUG_MODE) {
console.log('🔄 Demo info updated:', info);
}
console.log('🔄 Demo info updated:', info);
}
setDemoInfo(info);
setLastRefresh(Date.now());
} catch (error) {
@@ -23,10 +23,8 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
// Update information on load and every 10 seconds
React.useEffect(() => {
updateDemoInfo();
const interval = setInterval(updateDemoInfo, 10000);
setRefreshTimer(interval);
return () => {
if (interval) clearInterval(interval);
};
@@ -49,33 +47,66 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
price: '0 sat',
usd: '$0.00',
popular: false,
description: 'Limited testing session',
features: ['End-to-end encryption', 'Basic features', 'No payment required']
securityLevel: 'Basic',
securityBadge: 'BASIC',
securityColor: 'bg-blue-500/20 text-blue-300',
description: 'Limited testing session with basic security',
features: [
'Basic end-to-end encryption',
'Simple key exchange',
'Message integrity',
'Rate limiting'
],
limitations: [
'No advanced security features',
'No traffic obfuscation',
'No metadata protection'
]
},
{
id: 'basic',
name: 'Basic',
duration: '1 hour',
price: '500 sat',
usd: '$0.20',
features: ['End-to-end encryption', 'Full features', '1 hour duration']
price: '5,000 sat',
usd: '$2.00',
securityLevel: 'Enhanced',
securityBadge: 'ENHANCED',
securityColor: 'bg-orange-500/20 text-orange-300',
popular: true,
description: 'Full featured session with enhanced security',
features: [
'All basic features',
'ECDSA digital signatures',
'Metadata protection',
'Perfect forward secrecy',
'Nested encryption',
'Packet padding'
],
limitations: [
'Limited traffic obfuscation',
'No fake traffic generation'
]
},
{
id: 'premium',
name: 'Premium',
duration: '4 hours',
price: '1000 sat',
usd: '$0.40',
popular: true,
features: ['End-to-end encryption', 'Full features', '4 hours duration', 'Priority support']
},
{
id: 'extended',
name: 'Extended',
duration: '24 hours',
price: '2000 sat',
usd: '$0.80',
features: ['End-to-end encryption', 'Full features', '24 hours duration', 'Priority support']
duration: '6 hours',
price: '20,000 sat',
usd: '$8.00',
securityLevel: 'Maximum',
securityBadge: 'MAXIMUM',
securityColor: 'bg-green-500/20 text-green-300',
description: 'Extended session with maximum security protection',
features: [
'All enhanced features',
'Traffic obfuscation',
'Fake traffic generation',
'Decoy channels',
'Anti-fingerprinting',
'Message chunking',
'Advanced replay protection'
],
limitations: []
}
];
@@ -127,10 +158,10 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
React.createElement('p', {
key: 'subtitle',
className: 'text-gray-300 text-sm'
}, 'Pay via Lightning Network or try our demo session')
}, 'Different security levels for different needs')
]),
React.createElement('div', { key: 'types', className: 'space-y-3' },
React.createElement('div', { key: 'types', className: 'space-y-4' },
sessionTypes.map(type => {
const isDemo = type.id === 'demo';
const isDisabled = isDemo && demoInfo && !demoInfo.canUseNow;
@@ -138,82 +169,137 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
return React.createElement('div', {
key: type.id,
onClick: () => !isDisabled && handleTypeSelect(type.id),
className: `card-minimal rounded-lg p-4 border-2 transition-all ${
className: `relative card-minimal rounded-lg p-5 border-2 transition-all ${
selectedType === type.id ? 'border-orange-500 bg-orange-500/10' : 'border-gray-600 hover:border-orange-400'
} ${type.popular ? 'relative' : ''} ${
} ${type.popular ? 'ring-2 ring-orange-500/30' : ''} ${
isDisabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'
}`
}, [
// Popular badge
type.popular && React.createElement('div', {
key: 'badge',
className: 'absolute -top-2 right-3 bg-orange-500 text-white text-xs px-2 py-1 rounded-full'
}, 'Popular'),
key: 'popular-badge',
className: 'absolute -top-2 right-3 bg-orange-500 text-white text-xs px-3 py-1 rounded-full font-medium'
}, 'Most Popular'),
React.createElement('div', { key: 'content', className: 'flex items-start justify-between' }, [
React.createElement('div', { key: 'info', className: 'flex-1' }, [
React.createElement('div', { key: 'header', className: 'flex items-center gap-2 mb-2' }, [
React.createElement('h4', {
key: 'name',
className: 'text-lg font-semibold text-white'
}, type.name),
isDemo && React.createElement('span', {
key: 'demo-badge',
className: 'text-xs bg-blue-500/20 text-blue-300 px-2 py-1 rounded-full'
}, 'FREE')
React.createElement('div', { key: 'content', className: 'space-y-4' }, [
// Header with name and security level
React.createElement('div', { key: 'header', className: 'flex items-start justify-between' }, [
React.createElement('div', { key: 'title-section' }, [
React.createElement('div', { key: 'name-row', className: 'flex items-center gap-3 mb-2' }, [
React.createElement('h4', {
key: 'name',
className: 'text-xl font-bold text-white'
}, type.name),
isDemo && React.createElement('span', {
key: 'free-badge',
className: 'text-xs bg-blue-500/20 text-blue-300 px-2 py-1 rounded-full font-medium'
}, 'FREE'),
React.createElement('span', {
key: 'security-badge',
className: `text-xs px-2 py-1 rounded-full font-medium ${type.securityColor}`
}, type.securityBadge)
]),
React.createElement('p', {
key: 'duration',
className: 'text-gray-300 font-medium mb-1'
}, `Duration: ${type.duration}`),
React.createElement('p', {
key: 'description',
className: 'text-sm text-gray-400'
}, type.description)
]),
React.createElement('p', {
key: 'duration',
className: 'text-gray-300 text-sm mb-1'
}, `Duration: ${type.duration}`),
type.description && React.createElement('p', {
key: 'description',
className: 'text-xs text-gray-400 mb-2'
}, type.description),
isDemo && demoInfo && React.createElement('div', {
key: 'demo-status',
className: 'text-xs mb-2'
}, [
React.createElement('div', {
key: 'availability',
className: demoInfo.canUseNow ? 'text-green-400' : 'text-yellow-400'
}, demoInfo.canUseNow ?
`✅ Available (${demoInfo.available}/${demoInfo.total} today)` :
`⏰ Next: ${demoInfo.nextAvailable}`
),
demoInfo.globalActive > 0 && React.createElement('div', {
key: 'global-status',
className: 'text-blue-300 mt-1'
}, `🌐 Global: ${demoInfo.globalActive}/${demoInfo.globalLimit} active`)
]),
type.features && React.createElement('div', {
key: 'features',
className: 'text-xs text-gray-400 space-y-1'
}, type.features.map((feature, index) =>
React.createElement('div', {
key: index,
className: 'flex items-center gap-1'
React.createElement('div', { key: 'pricing', className: 'text-right' }, [
React.createElement('div', {
key: 'sats',
className: `text-xl font-bold ${isDemo ? 'text-green-400' : 'text-orange-400'}`
}, type.price),
React.createElement('div', {
key: 'usd',
className: 'text-sm text-gray-400'
}, type.usd)
])
]),
// Demo status info
isDemo && demoInfo && React.createElement('div', {
key: 'demo-status',
className: 'p-3 bg-blue-900/20 border border-blue-700/30 rounded-lg'
}, [
React.createElement('div', {
key: 'availability',
className: `text-sm font-medium ${demoInfo.canUseNow ? 'text-green-400' : 'text-yellow-400'}`
}, demoInfo.canUseNow ?
`✅ Available (${demoInfo.available}/${demoInfo.total} today)` :
`⏰ Next: ${demoInfo.nextAvailable}`
),
demoInfo.globalActive > 0 && React.createElement('div', {
key: 'global-status',
className: 'text-blue-300 text-xs mt-1'
}, `🌐 Global: ${demoInfo.globalActive}/${demoInfo.globalLimit} active`)
]),
// Security features
React.createElement('div', { key: 'features-section', className: 'space-y-3' }, [
React.createElement('div', { key: 'features' }, [
React.createElement('h5', {
key: 'features-title',
className: 'text-sm font-medium text-green-300 mb-2 flex items-center'
}, [
React.createElement('i', {
key: 'check',
className: 'fas fa-check text-green-400 w-3'
key: 'shield-icon',
className: 'fas fa-shield-alt mr-2'
}),
React.createElement('span', {
key: 'text'
}, feature)
])
))
]),
React.createElement('div', { key: 'pricing', className: 'text-right' }, [
React.createElement('div', {
key: 'sats',
className: `text-lg font-bold ${isDemo ? 'text-green-400' : 'text-orange-400'}`
}, type.price),
React.createElement('div', {
key: 'usd',
className: 'text-xs text-gray-400'
}, type.usd)
'Security Features'
]),
React.createElement('div', {
key: 'features-list',
className: 'grid grid-cols-1 gap-1'
}, type.features.map((feature, index) =>
React.createElement('div', {
key: index,
className: 'flex items-center gap-2 text-xs text-gray-300'
}, [
React.createElement('i', {
key: 'check',
className: 'fas fa-check text-green-400 w-3'
}),
React.createElement('span', {
key: 'text'
}, feature)
])
))
]),
// Limitations (if any)
type.limitations && type.limitations.length > 0 && React.createElement('div', { key: 'limitations' }, [
React.createElement('h5', {
key: 'limitations-title',
className: 'text-sm font-medium text-yellow-300 mb-2 flex items-center'
}, [
React.createElement('i', {
key: 'info-icon',
className: 'fas fa-info-circle mr-2'
}),
'Limitations'
]),
React.createElement('div', {
key: 'limitations-list',
className: 'grid grid-cols-1 gap-1'
}, type.limitations.map((limitation, index) =>
React.createElement('div', {
key: index,
className: 'flex items-center gap-2 text-xs text-gray-400'
}, [
React.createElement('i', {
key: 'minus',
className: 'fas fa-minus text-yellow-400 w-3'
}),
React.createElement('span', {
key: 'text'
}, limitation)
])
))
])
])
])
])
@@ -254,12 +340,17 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
}, `🎯 Status: ${demoInfo.canUseNow ? 'Available now' : demoInfo.nextAvailable}`)
])
]),
React.createElement('div', {
key: 'security-note',
className: 'mt-3 p-2 bg-yellow-500/10 border border-yellow-500/20 rounded text-yellow-200 text-xs'
}, '⚠️ Demo sessions use basic security only. Upgrade to paid sessions for enhanced protection.'),
React.createElement('div', {
key: 'last-updated',
className: 'text-xs text-gray-400 mt-3 text-center'
className: 'text-xs text-gray-400 mt-2 text-center'
}, `Last updated: ${new Date(lastRefresh).toLocaleTimeString()}`)
]),
// Action buttons
React.createElement('div', { key: 'buttons', className: 'flex space-x-3' }, [
React.createElement('button', {
key: 'continue',
@@ -289,9 +380,7 @@ const SessionTypeSelector = ({ onSelectType, onCancel, sessionManager }) => {
className: 'px-3 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-lg transition-all',
title: 'Refresh demo status'
}, React.createElement('i', { className: 'fas fa-sync-alt' }))
]),
])
]);
};