This commit is contained in:
Mateusz Gruszczyński
2026-03-06 10:06:14 +01:00
parent e8f6c4c609
commit 7b8a81dc3b
28 changed files with 1270 additions and 312 deletions

View File

@@ -4,7 +4,7 @@
<div class="d-flex align-items-center justify-content-between mb-3">
<div>
<h1 class="h3 mb-0">Dashboards</h1>
<div class="text-muted">Your monitoring dashboards.</div>
<div class="text-muted">Your monitoring dashboards</div>
</div>
<a class="btn btn-primary" href="{{ url_for('dashboards.new') }}"><i class="fa-solid fa-plus me-1"></i>New dashboard</a>
</div>
@@ -15,7 +15,13 @@
<div class="card shadow-sm h-100">
<div class="card-body">
<div class="fw-semibold">{{ d.name }}</div>
<div class="text-muted small">{{ d.description or '' }}</div>
<div class="text-muted small mb-3">{{ d.description or '' }}</div>
<div class="d-flex flex-wrap gap-2 small">
<span class="badge text-bg-light">{{ d.widgets_count }} widgets</span>
<span class="badge text-bg-light">{{ d.devices_count }} devices</span>
<span class="badge text-bg-light">{{ d.charts_count }} charts</span>
<span class="badge text-bg-light">{{ d.tables_count }} tables</span>
</div>
</div>
<div class="card-footer bg-white border-0 pt-0 pb-3 px-3">
<a class="btn btn-outline-primary btn-sm" href="{{ url_for('dashboards.view', dashboard_id=d.id) }}">Open <i class="fa-solid fa-arrow-right ms-1"></i></a>

View File

@@ -31,13 +31,20 @@ window.MIKROMON = {
{% for w in widgets %}
<div class="col-12 col-lg-6">
<div class="card shadow-sm h-100">
<div class="card-header bg-white">
<div class="card-header bg-white d-flex align-items-center justify-content-between gap-2 flex-wrap">
<div class="fw-semibold">{{ w.title }}</div>
{% if w.widget_type != 'table' %}
<select class="form-select form-select-sm" data-range-widget="{{ w.id }}" style="width:auto">
<option value="1m">1m</option>
<option value="10m">10m</option>
<option value="1h" selected>1h</option>
<option value="3h">3h</option>
<option value="10h">10h</option>
</select>
{% endif %}
</div>
<div class="card-body">
{% if w.widget_type == 'table' %}
<div class="table-responsive">
<table class="table table-sm align-middle mb-0" data-table-widget="{{ w.id }}">
@@ -46,7 +53,7 @@ window.MIKROMON = {
</table>
</div>
{% else %}
<div class="chart-wrap">
<div class="chart-wrap" style="height: {{ w.height_px or 260 }}px;">
<canvas id="chart-{{ w.id }}"></canvas>
</div>
<div class="text-muted small mt-2" id="meta-{{ w.id }}">

View File

@@ -14,7 +14,7 @@
<div class="card shadow-sm">
<div class="card-body">
<div class="fw-semibold mb-2"><i class="fa-solid fa-user-plus me-2"></i>Share with user</div>
<form method="post" action="{{ url_for('dashboards.share_post', dashboard_id=dashboard.id) }}">
<form method="post" action="{{ url_for('dashboards.share_add', dashboard_id=dashboard.id) }}">
{{ form.hidden_tag() }}
<div class="row g-2">
<div class="col-12 col-md-7">{{ form.email(class_="form-control", placeholder="email@example.com") }}</div>
@@ -46,18 +46,21 @@
<div class="card shadow-sm">
<div class="card-body">
<div class="fw-semibold mb-2"><i class="fa-solid fa-link me-2"></i>Public link</div>
{% if public %}
{% if public_link %}
<div class="alert alert-success small">
<a href="{{ url_for('dashboards.public_view', token=public.token) }}" target="_blank">{{ url_for('dashboards.public_view', token=public.token, _external=true) }}</a>
<a href="{{ url_for('dashboards.public_view', token=public_link.token) }}" target="_blank">{{ url_for('dashboards.public_view', token=public_link.token, _external=true) }}</a>
</div>
<form method="post" action="{{ url_for('dashboards.public_link_delete', dashboard_id=dashboard.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-outline-danger" type="submit"><i class="fa-solid fa-trash me-1"></i>Delete link</button>
</form>
{% else %}
<div class="alert alert-secondary small">No active public link.</div>
<form method="post" action="{{ url_for('dashboards.public_link_create', dashboard_id=dashboard.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-outline-primary" type="submit"><i class="fa-solid fa-link me-1"></i>Create link</button>
</form>
{% endif %}
<form method="post" action="{{ url_for('dashboards.share_public', dashboard_id=dashboard.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-outline-primary" type="submit"><i class="fa-solid fa-rotate me-1"></i>Create / refresh</button>
</form>
</div>
</div>
</div>

View File

@@ -19,12 +19,23 @@
{% for w in widgets %}
<div class="col-12 col-lg-{{ w.col_span or 6 }}">
<div class="card shadow-sm h-100">
<div class="card-header bg-white d-flex align-items-center justify-content-between">
<div class="card-header bg-white d-flex align-items-center justify-content-between gap-2 flex-wrap">
<div class="fw-semibold">{{ w.title }}</div>
<form method="post" action="{{ url_for('dashboards.widget_delete', dashboard_id=dashboard.id, widget_id=w.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-sm btn-outline-danger" type="submit" title="Delete"><i class="fa-solid fa-trash"></i></button>
</form>
<div class="d-flex align-items-center gap-2">
{% if w.widget_type != 'table' %}
<select class="form-select form-select-sm" data-range-widget="{{ w.id }}" style="width:auto">
<option value="1m">1m</option>
<option value="10m">10m</option>
<option value="1h" selected>1h</option>
<option value="3h">3h</option>
<option value="10h">10h</option>
</select>
{% endif %}
<form method="post" action="{{ url_for('dashboards.widget_delete', dashboard_id=dashboard.id, widget_id=w.id) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button class="btn btn-sm btn-outline-danger" type="submit" title="Delete"><i class="fa-solid fa-trash"></i></button>
</form>
</div>
</div>
<div class="card-body">
{% if w.widget_type == 'table' %}

View File

@@ -42,7 +42,7 @@
<select class="form-select" id="itemSelect">
<option value="">— select —</option>
</select>
<div class="form-text">E.g. interface / queue (depends on preset).</div>
<div class="form-text" id="itemHelp">E.g. interface / queue (depends on preset).</div>
</div>
<div class="mt-3">
@@ -91,6 +91,7 @@
const itemWrap = document.getElementById('itemWrap');
const itemSelect = document.getElementById('itemSelect');
const itemLabel = document.getElementById('itemLabel');
const itemHelp = document.getElementById('itemHelp');
function presetNeedsItem(p){
if(!p) return false;
@@ -109,7 +110,8 @@
}
itemWrap.classList.remove('d-none');
itemLabel.textContent = String(presetKey).includes('queue') ? 'Queue' : 'Interface / item';
itemLabel.textContent = p.item_label || (String(presetKey).includes('queue') ? 'Queue' : 'Interface / item');
itemHelp.textContent = p.item_help || 'E.g. interface / queue / metric (depends on preset).';
itemSelect.innerHTML = '<option value="">Loading…</option>';
try{