chartboard/app/Views/dashboard/view.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 &amp; 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&#10;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&#10;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() ?>