Zusätzliche modale anzeigen, sowie erste Zuweisung #1
5
README.md
Normal file
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
Installieren von rust+cargo: [https://www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install)
|
||||
|
||||
Installieren von Trunk: `$cargo install trunk`
|
||||
|
||||
Befehl zum compilieren: `$ trunk serve --release --public-url /static --filehash false --dist ../solawispielplatz/src/wunschliste/static/wasm/`
|
@ -16,7 +16,7 @@ pub struct GemüseAnfrage {
|
||||
pub user: Benutzer,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Hash, Eq)]
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Hash, Eq, Copy)]
|
||||
pub struct VpWunsch {
|
||||
pub id: i32,
|
||||
pub anfrage_id: i32,
|
||||
|
240
src/zuordnung.rs
240
src/zuordnung.rs
@ -39,6 +39,10 @@ pub enum ZuordnungMessage {
|
||||
ShowDialog(Dialog),
|
||||
/// A message indicating that the current dialog should be hidden.
|
||||
HideDialog,
|
||||
AssignAll {
|
||||
verteilpunkt: Verteilpunkt,
|
||||
mitgliedschaften: Vec<VpWunsch>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct Dialog {
|
||||
@ -47,7 +51,9 @@ pub struct Dialog {
|
||||
}
|
||||
|
||||
pub enum DialogContent {
|
||||
Wunschliste(Vec<VpWunsch>),
|
||||
Wunschliste(Verteilpunkt, Vec<VpWunsch>),
|
||||
Vorjahr(Vec<VpZuordnung>),
|
||||
Geplant(HashMap<i32, GeplanteVpZuordnung>),
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq, Default)]
|
||||
@ -66,7 +72,7 @@ pub struct ZuordnungApp {
|
||||
/// The assignments made for the previous year.
|
||||
zuordnung_vorjahr: Option<JahresVpZuordnung>,
|
||||
/// The planned assignments for the current year.
|
||||
geplante_zuordnung: HashMap<i32, GeplanteVpZuordnung>,
|
||||
geplante_zuordnung: HashMap<i32, HashMap<i32, GeplanteVpZuordnung>>,
|
||||
/// The currently displayed dialog.
|
||||
dialog: Option<Dialog>,
|
||||
}
|
||||
@ -137,6 +143,12 @@ impl Component for ZuordnungApp {
|
||||
NetworkObjekte::Verteilpunkt(v) => {
|
||||
log!("Verteilpunkte geparsed");
|
||||
self.verteilpunkte = v;
|
||||
for vp in self.verteilpunkte.iter() {
|
||||
log!(format!(
|
||||
"{:?}",
|
||||
self.geplante_zuordnung.insert(*vp.0, HashMap::new())
|
||||
));
|
||||
}
|
||||
true
|
||||
}
|
||||
NetworkObjekte::Wunsch(v) => {
|
||||
@ -163,6 +175,26 @@ impl Component for ZuordnungApp {
|
||||
self.dialog = None;
|
||||
true
|
||||
}
|
||||
ZuordnungMessage::AssignAll {
|
||||
verteilpunkt,
|
||||
mitgliedschaften,
|
||||
} => {
|
||||
log!(format!("{:?}", mitgliedschaften));
|
||||
if let Some(liste) = self.geplante_zuordnung.get_mut(&verteilpunkt.id) {
|
||||
for m in mitgliedschaften {
|
||||
liste.insert(
|
||||
m.anfrage_id,
|
||||
GeplanteVpZuordnung {
|
||||
mitgliedschaft_id: m.anfrage_id,
|
||||
user_mitgliedsnummer: m.user_id,
|
||||
verteilpunkt_id: m.verteilpunkt_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
log!(format!("{:?}", self.geplante_zuordnung));
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,8 +217,8 @@ impl Component for ZuordnungApp {
|
||||
.map(|(id, v)| html! {
|
||||
<tr>
|
||||
<td>{self.verteilpunkt_name(v)}</td>
|
||||
<td>{self.aktuelle_mitglieder(v, &self.zuordnung_vorjahr)}</td>
|
||||
<td>{self.zukünftige_mitglieder(v)}</td>
|
||||
<td>{self.aktuelle_mitglieder(v, ctx)}</td>
|
||||
<td>{self.zukünftige_mitglieder(v, ctx)}</td>
|
||||
{self.count_wünsche(*id, ctx)}
|
||||
</tr>
|
||||
})
|
||||
@ -198,6 +230,9 @@ impl Component for ZuordnungApp {
|
||||
}
|
||||
}
|
||||
impl ZuordnungApp {
|
||||
/**
|
||||
Erstelle Die Tabellenzellen für die 1. 2. und n. Wünsche.
|
||||
*/
|
||||
fn count_wünsche(&self, verteilpunkt_id: i32, ctx: &yew::Context<ZuordnungApp>) -> Html {
|
||||
// Erst wenn die Wünsche geladen sind wird etwas angezeigt.
|
||||
if let Some(wünsche) = &self.wünsche {
|
||||
@ -206,6 +241,7 @@ impl ZuordnungApp {
|
||||
.wünsche
|
||||
.iter()
|
||||
.filter(|(_, wunsch)| wunsch.verteilpunkt_id == verteilpunkt_id);
|
||||
|
||||
// A mapping from priority values to lists of wishes.
|
||||
let counts: HashMap<i32, Vec<VpWunsch>> = wünsche
|
||||
.prioritäten
|
||||
@ -214,11 +250,19 @@ impl ZuordnungApp {
|
||||
.collect();
|
||||
let counts: HashMap<i32, Vec<VpWunsch>> =
|
||||
filtered.fold(counts, |mut counts, (_, wunsch)| {
|
||||
if self
|
||||
.geplante_zuordnung
|
||||
.iter()
|
||||
.any(|(_, v)| v.contains_key(&wunsch.anfrage_id))
|
||||
{
|
||||
counts
|
||||
} else {
|
||||
counts
|
||||
.entry(wunsch.prioritaet)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(wunsch.clone());
|
||||
.push(*wunsch);
|
||||
counts
|
||||
}
|
||||
});
|
||||
// sortiere nach Priorität
|
||||
let sorted_and_grouped_by_priority = counts.into_iter().sorted_by_key(|x| x.0);
|
||||
@ -230,14 +274,12 @@ impl ZuordnungApp {
|
||||
let verteilpunkte = self.verteilpunkte.clone();
|
||||
|
||||
let onclick = ctx.link().callback(move |_| {
|
||||
ZuordnungMessage::ShowDialog(Dialog {
|
||||
title: {
|
||||
let vp = verteilpunkte
|
||||
.get(&verteilpunkt_id)
|
||||
.expect("should allways be there");
|
||||
format!("Wünsche für {}", vp.name)
|
||||
},
|
||||
content: DialogContent::Wunschliste(wunsch.clone()),
|
||||
ZuordnungMessage::ShowDialog(Dialog {
|
||||
title: { format!("Wünsche für {}", vp.name) },
|
||||
content: DialogContent::Wunschliste(vp.clone(), wunsch.clone()),
|
||||
})
|
||||
});
|
||||
html! {<td><b {onclick}>{length}</b></td>}
|
||||
@ -294,53 +336,95 @@ impl ZuordnungApp {
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn aktuelle_mitglieder(&self, v: &Verteilpunkt, z: &Option<JahresVpZuordnung>) -> Html {
|
||||
// Count the number of items in the zuordnungen map with the matching verteilpunkt_id
|
||||
fn count_zuordnungen(v: &Verteilpunkt, zuordnungen: &HashMap<i32, VpZuordnung>) -> usize {
|
||||
/**
|
||||
Count the number of items in the zuordnungen map with the matching verteilpunkt_id
|
||||
*/
|
||||
fn count_zuordnungen(
|
||||
v: &Verteilpunkt,
|
||||
zuordnungen: &HashMap<i32, VpZuordnung>,
|
||||
) -> Vec<VpZuordnung> {
|
||||
zuordnungen
|
||||
.iter()
|
||||
.filter(|(_, zuo)| v.id == zuo.verteilpunkt_id)
|
||||
.count()
|
||||
.filter_map(|(_, zuo)| {
|
||||
if v.id == zuo.verteilpunkt_id {
|
||||
Some(zuo.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
match z {
|
||||
fn aktuelle_mitglieder(
|
||||
&self,
|
||||
verteilpunkt: &Verteilpunkt,
|
||||
ctx: &yew::Context<ZuordnungApp>,
|
||||
) -> Html {
|
||||
match &self.zuordnung_vorjahr {
|
||||
Some(z) => {
|
||||
// Render the count and the kapazitaet as "X/Y"
|
||||
let count = count_zuordnungen(v, &z.zuordnungen);
|
||||
html! {<>{count}{"/"} {v.kapazitaet}</>}
|
||||
let liste = Self::count_zuordnungen(verteilpunkt, &z.zuordnungen);
|
||||
let count = liste.len();
|
||||
let title = format!("Aktuelle Mitglieder am VP {}", verteilpunkt.name);
|
||||
let onclick = ctx.link().callback(move |_| {
|
||||
ZuordnungMessage::ShowDialog(Dialog {
|
||||
title: title.clone(),
|
||||
content: DialogContent::Vorjahr(liste.clone()),
|
||||
})
|
||||
});
|
||||
|
||||
html! {<span {onclick}>{count}{"/"} {verteilpunkt.kapazitaet}</span>}
|
||||
}
|
||||
None => html!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn zukünftige_mitglieder(&self, verteilpunkt: &Verteilpunkt) -> Html {
|
||||
/**
|
||||
Count the number of items in the zuordnungen map with the matching verteilpunkt_id
|
||||
*/
|
||||
fn count_geplante_zuordnungen(
|
||||
v: &Verteilpunkt,
|
||||
zuordnungen: &HashMap<i32, HashMap<i32, GeplanteVpZuordnung>>,
|
||||
) -> HashMap<i32, GeplanteVpZuordnung> {
|
||||
zuordnungen.get(&v.id).cloned().unwrap_or_default()
|
||||
}
|
||||
/**
|
||||
Zeige die Anzahl und Kappazität der zukünftigen Mitglieder an diesem Verteilpunkt.
|
||||
*/
|
||||
fn zukünftige_mitglieder(
|
||||
&self,
|
||||
verteilpunkt: &Verteilpunkt,
|
||||
ctx: &yew::Context<ZuordnungApp>,
|
||||
) -> Html {
|
||||
// Count the number of items in the z map with the matching verteilpunkt_id
|
||||
let count = &self
|
||||
.geplante_zuordnung
|
||||
.iter()
|
||||
.filter(|(_, zuo)| verteilpunkt.id == zuo.verteilpunkt_id)
|
||||
.count();
|
||||
let liste = Self::count_geplante_zuordnungen(verteilpunkt, &self.geplante_zuordnung);
|
||||
let count = liste.len();
|
||||
let title = format!("Geplante Mitglieder am VP {}", verteilpunkt.name);
|
||||
let onclick = ctx.link().callback(move |_| {
|
||||
ZuordnungMessage::ShowDialog(Dialog {
|
||||
title: title.clone(),
|
||||
content: DialogContent::Geplant(liste.clone()),
|
||||
})
|
||||
});
|
||||
|
||||
// Render the count and the kapazitaet as "X/Y"
|
||||
html! {<>{count}{"/"} {verteilpunkt.kapazitaet}</>}
|
||||
html! {<div {onclick}>{count}{"/"} {verteilpunkt.kapazitaet}</div>}
|
||||
}
|
||||
|
||||
fn show_dialog(&self, ctx: &yew::Context<Self>) -> Html {
|
||||
match &self.dialog {
|
||||
Some(dialog) => {
|
||||
// Create a callback that calls the HideDialog function when triggered
|
||||
let onclick = ctx.link().callback(|_| ZuordnungMessage::HideDialog);
|
||||
|
||||
html! {
|
||||
// Render the dialog container with the onclick callback attached
|
||||
<div class="modal_wasm" {onclick}>
|
||||
<div class="container">
|
||||
<div class="body">
|
||||
// Render the dialog title
|
||||
<h3>{&dialog.title}</h3>
|
||||
// Render the dialog content
|
||||
{self.show_dialog_content(&dialog.content)}
|
||||
<div class="modal show" tabindex="-1" role="dialog" style="display:block;" onclick={ctx.link().callback(|_| ZuordnungMessage::HideDialog)}>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{&dialog.title}</h5>
|
||||
<button type="button" class="btn btn-link link-secondary" data-dismiss="modal" aria-label="Close" onclick={ctx.link().callback(|_| ZuordnungMessage::HideDialog)}>
|
||||
<span aria-hidden="true"><i class="bi bi-x-circle-fill"></i></span>
|
||||
</button>
|
||||
</div>
|
||||
{self.show_dialog_content(&dialog.content, ctx)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -350,13 +434,14 @@ impl ZuordnungApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn show_dialog_content(&self, content: &DialogContent) -> Html {
|
||||
match content {
|
||||
DialogContent::Wunschliste(content) => {
|
||||
fn show_dialog_content(&self, content: &DialogContent, ctx: &yew::Context<Self>) -> Html {
|
||||
// Generate the lines and the buttons at the bottom for the different dialogs
|
||||
let (lines, buttons) = match content {
|
||||
DialogContent::Wunschliste(vp, content) => {
|
||||
let mut htmlnodes = Vec::new();
|
||||
|
||||
// Iterate over each wish in the list
|
||||
for wunsch in content {
|
||||
for wunsch in content.clone() {
|
||||
// Find the user associated with this wish
|
||||
let user = match &self.user {
|
||||
Some(users) => match users.get(&wunsch.user_id) {
|
||||
@ -368,11 +453,80 @@ impl ZuordnungApp {
|
||||
|
||||
// Append the user's first and last name to the HTML
|
||||
htmlnodes
|
||||
.push(html! {<tr><td>{&user.vorname}</td><td>{&user.nachname}</td></tr>});
|
||||
.push(html! {<tr><td scope="col">{&user.vorname}</td><td scope="col">{&user.nachname}</td></tr>});
|
||||
}
|
||||
let lines = html! {{ htmlnodes.into_iter().collect::<Html>() }};
|
||||
let vp = vp.clone();
|
||||
let content = content.clone();
|
||||
let onclick = ctx.link().callback(move |_| {
|
||||
let verteilpunkt = vp.clone();
|
||||
let mitgliedschaften = content.clone();
|
||||
ZuordnungMessage::AssignAll {
|
||||
verteilpunkt,
|
||||
mitgliedschaften,
|
||||
}
|
||||
});
|
||||
let buttons = html! {<>
|
||||
<button type="button" class="btn btn-primary" {onclick}>{"Alle zuweisen"}</button>
|
||||
</>};
|
||||
(lines, buttons)
|
||||
}
|
||||
DialogContent::Vorjahr(content) => {
|
||||
let mut htmlnodes = Vec::new();
|
||||
|
||||
// Iterate over each wish in the list
|
||||
for mitgliedschaft in content {
|
||||
// Find the user associated with this wish
|
||||
let user = match &self.user {
|
||||
Some(users) => match users.get(&mitgliedschaft.user_mitgliedsnummer) {
|
||||
Some(user) => user,
|
||||
None => continue, // Skip this wish if the user is not found
|
||||
},
|
||||
None => continue, // Skip this wish if self.user is not set
|
||||
};
|
||||
|
||||
// Append the user's first and last name to the HTML
|
||||
htmlnodes
|
||||
.push(html! {<tr><td scope="col">{&user.vorname}</td><td scope="col">{&user.nachname}</td></tr>});
|
||||
}
|
||||
|
||||
html! {{ htmlnodes.into_iter().collect::<Html>() }}
|
||||
(html! {{ htmlnodes.into_iter().collect::<Html>() }}, html!())
|
||||
}
|
||||
DialogContent::Geplant(content) => {
|
||||
let mut htmlnodes = Vec::new();
|
||||
|
||||
// Iterate over each wish in the list
|
||||
for mitgliedschaft in content.values() {
|
||||
// Find the user associated with this wish
|
||||
let user = match &self.user {
|
||||
Some(users) => match users.get(&mitgliedschaft.user_mitgliedsnummer) {
|
||||
Some(user) => user,
|
||||
None => continue, // Skip this wish if the user is not found
|
||||
},
|
||||
None => continue, // Skip this wish if self.user is not set
|
||||
};
|
||||
|
||||
// Append the user's first and last name to the HTML
|
||||
htmlnodes
|
||||
.push(html! {<tr><td scope="col">{&user.vorname}</td><td scope="col">{&user.nachname}</td></tr>});
|
||||
}
|
||||
|
||||
(html! {{ htmlnodes.into_iter().collect::<Html>() }}, html!())
|
||||
}
|
||||
};
|
||||
html! {
|
||||
<>
|
||||
<div class="modal-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped align-middle">
|
||||
<thead><tr><th scope="col">{"Vorname"}</th><th scope="col">{"Nachname"}</th></tr></thead>
|
||||
<tbody>{lines}</tbody></table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{buttons}
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user