diff --git a/front/src/app/embalse/[embalse]/page.tsx b/front/src/app/embalse/[embalse]/page.tsx index 65b0349..7e7e9da 100644 --- a/front/src/app/embalse/[embalse]/page.tsx +++ b/front/src/app/embalse/[embalse]/page.tsx @@ -12,7 +12,7 @@ import { mapEmbalseToReservoirData, mapHistoricalReservoirToViewModel, } from "@/pods/embalse/embalse.mapper"; -import { formatEmbalseDisplayName } from "@/pods/embalse/embalse-name.helper"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; export const revalidate = 300; // ISR: regenerar cada 5 minutos diff --git a/front/src/pods/embalse/embalse-name.helper.spec.ts b/front/src/common/helpers/embalse-name.helper.spec.ts similarity index 77% rename from front/src/pods/embalse/embalse-name.helper.spec.ts rename to front/src/common/helpers/embalse-name.helper.spec.ts index 83c5112..7923483 100644 --- a/front/src/pods/embalse/embalse-name.helper.spec.ts +++ b/front/src/common/helpers/embalse-name.helper.spec.ts @@ -6,6 +6,9 @@ describe("formatEmbalseDisplayName", () => { expect(formatEmbalseDisplayName("Atazar, El")).toBe("El Atazar"); expect(formatEmbalseDisplayName("Pardo, El")).toBe("El Pardo"); expect(formatEmbalseDisplayName("Villar, El")).toBe("El Villar"); + expect(formatEmbalseDisplayName("Forcadas, As")).toBe("As Forcadas"); + expect(formatEmbalseDisplayName("Peares, Os")).toBe("Os Peares"); + expect(formatEmbalseDisplayName("Ribeira, A")).toBe("A Ribeira"); }); it("flips comma-inverted La", () => { @@ -30,4 +33,7 @@ describe("formatEmbalseDisplayName", () => { it("returns empty string for empty input", () => { expect(formatEmbalseDisplayName("")).toBe(""); }); + it("inverts article in parenthesized format", () => { + expect(formatEmbalseDisplayName("Loteta (La)")).toBe("La Loteta"); + }); }); diff --git a/front/src/pods/embalse/embalse-name.helper.ts b/front/src/common/helpers/embalse-name.helper.ts similarity index 80% rename from front/src/pods/embalse/embalse-name.helper.ts rename to front/src/common/helpers/embalse-name.helper.ts index 8e7abcb..473a9b9 100644 --- a/front/src/pods/embalse/embalse-name.helper.ts +++ b/front/src/common/helpers/embalse-name.helper.ts @@ -1,4 +1,5 @@ -const INVERTED_ARTICLE_PATTERN = /^(.+?),\s*(El|La|Los|Las)\b\s*(.*)$/; +const INVERTED_ARTICLE_PATTERN = + /^(.+?)(?:,\s*|\s*\()(El|La|Los|Las|Os|A|As)(?:\)|\b\s*)\s*(.*)$/; export const formatEmbalseDisplayName = (rawName: string): string => { if (!rawName) return ""; diff --git a/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx b/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx index 2e5879e..4cb4150 100644 --- a/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx +++ b/front/src/pods/embalse-cuenca/embalse-cuenca.component.tsx @@ -1,8 +1,8 @@ -"use client"; import { Card } from "@/common/components/card.component"; import { Lookup } from "@/common/models"; import { generateSlug } from "db-model"; import Link from "next/link"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; export interface Props { nombreCuenca: string; @@ -26,7 +26,7 @@ export const EmbalseCuencaComponent: React.FC = (props) => { href={`/embalse/${generateSlug(name)}`} className="link-accessible" > - {name} + {formatEmbalseDisplayName(name)} ))} diff --git a/front/src/pods/embalse-provincia/embalse-provincia.component.tsx b/front/src/pods/embalse-provincia/embalse-provincia.component.tsx index 6f65f1f..74ed792 100644 --- a/front/src/pods/embalse-provincia/embalse-provincia.component.tsx +++ b/front/src/pods/embalse-provincia/embalse-provincia.component.tsx @@ -2,6 +2,7 @@ import { Card } from "@/common/components/card.component"; import { Lookup } from "@/common/models"; import Link from "next/link"; import React from "react"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; interface Props { nombreProvincia: string; @@ -21,7 +22,7 @@ export const EmbalseProvincia: React.FC = (props) => {
{embalses.map(({ id, name }) => ( - {name} + {formatEmbalseDisplayName(name)} ))}
diff --git a/front/src/pods/embalse-search/components/filtered-list.tsx b/front/src/pods/embalse-search/components/filtered-list.tsx index 1c1a096..4256716 100644 --- a/front/src/pods/embalse-search/components/filtered-list.tsx +++ b/front/src/pods/embalse-search/components/filtered-list.tsx @@ -1,6 +1,7 @@ import React from "react"; import { useCombobox } from "downshift"; import { EmbalseSearchModel } from "../embalse-search.vm"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; interface Props { isOpen: boolean; @@ -35,7 +36,7 @@ export const FilteredList: React.FC = (props) => { highlightedIndex === index ? "bg-primary text-white" : "" }`} > - {item.name} + {formatEmbalseDisplayName(item.name)} ))} diff --git a/front/src/pods/embalse-search/components/recent-searches/recent-searches.component.tsx b/front/src/pods/embalse-search/components/recent-searches/recent-searches.component.tsx index b92229e..56490c3 100644 --- a/front/src/pods/embalse-search/components/recent-searches/recent-searches.component.tsx +++ b/front/src/pods/embalse-search/components/recent-searches/recent-searches.component.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { EmbalseSearchModel } from "../../embalse-search.vm"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; interface Props { searches: EmbalseSearchModel[]; @@ -14,7 +15,9 @@ export const RecentSearches: React.FC = (props) => {
    {searches.map((item) => (
  • - {item.name} + + {formatEmbalseDisplayName(item.name)} +
  • ))}
diff --git a/front/src/pods/embalse-search/embalse-search.business.ts b/front/src/pods/embalse-search/embalse-search.business.ts index 792e2cc..02e77cc 100644 --- a/front/src/pods/embalse-search/embalse-search.business.ts +++ b/front/src/pods/embalse-search/embalse-search.business.ts @@ -1,16 +1,19 @@ import { mapEmbalseToSearch } from "./embalse-search.mapper"; import { Embalse } from "./api"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; export const normalizeSearchString = (input: string): string => { - return input ? - input.toString() - .normalize("NFD") // Separa los acentos de las letras - .replace(/[\u0300-\u036f]/g, "") // Elimina acentos - .toLowerCase() - .replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, "") // Elimina signos de puntuación - .replace(/\s+/g, " ").trim() : ""; // Reemplaza múltiples espacios por uno + return input + ? input + .toString() + .normalize("NFD") // Separa los acentos de las letras + .replace(/[\u0300-\u036f]/g, "") // Elimina acentos + .toLowerCase() + .replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, "") // Elimina signos de puntuación + .replace(/\s+/g, " ") + .trim() + : ""; // Reemplaza múltiples espacios por uno }; -// encontre todos esos filtros por internet y los aplico si sobra alguno lo quitamos, también dejé las descripciones export const getFilteredEmbalses = ( inputValue: string, @@ -19,17 +22,16 @@ export const getFilteredEmbalses = ( if (!embalses || embalses.length === 0) { return []; } - - const lower = normalizeSearchString(inputValue); - const normalizedInputValue = normalizeSearchString(inputValue); + // Separa campo de búsqueda en diferentes palabras y busca que contenta las palabras juntas en base de datos, independiente del orden + const words = normalizeSearchString(inputValue).split(" "); return embalses - .filter( - (e) => - e.nombre.toLowerCase().includes(lower) || - (e.provincia ?? "").toLowerCase().includes(lower) || - normalizeSearchString(e.nombre ?? "").includes(normalizedInputValue) || - normalizeSearchString(e.provincia ?? "").includes(normalizedInputValue), - ) + .filter((e) => { + const nombre = normalizeSearchString(e.nombre ?? ""); + const provincia = normalizeSearchString(e.provincia ?? ""); + return words.every( + (word) => nombre.includes(word) || provincia.includes(word), + ); + }) .map(mapEmbalseToSearch); }; diff --git a/front/src/pods/embalse-search/embalse-search.tsx b/front/src/pods/embalse-search/embalse-search.tsx index c98cbbd..e19e033 100644 --- a/front/src/pods/embalse-search/embalse-search.tsx +++ b/front/src/pods/embalse-search/embalse-search.tsx @@ -10,6 +10,7 @@ import { getFilteredEmbalses as getFilteredEmbalsesBusiness } from "./embalse-se import { FilteredList } from "./components/filtered-list"; import { Input } from "./components/input"; import { RecentSearches, useRecentSearches } from "./components"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; interface Props { embalses: Embalse[]; @@ -23,7 +24,8 @@ export const EmbalseSearch: React.FC = (props) => { >([]); const [isNavigating, setIsNavigating] = useState(false); const [inputValue, setInputValue] = useState(""); - const { addNewEmbalseToLatestSearchCollection, recentSearches } = useRecentSearches(); + const { addNewEmbalseToLatestSearchCollection, recentSearches } = + useRecentSearches(); const getFilteredEmbalses = (inputValue: string): EmbalseSearchModel[] => { return getFilteredEmbalsesBusiness(inputValue, embalses); @@ -36,7 +38,7 @@ export const EmbalseSearch: React.FC = (props) => { highlightedIndex, } = useCombobox({ items: filteredEmbalses, - itemToString: (item) => (item ? item.name : ""), + itemToString: (item) => (item ? formatEmbalseDisplayName(item.name) : ""), onInputValueChange: ({ inputValue: newValue }) => { setInputValue(newValue || ""); setFilteredEmbalses(newValue ? getFilteredEmbalses(newValue) : []); diff --git a/front/src/pods/embalse/components/reservoir-card-gauge.tsx b/front/src/pods/embalse/components/reservoir-card-gauge.tsx index 500a2cc..7f7f909 100644 --- a/front/src/pods/embalse/components/reservoir-card-gauge.tsx +++ b/front/src/pods/embalse/components/reservoir-card-gauge.tsx @@ -9,7 +9,7 @@ import { GaugeChart } from "./reservoir-gauge"; import { GaugeLegend } from "./reservoir-gauge/gauge-chart/components/gauge-legend.component"; import { HistoryChart } from "./chart"; import { useIsMobile } from "./useIsMobile"; -import { formatEmbalseDisplayName } from "../embalse-name.helper"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; interface Props { name: string; reservoirData: ReservoirData; diff --git a/front/src/pods/embalse/components/reservoir-card-info.component.tsx b/front/src/pods/embalse/components/reservoir-card-info.component.tsx index ee6dd8a..121c7e7 100644 --- a/front/src/pods/embalse/components/reservoir-card-info.component.tsx +++ b/front/src/pods/embalse/components/reservoir-card-info.component.tsx @@ -1,6 +1,6 @@ import { ReservoirInfo } from "../embalse.vm"; import React from "react"; -import Image from "next/image"; +import { formatEmbalseDisplayName } from "@/common/helpers/embalse-name.helper"; interface Props { reservoirInfo: ReservoirInfo; @@ -14,7 +14,9 @@ export const ReservoirCardInfo: React.FC = (props) => { className="flex w-full flex-col items-start gap-4" aria-labelledby="discover-title" > -

Descubre el embalse {reservoirInfo?.name}

+

+ Descubre el embalse {formatEmbalseDisplayName(reservoirInfo?.name)} +

{reservoirInfo?.description}

{reservoirInfo?.mainPicture?.url && ( <>