Files
PinePods-nix/PinePods-0.8.2/web/src/components/setting_components/user_settings.rs
2026-03-03 10:57:43 -05:00

1084 lines
57 KiB
Rust

use crate::components::context::AppState;
use crate::components::gen_funcs::validate_user_input;
use crate::components::gen_funcs::{
encode_password, validate_email, validate_username, ValidationError,
};
use crate::requests::setting_reqs::call_get_user_info;
use crate::requests::setting_reqs::{
call_add_user, call_check_admin, call_delete_user, call_set_email, call_set_fullname,
call_set_isadmin, call_set_password, call_set_username, AddSettingsUserRequest, SettingsUser,
};
use std::borrow::Borrow;
use wasm_bindgen::JsCast;
use web_sys::console;
use yew::platform::spawn_local;
use yew::prelude::*;
use yewdux::prelude::*;
use i18nrs::yew::use_translation;
// use crate::gen_components::_ErrorMessageProps::error_message;
#[function_component(UserSettings)]
pub fn user_settings() -> Html {
let (i18n, _) = use_translation();
let (state, _dispatch) = use_store::<AppState>();
let ui_user = _dispatch.clone();
let ui_wasm = _dispatch.clone();
let server_name = state.auth_details.as_ref().map(|ud| ud.server_name.clone());
let api_key = state.auth_details.as_ref().map(|ud| ud.api_key.clone());
let new_username = use_state(|| "".to_string());
let new_password = use_state(|| "".to_string());
let email = use_state(|| "".to_string());
let fullname = use_state(|| "".to_string());
let admin_status = use_state(|| false);
let selected_user_id = use_state(|| None);
let _error_message = state.error_message.clone();
let _info_message = state.info_message.clone();
let error_message_container = use_state(|| "".to_string());
let admin_edit_status = use_state(|| 0);
let update_trigger = use_state(|| false);
// Define the type of user in the Vec
let users: UseStateHandle<Vec<SettingsUser>> = use_state(|| Vec::new());
// let selected_user = users
// .iter()
// .find(|u| Some(Some(u.userid)) == *selected_user_id);
{
let users = users.clone();
let update_trigger_effect = update_trigger.clone();
use_effect_with(
(api_key.clone(), server_name.clone(), *update_trigger_effect),
move |(api_key, server_name, _update_trigger_effect)| {
let users = users.clone();
let api_key = api_key.clone();
let server_name = server_name.clone();
let future = async move {
if let (Some(api_key), Some(server_name)) = (api_key, server_name) {
let response = call_get_user_info(server_name, api_key.unwrap()).await;
match response {
Ok(user_info) => {
users.set(user_info);
}
Err(e) => {
console::log_1(&format!("Error getting user info: {}", e).into());
// user_dispatch.reduce_mut(|state| state.error_message = Option::from(format!("Error getting user info: {}", e).to_string()))
}
}
}
};
spawn_local(future);
// Return cleanup function
|| {}
},
);
}
{
let selected_user_id = selected_user_id.clone();
let users = users.clone();
let new_username = new_username.clone();
let fullname = fullname.clone();
let email = email.clone();
let admin_status = admin_status.clone();
let new_password = new_password.clone(); // Add this
use_effect_with(selected_user_id.clone(), move |_| {
if let Some(Some(user_id)) = *selected_user_id {
if let Some(user) = users.iter().find(|u| u.userid == user_id) {
new_username.set(user.username.clone());
fullname.set(user.fullname.clone());
email.set(user.email.clone());
admin_status.set(user.isadmin == 1);
new_password.set("password".to_string()); // Set default password
}
}
|| ()
});
}
// Define the state of the application
#[derive(Clone, PartialEq)]
enum PageState {
Hidden,
Shown,
Edit,
}
#[allow(non_camel_case_types)]
enum email_error_notice {
Hidden,
Shown,
}
#[allow(non_camel_case_types)]
enum password_error_notice {
Hidden,
Shown,
}
#[allow(non_camel_case_types)]
enum username_error_notice {
Hidden,
Shown,
}
#[allow(non_camel_case_types)]
enum error_container_state {
Hidden,
Shown,
}
//Define States for error message
let email_error = use_state(|| email_error_notice::Hidden);
let password_error = use_state(|| password_error_notice::Hidden);
let username_error = use_state(|| username_error_notice::Hidden);
let error_container = use_state(|| error_container_state::Hidden);
// Define the initial state
let page_state = use_state(|| PageState::Hidden);
let on_close_modal = {
let page_state = page_state.clone();
Callback::from(move |_: MouseEvent| {
page_state.set(PageState::Hidden);
})
};
let on_background_click = {
let on_close_modal = on_close_modal.clone();
Callback::from(move |e: MouseEvent| {
let target = e.target().unwrap();
let element = target.dyn_into::<web_sys::Element>().unwrap();
if element.tag_name() == "DIV" {
on_close_modal.emit(e);
}
})
};
let stop_propagation = Callback::from(|e: MouseEvent| {
e.stop_propagation();
});
// Define the callback functions
let on_create_new_user = {
let page_state = page_state.clone();
Callback::from(move |_| {
page_state.set(PageState::Shown);
})
};
let on_fullname_change = {
let fullname = fullname.clone();
Callback::from(move |e: InputEvent| {
let value = e
.target_unchecked_into::<web_sys::HtmlInputElement>()
.value();
// This resets the default value to whatever was typed
fullname.set(value);
})
};
let on_username_change = {
let new_username = new_username.clone();
Callback::from(move |e: InputEvent| {
let value = e
.target_unchecked_into::<web_sys::HtmlInputElement>()
.value();
// This resets the default value to whatever was typed
new_username.set(value);
})
};
let on_email_change = {
let email = email.clone();
Callback::from(move |e: InputEvent| {
let value = e
.target_unchecked_into::<web_sys::HtmlInputElement>()
.value();
// This resets the default value to whatever was typed
email.set(value);
})
};
let on_password_change = {
let new_password = new_password.clone();
Callback::from(move |e: InputEvent| {
new_password.set(
e.target_unchecked_into::<web_sys::HtmlInputElement>()
.value(),
);
})
};
let on_admin_change = {
let admin_status = admin_status.clone();
Callback::from(move |e: Event| {
let target = e.target().unwrap();
let input = target.dyn_into::<web_sys::HtmlInputElement>().unwrap();
admin_status.set(input.checked());
})
};
let error_container_create = error_container.clone();
let error_message_container_create = error_message_container.clone();
let on_create_submit = {
let page_state = page_state.clone();
let server_name = server_name.clone();
let api_key = api_key.clone();
let fullname = fullname.clone().to_string();
let new_username = new_username.clone().to_string();
let email = email.clone().to_string();
let new_password = new_password.clone();
let username_error = username_error.clone();
let password_error = password_error.clone();
let email_error = email_error.clone();
let on_update_trigger = update_trigger.clone();
// Capture translated message before move
let invalid_user_data_msg = i18n.t("settings.invalid_user_data").to_string();
Callback::from(move |e: MouseEvent| {
let invalid_user_data_msg = invalid_user_data_msg.clone();
let error_container = error_container_create.clone();
let error_message_container = error_message_container_create.clone();
let update_trigger = on_update_trigger.clone();
let call_server = server_name.clone();
let call_api = api_key.clone();
let new_username = new_username.clone();
let new_password = new_password.clone();
let fullname = fullname.clone();
let email = email.clone();
e.prevent_default();
// Validate input
let errors = validate_user_input(&new_username, &new_password, &email);
if errors.contains(&ValidationError::UsernameTooShort) {
username_error.set(username_error_notice::Shown);
} else {
username_error.set(username_error_notice::Hidden);
}
if errors.contains(&ValidationError::PasswordTooShort) {
password_error.set(password_error_notice::Shown);
} else {
password_error.set(password_error_notice::Hidden);
}
if errors.contains(&ValidationError::InvalidEmail) {
email_error.set(email_error_notice::Shown);
} else {
email_error.set(email_error_notice::Hidden);
}
if errors.is_empty() {
match encode_password(&new_password) {
Ok(hash_pw) => {
let user_settings = AddSettingsUserRequest {
fullname: fullname.clone(),
username: new_username.clone(),
email: email.clone(),
hash_pw: hash_pw.clone(),
};
let add_user_request = Some(user_settings);
page_state.set(PageState::Hidden);
wasm_bindgen_futures::spawn_local(async move {
let on_update_trigger = update_trigger.clone();
if let Some(add_user_request_value) = add_user_request {
match call_add_user(
call_server.unwrap(),
call_api.unwrap().unwrap(),
&add_user_request_value,
)
.await
{
Ok(_success) => {
on_update_trigger.set(!*update_trigger);
}
Err(e) => {
error_container.set(error_container_state::Shown);
// Display the actual error message from the server
error_message_container.set(e.to_string());
}
}
} else {
error_container.set(error_container_state::Shown);
error_message_container
.set(invalid_user_data_msg.clone());
}
});
}
Err(e) => {
error_container.set(error_container_state::Shown);
error_message_container.set(format!("Error creating password hash: {}", e));
}
}
}
})
};
// Define the modal components
let create_user_modal = html! {
<div id="create-user-modal" tabindex="-1" aria-hidden="true" class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full h-[calc(100%-1rem)] max-h-full bg-black bg-opacity-25" onclick={on_background_click.clone()}>
<div class="modal-container relative p-4 w-full max-w-md max-h-full rounded-lg shadow" onclick={stop_propagation.clone()}>
<div class="modal-container relative rounded-lg shadow">
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t">
<h3 class="text-xl font-semibold">
{i18n.t("settings.create_new_user")}
</h3>
<button onclick={on_close_modal.clone()} class="end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">{i18n.t("settings.close_modal")}</span>
</button>
</div>
<div class="p-4 md:p-5">
<form class="space-y-4" action="#">
<div>
<label for="username" class="block mb-2 text-sm font-medium">{i18n.t("settings.username")}</label>
<input oninput={on_username_change.clone()} placeholder="pinepods_user1" type="text" id="username" name="username" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
{
match *username_error {
username_error_notice::Hidden => html! {},
username_error_notice::Shown => html! {<p class="text-red-500 text-xs italic">{i18n.t("settings.username_min_length")}</p>},
}
}
</div>
<div>
<label for="fullname" class="block mb-2 text-sm font-medium">{i18n.t("settings.full_name")}</label>
<input oninput={on_fullname_change.clone()} placeholder="Pinepods User" type="text" id="fullname" name="fullname" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
</div>
<div>
<label for="email" class="block mb-2 text-sm font-medium">{i18n.t("settings.email")}</label>
<input oninput={on_email_change.clone()} placeholder="user@pinepods.online" type="email" id="email" name="email" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
{
match *email_error {
email_error_notice::Hidden => html! {},
email_error_notice::Shown => html! {<p class="text-red-500 text-xs italic">{i18n.t("settings.invalid_email_address")}</p>},
}
}
</div>
<div>
<label for="password" class="block mb-2 text-sm font-medium">{i18n.t("settings.password")}</label>
<input oninput={on_password_change.clone()} placeholder="my_S3creT_P@$$" type="password" id="password" name="password" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
{
match *password_error {
password_error_notice::Hidden => html! {},
password_error_notice::Shown => html! {<p class="text-red-500 text-xs italic">{i18n.t("settings.password_min_length")}</p>},
}
}
</div>
<button type="submit" onclick={on_create_submit} class="download-button w-full focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center">{i18n.t("settings.submit")}</button>
</form>
</div>
</div>
</div>
</div>
};
let user_dispatch = ui_user.clone();
let edit_admin_call = admin_edit_status.clone();
let on_user_row_click = {
let selected_user_id = selected_user_id.clone();
let page_state = page_state.clone();
move |select_user_id: i32, is_admin: i32| {
// admin_edit_status.set(is_admin);
Callback::from(move |_| {
if select_user_id == 1 {
user_dispatch.reduce_mut(|state| {
state.error_message =
Option::from("You cannot edit the background_tasks user.".to_string())
});
return;
}
edit_admin_call.set(is_admin);
selected_user_id.set(Some(Some(select_user_id))); // Wrap the value in double Some()
page_state.set(PageState::Edit); // Move to the edit page state
})
}
};
let error_message_container_edit = error_message_container.clone();
let error_container_edit = error_container.clone();
let delete_dispatch = ui_user.clone();
let on_delete_click = {
let server_name = server_name.clone();
let api_key = api_key.clone();
let selected_user_id = selected_user_id.clone();
let page_state = page_state.clone();
let user_dispatch = delete_dispatch.clone();
Callback::from(move |e: MouseEvent| {
e.prevent_default();
if let Some(Some(user_id)) = *selected_user_id {
let server_name = server_name.clone();
let api_key = api_key.clone();
let page_state = page_state.clone();
let user_dispatch = user_dispatch.clone();
wasm_bindgen_futures::spawn_local(async move {
match call_delete_user(server_name.unwrap(), api_key.unwrap().unwrap(), user_id)
.await
{
Ok(_response) => {
user_dispatch.reduce_mut(|state| {
state.info_message = Some("User deleted successfully".to_string())
});
page_state.set(PageState::Hidden); // Navigate back to the list page state
}
Err(e) => {
web_sys::console::log_1(
&format!("Failed to delete user: {}", e).into(),
);
user_dispatch.reduce_mut(|state| {
state.error_message = Some("Failed to delete user".to_string())
});
}
}
});
} else {
user_dispatch
.reduce_mut(|state| state.error_message = Some("No user selected".to_string()));
}
})
};
let edit_users = users.clone();
let username_users = users.clone();
let admin_status_clone = admin_status.clone();
let on_edit_submit = {
let fullname = fullname.clone().to_string();
let page_state = page_state.clone();
let new_username = new_username.clone().to_string();
let server_name = server_name.clone();
let api_key = api_key.clone();
let email = email.clone().to_string();
let new_password = new_password.clone();
let dispatch_wasm = ui_wasm.clone();
let edit_selected_user_id = selected_user_id.clone();
let username_error_edit = username_error.clone();
let email_error_edit = email_error.clone();
let password_error_edit = password_error.clone();
let on_update_trigger = update_trigger.clone();
Callback::from(move |e: MouseEvent| {
let error_container = error_container_edit.clone();
let error_message_container = error_message_container_edit.clone();
// let update_trigger = on_update_trigger.clone();
let username_error = username_error_edit.clone();
let email_error = email_error_edit.clone();
let password_error = password_error_edit.clone();
let dispatch_wasm = dispatch_wasm.clone();
let new_username = new_username.clone();
let new_password = new_password.clone();
let fullname = fullname.clone();
let email = email.clone();
let admin_status = admin_status_clone.clone();
let call_selected_user_id = edit_selected_user_id.clone();
e.prevent_default();
// Check if each field has input and call the corresponding API function
let fullname_dispatch = dispatch_wasm.clone();
let page_state_name = page_state.clone();
let page_state_user = page_state.clone();
let page_state_pass = page_state.clone();
let page_state_email = page_state.clone();
let page_state_true = page_state.clone();
let page_state_false = page_state.clone();
let admin_edit_status_call = admin_edit_status.clone();
let admin_edit_status_false = admin_edit_status.clone();
let update_trigger_name = on_update_trigger.clone();
let update_trigger_user = on_update_trigger.clone();
let update_trigger_pass = on_update_trigger.clone();
let update_trigger_email = on_update_trigger.clone();
let update_trigger_true = on_update_trigger.clone();
let update_trigger_false = on_update_trigger.clone();
let error_container_name = error_container.clone();
let error_message_container_name = error_message_container.clone();
if !fullname.is_empty()
&& fullname
!= username_users
.iter()
.find(|u| Some(Some(u.userid)) == *selected_user_id)
.map(|u| u.fullname.clone())
.unwrap_or_default()
{
wasm_bindgen_futures::spawn_local({
let update_trigger_in_check = update_trigger_name.clone();
let server_name_cloned = server_name.clone();
let api_key_cloned = api_key.clone();
let name_cloned = fullname.clone();
let selected_user_id_cloned = call_selected_user_id.clone();
async move {
if let Some(server_name_unwrapped) = server_name_cloned {
if let Some(api_key_unwrapped) =
api_key_cloned.as_ref().and_then(|key| key.as_ref())
{
if let Some(user_id) = *selected_user_id_cloned {
page_state_name.set(PageState::Hidden);
match call_set_fullname(
server_name_unwrapped,
api_key_unwrapped.clone(),
user_id.unwrap(),
name_cloned,
)
.await
{
Ok(_) => {
update_trigger_in_check.set(!*update_trigger_in_check);
}
Err(e) => {
error_container_name.set(error_container_state::Shown);
error_message_container_name.set(
format!("Error updating name: {}", e).to_string(),
);
}
}
} else {
fullname_dispatch.reduce_mut(|state| {
state.error_message = Option::from(
"User ID not available for name update.".to_string(),
)
});
}
} else {
fullname_dispatch.reduce_mut(|state| {
state.error_message = Option::from(
"API key not available for name update.".to_string(),
)
});
}
} else {
fullname_dispatch.reduce_mut(|state| {
state.error_message = Option::from(
"Server name not available for name update.".to_string(),
)
});
}
}
});
}
let error_container_user = error_container.clone();
let error_message_container_user = error_message_container.clone();
if !new_username.is_empty()
&& new_username
!= username_users
.iter()
.find(|u| Some(Some(u.userid)) == *selected_user_id)
.map(|u| u.username.clone())
.unwrap_or_default()
{
wasm_bindgen_futures::spawn_local({
let server_name_cloned = server_name.clone();
let api_key_cloned = api_key.clone();
let update_trigger_in_check = update_trigger_user.clone();
let user_cloned = new_username.clone();
let selected_user_id_cloned = call_selected_user_id.clone();
async move {
if let Some(server_name_unwrapped) = server_name_cloned {
if let Some(api_key_unwrapped) =
api_key_cloned.as_ref().and_then(|key| key.as_ref())
{
if let Some(user_id) = *selected_user_id_cloned {
let errors = validate_username(new_username.clone().as_str());
if errors.contains(&ValidationError::UsernameTooShort) {
username_error.set(username_error_notice::Shown);
} else {
page_state_user.set(PageState::Hidden);
match call_set_username(
server_name_unwrapped,
api_key_unwrapped.clone(),
user_id.unwrap(),
user_cloned,
)
.await
{
Ok(_) => {
update_trigger_in_check
.set(!*update_trigger_in_check);
}
Err(_e) => {
error_container_user
.set(error_container_state::Shown);
error_message_container_user.set("Error updating username. Usernames must be at least 4 characters long.".to_string());
}
}
}
} else {
dispatch_wasm.reduce_mut(|state| {
state.error_message = Option::from(
"API key not available for username update."
.to_string(),
)
});
}
} else {
dispatch_wasm.reduce_mut(|state| {
state.error_message = Option::from(
"API key not available for username update.".to_string(),
)
});
}
} else {
dispatch_wasm.reduce_mut(|state| {
state.error_message = Option::from(
"Server name not available for username update.".to_string(),
)
});
}
}
});
}
let error_container_email = error_container.clone();
let error_message_container_email = error_message_container.clone();
if !email.is_empty()
&& email
!= username_users
.iter()
.find(|u| Some(Some(u.userid)) == *selected_user_id)
.map(|u| u.email.clone())
.unwrap_or_default()
{
wasm_bindgen_futures::spawn_local({
let server_name_cloned = server_name.clone();
let api_key_cloned = api_key.clone();
let email_cloned = email.clone();
let selected_user_id_cloned = call_selected_user_id.clone();
let update_trigger_in_check = update_trigger_email.clone();
async move {
if let Some(server_name_unwrapped) = server_name_cloned {
if let Some(api_key_unwrapped) =
api_key_cloned.as_ref().and_then(|key| key.as_ref())
{
if let Some(user_id) = *selected_user_id_cloned {
let errors = validate_email(email_cloned.clone().as_str());
if errors.contains(&ValidationError::InvalidEmail) {
email_error.set(email_error_notice::Shown);
} else {
page_state_email.set(PageState::Hidden);
match call_set_email(
server_name_unwrapped,
api_key_unwrapped.clone(),
user_id.unwrap(),
email_cloned,
)
.await
{
Ok(_) => {
update_trigger_in_check
.set(!*update_trigger_in_check);
}
Err(_e) => {
error_container_email
.set(error_container_state::Shown);
error_message_container_email.set(format!("Error updating email. The formatting didn't look quite right").to_string());
}
}
}
} else {
console::log_1(
&"User ID not available for email update.".into(),
);
}
} else {
console::log_1(&"API key not available for email update.".into());
}
} else {
console::log_1(&"Server name not available for email update.".into());
}
}
});
}
let error_container_pass = error_container.clone();
let error_message_container_pass = error_message_container.clone();
if !new_password.is_empty() && new_password.to_string() != "password" {
wasm_bindgen_futures::spawn_local({
let server_name_cloned = server_name.clone();
let api_key_cloned = api_key.clone();
let new_password_cloned = new_password.clone();
let selected_user_id_cloned = (*call_selected_user_id).clone();
let update_trigger_in_check = update_trigger_pass.clone();
async move {
if let Some(server_name_unwrapped) = server_name_cloned {
if let Some(api_key_unwrapped) =
api_key_cloned.as_ref().and_then(|key| key.as_ref())
{
if let Some(Some(user_id)) = selected_user_id_cloned {
match encode_password(&new_password_cloned) {
Ok(hash_pw) => {
let errors = validate_email(
&new_password_cloned.clone().as_str(),
);
if errors.contains(&ValidationError::PasswordTooShort) {
password_error.set(password_error_notice::Shown);
} else {
page_state_pass.set(PageState::Hidden);
match call_set_password(
server_name_unwrapped,
api_key_unwrapped.clone(),
user_id,
hash_pw,
)
.await
{
Ok(_) => {
update_trigger_in_check
.set(!*update_trigger_in_check);
}
Err(_e) => {
error_container_pass
.set(error_container_state::Shown);
error_message_container_pass.set(format!("Error updating password. Passwords must be at least 6 characters long.").to_string());
}
}
}
}
Err(e) => {
console::log_1(
&format!("Password encoding failed: {:?}", e)
.into(),
);
}
}
} else {
console::log_1(
&"User ID not available for password update.".into(),
);
}
} else {
console::log_1(
&"API key not available for password update.".into(),
);
}
} else {
console::log_1(
&"Server name not available for password update.".into(),
);
}
}
});
}
let error_container_admin = error_container.clone();
let error_message_container_admin = error_message_container.clone();
if *admin_status == true {
wasm_bindgen_futures::spawn_local({
let server_name_cloned = server_name.clone();
let api_key_cloned = api_key.clone();
let admin_status_cloned = admin_status.clone();
let selected_user_id_cloned = (*call_selected_user_id).clone();
let update_trigger_in_check = update_trigger_true.clone();
let users = edit_users.clone();
async move {
let current_admin_status = users
.iter()
.find(|u| Some(Some(u.userid)) == selected_user_id_cloned)
.map(|u| u.isadmin == 1)
.unwrap_or(false);
if *admin_status_cloned != current_admin_status {
if let Some(server_name_unwrapped) = server_name_cloned {
if let Some(api_key_unwrapped) =
api_key_cloned.as_ref().and_then(|key| key.as_ref())
{
if let Some(Some(user_id)) = selected_user_id_cloned {
if *admin_edit_status_call == 0 {
page_state_true.set(PageState::Hidden);
}
// page_state_true.set(PageState::Hidden);
match call_set_isadmin(
server_name_unwrapped,
api_key_unwrapped.clone(),
user_id,
*admin_status_cloned,
)
.await
{
Ok(_) => {
update_trigger_in_check
.set(!*update_trigger_in_check);
}
Err(e) => {
error_container_admin
.set(error_container_state::Shown);
error_message_container_admin.set(
format!("Error updating admin status: {:?}", e)
.to_string(),
);
}
}
} else {
console::log_1(
&"User ID not available for admin status update."
.into(),
);
}
} else {
console::log_1(
&"API key not available for admin status update.".into(),
);
}
} else {
console::log_1(
&"Server name not available for admin status update.".into(),
);
}
}
}
});
}
if *admin_status == false {
wasm_bindgen_futures::spawn_local({
let server_name_cloned = server_name.clone();
let api_key_cloned = api_key.clone();
let admin_status_cloned = admin_status.clone();
let selected_user_id_cloned = (*call_selected_user_id).clone();
let update_trigger_in_check = update_trigger_false.clone();
let users = edit_users.clone();
async move {
let current_admin_status = users
.iter()
.find(|u| Some(Some(u.userid)) == selected_user_id_cloned)
.map(|u| u.isadmin == 1)
.unwrap_or(false);
if *admin_status_cloned != current_admin_status {
if let Some(server_name_unwrapped) = server_name_cloned {
if let Some(api_key_unwrapped) =
api_key_cloned.as_ref().and_then(|key| key.as_ref())
{
if let Some(Some(user_id)) = selected_user_id_cloned {
match call_check_admin(
server_name_unwrapped.clone(),
api_key_unwrapped.clone(),
user_id,
)
.await
{
Ok(final_admin) => {
if final_admin.final_admin == true {
error_container
.set(error_container_state::Shown);
error_message_container.set(format!("Unable to remove admin status from final administrator").to_string());
} else {
if *admin_edit_status_false == 1 {
page_state_false.set(PageState::Hidden);
}
// page_state_false.set(PageState::Hidden);
match call_set_isadmin(
server_name_unwrapped,
api_key_unwrapped.clone(),
user_id,
*admin_status_cloned,
)
.await
{
Ok(_) => {
update_trigger_in_check
.set(!*update_trigger_in_check);
}
Err(e) => {
error_container
.set(error_container_state::Shown);
error_message_container.set(
format!(
"Error updating admin status: {:?}",
e
)
.to_string(),
);
}
}
}
}
Err(e) => console::log_1(
&format!("Error checking admin status: {:?}", e)
.into(),
),
}
} else {
console::log_1(
&"User ID not available for admin status update."
.into(),
);
}
} else {
console::log_1(
&"API key not available for admin status update.".into(),
);
}
} else {
console::log_1(
&"Server name not available for admin status update.".into(),
);
}
}
}
});
}
// Handle admin status change if applicable
})
};
// Define the modal components
let edit_user_modal = html! {
<div id="create-user-modal" tabindex="-1" aria-hidden="true" class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full h-[calc(100%-1rem)] max-h-full bg-black bg-opacity-25" onclick={on_background_click.clone()}>
<div class="modal-container relative p-4 w-full max-w-md max-h-full rounded-lg shadow z-50" onclick={stop_propagation.clone()}>
<div class="modal-container relative rounded-lg shadow">
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t">
<h3 class="text-xl font-semibold">
{i18n.t("settings.edit_existing_user")}
</h3>
<button onclick={on_close_modal.clone()} class="end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">{i18n.t("settings.close_modal")}</span>
</button>
</div>
<p class="text-m font-semibold">
{i18n.t("settings.change_user_fields_instructions")}
</p>
<div class="p-4 md:p-5">
<form class="space-y-4" action="#">
<div>
<label for="username" class="block mb-2 text-sm font-medium">{i18n.t("settings.username")}</label>
<input oninput={on_username_change.clone()} value={new_username.to_string()} placeholder="pinepods_user1" type="text" id="username" name="username" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
{
match *username_error {
username_error_notice::Hidden => html! {},
username_error_notice::Shown => html! {<p class="text-red-500 text-xs italic">{i18n.t("settings.username_min_length")}</p>},
}
}
</div>
<div>
<label for="fullname" class="block mb-2 text-sm font-medium">{i18n.t("settings.full_name")}</label>
<input oninput={on_fullname_change} value={fullname.to_string()} placeholder="Pinepods User" type="text" id="fullname" name="fullname" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
</div>
<div>
<label for="email" class="block mb-2 text-sm font-medium">{i18n.t("settings.email")}</label>
<input oninput={on_email_change} value={email.to_string()} placeholder="user@pinepods.online" type="email" id="email" name="email" class="search-bar-input border text-sm rounded-lg block w-full p-2.5" required=true />
{
match *email_error {
email_error_notice::Hidden => html! {},
email_error_notice::Shown => html! {<p class="text-red-500 text-xs italic">{i18n.t("settings.invalid_email_address")}</p>},
}
}
</div>
<div>
<label for="password" class="block mb-2 text-sm font-medium">{i18n.t("settings.password")}</label>
<input
oninput={on_password_change.clone()}
value={new_password.to_string()} // Use state instead of static value
placeholder="my_S3creT_P@$$"
type="password"
id="password"
name="password"
class="search-bar-input border text-sm rounded-lg block w-full p-2.5"
required=true
/>
{
match *password_error {
password_error_notice::Hidden => html! {},
password_error_notice::Shown => html! {<p class="text-red-500 text-xs italic">{i18n.t("settings.password_min_length")}</p>},
}
}
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">
<label for="admin" class="mr-2 text-sm font-medium">{i18n.t("settings.admin_user_question")}</label>
<input
onchange={on_admin_change} // Changed from oninput to onchange
checked={*admin_status} // Use the state value instead of computing from users
type="checkbox"
id="admin"
name="admin"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white"
required=true
/>
</div>
<button
type="button"
onclick={on_delete_click}
class="delete-button bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
>
{i18n.t("settings.delete_user")}
</button>
</div>
<button type="submit" onclick={on_edit_submit} class="download-button w-full focus:ring-4 focus:outline-none font-medium rounded-lg text-sm px-5 py-2.5 text-center">{i18n.t("settings.submit")}</button>
</form>
</div>
</div>
</div>
</div>
};
html! {
<>
{
match *page_state {
PageState::Shown => create_user_modal,
PageState::Edit => edit_user_modal,
_ => html! {},
}
}
<div class="p-4">
<p class="item_container-text text-lg font-bold mb-4">{i18n.t("settings.user_management")}</p>
<p class="item_container-text text-md mb-4">{i18n.t("settings.user_management_description")}</p>
<button onclick={on_create_new_user} class="mt-4 settings-button font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
{i18n.t("settings.create_new_user")}
</button>
</div>
<div class="relative overflow-x-auto">
{
match *error_container {
error_container_state::Hidden => html! {},
error_container_state::Shown => html! {<p class="text-red-500 text-xs italic">{&*error_message_container}</p>},
}
}
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
<thead class="text-xs uppercase table-header">
<tr>
<th scope="col" class="px-6 py-3">{i18n.t("settings.user_id")}</th>
<th scope="col" class="px-6 py-3">{i18n.t("settings.fullname")}</th>
<th scope="col" class="px-6 py-3">{i18n.t("settings.email")}</th>
<th scope="col" class="px-6 py-3">{i18n.t("settings.username")}</th>
<th scope="col" class="px-6 py-3">{i18n.t("settings.admin_status")}</th>
</tr>
</thead>
<tbody>
{ for users.borrow().iter().map(|user| {
let user_row_copy = on_user_row_click.clone();
let user_row_click = user_row_copy(user.userid, user.isadmin);
{
html! {
<tr class="table-row border-b cursor-pointer" onclick={user_row_click}> // Adjust this line accordingly
<td class="px-6 py-4">{ user.userid }</td>
<td class="px-6 py-4">{ &user.fullname }</td>
<td class="px-6 py-4">{ &user.email }</td>
<td class="px-6 py-4">{ &user.username }</td>
<td class="px-6 py-4">{ if user.isadmin == 1 { i18n.t("settings.yes") } else { i18n.t("settings.no") } }</td>
</tr>
}
}
})}
</tbody>
</table>
</div>
</>
}
}