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 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 login;
|
||||||
pub mod register_teacher;
|
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;
|
use crate::routes::home::student_vote;
|
||||||
|
|
||||||
|
pub enum Msg {
|
||||||
|
Registered,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct StudentHomeProps {
|
pub struct StudentHomeProps {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
pub voted: bool,
|
pub voted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StudentHome;
|
pub struct StudentHome {
|
||||||
|
voted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for StudentHome {
|
impl Component for StudentHome {
|
||||||
type Message = ();
|
type Message = Msg;
|
||||||
type Properties = StudentHomeProps;
|
type Properties = StudentHomeProps;
|
||||||
|
|
||||||
fn create(_ctx: &Context<Self>) -> Self {
|
fn create(ctx: &Context<Self>) -> 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 {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
html! {
|
html! {
|
||||||
if ctx.props().voted {
|
if self.voted {
|
||||||
<p>{ "Alles in Ordnung." }</p>
|
<p>{ "Alles in Ordnung." }</p>
|
||||||
} else {
|
} 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,
|
teacher: i64,
|
||||||
},
|
},
|
||||||
Submit,
|
Submit,
|
||||||
|
Vote(Option<Vec<String>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct StudentVoteProps {
|
pub struct StudentVoteProps {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
|
pub onvoted: Callback<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StudentVote {
|
pub struct StudentVote {
|
||||||
|
@ -77,25 +79,30 @@ impl Component for StudentVote {
|
||||||
self.errors = errors;
|
self.errors = errors;
|
||||||
self.can_vote = can_vote;
|
self.can_vote = can_vote;
|
||||||
|
|
||||||
let client = graphql::client(Some(&ctx.props().token)).unwrap();
|
if self.can_vote {
|
||||||
ctx.link().send_future(async move {
|
let client = graphql::client(Some(&ctx.props().token)).unwrap();
|
||||||
let response = post_graphql::<graphql::queries::config::Config, _>(
|
ctx.link().send_future(async move {
|
||||||
&client,
|
let response = post_graphql::<graphql::queries::config::Config, _>(
|
||||||
graphql::URL.as_str(),
|
&client,
|
||||||
graphql::queries::config::config::Variables,
|
graphql::URL.as_str(),
|
||||||
)
|
graphql::queries::config::config::Variables,
|
||||||
.await
|
)
|
||||||
.unwrap();
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Msg::DoneFetchingConfig {
|
Msg::DoneFetchingConfig {
|
||||||
errors: components::graphql_errors::convert(response.errors),
|
errors: components::graphql_errors::convert(response.errors),
|
||||||
min: response
|
min: response
|
||||||
.data
|
.data
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.config
|
.config
|
||||||
.minimum_teacher_selection_count as usize,
|
.minimum_teacher_selection_count
|
||||||
}
|
as usize,
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.fetching = false;
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -138,9 +145,39 @@ impl Component for StudentVote {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Msg::Submit => {
|
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
|
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! {
|
html! {
|
||||||
<p>{ "Fetching..." }</p>
|
<p>{ "Fetching..." }</p>
|
||||||
}
|
}
|
||||||
|
} else if !self.can_vote {
|
||||||
|
html! {
|
||||||
|
<p>{ "Noch nicht erlaubt zu wählen." }</p>
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let onsubmit = ctx.link().callback(|e: FocusEvent| {
|
let onsubmit = ctx.link().callback(|e: FocusEvent| {
|
||||||
e.prevent_default();
|
e.prevent_default();
|
||||||
|
@ -210,8 +251,11 @@ impl Component for StudentVote {
|
||||||
}) }
|
}) }
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button type="submit">{ "Submit" }</button>
|
<input type="submit" value="Submit" />
|
||||||
|
<input type="reset" value="Reset" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
|
||||||
</form>
|
</form>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_agent::{Bridge, Bridged};
|
|
||||||
|
|
||||||
use crate::agents;
|
|
||||||
use crate::routes::home::teacher_registration;
|
use crate::routes::home::teacher_registration;
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
|
@ -16,7 +14,6 @@ pub struct TeacherHomeProps {
|
||||||
|
|
||||||
pub struct TeacherHome {
|
pub struct TeacherHome {
|
||||||
registered: bool,
|
registered: bool,
|
||||||
_teacher_registered_producer: Box<dyn Bridge<agents::teacher_registered::EventBus>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for TeacherHome {
|
impl Component for TeacherHome {
|
||||||
|
@ -26,9 +23,6 @@ impl Component for TeacherHome {
|
||||||
fn create(ctx: &Context<Self>) -> Self {
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registered: ctx.props().registered,
|
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 {
|
if self.registered {
|
||||||
<p>{ "Alles in Ordnung." }</p>
|
<p>{ "Alles in Ordnung." }</p>
|
||||||
} else {
|
} 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 graphql_client::reqwest::post_graphql;
|
||||||
use web_sys::HtmlInputElement;
|
use web_sys::HtmlInputElement;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_agent::{Dispatched, Dispatcher};
|
|
||||||
|
|
||||||
use crate::agents;
|
|
||||||
use crate::components;
|
use crate::components;
|
||||||
use crate::graphql;
|
use crate::graphql;
|
||||||
|
|
||||||
|
@ -15,12 +13,12 @@ pub enum Msg {
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct TeacherRegistrationProps {
|
pub struct TeacherRegistrationProps {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
|
pub onregistered: Callback<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TeacherRegistration {
|
pub struct TeacherRegistration {
|
||||||
max_students: NodeRef,
|
max_students: NodeRef,
|
||||||
errors: Option<Vec<String>>,
|
errors: Option<Vec<String>>,
|
||||||
teacher_registered_event_bus: Dispatcher<agents::teacher_registered::EventBus>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for TeacherRegistration {
|
impl Component for TeacherRegistration {
|
||||||
|
@ -31,7 +29,6 @@ impl Component for TeacherRegistration {
|
||||||
Self {
|
Self {
|
||||||
max_students: NodeRef::default(),
|
max_students: NodeRef::default(),
|
||||||
errors: None,
|
errors: None,
|
||||||
teacher_registered_event_bus: agents::teacher_registered::EventBus::dispatcher(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +54,6 @@ impl Component for TeacherRegistration {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
log::debug!("{:?}", response.data.unwrap().register_teacher);
|
|
||||||
|
|
||||||
Msg::Registration(components::graphql_errors::convert(response.errors))
|
Msg::Registration(components::graphql_errors::convert(response.errors))
|
||||||
});
|
});
|
||||||
|
@ -68,8 +64,7 @@ impl Component for TeacherRegistration {
|
||||||
Msg::Registration(errors) => {
|
Msg::Registration(errors) => {
|
||||||
self.errors = errors;
|
self.errors = errors;
|
||||||
if self.errors.is_none() {
|
if self.errors.is_none() {
|
||||||
self.teacher_registered_event_bus
|
ctx.props().onregistered.emit(());
|
||||||
.send(agents::teacher_registered::Request::Registered);
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
|
@ -88,15 +83,17 @@ impl Component for TeacherRegistration {
|
||||||
html! {
|
html! {
|
||||||
<>
|
<>
|
||||||
<form {onsubmit}>
|
<form {onsubmit}>
|
||||||
<label for="max_students">
|
<div>
|
||||||
{ "Maximale Anzahl von Schülern:" }
|
<label for="max_students">
|
||||||
<br />
|
{ "Maximale Anzahl von Schülern:" }
|
||||||
<input ref={self.max_students.clone()} type="number" id="max_students" name="max_students" min=1 />
|
<br />
|
||||||
</label>
|
<input ref={self.max_students.clone()} type="number" id="max_students" name="max_students" min=1 />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br /><br />
|
<div>
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
<button type="submit">{ "Submit" }</button>
|
</div>
|
||||||
|
|
||||||
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
|
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -89,23 +89,25 @@ impl Component for Login {
|
||||||
<>
|
<>
|
||||||
<Title value="Login" />
|
<Title value="Login" />
|
||||||
<form {onsubmit}>
|
<form {onsubmit}>
|
||||||
<label for="username">
|
<div>
|
||||||
{ "Benutzername:" }
|
<label for="username">
|
||||||
<br />
|
{ "Benutzername:" }
|
||||||
<input ref={self.username.clone()} type="text" id="username" name="username" />
|
<br />
|
||||||
</label>
|
<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">
|
<div>
|
||||||
{ "Kennwort:" }
|
<input type="submit" value="Login" />
|
||||||
<br />
|
</div>
|
||||||
<input ref={self.password.clone()} type="password" id="password" name="password" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
<button type="submit">{ "Login" }</button>
|
|
||||||
|
|
||||||
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
|
<components::graphql_errors::GraphQLErrors errors={self.errors.clone()} />
|
||||||
</form>
|
</form>
|
||||||
|
|
Loading…
Reference in New Issue