From a477dda9dc4291ab25fffe2525acf44177154c86 Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Sun, 30 Nov 2025 23:27:33 +0100 Subject: Remake home and settings pages using Tailwind styles --- src/frontend/app/routes/home.css | 320 ----------------------------------- src/frontend/app/routes/home.tsx | 67 +++++--- src/frontend/app/routes/settings.css | 283 ------------------------------- src/frontend/app/routes/settings.tsx | 145 +++++++++------- 4 files changed, 122 insertions(+), 693 deletions(-) delete mode 100644 src/frontend/app/routes/home.css delete mode 100644 src/frontend/app/routes/settings.css (limited to 'src/frontend/app/routes') 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(null); const [loading, setLoading] = useState(true); const [searchResults, setSearchResults] = useState(null); - const [favouriteIds, setFavouriteIds] = useState([]); - const [recentIds, setRecentIds] = useState([]); const [favouriteStops, setFavouriteStops] = useState([]); const [recentStops, setRecentStops] = useState([]); 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 ( -
-
-

{t("stoplist.search_label", "Buscar paradas")}

+
+ {/* Search Section */} +
+

+ {t("stoplist.search_label", "Buscar paradas")} +

+ {/* Search Results */} {searchResults && searchResults.length > 0 && ( -
-

+
+

{t("stoplist.search_results", "Resultados de la búsqueda")}

-
    +
      {searchResults.map((stop: Stop) => ( ))} @@ -270,6 +272,7 @@ export default function StopList() {
)} + {/* Favourites Gallery */} {!loading && ( 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 && ( */} -
-

- {userLocation - ? t("stoplist.nearby_stops", "Nearby stops") - : t("stoplist.all_stops", "Paradas")} -

+ {/* All Stops / Nearby Stops */} +
+
+ {userLocation && ( + + + + + )} +

+ {userLocation + ? t("stoplist.nearby_stops", "Nearby stops") + : t("stoplist.all_stops", "Paradas")} +

+
-
    +
      {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 ( -
      -
      -

      {t("about.settings")}

      - -
      - + 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 }, + ]; -
      - - - -
      - - + ))}
      +
      -
      - - -
      -
      - - -
      + {/* Map Position Mode */} +
      + + +
      + {/* Language Selection */} +
      + +
      ); -- cgit v1.3