diff --git a/AGENTS.md b/AGENTS.md index 52210c6..4cdfa62 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,6 +39,12 @@ This document outlines general practices and expectations for AI agents assistin * Use latest CDN version for embedded dashboards * Charts should be rendered from pre-generated JSON blobs in `/json/` +### Tables: DataTables + +* Use DataTables via CDN for reports with `chart: table` +* Requires jQuery from a CDN +* Table data comes from the same `/json/` files as charts + ### Styling: Bulma CSS * Use via CDN or vendored minified copy (to keep reports fully static) diff --git a/reports.yml b/reports.yml index 754d311..01716e1 100644 --- a/reports.yml +++ b/reports.yml @@ -1,6 +1,6 @@ - name: hits label: Hits - chart: bar + chart: line query: | SELECT {bucket} AS bucket, COUNT(*) AS value @@ -20,7 +20,7 @@ - name: cache_status_breakdown label: Cache Status - chart: bar + chart: polarArea query: | SELECT cache_status AS bucket, COUNT(*) AS value @@ -30,7 +30,7 @@ - name: domain_traffic label: Top Domains - chart: bar + chart: table query: | SELECT host AS bucket, COUNT(*) AS value @@ -50,7 +50,7 @@ - name: top_paths label: Top Paths - chart: bar + chart: table query: | SELECT path AS bucket, COUNT(*) AS value @@ -65,7 +65,7 @@ - name: user_agents label: User Agents - chart: bar + chart: table query: | SELECT user_agent AS bucket, COUNT(*) AS value @@ -76,7 +76,7 @@ - name: referrers label: Referrers - chart: bar + chart: table query: | SELECT referer AS bucket, COUNT(*) AS value @@ -87,7 +87,7 @@ - name: status_distribution label: HTTP Statuses - chart: bar + chart: stackedBar query: | SELECT CASE WHEN status BETWEEN 200 AND 299 THEN '2xx' diff --git a/templates/report.html b/templates/report.html index 3288269..bbb6e37 100644 --- a/templates/report.html +++ b/templates/report.html @@ -4,7 +4,10 @@ {{ interval.title() }} Report + + +
@@ -12,7 +15,11 @@ {% for report in reports %}

{{ report.label }}

+ {% if report.chart == 'table' %} +
+ {% else %} + {% endif %}
{% endfor %}
@@ -22,10 +29,32 @@ fetch(rep.json) .then(r => r.json()) .then(data => { + if (rep.chart === 'table') { + const rows = data.map(x => [x.bucket, x.value]); + new DataTable('#table-' + rep.name, { + data: rows, + columns: [ + { title: 'Bucket' }, + { title: 'Value' } + ] + }); + return; + } + const labels = data.map(x => x.bucket); const values = data.map(x => x.value); + const chartType = rep.chart === 'stackedBar' ? 'bar' : rep.chart; + const options = { + scales: { + y: { beginAtZero: true } + } + }; + if (rep.chart === 'stackedBar') { + options.scales.x = { stacked: true }; + options.scales.y.stacked = true; + } new Chart(document.getElementById('chart-' + rep.name), { - type: rep.chart, + type: chartType, data: { labels: labels, datasets: [{ @@ -34,14 +63,10 @@ backgroundColor: 'rgba(54, 162, 235, 0.5)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1, - fill: rep.chart !== 'bar', + fill: rep.chart !== 'bar' && rep.chart !== 'stackedBar', }] }, - options: { - scales: { - y: { beginAtZero: true } - } - } + options: options }); }); });