- Created dedicated EventModal component in src/components/event_modal.rs - Extracted modal logic and styling from calendar component for better separation - Updated data flow to pass full CalendarEvent objects instead of strings - Added PartialEq derive to CalendarEvent for component props - Updated service layer to group events by CalendarEvent objects - Enhanced event click handling to show detailed event information - Modal displays title, description, location, start/end times, and status - Maintained existing modal styling and user interaction patterns 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
182 lines
6.5 KiB
Rust
182 lines
6.5 KiB
Rust
use yew::prelude::*;
|
|
use yew_router::prelude::*;
|
|
use gloo_storage::{LocalStorage, Storage};
|
|
use crate::components::{Login, Register, Calendar};
|
|
use crate::services::{CalendarService, CalendarEvent};
|
|
use std::collections::HashMap;
|
|
use chrono::{Local, NaiveDate, Datelike};
|
|
|
|
#[derive(Clone, Routable, PartialEq)]
|
|
enum Route {
|
|
#[at("/")]
|
|
Home,
|
|
#[at("/login")]
|
|
Login,
|
|
#[at("/register")]
|
|
Register,
|
|
#[at("/calendar")]
|
|
Calendar,
|
|
}
|
|
|
|
#[function_component]
|
|
pub fn App() -> Html {
|
|
let auth_token = use_state(|| -> Option<String> {
|
|
LocalStorage::get("auth_token").ok()
|
|
});
|
|
|
|
let on_login = {
|
|
let auth_token = auth_token.clone();
|
|
Callback::from(move |token: String| {
|
|
auth_token.set(Some(token));
|
|
})
|
|
};
|
|
|
|
let on_logout = {
|
|
let auth_token = auth_token.clone();
|
|
Callback::from(move |_| {
|
|
let _ = LocalStorage::delete("auth_token");
|
|
auth_token.set(None);
|
|
})
|
|
};
|
|
|
|
html! {
|
|
<BrowserRouter>
|
|
<div class="app">
|
|
<header class="app-header">
|
|
<h1>{"Calendar App"}</h1>
|
|
{
|
|
if auth_token.is_some() {
|
|
html! {
|
|
<nav>
|
|
<Link<Route> to={Route::Calendar}>{"Calendar"}</Link<Route>>
|
|
<button onclick={on_logout} class="logout-button">{"Logout"}</button>
|
|
</nav>
|
|
}
|
|
} else {
|
|
html! {
|
|
<nav>
|
|
<Link<Route> to={Route::Login}>{"Login"}</Link<Route>>
|
|
<Link<Route> to={Route::Register}>{"Register"}</Link<Route>>
|
|
</nav>
|
|
}
|
|
}
|
|
}
|
|
</header>
|
|
|
|
<main class="app-main">
|
|
<Switch<Route> render={move |route| {
|
|
let auth_token = (*auth_token).clone();
|
|
let on_login = on_login.clone();
|
|
|
|
match route {
|
|
Route::Home => {
|
|
if auth_token.is_some() {
|
|
html! { <Redirect<Route> to={Route::Calendar}/> }
|
|
} else {
|
|
html! { <Redirect<Route> to={Route::Login}/> }
|
|
}
|
|
}
|
|
Route::Login => {
|
|
if auth_token.is_some() {
|
|
html! { <Redirect<Route> to={Route::Calendar}/> }
|
|
} else {
|
|
html! { <Login {on_login} /> }
|
|
}
|
|
}
|
|
Route::Register => {
|
|
if auth_token.is_some() {
|
|
html! { <Redirect<Route> to={Route::Calendar}/> }
|
|
} else {
|
|
html! { <Register on_register={on_login.clone()} /> }
|
|
}
|
|
}
|
|
Route::Calendar => {
|
|
if auth_token.is_some() {
|
|
html! { <CalendarView /> }
|
|
} else {
|
|
html! { <Redirect<Route> to={Route::Login}/> }
|
|
}
|
|
}
|
|
}
|
|
}} />
|
|
</main>
|
|
</div>
|
|
</BrowserRouter>
|
|
}
|
|
}
|
|
|
|
#[function_component]
|
|
fn CalendarView() -> Html {
|
|
let events = use_state(|| HashMap::<NaiveDate, Vec<CalendarEvent>>::new());
|
|
let loading = use_state(|| true);
|
|
let error = use_state(|| None::<String>);
|
|
|
|
// Get current auth token
|
|
let auth_token: Option<String> = LocalStorage::get("auth_token").ok();
|
|
|
|
let today = Local::now().date_naive();
|
|
let current_year = today.year();
|
|
let current_month = today.month();
|
|
|
|
// Fetch events when component mounts
|
|
{
|
|
let events = events.clone();
|
|
let loading = loading.clone();
|
|
let error = error.clone();
|
|
let auth_token = auth_token.clone();
|
|
|
|
use_effect_with((), move |_| {
|
|
if let Some(token) = auth_token {
|
|
let events = events.clone();
|
|
let loading = loading.clone();
|
|
let error = error.clone();
|
|
|
|
wasm_bindgen_futures::spawn_local(async move {
|
|
let calendar_service = CalendarService::new();
|
|
|
|
match calendar_service.fetch_events_for_month(&token, current_year, current_month).await {
|
|
Ok(calendar_events) => {
|
|
let grouped_events = CalendarService::group_events_by_date(calendar_events);
|
|
events.set(grouped_events);
|
|
loading.set(false);
|
|
}
|
|
Err(err) => {
|
|
error.set(Some(format!("Failed to load events: {}", err)));
|
|
loading.set(false);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
loading.set(false);
|
|
error.set(Some("No authentication token found".to_string()));
|
|
}
|
|
|
|
|| ()
|
|
});
|
|
}
|
|
|
|
html! {
|
|
<div class="calendar-view">
|
|
{
|
|
if *loading {
|
|
html! {
|
|
<div class="calendar-loading">
|
|
<p>{"Loading calendar events..."}</p>
|
|
</div>
|
|
}
|
|
} else if let Some(err) = (*error).clone() {
|
|
html! {
|
|
<div class="calendar-error">
|
|
<p>{format!("Error: {}", err)}</p>
|
|
<Calendar events={HashMap::new()} />
|
|
</div>
|
|
}
|
|
} else {
|
|
html! {
|
|
<Calendar events={(*events).clone()} />
|
|
}
|
|
}
|
|
}
|
|
</div>
|
|
}
|
|
} |