diff --git a/src/main.rs b/src/main.rs index e338992..178cc45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -492,6 +492,16 @@ async fn result_page(State(ctx): State, h: HeaderMap, Path(attempt): Pat } }; let a = analyse(&rows, score, max, used); + // Share link for the underlying paper (empty when the paper is gone). + let token: String = sqlx::query_scalar( + "SELECT p.share_token FROM papers p JOIN attempts a ON a.paper_id = p.id WHERE a.id=?", + ) + .bind(&attempt) + .fetch_optional(&ctx.pool) + .await + .ok() + .flatten() + .unwrap_or_default(); // Existing tags per question so the form shows current state. let tag_rows: Vec<(i64, String)> = sqlx::query_as( "SELECT question_id, tag FROM mistake_tags WHERE attempt_id=?", @@ -519,7 +529,7 @@ async fn result_page(State(ctx): State, h: HeaderMap, Path(attempt): Pat } }) .collect(); - hx(views::result(&ctx, &name, &attempt, &a, &rv), StatusCode::OK).into_response() + hx(views::result(&ctx, &name, &attempt, &token, &a, &rv), StatusCode::OK).into_response() } #[derive(serde::Deserialize)] diff --git a/src/views.rs b/src/views.rs index 15041bf..ee50865 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1028,13 +1028,26 @@ fn bucket_rows(items: &[Bucket]) -> String { .collect() } -pub fn result(ctx: &Ctx, user: &str, attempt_id: &str, a: &Analysis, rows: &[RowView]) -> String { +pub fn result(ctx: &Ctx, user: &str, attempt_id: &str, share_token: &str, a: &Analysis, rows: &[RowView]) -> String { let base = ctx.base.clone(); let mistakes_pending = rows .iter() .filter(|r| !r.correct && r.tags.is_empty()) .count(); let title = format!("practice paper ยท {attempt_id}"); + // Share box mirrors the paper-preview page: link + copy button. + let share_box = if share_token.is_empty() { + String::new() + } else { + format!( + r##""##, + b = public_origin(), + tok = esc(share_token) + ) + }; let detail: String = rows .iter() .enumerate() @@ -1114,6 +1127,7 @@ pub fn result(ctx: &Ctx, user: &str, attempt_id: &str, a: &Analysis, rows: &[Row {eh}

{eb}

+{share_box}

by subject

{subj}
subjectrightwrongblank%

by difficulty

{diff}
difficultyrightwrongblank%
@@ -1126,7 +1140,25 @@ pub fn result(ctx: &Ctx, user: &str, attempt_id: &str, a: &Analysis, rows: &[Row

-"##, + +"##, score = a.score, max = a.max_score, neg = neg, @@ -1134,6 +1166,7 @@ pub fn result(ctx: &Ctx, user: &str, attempt_id: &str, a: &Analysis, rows: &[Row dur = fmt_dur(a.used_secs), eh = enc_head, eb = enc_body, + share_box = share_box, subj = bucket_rows(&a.by_subject), diff = bucket_rows(&a.by_difficulty), topic = bucket_rows(&topic_buckets), diff --git a/static/style.css b/static/style.css index 7cca4df..fb6473d 100644 --- a/static/style.css +++ b/static/style.css @@ -1315,6 +1315,33 @@ footer { background: #ef4444; } +/* Share box on the result page */ +.card.share-box { + margin: 14px 0; +} + +.share-row { + display: flex; + gap: 10px; + align-items: center; + flex-wrap: wrap; + margin-top: 10px; +} + +.share-row code { + flex: 1 1 240px; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + background: var(--panel); + border: 1px solid var(--border); + border-radius: var(--radius-sm); + padding: 8px 10px; + font-size: 0.8rem; + color: #38bdf8; +} + /* Encouragement card under the overview stats */ .card.encourage { margin: 14px 0;