Folder reorganize 1
This commit is contained in:
281
personal/dashboard.php
Normal file
281
personal/dashboard.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php // phpcs:ignoreFile
|
||||
|
||||
require __DIR__ . '/../session.php';
|
||||
require __DIR__ . '/../userAccess.php';
|
||||
|
||||
$pageTitle = 'My Dashboard';
|
||||
$pageSubtitle = 'Monitor the tags you care about';
|
||||
$pageDescription = 'Your personal workspace for saved historian tags and quick-launch tools.';
|
||||
$assetBasePath = '../';
|
||||
$layoutReturnUrl = '../overview.php';
|
||||
$layoutReturnLabel = 'Back to overview';
|
||||
|
||||
require __DIR__ . '/../includes/layout/header.php';
|
||||
?>
|
||||
|
||||
<div class="app-content">
|
||||
<section class="data-panel personal-dashboard-panel">
|
||||
<header class="personal-dashboard-panel__header">
|
||||
<div>
|
||||
<h2>Personal dashboard</h2>
|
||||
<p id="personalDashboardSummary">
|
||||
Loading your saved widgets...
|
||||
</p>
|
||||
</div>
|
||||
<div class="personal-dashboard-panel__actions">
|
||||
<button
|
||||
type="button"
|
||||
class="button button--ghost"
|
||||
id="personalDashboardRefresh"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div
|
||||
id="personalDashboardStatus"
|
||||
class="personal-dashboard__status personal-dashboard__status--loading"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
>
|
||||
Fetching saved selections...
|
||||
</div>
|
||||
|
||||
<div id="personalDashboardGrid" class="personal-dashboard__grid" aria-live="polite"></div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const personalDashboardState = {
|
||||
dashboardKey: 'default',
|
||||
widgets: [],
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
const summaryElement = document.getElementById('personalDashboardSummary');
|
||||
const statusElement = document.getElementById('personalDashboardStatus');
|
||||
const gridElement = document.getElementById('personalDashboardGrid');
|
||||
const refreshButton = document.getElementById('personalDashboardRefresh');
|
||||
|
||||
function setStatus(message, type) {
|
||||
const stateClass = type === 'error'
|
||||
? 'personal-dashboard__status--error'
|
||||
: type === 'empty'
|
||||
? 'personal-dashboard__status--empty'
|
||||
: 'personal-dashboard__status--loading';
|
||||
|
||||
statusElement.textContent = message;
|
||||
statusElement.className = `personal-dashboard__status ${stateClass}`;
|
||||
}
|
||||
|
||||
function formatInterval(ms) {
|
||||
if (ms < 1000) {
|
||||
return `${ms} ms`;
|
||||
}
|
||||
|
||||
const seconds = ms / 1000;
|
||||
if (seconds < 60) {
|
||||
return `${seconds.toFixed(0)}s`;
|
||||
}
|
||||
|
||||
const minutes = seconds / 60;
|
||||
return `${minutes.toFixed(1)} min`;
|
||||
}
|
||||
|
||||
function formatTimeWindow(minutes) {
|
||||
if (minutes < 60) {
|
||||
return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
|
||||
}
|
||||
|
||||
const hours = (minutes / 60).toFixed(1);
|
||||
return `${hours.replace(/\.0$/, '')} hour${hours !== '1' ? 's' : ''}`;
|
||||
}
|
||||
|
||||
function buildLaunchUrl(widget) {
|
||||
const base = '../trends/live/autochart.php';
|
||||
const params = new URLSearchParams({
|
||||
primary: widget.tag_name,
|
||||
autostart: '1',
|
||||
});
|
||||
|
||||
params.set('interval', widget.update_interval_ms);
|
||||
|
||||
if (widget.display_label) {
|
||||
params.set('primary_label', widget.display_label);
|
||||
}
|
||||
|
||||
if (widget.time_window_minutes) {
|
||||
params.set('window', widget.time_window_minutes);
|
||||
}
|
||||
|
||||
return `${base}?${params.toString()}`;
|
||||
}
|
||||
|
||||
function renderWidgetCard(widget) {
|
||||
const card = document.createElement('article');
|
||||
card.className = 'personal-card';
|
||||
|
||||
const header = document.createElement('header');
|
||||
header.className = 'personal-card__header';
|
||||
|
||||
const title = document.createElement('div');
|
||||
title.className = 'personal-card__title';
|
||||
|
||||
const tagName = document.createElement('span');
|
||||
tagName.className = 'personal-card__tag';
|
||||
tagName.textContent = widget.display_label || widget.tag_name;
|
||||
title.appendChild(tagName);
|
||||
|
||||
if (widget.display_label && widget.display_label !== widget.tag_name) {
|
||||
const subtitle = document.createElement('span');
|
||||
subtitle.className = 'personal-card__subtitle';
|
||||
subtitle.textContent = widget.tag_name;
|
||||
title.appendChild(subtitle);
|
||||
}
|
||||
|
||||
header.appendChild(title);
|
||||
|
||||
if (widget.series_color) {
|
||||
const swatch = document.createElement('span');
|
||||
swatch.className = 'personal-card__swatch';
|
||||
swatch.setAttribute('title', `Series color ${widget.series_color}`);
|
||||
swatch.style.setProperty('--personal-series-color', widget.series_color);
|
||||
header.appendChild(swatch);
|
||||
}
|
||||
|
||||
card.appendChild(header);
|
||||
|
||||
const meta = document.createElement('dl');
|
||||
meta.className = 'personal-card__meta';
|
||||
|
||||
const metaItems = [
|
||||
['Widget type', widget.widget_type],
|
||||
['Update interval', formatInterval(widget.update_interval_ms)],
|
||||
['Time window', formatTimeWindow(widget.time_window_minutes)],
|
||||
['Axis preference', widget.preferred_axis],
|
||||
['Rollup', widget.rollup_function],
|
||||
];
|
||||
|
||||
metaItems.forEach((item) => {
|
||||
const [label, value] = item;
|
||||
const dt = document.createElement('dt');
|
||||
dt.textContent = label;
|
||||
const dd = document.createElement('dd');
|
||||
dd.textContent = value || '—';
|
||||
meta.appendChild(dt);
|
||||
meta.appendChild(dd);
|
||||
});
|
||||
|
||||
if (widget.scale_min !== null || widget.scale_max !== null) {
|
||||
const dt = document.createElement('dt');
|
||||
dt.textContent = 'Manual scale';
|
||||
const minValue = widget.scale_min !== null ? widget.scale_min : 'auto';
|
||||
const maxValue = widget.scale_max !== null ? widget.scale_max : 'auto';
|
||||
const dd = document.createElement('dd');
|
||||
dd.textContent = `${minValue} → ${maxValue}`;
|
||||
meta.appendChild(dt);
|
||||
meta.appendChild(dd);
|
||||
}
|
||||
|
||||
if (widget.threshold_low !== null || widget.threshold_high !== null) {
|
||||
const dt = document.createElement('dt');
|
||||
dt.textContent = 'Thresholds';
|
||||
const lowValue = widget.threshold_low !== null ? widget.threshold_low : '—';
|
||||
const highValue = widget.threshold_high !== null ? widget.threshold_high : '—';
|
||||
const dd = document.createElement('dd');
|
||||
dd.textContent = `${lowValue} / ${highValue}`;
|
||||
meta.appendChild(dt);
|
||||
meta.appendChild(dd);
|
||||
}
|
||||
|
||||
card.appendChild(meta);
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'personal-card__actions';
|
||||
|
||||
const launchLink = document.createElement('a');
|
||||
launchLink.className = 'button button--success';
|
||||
launchLink.href = buildLaunchUrl(widget);
|
||||
launchLink.target = '_blank';
|
||||
launchLink.rel = 'noopener';
|
||||
launchLink.textContent = 'Open live chart';
|
||||
actions.appendChild(launchLink);
|
||||
|
||||
const details = document.createElement('div');
|
||||
details.className = 'personal-card__details';
|
||||
details.textContent = 'Future updates will let you pin tables, gauges, and alarms here.';
|
||||
actions.appendChild(details);
|
||||
|
||||
card.appendChild(actions);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
function renderDashboard() {
|
||||
gridElement.innerHTML = '';
|
||||
|
||||
if (!personalDashboardState.widgets.length) {
|
||||
setStatus('No saved widgets yet. Use the upcoming editor to add your favorite tags.', 'empty');
|
||||
summaryElement.textContent = 'Nothing is saved for this dashboard yet.';
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(`${personalDashboardState.widgets.length} widget(s) loaded.`, 'complete');
|
||||
summaryElement.textContent = `Showing ${personalDashboardState.widgets.length} saved widget(s).`;
|
||||
|
||||
personalDashboardState.widgets.forEach((widget) => {
|
||||
gridElement.appendChild(renderWidgetCard(widget));
|
||||
});
|
||||
}
|
||||
|
||||
async function loadDashboard() {
|
||||
if (personalDashboardState.isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
personalDashboardState.isLoading = true;
|
||||
setStatus('Fetching saved selections...', 'loading');
|
||||
summaryElement.textContent = 'Loading your saved widgets...';
|
||||
|
||||
try {
|
||||
const url = new URL(
|
||||
'../data/personal/get_user_tags.php',
|
||||
window.location.href
|
||||
);
|
||||
url.searchParams.set('dashboard', personalDashboardState.dashboardKey);
|
||||
|
||||
const response = await fetch(url.toString(), { cache: 'no-cache' });
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
if (!payload.success) {
|
||||
throw new Error(payload.error || 'Unable to load personal dashboard');
|
||||
}
|
||||
|
||||
personalDashboardState.widgets = Array.isArray(payload.widgets)
|
||||
? payload.widgets
|
||||
: [];
|
||||
|
||||
renderDashboard();
|
||||
} catch (error) {
|
||||
console.error('Failed to load personal dashboard:', error);
|
||||
setStatus(`Could not load widgets: ${error.message}`, 'error');
|
||||
summaryElement.textContent = 'Personal dashboard failed to load.';
|
||||
} finally {
|
||||
personalDashboardState.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadDashboard();
|
||||
});
|
||||
|
||||
refreshButton.addEventListener('click', () => {
|
||||
loadDashboard();
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php require __DIR__ . '/../includes/layout/footer.php'; ?>
|
||||
Reference in New Issue
Block a user