Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 59
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        .sidebar.collapsed a span,
67        .sidebar.collapsed .text-uppercase {
68            display: none;
69        }
70
71        /* ====================== LOGO ====================== */
72        .logo-container {
73            height: 48px;
74            display: flex;
75            align-items: center;
76            margin-bottom: 1.5rem;
77            transition: none;
78        }
79        .logo-full {
80            max-width: 215px;
81            max-height: 100%;
82            width: auto;
83            height: auto;
84            display: block;
85        }
86        .logo-icon {
87            display: none;
88            width: 32px;
89            height: 32px;
90            object-fit: contain;
91        }
92        .sidebar.collapsed .logo-full {
93            display: none;
94        }
95        .sidebar.collapsed .logo-icon {
96            display: block;
97            margin: 0 auto;
98        }
99        .sidebar.collapsed .logo-container {
100            justify-content: center;
101        }
102
103        /* ====================== MAIN CONTENT ====================== */
104        .content {
105            margin-left: 260px;
106            padding: 25px 30px;
107            transition: margin-left 0.25s ease;
108            min-height: 100vh;
109        }
110        .content.expanded {
111            margin-left: 70px;
112        }
113
114        /* ====================== TOPBAR ====================== */
115        .topbar {
116            background: #fff;
117            padding: 14px 24px;
118            border-radius: 14px;
119            margin-bottom: 24px;
120            box-shadow: 0 4px 14px rgba(0, 0, 0, 0.04);
121            border: 1px solid #f0f2f7;
122            display: flex;
123            justify-content: space-between;
124            align-items: center;
125        }
126
127        /* ====================== STYLE DROPDOWN (harmonisé avec la sidebar) ====================== */
128        .topbar .dropdown-toggle {
129            background-color: #1f2937;
130            border-color: #374151;
131            color: #fff;
132        }
133        .topbar .dropdown-toggle:hover {
134            background-color: #374151;
135        }
136        .dropdown-menu {
137            background-color: #111827;
138            border: 1px solid #1f2937;
139            border-radius: 12px;
140            box-shadow: 0 4px 14px rgba(0,0,0,0.3);
141        }
142        .dropdown-menu .dropdown-header {
143            color: #9ca3af;
144            background-color: transparent;
145            font-size: 0.75rem; /* taille conservée */
146        }
147        .dropdown-menu .dropdown-item {
148            color: #cbd5e1;
149            font-size: 0.9rem; /* taille conservée */
150        }
151        .dropdown-menu .dropdown-item:hover {
152            background-color: #1f2937;
153            color: #fff;
154        }
155        .dropdown-menu .dropdown-divider {
156            border-top-color: #1f2937;
157        }
158        /* Conservation des boutons internes au menu (Cron, Supervision, etc.) */
159        .dropdown-menu .btn-dark, 
160        .dropdown-menu .btn-primary, 
161        .dropdown-menu .btn-warning {
162            /* styles Bootstrap conservés */
163        }
164
165        /* ====================== MONITOR WRAP ====================== */
166        .monitor-wrap {
167            max-width: 1800px;
168            margin: 0 auto;
169            padding: 0;
170        }
171
172        .sidebar::-webkit-scrollbar { width: 0; }
173        .sidebar { scrollbar-width: none; }
174
175        body.preload-transitions-off * {
176            transition: none !important;
177        }
178    </style>
179</head>
180<body class="preload-transitions-off">
181
182<!-- SIDEBAR (identique) -->
183<div class="sidebar p-3" id="sidebar">
184    <div class="logo-container">
185        <?php
186        $logoPath = FCPATH . 'assets/img/logo.png';
187        $logoData = '';
188        if (file_exists($logoPath)) {
189            $mime = mime_content_type($logoPath);
190            $base64 = base64_encode(file_get_contents($logoPath));
191            $logoData = 'data:' . $mime . ';base64,' . $base64;
192        } else {
193            $logoData = base_url('assets/img/logo.png');
194        }
195        ?>
196        <img src="<?= $logoData ?>" class="logo-full" alt="logo">
197        <img src="<?= base_url('assets/img/logo-icon.png') ?>" class="logo-icon" alt="icon">
198    </div>
199
200    <!-- DASHBOARD -->
201    <div class="menu-group mb-3">
202        <a href="<?= base_url('dashboard') ?>">
203            <i class="bi bi-speedometer2"></i><span>Tableau de bord</span>
204        </a>
205    </div>
206
207    <!-- GESTION DES ENVOIS -->
208    <div class="menu-group mb-3">
209        <div class="text-uppercase text-secondary small mb-2 px-2">Gestion des envois</div>
210        <a data-bs-toggle="collapse" href="#menu-envois">
211            <i class="bi bi-send"></i><span>Envois</span>
212        </a>
213        <div class="collapse ps-4 mt-1" id="menu-envois">
214            <a href="<?= base_url('dashboard/envoi/unique') ?>">Unique</a>
215            <a href="<?= base_url('dashboard/envoi/unique/brouillons') ?>">Brouillons</a>
216            <a href="<?= base_url('dashboard/envoi/unique/envoyes') ?>">Envoyés</a>
217        </div>
218        <a data-bs-toggle="collapse" href="#menu-masse">
219            <i class="bi bi-stack"></i><span>Masse</span>
220        </a>
221        <div class="collapse ps-4 mt-1" id="menu-masse">
222            <a href="<?= base_url('dashboard/envoi/masse') ?>">Composer</a>
223            <a href="<?= base_url('dashboard/envoi/masse/brouillons') ?>">Brouillons</a>
224            <a href="<?= base_url('dashboard/envoi/masse/envoyes') ?>">Envoyés</a>
225        </div>
226        <a data-bs-toggle="collapse" href="#menu-campagne">
227            <i class="bi bi-megaphone"></i><span>Campagne</span>
228        </a>
229        <div class="collapse ps-4 mt-1" id="menu-campagne">
230            <a href="<?= base_url('dashboard/campagnes/lancer') ?>">Lancer</a>
231            <a href="<?= base_url('dashboard/campagnes') ?>">Liste</a>
232            <a href="<?= base_url('dashboard/campagne/statistiques') ?>">Statistiques</a>
233        </div>
234    </div>
235
236    <!-- GESTION DES CONTACTS -->
237    <div class="menu-group mb-3">
238        <div class="text-uppercase text-secondary small mb-2 px-2">Gestion des contacts</div>
239        <a href="<?= base_url('dashboard/contacts') ?>"><i class="bi bi-person-lines-fill"></i><span>Contacts</span></a>
240        <a href="<?= base_url('dashboard/segments') ?>"><i class="bi bi-diagram-2"></i><span>Segments</span></a>
241        <a href="<?= base_url('dashboard/contact-listes') ?>"><i class="bi bi-list-ul"></i><span>Listes</span></a>
242        <a href="<?= base_url('dashboard/expediteurs') ?>"><i class="bi bi-hdd-network"></i><span>Expéditeurs</span></a>
243    </div>
244
245    <!-- COMMUNICATION -->
246    <div class="menu-group mb-3">
247        <div class="text-uppercase text-secondary small mb-2 px-2">Communication</div>
248        <a href="<?= base_url('dashboard/notification/regles/liste') ?>"><i class="bi bi-diagram-3"></i><span>Règles</span></a>
249        <a href="<?= base_url('dashboard/notification/regles') ?>"><i class="bi bi-bezier2"></i><span>Rule Builder</span></a>
250        <a href="<?= base_url('dashboard/templates') ?>"><i class="bi bi-file-earmark-text"></i><span>Templates</span></a>
251    </div>
252
253    <!-- MONITORING -->
254    <div class="menu-group mb-3">
255        <div class="text-uppercase text-secondary small mb-2 px-2">Monitoring</div>
256        <a href="<?= base_url('dashboard/monitoring/overview') ?>"><i class="bi bi-activity"></i><span>Vue globale</span></a>
257        <a href="<?= base_url('dashboard/monitoring/queue') ?>"><i class="bi bi-hourglass"></i><span>File</span></a>
258        <a href="<?= base_url('dashboard/monitoring/success') ?>"><i class="bi bi-check2-circle"></i><span>Succès</span></a>
259        <a href="<?= base_url('dashboard/monitoring/failed') ?>"><i class="bi bi-x-circle"></i><span>Échecs</span></a>
260        <a href="<?= base_url('dashboard/monitoring/stats') ?>"><i class="bi bi-bar-chart-line"></i><span>Statistiques</span></a>
261        <a href="<?= base_url('dashboard/monitoring/logs') ?>"><i class="bi bi-journal-text"></i><span>Logs</span></a>
262    </div>
263
264    <!-- SYSTEM ACTIONS -->
265    <div class="mt-auto pt-3 border-top">
266        <a href="<?= base_url('dashboard/system') ?>" class="btn btn-dark w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
267            <i class="bi bi-gear-fill"></i><span>Système</span>
268        </a>
269        <a href="<?= base_url('dashboard/updater') ?>" class="btn btn-primary w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
270            <i class="bi bi-arrow-repeat"></i><span>Mise à jour</span>
271        </a>
272        <a href="<?= base_url('dashboard/support') ?>" class="btn btn-success w-100 d-flex align-items-center justify-content-center gap-2">
273            <i class="bi bi-life-preserver"></i><span>Support</span>
274        </a>
275    </div>
276</div>
277
278<!-- CONTENT -->
279<div class="content" id="content">
280
281    <!-- TOPBAR (avec dropdown harmonisée) -->
282    <div class="topbar">
283        <div class="d-flex align-items-center gap-3">
284            <button class="btn btn-light border px-3 py-2" id="toggleSidebar" style="border-radius: 10px;">
285                <i class="bi bi-list fs-5"></i>
286            </button>
287            <h5 class="mb-0 fw-semibold text-dark"><?= $title ?? 'Tableau de bord' ?></h5>
288        </div>
289
290        <div class="dropdown">
291            <button class="btn btn-light border dropdown-toggle d-flex align-items-center gap-2 px-3 py-2" 
292                    data-bs-toggle="dropdown" style="border-radius: 10px;">
293                <i class="bi bi-person-circle fs-5"></i>
294                <span class="fw-medium">Administrateur</span>
295            </button>
296
297            <ul class="dropdown-menu dropdown-menu-end shadow" style="border-radius: 12px; min-width: 280px;">
298                <li class="dropdown-header">Paramètres globaux</li>
299                <li><a class="dropdown-item" href="<?= base_url('dashboard/profile') ?>"><i class="bi bi-people"></i> Profil</a></li>
300                <li><a class="dropdown-item" href="<?= base_url('dashboard/canaux') ?>"><i class="bi bi-gear-wide-connected"></i> Canaux</a></li>
301
302                <li class="dropdown-divider"></li>
303                <li class="dropdown-header">Providers</li>
304                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/sms') ?>"><i class="bi bi-chat-text"></i> SMS / OTP</a></li>
305                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/whatsapp') ?>"><i class="bi bi-whatsapp"></i> WhatsApp</a></li>
306                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/email') ?>"><i class="bi bi-envelope-at"></i> Email</a></li>
307
308                <li class="dropdown-divider"></li>
309                <li class="dropdown-header">Intégrations</li>
310                <li><a class="dropdown-item" href="<?= base_url('dashboard/integrations') ?>"><i class="bi bi-plug"></i> Liste</a></li>
311                <li><a class="dropdown-item" href="<?= base_url('dashboard/integrations') ?>"><i class="bi bi-puzzle"></i> Gestion</a></li>
312                <li class="dropdown-divider"></li>
313                <li class="dropdown-header fw-bold">Orchestration système</li>
314                <li class="px-3 py-2">
315                    <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">
316                        <i class="bi bi-clock-history"></i> Exécuter le Cron
317                    </a>
318                    <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">
319                        <i class="bi bi-cpu"></i> Supervision
320                    </a>
321                    <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">
322                        <i class="bi bi-list-check"></i> File de traitement
323                    </a>
324                </li>
325
326                <li class="dropdown-divider"></li>
327                <li class="dropdown-header fw-bold">Partager</li>
328                <li class="px-3 py-2">
329                    <button type="button" data-bs-toggle="modal" data-bs-target="#shareAppModal"
330                            class="btn btn-warning btn-sm w-100 d-flex align-items-center justify-content-center gap-2">
331                        <i class="bi bi-share"></i> Partager l’application
332                    </button>
333                </li>
334
335                <li class="dropdown-divider"></li>
336                <li>
337                    <form action="/logout" method="post">
338                        <?= csrf_field() ?>
339                        <button class="dropdown-item text-danger" type="submit">
340                            <i class="bi bi-box-arrow-right"></i> Déconnexion
341                        </button>
342                    </form>
343                </li>
344            </ul>
345        </div>
346    </div>
347
348    <!-- Modal Partager -->
349    <div class="modal fade" id="shareAppModal" tabindex="-1">
350        <div class="modal-dialog modal-dialog-centered">
351            <div class="modal-content">
352                <div class="modal-header">
353                    <h5 class="modal-title">Partager l’application</h5>
354                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
355                </div>
356                <div class="modal-body text-center">
357                    <?php $url = base_url(); ?>
358                    <p class="small text-muted"><?= $url ?></p>
359                    <div class="d-grid gap-2">
360                        <a class="btn btn-success" href="https://wa.me/?text=<?= urlencode('Application : ' . $url) ?>" target="_blank">WhatsApp</a>
361                        <button class="btn btn-secondary" onclick="copyLink()">Copier le lien</button>
362                    </div>
363                </div>
364            </div>
365        </div>
366    </div>
367
368    <!-- CONTENU DES VUES ENFANTS -->
369    <?= $this->renderSection('content') ?>
370
371</div>
372
373<!-- JS -->
374<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
375
376<script>
377    (function() {
378        const sidebar = document.getElementById('sidebar');
379        const content = document.getElementById('content');
380        const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';
381        if (isCollapsed) {
382            sidebar.classList.add('collapsed');
383            content.classList.add('expanded');
384        } else {
385            sidebar.classList.remove('collapsed');
386            content.classList.remove('expanded');
387        }
388    })();
389
390    const toggleBtn = document.getElementById('toggleSidebar');
391    const sidebarEl = document.getElementById('sidebar');
392    const contentEl = document.getElementById('content');
393
394    function toggleSidebar() {
395        sidebarEl.classList.toggle('collapsed');
396        contentEl.classList.toggle('expanded');
397        const nowCollapsed = sidebarEl.classList.contains('collapsed');
398        localStorage.setItem('sidebarCollapsed', nowCollapsed);
399    }
400
401    if (toggleBtn) {
402        toggleBtn.addEventListener('click', toggleSidebar);
403    }
404
405    window.addEventListener('DOMContentLoaded', function() {
406        document.body.classList.remove('preload-transitions-off');
407    });
408
409    window.copyLink = function() {
410        const url = '<?= base_url() ?>';
411        navigator.clipboard.writeText(url).then(() => {
412            alert('Lien copié dans le presse-papier');
413        }).catch(() => {
414            prompt('Copiez manuellement :', url);
415        });
416    };
417</script>
418
419</body>
420</html>