Members migration cleanup
This commit is contained in:
parent
b36fb6af89
commit
6d6b2c57c5
@ -1,231 +1 @@
|
|||||||
use crate::util::model::member::{Member, MembersMigration};
|
pub mod migration;
|
||||||
use dioxus::prelude::{dioxus_elements::FileEngine, *};
|
|
||||||
use manganis::ImageAsset;
|
|
||||||
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> {
|
|
||||||
match Member::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> {
|
|
||||||
if correct {
|
|
||||||
match Member::migrate(id).await {
|
|
||||||
Err(err) => Err(ServerFnError::new(err.to_string())),
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
}?;
|
|
||||||
} else {
|
|
||||||
tracing::info!("Migrations denied");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
230
src/components/admin/members/migration.rs
Normal file
230
src/components/admin/members/migration.rs
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
use crate::util::model::member::{Member, MembersMigration};
|
||||||
|
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> {
|
||||||
|
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> {
|
||||||
|
if correct {
|
||||||
|
match MembersMigration::migrate(id).await {
|
||||||
|
Err(err) => Err(ServerFnError::new(err.to_string())),
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
}?;
|
||||||
|
} else {
|
||||||
|
tracing::info!("Migrations denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -7,7 +7,7 @@ use dioxus::prelude::*;
|
|||||||
use tracing::{info, Level};
|
use tracing::{info, Level};
|
||||||
|
|
||||||
// Use routes
|
// Use routes
|
||||||
use components::admin::members::Migration;
|
use components::admin::members::migration::Migration;
|
||||||
use components::home::Home;
|
use components::home::Home;
|
||||||
|
|
||||||
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Routable, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -104,8 +104,9 @@ impl Row {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the raw csv file to rust objects
|
impl MembersMigration {
|
||||||
fn csv_to_rows(input: String) -> Result<Vec<Row>, Box<dyn std::error::Error>> {
|
// Convert the raw csv file to rust objects
|
||||||
|
fn csv_to_rows(input: String) -> Result<Vec<Row>, Box<dyn std::error::Error>> {
|
||||||
let mut members: Vec<Row> = vec![];
|
let mut members: Vec<Row> = vec![];
|
||||||
|
|
||||||
let mut rdr = csv::Reader::from_reader(input.as_bytes());
|
let mut rdr = csv::Reader::from_reader(input.as_bytes());
|
||||||
@ -116,10 +117,10 @@ fn csv_to_rows(input: String) -> Result<Vec<Row>, Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(members)
|
Ok(members)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Covert the rows to formatted members
|
// Covert the rows to formatted members
|
||||||
fn rows_to_members(rows: Vec<Row>) -> Vec<Member> {
|
fn rows_to_members(rows: Vec<Row>) -> Vec<Member> {
|
||||||
let mut members: Vec<Member> = vec![];
|
let mut members: Vec<Member> = vec![];
|
||||||
|
|
||||||
for row in rows {
|
for row in rows {
|
||||||
@ -127,13 +128,13 @@ fn rows_to_members(rows: Vec<Row>) -> Vec<Member> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
members
|
members
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare the new members list with the current
|
// Compare the new members list with the current
|
||||||
fn combine_members_lists(
|
fn combine_members_lists(
|
||||||
new_members_list: Vec<Member>,
|
new_members_list: Vec<Member>,
|
||||||
current_members_list: Vec<Member>,
|
current_members_list: Vec<Member>,
|
||||||
) -> MembersMigration {
|
) -> MembersMigration {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"Current: {}, New: {}",
|
"Current: {}, New: {}",
|
||||||
current_members_list.len(),
|
current_members_list.len(),
|
||||||
@ -183,24 +184,23 @@ fn combine_members_lists(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MembersMigration {
|
Self {
|
||||||
inserted: inserted_members,
|
inserted: inserted_members,
|
||||||
updated: updated_members,
|
updated: updated_members,
|
||||||
removed: removed_members,
|
removed: removed_members,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Member {
|
|
||||||
pub async fn migrate_proposal(
|
pub async fn migrate_proposal(
|
||||||
csv: String,
|
csv: String,
|
||||||
) -> Result<(u16, MembersMigration), Box<dyn std::error::Error>> {
|
) -> Result<(u16, MembersMigration), Box<dyn std::error::Error>> {
|
||||||
let rows = csv_to_rows(csv)?;
|
let rows = Self::csv_to_rows(csv)?;
|
||||||
|
|
||||||
let new_members = rows_to_members(rows);
|
let new_members = Self::rows_to_members(rows);
|
||||||
|
|
||||||
let current_members = Member::fetch_all().await?;
|
let current_members = Member::fetch_all().await?;
|
||||||
|
|
||||||
let members_migration = combine_members_lists(new_members, current_members);
|
let members_migration = Self::combine_members_lists(new_members, current_members);
|
||||||
|
|
||||||
let count = MEMBERS_STORE.lock().await.insert(members_migration.clone());
|
let count = MEMBERS_STORE.lock().await.insert(members_migration.clone());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user