From 40307f0c2711dc157c8722da3173fc68b78f44f9 Mon Sep 17 00:00:00 2001 From: josemi1189 Date: Fri, 27 Feb 2026 16:11:04 +0100 Subject: [PATCH 1/4] add page and link to river basin list --- front/src/app/embalse-cuenca/page.tsx | 5 +++++ front/src/layouts/footer.component.tsx | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 front/src/app/embalse-cuenca/page.tsx diff --git a/front/src/app/embalse-cuenca/page.tsx b/front/src/app/embalse-cuenca/page.tsx new file mode 100644 index 0000000..57dddaf --- /dev/null +++ b/front/src/app/embalse-cuenca/page.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export default function EmbalsesCuencasPage() { + return
EmbalsesCuencasPage
; +} diff --git a/front/src/layouts/footer.component.tsx b/front/src/layouts/footer.component.tsx index 5783e29..9a4312c 100644 --- a/front/src/layouts/footer.component.tsx +++ b/front/src/layouts/footer.component.tsx @@ -14,6 +14,12 @@ export const FooterComponent: FC = () => { > Embalses por provincias + + Embalses por cuencas + Date: Mon, 2 Mar 2026 19:14:57 +0100 Subject: [PATCH 2/4] Feat: add river basin list page and components --- front/src/app/embalse-cuenca/page.tsx | 8 +++-- front/src/common/models/cuencas.model.ts | 24 +++++++++++++ front/src/common/models/index.ts | 1 + front/src/core/constants/cuencas.constants.ts | 36 +++++++++++++++++++ .../embalse-cuenca-list.component.tsx | 29 +++++++++++++++ .../embalse-cuenca-list.pod.tsx | 6 ++++ .../embalse-cuenca-list.repository.ts | 26 ++++++++++++++ .../embalse-cuenca.mapper.ts | 11 ++++++ front/src/pods/embalse-cuenca-list/index.ts | 1 + 9 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 front/src/common/models/cuencas.model.ts create mode 100644 front/src/core/constants/cuencas.constants.ts create mode 100644 front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx create mode 100644 front/src/pods/embalse-cuenca-list/embalse-cuenca-list.pod.tsx create mode 100644 front/src/pods/embalse-cuenca-list/embalse-cuenca-list.repository.ts create mode 100644 front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts create mode 100644 front/src/pods/embalse-cuenca-list/index.ts diff --git a/front/src/app/embalse-cuenca/page.tsx b/front/src/app/embalse-cuenca/page.tsx index 57dddaf..92728e6 100644 --- a/front/src/app/embalse-cuenca/page.tsx +++ b/front/src/app/embalse-cuenca/page.tsx @@ -1,5 +1,9 @@ -import React from "react"; +import { EmbalseCuencaListPod } from "@/pods/embalse-cuenca-list"; +import { Metadata } from "next"; +export const metadata: Metadata = { + title: "Embalses por cuencas", +}; export default function EmbalsesCuencasPage() { - return
EmbalsesCuencasPage
; + return ; } diff --git a/front/src/common/models/cuencas.model.ts b/front/src/common/models/cuencas.model.ts new file mode 100644 index 0000000..1466836 --- /dev/null +++ b/front/src/common/models/cuencas.model.ts @@ -0,0 +1,24 @@ +import { Lookup } from "./lookup.model"; + +export interface CuencasModel { + id: string; + name: string; +} + +export interface CuencasModelApi { + _id: string; + nombre: string; +} + +export interface EmbalsesCuencaListApi { + embalse_id: string; + nombre: string; + slug: string; + cuenca: CuencasModelApi; +} + +export interface EmbalsesCuencaList { + id: string; + name: string; + slug?: string; +} diff --git a/front/src/common/models/index.ts b/front/src/common/models/index.ts index a13e258..4baf92a 100644 --- a/front/src/common/models/index.ts +++ b/front/src/common/models/index.ts @@ -1 +1,2 @@ export * from "./lookup.model"; +export * from "./cuencas.model"; diff --git a/front/src/core/constants/cuencas.constants.ts b/front/src/core/constants/cuencas.constants.ts new file mode 100644 index 0000000..8f58b15 --- /dev/null +++ b/front/src/core/constants/cuencas.constants.ts @@ -0,0 +1,36 @@ +export const cuencas = { + SEGURA: { nombre: "Segura", slug: "segura" }, + MINO_SIL: { nombre: "Miño - Sil", slug: "mino-sil" }, + EBRO: { nombre: "Ebro", slug: "ebro" }, + GUADALQUIVIR: { nombre: "Guadalquivir", slug: "guadalquivir" }, + JUCAR: { nombre: "Júcar", slug: "jucar" }, + DUERO: { nombre: "Duero", slug: "duero" }, + CANTABRICO_ORIENTAL: { + nombre: "Cantábrico Oriental", + slug: "cantabrico-oriental", + }, + CUENCA_MEDITERRANEA_ANDALUZA: { + nombre: "Cuenca Mediterránea Andaluza", + slug: "cuenca-mediterranea-andaluza", + }, + GUADIANA: { nombre: "Guadiana", slug: "guadiana" }, + CANTABRICO_OCCIDENTAL: { + nombre: "Cantábrico Occidental", + slug: "cantabrico-occidental", + }, + TINTO_ODIEL_Y_PIEDRAS: { + nombre: "Tinto, Odiel y Piedras", + slug: "tinto-odiel-y-piedras", + }, + TAJO: { nombre: "Tajo", slug: "tajo" }, + GALICIA_COSTA: { nombre: "Galicia Costa", slug: "galicia-costa" }, + GUADALETE_BARBATE: { nombre: "Guadalete-Barbate", slug: "guadalete-barbate" }, + CUENCAS_INTERNAS_DEL_PAIS_VASCO: { + nombre: "Cuencas Internas del País Vasco", + slug: "cuencas-internas-del-pais-vasco", + }, + CUENCAS_INTERNAS_DE_CATALUNA: { + nombre: "Cuencas Internas de Cataluña", + slug: "cuencas-internas-de-cataluna", + }, +} as const; diff --git a/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx new file mode 100644 index 0000000..fed72d9 --- /dev/null +++ b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx @@ -0,0 +1,29 @@ +import { Card } from "@/common/components/card.component"; +import { mapRiverBasinListFromApiToView } from "./embalse-cuenca.mapper"; +import { getRiverBasins } from "./embalse-cuenca-list.repository"; +import Link from "next/link"; +import { generateSlug } from "db-model"; + +export const EmbalsesCuencaList: React.FC = async () => { + const cuencasAPI = await getRiverBasins(); + const cuencasList = mapRiverBasinListFromApiToView(cuencasAPI); + + return ( + +
+

Embalses por cuencas

+
+ {cuencasList.map(({ id, name }) => ( + + {name} + + ))} +
+
+
+ ); +}; diff --git a/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.pod.tsx b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.pod.tsx new file mode 100644 index 0000000..cd2c8c7 --- /dev/null +++ b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.pod.tsx @@ -0,0 +1,6 @@ +import React from "react"; +import { EmbalsesCuencaList } from "./embalse-cuenca-list.component"; + +export const EmbalseCuencaListPod: React.FC = () => { + return ; +}; diff --git a/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.repository.ts b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.repository.ts new file mode 100644 index 0000000..527ab6e --- /dev/null +++ b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.repository.ts @@ -0,0 +1,26 @@ +"use server"; + +import { getDb } from "@/lib/mongodb"; +import type { CuencasModelApi } from "@/common/models/cuencas.model"; + +export async function getRiverBasins(): Promise { + try { + const db = await getDb(); + const docs = await db + .collection("cuencas") + .find({}, { projection: { _id: 1, nombre: 1 } }) + .toArray(); + + return docs.map((doc) => ({ + _id: doc.slug ?? String(doc._id), + nombre: doc.nombre ?? "", + })); + } catch (error) { + console.warn( + "getEmbalsesByRiverBasin: MongoDB not available (build time?), returning empty array.", + "Error:", + error instanceof Error ? error.message : error, + ); + } + return []; +} diff --git a/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts b/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts new file mode 100644 index 0000000..c6aec44 --- /dev/null +++ b/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts @@ -0,0 +1,11 @@ +import { CuencasModel, CuencasModelApi } from "@/common/models/cuencas.model"; + +export const mapRiverBasinListFromApiToView = ( + cuencas: CuencasModelApi[], +): CuencasModel[] => + Array.isArray(cuencas) + ? cuencas.map((cuenca) => ({ + id: cuenca._id, + name: cuenca.nombre, + })) + : []; diff --git a/front/src/pods/embalse-cuenca-list/index.ts b/front/src/pods/embalse-cuenca-list/index.ts new file mode 100644 index 0000000..7ee1112 --- /dev/null +++ b/front/src/pods/embalse-cuenca-list/index.ts @@ -0,0 +1 @@ +export * from "./embalse-cuenca-list.pod"; From a2b54a3974c552a2f0db24df3035a9de4656d677 Mon Sep 17 00:00:00 2001 From: josemi1189 Date: Mon, 2 Mar 2026 19:19:07 +0100 Subject: [PATCH 3/4] Feat: Add reservoirs list by river basin --- .../src/app/embalse-cuenca/[cuenca]/page.tsx | 46 +++++++++++++++++++ .../embalse-cuenca.component.tsx | 42 +++++++++++++++++ .../embalse-cuenca/embalse-cuenca.mapper.ts | 14 ++++++ .../embalse-cuenca/embalse-cuenca.pod.tsx | 19 ++++++++ .../embalse-cuenca.repository.ts | 28 +++++++++++ front/src/pods/embalse-cuenca/index.ts | 2 + 6 files changed, 151 insertions(+) create mode 100644 front/src/app/embalse-cuenca/[cuenca]/page.tsx create mode 100644 front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx create mode 100644 front/src/pods/embalse-cuenca/embalse-cuenca.mapper.ts create mode 100644 front/src/pods/embalse-cuenca/embalse-cuenca.pod.tsx create mode 100644 front/src/pods/embalse-cuenca/embalse-cuenca.repository.ts create mode 100644 front/src/pods/embalse-cuenca/index.ts diff --git a/front/src/app/embalse-cuenca/[cuenca]/page.tsx b/front/src/app/embalse-cuenca/[cuenca]/page.tsx new file mode 100644 index 0000000..1f1ebae --- /dev/null +++ b/front/src/app/embalse-cuenca/[cuenca]/page.tsx @@ -0,0 +1,46 @@ +import { EmbalsesCuencaPod, getEmbalsesPorCuenca } from "@/pods/embalse-cuenca"; +import { cuencas } from "@/core/constants/cuencas.constants"; +import { Metadata } from "next"; +import { mapRiverBasinListFromApiToView } from "@/pods/embalse-cuenca-list/embalse-cuenca.mapper"; + +interface Props { + params: Promise<{ cuenca: string }>; +} + +const getCuencaBySlug = (slug: string) => { + return Object.values(cuencas).find((cuenca) => cuenca.slug === slug); +}; + +export async function generateMetadata({ params }: Props): Promise { + const { cuenca } = await params; + const datosCuenca = getCuencaBySlug(cuenca.toString()); + + return { + title: `Embalses de ${datosCuenca.nombre}`, + }; +} + +export default async function EmbalseCuencaListadoPage({ + params, +}: Props): Promise { + const { cuenca } = await params; + const datosCuenca = getCuencaBySlug(cuenca.toString()); + const embalsesByRiverBasinFromApi = await getEmbalsesPorCuenca( + datosCuenca.nombre, + ); + const embalsesByRiverBasinLookup = mapRiverBasinListFromApiToView( + embalsesByRiverBasinFromApi, + ); + + if (!cuenca) { + return
Cuenca no encontrada
; + } + + return ( + + ); +} diff --git a/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx b/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx new file mode 100644 index 0000000..1f2f1b6 --- /dev/null +++ b/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx @@ -0,0 +1,42 @@ +"use client"; +import { Card } from "@/common/components/card.component"; +import { CuencasModel } from "@/common/models"; +import { generateSlug } from "db-model"; +import Link from "next/link"; + +export interface Props { + nombreCuenca: string; + slug: string; + embalses: CuencasModel[]; +} + +export const EmbalseCuencaComponent: React.FC = (props) => { + const { nombreCuenca, slug, embalses } = props; + return ( + +
+ {embalses.length === 0 ? ( +

No se encontraron embalses para {nombreCuenca}

+ ) : ( +

Embalses de {nombreCuenca}

+ )} +
+ {embalses.map(({ id, name }) => ( + + {name} + + ))} +
+ {`Mapa +
+
+ ); +}; diff --git a/front/src/pods/embalse-cuenca/embalse-cuenca.mapper.ts b/front/src/pods/embalse-cuenca/embalse-cuenca.mapper.ts new file mode 100644 index 0000000..79f52c9 --- /dev/null +++ b/front/src/pods/embalse-cuenca/embalse-cuenca.mapper.ts @@ -0,0 +1,14 @@ +import { + EmbalsesCuencaList, + EmbalsesCuencaListApi, +} from "@/common/models/cuencas.model"; + +export const mapRiverBasinListFromApiToView = ( + embalses: EmbalsesCuencaListApi[], +): EmbalsesCuencaList[] => + Array.isArray(embalses) + ? embalses.map((embalse) => ({ + id: embalse.embalse_id, + name: embalse.nombre, + })) + : []; diff --git a/front/src/pods/embalse-cuenca/embalse-cuenca.pod.tsx b/front/src/pods/embalse-cuenca/embalse-cuenca.pod.tsx new file mode 100644 index 0000000..758932f --- /dev/null +++ b/front/src/pods/embalse-cuenca/embalse-cuenca.pod.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { EmbalseCuencaComponent } from "./embalse-cuenca.component"; +import { CuencasModel } from "@/common/models"; + +export interface Props { + nombreCuenca: string; + slug: string; + embalses: CuencasModel[]; +} +export const EmbalsesCuencaPod: React.FC = (props) => { + const { nombreCuenca, slug, embalses } = props; + return ( + + ); +}; diff --git a/front/src/pods/embalse-cuenca/embalse-cuenca.repository.ts b/front/src/pods/embalse-cuenca/embalse-cuenca.repository.ts new file mode 100644 index 0000000..2e98146 --- /dev/null +++ b/front/src/pods/embalse-cuenca/embalse-cuenca.repository.ts @@ -0,0 +1,28 @@ +"use server"; + +import { getDb } from "@/lib/mongodb"; +import { CuencasModelApi } from "@/common/models/cuencas.model"; + +export async function getEmbalsesPorCuenca( + nombre: string, +): Promise { + try { + const db = await getDb(); + const docs = await db + .collection("embalses") + .find({ "cuenca.nombre": nombre }, { projection: { _id: 1, nombre: 1 } }) + .toArray(); + + return docs.map((doc) => ({ + _id: doc.slug ?? String(doc._id), + nombre: doc.nombre ?? "", + })); + } catch (error) { + console.warn( + "getEmbalsesByRiverBasin: MongoDB not available (build time?), returning empty array.", + "Error:", + error instanceof Error ? error.message : error, + ); + } + return []; +} diff --git a/front/src/pods/embalse-cuenca/index.ts b/front/src/pods/embalse-cuenca/index.ts new file mode 100644 index 0000000..25806f0 --- /dev/null +++ b/front/src/pods/embalse-cuenca/index.ts @@ -0,0 +1,2 @@ +export * from "./embalse-cuenca.pod"; +export * from "./embalse-cuenca.repository"; From 2b3248459c5a2c10f5aab7a9bf7acdca5cc19e39 Mon Sep 17 00:00:00 2001 From: josemi1189 Date: Mon, 2 Mar 2026 20:23:39 +0100 Subject: [PATCH 4/4] Refactor: rename function names to spanish --- .../src/app/embalse-cuenca/[cuenca]/page.tsx | 22 +++++++++++-------- .../embalse-cuenca-list.component.tsx | 4 ++-- .../embalse-cuenca.mapper.ts | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/front/src/app/embalse-cuenca/[cuenca]/page.tsx b/front/src/app/embalse-cuenca/[cuenca]/page.tsx index 1f1ebae..49690cc 100644 --- a/front/src/app/embalse-cuenca/[cuenca]/page.tsx +++ b/front/src/app/embalse-cuenca/[cuenca]/page.tsx @@ -1,7 +1,8 @@ import { EmbalsesCuencaPod, getEmbalsesPorCuenca } from "@/pods/embalse-cuenca"; import { cuencas } from "@/core/constants/cuencas.constants"; import { Metadata } from "next"; -import { mapRiverBasinListFromApiToView } from "@/pods/embalse-cuenca-list/embalse-cuenca.mapper"; +import { mapListaCuencasDesdeApiParaVista } from "@/pods/embalse-cuenca-list/embalse-cuenca.mapper"; +import { notFound } from "next/navigation"; interface Props { params: Promise<{ cuenca: string }>; @@ -15,6 +16,10 @@ export async function generateMetadata({ params }: Props): Promise { const { cuenca } = await params; const datosCuenca = getCuencaBySlug(cuenca.toString()); + if (!datosCuenca) { + return {}; + } + return { title: `Embalses de ${datosCuenca.nombre}`, }; @@ -25,22 +30,21 @@ export default async function EmbalseCuencaListadoPage({ }: Props): Promise { const { cuenca } = await params; const datosCuenca = getCuencaBySlug(cuenca.toString()); - const embalsesByRiverBasinFromApi = await getEmbalsesPorCuenca( + if (!datosCuenca) { + notFound(); + } + const embalsesPorCuencaDesdeApi = await getEmbalsesPorCuenca( datosCuenca.nombre, ); - const embalsesByRiverBasinLookup = mapRiverBasinListFromApiToView( - embalsesByRiverBasinFromApi, + const embalsesPorCuenca = mapListaCuencasDesdeApiParaVista( + embalsesPorCuencaDesdeApi, ); - if (!cuenca) { - return
Cuenca no encontrada
; - } - return ( ); } diff --git a/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx index fed72d9..3cbd08c 100644 --- a/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx +++ b/front/src/pods/embalse-cuenca-list/embalse-cuenca-list.component.tsx @@ -1,12 +1,12 @@ import { Card } from "@/common/components/card.component"; -import { mapRiverBasinListFromApiToView } from "./embalse-cuenca.mapper"; +import { mapListaCuencasDesdeApiParaVista } from "./embalse-cuenca.mapper"; import { getRiverBasins } from "./embalse-cuenca-list.repository"; import Link from "next/link"; import { generateSlug } from "db-model"; export const EmbalsesCuencaList: React.FC = async () => { const cuencasAPI = await getRiverBasins(); - const cuencasList = mapRiverBasinListFromApiToView(cuencasAPI); + const cuencasList = mapListaCuencasDesdeApiParaVista(cuencasAPI); return ( diff --git a/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts b/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts index c6aec44..787a4c6 100644 --- a/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts +++ b/front/src/pods/embalse-cuenca-list/embalse-cuenca.mapper.ts @@ -1,6 +1,6 @@ import { CuencasModel, CuencasModelApi } from "@/common/models/cuencas.model"; -export const mapRiverBasinListFromApiToView = ( +export const mapListaCuencasDesdeApiParaVista = ( cuencas: CuencasModelApi[], ): CuencasModel[] => Array.isArray(cuencas)