Add basic student voting
continuous-integration/drone/push Build is failing Details
continuous-integration/drone Build is failing Details

This commit is contained in:
Dominic Grimm 2022-11-13 12:44:08 +01:00
parent 2ed278683b
commit 2b568d37f6
No known key found for this signature in database
GPG Key ID: 6F294212DEAAC530
10 changed files with 138 additions and 111 deletions

View File

@ -0,0 +1,5 @@
mutation Vote($teacherIds: [Int!]!) {
createVote(input: { teacherIds: $teacherIds }) {
id
}
}

View File

@ -1,2 +1 @@
pub mod logged_in;
pub mod teacher_registered;

View File

@ -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<Self>,
subscribers: HashSet<HandlerId>,
}
impl Agent for EventBus {
type Reach = Context<Self>;
type Message = ();
type Input = Request;
type Output = ();
fn create(link: AgentLink<Self>) -> 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);
}
}

View File

@ -1,2 +1,3 @@
pub mod login;
pub mod register_teacher;
pub mod vote;

View File

@ -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;

View File

@ -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 {
Self
fn create(ctx: &Context<Self>) -> Self {
Self {
voted: ctx.props().voted,
}
}
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
match msg {
Msg::Registered => {
self.voted = true;
true
}
}
}
fn view(&self, ctx: &Context<Self>) -> Html {
html! {
if ctx.props().voted {
if self.voted {
<p>{ "Alles in Ordnung." }</p>
} else {
<student_vote::StudentVote token={ctx.props().token.to_owned()} />
<student_vote::StudentVote
token={ctx.props().token.to_owned()}
onvoted={ctx.link().callback(|_| Msg::Registered)}
/>
}
}
}

View File

@ -23,11 +23,13 @@ pub enum Msg {
teacher: i64,
},
Submit,
Vote(Option<Vec<String>>),
}
#[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::<graphql::queries::config::Config, _>(
&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::<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,
}
});
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<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
}
}
}
}
@ -149,6 +186,10 @@ impl Component for StudentVote {
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();
@ -210,8 +251,11 @@ impl Component for StudentVote {
}) }
<div>
<button type="submit">{ "Submit" }</button>
<input type="submit" value="Submit" />
<input type="reset" value="Reset" />
</div>
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
</form>
</>
}

View File

@ -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<dyn Bridge<agents::teacher_registered::EventBus>>,
}
impl Component for TeacherHome {
@ -26,9 +23,6 @@ impl Component for TeacherHome {
fn create(ctx: &Context<Self>) -> 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 {
<p>{ "Alles in Ordnung." }</p>
} else {
<teacher_registration::TeacherRegistration token={ctx.props().token.to_owned()} />
<teacher_registration::TeacherRegistration
token={ctx.props().token.to_owned()}
onregistered={ctx.link().callback(|_| Msg::Registered)}
/>
}
}
}

View File

@ -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<Vec<String>>,
teacher_registered_event_bus: Dispatcher<agents::teacher_registered::EventBus>,
}
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! {
<>
<form {onsubmit}>
<label for="max_students">
{ "Maximale Anzahl von Schülern:" }
<br />
<input ref={self.max_students.clone()} type="number" id="max_students" name="max_students" min=1 />
</label>
<div>
<label for="max_students">
{ "Maximale Anzahl von Schülern:" }
<br />
<input ref={self.max_students.clone()} type="number" id="max_students" name="max_students" min=1 />
</label>
</div>
<br /><br />
<button type="submit">{ "Submit" }</button>
<div>
<input type="submit" value="Submit" />
</div>
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
</form>

View File

@ -89,23 +89,25 @@ impl Component for Login {
<>
<Title value="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>