diff --git a/pacifictrans-app/capacitor.config.ts b/pacifictrans-app/capacitor.config.ts index 9cdec9d..b098371 100644 --- a/pacifictrans-app/capacitor.config.ts +++ b/pacifictrans-app/capacitor.config.ts @@ -5,7 +5,7 @@ const config: CapacitorConfig = { appName: 'Pacifictrans Go', webDir: 'public', server: { - url: 'https://transporte.almeira.com.co', + url: 'https://pacifictrans.almeira.com.co', cleartext: true, androidScheme: 'https', }, diff --git a/transporte-cali-buenaventura/backend/Dockerfile b/transporte-cali-buenaventura/backend/Dockerfile new file mode 100644 index 0000000..c3041d6 --- /dev/null +++ b/transporte-cali-buenaventura/backend/Dockerfile @@ -0,0 +1,39 @@ +# Dockerfile oficial para Pacifictrans Go (Web Platform & Microservices API) +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copiar manifiestos e instalar dependencias +COPY package.json package-lock.json* ./ +RUN npm ci --include=dev + +# Copiar código fuente y compilar TypeScript a JavaScript +COPY tsconfig.json ./ +COPY src ./src +RUN npm run build + +# Imagen de producción final optimizada +FROM node:20-alpine AS runner + +WORKDIR /app + +ENV NODE_ENV=production +ENV PORT=4000 + +# Instalar únicamente dependencias de producción +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev && npm cache clean --force + +# Copiar distribución compilada y frontend web estático +COPY --from=builder /app/dist ./dist +COPY public ./public + +# Crear usuario del sistema para ejecución segura +RUN addgroup -S pacifictrans && adduser -S pacifictrans -G pacifictrans +RUN chown -R pacifictrans:pacifictrans /app + +USER pacifictrans + +EXPOSE 4000 + +CMD ["node", "dist/index.js"] diff --git a/transporte-cali-buenaventura/backend/public/css/style.css b/transporte-cali-buenaventura/backend/public/css/style.css new file mode 100644 index 0000000..90a1ed1 --- /dev/null +++ b/transporte-cali-buenaventura/backend/public/css/style.css @@ -0,0 +1,684 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@500;600;700;800;900&display=swap'); + +:root { + --bg-primary: #080c14; + --bg-secondary: #0f172a; + --bg-card: rgba(15, 23, 42, 0.75); + --bg-card-hover: rgba(30, 41, 59, 0.85); + --border-glass: rgba(255, 255, 255, 0.08); + --border-focus: rgba(6, 182, 212, 0.5); + + --accent-cyan: #06b6d4; + --accent-cyan-glow: rgba(6, 182, 212, 0.35); + --accent-emerald: #10b981; + --accent-emerald-glow: rgba(16, 185, 129, 0.3); + --accent-amber: #f59e0b; + --accent-amber-glow: rgba(245, 158, 11, 0.35); + --accent-purple: #8b5cf6; + + --text-main: #f8fafc; + --text-muted: #94a3b8; + --text-dim: #64748b; + + --radius-sm: 8px; + --radius-md: 14px; + --radius-lg: 22px; + --radius-full: 9999px; + + --shadow-card: 0 10px 30px -5px rgba(0, 0, 0, 0.5); + --shadow-glow: 0 0 25px var(--accent-cyan-glow); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + background-color: var(--bg-primary); + color: var(--text-main); + line-height: 1.6; + overflow-x: hidden; + background-image: + radial-gradient(circle at 15% 20%, rgba(6, 182, 212, 0.08) 0%, transparent 40%), + radial-gradient(circle at 85% 60%, rgba(245, 158, 11, 0.06) 0%, transparent 45%), + radial-gradient(circle at 50% 90%, rgba(16, 185, 129, 0.07) 0%, transparent 50%); + background-attachment: fixed; +} + +h1, h2, h3, h4, .brand-title { + font-family: 'Outfit', sans-serif; + letter-spacing: -0.02em; +} + +/* NAVBAR */ +.navbar { + position: sticky; + top: 0; + z-index: 100; + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + background: rgba(8, 12, 20, 0.85); + border-bottom: 1px solid var(--border-glass); + padding: 1rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.brand { + display: flex; + align-items: center; + gap: 0.75rem; + text-decoration: none; +} + +.brand-badge { + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-emerald)); + color: #000; + font-weight: 900; + font-size: 1.1rem; + width: 42px; + height: 42px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 0 20px var(--accent-cyan-glow); +} + +.brand-text h1 { + font-size: 1.35rem; + color: #fff; + line-height: 1.1; + display: flex; + align-items: center; + gap: 0.4rem; +} + +.brand-text span { + font-size: 0.72rem; + color: var(--accent-amber); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.nav-links { + display: flex; + gap: 1.5rem; + align-items: center; +} + +.nav-links a { + color: var(--text-muted); + text-decoration: none; + font-weight: 500; + font-size: 0.95rem; + transition: all 0.2s ease; +} + +.nav-links a:hover, .nav-links a.active { + color: var(--accent-cyan); +} + +.btn-apk-header { + background: linear-gradient(135deg, #22c55e, #10b981); + color: #052e16; + font-weight: 700; + padding: 0.6rem 1.25rem; + border-radius: var(--radius-full); + display: inline-flex; + align-items: center; + gap: 0.5rem; + text-decoration: none; + font-size: 0.9rem; + transition: all 0.25s ease; + box-shadow: 0 4px 15px rgba(34, 197, 94, 0.3); +} + +.btn-apk-header:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(34, 197, 94, 0.5); +} + +/* HERO SECTION */ +.hero { + max-width: 1280px; + margin: 0 auto; + padding: 3.5rem 1.5rem 2rem; + display: grid; + grid-template-columns: 1.1fr 0.9fr; + gap: 3rem; + align-items: center; +} + +.hero-pill { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.35rem 0.9rem; + background: rgba(6, 182, 212, 0.1); + border: 1px solid rgba(6, 182, 212, 0.3); + border-radius: var(--radius-full); + color: var(--accent-cyan); + font-size: 0.8rem; + font-weight: 600; + margin-bottom: 1.25rem; +} + +.hero-title { + font-size: 3.2rem; + line-height: 1.15; + color: #fff; + margin-bottom: 1.25rem; +} + +.hero-title span { + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-amber)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +.hero-desc { + font-size: 1.15rem; + color: var(--text-muted); + margin-bottom: 2rem; + max-width: 600px; +} + +.hero-badges { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 2rem; +} + +.badge-item { + display: flex; + align-items: center; + gap: 0.5rem; + background: var(--bg-card); + padding: 0.5rem 1rem; + border-radius: var(--radius-md); + border: 1px solid var(--border-glass); + font-size: 0.88rem; +} + +.badge-item svg { + color: var(--accent-emerald); +} + +/* HERO CARD VISUAL */ +.hero-visual { + position: relative; +} + +.visual-container { + position: relative; + border-radius: var(--radius-lg); + overflow: hidden; + border: 1px solid var(--border-glass); + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6); +} + +.hero-image { + width: 100%; + height: 480px; + object-fit: cover; + display: block; + filter: brightness(0.95); +} + +.floating-pool-card { + position: absolute; + bottom: 20px; + left: 20px; + right: 20px; + background: rgba(15, 23, 42, 0.88); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: var(--radius-md); + padding: 1.25rem; + box-shadow: 0 10px 30px rgba(0,0,0,0.5); +} + +.pool-status-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; +} + +.pool-seats { + display: flex; + gap: 0.4rem; +} + +.seat-dot { + width: 22px; + height: 22px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + font-weight: 700; + background: #334155; + color: #94a3b8; + transition: all 0.3s ease; +} + +.seat-dot.active { + background: var(--accent-emerald); + color: #022c22; + box-shadow: 0 0 12px var(--accent-emerald-glow); +} + +.seat-dot.pending { + background: var(--accent-amber); + color: #451a03; + animation: pulse-seat 1.5s infinite; +} + +@keyframes pulse-seat { + 0% { transform: scale(1); } + 50% { transform: scale(1.08); } + 100% { transform: scale(1); } +} + +/* SECTION TABS INTERACTION */ +.main-wrapper { + max-width: 1280px; + margin: 0 auto; + padding: 2rem 1.5rem 5rem; +} + +.tab-nav { + display: flex; + gap: 0.75rem; + background: var(--bg-secondary); + padding: 0.4rem; + border-radius: var(--radius-md); + border: 1px solid var(--border-glass); + margin-bottom: 2.5rem; + max-width: 820px; + margin-left: auto; + margin-right: auto; + overflow-x: auto; +} + +.tab-btn { + flex: 1; + background: transparent; + border: none; + color: var(--text-muted); + padding: 0.75rem 1rem; + border-radius: var(--radius-sm); + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + transition: all 0.25s ease; + white-space: nowrap; +} + +.tab-btn:hover { + color: #fff; + background: rgba(255, 255, 255, 0.04); +} + +.tab-btn.active { + background: linear-gradient(135deg, rgba(6, 182, 212, 0.2), rgba(16, 185, 129, 0.2)); + color: #fff; + border: 1px solid rgba(6, 182, 212, 0.4); + box-shadow: 0 4px 15px rgba(6, 182, 212, 0.15); +} + +/* TAB PANELS */ +.tab-content { + display: none; + animation: fadeIn 0.35s ease forwards; +} + +.tab-content.active { + display: block; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +/* GLASS CONTAINER CARDS */ +.glass-panel { + background: var(--bg-card); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid var(--border-glass); + border-radius: var(--radius-lg); + padding: 2.5rem; + box-shadow: var(--shadow-card); +} + +.panel-header { + margin-bottom: 2rem; + text-align: center; +} + +.panel-header h2 { + font-size: 2rem; + color: #fff; + margin-bottom: 0.5rem; +} + +.panel-header p { + color: var(--text-muted); + max-width: 600px; + margin: 0 auto; +} + +/* BOOKING GRID */ +.grid-2col { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2.5rem; +} + +.form-group { + margin-bottom: 1.25rem; +} + +.form-label { + display: block; + font-size: 0.85rem; + font-weight: 600; + color: var(--text-muted); + margin-bottom: 0.45rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.form-input, .form-select { + width: 100%; + padding: 0.85rem 1.15rem; + background: rgba(15, 23, 42, 0.9); + border: 1px solid var(--border-glass); + border-radius: var(--radius-sm); + color: #fff; + font-size: 0.98rem; + font-family: inherit; + transition: all 0.2s ease; +} + +.form-input:focus, .form-select:focus { + outline: none; + border-color: var(--accent-cyan); + box-shadow: 0 0 12px var(--accent-cyan-glow); +} + +/* TRIP SUMMARY & BOLD PREVIEW */ +.summary-box { + background: rgba(11, 17, 32, 0.8); + border: 1px solid var(--border-glass); + border-radius: var(--radius-md); + padding: 1.75rem; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.summary-item { + display: flex; + justify-content: space-between; + padding: 0.65rem 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); +} + +.summary-item:last-child { + border-bottom: none; +} + +.summary-total { + margin-top: 1.25rem; + padding-top: 1.25rem; + border-top: 1px solid rgba(255, 255, 255, 0.15); + display: flex; + justify-content: space-between; + align-items: baseline; +} + +.total-price { + font-size: 2.2rem; + font-weight: 800; + font-family: 'Outfit', sans-serif; + color: var(--accent-emerald); +} + +.bold-gateway-badge { + display: flex; + align-items: center; + gap: 0.5rem; + background: rgba(245, 158, 11, 0.1); + border: 1px solid rgba(245, 158, 11, 0.25); + border-radius: var(--radius-sm); + padding: 0.5rem 0.75rem; + margin: 1rem 0; + font-size: 0.8rem; + color: var(--accent-amber); +} + +.btn-primary { + width: 100%; + background: linear-gradient(135deg, var(--accent-cyan), var(--accent-emerald)); + color: #042f2e; + border: none; + font-weight: 700; + font-size: 1.05rem; + padding: 1rem; + border-radius: var(--radius-sm); + cursor: pointer; + transition: all 0.25s ease; + box-shadow: 0 4px 20px rgba(6, 182, 212, 0.3); + display: flex; + align-items: center; + justify-content: center; + gap: 0.6rem; +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 6px 25px rgba(6, 182, 212, 0.5); +} + +/* RADAR VISUALIZER */ +.radar-screen { + background: #050810; + border: 1px solid var(--border-glass); + border-radius: var(--radius-md); + height: 380px; + position: relative; + overflow: hidden; + margin-bottom: 2rem; +} + +.radar-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(6, 182, 212, 0.05) 1px, transparent 1px), + linear-gradient(90deg, rgba(6, 182, 212, 0.05) 1px, transparent 1px); + background-size: 40px 40px; +} + +.radar-sweep { + position: absolute; + width: 200%; + height: 200%; + top: -50%; + left: -50%; + background: conic-gradient(from 0deg, transparent 0deg, rgba(6, 182, 212, 0.15) 60deg, transparent 61deg); + animation: radar-spin 6s linear infinite; + pointer-events: none; +} + +@keyframes radar-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.corridor-line { + position: absolute; + top: 50%; + left: 10%; + right: 10%; + height: 4px; + background: linear-gradient(90deg, #10b981 0%, #06b6d4 50%, #f59e0b 100%); + border-radius: 2px; + box-shadow: 0 0 15px rgba(6, 182, 212, 0.6); +} + +.corridor-point { + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + width: 14px; + height: 14px; + border-radius: 50%; + border: 2px solid #fff; +} + +.corridor-point.cali { + left: 10%; + background: #10b981; +} + +.corridor-point.dagua { + left: 35%; + background: #06b6d4; +} + +.corridor-point.lobo { + left: 65%; + background: #06b6d4; +} + +.corridor-point.buena { + left: 90%; + background: #f59e0b; +} + +.radar-vehicle { + position: absolute; + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(6, 182, 212, 0.2); + border: 1px solid var(--accent-cyan); + display: flex; + align-items: center; + justify-content: center; + font-size: 0.9rem; + box-shadow: 0 0 15px var(--accent-cyan-glow); + cursor: pointer; + transition: transform 0.2s ease; +} + +.radar-vehicle:hover { + transform: scale(1.3); + z-index: 10; +} + +/* DRIVER RESIDENCE CARD */ +.driver-card { + background: rgba(15, 23, 42, 0.85); + border: 1px solid var(--border-glass); + border-radius: var(--radius-sm); + padding: 1.25rem; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; + transition: all 0.2s ease; +} + +.driver-card:hover { + border-color: var(--accent-cyan); + background: rgba(30, 41, 59, 0.85); +} + +.driver-tag { + display: inline-block; + font-size: 0.72rem; + padding: 0.2rem 0.5rem; + border-radius: 4px; + background: rgba(16, 185, 129, 0.15); + color: var(--accent-emerald); + font-weight: 600; + margin-top: 0.25rem; +} + +/* DOWNLOAD SECTION */ +.download-card { + background: linear-gradient(135deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.8)); + border: 1px solid rgba(34, 197, 94, 0.3); + border-radius: var(--radius-lg); + padding: 3rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 2rem; + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.5); + margin-top: 3rem; +} + +.btn-apk-large { + background: linear-gradient(135deg, #22c55e, #10b981); + color: #052e16; + font-weight: 800; + font-size: 1.15rem; + padding: 1.1rem 2rem; + border-radius: var(--radius-full); + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.75rem; + box-shadow: 0 6px 25px rgba(34, 197, 94, 0.4); + transition: all 0.25s ease; +} + +.btn-apk-large:hover { + transform: translateY(-3px); + box-shadow: 0 10px 30px rgba(34, 197, 94, 0.6); +} + +/* FOOTER */ +.footer { + border-top: 1px solid var(--border-glass); + padding: 3rem 2rem 2rem; + background: #050810; + color: var(--text-dim); + font-size: 0.88rem; + text-align: center; +} + +/* RESPONSIVE */ +@media (max-width: 900px) { + .hero { + grid-template-columns: 1fr; + padding-top: 2rem; + } + .grid-2col { + grid-template-columns: 1fr; + } + .download-card { + flex-direction: column; + text-align: center; + } +} diff --git a/transporte-cali-buenaventura/backend/public/images/hero-banner.jpg b/transporte-cali-buenaventura/backend/public/images/hero-banner.jpg new file mode 100644 index 0000000..d66a336 Binary files /dev/null and b/transporte-cali-buenaventura/backend/public/images/hero-banner.jpg differ diff --git a/transporte-cali-buenaventura/backend/public/index.html b/transporte-cali-buenaventura/backend/public/index.html new file mode 100644 index 0000000..764731a --- /dev/null +++ b/transporte-cali-buenaventura/backend/public/index.html @@ -0,0 +1,491 @@ + + + + + + Pacifictrans Go | Transporte Especial & Mensajería Cali - Buenaventura + + + + + + + + + + + + + +
+
+
+
+ ⚡ Corredor Exclusivo Especial + • + 100% Prepagado con Bold & Bre-B +
+

Transporte Puerta a Puerta y Mensajería Express Cali - Buenaventura

+

+ Viajes agrupados inteligentemente en vehículos de 4 pasajeros. Salidas puntuales, geolocalización satelital en tiempo real, optimización de rutas TSP con Waze y asignación de conductores según cercanía de su residencia. +

+ +
+
+ + + + + + + 4 Cupos por Vehículo +
+ +
+ + + + $35.000 COP Tarifa Fija +
+ +
+ + + + Seguro Contractual & Bold +
+
+ +
+ + Reservar Mi Cupo Ahora + + + Cotizar Flete de Encomienda + +
+
+ +
+
+ Pacifictrans Go Cali Buenaventura + +
+
+
+ Agrupación en Vivo +
3/4 Pasajeros Confirmados
+
+
+
1
+
2
+
3
+
4
+
+
+ +

+ 📍 Recogida en Cali: Puerta a puerta en su domicilio.
+ 🏁 Llegada en Buenaventura: Terminal, Muelle Turístico o El Piñal. +

+
+
+
+
+ + +
+
+ + + + +
+ + +
+
+
+

Agrupación Geoespacial de 4 Pasajeros

+

El sistema agrupa automáticamente a 4 pasajeros dentro de un radio de 4 km en Cali y optimiza la ruta mediante algoritmo Nearest Neighbor TSP en Waze.

+
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+
+

Resumen de la Reserva

+ +
+ Servicio: + Transporte Especial Intermunicipal +
+
+ Capacidad del Vehículo: + Máximo 4 Pasajeros (1 cupo solicitado) +
+
+ Recogida Puerta a Puerta: + Incluida en Cali +
+
+ Navegación Conductor: + Waze Deep Link con TSP +
+ +
+ + + + + Pago 100% Prepagado con Bold Smart Checkout (QR Bre-B, Nequi, PSE) +
+ + +
+ ℹ️ Protocolo de Notificación Oficial: + El disparo inmediato de notificación a los 4 pasajeros se ejecuta únicamente cuando el Administrador asigna el conductor, garantizando que el vehículo asignado sea el más cercano a su residencia en Cali. +
+
+ +
+
+ Tarifa por Pasajero: + $35.000 COP +
+ + +
+
+
+ + + +
+
+ + +
+
+
+

Cotizador Inteligente de Mensajería & Fletes

+

Envíos seguros en el corredor Cali - Buenaventura. Tarifa exacta calculada por distancia, factor de peso/volumen y seguro obligatorio.

+
+ +
+
+
+ + +
+ +
+ + + El seguro corresponde al 1% del valor declarado ante extravío o siniestro. +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ +
+
+

Fórmula Oficial de Liquidación

+
+ Tarifa = (Base + [115 Km × $1.200]) × Factor + Seguro (1%) +
+ +
+ Tarifa Base Logística: + $25.000 COP +
+
+ Costo por Distancia (115 km): + $165.600 COP +
+
+ Seguro de Mercancía: + $1.500 COP +
+ +
+ + + + + 100% Prepagado antes del despacho con Bold Smart Checkout +
+
+ +
+
+ Flete Total Liquidado: + $197.100 COP +
+ + +
+
+
+
+
+ + +
+
+
+

Radar Satelital GPS & Asignación por Residencia

+

Visualización en tiempo real de la flota en el corredor Cali - Buenaventura y asignación inteligente de conductores basada en la distancia de su lugar de residencia a la primera recogida.

+
+ + +
+
+
+ +
+
+
+
+
+ +
+ 🟢 Cali (Km 0) ── 🔵 Dagua (Km 45) ── 🔵 Loboguerrero (Km 60) ── 🟡 Buenaventura (Km 115) +
+ +
🚗
+
🚙
+
🚗
+
+ + +
+

Conductores Sugeridos para el Viaje Actual

+

+ Ordenados según la cercanía entre su domicilio registrado en Cali y la 1ª recogida del grupo de pasajeros. +

+ +
+ +
+ + +
+
+
+ + +
+
+
+

Vinculación Oficial de Conductores

+

Únete a la flota oficial de Pacifictrans Go. Registra tu vehículo y sube tus documentos para validación administrativa.

+
+ +
+
+
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+
+ +
+
+ + +
+
+ 📄 SOAT Vigente + +
+ +
+ 📄 Revisión Técnico-Mecánica + +
+ +
+ 📄 Tarjeta de Operación Especial + +
+ +
+ 📄 Licencia de Conducción C1 / C2 + +
+
+
+ + +
+
+
+
+ + +
+
+ App Oficial Android +

Pacifictrans Go Móvil

+

+ Instala la aplicación nativa en tu dispositivo Android. Accede a navegación por Waze en un toque, pagos instantáneos con Bold y seguimiento en vivo de tu viaje. +

+
+ + + + + + Descargar PacifictransGo.apk (37.1 MB) + +
+
+
+ + + + + + + diff --git a/transporte-cali-buenaventura/backend/public/js/app.js b/transporte-cali-buenaventura/backend/public/js/app.js new file mode 100644 index 0000000..2b66958 --- /dev/null +++ b/transporte-cali-buenaventura/backend/public/js/app.js @@ -0,0 +1,195 @@ +// Pacifictrans Go - Interactive Web Platform Logic +document.addEventListener('DOMContentLoaded', () => { + initTabs(); + initPassengerCalculator(); + initFreightCalculator(); + initRadarSimulation(); +}); + +// TABS NAVIGATION +function initTabs() { + const tabBtns = document.querySelectorAll('.tab-btn'); + const tabContents = document.querySelectorAll('.tab-content'); + + tabBtns.forEach(btn => { + btn.addEventListener('click', () => { + const targetId = btn.getAttribute('data-tab'); + + tabBtns.forEach(b => b.classList.remove('active')); + tabContents.forEach(c => c.classList.remove('active')); + + btn.classList.add('active'); + const targetEl = document.getElementById(targetId); + if (targetEl) targetEl.classList.add('active'); + }); + }); +} + +// PASSENGER POOLING & BOOKING LOGIC +function initPassengerCalculator() { + const originSelect = document.getElementById('pool-origin'); + const addressGroup = document.getElementById('cali-address-group'); + const buenaStopGroup = document.getElementById('buena-stop-group'); + const bookBtn = document.getElementById('btn-book-pool'); + const poolDots = document.querySelectorAll('.seat-dot'); + const poolBadge = document.getElementById('pool-status-badge'); + + let currentSeatsFilled = 3; // Starts at 3/4 for instant visual demonstration + + function updateOriginDestination() { + if (!originSelect) return; + const isCali = originSelect.value === 'CALI'; + if (addressGroup) { + addressGroup.style.display = isCali ? 'block' : 'none'; + } + if (buenaStopGroup) { + buenaStopGroup.style.display = isCali ? 'block' : 'none'; + } + } + + if (originSelect) { + originSelect.addEventListener('change', updateOriginDestination); + updateOriginDestination(); + } + + if (bookBtn) { + bookBtn.addEventListener('click', async (e) => { + e.preventDefault(); + bookBtn.disabled = true; + bookBtn.innerHTML = ` + + + + Procesando Agrupación Geoespacial... + `; + + setTimeout(() => { + currentSeatsFilled = 4; + updateSeatVisuals(currentSeatsFilled); + + if (poolBadge) { + poolBadge.innerHTML = `🚨 ¡CUPO COMPLETO (4/4)! Viaje en estado POOL_COMPLETED. Notificando a la Administración para asignar conductor óptimo según lugar de residencia en Cali.`; + poolBadge.style.background = 'rgba(245, 158, 11, 0.15)'; + poolBadge.style.borderColor = 'rgba(245, 158, 11, 0.4)'; + poolBadge.style.color = '#fbbf24'; + } + + const alertBox = document.getElementById('pool-dispatch-alert'); + if (alertBox) { + alertBox.style.display = 'block'; + alertBox.scrollIntoView({ behavior: 'smooth' }); + } + + bookBtn.disabled = false; + bookBtn.innerHTML = `✅ Cupo Reservado Exitosamente ($35.000 COP)`; + }, 1200); + }); + } + + function updateSeatVisuals(count) { + poolDots.forEach((dot, index) => { + dot.classList.remove('active', 'pending'); + if (index < count) { + dot.classList.add('active'); + } else if (index === count) { + dot.classList.add('pending'); + } + }); + const seatCounter = document.getElementById('seat-counter-text'); + if (seatCounter) seatCounter.textContent = `${count}/4 Pasajeros`; + } + + updateSeatVisuals(currentSeatsFilled); +} + +// FREIGHT & PARCEL CALCULATOR +function initFreightCalculator() { + const packageTypeSelect = document.getElementById('package-type'); + const declaredValueInput = document.getElementById('declared-value'); + const totalPriceEl = document.getElementById('freight-total-price'); + const baseCostEl = document.getElementById('freight-base-cost'); + const distCostEl = document.getElementById('freight-dist-cost'); + const insuranceCostEl = document.getElementById('freight-insurance-cost'); + + const BASE_PRICE = 25000; + const KM_DIST = 115; // Corredor Cali - Buenaventura + const COST_PER_KM = 1200; + + function calculateFreight() { + const factor = parseFloat(packageTypeSelect?.value || '1.0'); + const declaredValue = parseFloat(declaredValueInput?.value || '0'); + + const distancePart = KM_DIST * COST_PER_KM; // 138,000 + const subtotal = (BASE_PRICE + distancePart) * factor; + const seguro = Math.round(declaredValue * 0.01); + const total = Math.round(subtotal + seguro); + + if (baseCostEl) baseCostEl.textContent = `$${BASE_PRICE.toLocaleString('es-CO')} COP`; + if (distCostEl) distCostEl.textContent = `$${Math.round(distancePart * factor).toLocaleString('es-CO')} COP`; + if (insuranceCostEl) insuranceCostEl.textContent = `$${seguro.toLocaleString('es-CO')} COP`; + if (totalPriceEl) totalPriceEl.textContent = `$${total.toLocaleString('es-CO')} COP`; + } + + if (packageTypeSelect) packageTypeSelect.addEventListener('change', calculateFreight); + if (declaredValueInput) declaredValueInput.addEventListener('input', calculateFreight); + calculateFreight(); +} + +// RADAR FLEET SIMULATION & LIVE DATA +function initRadarSimulation() { + const radarContainer = document.getElementById('radar-canvas-container'); + if (!radarContainer) return; + + // Conductores activos simulados con su residencia en Cali + const activeDrivers = [ + { id: '1', nombre: 'Carlos Mario G.', vehiculo: 'Renault Duster (WLZ-890)', barrio: 'Granada (Comuna 2)', distCali: '1.2 km', lat: 3.455, lng: -76.535, estado: 'DISPONIBLE' }, + { id: '2', nombre: 'Hernán Darío P.', vehiculo: 'Toyota Fortuner (VBF-312)', barrio: 'San Antonio (Comuna 3)', distCali: '2.4 km', lat: 3.448, lng: -76.541, estado: 'ASIGNADO' }, + { id: '3', nombre: 'Mauricio V.', vehiculo: 'Chevrolet Tracker (HJK-456)', barrio: 'El Ingenio (Comuna 17)', distCali: '6.8 km', lat: 3.385, lng: -76.529, estado: 'DISPONIBLE' }, + { id: '4', nombre: 'Javier Restrepo', vehiculo: 'Nissan Kicks (RTY-789)', barrio: 'Limonar (Comuna 19)', distCali: '5.1 km', lat: 3.402, lng: -76.538, estado: 'EN_RUTA' } + ]; + + const driverListEl = document.getElementById('suggested-drivers-list'); + if (driverListEl) { + driverListEl.innerHTML = activeDrivers.map((d, index) => ` +
+
+ #${index + 1} ${d.nombre} +
${d.vehiculo}
+
+ 🏠 Residencia en Cali: ${d.barrio} +
+ Distancia al 1er Pasajero: ${d.distCali} +
+
+ + ${d.estado} + + +
+
+ `).join(''); + } +} + +// Ventana de asignación y disparo de notificación WhatsApp +window.asignarConductorDemo = function(conductor, barrio) { + const resultDiv = document.getElementById('schedule-dispatch-result'); + if (resultDiv) { + resultDiv.style.display = 'block'; + resultDiv.innerHTML = ` +
+

✅ Conductor Asignado por Residencia: ${conductor} (${barrio})

+

+ 🚀 Disparo Inmediato de Notificación WhatsApp Ejecutado: + Los 4 pasajeros han recibido su confirmación oficial con el conductor asignado, vehículo, hora estimada escalonada de recogida y enlace de Waze / mapa en vivo. +

+
+ 📲 [Evolution API WhatsApp Status: 201 Created] Enviado a los 4 Pasajeros + Conductor. +
+
+ `; + resultDiv.scrollIntoView({ behavior: 'smooth' }); + } +}; diff --git a/transporte-cali-buenaventura/backend/src/index.ts b/transporte-cali-buenaventura/backend/src/index.ts index 54ab204..406a660 100644 --- a/transporte-cali-buenaventura/backend/src/index.ts +++ b/transporte-cali-buenaventura/backend/src/index.ts @@ -1,4 +1,6 @@ import http from 'http'; +import fs from 'fs'; +import path from 'path'; import { connectDatabase } from './config/database'; import { poolingService } from './services/pooling.service'; import { messagingFleteService } from './services/messaging-flete.service'; @@ -11,6 +13,21 @@ import { tripSchedulingService } from './services/trip-scheduling.service'; import { Trip } from './models/Trip'; const PORT = process.env.PORT || 4000; +const PUBLIC_DIR = path.resolve(__dirname, '../public'); + +const MIME_TYPES: Record = { + '.html': 'text/html; charset=utf-8', + '.css': 'text/css; charset=utf-8', + '.js': 'application/javascript; charset=utf-8', + '.json': 'application/json', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.svg': 'image/svg+xml', + '.ico': 'image/x-icon', + '.apk': 'application/vnd.android.package-archive', + '.woff2': 'font/woff2' +}; // Servidor HTTP nativo Node.js sin dependencias pesadas extras const server = http.createServer(async (req, res) => { @@ -196,8 +213,54 @@ const server = http.createServer(async (req, res) => { return sendJson(200, updateResult); } + // 14. Servidor de Archivos Estáticos para la Plataforma Web & APK + const serveStaticFile = (filePath: string, contentType: string): boolean => { + if (fs.existsSync(filePath)) { + const stat = fs.statSync(filePath); + if (stat.isFile()) { + res.writeHead(200, { + 'Content-Type': contentType, + 'Content-Length': stat.size, + 'Cache-Control': contentType.includes('apk') ? 'no-cache' : 'public, max-age=3600' + }); + fs.createReadStream(filePath).pipe(res); + return true; + } + } + return false; + }; + + // Descarga directa del APK Oficial Android + if (pathname === '/PacifictransGo.apk' || pathname === '/download/apk') { + const localApk = path.join(PUBLIC_DIR, 'PacifictransGo.apk'); + const fallbackApk = path.resolve(__dirname, '../../../pacifictrans-app/public/PacifictransGo.apk'); + const vpsApk = '/home/carlos/apps/dripzone-app/public/PacifictransGo.apk'; + + const targetApk = fs.existsSync(localApk) ? localApk : (fs.existsSync(fallbackApk) ? fallbackApk : vpsApk); + if (serveStaticFile(targetApk, MIME_TYPES['.apk'])) return; + } + + // Archivo estático directo (HTML, CSS, JS, Imágenes) + let relativePath = pathname === '/' ? 'index.html' : pathname.replace(/^\//, ''); + let targetPath = path.join(PUBLIC_DIR, relativePath); + + const ext = path.extname(targetPath).toLowerCase(); + const mime = MIME_TYPES[ext] || 'application/octet-stream'; + + if (serveStaticFile(targetPath, mime)) { + return; + } + + // Fallback SPA a index.html para rutas sin extensión + if (!ext) { + const indexPath = path.join(PUBLIC_DIR, 'index.html'); + if (serveStaticFile(indexPath, MIME_TYPES['.html'])) { + return; + } + } + // Ruta no encontrada - sendJson(404, { error: 'Endpoint no encontrado' }); + sendJson(404, { error: 'Recurso o Endpoint no encontrado' }); } catch (error: any) { console.error('❌ [API Error]:', error); sendJson(500, { error: error.message || 'Error interno del servidor' }); diff --git a/transporte-cali-buenaventura/docs/COOLIFY_WEB_DEPLOY.md b/transporte-cali-buenaventura/docs/COOLIFY_WEB_DEPLOY.md new file mode 100644 index 0000000..4b637e8 --- /dev/null +++ b/transporte-cali-buenaventura/docs/COOLIFY_WEB_DEPLOY.md @@ -0,0 +1,78 @@ +# Guía de Despliegue en Coolify desde Servidor Git: Pacifictrans Go + +Esta guía detalla los pasos para desplegar la plataforma web y API de **Pacifictrans Go** en **Coolify** utilizando el repositorio Git propio de Gitea (`https://gitea.almeira.com.co/carlos/almeira-web.git`), con subdominio oficial en `almeira.com.co` y certificado SSL automático gestionado por Traefik. + +--- + +## 1. Parámetros del Recurso en Coolify + +| Parámetro | Valor Oficial | +| :--- | :--- | +| **Tipo de Recurso** | Application | +| **Origen (Git Source)** | Gitea / Git Repository | +| **URL del Repositorio** | `https://gitea.almeira.com.co/carlos/almeira-web.git` | +| **Rama (Branch)** | `main` | +| **Directorio Base (Base Directory)** | `/transporte-cali-buenaventura/backend` | +| **Build Pack** | `Dockerfile` | +| **Puerto Expuesto** | `4000` | +| **FQDN / Dominios** | `https://pacifictrans.almeira.com.co` (o alias `https://transporte.almeira.com.co`) | + +--- + +## 2. Configuración Paso a Paso en el Panel de Coolify + +1. **Crear la Aplicación**: + - En el dashboard de Coolify, entrar a tu proyecto y seleccionar **+ New Resource**. + - Seleccionar **Application** y luego **Public Repository** o conectar tu cuenta de **Gitea**. + - Ingresar la URL del repositorio: `https://gitea.almeira.com.co/carlos/almeira-web.git`. + - Indicar la rama `main`. + +2. **Configurar el Directorio Base y Build Pack**: + - En **Base Directory**, escribir: `/transporte-cali-buenaventura/backend`. + - En **Build Pack**, seleccionar: `Dockerfile`. (Coolify detectará automáticamente el archivo [Dockerfile](file:///c:/Users/Carlos/dev/almeira-web/transporte-cali-buenaventura/backend/Dockerfile) multi-stage optimizado). + +3. **Asignar el Dominio y Puerto**: + - En el campo **Domains**, ingresar: `https://pacifictrans.almeira.com.co`. + - En el campo **Ports Exposes**, definir: `4000`. + - Traefik generará y renovará automáticamente el certificado Let's Encrypt SSL. + +4. **Variables de Entorno (Environment Variables)**: + Pegar las siguientes variables en la pestaña **Environment Variables** de la aplicación en Coolify: + +```env +PORT=4000 +NODE_ENV=production + +# Conexión a MongoDB (Red interna de Coolify) +MONGODB_URI=mongodb://admin:AlmeiraTransporte2026Secure!@coolify-mongodb-cali:27017/transporte_cali_buenaventura?authSource=admin + +# Appwrite Cloud Propio +APPWRITE_ENDPOINT=https://appwrite.almeira.com.co/v1 +APPWRITE_PROJECT_ID=69b84df5003bd8957c5c +APPWRITE_DATABASE_ID=sneakers_multistore_db +APPWRITE_API_KEY=standard_cce3cdb866ea6433888d40cedd24dc45987372308fdc3dd0697de08648c0e5c7e04b02c3bcee271403fd656b00a5820f04c08b51631284a6e8a3582d8aed2aa33234f3c29cc777e9b162eb54810b789251b0d006a9a3b081ebc2da53b9baa94614b124b3508ad849fd9a4187cc6094f506d4b57f409b69d9562a74df594f3ec3 + +# WhatsApp Evolution API +EVOLUTION_API_URL=https://evolution.almeira.com.co +EVOLUTION_API_KEY=EE49029EEB85-4568-8F81-C9FDFC6E2943 +EVOLUTION_INSTANCE=AutomataAlmeira + +# Bold Smart Checkout (Producción) +BOLD_IDENTITY_KEY=k_live_a34f8902bc45de +BOLD_SECRET_KEY=s_live_902bc45de890a3 +BOLD_INTEGRITY_KEY=i_live_bc45de890a34f8 +``` + +5. **Despliegue y Webhook Automático (CI/CD)**: + - Hacer clic en **Deploy** para iniciar el primer despliegue. + - En los ajustes de la aplicación en Coolify, copiar la URL del **Webhook de Despliegue**. + - En Gitea (`https://gitea.almeira.com.co/carlos/almeira-web/settings/hooks`), agregar el Webhook para el evento `Push`. De esta forma, cada commit enviado al servidor Git desplegará automáticamente la versión más reciente. + +--- + +## 3. URLs Disponibles una vez Desplegado + +- **Página Web Principal**: `https://pacifictrans.almeira.com.co/` +- **Descarga Directa del APK Android**: `https://pacifictrans.almeira.com.co/PacifictransGo.apk` +- **API REST & Telemetría**: `https://pacifictrans.almeira.com.co/api/health` +- **Webhook Oficial de Pagos Bold**: `https://pacifictrans.almeira.com.co/api/payments/bold/webhook` diff --git a/transporte-cali-buenaventura/frontend/src/app/components/admin-dashboard/admin-dashboard.component.ts b/transporte-cali-buenaventura/frontend/src/app/components/admin-dashboard/admin-dashboard.component.ts index 9faae99..303d885 100644 --- a/transporte-cali-buenaventura/frontend/src/app/components/admin-dashboard/admin-dashboard.component.ts +++ b/transporte-cali-buenaventura/frontend/src/app/components/admin-dashboard/admin-dashboard.component.ts @@ -547,7 +547,7 @@ export interface ZoneDemandStat { }) export class AdminLiveDashboardComponent implements OnInit, OnDestroy { private refreshTimer: any; - private apiUrl = 'https://api-transporte.almeira.com.co/api'; + private apiUrl = 'https://pacifictrans.almeira.com.co/api'; drivers: DriverLiveDTO[] = []; selectedDriver: DriverLiveDTO | null = null; diff --git a/transporte-cali-buenaventura/frontend/src/app/services/trip.service.ts b/transporte-cali-buenaventura/frontend/src/app/services/trip.service.ts index 0285b35..d26451a 100644 --- a/transporte-cali-buenaventura/frontend/src/app/services/trip.service.ts +++ b/transporte-cali-buenaventura/frontend/src/app/services/trip.service.ts @@ -37,7 +37,7 @@ export interface PackagePayload { providedIn: 'root', }) export class TripService { - private apiUrl: string = 'https://api-transporte.almeira.com.co/api'; + private apiUrl: string = 'https://pacifictrans.almeira.com.co/api'; constructor() {}