232 lines
7.8 KiB
Rust
232 lines
7.8 KiB
Rust
use crate::util::surrealdb::{client::sort_participants, client::Time, schemas};
|
|
use leptos::{ev::keydown, *};
|
|
use leptos_toaster::{Toast, ToastId, ToastVariant, Toasts};
|
|
use leptos_use::*;
|
|
use web_sys::ScrollIntoViewOptions;
|
|
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(feature = "ssr")] {
|
|
use crate::util::surrealdb::{DB, schemas::Participant};
|
|
use crate::util::websocket::{server, ParticipantsAction};
|
|
use surrealdb::opt::PatchOp;
|
|
use leptos::logging;
|
|
}
|
|
}
|
|
|
|
#[server(AddTime)]
|
|
async fn add_time(
|
|
mut participant: schemas::Participant,
|
|
event: String,
|
|
time: u32,
|
|
) -> Result<(), ServerFnError> {
|
|
let websocket_state = &server::WEBSOCKET_STATE;
|
|
|
|
let updated: Option<schemas::ParticipantRecord> = DB
|
|
.update(("participant", &participant.id))
|
|
.patch(PatchOp::replace(&("/events/".to_owned() + &event), time))
|
|
.await?;
|
|
|
|
match updated {
|
|
Some(participant_updated) => {
|
|
logging::log!(
|
|
"Updated participant: {} ({})",
|
|
participant_updated.name,
|
|
participant_updated.group
|
|
);
|
|
let action = ParticipantsAction::Replace {
|
|
participant: Participant {
|
|
name: participant_updated.name.clone(),
|
|
group: participant_updated.group.clone(),
|
|
id: participant.id,
|
|
events: participant_updated.events.clone(),
|
|
},
|
|
};
|
|
|
|
match websocket_state.apply(action) {
|
|
Ok(_) => Ok(()),
|
|
Err(_) => Err(ServerFnError::new("Error sending websocket action")),
|
|
}
|
|
}
|
|
None => Err(ServerFnError::ServerError(String::from(
|
|
"Could not update participant",
|
|
))),
|
|
}
|
|
}
|
|
|
|
/// Renders the home page of your application.
|
|
#[component]
|
|
pub fn AddTime() -> impl IntoView {
|
|
let participants = use_context::<schemas::ParticipantsContext>().unwrap();
|
|
let toasts_context = expect_context::<Toasts>();
|
|
|
|
let container_ref: NodeRef<html::Ul> = create_node_ref();
|
|
let name_input_ref: NodeRef<html::Input> = create_node_ref();
|
|
|
|
let event = create_rw_signal("lifesaver".to_string());
|
|
let name = create_rw_signal("".to_string());
|
|
let time = create_rw_signal(Time {
|
|
minutes: 0,
|
|
seconds: 0,
|
|
milliseconds: 0,
|
|
});
|
|
let selected_index = create_rw_signal::<usize>(0);
|
|
|
|
let participants_sorted =
|
|
create_memo(move |_| sort_participants(participants.get(), name.get()));
|
|
|
|
let add_time_action = create_action(|input: &(schemas::Participant, String, u32)| {
|
|
let input = input.to_owned();
|
|
async move { add_time(input.0, input.1, input.2).await }
|
|
});
|
|
|
|
let form_submit = move |ev: ev::SubmitEvent| {
|
|
ev.prevent_default();
|
|
|
|
let participant = &participants_sorted.get()[selected_index.get()];
|
|
add_time_action.dispatch((
|
|
participant.value.get(),
|
|
event.get(),
|
|
time.get().as_milliseconds(),
|
|
));
|
|
|
|
let toast_id = ToastId::new();
|
|
|
|
toasts_context.toast(
|
|
view! {
|
|
<Toast
|
|
toast_id
|
|
variant=ToastVariant::Success
|
|
title=view! { "Successfully added time" }.into_view()
|
|
/>
|
|
},
|
|
Some(toast_id),
|
|
None,
|
|
);
|
|
|
|
name.set("".to_string());
|
|
time.set(Time {
|
|
minutes: 0,
|
|
seconds: 0,
|
|
milliseconds: 0,
|
|
});
|
|
let _ = name_input_ref.get().unwrap().focus();
|
|
};
|
|
|
|
let _ = use_event_listener(name_input_ref, keydown, move |evt| {
|
|
match evt.key().as_str() {
|
|
"ArrowDown" => selected_index.update(|x| {
|
|
let len = participants.get_untracked().len();
|
|
if *x != len {
|
|
*x += 1;
|
|
}
|
|
}),
|
|
"ArrowUp" => selected_index.update(|x| {
|
|
if *x != 0 {
|
|
*x -= 1;
|
|
}
|
|
}),
|
|
"Enter" => evt.prevent_default(),
|
|
_ => (),
|
|
}
|
|
let el: web_sys::Element = container_ref
|
|
.get_untracked()
|
|
.unwrap()
|
|
.children()
|
|
.item(selected_index.get_untracked().try_into().unwrap())
|
|
.unwrap();
|
|
el.scroll_into_view_with_scroll_into_view_options(
|
|
&ScrollIntoViewOptions::new().block(web_sys::ScrollLogicalPosition::Center),
|
|
);
|
|
});
|
|
|
|
view! {
|
|
<h2>"Tijd toevoegen"</h2>
|
|
<form on:submit=form_submit>
|
|
<label>Onderdeel</label>
|
|
<select autocomplete="off"
|
|
on:change=move |ev| {
|
|
event.set(event_target_value(&ev))
|
|
}
|
|
>
|
|
<option value="lifesaver">"Lifesaver"</option>
|
|
<option value="hindernis">"Hindernis"</option>
|
|
<option value="popduiken">"Popduiken"</option>
|
|
</select>
|
|
<label>Naam</label>
|
|
<div class="autocomplete">
|
|
<input type="text"
|
|
name="name"
|
|
autocomplete="off"
|
|
autofocus=true
|
|
node_ref=name_input_ref
|
|
on:input=move |ev| {
|
|
name.set(event_target_value(&ev));
|
|
selected_index.set(0);
|
|
}
|
|
prop:value=name
|
|
/>
|
|
<ul node_ref=container_ref tabindex=-1>
|
|
{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.value.get().name + " " + "(" + &participant.value.get().group + ")" }</li>
|
|
}).collect_view()}
|
|
</ul>
|
|
</div>
|
|
<label>Tijd</label>
|
|
<div class="time">
|
|
<input type="number"
|
|
autocomplete="off"
|
|
placeholder="mm"
|
|
min=0
|
|
max=99
|
|
on:input=move |ev| {
|
|
time.update(|time| time.minutes = match event_target_value(&ev).parse::<u32>() {
|
|
Ok(x) => x,
|
|
Err(_) => 0,
|
|
});
|
|
}
|
|
prop:value=move || time.get().minutes
|
|
/>
|
|
<input type="number"
|
|
autocomplete="off"
|
|
placeholder="ss"
|
|
min=0
|
|
max=59
|
|
on:input=move |ev| {
|
|
time.update(|time| time.seconds = match event_target_value(&ev).parse::<u32>() {
|
|
Ok(x) => x,
|
|
Err(_) => 0,
|
|
});
|
|
}
|
|
prop:value=move || time.get().seconds
|
|
/>
|
|
<input type="number"
|
|
autocomplete="off"
|
|
placeholder="ms"
|
|
min=0
|
|
max=99
|
|
on:input=move |ev| {
|
|
time.update(|time| time.milliseconds = match event_target_value(&ev).parse::<u32>() {
|
|
Ok(x) => x,
|
|
Err(_) => 0,
|
|
});
|
|
}
|
|
prop:value=move || time.get().milliseconds
|
|
/>
|
|
</div>
|
|
<input type="submit" value="Tijd toevoegen" />
|
|
</form>
|
|
}
|
|
}
|
|
|
|
#[component]
|
|
pub fn SelectOption(is: &'static str, value: ReadSignal<String>) -> impl IntoView {
|
|
view! {
|
|
<option
|
|
value=is
|
|
selected=move || value.get() == is
|
|
>
|
|
{is}
|
|
</option>
|
|
}
|
|
}
|