From 2b568d37f6ecff4eda9d967efae445f5b46b3c1b Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Sun, 13 Nov 2022 12:44:08 +0100 Subject: [PATCH] Add basic student voting --- frontend/graphql/mutations/vote.graphql | 5 ++ frontend/src/agents/mod.rs | 1 - frontend/src/agents/teacher_registered.rs | 47 ----------- frontend/src/graphql/mutations/mod.rs | 1 + frontend/src/graphql/mutations/vote.rs | 9 ++ frontend/src/routes/home/student_home.rs | 32 +++++-- frontend/src/routes/home/student_vote.rs | 84 ++++++++++++++----- frontend/src/routes/home/teacher_home.rs | 11 +-- .../src/routes/home/teacher_registration.rs | 27 +++--- frontend/src/routes/login.rs | 32 +++---- 10 files changed, 138 insertions(+), 111 deletions(-) create mode 100644 frontend/graphql/mutations/vote.graphql delete mode 100644 frontend/src/agents/teacher_registered.rs create mode 100644 frontend/src/graphql/mutations/vote.rs diff --git a/frontend/graphql/mutations/vote.graphql b/frontend/graphql/mutations/vote.graphql new file mode 100644 index 0000000..102181b --- /dev/null +++ b/frontend/graphql/mutations/vote.graphql @@ -0,0 +1,5 @@ +mutation Vote($teacherIds: [Int!]!) { + createVote(input: { teacherIds: $teacherIds }) { + id + } +} diff --git a/frontend/src/agents/mod.rs b/frontend/src/agents/mod.rs index 6380c68..a5931af 100644 --- a/frontend/src/agents/mod.rs +++ b/frontend/src/agents/mod.rs @@ -1,2 +1 @@ pub mod logged_in; -pub mod teacher_registered; diff --git a/frontend/src/agents/teacher_registered.rs b/frontend/src/agents/teacher_registered.rs deleted file mode 100644 index 291d0d2..0000000 --- a/frontend/src/agents/teacher_registered.rs +++ /dev/null @@ -1,47 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; -use yew_agent::{Agent, AgentLink, Context, HandlerId}; - -#[derive(Serialize, Deserialize, Debug)] -pub enum Request { - Registered, -} - -pub struct EventBus { - link: AgentLink, - subscribers: HashSet, -} - -impl Agent for EventBus { - type Reach = Context; - type Message = (); - type Input = Request; - type Output = (); - - fn create(link: AgentLink) -> Self { - Self { - link, - subscribers: HashSet::new(), - } - } - - fn update(&mut self, _msg: Self::Message) {} - - fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) { - match msg { - Request::Registered => { - for sub in self.subscribers.iter() { - self.link.respond(*sub, ()); - } - } - } - } - - fn connected(&mut self, id: HandlerId) { - self.subscribers.insert(id); - } - - fn disconnected(&mut self, id: HandlerId) { - self.subscribers.remove(&id); - } -} diff --git a/frontend/src/graphql/mutations/mod.rs b/frontend/src/graphql/mutations/mod.rs index 6089d09..521c7c4 100644 --- a/frontend/src/graphql/mutations/mod.rs +++ b/frontend/src/graphql/mutations/mod.rs @@ -1,2 +1,3 @@ pub mod login; pub mod register_teacher; +pub mod vote; diff --git a/frontend/src/graphql/mutations/vote.rs b/frontend/src/graphql/mutations/vote.rs new file mode 100644 index 0000000..4588dd5 --- /dev/null +++ b/frontend/src/graphql/mutations/vote.rs @@ -0,0 +1,9 @@ +use graphql_client::GraphQLQuery; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/mutations/vote.graphql", + response_derives = "Debug" +)] +pub struct Vote; diff --git a/frontend/src/routes/home/student_home.rs b/frontend/src/routes/home/student_home.rs index d66d46e..590b9cf 100644 --- a/frontend/src/routes/home/student_home.rs +++ b/frontend/src/routes/home/student_home.rs @@ -2,28 +2,48 @@ use yew::prelude::*; use crate::routes::home::student_vote; +pub enum Msg { + Registered, +} + #[derive(Properties, PartialEq)] pub struct StudentHomeProps { pub token: String, pub voted: bool, } -pub struct StudentHome; +pub struct StudentHome { + voted: bool, +} impl Component for StudentHome { - type Message = (); + type Message = Msg; type Properties = StudentHomeProps; - fn create(_ctx: &Context) -> Self { - Self + fn create(ctx: &Context) -> Self { + Self { + voted: ctx.props().voted, + } + } + + fn update(&mut self, _ctx: &Context, msg: Self::Message) -> bool { + match msg { + Msg::Registered => { + self.voted = true; + true + } + } } fn view(&self, ctx: &Context) -> Html { html! { - if ctx.props().voted { + if self.voted {

{ "Alles in Ordnung." }

} else { - + } } } diff --git a/frontend/src/routes/home/student_vote.rs b/frontend/src/routes/home/student_vote.rs index 9e5e96b..32dfc0e 100644 --- a/frontend/src/routes/home/student_vote.rs +++ b/frontend/src/routes/home/student_vote.rs @@ -23,11 +23,13 @@ pub enum Msg { teacher: i64, }, Submit, + Vote(Option>), } #[derive(Properties, PartialEq)] pub struct StudentVoteProps { pub token: String, + pub onvoted: Callback<()>, } pub struct StudentVote { @@ -77,25 +79,30 @@ impl Component for StudentVote { self.errors = errors; self.can_vote = can_vote; - let client = graphql::client(Some(&ctx.props().token)).unwrap(); - ctx.link().send_future(async move { - let response = post_graphql::( - &client, - graphql::URL.as_str(), - graphql::queries::config::config::Variables, - ) - .await - .unwrap(); + if self.can_vote { + let client = graphql::client(Some(&ctx.props().token)).unwrap(); + ctx.link().send_future(async move { + let response = post_graphql::( + &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, - } - }); + 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 } @@ -138,9 +145,39 @@ impl Component for StudentVote { true } Msg::Submit => { - log::debug!("{:?}", self.votes); + 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 = ids.into_iter().map(|(_, t)| t).collect(); + + ctx.link().send_future(async move { + let response = post_graphql::( + &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 + } + } } } @@ -149,6 +186,10 @@ impl Component for StudentVote { html! {

{ "Fetching..." }

} + } else if !self.can_vote { + html! { +

{ "Noch nicht erlaubt zu wählen." }

+ } } else { let onsubmit = ctx.link().callback(|e: FocusEvent| { e.prevent_default(); @@ -210,8 +251,11 @@ impl Component for StudentVote { }) }
- + +
+ + } diff --git a/frontend/src/routes/home/teacher_home.rs b/frontend/src/routes/home/teacher_home.rs index cd2fe07..a05d9e9 100644 --- a/frontend/src/routes/home/teacher_home.rs +++ b/frontend/src/routes/home/teacher_home.rs @@ -1,7 +1,5 @@ use yew::prelude::*; -use yew_agent::{Bridge, Bridged}; -use crate::agents; use crate::routes::home::teacher_registration; pub enum Msg { @@ -16,7 +14,6 @@ pub struct TeacherHomeProps { pub struct TeacherHome { registered: bool, - _teacher_registered_producer: Box>, } impl Component for TeacherHome { @@ -26,9 +23,6 @@ impl Component for TeacherHome { fn create(ctx: &Context) -> Self { Self { registered: ctx.props().registered, - _teacher_registered_producer: agents::teacher_registered::EventBus::bridge( - ctx.link().callback(|_| Msg::Registered), - ), } } @@ -46,7 +40,10 @@ impl Component for TeacherHome { if self.registered {

{ "Alles in Ordnung." }

} else { - + } } } diff --git a/frontend/src/routes/home/teacher_registration.rs b/frontend/src/routes/home/teacher_registration.rs index 9bf2322..6f77ae1 100644 --- a/frontend/src/routes/home/teacher_registration.rs +++ b/frontend/src/routes/home/teacher_registration.rs @@ -1,9 +1,7 @@ use graphql_client::reqwest::post_graphql; use web_sys::HtmlInputElement; use yew::prelude::*; -use yew_agent::{Dispatched, Dispatcher}; -use crate::agents; use crate::components; use crate::graphql; @@ -15,12 +13,12 @@ pub enum Msg { #[derive(Properties, PartialEq)] pub struct TeacherRegistrationProps { pub token: String, + pub onregistered: Callback<()>, } pub struct TeacherRegistration { max_students: NodeRef, errors: Option>, - teacher_registered_event_bus: Dispatcher, } impl Component for TeacherRegistration { @@ -31,7 +29,6 @@ impl Component for TeacherRegistration { Self { max_students: NodeRef::default(), errors: None, - teacher_registered_event_bus: agents::teacher_registered::EventBus::dispatcher(), } } @@ -57,7 +54,6 @@ impl Component for TeacherRegistration { ) .await .unwrap(); - log::debug!("{:?}", response.data.unwrap().register_teacher); Msg::Registration(components::graphql_errors::convert(response.errors)) }); @@ -68,8 +64,7 @@ impl Component for TeacherRegistration { Msg::Registration(errors) => { self.errors = errors; if self.errors.is_none() { - self.teacher_registered_event_bus - .send(agents::teacher_registered::Request::Registered); + ctx.props().onregistered.emit(()); false } else { true @@ -88,15 +83,17 @@ impl Component for TeacherRegistration { html! { <>
- +
+ +
-

- - +
+ +
diff --git a/frontend/src/routes/login.rs b/frontend/src/routes/login.rs index 348a23e..4e5c680 100644 --- a/frontend/src/routes/login.rs +++ b/frontend/src/routes/login.rs @@ -89,23 +89,25 @@ impl Component for Login { <> <form {onsubmit}> - <label for="username"> - { "Benutzername:" } - <br /> - <input ref={self.username.clone()} type="text" id="username" name="username" /> - </label> + <div> + <label for="username"> + { "Benutzername:" } + <br /> + <input ref={self.username.clone()} type="text" id="username" name="username" /> + </label> + </div> - <br /> + <div> + <label for="password"> + { "Kennwort:" } + <br /> + <input ref={self.password.clone()} type="password" id="password" name="password" /> + </label> + </div> - <label for="password"> - { "Kennwort:" } - <br /> - <input ref={self.password.clone()} type="password" id="password" name="password" /> - </label> - - <br /><br /> - - <button type="submit">{ "Login" }</button> + <div> + <input type="submit" value="Login" /> + </div> <components::graphql_errors::GraphQLErrors errors={self.errors.clone()} /> </form>