Skip to content

Commit 7c4861f

Browse files
authored
Merge pull request #71 from chechojgb/buttonsLovers
refactor: Mejora estructura tabla marcacion
2 parents 9472e31 + ad79204 commit 7c4861f

File tree

2 files changed

+183
-35
lines changed

2 files changed

+183
-35
lines changed

resources/js/components/BL/tablaMarcacionBL.jsx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { usePage } from '@inertiajs/react';
1111
import { router } from '@inertiajs/react';
1212
import { Toast } from "flowbite-react";
1313
import { useEffect } from 'react';
14-
import { HiCheck, HiX } from "react-icons/hi";
14+
import { HiCheck, HiX, HiDownload } from "react-icons/hi";
1515

1616
export default function TablaMarcacionBL({itemsPedidos, search}) {
1717
const { props } = usePage();
@@ -31,6 +31,29 @@ export default function TablaMarcacionBL({itemsPedidos, search}) {
3131
return () => clearTimeout(timer);
3232
}
3333
}, [toast]);
34+
const exportToExcel = () => {
35+
if (itemsPedidos.length === 0) {
36+
setToast({ show: true, success: false, message: "No hay datos para exportar" });
37+
return;
38+
}
39+
40+
const dataForExcel = itemsPedidos.map(item => ({
41+
'Cliente': item.pedido?.cliente?.nombre || '',
42+
'Pedido': `PED #${item.pedido_id || ''} - ${item.empaque?.producto?.descripcion || '—'}`,
43+
'Cantidad': item.cantidad_empaques,
44+
'Nota': item.nota || '',
45+
'Estado': item.estado,
46+
'Completado por': item.marcaciones?.[0]?.trabajador?.name || 'Sin asignar',
47+
'Fecha': item.fecha ? new Date(item.fecha).toLocaleDateString() : ''
48+
}));
49+
50+
const wb = XLSX.utils.book_new();
51+
const ws = XLSX.utils.json_to_sheet(dataForExcel);
52+
XLSX.utils.book_append_sheet(wb, ws, "Pedidos");
53+
XLSX.writeFile(wb, `Pedidos_${new Date().toISOString().split('T')[0]}.xlsx`);
54+
55+
setToast({ show: true, success: true, message: "Datos exportados correctamente" });
56+
};
3457

3558
const columns = [
3659
{ accessorKey: 'pedido.cliente.nombre', header: 'cliente' },
@@ -101,6 +124,21 @@ export default function TablaMarcacionBL({itemsPedidos, search}) {
101124
{
102125
header: 'Completado por',
103126
accessorFn: row => row.marcaciones?.[0]?.trabajador?.name ?? 'Sin asignar'
127+
},
128+
{
129+
accessorKey: 'updated_at',
130+
header: 'Fecha',
131+
cell: ({ row }) => {
132+
const fechaRaw = row.original.updated_at; // viene como 2025-09-17T18:42:53.000000Z
133+
const fechaObj = new Date(fechaRaw);
134+
135+
// Lo mostramos en formato local
136+
return fechaObj.toLocaleDateString("es-CO", {
137+
year: "numeric",
138+
month: "2-digit",
139+
day: "2-digit",
140+
});
141+
}
104142
}
105143
];
106144

@@ -125,6 +163,16 @@ export default function TablaMarcacionBL({itemsPedidos, search}) {
125163

126164
return (
127165
<>
166+
<div className="bg-white p-4 flex justify-end">
167+
<button
168+
onClick={exportToExcel}
169+
disabled={itemsPedidos.length === 0}
170+
className="flex items-center gap-2 bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md text-sm disabled:opacity-50"
171+
>
172+
<HiDownload className="h-4 w-4" />
173+
Exportar a Excel
174+
</button>
175+
</div>
128176
<table className="min-w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-8">
129177
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800 dark:text-gray-400">
130178
{table.getHeaderGroups().map(headerGroup => (

resources/js/pages/BLMarcacion.jsx

Lines changed: 134 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,62 @@ export default function MarcadoPage({ orderCustomer, buttonUser, itemsPedidos })
2929
const [search, setSearch] = useState('');
3030
const [estadoFiltro, setEstadoFiltro] = useState("");
3131
const [trabajadorFiltro, setTrabajadorFiltro] = useState("");
32+
const [fechaInicio, setFechaInicio] = useState('');
33+
const [fechaFin, setFechaFin] = useState('');
34+
35+
const filtrarPorFecha = (item, inicio, fin) => {
36+
if (!inicio && !fin) return true; // Si no hay filtros de fecha
37+
38+
const fechaItem = new Date(item.updated_at);
39+
40+
if (inicio && fin) {
41+
const start = new Date(inicio);
42+
const end = new Date(fin);
43+
end.setHours(23, 59, 59); // Incluir todo el día final
44+
return fechaItem >= start && fechaItem <= end;
45+
}
46+
47+
if (inicio) {
48+
const start = new Date(inicio);
49+
return fechaItem >= start;
50+
}
51+
52+
if (fin) {
53+
const end = new Date(fin);
54+
end.setHours(23, 59, 59);
55+
return fechaItem <= end;
56+
}
57+
58+
return true;
59+
};
3260
const itemsFiltrados = itemsPedidos.filter((item) => {
33-
// Filtrar por estado (si aplica)
61+
// Filtrar por estado
3462
const coincideEstado = estadoFiltro === "" ? true : item.estado === estadoFiltro;
35-
36-
// Filtrar por trabajador (si aplica)
37-
const coincideTrabajador =trabajadorFiltro === "" ? true : item.marcaciones?.[0]?.trabajador?.id === parseInt(trabajadorFiltro);
38-
39-
// El item debe cumplir con ambos filtros
40-
return coincideEstado && coincideTrabajador;
63+
64+
// Filtrar por trabajador
65+
const coincideTrabajador = trabajadorFiltro === "" ? true :
66+
item.marcaciones?.[0]?.trabajador?.id === parseInt(trabajadorFiltro);
67+
68+
// Filtrar por fecha
69+
const coincideFecha = filtrarPorFecha(item, fechaInicio, fechaFin);
70+
71+
// El item debe cumplir con todos los filtros
72+
return coincideEstado && coincideTrabajador && coincideFecha;
4173
});
4274

75+
// Función para filtrar por rango de fechas
76+
77+
console.log(itemsFiltrados, fechaFin);
78+
79+
// Limpiar filtros
80+
const limpiarFiltros = () => {
81+
setEstadoFiltro('');
82+
setTrabajadorFiltro('');
83+
setFechaInicio('');
84+
setFechaFin('');
85+
setSearch('');
86+
};
87+
4388
const {
4489
marcados,
4590
seleccionados,
@@ -198,70 +243,125 @@ export default function MarcadoPage({ orderCustomer, buttonUser, itemsPedidos })
198243
)}
199244
</div>
200245

201-
<div className="">
202-
{/* Header de la tabla con buscador */}
203-
<div className="flex flex-col sm:flex-row sm:items-center justify-between p-4 gap-3 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
204-
<div className="">
246+
<div className="bg-white dark:bg-gray-900 rounded-lg shadow">
247+
{/* Header con filtros */}
248+
<div className="flex flex-col p-4 gap-4 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
249+
<div className="flex justify-between items-center">
205250
<h2 className="text-lg font-semibold text-gray-800 dark:text-gray-200">
206251
Lista de items
207252
</h2>
253+
<button
254+
onClick={limpiarFiltros}
255+
className="px-3 py-1 text-sm text-gray-600 dark:text-gray-300 bg-gray-200 dark:bg-gray-700 rounded-md hover:bg-gray-300 dark:hover:bg-gray-600"
256+
>
257+
Limpiar filtros
258+
</button>
259+
</div>
260+
261+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
262+
{/* Filtro por estado */}
208263
<div>
209-
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
210-
Filtrar por estado:
264+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
265+
Estado:
211266
</label>
212267
<select
213268
value={estadoFiltro}
214269
onChange={(e) => setEstadoFiltro(e.target.value)}
215-
className="border rounded-lg px-2 py-1 text-sm dark:bg-gray-800 dark:text-white"
270+
className="w-full border rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white dark:border-gray-600"
216271
>
217-
<option value="">Todos</option>
272+
<option value="">Todos los estados</option>
218273
<option value="pendiente">Pendiente</option>
219274
<option value="en proceso">En proceso</option>
220275
<option value="completado">Completado</option>
221276
</select>
222277
</div>
278+
279+
{/* Filtro por trabajador */}
223280
<div>
224-
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">
225-
Filtrar por trabajador:
281+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
282+
Trabajador:
226283
</label>
227284
<select
228285
value={trabajadorFiltro}
229286
onChange={(e) => setTrabajadorFiltro(e.target.value)}
230-
className="border rounded-lg px-2 py-1 text-sm dark:bg-gray-800 dark:text-white"
287+
className="w-full border rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white dark:border-gray-600"
231288
>
232-
<option value="">Seleccionar trabajador</option>
289+
<option value="">Todos los trabajadores</option>
233290
{buttonUser.map((trabajador) => (
234291
<option key={trabajador.id} value={trabajador.id}>
235292
{trabajador.name}
236293
</option>
237294
))}
238295
</select>
239296
</div>
297+
298+
{/* Filtro por fecha inicio */}
299+
<div>
300+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
301+
Fecha inicio:
302+
</label>
303+
<input
304+
type="date"
305+
value={fechaInicio}
306+
onChange={(e) => setFechaInicio(e.target.value)}
307+
className="w-full border rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white dark:border-gray-600"
308+
/>
309+
</div>
310+
311+
{/* Filtro por fecha fin */}
312+
<div>
313+
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
314+
Fecha fin:
315+
</label>
316+
<input
317+
type="date"
318+
value={fechaFin}
319+
onChange={(e) => setFechaFin(e.target.value)}
320+
className="w-full border rounded-lg px-3 py-2 text-sm dark:bg-gray-700 dark:text-white dark:border-gray-600"
321+
/>
322+
</div>
240323
</div>
241-
<div className="relative w-full sm:w-72">
242-
<svg
243-
className="absolute left-3 top-2.5 w-4 h-4 text-gray-400"
244-
xmlns="http://www.w3.org/2000/svg"
245-
fill="none"
246-
viewBox="0 0 24 24"
247-
stroke="currentColor"
248-
>
249-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-4.35-4.35M15.5 10.5a5 5 0 11-10 0 5 5 0 0110 0z" />
250-
</svg>
324+
325+
{/* Búsqueda general */}
326+
<div className="relative">
327+
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
328+
<svg
329+
className="w-4 h-4 text-gray-400"
330+
xmlns="http://www.w3.org/2000/svg"
331+
fill="none"
332+
viewBox="0 0 24 24"
333+
stroke="currentColor"
334+
>
335+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-4.35-4.35M15.5 10.5a5 5 0 11-10 0 5 5 0 0110 0z" />
336+
</svg>
337+
</div>
251338
<input
252339
type="text"
253340
value={search}
254341
onChange={(e) => setSearch(e.target.value)}
255-
className="w-full pl-10 pr-4 py-2 text-sm text-gray-900 border border-gray-200 rounded-lg bg-white dark:bg-gray-900 dark:border-gray-700 dark:placeholder-gray-400 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500 shadow-sm"
256-
placeholder="Buscar producto..."
342+
className="w-full pl-10 pr-4 py-2 text-sm text-gray-900 border border-gray-200 rounded-lg bg-white dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500 shadow-sm"
343+
placeholder="Buscar en todos los campos..."
257344
/>
258345
</div>
259-
</div>
260346

347+
{/* Info de filtros aplicados */}
348+
{(estadoFiltro || trabajadorFiltro || fechaInicio || fechaFin) && (
349+
<div className="text-sm text-gray-600 dark:text-gray-400">
350+
Filtros aplicados:
351+
{estadoFiltro && <span className="ml-2 bg-blue-100 dark:bg-blue-900 px-2 py-1 rounded">Estado: {estadoFiltro}</span>}
352+
{trabajadorFiltro && <span className="ml-2 bg-green-100 dark:bg-green-900 px-2 py-1 rounded">Trabajador: {buttonUser.find(t => t.id === parseInt(trabajadorFiltro))?.name}</span>}
353+
{fechaInicio && <span className="ml-2 bg-yellow-100 dark:bg-yellow-900 px-2 py-1 rounded">Desde: {new Date(fechaInicio).toLocaleDateString()}</span>}
354+
{fechaFin && <span className="ml-2 bg-purple-100 dark:bg-purple-900 px-2 py-1 rounded">Hasta: {new Date(fechaFin).toLocaleDateString()}</span>}
355+
</div>
356+
)}
357+
</div>
261358

262-
{/* Contenedor de la tabla */}
359+
{/* Tabla con los datos filtrados */}
263360
<div className="overflow-x-auto">
264-
<TablaMarcacionBL itemsPedidos={itemsFiltrados} search={search} estadoFiltro={estadoFiltro}/>
361+
<TablaMarcacionBL
362+
itemsPedidos={itemsFiltrados}
363+
search={search}
364+
/>
265365
</div>
266366
</div>
267367

0 commit comments

Comments
 (0)