diff --git a/aep-schedule-website/src/frontend/components/options/todo.rs b/aep-schedule-website/src/frontend/components/options/todo.rs index 526f973a3c34adcda6f9062d881fb2dae42bae28..6ba9845852a8e2158739b7ec2263e8d6233213f6 100644 --- a/aep-schedule-website/src/frontend/components/options/todo.rs +++ b/aep-schedule-website/src/frontend/components/options/todo.rs @@ -13,6 +13,7 @@ pub fn Step( step: ReadSignal<u8>, title: &'static str, description: &'static str, + #[prop(optional)] children: Option<Children>, ) -> impl IntoView { let bg_color = move || { match n.cmp(&step.get()) { @@ -40,6 +41,7 @@ pub fn Step( <p class="text-gray-700"> {description} </p> + {children.map(|c| c())} </div> </div> } @@ -49,6 +51,8 @@ pub fn Step( pub fn Todo( action: Action<SchedulesOptions, Vec<Schedule>>, step: ReadSignal<u8>, + section_error: RwSignal<String>, + personal_error: RwSignal<String>, ) -> impl IntoView { let state: OptionState = use_context().unwrap(); let first_generation_done: FirstGenerationDone = use_context().unwrap(); @@ -71,8 +75,12 @@ pub fn Todo( <div class="grid gap-6 row-gap-10"> <div class="lg:py-6 lg:pr-16"> <Step n=1 step title="Ajoutez vos cours" description="Utilisez la barre de recherche à gauche pour trouver et sélectionner vos cours. Une fois les cours sélectionnés, ils apparaîtront comme un onglet."/> - <Step n=2 step title="Ouvrez des sections" description="Assurez d'avoir au moins une section d'ouverte pour la théorie et la pratique. En sélectionnant l'onglet du cours et en appuyant sur les sections."/> - <Step n=3 step title="Forcer des heures libres" description="Sélectionnez une plage de temps à avoir absolument libre en pressant et relâchant sur votre horaire personnel."/> + <Step n=2 step title="Ouvrez des sections" description="Assurez d'avoir au moins une section d'ouverte pour la théorie et la pratique. En sélectionnant l'onglet du cours et en appuyant sur les sections."> + <span class="text-red-800">{section_error}</span> + </Step> + <Step n=3 step title="Forcer des heures libres" description="Sélectionnez une plage de temps à avoir absolument libre en pressant et relâchant sur votre horaire personnel."> + <span class="text-red-800">{personal_error}</span> + </Step> <Step n=4 step title="Ajustez les paramètres" description="Bougez les curseurs en bas pour ajuster vos préférences. Vous pouvez choisir d'avoir plus de congés, de commencer en moyenne les cours plus tôt ou plus tard, ou de finir en moyenne plus tôt."/> <div class="flex items-center"> <div class="flex flex-col items-center mr-4"> diff --git a/aep-schedule-website/src/frontend/components/schedules.rs b/aep-schedule-website/src/frontend/components/schedules.rs index 2ec64221d114554c79ce2842eca7d422296a035d..a03e5aa77fe57436d3c836033207903e494abb64 100644 --- a/aep-schedule-website/src/frontend/components/schedules.rs +++ b/aep-schedule-website/src/frontend/components/schedules.rs @@ -8,6 +8,8 @@ use leptos::*; pub fn SchedulesComponent( read_signal: RwSignal<Option<Vec<Schedule>>>, action: Action<SchedulesOptions, Vec<Schedule>>, + section_error: RwSignal<String>, + personal_error: RwSignal<String>, step: ReadSignal<u8>, ) -> impl IntoView { view! { @@ -23,7 +25,7 @@ pub fn SchedulesComponent( } }).collect_view(), _ => view ! { - <Todo action step/> + <Todo action step section_error personal_error/> } } } diff --git a/aep-schedule-website/src/frontend/pages/generator.rs b/aep-schedule-website/src/frontend/pages/generator.rs index 783631efc44307d615d17f88997f90d3d7573e98..a9b6757fc49a5be88ad3d5434c41aa452a7dce13 100644 --- a/aep-schedule-website/src/frontend/pages/generator.rs +++ b/aep-schedule-website/src/frontend/pages/generator.rs @@ -31,18 +31,37 @@ pub fn GeneratorPage() -> impl IntoView { let state = OptionState::default(); + let section_error = create_rw_signal("".to_string()); + let personal_error = create_rw_signal("".to_string()); + let validate = move |state: OptionState| { let mut options: SchedulesOptions = (&state).into(); if options.courses_to_take.is_empty() { set_step.set(1); return; } - if !options.get_impossible_course().is_empty() { + let mut impossible_courses = options.get_impossible_course().into_iter(); + if let Some(first_impossible_course) = impossible_courses.next() { + let mut error = format!("Les sections des/du cours {}", first_impossible_course); + for impossible_course in impossible_courses { + error.push_str(", "); + error.push_str(&impossible_course); + } + error.push_str(" sont toutes fermées."); + section_error.set(error); set_step.set(2); return; } options.apply_personal_schedule(); - if !options.get_impossible_course().is_empty() { + let mut impossible_courses = options.get_impossible_course().into_iter(); + if let Some(first_impossible_course) = impossible_courses.next() { + let mut error = format!("Les sections des/du cours {}", first_impossible_course); + for impossible_course in impossible_courses { + error.push_str(", "); + error.push_str(&impossible_course); + } + error.push_str(" sont en conflits avec les heures libres sélectionnées."); + personal_error.set(error); set_step.set(3); return; } @@ -55,10 +74,10 @@ pub fn GeneratorPage() -> impl IntoView { view! { <aside class="left-panel" class=("hide-left-panel", hide)> - <OptionsForms action=action validate step/> + <OptionsForms action validate step/> </aside> <section class="right-panel"> - <SchedulesComponent action=action read_signal=action.value() step/> + <SchedulesComponent section_error personal_error action=action read_signal=action.value() step/> </section> <Notifications modal set_modal/> <button on:click=move |_| {set_hide(false)} id="go-back"><CaretDoubleRight weight=IconWeight::Regular size="3vh"/></button>