nhance/app/Views/meta_dashboard_demo_one.php

346 lines
13 KiB
PHP

<style>
.meta-dashboard-demo .filter-bar {
display: flex;
flex-wrap: wrap;
gap: 12px 20px;
align-items: flex-end;
background: #eef5fa;
border-radius: 10px;
padding: 10px 14px;
margin-bottom: 10px;
}
.meta-dashboard-demo .filter-field {
flex: 1 1 140px;
min-width: 120px;
max-width: 220px;
}
.meta-dashboard-demo .filter-field label {
display: block;
font-size: 12px;
font-weight: 700;
margin-bottom: 4px;
color: #111;
}
.meta-dashboard-demo .filter-field .select2-container {
width: 100% !important;
}
.meta-dashboard-demo .filter-field .select2-selection--single {
height: 32px !important;
min-height: 32px;
border: 1px solid #d8e2ea !important;
border-radius: 8px !important;
font-size: 12px;
}
.meta-dashboard-demo .filter-field .select2-selection__rendered {
line-height: 30px !important;
padding-left: 10px !important;
color: #333;
}
.meta-dashboard-demo .filter-field .select2-selection__arrow {
height: 30px !important;
}
.meta-dashboard-demo .filter-field .select2-container--disabled .select2-selection--single {
background-color: #f5f7f9 !important;
cursor: not-allowed;
opacity: 0.85;
}
.meta-dashboard-demo .dashboard-status {
font-size: 12px;
color: #5c6b7a;
padding: 4px 0 10px;
min-height: 20px;
}
.meta-dashboard-demo .dashboard-status.is-error { color: #c0392b; }
.meta-dashboard-demo .dashboard-status.is-loading { color: #0a8794; }
.meta-dashboard-demo #dashboard-container {
min-height: 480px;
}
.meta-dashboard-demo #dashboard-container metabase-dashboard {
display: block;
width: 100%;
min-height: 480px;
}
.meta-dashboard-demo .dashboard-placeholder {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
color: #7a8a99;
font-size: 13px;
background: #fff;
border: 1px dashed #d0dce6;
border-radius: 8px;
}
.emotion-65tole {
display: none !important;
}
</style>
<div class="row meta-dashboard-demo">
<div class="col-xl-12">
<div class="card">
<div class="card-body">
<!-- <h4 class="mb-3"><?= esc($page_name ?? 'Metabase Dashboard') ?></h4> -->
<div class="filter-bar">
<div class="filter-field">
<label for="filter-client">Client</label>
<select id="filter-client" class="form-control form-control-sm meta-filter-select2">
<option value="">Select</option>
</select>
</div>
<div class="filter-field">
<label for="filter-branch">Branch</label>
<select id="filter-branch" class="form-control form-control-sm meta-filter-select2" disabled>
<option value="">Select</option>
</select>
</div>
<div class="filter-field">
<label for="filter-policy">Policy</label>
<select id="filter-policy" class="form-control form-control-sm meta-filter-select2" disabled>
<option value="">Select</option>
</select>
</div>
</div>
<div id="dashboard-status" class="dashboard-status"></div>
<div id="dashboard-container">
<div class="dashboard-placeholder" id="dashboard-placeholder">
Select client, branch, and policy to load the dashboard.
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function defineMetabaseConfig(config) {
window.metabaseConfig = config;
}
defineMetabaseConfig({
theme: { preset: "light" },
isGuest: true,
instanceUrl: "<?= esc($metabaseUrl ?? '') ?>"
});
</script>
<script>
$(function () {
const CLIENTS_API = <?= json_encode(base_url('/util/getClientAndBranchAndPolicy')) ?>;
const DASHBOARD_API = <?= json_encode($dashboardApiUrl ?? base_url('/metaTpaDashboardDemo')) ?>;
const METABASE_URL = <?= json_encode($metabaseUrl ?? '') ?>;
const select2Options = {
placeholder: 'Select',
allowClear: true,
width: '100%',
minimumResultsForSearch: 0,
dropdownParent: $(document.body)
};
let branchByClient = {};
let policyByClient = {};
let embedScriptPromise = null;
const $elClient = $('#filter-client');
const $elBranch = $('#filter-branch');
const $elPolicy = $('#filter-policy');
const elStatus = document.getElementById('dashboard-status');
const elContainer = document.getElementById('dashboard-container');
const elPlaceholder = document.getElementById('dashboard-placeholder');
function setStatus(message, type) {
elStatus.textContent = message || '';
elStatus.className = 'dashboard-status' + (type ? ' is-' + type : '');
}
function initSelect2($select, disabled) {
if ($select.hasClass('select2-hidden-accessible')) {
$select.select2('destroy');
}
$select.prop('disabled', false);
$select.select2(select2Options);
if (disabled) {
$select.prop('disabled', true);
}
}
function rebuildSelect($select, placeholder, disabled, items, getValue, getLabel) {
if ($select.hasClass('select2-hidden-accessible')) {
$select.select2('destroy');
}
$select.empty().append($('<option>', { value: '', text: placeholder }));
(items || []).forEach(function (item) {
$select.append($('<option>', {
value: String(getValue(item)),
text: getLabel(item)
}));
});
$select.val('');
initSelect2($select, disabled);
}
function filtersComplete() {
return !!($elClient.val() && $elBranch.val() && $elPolicy.val());
}
function loadMetabaseEmbedScript() {
if (window.customElements && window.customElements.get('metabase-dashboard')) {
return Promise.resolve();
}
if (embedScriptPromise) {
return embedScriptPromise;
}
embedScriptPromise = new Promise(function (resolve, reject) {
const existing = document.querySelector('script[data-metabase-embed]');
if (existing) {
existing.addEventListener('load', function () { resolve(); }, { once: true });
existing.addEventListener('error', reject, { once: true });
return;
}
const script = document.createElement('script');
script.src = METABASE_URL + '/app/embed.js';
script.defer = true;
script.dataset.metabaseEmbed = '1';
script.onload = function () { resolve(); };
script.onerror = reject;
document.head.appendChild(script);
});
return embedScriptPromise;
}
function clearDashboard() {
const existing = elContainer.querySelector('metabase-dashboard');
if (existing) {
existing.remove();
}
if (elPlaceholder) {
elPlaceholder.style.display = '';
}
}
async function renderDashboard(token) {
clearDashboard();
if (!token) {
return;
}
await loadMetabaseEmbedScript();
if (elPlaceholder) {
elPlaceholder.style.display = 'none';
}
const dash = document.createElement('metabase-dashboard');
dash.setAttribute('token', token);
dash.setAttribute('with-title', 'true');
dash.setAttribute('with-downloads', 'true');
elContainer.appendChild(dash);
}
async function loadFilterData() {
setStatus('Loading clients…', 'loading');
try {
const res = await fetch(CLIENTS_API, { credentials: 'same-origin' });
const json = await res.json();
if (!json.status) {
setStatus('Could not load client list.', 'error');
return;
}
branchByClient = json.branch_data || {};
policyByClient = json.policyListByClient || {};
rebuildSelect($elClient, 'Select', false, json.client_data || [], function (c) { return c.id; }, function (c) {
return c.client_name || c.client_short_name || ('Client ' + c.id);
});
rebuildSelect($elBranch, 'Select', true);
rebuildSelect($elPolicy, 'Select', true);
setStatus('');
} catch (e) {
setStatus('Failed to load filter options.', 'error');
}
}
async function fetchDashboard(policyId) {
if (!filtersComplete()) {
clearDashboard();
setStatus('Select client, branch, and policy to load the dashboard.');
return;
}
if (!policyId) {
clearDashboard();
setStatus('Select client, branch, and policy to load the dashboard.');
return;
}
setStatus('Loading dashboard…', 'loading');
try {
const url = DASHBOARD_API + '?api=1&client_policy=' + encodeURIComponent(policyId);
const res = await fetch(url, { credentials: 'same-origin' });
const json = await res.json();
if (json.status !== 'success' || !json.data || !json.data.metabaseToken) {
clearDashboard();
setStatus(json.message || 'No dashboard available for this policy.', 'error');
return;
}
renderDashboard(json.data.metabaseToken);
setStatus('');
} catch (e) {
clearDashboard();
setStatus('Failed to load dashboard.', 'error');
}
}
$elClient.on('change', function () {
const clientId = $(this).val();
rebuildSelect($elBranch, 'Select', !clientId);
rebuildSelect($elPolicy, 'Select', true);
clearDashboard();
setStatus(clientId ? '' : 'Select client, branch, and policy to load the dashboard.');
if (!clientId) {
return;
}
const branches = branchByClient[clientId] || [];
rebuildSelect($elBranch, 'Select', branches.length === 0, branches, function (b) { return b.id; }, function (b) {
return b.branch_name || ('Branch ' + b.id);
});
});
$elBranch.on('change', function () {
const branchId = $(this).val();
const clientId = $elClient.val();
rebuildSelect($elPolicy, 'Select', !branchId);
clearDashboard();
setStatus(branchId ? '' : 'Select branch and policy to load the dashboard.');
if (!branchId || !clientId) {
return;
}
const policiesForClient = policyByClient[clientId] || [];
const policies = policiesForClient.filter(function (p) {
return String(p.client_branch_id) === String(branchId);
});
rebuildSelect($elPolicy, 'Select', policies.length === 0, policies, function (p) { return p.id; }, function (p) {
const type = p.policy_type || '';
const no = p.policy_no || '';
return (type && no) ? (type + ' - ' + no) : (no || type || ('Policy ' + p.id));
});
if (policies.length === 0) {
setStatus('No policies found for this branch.', 'error');
}
});
$elPolicy.on('change', function () {
fetchDashboard($(this).val());
});
initSelect2($elClient, false);
initSelect2($elBranch, true);
initSelect2($elPolicy, true);
loadFilterData();
});
</script>