<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ClearView — Gestion interventions</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3.19.0/dist/tabler-icons.min.css">
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:#f5f5f3;color:#1a1a1a;font-size:14px;line-height:1.5}
.app{display:flex;height:100vh;overflow:hidden}
/* Sidebar */
.sidebar{width:220px;flex-shrink:0;background:#fff;border-right:1px solid #e8e8e8;display:flex;flex-direction:column}
.logo{padding:18px 16px 14px;border-bottom:1px solid #e8e8e8}
.logo-row{display:flex;align-items:center;gap:10px}
.logo-icon{width:32px;height:32px;background:#1D9E75;border-radius:8px;display:flex;align-items:center;justify-content:center;color:#fff;font-size:16px;flex-shrink:0}
.logo-name{font-size:15px;font-weight:600}
.logo-tagline{font-size:11px;color:#888;margin-top:1px}
.nav{padding:10px 8px;flex:1;display:flex;flex-direction:column;gap:2px}
.nav-item{display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:8px;font-size:13px;color:#666;cursor:pointer;border:none;background:none;width:100%;text-align:left;font-family:inherit}
.nav-item i{font-size:17px;flex-shrink:0}
.nav-item:hover{background:#f5f5f3;color:#1a1a1a}
.nav-item.active{background:#E1F5EE;color:#0F6E56;font-weight:600}
/* Topbar */
.topbar{height:52px;background:#fff;border-bottom:1px solid #e8e8e8;padding:0 20px;display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
.topbar-left{display:flex;align-items:center;gap:8px}
.topbar-icon{font-size:18px;color:#1D9E75}
.topbar-title{font-size:15px;font-weight:600}
.content{flex:1;overflow-y:auto;padding:20px}
/* Search bar */
.search-bar{display:flex;align-items:center;gap:10px;background:#fff;border:1px solid #e8e8e8;border-radius:10px;padding:8px 14px;margin-bottom:14px}
.search-bar i{color:#aaa;font-size:16px;flex-shrink:0}
.search-bar input{border:none;outline:none;font-size:13px;font-family:inherit;flex:1;background:none;color:#1a1a1a}
.search-bar input::placeholder{color:#bbb}
.search-count{font-size:12px;color:#aaa;flex-shrink:0}
/* Buttons */
.btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:8px;font-size:13px;cursor:pointer;border:1px solid #ddd;background:#fff;color:#1a1a1a;font-family:inherit;font-weight:400;line-height:1;transition:all .15s}
.btn:hover{background:#f5f5f3}
.btn-green{background:#1D9E75;color:#fff;border-color:#1D9E75;font-weight:600}
.btn-green:hover{background:#0F6E56;border-color:#0F6E56}
.btn-sm{padding:5px 10px;font-size:12px}
.btn-sms{background:#E6F1FB;color:#0C447C;border-color:#B5D4F4}
.btn-sms:hover{background:#B5D4F4}
.btn-star{background:#FAEEDA;color:#633806;border-color:#FAC775}
.btn-star:hover{background:#FAC775}
.btn-red{background:#FCEBEB;color:#791F1F;border-color:#F7C1C1}
.btn-red:hover{background:#F7C1C1}
.btn-orange{background:#FFF3E0;color:#7C4700;border-color:#FFCC80}
.btn-orange:hover{background:#FFE0B2}
.btn-purple{background:#F0EBFB;color:#4A1D96;border-color:#C4B5FD}
.btn-purple:hover{background:#E9D5FF}
/* Stats */
.stats-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}
.stat{background:#fff;border:1px solid #e8e8e8;border-radius:10px;padding:14px 16px}
.stat-num{font-size:24px;font-weight:700;line-height:1;margin-bottom:4px}
.stat-label{font-size:12px;color:#888}
.stat-sub{font-size:11px;color:#aaa;margin-top:3px}
.section-label{font-size:11px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}
/* Cards & Tables */
.card{background:#fff;border:1px solid #e8e8e8;border-radius:12px;overflow:hidden;margin-bottom:16px}
.card-head{padding:12px 16px;border-bottom:1px solid #e8e8e8;display:flex;align-items:center;justify-content:space-between}
.card-head-title{font-size:13px;font-weight:600;display:flex;align-items:center;gap:7px}
.tbl-head{display:grid;padding:9px 16px;background:#f9f9f7;border-bottom:1px solid #e8e8e8;font-size:11px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.05em}
.tbl-row{display:grid;padding:0;border-bottom:1px solid #e8e8e8;align-items:stretch;font-size:13px;cursor:pointer}
.tbl-row:last-child{border-bottom:none}
.tbl-row:hover{background:#f9f9f7}
.tbl-row-inner{display:grid;padding:12px 16px;align-items:center;width:100%}
.cols-dash{grid-template-columns:minmax(0,2fr) minmax(80px,120px) 120px 120px 110px}
.name{font-weight:600;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sub{font-size:11px;color:#888;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Pills */
.pill{display:inline-flex;align-items:center;padding:3px 8px;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap}
.pill-blue{background:#E6F1FB;color:#0C447C}
.pill-green{background:#E1F5EE;color:#085041}
.pill-amber{background:#FAEEDA;color:#633806}
.pill-red{background:#FCEBEB;color:#791F1F}
.pill-gray{background:#f5f5f3;color:#666}
.pill-purple{background:#F0EBFB;color:#4A1D96}
.date-ok{color:#0F6E56;font-size:13px}
.date-warn{color:#BA7517;font-weight:600;font-size:13px}
.date-muted{color:#888;font-size:13px}
/* Relances */
.rel-row{display:flex;align-items:center;gap:12px;padding:12px 16px;border-bottom:1px solid #e8e8e8}
.rel-row:last-child{border-bottom:none}
.rel-avatar{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:15px;flex-shrink:0}
.rel-body{flex:1;min-width:0}
.rel-name{font-size:13px;font-weight:600}
.rel-sub{font-size:11px;color:#888;margin-top:1px}
/* Views */
.view{display:none}
.view.active{display:block}
.empty-state{padding:40px 20px;text-align:center;color:#aaa}
.empty-state i{font-size:32px;display:block;margin-bottom:8px}
.empty-state p{font-size:13px}
/* Modals */
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;z-index:100}
.modal{background:#fff;border:1px solid #e8e8e8;border-radius:14px;overflow:hidden;box-shadow:0 12px 40px rgba(0,0,0,.15)}
.modal-head{padding:14px 18px;border-bottom:1px solid #e8e8e8;display:flex;align-items:center;justify-content:space-between}
.modal-head-title{font-size:14px;font-weight:600}
.modal-body{padding:18px;display:flex;flex-direction:column;gap:14px;max-height:520px;overflow-y:auto}
.modal-foot{padding:12px 18px;border-top:1px solid #e8e8e8;display:flex;justify-content:flex-end;gap:8px}
.field label{font-size:12px;font-weight:600;color:#555;display:block;margin-bottom:5px}
.field input,.field select,.field textarea{width:100%;padding:8px 11px;border:1px solid #ddd;border-radius:8px;font-size:13px;background:#fff;color:#1a1a1a;font-family:inherit;outline:none;line-height:1.4}
.field input:focus,.field select:focus,.field textarea:focus{border-color:#1D9E75;box-shadow:0 0 0 3px rgba(29,158,117,.1)}
.field textarea{resize:vertical;min-height:60px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.close-btn{background:none;border:none;cursor:pointer;color:#aaa;font-size:20px;line-height:1;padding:2px;display:flex}
.close-btn:hover{color:#1a1a1a}
.notice{background:#f5f5f3;border-radius:8px;padding:10px 12px;font-size:12px;color:#666;display:flex;align-items:flex-start;gap:8px}
.notice i{font-size:15px;color:#185FA5;flex-shrink:0;margin-top:1px}
.two-col{display:grid;grid-template-columns:1fr 1fr;gap:14px}
/* Fiche */
.fiche-grid{display:grid;grid-template-columns:1fr 1fr}
.fiche-cell{padding:10px 14px;border-bottom:1px solid #e8e8e8;border-right:1px solid #e8e8e8}
.fiche-cell:nth-child(even){border-right:none}
.fiche-cell-full{grid-column:1/-1;padding:10px 14px;border-bottom:1px solid #e8e8e8}
.fiche-label{font-size:11px;color:#888;margin-bottom:3px}
.fiche-value{font-size:13px;font-weight:600}
/* Schedule mode toggle */
.mode-toggle{display:flex;gap:0;border:1px solid #e8e8e8;border-radius:8px;overflow:hidden;margin-bottom:4px}
.mode-btn{flex:1;padding:7px 10px;font-size:12px;font-weight:500;border:none;background:#fff;color:#888;cursor:pointer;font-family:inherit;transition:all .15s}
.mode-btn.active{background:#1D9E75;color:#fff;font-weight:600}
.schedule-preview{background:#E1F5EE;border-radius:8px;padding:9px 12px;font-size:12px;color:#0F6E56;display:flex;align-items:center;gap:7px}
.schedule-preview i{font-size:14px;flex-shrink:0}
/* Services form */
.service-block{border:1px solid #e8e8e8;border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:10px;position:relative;background:#fafaf9}
.service-block-title{font-size:12px;font-weight:600;color:#555;margin-bottom:2px}
.service-remove{position:absolute;top:10px;right:10px;background:none;border:none;cursor:pointer;color:#ccc;font-size:16px;line-height:1;padding:2px}
.service-remove:hover{color:#e55}
.add-service-btn{display:flex;align-items:center;gap:6px;padding:8px 12px;border:1px dashed #ccc;border-radius:8px;background:none;cursor:pointer;font-size:12px;color:#888;font-family:inherit;width:100%;justify-content:center}
.add-service-btn:hover{border-color:#1D9E75;color:#1D9E75;background:#f0faf6}
/* CA */
.ca-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin-bottom:20px}
.ca-stat{background:#fff;border:1px solid #e8e8e8;border-radius:10px;padding:14px 16px}
.ca-stat-num{font-size:22px;font-weight:700;line-height:1;margin-bottom:4px}
.ca-stat-label{font-size:12px;color:#888}
.bar-chart{display:flex;flex-direction:column;gap:8px}
.bar-row{display:flex;align-items:center;gap:12px;font-size:13px}
.bar-label{width:160px;flex-shrink:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500}
.bar-track{flex:1;height:26px;background:#f5f5f3;border-radius:6px;overflow:hidden}
.bar-fill{height:100%;border-radius:6px;display:flex;align-items:center;padding-left:10px;font-size:12px;font-weight:600;color:#fff;min-width:40px;transition:width .4s ease}
.bar-amount{width:70px;text-align:right;font-weight:600;flex-shrink:0}
.bar-freq{width:60px;text-align:right;font-size:11px;color:#aaa;flex-shrink:0}
/* Historique */
.hist-item{display:flex;align-items:flex-start;gap:12px;padding:12px 16px;border-bottom:1px solid #e8e8e8}
.hist-item:last-child{border-bottom:none}
.hist-dot{width:10px;height:10px;border-radius:50%;background:#1D9E75;flex-shrink:0;margin-top:4px}
.hist-dot.old{background:#ccc}
.hist-body{flex:1;min-width:0}
.hist-date{font-size:13px;font-weight:600;color:#1a1a1a}
.hist-detail{font-size:12px;color:#888;margin-top:2px}
.hist-amount{font-size:13px;font-weight:600;color:#1D9E75;white-space:nowrap;flex-shrink:0}
.tab-bar{display:flex;border-bottom:1px solid #e8e8e8}
.tab{padding:10px 16px;font-size:13px;color:#888;cursor:pointer;border-bottom:2px solid transparent;font-weight:500;background:none;border-top:none;border-left:none;border-right:none;font-family:inherit}
.tab.active{color:#0F6E56;border-bottom-color:#1D9E75}
.tab:hover{color:#1a1a1a}
</style>
</head>
<body>
<div class="app">
<div class="sidebar">
<div class="logo">
<div class="logo-row">
<div class="logo-icon"><i class="ti ti-droplet"></i></div>
<div><div class="logo-name">ClearView</div><div class="logo-tagline">Laveur de vitres</div></div>
</div>
</div>
<nav class="nav">
<button class="nav-item active" onclick="goTo('dashboard',this)"><i class="ti ti-layout-dashboard"></i> Tableau de bord</button>
<button class="nav-item" onclick="goTo('clients',this)"><i class="ti ti-users"></i> Mes clients</button>
<button class="nav-item" onclick="goTo('planning',this)"><i class="ti ti-calendar"></i> Planning</button>
<button class="nav-item" onclick="goTo('relances',this)"><i class="ti ti-bell"></i> Relances</button>
<button class="nav-item" onclick="goTo('historique',this)"><i class="ti ti-history"></i> Historique</button>
<button class="nav-item" onclick="goTo('ca',this)"><i class="ti ti-chart-bar"></i> Chiffre d'affaires</button>
</nav>
</div>
<div style="flex:1;display:flex;flex-direction:column;overflow:hidden;min-width:0">
<div class="topbar">
<div class="topbar-left">
<i class="ti ti-layout-dashboard topbar-icon" id="topbar-icon"></i>
<span class="topbar-title" id="topbar-title">Tableau de bord</span>
</div>
<div id="topbar-btn">
<button class="btn btn-green" onclick="openAddModal()"><i class="ti ti-plus"></i> Nouveau client</button>
</div>
</div>
<div class="content">
<!-- DASHBOARD -->
<div class="view active" id="view-dashboard">
<div class="stats-grid">
<div class="stat"><div class="stat-num" id="s-clients">0</div><div class="stat-label">Clients actifs</div></div>
<div class="stat"><div class="stat-num" id="s-mois">0</div><div class="stat-label">Interventions ce mois</div></div>
<div class="stat"><div class="stat-num" id="s-relances">0</div><div class="stat-label">Relances à envoyer</div></div>
<div class="stat"><div class="stat-num" id="s-ca" style="color:#1D9E75">0 €</div><div class="stat-label">CA mensuel estimé</div><div class="stat-sub" id="s-ca-sub"></div></div>
</div>
<div class="section-label">Prochaines interventions</div>
<div class="card">
<div class="tbl-head cols-dash">
<span>Client</span><span>Fréquence</span><span>Dernier passage</span><span>Prochain passage</span><span>CA / mois</span>
</div>
<div id="dash-rows"></div>
</div>
</div>
<!-- CLIENTS -->
<div class="view" id="view-clients">
<div class="search-bar">
<i class="ti ti-search"></i>
<input id="search-input" placeholder="Rechercher un client par nom, adresse, téléphone…" oninput="renderClients()">
<span class="search-count" id="search-count"></span>
</div>
<div class="card">
<div class="card-head">
<span class="card-head-title"><i class="ti ti-users"></i> Tous mes clients</span>
<button class="btn btn-green btn-sm" onclick="openAddModal()"><i class="ti ti-plus"></i> Ajouter</button>
</div>
<div class="tbl-head cols-dash">
<span>Client</span><span>Fréquence</span><span>Dernier passage</span><span>Prochain passage</span><span>CA / mois</span>
</div>
<div id="client-rows"></div>
</div>
</div>
<!-- PLANNING -->
<div class="view" id="view-planning">
<div class="card">
<div class="card-head">
<span class="card-head-title"><i class="ti ti-calendar"></i> Interventions à planifier</span>
</div>
<div class="tbl-head" style="grid-template-columns:minmax(0,2fr) minmax(80px,120px) 120px 120px 110px">
<span>Client</span><span>Fréquence</span><span>Date suggérée</span><span>Date confirmée</span><span>Action</span>
</div>
<div id="planning-rows"></div>
</div>
</div>
<!-- RELANCES -->
<div class="view" id="view-relances">
<div class="two-col">
<div>
<div class="section-label" style="display:flex;align-items:center;gap:6px"><i class="ti ti-message" style="font-size:13px;color:#185FA5"></i> Rappels avant intervention</div>
<div class="card"><div id="rel-avant"></div></div>
</div>
<div>
<div class="section-label" style="display:flex;align-items:center;gap:6px"><i class="ti ti-star" style="font-size:13px;color:#BA7517"></i> Demandes d'avis Google</div>
<div class="card"><div id="rel-apres"></div></div>
</div>
</div>
</div>
<!-- HISTORIQUE -->
<div class="view" id="view-historique">
<div class="search-bar">
<i class="ti ti-search"></i>
<input id="hist-search" placeholder="Rechercher dans l'historique…" oninput="renderHistorique()">
<span class="search-count" id="hist-count"></span>
</div>
<div class="card">
<div class="card-head">
<span class="card-head-title"><i class="ti ti-history"></i> Toutes les interventions</span>
<button class="btn btn-green btn-sm" onclick="openAddIntervModal()"><i class="ti ti-plus"></i> Ajouter une intervention</button>
</div>
<div class="tbl-head" style="grid-template-columns:120px minmax(0,2fr) 1fr 100px 90px">
<span>Date</span><span>Client</span><span>Service</span><span>Montant</span><span>Statut</span>
</div>
<div id="hist-rows"></div>
</div>
</div>
<!-- CA -->
<div class="view" id="view-ca">
<div class="ca-grid">
<div class="ca-stat"><div class="ca-stat-num" id="ca-semaine" style="color:#1D9E75">0 €</div><div class="ca-stat-label">CA par semaine (estimé)</div></div>
<div class="ca-stat"><div class="ca-stat-num" id="ca-mois" style="color:#1D9E75">0 €</div><div class="ca-stat-label">CA par mois (estimé)</div></div>
<div class="ca-stat"><div class="ca-stat-num" id="ca-an" style="color:#1D9E75">0 €</div><div class="ca-stat-label">CA par an (estimé)</div></div>
</div>
<div class="section-label">Revenus par client</div>
<div class="card">
<div class="card-head"><span class="card-head-title"><i class="ti ti-chart-bar"></i> Détail par client</span></div>
<div style="padding:16px"><div class="bar-chart" id="ca-bars"></div></div>
</div>
</div>
</div>
</div>
</div>
<!-- MODAL : Ajouter / Modifier client -->
<div class="overlay" id="overlay-add" style="display:none">
<div class="modal" style="width:500px">
<div class="modal-head">
<span class="modal-head-title" id="modal-add-title">Ajouter un client</span>
<button class="close-btn" onclick="closeModal('add')"><i class="ti ti-x"></i></button>
</div>
<div class="modal-body">
<div class="grid2">
<div class="field"><label>Nom / Entreprise *</label><input id="f-nom" placeholder="Mme Dupont"></div>
<div class="field"><label>Téléphone *</label><input id="f-tel" placeholder="0470 00 00 00" type="tel"></div>
</div>
<div class="field"><label>Adresse</label><input id="f-addr" placeholder="Rue, code postal, ville"></div>
<div class="field"><label>Dernier passage</label><input id="f-last" type="date"></div>
<div>
<label style="font-size:12px;font-weight:600;color:#555;display:block;margin-bottom:6px">Planification du prochain passage</label>
<div class="mode-toggle">
<button class="mode-btn active" id="mode-btn-date" onclick="setScheduleMode('date')"><i class="ti ti-calendar"></i> Date fixe</button>
<button class="mode-btn" id="mode-btn-recur" onclick="setScheduleMode('recur')"><i class="ti ti-refresh"></i> Récurrence</button>
</div>
<!-- Mode date fixe -->
<div id="mode-date-panel">
<div class="field" style="margin-top:8px"><input id="f-prochain" type="date" placeholder="Laisser vide pour calcul auto"></div>
<div style="font-size:11px;color:#aaa;margin-top:4px">Laisser vide : la date sera calculée automatiquement selon la fréquence du service.</div>
</div>
<!-- Mode récurrence -->
<div id="mode-recur-panel" style="display:none;margin-top:8px;display:none">
<div class="grid2" style="margin-bottom:8px">
<div class="field">
<label>Quelle occurrence ?</label>
<select id="f-recur-occ" onchange="updateRecurPreview()">
<option value="1">Le 1er</option>
<option value="2">Le 2ème</option>
<option value="3">Le 3ème</option>
<option value="4">Le 4ème</option>
<option value="-1">Le dernier</option>
</select>
</div>
<div class="field">
<label>Quel jour ?</label>
<select id="f-recur-day" onchange="updateRecurPreview()">
<option value="1">Lundi</option>
<option value="2">Mardi</option>
<option value="3">Mercredi</option>
<option value="4">Jeudi</option>
<option value="5">Vendredi</option>
<option value="6">Samedi</option>
<option value="0">Dimanche</option>
</select>
</div>
</div>
<div class="field">
<label>De chaque</label>
<select id="f-recur-period" onchange="updateRecurPreview()">
<option value="week">Semaine</option>
<option value="month" selected>Mois</option>
</select>
</div>
<div id="recur-preview" class="schedule-preview" style="margin-top:8px;display:none">
<i class="ti ti-calendar-check"></i>
<span id="recur-preview-text"></span>
</div>
</div>
</div>
<div>
<div style="font-size:12px;font-weight:600;color:#555;margin-bottom:8px">Services & fréquences</div>
<div id="services-list" style="display:flex;flex-direction:column;gap:8px"></div>
<button class="add-service-btn" style="margin-top:8px" onclick="addServiceBlock()"><i class="ti ti-plus"></i> Ajouter un service</button>
</div>
<div class="field"><label>Notes</label><textarea id="f-notes" placeholder="Accès, type de vitres, remarques…"></textarea></div>
</div>
<div class="modal-foot">
<button class="btn" onclick="closeModal('add')">Annuler</button>
<button class="btn btn-green" id="btn-save-client" onclick="saveClient()"><i class="ti ti-check"></i> Enregistrer</button>
</div>
</div>
</div>
<!-- MODAL : Confirmer date -->
<div class="overlay" id="overlay-date" style="display:none">
<div class="modal" style="width:400px">
<div class="modal-head">
<span class="modal-head-title">Confirmer la date d'intervention</span>
<button class="close-btn" onclick="closeModal('date')"><i class="ti ti-x"></i></button>
</div>
<div class="modal-body">
<div style="font-size:13px;color:#666">Client : <span id="d-name" style="font-weight:600;color:#1a1a1a"></span></div>
<div class="field"><label>Date proposée — ajustez si besoin</label><input id="d-date" type="date"></div>
<div class="notice"><i class="ti ti-calendar-event"></i><span>En confirmant, un événement sera créé dans votre Google Agenda.</span></div>
</div>
<div class="modal-foot">
<button class="btn" onclick="closeModal('date')">Annuler</button>
<button class="btn btn-green" onclick="confirmDate()"><i class="ti ti-calendar-check"></i> Confirmer & Google Agenda</button>
</div>
</div>
</div>
<!-- MODAL : Fiche client -->
<div class="overlay" id="overlay-fiche" style="display:none">
<div class="modal" style="width:500px">
<div class="modal-head">
<span class="modal-head-title" id="fiche-title">Fiche client</span>
<button class="close-btn" onclick="closeModal('fiche')"><i class="ti ti-x"></i></button>
</div>
<div>
<div class="tab-bar">
<button class="tab active" onclick="fichTab('infos',this)">Infos</button>
<button class="tab" onclick="fichTab('historique',this)">Historique</button>
</div>
<div id="fiche-infos"></div>
<div id="fiche-hist" style="display:none"></div>
</div>
<div class="modal-foot" style="justify-content:space-between">
<button class="btn btn-red btn-sm" id="fiche-del"><i class="ti ti-trash"></i> Supprimer</button>
<div style="display:flex;gap:8px">
<button class="btn btn-orange btn-sm" id="fiche-edit"><i class="ti ti-edit"></i> Modifier</button>
<button class="btn" onclick="closeModal('fiche')">Fermer</button>
</div>
</div>
</div>
</div>
<!-- MODAL : Ajouter intervention -->
<div class="overlay" id="overlay-interv" style="display:none">
<div class="modal" style="width:420px">
<div class="modal-head">
<span class="modal-head-title" id="interv-title">Ajouter une intervention</span>
<button class="close-btn" onclick="closeModal('interv')"><i class="ti ti-x"></i></button>
</div>
<div class="modal-body">
<div class="field"><label>Client *</label>
<select id="i-client">
<option value="">— Choisir un client —</option>
</select>
</div>
<div class="field"><label>Service effectué</label><input id="i-service" placeholder="Ex : Vitres extérieures"></div>
<div class="grid2">
<div class="field"><label>Date *</label><input id="i-date" type="date"></div>
<div class="field"><label>Montant (€)</label><input id="i-montant" type="number" min="0" step="0.5" placeholder="Ex : 80"></div>
</div>
<div class="field"><label>Notes</label><textarea id="i-notes" placeholder="Remarques sur cette intervention…" style="min-height:48px"></textarea></div>
</div>
<div class="modal-foot">
<button class="btn" onclick="closeModal('interv')">Annuler</button>
<button class="btn btn-green" onclick="saveInterv()"><i class="ti ti-check"></i> Enregistrer</button>
</div>
</div>
</div>
<script>
const GOOGLE_REVIEW='https://g.page/r/CTmMyq1mZ6X9EAE/review';
const FREQ_DAYS={hebdo:7,bimensuel:14,mensuel:30,bimestriel:60,trimestriel:90,semestriel:180,annuel:365};
const FREQ_LABEL={hebdo:'Hebdo',bimensuel:'Bimensuel',mensuel:'Mensuel',bimestriel:'Bimestriel',trimestriel:'Trimestriel',semestriel:'Semestriel',annuel:'Annuel'};
const FREQ_PER_MONTH={hebdo:4.33,bimensuel:2.17,mensuel:1,bimestriel:0.5,trimestriel:0.33,semestriel:0.167,annuel:0.083};
let clients=JSON.parse(localStorage.getItem('cv5-clients')||'[]');
let interventions=JSON.parse(localStorage.getItem('cv5-interventions')||'[]');
let pendingId=null,editingId=null,serviceCount=0;
let currentFicheId=null;
let scheduleMode='date'; // 'date' or 'recur'
function save(){
localStorage.setItem('cv5-clients',JSON.stringify(clients));
localStorage.setItem('cv5-interventions',JSON.stringify(interventions));
}
function fmt(d){if(!d)return '—';const[y,m,j]=d.split('-');return `${j}/${m}/${y}`;}
function daysTo(d){if(!d)return null;return Math.ceil((new Date(d)-new Date())/86400000);}
// ---- SCHEDULE MODE ----
const JOURS=['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'];
const OCC_LABEL={1:'1er',2:'2ème',3:'3ème',4:'4ème','-1':'dernier'};
function setScheduleMode(mode){
scheduleMode=mode;
document.getElementById('mode-btn-date').classList.toggle('active',mode==='date');
document.getElementById('mode-btn-recur').classList.toggle('active',mode==='recur');
document.getElementById('mode-date-panel').style.display=mode==='date'?'block':'none';
document.getElementById('mode-recur-panel').style.display=mode==='recur'?'block':'none';
if(mode==='recur')updateRecurPreview();
}
function updateRecurPreview(){
const occ=document.getElementById('f-recur-occ')?.value;
const day=parseInt(document.getElementById('f-recur-day')?.value);
const period=document.getElementById('f-recur-period')?.value;
if(!occ||isNaN(day)||!period)return;
const next=calcNextRecur({recurOcc:occ,recurDay:day,recurPeriod:period},new Date());
const prev=document.getElementById('recur-preview');
const pt=document.getElementById('recur-preview-text');
if(next&&prev&&pt){
prev.style.display='flex';
pt.textContent=`Prochain passage : ${fmt(next)} (${OCC_LABEL[occ]} ${JOURS[day]} de chaque ${period==='week'?'semaine':'mois'})`;
}
}
// Calculate next occurrence of a recurring rule from a given date
function calcNextRecur(rule,fromDate){
const {recurOcc,recurDay,recurPeriod}=rule;
const occ=parseInt(recurOcc);
const day=parseInt(recurDay);
const from=new Date(fromDate);
from.setHours(0,0,0,0);
if(recurPeriod==='month'){
// Find the Nth (or last) weekday of next few months
for(let monthOffset=0;monthOffset<=3;monthOffset++){
const d=new Date(from.getFullYear(),from.getMonth()+monthOffset,1);
let result=null;
if(occ===-1){
// Last occurrence: find last day of month, go backwards
const lastDay=new Date(d.getFullYear(),d.getMonth()+1,0);
let cur=new Date(lastDay);
while(cur.getDay()!==day)cur.setDate(cur.getDate()-1);
result=cur;
} else {
// Nth occurrence: find first weekday, then add weeks
let cur=new Date(d);
while(cur.getDay()!==day)cur.setDate(cur.getDate()+1);
cur.setDate(cur.getDate()+(occ-1)*7);
if(cur.getMonth()===d.getMonth())result=cur;
}
if(result&&result>from){
return result.toISOString().split('T')[0];
}
}
} else if(recurPeriod==='week'){
// Next occurrence of that weekday
const cur=new Date(from);
cur.setDate(cur.getDate()+1);
while(cur.getDay()!==day)cur.setDate(cur.getDate()+1);
return cur.toISOString().split('T')[0];
}
return null;
}
function recurLabel(c){
if(!c.recurOcc||!c.recurDay===undefined||!c.recurPeriod)return null;
const occ=OCC_LABEL[c.recurOcc]||c.recurOcc;
const day=JOURS[parseInt(c.recurDay)]||'';
const period=c.recurPeriod==='week'?'semaine':'mois';
return `${occ} ${day} du ${period}`;
}
function suggest(c){
// If recurring rule is set, use it
if(c.recurOcc!==undefined&&c.recurDay!==undefined&&c.recurPeriod){
return calcNextRecur(c,new Date());
}
// Otherwise use frequency-based calculation
if(!c.services||!c.services.length||!c.dernier)return null;
const d=new Date(c.dernier);d.setDate(d.getDate()+FREQ_DAYS[c.services[0].freq]);
return d.toISOString().split('T')[0];
}
function nextDate(c){return c.prochain||suggest(c);}
function clientMensuel(c){
if(!c.services)return 0;
return c.services.reduce((s,sv)=>s+(parseFloat(sv.prix)||0)*FREQ_PER_MONTH[sv.freq||'mensuel'],0);
}
function servicesLabel(c){
if(c.recurOcc!==undefined&&c.recurDay!==undefined&&c.recurPeriod){
return recurLabel(c)||'—';
}
if(!c.services||!c.services.length)return '—';
return c.services.map(s=>FREQ_LABEL[s.freq]).join(' + ');
}
// ---- SMS ----
function smsRappel(c){
const msg=`Bonjour ${c.nom},\n\nJe vous contacte pour vous rappeler mon passage pour le nettoyage de vos vitres le ${fmt(nextDate(c))}.\n\nN'hésitez pas si vous avez des questions. À bientôt !`;
window.open(`sms:${c.tel}?body=${encodeURIComponent(msg)}`);
}
function smsAvis(c){
const msg=`Bonjour ${c.nom},\n\nJ'espère que vous êtes satisfait(e) du nettoyage. Si vous avez un moment, un avis Google me ferait vraiment plaisir :\n${GOOGLE_REVIEW}\n\nMerci beaucoup et à bientôt !`;
window.open(`sms:${c.tel}?body=${encodeURIComponent(msg)}`);
}
function smsAvisMarked(id){const c=clients.find(x=>x.id===id);smsAvis(c);c.avisEnvoye=true;save();render();}
// ---- SERVICE BLOCKS ----
function addServiceBlock(service){
serviceCount++;
const idx=serviceCount;
const sl=document.getElementById('services-list');
const s=service||{};
const div=document.createElement('div');
div.className='service-block';div.id='svc-'+idx;
div.innerHTML=`
<div class="service-block-title">Service ${sl.children.length+1}</div>
<button class="service-remove" onclick="removeServiceBlock('svc-${idx}')"><i class="ti ti-x"></i></button>
<div class="field"><label>Nom du service</label><input class="svc-nom" value="${s.nom||''}" placeholder="Ex : Vitres extérieures"></div>
<div class="grid2">
<div class="field"><label>Fréquence</label>
<select class="svc-freq">
${['hebdo','bimensuel','mensuel','bimestriel','trimestriel','semestriel','annuel'].map(f=>`<option value="${f}" ${(s.freq||'mensuel')===f?'selected':''}>${FREQ_LABEL[f]}</option>`).join('')}
</select>
</div>
<div class="field"><label>Prix (€)</label><input class="svc-prix" type="number" min="0" step="0.5" value="${s.prix||''}" placeholder="Ex : 80"></div>
</div>`;
sl.appendChild(div);renumberServices();
}
function removeServiceBlock(id){const el=document.getElementById(id);if(el)el.remove();renumberServices();}
function renumberServices(){document.querySelectorAll('.service-block').forEach((el,i)=>{const t=el.querySelector('.service-block-title');if(t)t.textContent='Service '+(i+1);});}
function getServicesFromForm(){
return [...document.querySelectorAll('.service-block')].map(b=>({
nom:b.querySelector('.svc-nom').value.trim(),
freq:b.querySelector('.svc-freq').value,
prix:b.querySelector('.svc-prix').value.trim()
})).filter(s=>s.nom||s.freq);
}
// ---- MODALS ----
function openModal(id){document.getElementById('overlay-'+id).style.display='flex';}
function closeModal(id){document.getElementById('overlay-'+id).style.display='none';}
function openAddModal(){
editingId=null;
document.getElementById('modal-add-title').textContent='Ajouter un client';
document.getElementById('btn-save-client').innerHTML='<i class="ti ti-check"></i> Enregistrer';
['f-nom','f-tel','f-addr','f-notes'].forEach(i=>document.getElementById(i).value='');
document.getElementById('f-last').value='';
document.getElementById('f-prochain').value='';
document.getElementById('services-list').innerHTML='';serviceCount=0;
addServiceBlock();
setScheduleMode('date');
openModal('add');
}
function openEditModal(id){
const c=clients.find(x=>x.id===id);editingId=id;
document.getElementById('modal-add-title').textContent='Modifier le client';
document.getElementById('btn-save-client').innerHTML='<i class="ti ti-check"></i> Mettre à jour';
document.getElementById('f-nom').value=c.nom||'';
document.getElementById('f-tel').value=c.tel||'';
document.getElementById('f-addr').value=c.adresse||'';
document.getElementById('f-last').value=c.dernier||'';
document.getElementById('f-prochain').value=c.prochain||'';
document.getElementById('f-notes').value=c.notes||'';
document.getElementById('services-list').innerHTML='';serviceCount=0;
(c.services&&c.services.length?c.services:[{nom:'',freq:c.freq||'mensuel',prix:c.prix||''}]).forEach(s=>addServiceBlock(s));
// Restore schedule mode
if(c.recurOcc!==undefined&&c.recurDay!==undefined&&c.recurPeriod){
setScheduleMode('recur');
document.getElementById('f-recur-occ').value=c.recurOcc;
document.getElementById('f-recur-day').value=c.recurDay;
document.getElementById('f-recur-period').value=c.recurPeriod;
updateRecurPreview();
} else {
setScheduleMode('date');
}
closeModal('fiche');openModal('add');
}
function saveClient(){
const nom=document.getElementById('f-nom').value.trim();
const tel=document.getElementById('f-tel').value.trim();
if(!nom||!tel){alert('Le nom et le téléphone sont obligatoires.');return;}
const services=getServicesFromForm();
if(!services.length){alert('Ajoutez au moins un service.');return;}
// Schedule
let scheduleFields={};
if(scheduleMode==='recur'){
scheduleFields={
recurOcc:document.getElementById('f-recur-occ').value,
recurDay:parseInt(document.getElementById('f-recur-day').value),
recurPeriod:document.getElementById('f-recur-period').value,
prochain:'' // will be computed
};
} else {
scheduleFields={
recurOcc:undefined,recurDay:undefined,recurPeriod:undefined,
prochain:document.getElementById('f-prochain').value||''
};
}
if(editingId){
const c=clients.find(x=>x.id===editingId);
Object.assign(c,{nom,tel,adresse:document.getElementById('f-addr').value.trim(),dernier:document.getElementById('f-last').value,notes:document.getElementById('f-notes').value.trim(),services,...scheduleFields});
// Clean up old recur fields if switched to date mode
if(scheduleMode==='date'){delete c.recurOcc;delete c.recurDay;delete c.recurPeriod;}
} else {
clients.push({id:Date.now().toString(),nom,tel,adresse:document.getElementById('f-addr').value.trim(),dernier:document.getElementById('f-last').value,notes:document.getElementById('f-notes').value.trim(),services,avisEnvoye:false,...scheduleFields});
}
save();closeModal('add');editingId=null;render();
}
// ---- INTERVENTIONS ----
function openAddIntervModal(clientId){
// populate select
const sel=document.getElementById('i-client');
sel.innerHTML='<option value="">— Choisir un client —</option>'+clients.map(c=>`<option value="${c.id}" ${clientId===c.id?'selected':''}>${c.nom}</option>`).join('');
document.getElementById('i-service').value='';
document.getElementById('i-date').value=new Date().toISOString().split('T')[0];
document.getElementById('i-montant').value='';
document.getElementById('i-notes').value='';
document.getElementById('interv-title').textContent='Ajouter une intervention';
openModal('interv');
}
function saveInterv(){
const cid=document.getElementById('i-client').value;
const date=document.getElementById('i-date').value;
if(!cid||!date){alert('Client et date sont obligatoires.');return;}
const c=clients.find(x=>x.id===cid);
interventions.push({
id:Date.now().toString(),clientId:cid,clientNom:c?c.nom:'',
service:document.getElementById('i-service').value.trim(),
date,montant:document.getElementById('i-montant').value.trim(),
notes:document.getElementById('i-notes').value.trim()
});
// update dernier passage on client
if(c&&(!c.dernier||date>c.dernier)){c.dernier=date;c.prochain='';}
save();closeModal('interv');render();
}
// ---- FICHE ----
let ficheTab='infos';
function fichTab(tab,btn){
ficheTab=tab;
document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));
btn.classList.add('active');
document.getElementById('fiche-infos').style.display=tab==='infos'?'block':'none';
document.getElementById('fiche-hist').style.display=tab==='historique'?'block':'none';
if(tab==='historique'&¤tFicheId)renderFicheHist(currentFicheId);
}
function renderFicheHist(id){
const items=interventions.filter(i=>i.clientId===id).sort((a,b)=>b.date.localeCompare(a.date));
const el=document.getElementById('fiche-hist');
if(!items.length){el.innerHTML='<div class="empty-state"><i class="ti ti-history"></i><p>Aucune intervention enregistrée</p></div>';return;}
el.innerHTML=`<div style="max-height:320px;overflow-y:auto">`+items.map((iv,i)=>`
<div class="hist-item">
<div class="hist-dot ${i>0?'old':''}"></div>
<div class="hist-body">
<div class="hist-date">${fmt(iv.date)}</div>
<div class="hist-detail">${iv.service||'Intervention'}${iv.notes?' · '+iv.notes:''}</div>
</div>
${iv.montant?`<div class="hist-amount">${iv.montant} €</div>`:''}
</div>`).join('')+`</div>`;
}
function openFiche(id){
currentFicheId=id;ficheTab='infos';
const c=clients.find(x=>x.id===id);
document.getElementById('fiche-title').textContent=c.nom;
document.querySelectorAll('.tab').forEach((t,i)=>{t.classList.toggle('active',i===0);});
document.getElementById('fiche-infos').style.display='block';
document.getElementById('fiche-hist').style.display='none';
const servicesHtml=(c.services||[]).map((s,i)=>`
<div style="padding:10px 14px;border-bottom:1px solid #e8e8e8">
<div style="display:flex;align-items:center;justify-content:space-between">
<span style="font-size:12px;font-weight:600;color:#555">${s.nom||'Service '+(i+1)}</span>
<span class="pill pill-blue">${FREQ_LABEL[s.freq]}</span>
</div>
${s.prix?`<div style="font-size:13px;font-weight:700;color:#1D9E75;margin-top:4px">${s.prix} € / passage</div>`:''}
</div>`).join('');
const intervCount=interventions.filter(i=>i.clientId===id).length;
document.getElementById('fiche-infos').innerHTML=`
<div class="fiche-grid">
<div class="fiche-cell"><div class="fiche-label">Téléphone</div><div class="fiche-value">${c.tel}</div></div>
<div class="fiche-cell"><div class="fiche-label">CA mensuel estimé</div><div class="fiche-value" style="color:#1D9E75">${clientMensuel(c).toFixed(0)} €/mois</div></div>
<div class="fiche-cell"><div class="fiche-label">Dernier passage</div><div class="fiche-value">${fmt(c.dernier)}</div></div>
<div class="fiche-cell"><div class="fiche-label">Prochain passage</div><div class="fiche-value" style="color:#1D9E75">${fmt(nextDate(c))}</div></div>
${c.recurOcc!==undefined?`<div class="fiche-cell-full"><div class="fiche-label">Planification automatique</div><div class="fiche-value" style="color:#185FA5"><i class="ti ti-refresh" style="font-size:12px;margin-right:4px"></i>${recurLabel(c)}</div></div>`:''}
${c.adresse?`<div class="fiche-cell-full"><div class="fiche-label">Adresse</div><div class="fiche-value">${c.adresse}</div></div>`:''}
<div class="fiche-cell-full"><div class="fiche-label">Interventions enregistrées</div><div class="fiche-value">${intervCount} intervention${intervCount!==1?'s':''}</div></div>
</div>
<div style="border-top:1px solid #e8e8e8">
<div style="padding:8px 14px;font-size:11px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.05em;background:#f9f9f7">Services</div>
${servicesHtml}
</div>
${c.notes?`<div style="padding:10px 14px;border-top:1px solid #e8e8e8"><div class="fiche-label">Notes</div><div style="font-size:13px;color:#555;margin-top:3px">${c.notes}</div></div>`:''}
<div style="padding:12px 16px;border-top:1px solid #e8e8e8;display:flex;gap:8px;flex-wrap:wrap">
<button class="btn btn-sms btn-sm" onclick="smsRappel(clients.find(x=>x.id==='${id}'))"><i class="ti ti-message"></i> SMS rappel</button>
<button class="btn btn-star btn-sm" onclick="smsAvis(clients.find(x=>x.id==='${id}'))"><i class="ti ti-star"></i> Avis Google</button>
<button class="btn btn-sm" onclick="openDateModal('${id}');closeModal('fiche')"><i class="ti ti-calendar"></i> Planifier</button>
<button class="btn btn-purple btn-sm" onclick="openAddIntervModal('${id}');closeModal('fiche')"><i class="ti ti-plus"></i> Intervention</button>
</div>`;
document.getElementById('fiche-del').onclick=()=>{if(confirm('Supprimer ce client ?')){clients=clients.filter(x=>x.id!==id);interventions=interventions.filter(i=>i.clientId!==id);save();closeModal('fiche');render();}};
document.getElementById('fiche-edit').onclick=()=>openEditModal(id);
openModal('fiche');
}
// ---- PLANNING ----
function openDateModal(id){
const c=clients.find(x=>x.id===id);pendingId=id;
document.getElementById('d-name').textContent=c.nom;
document.getElementById('d-date').value=suggest(c)||new Date().toISOString().split('T')[0];
openModal('date');
}
function confirmDate(){
const d=document.getElementById('d-date').value;if(!d)return;
const c=clients.find(x=>x.id===pendingId);
c.prochain=d;c.avisEnvoye=false;save();closeModal('date');
const title=encodeURIComponent(`Nettoyage vitres — ${c.nom}`);
const loc=encodeURIComponent(c.adresse||'');
const ds=d.replace(/-/g,'');
window.open(`https://calendar.google.com/calendar/render?action=TEMPLATE&text=${title}&dates=${ds}/${ds}&location=${loc}`,'_blank');
render();
}
function markDone(id){
const c=clients.find(x=>x.id===id);
c.dernier=c.prochain||new Date().toISOString().split('T')[0];
c.prochain='';c.avisEnvoye=false;save();render();
}
// ---- ROW HTML ----
function rowHtml(c){
const n=nextDate(c);const nd=daysTo(n);
const ncls=nd!==null&&nd<=5?'date-warn':'date-ok';
const mensuel=clientMensuel(c);
return `<div class="tbl-row" onclick="openFiche('${c.id}')">
<div class="tbl-row-inner cols-dash">
<div><div class="name">${c.nom}</div><div class="sub">${c.adresse||'—'}</div></div>
<span style="font-size:12px;color:#444">${servicesLabel(c)}</span>
<span class="date-muted">${fmt(c.dernier)}</span>
<span class="${ncls}">${fmt(n)}</span>
<span style="font-weight:600;color:#1D9E75">${mensuel>0?mensuel.toFixed(0)+' €':'—'}</span>
</div>
</div>`;
}
function planRowHtml(c){
const sug=suggest(c);
return `<div style="display:grid;grid-template-columns:minmax(0,2fr) minmax(80px,120px) 120px 120px 110px;padding:12px 16px;border-bottom:1px solid #e8e8e8;align-items:center;font-size:13px">
<div><div class="name">${c.nom}</div><div class="sub">${c.adresse||''}</div></div>
<span style="font-size:12px;color:#444">${servicesLabel(c)}</span>
<span class="date-muted">${fmt(sug)}</span>
<span style="color:${c.prochain?'#0F6E56':'#aaa'}">${c.prochain?fmt(c.prochain):'Non confirmé'}</span>
<div>${!c.prochain
?`<button class="btn btn-green btn-sm" onclick="openDateModal('${c.id}')"><i class="ti ti-check"></i> Confirmer</button>`
:`<button class="btn btn-sm" onclick="markDone('${c.id}')"><i class="ti ti-check"></i> Fait</button>`}
</div>
</div>`;
}
// ---- NAVIGATION ----
const PAGE_CFG={
dashboard:['Tableau de bord','ti-layout-dashboard',true],
clients:['Mes clients','ti-users',true],
planning:['Planning','ti-calendar',false],
relances:['Relances','ti-bell',false],
historique:['Historique','ti-history',false],
ca:['Chiffre d\'affaires','ti-chart-bar',false]
};
function goTo(page,btn){
document.querySelectorAll('.view').forEach(v=>v.classList.remove('active'));
document.getElementById('view-'+page).classList.add('active');
document.querySelectorAll('.nav-item').forEach(b=>b.classList.remove('active'));
btn.classList.add('active');
const[title,icon,showAdd]=PAGE_CFG[page];
document.getElementById('topbar-title').textContent=title;
document.getElementById('topbar-icon').className='ti '+icon+' topbar-icon';
document.getElementById('topbar-btn').innerHTML=showAdd?`<button class="btn btn-green" onclick="openAddModal()"><i class="ti ti-plus"></i> Nouveau client</button>`:'';
if(page==='clients'){setTimeout(()=>document.getElementById('search-input').focus(),50);}
render();
}
// ---- RENDER ----
function renderClients(){
const q=(document.getElementById('search-input')?.value||'').toLowerCase().trim();
const sorted=[...clients].sort((a,b)=>(nextDate(a)||'9999').localeCompare(nextDate(b)||'9999'));
const filtered=q?sorted.filter(c=>[c.nom,c.adresse,c.tel].some(v=>v&&v.toLowerCase().includes(q))):sorted;
document.getElementById('client-rows').innerHTML=filtered.length?filtered.map(rowHtml).join(''):`<div class="empty-state"><i class="ti ti-search"></i><p>Aucun client ne correspond à "${q}"</p></div>`;
const sc=document.getElementById('search-count');
if(sc)sc.textContent=q?`${filtered.length} résultat${filtered.length!==1?'s':''}`:clients.length+' client'+(clients.length!==1?'s':'');
}
function renderHistorique(){
const q=(document.getElementById('hist-search')?.value||'').toLowerCase().trim();
const sorted=[...interventions].sort((a,b)=>b.date.localeCompare(a.date));
const filtered=q?sorted.filter(i=>[i.clientNom,i.service,i.notes].some(v=>v&&v.toLowerCase().includes(q))):sorted;
const hc=document.getElementById('hist-count');
if(hc)hc.textContent=filtered.length+' intervention'+(filtered.length!==1?'s':'');
const el=document.getElementById('hist-rows');
if(!el)return;
if(!filtered.length){
el.innerHTML=`<div class="empty-state"><i class="ti ti-history"></i><p>${q?'Aucune intervention ne correspond à "'+q+'"':'Aucune intervention enregistrée'}</p></div>`;
return;
}
el.innerHTML=filtered.map(iv=>`
<div style="display:grid;grid-template-columns:120px minmax(0,2fr) 1fr 100px 90px;padding:11px 16px;border-bottom:1px solid #e8e8e8;align-items:center;font-size:13px;cursor:pointer" onclick="openFiche('${iv.clientId}')">
<span class="date-muted">${fmt(iv.date)}</span>
<div><div class="name">${iv.clientNom}</div>${iv.notes?`<div class="sub">${iv.notes}</div>`:''}</div>
<span style="color:#555">${iv.service||'—'}</span>
<span style="font-weight:600;color:#1D9E75">${iv.montant?iv.montant+' €':'—'}</span>
<span><span class="pill pill-green">Effectué</span></span>
</div>`).join('');
}
function render(){
const sorted=[...clients].sort((a,b)=>(nextDate(a)||'9999').localeCompare(nextDate(b)||'9999'));
const now=new Date();const m=now.getMonth();const y=now.getFullYear();
const empty=(icon,msg)=>`<div class="empty-state"><i class="ti ${icon}"></i><p>${msg}</p></div>`;
// Stats
document.getElementById('s-clients').textContent=clients.length;
document.getElementById('s-mois').textContent=clients.filter(c=>{const n=nextDate(c);return n&&new Date(n).getMonth()===m&&new Date(n).getFullYear()===y;}).length;
document.getElementById('s-relances').textContent=clients.filter(c=>{const n=nextDate(c);const d=daysTo(n);return d!==null&&d>=0&&d<=5;}).length;
const totalMensuel=clients.reduce((s,c)=>s+clientMensuel(c),0);
document.getElementById('s-ca').textContent=totalMensuel.toFixed(0)+' €';
document.getElementById('s-ca-sub').textContent='≈ '+(totalMensuel*12).toFixed(0)+' €/an';
// Dashboard
const dr=document.getElementById('dash-rows');
dr.innerHTML=clients.length?sorted.slice(0,6).map(rowHtml).join(''):empty('ti-users','Ajoutez votre premier client pour commencer');
// Clients
renderClients();
// Planning
document.getElementById('planning-rows').innerHTML=sorted.length?sorted.map(planRowHtml).join(''):empty('ti-calendar','Aucune intervention à planifier');
// Relances
const avant=clients.filter(c=>{const n=nextDate(c);const d=daysTo(n);return d!==null&&d>=0&&d<=5;});
const apres=clients.filter(c=>c.prochain&&daysTo(c.prochain)<-1&&!c.avisEnvoye);
document.getElementById('rel-avant').innerHTML=avant.length?avant.map(c=>`
<div class="rel-row">
<div class="rel-avatar" style="background:#E6F1FB;color:#0C447C"><i class="ti ti-message"></i></div>
<div class="rel-body"><div class="rel-name">${c.nom}</div><div class="rel-sub">Passage prévu le ${fmt(nextDate(c))}</div></div>
<button class="btn btn-sms btn-sm" onclick="smsRappel(clients.find(x=>x.id==='${c.id}'))"><i class="ti ti-send"></i> SMS</button>
</div>`).join(''):empty('ti-check','Aucun rappel à envoyer');
document.getElementById('rel-apres').innerHTML=apres.length?apres.map(c=>`
<div class="rel-row">
<div class="rel-avatar" style="background:#FAEEDA;color:#633806"><i class="ti ti-star"></i></div>
<div class="rel-body"><div class="rel-name">${c.nom}</div><div class="rel-sub">Intervention du ${fmt(c.prochain)}</div></div>
<button class="btn btn-star btn-sm" onclick="smsAvisMarked('${c.id}')"><i class="ti ti-send"></i> SMS</button>
</div>`).join(''):empty('ti-star','Aucune demande d\'avis en attente');
// Historique
renderHistorique();
// CA
const totalSemaine=totalMensuel/4.33;
document.getElementById('ca-semaine').textContent=totalSemaine.toFixed(0)+' €';
document.getElementById('ca-mois').textContent=totalMensuel.toFixed(0)+' €';
document.getElementById('ca-an').textContent=(totalMensuel*12).toFixed(0)+' €';
const sortedCA=[...clients].sort((a,b)=>clientMensuel(b)-clientMensuel(a));
const maxCA=sortedCA.length?clientMensuel(sortedCA[0]):1;
const colors=['#1D9E75','#2BB5A0','#0F6E56','#48C9B0','#3AC0A0','#1ABC9C'];
document.getElementById('ca-bars').innerHTML=sortedCA.length?sortedCA.map((c,i)=>{
const mensuel=clientMensuel(c);const pct=maxCA>0?Math.max(8,(mensuel/maxCA)*100):8;
return `<div class="bar-row">
<div class="bar-label" title="${c.nom}">${c.nom}</div>
<div class="bar-track"><div class="bar-fill" style="width:${pct}%;background:${colors[i%colors.length]}">${mensuel>=15?mensuel.toFixed(0)+' €':''}</div></div>
<div class="bar-amount">${mensuel.toFixed(0)} €</div>
<div class="bar-freq">/mois</div>
</div>`;
}).join(''):empty('ti-chart-bar','Aucun client avec prix renseigné');
}
// Close on backdrop
document.querySelectorAll('.overlay').forEach(o=>o.addEventListener('click',e=>{if(e.target===o)o.style.display='none';}));
// ---- DEMO DATA ----
if(!clients.length){
const ago=n=>{const d=new Date();d.setDate(d.getDate()-n);return d.toISOString().split('T')[0];};
const fwd=n=>{const d=new Date();d.setDate(d.getDate()+n);return d.toISOString().split('T')[0];};
clients=[
{id:'1',nom:'Dupont & Fils SARL',tel:'0470123456',adresse:'12 rue du Commerce, Liège',dernier:ago(28),prochain:fwd(3),notes:"Accès par l'arrière",avisEnvoye:false,services:[{nom:'Vitres extérieures',freq:'hebdo',prix:'45'},{nom:'Vitres intérieures',freq:'mensuel',prix:'80'}]},
{id:'2',nom:'Mme Renard',tel:'0478654321',adresse:'8 av. des Lilas, Namur',dernier:ago(80),prochain:fwd(10),notes:'',avisEnvoye:false,services:[{nom:'Nettoyage complet',freq:'trimestriel',prix:'120'}]},
{id:'3',nom:'Boulangerie Simon',tel:'0465987654',adresse:'3 Grand-Place, Mons',dernier:ago(12),prochain:'',notes:'Tôt le matin',avisEnvoye:false,services:[{nom:'Vitres extérieures',freq:'bimensuel',prix:'60'},{nom:'Vitres intérieures',freq:'mensuel',prix:'90'}]},
{id:'4',nom:'Résidence Les Pins',tel:'0491234567',adresse:'45 rue des Pins, Bruxelles',dernier:ago(5),prochain:'',notes:'',avisEnvoye:true,services:[{nom:'Nettoyage annuel',freq:'annuel',prix:'350'}]},
];
interventions=[
{id:'h1',clientId:'1',clientNom:'Dupont & Fils SARL',service:'Vitres extérieures',date:ago(28),montant:'45',notes:''},
{id:'h2',clientId:'1',clientNom:'Dupont & Fils SARL',service:'Vitres extérieures',date:ago(21),montant:'45',notes:''},
{id:'h3',clientId:'1',clientNom:'Dupont & Fils SARL',service:'Vitres extérieures + intérieures',date:ago(14),montant:'125',notes:'Grand nettoyage mensuel'},
{id:'h4',clientId:'3',clientNom:'Boulangerie Simon',service:'Vitres extérieures',date:ago(12),montant:'60',notes:''},
{id:'h5',clientId:'2',clientNom:'Mme Renard',service:'Nettoyage complet',date:ago(80),montant:'120',notes:'Client très satisfait'},
];
save();
}
render();
</script>
</body>
</html>