- App.tsx: full navigation (Auth stack + Main tabs with 5 screens) - Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen - HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors - MapScreen: interactive map with layer toggles (6 layers) - MarketplaceScreen: categories (6), products (5), search - ChatScreen: AI chat with quick prompts (4), bot responses - ProfileScreen: user info, stats, menu (9 items), logout - AlertsScreen: alert list with severity, acknowledge - SensorsScreen: sensor list with type filters (6 types), search - ZonesScreen: zone cards with stats - SettingsScreen: language picker (FR/EN/ES/DE), privacy, about - Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n - Hooks: useSensors, useAlerts, useNotifications, useLocation - Components: Card, Button, LoadingSpinner, ErrorBoundary, Header - Services: iotService, notificationService (with axios API client) - Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN) - Theme: colors.ts with full design system (Blue Ocean palette) - Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
180 lines
8.8 KiB
HTML
180 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Smart App City — 10 Transport (Blue v2)</title>
|
|
<link rel="stylesheet" href="shared-blue.css">
|
|
<style>
|
|
.transport-header {
|
|
background: linear-gradient(135deg, #1565C0, #0288D1);
|
|
padding: 16px; color: white;
|
|
}
|
|
.transport-title { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
|
|
.transport-sub { font-size: 13px; opacity: 0.8; }
|
|
|
|
.line-card {
|
|
background: white; border-radius: 16px; padding: 16px;
|
|
margin: 12px 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
|
border: 1px solid var(--neutral-100);
|
|
}
|
|
.line-header { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
|
|
.line-badge {
|
|
width: 40px; height: 40px; border-radius: 12px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 18px; font-weight: 700; color: white;
|
|
}
|
|
.line-name { font-size: 16px; font-weight: 700; }
|
|
.line-route { font-size: 12px; color: var(--neutral-500); }
|
|
.line-status { margin-left: auto; text-align: right; }
|
|
.status-ontime { color: var(--alert-success); font-size: 12px; font-weight: 600; }
|
|
.status-delayed { color: var(--alert-warning); font-size: 12px; font-weight: 600; }
|
|
.next-arrivals { display: flex; gap: 8px; }
|
|
.arrival-chip {
|
|
background: var(--primary-50); color: var(--primary-600);
|
|
padding: 4px 10px; border-radius: 8px; font-size: 12px; font-weight: 600;
|
|
}
|
|
.arrival-chip.soon { background: var(--alert-danger); color: white; }
|
|
|
|
.map-transport {
|
|
margin: 0 16px 12px; height: 180px; border-radius: 16px;
|
|
background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
|
|
position: relative; overflow: hidden;
|
|
}
|
|
|
|
.nearby-stops { padding: 0 16px 16px; }
|
|
.stop-item {
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 12px 0; border-bottom: 1px solid var(--neutral-100);
|
|
}
|
|
.stop-icon {
|
|
width: 36px; height: 36px; border-radius: 50%;
|
|
background: #E3F2FD; display: flex; align-items: center; justify-content: center;
|
|
font-size: 16px;
|
|
}
|
|
.stop-info { flex: 1; }
|
|
.stop-name { font-size: 14px; font-weight: 600; }
|
|
.stop-lines { font-size: 11px; color: var(--neutral-500); }
|
|
.stop-distance { font-size: 12px; color: var(--neutral-400); }
|
|
|
|
.ticket-card {
|
|
margin: 0 16px 12px;
|
|
background: linear-gradient(135deg, #1565C0, #3949AB);
|
|
border-radius: 16px; padding: 16px; color: white;
|
|
}
|
|
.ticket-title { font-size: 14px; font-weight: 700; margin-bottom: 8px; }
|
|
.ticket-options { display: flex; gap: 8px; }
|
|
.ticket-option {
|
|
background: rgba(255,255,255,0.15); border-radius: 10px;
|
|
padding: 8px 12px; text-align: center; flex: 1;
|
|
}
|
|
.ticket-price { font-size: 18px; font-weight: 700; }
|
|
.ticket-type { font-size: 10px; opacity: 0.7; }
|
|
/* Fixed viewport for PDF/iframe embedding */
|
|
html, body { width: 390px !important; height: 844px !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="mobile-frame">
|
|
<div class="status-bar"><span>9:41</span></div>
|
|
|
|
<div class="transport-header">
|
|
<div class="transport-title">🚌 Transport en commun</div>
|
|
<div class="transport-sub">Martinique · Temps réel</div>
|
|
</div>
|
|
|
|
<div class="content-area" style="top:80px;background:var(--neutral-50);">
|
|
|
|
<!-- Map -->
|
|
<div class="map-transport">
|
|
<svg width="100%" height="100%" viewBox="0 0 360 180">
|
|
<rect width="360" height="180" fill="#E3F2FD"/>
|
|
<!-- Roads -->
|
|
<path d="M20 90 Q100 70 180 80 Q260 90 340 75" stroke="white" stroke-width="4" fill="none" opacity="0.6"/>
|
|
<path d="M50 40 Q80 80 70 120 Q60 150 80 170" stroke="white" stroke-width="3" fill="none" opacity="0.5"/>
|
|
<path d="M200 30 Q220 70 230 110 Q240 150 260 170" stroke="white" stroke-width="3" fill="none" opacity="0.5"/>
|
|
<!-- Bus stops -->
|
|
<circle cx="60" cy="85" r="6" fill="#1565C0" stroke="white" stroke-width="2"/>
|
|
<circle cx="120" cy="78" r="6" fill="#1565C0" stroke="white" stroke-width="2"/>
|
|
<circle cx="180" cy="80" r="6" fill="#1565C0" stroke="white" stroke-width="2"/>
|
|
<circle cx="250" cy="82" r="6" fill="#1565C0" stroke="white" stroke-width="2"/>
|
|
<circle cx="310" cy="76" r="6" fill="#1565C0" stroke="white" stroke-width="2"/>
|
|
<!-- Bus icon -->
|
|
<rect x="145" y="68" width="24" height="14" rx="3" fill="#00ACC1" stroke="white" stroke-width="1.5"/>
|
|
<text x="157" y="79" font-size="8" fill="white" text-anchor="middle">🚌</text>
|
|
</svg>
|
|
</div>
|
|
|
|
<!-- Line A1 -->
|
|
<div class="line-card">
|
|
<div class="line-header">
|
|
<div class="line-badge" style="background:linear-gradient(135deg,#1565C0,#42A5F5);">A1</div>
|
|
<div><div class="line-name">Ligne A1</div><div class="line-route">FDF Centre ↔ Trinité</div></div>
|
|
<div class="line-status"><div class="status-ontime">● À l'heure</div><div style="font-size:10px;color:var(--neutral-400);">Toutes les 15 min</div></div>
|
|
</div>
|
|
<div class="next-arrivals">
|
|
<div class="arrival-chip soon">3 min</div>
|
|
<div class="arrival-chip">18 min</div>
|
|
<div class="arrival-chip">33 min</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Line B2 -->
|
|
<div class="line-card">
|
|
<div class="line-header">
|
|
<div class="line-badge" style="background:linear-gradient(135deg,#00838F,#26C6DA);">B2</div>
|
|
<div><div class="line-name">Ligne B2</div><div class="line-route">Schœlcher ↔ Lamentin</div></div>
|
|
<div class="line-status"><div class="status-delayed">⚠ Retard 5 min</div><div style="font-size:10px;color:var(--neutral-400);">Toutes les 20 min</div></div>
|
|
</div>
|
|
<div class="next-arrivals">
|
|
<div class="arrival-chip">8 min</div>
|
|
<div class="arrival-chip">28 min</div>
|
|
<div class="arrival-chip">48 min</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tickets -->
|
|
<div class="ticket-card">
|
|
<div class="ticket-title">🎫 Titres de transport</div>
|
|
<div class="ticket-options">
|
|
<div class="ticket-option"><div class="ticket-price">1.50€</div><div class="ticket-type">Trajet simple</div></div>
|
|
<div class="ticket-option"><div class="ticket-price">4.50€</div><div class="ticket-type">Carnet 4 trajets</div></div>
|
|
<div class="ticket-option"><div class="ticket-price">35€</div><div class="ticket-type">Mois illimité</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Nearby stops -->
|
|
<div style="padding:0 16px 8px; font-size:16px; font-weight:700;">Arrêts proches 📍</div>
|
|
<div class="nearby-stops">
|
|
<div class="stop-item">
|
|
<div class="stop-icon">🚏</div>
|
|
<div class="stop-info"><div class="stop-name">Gare Maritime</div><div class="stop-lines">A1 · B2 · C3</div></div>
|
|
<div class="stop-distance">120 m</div>
|
|
</div>
|
|
<div class="stop-item">
|
|
<div class="stop-icon">🚏</div>
|
|
<div class="stop-info"><div class="stop-name">Place Perrinon</div><div class="stop-lines">A1 · C3</div></div>
|
|
<div class="stop-distance">340 m</div>
|
|
</div>
|
|
<div class="stop-item">
|
|
<div class="stop-icon">🚏</div>
|
|
<div class="stop-info"><div class="stop-name">Campus Schoelcher</div><div class="stop-lines">B2 · D4</div></div>
|
|
<div class="stop-distance">520 m</div>
|
|
</div>
|
|
</div>
|
|
<div style="height:16px;"></div>
|
|
</div>
|
|
|
|
<!-- Bottom Nav -->
|
|
<div class="bottom-nav">
|
|
<div class="bottom-nav-item active"><svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22" fill="none" stroke="currentColor" stroke-width="2"/></svg><span>Accueil</span></div>
|
|
<div class="bottom-nav-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg><span>Carte</span></div>
|
|
<div class="bottom-nav-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg><span>AI Chat</span></div>
|
|
<div class="bottom-nav-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg><span>Marché</span></div>
|
|
<div class="bottom-nav-item"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg><span>Profil</span></div>
|
|
</div>
|
|
<div style="position:absolute;bottom:88px;left:0;right:0;text-align:center;font-size:10px;color:var(--neutral-400);">10 — Transport · Blue v2</div>
|
|
</div>
|
|
</body>
|
|
</html>
|