796 lines
53 KiB
Plaintext
796 lines
53 KiB
Plaintext
<%- include('../partials/header') %>
|
|
<% title = 'Herramientas & Sets'; %>
|
|
|
|
<!-- Alertas Flash -->
|
|
<% if (success_msg && success_msg.length > 0) { %>
|
|
<% success_msg.forEach(msg => { %>
|
|
<div class="alert alert-success alert-dismissible fade show rounded-3 border-0 shadow-sm mb-3" role="alert">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-check-circle me-2 fs-5 text-success"></i>
|
|
<div><%= msg %></div>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
<% }) %>
|
|
<% } %>
|
|
<% if (error_msg && error_msg.length > 0) { %>
|
|
<% error_msg.forEach(msg => { %>
|
|
<div class="alert alert-danger alert-dismissible fade show rounded-3 border-0 shadow-sm mb-3" role="alert">
|
|
<div class="d-flex align-items-center">
|
|
<i class="fas fa-exclamation-triangle me-2 fs-5 text-danger"></i>
|
|
<div><%= msg %></div>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
<% }) %>
|
|
<% } %>
|
|
|
|
<!-- Cabecera de Página -->
|
|
<div class="page-header d-flex flex-wrap justify-content-between align-items-center gap-3 mb-4">
|
|
<div>
|
|
<div class="d-flex align-items-center gap-2 mb-1">
|
|
<h1 class="page-title h3 mb-0">Herramientas & Sets de Campo (FSM)</h1>
|
|
<span class="badge bg-warning-subtle text-dark border px-2.5 py-1" style="font-size: 0.75rem;">
|
|
<i class="fas fa-toolbox text-warning me-1"></i> Control de Equipamiento
|
|
</span>
|
|
</div>
|
|
<p class="text-muted small mb-0">Administra el inventario de instrumental, kits especializados y asignaciones fijas o por visita presencial a técnicos.</p>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<button type="button" class="btn btn-outline-primary btn-sm d-inline-flex align-items-center gap-1.5 shadow-sm" data-bs-toggle="modal" data-bs-target="#modalNuevaHerramienta">
|
|
<i class="fas fa-plus"></i>
|
|
<span>Nueva Herramienta</span>
|
|
</button>
|
|
<button type="button" class="btn btn-warning btn-sm fw-bold d-inline-flex align-items-center gap-1.5 shadow-sm text-dark" data-bs-toggle="modal" data-bs-target="#modalNuevoSet">
|
|
<i class="fas fa-layer-group"></i>
|
|
<span>Nuevo Set / Kit</span>
|
|
</button>
|
|
<button type="button" class="btn btn-primary btn-sm d-inline-flex align-items-center gap-1.5 shadow-sm" data-bs-toggle="modal" data-bs-target="#modalAsignar">
|
|
<i class="fas fa-user-tag"></i>
|
|
<span>Asignar a Técnico</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tarjetas KPI -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="app-card p-3 d-flex align-items-center gap-3">
|
|
<div class="icon-circle bg-primary-subtle text-primary" style="width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem;">
|
|
<i class="fas fa-tools"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small text-uppercase fw-semibold" style="font-size: 0.7rem; letter-spacing: 0.5px;">Total Instrumentos</div>
|
|
<div class="fs-4 fw-bold text-dark"><%= stats.total %></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="app-card p-3 d-flex align-items-center gap-3">
|
|
<div class="icon-circle bg-success-subtle text-success" style="width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem;">
|
|
<i class="fas fa-check-double"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small text-uppercase fw-semibold" style="font-size: 0.7rem; letter-spacing: 0.5px;">Operativas</div>
|
|
<div class="fs-4 fw-bold text-success"><%= stats.operativo %></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="app-card p-3 d-flex align-items-center gap-3">
|
|
<div class="icon-circle bg-warning-subtle text-warning" style="width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem;">
|
|
<i class="fas fa-wrench"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small text-uppercase fw-semibold" style="font-size: 0.7rem; letter-spacing: 0.5px;">Mantenimiento / Baja</div>
|
|
<div class="fs-4 fw-bold text-warning"><%= (stats.en_mantenimiento || 0) + (stats.danado || 0) + (stats.baja || 0) %></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-6 col-xl-3">
|
|
<div class="app-card p-3 d-flex align-items-center gap-3">
|
|
<div class="icon-circle bg-info-subtle text-info" style="width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 1.25rem;">
|
|
<i class="fas fa-boxes-stacked"></i>
|
|
</div>
|
|
<div>
|
|
<div class="text-muted small text-uppercase fw-semibold" style="font-size: 0.7rem; letter-spacing: 0.5px;">Sets / Kits Configurados</div>
|
|
<div class="fs-4 fw-bold text-info"><%= sets.length %></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pestañas de Navegación -->
|
|
<ul class="nav nav-pills custom-pills mb-3 gap-2" id="herramientasTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active px-3 py-2 fw-semibold d-flex align-items-center gap-2" id="tab-sets-btn" data-bs-toggle="pill" data-bs-target="#tab-sets" type="button" role="tab">
|
|
<i class="fas fa-layer-group"></i>
|
|
<span>Sets / Kits de Herramientas (<%= sets.length %>)</span>
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link px-3 py-2 fw-semibold d-flex align-items-center gap-2" id="tab-herramientas-btn" data-bs-toggle="pill" data-bs-target="#tab-herramientas" type="button" role="tab">
|
|
<i class="fas fa-toolbox"></i>
|
|
<span>Inventario Detallado (<%= herramientas.length %>)</span>
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link px-3 py-2 fw-semibold d-flex align-items-center gap-2" id="tab-asignaciones-btn" data-bs-toggle="pill" data-bs-target="#tab-asignaciones" type="button" role="tab">
|
|
<i class="fas fa-user-check"></i>
|
|
<span>Asignaciones a Técnicos (<%= asignaciones.length %>)</span>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="tab-content" id="herramientasTabsContent">
|
|
|
|
<!-- ============================================== -->
|
|
<!-- TAB 1: SETS / KITS DE HERRAMIENTAS -->
|
|
<!-- ============================================== -->
|
|
<div class="tab-pane fade show active" id="tab-sets" role="tabpanel">
|
|
<div class="row g-3">
|
|
<% if (sets.length === 0) { %>
|
|
<div class="col-12">
|
|
<div class="app-card p-5 text-center">
|
|
<i class="fas fa-toolbox text-muted fs-1 mb-2"></i>
|
|
<h5 class="fw-bold text-dark">No hay Sets o Kits creados</h5>
|
|
<p class="text-muted small mb-3">Agrupa herramientas físicas en sets reutilizables para despacharlos rápidamente en terreno.</p>
|
|
<button type="button" class="btn btn-warning btn-sm px-3 fw-bold" data-bs-toggle="modal" data-bs-target="#modalNuevoSet">
|
|
<i class="fas fa-plus me-1"></i> Crear primer Kit de Herramientas
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<% } %>
|
|
|
|
<% sets.forEach(s => { %>
|
|
<div class="col-md-6 col-xl-4">
|
|
<div class="app-card h-100 p-3.5 d-flex flex-column justify-content-between border shadow-sm rounded-3">
|
|
<div>
|
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
|
<span class="badge bg-dark text-white font-monospace" style="font-size: 0.7rem;">
|
|
<%= s.codigo || 'KIT-' + s.id %>
|
|
</span>
|
|
<span class="badge <%= s.is_active ? 'bg-success' : 'bg-secondary' %>" style="font-size: 0.68rem;">
|
|
<%= s.is_active ? 'Activo' : 'Inactivo' %>
|
|
</span>
|
|
</div>
|
|
<h5 class="fw-bold text-dark mb-1"><%= s.nombre %></h5>
|
|
<p class="text-muted small mb-3" style="font-size: 0.8rem;"><%= s.descripcion || 'Sin descripción detallada.' %></p>
|
|
|
|
<div class="border-top pt-2.5 mb-3">
|
|
<div class="d-flex align-items-center justify-content-between mb-2">
|
|
<span class="small fw-bold text-dark" style="font-size: 0.75rem;">
|
|
<i class="fas fa-tools text-primary me-1"></i> Herramientas incluidas (<%= s.herramientas ? s.herramientas.length : 0 %>)
|
|
</span>
|
|
<span class="badge bg-light text-muted border" style="font-size: 0.65rem;">
|
|
Cat: <%= s.categoria %>
|
|
</span>
|
|
</div>
|
|
|
|
<% if (s.herramientas && s.herramientas.length > 0) { %>
|
|
<ul class="list-unstyled mb-0 small" style="font-size: 0.78rem;">
|
|
<% s.herramientas.forEach(h => { %>
|
|
<li class="d-flex align-items-center justify-content-between py-1 border-bottom border-light">
|
|
<div class="text-truncate me-2">
|
|
<i class="fas fa-check text-success me-1.5" style="font-size: 0.7rem;"></i>
|
|
<strong><%= h.nombre %></strong>
|
|
<% if (h.marca) { %><span class="text-muted">(<%= h.marca %>)</span><% } %>
|
|
</div>
|
|
<span class="badge bg-light text-muted border font-monospace" style="font-size: 0.68rem;">
|
|
<%= h.codigo || h.numero_serie || 'SN-N/D' %>
|
|
</span>
|
|
</li>
|
|
<% }) %>
|
|
</ul>
|
|
<% } else { %>
|
|
<div class="small text-muted fst-italic">No tiene herramientas asociadas aún.</div>
|
|
<% } %>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="border-top pt-3 d-flex align-items-center justify-content-between mt-auto">
|
|
<button type="button" class="btn btn-sm btn-outline-primary d-inline-flex align-items-center gap-1" data-bs-toggle="modal" data-bs-target="#modalAsignarDirecto<%= s.id %>">
|
|
<i class="fas fa-user-plus"></i>
|
|
<span>Asignar</span>
|
|
</button>
|
|
<div class="d-flex align-items-center gap-1">
|
|
<button type="button" class="btn btn-sm btn-light border" data-bs-toggle="modal" data-bs-target="#modalEditarSet<%= s.id %>" title="Editar Kit">
|
|
<i class="fas fa-edit text-secondary"></i>
|
|
</button>
|
|
<form action="/admin/herramientas/sets/delete/<%= s.id %>" method="POST" onsubmit="return confirm('¿Eliminar este kit de herramientas?');" class="d-inline">
|
|
<button type="submit" class="btn btn-sm btn-light border text-danger" title="Eliminar Kit">
|
|
<i class="fas fa-trash-alt"></i>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MODAL EDITAR SET -->
|
|
<div class="modal fade" id="modalEditarSet<%= s.id %>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/sets/edit/<%= s.id %>" method="POST">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Editar Kit: <%= s.nombre %></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label small fw-bold">Nombre del Set / Kit *</label>
|
|
<input type="text" name="nombre" class="form-control" value="<%= s.nombre %>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Código Identificador</label>
|
|
<input type="text" name="codigo" class="form-control font-monospace" value="<%= s.codigo || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Categoría Operativa</label>
|
|
<select name="categoria" class="form-select">
|
|
<option value="cctv" <%= s.categoria === 'cctv' ? 'selected' : '' %>>Seguridad Electrónica & CCTV</option>
|
|
<option value="fibra_optica" <%= s.categoria === 'fibra_optica' ? 'selected' : '' %>>Fibra Óptica & Enlaces</option>
|
|
<option value="redes" <%= s.categoria === 'redes' ? 'selected' : '' %>>Redes & Cableado Estructurado</option>
|
|
<option value="electricidad" <%= s.categoria === 'electricidad' ? 'selected' : '' %>>Electricidad & Potencia</option>
|
|
<option value="general" <%= s.categoria === 'general' ? 'selected' : '' %>>Herramientas Generales</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Estado del Kit</label>
|
|
<select name="is_active" class="form-select">
|
|
<option value="1" <%= s.is_active ? 'selected' : '' %>>Activo (Disponible para despacho)</option>
|
|
<option value="0" <%= !s.is_active ? 'selected' : '' %>>Inactivo (Bloqueado temporalmente)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold">Descripción / Alcance</label>
|
|
<textarea name="descripcion" class="form-control" rows="2"><%= s.descripcion || '' %></textarea>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold mb-1">Seleccionar Herramientas del Inventario</label>
|
|
<div class="border rounded p-2.5" style="max-height: 200px; overflow-y: auto;">
|
|
<% const currentItemIds = (s.herramientas || []).map(item => item.id); %>
|
|
<% herramientas.forEach(h => { %>
|
|
<div class="form-check py-1">
|
|
<input class="form-check-input" type="checkbox" name="herramientas_ids" value="<%= h.id %>" id="checkSet<%= s.id %>_<%= h.id %>" <%= currentItemIds.includes(h.id) ? 'checked' : '' %>>
|
|
<label class="form-check-label small d-flex align-items-center justify-content-between" for="checkSet<%= s.id %>_<%= h.id %>">
|
|
<span><strong><%= h.nombre %></strong> <% if (h.marca) { %><span class="text-muted">(<%= h.marca %>)</span><% } %></span>
|
|
<span class="badge bg-light text-muted border font-monospace ms-2"><%= h.codigo || h.numero_serie || 'SN-N/D' %></span>
|
|
</label>
|
|
</div>
|
|
<% }) %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MODAL ASIGNAR DIRECTO -->
|
|
<div class="modal fade" id="modalAsignarDirecto<%= s.id %>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/asignar" method="POST">
|
|
<input type="hidden" name="set_id" value="<%= s.id %>">
|
|
<div class="modal-header bg-primary text-white">
|
|
<h5 class="modal-title fw-bold">Asignar Kit: <%= s.nombre %></h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Técnico Responsable *</label>
|
|
<select name="tecnico_id" class="form-select" required>
|
|
<option value="">-- Seleccionar Técnico --</option>
|
|
<% tecnicos.forEach(t => { %>
|
|
<option value="<%= t.id %>"><%= t.nombre %> (<%= t.email %>)</option>
|
|
<% }) %>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Tipo de Asignación</label>
|
|
<select name="tipo_asignacion" class="form-select">
|
|
<option value="dotacion_fija">Dotación Fija (Custodia permanente del técnico)</option>
|
|
<option value="visita_fsm">Asignación Temporal de Turno / Visita</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Observaciones de Entrega</label>
|
|
<textarea name="observaciones_entrega" class="form-control" rows="2" placeholder="Ej: Entregado completo con estuche y cargadores en buen estado"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary fw-bold">Confirmar Asignación</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }) %>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================== -->
|
|
<!-- TAB 2: INVENTARIO DETALLADO DE HERRAMIENTAS -->
|
|
<!-- ============================================== -->
|
|
<div class="tab-pane fade" id="tab-herramientas" role="tabpanel">
|
|
<div class="app-card border shadow-sm rounded-3 overflow-hidden">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0" style="font-size: 0.85rem;">
|
|
<thead class="table-light text-muted small text-uppercase">
|
|
<tr>
|
|
<th class="ps-3">Código</th>
|
|
<th>Herramienta / Instrumento</th>
|
|
<th>Categoría</th>
|
|
<th>Marca & Modelo</th>
|
|
<th>Nº Serie</th>
|
|
<th>Ubicación</th>
|
|
<th>Estado</th>
|
|
<th class="text-end pe-3">Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% if (herramientas.length === 0) { %>
|
|
<tr>
|
|
<td colspan="8" class="text-center py-4 text-muted">No hay herramientas registradas en el catálogo.</td>
|
|
</tr>
|
|
<% } %>
|
|
|
|
<% herramientas.forEach(h => { %>
|
|
<tr>
|
|
<td class="ps-3 font-monospace fw-bold text-dark"><%= h.codigo || 'HERR-' + h.id %></td>
|
|
<td>
|
|
<strong class="text-dark"><%= h.nombre %></strong>
|
|
<% if (h.notas) { %><div class="small text-muted text-truncate" style="max-width: 250px;"><%= h.notas %></div><% } %>
|
|
</td>
|
|
<td>
|
|
<span class="badge bg-light text-dark border"><%= h.categoria %></span>
|
|
</td>
|
|
<td>
|
|
<%= h.marca || '-' %> <%= h.modelo ? `(${h.modelo})` : '' %>
|
|
</td>
|
|
<td class="font-monospace text-muted"><%= h.numero_serie || 'N/D' %></td>
|
|
<td>
|
|
<i class="fas fa-warehouse text-muted me-1"></i> <%= h.ubicacion_bodega || 'Bodega Central' %>
|
|
</td>
|
|
<td>
|
|
<% if (h.estado === 'operativo') { %>
|
|
<span class="badge bg-success-subtle text-success border border-success-subtle"><i class="fas fa-check-circle me-1"></i> Operativo</span>
|
|
<% } else if (h.estado === 'en_mantenimiento') { %>
|
|
<span class="badge bg-warning-subtle text-warning border border-warning-subtle"><i class="fas fa-wrench me-1"></i> Mantenimiento</span>
|
|
<% } else if (h.estado === 'danado') { %>
|
|
<span class="badge bg-danger-subtle text-danger border border-danger-subtle"><i class="fas fa-times-circle me-1"></i> Dañado</span>
|
|
<% } else { %>
|
|
<span class="badge bg-secondary"><%= h.estado %></span>
|
|
<% } %>
|
|
</td>
|
|
<td class="text-end pe-3">
|
|
<button type="button" class="btn btn-sm btn-light border" data-bs-toggle="modal" data-bs-target="#modalEditarHerramienta<%= h.id %>" title="Editar">
|
|
<i class="fas fa-edit text-secondary"></i>
|
|
</button>
|
|
<form action="/admin/herramientas/delete/<%= h.id %>" method="POST" onsubmit="return confirm('¿Eliminar esta herramienta?');" class="d-inline">
|
|
<button type="submit" class="btn btn-sm btn-light border text-danger" title="Eliminar">
|
|
<i class="fas fa-trash-alt"></i>
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- MODAL EDITAR HERRAMIENTA -->
|
|
<div class="modal fade" id="modalEditarHerramienta<%= h.id %>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/edit/<%= h.id %>" method="POST">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title fw-bold">Editar Herramienta: <%= h.nombre %></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label small fw-bold">Nombre *</label>
|
|
<input type="text" name="nombre" class="form-control" value="<%= h.nombre %>" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Código</label>
|
|
<input type="text" name="codigo" class="form-control font-monospace" value="<%= h.codigo || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Categoría</label>
|
|
<select name="categoria" class="form-select">
|
|
<option value="fibra_optica" <%= h.categoria === 'fibra_optica' ? 'selected' : '' %>>Fibra Óptica</option>
|
|
<option value="cctv" <%= h.categoria === 'cctv' ? 'selected' : '' %>>Seguridad & CCTV</option>
|
|
<option value="redes" <%= h.categoria === 'redes' ? 'selected' : '' %>>Redes & Cableado</option>
|
|
<option value="electricidad" <%= h.categoria === 'electricidad' ? 'selected' : '' %>>Electricidad</option>
|
|
<option value="medicion" <%= h.categoria === 'medicion' ? 'selected' : '' %>>Instrumento de Medición</option>
|
|
<option value="mano" <%= h.categoria === 'mano' ? 'selected' : '' %>>Herramienta Manual/Poder</option>
|
|
<option value="general" <%= h.categoria === 'general' ? 'selected' : '' %>>General</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Marca</label>
|
|
<input type="text" name="marca" class="form-control" value="<%= h.marca || '' %>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Modelo</label>
|
|
<input type="text" name="modelo" class="form-control" value="<%= h.modelo || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Número de Serie</label>
|
|
<input type="text" name="numero_serie" class="form-control font-monospace" value="<%= h.numero_serie || '' %>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Estado Físico / Operativo</label>
|
|
<select name="estado" class="form-select">
|
|
<option value="operativo" <%= h.estado === 'operativo' ? 'selected' : '' %>>Operativo (Listo para uso)</option>
|
|
<option value="en_mantenimiento" <%= h.estado === 'en_mantenimiento' ? 'selected' : '' %>>En Mantenimiento / Calibración</option>
|
|
<option value="danado" <%= h.estado === 'danado' ? 'selected' : '' %>>Dañado / Requiere Reparación</option>
|
|
<option value="baja" <%= h.estado === 'baja' ? 'selected' : '' %>>Dado de Baja</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold">Ubicación en Bodega / Almacén</label>
|
|
<input type="text" name="ubicacion_bodega" class="form-control" value="<%= h.ubicacion_bodega || '' %>">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold">Notas Adicionales</label>
|
|
<textarea name="notas" class="form-control" rows="2"><%= h.notas || '' %></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }) %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================== -->
|
|
<!-- TAB 3: ASIGNACIONES A TÉCNICOS -->
|
|
<!-- ============================================== -->
|
|
<div class="tab-pane fade" id="tab-asignaciones" role="tabpanel">
|
|
<div class="app-card border shadow-sm rounded-3 overflow-hidden">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0" style="font-size: 0.85rem;">
|
|
<thead class="table-light text-muted small text-uppercase">
|
|
<tr>
|
|
<th class="ps-3">Técnico</th>
|
|
<th>Kit / Herramienta Asignada</th>
|
|
<th>Modalidad</th>
|
|
<th>Fecha Asignación</th>
|
|
<th>Contexto / Visita</th>
|
|
<th>Estado</th>
|
|
<th class="text-end pe-3">Acciones</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% if (asignaciones.length === 0) { %>
|
|
<tr>
|
|
<td colspan="7" class="text-center py-4 text-muted">No hay asignaciones registradas actualmente.</td>
|
|
</tr>
|
|
<% } %>
|
|
|
|
<% asignaciones.forEach(a => { %>
|
|
<tr>
|
|
<td class="ps-3">
|
|
<div class="fw-bold text-dark"><%= a.tecnico_nombre %></div>
|
|
<div class="text-muted small" style="font-size: 0.75rem;"><%= a.tecnico_email %></div>
|
|
</td>
|
|
<td>
|
|
<% if (a.set_nombre) { %>
|
|
<div class="fw-bold text-primary"><i class="fas fa-layer-group me-1"></i> <%= a.set_nombre %></div>
|
|
<% if (a.set_codigo) { %><span class="badge bg-light text-muted border font-monospace" style="font-size: 0.65rem;"><%= a.set_codigo %></span><% } %>
|
|
<% } else if (a.herramienta_nombre) { %>
|
|
<div class="fw-bold text-dark"><i class="fas fa-wrench me-1"></i> <%= a.herramienta_nombre %></div>
|
|
<% if (a.herramienta_codigo) { %><span class="badge bg-light text-muted border font-monospace" style="font-size: 0.65rem;"><%= a.herramienta_codigo %></span><% } %>
|
|
<% } else { %>
|
|
<span class="text-muted">Equipamiento general</span>
|
|
<% } %>
|
|
</td>
|
|
<td>
|
|
<% if (a.tipo_asignacion === 'dotacion_fija') { %>
|
|
<span class="badge bg-info-subtle text-info border border-info-subtle"><i class="fas fa-id-badge me-1"></i> Dotación Fija</span>
|
|
<% } else { %>
|
|
<span class="badge bg-warning-subtle text-dark border border-warning-subtle"><i class="fas fa-truck-pickup me-1"></i> Visita Terreno</span>
|
|
<% } %>
|
|
</td>
|
|
<td>
|
|
<div><%= new Date(a.fecha_asignacion).toLocaleDateString('es-EC') %></div>
|
|
<div class="text-muted small" style="font-size: 0.72rem;"><%= new Date(a.fecha_asignacion).toLocaleTimeString('es-EC') %></div>
|
|
</td>
|
|
<td>
|
|
<% if (a.ticket_id) { %>
|
|
<a href="/agent/ticket/<%= a.ticket_id %>" class="text-decoration-none fw-semibold">
|
|
Ticket #<%= a.ticket_id %>
|
|
</a>
|
|
<div class="text-muted small text-truncate" style="max-width: 180px;"><%= a.visita_titulo || a.ticket_asunto || '' %></div>
|
|
<% } else { %>
|
|
<span class="text-muted small"><%= a.observaciones_entrega || 'Sin observaciones' %></span>
|
|
<% } %>
|
|
</td>
|
|
<td>
|
|
<% if (a.estado === 'asignado') { %>
|
|
<span class="badge bg-success"><i class="fas fa-user-check me-1"></i> En Custodia</span>
|
|
<% } else if (a.estado === 'devuelto') { %>
|
|
<span class="badge bg-secondary"><i class="fas fa-undo me-1"></i> Devuelto</span>
|
|
<% if (a.fecha_devolucion) { %>
|
|
<div class="text-muted" style="font-size: 0.68rem;"><%= new Date(a.fecha_devolucion).toLocaleDateString('es-EC') %></div>
|
|
<% } %>
|
|
<% } else { %>
|
|
<span class="badge bg-warning text-dark"><%= a.estado %></span>
|
|
<% } %>
|
|
</td>
|
|
<td class="text-end pe-3">
|
|
<% if (a.estado === 'asignado') { %>
|
|
<button type="button" class="btn btn-sm btn-outline-success d-inline-flex align-items-center gap-1" data-bs-toggle="modal" data-bs-target="#modalDevolver<%= a.id %>">
|
|
<i class="fas fa-undo-alt"></i>
|
|
<span>Devolver</span>
|
|
</button>
|
|
<% } else { %>
|
|
<span class="text-muted small" style="font-size: 0.75rem;"><%= a.observaciones_devolucion || 'Finalizado' %></span>
|
|
<% } %>
|
|
</td>
|
|
</tr>
|
|
|
|
<!-- MODAL DEVOLVER -->
|
|
<div class="modal fade" id="modalDevolver<%= a.id %>" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/devolver/<%= a.id %>" method="POST">
|
|
<div class="modal-header bg-success text-white">
|
|
<h5 class="modal-title fw-bold">Registrar Devolución a Bodega</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="small text-muted mb-3">
|
|
Confirmar el reingreso del kit <strong><%= a.set_nombre || a.herramienta_nombre %></strong> retornado por <strong><%= a.tecnico_nombre %></strong>.
|
|
</p>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Condición al Reingreso</label>
|
|
<select name="estado" class="form-select">
|
|
<option value="devuelto">Devuelto en Buen Estado / Operativo</option>
|
|
<option value="observado">Con Observaciones / Ítem Dañado o Faltante</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Observaciones de Devolución</label>
|
|
<textarea name="observaciones_devolucion" class="form-control" rows="2" placeholder="Ej: Kit revisado e ingresado completo al estante A1 sin novedades"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-success fw-bold">Confirmar Devolución</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }) %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================== -->
|
|
<!-- MODALES GLOBALES DE CREACIÓN -->
|
|
<!-- ============================================== -->
|
|
|
|
<!-- MODAL NUEVA HERRAMIENTA -->
|
|
<div class="modal fade" id="modalNuevaHerramienta" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/create" method="POST">
|
|
<div class="modal-header bg-primary text-white">
|
|
<h5 class="modal-title fw-bold"><i class="fas fa-plus-circle me-1"></i> Registrar Nueva Herramienta / Instrumento</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4">
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label small fw-bold">Nombre de la Herramienta *</label>
|
|
<input type="text" name="nombre" class="form-control" placeholder="Ej: Fusionadora de Fibra Óptica" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Código / Placa Interna</label>
|
|
<input type="text" name="codigo" class="form-control font-monospace" placeholder="Ej: TOOL-FIB-02">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Categoría</label>
|
|
<select name="categoria" class="form-select">
|
|
<option value="cctv">Seguridad Electrónica & CCTV</option>
|
|
<option value="fibra_optica">Fibra Óptica & Enlaces</option>
|
|
<option value="redes">Redes & Cableado Estructurado</option>
|
|
<option value="electricidad">Electricidad & Tableros</option>
|
|
<option value="medicion">Instrumento de Medición</option>
|
|
<option value="mano">Herramienta Manual / Eléctrica</option>
|
|
<option value="general" selected>General</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Marca</label>
|
|
<input type="text" name="marca" class="form-control" placeholder="Ej: Fluke, DeWalt, Fujikura">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Modelo</label>
|
|
<input type="text" name="modelo" class="form-control" placeholder="Ej: 70S Pro">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Número de Serie del Fabricante</label>
|
|
<input type="text" name="numero_serie" class="form-control font-monospace" placeholder="Ej: SN-998241A">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Estado Inicial</label>
|
|
<select name="estado" class="form-select">
|
|
<option value="operativo" selected>Operativo (Listo para asignación)</option>
|
|
<option value="en_mantenimiento">En Mantenimiento</option>
|
|
<option value="danado">Dañado</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold">Ubicación en Bodega</label>
|
|
<input type="text" name="ubicacion_bodega" class="form-control" placeholder="Ej: Bodega Central - Gaveta 4, Estante B">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold">Observaciones / Especificaciones</label>
|
|
<textarea name="notas" class="form-control" rows="2" placeholder="Detalles de calibración, accesorios incluidos o precauciones"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary fw-bold">Guardar en Inventario</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MODAL NUEVO SET / KIT -->
|
|
<div class="modal fade" id="modalNuevoSet" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/sets/create" method="POST">
|
|
<div class="modal-header bg-warning text-dark">
|
|
<h5 class="modal-title fw-bold"><i class="fas fa-layer-group me-1"></i> Crear Set / Kit de Herramientas</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4">
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label small fw-bold">Nombre del Set / Kit *</label>
|
|
<input type="text" name="nombre" class="form-control" placeholder="Ej: Kit Seguridad CCTV & Control de Accesos" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold">Código del Kit</label>
|
|
<input type="text" name="codigo" class="form-control font-monospace" placeholder="Ej: SET-CCTV-02">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Categoría de Especialidad</label>
|
|
<select name="categoria" class="form-select">
|
|
<option value="cctv">Seguridad Electrónica & CCTV</option>
|
|
<option value="fibra_optica">Fibra Óptica & Enlaces</option>
|
|
<option value="redes">Redes & Cableado Estructurado</option>
|
|
<option value="electricidad">Electricidad & Potencia</option>
|
|
<option value="general" selected>General / Multiuso</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold">Descripción del Kit</label>
|
|
<input type="text" name="descripcion" class="form-control" placeholder="Ej: Kit completo para instalación y certificación en campo">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label small fw-bold mb-1">Seleccionar Herramientas a Integrar en este Set</label>
|
|
<div class="border rounded p-2.5 bg-light" style="max-height: 220px; overflow-y: auto;">
|
|
<% if (herramientas.length === 0) { %>
|
|
<div class="small text-muted p-2">No hay herramientas disponibles en el catálogo. Registra herramientas primero.</div>
|
|
<% } %>
|
|
<% herramientas.forEach(h => { %>
|
|
<div class="form-check py-1 border-bottom border-light">
|
|
<input class="form-check-input" type="checkbox" name="herramientas_ids" value="<%= h.id %>" id="checkNewSet_<%= h.id %>">
|
|
<label class="form-check-label small d-flex align-items-center justify-content-between" for="checkNewSet_<%= h.id %>">
|
|
<span>
|
|
<strong><%= h.nombre %></strong>
|
|
<% if (h.marca) { %><span class="text-muted">(<%= h.marca %>)</span><% } %>
|
|
</span>
|
|
<span class="badge bg-white text-muted border font-monospace ms-2">
|
|
<%= h.codigo || h.numero_serie || 'SN-N/D' %>
|
|
</span>
|
|
</label>
|
|
</div>
|
|
<% }) %>
|
|
</div>
|
|
<small class="text-muted" style="font-size: 0.72rem;">Podrás marcar los instrumentos que conforman el equipamiento estándar de este kit.</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-warning fw-bold text-dark">Crear Kit</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MODAL ASIGNAR GENERAL -->
|
|
<div class="modal fade" id="modalAsignar" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content rounded-3 border-0 shadow">
|
|
<form action="/admin/herramientas/asignar" method="POST">
|
|
<div class="modal-header bg-primary text-white">
|
|
<h5 class="modal-title fw-bold"><i class="fas fa-user-tag me-1"></i> Asignar Set de Herramientas a Técnico</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4">
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Técnico Receptor *</label>
|
|
<select name="tecnico_id" class="form-select" required>
|
|
<option value="">-- Seleccionar Técnico --</option>
|
|
<% tecnicos.forEach(t => { %>
|
|
<option value="<%= t.id %>"><%= t.nombre %> (<%= t.email %>)</option>
|
|
<% }) %>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Set / Kit a Asignar *</label>
|
|
<select name="set_id" class="form-select" required>
|
|
<option value="">-- Seleccionar Set / Kit --</option>
|
|
<% sets.forEach(s => { %>
|
|
<option value="<%= s.id %>"><%= s.nombre %> <%= s.codigo ? `(${s.codigo})` : '' %> — <%= s.total_herramientas || (s.herramientas ? s.herramientas.length : 0) %> herramientas</option>
|
|
<% }) %>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Tipo de Asignación</label>
|
|
<select name="tipo_asignacion" class="form-select">
|
|
<option value="dotacion_fija">Dotación Fija (Custodia permanente del técnico)</option>
|
|
<option value="visita_fsm">Asignación Temporal para Turno / Proyecto</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label small fw-bold">Observaciones de Entrega</label>
|
|
<textarea name="observaciones_entrega" class="form-control" rows="2" placeholder="Ej: Entregado maletín completo con precintos y cargadores"></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary fw-bold">Confirmar Asignación</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Activar pestaña según el hash de URL (#tab-herramientas, #tab-sets, #tab-asignaciones)
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const hash = window.location.hash;
|
|
if (hash) {
|
|
const btn = document.querySelector(`button[data-bs-target="${hash}"]`);
|
|
if (btn) {
|
|
const tab = new bootstrap.Tab(btn);
|
|
tab.show();
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<%- include('../partials/footer') %>
|