- 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
359 lines
14 KiB
HTML
359 lines
14 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 — 06 Profil (Blue v2)</title>
|
||
<link rel="stylesheet" href="shared-blue.css">
|
||
<style>
|
||
.profil-header {
|
||
background: linear-gradient(160deg, var(--primary-700) 0%, var(--ocean-600) 50%, var(--ocean-500) 100%);
|
||
padding: 24px 20px 20px;
|
||
color: white; position: relative;
|
||
}
|
||
.profil-avatar-wrap {
|
||
display: flex; align-items: center; gap: 14px;
|
||
}
|
||
.profil-avatar {
|
||
width: 68px; height: 68px; border-radius: var(--radius-full);
|
||
background: rgba(255,255,255,0.2);
|
||
border: 3px solid rgba(255,255,255,0.4);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 32px;
|
||
}
|
||
.profil-name { font-size: var(--text-lg); font-weight: 700; }
|
||
.profil-email { font-size: var(--text-sm); opacity: 0.75; }
|
||
.profil-level {
|
||
margin-top: 4px; font-size: 11px;
|
||
display: inline-flex; align-items: center; gap: 4px;
|
||
background: rgba(255,255,255,0.15);
|
||
padding: 3px 10px; border-radius: var(--radius-full);
|
||
}
|
||
|
||
.profil-stats {
|
||
display: grid; grid-template-columns: repeat(3, 1fr);
|
||
gap: 8px; padding: 0 16px;
|
||
margin-top: -20px; margin-bottom: 16px;
|
||
position: relative; z-index: 2;
|
||
}
|
||
.stat-card {
|
||
background: white; border-radius: var(--radius-lg);
|
||
padding: 14px 8px; text-align: center;
|
||
box-shadow: var(--shadow-md);
|
||
border: 1px solid var(--neutral-100);
|
||
}
|
||
.stat-value { font-size: var(--text-xl); font-weight: 700; }
|
||
.stat-label { font-size: 9px; color: var(--neutral-500); margin-top: 2px; }
|
||
|
||
.profil-section {
|
||
padding: 0 16px 16px;
|
||
}
|
||
.profil-section-title {
|
||
font-size: 12px; font-weight: 700;
|
||
color: var(--primary-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
margin-bottom: 8px; display: flex;
|
||
align-items: center; gap: 6px;
|
||
}
|
||
|
||
.profil-card {
|
||
background: white; border-radius: var(--radius-lg);
|
||
border: 1px solid var(--neutral-100);
|
||
overflow: hidden;
|
||
}
|
||
.profil-item {
|
||
display: flex; align-items: center; gap: 12px;
|
||
padding: 14px 16px;
|
||
border-bottom: 1px solid var(--neutral-100);
|
||
cursor: pointer; transition: background 0.15s;
|
||
}
|
||
.profil-item:last-child { border-bottom: none; }
|
||
.profil-item:hover { background: var(--neutral-50); }
|
||
.profil-item-icon {
|
||
width: 36px; height: 36px; border-radius: var(--radius-md);
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 18px; flex-shrink: 0;
|
||
}
|
||
.profil-item-text { flex: 1; }
|
||
.profil-item-title { font-size: var(--text-sm); font-weight: 600; }
|
||
.profil-item-sub { font-size: 11px; color: var(--neutral-500); }
|
||
.profil-item-right {
|
||
font-size: var(--text-sm); color: var(--neutral-500);
|
||
display: flex; align-items: center; gap: 4px;
|
||
}
|
||
|
||
.toggle-switch {
|
||
width: 44px; height: 24px; border-radius: 12px;
|
||
background: var(--neutral-300); position: relative;
|
||
cursor: pointer; transition: background 0.2s; flex-shrink: 0;
|
||
}
|
||
.toggle-switch.on { background: var(--primary-500); }
|
||
.toggle-switch::after {
|
||
content: ''; width: 20px; height: 20px;
|
||
border-radius: 50%; background: white;
|
||
position: absolute; top: 2px; left: 2px;
|
||
transition: transform 0.2s;
|
||
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
|
||
}
|
||
.toggle-switch.on::after { transform: translateX(20px); }
|
||
|
||
.lang-grid {
|
||
display: grid; grid-template-columns: repeat(4, 1fr);
|
||
gap: 8px;
|
||
}
|
||
.lang-option {
|
||
padding: 10px; border-radius: var(--radius-md);
|
||
border: 2px solid var(--neutral-200);
|
||
background: white; text-align: center;
|
||
cursor: pointer; transition: all 0.2s;
|
||
}
|
||
.lang-option.active {
|
||
border-color: var(--primary-500);
|
||
background: var(--primary-50);
|
||
}
|
||
.lang-flag { font-size: 20px; }
|
||
.lang-code { font-size: 11px; font-weight: 600; margin-top: 2px; }
|
||
|
||
.section-divider { height: 8px; background: var(--neutral-50); }
|
||
|
||
.profil-badge {
|
||
display: inline-flex; align-items: center; gap: 3px;
|
||
padding: 2px 8px; border-radius: var(--radius-full);
|
||
font-size: 10px; font-weight: 600;
|
||
}
|
||
/* 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="profil-header">
|
||
<div style="display:flex;justify-content:flex-end;margin-bottom:8px;">
|
||
<svg class="icon-lg" style="cursor:pointer;" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-2 2 2 2 0 01-2-2v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83 0 2 2 0 010-2.83l.06-.06a1.65 1.65 0 00.33-1.82 1.65 1.65 0 00-1.51-1H3a2 2 0 01-2-2 2 2 0 012-2h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 010-2.83 2 2 0 012.83 0l.06.06a1.65 1.65 0 001.82.33H9a1.65 1.65 0 001-1.51V3a2 2 0 012-2 2 2 0 012 2v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 0 2 2 0 010 2.83l-.06.06a1.65 1.65 0 00-.33 1.82V9a1.65 1.65 0 001.51 1H21a2 2 0 012 2 2 2 0 01-2 2h-.09a1.65 1.65 0 00-1.51 1z"/></svg>
|
||
</div>
|
||
<div class="profil-avatar-wrap">
|
||
<div class="profil-avatar">👨🏻💻</div>
|
||
<div>
|
||
<div class="profil-name">Eric Ferdinand</div>
|
||
<div class="profil-email">eric@smartappcity.com</div>
|
||
<div class="profil-level">🏆 Contributeur · Niveau 5</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Content -->
|
||
<div class="content-area" style="background:var(--neutral-50);">
|
||
|
||
<!-- Stats -->
|
||
<div class="profil-stats">
|
||
<div class="stat-card">
|
||
<div class="stat-value" style="color:var(--primary-500)">27</div>
|
||
<div class="stat-label">Signalements</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" style="color:var(--ocean-500)">1,420</div>
|
||
<div class="stat-label">Points</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" style="color:var(--indigo-500)">84</div>
|
||
<div class="stat-label">Visites</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Langue -->
|
||
<div class="profil-section">
|
||
<div class="profil-section-title">🌐 Langue</div>
|
||
<div class="lang-grid">
|
||
<div class="lang-option active">
|
||
<div class="lang-flag">🇫🇷</div>
|
||
<div class="lang-code">FR</div>
|
||
</div>
|
||
<div class="lang-option">
|
||
<div class="lang-flag">🇬🇧</div>
|
||
<div class="lang-code">EN</div>
|
||
</div>
|
||
<div class="lang-option">
|
||
<div class="lang-flag">🇪🇸</div>
|
||
<div class="lang-code">ES</div>
|
||
</div>
|
||
<div class="lang-option">
|
||
<div class="lang-flag">🇩🇪</div>
|
||
<div class="lang-code">DE</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-divider"></div>
|
||
|
||
<!-- Mon compte -->
|
||
<div class="profil-section">
|
||
<div class="profil-section-title">👤 Mon compte</div>
|
||
<div class="profil-card">
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E3F2FD;">✏️</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Modifier le profil</div>
|
||
<div class="profil-item-sub">Nom, avatar, email</div>
|
||
</div>
|
||
<div class="profil-item-right">›</div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#FFF3E0;">🔑</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Sécurité & mot de passe</div>
|
||
<div class="profil-item-sub">2FA, changement</div>
|
||
</div>
|
||
<div class="profil-item-right">›</div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E8F5E9;">📋</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Données personnelles</div>
|
||
<div class="profil-item-sub">RGPD, export</div>
|
||
</div>
|
||
<div class="profil-item-right">›</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-divider"></div>
|
||
|
||
<!-- Préférences -->
|
||
<div class="profil-section">
|
||
<div class="profil-section-title">⚙️ Préférences</div>
|
||
<div class="profil-card">
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E8EAF6;">🌙</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Mode sombre</div>
|
||
<div class="profil-item-sub">Thème foncé</div>
|
||
</div>
|
||
<div class="toggle-switch on"></div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E3F2FD;">🔔</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Notifications</div>
|
||
<div class="profil-item-sub">Push & alertes</div>
|
||
</div>
|
||
<div class="toggle-switch on"></div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E0F7FA;">📍</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Géolocalisation</div>
|
||
<div class="profil-item-sub">Services proches</div>
|
||
</div>
|
||
<div class="toggle-switch on"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-divider"></div>
|
||
|
||
<!-- Paiements / DPI -->
|
||
<div class="profil-section">
|
||
<div class="profil-section-title">🔗 Paiements / DPI / OTN</div>
|
||
<div class="profil-card">
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#1565C0;color:white;">💳</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Portefeuille numérique</div>
|
||
<div class="profil-item-sub">Solde Wallet · Identité DPI</div>
|
||
</div>
|
||
<div class="profil-item-right"><span style="font-weight:700;color:var(--primary-500);">45,80 €</span></div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#3949AB;color:white;">🛒</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Mes commandes</div>
|
||
<div class="profil-item-sub">3 en cours · 12 terminées</div>
|
||
</div>
|
||
<div class="profil-item-right"><span class="badge" style="position:static;">3</span></div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#00ACC1;color:white;">🪪</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Identité DID</div>
|
||
<div class="profil-item-sub">did:smartcity:mq:0x8f2a...</div>
|
||
</div>
|
||
<div class="profil-item-right"><span class="profil-badge" style="background:#E8F5E9;color:#2E7D32;">✓ Vérifié</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-divider"></div>
|
||
|
||
<!-- Signalements ODK -->
|
||
<div class="profil-section">
|
||
<div class="profil-section-title">📸 Signalements ODK</div>
|
||
<div class="profil-card">
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#FFF3E0;">📋</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Mes signalements</div>
|
||
<div class="profil-item-sub">27 soumis · 18 résolus</div>
|
||
</div>
|
||
<div class="profil-item-right">›</div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E3F2FD;">📝</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Formulaires ODK</div>
|
||
<div class="profil-item-sub">Saisie terrain hors-ligne</div>
|
||
</div>
|
||
<div class="profil-item-right">›</div>
|
||
</div>
|
||
<div class="profil-item">
|
||
<div class="profil-item-icon" style="background:#E8F5E9;">✅</div>
|
||
<div class="profil-item-text">
|
||
<div class="profil-item-title">Validés</div>
|
||
<div class="profil-item-sub">Approuvés par la collectivité</div>
|
||
</div>
|
||
<div class="profil-item-right"><span class="profil-badge" style="background:#E8F5E9;color:#2E7D32;">18</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div style="height:24px;"></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">
|
||
<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 active">
|
||
<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);">06 — Profil Utilisateur · Blue v2</div>
|
||
</div>
|
||
</body>
|
||
</html>
|