Refactor case detail into a dedicated page
This commit restructures the web interface for case details. The
previous `/web/cases/{case_id}` route, which previously showed both the
case summary and its recordings, has been split into two distinct pages:
- `/web/cases/{case_id}`: This page now displays the overall case
information, including the document if available.
- `/web/cases/{case_id}/recordings`: This new page is dedicated to
listing and displaying individual recordings within a case.
This change improves the organization and clarity of the web UI,
allowing for more focused views of case data. Additionally, the
`case_detail.html` and `document.html` templates have been removed as
their functionality is now handled by the new `case_page.html` and the
upcoming document rendering logic. The `cases.html` template has also
been removed, indicating a shift towards more granular page views.
This commit is contained in:
@@ -125,31 +125,9 @@ struct MyCasesTemplate {
|
||||
is_admin: bool,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "case_detail.html")]
|
||||
struct CaseDetailTemplate {
|
||||
slug: String,
|
||||
case_id: String,
|
||||
case_id_short: String,
|
||||
oneliner: Option<String>,
|
||||
recordings: Vec<RecordingView>,
|
||||
can_analyze: bool,
|
||||
llm_missing: bool,
|
||||
analyzing: bool,
|
||||
has_document: bool,
|
||||
/// True iff the transcribe worker is currently running. Gate for the
|
||||
/// per-recording "Transkription läuft…" label — suppresses the lie when
|
||||
/// a transcript is missing but no worker is active.
|
||||
transcribe_busy: bool,
|
||||
/// True iff the session user is an admin — toggles admin-only controls
|
||||
/// (currently the Reset button).
|
||||
is_admin: bool,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "case_page.html")]
|
||||
struct CasePageTemplate {
|
||||
slug: String,
|
||||
case_id: String,
|
||||
case_id_short: String,
|
||||
oneliner: Option<String>,
|
||||
@@ -175,7 +153,6 @@ struct CaseRecordingsTemplate {
|
||||
oneliner: Option<String>,
|
||||
recordings: Vec<RecordingView>,
|
||||
transcribe_busy: bool,
|
||||
is_admin: bool,
|
||||
}
|
||||
|
||||
/// Flags derived from filesystem state + config.
|
||||
@@ -275,57 +252,6 @@ pub async fn handle_my_cases(
|
||||
.map_err(|e| AppError::Internal(format!("Template render failed: {e}")))
|
||||
}
|
||||
|
||||
pub async fn handle_case_detail(
|
||||
user: AuthenticatedWebUser,
|
||||
State(config): State<Arc<Config>>,
|
||||
State(pipeline): State<PipelineState>,
|
||||
CaseIdPath(case_id): CaseIdPath,
|
||||
) -> Result<Html<String>, AppError> {
|
||||
// IDOR guard: case_dir must live under the session's user_slug.
|
||||
let user_root = config.data_path.join(&user.slug);
|
||||
pipeline.heal_orphans_if_idle(&user_root, &user.slug).await;
|
||||
|
||||
let case_dir = locate_case_or_404(
|
||||
&user_root,
|
||||
&case_id,
|
||||
&user.slug,
|
||||
"case detail (possible IDOR probe)",
|
||||
)
|
||||
.await?;
|
||||
let case_id_str = case_id.to_string();
|
||||
info!(slug = %user.slug, case_id = %case_id, "case detail viewed");
|
||||
|
||||
let recordings = scan_recordings(&case_dir).await;
|
||||
let oneliner = tokio::fs::read_to_string(case_dir.join("oneliner.txt"))
|
||||
.await
|
||||
.ok()
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
let a_busy = pipeline.analyze_busy.0.load(Ordering::Acquire);
|
||||
let t_busy = pipeline.transcribe_busy.0.load(Ordering::Acquire);
|
||||
let flags = compute_flags(&case_dir, &recordings, config.llm_configured(), a_busy).await;
|
||||
let case_id_short = case_id_str.chars().take(8).collect();
|
||||
let is_admin = user.is_admin();
|
||||
|
||||
CaseDetailTemplate {
|
||||
slug: user.slug,
|
||||
case_id: case_id_str,
|
||||
case_id_short,
|
||||
oneliner,
|
||||
recordings,
|
||||
can_analyze: flags.can_analyze,
|
||||
llm_missing: flags.llm_missing,
|
||||
analyzing: flags.analyzing,
|
||||
has_document: flags.has_document,
|
||||
transcribe_busy: t_busy,
|
||||
is_admin,
|
||||
}
|
||||
.render()
|
||||
.map(Html)
|
||||
.map_err(|e| AppError::Internal(format!("Template render failed: {e}")))
|
||||
}
|
||||
|
||||
/// GET /web/cases/{case_id}
|
||||
///
|
||||
/// Canonical case page. Renders the document inline if present; otherwise
|
||||
@@ -374,7 +300,6 @@ pub async fn handle_case_page(
|
||||
let is_admin = user.is_admin();
|
||||
|
||||
CasePageTemplate {
|
||||
slug: user.slug,
|
||||
case_id: case_id_str,
|
||||
case_id_short,
|
||||
oneliner,
|
||||
@@ -424,7 +349,6 @@ pub async fn handle_case_recordings(
|
||||
|
||||
let t_busy = pipeline.transcribe_busy.0.load(Ordering::Acquire);
|
||||
let case_id_short = case_id_str.chars().take(8).collect();
|
||||
let is_admin = user.is_admin();
|
||||
|
||||
CaseRecordingsTemplate {
|
||||
slug: user.slug,
|
||||
@@ -433,7 +357,6 @@ pub async fn handle_case_recordings(
|
||||
oneliner,
|
||||
recordings,
|
||||
transcribe_busy: t_busy,
|
||||
is_admin,
|
||||
}
|
||||
.render()
|
||||
.map(Html)
|
||||
|
||||
Reference in New Issue
Block a user