Skip to content

Commit 02e9843

Browse files
authored
Merge pull request #56 from chechojgb/buttonsLovers
Add: agregar edicion producto
2 parents 67ef874 + 0a7eaaf commit 02e9843

File tree

4 files changed

+271
-40
lines changed

4 files changed

+271
-40
lines changed

app/Http/Controllers/BlProductoController.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,4 +221,15 @@ public function update(Request $request, BlProducto $producto)
221221
]
222222
]);
223223
}
224+
225+
public function show($id)
226+
{
227+
$productDetails = BlProducto::with(['color'])
228+
->findOrFail($id);
229+
$colores = BlColor::get();
230+
return response()->json([
231+
'productDetails' => $productDetails,
232+
'colores' => $colores
233+
]);
234+
}
224235
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import React, { useState, useEffect } from "react";
2+
import { Button, Label, TextInput, Select } from "flowbite-react";
3+
import { ShoppingCartIcon } from "@heroicons/react/24/solid";
4+
5+
const EditarProductForm = ({ productDetail, onSave, onCancel, onClose, colores }) => {
6+
console.log("editar:", productDetail);
7+
8+
const [formData, setFormData] = useState({
9+
tipo_producto: productDetail.tipo_producto || "",
10+
tamanio: productDetail.tamanio || "",
11+
color_id: productDetail.color?.id || "", // guardamos id del color
12+
});
13+
14+
const [descripcion, setDescripcion] = useState(productDetail.descripcion || "");
15+
16+
useEffect(() => {
17+
// buscamos el color seleccionado dentro del array colores
18+
const colorSeleccionado = colores?.find(
19+
(c) => c.id === parseInt(formData.color_id)
20+
);
21+
22+
// construimos la descripcion
23+
const nuevaDescripcion = `${formData.tipo_producto || ""} ${formData.tamanio || ""} ${colorSeleccionado?.codigo || ""}`.trim();
24+
setDescripcion(nuevaDescripcion);
25+
}, [formData.tipo_producto, formData.tamanio, formData.color_id, colores]);
26+
27+
const handleChange = (e) => {
28+
const { name, value } = e.target;
29+
setFormData((prev) => ({ ...prev, [name]: value }));
30+
};
31+
32+
const handleSubmit = (e) => {
33+
e.preventDefault();
34+
// pasamos la descripcion calculada al guardar
35+
onSave({ ...formData, descripcion });
36+
};
37+
38+
const hasColors = colores && colores.length > 0;
39+
40+
return (
41+
<form
42+
onSubmit={handleSubmit}
43+
className="space-y-6 p-6 bg-white dark:bg-gray-900 rounded-2xl shadow-2xl border border-gray-200 dark:border-gray-700 relative overflow-hidden"
44+
>
45+
{/* Encabezado con gradiente */}
46+
<div className=" inset-x-0 top-0 h-28 bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500 rounded-t-2xl" />
47+
48+
{/* Avatar */}
49+
<div className="relative flex justify-center -mt-10">
50+
<div className="w-20 h-20 rounded-full bg-white dark:bg-gray-800 shadow-lg flex items-center justify-center ring-4 ring-white dark:ring-gray-900">
51+
<ShoppingCartIcon className="w-16 h-16 text-indigo-500" />
52+
</div>
53+
</div>
54+
55+
{/* Nombre en header */}
56+
<div className="text-center mt-2">
57+
<h2 className="text-2xl font-extrabold text-gray-800 dark:text-white">
58+
{descripcion || "Editar Producto"}
59+
</h2>
60+
<p className="text-sm text-gray-500 dark:text-gray-400">
61+
Modifica los datos y guarda los cambios
62+
</p>
63+
</div>
64+
65+
{/* Campos */}
66+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 pt-4">
67+
{/* Tipo de Producto */}
68+
<div>
69+
<Label htmlFor="tipo_producto" value="Tipo de Producto" />
70+
<Select
71+
id="tipo_producto"
72+
required
73+
value={formData.tipo_producto}
74+
onChange={(e) =>
75+
setFormData({ ...formData, tipo_producto: e.target.value })
76+
}
77+
>
78+
<option value="">Seleccione...</option>
79+
<option value="BT">Botón (BT)</option>
80+
<option value="OJ">Ojillo (OJ)</option>
81+
<option value="BR">Broche (BR)</option>
82+
</Select>
83+
</div>
84+
85+
{/* Tamaño */}
86+
<div>
87+
<Label htmlFor="tamanio" value="Tamaño" />
88+
<TextInput
89+
id="tamanio"
90+
name="tamanio"
91+
value={formData.tamanio}
92+
onChange={handleChange}
93+
className="rounded-xl focus:ring-2 focus:ring-indigo-500"
94+
/>
95+
</div>
96+
97+
{/* Color */}
98+
<div>
99+
<Label htmlFor="color_id" value="Color" />
100+
<Select
101+
id="color_id"
102+
required
103+
value={formData.color_id}
104+
onChange={(e) =>
105+
setFormData({ ...formData, color_id: e.target.value })
106+
}
107+
>
108+
<option value="">Seleccione...</option>
109+
{hasColors ? (
110+
colores.map((color) => (
111+
<option key={color.id} value={color.id}>
112+
{color.nombre} ({color.codigo})
113+
</option>
114+
))
115+
) : (
116+
<option value="" disabled>
117+
No hay colores disponibles
118+
</option>
119+
)}
120+
</Select>
121+
</div>
122+
</div>
123+
124+
{/* Descripción automática */}
125+
<div>
126+
<Label htmlFor="descripcion" value="Descripción (automática)" />
127+
<TextInput
128+
id="descripcion"
129+
name="descripcion"
130+
value={descripcion}
131+
readOnly
132+
className="rounded-xl bg-gray-100 dark:bg-gray-800 cursor-not-allowed"
133+
/>
134+
</div>
135+
136+
{/* Botones */}
137+
<div className="flex justify-end gap-3 pt-4">
138+
<Button
139+
color="light"
140+
onClick={onClose}
141+
type="button"
142+
className="rounded-xl shadow-md hover:shadow-lg transition-all hover:scale-105"
143+
>
144+
Cancelar
145+
</Button>
146+
<Button
147+
color="purple"
148+
type="submit"
149+
className="rounded-xl shadow-md hover:shadow-lg transition-all hover:scale-105"
150+
>
151+
Guardar Cambios
152+
</Button>
153+
</div>
154+
</form>
155+
);
156+
};
157+
158+
export default EditarProductForm;

resources/js/components/BL/tablaProdBL.jsx

Lines changed: 101 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,64 @@ import {
88
import { useState } from 'react';
99
import { themeByProject } from '../utils/theme';
1010
import { usePage } from '@inertiajs/react';
11+
import axios from 'axios';
12+
import EditarProductForm from './EditProductModal';
13+
import AgentModalWrapper from '../agentsModalWrapper';
1114

1215
export default function TablaProductosBL({ productos, openModal, search }) {
1316
const { props } = usePage();
1417
const proyecto = props?.auth?.user?.proyecto || 'AZZU';
1518
const theme = themeByProject[proyecto];
19+
const [productDetail, setProductDetail] = useState(null);
20+
const [colores, setColores] = useState(null);
21+
const [modalOpenProductEdit, setModalOpenProductEdit] = useState(false);
22+
const openModalEditProduct = async(id) => {
23+
try {
24+
const response = await axios.get(`BLProductShow/${id}`);
25+
setProductDetail(response.data.productDetails);
26+
setColores(response.data.colores);
27+
setModalOpenProductEdit(true);
28+
} catch (error) {
29+
console.error("Error al cargar el producto", error);
30+
}
31+
}
32+
const closeModal = () => {
33+
setModalOpenProductEdit(false);
34+
};
35+
const handleEditarProducto = (clientData) => {
36+
console.log('datos producto',clientData);
37+
router.post(route(`clientesBL.update`, clientData.id), clientData, {
38+
preserveState: true,
39+
onSuccess: () => {
40+
setToast({
41+
show: true,
42+
success: true,
43+
message: "cliente editado correctamente"
44+
});
45+
// Refrescar la lista de clientes
46+
// router.visit(route('clientes.index'));
47+
},
48+
onError: (errors) => {
49+
const primerError = Object.values(errors)[0];
50+
setToast({
51+
show: true,
52+
success: false,
53+
message: primerError || "Error al editar el cliente"
54+
});
55+
},
56+
onFinish: (visit) => {
57+
// Si hubo error de servidor (status 500 o más)
58+
if (visit.response?.status >= 500) {
59+
const msg = visit.response?.data?.message || "Error interno del servidor";
60+
setToast({
61+
show: true,
62+
success: false,
63+
message: msg
64+
});
65+
}
66+
}
67+
});
68+
};
1669

1770
const columns = [
1871
{ accessorKey: 'tipo_producto', header: 'Tipo de producto' },
@@ -26,7 +79,7 @@ export default function TablaProductosBL({ productos, openModal, search }) {
2679
cell: ({ row }) => (
2780
<button
2881
className={`${theme.text} font-medium hover:underline cursor-pointer`}
29-
onClick={() => openModal(row.original.id)}
82+
onClick={() => openModalEditProduct(row.original.id)}
3083
>
3184
Editar
3285
</button>
@@ -54,44 +107,52 @@ export default function TablaProductosBL({ productos, openModal, search }) {
54107
});
55108

56109
return (
57-
<table className="min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
58-
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800 dark:text-gray-400">
59-
{table.getHeaderGroups().map(headerGroup => (
60-
<tr key={headerGroup.id}>
61-
{headerGroup.headers.map(header => (
62-
<th
63-
key={header.id}
64-
className="px-6 py-3 cursor-pointer select-none"
65-
onClick={header.column.getToggleSortingHandler()}
66-
>
67-
{flexRender(header.column.columnDef.header, header.getContext())}
68-
{{
69-
asc: ' 🔼',
70-
desc: ' 🔽',
71-
}[header.column.getIsSorted()] ?? null}
72-
</th>
73-
))}
74-
</tr>
75-
))}
76-
</thead>
77-
<tbody>
78-
{table.getRowModel().rows.map((row, index) => (
79-
<tr
80-
key={row.id}
81-
className={`${
82-
index % 2 === 0
83-
? 'bg-white dark:bg-gray-800'
84-
: 'bg-gray-50 dark:bg-gray-700'
85-
} hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors`}
86-
>
87-
{row.getVisibleCells().map(cell => (
88-
<td key={cell.id} className="px-6 py-4 whitespace-nowrap">
89-
{flexRender(cell.column.columnDef.cell, cell.getContext())}
90-
</td>
91-
))}
92-
</tr>
93-
))}
94-
</tbody>
95-
</table>
110+
<>
111+
<table className="min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
112+
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-800 dark:text-gray-400">
113+
{table.getHeaderGroups().map(headerGroup => (
114+
<tr key={headerGroup.id}>
115+
{headerGroup.headers.map(header => (
116+
<th
117+
key={header.id}
118+
className="px-6 py-3 cursor-pointer select-none"
119+
onClick={header.column.getToggleSortingHandler()}
120+
>
121+
{flexRender(header.column.columnDef.header, header.getContext())}
122+
{{
123+
asc: ' 🔼',
124+
desc: ' 🔽',
125+
}[header.column.getIsSorted()] ?? null}
126+
</th>
127+
))}
128+
</tr>
129+
))}
130+
</thead>
131+
<tbody>
132+
{table.getRowModel().rows.map((row, index) => (
133+
<tr
134+
key={row.id}
135+
className={`${
136+
index % 2 === 0
137+
? 'bg-white dark:bg-gray-800'
138+
: 'bg-gray-50 dark:bg-gray-700'
139+
} hover:bg-gray-100 dark:hover:bg-gray-600 transition-colors`}
140+
>
141+
{row.getVisibleCells().map(cell => (
142+
<td key={cell.id} className="px-6 py-4 whitespace-nowrap">
143+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
144+
</td>
145+
))}
146+
</tr>
147+
))}
148+
</tbody>
149+
</table>
150+
{modalOpenProductEdit && (
151+
<AgentModalWrapper closeModal={closeModal}>
152+
<EditarProductForm onClose={closeModal} productDetail={productDetail} onSave={handleEditarProducto} colores={colores} />
153+
</AgentModalWrapper>
154+
)
155+
}
156+
</>
96157
);
97158
}

routes/web.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@
171171
Route::post('pedidos', [BLPedidosController::class, 'store'])->name('pedidosBL.store');
172172
Route::get('BLPedidosShow/{id}', [BLPedidosController::class, 'show'])->name('pedidosBL.show');
173173
Route::get('BLClientesShow/{id}', [BLClientesController::class, 'show'])->name('ClientesBL.show');
174+
Route::get('BLProductShow/{id}', [BlProductoController::class, 'show'])->name('ProductBL.show');
174175
});
175176
});
176177

0 commit comments

Comments
 (0)