feat: Version 1.10.0 - Refactorizacion, optimizacion UI y mejoras de seguridad
- Extraccion de helpers en backend: audit_helpers.py, helpers.py - Modularizacion de schemas en archivos individuales por dominio - Reduccion de audit.py en 953 lineas (74% del archivo) - Reduccion de tickets.py en 655 lineas (60% del archivo) - Expansion de auth.py con recuperacion de contrasenia y tokens - Nuevos modulos: core/email.py, core/cache.py - Reorganizacion de scripts a backend/scripts/ - Frontend: refactorizacion de audit page con array-driven components - Frontend: correccion de 11 errores ortograficos en tickets page - Frontend: proxy Docker corregido en vite.config.js - Frontend: nuevas rutas forgot-password, reset-password, organization, profile - Nuevas utilidades TS: colorUtils.ts, dateFormats.ts - 5 nuevos archivos de tests unitarios en backend/tests/unit/ - Eliminacion de 3 scripts temporales de prueba - Documentacion tecnica: CAMBIOS_v1.10.0.md, OPTIMIZACIONES_RENDIMIENTO.md
This commit is contained in:
@@ -160,14 +160,17 @@
|
||||
|
||||
<!-- User info -->
|
||||
<div class="px-4 py-4 border-t border-gray-200">
|
||||
<div class="flex items-center space-x-3">
|
||||
<div class="w-8 h-8 bg-primary-100 rounded-full flex items-center justify-center">
|
||||
<a
|
||||
href="/profile"
|
||||
class="flex items-center space-x-3 rounded-lg p-1 -m-1 hover:bg-gray-100 transition-colors group"
|
||||
>
|
||||
<div class="w-8 h-8 bg-primary-100 rounded-full flex items-center justify-center shrink-0">
|
||||
<span class="text-primary-600 text-sm font-medium">
|
||||
{$auth.user?.first_name?.[0]}{$auth.user?.last_name?.[0]}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium text-gray-900 truncate">
|
||||
<p class="text-sm font-medium text-gray-900 truncate group-hover:text-primary-600">
|
||||
{$auth.user?.first_name}
|
||||
{$auth.user?.last_name}
|
||||
</p>
|
||||
@@ -181,12 +184,20 @@
|
||||
: 'Auditor'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
class="w-4 h-4 text-gray-400 group-hover:text-primary-500 shrink-0"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Version info -->
|
||||
<div class="px-4 py-2 border-t border-gray-100">
|
||||
<p class="text-xs text-gray-400 text-center">v1.6.0</p>
|
||||
<p class="text-xs text-gray-400 text-center">v1.9.0</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
73
frontend-internal/src/lib/utils/colorUtils.ts
Normal file
73
frontend-internal/src/lib/utils/colorUtils.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
// Utilidades de colores para diferentes estados y severidades
|
||||
type ColorType = 'severity' | 'status' | 'action' | 'priority';
|
||||
|
||||
const COLOR_MAPS = {
|
||||
severity: {
|
||||
'critical': 'bg-red-600 text-white',
|
||||
'high': 'bg-orange-600 text-white',
|
||||
'medium': 'bg-yellow-500 text-white',
|
||||
'low': 'bg-blue-600 text-white'
|
||||
},
|
||||
status: {
|
||||
'active': 'bg-blue-600 text-white',
|
||||
'open': 'bg-blue-600 text-white',
|
||||
'resolved': 'bg-green-600 text-white',
|
||||
'closed': 'bg-green-600 text-white',
|
||||
'investigating': 'bg-yellow-500 text-white',
|
||||
'new': 'bg-blue-500 text-white',
|
||||
'in_progress': 'bg-purple-600 text-white',
|
||||
'waiting_customer': 'bg-orange-500 text-white',
|
||||
'reopened': 'bg-red-500 text-white'
|
||||
},
|
||||
action: {
|
||||
'delete': 'bg-red-600 text-white',
|
||||
'update': 'bg-blue-600 text-white',
|
||||
'login': 'bg-indigo-600 text-white',
|
||||
'logout': 'bg-indigo-600 text-white',
|
||||
'create': 'bg-green-600 text-white',
|
||||
'failed': 'bg-red-500 text-white'
|
||||
},
|
||||
priority: {
|
||||
'urgent': 'bg-red-600 text-white',
|
||||
'high': 'bg-orange-500 text-white',
|
||||
'medium': 'bg-yellow-500 text-white',
|
||||
'low': 'bg-blue-500 text-white'
|
||||
}
|
||||
};
|
||||
|
||||
export function getColorClass(value: string, type: ColorType = 'status'): string {
|
||||
const map = COLOR_MAPS[type];
|
||||
const key = value?.toLowerCase();
|
||||
|
||||
if (type === 'action') {
|
||||
const matchKey = Object.keys(map).find(k => key?.includes(k));
|
||||
return map[matchKey as keyof typeof map] || 'bg-gray-600 text-white';
|
||||
}
|
||||
|
||||
return map[key as keyof typeof map] || 'bg-gray-600 text-white';
|
||||
}
|
||||
|
||||
export function getStatusIcon(status: string): string {
|
||||
const icons = {
|
||||
'active': '🔴',
|
||||
'open': '📂',
|
||||
'resolved': '✅',
|
||||
'closed': '🔒',
|
||||
'investigating': '🔍',
|
||||
'new': '🆕',
|
||||
'in_progress': '⚙️',
|
||||
'waiting_customer': '⏳',
|
||||
'reopened': '🔄'
|
||||
};
|
||||
return icons[status?.toLowerCase() as keyof typeof icons] || '📋';
|
||||
}
|
||||
|
||||
export function getSeverityIcon(severity: string): string {
|
||||
const icons = {
|
||||
'critical': '🚨',
|
||||
'high': '⚠️',
|
||||
'medium': '⚡',
|
||||
'low': 'ℹ️'
|
||||
};
|
||||
return icons[severity?.toLowerCase() as keyof typeof icons] || '📊';
|
||||
}
|
||||
77
frontend-internal/src/lib/utils/dateFormats.ts
Normal file
77
frontend-internal/src/lib/utils/dateFormats.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
// Utilidades de formato de fecha
|
||||
export type DateFormat = 'full' | 'short' | 'simple' | 'time';
|
||||
|
||||
export function formatDate(dateString: string, format: DateFormat = 'full'): string {
|
||||
const date = new Date(dateString);
|
||||
const today = new Date();
|
||||
const isToday = date.toDateString() === today.toDateString();
|
||||
|
||||
const formats = {
|
||||
full: () => date.toLocaleString('es-MX', {
|
||||
year: 'numeric', month: 'short', day: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit'
|
||||
}),
|
||||
short: () => isToday
|
||||
? date.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit' })
|
||||
: date.toLocaleDateString('es-MX', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }),
|
||||
simple: () => date.toLocaleDateString('es-MX', {
|
||||
day: '2-digit', month: '2-digit', year: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit'
|
||||
}),
|
||||
time: () => date.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit' })
|
||||
};
|
||||
|
||||
return formats[format]();
|
||||
}
|
||||
|
||||
export function getRelativeTime(dateString: string): string {
|
||||
const now = new Date().getTime();
|
||||
const then = new Date(dateString).getTime();
|
||||
const diffMs = now - then;
|
||||
const diffMins = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
const diffDays = Math.floor(diffMs / 86400000);
|
||||
|
||||
if (diffMins < 1) return 'Hace un momento';
|
||||
if (diffMins < 60) return `Hace ${diffMins} minuto${diffMins > 1 ? 's' : ''}`;
|
||||
if (diffHours < 24) return `Hace ${diffHours} hora${diffHours > 1 ? 's' : ''}`;
|
||||
if (diffDays < 7) return `Hace ${diffDays} día${diffDays > 1 ? 's' : ''}`;
|
||||
return formatDate(dateString, 'short');
|
||||
}
|
||||
|
||||
export function getDateRangeForPeriod(periodFilter: string, customDateFrom?: string, customDateTo?: string): { from: string; to: string } {
|
||||
const now = new Date();
|
||||
let from: Date;
|
||||
let to: Date = new Date();
|
||||
|
||||
switch (periodFilter) {
|
||||
case 'today':
|
||||
from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0));
|
||||
break;
|
||||
case 'yesterday':
|
||||
from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 1, 0, 0, 0));
|
||||
to = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0));
|
||||
break;
|
||||
case 'last7days':
|
||||
from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 7, 0, 0, 0));
|
||||
break;
|
||||
case 'last30days':
|
||||
from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() - 30, 0, 0, 0));
|
||||
break;
|
||||
case 'custom':
|
||||
if (!customDateFrom || !customDateTo) {
|
||||
from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0));
|
||||
} else {
|
||||
from = new Date(customDateFrom + 'T00:00:00Z');
|
||||
to = new Date(customDateTo + 'T23:59:59Z');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
from = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0));
|
||||
}
|
||||
|
||||
return {
|
||||
from: from.toISOString(),
|
||||
to: to.toISOString()
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user