Zusätzliche modale anzeigen, sowie erste Zuweisung

This commit is contained in:
Dietrich 2023-01-06 17:26:34 +01:00
parent 4d46e985a5
commit 8a8030dc2b
Signed by: dietrich
GPG Key ID: F0CE5A20AB5C4B27
2 changed files with 210 additions and 56 deletions

View File

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

View File

@ -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>
</>
}
}
}