Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/davidmpizarro/QuipuEco-Hackaton/llms.txt

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

useHistorial is the single source of truth for recycling activity in QuipuEco. It persists every AI classification result to localStorage, exposes a prepend-only history array ordered newest-first, and derives five computed statistics—including eco-rank points—on every render without requiring Redux, Zustand, or any other external state library. Any component that needs historical data or wants to write a new record simply calls this hook.

Import and usage

import { useHistorial } from "./hooks/useHistorial";

const { historial, agregarRegistro, limpiarHistorial, stats } = useHistorial();
Call the hook at the top level of the component that owns the shared state. In the QuipuEco architecture, this is App.jsx, which then passes derived values down to ImpactDashboard and records down to ClassificationResult.

Return value

historial
array
required
An array of classification records, ordered newest-first. Each entry is the merged result of a /clasificar API response and the two fields added by agregarRegistro (id and fecha). Initialized from localStorage on first render; falls back to [] if the key is missing or the JSON is malformed.
agregarRegistro
(resultado: ClassificationResult, imagen: string) => Record
required
Prepends a new record to historial and synchronously persists the updated array to localStorage.Parameters:
  • resultado — the full JSON object returned by the /clasificar endpoint
  • imagen — the blob: URL string produced by URL.createObjectURL() for the selected or captured image
Returns the newly created record object (useful for immediate use without reading back from state).
limpiarHistorial
() => void
required
Resets historial to an empty array by calling setHistorial([]). The persistence useEffect then fires and writes [] to localStorage under the key quipueco_historial. Used by ImpactDashboard when the user taps Limpiar Historial.
stats
object
required
A computed statistics object derived from the current historial array on every render. See the full field breakdown below.

Record structure

Every entry in historial—and the object returned by agregarRegistro—has the following shape. The id and fecha fields are injected by the hook; all other fields are spread directly from the /clasificar API response.
{
  "id": 1718000000000,
  "fecha": "2024-06-10T14:30:00.000Z",
  "imagen": "blob:http://localhost:5173/a1b2c3d4-...",
  "nombre": "Botella de plástico PET",
  "tipo": "plastico",
  "co2_evitado_kg": 0.18,
  "peso_estimado_kg": 0.03,
  "instrucciones": "Retira la etiqueta y enjuaga con agua fría.",
  "punto_acopio": "Tiendas Tambo",
  "consejo": "El plástico PET puede reciclarse hasta 10 veces.",
  "respuesta_voz": "He clasificado una botella de plástico PET."
}
FieldSourceNotes
idDate.now()Millisecond timestamp, used as React key
fechanew Date().toISOString()ISO 8601 string in UTC
imagenCaller-suppliedBlob URL — see note below
nombre/clasificar responseHuman-readable item name
tipo/clasificar responseMaterial category slug
co2_evitado_kg/clasificar responseMay be 0 or absent for non-recyclables
peso_estimado_kg/clasificar responseEstimated item weight
instrucciones/clasificar responseLocale-specific disposal instructions
punto_acopio/clasificar responseSuggested drop-off network name
consejo/clasificar responseEco tip shown on the result card
respuesta_voz/clasificar responseText read aloud by the browser TTS engine

Persistence

The hook uses a useEffect that fires whenever historial changes, writing the serialized array to localStorage under the key quipueco_historial. Initialization reads the same key inside the useState lazy initializer, so history is restored synchronously before the first render—no flash of empty state.
// Storage key used internally
const STORAGE_KEY = "quipueco_historial";

// Lazy initializer — runs once before first render
const [historial, setHistorial] = useState(() => {
  try {
    const guardado = localStorage.getItem(STORAGE_KEY);
    return guardado ? JSON.parse(guardado) : [];
  } catch {
    return [];
  }
});

// Persistence effect — runs after every state change
useEffect(() => {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(historial));
}, [historial]);

Example: eco-rank tier check

Use stats.puntos to determine which rank tier a user has reached. The ImpactDashboard component uses the same thresholds to render a progress bar.
const NIVELES = [
  { min: 0,   label: "Semilla Eco",        emoji: "🌱" },
  { min: 30,  label: "Árbol Joven",        emoji: "🌳" },
  { min: 100, label: "Bosque Verde",       emoji: "🌲" },
  { min: 250, label: "Guardián Eco",       emoji: "🦅" },
  { min: 500, label: "Héroe del Planeta",  emoji: "🏆" },
];

function getNivel(puntos) {
  return [...NIVELES].reverse().find((n) => puntos >= n.min) || NIVELES[0];
}

// Inside a component:
const { stats } = useHistorial();
const nivelActual = getNivel(stats.puntos);
// → { min: 100, label: "Bosque Verde", emoji: "🌲" }
To compute the progress percentage toward the next tier, find the next threshold above stats.puntos and use (currentPts - currentMin) / (nextMin - currentMin) * 100. This is exactly what ImpactDashboard does to fill the animated progress bar.
The imagen field in each record stores a blob: URL created with URL.createObjectURL() at capture time. Blob URLs are session-scoped—they are revoked when the browser tab closes. On the next page load, historial will be restored from localStorage with all metadata intact, but the imagen value will be a stale blob reference that no longer resolves to an image. The ImpactDashboard history list renders a broken thumbnail in this case. If persistent thumbnails are required, encode the image as a Base64 data URL before passing it to agregarRegistro.

Build docs developers (and LLMs) love