add all files

This commit is contained in:
Rucus
2026-02-17 09:29:34 -06:00
parent b8c8d67c67
commit 782d203799
21925 changed files with 2433086 additions and 0 deletions

View 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'; ?>