Volver a proyectos N.º 001 · Web app

Cladum

Construyo apps en React con Redux Toolkit sobre datos científicos reales, y hago visible de dónde sale cada dato para que nada se dé por cierto sin poder verificarlo.

Portada de Cladum: buscador de especies con cifras en vivo de GBIF, ejemplares destacados, reinos del árbol de la vida y clave de procedencia
Año
2026
Tipo
Web app · SPA
Rol
Diseño y desarrollo front-end
Stack
React · Redux Toolkit · Axios · Sass · Jest

Reto, solución y resultado

El reto

Datos científicos que no se pueden inventar

GBIF reúne millones de registros de especies, pero no siempre trae nombre común, foto o ubicación exacta. La interfaz no podía rellenar esos huecos con suposiciones.

La solución

Procedencia visible en cada dato

Cada valor se marca como Verificado · GBIF, Fuente externa o Sin dato, con un enlace a la consulta exacta de la API.

El resultado

Un explorador probado y publicado

Busca especies, recorre su clasificación de reino a especie y consulta sus ocurrencias. 105 pruebas y despliegue continuo que no publica si alguna falla.

Lo destacado

Qué hace y cómo lo resuelve

01

Ficha de especie con índice taxonómico

La ruta Reino → Especie con el identificador GBIF de cada rango, nomenclatura, nombre común en español y una galería con autoría y licencia. Si GBIF no tiene fotos, se buscan en iNaturalist solo con coincidencia exacta y licencia abierta.

Ficha de Panthera onca en Cladum con el índice taxonómico, la galería de fotografías y la tabla de nomenclatura
02

Red robusta

Cliente Axios con tiempo máximo, caché y errores tipados. Reintento automático ante el límite de consultas (429) y cancelación al salir de una vista.

03

Accesible con teclado

Buscador como combobox ARIA, foco visible, región viva que anuncia los cambios de estado y modo claro y oscuro con contraste WCAG 2.2 AA.

Una decisión técnica

Un bloque que falla no tumba la ficha

Una ficha junta cuatro consultas: el taxón, su nombre común, sus fotos y sus ocurrencias. Si solo falla la de fotos, el resto debe verse igual.

Sin taxón no hay ficha, así que esa consulta va primero. Las demás corren en paralelo con Promise.allSettled y cada bloque explica su propio «sin dato». La señal de cancelación viaja hasta Axios.

features/species/speciesSlice.js · simplificado
export const fetchSpecies = apiThunk('species/fetch',
  async (key, { signal }) => {
    // Sin taxón no hay ficha: este error sí es de pantalla completa
    const taxon = await getSpecies(key, { signal });

    // El resto falla por separado
    const [vernacular, photos, occ] = await Promise.allSettled([
      getVernacularNames(key, { signal }),
      getTaxonPhotos(key, taxon.canonicalName, { signal }),
      searchOccurrences({ taxonKey: key, limit: 3 }, { signal }),
    ]);

    return {
      taxon,
      photos: settled(photos) ?? [],
      photosFailed: photos.status === 'rejected',
      // …
    };
  });
Siguiente proyecto ALCOBA