115 lines
2.8 KiB
Rust
115 lines
2.8 KiB
Rust
use crate::localize_day;
|
|
use crate::session_date_calculator::{DayIter, NthWeekday};
|
|
use chrono::Weekday;
|
|
use leptos::prelude::*;
|
|
use leptos::server_fn::request::browser::Request;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[component]
|
|
pub fn App() -> impl IntoView {
|
|
let session_config =
|
|
LocalResource::new(|| async { load_config::<SessionConfig>("session_config").await });
|
|
|
|
let session_dates = move || {
|
|
session_config
|
|
.get()
|
|
.as_deref()
|
|
.cloned()
|
|
.map(|config| match config {
|
|
Ok(config) => {
|
|
let config = config.unwrap_or_default();
|
|
view! { <Sessions config /> }.into_any()
|
|
}
|
|
Err(e) => view! {
|
|
<div class="box error-background">
|
|
<h1>"Error"</h1>
|
|
<p>{e}</p>
|
|
</div>
|
|
}
|
|
.into_any(),
|
|
})
|
|
};
|
|
|
|
view! {
|
|
<div class="background">
|
|
<Suspense fallback=|| "Laden...">{session_dates}</Suspense>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
fn Sessions(config: SessionConfig) -> impl IntoView {
|
|
let mut session_iter = DayIter::default().filter(move |day| {
|
|
config
|
|
.sessions
|
|
.iter()
|
|
.any(|session_day| session_day.matches(day))
|
|
});
|
|
let (session_dates, set_session_dates) =
|
|
signal(session_iter.by_ref().take(2).collect::<Vec<_>>());
|
|
|
|
view! {
|
|
<div class="column">
|
|
<div class="wide box elem-background">
|
|
<h1>"Anstehende Proben Termine"</h1>
|
|
<p>{config.motd}</p>
|
|
</div>
|
|
<For
|
|
each=move || session_dates.get()
|
|
key=|day| day.date
|
|
children=move |day| view! { <div class="box elem-background">{localize_day(&day)}</div> }
|
|
/>
|
|
<div
|
|
class="box button elem-background"
|
|
on:click=move |_| {
|
|
set_session_dates.write().extend(session_iter.by_ref().take(3));
|
|
}
|
|
>
|
|
"Mehr anzeigen"
|
|
</div>
|
|
</div>
|
|
}
|
|
}
|
|
|
|
async fn load_config<T>(name: &str) -> Result<Option<T>, String>
|
|
where
|
|
T: for<'a> Deserialize<'a>,
|
|
{
|
|
let response = Request::get(&format!("/{name}.json"))
|
|
.send()
|
|
.await
|
|
.map_err(|e| format!("HTTP error: {e}"))?;
|
|
if response
|
|
.headers()
|
|
.get("content-type")
|
|
.is_none_or(|content_type| content_type != "application/json")
|
|
{
|
|
return Ok(None);
|
|
}
|
|
|
|
let config = response
|
|
.json()
|
|
.await
|
|
.map_err(|e| format!("JSON error: {e}"))?;
|
|
Ok(Some(config))
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(default)]
|
|
pub struct SessionConfig {
|
|
pub motd: Option<String>,
|
|
pub sessions: Vec<NthWeekday>,
|
|
}
|
|
|
|
impl Default for SessionConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
motd: Some("Probe jeden ersten Sonntag im Monat um 10:00 Uhr und jeden dritten Dienstag im Monat um 18:30 Uhr".to_string()),
|
|
sessions: vec![
|
|
NthWeekday::new(1, Weekday::Sun),
|
|
NthWeekday::new(3, Weekday::Tue),
|
|
],
|
|
}
|
|
}
|
|
}
|