mentorenwahl/frontend/src/routes/home/student_vote.rs

265 lines
9.6 KiB
Rust

use graphql_client::reqwest::post_graphql;
use std::collections::HashMap;
use yew::prelude::*;
use crate::components;
use crate::graphql;
pub enum Msg {
DoneFetchingCanVote {
errors: Option<Vec<String>>,
can_vote: bool,
},
DoneFetchingConfig {
errors: Option<Vec<String>>,
min: usize,
},
DoneFetchingTeachers {
errors: Option<Vec<String>>,
teachers: Vec<graphql::queries::teachers::teachers::TeachersTeachers>,
},
RadioSelect {
priority: usize,
teacher: i64,
},
Submit,
Vote(Option<Vec<String>>),
}
#[derive(Properties, PartialEq)]
pub struct StudentVoteProps {
pub token: String,
pub onvoted: Callback<()>,
}
pub struct StudentVote {
fetching: bool,
can_vote: bool,
errors: Option<Vec<String>>,
min: usize,
teachers: Option<Vec<graphql::queries::teachers::teachers::TeachersTeachers>>,
votes: HashMap<usize, Option<i64>>,
}
impl Component for StudentVote {
type Message = Msg;
type Properties = StudentVoteProps;
fn create(ctx: &Context<Self>) -> Self {
let client = graphql::client(Some(&ctx.props().token)).unwrap();
ctx.link().send_future(async move {
let can_vote_response =
post_graphql::<graphql::queries::students_can_vote::StudentsCanVote, _>(
&client,
graphql::URL.as_str(),
graphql::queries::students_can_vote::students_can_vote::Variables,
)
.await
.unwrap();
Msg::DoneFetchingCanVote {
errors: components::graphql_errors::convert(can_vote_response.errors),
can_vote: can_vote_response.data.unwrap().students_can_vote,
}
});
Self {
fetching: true,
can_vote: false,
errors: None,
min: 0,
teachers: None,
votes: HashMap::new(),
}
}
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::DoneFetchingCanVote { errors, can_vote } => {
self.errors = errors;
self.can_vote = can_vote;
if self.can_vote {
let client = graphql::client(Some(&ctx.props().token)).unwrap();
ctx.link().send_future(async move {
let response = post_graphql::<graphql::queries::config::Config, _>(
&client,
graphql::URL.as_str(),
graphql::queries::config::config::Variables,
)
.await
.unwrap();
Msg::DoneFetchingConfig {
errors: components::graphql_errors::convert(response.errors),
min: response
.data
.unwrap()
.config
.minimum_teacher_selection_count
as usize,
}
});
} else {
self.fetching = false;
}
true
}
Msg::DoneFetchingConfig { errors, min } => {
self.errors = errors;
self.min = min;
let client = graphql::client(Some(&ctx.props().token)).unwrap();
ctx.link().send_future(async move {
let response = post_graphql::<graphql::queries::teachers::Teachers, _>(
&client,
graphql::URL.as_str(),
graphql::queries::teachers::teachers::Variables,
)
.await
.unwrap();
Msg::DoneFetchingTeachers {
errors: components::graphql_errors::convert(response.errors),
teachers: response.data.unwrap().teachers,
}
});
true
}
Msg::DoneFetchingTeachers { errors, teachers } => {
self.fetching = false;
self.errors = errors;
self.teachers = Some(teachers);
true
}
Msg::RadioSelect { priority, teacher } => {
self.votes.insert(priority, Some(teacher));
for (p, t) in self.votes.iter_mut() {
if *p > priority && *t == Some(teacher) {
*t = None;
}
}
true
}
Msg::Submit => {
let client = graphql::client(Some(&ctx.props().token)).unwrap();
let mut ids: Vec<(&usize, i64)> = self
.votes
.iter()
.filter_map(|(p, t)| t.map(|x| (p, x)))
.collect();
ids.sort_by(|a, b| a.0.cmp(b.0));
let teacher_ids: Vec<i64> = ids.into_iter().map(|(_, t)| t).collect();
ctx.link().send_future(async move {
let response = post_graphql::<graphql::mutations::vote::Vote, _>(
&client,
graphql::URL.as_str(),
graphql::mutations::vote::vote::Variables { teacher_ids },
)
.await
.unwrap();
log::debug!("{:?}", response.data.unwrap().create_vote.unwrap());
Msg::Vote(components::graphql_errors::convert(response.errors))
});
false
}
Msg::Vote(errors) => {
self.errors = errors;
if self.errors.is_none() {
ctx.props().onvoted.emit(());
false
} else {
true
}
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
if self.fetching {
html! {
<p>{ "Fetching..." }</p>
}
} else if !self.can_vote {
html! {
<p>{ "Noch nicht erlaubt zu wählen." }</p>
}
} else {
let onsubmit = ctx.link().callback(|e: FocusEvent| {
e.prevent_default();
Msg::Submit
});
let mut teachers: Vec<i64> = vec![];
html! {
<>
<h3>{ "Wähle deine Wunschmentoren aus:" }</h3>
<form {onsubmit}>
{ for (0..self.min).map(|i| {
let curr_t = self.votes.get(&i);
if let Some(te) = curr_t {
if let Some(t) = te {
teachers.push(*t);
}
}
let name = format!("mentors_{}", i);
html! {
<>
<fieldset>
<legend>{ format!("{}. Wahl", i + 1) }</legend>
{ for self.teachers.as_ref().unwrap().iter().enumerate().filter_map(|(j, t)| {
let checked = curr_t == Some(&Some(t.id));
if teachers.contains(&t.id) && !checked {
None
} else {
let id = format!("{}_{}", name, j);
let teacher_id = t.id;
let onchange = ctx.link().callback(move |_| Msg::RadioSelect { priority: i, teacher: teacher_id });
Some(html! {
<div>
<input
type="radio"
id={id.to_owned()}
name={name.to_owned()}
value={t.user.name.to_owned()}
required=true
{onchange}
{checked}
/>
<label for={id}>{ &t.user.name }</label>
</div>
})
}
}) }
</fieldset>
if i < self.min - 1 {
<br />
}
</>
}
}) }
<div>
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</div>
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
</form>
</>
}
}
}
}