feat: agregar modo claro/oscuro y optimizaciones móviles (v1.3.3)

- Agregar sistema de modo claro/oscuro con botón toggle (☀️/🌙)
- Implementar persistencia de tema con localStorage
- Optimizar tabla para móviles (3 columnas en lugar de 5)
- Cambiar límite por defecto a 5 IPs para búsquedas más rápidas
- Mejorar tipografía de direcciones IP (más grande y negrita)
- Actualizar documentación y versión a 1.3.3

Características del modo claro/oscuro:
- Botón flotante en esquina superior derecha
- Paleta de colores profesional para tema claro
- Transiciones suaves entre temas
- Preferencia guardada automáticamente

Mejoras móviles:
- Columna "Tipo de IP" oculta en móviles
- Badge de tipo mostrado bajo la dirección IP
- Mejor distribución de espacio (40% IP, 35% Estado, 25% Acción)
- Fuentes y padding optimizados

Archivos modificados:
- public.php: Sistema de temas, optimizaciones móviles, límite por defecto
- CHANGELOG.md: entrada v1.3.3
- README.md: documentación de nuevas características
- manifest.json: actualización de versión a 1.3.3
This commit is contained in:
DANYDHSV 2025-11-27 09:09:58 -06:00
parent 99d283dd3d
commit 1117b2ade2
7 changed files with 1486 additions and 13 deletions

View File

@ -7,6 +7,17 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/lang/es/).
---
## [1.3.3] - 2025-11-27
### ✨ Añadido
- **Modo Claro/Oscuro**: Botón toggle (☀️/🌙) en esquina superior derecha para cambiar entre temas con persistencia en localStorage.
- **Tema Claro**: Paleta de colores profesional para uso diurno con fondo claro y textos oscuros.
### 🔄 Mejorado
- **Tabla Móvil**: Optimizada para pantallas pequeñas ocultando columna "Tipo de IP" y mostrando el badge bajo la dirección IP (3 columnas en lugar de 5).
- **Límite por Defecto**: Cambiado a 5 IPs para búsquedas más rápidas.
- **Tipografía**: Direcciones IP más destacadas con fuente más grande y negrita.
## [1.3.2] - 2025-11-26
### ✨ Añadido

View File

@ -1,6 +1,6 @@
# SIIP - Buscador de IP's Disponibles UISP
[![Version](https://img.shields.io/badge/version-1.3.2-blue.svg)](manifest.json)
[![Version](https://img.shields.io/badge/version-1.3.3-blue.svg)](manifest.json)
[![UCRM](https://img.shields.io/badge/UCRM-Compatible-green.svg)](https://uisp.com/)
[![UNMS](https://img.shields.io/badge/UNMS-Compatible-green.svg)](https://uisp.com/)
@ -30,10 +30,12 @@ Plugin para UISP CRM (anteriormente UCRM) que permite buscar direcciones IP disp
- 🔍 **Búsqueda de IPs disponibles** por segmento de red (172.16.X.x)
- 🌐 **Interfaz web moderna** con diseño responsive y atractivo
- 🌓 **Modo Claro/Oscuro** con toggle y persistencia de preferencia
- 🔌 **API REST completa** para integraciones externas
- 📋 **Copiar al portapapeles** con un solo clic
- 🎯 **Filtrado inteligente** de IPs administrativas vs. IPs para clientes
- 🏓 **Verificación por ping** para detectar dispositivos no registrados (opcional)
- 🛑 **Cancelar verificación** en cualquier momento conservando resultados parciales
- 📊 **Estadísticas en tiempo real** de IPs disponibles y en uso
- 🔐 **Integración nativa** con UISP CRM y UNMS
- 🪝 **Soporte para webhooks** y eventos personalizados

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
"displayName": "SIIP - Buscador de IP's Disponibles UISP",
"description": "Este plugin permite buscar IP's disponibles en UISP (UNMS) y asignarlas a los clientes en UCRM. Evitando así la asignación de IP's duplicadas y mejorando la gestión de direcciones IP en la red.",
"url": "https://siip.mx",
"version": "1.3.2",
"version": "1.3.3",
"ucrmVersionCompliancy": {
"min": "1.0.0",
"max": null

View File

@ -245,7 +245,8 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
box-sizing: border-box;
}
:root {
/* Tema Oscuro (por defecto) */
[data-theme="dark"] {
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
--success-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
@ -258,6 +259,20 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
--shadow-xl: 0 25px 80px rgba(0, 0, 0, 0.4);
}
/* Tema Claro */
[data-theme="light"] {
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
--success-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
--dark-bg: #f7fafc;
--card-bg: rgba(255, 255, 255, 0.95);
--card-border: rgba(0, 0, 0, 0.1);
--text-primary: #1a202c;
--text-secondary: #4a5568;
--shadow-lg: 0 10px 40px rgba(0, 0, 0, 0.08);
--shadow-xl: 0 15px 50px rgba(0, 0, 0, 0.12);
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: var(--dark-bg);
@ -271,6 +286,7 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
radial-gradient(circle at 20% 50%, rgba(102, 126, 234, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 80%, rgba(245, 87, 108, 0.15) 0%, transparent 50%);
background-attachment: fixed;
transition: background 0.3s ease, color 0.3s ease;
}
.container {
@ -556,9 +572,9 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
.ip-address {
font-family: 'Courier New', monospace;
font-weight: 600;
font-size: 1rem;
color: #4facfe;
font-weight: 900;
font-size: 1.7rem;
color: #2095fbff;
}
.btn-copy {
@ -666,6 +682,67 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
.stats {
grid-template-columns: 1fr;
}
/* Optimizaciones para tabla en móvil */
th, td {
padding: 10px 6px;
font-size: 0.8rem;
}
/* Ocultar columnas # y Tipo de IP en móvil */
th:first-child,
td:first-child,
th:nth-child(3),
td:nth-child(3) {
display: none;
}
/* Contenedor de IP con badge debajo */
.ip-cell-mobile {
display: flex;
flex-direction: column;
gap: 6px;
}
.ip-address {
font-size: 0.85rem;
}
.ip-type-badge {
font-size: 0.65rem;
padding: 3px 8px;
align-self: flex-start;
}
.status-badge {
font-size: 0.7rem;
padding: 4px 8px;
white-space: normal;
line-height: 1.3;
}
.btn-copy {
padding: 8px 12px;
font-size: 0.75rem;
}
/* Hacer la tabla más compacta */
.table-container {
font-size: 0.85rem;
}
/* Ajustar anchos de columnas en móvil (3 columnas) */
th:nth-child(2), td:nth-child(2) { /* Dirección IP + Tipo */
width: 40%;
}
th:nth-child(4), td:nth-child(4) { /* Estado */
width: 35%;
}
th:nth-child(5), td:nth-child(5) { /* Acción */
width: 25%;
}
}
.status-badge {
@ -730,9 +807,49 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
width: 100%;
}
}
/* Botón de cambio de tema */
.theme-toggle {
position: fixed;
top: 20px;
right: 20px;
width: 50px;
height: 50px;
border-radius: 50%;
background: var(--card-bg);
border: 2px solid var(--card-border);
color: var(--text-primary);
font-size: 1.5rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: var(--shadow-lg);
z-index: 1000;
}
.theme-toggle:hover {
transform: scale(1.1) rotate(15deg);
box-shadow: var(--shadow-xl);
}
@media (max-width: 768px) {
.theme-toggle {
width: 45px;
height: 45px;
font-size: 1.3rem;
top: 15px;
right: 15px;
}
}
</style>
</head>
<body>
<body data-theme="dark">
<button id="themeToggle" class="theme-toggle" title="Cambiar tema">
<span id="themeIcon">🌙</span>
</button>
<div class="container">
<div class="header">
<div class="logo">🌐</div>
@ -767,14 +884,14 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
<div class="input-group" style="flex: 0 0 auto; min-width: auto;">
<label class="checkbox-label">
<input type="checkbox" id="hideAdmin" name="hide_admin" style="width: auto; padding: 0; margin: 0;">
<span style="color: var(--text-secondary); font-size: 0.9rem;">👁️ Ocultar Admin IPs</span>
<span style="color: var(--text-secondary); font-size: 0.9rem;">👁️ Ocultar IP's de Administración</span>
</label>
</div>
<div class="input-group" id="pingLimitContainer" style="flex: 0 0 auto; min-width: 150px;">
<label for="pingLimit">Límite de IPs</label>
<select id="pingLimit" name="ping_limit" style="width: 100%; padding: 16px; background: rgba(255, 255, 255, 0.05); border: 2px solid var(--card-border); border-radius: 12px; color: var(--text-primary); font-size: 1rem;">
<option value="0">Todas (Lento)</option>
<option value="5">5 IPs (Rápido)</option>
<option value="5" selected>5 IPs (Rápido)</option>
<option value="10">10 IPs</option>
<option value="20">20 IPs</option>
</select>
@ -852,6 +969,29 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
const cancelBtn = document.getElementById('cancelBtn');
let verificationCancelled = false;
// Theme toggle functionality
const themeToggle = document.getElementById('themeToggle');
const themeIcon = document.getElementById('themeIcon');
const body = document.body;
// Load saved theme or default to dark
const savedTheme = localStorage.getItem('theme') || 'dark';
body.setAttribute('data-theme', savedTheme);
updateThemeIcon(savedTheme);
// Theme toggle event
themeToggle.addEventListener('click', () => {
const currentTheme = body.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
body.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
updateThemeIcon(newTheme);
});
function updateThemeIcon(theme) {
themeIcon.textContent = theme === 'dark' ? '🌙' : '☀️';
}
// Toggle visibility of ping limit select
<?php if ($pingEnabled): ?>
const verifyPingCheckbox = document.getElementById('verifyPing');
@ -1015,12 +1155,19 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
if (isHidden) row.classList.add('hidden-row');
if (ipTypeLabel === 'Administración') row.classList.add('admin-row');
// Calcular índice visual (opcional, por ahora usamos conteo de filas)
// Calcular índice visual
const index = ipTableBody.children.length + 1;
row.innerHTML = `
<td>${index}</td>
<td><span class="ip-address">${ip}</span></td>
<td>
<div class="ip-cell-mobile">
<span class="ip-address">${ip}</span>
<span class="ip-type-badge ip-type-${ipTypeLabel === 'Administración' ? 'admin' : 'client'}">
${ipTypeLabel}
</span>
</div>
</td>
<td>
<span class="ip-type-badge ip-type-${ipTypeLabel === 'Administración' ? 'admin' : 'client'}">
${ipTypeLabel}

Binary file not shown.

View File

@ -5,7 +5,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'fd47782cbe549fa50c32e6a36c4c3852ca98ec66',
'reference' => '99d283dd3d70dde73fbcfd497ea5e9f562448847',
'name' => '__root__',
'dev' => false,
),
@ -16,7 +16,7 @@
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => 'fd47782cbe549fa50c32e6a36c4c3852ca98ec66',
'reference' => '99d283dd3d70dde73fbcfd497ea5e9f562448847',
'dev_requirement' => false,
),
'guzzlehttp/guzzle' => array(