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, 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| 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, 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) -> 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(()) }