Updated migration UI and added migration function
This commit is contained in:
parent
227b692052
commit
7ea256f85b
@ -1401,6 +1401,27 @@ html {
|
||||
margin-inline-start: calc(var(--border-btn) * -1);
|
||||
}
|
||||
|
||||
.loading {
|
||||
pointer-events: none;
|
||||
display: inline-block;
|
||||
aspect-ratio: 1 / 1;
|
||||
width: 1.5rem;
|
||||
background-color: currentColor;
|
||||
-webkit-mask-size: 100%;
|
||||
mask-size: 100%;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-position: center;
|
||||
mask-position: center;
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg width='24' height='24' stroke='%23000' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cstyle%3E.spinner_V8m1%7Btransform-origin:center;animation:spinner_zKoa 2s linear infinite%7D.spinner_V8m1 circle%7Bstroke-linecap:round;animation:spinner_YpZS 1.5s ease-out infinite%7D%40keyframes spinner_zKoa%7B100%25%7Btransform:rotate(360deg)%7D%7D%40keyframes spinner_YpZS%7B0%25%7Bstroke-dasharray:0 150;stroke-dashoffset:0%7D47.5%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-16%7D95%25%2C100%25%7Bstroke-dasharray:42 150;stroke-dashoffset:-59%7D%7D%3C%2Fstyle%3E%3Cg class='spinner_V8m1'%3E%3Ccircle cx='12' cy='12' r='9.5' fill='none' stroke-width='3'%3E%3C%2Fcircle%3E%3C%2Fg%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
|
||||
.mockup-browser .mockup-browser-toolbar .input {
|
||||
position: relative;
|
||||
margin-left: auto;
|
||||
@ -1899,8 +1920,8 @@ html {
|
||||
padding-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.pb-16 {
|
||||
padding-bottom: 4rem;
|
||||
.pb-36 {
|
||||
padding-bottom: 9rem;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
|
@ -7,10 +7,45 @@ struct UploadedFile {
|
||||
contents: String,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Steps {
|
||||
Upload,
|
||||
Verify,
|
||||
Done,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Migration() -> Element {
|
||||
let step = use_signal(|| Steps::Upload);
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex flex-col items-center justify-center h-full py-10",
|
||||
ul {
|
||||
class: "steps pb-36",
|
||||
li { class: "step step-primary", "Uploaden" },
|
||||
li {
|
||||
class: "step",
|
||||
class: if *step.read() == Steps::Verify { "step-primary" },
|
||||
"Controleren"
|
||||
},
|
||||
li { class: "step", "Klaar" },
|
||||
},
|
||||
match *step.read() {
|
||||
Steps::Upload => rsx! { Upload { step: step } },
|
||||
Steps::Verify => rsx! { Verify {} },
|
||||
Steps::Done => rsx! { Verify {} },
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Upload(step: Signal<Steps>) -> 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 {
|
||||
@ -32,47 +67,68 @@ pub fn Migration() -> Element {
|
||||
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 {
|
||||
tracing::info!("Loaded!!");
|
||||
tracing::info!("Done");
|
||||
step.set(Steps::Verify);
|
||||
}
|
||||
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", "Selecteer het ledenbestand" },
|
||||
input {
|
||||
r#type: "file",
|
||||
class: "file-input file-input-bordered w-full mt-16",
|
||||
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"
|
||||
}
|
||||
"{file_uploaded.read().is_none()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn Verify() -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex flex-col items-center justify-center h-full py-10",
|
||||
ul {
|
||||
class: "steps pb-16",
|
||||
li { class: "step step-primary", "Uploaden" },
|
||||
li { class: "step", "Controleren" },
|
||||
li { class: "step", "Klaar" },
|
||||
}
|
||||
form {
|
||||
class: "flex flex-col items-center w-full h-full max-w-md mx-auto px-2",
|
||||
onsubmit: sumbit,
|
||||
h2 { class: "text-xl", "Selecteer het ledenbestand" },
|
||||
input {
|
||||
r#type: "file",
|
||||
class: "file-input file-input-bordered w-full mt-16",
|
||||
accept: ".csv",
|
||||
multiple: false,
|
||||
autocomplete: false,
|
||||
onchange: upload_files,
|
||||
}
|
||||
input {
|
||||
r#type: "submit",
|
||||
class: "btn btn-primary btn-wide mt-5",
|
||||
disabled: file_uploaded.read().is_none(),
|
||||
value: "Uploaden"
|
||||
}
|
||||
}
|
||||
class: "flex flex-col items-center w-full h-full max-w-md mx-auto px-2",
|
||||
h2 { class: "text-xl", "Controleer de actie" },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn upload_members_list(input: String) -> Result<String, ServerFnError> {
|
||||
use crate::util::model::member::Member;
|
||||
|
||||
tracing::info!("Getting members...");
|
||||
|
||||
let result = Member::migrate_proposal(input).await;
|
||||
|
||||
tracing::info!("{:?}", result);
|
||||
|
||||
Ok("Whoo".to_string())
|
||||
}
|
||||
|
||||
#[server]
|
||||
async fn check_members_list(correct: bool, id: u32) -> Result<(), ServerFnError> {
|
||||
tracing::info!("boe");
|
||||
Ok(())
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use std::collections::BTreeSet;
|
||||
|
||||
mod migration;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct Member {
|
||||
id: String,
|
||||
name: Name,
|
||||
@ -15,7 +15,7 @@ pub struct Member {
|
||||
registration_token: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct Name {
|
||||
first: String,
|
||||
full: String,
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::util::surrealdb::DB;
|
||||
|
||||
use super::Member;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
@ -126,6 +128,78 @@ fn rows_to_members(rows: Vec<Row>) -> Vec<Member> {
|
||||
members
|
||||
}
|
||||
|
||||
// Compare the new members list with the current
|
||||
// (inserted, updated, removed)
|
||||
fn combine_members_lists(
|
||||
new_members_list: Vec<Member>,
|
||||
current_members_list: Vec<Member>,
|
||||
) -> (Vec<Member>, Vec<Member>, Vec<Member>) {
|
||||
tracing::info!(
|
||||
"Current: {}, New: {}",
|
||||
current_members_list.len(),
|
||||
new_members_list.len()
|
||||
);
|
||||
|
||||
let current_members_map: HashMap<String, Member> = current_members_list
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|m| (m.id.clone(), m))
|
||||
.collect();
|
||||
|
||||
let new_members_map: HashMap<String, Member> = new_members_list
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|m| (m.id.clone(), m))
|
||||
.collect();
|
||||
|
||||
let mut inserted_members: Vec<Member> = vec![];
|
||||
let mut updated_members: Vec<Member> = vec![];
|
||||
let mut removed_members: Vec<Member> = vec![];
|
||||
|
||||
for current_member in current_members_list {
|
||||
if let Some(new_member) = new_members_map.get(¤t_member.id) {
|
||||
// Update existing member
|
||||
let new_member_clone = new_member.clone();
|
||||
|
||||
updated_members.push(Member {
|
||||
id: current_member.id,
|
||||
name: new_member_clone.name,
|
||||
hours: new_member_clone.hours,
|
||||
groups: current_member.groups,
|
||||
diploma: new_member_clone.diploma,
|
||||
registration_token: current_member.diploma,
|
||||
})
|
||||
} else {
|
||||
// Remove member
|
||||
removed_members.push(current_member);
|
||||
}
|
||||
}
|
||||
|
||||
for new_member in new_members_list {
|
||||
// Insert new member
|
||||
// TODO Generate registration token
|
||||
if !current_members_map.contains_key(&new_member.id) {
|
||||
inserted_members.push(new_member);
|
||||
}
|
||||
}
|
||||
|
||||
(inserted_members, updated_members, removed_members)
|
||||
}
|
||||
|
||||
impl Member {
|
||||
pub async fn migrate_proposal(
|
||||
csv: String,
|
||||
) -> Result<(Vec<Member>, Vec<Member>, Vec<Member>), Box<dyn std::error::Error>> {
|
||||
let rows = csv_to_rows(csv)?;
|
||||
|
||||
let new_members = rows_to_members(rows);
|
||||
|
||||
let current_members = Member::fetch_all().await?;
|
||||
|
||||
Ok(combine_members_lists(new_members, current_members))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
Loading…
Reference in New Issue
Block a user