147 lines
4.8 KiB
TypeScript
147 lines
4.8 KiB
TypeScript
|
|
import React, { useState, useEffect } from 'react';
|
||
|
|
import { extendDemoSession, destroyDemoSession } from '../../../api/services/demo';
|
||
|
|
import { apiClient } from '../../../api/client';
|
||
|
|
import { useNavigate } from 'react-router-dom';
|
||
|
|
|
||
|
|
export const DemoBanner: React.FC = () => {
|
||
|
|
const navigate = useNavigate();
|
||
|
|
const [isDemo, setIsDemo] = useState(false);
|
||
|
|
const [expiresAt, setExpiresAt] = useState<string | null>(null);
|
||
|
|
const [timeRemaining, setTimeRemaining] = useState<string>('');
|
||
|
|
const [canExtend, setCanExtend] = useState(true);
|
||
|
|
const [extending, setExtending] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const demoMode = localStorage.getItem('demo_mode') === 'true';
|
||
|
|
const expires = localStorage.getItem('demo_expires_at');
|
||
|
|
|
||
|
|
setIsDemo(demoMode);
|
||
|
|
setExpiresAt(expires);
|
||
|
|
|
||
|
|
if (demoMode && expires) {
|
||
|
|
const interval = setInterval(() => {
|
||
|
|
const now = new Date().getTime();
|
||
|
|
const expiryTime = new Date(expires).getTime();
|
||
|
|
const diff = expiryTime - now;
|
||
|
|
|
||
|
|
if (diff <= 0) {
|
||
|
|
setTimeRemaining('Sesión expirada');
|
||
|
|
handleExpiration();
|
||
|
|
} else {
|
||
|
|
const minutes = Math.floor(diff / 60000);
|
||
|
|
const seconds = Math.floor((diff % 60000) / 1000);
|
||
|
|
setTimeRemaining(`${minutes}:${seconds.toString().padStart(2, '0')}`);
|
||
|
|
}
|
||
|
|
}, 1000);
|
||
|
|
|
||
|
|
return () => clearInterval(interval);
|
||
|
|
}
|
||
|
|
}, [expiresAt]);
|
||
|
|
|
||
|
|
const handleExpiration = () => {
|
||
|
|
localStorage.removeItem('demo_mode');
|
||
|
|
localStorage.removeItem('demo_session_id');
|
||
|
|
localStorage.removeItem('demo_account_type');
|
||
|
|
localStorage.removeItem('demo_expires_at');
|
||
|
|
apiClient.setDemoSessionId(null);
|
||
|
|
navigate('/demo');
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleExtendSession = async () => {
|
||
|
|
const sessionId = apiClient.getDemoSessionId();
|
||
|
|
if (!sessionId) return;
|
||
|
|
|
||
|
|
setExtending(true);
|
||
|
|
try {
|
||
|
|
const updatedSession = await extendDemoSession({ session_id: sessionId });
|
||
|
|
localStorage.setItem('demo_expires_at', updatedSession.expires_at);
|
||
|
|
setExpiresAt(updatedSession.expires_at);
|
||
|
|
|
||
|
|
if (updatedSession.remaining_extensions === 0) {
|
||
|
|
setCanExtend(false);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error extending session:', error);
|
||
|
|
alert('No se pudo extender la sesión');
|
||
|
|
} finally {
|
||
|
|
setExtending(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleEndSession = async () => {
|
||
|
|
const sessionId = apiClient.getDemoSessionId();
|
||
|
|
if (!sessionId) return;
|
||
|
|
|
||
|
|
if (confirm('¿Estás seguro de que quieres terminar la sesión demo?')) {
|
||
|
|
try {
|
||
|
|
await destroyDemoSession({ session_id: sessionId });
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error destroying session:', error);
|
||
|
|
} finally {
|
||
|
|
handleExpiration();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
if (!isDemo) return null;
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="bg-gradient-to-r from-amber-500 to-orange-500 text-white px-4 py-2 shadow-md">
|
||
|
|
<div className="max-w-7xl mx-auto flex items-center justify-between">
|
||
|
|
<div className="flex items-center space-x-4">
|
||
|
|
<div className="flex items-center">
|
||
|
|
<svg
|
||
|
|
className="w-5 h-5 mr-2"
|
||
|
|
fill="currentColor"
|
||
|
|
viewBox="0 0 20 20"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
fillRule="evenodd"
|
||
|
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||
|
|
clipRule="evenodd"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
<span className="font-medium">Modo Demo</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="hidden sm:flex items-center text-sm">
|
||
|
|
<svg
|
||
|
|
className="w-4 h-4 mr-1"
|
||
|
|
fill="currentColor"
|
||
|
|
viewBox="0 0 20 20"
|
||
|
|
>
|
||
|
|
<path
|
||
|
|
fillRule="evenodd"
|
||
|
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z"
|
||
|
|
clipRule="evenodd"
|
||
|
|
/>
|
||
|
|
</svg>
|
||
|
|
Tiempo restante: <span className="font-mono ml-1">{timeRemaining}</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="flex items-center space-x-2">
|
||
|
|
{canExtend && (
|
||
|
|
<button
|
||
|
|
onClick={handleExtendSession}
|
||
|
|
disabled={extending}
|
||
|
|
className="px-3 py-1 bg-white/20 hover:bg-white/30 rounded-md text-sm font-medium transition-colors disabled:opacity-50"
|
||
|
|
>
|
||
|
|
{extending ? 'Extendiendo...' : '+30 min'}
|
||
|
|
</button>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<button
|
||
|
|
onClick={handleEndSession}
|
||
|
|
className="px-3 py-1 bg-white/20 hover:bg-white/30 rounded-md text-sm font-medium transition-colors"
|
||
|
|
>
|
||
|
|
Terminar Demo
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
export default DemoBanner;
|