246 lines
7.2 KiB
Rust

use crate::util::model::{
member::{Member, MembersMigration},
session::Session,
};
use dioxus::prelude::{dioxus_elements::FileEngine, *};
use std::sync::Arc;
#[derive(Debug)]
struct UploadedFile {
name: String,
contents: String,
}
#[derive(PartialEq)]
enum Steps {
Upload,
Verify,
Done,
}
#[component]
pub fn Migration() -> Element {
let step = use_signal(|| Steps::Upload);
let members_migration = use_signal(|| (0, MembersMigration::new()));
rsx! {
div {
class: "flex flex-col items-center justify-center py-10",
ul {
class: "steps pb-10",
li { class: "step step-primary", "Uploaden" },
li {
class: "step",
class: if let Steps::Verify | Steps::Done = *step.read() { { "step-primary" } },
"Controleren"
},
li {
class: "step",
class: if let Steps::Done = *step.read() { { "step-primary" } },
"Klaar"
},
},
match *step.read() {
Steps::Upload => rsx! { Upload { step: step, members_migration: members_migration } },
Steps::Verify => rsx! { Verify { step: step, members_migration: members_migration } },
Steps::Done => rsx! { Done {} },
}
}
}
}
#[component]
fn Upload(step: Signal<Steps>, members_migration: Signal<(u16, MembersMigration)>) -> Element {
let mut file_uploaded = use_signal(|| None);
let mut loading = use_signal(|| false);
let read_files = move |file_engine: Arc<dyn FileEngine>| async move {
let files = file_engine.files();
for file_name in &files {
if let Some(contents) = file_engine.read_file_to_string(file_name).await {
file_uploaded.set(Some(UploadedFile {
name: file_name.clone(),
contents,
}));
}
}
};
let upload_files = move |evt: FormEvent| async move {
if let Some(file_engine) = evt.files() {
read_files(file_engine).await;
}
};
let sumbit = move |_evt: FormEvent| async move {
match &*file_uploaded.read() {
Some(file) => {
loading.set(true);
if let Ok(response) = upload_members_list(file.contents.clone()).await {
members_migration.set(response);
step.set(Steps::Verify);
loading.set(false);
}
loading.set(false);
}
None => tracing::info!("File doesn't exists"),
}
};
rsx! {
form {
class: "flex flex-col items-center w-full h-full max-w-md mx-auto px-2",
onsubmit: sumbit,
h2 { class: "text-xl mb-5", "Selecteer de ledenlijst" },
input {
r#type: "file",
class: "file-input file-input-bordered w-full",
accept: ".csv",
multiple: false,
autocomplete: "off",
onchange: upload_files,
}
button {
class: "btn btn-primary btn-wide mt-5",
disabled: file_uploaded.read().is_none() || loading(),
if loading() {
span { class: "loading loading-spinner" }
}
"Uploaden"
}
}
}
}
#[component]
fn Verify(step: Signal<Steps>, members_migration: Signal<(u16, MembersMigration)>) -> Element {
let mut loading = use_signal(|| false);
let submit_accept = move |_| async move {
loading.set(true);
if let Ok(_response) = migration_response(true, members_migration.read().0).await {
step.set(Steps::Done);
loading.set(false);
};
loading.set(false);
};
rsx! {
div {
class: "flex flex-col items-center justify-center w-full mx-auto px-2",
h2 { class: "text-xl mb-5", "Controleer de verandering" },
div {
class: "flex flex-wrap gap-5",
div {
class: "card bg-base-200 p-5",
h2 { class: "card-title mb-1", "Toevoegen" }
MembersTable { members: members_migration.read().1.inserted.clone() }
}
div {
class: "card bg-base-200 p-5",
h2 { class: "card-title mb-1", "Verwijderen" }
MembersTable { members: members_migration.read().1.removed.clone() }
}
div {
class: "card bg-base-200 p-5",
h2 { class: "card-title mb-1", "Updaten" }
MembersTable { members: members_migration.read().1.updated.clone() }
}
}
button {
class: "btn btn-primary btn-wide mt-5",
onclick: submit_accept,
disabled: loading(),
if loading() {
span { class: "loading loading-spinner" }
}
"Toepassen"
}
}
}
}
#[component]
fn Done() -> Element {
rsx! {
div {
class: "flex flex-col items-center justify-center w-full mx-auto px-2",
h2 { class: "text-xl mb-5", "Ledenlijst is geüpdate" },
div {
class: "w-80 mt-10",
img {
src: "/gifs/nick.webp",
}
}
button {
class: "btn btn-primary btn-wide mt-5",
"Terug naar Start"
}
}
}
}
#[component]
fn MembersTable(members: Vec<Member>) -> Element {
rsx! {
div {
class: "h-[30rem] w-96 overflow-auto font-normal",
table {
class: "table table-pin-rows",
thead {
tr {
th { "Relatiecode" }
th { "Naam" }
}
}
tbody {
for member in members {
tr {
th { "{member.id}" }
th { "{member.name.full}" }
}
}
}
}
}
}
}
#[server]
async fn upload_members_list(input: String) -> Result<(u16, MembersMigration), ServerFnError> {
let user = Session::fetch_current_user().await?;
if !user.admin {
return Err(crate::Error::NoPermissions.into());
}
match MembersMigration::migrate_proposal(input).await {
Ok(r) => Ok(r),
Err(err) => Err(ServerFnError::new(err.to_string())),
}
}
#[server]
async fn migration_response(correct: bool, id: u16) -> Result<(), ServerFnError> {
let user = Session::fetch_current_user().await?;
if !user.admin {
return Err(crate::Error::NoPermissions.into());
}
if correct {
match MembersMigration::migrate(id).await {
Err(err) => Err(ServerFnError::new(err.to_string())),
Ok(_) => Ok(()),
}?;
} else {
tracing::info!("Migrations denied");
}
Ok(())
}