Improve report source column and mobile table layout

This commit is contained in:
2026-03-23 23:08:31 +00:00
parent 275449d6d7
commit 4ccd310667

View File

@@ -1053,25 +1053,18 @@ pub fn render_html_report(conn: &Connection, limit: i64, all_time_limit: i64) ->
.flatten() .flatten()
.and_then(|m| m.cover_url); .and_then(|m| m.cover_url);
let cover = resolve_cover(cover_url, &mut cover_files); let cover = resolve_cover(cover_url, &mut cover_files);
// Prepend a subtle source dot to the album name if we know let source_cell = a
// the dominant source for this album. .dominant_source
let album_cell = if let Some(src) = a.dominant_source.as_deref() { .as_deref()
if let Some((bg, _)) = source_colours(src, &ordered_sources) { .map(html_escape)
format!( .unwrap_or_else(|| "-".to_string());
"<span style=\"display:inline-block;width:9px;height:9px;\
border-radius:50%;background:{};margin-right:5px;\
vertical-align:middle\"></span>{}",
bg,
html_escape(&a.album)
)
} else {
html_escape(&a.album)
}
} else {
html_escape(&a.album)
};
BarRow { BarRow {
cells: vec![(i + 1).to_string(), html_escape(&a.artist), album_cell], cells: vec![
(i + 1).to_string(),
html_escape(&a.artist),
html_escape(&a.album),
source_cell,
],
value: a.plays, value: a.plays,
suffix: format_duration(a.listen_time_secs), suffix: format_duration(a.listen_time_secs),
cover, cover,
@@ -1082,7 +1075,7 @@ pub fn render_html_report(conn: &Connection, limit: i64, all_time_limit: i64) ->
write_bar_table( write_bar_table(
&mut h, &mut h,
"Top Albums", "Top Albums",
&["#", "Artist", "Album", "Plays", ""], &["#", "Artist", "Album", "Source", "Plays", ""],
&album_rows, &album_rows,
); );
@@ -1146,23 +1139,18 @@ pub fn render_html_report(conn: &Connection, limit: i64, all_time_limit: i64) ->
.flatten() .flatten()
.and_then(|m| m.cover_url); .and_then(|m| m.cover_url);
let cover = resolve_cover(cover_url, &mut cover_files); let cover = resolve_cover(cover_url, &mut cover_files);
let title_cell = if let Some(src) = t.dominant_source.as_deref() { let source_cell = t
if let Some((bg, _)) = source_colours(src, &ordered_sources) { .dominant_source
format!( .as_deref()
"<span style=\"display:inline-block;width:9px;height:9px;\ .map(html_escape)
border-radius:50%;background:{};margin-right:5px;\ .unwrap_or_else(|| "-".to_string());
vertical-align:middle\"></span>{}",
bg,
html_escape(&t.title)
)
} else {
html_escape(&t.title)
}
} else {
html_escape(&t.title)
};
BarRow { BarRow {
cells: vec![(i + 1).to_string(), html_escape(&t.artist), title_cell], cells: vec![
(i + 1).to_string(),
html_escape(&t.artist),
html_escape(&t.title),
source_cell,
],
value: t.plays, value: t.plays,
suffix: format_duration(t.listen_time_secs), suffix: format_duration(t.listen_time_secs),
cover, cover,
@@ -1173,7 +1161,7 @@ pub fn render_html_report(conn: &Connection, limit: i64, all_time_limit: i64) ->
write_bar_table( write_bar_table(
&mut h, &mut h,
"Top Tracks", "Top Tracks",
&["#", "Artist", "Title", "Plays", ""], &["#", "Artist", "Title", "Source", "Plays", ""],
&track_rows, &track_rows,
); );
@@ -1294,7 +1282,7 @@ h1, h2, h3 { margin: 0; }
.kpi .v { margin-top: 4px; font-size: 22px; font-weight: 700; } .kpi .v { margin-top: 4px; font-size: 22px; font-weight: 700; }
section { margin-top: 18px; } section { margin-top: 18px; }
.section-title { font-size: 16px; margin-bottom: 8px; } .section-title { font-size: 16px; margin-bottom: 8px; }
.card { background: var(--panel2); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; } .card { background: var(--panel2); border: 1px solid var(--line); border-radius: 10px; overflow-x: auto; overflow-y: hidden; }
table { width: 100%; border-collapse: collapse; } table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--line); } th, td { text-align: left; padding: 8px 12px; border-bottom: 1px solid var(--line); }
th { font-size: 11px; color: var(--muted); letter-spacing: .02em; text-transform: uppercase; } th { font-size: 11px; color: var(--muted); letter-spacing: .02em; text-transform: uppercase; }
@@ -1344,6 +1332,11 @@ tr:last-child td { border-bottom: none; }
.wrap { padding: 16px 12px 36px; } .wrap { padding: 16px 12px 36px; }
.grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.jump-nav { margin-top: 10px; padding: 7px; } .jump-nav { margin-top: 10px; padding: 7px; }
th, td { padding: 7px 8px; }
.source-col { display: none; }
.bar-cell { width: 32%; }
.bar-wrap { gap: 4px; }
.bar-label { font-size: 11px; }
/* On mobile show rank + duration only; drop the raw play-count and the bar graphic. /* On mobile show rank + duration only; drop the raw play-count and the bar graphic.
The .bar-label (duration text) remains visible inside .bar-cell. */ The .bar-label (duration text) remains visible inside .bar-cell. */
.play-count { display: none; } .play-count { display: none; }
@@ -1538,7 +1531,12 @@ fn write_bar_table(h: &mut HtmlWriter, title: &str, headers: &[&str], rows: &[Ba
// it can be hidden together with the td.play-count cells on mobile. // it can be hidden together with the td.play-count cells on mobile.
let play_count_idx = headers.len().saturating_sub(2); let play_count_idx = headers.len().saturating_sub(2);
for (i, hdr) in headers.iter().enumerate() { for (i, hdr) in headers.iter().enumerate() {
if i == play_count_idx { if *hdr == "Source" {
h.linef(format_args!(
"<th class=\"source-col\">{}</th>",
html_escape(hdr)
));
} else if i == play_count_idx {
h.linef(format_args!( h.linef(format_args!(
"<th class=\"play-count\">{}</th>", "<th class=\"play-count\">{}</th>",
html_escape(hdr) html_escape(hdr)
@@ -1564,8 +1562,17 @@ fn write_bar_table(h: &mut HtmlWriter, title: &str, headers: &[&str], rows: &[Ba
h.line("<td><span class=\"mini-ph\"></span></td>"); h.line("<td><span class=\"mini-ph\"></span></td>");
} }
} }
for cell in &row.cells { for (i, cell) in row.cells.iter().enumerate() {
if row.raw_cells { if headers.get(i).is_some_and(|h| *h == "Source") {
if row.raw_cells {
h.linef(format_args!("<td class=\"source-col\">{}</td>", cell));
} else {
h.linef(format_args!(
"<td class=\"source-col\">{}</td>",
html_escape(cell)
));
}
} else if row.raw_cells {
h.linef(format_args!("<td>{}</td>", cell)); h.linef(format_args!("<td>{}</td>", cell));
} else { } else {
h.linef(format_args!("<td>{}</td>", html_escape(cell))); h.linef(format_args!("<td>{}</td>", html_escape(cell)));