Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/elzackarias/Hackaton3B-Reto1/llms.txt

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

The dashboard has four pages, each a standalone React component living in src/pages/. All four are mounted inside the shared Layout shell (sidebar + main content area) and every one of them reads exclusively from the SocketContext provided by useSocket — there are no local fetches or independent state managers. Data flows in one direction: backend → Socket.IO → useSocket state → useSocketContext() → page render.

Live Panel

Route: /
Component: src/pages/LivePanel.tsx
Socket data consumed: products, videoFrame, narratives, events, predictions, cameras, connected, usingMock
LivePanel is the default landing view of the dashboard. It combines a live video grid with an inventory bar chart and a real-time narrative feed so operators can see shelf status at a glance.

Layout

The page is split into two horizontal bands:
  • Top band (white background): page heading, connection badge, camera-filter pill toggles, dismissible alert banner, and a KPI bar showing total products, aggregate availability gauge, active alert count, and cameras online.
  • Bottom band (light grey background): a 12-column grid containing the camera feed area (8 cols), inventory bar chart (4 cols), latest events list (5 cols), live narrative feed (4 cols), and an “at-risk products” column (3 cols).

KPI Bar

Four summary metrics are computed directly from socket state:
KPIDerivation
Productosproducts.length
Disponibilidad(totalStock / maxStock) * 100 rendered as a semi-circle tick gauge
Alertas Activasproducts.filter(p => p.alert_active).length
Cámaras Onlinecameras.filter(c => c.status === "online").length

Alert Banner

The first undismissed critical or warning narrative message is shown as a left-bordered banner at the top of the page. Clicking the banner navigates to /activity; the × button dismisses it locally without affecting socket state.

Video Feed Area

Camera feeds are rendered with the VideoFeed component. When the “Todas” (all) camera filter is selected, each camera gets its own VideoFeed card in a two-column grid. Selecting a specific camera renders a single large feed (large prop):
{selectedCam === "all" ? (
  <div className="grid grid-cols-2 gap-grid-gap">
    {cameras.map(cam => (
      <VideoFeed
        key={cam.source_id}
        frame={videoFrame}
        label={cam.label}
        location={cam.location}
      />
    ))}
  </div>
) : (
  <VideoFeed
    frame={videoFrame}
    label={cameras.find(c => c.source_id === selectedCam)!.label}
    location={cameras.find(c => c.source_id === selectedCam)!.location}
    large
  />
)}

Inventory Bar Chart

Stock levels are visualised as a horizontal Recharts BarChart (layout "vertical"). Each bar is coloured using the same traffic-light threshold used throughout the app:
Fill rateColor
> 50 %#22C55E (green)
25 – 50 %#F59E0B (amber)
≤ 25 %#EF4444 (red)

Products at Risk Panel

Predictions with minutes_remaining < 150 are surfaced in a compact side column. Items with minutes_remaining < 60 receive a red border-l-red-500 border; items between 60 and 150 minutes receive amber. Each card shows current stock, estimated minutes to depletion, and trend direction (🔺 Acelerando / ➡️ Estable / 🔻 Desacelerando).

Natural-Language Summary

Below the KPI bar, LivePanel renders a single prose sentence derived from live socket state — listing critical SKUs, at-risk predictions, and low-stock items — so operators can read overall shelf health without interpreting charts.

VideoFeed Component

The VideoFeed component (src/components/VideoFeed.tsx) handles all camera stream rendering. It accepts a frame prop — the raw base64 string from the video_frame Socket.IO event — and constructs the full data URI before setting it as the src of an <img> element:
const frameSrc = frame
  ? frame.startsWith("data:") ? frame : `data:image/jpeg;base64,${frame}`
  : null;

// ...

{hasSignal && frameSrc ? (
  <img
    src={frameSrc}
    alt={`Feed de ${label}`}
    className="w-full h-full object-contain"
  />
) : (
  <div className="...">
    {/* No-signal placeholder */}
  </div>
)}
A 5-second inactivity timer (NO_SIGNAL_TIMEOUT_MS = 5_000) flips hasSignal back to false if no new frame arrives — showing a “Sin señal” placeholder and a grey indicator dot instead of a stale frozen image. The component also accepts label, location, large (controls minimum card height: 420px vs. 260px), and a children slot for extra camera controls. When the user is on any page other than LivePanel (/), the Layout component renders a VideoFeedPiP picture-in-picture overlay so the stream remains visible while browsing other views.
During frontend development without a running backend, the videoFrame value from useSocketContext() will always be null because mock data in src/mocks/mockData.ts does not include a video stream. The VideoFeed component handles this gracefully by showing the “Sin señal — verificar conexión con el backend” placeholder. All other socket state (products, predictions, narratives, events, heatmap) is fully mocked and the UI renders completely.

Build docs developers (and LLMs) love