diff --git a/frontend/src/pages/utilities.rs b/frontend/src/pages/utilities.rs index 57c6583..d053b5e 100644 --- a/frontend/src/pages/utilities.rs +++ b/frontend/src/pages/utilities.rs @@ -1,3 +1,5 @@ +use std::{collections::HashMap, fmt::format}; + use chrono::{DateTime, Datelike, Utc}; use gloo_net::http::Request; use serde::Deserialize; @@ -6,9 +8,15 @@ use yew::prelude::*; use crate::pages::ticket::{ActiveUser, Ticket}; -#[derive(Debug, Deserialize, Clone)] -struct Date { - date: DateTime, // matches JSON "date": "2026-05-01T14:23:00Z" +#[derive(Debug, Deserialize, Clone, PartialEq)] +struct TicketPartial { + date: DateTime, + room: i16, +} + +#[derive(Properties, PartialEq)] +struct RoomTotalsProps { + tickets: Vec, } fn weekday_index(dt: &DateTime) -> usize { @@ -24,7 +32,7 @@ fn weekday_index(dt: &DateTime) -> usize { } } -fn count_by_weekday(tickets: &[Date]) -> [usize; 7] { +fn count_by_weekday(tickets: &[TicketPartial]) -> [usize; 7] { let mut counts = [0usize; 7]; for t in tickets { counts[weekday_index(&t.date)] += 1; @@ -32,14 +40,14 @@ fn count_by_weekday(tickets: &[Date]) -> [usize; 7] { counts } -fn day_counts(dates: &[Date]) -> [usize; 7] { - if dates.is_empty() { +fn day_counts(partials: &[TicketPartial]) -> [usize; 7] { + if partials.is_empty() { return [0usize; 7]; } - let mut min = dates[0].date.date_naive(); + let mut min = partials[0].date.date_naive(); let mut max = min; - for d in dates.iter().skip(1) { + for d in partials.iter().skip(1) { let dt = d.date.date_naive(); if dt < min { min = dt @@ -74,6 +82,16 @@ fn day_counts(dates: &[Date]) -> [usize; 7] { occ } +fn parse_room(r: i16) -> String { + if r < 0 { + format!("K{}", r.abs()) + } else if r > 1000 { + format!("D{}", r - 1000) + } else { + r.to_string() + } +} + #[component(Diagnostics)] pub fn diagnostics_component() -> Html { html! { @@ -180,7 +198,7 @@ pub fn ticket_count_component() -> Html { #[component(SubmitStats)] pub fn submit_stats_component() -> Html { - let tickets = use_state(|| Vec::::new()); + let tickets = use_state(|| Vec::::new()); let error = use_state(|| None::); let loading = use_state(|| false); @@ -195,7 +213,7 @@ pub fn submit_stats_component() -> Html { let url = "/api/tickets".to_string(); match Request::get(&url).send().await { Ok(response) if response.status() == 200 => { - match response.json::>().await { + match response.json::>().await { Ok(t) => tickets.set(t), Err(e) => error.set(Some(format!("parse error: {}", e))), } @@ -255,6 +273,30 @@ pub fn submit_stats_component() -> Html { } })} + + + } +} + +#[component(RoomTotalTickets)] +fn room_total_component(props: &RoomTotalsProps) -> Html { + let mut totals: HashMap = HashMap::new(); + for t in &props.tickets { + *totals.entry(t.room).or_insert(0) += 1; + } + + let mut totals_vec: Vec<(i16, usize)> = totals.into_iter().collect(); + totals_vec.sort_by(|a, b| b.1.cmp(&a.1)); + + html! { +
+

{ "Tickets pro Raum" }

+
    + { for totals_vec.into_iter().map(|(room, count)| { + let label = parse_room(room); + html! {
  • { format!("{}: {}", label, count) }
  • } + }) } +
} }