Average of tickets submitted per weekday

Avvailable in the diagnostics
This commit is contained in:
2026-05-02 15:00:46 +02:00
parent 37c00b0190
commit 504569e305

View File

@@ -1,15 +1,85 @@
use chrono::{DateTime, Datelike, Utc};
use gloo_net::http::Request; use gloo_net::http::Request;
use serde::Deserialize;
use wasm_bindgen_futures::spawn_local; use wasm_bindgen_futures::spawn_local;
use yew::prelude::*; use yew::prelude::*;
use yew_router::prelude::*;
use crate::pages::ticket::{ActiveUser, Ticket}; use crate::pages::ticket::{ActiveUser, Ticket};
#[derive(Debug, Deserialize, Clone)]
struct Date {
date: DateTime<Utc>, // matches JSON "date": "2026-05-01T14:23:00Z"
}
fn weekday_index(dt: &DateTime<Utc>) -> usize {
// chrono::Weekday: Mon = 1 ... Sun = 7
match dt.weekday() {
chrono::Weekday::Mon => 0,
chrono::Weekday::Tue => 1,
chrono::Weekday::Wed => 2,
chrono::Weekday::Thu => 3,
chrono::Weekday::Fri => 4,
chrono::Weekday::Sat => 5,
chrono::Weekday::Sun => 6,
}
}
fn count_by_weekday(tickets: &[Date]) -> [usize; 7] {
let mut counts = [0usize; 7];
for t in tickets {
counts[weekday_index(&t.date)] += 1;
}
counts
}
fn day_counts(dates: &[Date]) -> [usize; 7] {
if dates.is_empty() {
return [0usize; 7];
}
let mut min = dates[0].date.date_naive();
let mut max = min;
for d in dates.iter().skip(1) {
let dt = d.date.date_naive();
if dt < min {
min = dt
}
if dt > max {
max = dt
}
}
let total_days = (max - min).num_days() + 1;
if total_days <= 0 {
return [0usize; 7];
}
let mut occ = [0usize; 7];
let mut current = min;
for _ in 0..total_days {
let wd = current.weekday();
let idx = match wd {
chrono::Weekday::Mon => 0,
chrono::Weekday::Tue => 1,
chrono::Weekday::Wed => 2,
chrono::Weekday::Thu => 3,
chrono::Weekday::Fri => 4,
chrono::Weekday::Sat => 5,
chrono::Weekday::Sun => 6,
};
occ[idx] += 1;
current = current.succ();
}
occ
}
#[component(Diagnostics)] #[component(Diagnostics)]
pub fn diagnostics_component() -> Html { pub fn diagnostics_component() -> Html {
html! { html! {
<div> <div>
<TicketCount/> <TicketCount/>
<SubmitStats/>
</div> </div>
} }
} }
@@ -108,5 +178,83 @@ pub fn ticket_count_component() -> Html {
} }
} }
// #[component(SubmitStats)] #[component(SubmitStats)]
// pub fn submit_stats_component() -> Html {} pub fn submit_stats_component() -> Html {
let tickets = use_state(|| Vec::<Date>::new());
let error = use_state(|| None::<String>);
let loading = use_state(|| false);
{
let tickets = tickets.clone();
let error = error.clone();
let loading = loading.clone();
use_effect_with((), move |_| {
loading.set(true);
spawn_local(async move {
let url = "/api/tickets".to_string();
match Request::get(&url).send().await {
Ok(response) if response.status() == 200 => {
match response.json::<Vec<Date>>().await {
Ok(t) => tickets.set(t),
Err(e) => error.set(Some(format!("parse error: {}", e))),
}
}
Ok(response) => {
if let Ok(text) = response.text().await {
error.set(Some(text));
} else {
error.set(Some(format!("status {}", response.status())));
}
}
Err(err) => error.set(Some(format!("Network error: {}", err))),
}
loading.set(false);
});
|| ()
});
}
let counts = count_by_weekday(&tickets);
let occ = day_counts(&tickets);
let mut avg = [0.0f64; 7];
for i in 0..7 {
if occ[i] > 0 {
avg[i] = counts[i] as f64 / occ[i] as f64;
} else {
avg[i] = 0.0;
}
}
let weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
let (max_idx, _max_val) = counts
.iter()
.enumerate()
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
.map(|(i, _)| (i, ()))
.unwrap_or((0, ()));
html! {
<div>
if *loading {
<p>{ "Loading..." }</p>
}
if let Some(e) = &*error {
<p style="color: red;">{ e.clone() }</p>
}
<h3>{ "Tickets per weekday" }</h3>
<ul>
{ for (0..7).map(|i| {
let is_max = i == max_idx;
html! {
<li style={ if is_max { "font-weight: bold; color: green;" } else { "" } }>
{ format!("{}: {:3}", weekdays[i], avg[i]) }
{ if is_max { html!{ <span>{ " ← most" }</span> } } else { html!{} } }
</li>
}
})}
</ul>
</div>
}
}