From 6d4f50cafb8457ed9da5def053e417808faf8bf3 Mon Sep 17 00:00:00 2001 From: Hanzo_dev <2002samudiojohan@gmail.com> Date: Tue, 24 Feb 2026 10:46:17 -0500 Subject: [PATCH] feat: habilitar set completo de APIs de Google Maps y actualizar llaves funcionales para produccion --- .gitignore | 3 +- backend/app/main.py | 8 +- frontend/.env.production | 4 +- frontend/src/services/routesService.ts | 5 + frontend/src/views/AdminRoutes.vue | 888 ++++++++++++------------- frontend/src/views/AuthView.vue | 34 + frontend/src/views/TaxiView.vue | 433 ++++++++---- 7 files changed, 758 insertions(+), 617 deletions(-) diff --git a/.gitignore b/.gitignore index e469417..e4b64a4 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,8 @@ **/*.venv **/venv/ **/env/ -**/.env +**/.env* + **/instance/ **/.pdm-python **/.pdm-build/ diff --git a/backend/app/main.py b/backend/app/main.py index acccc93..117bd49 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -110,12 +110,8 @@ app = FastAPI( ) # CORS middleware -origins = [ - "http://localhost:5173", - "http://127.0.0.1:5173", - "https://sibu2-0-transport-2026.web.app", - "https://sibu2-0-transport-2026.firebaseapp.com", -] +origins = ["*"] # Permitir todos para producción corregida + app.add_middleware( CORSMiddleware, diff --git a/frontend/.env.production b/frontend/.env.production index e046d3b..269fd27 100644 --- a/frontend/.env.production +++ b/frontend/.env.production @@ -1,2 +1,2 @@ -VITE_API_URL=https://sibu-backend.onrender.com -VITE_GOOGLE_MAPS_API_KEY=AIzaSyBKSSmUXBxLbKBqhAtVJ2CljDfkJcWIGzg +VITE_GOOGLE_MAPS_API_KEY=AIzaSyDrKzxJ-9A48cWFRffpOnEdmRR1Ijjj--Y +VITE_API_URL=https://sibu2-0.onrender.com diff --git a/frontend/src/services/routesService.ts b/frontend/src/services/routesService.ts index 10b70cd..02ee48f 100644 --- a/frontend/src/services/routesService.ts +++ b/frontend/src/services/routesService.ts @@ -51,6 +51,11 @@ export const routesService = { /** Update a stop on a route (Admin) - including reorder */ async updateRouteStop(routeId: string, stopId: string, data: import('@/types').RouteStopUpdate): Promise { await apiClient.put(`/api/routes/${routeId}/stops/${stopId}`, data) + }, + + /** Remove a stop from a route (Admin) */ + async removeStopFromRoute(routeId: string, stopId: string): Promise { + await apiClient.delete(`/api/routes/${routeId}/stops/${stopId}`) } } diff --git a/frontend/src/views/AdminRoutes.vue b/frontend/src/views/AdminRoutes.vue index c5e5e3d..a34737b 100644 --- a/frontend/src/views/AdminRoutes.vue +++ b/frontend/src/views/AdminRoutes.vue @@ -1,92 +1,136 @@ diff --git a/frontend/src/views/AuthView.vue b/frontend/src/views/AuthView.vue index 54d0de0..85eff93 100644 --- a/frontend/src/views/AuthView.vue +++ b/frontend/src/views/AuthView.vue @@ -39,6 +39,12 @@ onMounted(async () => {
+ + +

SIBU

@@ -191,6 +197,34 @@ onMounted(async () => { transform: translateX(-16px); } +/* ─── Botón volver ─── */ +.back-to-map { + display: flex; + align-items: center; + gap: 8px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + color: var(--text-secondary); + font-size: 0.85rem; + font-weight: 700; + font-family: inherit; + padding: 10px 16px; + border-radius: 12px; + cursor: pointer; + transition: all 0.2s ease; + align-self: flex-start; +} + +.back-to-map:hover { + color: var(--active-color); + border-color: var(--active-color); + background: rgba(254, 231, 21, 0.06); +} + +.back-to-map .material-icons { + font-size: 18px; +} + /* ─── Footer ─── */ .auth-footer { text-align: center; diff --git a/frontend/src/views/TaxiView.vue b/frontend/src/views/TaxiView.vue index ab9a926..605bf79 100644 --- a/frontend/src/views/TaxiView.vue +++ b/frontend/src/views/TaxiView.vue @@ -251,12 +251,21 @@ function getShiftLabel(shift: string) {
+ + +
+
+
+
@@ -273,12 +283,13 @@ function getShiftLabel(shift: string) {
$ {{ shuttle.price_per_person }} + /p
{{ shuttle.origin }} - east + east {{ shuttle.destination }}
@@ -293,8 +304,10 @@ function getShiftLabel(shift: string) {
- -
+ +
+
+
schedule @@ -310,41 +323,54 @@ function getShiftLabel(shift: string) {

{{ shuttle.departure_times }}

+
+ g_translate +
+

IDIOMA

+

Español · English

+
+
- -
- directions_bus_filled -

{{ t('shuttle.noShuttles') }}

+
+ directions_bus_filled +

{{ t('shuttle.noShuttles') }}

+
@@ -406,75 +432,180 @@ function getShiftLabel(shift: string) { box-shadow: 0 4px 15px rgba(254, 231, 21, 0.4); } -/* Shuttles Grid & Compact Cards */ +/* ============================================= + SHUTTLE CARDS — DISEÑO PREMIUM CON FOTO + ============================================= */ + +/* Backdrop modal al expandir */ +.shuttle-modal-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.65); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + z-index: 50; + animation: fadeIn 0.25s ease; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +/* Grid de cards */ .shuttles-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; padding: 20px 16px; + position: relative; } +/* ---- La tarjeta base ---- */ +.shuttle-card { + border-radius: 20px; + overflow: hidden; + border: 1px solid rgba(255,255,255,0.12); + background-size: cover; + background-position: center; + position: relative; + min-height: 170px; + display: flex; + flex-direction: column; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + cursor: pointer; +} + +/* Overlay oscuro base (compacto) */ +.shuttle-card::before { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient( + to top, + rgba(0, 0, 0, 0.93) 0%, + rgba(0, 0, 0, 0.65) 55%, + rgba(0, 0, 0, 0.30) 100% + ); + z-index: 0; + transition: background 0.4s ease; +} + +/* Cuando está expandida, reforzar el overlay inferior */ .shuttle-card.expanded { - border-color: var(--active-color); - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); + border-color: #FEE715; + box-shadow: 0 0 0 2px #FEE715, 0 24px 50px rgba(0, 0, 0, 0.7); + z-index: 60; + position: relative; +} + +.shuttle-card.expanded::before { + background: linear-gradient( + to top, + rgba(0, 0, 0, 0.97) 0%, + rgba(0, 0, 0, 0.80) 45%, + rgba(0, 0, 0, 0.40) 100% + ); +} + +/* Todos los hijos van sobre el overlay */ +.shuttle-main-info, +.shuttle-details { + position: relative; + z-index: 1; + padding: 18px 20px; } .shuttle-main-info { - padding: 0; /* Ya manejado por el contenedor superior */ + padding-bottom: 14px; } +/* Fila superior: badge empresa + precio */ .shuttle-header-mini { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 12px; + margin-bottom: 14px; } .company-badge { display: flex; align-items: center; gap: 6px; - background: rgba(0, 0, 0, 0.55); + background: rgba(0, 0, 0, 0.60); color: #FEE715; - padding: 4px 10px; - border-radius: 8px; + padding: 5px 11px; + border-radius: 10px; font-size: 0.75rem; font-weight: 800; - border: 1px solid rgba(254, 231, 21, 0.5); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - text-shadow: 0 1px 3px rgba(0,0,0,0.5); + border: 1px solid rgba(254, 231, 21, 0.45); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + text-shadow: 0 1px 4px rgba(0,0,0,0.8); + letter-spacing: 0.02em; +} + +.company-badge .material-icons { + font-size: 14px; } .price-pill { - background: var(--active-color); + background: #FEE715; color: #101820; - padding: 4px 10px; - border-radius: 8px; + padding: 5px 11px; + border-radius: 10px; font-weight: 900; - font-size: 0.9rem; + font-size: 1rem; display: flex; align-items: baseline; - gap: 2px; + gap: 1px; + box-shadow: 0 4px 12px rgba(254, 231, 21, 0.35); } +.price-pill .currency { + font-size: 0.8rem; + font-weight: 900; +} + +.price-pill .amount { + font-size: 1rem; + font-weight: 900; +} + +.price-pill-label { + font-size: 0.7rem; + font-weight: 700; + margin-left: 1px; + opacity: 0.75; +} + +/* Ruta origen → destino */ .shuttle-route-compact { display: flex; align-items: center; gap: 10px; - font-size: 1rem; - font-weight: 800; - /* Siempre blanco — se lee sobre el overlay oscuro de la imagen */ + font-size: 1.1rem; + font-weight: 900; color: #ffffff; - text-shadow: 0 1px 6px rgba(0,0,0,0.7); - margin-bottom: 12px; + text-shadow: 0 2px 8px rgba(0,0,0,0.9), 0 0 20px rgba(0,0,0,0.6); + margin-bottom: 14px; + letter-spacing: -0.01em; } -.shuttle-route-compact .material-icons { +.route-text { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.route-arrow { color: #FEE715; - font-size: 18px; + font-size: 20px; + flex-shrink: 0; + filter: drop-shadow(0 0 4px rgba(254,231,21,0.6)); } +/* Fila inferior: tipo vehículo + expand arrow */ .shuttle-tags { display: flex; justify-content: space-between; @@ -482,44 +613,58 @@ function getShiftLabel(shift: string) { } .vehicle-tag-mini { - padding: 4px 10px; - background: rgba(255, 255, 255, 0.15); - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); + padding: 5px 10px; + background: rgba(255, 255, 255, 0.12); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); border-radius: 8px; - font-size: 0.7rem; + font-size: 0.72rem; font-weight: 700; display: flex; align-items: center; gap: 6px; - /* Blanco para contraste sobre imagen */ color: #ffffff; - border: 1px solid rgba(255, 255, 255, 0.25); + text-shadow: 0 1px 4px rgba(0,0,0,0.8); + border: 1px solid rgba(255, 255, 255, 0.20); +} + +.vehicle-tag-mini .material-icons { + font-size: 15px; + color: #FEE715; } .expand-indicator { - color: var(--active-color); + color: #FEE715; display: flex; align-items: center; + filter: drop-shadow(0 0 4px rgba(254,231,21,0.5)); } -/* Expanded content */ +/* ============================================= + CONTENIDO EXPANDIDO + ============================================= */ .shuttle-details { - margin-top: 16px; - padding-top: 16px; - border-top: 1px dashed var(--border-color); + padding-top: 0; animation: slideDown 0.3s ease-out; } @keyframes slideDown { - from { opacity: 0; transform: translateY(-10px); } - to { opacity: 1; transform: translateY(0); } + from { opacity: 0; transform: translateY(-8px); } + to { opacity: 1; transform: translateY(0); } +} + +/* Separador dashed */ +.shuttle-separator { + height: 1px; + background: linear-gradient(to right, transparent, rgba(255,255,255,0.25), transparent); + margin: 0 20px 16px; } .shuttle-body { + padding: 0 20px; display: flex; flex-direction: column; - gap: 12px; + gap: 14px; margin-bottom: 20px; } @@ -530,108 +675,140 @@ function getShiftLabel(shift: string) { } .info-row .material-icons { - color: var(--active-color); - font-size: 20px; + color: #FEE715; + font-size: 22px; + flex-shrink: 0; + filter: drop-shadow(0 0 4px rgba(254,231,21,0.4)); } .info-row .label { - font-size: 0.7rem; - color: rgba(255, 255, 255, 0.65); + font-size: 0.65rem; + color: rgba(255, 255, 255, 0.55); margin: 0; text-transform: uppercase; font-weight: 700; + letter-spacing: 0.06em; } .info-row .value { - font-size: 0.9rem; + font-size: 0.95rem; color: #ffffff; margin: 2px 0 0; - font-weight: 600; - text-shadow: 0 1px 4px rgba(0,0,0,0.5); + font-weight: 700; + text-shadow: 0 1px 6px rgba(0,0,0,0.9); } -.shuttle-footer { - display: flex; - justify-content: space-between; - align-items: flex-end; /* Alineados a la base */ - margin-top: auto; - padding-top: 15px; - width: 100%; -} - -.price-box { +/* ---- Bloque de precios ---- */ +.price-block { + padding: 14px 20px; + background: rgba(0, 0, 0, 0.40); + border-top: 1px solid rgba(255,255,255,0.08); + border-bottom: 1px solid rgba(255,255,255,0.08); + margin-bottom: 16px; display: flex; flex-direction: column; + gap: 8px; } -.price-main { - color: var(--active-color); +.price-row-main { display: flex; align-items: baseline; - gap: 2px; + gap: 8px; } -.price-main .currency { - font-size: 0.9rem; +.price-amount-big { + font-size: 2rem; font-weight: 900; + color: #FEE715; + text-shadow: 0 0 20px rgba(254,231,21,0.4), 0 2px 8px rgba(0,0,0,0.9); + line-height: 1; } -.price-main .amount { - font-size: 1.5rem; - font-weight: 900; +.price-label-big { + font-size: 0.85rem; + color: rgba(255,255,255,0.75); + font-weight: 600; + text-shadow: 0 1px 4px rgba(0,0,0,0.8); } -.price-main .suffix { - font-size: 0.7rem; - color: var(--text-secondary); - margin-left: 4px; +.price-row-secondary { + display: flex; + align-items: center; + gap: 6px; } -.price-sub { - font-size: 0.7rem; - color: var(--text-secondary); +.price-icon-secondary { + font-size: 16px; + color: rgba(255,255,255,0.55); +} + +.price-amount-secondary { + font-size: 1rem; + font-weight: 800; + color: rgba(255,255,255,0.85); + text-shadow: 0 1px 4px rgba(0,0,0,0.8); +} + +.price-label-secondary { + font-size: 0.78rem; + color: rgba(255,255,255,0.55); font-weight: 600; } -.contact-hub { +/* ---- Botones de contacto ---- */ +.contact-buttons { display: flex; gap: 10px; - margin-left: auto; /* Empuja al máximo a la derecha */ + padding: 0 20px 20px; } -.mini-btn { - width: 44px; - height: 44px; - border-radius: 12px; +.contact-btn { + flex: 1; + height: 52px; + border-radius: 16px; display: flex; align-items: center; justify-content: center; - border: none; + gap: 8px; + font-size: 0.95rem; + font-weight: 700; cursor: pointer; - transition: all 0.3s; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); text-decoration: none; + border: none; + letter-spacing: 0.01em; } -.mini-btn.phone { background: rgba(255,255,255,0.1); color: white; border: 1px solid rgba(255,255,255,0.2); } -.mini-btn.wa { background: #25d366; color: white; box-shadow: 0 4px 12px rgba(37, 211, 102, 0.2); } - -.mini-btn:hover { transform: translateY(-3px); } - -.lang-badge { - display: flex; - align-items: center; - gap: 4px; - background: rgba(254, 231, 21, 0.2); - color: var(--active-color); - padding: 2px 8px; - border-radius: 6px; - font-size: 0.6rem; - font-weight: 800; - margin-top: 4px; - width: fit-content; +.btn-call { + background: rgba(255, 255, 255, 0.10); + color: #ffffff; + border: 1.5px solid rgba(255, 255, 255, 0.30); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + text-shadow: 0 1px 4px rgba(0,0,0,0.6); } -.lang-badge .material-icons { font-size: 10px; } +.btn-call:hover { + background: rgba(255, 255, 255, 0.18); + border-color: rgba(255, 255, 255, 0.50); + transform: translateY(-2px); +} + +.btn-call .material-icons { font-size: 20px; } + +.btn-whatsapp { + background: #25d366; + color: #ffffff; + box-shadow: 0 6px 20px rgba(37, 211, 102, 0.35); +} + +.btn-whatsapp:hover { + background: #1ebe5a; + box-shadow: 0 8px 24px rgba(37, 211, 102, 0.50); + transform: translateY(-2px); +} + +.btn-whatsapp .material-icons { font-size: 20px; } /* Original Styles */ .taxi-view {