Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 60
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 Unique</span>
212        </a>
213        <div class="collapse ps-4 mt-1" id="menu-envois">
214            <a href="<?= base_url('dashboard/envoi/unique') ?>">Composer</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>Envois 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-layout-text-window"></i><span>Templates</span></a>
251        <a href="<?= base_url('dashboard/models') ?>"><i class="bi-collection"></i><span>Modèles</span></a>
252    </div>
253
254    <!-- MONITORING -->
255    <div class="menu-group mb-3">
256        <div class="text-uppercase text-secondary small mb-2 px-2">Monitoring</div>
257        <a href="<?= base_url('dashboard/monitoring/overview') ?>"><i class="bi bi-activity"></i><span>Vue globale</span></a>
258        <a href="<?= base_url('dashboard/monitoring/queue') ?>"><i class="bi bi-hourglass"></i><span>File</span></a>
259        <a href="<?= base_url('dashboard/monitoring/success') ?>"><i class="bi bi-check2-circle"></i><span>Succès</span></a>
260        <a href="<?= base_url('dashboard/monitoring/failed') ?>"><i class="bi bi-x-circle"></i><span>Échecs</span></a>
261        <a href="<?= base_url('dashboard/monitoring/stats') ?>"><i class="bi bi-bar-chart-line"></i><span>Statistiques</span></a>
262        <a href="<?= base_url('dashboard/monitoring/logs') ?>"><i class="bi bi-journal-text"></i><span>Logs</span></a>
263    </div>
264
265    <!-- SYSTEM ACTIONS -->
266    <div class="mt-auto pt-3 border-top">
267        <a href="<?= base_url('dashboard/system') ?>" class="btn btn-dark w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
268            <i class="bi bi-gear-fill"></i><span>Système</span>
269        </a>
270        <a href="<?= base_url('dashboard/updater') ?>" class="btn btn-primary w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
271            <i class="bi bi-arrow-repeat"></i><span>Mise à jour</span>
272        </a>
273        <a href="<?= base_url('dashboard/support') ?>" class="btn btn-success w-100 d-flex align-items-center justify-content-center gap-2">
274            <i class="bi bi-life-preserver"></i><span>Support</span>
275        </a>
276    </div>
277</div>
278
279<!-- CONTENT -->
280<div class="content" id="content">
281
282    <!-- TOPBAR (avec dropdown harmonisée) -->
283    <div class="topbar">
284        <div class="d-flex align-items-center gap-3">
285            <button class="btn btn-light border px-3 py-2" id="toggleSidebar" style="border-radius: 10px;">
286                <i class="bi bi-list fs-5"></i>
287            </button>
288            <h5 class="mb-0 fw-semibold text-dark"><?= $title ?? 'Tableau de bord' ?></h5>
289        </div>
290
291        <div class="dropdown">
292            <button class="btn btn-light border dropdown-toggle d-flex align-items-center gap-2 px-3 py-2" 
293                    data-bs-toggle="dropdown" style="border-radius: 10px;">
294                <i class="bi bi-person-circle fs-5"></i>
295                <span class="fw-medium">Administrateur</span>
296            </button>
297
298            <ul class="dropdown-menu dropdown-menu-end shadow" style="border-radius: 12px; min-width: 280px;">
299                <li class="dropdown-header">Paramètres globaux</li>
300                <li><a class="dropdown-item" href="<?= base_url('dashboard/profile') ?>"><i class="bi bi-people"></i> Profil</a></li>
301                <li><a class="dropdown-item" href="<?= base_url('dashboard/canaux') ?>"><i class="bi bi-gear-wide-connected"></i> Canaux</a></li>
302
303                <li class="dropdown-divider"></li>
304                <li class="dropdown-header">Providers</li>
305                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/sms') ?>"><i class="bi bi-chat-text"></i> SMS / OTP</a></li>
306                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/whatsapp') ?>"><i class="bi bi-whatsapp"></i> WhatsApp</a></li>
307                <li><a class="dropdown-item" href="<?= base_url('dashboard/fournisseurs/email') ?>"><i class="bi bi-envelope-at"></i> Email</a></li>
308
309                <li class="dropdown-divider"></li>
310                <li class="dropdown-header">Intégrations</li>
311                <li><a class="dropdown-item" href="<?= base_url('dashboard/integrations') ?>"><i class="bi bi-plug"></i> Liste</a></li>
312                <li><a class="dropdown-item" href="<?= base_url('dashboard/integrations') ?>"><i class="bi bi-puzzle"></i> Gestion</a></li>
313                <li class="dropdown-divider"></li>
314                <li class="dropdown-header fw-bold">Orchestration système</li>
315                <li class="px-3 py-2">
316                    <a href="<?= base_url('dashboard/taches') ?>" class="btn btn-dark btn-sm w-100 mb-2 d-flex align-items-center justify-content-center gap-2">
317                        <i class="bi bi-clock-history"></i> Exécuter une tâche
318                    </a>
319                    <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">
320                        <i class="bi bi-cpu"></i> Status tâches
321                    </a>
322                    <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">
323                        <i class="bi bi-list-check"></i> File de traitement
324                    </a>
325                </li>
326
327                <li class="dropdown-divider"></li>
328                <li class="dropdown-header fw-bold">Partager</li>
329                <li class="px-3 py-2">
330                    <button type="button" data-bs-toggle="modal" data-bs-target="#shareAppModal"
331                            class="btn btn-warning btn-sm w-100 d-flex align-items-center justify-content-center gap-2">
332                        <i class="bi bi-share"></i> Partager l’application
333                    </button>
334                </li>
335
336                <li class="dropdown-divider"></li>
337                <li>
338                    <form action="/logout" method="post">
339                        <?= csrf_field() ?>
340                        <button class="dropdown-item text-danger" type="submit">
341                            <i class="bi bi-box-arrow-right"></i> Déconnexion
342                        </button>
343                    </form>
344                </li>
345            </ul>
346        </div>
347    </div>
348
349    <!-- Modal Partager -->
350    <div class="modal fade" id="shareAppModal" tabindex="-1">
351        <div class="modal-dialog modal-dialog-centered">
352            <div class="modal-content">
353                <div class="modal-header">
354                    <h5 class="modal-title">Partager l’application</h5>
355                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
356                </div>
357                <div class="modal-body text-center">
358                    <?php $url = base_url(); ?>
359                    <p class="small text-muted"><?= $url ?></p>
360                    <div class="d-grid gap-2">
361                        <a class="btn btn-success" href="https://wa.me/?text=<?= urlencode('Application : ' . $url) ?>" target="_blank">WhatsApp</a>
362                        <button class="btn btn-secondary" onclick="copyLink()">Copier le lien</button>
363                    </div>
364                </div>
365            </div>
366        </div>
367    </div>
368
369    <!-- CONTENU DES VUES ENFANTS -->
370    <?= $this->renderSection('content') ?>
371
372</div>
373
374<!-- JS -->
375<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
376
377<script>
378    (function() {
379        const sidebar = document.getElementById('sidebar');
380        const content = document.getElementById('content');
381        const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';
382        if (isCollapsed) {
383            sidebar.classList.add('collapsed');
384            content.classList.add('expanded');
385        } else {
386            sidebar.classList.remove('collapsed');
387            content.classList.remove('expanded');
388        }
389    })();
390
391    const toggleBtn = document.getElementById('toggleSidebar');
392    const sidebarEl = document.getElementById('sidebar');
393    const contentEl = document.getElementById('content');
394
395    function toggleSidebar() {
396        sidebarEl.classList.toggle('collapsed');
397        contentEl.classList.toggle('expanded');
398        const nowCollapsed = sidebarEl.classList.contains('collapsed');
399        localStorage.setItem('sidebarCollapsed', nowCollapsed);
400    }
401
402    if (toggleBtn) {
403        toggleBtn.addEventListener('click', toggleSidebar);
404    }
405
406    window.addEventListener('DOMContentLoaded', function() {
407        document.body.classList.remove('preload-transitions-off');
408    });
409
410    window.copyLink = function() {
411        const url = '<?= base_url() ?>';
412        navigator.clipboard.writeText(url).then(() => {
413            alert('Lien copié dans le presse-papier');
414        }).catch(() => {
415            prompt('Copiez manuellement :', url);
416        });
417    };
418</script>
419
420</body>
421</html>