@extends('layouts.app') @section('titulo','Órdenes') @section('contenido') @php // Códigos de estado que protegen la OT de borrado $protegidos = ['EJE','APR']; // Helpers para chips y headers $tieneFiltro = fn ($k, $v = 1) => (string) request($k) === (string) $v; $sortUrl = function ($col) use ($sort, $dir) { $nuevoDir = ($sort === $col && $dir === 'desc') ? 'asc' : 'desc'; return request()->fullUrlWithQuery(['sort' => $col, 'dir' => $nuevoDir, 'page' => null]); }; $flecha = function ($col) use ($sort, $dir) { if ($sort !== $col) return ''; return '' . ($dir === 'asc' ? '↑' : '↓') . ''; }; @endphp

Órdenes de trabajo {{ $clase ? '— '.ucfirst($clase) : '' }} {{ $registros->total() }} en total @if($filtrosActivos > 0) {{ $filtrosActivos }} filtro{{ $filtrosActivos == 1 ? '' : 's' }} activo{{ $filtrosActivos == 1 ? '' : 's' }} @endif

Excel PDF @can('ordenes.crear') Nueva @endcan
{{-- ===== CHIPS RÁPIDOS ===== --}}
Vencidas Mis OT | Hoy Esta semana Este mes @if($filtrosActivos > 0 || request('q') || request('vencidas') || request('mias') || request('periodo')) Limpiar todo @endif
{{-- ===== FILTROS BÁSICOS ===== --}}
@if($clase)@endif {{-- mantener chips activos al aplicar filtros básicos --}} @if(request('vencidas'))@endif @if(request('mias'))@endif @if(request('periodo'))@endif @if(request('sort'))@endif @if(request('dir'))@endif
{{-- ===== FILTROS AVANZADOS (acordeón) ===== --}}

Limpiar avanzados
{{-- Barra de acciones masivas --}}
0 seleccionada(s) · 0 protegidas
@csrf
@forelse($registros as $o) @php $estaProtegida = in_array(optional($o->estado)->codigo, $protegidos); @endphp @empty @endforelse
Código {!! $flecha('codigo') !!} Clase Equipo Área Programada {!! $flecha('fecha_programada') !!} Estado Prioridad {!! $flecha('prioridad') !!} Responsable Costo {!! $flecha('costo_total') !!}
{{ $o->codigo }} @if($estaProtegida)@endif {{ ucfirst($o->clase) }} {{ optional($o->equipo)->nombre }} {{ optional($o->area)->nombre }} {{ $o->fecha_programada?->format('d/m/Y') }} @if($o->estado){{ $o->estado->nombre }}@endif @if($o->prioridad){{ $o->prioridad->nombre }}@endif {{ optional($o->responsable)->nombre_completo }} {{ number_format($o->costo_total,2) }}
Sin órdenes que coincidan con los filtros
Mostrando {{ $registros->firstItem() ?? 0 }}–{{ $registros->lastItem() ?? 0 }} de {{ $registros->total() }} {{ $registros->links() }}
{{-- Modal de confirmación de eliminación masiva --}} @push('scripts_inline') const selectAll = document.getElementById('selectAll'); const checks = document.querySelectorAll('.chk-ot'); const barra = document.getElementById('barraAcciones'); const contadorSel = document.getElementById('contadorSel'); const contadorProt = document.getElementById('contadorProt'); function actualizarBarra() { const seleccionadas = [...checks].filter(c => c.checked); const total = seleccionadas.length; const protegidas = seleccionadas.filter(c => c.dataset.protegida === '1').length; contadorSel.textContent = total; if (protegidas > 0) { contadorProt.textContent = protegidas + ' protegida(s)'; contadorProt.classList.remove('d-none'); } else { contadorProt.classList.add('d-none'); } barra.classList.toggle('d-none', total === 0); } function limpiarSeleccion() { checks.forEach(c => c.checked = false); selectAll.checked = false; actualizarBarra(); } selectAll?.addEventListener('change', () => { checks.forEach(c => c.checked = selectAll.checked); actualizarBarra(); }); checks.forEach(c => c.addEventListener('change', () => { if (selectAll) selectAll.checked = [...checks].every(c => c.checked); actualizarBarra(); })); document.getElementById('modalEliminar')?.addEventListener('show.bs.modal', () => { const sel = [...checks].filter(c => c.checked); const total = sel.length; const protegidas = sel.filter(c => c.dataset.protegida === '1'); const eliminables = sel.filter(c => c.dataset.protegida !== '1'); document.getElementById('modalContador').textContent = total; const block = document.getElementById('modalProtegidasBlock'); const lista = document.getElementById('modalProtegidasLista'); if (protegidas.length > 0) { block.classList.remove('d-none'); document.getElementById('modalProtegidasCount').textContent = protegidas.length; lista.innerHTML = ''; protegidas.slice(0, 8).forEach(p => { const li = document.createElement('li'); li.innerHTML = `${p.dataset.codigo} — ${p.dataset.estadoNombre}`; lista.appendChild(li); }); if (protegidas.length > 8) { const li = document.createElement('li'); li.textContent = `... y ${protegidas.length - 8} más`; lista.appendChild(li); } } else { block.classList.add('d-none'); } const elimBlock = document.getElementById('modalEliminarBlock'); if (eliminables.length > 0) { elimBlock.classList.remove('d-none'); document.getElementById('modalEliminarCount').textContent = eliminables.length; } else { elimBlock.classList.add('d-none'); } document.getElementById('btnConfirmarEliminar').disabled = eliminables.length === 0; }); // === Autocompletado predictivo del buscador === (function () { const input = document.getElementById('inputBuscarOT'); const drop = document.getElementById('dropdownSugerencias'); const wrapper = document.getElementById('buscadorOT'); if (!input || !drop) return; const claseActual = @json($clase ?? ''); const urlBuscar = @json(route('ordenes.buscar-equipo')); let timer = null; let abortCtrl = null; let indiceActivo = -1; const escapar = (s) => String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const resaltar = (texto, q) => { if (!q) return escapar(texto); const re = new RegExp('(' + q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + ')', 'gi'); return escapar(texto).replace(re, '$1'); }; function cerrarDropdown() { drop.classList.add('d-none'); drop.innerHTML = ''; indiceActivo = -1; } function pintarVacio(q) { drop.innerHTML = `
Sin coincidencias para "${escapar(q)}"
`; drop.classList.remove('d-none'); } function pintarLoading() { drop.innerHTML = '
Buscando...
'; drop.classList.remove('d-none'); } function pintarSugerencias(data) { const q = data.busqueda; if (!data.sugerencias || data.sugerencias.length === 0) { pintarVacio(q); return; } let html = ''; data.sugerencias.forEach((s, i) => { html += `
${escapar(s.codigo)} ${s.frecuencia ? `· ${escapar(s.frecuencia)}` : ''}
${resaltar(s.equipo_codigo, q)} — ${resaltar(s.equipo_nombre, q)}
${escapar(s.fecha || '—')}
${escapar(s.estado_nombre || '—')}
`; }); if (data.total > data.mostrados) { const url = new URL(window.location.href); url.searchParams.set('q', q); if (claseActual) url.searchParams.set('clase', claseActual); url.searchParams.delete('page'); html += ``; } drop.innerHTML = html; drop.classList.remove('d-none'); drop.querySelectorAll('.item').forEach(el => { el.addEventListener('click', () => window.location.href = el.dataset.url); }); const footer = drop.querySelector('.footer'); if (footer) { footer.addEventListener('click', e => { window.location.href = e.currentTarget.dataset.urlTodos; }); } } function buscar(q) { if (abortCtrl) abortCtrl.abort(); abortCtrl = new AbortController(); const url = new URL(urlBuscar, window.location.origin); url.searchParams.set('q', q); if (claseActual) url.searchParams.set('clase', claseActual); fetch(url, { signal: abortCtrl.signal, headers: { 'Accept': 'application/json' } }) .then(r => r.json()) .then(pintarSugerencias) .catch(err => { if (err.name !== 'AbortError') console.error(err); }); } input.addEventListener('input', () => { const q = input.value.trim(); clearTimeout(timer); if (q.length < 2) { cerrarDropdown(); return; } pintarLoading(); timer = setTimeout(() => buscar(q), 250); }); input.addEventListener('keydown', (e) => { const items = drop.querySelectorAll('.item'); if (e.key === 'Escape') { cerrarDropdown(); return; } if (e.key === 'ArrowDown' && items.length) { e.preventDefault(); indiceActivo = (indiceActivo + 1) % items.length; items.forEach(it => it.classList.remove('activo')); items[indiceActivo].classList.add('activo'); items[indiceActivo].scrollIntoView({ block: 'nearest' }); } else if (e.key === 'ArrowUp' && items.length) { e.preventDefault(); indiceActivo = indiceActivo <= 0 ? items.length - 1 : indiceActivo - 1; items.forEach(it => it.classList.remove('activo')); items[indiceActivo].classList.add('activo'); items[indiceActivo].scrollIntoView({ block: 'nearest' }); } else if (e.key === 'Enter' && indiceActivo >= 0 && items[indiceActivo]) { e.preventDefault(); window.location.href = items[indiceActivo].dataset.url; } }); document.addEventListener('click', (e) => { if (!wrapper.contains(e.target)) cerrarDropdown(); }); })(); @endpush @endsection