Ajout de l'aide

master
Florent Becker 2022-07-29 18:23:01 +02:00
parent 6cbc951c59
commit 6b09a15e1a
2 changed files with 117 additions and 67 deletions

View File

@ -2,10 +2,26 @@ body {
margin: 0; margin: 0;
} }
details.aide[open] {
columns: 2;
background-color: rgba(255, 255, 255, 0.5);
border-radius: .5rem;
}
details.aide > summary {
font-weight: 1000;
}
details.configuration[open] {
background-color: rgba(0, 0, 0, 0.2);
padding: 1ex;
border-radius: .5rem;
}
body > div { body > div {
background-color: aliceblue; background-color: aliceblue;
display: flex; display: flex;
height: 100vh; min-height: 100vh;
padding: 8px; padding: 8px;
} }

View File

@ -136,7 +136,9 @@ fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, q: QueueProp<'a>) -> View<G> {
}); });
section() section()
.c(h3().dyn_t(move || nom.clone())) .c(h3().dyn_t(move || format!("{} ", nom)).c(button()
.t("Supprimer")
.on("click", move |_ev| (queue.à_détruire.set(true)))))
.c(input() .c(input()
.attr("type", "checkbox") .attr("type", "checkbox")
.dyn_attr("id", { .dyn_attr("id", {
@ -166,9 +168,6 @@ fn ConfigQueue<'a, G: Html>(cx: Scope<'a>, q: QueueProp<'a>) -> View<G> {
move || Some(format!("prio_queue_{}", &suffix.clone())) move || Some(format!("prio_queue_{}", &suffix.clone()))
}) })
.dyn_t(|| format!("Priorité: {}", queue.priorité.get()))) .dyn_t(|| format!("Priorité: {}", queue.priorité.get())))
.c(button()
.t("Supprimer")
.on("click", move |ev| (queue.à_détruire.set(true))))
.view(cx) .view(cx)
} }
@ -192,6 +191,76 @@ fn Queue<'a, G: Html>(cx: Scope<'a>, queue: QueueProp<'a>) -> View<G> {
} }
} }
#[derive(Prop, Clone, Copy)]
struct BarreCôtéProp<'a> {
queues: &'a Signal<Vec<ÉtatQueue<'a>>>,
}
fn classe_visible(b: bool) -> &'static str {
if b {
"visible"
} else {
"hidden"
}
}
#[component]
fn Explications<'a, G: Html>(cx: Scope<'a>) -> View<G> {
details()
.class("aide")
.c(summary().t("Comment utiliser Kikikoz"))
.c(p().t("Kikikoz est un outil qui vous aide à répartir la parole dans une réunion."))
.c(p().t("Quand vous lancez Kikikoz, l'interface que vous voyez correspond à une réunion avec un tour de parole divisé en deux file. C'est une pratique que l'on observe souvent, notamment pour répartir équitablement la parole entre femmes et hommes."))
.c(p().t("Quand une personne lève la main, vous allez inscrire son nom dans le champ de texte correspondant à la file d'attente idoine."))
.c(p().t("Pour donner la parole à la personne suivante, il suffit de cliquer sur le bouton \"suivantə\", ou de taper CTRL+ENTRÉE. Son nom et son temps de parole apparaissent alors en gros caractères"))
.c(p().t("La parole alterne entre les deux files. Vous l'avez peut-être remarqué, quand une personne lève la main, elle ne va pas à la fin de la file, mais à une position aléatoire. C'est inhabituel, mais pas moins juste. Il y a des chances que ce qu'elle à a dire soit plus pertinent, puisqu'elle lève la main après avoir entendu plus de choses. Vous pouvez changer ce comportement pour chaque file."))
.c(p().t("Pour faire une pause dans la réunion (quand personne n'a la parole), cliquez sur \"pause\"."))
.c(p().t("Vous pouvez configurer toutes sortes de détails via le panneau configaration. Vous pouvez y ajouter ou supprimer des files, décider si l'ajout de personnes dans chaque file doit se faire à une position aléatoire ou à la fin. Vous pouvez également attribuer une priorité différente aux files, si vous décidez que la parole doit revenir à l'une d'entre elles plus souvent qu'aux autres."))
.view(cx)
}
#[component]
fn BarreCôté<'a, G: Html>(cx: Scope<'a>, p: BarreCôtéProp<'a>) -> View<G> {
let conf_visible = create_signal(cx, false);
let nom_nouvelle_file = create_signal(cx, String::new());
let nouvelle_file = {
let queues = p.queues.clone();
move |_ev| {
let q = ÉtatQueue::new(cx, &nom_nouvelle_file.get());
queues.modify().push(q);
*nom_nouvelle_file.modify() = String::new();
}
};
let nouvelle_file_clav = move |ev: web_sys::Event| {
let event: web_sys::KeyboardEvent = ev.clone().unchecked_into();
if event.key_code() == 13 && !event.ctrl_key() {
nouvelle_file(ev)
}
};
view! {cx,
aside {
details(class="configuration") {
summary { "Configuration " }
Keyed {
iterable: p.queues,
key: |q| q.id,
view: |cx, q| view!{cx, ConfigQueue{ q }}
}
section {
h3 {"Nouvelle file" }
input(type="text", bind:value=nom_nouvelle_file, on:keypress=nouvelle_file_clav) {}
button(on:click=nouvelle_file) { "Créer" }
}
}
}
}
}
fn main() { fn main() {
sycamore::render(|cx| { sycamore::render(|cx| {
let heure_courante = create_signal(cx, 0.0); let heure_courante = create_signal(cx, 0.0);
@ -207,21 +276,17 @@ fn main() {
let date_prise_parole = create_signal(cx, 0.0); let date_prise_parole = create_signal(cx, 0.0);
let courantə = create_signal(cx, None); let courantə = create_signal(cx, None);
// let courantə2 = create_signal(cx, None).clone(); // let courantə2 = create_signal(cx, None).clone();
let conf_visible = create_signal(cx, false);
let queues = create_signal( let queues = create_signal(
cx, cx,
vec![ÉtatQueue::new(cx, "Gauche"), ÉtatQueue::new(cx, "Droite")], vec![ÉtatQueue::new(cx, "Gauche"), ÉtatQueue::new(cx, "Droite")],
); );
let supprime_queue = create_effect(cx, || { create_effect(cx, || {
for q in queues.get().iter() { for q in queues.get().iter() {
q.à_détruire.track() q.à_détruire.track()
} }
queues.modify().retain(|q| !(*q.à_détruire.get())); queues.modify().retain(|q| !(*q.à_détruire.get()));
}); });
let index_queues = create_memo(cx, {
|| (0..queues.get().len()).into_iter().collect::<Vec<_>>()
});
let total_priorités: &ReadSignal<usize> = create_memo(cx, { let total_priorités: &ReadSignal<usize> = create_memo(cx, {
let queues = queues.clone(); let queues = queues.clone();
move || { move || {
@ -233,14 +298,13 @@ fn main() {
} }
}); });
let queue_courante: &ReadSignal<(usize, usize)> = create_memo(cx, { let queue_courante: &ReadSignal<(usize, usize)> = create_memo(cx, {
let queues = queues.clone();
move || { move || {
let (idx, (id, _q)) = queues let (idx, (id, _q)) = queues
.get() .get()
.iter() .iter()
.map(|q| (q.id, *q.crédit_parole.get())) .map(|q| (q.id, *q.crédit_parole.get()))
.enumerate() .enumerate()
.max_by(|(ia, (id_a, a)), (ib, (id_b, b))| { .max_by(|(_ia, (_id_a, a)), (_ib, (_id_b, b))| {
a.partial_cmp(b).expect("Tried to compare a NaN") a.partial_cmp(b).expect("Tried to compare a NaN")
}) })
.expect("bla"); .expect("bla");
@ -248,33 +312,24 @@ fn main() {
} }
}); });
provide_context_ref(cx, queue_courante); provide_context_ref(cx, queue_courante);
let next = { let pause = move |_ev| {
let mut queues = queues.clone(); courantə.set(None);
let queue_courante = queue_courante.clone(); };
move |_ev| { let next = move |_ev| {
//js_sys::alert("coucou"); //js_sys::alert("coucou");
let mut queue: ÉtatQueue = queues.get()[queue_courante.get().0].clone(); let mut queue: ÉtatQueue = queues.get()[queue_courante.get().0].clone();
match queue.dequeue() { match queue.dequeue() {
Some(p) => { Some(p) => {
courantə.set(Some(p)); courantə.set(Some(p));
date_prise_parole.set(js_sys::Date::now()) date_prise_parole.set(js_sys::Date::now())
}
None => (),
} }
for q in queues.get().iter() { None => {
*q.crédit_parole.modify() += courantə.set(None);
(*q.priorité.get() as f32) / (*total_priorités.get() as f32)
} }
} }
}; for q in queues.get().iter() {
*q.crédit_parole.modify() +=
let nom_nouvelle_file = create_signal(cx, String::new()); (*q.priorité.get() as f32) / (*total_priorités.get() as f32)
let nouvelle_file = {
let mut queues = queues.clone();
move |_ev| {
let q = ÉtatQueue::new(cx, &nom_nouvelle_file.get());
queues.modify().push(q);
*nom_nouvelle_file.modify() = String::new();
} }
}; };
@ -294,14 +349,16 @@ fn main() {
match &*courantə.get() { match &*courantə.get() {
None => "En attente".into(), None => "En attente".into(),
Some(p) => { Some(p) => {
let temps_parole = std::time::Duration::from_millis((*heure_courante.get() - *date_prise_parole.get()) as u64); let temps_parole = std::time::Duration::from_millis((*heure_courante.get() - *date_prise_parole.get()) as u64).as_secs();
format!("On écoute {} depuis {:2}:{:02}", p, 0, temps_parole.as_secs()) format!("On écoute {} depuis {:2}:{:02}", p, temps_parole / 60, temps_parole % 60)
} }
} }
) )
br {} br {}
button(on:click=next) { "Suivantə" } button(on:click=next) { "Suivantə" }
" "
button(on:click=pause) { "Pause" }
} }
section(id="les_queues") { section(id="les_queues") {
Keyed { Keyed {
@ -319,37 +376,14 @@ fn main() {
} }
} }
} }
} br {}
aside {
button(on:click=|_| conf_visible.set(! *conf_visible.get())) { Explications {}
(
if *conf_visible.get() {
"cacher"
} else {
"configuration"
}
)
}
div(class=if *conf_visible.get() { "visible" } else { "hidden" }) {
h2 { "Configuration" }
Keyed {
iterable: queues,
key: |q| q.id,
view: |cx, q| view!{cx, ConfigQueue{ q }}
}
section {
h3 {"Nouvelle file" }
input(type="text", bind:value=nom_nouvelle_file, on:keypress=move |ev: web_sys::Event| {
let event : web_sys::KeyboardEvent = ev.clone().unchecked_into();
if event.key_code() == 13 && !event.ctrl_key() {
nouvelle_file(ev)
}
}) {}
button(on:click=nouvelle_file) { "Créer" }
}
}
} }
BarreCôté {
queues
}
} }
} }
}); });