Componente: ActualizacionProductos¶
Autor: Adalberto González
Selector: app-actualizacion-productos
Ubicación: SitioLogiGho/src/app/components/actualizacion-productos
¿Qué hace?¶
Modal de edición de un producto existente. Recibe el producto a editar desde el componente padre (PortafolioComponent), pre-carga todos sus campos en un formulario reactivo y permite modificar características, dimensiones, variación, etiquetas (tipo, proveedor, tienda) y estado. Al confirmar, persiste los cambios en la base de datos usando el _id de MongoDB. La imagen es de solo lectura en la vista actual.
Diferencia clave respecto a CreacionProductos¶
A diferencia de CreacionProductos, este componente sí usa *ngIf="abierto", por lo que se destruye completamente al cerrarse. Esto significa que no hay estado residual entre aperturas: el formulario y los dropdowns siempre parten limpios.
Roles y acceso¶
| Acceso | Descripción |
|---|---|
| Autenticado | Requiere sesión activa. La apertura del modal la controla PortafolioComponent, que está protegido por AuthGuard. |
Estructura de archivos¶
actualizacion-productos/
├── actualizacion-productos.component.ts
├── actualizacion-productos.component.html
└── actualizacion-productos.component.scss
Inputs y Outputs¶
| Decorador | Nombre | Tipo | Descripción |
|---|---|---|---|
@Input |
producto |
Producto \| null |
El producto a editar. El padre lo inyecta al abrir el modal. |
@Input |
abierto |
boolean |
Controla si el modal es visible (vía *ngIf). |
@Output |
cerrar |
EventEmitter<void> |
Emitido al llamar a close(). El padre lo escucha para ocultar el modal. |
@Output |
actualizado |
EventEmitter<void> |
Emitido tras una actualización exitosa. El padre lo escucha en despuesDeActualizar() para recargar la tabla. |
Interfaz Producto (interna)¶
interface Producto {
_id?: { $oid: string } | string;
id?: number | null;
nombre?: string;
imagenUrl?: string;
precioventa?: number | null;
cantidad?: number | null;
idproducto?: number | null | string;
idvariacion?: string;
variacion?: string;
precioproveedor?: number | null;
sku?: string;
tipoproducto?: string;
IdTienda?: string;
Tienda?: string;
proveedor?: string;
categoria?: string;
peso?: number | null;
largo?: number | null;
ancho?: number | null;
alto?: number | null;
color?: string;
estado?: string;
perfilproducto?: string;
}
Todos los campos son opcionales (?) a diferencia de la interfaz de creación, ya que el producto puede venir con datos parciales desde la tabla.
Propiedades del componente¶
Formulario¶
| Propiedad | Tipo | Descripción |
|---|---|---|
form |
FormGroup |
Formulario reactivo con todos los campos del producto. |
Listas de datos¶
| Propiedad | Tipo | Descripción |
|---|---|---|
tiendas |
any[] |
Tiendas activas cargadas desde Tienda en la BD (filtradas por Integracion=Interrapidisimo y NombreTienda). |
categorias |
any[] |
Categorías cargadas desde ProductosCategorias. |
proveedores |
any[] |
Subconjunto de tiendas cuyo TipoTienda incluye "Proveedor". |
Estado de imagen¶
| Propiedad | Tipo | Default | Descripción |
|---|---|---|---|
selectedFile |
File \| null |
null |
Archivo de imagen seleccionado. Declarado pero sin uso activo (la imagen no es editable en la vista actual). |
imagenPreviaUrl |
string \| null |
null |
URL de la imagen actual del producto para previsualización. |
Estado de dropdowns¶
| Propiedad | Tipo | Descripción |
|---|---|---|
buscarCategoria |
string |
Texto en el buscador de categoría. |
mostrarCategoriaMenu |
boolean |
Visibilidad del dropdown de categoría. |
categoriasFiltradas |
any[] |
Lista filtrada de categorías. |
categoriaSeleccionadaNombre |
string |
Nombre de la categoría seleccionada (mostrado en el placeholder). |
buscarProveedor |
string |
Texto en el buscador de proveedor. |
mostrarProveedorMenu |
boolean |
Visibilidad del dropdown de proveedor. |
proveedoresFiltrados |
any[] |
Lista filtrada de proveedores. |
proveedorSeleccionadoNombre |
string |
Nombre del proveedor seleccionado. |
buscarTienda |
string |
Texto en el buscador de tienda. |
mostrarTiendaMenu |
boolean |
Visibilidad del dropdown de tienda. |
tiendasFiltradas |
any[] |
Lista filtrada de tiendas. |
tiendaSeleccionadaNombre |
string |
Nombre de la tienda seleccionada. |
buscarTipoProducto |
string |
Texto en el buscador de tipo de producto. |
mostrarTipoProductoMenu |
boolean |
Visibilidad del dropdown de tipo de producto. |
tipoProductoFiltradas |
any[] |
Lista filtrada de tipos de producto. |
tipoProductoSeleccionadoNombre |
string |
Nombre del tipo seleccionado. |
tipoProductoOpciones |
{ Nombre, Value }[] |
Lista estática: Dropshipping, Propia, Proveedor. |
Secciones de la vista (modal)¶
| # | Sección | Descripción |
|---|---|---|
| 1 | Cabecera | Título "Actualización de Producto" y botón de cierre (×). |
| 2 | Características | Nombre, precio de venta, precio proveedor, cantidad y categoría (con dropdown buscador). |
| 3 | Imagen del Producto | Muestra la imagen actual como previsualización. No es editable desde la UI. |
| 4 | Dimensiones | Ancho, alto, largo y peso en sus respectivas unidades. |
| 5 | Características (variación) | Variación, color y SKU del producto. |
| 6 | Etiquetas del producto | Tipo de producto, proveedor y tienda mediante buscadores con dropdown custom. |
| 7 | Otros | Estado (Activo/Inactivo) y perfil del producto (Propio/Publico/Privado). |
| 8 | Footer | Botones "Cerrar" (close()) y "Actualizar" (onActualizar()). |
Flujo de inicialización¶
ngOnInit()
→ fetchTableDataTienda() (carga tiendas activas desde BD)
→ fetchTableDataCategoria() (carga categorías desde BD)
→ fetchTableDataProveedor() (carga proveedores desde BD)
ngOnChanges()
→ si producto cambia y abierto = true:
→ cargarDatosProducto() (pre-carga el formulario con los datos del producto)
→ si abierto cambia a false:
→ resetForm() (limpia el formulario y todos los estados de dropdown)
Métodos¶
cargarDatosProducto()¶
Pre-carga el formulario con los datos del @Input() producto.
Proceso:
1. Busca en las listas locales (tiendas, proveedores, categorias) el ítem cuyo Nombre o Id coincida con los campos del producto.
2. Hace patchValue en el formulario con todos los campos del producto, asignando los IDs encontrados a nombreTienda, nombreProveedor y nombreCategoria.
3. Establece los nombres en los placeholders de los dropdowns (tiendaSeleccionadaNombre, proveedorSeleccionadoNombre, categoriaSeleccionadaNombre, tipoProductoSeleccionadoNombre).
4. Asigna imagenPreviaUrl para la previsualización.
resetForm()¶
Limpia el formulario y el estado de todos los dropdowns al cerrar el modal.
Qué limpia:
- form.reset()
- selectedFile → null
- imagenPreviaUrl → null
- Nombres seleccionados de los cuatro dropdowns
- Campos de búsqueda (buscarCategoria, buscarProveedor, buscarTienda, buscarTipoProducto)
- Input de archivo (si existe el @ViewChild)
onActualizar(): Promise<void>¶
Método principal de actualización.
Proceso:
1. Valida que producto._id exista; si no, muestra Swal.fire de error.
2. Muestra Swal.fire de carga con spinner.
3. Si selectedFile tiene valor, sube la imagen con cargarImagenProducto() y actualiza imagenUrl (no ocurre en la UI actual porque no hay input de archivo).
4. Resuelve nombres de tienda, proveedor y categoría buscando en sus listas locales.
5. Construye productoActualizado haciendo spread de this.producto y sobreescribiendo con los valores del formulario.
6. Extrae el _id real de MongoDB (maneja tanto string como { $oid: string }).
7. Llama a actualizarGenerico(productoActualizado, productoId, 'metodoGenerico?coleccion=Productos').
8. Emite actualizado y llama a close() al finalizar.
Manejo de error: Cualquier excepción muestra Swal.fire de error con el mensaje de la excepción.
fetchTableDataTienda(): Promise<void>¶
Carga tiendas activas filtrando por Estado=ACTIVO, Integracion=Interrapidisimo y NombreTienda (tiendas asignadas al usuario). Descomprime con decompressZstd. Ordena alfabéticamente y mapea a { Nombre, Id }.
Diferencia respecto a CreacionProductos: Incluye los filtros adicionales Integracion y NombreTienda en la query, lo que restringe el resultado a tiendas con integración activa asignadas al usuario.
fetchTableDataCategoria(): Promise<void>¶
Carga categorías desde ProductosCategorias. Descomprime con decompressGzip. Mapea a { Nombre, Id }.
fetchTableDataProveedor(): Promise<void>¶
Carga tiendas con TipoTienda que contiene "Proveedor", usando los mismos filtros adicionales que fetchTableDataTienda. Descomprime con decompressZstd. Mapea a { Nombre, Id, TipoTienda }.
cargarImagenProducto(key, userData): Promise<void>¶
Sube una imagen al bucket S3 imagenes-productos-logigho.
Nota: Declarado e implementado, pero no se invoca desde la vista actual porque el campo de imagen no es editable. Está disponible para cuando se habilite la edición de imágenes.
Métodos de dropdown (patrón repetido para categoría, proveedor, tienda y tipo de producto)¶
A diferencia de CreacionProductos, los dropdowns aquí no están indexados por array — son variables simples (no por formulario), ya que solo existe un producto a la vez.
| Método | Descripción |
|---|---|
filtrarXxx() |
Filtra la lista completa según el texto en buscarXxx (case-insensitive). |
onXxxSeleccionado(item) |
Guarda el nombre en xxxSeleccionadoNombre, limpia la búsqueda, cierra el menú y hace patchValue con el ID del ítem. |
abrirXxxMenu() |
Cierra todos los menús, abre el de xxx y puebla la lista sin filtro. |
alternadorXxxMenu() |
Alterna la visibilidad del menú de xxx. Si se abre, puebla la lista sin filtro. |
cerrarTodosLosMenus() |
Escuchador @HostListener('document:click'). Pone todos los mostrarXxxMenu en false. |
Servicios Angular utilizados¶
| Servicio | Métodos usados | Propósito |
|---|---|---|
ConsumoGenericoService |
consultarGenerico(), actualizarGenerico() |
Consulta de tiendas, categorías y proveedores; actualización del producto |
DecompressionService |
decompressGzip(), decompressZstd() |
Descomprime las respuestas del backend |
PutObjectService |
cargarInformacion() |
Sube imágenes a S3 (disponible pero sin uso activo en la UI actual) |
GetObjectService |
— | Inyectado pero sin uso activo en este componente |
TiendasService |
— | Inyectado pero sin uso activo en este componente |
FormBuilder |
group() |
Construcción del formulario reactivo |
Dependencias externas¶
| Librería | Uso |
|---|---|
sweetalert2 |
Alertas de carga, éxito y error durante la actualización |
Endpoints que consume¶
| Método | Ruta | Cuándo |
|---|---|---|
GET |
metodoGenerico?coleccion=Tienda&Estado=ACTIVO&Integracion=Interrapidisimo&NombreTienda=... |
Al inicializar (tiendas y proveedores) |
GET |
metodoGenerico?coleccion=ProductosCategorias |
Al inicializar (categorías) |
PUT/PATCH |
metodoGenerico?coleccion=Productos (vía actualizarGenerico) |
Al confirmar la actualización |
Changelog del componente¶
| Fecha | Autor | Cambio |
|---|---|---|
| 2026-04-23 | Adalberto González | Creación del componente de actualización de productos con formulario reactivo |
| 2026-04-23 | Adalberto González | Migración de <select> nativos a buscadores con dropdown custom para categoría, proveedor, tienda y tipo de producto |
Observaciones¶
- Imagen no editable: El HTML muestra la imagen como previsualización con el mensaje "La imagen del producto no es editable", pero el TypeScript tiene
onFileSelected,selectedFileycargarImagenProductopreparados. La edición de imagen puede habilitarse añadiendo un<input type="file">al template sin cambios en el.ts. GetObjectServiceyTiendasServicesin uso: En el codigo pero sin llamadas activas.- Filtro de tiendas diferente al de creación:
fetchTableDataTiendayfetchTableDataProveedorincluyenIntegracion=InterrapidisimoyNombreTienda, lo que puede dar un resultado distinto al deCreacionProductos. Conviene unificar el criterio de consulta. cargarDatosProductodepende de que las listas estén cargadas: SingOnChangesdispara antes de quefetchTableDataTienda/Categoria/Proveedorterminen, los dropdowns no mostrarán el ítem preseleccionado correctamente.- Sin validadores en el formulario: Al igual que en creación, no hay
Validatorsdefinidos, por lo que el botón "Actualizar" no bloquea envíos con campos vacíos. - Ciclo de vida limpio: Al usar
*ngIf="abierto", el componente se destruye al cerrar. No hay problemas de estado residual entre aperturas, a diferencia deCreacionProductos.