Registro y elevación de componentes base (ADN, Scripts, Servicios)

This commit is contained in:
Ricardo Monla
2026-02-15 20:37:53 -03:00
parent 9e9bf62c68
commit c865a7772b
25279 changed files with 3354766 additions and 0 deletions
@@ -0,0 +1,112 @@
import { useGenericEntityStore } from '../stores/genericEntityStore'
import toast from 'react-hot-toast'
export interface ActionHandler {
(entity: any, ...args: any[]): Promise<void> | void
}
export interface ActionHandlers {
[key: string]: ActionHandler
}
export const createActionHandlers = (entityKey: string): ActionHandlers => {
const store = useGenericEntityStore()
return {
view: (entity: any) => {
// This will be handled by the modal system in EntityPage
console.log('View entity:', entity)
// The actual modal opening is handled in EntityPage.tsx
},
edit: (entity: any) => {
store.setEditingEntity?.(entity.id)
store.setShowEditForm?.(true)
},
delete: async (entity: any) => {
// This is now handled by the EntityPage component with custom confirmation
// The actual deletion logic is in EntityPage.tsx
},
changePassword: (entity: any) => {
// This will be handled by the modal system
console.log('Change password for:', entity)
},
toggleStatus: async (entity: any, isActive?: boolean) => {
const action = entity.is_active ? 'desactivar' : 'reactivar'
if (window.confirm(`¿Está seguro de ${action} este ${entityKey.slice(0, -1)}?`)) {
try {
await store.toggleEntityStatus?.(entity.id, !entity.is_active)
toast.success(`${entityKey.slice(0, -1)} ${action}do exitosamente`)
} catch (error) {
// Error handled by store
}
}
},
assign: (entity: any) => {
// This will be handled by the modal system
console.log('Assign entity:', entity)
},
unassign: async (entity: any, userId: number) => {
if (window.confirm('¿Está seguro de desasignar este recurso del usuario?')) {
try {
await store.unassignEntity?.(entity.id, userId)
} catch (error) {
// Error handled by store
}
}
}
}
}
export const getActionIcon = (actionKey: string, condition?: boolean): string => {
const icons: Record<string, string> = {
view: 'fa-eye',
edit: 'fa-edit',
delete: 'fa-trash',
changePassword: 'fa-key',
assign: 'fa-user-plus',
unassign: 'fa-user-minus'
}
if (actionKey === 'toggleStatus') {
return condition ? 'fa-ban' : 'fa-check'
}
return icons[actionKey] || 'fa-cog'
}
export const getActionColor = (actionKey: string, condition?: boolean): string => {
const colors: Record<string, string> = {
view: 'primary',
edit: 'warning',
delete: 'danger',
changePassword: 'info',
assign: 'success',
unassign: 'secondary'
}
if (actionKey === 'toggleStatus') {
return condition ? 'danger' : 'success'
}
return colors[actionKey] || 'secondary'
}
export const getActionLabel = (actionKey: string, entityName: string): string => {
const labels: Record<string, string> = {
view: `Ver ${entityName}`,
edit: 'Editar',
delete: `Eliminar ${entityName}`,
changePassword: 'Cambiar contraseña',
assign: 'Asignar Usuario',
unassign: 'Desasignar',
toggleStatus: 'Desactivar/Reactivar'
}
return labels[actionKey] || actionKey
}
@@ -0,0 +1,275 @@
// Utilidades compartidas para formateo y configuración de entidades
import React from 'react'
export interface BadgeConfig {
text: string
class: string
}
export interface EntityConfig {
formatters: Record<string, (value: any) => string>
icons: Record<string, string>
badges: Record<string, BadgeConfig>
categories?: Record<string, string>
roles?: Record<string, string>
departments?: Record<string, string>
}
export interface PartialEntityConfig extends Partial<EntityConfig> {
formatters: Record<string, (value: any) => string>
icons: Record<string, string>
badges: Record<string, BadgeConfig>
}
// Configuración específica para técnicos
export const tecnicoConfig: EntityConfig = {
formatters: {
role: (role: string) => ({
'admin': 'Administrador',
'technician': 'Técnico',
'viewer': 'Visualizador'
}[role] || role),
department: (dept: string) => ({
'dtic': 'DTIC',
'sistemas': 'Sistemas',
'redes': 'Redes',
'seguridad': 'Seguridad'
}[dept] || dept),
status: (status: boolean) => status ? 'Activo' : 'Inactivo',
date: (date: string) => new Date(date).toLocaleDateString('es-AR')
},
icons: {
'admin': 'fa-user-shield',
'technician': 'fa-user-cog',
'viewer': 'fa-user'
},
badges: {
'admin': { text: 'Administrador', class: 'bg-danger' },
'technician': { text: 'Técnico', class: 'bg-info' },
'viewer': { text: 'Visualizador', class: 'bg-secondary' },
'active': { text: 'Activo', class: 'bg-success' },
'inactive': { text: 'Inactivo', class: 'bg-warning' }
},
roles: {
'admin': 'Administrador',
'technician': 'Técnico',
'viewer': 'Visualizador'
},
departments: {
'dtic': 'DTIC',
'sistemas': 'Sistemas',
'redes': 'Redes',
'seguridad': 'Seguridad'
}
}
// Configuración específica para recursos
export const recursoConfig: EntityConfig = {
formatters: {
category: (category: string) => ({
'hardware': 'Hardware',
'software': 'Software',
'network': 'Redes',
'security': 'Seguridad',
'tools': 'Herramientas',
'facilities': 'Instalaciones'
}[category] || category),
status: (status: string) => ({
'available': 'Disponible',
'assigned': 'Asignado',
'maintenance': 'Mantenimiento',
'retired': 'Retirado'
}[status] || status),
statusCategory: (entity: any) => {
const statusText = ({
'available': 'Disponible',
'assigned': 'Asignado',
'maintenance': 'Mantenimiento',
'retired': 'Retirado'
} as Record<string, string>)[entity.status] || entity.status;
const categoryText = ({
'hardware': 'Hardware',
'software': 'Software',
'network': 'Redes',
'security': 'Seguridad',
'tools': 'Herramientas',
'facilities': 'Instalaciones'
} as Record<string, string>)[entity.category] || entity.category;
return `${statusText} - ${categoryText}`;
},
modeloSerie: (entity: any) => {
const model = entity.model || '';
const serie = entity.serial_number || '';
if (model && serie) return `${model} - ${serie}`;
if (model) return model;
if (serie) return serie;
return '-';
},
recursoModeloSerie: (entity: any) => {
const name = entity.name || '';
const model = entity.model || '';
const serie = entity.serial_number || '';
let modelSerie = '';
if (model && serie) modelSerie = `${model} - ${serie}`;
else if (model) modelSerie = model;
else if (serie) modelSerie = serie;
else modelSerie = '-';
return `${name}\n${modelSerie}`;
},
formatRelatedTasks: (entity: any) => {
const relatedTasks = entity.related_tasks || []
if (!Array.isArray(relatedTasks) || relatedTasks.length === 0) {
return 'Sin tareas relacionadas'
}
const firstTask = relatedTasks[0]
const remainingCount = relatedTasks.length - 1
let result = firstTask.title || 'Sin título'
if (remainingCount > 0) {
result += ` y ${remainingCount} tarea${remainingCount !== 1 ? 's' : ''} más`
}
return result
},
date: (date: string) => new Date(date).toLocaleDateString('es-AR')
},
icons: {
'hardware': 'fa-laptop',
'software': 'fa-key',
'network': 'fa-router',
'security': 'fa-shield-alt',
'tools': 'fa-tools',
'facilities': 'fa-building'
},
badges: {
'available': { text: 'Disponible', class: 'bg-success' },
'assigned': { text: 'Asignado', class: 'bg-info' },
'maintenance': { text: 'Mantenimiento', class: 'bg-warning' },
'retired': { text: 'Retirado', class: 'bg-secondary' }
},
categories: {
'hardware': 'Hardware',
'software': 'Software',
'network': 'Redes',
'security': 'Seguridad',
'tools': 'Herramientas',
'facilities': 'Instalaciones'
}
}
// Clase utilitaria para trabajar con configuraciones de entidades
export class EntityUtils {
constructor(private config: EntityConfig) {}
formatValue(key: string, value: any): string {
const formatter = this.config.formatters[key]
return formatter ? formatter(value) : String(value)
}
getIcon(key: string): string {
return this.config.icons[key] || 'fa-question'
}
getBadge(status: string): BadgeConfig {
return this.config.badges[status] || { text: status, class: 'bg-secondary' }
}
getCategoryIcon(category: string): string {
return this.config.icons[category] || 'fa-box'
}
formatRole(role: string): string {
return this.config.roles?.[role] || role
}
formatDepartment(dept: string): string {
return this.config.departments?.[dept] || dept
}
formatCategory(category: string): string {
return this.config.categories?.[category] || category
}
formatPriority(priority: string): string {
return this.config.formatters.priority(priority)
}
formatStatus(status: string): string {
return this.config.formatters.status(status)
}
formatDate(dateString: string): string {
return this.config.formatters.date(dateString)
}
}
// Configuración específica para tareas
export const tareaConfig: EntityConfig = {
formatters: {
priority: (priority: string) => ({
'low': 'Baja',
'medium': 'Media',
'high': 'Alta',
'urgent': 'Urgente'
}[priority] || priority),
status: (status: string) => ({
'pending': 'Pendiente',
'in_progress': 'En Progreso',
'completed': 'Completada',
'cancelled': 'Cancelada'
}[status] || status),
date: (date: string) => new Date(date).toLocaleDateString('es-AR')
},
icons: {
'low': 'fa-arrow-down',
'medium': 'fa-minus',
'high': 'fa-arrow-up',
'urgent': 'fa-exclamation-triangle',
'pending': 'fa-clock',
'in_progress': 'fa-play',
'completed': 'fa-check',
'cancelled': 'fa-times'
},
badges: {
'pending': { text: 'Pendiente', class: 'badge-status-pending' },
'in_progress': { text: 'En Progreso', class: 'badge-status-in_progress' },
'completed': { text: 'Completada', class: 'badge-status-completed' },
'cancelled': { text: 'Cancelada', class: 'badge-status-cancelled' },
'low': { text: 'Baja', class: 'badge-priority-low' },
'medium': { text: 'Media', class: 'badge-priority-medium' },
'high': { text: 'Alta', class: 'badge-priority-high' },
'urgent': { text: 'Urgente', class: 'badge-priority-urgent' }
}
}
// Instancias pre-configuradas
export const tecnicoUtils = new EntityUtils(tecnicoConfig)
export const recursoUtils = new EntityUtils(recursoConfig)
export const tareaUtils = new EntityUtils(tareaConfig)
// Función helper para crear utilidades personalizadas
export const createEntityUtils = (config: PartialEntityConfig) => new EntityUtils(config as EntityConfig)