feat: add dashboard change summaries
This commit is contained in:
@@ -116,6 +116,23 @@
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
<!-- What Changed -->
|
||||
<div x-show="hasDomainData" x-cloak>
|
||||
{% call card() %}
|
||||
{% call card_header() %}
|
||||
{% call card_title() %}What Changed{% endcall %}
|
||||
{% call card_description() %}
|
||||
New sending sources and sharp compliance changes from the last 30 days
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% call card_content() %}
|
||||
<div id="change-summary-list" class="space-y-3">
|
||||
<!-- Change summaries will be populated here via JavaScript -->
|
||||
</div>
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
<!-- Top Sending Sources -->
|
||||
<div x-show="hasDomainData" x-cloak>
|
||||
{% call card() %}
|
||||
@@ -309,12 +326,14 @@ function dashboardApp() {
|
||||
} else {
|
||||
this.hasDomainData = false;
|
||||
this.clearDashboardCharts();
|
||||
this.populateChangeSummary([]);
|
||||
this.populateTopSources([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching domain summary:', error);
|
||||
this.hasDomainData = false;
|
||||
this.clearDashboardCharts();
|
||||
this.populateChangeSummary([]);
|
||||
this.populateTopSources([]);
|
||||
}
|
||||
},
|
||||
@@ -329,6 +348,7 @@ function dashboardApp() {
|
||||
|
||||
const data = await response.json();
|
||||
this.renderDashboardCharts(data.compliance_trend || []);
|
||||
this.populateChangeSummary(data.change_summary || []);
|
||||
this.populateTopSources(data.top_sources || []);
|
||||
} catch (error) {
|
||||
console.error('Error fetching dashboard stats:', error);
|
||||
@@ -507,6 +527,61 @@ function dashboardApp() {
|
||||
return new Intl.NumberFormat(undefined, { notation: 'compact' }).format(value);
|
||||
},
|
||||
|
||||
populateChangeSummary(changes) {
|
||||
const list = document.getElementById('change-summary-list');
|
||||
if (!list) return;
|
||||
|
||||
list.textContent = '';
|
||||
|
||||
if (!changes || !changes.length) {
|
||||
const empty = document.createElement('p');
|
||||
empty.className = 'text-sm text-muted-foreground';
|
||||
empty.textContent = 'No major sender or compliance changes detected.';
|
||||
list.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
|
||||
changes.forEach(change => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'rounded-md border border-base-300 bg-base-100 p-3';
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.className = 'flex items-start gap-2';
|
||||
|
||||
const dot = document.createElement('span');
|
||||
dot.className = `mt-1 inline-flex h-2.5 w-2.5 flex-none rounded-full ${this.changeSeverityClass(change.severity)}`;
|
||||
header.appendChild(dot);
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.className = 'min-w-0';
|
||||
|
||||
const title = document.createElement('p');
|
||||
title.className = 'text-sm font-semibold';
|
||||
title.textContent = change.title || 'Change detected';
|
||||
content.appendChild(title);
|
||||
|
||||
const detail = document.createElement('p');
|
||||
detail.className = 'mt-1 text-sm text-muted-foreground';
|
||||
detail.textContent = change.detail || '';
|
||||
content.appendChild(detail);
|
||||
|
||||
const action = document.createElement('p');
|
||||
action.className = 'mt-1 text-sm';
|
||||
action.textContent = change.action || '';
|
||||
content.appendChild(action);
|
||||
|
||||
header.appendChild(content);
|
||||
item.appendChild(header);
|
||||
list.appendChild(item);
|
||||
});
|
||||
},
|
||||
|
||||
changeSeverityClass(severity) {
|
||||
if (severity === 'error') return 'bg-red-500';
|
||||
if (severity === 'warning') return 'bg-yellow-500';
|
||||
return 'bg-blue-500';
|
||||
},
|
||||
|
||||
populateTopSources(sources) {
|
||||
const tableBody = document.getElementById('top-sources-table-body');
|
||||
if (!tableBody) return;
|
||||
|
||||
Reference in New Issue
Block a user