add landing page
This commit is contained in:
@@ -1,13 +1,176 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="es">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>frontend</title>
|
<title>Control de Patentes - ALPR</title>
|
||||||
|
<meta name="description" content="Sistema de Control de Acceso mediante Reconocimiento Automático de Patentes" />
|
||||||
|
|
||||||
|
<!-- Loading Screen Styles (inline para carga inmediata) -->
|
||||||
|
<style>
|
||||||
|
/* Loading screen container */
|
||||||
|
#loading-screen {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
transition: opacity 0.5s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loading-screen.fade-out {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo container with glow effect */
|
||||||
|
.loading-logo {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
animation: pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-logo svg {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
filter: drop-shadow(0 0 20px rgba(59, 130, 246, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spinner ring */
|
||||||
|
.spinner-ring {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border: 3px solid rgba(59, 130, 246, 0.2);
|
||||||
|
border-top-color: #3b82f6;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading text */
|
||||||
|
.loading-text {
|
||||||
|
color: #94a3b8;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-subtext {
|
||||||
|
color: #64748b;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress bar */
|
||||||
|
.progress-container {
|
||||||
|
width: 200px;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(59, 130, 246, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, #3b82f6, #8b5cf6, #3b82f6);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 1.5s ease-in-out infinite;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animations */
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { transform: scale(1); opacity: 1; }
|
||||||
|
50% { transform: scale(1.05); opacity: 0.8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% { background-position: -200% 0; }
|
||||||
|
100% { background-position: 200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dots animation */
|
||||||
|
.loading-dots::after {
|
||||||
|
content: '';
|
||||||
|
animation: dots 1.5s steps(4, end) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dots {
|
||||||
|
0%, 20% { content: ''; }
|
||||||
|
40% { content: '.'; }
|
||||||
|
60% { content: '..'; }
|
||||||
|
80%, 100% { content: '...'; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Loading Screen (se remueve cuando React carga) -->
|
||||||
|
<div id="loading-screen">
|
||||||
|
<!-- Logo SVG (Car + Shield icon) -->
|
||||||
|
<div class="loading-logo">
|
||||||
|
<svg viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<!-- Shield background -->
|
||||||
|
<path d="M50 8L15 25V50C15 72 30 88 50 95C70 88 85 72 85 50V25L50 8Z"
|
||||||
|
fill="url(#shield-gradient)" stroke="#3b82f6" stroke-width="2"/>
|
||||||
|
<!-- Car silhouette -->
|
||||||
|
<path d="M30 55H70M35 45H65L60 38H40L35 45ZM32 55V62H38V55H32ZM62 55V62H68V55H62Z"
|
||||||
|
stroke="#fff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<!-- Plate icon -->
|
||||||
|
<rect x="38" y="48" width="24" height="8" rx="1" fill="#3b82f6" stroke="#fff" stroke-width="1"/>
|
||||||
|
<text x="50" y="54" text-anchor="middle" fill="#fff" font-size="5" font-family="monospace" font-weight="bold">ALPR</text>
|
||||||
|
<!-- Gradient definitions -->
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="shield-gradient" x1="15" y1="8" x2="85" y2="95" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0%" stop-color="#1e40af"/>
|
||||||
|
<stop offset="100%" stop-color="#3730a3"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Spinner -->
|
||||||
|
<div class="spinner-ring"></div>
|
||||||
|
|
||||||
|
<!-- Loading text -->
|
||||||
|
<div class="loading-text">Control de Patentes</div>
|
||||||
|
<div class="loading-subtext">Cargando sistema<span class="loading-dots"></span></div>
|
||||||
|
|
||||||
|
<!-- Progress bar -->
|
||||||
|
<div class="progress-container">
|
||||||
|
<div class="progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
|
||||||
|
<!-- Script para remover loading screen cuando React está listo -->
|
||||||
|
<script>
|
||||||
|
// Fallback: remover loading screen después de 10 segundos máximo
|
||||||
|
setTimeout(function() {
|
||||||
|
var loadingScreen = document.getElementById('loading-screen');
|
||||||
|
if (loadingScreen) {
|
||||||
|
loadingScreen.classList.add('fade-out');
|
||||||
|
setTimeout(function() {
|
||||||
|
loadingScreen.remove();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
import './index.css'
|
import './index.css'
|
||||||
|
import './i18n'
|
||||||
import App from './App.jsx'
|
import App from './App.jsx'
|
||||||
|
|
||||||
|
// Función para remover la pantalla de carga
|
||||||
|
const removeLoadingScreen = () => {
|
||||||
|
const loadingScreen = document.getElementById('loading-screen');
|
||||||
|
if (loadingScreen) {
|
||||||
|
loadingScreen.classList.add('fade-out');
|
||||||
|
setTimeout(() => {
|
||||||
|
loadingScreen.remove();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Renderizar React y remover loading screen
|
||||||
createRoot(document.getElementById('root')).render(
|
createRoot(document.getElementById('root')).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Remover loading screen cuando React esté montado
|
||||||
|
// Pequeño delay para asegurar que la UI esté lista
|
||||||
|
setTimeout(removeLoadingScreen, 100);
|
||||||
|
|||||||
Reference in New Issue
Block a user