Skip to content

Commit 30b0e00

Browse files
authored
Merge pull request #66 from chechojgb/buttonsLovers
add: nueva tabla visualizacion de marcaciones
2 parents f2fa417 + c1afd2e commit 30b0e00

File tree

6 files changed

+258
-53
lines changed

6 files changed

+258
-53
lines changed

app/Http/Controllers/BLMarcacionController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public function index()
1919
$user = Auth::user();
2020
$orderCustomer = BLCliente::with(['pedidos.items.empaque.producto', 'pedidos.items.marcaciones.trabajador'])->get();
2121
$buttonUser = User::whereIn('proyecto', ['Button LoversM', 'Button LoversMN'])->get();
22+
// dd($buttonUser);
2223
return Inertia::render('BLMarcacion', [
2324
'user' => $user,
2425
'orderCustomer' => $orderCustomer,

app/Http/Controllers/BLanalisisController.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function index()
4040
'semanal' => $this->produccionSemanal(),
4141
'mensual' => $this->produccionMensual(),
4242
'trimestral' => $this->produccionTrimestral(),
43+
'comparativa' => $this->comparativaPeriodos(),
4344
'user' => $user, // Para formularios
4445
]);
4546
}
@@ -153,6 +154,49 @@ public function produccionSemanal()
153154

154155
return $data->values();
155156
}
157+
public function comparativaPeriodos()
158+
{
159+
$meses = [
160+
'01' => 'Enero',
161+
'02' => 'Febrero',
162+
'03' => 'Marzo',
163+
'04' => 'Abril',
164+
'05' => 'Mayo',
165+
'06' => 'Junio',
166+
'07' => 'Julio',
167+
'08' => 'Agosto',
168+
'09' => 'Septiembre',
169+
'10' => 'Octubre',
170+
'11' => 'Noviembre',
171+
'12' => 'Diciembre',
172+
];
173+
174+
$ahora = \Carbon\Carbon::now('America/Bogota');
175+
$mesActual = $ahora->copy()->startOfMonth();
176+
$mesAnterior = $ahora->copy()->subMonth()->startOfMonth();
177+
178+
// Producción del mes actual
179+
$produccionActual = \App\Models\BLPedidoItem::where('estado', 'completado')
180+
->whereBetween('updated_at', [$mesActual, $ahora->copy()->endOfMonth()])
181+
->sum('cantidad_empaques');
182+
183+
// Producción del mes anterior
184+
$produccionAnterior = \App\Models\BLPedidoItem::where('estado', 'completado')
185+
->whereBetween('updated_at', [$mesAnterior, $mesAnterior->copy()->endOfMonth()])
186+
->sum('cantidad_empaques');
187+
188+
return [
189+
[
190+
'mes' => $meses[$mesAnterior->format('m')],
191+
'produccion' => $produccionAnterior,
192+
],
193+
[
194+
'mes' => $meses[$mesActual->format('m')],
195+
'produccion' => $produccionActual,
196+
],
197+
];
198+
}
199+
156200

157201

158202
}

resources/js/components/BL/graficoProduccionBL.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function GraficoProduccionBL({ semanal, mensual, trimestral }) {
4646
<XAxis dataKey="dia" stroke="#888888" /> {/* usa 'dia' del mes */}
4747
<YAxis stroke="#888888" />
4848
<Tooltip />
49-
<Bar dataKey="produccion" fill="#10b981" barSize={40} />
49+
<Bar dataKey="produccion" fill="#3b82f6" barSize={40} />
5050
</BarChart>
5151
);
5252
}
@@ -61,8 +61,8 @@ export default function GraficoProduccionBL({ semanal, mensual, trimestral }) {
6161
<Area
6262
type="monotone"
6363
dataKey="produccion"
64-
stroke="#6366f1"
65-
fill="#818cf8"
64+
stroke="#3b82f6"
65+
fill="#4e90f9ff"
6666
fillOpacity={0.4}
6767
/>
6868
</AreaChart>
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import {
2+
useReactTable,
3+
getCoreRowModel,
4+
getSortedRowModel,
5+
getFilteredRowModel,
6+
flexRender,
7+
} from '@tanstack/react-table';
8+
import { useState } from 'react';
9+
import { themeByProject } from '../utils/theme';
10+
import { usePage } from '@inertiajs/react';
11+
import axios from 'axios';
12+
import EditarProductForm from './EditProductModal';
13+
import AgentModalWrapper from '../agentsModalWrapper';
14+
import { router } from '@inertiajs/react';
15+
import { Toast } from "flowbite-react";
16+
import { useEffect } from 'react';
17+
import { HiCheck, HiX } from "react-icons/hi";
18+
19+
export default function TablaProductosBL() {
20+
const { props } = usePage();
21+
const proyecto = props?.auth?.user?.proyecto || 'AZZU';
22+
const theme = themeByProject[proyecto];
23+
const [productDetail, setProductDetail] = useState(null);
24+
const [colores, setColores] = useState(null);
25+
const [modalOpenProductEdit, setModalOpenProductEdit] = useState(false);
26+
const [toast, setToast] = useState({
27+
show: false,
28+
success: false,
29+
message: "",
30+
});
31+
32+
useEffect(() => {
33+
if (toast.show) {
34+
const timer = setTimeout(() => {
35+
setToast({ show: false, success: false, message: '' });
36+
}, 4000);
37+
38+
return () => clearTimeout(timer);
39+
}
40+
}, [toast]);
41+
42+
const openModalEditProduct = async(id) => {
43+
try {
44+
const response = await axios.get(`BLProductShow/${id}`);
45+
setProductDetail(response.data.productDetails);
46+
setColores(response.data.colores);
47+
setModalOpenProductEdit(true);
48+
} catch (error) {
49+
console.error("Error al cargar el producto", error);
50+
}
51+
}
52+
const closeModal = () => {
53+
setModalOpenProductEdit(false);
54+
};
55+
const handleEditarProducto = (productData) => {
56+
console.log("➡️ productData recibido:", productData);
57+
console.log("➡️ id:", productData?.id);
58+
router.put(route(`productBL.update`, {producto: productData.id}), productData, {
59+
preserveState: true,
60+
onSuccess: () => {
61+
setToast({
62+
show: true,
63+
success: true,
64+
message: "producto editado correctamente"
65+
});
66+
// closeModal();
67+
// Refrescar la lista de clientes
68+
// router.visit(route('clientes.index'));
69+
},
70+
onError: (errors) => {
71+
const primerError = Object.values(errors)[0];
72+
setToast({
73+
show: true,
74+
success: false,
75+
message: primerError || "Error al editar el producto"
76+
});
77+
},
78+
onFinish: (visit) => {
79+
// Si hubo error de servidor (status 500 o más)
80+
if (visit.response?.status >= 500) {
81+
const msg = visit.response?.data?.message || "Error interno del servidor";
82+
setToast({
83+
show: true,
84+
success: false,
85+
message: msg
86+
});
87+
}
88+
}
89+
});
90+
};
91+
92+
// <th className="px-2 sm:px-4 py-2 sm:py-3">Cliente</th>
93+
// <th className="px-2 sm:px-4 py-2 sm:py-3">Pedido</th>
94+
// <th className="px-2 sm:px-4 py-2 sm:py-3">Referencia</th>
95+
// <th className="px-2 sm:px-4 py-2 sm:py-3">Cantidad</th>
96+
// <th className="px-2 sm:px-4 py-2 sm:py-3">Nota</th>
97+
// <th className="px-2 sm:px-4 py-2 sm:py-3">Estado</th>
98+
const columns = [
99+
{ accessorKey: 'cliente', header: 'cliente' },
100+
{ accessorKey: 'pedido', header: 'pedido' },
101+
{ accessorKey: 'cantidad', header: 'cantidad' },
102+
{ accessorKey: 'nota', header: 'nota' },
103+
{ accessorKey: 'estado', header: 'estado' },
104+
{
105+
id: 'acciones',
106+
header: 'Editar Producto',
107+
cell: ({ row }) => (
108+
<button
109+
className={`${theme.text} font-medium hover:underline cursor-pointer`}
110+
onClick={() => openModalEditProduct(row.original.id)}
111+
>
112+
Editar
113+
</button>
114+
),
115+
},
116+
];
117+
118+
const [sorting, setSorting] = useState([]);
119+
120+
const table = useReactTable({
121+
data: productos,
122+
columns,
123+
state: {
124+
sorting,
125+
globalFilter: search,
126+
},
127+
onSortingChange: setSorting,
128+
getCoreRowModel: getCoreRowModel(),
129+
getSortedRowModel: getSortedRowModel(),
130+
getFilteredRowModel: getFilteredRowModel(),
131+
globalFilterFn: (row, columnId, value) => {
132+
const cellValue = String(row.getValue(columnId) ?? '').toLowerCase();
133+
return cellValue.includes(value.toLowerCase());
134+
},
135+
});
136+
137+
return (
138+
<>
139+
<table className="min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
140+
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800 dark:text-gray-400">
141+
{table.getHeaderGroups().map(headerGroup => (
142+
<tr key={headerGroup.id}>
143+
{headerGroup.headers.map(header => (
144+
<th
145+
key={header.id}
146+
className="px-6 py-3 cursor-pointer select-none"
147+
onClick={header.column.getToggleSortingHandler()}
148+
>
149+
{flexRender(header.column.columnDef.header, header.getContext())}
150+
{{
151+
asc: ' 🔼',
152+
desc: ' 🔽',
153+
}[header.column.getIsSorted()] ?? null}
154+
</th>
155+
))}
156+
</tr>
157+
))}
158+
</thead>
159+
<tbody>
160+
{table.getRowModel().rows.map((row, index) => (
161+
<tr
162+
key={row.id}
163+
className={`${
164+
index % 2 === 0
165+
? 'bg-white dark:bg-gray-800'
166+
: 'bg-gray-50 dark:bg-gray-700'
167+
} hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors`}
168+
>
169+
{row.getVisibleCells().map(cell => (
170+
<td key={cell.id} className="px-6 py-4 whitespace-nowrap">
171+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
172+
</td>
173+
))}
174+
</tr>
175+
))}
176+
</tbody>
177+
</table>
178+
{modalOpenProductEdit && (
179+
<AgentModalWrapper closeModal={closeModal}>
180+
<EditarProductForm onClose={closeModal} productDetail={productDetail} onSave={handleEditarProducto} colores={colores} />
181+
</AgentModalWrapper>
182+
)
183+
}
184+
{toast.show && (
185+
<div className="fixed bottom-6 right-6 z-51">
186+
<Toast>
187+
<div
188+
className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${
189+
toast.success ? "bg-green-100 text-green-500" : "bg-red-100 text-red-500"
190+
}`}
191+
>
192+
{toast.success ? <HiCheck className="h-5 w-5" /> : <HiX className="h-5 w-5" />}
193+
</div>
194+
<div className="ml-3 text-sm font-normal">{toast.message}</div>
195+
</Toast>
196+
</div>
197+
)}
198+
</>
199+
);
200+
}

resources/js/pages/BLAnalisis.jsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,10 @@ import { Separator } from '@/components/ui/separator';
66
import { CardHeader } from "@/components/ui/card";
77
import GraficoProduccionBL from '@/components/BL/graficoProduccionBL';
88
import { useState } from 'react';
9-
const datosClientes = [
10-
{ cliente: 'Cliente A', total: 120 },
11-
{ cliente: 'Cliente B', total: 95 },
12-
{ cliente: 'Cliente C', total: 70 },
13-
{ cliente: 'Cliente D', total: 40 },
14-
{ cliente: 'Cliente E', total: 25 },
15-
];
169

17-
const datosComparativa = [
18-
{ periodo: 'Julio', valor: 420 },
19-
{ periodo: 'Agosto', valor: 530 },
20-
];
2110

22-
export default function BLAnalisis({pedidosMes, cantidadProduccion, clientesActivos, cantidadProductosStock, rankingClientes, semanal, mensual, trimestral}) {
23-
console.log('trimestal main:', trimestral);
11+
export default function BLAnalisis({pedidosMes, cantidadProduccion, clientesActivos, cantidadProductosStock, rankingClientes, semanal, mensual, trimestral, comparativa}) {
12+
console.log('comparativa main:', comparativa);
2413

2514
const [data, setData] = useState({ semanal: [], mensual: [], trimestral: [] });
2615
const [tipo, setTipo] = useState("semanal");
@@ -66,7 +55,7 @@ export default function BLAnalisis({pedidosMes, cantidadProduccion, clientesActi
6655
<div className="lg:col-span-2 h-[300px]">
6756
<Card className="h-full">
6857
<CardContent className="h-full">
69-
<GraficoProduccionBL semanal={semanal} mensual={mensual} trimestal={trimestral}/>
58+
<GraficoProduccionBL semanal={semanal} mensual={mensual} trimestral={trimestral} />
7059
</CardContent>
7160
</Card>
7261
</div>
@@ -105,13 +94,13 @@ export default function BLAnalisis({pedidosMes, cantidadProduccion, clientesActi
10594
<CardContent className="p-4">
10695
<h3 className="text-lg font-semibold mb-4">Comparativa entre periodos</h3>
10796
<div className="grid grid-cols-2 gap-2">
108-
{datosComparativa.map((item, idx) => (
97+
{comparativa.map((item, idx) => (
10998
<div
11099
key={idx}
111100
className="p-3 rounded-md shadow bg-gradient-to-r from-white to-blue-50 dark:from-gray-800 dark:to-blue-900"
112101
>
113-
<p className="text-sm font-medium text-gray-600 dark:text-gray-300">{item.periodo}</p>
114-
<p className="text-2xl font-bold text-blue-700">{item.valor}</p>
102+
<p className="text-sm font-medium text-gray-600 dark:text-gray-300">{item.mes}</p>
103+
<p className="text-2xl font-bold text-blue-700">{item.produccion}</p>
115104
</div>
116105
))}
117106
</div>

resources/js/pages/BLMarcacion.jsx

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import ItemsTable from "@/components/BLHistorico/ItemsTable";
1212
import ResultTable from "@/components/BLHistorico/ResultTable";
1313
import FiltrosMarcaciones from "@/components/BLHistorico/FiltrosMarcaciones";
1414
import FiltroUsuario from "@/components/BLHistorico/FiltroUsuario";
15+
import TablaProductosBL from "@/components/BL/tablaProdBL";
1516
import {
1617
BookText,
1718
SaveAll
@@ -217,40 +218,10 @@ export default function MarcadoPage({ orderCustomer, buttonUser }) {
217218
</div>
218219
)}
219220
</div>
220-
221-
<div className="mt-8">
222-
<div className="flex justify-between items-center mb-6">
223-
<h2 className="text-xl font-semibold">Historial de Marcaciones</h2>
224-
<div className="text-sm text-gray-500">
225-
Mostrando: {datosMostrar().length} items
226-
</div>
227-
</div>
228-
229-
<FiltrosMarcaciones
230-
filtroActivo={filtroActivo}
231-
onChangeFiltro={setFiltroActivo}
232-
contadores={{
233-
enProceso: enProceso.length,
234-
completados: completados.length,
235-
pendientes: pendientes.length
236-
}}
237-
/>
238-
{filtroActivo === 'completados' && usuarios.length > 0 && (
239-
<FiltroUsuario
240-
usuarios={usuarios}
241-
usuarioFiltro={usuarioFiltro}
242-
onChangeUsuarioFiltro={setUsuarioFiltro}
243-
/>
244-
)}
245-
<div className="mt-6 overflow-x-auto">
246-
<ResultTable
247-
datos={datosMostrar()}
248-
tipo={filtroActivo}
249-
onActualizarEstado={actualizarEstadoItem}
250-
mostrarAcciones={filtroActivo === 'enProceso' || filtroActivo === 'todos'}
251-
/>
252-
</div>
221+
<div className="overflow-x-auto">
222+
<TablaProductosBL productos={productos} search={search} />
253223
</div>
224+
254225
</div>
255226
</AppLayout>
256227
);

0 commit comments

Comments
 (0)