aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/app/routes
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/app/routes')
-rw-r--r--src/frontend/app/routes/home.css320
-rw-r--r--src/frontend/app/routes/home.tsx67
-rw-r--r--src/frontend/app/routes/settings.css283
-rw-r--r--src/frontend/app/routes/settings.tsx145
4 files changed, 122 insertions, 693 deletions
diff --git a/src/frontend/app/routes/home.css b/src/frontend/app/routes/home.css
deleted file mode 100644
index b935518..0000000
--- a/src/frontend/app/routes/home.css
+++ /dev/null
@@ -1,320 +0,0 @@
-/* Common page styles */
-.stoplist-page {
- display: flex;
- flex-direction: column;
- gap: 1.5rem;
- padding: 1rem 0 2rem;
-}
-
-.stoplist-section {
- width: 100%;
- padding: 0 1rem;
- box-sizing: border-box;
-}
-
-.search-container {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
-}
-
-.search-bar {
- width: 100%;
- padding: 0.75rem 1rem;
- font-size: 1rem;
- border: 1px solid var(--border-color);
- border-radius: 0.75rem;
- background-color: var(--card-background, var(--background-color));
- color: var(--text-color);
- transition: border-color 0.2s ease, box-shadow 0.2s ease;
-}
-
-.search-bar::placeholder {
- color: var(--subtitle-color);
- opacity: 0.8;
-}
-
-.search-bar:focus {
- outline: none;
- border-color: var(--button-background-color);
- box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.05);
-}
-
-/* Form styles */
-.search-form {
- margin: 0;
-}
-
-.form-group {
- margin: 0;
- display: flex;
- flex-direction: column;
-}
-
-.form-label {
- font-size: 0.85rem;
- margin-bottom: 0.5rem;
- font-weight: 500;
-}
-
-.form-input {
- padding: 0.75rem;
- font-size: 1rem;
- border: 1px solid var(--border-color);
- border-radius: 8px;
-}
-
-/* List styles */
-.list-container {
- margin: 0;
- display: flex;
- flex-direction: column;
- gap: 0.75rem;
-}
-
-.list {
- list-style: none;
- padding: 0;
- margin: 0;
- display: flex;
- flex-direction: column;
- gap: 0.75rem;
-}
-
-.list-item {
- padding: 0.75rem;
- border-bottom: 1px solid var(--border-color);
-}
-
-.list-item-link {
- display: block;
- color: var(--text-color);
- text-decoration: none;
- font-size: 1rem; /* Reduced font size */
-}
-
-.list-item-link:hover {
- color: var(--button-background-color);
-}
-
-.list-item-link:hover .line-icon {
- color: var(--text-color);
-}
-
-.distance-info {
- font-size: 0.9rem;
- color: var(--subtitle-color);
-}
-
-/* Message styles */
-.message {
- padding: 1rem;
- background-color: var(--message-background-color);
- border-radius: 8px;
- margin-bottom: 1rem;
-}
-
-/* About page specific styles */
-.about-page {
- text-align: center;
- padding: 1rem;
-}
-
-.about-version {
- color: var(--subtitle-color);
- font-size: 0.9rem;
- margin-top: 2rem;
-}
-
-.about-description {
- margin-top: 1rem;
- line-height: 1.6;
-}
-
-/* Map page specific styles */
-.map-container {
- height: calc(100dvh - 140px);
- margin: -16px;
- margin-bottom: 1rem;
- position: relative;
-}
-
-/* Fullscreen map styles */
-.fullscreen-container {
- position: absolute;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100dvh;
- padding: 0;
- margin: 0;
- max-width: none;
- overflow: hidden;
-}
-
-.fullscreen-map {
- width: 100%;
- height: 100%;
-}
-
-.fullscreen-loading {
- display: flex;
- justify-content: center;
- align-items: center;
- height: 100dvh;
- width: 100vw;
- font-size: 1.8rem;
- font-weight: 600;
- color: var(--text-color);
-}
-
-/* Map marker and popup styles */
-.stop-marker {
- box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
- transition: all 0.2s ease-in-out;
-}
-
-.stop-marker:hover {
- transform: scale(1.2);
-}
-
-.maplibregl-popup {
- max-width: 250px;
-}
-
-.maplibregl-popup-content {
- padding: 12px;
- border-radius: 8px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
-}
-
-.popup-line-icons {
- display: flex;
- flex-wrap: wrap;
- margin: 6px 0;
- gap: 5px;
-}
-
-.popup-line {
- display: inline-block;
- background-color: var(--button-background-color);
- color: white;
- padding: 2px 6px;
- margin-right: 4px;
- border-radius: 4px;
- font-size: 0.8rem;
- font-weight: 500;
-}
-
-.popup-link {
- display: block;
- margin-top: 8px;
- color: var(--button-background-color);
- text-decoration: none;
- font-weight: 500;
-}
-
-.popup-link:hover {
- text-decoration: underline;
-}
-
-/* Estimates page specific styles */
-.estimates-header {
- display: flex;
- align-items: center;
- margin-bottom: 1rem;
-}
-
-.estimates-stop-id {
- font-size: 1rem;
- color: var(--subtitle-color);
- margin-left: 0.5rem;
-}
-
-.estimates-arrival {
- color: #28a745;
- font-weight: 500;
-}
-
-.estimates-delayed {
- color: #dc3545;
-}
-
-.button-group {
- display: flex;
- gap: 1rem;
- margin-bottom: 1.5rem;
- flex-wrap: wrap;
-}
-
-.button {
- padding: 0.75rem 1rem;
- background-color: var(--button-background-color);
- color: white;
- border: none;
- border-radius: 8px;
- font-size: 1rem;
- font-weight: 500;
- cursor: pointer;
- text-align: center;
- text-decoration: none;
- display: inline-block;
-}
-
-.button:hover {
- background-color: var(--button-hover-background-color);
-}
-
-.button:disabled {
- background-color: var(--button-disabled-background-color);
- cursor: not-allowed;
-}
-
-.star-icon {
- margin-right: 0.5rem;
- color: #ccc;
- fill: none;
-}
-
-.star-icon.active {
- color: var(--star-color); /* Yellow color for active star */
- fill: var(--star-color);
-}
-
-/* Tablet and larger breakpoint */
-@media (min-width: 768px) {
- .search-form {
- display: flex;
- align-items: flex-end;
- gap: 1rem;
- }
-
- .form-group {
- flex: 1;
- margin-bottom: 0;
- }
-
- .form-button {
- width: auto;
- margin-top: 0;
- }
-
- .list {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
- gap: 1rem;
- }
-
- .list-item {
- border: 1px solid var(--border-color);
- border-radius: 8px;
- margin-bottom: 0;
- }
-}
-
-/* Desktop breakpoint */
-@media (min-width: 1024px) {
- .list {
- grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
- }
-}
diff --git a/src/frontend/app/routes/home.tsx b/src/frontend/app/routes/home.tsx
index 7d8338f..cb640c3 100644
--- a/src/frontend/app/routes/home.tsx
+++ b/src/frontend/app/routes/home.tsx
@@ -2,22 +2,18 @@ import Fuse from "fuse.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { usePageTitle } from "~/contexts/PageTitleContext";
-import { useApp } from "../AppContext";
import StopGallery from "../components/StopGallery";
import StopItem from "../components/StopItem";
import StopItemSkeleton from "../components/StopItemSkeleton";
import StopDataProvider, { type Stop } from "../data/StopDataProvider";
-import "./home.css";
+import "../tailwind-full.css";
export default function StopList() {
const { t } = useTranslation();
usePageTitle(t("navbar.stops", "Paradas"));
- const { region } = useApp();
const [data, setData] = useState<Stop[] | null>(null);
const [loading, setLoading] = useState(true);
const [searchResults, setSearchResults] = useState<Stop[] | null>(null);
- const [favouriteIds, setFavouriteIds] = useState<number[]>([]);
- const [recentIds, setRecentIds] = useState<number[]>([]);
const [favouriteStops, setFavouriteStops] = useState<Stop[]>([]);
const [recentStops, setRecentStops] = useState<Stop[]>([]);
const [userLocation, setUserLocation] = useState<{
@@ -158,12 +154,6 @@ export default function StopList() {
.map(({ stop }) => stop);
}, [data, userLocation]);
- // Load favourite and recent IDs immediately from localStorage
- useEffect(() => {
- setFavouriteIds(StopDataProvider.getFavouriteIds());
- setRecentIds(StopDataProvider.getRecent());
- }, [region]);
-
// Load stops from network
const loadStops = useCallback(async () => {
try {
@@ -196,7 +186,7 @@ export default function StopList() {
} finally {
setLoading(false);
}
- }, [region]);
+ }, []);
useEffect(() => {
loadStops();
@@ -246,23 +236,35 @@ export default function StopList() {
};
return (
- <div className="stoplist-page">
- <div className="stoplist-section search-container">
- <h3 className="page-subtitle">{t("stoplist.search_label", "Buscar paradas")}</h3>
+ <div className="flex flex-col gap-4 py-4 pb-8">
+ {/* Search Section */}
+ <div className="w-full px-4">
+ <h3 className="text-lg font-semibold mb-2 text-gray-900 dark:text-gray-100">
+ {t("stoplist.search_label", "Buscar paradas")}
+ </h3>
<input
type="search"
placeholder={randomPlaceholder}
onChange={handleStopSearch}
- className="search-bar"
+ className="
+ w-full px-4 py-3 text-base
+ border border-gray-300 dark:border-gray-700 rounded-xl
+ bg-white dark:bg-gray-800
+ text-gray-900 dark:text-gray-100
+ placeholder:text-gray-500 dark:placeholder:text-gray-400 placeholder:opacity-80
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
+ transition-all duration-200
+ "
/>
</div>
+ {/* Search Results */}
{searchResults && searchResults.length > 0 && (
- <div className="stoplist-section list-container">
- <h2 className="page-subtitle">
+ <div className="w-full px-4 flex flex-col gap-2">
+ <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
{t("stoplist.search_results", "Resultados de la búsqueda")}
</h2>
- <ul className="list">
+ <ul className="list-none p-0 m-0 flex flex-col gap-2 md:grid md:grid-cols-[repeat(auto-fill,minmax(300px,1fr))] lg:grid-cols-[repeat(auto-fill,minmax(320px,1fr))]">
{searchResults.map((stop: Stop) => (
<StopItem key={stop.stopId} stop={stop} />
))}
@@ -270,6 +272,7 @@ export default function StopList() {
</div>
)}
+ {/* Favourites Gallery */}
{!loading && (
<StopGallery
stops={favouriteStops.sort((a, b) => a.stopId - b.stopId)}
@@ -278,7 +281,8 @@ export default function StopList() {
/>
)}
- {!loading && (
+ {/* Recent Stops Gallery - only show if no favourites */}
+ {!loading && favouriteStops.length === 0 && (
<StopGallery
stops={recentStops.slice(0, 5)}
title={t("stoplist.recents")}
@@ -287,14 +291,23 @@ export default function StopList() {
{/*<ServiceAlerts />*/}
- <div className="stoplist-section list-container">
- <h2 className="page-subtitle">
- {userLocation
- ? t("stoplist.nearby_stops", "Nearby stops")
- : t("stoplist.all_stops", "Paradas")}
- </h2>
+ {/* All Stops / Nearby Stops */}
+ <div className="w-full px-4 flex flex-col gap-2">
+ <div className="flex items-center gap-2">
+ {userLocation && (
+ <svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
+ </svg>
+ )}
+ <h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">
+ {userLocation
+ ? t("stoplist.nearby_stops", "Nearby stops")
+ : t("stoplist.all_stops", "Paradas")}
+ </h2>
+ </div>
- <ul className="list">
+ <ul className="list-none p-0 m-0 flex flex-col gap-2 md:grid md:grid-cols-[repeat(auto-fill,minmax(300px,1fr))] lg:grid-cols-[repeat(auto-fill,minmax(320px,1fr))]">
{loading && (
<>
{Array.from({ length: 6 }, (_, index) => (
diff --git a/src/frontend/app/routes/settings.css b/src/frontend/app/routes/settings.css
deleted file mode 100644
index 02708a7..0000000
--- a/src/frontend/app/routes/settings.css
+++ /dev/null
@@ -1,283 +0,0 @@
-/* About page specific styles */
-.about-page {
- text-align: center;
- padding: 1rem;
-}
-
-.about-version {
- color: var(--subtitle-color);
- font-size: 0.9rem;
- margin-top: 2rem;
-}
-
-.about-description {
- margin-top: 1rem;
- line-height: 1.6;
-}
-
-.settings-section {
- margin-bottom: 2em;
- padding: 1rem;
- border: 1px solid var(--border-color);
- border-radius: 8px;
- background-color: var(--message-background-color);
- text-align: left;
-}
-
-.settings-section h2 {
- margin-bottom: 1em;
-}
-
-.settings-content {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- margin-bottom: 1em;
-}
-
-.settings-content-inline {
- display: flex;
- flex-direction: column;
- align-items: stretch;
- margin-bottom: 1em;
-}
-
-.settings-section .form-button {
- margin-bottom: 1em;
- padding: 0.75rem 1.5rem;
- font-size: 1.1rem;
-}
-
-.settings-section .form-select-inline {
- margin-left: 0.5em;
- padding: 0.5rem;
- font-size: 1rem;
- border-radius: 8px;
-}
-
-.settings-section .form-label-inline {
- font-weight: 500;
-}
-
-.settings-section .form-label {
- display: block;
- margin-bottom: 0.5em;
- font-weight: 500;
-}
-
-.settings-section .form-description {
- margin-top: 0.5em;
- font-size: 0.9rem;
- color: var(--subtitle-color);
-}
-
-.settings-section .form-details {
- margin-top: 0.5em;
- font-size: 0.9rem;
- color: var(--subtitle-color);
- border: 1px solid var(--border-color);
- border-radius: 8px;
- padding: 0.5rem;
-}
-
-.settings-section .form-details summary {
- cursor: pointer;
- font-weight: 500;
-}
-
-.settings-section .form-details p {
- margin-top: 0.5em;
-}
-
-.settings-section p {
- margin-top: 0.5em;
-}
-
-/* Update controls styles */
-.update-controls {
- display: flex;
- gap: 1rem;
- margin-bottom: 1rem;
- flex-wrap: wrap;
-}
-
-.update-button,
-.clear-cache-button {
- display: inline-flex;
- align-items: center;
- gap: 0.5rem;
- padding: 0.75rem 1rem;
- border: none;
- border-radius: 8px;
- font-size: 0.9rem;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.2s ease;
- text-decoration: none;
-}
-
-.update-button {
- background-color: var(--button-background-color);
- color: white;
-}
-
-.update-button:hover:not(:disabled) {
- background-color: var(--button-hover-background-color);
-}
-
-.update-button:disabled {
- background-color: var(--button-disabled-background-color);
- cursor: not-allowed;
-}
-
-.clear-cache-button {
- background-color: #6c757d;
- color: white;
-}
-
-.clear-cache-button:hover {
- background-color: #5a6268;
-}
-
-.reset-pwa-button {
- background-color: #dc3545;
- color: white;
- font-weight: bold;
-}
-
-.reset-pwa-button:hover {
- background-color: #c82333;
-}
-
-.update-message {
- padding: 0.75rem;
- border-radius: 6px;
- font-size: 0.9rem;
- margin-bottom: 1rem;
-}
-
-.update-message.success {
- background-color: #d4edda;
- color: #155724;
- border: 1px solid #c3e6cb;
-}
-
-.update-message.error {
- background-color: #f8d7da;
- color: #721c24;
- border: 1px solid #f5c6cb;
-}
-
-.update-help-text {
- font-size: 0.85rem;
- color: var(--subtitle-color);
- line-height: 1.4;
- margin: 0;
-}
-
-.spinning {
- animation: spin 1s linear infinite;
-}
-
-@keyframes spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
-
-/* Modal styles */
-.modal-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 1000;
- padding: 1rem;
-}
-
-.modal-content {
- background-color: var(--message-background-color);
- padding: 2rem;
- border-radius: 12px;
- max-width: 500px;
- width: 100%;
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
-}
-
-.modal-content h2 {
- margin-top: 0;
- margin-bottom: 1rem;
- color: var(--text-color);
-}
-
-.modal-content p {
- margin-bottom: 1.5rem;
- line-height: 1.6;
- color: var(--text-color);
-}
-
-.modal-buttons {
- display: flex;
- gap: 1rem;
- justify-content: flex-end;
-}
-
-.modal-button {
- padding: 0.75rem 1.5rem;
- border: none;
- border-radius: 8px;
- font-size: 1rem;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.2s ease;
-}
-
-.modal-button-cancel {
- background-color: #6c757d;
- color: white;
-}
-
-.modal-button-cancel:hover {
- background-color: #5a6268;
-}
-
-.modal-button-confirm {
- background-color: var(--button-background-color);
- color: white;
-}
-
-.modal-button-confirm:hover {
- background-color: var(--button-hover-background-color);
-}
-
-@media (max-width: 768px) {
- .update-controls {
- flex-direction: column;
- }
-
- .update-button,
- .clear-cache-button {
- justify-content: center;
- }
-
- .modal-content {
- padding: 1.5rem;
- }
-
- .modal-buttons {
- flex-direction: column;
- gap: 0.5rem;
- }
-
- .modal-button {
- width: 100%;
- }
-}
diff --git a/src/frontend/app/routes/settings.tsx b/src/frontend/app/routes/settings.tsx
index faad5a6..9b4625f 100644
--- a/src/frontend/app/routes/settings.tsx
+++ b/src/frontend/app/routes/settings.tsx
@@ -1,8 +1,8 @@
import { Computer, Moon, Sun } from "lucide-react";
import { useTranslation } from "react-i18next";
import { usePageTitle } from "~/contexts/PageTitleContext";
-import { type Theme, useApp } from "../AppContext";
-import "./settings.css";
+import { useApp, type Theme } from "../AppContext";
+import '../tailwind-full.css';
export default function Settings() {
const { t, i18n } = useTranslation();
@@ -14,72 +14,91 @@ export default function Settings() {
setMapPositionMode
} = useApp();
- return (
- <div className="page-container">
- <section className="settings-section">
- <h2>{t("about.settings")}</h2>
-
- <div className="settings-content-inline">
- <label htmlFor="theme" className="form-label-inline">
- {t("about.theme")}
- </label>
+ const THEMES = [
+ { value: "light" as Theme, label: t("about.theme_light", "Claro"), icon: Sun },
+ { value: "dark" as Theme, label: t("about.theme_dark", "Oscuro"), icon: Moon },
+ { value: "system" as Theme, label: t("about.theme_system", "Sistema"), icon: Computer },
+ ];
- <div className="flex">
- <button onClick={() => setTheme("light")}>
- <Sun />
- </button>
- <button onClick={() => setTheme("dark")}>
- <Moon />
- </button>
- <button onClick={() => setTheme("system")}>
- <Computer />
+ return (
+ <div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
+ {/* Theme Selection */}
+ <section className="mb-8">
+ <h2 className="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
+ {t("about.theme", "Tema")}
+ </h2>
+ <div className="grid grid-cols-3 gap-3 sm:gap-4">
+ {THEMES.map(({ value, label, icon: Icon }) => (
+ <button
+ key={value}
+ onClick={() => setTheme(value)}
+ className={`
+ p-4 sm:p-6 flex flex-col items-center justify-center gap-2
+ rounded-lg border-2 transition-all duration-200
+ hover:bg-gray-50 dark:hover:bg-gray-800
+ focus:outline-none focus:ring focus:ring-blue-500 dark:focus:ring-offset-gray-900
+ ${value === theme
+ ? "border-blue-600 bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-400 font-semibold"
+ : "border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-300"
+ }
+ `}
+ >
+ <Icon className="w-6 h-6" />
+ <span className="text-sm sm:text-base">{label}</span>
</button>
- </div>
-
- <select
- id="theme"
- className="form-select-inline"
- value={theme}
- onChange={(e) => setTheme(e.target.value as Theme)}
- >
- <option value="light">{t("about.theme_light")}</option>
- <option value="dark">{t("about.theme_dark")}</option>
- <option value="system">{t("about.theme_system")}</option>
- </select>
+ ))}
</div>
+ </section>
- <div className="settings-content-inline">
- <label htmlFor="mapPositionMode" className="form-label-inline">
- {t("about.map_position_mode")}
- </label>
- <select
- id="mapPositionMode"
- className="form-select-inline"
- value={mapPositionMode}
- onChange={(e) =>
- setMapPositionMode(e.target.value as "gps" | "last")
- }
- >
- <option value="gps">{t("about.map_position_gps")}</option>
- <option value="last">{t("about.map_position_last")}</option>
- </select>
- </div>
- <div className="settings-content-inline">
- <label htmlFor="language" className="form-label-inline">
- {t("about.language", "Idioma")}:
- </label>
- <select
- id="language"
- className="form-select-inline"
- value={i18n.language}
- onChange={(e) => i18n.changeLanguage(e.target.value)}
- >
- <option value="es-ES">Español</option>
- <option value="gl-ES">Galego</option>
- <option value="en-GB">English</option>
- </select>
- </div>
+ {/* Map Position Mode */}
+ <section className="mb-8">
+ <label
+ htmlFor="mapPositionMode"
+ className="block text-lg font-medium text-gray-900 dark:text-gray-100 mb-3"
+ >
+ {t("about.map_position_mode")}
+ </label>
+ <select
+ id="mapPositionMode"
+ className="
+ w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-700
+ bg-white dark:bg-gray-800
+ text-gray-900 dark:text-gray-100
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
+ transition-colors duration-200
+ "
+ value={mapPositionMode}
+ onChange={(e) => setMapPositionMode(e.target.value as "gps" | "last")}
+ >
+ <option value="gps">{t("about.map_position_gps")}</option>
+ <option value="last">{t("about.map_position_last")}</option>
+ </select>
+ </section>
+ {/* Language Selection */}
+ <section>
+ <label
+ htmlFor="language"
+ className="block text-lg font-medium text-gray-900 dark:text-gray-100 mb-3"
+ >
+ {t("about.language", "Idioma")}
+ </label>
+ <select
+ id="language"
+ className="
+ w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-700
+ bg-white dark:bg-gray-800
+ text-gray-900 dark:text-gray-100
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
+ transition-colors duration-200
+ "
+ value={i18n.language}
+ onChange={(e) => i18n.changeLanguage(e.target.value)}
+ >
+ <option value="es-ES">Español</option>
+ <option value="gl-ES">Galego</option>
+ <option value="en-GB">English</option>
+ </select>
</section>
</div>
);