Tickets per rooms shown
In diagnostics
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
use std::{collections::HashMap, fmt::format};
|
||||||
|
|
||||||
use chrono::{DateTime, Datelike, Utc};
|
use chrono::{DateTime, Datelike, Utc};
|
||||||
use gloo_net::http::Request;
|
use gloo_net::http::Request;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -6,9 +8,15 @@ use yew::prelude::*;
|
|||||||
|
|
||||||
use crate::pages::ticket::{ActiveUser, Ticket};
|
use crate::pages::ticket::{ActiveUser, Ticket};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone, PartialEq)]
|
||||||
struct Date {
|
struct TicketPartial {
|
||||||
date: DateTime<Utc>, // matches JSON "date": "2026-05-01T14:23:00Z"
|
date: DateTime<Utc>,
|
||||||
|
room: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
struct RoomTotalsProps {
|
||||||
|
tickets: Vec<TicketPartial>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn weekday_index(dt: &DateTime<Utc>) -> usize {
|
fn weekday_index(dt: &DateTime<Utc>) -> usize {
|
||||||
@@ -24,7 +32,7 @@ fn weekday_index(dt: &DateTime<Utc>) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_by_weekday(tickets: &[Date]) -> [usize; 7] {
|
fn count_by_weekday(tickets: &[TicketPartial]) -> [usize; 7] {
|
||||||
let mut counts = [0usize; 7];
|
let mut counts = [0usize; 7];
|
||||||
for t in tickets {
|
for t in tickets {
|
||||||
counts[weekday_index(&t.date)] += 1;
|
counts[weekday_index(&t.date)] += 1;
|
||||||
@@ -32,14 +40,14 @@ fn count_by_weekday(tickets: &[Date]) -> [usize; 7] {
|
|||||||
counts
|
counts
|
||||||
}
|
}
|
||||||
|
|
||||||
fn day_counts(dates: &[Date]) -> [usize; 7] {
|
fn day_counts(partials: &[TicketPartial]) -> [usize; 7] {
|
||||||
if dates.is_empty() {
|
if partials.is_empty() {
|
||||||
return [0usize; 7];
|
return [0usize; 7];
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut min = dates[0].date.date_naive();
|
let mut min = partials[0].date.date_naive();
|
||||||
let mut max = min;
|
let mut max = min;
|
||||||
for d in dates.iter().skip(1) {
|
for d in partials.iter().skip(1) {
|
||||||
let dt = d.date.date_naive();
|
let dt = d.date.date_naive();
|
||||||
if dt < min {
|
if dt < min {
|
||||||
min = dt
|
min = dt
|
||||||
@@ -74,6 +82,16 @@ fn day_counts(dates: &[Date]) -> [usize; 7] {
|
|||||||
occ
|
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)]
|
#[component(Diagnostics)]
|
||||||
pub fn diagnostics_component() -> Html {
|
pub fn diagnostics_component() -> Html {
|
||||||
html! {
|
html! {
|
||||||
@@ -180,7 +198,7 @@ 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 tickets = use_state(|| Vec::<TicketPartial>::new());
|
||||||
let error = use_state(|| None::<String>);
|
let error = use_state(|| None::<String>);
|
||||||
let loading = use_state(|| false);
|
let loading = use_state(|| false);
|
||||||
|
|
||||||
@@ -195,7 +213,7 @@ pub fn submit_stats_component() -> Html {
|
|||||||
let url = "/api/tickets".to_string();
|
let url = "/api/tickets".to_string();
|
||||||
match Request::get(&url).send().await {
|
match Request::get(&url).send().await {
|
||||||
Ok(response) if response.status() == 200 => {
|
Ok(response) if response.status() == 200 => {
|
||||||
match response.json::<Vec<Date>>().await {
|
match response.json::<Vec<TicketPartial>>().await {
|
||||||
Ok(t) => tickets.set(t),
|
Ok(t) => tickets.set(t),
|
||||||
Err(e) => error.set(Some(format!("parse error: {}", e))),
|
Err(e) => error.set(Some(format!("parse error: {}", e))),
|
||||||
}
|
}
|
||||||
@@ -255,6 +273,30 @@ pub fn submit_stats_component() -> Html {
|
|||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
|
<RoomTotalTickets tickets={(*tickets).clone()}/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component(RoomTotalTickets)]
|
||||||
|
fn room_total_component(props: &RoomTotalsProps) -> Html {
|
||||||
|
let mut totals: HashMap<i16, usize> = 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! {
|
||||||
|
<div>
|
||||||
|
<h3>{ "Tickets pro Raum" }</h3>
|
||||||
|
<ul>
|
||||||
|
{ for totals_vec.into_iter().map(|(room, count)| {
|
||||||
|
let label = parse_room(room);
|
||||||
|
html! { <li>{ format!("{}: {}", label, count) }</li> }
|
||||||
|
}) }
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user