use std::rc::Rc;
use crate::components::common::bordered_panel::*;
use crate::components::common::horizontal_header_table::*;
use crate::components::common::loading::*;
use crate::components::common::titled_panel::*;
use crate::components::common::unadorned_panel::*;
use crate::components::common::vertical_header_table::*;
use crate::components::common::Datum;
use crate::utils::DATETIME_FORMAT;
use crate::MainRoute;
use crate::{
app_types::AppSiteConnection,
components::{common::main_layout::*, pages::error::handle_error},
};
use bounce::{use_atom, UseAtomHandle};
use dialtone_common::rest::users::web_user::LastLoginData;
use dialtone_common::rest::users::web_user::LastSeenData;
use dialtone_common::rest::users::web_user::WebUser;
use dialtone_reqwest::api_v1::users::get_user::get_user;
use yew::prelude::*;
use yew_router::prelude::*;
#[derive(Properties, PartialEq)]
pub struct AccountProps {
pub acct: String,
}
struct AccountPageState {
pub sc_atom: UseAtomHandle<AppSiteConnection>,
pub webuser: UseStateHandle<Option<WebUser>>,
pub acct: String,
pub history: AnyHistory,
}
#[function_component(Account)]
pub fn account(AccountProps { acct }: &AccountProps) -> Html {
let page_state = Rc::new(AccountPageState {
sc_atom: use_atom::<AppSiteConnection>(),
webuser: use_state(|| None),
acct: acct.clone(),
history: use_history().unwrap(),
});
use_effect_with_deps(
{
let page_state = Rc::clone(&page_state);
move |_| {
let page_state = Rc::clone(&page_state);
wasm_bindgen_futures::spawn_local(async move {
let page_state = Rc::clone(&page_state);
let webuser = get_user(&page_state.sc_atom.0, Some(&page_state.acct)).await;
match webuser {
Ok(webuser) => page_state.webuser.set(Some(webuser)),
Err(err) => handle_error(&page_state.history, err),
}
});
|| ()
}
},
(),
);
html! {
<MainLayout>
if let Some(webuser) = (*page_state.webuser).clone() {
<div class="asymmetrical_split_page">
<div class="asymmetrical_col_left">
<TitledPanel title="Actors">
<div class="link_nav">
<div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Persons"}</Link<MainRoute>></div>
<div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Groups"}</Link<MainRoute>></div>
<div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Services"}</Link<MainRoute>></div>
</div>
</TitledPanel>
<TitledPanel title="Account">
<div class="link_nav">
<div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Preferences"}</Link<MainRoute>></div>
<div><Link<MainRoute> classes={classes!("in_page_action")} to={MainRoute::NotYetImplemented}>{"Password"}</Link<MainRoute>></div>
</div>
</TitledPanel>
</div>
<div class="asymmetrical_col_right">
<BorderedPanel>
<div style="text-align:center">
<span class="info_text">{page_state.acct.clone()}</span>
</div>
</BorderedPanel>
<AccountInfoPanel webuser={webuser.clone()}>
</AccountInfoPanel>
<LoginInfoPanel logins={webuser.last_login_data}>
</LoginInfoPanel>
<PingsInfoPanel pings={webuser.last_seen_data}>
</PingsInfoPanel>
</div>
</div>
} else {
<Loading/>
}
</MainLayout>
}
}
#[derive(Properties, Debug, PartialEq)]
struct AccountInfoPanelProps {
pub webuser: WebUser,
}
#[function_component(AccountInfoPanel)]
fn account_info_panel(AccountInfoPanelProps { webuser }: &AccountInfoPanelProps) -> Html {
let rows = vec![
VerticalHeaderTableRow {
header: "Status".to_string(),
data: vec![Datum::new(webuser.status.to_string())],
},
VerticalHeaderTableRow {
header: "Created".to_string(),
data: vec![Datum::new_mono(
webuser.created_at.format(DATETIME_FORMAT).to_string(),
)],
},
VerticalHeaderTableRow {
header: "Last Modified".to_string(),
data: vec![Datum::new_mono(
webuser.modified_at.format(DATETIME_FORMAT).to_string(),
)],
},
];
html! {
<UnadornedPanel>
<div class="centered_col_container">
<VerticalHeaderTable rows={rows}>
</VerticalHeaderTable>
</div>
</UnadornedPanel>
}
}
#[derive(Properties, PartialEq, Debug)]
struct LoginInfoPanelProps {
pub logins: Vec<LastLoginData>,
}
#[function_component(LoginInfoPanel)]
fn login_info_panel(LoginInfoPanelProps { logins }: &LoginInfoPanelProps) -> Html {
let headers = vec!["From".to_string(), "When".to_string(), "Login".to_string()];
let rows = logins
.iter()
.map(|login| {
vec![
Datum::new(login.from.to_string()),
Datum::new_mono(login.at.format(DATETIME_FORMAT).to_string()),
Datum::new(
if login.success {
"Succeeded".to_string()
} else {
"Failed".to_string()
}
),
]
})
.collect::<Vec<Vec<Datum>>>();
html! {
<TitledPanel title="Logins">
<div class="centered_col_container">
<HorizontalHeaderTable headers={headers} rows={rows}>
</HorizontalHeaderTable>
</div>
</TitledPanel>
}
}
#[derive(Properties, PartialEq, Clone)]
struct PingsInfoPanelProps {
pub pings: Vec<LastSeenData>,
}
#[function_component(PingsInfoPanel)]
fn pings_info_panel(PingsInfoPanelProps { pings }: &PingsInfoPanelProps) -> Html {
let headers = vec!["From".to_string(), "When".to_string()];
let rows = pings
.iter()
.map(|ping| {
vec![
Datum::new(ping.from.to_string()),
Datum::new_mono(ping.at.format(DATETIME_FORMAT).to_string()),
]
})
.collect::<Vec<Vec<Datum>>>();
html! {
<TitledPanel title="Pings">
<div class="centered_col_container">
<HorizontalHeaderTable headers={headers} rows={rows}>
</HorizontalHeaderTable>
</div>
</TitledPanel>
}
}