Tarjeta

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Configurador de Ofertas - Red New</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.4.0/css/all.min.css">
<style>
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 300;
src: local('Montserrat Light'), local('Montserrat-Light'),
url(https://cdn.jsdelivr.net/npm/@fontsource/montserrat@4.5.14/files/montserrat-latin-300-normal.woff2) format('woff2');
font-display: swap;
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 400;
src: local('Montserrat Regular'), local('Montserrat-Regular'),
url(https://cdn.jsdelivr.net/npm/@fontsource/montserrat@4.5.14/files/montserrat-latin-400-normal.woff2) format('woff2');
font-display: swap;
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 500;
src: local('Montserrat Medium'), local('Montserrat-Medium'),
url(https://cdn.jsdelivr.net/npm/@fontsource/montserrat@4.5.14/files/montserrat-latin-500-normal.woff2) format('woff2');
font-display: swap;
}
@font-face {
font-family: 'Montserrat';
font-style: normal;
font-weight: 700;
src: local('Montserrat Bold'), local('Montserrat-Bold'),
url(https://cdn.jsdelivr.net/npm/@fontsource/montserrat@4.5.14/files/montserrat-latin-700-normal.woff2) format('woff2');
font-display: swap;
}
:root {
--vodafone-red: #e60000;
--dark-gray: #333333;
--table-row-odd: rgba(25, 25, 25, 0.7);
--table-row-even: rgba(35, 35, 35, 0.7);
--table-hover: rgba(230, 0, 0, 0.15);
}
body {
font-family: 'Montserrat', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #000;
}
.slide {
width: 100%;
min-height: 100vh;
background-color: #000;
position: relative;
overflow: hidden;
display: flex;
}
.red-circle {
background-color: rgba(230, 0, 0, 1);
border-radius: 50%;
position: absolute;
opacity: 0.20;
filter: blur(60px);
animation: pulse 8s infinite alternate;
}
.white-circle {
background-color: white;
border-radius: 50%;
position: absolute;
opacity: 0.05;
filter: blur(30px);
animation: float 15s infinite alternate;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 0.7; }
100% { transform: scale(1.1); opacity: 0.9; }
}
@keyframes float {
0% { transform: translateY(0) translateX(0); }
50% { transform: translateY(-20px) translateX(10px); }
100% { transform: translateY(20px) translateX(-10px); }
}
.header {
padding: 40px 0 20px;
text-align: center;
position: relative;
z-index: 10;
}
.title {
font-size: 3.8rem;
font-weight: 700;
color: white;
margin-bottom: 16px;
position: relative;
display: inline-block;
z-index: 10;
}
.title span {
color: var(--vodafone-red);
}
.title::after {
content: '';
position: absolute;
bottom: -10px;
left: 10%;
width: 80%;
height: 4px;
background: linear-gradient(90deg, transparent, var(--vodafone-red), transparent);
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-grow: 1;
position relative;
z-index: 10;
padding: 10px 80px 40px;
}
.intro-text {
font-size: 1.3rem;
line-height: 1.5;
color: white;
text-align: center;
margin-bottom: 25px;
max-width: 1200px;
}
.highlight {
color: var(--vodafone-red);
font-weight: 500;
}
.budget-container {
width: 100%;
max-width: 1400px;
background: rgba(20, 20, 20, 0.7);
backdrop-filter: blur(10px);
border-radius: 15px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2), 0 0 20px rgba(230, 0, 0, 0.1);
animation: fadeInUp 1s ease-out;
border: 1px solid rgba(255, 255, 255, 0.1);
}
@keyframes fadeInUp {
0% { opacity: 0; transform: translateY(30px); }
100% { opacity: 1; transform: translateY(0); }
}
.budget-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
}
.budget-table th {
background-color: rgba(230, 0, 0, 0.9);
color white;
font-weight: 600;
text-align: left;
padding: 15px 20px;
font-size: 1.1rem;
}
.budget-table td {
padding: 12px 20px;
font-size: 1.05rem;
color: rgba(255, 255, 255, 0.9);
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
transition: all 0.2s ease;
}
.budget-table tr:nth-child(odd) td {
background-color: var(--table-row-odd);
}
.budget-table tr:nth-child(even) td {
background-color: var(--table-row-even);
}
.budget-table tr:hover td {
background-color: var(--table-hover);
}
.item-name {
font-weight: 500;
color: white;
}
.item-desc {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
margin-top: 3px;
}
.item-desc ul {
margin: 5px 0 0 0;
padding-left: 20px;
}
.item-desc li {
margin-bottom: 3px;
}
.quantity, .unit-price {
text-align: center;
}
.total-price {
text-align: right;
font-weight: 600;
color: white;
}
.subtotal-row td {
border-top: 2px solid rgba(230, 0, 0, 0.3);
font-weight: 600;
font-size: 1.1rem;
}
.grand-total-row td {
background-color: rgba(230, 0, 0, 0.2) !important;
font-weight: 700;
font-size: 1.2rem;
color: white;
border-bottom: none;
}
.notes-container {
display: flex;
justify-content: space-between;
margin-top: 25px;
width: 100%;
max-width: 1400px;
}
.notes-box {
background: rgba(25, 25, 25, 0.8);
border-radius: 10px;
padding: 15px 20px;
width: 48%;
}
.notes-title {
font-size: 1.1rem;
font-weight: 600;
color: var(--vodafone-red);
margin-bottom: 10px;
}
.notes-content {
font-size: 0.95rem;
color: rgba(255, 255, 255, 0.8);
line-height: 1.5;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
margin-left: 10px;
}
.badge-success {
background-color: rgba(52, 211, 153, 0.2);
color: #34d399;
}
.badge-info {
background-color: rgba(59, 130, 246, 0.2);
color: #60a5fa;
}
.bottom-bar {
width: 100%;
height: 12px;
background: linear-gradient(90deg, #990000, var(--vodafone-red), #990000);
position: relative;
overflow: hidden;
margin-top: auto;
}
.bottom-bar::after {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
animation: shine 3s infinite;
}
@keyframes shine {
0% { left: -100%; }
100% { left: 100%; }
}
.logo-corner {
position: absolute;
top: 20px;
left: 20px;
display: flex;
align-items: center;
z-index: 20;
}
.logo-text {
font-weight: 700;
font-size: 1.8rem;
color: white;
margin-left: 10px;
}
.logo-text span {
color: var(--vodafone-red);
}
.note-highlight {
color: rgba(230, 0, 0, 0.8);
font-weight: 500;
}
.calculation-text {
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
font-style: italic;
margin-top: 2px;
}
/* Configurator sidebar styles */
.configurator-sidebar {
width: 350px;
background: rgba(20, 20, 20, 0.8);
backdrop-filter: blur(10px);
padding: 30px;
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 10;
margin-top: 80px; /* Added margin to avoid overlap with logo */
}
.client-info {
margin-bottom: 30px;
}
.client-info h3 {
color: white;
font-size: 1.3rem;
margin-bottom: 15px;
border-bottom: 1px solid var(--vodafone-red);
padding-bottom: 10px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 8px;
font-size: 0.95rem;
}
.form-control {
width: 100%;
padding: 10px 15px;
background: rgba(30, 30, 30, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 5px;
color: white;
font-family: 'Montserrat', sans-serif;
}
.form-control:focus {
outline: none;
border-color: var(--vodafone-red);
box-shadow: 0 0 0 2px rgba(230, 0, 0, 0.3);
}
.product-family {
margin-bottom: 30px;
}
.product-family h3 {
color: white;
font-size: 1.2rem;
margin-bottom: 15px;
display: flex;
align-items: center;
cursor: pointer;
}
.product-family h3 i {
margin-right: 10px;
transition: transform 0.3s;
}
.product-family.collapsed h3 i {
transform: rotate(-90deg);
}
.product-family-content {
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.product-family.collapsed .product-family-content {
max-height: 0 !important;
}
.product-option {
background: rgba(30, 30, 30, 0.7);
border-radius: 5px;
padding: 15px;
margin-bottom: 10px;
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.2s;
}
.product-option:hover {
border-color: rgba(230, 0, 0, 0.3);
}
.product-option.selected {
border-color: var(--vodafone-red);
background: rgba(230, 0, 0, 0.1);
}
.product-option h4 {
color: white;
font-size: 1rem;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
}
.product-option p {
color: rgba(255, 255, 255, 0.7);
font-size: 0.85rem;
margin-bottom: 5px;
}
.product-option ul {
margin: 8px 0 0 15px;
color: rgba(255, 255, 255, 0.6);
font-size: 0.8rem;
}
.product-option li {
margin-bottom: 3px;
}
.btn {
display: inline-block;
padding: 10px 20px;
background: var(--vodafone-red);
color: white;
border: none;
border-radius: 5px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.btn:hover {
background: #cc0000;
transform: translateY(-2px);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.2);
}
.main-content {
flex: 1;
overflow-y: auto;
position: relative;
}
.toggle-sidebar {
position: absolute;
top: 20px;
left: 20px;
background: rgba(30, 30, 30, 0.8);
border: none;
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 30;
}
.sidebar-collapsed .configurator-sidebar {
transform: translateX(-100%);
}
.sidebar-collapsed .main-content {
margin-left: 0;
}
.quantity-control {
display: flex;
align-items: center;
margin-top: 10px;
}
.quantity-control button {
width: 30px;
height: 30px;
background: rgba(255, 255, 255, 0.1);
border: none;
color: white;
font-size: 1rem;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.quantity-control button:hover {
background: rgba(255, 255, 255, 0.2);
}
.quantity-control input {
width: 50px;
text-align: center;
margin: 0 5px;
background: rgba(30, 30, 30, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
color: white;
padding: 5px;
}
.business-badge {
display: inline-block;
padding: 2px 8px;
background-color: rgba(0, 102, 204, 0.2);
color: #4dabf7;
border-radius: 4px;
font-size: 0.7rem;
font-weight: 600;
margin-left: 8px;
vertical-align: middle;
}
.ip-fixed-control {
margin-top: 10px;
display: flex;
align-items: center;
}
.ip-fixed-control label {
color: rgba(255, 255, 255, 0.8);
font-size: 0.85rem;
margin-left: 8px;
cursor: pointer;
}
.ip-fixed-control input[type="checkbox"] {
appearance: none;
width: 18px;
height: 18px;
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 3px;
background: rgba(30, 30, 30, 0.7);
cursor: pointer;
position: relative;
}
.ip-fixed-control input[type="checkbox"]:checked {
background-color: var(--vodafone-red);
border-color: var(--vodafone-red);
}
.ip-fixed-control input[type="checkbox"]:checked::after {
content: '✓';
position: absolute;
color: white;
font-size: 12px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.ip-fixed-quantity {
margin-left: 15px;
display: none;
}
.ip-fixed-quantity label {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.7);
margin-right: 5px;
}
.ip-fixed-quantity input {
width: 40px;
text-align: center;
background: rgba(30, 30, 30, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
color: white;
padding: 3px;
font-size: 0.8rem;
}
</style>
</head>
<body>
<div class="slide">
<!-- Logo in corner -->
<div class="logo-corner">
<div class="logo-text"><span>Red</span>New</div>
</div>
<!-- Configurator Sidebar -->
<div class="configurator-sidebar">
<div class="client-info">
<h3><i class="fas fa-user-tie"></i> Información del Cliente</h3>
<div class="form-group">
<label for="client-name">Nombre del Cliente</label>
<input type="text" id="client-name" class="form-control" placeholder="Ingrese el nombre del cliente">
</div>
<div class="form-group">
<label for="client-discount">Descuento (%)</label>
<input type="number" id="client-discount" class="form-control" placeholder="0" min="0" max="100" value="0">
</div>
<button id="apply-discount" class="btn btn-secondary w-full">Aplicar Descuento</button>
</div>
<div class="product-family">
<h3 onclick="toggleFamily(this.parentElement)"><i class="fas fa-chevron-down"></i> Bono</h3>
<div class="product-family-content">
<div class="product-option" onclick="selectOption(this, 'bono')" data-product='{"name":"Red Infinity Lite","price":0,"items":[]}'>
<h4>Red Infinity Lite <span>Gratis</span></h4>
<p>Bono básico para nuevos clientes</p>
</div>
<div class="product-option" onclick="selectOption(this, 'bono')" data-product='{
"name":"Red Infinity Pro",
"price":110,
"items":[
{"name":"Fibra 1 Gb PYME","price":0,"quantity":1,"desc":"Conexión premium de fibra óptica simétrica de 1 Gigabit para empresas (incluida en el bono)"},
{"name":"Línea móvil ilimitada","price":0,"quantity":1,"desc":"Llamadas y datos ilimitados a máxima velocidad 5G (incluida en el bono)"},
{"name":"Centralita avanzada","price":0,"quantity":1,"desc":"Centralita virtual con SOA para 10 extensiones (incluida en el bono)"}
]
}'>
<h4>Red Infinity Pro <span>110€/mes</span></h4>
<p>Bono premium con servicios avanzados</p>
<ul>
<li>Fibra 1Gb PYME incluida</li>
<li>Línea móvil ilimitada incluida</li>
<li>Centralita avanzada con SOA incluida</li>
</ul>
</div>
</div>
</div>
<div class="product-family">
<h3 onclick="toggleFamily(this.parentElement)"><i class="fas fa-chevron-down"></i> Fibra Óptica PYME</h3>
<div class="product-family-content">
<div class="product-option" onclick="selectOption(this, 'fibra')" data-product='{"name":"Fibra 100 Mb PYME","price":22,"quantity":1,"desc":"Conexión de fibra óptica simétrica de 100 Mb para empresas"}'>
<h4>Fibra 100 Mb PYME <span>22€/mes</span></h4>
<p>Conexión básica de fibra óptica para empresas</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
<div class="ip-fixed-control">
<input type="checkbox" id="ip-fixed-100" onclick="event.stopPropagation(); toggleIpFixed(this, 'Fibra 100 Mb PYME')">
<label for="ip-fixed-100">IP Fija (+17€/mes)</label>
<div class="ip-fixed-quantity" id="ip-fixed-qty-100">
<label>Nº IPs:</label>
<input type="number" min="1" value="1" max="1" onchange="updateIpFixedQuantity(this, 'Fibra 100 Mb PYME')">
</div>
</div>
</div>
<div class="product-option" onclick="selectOption(this, 'fibra')" data-product='{"name":"Fibra 600 Mb PYME","price":29,"quantity":1,"desc":"Conexión de fibra óptica simétrica de 600 Mb para empresas"}'>
<h4>Fibra 600 Mb PYME <span>29€/mes</span></h4>
<p>Conexión estándar de fibra óptica para empresas</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
<div class="ip-fixed-control">
<input type="checkbox" id="ip-fixed-600" onclick="event.stopPropagation(); toggleIpFixed(this, 'Fibra 600 Mb PYME')">
<label for="ip-fixed-600">IP Fija (+17€/mes)</label>
<div class="ip-fixed-quantity" id="ip-fixed-qty-600">
<label>Nº IPs:</label>
<input type="number" min="1" value="1" max="1" onchange="updateIpFixedQuantity(this, 'Fibra 600 Mb PYME')">
</div>
</div>
</div>
<div class="product-option" onclick="selectOption(this, 'fibra')" data-product='{"name":"Fibra 1 Gb PYME","price":39,"quantity":1,"desc":"Conexión premium de fibra óptica simétrica de 1 Gigabit para empresas"}'>
<h4>Fibra 1 Gb PYME <span>39€/mes</span></h4>
<p>Conexión premium de fibra óptica para empresas</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
<div class="ip-fixed-control">
<input type="checkbox" id="ip-fixed-1gb" onclick="event.stopPropagation(); toggleIpFixed(this, 'Fibra 1 Gb PYME')">
<label for="ip-fixed-1gb">IP Fija (+17€/mes)</label>
<div class="ip-fixed-quantity" id="ip-fixed-qty-1gb">
<label>Nº IPs:</label>
<input type="number" min="1" value="1" max="1" onchange="updateIpFixedQuantity(this, 'Fibra 1 Gb PYME')">
</div>
</div>
</div>
</div>
</div>
<div class="product-family">
<h3 onclick="toggleFamily(this.parentElement)"><i class="fas fa-chevron-down"></i> Líneas</h3>
<div class="product-family-content">
<div class="product-option" onclick="selectOption(this, 'movil')" data-product='{"name":"Solo Voz","price":12,"quantity":1,"desc":"Llamadas ilimitadas sin datos incluidos"}'>
<h4>Solo Voz <span>12€/mes</span></h4>
<p>Llamadas ilimitadas sin datos</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
</div>
<div class="product-option" onclick="selectOption(this, 'movil')" data-product='{"name":"5GB","price":15,"quantity":1,"desc":"Llamadas ilimitadas + 5GB de datos a máxima velocidad"}'>
<h4>5GB <span>15€/mes</span></h4>
<p>Llamadas ilimitadas + 5GB de datos</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
</div>
<div class="product-option" onclick="selectOption(this, 'movil')" data-product='{"name":"Datos Ilimitados 10MB","price":18,"quantity":1,"desc":"Llamadas ilimitadas + datos ilimitados a velocidad reducida (10MB)"}'>
<h4>Datos Ilimitados 10MB <span>18€/mes</span></h4>
<p>Llamadas ilimitadas + datos ilimitados a velocidad reducida</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
</div>
<div class="product-option" onclick="selectOption(this, 'movil')" data-product='{"name":"Datos Ilimitados Full Speed","price":25,"quantity":1,"desc":"Llamadas ilimitadas + datos ilimitados a máxima velocidad 5G"}'>
<h4>Datos Ilimitados Full Speed <span>25€/mes</span></h4>
<p>Llamadas y datos ilimitados a máxima velocidad</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
</div>
<div class="product-option" onclick="selectOption(this, 'movil')" data-product='{"name":"Tarifa Travel","price":30,"quantity":1,"desc":"Llamadas ilimitadas + datos ilimitados + roaming en Europa y EEUU"}'>
<h4>Tarifa Travel <span>30€/mes</span></h4>
<p>Llamadas y datos ilimitados con roaming internacional</p>
<div class="quantity-control">
<button onclick="event.stopPropagation(); changeQuantity(this, -1)">-</button>
<input type="number" value="1" min="1" onchange="updateQuantity(this)" onclick="event.stopPropagation()">
<button onclick="event.stopPropagation(); changeQuantity(this, 1)">+</button>
</div>
</div>
</div>
</div>
<div class="product-family">
<h3 onclick="toggleFamily(this.parentElement)"><i class="fas fa-chevron-down"></i> Corporate</h3>
<div class="product-family-content">
<div class="product-option" onclick="selectOption(this, 'corporate')" data-product='{"name":"Fibra corporate 600Mb + backup 4G/5G","price":99,"quantity":1,"desc":"Fibra simétrica 600Mb para empresas con backup automático 4G/5G."}'>
<h4>Fibra corporate 600Mb + backup 4G/5G <span>99€/mes</span></h4>
<p>Fibra simétrica 600Mb para empresas con backup automático 4G/5G.</p>
</div>
<div class="product-option" onclick="selectOption(this, 'corporate')" data-product='{"name":"Fibra corporate 1Gb + backup 4G/5G","price":129,"quantity":1,"desc":"Fibra simétrica 1Gb para empresas con backup automático 4G/5G."}'>
<h4>Fibra corporate 1Gb + backup 4G/5G <span>129€/mes</span></h4>
<p>Fibra simétrica 1Gb para empresas con backup automático 4G/5G.</p>
</div>
<div class="product-option" onclick="selectOption(this, 'corporate')" data-product='{"name":"Bundle fibra corporate + firewall Cisco Meraki MX67","price":179,"quantity":1,"desc":"Pack de fibra corporate (600Mb) con backup 4G/5G y firewall Cisco Meraki MX67 gestionado."}'>
<h4>Bundle fibra corporate + firewall Cisco Meraki MX67 <span>179€/mes</span></h4>
<p>Pack de fibra corporate (600Mb) con backup 4G/5G y firewall Cisco Meraki MX67 gestionado.</p>
</div>
</div>
</div>
<div class="product-family">
<h3 onclick="toggleFamily(this.parentElement)"><i class="fas fa-chevron-down"></i> SNAV</h3>
<div class="product-family-content">
<div class="product-option" onclick="selectOption(this, 'snav')" data-product='{"name":"SNAV Básico","price":15,"quantity":1,"desc":"Solución SNAV básica para empresas. Incluye funcionalidades esenciales de navegación segura."}'>
<h4>SNAV Básico <span>15€/mes</span></h4>
<p>Solución básica de navegación segura para empresas</p>
</div>
<div class="product-option" onclick="selectOption(this, 'snav')" data-product='{"name":"SNAV Avanzado","price":25,"quantity":1,"desc":"Solución SNAV avanzada para empresas. Incluye filtrado de contenidos, protección contra amenazas y estadísticas de uso."}'>
<h4>SNAV Avanzado <span>25€/mes</span></h4>
<p>Incluye filtrado de contenidos, protección y estadísticas</p>
</div>
<div class="product-option" onclick="selectOption(this, 'snav')" data-product='{"name":"SNAV Premium","price":40,"quantity":1,"desc":"Solución SNAV premium para empresas. Incluye todas las funcionalidades avanzadas, soporte prioritario y personalización de políticas."}'>
<h4>SNAV Premium <span>40€/mes</span></h4>
<p>Funcionalidades avanzadas, soporte prioritario y personalización</p>
</div>
</div>
</div>
<button id="generate-budget" class="btn w-full mt-10">Generar Presupuesto</button>
</div>
<!-- Main Content -->
<div class="main-content">
<button class="toggle-sidebar" onclick="toggleSidebar()">
<i class="fas fa-bars"></i>
</button>
<!-- Background Elements -->
<div class="red-circle" style="width: 600px; height: 600px; top: -300px; right: -200px;"></div>
<div class="red-circle" style="width: 400px; height: 400px; bottom: -150px; left: -100px;"></div>
<div class="white-circle" style="width: 900px; height: 900px; bottom: -450px; right: 300px;"></div>
<div class="white-circle" style="width=400px; height: 400px; top: 150px; left: 200px; opacity: 0.03;"></div>
<!-- Header -->
<div class="header">
<h1 class="title">Configurador de <span>Ofertas</span></h1>
</div>
<!-- Budget Preview -->
<div class="content">
<p class="intro-text">
Seleccione los servicios en el panel izquierdo para <span class="highlight">configurar una offerta personalizada</span> que combine conectividad, comunicación y seguridad adaptadas a sus necesidades.
</p>
<div class="budget-container" id="budget-preview">
<table class="budget-table">
<thead>
<tr>
<th style="width: 45%;">Servicio</th>
<th style="width: 15%;">Cantidad</th>
<th style="width: 20%;">Precio Unitario</th>
<th style="width: 20%;">Total</th>
</tr>
</thead>
<tbody id="budget-items">
<tr>
<td colspan="4" style="text-align: center; padding: 40px; color: rgba(255,255,255,0.5);">
Seleccione servicios en el panel izquierdo para comenzar
</td>
</tr>
</tbody>
</table>
</div>
<div class="notes-container">
<div class="notes-box">
<div class="notes-title">
<i class="fas fa-info-circle"></i> Condiciones especiales
</div>
<div class="notes-content">
<p>• Permanencia mínima de <span class="note-highlight">24 meses</span> en los servicios principales</p>
<p>• Instalación y configuración <span class="note-highlight">sin coste adicional</span></p>
<p>• Soporte técnico prioritario durante toda la vigencia del contrato</p>
<p>• IP Fija: <span class="note-highlight">17€/mes por cada IP</span> (máximo 1 IP por línea de fibra)</p>
</div>
</div>
<div class="notes-box">
<div class="notes-title">
<i class="fas fa-calendar-alt"></i> Resumen de selección
</div>
<div class="notes-content" id="selection-summary">
<p>No hay servicios seleccionados</p>
</div>
</div>
</div>
</div>
<!-- Bottom pattern -->
<div class="bottom-bar"></div>
</div>
</div>

<script>
// Global variables
let selectedProducts = [];
let currentDiscount = 0;
let clientName = '';
let ipFixedSelections = {
'Fibra 100 Mb PYME': { active: false, quantity: 1 },
'Fibra 600 Mb PYME': { active: false, quantity: 1 },
'Fibra 1 Gb PYME': { active: false, quantity: 1 }
};
// Toggle product family collapse/expand
function toggleFamily(element) {
element.classList.toggle('collapsed');
}
// Select a product option
function selectOption(element, family) {
// If it's a bono, remove any previous bono selection
if (family === 'bono') {
document.querySelectorAll('.product-option[data-family="bono"]').forEach(opt => {
opt.classList.remove('selected');
});
// Remove any bono items from selectedProducts
selectedProducts = selectedProducts.filter(item => item.family !== 'bono');
}
element.classList.toggle('selected');
const productData = JSON.parse(element.getAttribute('data-product'));
if (element.classList.contains('selected')) {
// Add to selected products
if (family === 'bono') {
// Add the bono itself first
selectedProducts.push({
name: productData.name,
price: productData.price,
quantity: 1,
family: 'bono',
originalPrice: productData.price,
desc: 'Bono premium con servicios incluidos'
});
// Then add included items if any
if (productData.items && productData.items.length > 0) {
productData.items.forEach(item => {
selectedProducts.push({
...item,
family: 'bono',
bonoName: productData.name,
originalPrice: item.price
});
});
}
} else {
// For regular products
const quantity = element.querySelector('.quantity-control input') ? 
parseInt(element.querySelector('.quantity-control input').value) : 1;
selectedProducts.push({
...productData,
family: family,
quantity: quantity,
originalPrice: productData.price
});
// If it's a fiber product and IP fixed is selected, add the IP fixed cost
if (family === 'fibra' && ipFixedSelections[productData.name].active) {
selectedProducts.push({
name: `IP Fija (${productData.name})`,
price: 17 * ipFixedSelections[productData.name].quantity,
quantity: 1,
family: 'ip-fija',
originalPrice: 17,
desc: `Dirección IP fija para ${productData.name}`,
associatedFiber: productData.name
});
}
}
// Set data-family attribute for easy filtering
element.setAttribute('data-family', family);
} else {
// Remove from selected products
if (family === 'bono') {
// Remove the bono and all its included items
selectedProducts = selectedProducts.filter(item => 
!(item.family === 'bono' && (item.name === productData.name || item.bonoName === productData.name))
);
} else {
// Remove this product and any associated IP fixed
selectedProducts = selectedProducts.filter(item => 
!(item.name === productData.name && item.family === family) &&
!(item.associatedFiber === productData.name && item.family === 'ip-fija')
);
}
// If it's a fiber product, uncheck IP fixed
if (family === 'fibra') {
const checkbox = element.querySelector('.ip-fixed-control input[type="checkbox"]');
if (checkbox) {
checkbox.checked = false;
ipFixedSelections[productData.name].active = false;
const qtyContainer = element.querySelector('.ip-fixed-quantity');
if (qtyContainer) qtyContainer.style.display = 'none';
}
}
}
updateBudgetPreview();
}
// Change quantity of a product
function changeQuantity(button, change) {
const input = button.parentElement.querySelector('input');
let newValue = parseInt(input.value) + change;
if (newValue < 1) newValue = 1;
input.value = newValue;
updateQuantity(input);
}
// Update quantity when input changes
function updateQuantity(input) {
const productOption = input.closest('.product-option');
if (productOption.classList.contains('selected')) {
const productData = JSON.parse(productOption.getAttribute('data-product'));
const family = productOption.getAttribute('data-family');
// Find and update the product in selectedProducts
selectedProducts = selectedProducts.map(item => {
if (item.name === productData.name && item.family === family) {
return {
...item,
quantity: parseInt(input.value)
};
}
return item;
});
// If it's a fiber product with IP fixed, update the IP fixed quantity
if (family === 'fibra' && ipFixedSelections[productData.name].active) {
// Find the IP fixed item and update it
selectedProducts = selectedProducts.map(item => {
if (item.family === 'ip-fija' && item.associatedFiber === productData.name) {
return {
...item,
price: 17 * ipFixedSelections[productData.name].quantity,
originalPrice: 17
};
}
return item;
});
}
updateBudgetPreview();
}
}
// Toggle IP fixed checkbox
function toggleIpFixed(checkbox, fiberName) {
const productOption = checkbox.closest('.product-option');
const qtyContainer = checkbox.parentElement.querySelector('.ip-fixed-quantity');
ipFixedSelections[fiberName].active = checkbox.checked;
if (checkbox.checked) {
qtyContainer.style.display = 'flex';
// If the fiber product is selected, add the IP fixed cost
if (productOption.classList.contains('selected')) {
selectedProducts.push({
name: `IP Fija (${fiberName})`,
price: 17 * ipFixedSelections[fiberName].quantity,
quantity: 1,
family: 'ip-fija',
originalPrice: 17,
desc: `Dirección IP fija para ${fiberName}`,
associatedFiber: fiberName
});
}
} else {
qtyContainer.style.display = 'none';
// Remove the IP fixed cost if it exists
selectedProducts = selectedProducts.filter(item => 
!(item.family === 'ip-fija' && item.associatedFiber === fiberName)
);
}
updateBudgetPreview();
}
// Update IP fixed quantity
function updateIpFixedQuantity(input, fiberName) {
let qty = parseInt(input.value);
if (isNaN(qty) || qty < 1) qty = 1;
if (qty > 1) qty = 1; // Maximum 1 IP per fiber
input.value = qty;
ipFixedSelections[fiberName].quantity = qty;
// Update the IP fixed cost if it exists
selectedProducts = selectedProducts.map(item => {
if (item.family === 'ip-fija' && item.associatedFiber === fiberName) {
return {
...item,
price: 17 * qty,
originalPrice: 17
};
}
return item;
});
updateBudgetPreview();
}
// Apply discount to all products
function applyDiscount() {
const discountInput = document.getElementById('client-discount');
currentDiscount = parseInt(discountInput.value) || 0;
clientName = document.getElementById('client-name').value || '';

selectedProducts = selectedProducts.map(item => {
// No discount for included items in bonos (price = 0)
if (item.price === 0) return item;

// No discount for corporate products
if (item.family === 'corporate') {
return {
...item,
price: item.originalPrice
};
}

// Bono Red Infinity Lite: max 45%
if (item.family === 'bono' && item.name === 'Red Infinity Lite') {
const maxDiscount = 45;
const appliedDiscount = Math.min(currentDiscount, maxDiscount);
const discountedPrice = item.originalPrice * (1 - appliedDiscount / 100);
return {
...item,
price: parseFloat(discountedPrice.toFixed(2))
};
}

// Bono Red Infinity Pro: max 35%
if (item.family === 'bono' && item.name === 'Red Infinity Pro') {
const maxDiscount = 35;
const appliedDiscount = Math.min(currentDiscount, maxDiscount);
const discountedPrice = item.originalPrice * (1 - appliedDiscount / 100);
return {
...item,
price: parseFloat(discountedPrice.toFixed(2))
};
}

// For other products, apply full discount
return {
...item,
price: parseFloat((item.originalPrice * (1 - currentDiscount / 100)).toFixed(2))
};
});

updateBudgetPreview();
}

// Update the budget preview table
function updateBudgetPreview() {
const budgetItemsContainer = document.getElementById('budget-items');
const summaryContainer = document.getElementById('selection-summary');
if (selectedProducts.length === 0) {
budgetItemsContainer.innerHTML = `
<tr>
<td colspan="4" style="text-align: center; padding: 40px; color: rgba(255,255,255,0.5);">
Seleccione servicios en el panel izquierdo para comenzar
</td>
</tr>
`;
summaryContainer.innerHTML = `<p>No hay servicios seleccionados</p>`;
return;
}
// Generate budget items HTML
let budgetItemsHTML = '';
let subtotal = 0;
selectedProducts.forEach(item => {
const total = item.price * item.quantity;
// Only add to subtotal if it's not an included item (price > 0)
if (item.price > 0) {
subtotal += total;
}
budgetItemsHTML += `
<tr>
<td>
<div class="item-name">${item.name} ${item.bonoName ? '<span class="badge badge-success">'+item.bonoName+'</span>' : ''}</div>
<div class="item-desc">${item.desc}</div>
</td>
<td class="quantity">${item.quantity}</td>
<td class="unit-price">${item.price > 0 ? item.price.toFixed(2)+' €' : 'Incluido'} ${currentDiscount > 0 && item.price > 0 ? '<small class="calculation-text">(antes '+item.originalPrice.toFixed(2)+'€)</small>' : ''}</td>
<td class="total-price">${item.price > 0 ? total.toFixed(2)+' €' : 'Incluido'}</td>
</tr>
`;
});
// Add subtotal and grand total
budgetItemsHTML += `
<tr class="subtotal-row">
<td colspan="3" class="text-right">Subtotal</td>
<td class="total-price">${subtotal.toFixed(2)} €</td>
</tr>
<tr class="grand-total-row">
<td colspan="3" class="text-right">TOTAL (IVA no incluido)</td>
<td class="total-price">${subtotal.toFixed(2)} €</td>
</tr>
`;
budgetItemsContainer.innerHTML = budgetItemsHTML;
// Update selection summary
let summaryHTML = '';
if (clientName) {
summaryHTML += `<p>Cliente: <strong>${clientName}</strong></p>`;
}
if (currentDiscount > 0) {
summaryHTML += `<p>Descuento aplicado: <strong>${currentDiscount}%</strong></p>`;
}
// Group by family
const families = {};
selectedProducts.forEach(item => {
if (!families[item.family]) {
families[item.family] = [];
}
families[item.family].push(item);
});
summaryHTML += `<div class="mt-3">`;
for (const family in families) {
const familyName = {
'bono': 'Bono',
'fibra': 'Fibra Óptica PYME',
'movil': 'Líneas',
'ip-fija': 'IP Fija',
'corporate': 'Corporate'
}[family] || family;
summaryHTML += `<p><strong>${familyName}:</strong></p><ul class="ml-4">`;
families[family].forEach(item => {
if (item.price > 0) {
summaryHTML += `<li>${item.name} (${item.quantity}x) - ${item.price.toFixed(2)}€/mes</li>`;
} else {
summaryHTML += `<li>${item.name} (Incluido)</li>`;
}
});
summaryHTML += `</ul>`;
}
summaryHTML += `</div>`;
summaryContainer.innerHTML = summaryHTML;
}
// Toggle sidebar visibility
function toggleSidebar() {
document.querySelector('.slide').classList.toggle('sidebar-collapsed');
}
// Initialize event listeners
document.addEventListener('DOMContentLoaded', function() {
// Apply discount button
document.getElementById('apply-discount').addEventListener('click', applyDiscount);
// Generate budget button
document.getElementById('generate-budget').addEventListener('click', function() {
alert('Presupuesto generado correctamente. Los detalles se muestran en la tabla.');
});
// Collapse all families by default
document.querySelectorAll('.product-family').forEach(family => {
family.classList.add('collapsed');
});
});
</script>
</body>
</html>