aboutsummaryrefslogtreecommitdiff
path: root/src/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend')
-rw-r--r--src/frontend/app/AppContext.tsx14
-rw-r--r--src/frontend/app/i18n/locales/en-GB.json4
-rw-r--r--src/frontend/app/i18n/locales/es-ES.json2
-rw-r--r--src/frontend/app/i18n/locales/gl-ES.json2
-rw-r--r--src/frontend/app/root.tsx14
-rw-r--r--src/frontend/app/routes/settings.css83
-rw-r--r--src/frontend/app/routes/settings.tsx72
-rw-r--r--src/frontend/app/routes/stoplist.tsx3
-rw-r--r--src/frontend/public/manifest.webmanifest6
-rw-r--r--src/frontend/public/pwa-worker.js2
10 files changed, 172 insertions, 30 deletions
diff --git a/src/frontend/app/AppContext.tsx b/src/frontend/app/AppContext.tsx
index 1a9b511..8f47a49 100644
--- a/src/frontend/app/AppContext.tsx
+++ b/src/frontend/app/AppContext.tsx
@@ -7,7 +7,7 @@ import {
type ReactNode,
} from "react";
import { type LngLatLike } from "maplibre-gl";
-import { type RegionId, DEFAULT_REGION, getRegionConfig, isValidRegion } from "./data/RegionConfig";
+import { type RegionId, DEFAULT_REGION, getRegionConfig, isValidRegion, REGIONS } from "./data/RegionConfig";
export type Theme = "light" | "dark" | "system";
type TableStyle = "regular" | "grouped";
@@ -43,10 +43,6 @@ interface AppContextProps {
setRegion: (region: RegionId) => void;
}
-// Coordenadas por defecto centradas en Vigo
-const DEFAULT_CENTER: LngLatLike = [42.229188855975046, -8.72246955783102];
-const DEFAULT_ZOOM = 14;
-
const AppContext = createContext<AppContextProps | undefined>(undefined);
export const AppProvider = ({ children }: { children: ReactNode }) => {
@@ -187,8 +183,8 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
try {
const parsed = JSON.parse(savedMapState);
return {
- center: parsed.center || DEFAULT_CENTER,
- zoom: parsed.zoom || DEFAULT_ZOOM,
+ center: parsed.center || REGIONS[region].defaultCenter,
+ zoom: parsed.zoom || REGIONS[region].defaultZoom,
userLocation: parsed.userLocation || null,
hasLocationPermission: parsed.hasLocationPermission || false,
};
@@ -197,8 +193,8 @@ export const AppProvider = ({ children }: { children: ReactNode }) => {
}
}
return {
- center: DEFAULT_CENTER,
- zoom: DEFAULT_ZOOM,
+ center: REGIONS[region].defaultCenter,
+ zoom: REGIONS[region].defaultZoom,
userLocation: null,
hasLocationPermission: false,
};
diff --git a/src/frontend/app/i18n/locales/en-GB.json b/src/frontend/app/i18n/locales/en-GB.json
index 4d0c91b..12c8121 100644
--- a/src/frontend/app/i18n/locales/en-GB.json
+++ b/src/frontend/app/i18n/locales/en-GB.json
@@ -1,7 +1,7 @@
{
"about": {
- "title": "About UrbanoVigo Web",
- "description": "Web app to find stops and arrival times for Vigo's urban buses, Spain.",
+ "title": "About BusUrbano",
+ "description": "Web app to find stops and arrival times for urban buses, Spain.",
"credits": "Credits",
"github": "Code on GitHub",
"developed_by": "Developed by",
diff --git a/src/frontend/app/i18n/locales/es-ES.json b/src/frontend/app/i18n/locales/es-ES.json
index 0915361..e929382 100644
--- a/src/frontend/app/i18n/locales/es-ES.json
+++ b/src/frontend/app/i18n/locales/es-ES.json
@@ -1,6 +1,6 @@
{
"about": {
- "title": "Sobre UrbanoVigo Web",
+ "title": "Sobre BusUrbano",
"description": "Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos de Vigo, España.",
"credits": "Créditos",
"github": "Código en GitHub",
diff --git a/src/frontend/app/i18n/locales/gl-ES.json b/src/frontend/app/i18n/locales/gl-ES.json
index 8949333..ed1ef53 100644
--- a/src/frontend/app/i18n/locales/gl-ES.json
+++ b/src/frontend/app/i18n/locales/gl-ES.json
@@ -1,6 +1,6 @@
{
"about": {
- "title": "Sobre UrbanoVigo Web",
+ "title": "Sobre BusUrbano",
"description": "Aplicación web para atopar paradas e tempos de chegada dos autobuses urbanos de Vigo, España.",
"credits": "Créditos",
"github": "Código en GitHub",
diff --git a/src/frontend/app/root.tsx b/src/frontend/app/root.tsx
index 25a873f..4815d2f 100644
--- a/src/frontend/app/root.tsx
+++ b/src/frontend/app/root.tsx
@@ -45,28 +45,28 @@ export function Layout({ children }: { children: React.ReactNode }) {
<link rel="apple-touch-icon" href="/logo-512.jpg" sizes="512x512" />
<meta name="theme-color" content="#007bff" />
- <link rel="canonical" href="https://urbanovigo.costas.dev/" />
+ <link rel="canonical" href="https://busurbano.costas.dev/" />
<meta
name="description"
- content="Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos de Vigo, España."
+ content="Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos"
/>
<meta
name="keywords"
- content="Vigo, autobús, urbano, parada, tiempo, llegada, transporte, público, España"
+ content="autobús, urbano, parada, tiempo, llegada, transporte, público, España"
/>
<meta name="author" content="Ariel Costas Guerrero" />
- <meta property="og:title" content="UrbanoVigo Web" />
+ <meta property="og:title" content="Busurbano Web" />
<meta property="og:type" content="website" />
- <meta property="og:url" content="https://urbanovigo.costas.dev/" />
+ <meta property="og:url" content="https://busurbano.costas.dev/" />
<meta
property="og:image"
- content="https://urbanovigo.costas.dev/logo-512.jpg"
+ content="https://busurbano.costas.dev/logo-512.jpg"
/>
<meta
property="og:description"
- content="Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos de Vigo, España."
+ content="Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos"
/>
<link rel="manifest" href="/manifest.webmanifest" />
diff --git a/src/frontend/app/routes/settings.css b/src/frontend/app/routes/settings.css
index ef9fbd5..c08ed68 100644
--- a/src/frontend/app/routes/settings.css
+++ b/src/frontend/app/routes/settings.css
@@ -183,6 +183,76 @@
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;
@@ -192,4 +262,17 @@
.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 eae6ad8..cb64f4e 100644
--- a/src/frontend/app/routes/settings.tsx
+++ b/src/frontend/app/routes/settings.tsx
@@ -2,10 +2,12 @@ import { type Theme, useApp } from "../AppContext";
import "./settings.css";
import { useTranslation } from "react-i18next";
import { useState } from "react";
-import { getAvailableRegions } from "../data/RegionConfig";
+import { getAvailableRegions, REGIONS } from "../data/RegionConfig";
+import { useNavigate } from "react-router";
export default function Settings() {
const { t, i18n } = useTranslation();
+ const navigate = useNavigate();
const {
theme,
setTheme,
@@ -18,6 +20,29 @@ export default function Settings() {
} = useApp();
const regions = getAvailableRegions();
+ const [showModal, setShowModal] = useState(false);
+ const [pendingRegion, setPendingRegion] = useState<string | null>(null);
+
+ const handleRegionChange = (newRegion: string) => {
+ if (newRegion !== region) {
+ setPendingRegion(newRegion);
+ setShowModal(true);
+ }
+ };
+
+ const confirmRegionChange = () => {
+ if (pendingRegion) {
+ setRegion(pendingRegion as any);
+ setShowModal(false);
+ setPendingRegion(null);
+ navigate("/stops");
+ }
+ };
+
+ const cancelRegionChange = () => {
+ setShowModal(false);
+ setPendingRegion(null);
+ };
return (
<div className="page-container">
@@ -33,7 +58,7 @@ export default function Settings() {
id="region"
className="form-select-inline"
value={region}
- onChange={(e) => setRegion(e.target.value as any)}
+ onChange={(e) => handleRegionChange(e.target.value)}
>
{regions.map((r) => (
<option key={r.id} value={r.id}>
@@ -118,13 +143,13 @@ export default function Settings() {
<h2>{t("about.credits")}</h2>
<p>
<a
- href="https://github.com/arielcostas/urbanovigo-web"
+ href="https://github.com/arielcostas/busurbano"
className="about-link"
rel="nofollow noreferrer noopener"
>
{t("about.github")}
</a>{" "}
- -{t("about.developed_by")}{" "}
+ - {t("about.developed_by")}{" "}
<a
href="https://www.costas.dev"
className="about-link"
@@ -133,6 +158,7 @@ export default function Settings() {
Ariel Costas
</a>
</p>
+ {region === "vigo" && (
<p>
{t("about.data_source_prefix")}{" "}
<a
@@ -149,8 +175,44 @@ export default function Settings() {
rel="nofollow noreferrer noopener"
>
Open Data Commons Attribution License
- </a>
+ </a>.
</p>
+ )}
+ {region === "santiago" && (
+ <p>
+ Datos obtenidos de app MaisBus (Concello de Santiago/TUSSA),
+ gracias a la documentación de [TP Galicia](https://tpgalicia.github.io/urban/santiago/)
+ en GitHub.
+ </p>
+ )}
+
+ {showModal && (
+ <div className="modal-overlay" onClick={cancelRegionChange}>
+ <div className="modal-content" onClick={(e) => e.stopPropagation()}>
+ <h2>{t("about.region_change_title", "Cambiar región")}</h2>
+ <p>
+ {t(
+ "about.region_change_message",
+ "¿Estás seguro de que quieres cambiar la región? Serás redirigido a la lista de paradas."
+ )}
+ </p>
+ <div className="modal-buttons">
+ <button
+ className="modal-button modal-button-cancel"
+ onClick={cancelRegionChange}
+ >
+ {t("about.cancel", "Cancelar")}
+ </button>
+ <button
+ className="modal-button modal-button-confirm"
+ onClick={confirmRegionChange}
+ >
+ {t("about.confirm", "Confirmar")}
+ </button>
+ </div>
+ </div>
+ </div>
+ )}
</div>
);
}
diff --git a/src/frontend/app/routes/stoplist.tsx b/src/frontend/app/routes/stoplist.tsx
index 13d3584..71b7d3c 100644
--- a/src/frontend/app/routes/stoplist.tsx
+++ b/src/frontend/app/routes/stoplist.tsx
@@ -6,6 +6,7 @@ import Fuse from "fuse.js";
import "./stoplist.css";
import { useTranslation } from "react-i18next";
import { useApp } from "../AppContext";
+import { REGIONS } from "~/data/RegionConfig";
export default function StopList() {
const { t } = useTranslation();
@@ -100,7 +101,7 @@ export default function StopList() {
return (
<div className="page-container stoplist-page">
- <h1 className="page-title">UrbanoVigo Web</h1>
+ <h1 className="page-title">BusUrbano - {REGIONS[region].name}</h1>
<form className="search-form">
<div className="form-group">
diff --git a/src/frontend/public/manifest.webmanifest b/src/frontend/public/manifest.webmanifest
index fe0cf39..e685a23 100644
--- a/src/frontend/public/manifest.webmanifest
+++ b/src/frontend/public/manifest.webmanifest
@@ -1,9 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/SchemaStore/schemastore/refs/heads/master/src/schemas/json/web-manifest.json",
"id": "https://busurbano.costas.dev/",
- "name": "UrbanoVigo Web",
- "description": "Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos de Vigo, España.",
- "short_name": "UrbanoVigo",
+ "name": "BusUrbano",
+ "description": "Aplicación web para encontrar paradas y tiempos de llegada de los autobuses urbanos.",
+ "short_name": "BusUrbano",
"start_url": "/stops",
"display": "standalone",
"orientation": "portrait-primary",
diff --git a/src/frontend/public/pwa-worker.js b/src/frontend/public/pwa-worker.js
index f9bb758..810478d 100644
--- a/src/frontend/public/pwa-worker.js
+++ b/src/frontend/public/pwa-worker.js
@@ -1,4 +1,4 @@
-const CACHE_VERSION = "20251021b";
+const CACHE_VERSION = "20251022a";
const STATIC_CACHE_NAME = `static-cache-${CACHE_VERSION}`;
const STATIC_CACHE_ASSETS = [
"/favicon.ico",