Refactor case page template and oneliner rendering
Introduced a new macro `render` in `partials/oneliner.html` to handle the display of the oneliner for cases. This macro encapsulates the logic for rendering the oneliner, including states for ready, empty, error, pending, and generating. The `case_page.html` and `my_cases.html` templates are updated to use this new macro. This refactoring centralizes the oneliner rendering logic, making it more maintainable and consistent across different views. Additionally, the `OnelinerDisplay` enum is now used in `CasePageTemplate` and `MyCasesTemplate` to represent the different states of the oneliner, improving type safety and clarity.
This commit is contained in:
@@ -324,6 +324,7 @@ pub async fn handle_reset_case(
|
||||
State(config): State<Arc<Config>>,
|
||||
State(events_tx): State<EventSender>,
|
||||
CaseIdPath(case_id): CaseIdPath,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Redirect, AppError> {
|
||||
if !user.is_admin() {
|
||||
return Err(AppError::Forbidden("Nur für Admins".into()));
|
||||
@@ -343,7 +344,7 @@ pub async fn handle_reset_case(
|
||||
case_id.to_string(),
|
||||
CaseEventKind::CaseReset,
|
||||
);
|
||||
Ok(Redirect::to("/web/cases"))
|
||||
Ok(Redirect::to(&resolve_return_path(&headers)))
|
||||
}
|
||||
|
||||
/// POST /web/cases/undo-delete
|
||||
|
||||
@@ -163,7 +163,7 @@ struct MyCasesTemplate {
|
||||
struct CasePageTemplate {
|
||||
case_id: String,
|
||||
case_id_short: String,
|
||||
oneliner: Option<String>,
|
||||
oneliner: OnelinerDisplay,
|
||||
/// RFC3339 UTC timestamp of the most recent recording. `None` iff the
|
||||
/// case has no recordings at all. Drives the datetime header under the
|
||||
/// title; browser JS formats it to "Heute 11:34" / "13.12.2022 11:34".
|
||||
@@ -337,7 +337,7 @@ pub async fn handle_case_page(
|
||||
.map(|md| crate::analyze::render::md_to_html(&md));
|
||||
|
||||
let recordings = scan_recordings(&case_dir).await;
|
||||
let oneliner = ready_text(&case_dir).await;
|
||||
let oneliner = compute_oneliner_display(&case_dir, &recordings).await;
|
||||
|
||||
let a_busy = pipeline.analyze_busy.0.load(Ordering::Acquire);
|
||||
let flags = compute_flags(&case_dir, &recordings, config.llm_configured(), a_busy).await;
|
||||
@@ -531,8 +531,54 @@ async fn compute_case_view(
|
||||
.filter(|r| !r.failed)
|
||||
.all(|r| r.transcript.is_some());
|
||||
|
||||
let (state, _) = paths::read_oneliner_state(case_path).await;
|
||||
let oneliner = if non_failed_count == 0 {
|
||||
let oneliner = compute_oneliner_display(case_path, &recordings).await;
|
||||
|
||||
let has_document = any_document_exists(case_path).await;
|
||||
let analyzing = !has_document && worker_busy && any_analysis_input_exists(case_path).await;
|
||||
let can_analyze = !analyzing && all_transcribed && llm_configured;
|
||||
|
||||
let time_hms_utc = extract_hhmm_utc(&most_recent);
|
||||
let recorded_at_iso = recorded_at_iso_of(&most_recent);
|
||||
Some(UserCaseView {
|
||||
case_id,
|
||||
most_recent,
|
||||
time_hms_utc,
|
||||
recorded_at_iso,
|
||||
oneliner,
|
||||
recordings_count,
|
||||
analyzing,
|
||||
has_document,
|
||||
can_analyze,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract only the text from a `Ready` state; other states (Empty,
|
||||
/// Error, missing file) collapse to `None`. Used by the recordings
|
||||
/// sub-page, which only needs a plain title string as heading.
|
||||
async fn ready_text(case_dir: &Path) -> Option<String> {
|
||||
match paths::read_oneliner_state(case_dir).await.0 {
|
||||
Some(OnelinerState::Ready { text, .. }) => Some(text),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the 5-state UI display for the oneliner. Identical logic to
|
||||
/// what `compute_case_view` uses for the case list — extracted so the
|
||||
/// case page can render the same states instead of a plain "Fall"
|
||||
/// fallback.
|
||||
async fn compute_oneliner_display(
|
||||
case_dir: &Path,
|
||||
recordings: &[RecordingView],
|
||||
) -> OnelinerDisplay {
|
||||
let non_failed_count = recordings.iter().filter(|r| !r.failed).count();
|
||||
let all_transcribed = non_failed_count > 0
|
||||
&& recordings
|
||||
.iter()
|
||||
.filter(|r| !r.failed)
|
||||
.all(|r| r.transcript.is_some());
|
||||
let (state, _) = paths::read_oneliner_state(case_dir).await;
|
||||
|
||||
if non_failed_count == 0 {
|
||||
// All recordings failed — stable end state, no LLM will ever run.
|
||||
// Missing state collapses to Empty ("unbenannt"): no title is
|
||||
// expected, regardless of why.
|
||||
@@ -558,34 +604,5 @@ async fn compute_case_view(
|
||||
None => OnelinerDisplay::Pending,
|
||||
Some(_) => OnelinerDisplay::Generating,
|
||||
}
|
||||
};
|
||||
|
||||
let has_document = any_document_exists(case_path).await;
|
||||
let analyzing = !has_document && worker_busy && any_analysis_input_exists(case_path).await;
|
||||
let can_analyze = !analyzing && all_transcribed && llm_configured;
|
||||
|
||||
let time_hms_utc = extract_hhmm_utc(&most_recent);
|
||||
let recorded_at_iso = recorded_at_iso_of(&most_recent);
|
||||
Some(UserCaseView {
|
||||
case_id,
|
||||
most_recent,
|
||||
time_hms_utc,
|
||||
recorded_at_iso,
|
||||
oneliner,
|
||||
recordings_count,
|
||||
analyzing,
|
||||
has_document,
|
||||
can_analyze,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract only the text from a `Ready` state; other states (Empty,
|
||||
/// Error, missing file) collapse to `None`. Used by single-case pages
|
||||
/// whose headings simply fall back to a generic placeholder — they do
|
||||
/// not need the full five-state UI treatment.
|
||||
async fn ready_text(case_dir: &Path) -> Option<String> {
|
||||
match paths::read_oneliner_state(case_dir).await.0 {
|
||||
Some(OnelinerState::Ready { text, .. }) => Some(text),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user