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
|
||||
|
||||
### ✨ Añadido
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# SIIP - Buscador de IP's Disponibles UISP
|
||||
|
||||
[](manifest.json)
|
||||
[](manifest.json)
|
||||
[](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)
|
||||
- 🌐 **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
|
||||
|
||||
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",
|
||||
"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
|
||||
|
||||
165
public.php
165
public.php
@ -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.
4
vendor/composer/installed.php
vendored
4
vendor/composer/installed.php
vendored
@ -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(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user