Add basic student voting
This commit is contained in:
parent
2ed278683b
commit
2b568d37f6
|
@ -0,0 +1,5 @@
|
|||
mutation Vote($teacherIds: [Int!]!) {
|
||||
createVote(input: { teacherIds: $teacherIds }) {
|
||||
id
|
||||
}
|
||||
}
|
|
@ -1,2 +1 @@
|
|||
pub mod logged_in;
|
||||
pub mod teacher_registered;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod login;
|
||||
pub mod register_teacher;
|
||||
pub mod vote;
|
||||
|
|
|
@ -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;
|
|
@ -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)}
|
||||
/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
}
|
||||
|
|
|
@ -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)}
|
||||
/>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue