223 lines
14 KiB
PHP
223 lines
14 KiB
PHP
<?= $this->extend('layouts/main') ?>
|
|
|
|
<?= $this->section('styles') ?>
|
|
<link href="https://cdn.jsdelivr.net/npm/gridstack@10.3.1/dist/gridstack.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="<?= base_url('assets/css/dashboard.css') ?>">
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('content') ?>
|
|
<?php
|
|
$did = (int) $dashboard['id'];
|
|
$theme = (string) ($dashboard['theme'] ?? 'system');
|
|
$themeClass = match ($theme) {
|
|
'dark' => 'cb-theme-dark',
|
|
'light' => 'cb-theme-light',
|
|
default => 'cb-theme-system',
|
|
};
|
|
?>
|
|
<div class="cb-dash-shell <?= esc($themeClass) ?>" id="cbDashShell">
|
|
<div class="cb-dash-toolbar">
|
|
<div class="cb-dash-toolbar-head flex-grow-1 min-w-0">
|
|
<h1><?= esc($dashboard['name']) ?></h1>
|
|
<div class="cb-dash-meta"><?= esc($dashboard['description'] ?? '') ?></div>
|
|
</div>
|
|
<div class="cb-dash-toolbar-actions">
|
|
<div class="cb-dash-toolbar-primary d-flex flex-wrap align-items-center gap-1">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary d-none cb-dash-icon-btn" id="cbBtnDiscard" title="Discard unsaved layout changes" aria-label="Discard unsaved layout changes">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">undo</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-primary d-none cb-dash-icon-btn" id="cbBtnSaveLayout" title="Save layout" aria-label="Save layout">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">save</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-dark cb-dash-icon-btn" id="cbBtnToggleEdit" title="Edit layout" aria-label="Edit layout">
|
|
<span class="material-symbols-outlined cb-btn-icon cb-dash-toggle-icon cb-dash-toggle-icon--edit" aria-hidden="true">edit_square</span>
|
|
<span class="material-symbols-outlined cb-btn-icon cb-dash-toggle-icon cb-dash-toggle-icon--close d-none" aria-hidden="true">close</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary cb-dash-icon-btn" id="cbBtnAddWidget" data-bs-toggle="modal" data-bs-target="#modalAddWidget" hidden title="Add widget" aria-label="Add widget">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">add_box</span>
|
|
</button>
|
|
</div>
|
|
<div class="cb-dash-toolbar-secondary d-flex flex-wrap align-items-center gap-1">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary cb-dash-icon-btn d-none" id="cbBtnVarsModal" data-bs-toggle="modal" data-bs-target="#modalDashboardVariables" title="Dashboard variables" aria-label="Dashboard variables">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">tune</span>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary cb-dash-icon-btn cb-dash-fs-btn" id="cbBtnFs" title="Fullscreen" aria-label="Fullscreen">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">open_in_full</span>
|
|
</button>
|
|
<div class="dropdown">
|
|
<button type="button" class="btn btn-sm btn-outline-secondary cb-dash-icon-btn dropdown-toggle-hide-caret" id="cbDashPdfMenuBtn" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false" title="PDF export & page breaks" aria-label="PDF export and page break options">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">picture_as_pdf</span>
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-end shadow border-0 rounded-3 p-2 cb-dash-pdf-dd" aria-labelledby="cbDashPdfMenuBtn">
|
|
<li>
|
|
<button type="button" class="dropdown-item rounded-2 d-flex align-items-center gap-2 fw-semibold" id="cbBtnExportDashPdf">
|
|
<span class="material-symbols-outlined flex-shrink-0" style="font-size:20px;">picture_as_pdf</span>
|
|
Export PDF
|
|
</button>
|
|
</li>
|
|
<li><hr class="dropdown-divider my-2"></li>
|
|
<li class="px-2 pb-1">
|
|
<div class="form-check form-switch mb-0">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="cbChkDashPdfPageBreaks" checked>
|
|
<label class="form-check-label small" for="cbChkDashPdfPageBreaks">Page breaks in PDF</label>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<button type="button" class="btn btn-sm btn-outline-secondary cb-dash-icon-btn" data-bs-toggle="modal" data-bs-target="#modalShare" data-share-type="dashboard" data-resource-id="<?= $did ?>" title="Share" aria-label="Share dashboard">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">share</span>
|
|
</button>
|
|
<a href="<?= base_url('dashboard/settings/' . $did) ?>" class="btn btn-sm btn-outline-secondary cb-dash-icon-btn" title="Dashboard settings" aria-label="Dashboard settings">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">settings</span>
|
|
</a>
|
|
<a href="<?= base_url('dashboard') ?>" class="btn btn-sm btn-light border cb-dash-icon-btn" title="Back to all dashboards" aria-label="Back to all dashboards">
|
|
<span class="material-symbols-outlined cb-btn-icon" aria-hidden="true">arrow_back</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (session()->getFlashdata('success')): ?>
|
|
<div class="alert alert-success border-0 shadow-sm mb-3"><?= esc(session()->getFlashdata('success')) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="cb-dash-grid-wrap">
|
|
<div class="grid-stack" id="cbGridStack">
|
|
<?php foreach ($widgets as $w): ?>
|
|
<?php
|
|
$wid = (int) $w['id'];
|
|
$gx = (int) ($w['grid_x'] ?? 0);
|
|
$gy = (int) ($w['grid_y'] ?? 0);
|
|
$gw = max(1, (int) ($w['grid_w'] ?? 4));
|
|
$gh = max(1, (int) ($w['grid_h'] ?? 3));
|
|
?>
|
|
<div class="grid-stack-item" gs-id="<?= $wid ?>" gs-x="<?= $gx ?>" gs-y="<?= $gy ?>" gs-w="<?= $gw ?>" gs-h="<?= $gh ?>">
|
|
<div class="grid-stack-item-content p-0">
|
|
<div class="cb-dash-widget-mount" id="cb-w-<?= $wid ?>" data-widget-id="<?= $wid ?>"></div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="modalDashboardVariables" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow-lg rounded-4">
|
|
<div class="modal-header border-0">
|
|
<h2 class="modal-title h5 fw-bold">Dashboard variables</h2>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body pt-0">
|
|
<div class="cb-dash-global-vars d-none" id="cbDashGlobalVars"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Add widget -->
|
|
<div class="modal fade" id="modalAddWidget" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow-lg rounded-4">
|
|
<div class="modal-header border-0">
|
|
<h2 class="modal-title h5 fw-bold">Add widget</h2>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<label class="form-label fw-semibold">Type</label>
|
|
<select class="form-select" id="cbAddType">
|
|
<option value="chart">Chart</option>
|
|
<option value="text">Text (Markdown)</option>
|
|
<option value="image">Image</option>
|
|
<option value="filter_date">Filter · date range</option>
|
|
<option value="filter_dropdown">Filter · dropdown</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-8" id="cbAddRowChart">
|
|
<label class="form-label fw-semibold">Chart</label>
|
|
<select class="form-select" id="cbAddChartId">
|
|
<option value="">— Select —</option>
|
|
<?php foreach ($chartsList as $c): ?>
|
|
<option value="<?= (int) $c['id'] ?>"><?= esc($c['name']) ?> (<?= esc($c['chart_type'] ?? '') ?>)</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-12 d-none" id="cbAddRowText">
|
|
<label class="form-label fw-semibold">Markdown</label>
|
|
<textarea class="form-control" id="cbAddText" rows="4" placeholder="## Hello Your **story** here."></textarea>
|
|
</div>
|
|
<div class="col-12 d-none" id="cbAddRowImage">
|
|
<label class="form-label fw-semibold">Image URL</label>
|
|
<input type="url" class="form-control" id="cbAddImageUrl" placeholder="https://…">
|
|
<label class="form-label fw-semibold mt-2">Fit</label>
|
|
<select class="form-select" id="cbAddImageFit">
|
|
<option value="cover">Cover</option>
|
|
<option value="contain">Contain</option>
|
|
<option value="fill">Fill</option>
|
|
<option value="scale-down">Scale down</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6 d-none" id="cbAddRowFdate1">
|
|
<label class="form-label fw-semibold">Start variable</label>
|
|
<input type="text" class="form-control" id="cbAddFdateStart" value="date_from" pattern="[a-zA-Z0-9_]+">
|
|
</div>
|
|
<div class="col-md-6 d-none" id="cbAddRowFdate2">
|
|
<label class="form-label fw-semibold">End variable</label>
|
|
<input type="text" class="form-control" id="cbAddFdateEnd" value="date_to" pattern="[a-zA-Z0-9_]+">
|
|
</div>
|
|
<div class="col-md-6 d-none" id="cbAddRowFdrop1">
|
|
<label class="form-label fw-semibold">Variable name</label>
|
|
<input type="text" class="form-control" id="cbAddFdropVar" value="region" pattern="[a-zA-Z0-9_]+">
|
|
</div>
|
|
<div class="col-md-6 d-none" id="cbAddRowFdrop2">
|
|
<label class="form-label fw-semibold">Options (comma-separated)</label>
|
|
<input type="text" class="form-control" id="cbAddFdropOpt" placeholder="East, West, North">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label fw-semibold">Title override <span class="text-muted fw-normal">(optional)</span></label>
|
|
<input type="text" class="form-control" id="cbAddTitle" maxlength="200" placeholder="Shown above widget">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer border-0">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-dark" id="cbBtnConfirmAdd">Add to board</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="modalEditTextWidget" tabindex="-1" aria-labelledby="modalEditTextWidgetLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow-lg rounded-4">
|
|
<div class="modal-header border-0">
|
|
<h2 class="modal-title h5 fw-bold" id="modalEditTextWidgetLabel">Edit text widget</h2>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" id="cbEditTextWidgetId" value="">
|
|
<label class="form-label fw-semibold" for="cbEditTextMarkdown">Markdown</label>
|
|
<textarea class="form-control font-monospace" id="cbEditTextMarkdown" rows="10" placeholder="## Section title Your **markdown** here."></textarea>
|
|
</div>
|
|
<div class="modal-footer border-0">
|
|
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="button" class="btn btn-dark" id="cbBtnSaveTextWidget">Save</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script id="cb-dash-boot" type="application/json"><?= json_encode($dashBoot, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?></script>
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('scripts') ?>
|
|
<script src="https://cdn.jsdelivr.net/npm/gridstack@10.3.1/dist/gridstack-all.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.1/dist/jspdf.umd.min.js"></script>
|
|
<?= view('dashboard/_view_scripts') ?>
|
|
<?= $this->endSection() ?>
|