Added live updating timings
This commit is contained in:
parent
36c23f3740
commit
1f96cbcba6
@ -1,4 +1,4 @@
|
|||||||
use crate::util::surrealdb::schemas;
|
use crate::util::surrealdb::{client::sort_participants, client::Time, schemas};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
/// Renders the home page of your application.
|
/// Renders the home page of your application.
|
||||||
@ -6,40 +6,53 @@ use leptos::*;
|
|||||||
pub fn Participants() -> impl IntoView {
|
pub fn Participants() -> impl IntoView {
|
||||||
let participants_context = use_context::<schemas::ParticipantsContext>().unwrap();
|
let participants_context = use_context::<schemas::ParticipantsContext>().unwrap();
|
||||||
|
|
||||||
|
let name = create_rw_signal("".to_string());
|
||||||
|
|
||||||
|
let participants_sorted =
|
||||||
|
create_memo(move |_| sort_participants(participants_context.get(), name.get()));
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div class="participants-container">
|
<input type="search"
|
||||||
<table>
|
class="participants-search"
|
||||||
|
placeholder="Search"
|
||||||
|
autocomplete="off"
|
||||||
|
autofocus=true
|
||||||
|
on:input=move |ev| {
|
||||||
|
name.set(event_target_value(&ev));
|
||||||
|
}
|
||||||
|
prop:value=name
|
||||||
|
/>
|
||||||
|
<table class="participants-table">
|
||||||
|
<tr>
|
||||||
|
<th>"Naam"</th>
|
||||||
|
<th>"Groep"</th>
|
||||||
|
<th>"Lifesaver"</th>
|
||||||
|
<th>"Hindernis"</th>
|
||||||
|
<th>"Popduiken"</th>
|
||||||
|
</tr>
|
||||||
|
<For
|
||||||
|
each=move || participants_sorted.get()
|
||||||
|
key=|state| state.id.clone()
|
||||||
|
let:child
|
||||||
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<th>"Naam"</th>
|
<td>{ move || child.value.get().name }</td>
|
||||||
<th>"Groep"</th>
|
<td>{ move || child.value.get().group }</td>
|
||||||
<th>"Lifesaver"</th>
|
{ move || match child.value.get().events {
|
||||||
<th>"Hindernis"</th>
|
Some(events) => view! {
|
||||||
<th>"Popduiken"</th>
|
<td>{ Time::from_milliseconds_to_string(events.lifesaver.unwrap_or(0)) }</td>
|
||||||
</tr>
|
<td>{ Time::from_milliseconds_to_string(events.hindernis.unwrap_or(0)) }</td>
|
||||||
<For
|
<td>{ Time::from_milliseconds_to_string(events.popduiken.unwrap_or(0)) }</td>
|
||||||
each=move || participants_context.get()
|
},
|
||||||
key=|state| state.id.clone()
|
None => view! {
|
||||||
let:child
|
<td>"0"</td>
|
||||||
>
|
<td>"0"</td>
|
||||||
<tr>
|
<td>"0"</td>
|
||||||
<td>{ move || child.value.get().name }</td>
|
|
||||||
<td>{ move || child.value.get().group }</td>
|
|
||||||
{ move || match child.value.get().events {
|
|
||||||
Some(events) => view! {
|
|
||||||
<td>{ events.lifesaver.unwrap_or(0) }</td>
|
|
||||||
<td>{ events.hindernis.unwrap_or(0) }</td>
|
|
||||||
<td>{ events.popduiken.unwrap_or(0) }</td>
|
|
||||||
},
|
|
||||||
None => view! {
|
|
||||||
<td>"0"</td>
|
|
||||||
<td>"0"</td>
|
|
||||||
<td>"0"</td>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</tr>
|
}
|
||||||
</For>
|
</tr>
|
||||||
</table>
|
</For>
|
||||||
</div>
|
</table>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ async fn add_participant(name: String, group: String) -> Result<(), ServerFnErro
|
|||||||
participant: schemas::Participant {
|
participant: schemas::Participant {
|
||||||
name: participant.name.clone(),
|
name: participant.name.clone(),
|
||||||
group: participant.group.clone(),
|
group: participant.group.clone(),
|
||||||
id: participant.id.to_string(),
|
id: participant.id.id.to_string(),
|
||||||
events: participant.events.clone(),
|
events: participant.events.clone(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use crate::util::surrealdb::schemas;
|
use crate::util::surrealdb::{client::sort_participants, client::Time, schemas};
|
||||||
use leptos::{ev::keydown, *};
|
use leptos::{ev::keydown, *};
|
||||||
use leptos_use::*;
|
use leptos_use::*;
|
||||||
use strsim::normalized_damerau_levenshtein;
|
|
||||||
use web_sys::ScrollIntoViewOptions;
|
use web_sys::ScrollIntoViewOptions;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
@ -13,19 +12,6 @@ cfg_if::cfg_if! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Time {
|
|
||||||
minutes: u32,
|
|
||||||
seconds: u32,
|
|
||||||
milliseconds: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Time {
|
|
||||||
fn as_milliseconds(&self) -> u32 {
|
|
||||||
self.minutes * 60 * 1000 + self.seconds * 1000 + self.milliseconds * 10
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[server(AddTime)]
|
#[server(AddTime)]
|
||||||
async fn add_time(
|
async fn add_time(
|
||||||
mut participant: schemas::Participant,
|
mut participant: schemas::Participant,
|
||||||
@ -96,7 +82,7 @@ pub fn AddTime() -> impl IntoView {
|
|||||||
|
|
||||||
let participant = &participants_sorted.get()[selected_index.get()];
|
let participant = &participants_sorted.get()[selected_index.get()];
|
||||||
add_time_action.dispatch((
|
add_time_action.dispatch((
|
||||||
participant.clone(),
|
participant.value.get(),
|
||||||
event.get(),
|
event.get(),
|
||||||
time.get().as_milliseconds(),
|
time.get().as_milliseconds(),
|
||||||
));
|
));
|
||||||
@ -165,7 +151,7 @@ pub fn AddTime() -> impl IntoView {
|
|||||||
/>
|
/>
|
||||||
<ul node_ref=container_ref tabindex=-1>
|
<ul node_ref=container_ref tabindex=-1>
|
||||||
{move || participants_sorted.get().into_iter().enumerate().map(|(i, participant)| view! {
|
{move || participants_sorted.get().into_iter().enumerate().map(|(i, participant)| view! {
|
||||||
<li on:click=move |_| selected_index.set(i) class:selected=move || selected_index.get() == i>{participant.name + " " + "(" + &participant.group + ")" }</li>
|
<li on:click=move |_| selected_index.set(i) class:selected=move || selected_index.get() == i>{participant.value.get().name + " " + "(" + &participant.value.get().group + ")" }</li>
|
||||||
}).collect_view()}
|
}).collect_view()}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -227,33 +213,3 @@ pub fn SelectOption(is: &'static str, value: ReadSignal<String>) -> impl IntoVie
|
|||||||
</option>
|
</option>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn sort_participants(
|
|
||||||
participants: Vec<schemas::ParticipantSignal>,
|
|
||||||
search: String,
|
|
||||||
) -> Vec<schemas::Participant> {
|
|
||||||
let mut filtered_sorted_list: Vec<(schemas::Participant, f64)> = participants
|
|
||||||
.into_iter()
|
|
||||||
.map(|participant| {
|
|
||||||
(
|
|
||||||
participant.value.get(),
|
|
||||||
normalized_damerau_levenshtein(
|
|
||||||
&participant.value.get().name.to_lowercase(),
|
|
||||||
&search.to_lowercase(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
filtered_sorted_list.sort_by(|a, b| {
|
|
||||||
let (_, sim_score_a) = a;
|
|
||||||
let (_, sim_score_b) = b;
|
|
||||||
sim_score_b
|
|
||||||
.partial_cmp(sim_score_a)
|
|
||||||
.unwrap_or(std::cmp::Ordering::Equal)
|
|
||||||
});
|
|
||||||
|
|
||||||
filtered_sorted_list
|
|
||||||
.into_iter()
|
|
||||||
.map(|(item, _)| item)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
pub mod schemas;
|
pub mod schemas;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
|
57
application/src/util/surrealdb/client.rs
Normal file
57
application/src/util/surrealdb/client.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use crate::util::surrealdb::schemas;
|
||||||
|
use leptos::*;
|
||||||
|
use strsim::normalized_damerau_levenshtein;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Time {
|
||||||
|
pub minutes: u32,
|
||||||
|
pub seconds: u32,
|
||||||
|
pub milliseconds: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Time {
|
||||||
|
pub fn as_milliseconds(&self) -> u32 {
|
||||||
|
self.minutes * 60 * 1000 + self.seconds * 1000 + self.milliseconds * 10
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_milliseconds_to_string(mut time: u32) -> String {
|
||||||
|
let milliseconds = (time % 1000) / 10;
|
||||||
|
time /= 1000;
|
||||||
|
let seconds = time % 60;
|
||||||
|
time /= 60;
|
||||||
|
let minutes = time;
|
||||||
|
|
||||||
|
format!("{minutes}:{seconds}:{milliseconds}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sort_participants(
|
||||||
|
participants: Vec<schemas::ParticipantSignal>,
|
||||||
|
search: String,
|
||||||
|
) -> Vec<schemas::ParticipantSignal> {
|
||||||
|
let mut filtered_sorted_list: Vec<(schemas::ParticipantSignal, f64)> = participants
|
||||||
|
.into_iter()
|
||||||
|
.map(|participant| {
|
||||||
|
(
|
||||||
|
participant.clone(),
|
||||||
|
normalized_damerau_levenshtein(
|
||||||
|
&participant.value.get().name.to_lowercase(),
|
||||||
|
&search.to_lowercase(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
filtered_sorted_list.sort_by(|a, b| {
|
||||||
|
let (_, sim_score_a) = a;
|
||||||
|
let (_, sim_score_b) = b;
|
||||||
|
sim_score_b
|
||||||
|
.partial_cmp(sim_score_a)
|
||||||
|
.unwrap_or(std::cmp::Ordering::Equal)
|
||||||
|
});
|
||||||
|
|
||||||
|
filtered_sorted_list
|
||||||
|
.into_iter()
|
||||||
|
.map(|(item, _)| item)
|
||||||
|
.collect()
|
||||||
|
}
|
@ -31,7 +31,7 @@ pub struct Participant {
|
|||||||
pub events: Option<Events>,
|
pub events: Option<Events>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
pub struct ParticipantSignal {
|
pub struct ParticipantSignal {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub value: RwSignal<Participant>,
|
pub value: RwSignal<Participant>,
|
||||||
|
@ -12,22 +12,17 @@ pub fn init_websocket() {
|
|||||||
} = use_websocket("ws://localhost:3000/ws");
|
} = use_websocket("ws://localhost:3000/ws");
|
||||||
|
|
||||||
provide_context(ready_state);
|
provide_context(ready_state);
|
||||||
|
let participants_context = use_context::<schemas::ParticipantsContext>().unwrap();
|
||||||
|
let owner = Owner::current().unwrap();
|
||||||
|
|
||||||
create_effect(move |prev_value| {
|
create_effect(move |_| {
|
||||||
let msg = message.get();
|
if let Some(m) = message.get() {
|
||||||
|
with_owner(owner, || handle_message(&participants_context, &m));
|
||||||
if prev_value != Some(msg.clone()) {
|
|
||||||
match msg {
|
|
||||||
Some(ref text) => handle_message(text),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_message(message: &str) {
|
fn handle_message(&participants_context: &schemas::ParticipantsContext, message: &str) {
|
||||||
let action: ParticipantsAction = match serde_json::from_str(message) {
|
let action: ParticipantsAction = match serde_json::from_str(message) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
@ -37,8 +32,6 @@ fn handle_message(message: &str) {
|
|||||||
|
|
||||||
logging::log!("Recieved action: {:?}", action);
|
logging::log!("Recieved action: {:?}", action);
|
||||||
|
|
||||||
let participants_context = use_context::<schemas::ParticipantsContext>().unwrap();
|
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
ParticipantsAction::Add { participant } => participants_context.update(|participants| {
|
ParticipantsAction::Add { participant } => participants_context.update(|participants| {
|
||||||
participants.push(schemas::ParticipantSignal {
|
participants.push(schemas::ParticipantSignal {
|
||||||
@ -49,15 +42,13 @@ fn handle_message(message: &str) {
|
|||||||
ParticipantsAction::Remove { id } => participants_context
|
ParticipantsAction::Remove { id } => participants_context
|
||||||
.update(|participants| participants.retain(|participant| participant.id != id)),
|
.update(|participants| participants.retain(|participant| participant.id != id)),
|
||||||
ParticipantsAction::Replace { participant } => {
|
ParticipantsAction::Replace { participant } => {
|
||||||
participants_context.with(|participants| {
|
let participants = participants_context.get();
|
||||||
for participant_signal in participants {
|
for participant_signal in participants {
|
||||||
if participant_signal.id == participant.id {
|
if participant_signal.id == participant.id {
|
||||||
participant_signal.value.update(|value| {
|
participant_signal.value.set(participant);
|
||||||
*value = participant.clone();
|
break;
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,3 +24,34 @@ a {
|
|||||||
background-color: $secondary-bg-color-light;
|
background-color: $secondary-bg-color-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.participants-search {
|
||||||
|
margin-top: 40px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants-table {
|
||||||
|
margin-top: 0px;
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants-table th {
|
||||||
|
position: sticky;
|
||||||
|
background-color: $secondary-bg-color-lighter;
|
||||||
|
z-index: 100;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants-table th,td {
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants-table tr:hover {
|
||||||
|
background-color: $secondary-color !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.participants-table tr:nth-child(odd) {
|
||||||
|
background-color: $secondary-bg-color-light;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user