- 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
408 lines
17 KiB
HTML
408 lines
17 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 — 02 Home Dashboard (Blue v2)</title>
|
|
<link rel="stylesheet" href="shared-blue.css">
|
|
<style>
|
|
.dashboard-header {
|
|
padding: 16px;
|
|
background: linear-gradient(135deg, var(--primary-500), var(--ocean-500));
|
|
color: white;
|
|
}
|
|
.dashboard-header-top {
|
|
display: flex; justify-content: space-between;
|
|
align-items: center; margin-bottom: 16px;
|
|
}
|
|
.greeting { font-size: var(--text-sm); opacity: 0.85; }
|
|
.user-name { font-size: var(--text-lg); font-weight: var(--weight-bold); }
|
|
.search-bar {
|
|
display: flex; align-items: center; gap: 8px;
|
|
background: rgba(255,255,255,0.2);
|
|
border-radius: var(--radius-full);
|
|
padding: 10px 16px;
|
|
backdrop-filter: blur(4px);
|
|
}
|
|
.search-bar input {
|
|
background: none; border: none; outline: none;
|
|
color: white; font-size: var(--text-sm);
|
|
width: 100%;
|
|
}
|
|
.search-bar input::placeholder { color: rgba(255,255,255,0.6); }
|
|
|
|
.metrics-row {
|
|
display: grid; grid-template-columns: repeat(4, 1fr);
|
|
gap: 8px; padding: 0 16px;
|
|
margin-top: -20px; margin-bottom: 16px;
|
|
}
|
|
.metric-card {
|
|
background: var(--neutral-0);
|
|
border-radius: var(--radius-lg);
|
|
padding: 14px 8px;
|
|
text-align: center;
|
|
box-shadow: var(--shadow-md);
|
|
border: 1px solid var(--neutral-100);
|
|
}
|
|
.metric-card-value {
|
|
font-size: var(--text-xl);
|
|
font-weight: var(--weight-bold);
|
|
}
|
|
.metric-card-label {
|
|
font-size: 9px;
|
|
color: var(--neutral-500);
|
|
margin-top: 2px;
|
|
}
|
|
.trend-up { color: var(--alert-success); }
|
|
.trend-down { color: var(--alert-danger); }
|
|
|
|
.section-title-row {
|
|
display: flex; justify-content: space-between;
|
|
align-items: center; padding: 0 16px 12px;
|
|
}
|
|
.section-title { font-size: var(--text-md); font-weight: var(--weight-bold); }
|
|
.see-all { font-size: var(--text-sm); color: var(--primary-500); font-weight: var(--weight-medium); }
|
|
|
|
.sensor-card {
|
|
flex-shrink: 0;
|
|
width: 150px;
|
|
background: var(--neutral-0);
|
|
border-radius: var(--radius-lg);
|
|
padding: 14px;
|
|
box-shadow: var(--shadow-sm);
|
|
border: 1px solid var(--neutral-100);
|
|
}
|
|
.sensor-header {
|
|
display: flex; justify-content: space-between;
|
|
align-items: flex-start; margin-bottom: 10px;
|
|
}
|
|
.sensor-icon {
|
|
width: 36px; height: 36px;
|
|
border-radius: var(--radius-md);
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 18px;
|
|
}
|
|
.sensor-value { font-size: var(--text-xl); font-weight: var(--weight-bold); }
|
|
.sensor-unit { font-size: var(--text-xs); color: var(--neutral-500); }
|
|
.sensor-name { font-size: var(--text-xs); color: var(--neutral-600); margin-top: 4px; }
|
|
.sensor-location { font-size: 10px; color: var(--neutral-400); }
|
|
|
|
.quick-actions {
|
|
display: grid; grid-template-columns: repeat(4, 1fr);
|
|
gap: 12px; padding: 0 16px 16px;
|
|
}
|
|
.quick-action {
|
|
display: flex; flex-direction: column;
|
|
align-items: center; gap: 6px;
|
|
cursor: pointer;
|
|
}
|
|
.quick-action-icon {
|
|
width: 52px; height: 52px;
|
|
border-radius: var(--radius-lg);
|
|
display: flex; align-items: center; justify-content: center;
|
|
color: white; font-size: 22px;
|
|
}
|
|
.quick-action-label { font-size: 10px; color: var(--neutral-600); text-align: center; }
|
|
|
|
.alert-banner {
|
|
margin: 0 16px 12px;
|
|
background: rgba(211,47,47,0.08);
|
|
border: 1px solid rgba(211,47,47,0.2);
|
|
border-radius: var(--radius-lg);
|
|
padding: 12px 14px;
|
|
display: flex; gap: 10px; align-items: flex-start;
|
|
}
|
|
.alert-text { font-size: var(--text-sm); color: var(--alert-danger); font-weight: var(--weight-medium); }
|
|
.alert-sub { font-size: var(--text-xs); color: var(--neutral-600); }
|
|
|
|
.live-indicator {
|
|
display: flex; align-items: center; gap: 6px;
|
|
font-size: var(--text-xs);
|
|
color: var(--alert-success);
|
|
font-weight: var(--weight-medium);
|
|
}
|
|
.live-dot {
|
|
width: 8px; height: 8px;
|
|
background: var(--alert-success);
|
|
border-radius: 50%;
|
|
animation: pulse 2s infinite;
|
|
}
|
|
@keyframes pulse {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.4; }
|
|
}
|
|
|
|
.section-padded { padding: 0 16px 12px; }
|
|
|
|
.weather-widget {
|
|
margin: 0 16px 12px;
|
|
background: linear-gradient(135deg, #1565C0, #0288D1);
|
|
border-radius: var(--radius-xl);
|
|
padding: 16px;
|
|
color: white;
|
|
display: flex; justify-content: space-between; align-items: center;
|
|
}
|
|
.weather-temp { font-size: var(--text-4xl); font-weight: var(--weight-bold); }
|
|
.weather-details { font-size: var(--text-sm); opacity: 0.8; }
|
|
.weather-emoji { font-size: 48px; }
|
|
|
|
.service-shortcuts {
|
|
display: grid; grid-template-columns: repeat(4, 1fr);
|
|
gap: 10px; padding: 0 16px 16px;
|
|
}
|
|
.service-shortcut {
|
|
display: flex; flex-direction: column;
|
|
align-items: center; gap: 6px;
|
|
cursor: pointer;
|
|
}
|
|
.service-shortcut-icon {
|
|
width: 48px; height: 48px;
|
|
border-radius: var(--radius-lg);
|
|
display: flex; align-items: center; justify-content: center;
|
|
font-size: 20px;
|
|
}
|
|
.service-shortcut-label { font-size: 9px; color: var(--neutral-600); text-align: center; line-height: 1.2; }
|
|
/* 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>
|
|
|
|
<!-- Header -->
|
|
<div class="dashboard-header">
|
|
<div class="dashboard-header-top">
|
|
<div>
|
|
<div class="greeting">Bonjour 👋</div>
|
|
<div class="user-name">Eric F.</div>
|
|
</div>
|
|
<div style="display:flex;gap:12px;align-items:center;">
|
|
<div class="live-indicator">
|
|
<div class="live-dot"></div>
|
|
EN DIRECT
|
|
</div>
|
|
<div style="position:relative;">
|
|
<svg class="icon-lg" viewBox="0 0 24 24"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>
|
|
<div class="badge" style="position:absolute;top:-4px;right:-4px;">3</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="search-bar">
|
|
<svg class="icon" viewBox="0 0 24 24" style="color:rgba(255,255,255,0.7)"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
|
|
<input type="text" placeholder="Rechercher un capteur, lieu, service...">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="content-area" style="background:var(--neutral-50);">
|
|
|
|
<!-- Metrics -->
|
|
<div class="metrics-row">
|
|
<div class="metric-card">
|
|
<div class="metric-card-value" style="color:var(--primary-500)">60</div>
|
|
<div class="metric-card-label">Capteurs actifs</div>
|
|
</div>
|
|
<div class="metric-card">
|
|
<div class="metric-card-value" style="color:var(--ocean-500)">24°</div>
|
|
<div class="metric-card-label">Temp. moyenne</div>
|
|
</div>
|
|
<div class="metric-card">
|
|
<div class="metric-card-value" style="color:var(--alert-info)">98%</div>
|
|
<div class="metric-card-label">Uptime réseau</div>
|
|
</div>
|
|
<div class="metric-card">
|
|
<div class="metric-card-value" style="color:var(--indigo-500)">12</div>
|
|
<div class="metric-card-label">Signalements</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Weather Widget -->
|
|
<div class="weather-widget">
|
|
<div>
|
|
<div class="weather-temp">28°</div>
|
|
<div class="weather-details">Fort-de-France</div>
|
|
<div class="weather-details">💧 65% · 💨 12 km/h</div>
|
|
</div>
|
|
<div class="weather-emoji">⛅</div>
|
|
</div>
|
|
|
|
<!-- Alert Banner -->
|
|
<div class="alert-banner">
|
|
<svg class="icon" viewBox="0 0 24 24" style="color:var(--alert-danger);flex-shrink:0;"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
|
<div>
|
|
<div class="alert-text">Qualité air — Fort-de-France</div>
|
|
<div class="alert-sub">Indice élevé détecté · Secteur Centre-Ville · Il y a 12 min</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="section-padded" style="margin-top:8px;margin-bottom:12px;">
|
|
<div class="section-title" style="margin-bottom:12px;">Accès rapide</div>
|
|
</div>
|
|
<div class="quick-actions" style="padding:0 16px 16px;">
|
|
<div class="quick-action">
|
|
<div class="quick-action-icon" style="background:linear-gradient(135deg,#1565C0,#42A5F5);">🗺️</div>
|
|
<span class="quick-action-label">Carte Live</span>
|
|
</div>
|
|
<div class="quick-action">
|
|
<div class="quick-action-icon" style="background:linear-gradient(135deg,#00ACC1,#4DD0E1);">🏪</div>
|
|
<span class="quick-action-label">Marché</span>
|
|
</div>
|
|
<div class="quick-action">
|
|
<div class="quick-action-icon" style="background:linear-gradient(135deg,#3949AB,#7986CB);">🤖</div>
|
|
<span class="quick-action-label">AI Chat</span>
|
|
</div>
|
|
<div class="quick-action">
|
|
<div class="quick-action-icon" style="background:linear-gradient(135deg,#00838F,#26C6DA);">📸</div>
|
|
<span class="quick-action-label">Signaler</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- All Services Shortcuts -->
|
|
<div class="section-padded" style="margin-bottom:8px;">
|
|
<div class="section-title" style="margin-bottom:12px;">Tous les services</div>
|
|
</div>
|
|
<div class="service-shortcuts" style="padding:0 16px 16px;">
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#E3F2FD;">🌡️</div>
|
|
<span class="service-shortcut-label">Météo</span>
|
|
</div>
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#E8EAF6;">🚌</div>
|
|
<span class="service-shortcut-label">Transport</span>
|
|
</div>
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#FFF3E0;">⚡</div>
|
|
<span class="service-shortcut-label">Énergie</span>
|
|
</div>
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#FCE4EC;">🏥</div>
|
|
<span class="service-shortcut-label">Santé</span>
|
|
</div>
|
|
</div>
|
|
<div class="service-shortcuts" style="padding:0 16px 16px;">
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#E0F7FA;">🅿️</div>
|
|
<span class="service-shortcut-label">Parking</span>
|
|
</div>
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#E8F5E9;">🌳</div>
|
|
<span class="service-shortcut-label">Environnement</span>
|
|
</div>
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#F3E5F5;">🎉</div>
|
|
<span class="service-shortcut-label">Événements</span>
|
|
</div>
|
|
<div class="service-shortcut">
|
|
<div class="service-shortcut-icon" style="background:#FFF9C4;">📊</div>
|
|
<span class="service-shortcut-label">Statistiques</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sensors scroll -->
|
|
<div class="section-title-row">
|
|
<div class="section-title">Capteurs en direct 📍</div>
|
|
<div class="see-all">Voir tout →</div>
|
|
</div>
|
|
<div class="scroll-row" style="padding-bottom:16px;">
|
|
<div style="width:16px;flex-shrink:0;"></div>
|
|
<div class="sensor-card">
|
|
<div class="sensor-header">
|
|
<div class="sensor-icon" style="background:#E3F2FD;color:#1565C0;">🌡️</div>
|
|
<div class="online-dot"></div>
|
|
</div>
|
|
<div class="sensor-value">24.3°</div>
|
|
<div class="sensor-unit">Température</div>
|
|
<div class="sensor-name">Temp-001</div>
|
|
<div class="sensor-location">Fort-de-France · Centre</div>
|
|
</div>
|
|
<div class="sensor-card">
|
|
<div class="sensor-header">
|
|
<div class="sensor-icon" style="background:#E0F7FA;color:#00838F;">💨</div>
|
|
<div class="online-dot"></div>
|
|
</div>
|
|
<div class="sensor-value">42</div>
|
|
<div class="sensor-unit">AQI (bon)</div>
|
|
<div class="sensor-name">AirQ-012</div>
|
|
<div class="sensor-location">Pointe des Nègres</div>
|
|
</div>
|
|
<div class="sensor-card">
|
|
<div class="sensor-header">
|
|
<div class="sensor-icon" style="background:#E8EAF6;color:#3949AB;">⚡</div>
|
|
<div class="online-dot"></div>
|
|
</div>
|
|
<div class="sensor-value">3.2</div>
|
|
<div class="sensor-unit">kW/h</div>
|
|
<div class="sensor-name">Ener-005</div>
|
|
<div class="sensor-location">Schœlcher · Zone Industrielle</div>
|
|
</div>
|
|
<div class="sensor-card">
|
|
<div class="sensor-header">
|
|
<div class="sensor-icon" style="background:#FCE4EC;color:#C62828;">💧</div>
|
|
<div class="online-dot"></div>
|
|
</div>
|
|
<div class="sensor-value">65%</div>
|
|
<div class="sensor-unit">Humidité</div>
|
|
<div class="sensor-name">Hum-003</div>
|
|
<div class="sensor-location">Schœlcher · Campus</div>
|
|
</div>
|
|
<div style="width:16px;flex-shrink:0;"></div>
|
|
</div>
|
|
|
|
<!-- Map Mini Preview -->
|
|
<div class="section-title-row">
|
|
<div class="section-title">Aperçu carte</div>
|
|
<div class="see-all">Ouvrir →</div>
|
|
</div>
|
|
<div class="card" style="margin:0 16px 16px;height:160px;background:linear-gradient(135deg,#E3F2FD,#E0F7FA);position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center;">
|
|
<svg width="100%" height="100%" viewBox="0 0 320 160" style="position:absolute;top:0;left:0;">
|
|
<path d="M80 130 Q100 90 140 75 Q180 55 220 60 Q260 65 280 90 Q300 115 290 135 Q270 150 230 148 Q190 150 150 140 Q110 145 80 130Z" fill="rgba(21,101,192,0.15)" stroke="rgba(21,101,192,0.3)" stroke-width="2"/>
|
|
<circle cx="150" cy="95" r="6" fill="#1565C0" opacity="0.8"><animate attributeName="r" values="6;10;6" dur="2s" repeatCount="indefinite"/></circle>
|
|
<circle cx="230" cy="105" r="6" fill="#00ACC1" opacity="0.8"><animate attributeName="r" values="6;10;6" dur="2.5s" repeatCount="indefinite"/></circle>
|
|
<circle cx="190" cy="120" r="5" fill="#3949AB" opacity="0.8"><animate attributeName="r" values="5;9;5" dur="1.8s" repeatCount="indefinite"/></circle>
|
|
<text x="148" y="85" font-size="8" fill="#0D47A1" text-anchor="middle" font-weight="600">FDF</text>
|
|
<text x="228" y="128" font-size="7" fill="#666" text-anchor="middle">Schœlcher</text>
|
|
</svg>
|
|
<div style="position:absolute;bottom:10px;right:10px;background:white;border-radius:8px;padding:6px 12px;font-size:11px;font-weight:600;box-shadow:0 2px 8px rgba(0,0,0,0.1);">
|
|
🗺️ 4 zones actives
|
|
</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 class="page-label" style="position:absolute;bottom:88px;left:0;right:0;text-align:center;font-size:10px;color:var(--neutral-400);">02 — Home Dashboard · Blue v2</div>
|
|
</div>
|
|
</body>
|
|
</html>
|