Files
smart-city-digital-twin-mar…/smart-app-city/design/03-carte-interactive.html
Eric FELIXINE e30ae8ed09 feat(smart-app): implement complete mobile app MVP
- 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
2026-06-01 18:00:35 -04:00

238 lines
11 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 — 03 Carte Interactive</title>
<link rel="stylesheet" href="shared.css">
<style>
.map-container {
position: absolute;
top: 44px; bottom: 80px; left: 0; right: 0;
background: #E8F5E9;
overflow: hidden;
}
.map-toolbar {
position: absolute;
top: 12px; left: 12px; right: 12px;
z-index: 20;
display: flex;
gap: 8px;
align-items: center;
}
.map-search {
flex: 1;
background: white;
border-radius: var(--radius-full);
padding: 10px 16px;
display: flex; align-items: center; gap: 8px;
box-shadow: var(--shadow-md);
font-size: var(--text-sm);
color: var(--neutral-500);
}
.map-fab {
width: 44px; height: 44px;
border-radius: var(--radius-full);
background: white;
box-shadow: var(--shadow-md);
display: flex; align-items: center; justify-content: center;
cursor: pointer;
font-size: 20px;
}
.map-overlay-info {
position: absolute;
bottom: 16px; left: 12px; right: 12px;
z-index: 20;
}
sensor-marker {
position: absolute;
cursor: pointer;
transition: transform 0.2s;
}
.sensor-marker:hover { transform: scale(1.2); }
.legend {
position: absolute;
top: 70px; right: 12px;
z-index: 20;
background: white;
border-radius: var(--radius-lg);
padding: 12px;
box-shadow: var(--shadow-md);
font-size: var(--text-xs);
}
.legend-item {
display: flex; align-items: center; gap: 6px;
margin-bottom: 6px;
}
.legend-dot {
width: 10px; height: 10px;
border-radius: 50%;
}
.filter-chips {
position: absolute;
top: 68px; left: 12px;
z-index: 20;
display: flex; gap: 6px;
}
/* Fixed viewport for PDF/iframe embedding */
html, body { width: 390px !important; height: 844px !important; overflow: hidden !important; margin: 0 !important; padding: 0 !important; }
</style>
<style>
/* 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">
<!-- Status Bar -->
<div class="status-bar">
<span>9:41</span>
<div style="display:flex;gap:4px;align-items:center;">
<svg width="16" height="12" viewBox="0 0 16 12"><rect x="0" y="4" width="3" height="8" rx="1" fill="#333"/><rect x="4.5" y="2.5" width="3" height="9.5" rx="1" fill="#333"/><rect x="9" y="0" width="3" height="12" rx="1" fill="#333"/><rect x="13.5" y="0" width="2.5" height="12" rx="1" fill="#ccc"/></svg>
</div>
</div>
<!-- Map -->
<div class="map-container">
<svg width="100%" height="100%" viewBox="0 0 390 580" preserveAspectRatio="xMidYMid slice">
<!-- Ocean -->
<rect width="390" height="580" fill="#D4EEF7"/>
<!-- Martinique island shape -->
<path d="M60 420 Q80 350 130 310 Q170 275 220 265 Q270 255 310 280 Q340 300 350 340 Q360 370 340 400 Q320 430 280 440 Q240 455 200 445 Q160 440 120 435 Q80 435 60 420Z" fill="#C8E6C9" stroke="#81C784" stroke-width="1.5"/>
<!-- Interior details -->
<path d="M120 340 Q140 320 170 315 Q200 310 230 320" stroke="#A5D6A7" stroke-width="1" fill="none" stroke-dasharray="4 2"/>
<path d="M150 380 Q170 365 200 360 Q230 355 255 365" stroke="#A5D6A7" stroke-width="1" fill="none" stroke-dasharray="4 2"/>
<!-- Roads -->
<path d="M130 310 Q150 330 160 360 Q165 380 155 400" stroke="white" stroke-width="2.5" fill="none" opacity="0.7"/>
<path d="M220 265 Q230 290 235 320 Q238 350 240 380" stroke="white" stroke-width="2.5" fill="none" opacity="0.7"/>
<path d="M170 315 Q190 325 210 330 Q230 335 255 340" stroke="white" stroke-width="2" fill="none" opacity="0.5"/>
<path d="M280 290 Q270 310 265 340 Q262 365 260 390" stroke="white" stroke-width="2" fill="none" opacity="0.5"/>
<!-- Zone labels -->
<text x="155" y="295" font-size="10" fill="#2E7D32" font-weight="600" text-anchor="middle">Fort-de-France</text>
<text x="260" y="320" font-size="9" fill="#388E3C" font-weight="500" text-anchor="middle">Schœlcher</text>
<text x="120" y="370" font-size="8" fill="#558B2F" text-anchor="middle">Lamentin</text>
<text x="200" y="420" font-size="8" fill="#558B2F" text-anchor="middle">Ducos</text>
<!-- Parks/green areas -->
<circle cx="180" cy="340" r="15" fill="#A5D6A7" opacity="0.4"/>
<circle cx="250" cy="370" r="12" fill="#A5D6A7" opacity="0.3"/>
</svg>
<!-- Sensor markers on map -->
<div style="position:absolute;top:35%;left:38%;z-index:10;">
<div style="width:14px;height:14px;background:#2E7D32;border-radius:50%;border:2px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);position:relative;">
<div style="position:absolute;top:-30px;left:50%;transform:translateX(-50%);background:#2E7D32;color:white;font-size:9px;padding:3px 8px;border-radius:6px;white-space:nowrap;font-weight:600;">🌡️ 24.3°</div>
</div>
</div>
<div style="position:absolute;top:42%;left:58%;z-index:10;">
<div style="width:14px;height:14px;background:#00ACC1;border-radius:50%;border:2px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);"></div>
</div>
<div style="position:absolute;top:50%;left:45%;z-index:10;">
<div style="width:14px;height:14px;background:#FF6D00;border-radius:50%;border:2px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);"></div>
</div>
<div style="position:absolute;top:38%;left:30%;z-index:10;">
<div style="width:14px;height:14px;background:#7C4DFF;border-radius:50%;border:2px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);"></div>
</div>
<div style="position:absolute;top:55%;left:62%;z-index:10;">
<div style="width:12px;height:12px;background:#D32F2F;border-radius:50%;border:2px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);"></div>
</div>
<div style="position:absolute;top:46%;left:52%;z-index:10;">
<div style="width:12px;height:12px;background:#2E7D32;border-radius:50%;border:2px solid white;box-shadow:0 2px 6px rgba(0,0,0,0.3);"></div>
</div>
<!-- User location -->
<div style="position:absolute;top:44%;left:42%;z-index:15;">
<div style="width:20px;height:20px;background:#1565C0;border-radius:50%;border:3px solid white;box-shadow:0 0 0 4px rgba(21,101,192,0.2), 0 2px 8px rgba(0,0,0,0.3);"></div>
</div>
<!-- Toolbar -->
<div class="map-toolbar">
<div class="map-search">
<svg class="icon-sm" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Rechercher un lieu...
</div>
<div class="map-fab">📍</div>
<div class="map-fab">🔄</div>
</div>
<!-- Filter chips -->
<div class="filter-chips">
<div class="chip" style="background:var(--primary-500);color:white;border:none;">Tous</div>
<div class="chip">🌡️ Temp</div>
<div class="chip">💨 Air</div>
<div class="chip">💡 Lumière</div>
</div>
<!-- Legend -->
<div class="legend">
<div style="font-weight:600;margin-bottom:8px;font-size:11px;">Légende</div>
<div class="legend-item"><div class="legend-dot" style="background:#2E7D32"></div> Température</div>
<div class="legend-item"><div class="legend-dot" style="background:#00ACC1"></div> Qualité air</div>
<div class="legend-item"><div class="legend-dot" style="background:#FF6D00"></div> Luminosité</div>
<div class="legend-item"><div class="legend-dot" style="background:#7C4DFF"></div> Humidité</div>
<div class="legend-item"><div class="legend-dot" style="background:#D32F2F"></div> Alerte</div>
</div>
<!-- Bottom info card -->
<div class="map-overlay-info">
<div class="card" style="padding:14px;">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<div style="font-weight:700;font-size:var(--text-base);">Capteur sélectionné</div>
<div style="font-size:var(--text-sm);color:var(--neutral-500);">Temp-001 · Fort-de-France Centre</div>
</div>
<div style="text-align:right;">
<div style="font-size:var(--text-xl);font-weight:700;color:var(--primary-500);">24.3°C</div>
<div style="font-size:10px;color:var(--alert-success);font-weight:600;">● En ligne</div>
</div>
</div>
<div style="display:flex;gap:8px;margin-top:10px;">
<div style="flex:1;background:var(--neutral-50);border-radius:8px;padding:8px;text-align:center;">
<div style="font-size:var(--text-sm);font-weight:700;color:var(--ocean-500);">42%</div>
<div style="font-size:9px;color:var(--neutral-500);">Humidité</div>
</div>
<div style="flex:1;background:var(--neutral-50);border-radius:8px;padding:8px;text-align:center;">
<div style="font-size:var(--text-sm);font-weight:700;color:var(--alert-success);">Bon</div>
<div style="font-size:9px;color:var(--neutral-500);">Qualité air</div>
</div>
<div style="flex:1;background:var(--neutral-50);border-radius:8px;padding:8px;text-align:center;">
<div style="font-size:var(--text-sm);font-weight:700;color:var(--neutral-700);">12 min</div>
<div style="font-size:9px;color:var(--neutral-500);">Dernière MAJ</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bottom Nav -->
<div class="bottom-nav">
<div class="bottom-nav-item">
<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 active">
<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);">03 — Carte Interactive</div>
</div>
</body>
</html>