feat(pacifictrans-go): plataforma web oficial en subdominio pacifictrans.almeira.com.co con Dockerfile y despliegue Git en Coolify
This commit is contained in:
parent
0cb63165cb
commit
8d5ee782ae
@ -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',
|
||||
},
|
||||
|
||||
39
transporte-cali-buenaventura/backend/Dockerfile
Normal file
39
transporte-cali-buenaventura/backend/Dockerfile
Normal file
@ -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"]
|
||||
684
transporte-cali-buenaventura/backend/public/css/style.css
Normal file
684
transporte-cali-buenaventura/backend/public/css/style.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 832 KiB |
491
transporte-cali-buenaventura/backend/public/index.html
Normal file
491
transporte-cali-buenaventura/backend/public/index.html
Normal file
@ -0,0 +1,491 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pacifictrans Go | Transporte Especial & Mensajería Cali - Buenaventura</title>
|
||||
<meta name="description" content="Plataforma tecnológica de transporte especial intermunicipal y mensajería en el corredor Cali - Buenaventura. Agrupación inteligente de 4 pasajeros, Waze TSP y pagos 100% seguros con Bold.">
|
||||
<meta property="og:title" content="Pacifictrans Go - Corredor Cali ↔ Buenaventura">
|
||||
<meta property="og:description" content="Servicio puerta a puerta de 4 cupos y paquetería express. Monitoreo satelital y conductores asignados por cercanía de residencia.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="theme-color" content="#080c14">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- NAVBAR -->
|
||||
<header class="navbar">
|
||||
<a href="#" class="brand">
|
||||
<div class="brand-badge">P</div>
|
||||
<div class="brand-text">
|
||||
<h1 class="brand-title">Pacifictrans <span>Go</span></h1>
|
||||
<span>Cali ↔ Buenaventura</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<nav class="nav-links">
|
||||
<a href="#reservas" class="active">Pasajeros (Pooling)</a>
|
||||
<a href="#fletes">Fletes & Encomiendas</a>
|
||||
<a href="#radar">Radar de Flota</a>
|
||||
<a href="#conductores">Conductores</a>
|
||||
<a href="/PacifictransGo.apk" class="btn-apk-header" id="btn-download-header">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.523 15.3414c-.5511 0-.9993-.4486-.9993-1.0001s.4482-1.0001.9993-1.0001c.552 0 1.0001.4486 1.0001 1.0001s-.4481 1.0001-1.0001 1.0001m-11.046 0c-.5511 0-.9993-.4486-.9993-1.0001s.4482-1.0001.9993-1.0001c.552 0 1.0001.4486 1.0001 1.0001s-.4481 1.0001-1.0001 1.0001m11.4045-6.02l1.997-3.4589c.1408-.2439.0573-.5552-.1866-.696-.2438-.1408-.5551-.0573-.696.1866l-2.0238 3.5054c-1.5037-.6873-3.1979-1.0772-4.9691-1.0772s-3.4654.3899-4.9691 1.0772l-2.0238-3.5054c-.1409-.2439-.4522-.3274-.696-.1866-.2439.1408-.3274.4521-.1866.696l1.997 3.4589c-3.7997 2.0623-6.375 5.9863-6.446 10.5369h24.646c-.071-4.5506-2.6463-8.4746-6.446-10.5369"/>
|
||||
</svg>
|
||||
Descargar APK Android
|
||||
</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- HERO SECTION -->
|
||||
<main>
|
||||
<section class="hero">
|
||||
<div class="hero-content">
|
||||
<div class="hero-pill">
|
||||
<span>⚡ Corredor Exclusivo Especial</span>
|
||||
<span>•</span>
|
||||
<span>100% Prepagado con Bold & Bre-B</span>
|
||||
</div>
|
||||
<h2 class="hero-title">Transporte Puerta a Puerta y Mensajería Express <span>Cali - Buenaventura</span></h2>
|
||||
<p class="hero-desc">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<div class="hero-badges">
|
||||
<div class="badge-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="9" cy="7" r="4"></circle>
|
||||
<path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
|
||||
</svg>
|
||||
<span><strong>4 Cupos</strong> por Vehículo</span>
|
||||
</div>
|
||||
|
||||
<div class="badge-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon>
|
||||
</svg>
|
||||
<span><strong>$35.000 COP</strong> Tarifa Fija</span>
|
||||
</div>
|
||||
|
||||
<div class="badge-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
|
||||
</svg>
|
||||
<span>Seguro Contractual & Bold</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 1rem;">
|
||||
<a href="#reservas" class="btn-primary" style="width: auto; padding: 0.9rem 1.8rem;">
|
||||
Reservar Mi Cupo Ahora
|
||||
</a>
|
||||
<a href="#fletes" class="btn-primary" style="width: auto; padding: 0.9rem 1.8rem; background: rgba(255,255,255,0.06); color: #fff; border: 1px solid var(--border-glass);">
|
||||
Cotizar Flete de Encomienda
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hero-visual">
|
||||
<div class="visual-container">
|
||||
<img src="/images/hero-banner.jpg" alt="Pacifictrans Go Cali Buenaventura" class="hero-image">
|
||||
|
||||
<div class="floating-pool-card">
|
||||
<div class="pool-status-row">
|
||||
<div>
|
||||
<span style="font-size: 0.75rem; text-transform: uppercase; color: var(--accent-cyan); font-weight: 700; letter-spacing: 0.05em;">Agrupación en Vivo</span>
|
||||
<div style="font-weight: 700; font-size: 1.05rem;" id="seat-counter-text">3/4 Pasajeros Confirmados</div>
|
||||
</div>
|
||||
<div class="pool-seats">
|
||||
<div class="seat-dot active">1</div>
|
||||
<div class="seat-dot active">2</div>
|
||||
<div class="seat-dot active">3</div>
|
||||
<div class="seat-dot pending">4</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 0.82rem; color: var(--text-muted); line-height: 1.4;">
|
||||
📍 Recogida en Cali: <strong>Puerta a puerta en su domicilio</strong>.<br>
|
||||
🏁 Llegada en Buenaventura: <strong>Terminal, Muelle Turístico o El Piñal</strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- MAIN PLATFORM APPS / TABS WRAPPER -->
|
||||
<div class="main-wrapper" id="reservas">
|
||||
<div class="tab-nav">
|
||||
<button class="tab-btn active" data-tab="tab-pasajeros" id="tab-btn-pasajeros">
|
||||
👥 1. Reserva Pasajeros (Geo-Pooling)
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="tab-fletes" id="tab-btn-fletes">
|
||||
📦 2. Mensajería & Fletes
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="tab-radar" id="tab-btn-radar">
|
||||
📡 3. Radar de Flota & Despacho
|
||||
</button>
|
||||
<button class="tab-btn" data-tab="tab-conductores" id="tab-btn-conductores">
|
||||
🚗 4. Portal Conductor
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- TAB 1: RESERVA PASAJEROS -->
|
||||
<section class="tab-content active" id="tab-pasajeros">
|
||||
<div class="glass-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Agrupación Geoespacial de 4 Pasajeros</h2>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid-2col">
|
||||
<!-- Formulario de Reserva -->
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="pool-origin">Sentido del Viaje</label>
|
||||
<select class="form-select" id="pool-origin">
|
||||
<option value="CALI">Cali ➔ Buenaventura (Recogida Domicilio)</option>
|
||||
<option value="BUENAVENTURA">Buenaventura ➔ Cali (Puntos Oficiales)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="cali-address-group">
|
||||
<label class="form-label" for="cali-address">Dirección de Recogida en Cali (Domicilio)</label>
|
||||
<input type="text" class="form-input" id="cali-address" placeholder="Ej: Calle 5 #66-20, Barrio Limonar, Cali" value="Calle 9 #42-15, Los Cámbulos, Cali">
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="buena-stop-group">
|
||||
<label class="form-label" for="buena-stop">Parada de Destino en Buenaventura</label>
|
||||
<select class="form-select" id="buena-stop">
|
||||
<option value="TERMINAL">Terminal de Transporte de Buenaventura</option>
|
||||
<option value="MUELLE">Muelle Turístico</option>
|
||||
<option value="PINAL">El Piñal</option>
|
||||
<option value="SENA">Sena Buenaventura</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="grid-2col" style="gap: 1rem; margin-bottom: 0;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="user-name">Nombre Completo</label>
|
||||
<input type="text" class="form-input" id="user-name" placeholder="Tu Nombre" value="Andrea Montoya">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="user-phone">WhatsApp (+57)</label>
|
||||
<input type="tel" class="form-input" id="user-phone" placeholder="3167455877" value="3167455877">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="user-notes">Notas / Equipaje</label>
|
||||
<input type="text" class="form-input" id="user-notes" placeholder="Maletín mediano de 10kg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resumen y Pasarela Bold -->
|
||||
<div class="summary-box">
|
||||
<div>
|
||||
<h3 style="font-size: 1.25rem; color: #fff; margin-bottom: 1rem;">Resumen de la Reserva</h3>
|
||||
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Servicio:</span>
|
||||
<span style="color: #fff; font-weight: 600;">Transporte Especial Intermunicipal</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Capacidad del Vehículo:</span>
|
||||
<span style="color: #fff; font-weight: 600;">Máximo 4 Pasajeros (1 cupo solicitado)</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Recogida Puerta a Puerta:</span>
|
||||
<span style="color: var(--accent-cyan); font-weight: 600;">Incluida en Cali</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Navegación Conductor:</span>
|
||||
<span style="color: #fff; font-weight: 600;">Waze Deep Link con TSP</span>
|
||||
</div>
|
||||
|
||||
<div class="bold-gateway-badge">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<rect x="2" y="5" width="20" height="14" rx="2"></rect>
|
||||
<line x1="2" y1="10" x2="22" y2="10"></line>
|
||||
</svg>
|
||||
<span>Pago 100% Prepagado con <strong>Bold Smart Checkout</strong> (QR Bre-B, Nequi, PSE)</span>
|
||||
</div>
|
||||
|
||||
<!-- ALERTA DE REGLA DE NEGOCIO -->
|
||||
<div style="background: rgba(6, 182, 212, 0.1); border: 1px solid rgba(6, 182, 212, 0.3); border-radius: 8px; padding: 0.85rem; font-size: 0.84rem; color: #cbd5e1; margin-bottom: 1rem;">
|
||||
ℹ️ <strong>Protocolo de Notificación Oficial:</strong>
|
||||
El disparo inmediato de notificación a los 4 pasajeros se ejecuta <strong>únicamente cuando el Administrador asigna el conductor</strong>, garantizando que el vehículo asignado sea el más cercano a su residencia en Cali.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="summary-total">
|
||||
<span style="font-weight: 600; color: #fff;">Tarifa por Pasajero:</span>
|
||||
<span class="total-price">$35.000 COP</span>
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" id="btn-book-pool" style="margin-top: 1rem;">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
|
||||
</svg>
|
||||
Confirmar y Pagar Cupo ($35.000 COP)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alerta tras completar cupo -->
|
||||
<div id="pool-dispatch-alert" style="display: none; margin-top: 2rem; background: rgba(245, 158, 11, 0.12); border: 1px solid rgba(245, 158, 11, 0.4); border-radius: 14px; padding: 1.5rem;">
|
||||
<div style="display: flex; gap: 1rem; align-items: flex-start;">
|
||||
<span style="font-size: 2rem;">🚗💨</span>
|
||||
<div>
|
||||
<h4 style="color: #fbbf24; font-size: 1.15rem; margin-bottom: 0.4rem;">¡Vehículo con 4/4 Cupos Completados!</h4>
|
||||
<p style="color: #cbd5e1; font-size: 0.92rem; margin-bottom: 0.75rem;">
|
||||
El viaje ha cambiado su estado a <code>POOL_COMPLETED</code>. Se ha enviado una alerta inmediata al Administrador para seleccionar el conductor de acuerdo a la cercanía de su residencia en Cali respecto a la primera recogida.
|
||||
</p>
|
||||
<div style="font-size: 0.85rem; color: #94a3b8;">
|
||||
📲 Una vez asignado, se disparará automáticamente el mensaje de WhatsApp a los 4 pasajeros con los datos del conductor y horario exacto de recogida.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- TAB 2: FLETES Y ENCOMIENDAS -->
|
||||
<section class="tab-content" id="tab-fletes">
|
||||
<div class="glass-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Cotizador Inteligente de Mensajería & Fletes</h2>
|
||||
<p>Envíos seguros en el corredor Cali - Buenaventura. Tarifa exacta calculada por distancia, factor de peso/volumen y seguro obligatorio.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid-2col">
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="package-type">Tipo de Encomienda / Paquete</label>
|
||||
<select class="form-select" id="package-type">
|
||||
<option value="1.0">Sobre / Documentos Urgentes (Factor 1.0)</option>
|
||||
<option value="1.2" selected>Paquete Pequeño (hasta 5 kg) - Factor 1.2</option>
|
||||
<option value="1.6">Paquete Mediano (hasta 15 kg) - Factor 1.6</option>
|
||||
<option value="2.2">Carga Grande / Caja Pesada (hasta 30 kg) - Factor 2.2</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="declared-value">Valor Declarado de la Mercancía ($ COP)</label>
|
||||
<input type="number" class="form-input" id="declared-value" placeholder="100000" value="150000">
|
||||
<span style="font-size: 0.75rem; color: var(--text-dim); margin-top: 4px; display: block;">El seguro corresponde al 1% del valor declarado ante extravío o siniestro.</span>
|
||||
</div>
|
||||
|
||||
<div class="grid-2col" style="gap: 1rem;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="sender-phone">Teléfono Remitente (WhatsApp)</label>
|
||||
<input type="tel" class="form-input" id="sender-phone" value="3167455877">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="receiver-phone">Teléfono Destinatario (WhatsApp)</label>
|
||||
<input type="tel" class="form-input" id="receiver-phone" value="3101234567">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="package-desc">Descripción del Contenido</label>
|
||||
<input type="text" class="form-input" id="package-desc" placeholder="Repuestos automotrices / Ropa / Medicamentos">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-box">
|
||||
<div>
|
||||
<h3 style="font-size: 1.25rem; color: #fff; margin-bottom: 1rem;">Fórmula Oficial de Liquidación</h3>
|
||||
<div style="background: rgba(0,0,0,0.4); border-radius: 8px; padding: 0.75rem; font-family: monospace; font-size: 0.8rem; color: #38bdf8; margin-bottom: 1rem;">
|
||||
Tarifa = (Base + [115 Km × $1.200]) × Factor + Seguro (1%)
|
||||
</div>
|
||||
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Tarifa Base Logística:</span>
|
||||
<span style="color: #fff; font-weight: 600;" id="freight-base-cost">$25.000 COP</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Costo por Distancia (115 km):</span>
|
||||
<span style="color: #fff; font-weight: 600;" id="freight-dist-cost">$165.600 COP</span>
|
||||
</div>
|
||||
<div class="summary-item">
|
||||
<span style="color: var(--text-muted);">Seguro de Mercancía:</span>
|
||||
<span style="color: var(--accent-emerald); font-weight: 600;" id="freight-insurance-cost">$1.500 COP</span>
|
||||
</div>
|
||||
|
||||
<div class="bold-gateway-badge">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
<polyline points="12 6 12 12 16 14"></polyline>
|
||||
</svg>
|
||||
<span>100% Prepagado antes del despacho con Bold Smart Checkout</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="summary-total">
|
||||
<span style="font-weight: 600; color: #fff;">Flete Total Liquidado:</span>
|
||||
<span class="total-price" id="freight-total-price">$197.100 COP</span>
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" style="margin-top: 1rem;">
|
||||
Generar Guía y Enlace de Pago Bold
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- TAB 3: RADAR DE FLOTA & DESPACHO -->
|
||||
<section class="tab-content" id="tab-radar">
|
||||
<div class="glass-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Radar Satelital GPS & Asignación por Residencia</h2>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
|
||||
<!-- Radar Visualizer -->
|
||||
<div class="radar-screen" id="radar-canvas-container">
|
||||
<div class="radar-grid"></div>
|
||||
<div class="radar-sweep"></div>
|
||||
|
||||
<div class="corridor-line"></div>
|
||||
<div class="corridor-point cali" title="Cali"></div>
|
||||
<div class="corridor-point dagua" title="Dagua"></div>
|
||||
<div class="corridor-point lobo" title="Loboguerrero"></div>
|
||||
<div class="corridor-point buena" title="Buenaventura"></div>
|
||||
|
||||
<div style="position: absolute; bottom: 15px; left: 20px; font-size: 0.8rem; color: #64748b; font-family: monospace;">
|
||||
🟢 Cali (Km 0) ── 🔵 Dagua (Km 45) ── 🔵 Loboguerrero (Km 60) ── 🟡 Buenaventura (Km 115)
|
||||
</div>
|
||||
|
||||
<div class="radar-vehicle" style="top: 45%; left: 22%;" title="Renault Duster - Carlos Mario">🚗</div>
|
||||
<div class="radar-vehicle" style="top: 48%; left: 52%;" title="Toyota Fortuner - Hernán Darío">🚙</div>
|
||||
<div class="radar-vehicle" style="top: 43%; left: 78%;" title="Chevrolet Tracker - Mauricio V.">🚗</div>
|
||||
</div>
|
||||
|
||||
<!-- Sugerencia de Conductores por Residencia -->
|
||||
<div>
|
||||
<h3 style="font-size: 1.25rem; color: #fff; margin-bottom: 0.5rem;">Conductores Sugeridos para el Viaje Actual</h3>
|
||||
<p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 1.25rem;">
|
||||
Ordenados según la cercanía entre su domicilio registrado en Cali y la 1ª recogida del grupo de pasajeros.
|
||||
</p>
|
||||
|
||||
<div id="suggested-drivers-list">
|
||||
<!-- Renderizado dinámicamente por app.js -->
|
||||
</div>
|
||||
|
||||
<div id="schedule-dispatch-result" style="display: none; margin-top: 1.5rem;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- TAB 4: PORTAL CONDUCTOR -->
|
||||
<section class="tab-content" id="tab-conductores">
|
||||
<div class="glass-panel">
|
||||
<div class="panel-header">
|
||||
<h2>Vinculación Oficial de Conductores</h2>
|
||||
<p>Únete a la flota oficial de Pacifictrans Go. Registra tu vehículo y sube tus documentos para validación administrativa.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid-2col">
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="driver-name">Nombre y Apellidos</label>
|
||||
<input type="text" class="form-input" id="driver-name" placeholder="Nombre del conductor">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="driver-residence">Lugar de Residencia en Cali (Barrio / Comuna)</label>
|
||||
<input type="text" class="form-input" id="driver-residence" placeholder="Ej: Barrio Granada, Comuna 2, Cali">
|
||||
</div>
|
||||
|
||||
<div class="grid-2col" style="gap: 1rem; margin-bottom: 0;">
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="driver-plate">Placa del Vehículo</label>
|
||||
<input type="text" class="form-input" id="driver-plate" placeholder="Ej: WLZ-890">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="driver-model">Marca y Modelo</label>
|
||||
<input type="text" class="form-input" id="driver-model" placeholder="Renault Duster 2024">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">Documentación Obligatoria (Appwrite Storage)</label>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
|
||||
<div style="background: rgba(15, 23, 42, 0.9); border: 1px dashed var(--border-glass); padding: 0.85rem; border-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 0.85rem; color: #cbd5e1;">📄 SOAT Vigente</span>
|
||||
<button style="background: rgba(6, 182, 212, 0.15); border: 1px solid var(--accent-cyan); color: var(--accent-cyan); padding: 4px 10px; border-radius: 4px; font-size: 0.75rem; cursor: pointer;">Subir Archivo</button>
|
||||
</div>
|
||||
|
||||
<div style="background: rgba(15, 23, 42, 0.9); border: 1px dashed var(--border-glass); padding: 0.85rem; border-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 0.85rem; color: #cbd5e1;">📄 Revisión Técnico-Mecánica</span>
|
||||
<button style="background: rgba(6, 182, 212, 0.15); border: 1px solid var(--accent-cyan); color: var(--accent-cyan); padding: 4px 10px; border-radius: 4px; font-size: 0.75rem; cursor: pointer;">Subir Archivo</button>
|
||||
</div>
|
||||
|
||||
<div style="background: rgba(15, 23, 42, 0.9); border: 1px dashed var(--border-glass); padding: 0.85rem; border-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 0.85rem; color: #cbd5e1;">📄 Tarjeta de Operación Especial</span>
|
||||
<button style="background: rgba(6, 182, 212, 0.15); border: 1px solid var(--accent-cyan); color: var(--accent-cyan); padding: 4px 10px; border-radius: 4px; font-size: 0.75rem; cursor: pointer;">Subir Archivo</button>
|
||||
</div>
|
||||
|
||||
<div style="background: rgba(15, 23, 42, 0.9); border: 1px dashed var(--border-glass); padding: 0.85rem; border-radius: 8px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 0.85rem; color: #cbd5e1;">📄 Licencia de Conducción C1 / C2</span>
|
||||
<button style="background: rgba(6, 182, 212, 0.15); border: 1px solid var(--accent-cyan); color: var(--accent-cyan); padding: 4px 10px; border-radius: 4px; font-size: 0.75rem; cursor: pointer;">Subir Archivo</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn-primary" style="margin-top: 1rem;">
|
||||
Enviar Solicitud de Vinculación
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SECCIÓN DESCARGA APK -->
|
||||
<section class="download-card" id="descarga-apk">
|
||||
<div>
|
||||
<span style="color: #22c55e; font-weight: 700; text-transform: uppercase; font-size: 0.8rem; letter-spacing: 0.08em;">App Oficial Android</span>
|
||||
<h3 style="font-size: 2.2rem; color: #fff; margin-top: 0.25rem; margin-bottom: 0.75rem;">Pacifictrans Go Móvil</h3>
|
||||
<p style="color: var(--text-muted); max-width: 500px; font-size: 1rem;">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a href="/PacifictransGo.apk" class="btn-apk-large" id="btn-download-apk-footer">
|
||||
<svg width="28" height="28" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M17.523 15.3414c-.5511 0-.9993-.4486-.9993-1.0001s.4482-1.0001.9993-1.0001c.552 0 1.0001.4486 1.0001 1.0001s-.4481 1.0001-1.0001 1.0001m-11.046 0c-.5511 0-.9993-.4486-.9993-1.0001s.4482-1.0001.9993-1.0001c.552 0 1.0001.4486 1.0001 1.0001s-.4481 1.0001-1.0001 1.0001m11.4045-6.02l1.997-3.4589c.1408-.2439.0573-.5552-.1866-.696-.2438-.1408-.5551-.0573-.696.1866l-2.0238 3.5054c-1.5037-.6873-3.1979-1.0772-4.9691-1.0772s-3.4654.3899-4.9691 1.0772l-2.0238-3.5054c-.1409-.2439-.4522-.3274-.696-.1866-.2439.1408-.3274.4521-.1866.696l1.997 3.4589c-3.7997 2.0623-6.375 5.9863-6.446 10.5369h24.646c-.071-4.5506-2.6463-8.4746-6.446-10.5369"/>
|
||||
</svg>
|
||||
Descargar PacifictransGo.apk (37.1 MB)
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="footer">
|
||||
<div style="max-width: 1280px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 1rem;">
|
||||
<div>
|
||||
<strong style="color: #fff;">Pacifictrans Go</strong> — Transporte Especial Cali - Buenaventura
|
||||
</div>
|
||||
<div>
|
||||
Atención y Soporte WhatsApp: <a href="https://wa.me/573167455877" target="_blank" style="color: var(--accent-cyan); text-decoration: none;">+57 316 745 5877</a>
|
||||
</div>
|
||||
<div>
|
||||
© 2026 Almeira Group. Todos los derechos reservados.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="/js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
195
transporte-cali-buenaventura/backend/public/js/app.js
Normal file
195
transporte-cali-buenaventura/backend/public/js/app.js
Normal file
@ -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 = `
|
||||
<svg class="animate-spin" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" stroke-opacity="0.25"></circle>
|
||||
<path d="M12 2a10 10 0 0 1 10 10" stroke="currentColor"></path>
|
||||
</svg> Procesando Agrupación Geoespacial...
|
||||
`;
|
||||
|
||||
setTimeout(() => {
|
||||
currentSeatsFilled = 4;
|
||||
updateSeatVisuals(currentSeatsFilled);
|
||||
|
||||
if (poolBadge) {
|
||||
poolBadge.innerHTML = `🚨 <strong>¡CUPO COMPLETO (4/4)!</strong> Viaje en estado <code>POOL_COMPLETED</code>. 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) => `
|
||||
<div class="driver-card">
|
||||
<div>
|
||||
<strong style="color: #fff; font-size: 1.05rem;">#${index + 1} ${d.nombre}</strong>
|
||||
<div style="font-size: 0.85rem; color: #94a3b8;">${d.vehiculo}</div>
|
||||
<div style="font-size: 0.82rem; color: #06b6d4; margin-top: 3px;">
|
||||
🏠 Residencia en Cali: <strong>${d.barrio}</strong>
|
||||
</div>
|
||||
<span class="driver-tag">Distancia al 1er Pasajero: ${d.distCali}</span>
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
<span style="display: inline-block; padding: 4px 10px; border-radius: 6px; font-size: 0.75rem; font-weight: 700; background: ${d.estado === 'DISPONIBLE' ? 'rgba(16, 185, 129, 0.2)' : 'rgba(245, 158, 11, 0.2)'}; color: ${d.estado === 'DISPONIBLE' ? '#10b981' : '#f59e0b'};">
|
||||
${d.estado}
|
||||
</span>
|
||||
<button onclick="asignarConductorDemo('${d.nombre}', '${d.barrio}')" style="margin-top: 8px; display: block; background: #0f172a; border: 1px solid rgba(6, 182, 212, 0.4); color: #06b6d4; font-size: 0.8rem; padding: 5px 12px; border-radius: 6px; cursor: pointer;">
|
||||
Asignar y Programar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`).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 = `
|
||||
<div style="background: rgba(16, 185, 129, 0.15); border: 1px solid rgba(16, 185, 129, 0.4); border-radius: 12px; padding: 1.25rem; color: #10b981;">
|
||||
<h4 style="margin-bottom: 0.5rem; font-size: 1.1rem; color: #fff;">✅ Conductor Asignado por Residencia: <strong>${conductor}</strong> (${barrio})</h4>
|
||||
<p style="color: #cbd5e1; font-size: 0.92rem; margin-bottom: 0.75rem;">
|
||||
🚀 <strong>Disparo Inmediato de Notificación WhatsApp Ejecutado:</strong>
|
||||
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.
|
||||
</p>
|
||||
<div style="background: rgba(0,0,0,0.4); padding: 0.75rem; border-radius: 8px; font-family: monospace; font-size: 0.85rem; color: #6ee7b7;">
|
||||
📲 [Evolution API WhatsApp Status: 201 Created] Enviado a los 4 Pasajeros + Conductor.
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
resultDiv.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
@ -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<string, string> = {
|
||||
'.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' });
|
||||
|
||||
78
transporte-cali-buenaventura/docs/COOLIFY_WEB_DEPLOY.md
Normal file
78
transporte-cali-buenaventura/docs/COOLIFY_WEB_DEPLOY.md
Normal file
@ -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`
|
||||
@ -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;
|
||||
|
||||
@ -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() {}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user