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:
parent
99d283dd3d
commit
1117b2ade2
11
CHANGELOG.md
11
CHANGELOG.md
@ -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
|
## [1.3.2] - 2025-11-26
|
||||||
|
|
||||||
### ✨ Añadido
|
### ✨ Añadido
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# SIIP - Buscador de IP's Disponibles UISP
|
# SIIP - Buscador de IP's Disponibles UISP
|
||||||
|
|
||||||
[](manifest.json)
|
[](manifest.json)
|
||||||
[](https://uisp.com/)
|
[](https://uisp.com/)
|
||||||
[](https://uisp.com/)
|
[](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)
|
- 🔍 **Búsqueda de IPs disponibles** por segmento de red (172.16.X.x)
|
||||||
- 🌐 **Interfaz web moderna** con diseño responsive y atractivo
|
- 🌐 **Interfaz web moderna** con diseño responsive y atractivo
|
||||||
|
- 🌓 **Modo Claro/Oscuro** con toggle y persistencia de preferencia
|
||||||
- 🔌 **API REST completa** para integraciones externas
|
- 🔌 **API REST completa** para integraciones externas
|
||||||
- 📋 **Copiar al portapapeles** con un solo clic
|
- 📋 **Copiar al portapapeles** con un solo clic
|
||||||
- 🎯 **Filtrado inteligente** de IPs administrativas vs. IPs para clientes
|
- 🎯 **Filtrado inteligente** de IPs administrativas vs. IPs para clientes
|
||||||
- 🏓 **Verificación por ping** para detectar dispositivos no registrados (opcional)
|
- 🏓 **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
|
- 📊 **Estadísticas en tiempo real** de IPs disponibles y en uso
|
||||||
- 🔐 **Integración nativa** con UISP CRM y UNMS
|
- 🔐 **Integración nativa** con UISP CRM y UNMS
|
||||||
- 🪝 **Soporte para webhooks** y eventos personalizados
|
- 🪝 **Soporte para webhooks** y eventos personalizados
|
||||||
|
|||||||
1313
data/plugin.log
1313
data/plugin.log
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,7 @@
|
|||||||
"displayName": "SIIP - Buscador de IP's Disponibles UISP",
|
"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.",
|
"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",
|
"url": "https://siip.mx",
|
||||||
"version": "1.3.2",
|
"version": "1.3.3",
|
||||||
"ucrmVersionCompliancy": {
|
"ucrmVersionCompliancy": {
|
||||||
"min": "1.0.0",
|
"min": "1.0.0",
|
||||||
"max": null
|
"max": null
|
||||||
|
|||||||
165
public.php
165
public.php
@ -245,7 +245,8 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
/* Tema Oscuro (por defecto) */
|
||||||
|
[data-theme="dark"] {
|
||||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
--secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
--success-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 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);
|
--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 {
|
body {
|
||||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
background: var(--dark-bg);
|
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 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%);
|
radial-gradient(circle at 80% 80%, rgba(245, 87, 108, 0.15) 0%, transparent 50%);
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
|
transition: background 0.3s ease, color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
@ -556,9 +572,9 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
|
|||||||
|
|
||||||
.ip-address {
|
.ip-address {
|
||||||
font-family: 'Courier New', monospace;
|
font-family: 'Courier New', monospace;
|
||||||
font-weight: 600;
|
font-weight: 900;
|
||||||
font-size: 1rem;
|
font-size: 1.7rem;
|
||||||
color: #4facfe;
|
color: #2095fbff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-copy {
|
.btn-copy {
|
||||||
@ -666,6 +682,67 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
|
|||||||
.stats {
|
.stats {
|
||||||
grid-template-columns: 1fr;
|
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 {
|
.status-badge {
|
||||||
@ -730,9 +807,49 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
|
|||||||
width: 100%;
|
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>
|
</style>
|
||||||
</head>
|
</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="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="logo">🌐</div>
|
<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;">
|
<div class="input-group" style="flex: 0 0 auto; min-width: auto;">
|
||||||
<label class="checkbox-label">
|
<label class="checkbox-label">
|
||||||
<input type="checkbox" id="hideAdmin" name="hide_admin" style="width: auto; padding: 0; margin: 0;">
|
<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>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group" id="pingLimitContainer" style="flex: 0 0 auto; min-width: 150px;">
|
<div class="input-group" id="pingLimitContainer" style="flex: 0 0 auto; min-width: 150px;">
|
||||||
<label for="pingLimit">Límite de IPs</label>
|
<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;">
|
<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="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="10">10 IPs</option>
|
||||||
<option value="20">20 IPs</option>
|
<option value="20">20 IPs</option>
|
||||||
</select>
|
</select>
|
||||||
@ -852,6 +969,29 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
|
|||||||
const cancelBtn = document.getElementById('cancelBtn');
|
const cancelBtn = document.getElementById('cancelBtn');
|
||||||
let verificationCancelled = false;
|
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
|
// Toggle visibility of ping limit select
|
||||||
<?php if ($pingEnabled): ?>
|
<?php if ($pingEnabled): ?>
|
||||||
const verifyPingCheckbox = document.getElementById('verifyPing');
|
const verifyPingCheckbox = document.getElementById('verifyPing');
|
||||||
@ -1015,12 +1155,19 @@ $pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePing
|
|||||||
if (isHidden) row.classList.add('hidden-row');
|
if (isHidden) row.classList.add('hidden-row');
|
||||||
if (ipTypeLabel === 'Administración') row.classList.add('admin-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;
|
const index = ipTableBody.children.length + 1;
|
||||||
|
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<td>${index}</td>
|
<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>
|
<td>
|
||||||
<span class="ip-type-badge ip-type-${ipTypeLabel === 'Administración' ? 'admin' : 'client'}">
|
<span class="ip-type-badge ip-type-${ipTypeLabel === 'Administración' ? 'admin' : 'client'}">
|
||||||
${ipTypeLabel}
|
${ipTypeLabel}
|
||||||
|
|||||||
Binary file not shown.
4
vendor/composer/installed.php
vendored
4
vendor/composer/installed.php
vendored
@ -5,7 +5,7 @@
|
|||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'fd47782cbe549fa50c32e6a36c4c3852ca98ec66',
|
'reference' => '99d283dd3d70dde73fbcfd497ea5e9f562448847',
|
||||||
'name' => '__root__',
|
'name' => '__root__',
|
||||||
'dev' => false,
|
'dev' => false,
|
||||||
),
|
),
|
||||||
@ -16,7 +16,7 @@
|
|||||||
'type' => 'library',
|
'type' => 'library',
|
||||||
'install_path' => __DIR__ . '/../../',
|
'install_path' => __DIR__ . '/../../',
|
||||||
'aliases' => array(),
|
'aliases' => array(),
|
||||||
'reference' => 'fd47782cbe549fa50c32e6a36c4c3852ca98ec66',
|
'reference' => '99d283dd3d70dde73fbcfd497ea5e9f562448847',
|
||||||
'dev_requirement' => false,
|
'dev_requirement' => false,
|
||||||
),
|
),
|
||||||
'guzzlehttp/guzzle' => array(
|
'guzzlehttp/guzzle' => array(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user