Infranete Interface
1. Descripción general
Infranete Interface es el panel de administración interno de Hawkers. El package.json no incluye campo description, por lo que esta descripción se basa en lo observado en el código.
Es una SPA Angular que centraliza la operativa interna del e-commerce y actúa como frontal de la API Infranete (https://infranete-api.hawkersco.net), compuesta por los microservicios Java/Spring Boot de Hawkers. Cubre las siguientes áreas funcionales:
- Pedidos: consulta y edición de pedidos, resolución de errores (código postal, teléfono, DANE, sin stock), facturación (descarga y generación de facturas), branding de pedidos y gestión de emails fraudulentos.
- Logística: métodos de envío de SFCC (Salesforce Commerce Cloud) y "Dr. Stock" (generación de Excel de stock, restringido a administradores).
- Cupones: alta, búsqueda y borrado de cupones y sus códigos.
- Marketplaces: pedidos por marketplace (Zalando, Decathlon…), actualización de información de Zalando y generación de reportes.
- Feeds: feeds de imágenes, redirecciones y feeds de Google Shopping por site/país (edición de campos, protección de columnas, publicación).
- Web: generación de JSON SEO para PLPs y actualización de stock en SFCC.
- Reportes: reportes de estado logístico y de pedidos.
El acceso está protegido con Auth0, y la visibilidad de cada área del menú depende de los scopes presentes en el token JWT del usuario.
2. Información técnica
| Propiedad | Valor |
|---|---|
| Nombre del paquete | infranete-interface |
| Versión | 0.0.0 (no se versiona el paquete; el versionado real es el BUILD_NUMBER de Jenkins) |
| Versión de Angular | ^21.1.0 |
| Versión de TypeScript | ~5.9.2 |
| Gestor de paquetes | npm (packageManager: npm@11.6.2) |
| Tipo de proyecto | Aplicación única (no workspace multi-proyecto), 100 % componentes standalone, sin NgModules |
| Renderizado | SPA (CSR). No usa Angular Universal/SSR |
| Builder | @angular/build:application (esbuild) |
| Tests unitarios | Vitest 4 (@angular/build:unit-test + jsdom) |
| Formateo | Prettier (printWidth: 100, tabWidth: 4, singleQuote: true, parser angular para HTML) |
3. Arquitectura y diseño
Arquitectura standalone completa: se arranca con bootstrapApplication(App, appConfig) en src/main.ts y cada componente declara sus propios imports. No existe ningún NgModule.
Estructura de src/app:
src/app/
├── app.ts / app.html / app.config.ts / app.routes.ts # shell, providers y rutas
├── components/
│ ├── shared/ # header, sidebar, breadcrumbs, footer (layout AdminLTE)
│ ├── home/ admin/
│ ├── orders/ # orders-info, order-edit, order-branding, invoices,
│ │ # order-generate-invoice, fraudulent-email (+ modales md-*)
│ ├── logistics/ # dr-stock, sfcc-shipping-methods (+ modal md-*)
│ ├── coupons/ # coupons-admin
│ ├── marketplaces/ # marketplaces-orders, zalando
│ ├── feeds/ # images-feeds, redirects, google-feeds, google-feed (+ modales md-*)
│ ├── web/ # json-seo-plp-generate, update-stock
│ └── reports/ # status-report, orders-report
├── services/ # un servicio HTTP por dominio + utilidades
├── models/ # interfaces TS por dominio (orders, coupons, feeds, auth…)
├── imports/ # barrels COMMON_IMPORTS, MATERIAL_IMPORTS, FILTER_IMPORTS
└── filters/ # SourceFilterPipe
El shell (app.html) monta un layout fijo AdminLTE 3: app-header + app-sidebar + content-wrapper con app-breadcrumbs y el router-outlet. Cada componente enrutado envuelve su contenido en <section class="content"><div class="container-fluid">….
Los componentes con prefijo md- son modales abiertos con MatDialog (p. ej. md-add-redirect, md-edit-product-line, md-add-shipping-method).
Flujo principal de autenticación y datos:
flowchart LR
U[Usuario] --> AG[AuthGuard Auth0]
AG -->|no autenticado| A0[Login Auth0]
A0 --> AG
AG -->|autenticado| C[Componente de ruta]
C --> S[Servicio de dominio]
S --> I[AuthHttpInterceptor<br/>añade JWT a /api/*]
I --> API[(infranete-api<br/>microservicios Spring Boot)]
API --> S --> C
C --> SW[AlertService / SweetAlert2]
La plantilla usa la sintaxis moderna de control de flujo de Angular (@if, @for, @switch).
4. Dependencias principales
| Dependencia | Versión | Propósito |
|---|---|---|
@angular/* | ^21.1.0 | Framework base (core, router, forms, http) |
@angular/material | ^21.0.6 | Componentes UI (diálogos, selects, autocomplete, tooltips…) |
@auth0/auth0-angular | ^2.2.3 | Autenticación: AuthGuard, AuthService, AuthHttpInterceptor |
admin-lte | ^3.2.0 | Layout del panel (sidebar, cards, tablas) — CSS y JS globales |
rxjs | ~7.8.0 | Programación reactiva (Observable, map, timeout) |
sweetalert2 | ^11.4.8 | Notificaciones al usuario vía AlertService |
ngx-pagination | ^6.0.3 | Paginación de tablas |
jwt-decode | ^3.1.2 | Decodificar el JWT para leer los scopes en el sidebar |
xlsx | ^0.18.5 | Lectura/generación de Excel en cliente (stock, reportes) |
dayjs | ^1.11.19 | Manejo de fechas |
bootstrap-daterangepicker | ^3.1.0 | Selector de rango de fechas (requiere jQuery y moment, cargados como scripts globales en angular.json) |
No usa NgRx ni librerías internas de Hawkers.
5. Componentes y rutas
Todos los componentes son standalone y todas las rutas están protegidas con canActivate: [AuthGuard] de Auth0. Cada ruta define data.pageTitle y data.breadcrumb (consumido por Breadcrumbs).
| Ruta | Componente | Descripción funcional |
|---|---|---|
/ → /home | Home | Página de inicio |
/admin | Admin | Utilidades de administración |
/orders-info | OrdersInfo | Listado/búsqueda de pedidos con información de envío |
/order-branding | OrderBranding | Pedidos de branding; alta de líneas de producto (modal) |
/order-edit/:order | OrderEdit | Edición de un pedido y sus líneas (modal md-edit-product-line) |
/invoices | Invoices | Búsqueda y descarga de facturas |
/fraudulent-email | FraudulentEmail | Lista negra de emails fraudulentos (alta/baja) |
/order-generate-invoice/:order | OrderGenerateInvoice | Generación de factura para un pedido |
/dr-stock | DrStock | Generación de Excel de stock (visible solo con scope admin en el menú) |
/sfcc-shipping-methods | SfccShippingMethods | CRUD de métodos de envío SFCC (modal md-add-shipping-method) |
/coupons-admin | CouponsAdmin | Administración de cupones y códigos |
/marketplaces-orders | MarketplacesOrders | Pedidos paginados por marketplace/source |
/zalando | Zalando | Actualización de información de Zalando |
/images-feeds | ImagesFeeds | Gestión y subida de imágenes de feeds |
/redirects | Redirects | Gestión de redirecciones (modal md-add-redirect) |
/google-feeds | GoogleFeeds | Listado de feeds de Google por site/país |
/google-feed/:site/:countryIso | GoogleFeed | Edición de un feed concreto: campos, búsqueda/reemplazo, append/prepend, publicación |
/update-stock | UpdateStock | Subida de stock a SFCC desde Excel |
/json-seo-plp-generate | JsonSeoPlpGenerate | Generación de JSON SEO para PLPs a partir de un Drive ID |
/status-report | StatusReport | Reporte de estado logístico |
/orders-report | OrdersReport | Reporte de pedidos |
El Sidebar decodifica el access token con jwt-decode y muestra cada bloque del menú según el scope correspondiente (orders, web, logistics, coupons, marketplaces, feeds, reports, admin):
@if (scopes.indexOf('orders') > -1) { … }
6. Comunicación con APIs / Integraciones externas
Toda la comunicación va contra environment.baseUrl (la API Infranete). Patrón común de los servicios: const baseUrl = environment.baseUrl a nivel de fichero, HttpClient inyectado, retorno Observable<any>; los GET usan query params y los POST/PUT envían HttpParams o FormData como cuerpo.
| Servicio | Prefijo(s) de API | Propósito |
|---|---|---|
OrderService | /api/orders, /api/orders-non-stock, /api/orders-zip-error, /api/orders-phone-error, /api/orders-dane-error, /api/orders-info | Ciclo de vida de pedidos: búsqueda paginada por estado, creación, cancelación, marcado como enviado/pendiente de etiqueta, reenvío a logística/Cooper, descarga y generación de facturas (responseType: 'blob') |
OrderlineService | /api/order-lines | Líneas de pedido (valores distintos) |
ShipmentService | /api/sfccsm | CRUD de métodos de envío SFCC |
StockService | /api/dr-stock | Generación de Excel de stock (responseType: 'blob') |
DynamicsService | /api/dynamics | Acuerdos de precios de venta (Microsoft Dynamics) |
CouponService | /api/coupons | Alta/búsqueda/borrado de cupones y códigos |
MarketplacesService | /api/marketplaces, /api/zalando, /api/reports | Pedidos paginados por source, actualización Zalando (timeout 300 s), reporte marketplace |
FeedsService | /api/images-feeds, /api/redirect-feeds, /api/google-feeds | Subida de imágenes, redirecciones, edición y publicación de feeds de Google |
ProductService | /api/stock-sfcc | Subida de stock a SFCC |
WebService | /api/json-seo-plp-generate | Generación de JSON SEO PLP (timeout 300 s) |
ReportService | /api/reports | Reportes de estado y de pedidos (descarga de blobs) |
FraudulentEmailService | /api/fraudulent-email | Lista de emails fraudulentos |
CountryService | /api/countries | Catálogo de países |
GoogleMapsService | https://maps.googleapis.com/maps/api/js | Carga dinámica del script de Google Maps con la API key del environment |
Ejemplo representativo (OrderService):
public findPaginateByStatus(request: OrderByStatusPaginateRequest): Observable<any> {
return this.http.post(`${baseUrl}/api/orders/find-paginate-by-status`, request);
}
public generateAndDownloadInvoices(orderList: string[]): Observable<Blob> {
return this.http.post(`${baseUrl}/api/orders/generate-and-download-invoices`, orderList, {
responseType: 'blob',
});
}
Interceptor: el AuthHttpInterceptor de Auth0 (registrado en app.config.ts vía HTTP_INTERCEPTORS) adjunta automáticamente el bearer token a las peticiones que casan con environment.auth0allowedList (<baseUrl>/api/*), solicitando audience y scope configurados.
7. Gestión de estado
No hay NgRx ni store global. El enfoque es:
- RxJS + servicios sin estado: los componentes se suscriben a los
Observablede los servicios HTTP y guardan los datos en propiedades locales. - Signals: uso puntual (
signalenApppara el título); no es el mecanismo principal de estado. - Auth0 (
AuthService) como fuente de estado de sesión (usuario, token). localStorage: elSidebarcachea los scopes del token en Base64 (clavescopes) para pintar el menú antes de que resuelvagetAccessTokenSilently().
8. Configuración y entornos
Ficheros src/environments/environment.ts (dev) y environment.prod.ts. Importante: angular.json no define fileReplacements, por lo que el build siempre compila environment.ts; en CI, Jenkins sobreescribe ambos ficheros con un fichero secreto (credencial infranete-interface-env), de modo que environment.prod.ts es en la práctica redundante.
| Clave | Descripción | Ejemplo (dev) |
|---|---|---|
production | Activa enableProdMode() en main.ts | false |
baseUrl | URL base de la API Infranete | http://localhost (prod: https://infranete-api.hawkersco.net) |
apiKeyGoogleMaps | API key de Google Maps (carga dinámica del script) | ******** |
auth0domain | Tenant de Auth0 | ******** |
auth0clientId | Client ID de la aplicación en Auth0 (distinto por entorno) | ******** |
auth0audience | Audience del API en Auth0 | https://infranete-api.hawkersco.net |
auth0allowedList | Patrones de URL a los que el interceptor añade el token | [`${baseUrl}/api/*`] |
scopes | Scopes solicitados en el token | openid profile email admin commons orders logistics coupons marketplaces feeds web reports |
Variables/credenciales necesarias en CI: la credencial de Jenkins infranete-interface-env (fichero de environment completo).
9. Estilos y UI
- AdminLTE 3 como base del layout (CSS + JS globales en
angular.json, junto con jQuery, moment y bootstrap-daterangepicker). - Angular Material con tema prebuilt
indigo-pink. - Font Awesome para iconografía.
- Overrides de marca Hawkers en
src/styles.css:btn-hawkers/btn-outline-hawkers(verde#74c344),badge-hawkers,card-hawkers,bg-hawkers,txt-error-hawkers. - Convenciones: tablas con
table table-bordered table-hover; modales conMatDialog; notificaciones siempre víaAlertService(SweetAlert2 con botón negro corporativo). - Estilos de componente inline (
inlineStyle: trueen schematics), con presupuesto máximo de 8 kB por componente.
10. Ejecución en local
Requisitos: Node 22 (imagen de build node:22-alpine) y npm 11.
npm install
npm start # ng serve → http://localhost:4200
- El entorno dev apunta a
baseUrl: http://localhost, por lo que se necesita la API Infranete (o un gateway) corriendo en local en el puerto 80. - Al abrir la app se redirige al login de Auth0 (tenant de desarrollo); tras autenticarse, el menú lateral muestra solo las secciones permitidas por los scopes del usuario.
- Verificación: la home carga bajo
/homey las llamadas a/api/*devuelven 200 con el bearer token adjunto (visible en la pestaña Network).
Otros comandos: ng build (build de producción por defecto), ng watch (build en modo desarrollo con watch), ng test (Vitest).
11. Despliegue
Pipeline declarativo de Jenkins (Jenkinsfile) con despliegue a GKE:
- Checkout del repositorio con workspace limpio.
- Environments: copia la credencial de fichero
infranete-interface-envsobresrc/environments/environment.tsyenvironment.prod.ts. - Build & Push:
gcloud builds submitconstruye la imagen Docker y la sube a Artifact Registry (europe-west3-docker.pkg.dev/pi-saldum/pi-repo/infranete-interface:BUILD_NUMBER). - Deploy: elimina el deployment anterior en el namespace
pidel clústerpi-cluster-hw(zonaeurope-west3-a), aplicak8s/deployment.yamlsustituyendo el taglatestpor el del build y espera al rollout.
La imagen Docker es multi-stage: build con node:22-alpine (npm run build) y runtime con nginx:1.27-alpine, que sirve dist/infranete-interface/browser con try_files $uri $uri/ /index.html para el enrutado client-side (puerto 80).
El deployment de Kubernetes usa 1 réplica, estrategia Recreate, y límites de 256 Mi de memoria / 250m de CPU.
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/infranete-interface/
12. Manejo de errores y logging
provideBrowserGlobalErrorListeners()enapp.config.tsregistra listeners globales de errores del navegador (mecanismo estándar de Angular 21). No hayErrorHandlerpersonalizado.- No hay interceptor propio de manejo de errores HTTP: cada componente gestiona el error en su suscripción y notifica al usuario mediante
AlertService.alert(mensaje, 'error', …), que además puede redirigir a home o cerrar sesión en Auth0. - Logging solo en consola (
console.erroren el bootstrap demain.ts). No hay integración con Sentry ni servicios externos de logging.
13. Notas y consideraciones
environment.prod.tssin efecto en el build: no hayfileReplacementsenangular.json; el build siempre usaenvironment.ts. Funciona porque Jenkins copia el mismo fichero secreto a ambos, pero un build de producción local usaría la configuración de desarrollo. Deuda técnica a valorar.- Restricción de Dr. Stock: el enlace del menú solo se muestra con scope
admin, pero la ruta/dr-stockestá protegida únicamente porAuthGuard(autenticación); la autorización fina se delega en la API. - Tipado laxo en servicios: los servicios devuelven
Observable<any>de forma generalizada, pese a existir modelos tipados ensrc/app/models. - Dependencias legacy globales: jQuery, moment y AdminLTE se cargan como scripts globales fuera del árbol de Angular, herencia del layout AdminLTE 3.
- Scopes en
localStorage: el sidebar cachea los scopes decodificados del JWT en Base64 para evitar parpadeo del menú; se sobreescriben al resolvergetAccessTokenSilently(). - Timeouts largos: operaciones pesadas (actualización Zalando, generación de site-list SEO) usan
timeout(300000)(5 min) sobre la petición HTTP. overrides.summernote: 0.9.1enpackage.jsonfija la versión de summernote (dependencia transitiva de AdminLTE) — probablemente por una vulnerabilidad o incompatibilidad. Pendiente de verificar el motivo.- No se han encontrado comentarios
TODO/FIXMEen el código fuente. - El proyecto no define
descriptionni versión significativa enpackage.json.