Skip to content

Commit

Permalink
feat(admin)!: enable or disable users (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
javierEd authored Feb 19, 2025
1 parent 9cba6e0 commit b770122
Show file tree
Hide file tree
Showing 51 changed files with 869 additions and 142 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions locales/fluent/en/mailer.ftl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
confirm-your-email = Confirm your email
if-not-please-contact-us-at-the-following-email-address = If not, please contact us at the following email address
if-you-have-any-questions-please-contact-us-at-the-following-email-address = If you have any questions, please contact us at the following email address
if-you-recognize-this-action-you-can-ignore-this-message = If you recognize this action, you can ignore this message
invitation-code = Invitation code
confirmation-code = Confirmation code
Expand All @@ -9,6 +10,8 @@ reset-your-password = Reset your password
someone-has-started-a-user-session-with-your-account = Someone has started a user session with your account
use-this-code-to-action = Use this code to {$action}
use-this-code-to-create-your-account = Use this code to create your account
we-regret-to-inform-you-that-we-have-locked-your-user-account-due-to-non-compliance-of-our-terms = We regret to inform you that we have locked your user account due to non-compliance of our terms
we-are-glad-to-inform-you-that-we-have-enabled-your-user-account = We are glad to inform you that we have enabled your user account
we-regret-to-inform-you-that-we-have-disabled-your-user-account = We regret to inform you that we have disabled your user account
welcome-to-title = Welcome to {$title}!
your-user-account-has-been-locked = Your user account has been locked
your-user-account-has-been-disabled = Your user account has been disabled
your-user-account-has-been-enabled = Your user account has been enabled
7 changes: 5 additions & 2 deletions locales/fluent/es/mailer.ftl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
confirm-your-email = Confirmar tu correo electrónico
if-not-please-contact-us-at-the-following-email-address = Si no, por favor contáctenos a la siguiente dirección de correo electrónico
if-you-have-any-questions-please-contact-us-at-the-following-email-address = Si tienes alguna pregunta, por favor contáctenos a la siguiente dirección de correo electrónico
if-you-recognize-this-action-you-can-ignore-this-message = Si reconoces esta acción, puedes ignorar este mensaje
invitation-code = Código de invitación
confirmation-code = Código de confirmación
Expand All @@ -9,6 +10,8 @@ reset-your-password = Reiniciar tu contraseña
someone-has-started-a-user-session-with-your-account = Alguien ha iniciado una sesión de usuario con tu cuenta
use-this-code-to-action = Use este código para {$action}
use-this-code-to-create-your-account = Use este código para crear tu cuenta
we-regret-to-inform-you-that-we-have-locked-your-user-account-due-to-non-compliance-of-our-terms = Lamentamos informarle que hemos bloqueado su cuenta de usuario debido al incumplimiento de nuestros términos
we-are-glad-to-inform-you-that-we-have-enabled-your-user-account = Estamos encantados de informarte que hemos habilitado tu cuenta de usuario
we-regret-to-inform-you-that-we-have-disabled-your-user-account = Lamentamos informarle que hemos deshabilitado tu cuenta de usuario
welcome-to-title = ¡Bienvenido a {$title}!
your-user-account-has-been-locked = Tu cuenta de usuario ha sido bloqueada
your-user-account-has-been-disabled = Tu cuenta de usuario ha sido deshabilitada
your-user-account-has-been-enabled = Tu cuenta de usuario ha sido habilitada
5 changes: 5 additions & 0 deletions locales/leptos/en/admin.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
admin: Admin
are_you_sure_you_want_to_disable_this_user: Are you sure you want to disable this user?
are_you_sure_you_want_to_enable_this_user: Are you sure you want to enable this user?
disable: Disable
enable: Enable
users: Users
1 change: 1 addition & 0 deletions locales/leptos/en/shared.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ count_weeks_ago: "{{count}} weeks ago"
count_years_ago: "{{count}} years ago"
country: Country
copy_url: Copy URL
disabled: Disabled
edited: edited
email: Email
failed_to_update_password: Failed to update password
Expand Down
5 changes: 5 additions & 0 deletions locales/leptos/es/admin.yaml
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
admin: Admin
are_you_sure_you_want_to_disable_this_user: ¿Estás seguro de que quieres deshabilitar este usuario?
are_you_sure_you_want_to_enable_this_user: ¿Estás seguro de que quieres habilitar este usuario?
disable: Deshabilitar
enable: Habilitar
users: Usuarios
1 change: 1 addition & 0 deletions locales/leptos/es/shared.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ count_weeks_ago: hace {{count}} semanas
count_years_ago: hace {{count}} años
country: País
copy_url: Copiar URL
disabled: Deshabilitado
edited: editado
email: Correo electrónico
failed_to_update_password: Error al actualizar contraseña
Expand Down
4 changes: 4 additions & 0 deletions mango3-admin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
console_error_panic_hook = { workspace = true }
leptos = { workspace = true }
leptos_axum = { workspace = true, optional = true }
leptos_i18n = { workspace = true }
leptos_meta = { workspace = true }
leptos_router = { workspace = true }
tokio = { workspace = true, optional = true }
uuid = { workspace = true, optional = true }
wasm-bindgen = { workspace = true }
mango3-core = { workspace = true, optional = true }
mango3-leptos-utils = { workspace = true }
Expand All @@ -21,6 +23,8 @@ mango3-leptos-utils = { workspace = true }
hydrate = ["leptos/hydrate", "leptos_i18n/hydrate", "mango3-leptos-utils/hydrate"]
ssr = [
"dep:tokio",
"dep:leptos_axum",
"dep:uuid",
"dep:mango3-core",
"leptos/ssr",
"leptos_i18n/axum",
Expand Down
9 changes: 6 additions & 3 deletions mango3-admin/src/app.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use leptos::prelude::*;
use leptos_meta::{provide_meta_context, Meta};
use leptos_router::components::{Route, Router, Routes};
use leptos_router::components::{ParentRoute, Route, Router, Routes};
use leptos_router::StaticSegment;

use mango3_leptos_utils::async_t_string;
Expand All @@ -11,7 +11,7 @@ use mango3_leptos_utils::i18n::use_i18n;
use mango3_leptos_utils::pages::NotFoundPage;
use mango3_leptos_utils::utils::ToSignalTrait;

use crate::pages::IndexPage;
use crate::pages::{IndexPage, IndexParentPage, UsersPage};

#[component]
pub fn App() -> impl IntoView {
Expand Down Expand Up @@ -39,7 +39,10 @@ pub fn App() -> impl IntoView {

<main class="flex flex-col grow md:m-6 m-4">
<Routes fallback=NotFoundPage>
<Route path=StaticSegment("") view=IndexPage />
<ParentRoute path=StaticSegment("") view=IndexParentPage>
<Route path=StaticSegment("") view=IndexPage />
<Route path=StaticSegment("users") view=UsersPage />
</ParentRoute>
</Routes>
</main>

Expand Down
7 changes: 4 additions & 3 deletions mango3-admin/src/pages/index_page.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use leptos::prelude::*;

use mango3_leptos_utils::async_t_string;
use mango3_leptos_utils::i18n::{t, use_i18n};
use mango3_leptos_utils::i18n::use_i18n;
use mango3_leptos_utils::utils::ToSignalTrait;

use crate::components::AdminPageContainer;

#[component]
pub fn IndexPage() -> impl IntoView {
let i18n = use_i18n();
let text_title = async_t_string!(i18n, shared.home).to_signal();

view! {
<AdminPageContainer title=async_t_string!(i18n, shared.home).to_signal()>
<h1 class="h1">{t!(i18n, shared.home)}</h1>
<AdminPageContainer title=text_title>
<h1 class="h1">{move || text_title.get()}</h1>
</AdminPageContainer>
}
}
25 changes: 25 additions & 0 deletions mango3-admin/src/pages/index_parent_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use leptos::prelude::*;
use leptos_router::components::Outlet;

use mango3_leptos_utils::async_t_string;
use mango3_leptos_utils::components::{Menu, MenuItem};
use mango3_leptos_utils::i18n::use_i18n;
use mango3_leptos_utils::icons::{HomeOutlined, UsersOutlined};

#[component]
pub fn IndexParentPage() -> impl IntoView {
let i18n = use_i18n();

view! {
<div class="flex grow gap-4">
<Menu>
<MenuItem href="/" icon=HomeOutlined label=async_t_string!(i18n, shared.home) />
<MenuItem href="/users" icon=UsersOutlined label=async_t_string!(i18n, admin.users) />
</Menu>

<div class="grow ml-4">
<Outlet />
</div>
</div>
}
}
4 changes: 4 additions & 0 deletions mango3-admin/src/pages/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
mod index_page;
mod index_parent_page;
mod users_page;

pub use index_page::IndexPage;
pub use index_parent_page::IndexParentPage;
pub use users_page::UsersPage;
136 changes: 136 additions & 0 deletions mango3-admin/src/pages/users_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use leptos::either::EitherOf3;
use leptos::prelude::*;

use mango3_leptos_utils::async_t_string;
use mango3_leptos_utils::components::{
ConfirmationDialog, InfiniteScroll, InfiniteScrollControllerTrait, InfiniteScrollLocalResourceController, UserCard,
UserTag,
};
use mango3_leptos_utils::context::use_basic_config;
use mango3_leptos_utils::i18n::{t, use_i18n};
use mango3_leptos_utils::models::{ActionFormResp, UserPreviewResp};
use mango3_leptos_utils::utils::ToSignalTrait;

use crate::components::AdminPageContainer;
use crate::server_functions::{get_users, AttemptToDisableUser, AttemptToEnableUser};

#[component]
pub fn UsersPage() -> impl IntoView {
let i18n = use_i18n();
let basic_config = use_basic_config();
let controller = InfiniteScrollLocalResourceController::new(|after| {
LocalResource::new(move || async move { get_users(after.get()).await })
});
let text_title = async_t_string!(i18n, admin.users).to_signal();
let server_action_disable_user = ServerAction::<AttemptToDisableUser>::new();
let action_value_disable_user = server_action_disable_user.value();
let server_action_enable_user = ServerAction::<AttemptToEnableUser>::new();
let action_value_enable_user = server_action_enable_user.value();
let disable_user = RwSignal::<Option<UserPreviewResp>>::new(None);
let enable_user = RwSignal::<Option<UserPreviewResp>>::new(None);
let show_disable_confirmation = RwSignal::new(false);
let show_enable_confirmation = RwSignal::new(false);

Effect::new({
let controller = controller.clone();
move || {
let response = ActionFormResp::from(action_value_disable_user);

if let Some(true) = response.success {
controller.clear_and_refetch();
enable_user.set(None);
}
}
});

Effect::new({
let controller = controller.clone();
move || {
let response = ActionFormResp::from(action_value_enable_user);

if let Some(true) = response.success {
controller.clear_and_refetch();
enable_user.set(None);
}
}
});

view! {
<AdminPageContainer title=text_title>
<h1 class="h1">{move || text_title.get()}</h1>

<section class="max-w-[720px] w-full mx-auto">
<ConfirmationDialog
is_open=show_disable_confirmation
on_accept=move || {
let user = disable_user.get().unwrap();
server_action_disable_user
.dispatch(AttemptToDisableUser {
id: user.id.clone(),
});
}
>
<div>{t!(i18n, admin.are_you_sure_you_want_to_disable_this_user)}</div>

{move || disable_user.get().map(|user| view! { <UserTag class="justify-center my-3" user=user /> })}
</ConfirmationDialog>

<ConfirmationDialog
is_open=show_enable_confirmation
on_accept=move || {
let user = enable_user.get().unwrap();
server_action_enable_user
.dispatch(AttemptToEnableUser {
id: user.id.clone(),
});
}
>
<div>{t!(i18n, admin.are_you_sure_you_want_to_enable_this_user)}</div>

{move || enable_user.get().map(|user| view! { <UserTag class="justify-center my-3" user=user /> })}
</ConfirmationDialog>

<InfiniteScroll controller=controller key=|user: &UserPreviewResp| user.id.clone() let:user>
<UserCard
user=user.clone()
hashtags_base_url=basic_config.home_url.clone()
actions=move || {
let user = user.clone();
if user.is_disabled {
EitherOf3::A(
view! {
<button
class="btn btn-ghost font-bold"
on:click=move |_| {
enable_user.set(Some(user.clone()));
show_enable_confirmation.set(true);
}
>
{t!(i18n, admin.enable)}
</button>
},
)
} else if user.role == "user" {
EitherOf3::B(
view! {
<button
class="btn btn-ghost font-bold"
on:click=move |_| {
disable_user.set(Some(user.clone()));
show_disable_confirmation.set(true);
}
>
{t!(i18n, admin.disable)}
</button>
},
)
} else {
EitherOf3::C(())
}
}
/>
</InfiniteScroll>
</section>
</AdminPageContainer>
}
}
Loading

0 comments on commit b770122

Please sign in to comment.