1
0
mirror of https://github.com/nitnelave/lldap.git synced 2023-04-12 14:25:13 +00:00

design improvements

This commit is contained in:
Lewis Larsen 2022-10-30 16:02:50 +00:00
parent e81c87f288
commit 730cee641a
17 changed files with 171 additions and 108 deletions

View File

@ -169,12 +169,13 @@ impl Component for AddGroupMemberComponent {
}
</Select>
</div>
<div class="col-sm-1">
<div class="col-3">
<button
class="btn btn-success"
class="btn btn-secondary"
disabled=self.selected_user.is_none() || self.common.is_task_running()
onclick=self.common.callback(|_| Msg::SubmitAddMember)>
{"Add"}
<i class="bi-person-plus me-2"></i>
{"Add to group"}
</button>
</div>
</div>

View File

@ -182,12 +182,13 @@ impl Component for AddUserToGroupComponent {
}
</Select>
</div>
<div class="col-sm-1">
<div class="col-sm-3">
<button
class="btn btn-success"
class="btn btn-secondary"
disabled=self.selected_group.is_none() || self.common.is_task_running()
onclick=self.common.callback(|_| Msg::SubmitAddGroup)>
{"Add"}
<i class="bi-person-plus me-2"></i>
{"Add to group"}
</button>
</div>
</div>

View File

@ -98,16 +98,18 @@ impl Component for App {
let link = self.link.clone();
let is_admin = self.is_admin();
html! {
<div class="container shadow-sm py-3">
<div>
{self.view_banner()}
<div class="container py-3 bg-kug">
<div class="row justify-content-center" style="padding-bottom: 80px;">
<div class="shadow-sm py-3" style="max-width: 1000px">
<div class="py-3" style="max-width: 1000px">
<Router<AppRoute>
render = Router::render(move |s| Self::dispatch_route(s, &link, is_admin))
/>
</div>
</div>
{self.view_footer()}
</div>
</div>
}
}
@ -171,7 +173,10 @@ impl App {
AppRoute::Index | AppRoute::ListUsers => html! {
<div>
<UserTable />
<NavButton classes="btn btn-primary" route=AppRoute::CreateUser>{"Create a user"}</NavButton>
<NavButton classes="btn btn-primary" route=AppRoute::CreateUser>
<i class="bi-person-plus me-2"></i>
{"Create a user"}
</NavButton>
</div>
},
AppRoute::CreateGroup => html! {
@ -180,7 +185,10 @@ impl App {
AppRoute::ListGroups => html! {
<div>
<GroupTable />
<NavButton classes="btn btn-primary" route=AppRoute::CreateGroup>{"Create a group"}</NavButton>
<NavButton classes="btn btn-primary" route=AppRoute::CreateGroup>
<i class="bi-plus-circle me-2"></i>
{"Create a group"}
</NavButton>
</div>
},
AppRoute::GroupDetails(group_id) => html! {
@ -203,11 +211,11 @@ impl App {
fn view_banner(&self) -> Html {
html! {
<header class="p-3 mb-4 border-bottom shadow-sm">
<header class="p-2 mb-3 border-bottom">
<div class="container">
<div class="d-flex flex-wrap align-items-center justify-content-center justify-content-lg-start">
<a href="/" class="d-flex align-items-center mb-2 mb-lg-0 me-md-5 text-dark text-decoration-none">
<h1>{"LLDAP"}</h1>
<a href="/" class="d-flex align-items-center mt-2 mb-lg-0 me-md-5 text-dark text-decoration-none">
<h2>{"LLDAP"}</h2>
</a>
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
@ -215,15 +223,17 @@ impl App {
<>
<li>
<Link
classes="nav-link px-2 link-dark h4"
classes="nav-link px-2 link-dark h6"
route=AppRoute::ListUsers>
<i class="bi-people me-2"></i>
{"Users"}
</Link>
</li>
<li>
<Link
classes="nav-link px-2 link-dark h4"
classes="nav-link px-2 link-dark h6"
route=AppRoute::ListGroups>
<i class="bi-collection me-2"></i>
{"Groups"}
</Link>
</li>
@ -246,6 +256,15 @@ impl App {
<path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
<path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/>
</svg>
// show user id
{if let Some((user_id, _)) = &self.user_info { html! {
<span class="ms-2">
{user_id}
</span>
}
} else { html!{} }
}
// end show user id
</a>
{if let Some((user_id, _)) = &self.user_info { html! {
<ul
@ -256,7 +275,7 @@ impl App {
<Link
classes="dropdown-item"
route=AppRoute::UserDetails(user_id.clone())>
{"Profile"}
{"View details"}
</Link>
</li>
<li><hr class="dropdown-divider" /></li>
@ -274,7 +293,7 @@ impl App {
fn view_footer(&self) -> Html {
html! {
<footer class="text-center text-muted fixed-bottom bg-light">
<footer class="text-center text-muted fixed-bottom bg-light py-2">
<div>
<span>{format!("LLDAP version {}", env!("CARGO_PKG_VERSION"))}</span>
</div>

View File

@ -220,6 +220,15 @@ impl Component for ChangePasswordForm {
type Field = yew_form::Field<FormModel>;
html! {
<>
<div class="mb-2 mt-2"><h5 class="fw-bold">{"Change password"}</h5></div>
{ if let Some(e) = &self.common.error {
html! {
<div class="alert alert-danger mt-3 mb-3">
{e.to_string() }
</div>
}
} else { html! {} }
}
<form
class="form">
{if !is_admin { html! {
@ -243,10 +252,12 @@ impl Component for ChangePasswordForm {
</div>
</div>
}} else { html! {} }}
<div class="form-group row">
<div class="form-group row mb-3">
<label for="new_password"
class="form-label col-sm-2 col-form-label">
{"New password*:"}
{"New Password"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-sm-10">
<Field
@ -263,10 +274,12 @@ impl Component for ChangePasswordForm {
</div>
</div>
</div>
<div class="form-group row">
<div class="form-group row mb-3">
<label for="confirm_password"
class="form-label col-sm-2 col-form-label">
{"Confirm password*:"}
{"Confirm Password"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-sm-10">
<Field
@ -283,31 +296,23 @@ impl Component for ChangePasswordForm {
</div>
</div>
</div>
<div class="form-group row">
<div class="form-group row justify-content-center">
<button
class="btn btn-primary col-sm-1 col-form-label"
class="btn btn-primary col-auto col-form-label"
type="submit"
disabled=self.common.is_task_running()
onclick=self.common.callback(|e: MouseEvent| {e.prevent_default(); Msg::Submit})>
{"Submit"}
<i class="bi-save me-2"></i>
{"Save changes"}
</button>
</div>
</form>
{ if let Some(e) = &self.common.error {
html! {
<div class="alert alert-danger">
{e.to_string() }
</div>
}
} else { html! {} }
}
<div>
<NavButton
classes="btn btn-primary"
classes="btn btn-secondary ms-2 col-auto col-form-label"
route=AppRoute::UserDetails(self.common.username.clone())>
<i class="bi-arrow-return-left me-2"></i>
{"Back"}
</NavButton>
</div>
</div>
</form>
</>
}
}

View File

@ -100,15 +100,17 @@ impl Component for CreateGroupForm {
type Field = yew_form::Field<CreateGroupModel>;
html! {
<div class="row justify-content-center">
<form class="form shadow-sm py-3" style="max-width: 636px">
<form class="form py-3" style="max-width: 636px">
<div class="row mb-3">
<h5 class="fw-bold">{"Create a group"}</h5>
</div>
<div class="form-group row mb-3">
<label for="groupname"
class="form-label col-4 col-form-label">
{"Group name*:"}
</label>
{"Group name"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-8">
<Field
form=&self.form
@ -129,7 +131,8 @@ impl Component for CreateGroupForm {
type="submit"
disabled=self.common.is_task_running()
onclick=self.common.callback(|e: MouseEvent| {e.prevent_default(); Msg::SubmitForm})>
{"Submit"}
<i class="bi-save me-2"></i>
{"Save changes"}
</button>
</div>
</form>

View File

@ -19,10 +19,10 @@ use yew_router::{
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "queries/create_user.graphql",
response_derives = "Debug",
custom_scalars_module = "crate::infra::graphql"
schema_path = "../schema.graphql",
query_path = "queries/create_user.graphql",
response_derives = "Debug",
custom_scalars_module = "crate::infra::graphql"
)]
pub struct CreateUser;
@ -43,8 +43,8 @@ pub struct CreateUserModel {
first_name: String,
last_name: String,
#[validate(custom(
function = "empty_or_long",
message = "Password should be longer than 8 characters (or left empty)"
function = "empty_or_long",
message = "Password should be longer than 8 characters (or left empty)"
))]
password: String,
#[validate(must_match(other = "password", message = "Passwords must match"))]
@ -194,14 +194,16 @@ impl Component for CreateUserForm {
type Field = yew_form::Field<CreateUserModel>;
html! {
<div class="row justify-content-center">
<form class="form shadow-sm py-3" style="max-width: 636px">
<form class="form py-3" style="max-width: 636px">
<div class="row mb-3">
<h5 class="fw-bold">{"Create a user"}</h5>
</div>
<div class="form-group row mb-3">
<label for="username"
class="form-label col-4 col-form-label">
{"User name*:"}
{"User name"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-8">
<Field
@ -220,7 +222,9 @@ impl Component for CreateUserForm {
<div class="form-group row mb-3">
<label for="email"
class="form-label col-4 col-form-label">
{"Email*:"}
{"Email"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-8">
<Field
@ -240,7 +244,9 @@ impl Component for CreateUserForm {
<div class="form-group row mb-3">
<label for="display-name"
class="form-label col-4 col-form-label">
{"Display name*:"}
{"Display name"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-8">
<Field
@ -340,7 +346,8 @@ impl Component for CreateUserForm {
disabled=self.common.is_task_running()
type="submit"
onclick=self.common.callback(|e: MouseEvent| {e.prevent_default(); Msg::SubmitForm})>
{"Submit"}
<i class="bi-save me-2"></i>
{"Save changes"}
</button>
</div>
</form>

View File

@ -154,12 +154,16 @@ impl DeleteGroup {
type="button"
class="btn btn-secondary"
onclick=self.common.callback(|_| Msg::DismissModal)>
<i class="bi-x-circle me-2"></i>
{"Cancel"}
</button>
<button
type="button"
onclick=self.common.callback(|_| Msg::ConfirmDeleteGroup)
class="btn btn-danger">{"Yes, I'm sure"}</button>
class="btn btn-danger">
<i class="bi-check-circle me-2"></i>
{"Yes, I'm sure"}
</button>
</div>
</div>
</div>

View File

@ -152,12 +152,16 @@ impl DeleteUser {
type="button"
class="btn btn-secondary"
onclick=self.common.callback(|_| Msg::DismissModal)>
<i class="bi-x-circle me-2"></i>
{"Cancel"}
</button>
<button
type="button"
onclick=self.common.callback(|_| Msg::ConfirmDeleteUser)
class="btn btn-danger">{"Yes, I'm sure"}</button>
class="btn btn-danger">
<i class="bi-check-circle me-2"></i>
{"Yes, I'm sure"}
</button>
</div>
</div>
</div>

View File

@ -133,7 +133,7 @@ impl GroupDetails {
<>
<h5 class="fw-bold">{"Members"}</h5>
<div class="table-responsive">
<table class="table table-striped">
<table class="table table-hover">
<thead>
<tr key="headerRow">
<th>{"User Id"}</th>
@ -145,7 +145,7 @@ impl GroupDetails {
{if g.users.is_empty() {
html! {
<tr key="EmptyRow">
<td>{"No members"}</td>
<td>{"There are currently no users in this group."}</td>
<td/>
</tr>
}

View File

@ -94,7 +94,7 @@ impl GroupTable {
let make_table = |groups: &Vec<Group>| {
html! {
<div class="table-responsive">
<table class="table table-striped">
<table class="table table-hover">
<thead>
<tr>
<th>{"Group name"}</th>

View File

@ -195,6 +195,7 @@ impl Component for LoginForm {
class="btn btn-primary"
disabled=self.common.is_task_running()
onclick=self.common.callback(|e: MouseEvent| {e.prevent_default(); Msg::Submit})>
<i class="bi-box-arrow-in-right me-2"/>
{"Login"}
</button>
<NavButton

View File

@ -113,6 +113,7 @@ impl Component for ResetPasswordStep1Form {
class="btn btn-primary"
disabled=self.common.is_task_running()
onclick=self.common.callback(|e: MouseEvent| {e.prevent_default(); Msg::Submit})>
<i class="bi-check-circle me-2"/>
{"Reset password"}
</button>
<NavButton
@ -127,7 +128,7 @@ impl Component for ResetPasswordStep1Form {
<div class="form-group">
{ if let Some(e) = &self.common.error {
html! {
<div class="alert alert-danger">
<div class="alert alert-danger mb-2">
{e.to_string() }
</div>
}

View File

@ -67,7 +67,7 @@ impl Component for Select {
fn view(&self) -> Html {
html! {
<select
<select class="form-select"
ref=self.node_ref.clone()
disabled=self.props.children.is_empty()
onchange=self.link.callback(SelectMsg::OnSelectChange)>

View File

@ -129,7 +129,7 @@ impl UserDetails {
<>
<h5 class="row m-3 fw-bold">{"Group memberships"}</h5>
<div class="table-responsive">
<table class="table table-striped">
<table class="table table-hover">
<thead>
<tr key="headerRow">
<th>{"Group"}</th>
@ -140,7 +140,7 @@ impl UserDetails {
{if u.groups.is_empty() {
html! {
<tr key="EmptyRow">
<td>{"Not member of any group"}</td>
<td>{"This user is not a member of any groups."}</td>
</tr>
}
} else {
@ -197,15 +197,19 @@ impl Component for UserDetails {
html! {
<>
<h3>{u.id.to_string()}</h3>
<UserDetailsForm
user=u.clone() />
<div class="row justify-content-center">
<div class="d-flex flex-row-reverse">
<NavButton
route=AppRoute::ChangePassword(u.id.clone())
classes="btn btn-primary col-auto">
{"Change password"}
classes="btn btn-secondary">
<i class="bi-key me-2"></i>
{"Modify password"}
</NavButton>
</div>
<div>
<h5 class="row m-3 fw-bold">{"User details"}</h5>
</div>
<UserDetailsForm
user=u.clone() />
{self.view_group_memberships(u)}
{self.view_add_group_button(u)}
{self.view_messages(error)}

View File

@ -52,11 +52,11 @@ pub struct UserModel {
/// The GraphQL query sent to the server to update the user details.
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "../schema.graphql",
query_path = "queries/update_user.graphql",
response_derives = "Debug",
variables_derives = "Clone,PartialEq,Eq",
custom_scalars_module = "crate::infra::graphql"
schema_path = "../schema.graphql",
query_path = "queries/update_user.graphql",
response_derives = "Debug",
variables_derives = "Clone,PartialEq,Eq",
custom_scalars_module = "crate::infra::graphql"
)]
pub struct UpdateUser;
@ -180,36 +180,39 @@ impl Component for UserDetailsForm {
html! {
<div class="py-3">
<form class="form">
<div class="form-group row mb-3">
<div class="form-group row mb-3">
<label for="userId"
class="form-label col-4 col-form-label">
{"User ID: "}
</label>
<div class="col-8">
<span id="userId" class="form-constrol-static"><b>{&self.common.user.id}</b></span>
<span id="userId" class="form-control-static"><i>{&self.common.user.id}</i></span>
</div>
</div>
<div class="form-group row mb-3">
<label for="creationDate"
class="form-label col-4 col-form-label">
{"Creation date: "}
</label>
<div class="col-8">
<span id="creationDate" class="form-control-static">{&self.common.user.creation_date.date().naive_local()}</span>
</div>
</div>
<div class="form-group row mb-3">
<div class="col-4 col-form-label">
<img
id="avatarDisplay"
src={format!("data:image/jpeg;base64, {}", avatar_string)}
style="max-height:128px;max-width:128px;height:auto;width:auto;"
alt="Avatar" />
</div>
<label for="uuid"
class="form-label col-4 col-form-label">
{"UUID: "}
</label>
<div class="col-8">
<input
class="form-control"
id="avatarInput"
type="file"
accept="image/jpeg"
oninput=self.common.callback(|_| Msg::Update) />
<span id="creationDate" class="form-control-static">{&self.common.user.uuid}</span>
</div>
</div>
<div class="form-group row mb-3">
<label for="email"
class="form-label col-4 col-form-label">
{"Email*: "}
{"Email"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-8">
<Field
@ -228,7 +231,9 @@ impl Component for UserDetailsForm {
<div class="form-group row mb-3">
<label for="display_name"
class="form-label col-4 col-form-label">
{"Display Name*: "}
{"Display Name"}
<span class="text-danger">{"*"}</span>
{":"}
</label>
<div class="col-8">
<Field
@ -278,31 +283,39 @@ impl Component for UserDetailsForm {
</div>
</div>
</div>
<div class="form-group row mb-3">
<label for="creationDate"
class="form-label col-4 col-form-label">
{"Creation date: "}
<div class="form-group row align-items-center mb-3">
<label for="avatar"
class="form-label col-4 col-form-label">
{"Avatar: "}
</label>
<div class="col-8">
<span id="creationDate" class="form-constrol-static">{&self.common.user.creation_date.date().naive_local()}</span>
</div>
<div class="row align-items-center">
<div class="col-8">
<input
class="form-control"
id="avatarInput"
type="file"
accept="image/jpeg"
oninput=self.common.callback(|_| Msg::Update) />
</div>
<div class="col-4">
<img
id="avatarDisplay"
src={format!("data:image/jpeg;base64, {}", avatar_string)}
style="max-height:128px;max-width:128px;height:auto;width:auto;"
alt="Avatar" />
</div>
</div>
</div>
</div>
<div class="form-group row mb-3">
<label for="uuid"
class="form-label col-4 col-form-label">
{"UUID: "}
</label>
<div class="col-8">
<span id="creationDate" class="form-constrol-static">{&self.common.user.uuid}</span>
</div>
</div>
<div class="form-group row justify-content-center">
<div class="form-group row justify-content-center mt-3">
<button
type="submit"
class="btn btn-primary col-auto col-form-label"
disabled=self.common.is_task_running()
onclick=self.common.callback(|e: MouseEvent| {e.prevent_default(); Msg::SubmitClicked})>
{"Update"}
<i class="bi-save me-2"></i>
{"Save changes"}
</button>
</div>
</form>
@ -315,7 +328,7 @@ impl Component for UserDetailsForm {
} else { html! {} }
}
<div hidden=!self.just_updated>
<span>{"User successfully updated!"}</span>
<div class="alert alert-success mt-4">{"User successfully updated!"}</div>
</div>
</div>
}

View File

@ -100,7 +100,7 @@ impl UserTable {
let make_table = |users: &Vec<User>| {
html! {
<div class="table-responsive">
<table class="table table-striped">
<table class="table table-hover">
<thead>
<tr>
<th>{"User ID"}</th>

View File

@ -1,4 +1,4 @@
header h1 {
header h2 {
font-family: 'Bebas Neue', cursive;
}