Skip to content

Commit 03fbc95

Browse files
committed
Show stats by domain in a doughnut chart for Link Previews
1 parent 8d2d086 commit 03fbc95

File tree

14 files changed

+178
-33
lines changed

14 files changed

+178
-33
lines changed

src/dashboard/dashboard.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func Init(mux *http.ServeMux) {
3434
mux.Handle("GET /dashboard/link-previews", chain.ThenFunc(linkPreviewsPageHandler))
3535
mux.Handle("GET /dashboard/link-previews/list", chain.ThenFunc(linkPreviewsListHandler))
3636
mux.Handle("GET /dashboard/link-previews/image", chain.ThenFunc(serveLinkPreviewHandler))
37+
mux.Handle("GET /dashboard/link-previews/stats", chain.ThenFunc(linkPreviewsStatsHandler))
3738
mux.Handle("DELETE /dashboard/link-previews/url", chain.ThenFunc(deleteLinkPreviewHandler))
3839

3940
mux.Handle("GET /dashboard/qr-codes", chain.ThenFunc(listQrCodesHandler))

src/dashboard/dashboard.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { initLinkPreviewsChart } from './linkpreviews_chart.js';
2+
3+
initLinkPreviewsChart();

src/dashboard/dashboard.templ

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ templ ContentTempl(title string, contents templ.Component) {
2121
<meta name="viewport" content="width=device-width,initial-scale=1"/>
2222
<link rel="manifest" href="/app.webmanifest"/>
2323
<script src="/static/htmx.min.js"></script>
24+
<script src="/static/chart.min.js"></script>
2425
<meta name="htmx-config" content='{"selfRequestsOnly":false}'/>
2526
<link rel="stylesheet" href="/static/gen.min.css"/>
2627
<link rel="icon" href="/static/favicon.svg"/>
@@ -34,6 +35,7 @@ templ ContentTempl(title string, contents templ.Component) {
3435
@FooterTempl()
3536
</main>
3637
</body>
38+
<script src="/static/dashboard.min.js"></script>
3739
</html>
3840
}
3941

src/dashboard/dashboard_templ.go

Lines changed: 17 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dashboard/linkpreviews.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dashboard
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"fmt"
67
"image"
78
_ "image/png"
@@ -281,6 +282,25 @@ func serveLinkPreviewHandler(w http.ResponseWriter, req *http.Request) {
281282
"hostname", u.Hostname())
282283
}
283284

285+
// GET /dashboard/link-previews/stats - Get link preview statistics by domain as JSON
286+
func linkPreviewsStatsHandler(w http.ResponseWriter, req *http.Request) {
287+
queries := db.New(db.Pool)
288+
stats, err := queries.GetLinkPreviewsByDomain(req.Context())
289+
if err != nil {
290+
slog.Error("failed to get link preview stats", tint.Err(err),
291+
"method", req.Method,
292+
"path", req.URL.Path,
293+
"url", req.URL.String(),
294+
"status", http.StatusInternalServerError)
295+
http.Error(w, err.Error(), http.StatusInternalServerError)
296+
return
297+
}
298+
299+
w.Header().Set("Content-Type", "application/json")
300+
w.WriteHeader(http.StatusOK)
301+
json.NewEncoder(w).Encode(stats)
302+
}
303+
284304
func calculateTotalPages(totalCount int64) int64 {
285305
limit := int64(conf.Config.Dashboard.Pagination.Limit)
286306
return (totalCount + (limit - 1)) / limit

src/dashboard/linkpreviews.templ

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import (
88

99
templ LinkPreviewsPageTempl(page int) {
1010
@ContentTempl("Link Previews", NilTemplate()) {
11+
<section>
12+
<h2>Requests by Domain</h2>
13+
<canvas id="linkpreviews-domain-chart" class="max-w-240 max-h-64"></canvas>
14+
</section>
1115
<section
1216
id="link-previews-section"
1317
hx-get={ "/dashboard/link-previews/list?page=" + fmt.Sprintf("%d", page) }
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @fileoverview Renders a doughnut chart for link preview statistics on the dashboard.
3+
*/
4+
export function initLinkPreviewsChart() {
5+
async function loadChart() {
6+
try {
7+
const response = await fetch('/dashboard/link-previews/stats');
8+
if (!response.ok) {
9+
console.error('Failed to fetch link preview statistics');
10+
return;
11+
}
12+
13+
const stats = await response.json();
14+
if (stats.length === 0) {
15+
console.log('No link preview data available');
16+
return;
17+
}
18+
19+
new Chart(document.getElementById('linkpreviews-domain-chart').getContext('2d'), {
20+
type: 'doughnut',
21+
data: {
22+
labels: stats.map(d => d.Domain),
23+
datasets: [{
24+
data: stats.map(d => d.TotalAccesses),
25+
borderWidth: 1
26+
}]
27+
},
28+
options: {
29+
responsive: true,
30+
plugins: {
31+
legend: {
32+
position: 'right',
33+
labels: {
34+
font: {
35+
size: 14,
36+
family: 'Inter'
37+
}
38+
}
39+
}
40+
}
41+
}
42+
});
43+
} catch (error) {
44+
console.error('Error loading link preview chart:', error);
45+
}
46+
}
47+
48+
if (document.readyState === 'loading') {
49+
document.addEventListener('DOMContentLoaded', loadChart);
50+
} else {
51+
loadChart();
52+
}
53+
}

0 commit comments

Comments
 (0)