Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 58
n/a
0 / 0
CRAP
n/a
0 / 0
1<!DOCTYPE html>
2<html lang="fr">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1">
6
7    <title><?= $title ?? 'Tableau de bord CRM' ?></title>
8
9    <meta name="description" content="Tableau de bord CRM">
10    <meta name="robots" content="noindex, nofollow">
11
12    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
13    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
14    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
15
16    <style>
17        :root {
18            --s-primary: #1a56db;
19        }
20
21        body {
22            background-color: #f4f6f9;
23            font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
24        }
25
26        /* ====================== SIDEBAR AVEC TOGGLE ====================== */
27        .sidebar {
28            width: 260px;
29            height: 100vh;
30            position: fixed;
31            top: 0;
32            left: 0;
33            background: #111827;
34            color: #fff;
35            padding: 20px 15px;
36            overflow-x: hidden;
37            z-index: 100;
38            transition: width 0.25s ease;
39        }
40
41        .sidebar.collapsed {
42            width: 70px;
43        }
44
45        .sidebar a {
46            color: #cbd5e1;
47            text-decoration: none;
48            display: flex;
49            align-items: center;
50            gap: 10px;
51            padding: 12px 14px;
52            border-radius: 8px;
53            white-space: nowrap;
54            transition: background 0.2s, color 0.2s;
55        }
56
57        .sidebar a:hover {
58            background: #1f2937;
59            color: #fff;
60        }
61
62        .sidebar.collapsed a {
63            justify-content: center;
64        }
65
66        /* Masquer le texte et les titres de section quand replié */
67        .sidebar.collapsed a span,
68        .sidebar.collapsed .text-uppercase {
69            display: none;
70        }
71
72        /* ====================== LOGO : conteneur fixe + image inline (plus de flicker) ====================== */
73        .logo-container {
74            height: 48px;           /* Ajustez selon la hauteur réelle de votre logo */
75            display: flex;
76            align-items: center;
77            margin-bottom: 1.5rem;
78            transition: none;       /* Pas de transition pour éviter tout décalage */
79        }
80        .logo-full {
81            max-width: 215px;
82            max-height: 100%;
83            width: auto;
84            height: auto;
85            display: block;
86        }
87        .logo-icon {
88            display: none;
89            width: 32px;
90            height: 32px;
91            object-fit: contain;
92        }
93
94        /* Quand sidebar repliée, on cache le logo large et on affiche l'icône */
95        .sidebar.collapsed .logo-full {
96            display: none;
97        }
98        .sidebar.collapsed .logo-icon {
99            display: block;
100            margin: 0 auto;
101        }
102        /* Ajustement du conteneur pour centrer l'icône */
103        .sidebar.collapsed .logo-container {
104            justify-content: center;
105        }
106
107        /* ====================== MAIN CONTENT ====================== */
108        .content {
109            margin-left: 260px;
110            padding: 25px 30px;
111            transition: margin-left 0.25s ease;
112            min-height: 100vh;
113        }
114
115        .content.expanded {
116            margin-left: 70px;
117        }
118
119        /* ====================== TOPBAR ====================== */
120        .topbar {
121            background: #fff;
122            padding: 14px 24px;
123            border-radius: 14px;
124            margin-bottom: 24px;
125            box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
126            border: 1px solid #f0f2f7;
127            display: flex;
128            justify-content: space-between;
129            align-items: center;
130        }
131
132        /* ====================== MONITOR WRAP ====================== */
133        .monitor-wrap {
134            max-width: 1800px;
135            margin: 0 auto;
136            padding: 0;
137        }
138
139        /* Scrollbar invisible */
140        .sidebar::-webkit-scrollbar { width: 0; }
141        .sidebar { scrollbar-width: none; }
142
143        /* Anti-flicker global pour les transitions (hors toggle) */
144        body.preload-transitions-off * {
145            transition: none !important;
146        }
147    </style>
148</head>
149<body class="preload-transitions-off">
150
151<!-- SIDEBAR AVEC TOGGLE -->
152<div class="sidebar p-3" id="sidebar">
153    <div class="logo-container">
154        <?php
155        // Conversion du logo en base64 pour éliminer toute requête réseau et tout clignotement
156        $logoPath = FCPATH . 'assets/img/logo.png';   // Chemin absolu vers le logo
157        $logoData = '';
158        if (file_exists($logoPath)) {
159            $mime = mime_content_type($logoPath);
160            $base64 = base64_encode(file_get_contents($logoPath));
161            $logoData = 'data:' . $mime . ';base64,' . $base64;
162        } else {
163            // Fallback : URL classique
164            $logoData = base_url('assets/img/logo.png');
165        }
166        ?>
167        <img src="<?= $logoData ?>" class="logo-full" alt="logo">
168        <img src="<?= base_url('assets/img/logo-icon.png') ?>" class="logo-icon" alt="icon">
169    </div>
170
171    <!-- DASHBOARD -->
172    <div class="menu-group mb-3">
173        <a href="<?= base_url('dashboard') ?>">
174            <i class="bi bi-speedometer2"></i><span>Tableau de bord</span>
175        </a>
176    </div>
177
178    <!-- GESTION DES ENVOIS -->
179    <div class="menu-group mb-3">
180        <div class="text-uppercase text-secondary small mb-2 px-2">Gestion des envois</div>
181
182        <a data-bs-toggle="collapse" href="#menu-envois">
183            <i class="bi bi-send"></i><span>Envois</span>
184        </a>
185        <div class="collapse ps-4 mt-1" id="menu-envois">
186            <a href="<?= base_url('dashboard/envoi/unique') ?>">Unique</a>
187            <a href="<?= base_url('dashboard/envoi/unique/brouillons') ?>">Brouillons</a>
188            <a href="<?= base_url('dashboard/envoi/unique/envoyes') ?>">Envoyés</a>
189        </div>
190
191        <a data-bs-toggle="collapse" href="#menu-masse">
192            <i class="bi bi-stack"></i><span>Masse</span>
193        </a>
194        <div class="collapse ps-4 mt-1" id="menu-masse">
195            <a href="<?= base_url('dashboard/envoi/masse') ?>">Composer</a>
196            <a href="<?= base_url('dashboard/envoi/masse/brouillons') ?>">Brouillons</a>
197            <a href="<?= base_url('dashboard/envoi/masse/envoyes') ?>">Envoyés</a>
198        </div>
199
200        <a data-bs-toggle="collapse" href="#menu-campagne">
201            <i class="bi bi-megaphone"></i><span>Campagne</span>
202        </a>
203        <div class="collapse ps-4 mt-1" id="menu-campagne">
204            <a href="<?= base_url('dashboard/campagnes/lancer') ?>">Lancer</a>
205            <a href="<?= base_url('dashboard/campagnes') ?>">Liste</a>
206            <a href="<?= base_url('dashboard/campagne/statistiques') ?>">Statistiques</a>
207        </div>
208    </div>
209
210    <!-- GESTION DES CONTACTS -->
211    <div class="menu-group mb-3">
212        <div class="text-uppercase text-secondary small mb-2 px-2">Gestion des contacts</div>
213        <a href="<?= base_url('dashboard/contacts') ?>"><i class="bi bi-person-lines-fill"></i><span>Contacts</span></a>
214        <a href="<?= base_url('dashboard/segments') ?>"><i class="bi bi-diagram-2"></i><span>Segments</span></a>
215        <a href="<?= base_url('dashboard/contact-listes') ?>"><i class="bi bi-list-ul"></i><span>Listes</span></a>
216        <a href="<?= base_url('dashboard/expediteurs') ?>"><i class="bi bi-hdd-network"></i><span>Expéditeurs</span></a>
217    </div>
218
219    <!-- COMMUNICATION -->
220    <div class="menu-group mb-3">
221        <div class="text-uppercase text-secondary small mb-2 px-2">Communication</div>
222        <a href="<?= base_url('dashboard/notification/regles/liste') ?>"><i class="bi bi-diagram-3"></i><span>Règles</span></a>
223        <a href="<?= base_url('dashboard/notification/regles') ?>"><i class="bi bi-bezier2"></i><span>Rule Builder</span></a>
224        <a href="<?= base_url('dashboard/templates') ?>"><i class="bi bi-file-earmark-text"></i><span>Templates</span></a>
225    </div>
226
227    <!-- MONITORING -->
228    <div class="menu-group mb-3">
229        <div class="text-uppercase text-secondary small mb-2 px-2">Monitoring</div>
230        <a href="<?= base_url('dashboard/monitoring/overview') ?>"><i class="bi bi-activity"></i><span>Vue globale</span></a>
231        <a href="<?= base_url('dashboard/monitoring/queue') ?>"><i class="bi bi-hourglass"></i><span>File</span></a>
232        <a href="<?= base_url('dashboard/monitoring/success') ?>"><i class="bi bi-check2-circle"></i><span>Succès</span></a>
233        <a href="<?= base_url('dashboard/monitoring/failed') ?>"><i class="bi bi-x-circle"></i><span>Échecs</span></a>
234        <a href="<?= base_url('dashboard/monitoring/stats') ?>"><i class="bi bi-bar-chart-line"></i><span>Statistiques</span></a>
235        <a href="<?= base_url('dashboard/monitoring/logs') ?>"><i class="bi bi-journal-text"></i><span>Logs</span></a>
236    </div>
237
238    <!-- SYSTEM ACTIONS -->
239    <div class="mt-auto pt-3 border-top">
240        <a href="<?= base_url('dashboard/system') ?>" class="btn btn-dark w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
241            <i class="bi bi-gear-fill"></i><span>Système</span>
242        </a>
243        <a href="<?= base_url('dashboard/updater') ?>" class="btn btn-primary w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
244            <i class="bi bi-arrow-repeat"></i><span>Mise à jour</span>
245        </a>
246        <a href="<?= base_url('dashboard/support') ?>" class="btn btn-success w-100 d-flex align-items-center justify-content-center gap-2">
247            <i class="bi bi-life-preserver"></i><span>Support</span>
248        </a>
249    </div>
250</div>
251
252<!-- CONTENT -->
253<div class="content" id="content">
254
255    <!-- TOPBAR avec bouton toggle -->
256    <div class="topbar">
257        <div class="d-flex align-items-center gap-3">
258            <button class="btn btn-light border px-3 py-2" id="toggleSidebar" style="border-radius: 10px;">
259                <i class="bi bi-list fs-5"></i>
260            </button>
261            <h5 class="mb-0 fw-semibold text-dark"><?= $title ?? 'Tableau de bord' ?></h5>
262        </div>
263
264        <div class="dropdown">
265            <button class="btn btn-light border dropdown-toggle d-flex align-items-center gap-2 px-3 py-2" 
266                    data-bs-toggle="dropdown" style="border-radius: 10px;">
267                <i class="bi bi-person-circle fs-5"></i>
268                <span class="fw-medium">Administrateur</span>
269            </button>
270
271            <ul class="dropdown-menu dropdown-menu-end shadow" style="border-radius: 12px; min-width: 280px;">
272                <li class="dropdown-header">Paramètres globaux</li>
273                <li><a class="dropdown-item" href="<?= base_url('dashboard/profile') ?>"><i class="bi bi-people"></i> Profil</a></li>
274                <li><a class="dropdown-item" href="<?= base_url('dashboard/canaux') ?>"><i class="bi bi-gear-wide-connected"></i> Canaux</a></li>
275
276                <li class="dropdown-divider"></li>
277                <li class="dropdown-header">Providers</li>
278                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/sms') ?>"><i class="bi bi-chat-text"></i> SMS / OTP</a></li>
279                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/whatsapp') ?>"><i class="bi bi-whatsapp"></i> WhatsApp</a></li>
280                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/email') ?>"><i class="bi bi-envelope-at"></i> Email</a></li>
281
282                <li class="dropdown-divider"></li>
283                <li class="dropdown-header">Intégrations</li>
284                <li><a class="dropdown-item" href="<?= base_url('dashboard/integrations') ?>"><i class="bi bi-plug"></i> Liste des intégrations</a></li>
285
286                <li class="dropdown-divider"></li>
287                <li class="dropdown-header fw-bold">Orchestration système</li>
288                <li class="px-3 py-2">
289                    <a href="<?= base_url('dashboard/lancer/cron') ?>" class="btn btn-dark btn-sm w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
290                        <i class="bi bi-clock-history"></i> Exécuter le Cron
291                    </a>
292                    <a href="<?= base_url('dashboard/stream/file') ?>" class="btn btn-primary btn-sm w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
293                        <i class="bi bi-cpu"></i> Supervision
294                    </a>
295                    <a href="<?= base_url('dashboard/system/queue') ?>" class="btn btn-warning btn-sm w-100 d-flex align-items-center justify-content-center gap-2">
296                        <i class="bi bi-list-check"></i> File de traitement
297                    </a>
298                </li>
299
300                <li class="dropdown-divider"></li>
301                <li class="dropdown-header fw-bold">Partager</li>
302                <li class="px-3 py-2">
303                    <button type="button" data-bs-toggle="modal" data-bs-target="#shareAppModal"
304                            class="btn btn-warning btn-sm w-100 d-flex align-items-center justify-content-center gap-2">
305                        <i class="bi bi-share"></i> Partager l’application
306                    </button>
307                </li>
308
309                <li class="dropdown-divider"></li>
310                <li>
311                    <form action="/logout" method="post">
312                        <?= csrf_field() ?>
313                        <button class="dropdown-item text-danger" type="submit">
314                            <i class="bi bi-box-arrow-right"></i> Déconnexion
315                        </button>
316                    </form>
317                </li>
318            </ul>
319        </div>
320    </div>
321
322    <!-- Modal Partager -->
323    <div class="modal fade" id="shareAppModal" tabindex="-1">
324        <div class="modal-dialog modal-dialog-centered">
325            <div class="modal-content">
326                <div class="modal-header">
327                    <h5 class="modal-title">Partager l’application</h5>
328                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
329                </div>
330                <div class="modal-body text-center">
331                    <?php $url = base_url(); ?>
332                    <p class="small text-muted"><?= $url ?></p>
333                    <div class="d-grid gap-2">
334                        <a class="btn btn-success" href="https://wa.me/?text=<?= urlencode('Application : ' . $url) ?>" target="_blank">WhatsApp</a>
335                        <button class="btn btn-secondary" onclick="copyLink()">Copier le lien</button>
336                    </div>
337                </div>
338            </div>
339        </div>
340    </div>
341
342    <!-- CONTENU DES VUES ENFANTS -->
343    <?= $this->renderSection('content') ?>
344
345</div>
346
347<!-- JS -->
348<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
349
350<script>
351    // Restauration de l'état de la sidebar (déployée ou repliée) depuis localStorage
352    (function() {
353        const sidebar = document.getElementById('sidebar');
354        const content = document.getElementById('content');
355        const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';
356        if (isCollapsed) {
357            sidebar.classList.add('collapsed');
358            content.classList.add('expanded');
359        } else {
360            sidebar.classList.remove('collapsed');
361            content.classList.remove('expanded');
362        }
363    })();
364
365    // Gestion du bouton toggle
366    const toggleBtn = document.getElementById('toggleSidebar');
367    const sidebarEl = document.getElementById('sidebar');
368    const contentEl = document.getElementById('content');
369
370    function toggleSidebar() {
371        sidebarEl.classList.toggle('collapsed');
372        contentEl.classList.toggle('expanded');
373        const nowCollapsed = sidebarEl.classList.contains('collapsed');
374        localStorage.setItem('sidebarCollapsed', nowCollapsed);
375    }
376
377    if (toggleBtn) {
378        toggleBtn.addEventListener('click', toggleSidebar);
379    }
380
381    // Anti-flicker : réactivation des transitions après le chargement complet
382    window.addEventListener('DOMContentLoaded', function() {
383        document.body.classList.remove('preload-transitions-off');
384    });
385
386    // Fonction utilitaire pour copier le lien
387    window.copyLink = function() {
388        const url = '<?= base_url() ?>';
389        navigator.clipboard.writeText(url).then(() => {
390            alert('Lien copié dans le presse-papier');
391        }).catch(() => {
392            prompt('Copiez manuellement :', url);
393        });
394    };
395</script>
396
397</body>
398</html>