Skip to content

Improve submission change displays#125

Merged
superg merged 2 commits into
mainfrom
whatever-industries
Jun 29, 2026
Merged

Improve submission change displays#125
superg merged 2 commits into
mainfrom
whatever-industries

Conversation

@whatever-industries

@whatever-industries whatever-industries commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add collapsible disc History rows that show field-level changes using the queue edit review presentation
  • replace raw submission change JSON on review/detail pages with structured human-readable change panels
  • render scalar, set, multiline, legacy, fallback, and ring-code changes, including Extra Offset and Sample Start
  • keep Queue filters always visible and center the Queue submission count

Validation

  • cargo build
  • cargo test routes::queue::tests
  • docker compose up -d --build app
  • full container test suite: 336 passed; 0 failed; 7 ignored

Summary by CodeRabbit

  • New Features

    • Added a more readable submission changes view across queue, detail, and edit screens, replacing raw JSON with structured tables and sections.
    • Queue history now supports expandable per-entry change details, including submission comments and field-level change breakdowns.
    • Improved disc history pages with clearer summary text and toggle controls for viewing approved and legacy submissions.
  • Bug Fixes

    • Preserved change information more consistently when loading submission lists and review pages.
    • Adjusted layout and spacing for change details and history tables for better readability.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Replaces raw JSON string rendering of submission changes with structured view models. New structs in models.rs represent scalar, set, multiline, ring, and fallback change rows aggregated into SubmissionChangesView. The SQL query, from_row mapping, route helpers, templates (queue.html, queue_detail.html, disc_edit.html), and CSS are all updated to produce and render these structured views instead of raw JSON blobs.

Changes

Structured Submission Change Views

Layer / File(s) Summary
Data model structs
src/db/models.rs
Adds SubmissionRingChangeCell, SubmissionRingChangeRow, SubmissionChangeDetail, SubmissionChangeScalarRow, SubmissionChangeSetRow, SubmissionChangeMultilineRow, SubmissionChangeRingSection, SubmissionChangeFallbackRow, and SubmissionChangesView; extends SubmissionListRow with submission_comment and change_details.
SQL query extensions
src/services/queue_service.rs
Introduces changes_summary_expr (conditional ds.changes or empty JSONB) and adds submission_comment and changes_summary to the list_submissions SELECT clause.
Change processing helpers and from_row mapping
src/routes/queue.rs
Adds ~700 lines of helpers: value filtering, field label mapping, operation parsing, scalar/set/multiline/ring formatting, fallback row generation, and submission_changes_views/submission_change_details builders; extends from_row to populate change_details from the changes_summary JSON column.
Route template wiring
src/routes/queue.rs, src/routes/disc_edit.rs
Removes changes_original_json/changes_json from QueueDetailTemplate and DiscEditTemplate; adds change_views: Vec<SubmissionChangesView> populated via submission_changes_views() in review and readonly render paths.
HTML templates
templates/queue_detail.html, templates/disc_edit.html, templates/queue.html
Replaces JSON <details> blocks with change_views-driven tables/sections for scalar, set, ring, multiline, legacy, and fallback rows; adds disc-history toggle rows with per-entry hidden change detail expansion and JS table-width locking.
CSS
static/css/app.css
Adds disc-history/queue-history layout rules, chevron expand/collapse toggle, .submission-change-view component styles, queue-history-table sizing, and ring column width constraints; removes old raw JSON pre/details rules.
Tests
src/routes/queue.rs
Updates queue_row helper, adds history_change_details_extract_current_and_previous_values, and revises queue-detail/review tests for change_views ordering, multiline sidecars, and universal-hash markers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • superg/vgindex#88: Touches templates/queue.html table row clickability and static/css/app.css queue table styles, the same files significantly modified in this PR.

Poem

🐇 Hoppity-hop through the JSON maze,
Raw blobs be gone, replaced with a gaze
Of scalars and rings and multiline rows,
Each change now displayed the way the queue knows.
No more mysterious {}s in sight—
Structured views hop into the light! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: replacing raw submission change JSON with structured, improved change displays.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch whatever-industries

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@whatever-industries whatever-industries changed the title Improve disc history change display Improve submission change displays Jun 28, 2026
@whatever-industries whatever-industries marked this pull request as ready for review June 28, 2026 15:51

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
templates/queue_detail.html (1)

26-110: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the duplicated change_views rendering into a shared Askama partial.

This block (lines 26-110) is duplicated almost verbatim in templates/disc_edit.html (lines 65-148). Since both templates expose a change_views field, you can move the loop body into a partial (e.g. templates/_submission_change_views.html) and {% include %} it in both, eliminating the drift risk across two large blocks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@templates/queue_detail.html` around lines 26 - 110, The `change_views`
rendering is duplicated between `queue_detail.html` and `disc_edit.html`, so
move the full loop body into a shared Askama partial such as a submission change
views template and include it from both places. Keep the partial aligned with
the existing `change_views`, `view`, `section`, and `row` rendering structure so
both templates reuse the same markup and any future changes happen in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/queue.rs`:
- Around line 214-359: The new change-rendering helpers in queue.rs are
formatting raw JSON values, so lookup-backed fields like system_code,
media_type, regions, and languages will show codes instead of human-readable
labels. Update the helper flow around friendly_json_value, change_scalar_row,
change_multiline_row, and change_set_row to apply the same reference-data
formatting used by build_review_diff_context() via review_named_value,
display_region, and display_language before building change_views/change_details
rows. Keep the raw JSON parsing, but thread the display conversion through these
helpers so the new UI preserves human-readable output.
- Around line 749-763: The multiline detection in push_labeled_change_detail is
too narrow because it only checks for newline characters, so review-sidecar
fields like contents, cuesheet, and dat may render inline even though they
should be multiline. Update the is_multiline decision to also consider field
semantics in this function, using the field identifier plus the existing
current_value/previous_value checks, so the known multiline fields always get
multiline presentation regardless of whether the normalized text contains a
newline.

---

Nitpick comments:
In `@templates/queue_detail.html`:
- Around line 26-110: The `change_views` rendering is duplicated between
`queue_detail.html` and `disc_edit.html`, so move the full loop body into a
shared Askama partial such as a submission change views template and include it
from both places. Keep the partial aligned with the existing `change_views`,
`view`, `section`, and `row` rendering structure so both templates reuse the
same markup and any future changes happen in one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ced54a36-e20a-44b7-90f4-49dc5b6ab05a

📥 Commits

Reviewing files that changed from the base of the PR and between 1a08740 and 1e76124.

📒 Files selected for processing (8)
  • src/db/models.rs
  • src/routes/disc_edit.rs
  • src/routes/queue.rs
  • src/services/queue_service.rs
  • static/css/app.css
  • templates/disc_edit.html
  • templates/queue.html
  • templates/queue_detail.html

Comment thread src/routes/queue.rs
Comment on lines +214 to +359
fn display_change_value(value: &serde_json::Value) -> String {
review_annotation_value(value)
}

fn friendly_json_value(value: &serde_json::Value) -> String {
match value {
serde_json::Value::Null => String::new(),
serde_json::Value::String(s) => s.clone(),
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::Bool(b) => b.to_string(),
serde_json::Value::Array(values) => values
.iter()
.map(friendly_json_value)
.filter(|s| !s.trim().is_empty())
.collect::<Vec<_>>()
.join(", "),
serde_json::Value::Object(map) => {
if let (Some(start), Some(end)) = (map.get("start"), map.get("end")) {
return format!(
"{}-{}",
friendly_json_value(start),
friendly_json_value(end)
);
}
map.iter()
.map(|(key, value)| format!("{key}: {}", friendly_json_value(value)))
.collect::<Vec<_>>()
.join("; ")
}
}
}

fn empty_display_value(value: String) -> String {
if value.trim().is_empty() {
"(empty)".to_string()
} else {
value
}
}

fn status_for_action(action: &str) -> &'static str {
match action {
"Added" => "item-added",
"Removed" => "item-removed",
"Modified" => "item-changed",
_ => "",
}
}

fn change_operation_parts(
change: &serde_json::Value,
) -> Option<(
&'static str,
Option<&serde_json::Value>,
Option<&serde_json::Value>,
)> {
if let Some(modify) = change.get("modify") {
return Some(("Modified", modify.get("old"), modify.get("new")));
}
if let Some(add) = change.get("add") {
return Some(("Added", None, add.get("new").or(Some(add))));
}
if let Some(remove) = change.get("remove") {
return Some(("Removed", remove.get("old").or(Some(remove)), None));
}
match (change.get("old"), change.get("new")) {
(Some(old), Some(new)) => Some(("Modified", Some(old), Some(new))),
(None, Some(new)) => Some(("Added", None, Some(new))),
(Some(old), None) => Some(("Removed", Some(old), None)),
(None, None) => None,
}
}

fn change_scalar_row(field: &str, change: &serde_json::Value) -> Option<SubmissionChangeScalarRow> {
let (action, old, new) = change_operation_parts(change)?;
Some(SubmissionChangeScalarRow {
field: change_field_label(field),
action: action.to_string(),
previous_value: old
.map(friendly_json_value)
.map(empty_display_value)
.unwrap_or_default(),
current_value: new
.map(friendly_json_value)
.map(empty_display_value)
.unwrap_or_default(),
status_class: status_for_action(action).to_string(),
})
}

fn change_multiline_row(
field: &str,
change: &serde_json::Value,
) -> Option<SubmissionChangeMultilineRow> {
let (action, old, new) = change_operation_parts(change)?;
Some(SubmissionChangeMultilineRow {
field: change_field_label(field),
action: action.to_string(),
previous_value: old.map(friendly_json_value).unwrap_or_default(),
current_value: new.map(friendly_json_value).unwrap_or_default(),
status_class: status_for_action(action).to_string(),
})
}

fn string_vec_values(value: Option<&serde_json::Value>) -> Vec<String> {
match value {
Some(serde_json::Value::Array(values)) => values
.iter()
.map(friendly_json_value)
.filter(|s| !s.trim().is_empty())
.collect(),
Some(value) => {
let display = friendly_json_value(value);
if display.trim().is_empty() {
Vec::new()
} else {
vec![display]
}
}
None => Vec::new(),
}
}

fn change_set_row(field: &str, change: &serde_json::Value) -> Option<SubmissionChangeSetRow> {
let added = string_vec_values(
change
.get("add")
.and_then(|value| value.get("new"))
.or_else(|| change.get("add")),
);
let removed = string_vec_values(
change
.get("remove")
.and_then(|value| value.get("old"))
.or_else(|| change.get("remove")),
);
if added.is_empty() && removed.is_empty() {
None
} else {
Some(SubmissionChangeSetRow {
field: change_field_label(field),
added,
removed,
})
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Humanize lookup-backed fields before using these helpers for the new change UI.

These paths stringify stored JSON directly, so system_code, media_type, regions, and languages will render as raw codes in both change_views and change_details. build_review_diff_context() already resolves those fields through review_named_value, display_region, and display_language, so this regresses the “human-readable” presentation the PR is introducing. Thread the same reference-data formatting through these helpers instead of formatting from raw JSON alone.

Also applies to: 777-849

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/queue.rs` around lines 214 - 359, The new change-rendering helpers
in queue.rs are formatting raw JSON values, so lookup-backed fields like
system_code, media_type, regions, and languages will show codes instead of
human-readable labels. Update the helper flow around friendly_json_value,
change_scalar_row, change_multiline_row, and change_set_row to apply the same
reference-data formatting used by build_review_diff_context() via
review_named_value, display_region, and display_language before building
change_views/change_details rows. Keep the raw JSON parsing, but thread the
display conversion through these helpers so the new UI preserves human-readable
output.

Comment thread src/routes/queue.rs
Comment on lines +749 to +763
fn push_labeled_change_detail(
details: &mut Vec<SubmissionChangeDetail>,
field: &str,
current_value: String,
previous_value: String,
current_label: String,
previous_label: String,
current_kind: String,
previous_kind: String,
) {
if current_value.trim().is_empty() && previous_value.trim().is_empty() {
return;
}
let is_multiline = current_value.contains('\n') || previous_value.contains('\n');
details.push(SubmissionChangeDetail {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Base is_multiline on field semantics, not just newline detection.

contents, cuesheet, dat, and the other review-sidecar fields are multiline in the existing review presentation even when the normalized value is a single line. With the current contains('\n') check, those history entries will fall back to inline rendering unless the text happens to contain a newline.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/routes/queue.rs` around lines 749 - 763, The multiline detection in
push_labeled_change_detail is too narrow because it only checks for newline
characters, so review-sidecar fields like contents, cuesheet, and dat may render
inline even though they should be multiline. Update the is_multiline decision to
also consider field semantics in this function, using the field identifier plus
the existing current_value/previous_value checks, so the known multiline fields
always get multiline presentation regardless of whether the normalized text
contains a newline.

@superg superg merged commit f0c9511 into main Jun 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants