Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/JoseOlivares19/Proyecto-PC3-JavaScript-Avanzado/llms.txt

Use this file to discover all available pages before exploring further.

SmartStock360’s frontend follows a deliberately lean single-file architecture: the entire UI lives inside src/App.tsx. This makes the codebase easy to read end-to-end, with no prop-drilling across multiple files and no additional component library beyond Bootstrap 5 (loaded via CDN in index.html) and Bootstrap Icons. The three logical UI sections — the product card, the prediction results panel, and the inventory table — are rendered as sibling columns and card elements within a single Bootstrap container.

Single-Component Architecture

All state, event handlers, data-fetching logic, and JSX markup are co-located in the App function component exported from src/App.tsx. The component:
  1. Declares two pieces of React state
  2. Runs a side-effect on mount to pre-load the product list
  3. Exposes a click handler that dispatches the prediction request
  4. Returns a single JSX fragment containing all three UI sections
No child components, no React context, no routing — everything renders in one pass.

React State

The component uses two useState hooks:
const [productos, setProductos] = useState<any[]>([]);
const [resultado, setResultado] = useState<any>(null);
State variableTypePurpose
productosany[]Holds the array of product objects fetched from GET /api/productos
resultadoanyHolds the AI prediction response returned from POST /api/productos/predict; starts as null
The useEffect hook calls cargarProductos() immediately on component mount with an empty dependency array ([]), so the inventory table is populated as soon as the page loads — no user interaction required.
useEffect(() => {
  cargarProductos();
}, []);

The formulario Demo Payload

Because this is a demo application, the prediction input is a hardcoded object defined directly inside the component body:
const formulario = {
  precio: 129.90,
  stock_actual: 80,
  ventas_7d: 420,
  descuento_pct: 20,
  temporada: 2,
  dias_sin_reabastecer: 18,
  rating_producto: 4.6
};
This object is sent verbatim to the backend when the user clicks “Ejecutar Predicción IA”. The values represent a specific Zapatillas Deportivas product (SKU: PROD-004) with high recent sales and a holiday/high-demand season flag (temporada: 2).

UI Section 1 — Product Card (col-lg-5)

The left column renders a saas-card that displays all formulario fields as labelled parameter badges, plus the action button:
Field displayedValue source
Product nameHardcoded — “Zapatillas Deportivas”
SKUHardcoded — “PROD-004”
Priceformulario.precio
Stock disponibleformulario.stock_actual
Ventas últimos 7 díasformulario.ventas_7d
Descuento activoformulario.descuento_pct
Temporadaformulario.temporada (displayed as “Nivel 2”)
The “Ejecutar Predicción IA” button calls manejarPrediccion() on click:
const manejarPrediccion = async () => {
  try {
    const data = await predecirDemanda(formulario);
    setResultado(data);
  } catch (error) {
    console.error("Error al predecir", error);
  }
};

UI Section 2 — Prediction Results Panel (col-lg-7)

The right column renders conditionally based on whether resultado is null:
  • Before prediction (resultado === null): shows an idle placeholder with a CPU icon and the instruction to click the button.
  • After prediction (resultado is set): renders the full results card.
The results card displays three key pieces of data from the API response:
Response fieldRendered as
resultado.prediccionRaw model label in a <code> block (e.g., DEMANDA_ALTA_REABASTECER)
obtenerDecision(resultado.prediccion)Human-readable strategic decision in a colour-coded banner
resultado.recomendacionesA mapped list of recommendation strings, one card per item
The banner colour is driven by whether the prediction label contains the substring 'ALTA':
  • Contains 'ALTA' → red banner (urgent restock)
  • Does not contain 'ALTA' → green banner (monitor or reduce)

The obtenerDecision() Helper

This utility function translates raw ML class labels into business-friendly action strings:
const obtenerDecision = (prediccion: string) => {
  if (prediccion === "DEMANDA_ALTA_REABASTECER") return "Reabastecer urgentemente";
  if (prediccion === "DEMANDA_MEDIA_MONITOREAR") return "Monitorear stock";
  return "Reducir compra";
};
AI labelHuman-readable decision
DEMANDA_ALTA_REABASTECERReabastecer urgentemente
DEMANDA_MEDIA_MONITOREARMonitorear stock
Any other valueReducir compra

UI Section 3 — Inventory Table

The bottom section renders a full-width saas-card containing a responsive Bootstrap table. It iterates over the productos state array:
{productos.length > 0 ? (
  productos.map(p => (
    <tr key={p.id}>
      <td className="fw-semibold text-muted">#{p.id}</td>
      <td className="fw-bold text-dark">{p.nombre}</td>
      <td className="fw-semibold text-primary">${p.precio}</td>
      <td>
        <span className={`badge ${p.stockActual < 50
          ? 'bg-danger bg-opacity-10 text-danger'
          : 'bg-success bg-opacity-10 text-success'
        } px-2 py-1 rounded-pill`}>
          {p.stockActual} uds
        </span>
      </td>
    </tr>
  ))
) : (
  <tr>
    <td colSpan={4} className="text-center py-4">
      No se encontraron registros en MySQL.
    </td>
  </tr>
)}
The table renders four columns sourced directly from each product object:
ColumnProduct fieldNotes
IDp.idPrefixed with #
Productop.nombreProduct name
Precio Unitariop.precioPrefixed with $
Stock Actualp.stockActualBadge colour: red if < 50, green otherwise
The low-stock threshold of 50 units controls the badge colour: products with fewer than 50 units in stock get a red danger badge, all others get a green success badge.
If the backend is unreachable or returns an empty array, the table shows a friendly empty-state row reading “No se encontraron registros en MySQL.” The cargarProductos function logs any fetch errors to the browser console without surfacing them to the UI.

Build docs developers (and LLMs) love