Skip to content

Commit

Permalink
UI: Profile view improvements (#363)
Browse files Browse the repository at this point in the history
  • Loading branch information
psrok1 committed May 7, 2021
1 parent ec39e52 commit 839d0f0
Show file tree
Hide file tree
Showing 8 changed files with 245 additions and 144 deletions.
4 changes: 2 additions & 2 deletions mwdb/web/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import UserPasswordRecover from "./components/UserPasswordRecover";
import ShowPendingUsers from "./components/ShowPendingUsers";
import Docs from "./components/Docs";
import RemoteViews from "./components/Remote/RemoteViews";
import ProfileViews from "./components/Profile/ProfileViews";
import ProfileView from "./components/Profile/ProfileView";

import { library } from "@fortawesome/fontawesome-svg-core";
import {
Expand Down Expand Up @@ -226,7 +226,7 @@ export default function App() {
<SettingsView />
</ProtectedRoute>
<ProtectedRoute path={["/profile/user/:user", "/profile"]}>
<ProfileViews />
<ProfileView />
</ProtectedRoute>
{fromPlugin("routes")}
<DefaultRoute />
Expand Down
10 changes: 9 additions & 1 deletion mwdb/web/src/commons/ui/GroupBadge.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import React from "react";
import { Link } from "react-router-dom";

import { faUser, faUsers, faGlobe } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

export default function GroupBadge({ group, clickable }) {
const icon = group.private
? faUser
: group.name === "public"
? faGlobe
: faUsers;
const badge = (
<span
className={`badge badge-${group.private ? "primary" : "secondary"}`}
>
{group.name}
<FontAwesomeIcon icon={icon} /> {group.name}
</span>
);

Expand Down
108 changes: 108 additions & 0 deletions mwdb/web/src/components/Profile/ProfileView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React, { useCallback, useContext, useEffect, useState } from "react";
import {
NavLink,
Route,
Switch,
useHistory,
useParams,
} from "react-router-dom";

import { faUserCog } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

import api from "@mwdb-web/commons/api";
import { AuthContext } from "@mwdb-web/commons/auth";
import { View, getErrorMessage } from "@mwdb-web/commons/ui";

import ProfileDetails from "./Views/ProfileDetails";
import ProfileAPIKeys from "./Views/ProfileAPIKeys";
import ProfileCapabilities from "./Views/ProfileCapabilities";
import ProfileResetPassword from "./Views/ProfileResetPassword";
import ProfileGroup from "./Views/ProfileGroup";
import ProfileGroups from "./Views/ProfileGroups";

function ProfileNav() {
return (
<div>
<strong>
<FontAwesomeIcon icon={faUserCog} /> Profile
</strong>
<div className="nav flex-column">
<NavLink exact to="/profile" className="nav-link">
Profile details
</NavLink>
<NavLink exact to="/profile/capabilities" className="nav-link">
Capabilities
</NavLink>
<NavLink exact to="/profile/groups" className="nav-link">
Groups
</NavLink>
<NavLink exact to="/profile/api-keys" className="nav-link">
API keys
</NavLink>
</div>
</div>
);
}

export default function ProfileView() {
const auth = useContext(AuthContext);
const history = useHistory();
const user = useParams().user || auth.user.login;
const [profile, setProfile] = useState();

async function updateProfile() {
try {
const response = await api.getUserProfile(user);
setProfile(response.data);
} catch (error) {
history.push({
pathname: "/profile",
state: { error: getErrorMessage(error) },
});
}
}

const getProfile = useCallback(updateProfile, [user]);

useEffect(() => {
getProfile();
}, [getProfile]);

if (!profile || profile.login !== user) return [];

return (
<View ident="profile" fluid>
<div className="row">
<div className="col-2">
<ProfileNav />
</div>
<div className="col-8">
<Switch>
<Route exact path={["/profile", "/profile/user/:user"]}>
<ProfileDetails profile={profile} />
</Route>
<Route exact path="/profile/group/:group">
<ProfileGroup profile={profile} />
</Route>
<Route exact path="/profile/groups">
<ProfileGroups profile={profile} />
</Route>
<Route exact path="/profile/capabilities">
<ProfileCapabilities profile={profile} />
</Route>
<Route exact path="/profile/api-keys">
<ProfileAPIKeys
profile={profile}
updateProfile={updateProfile}
/>
</Route>
<Route exact path="/profile/reset-password">
<ProfileResetPassword profile={profile} />
</Route>
</Switch>
</div>
</div>
</View>
);
}
105 changes: 0 additions & 105 deletions mwdb/web/src/components/Profile/ProfileViews.js

This file was deleted.

6 changes: 6 additions & 0 deletions mwdb/web/src/components/Profile/Views/ProfileAPIKeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ export default function ProfileAPIKeys({ profile, updateProfile }) {

return (
<div className="container">
<h2>API keys</h2>
<p className="lead">
API keys are just an alternative to password-based
authentication. They are recommended to use for scripts and
other automation instead of plaintext passwords.
</p>
{!profile.api_keys.length ? (
<p>
<i>
Expand Down
3 changes: 3 additions & 0 deletions mwdb/web/src/components/Profile/Views/ProfileCapabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ function CapabilitiesTable({ profile }) {
{capabilitiesList[cap] || "(no description)"}
</div>
<div>
<small className="text-muted">Got from:</small>
{profile.groups
.filter((group) =>
group.capabilities.includes(cap)
Expand All @@ -37,6 +38,8 @@ function CapabilitiesTable({ profile }) {
export default function ProfileCapabilities({ profile }) {
return (
<div className="container">
<h2>Capabilities</h2>
<p className="lead">Here is the list of account superpowers:</p>
<CapabilitiesTable profile={profile} />
</div>
);
Expand Down
50 changes: 14 additions & 36 deletions mwdb/web/src/components/Profile/Views/ProfileDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export default function ProfileDetails({ profile }) {
const isCurrentUser = profile.login === auth.user.login;

return (
<div>
<h4>Profile details</h4>
<div className="container">
<h2>Profile details</h2>
<table className="table table-striped table-bordered wrap-table">
<tbody>
<ProfileItem label="Login" value={profile.login} />
Expand All @@ -44,45 +44,23 @@ export default function ProfileDetails({ profile }) {
>
<DateString date={profile.set_password_on} />
</ProfileItem>
<ProfileItem
label="Groups"
value={profile.groups && profile.groups.length}
>
{profile.groups &&
profile.groups
.filter((group) => !group.private)
.map((group) => (
<Link to={`/profile/group/${group.name}`}>
<span className="badge badge-secondary">
{group.name}
</span>
</Link>
))}
</ProfileItem>
</tbody>
</table>
<b>Actions:</b>
<ul className="nav flex-column">
<li className="nav-item">
<ShowIf condition={isCurrentUser}>
<Link className="nav-link" to="/profile/capabilities">
Check your capabilities
<ShowIf
condition={
isCurrentUser &&
auth.hasCapability(Capability.personalize)
}
>
<Link className="nav-link" to="/profile/api-keys">
Set up API keys
</Link>
<Link className="nav-link" to="/profile/reset-password">
Reset password
</Link>
<ShowIf
condition={auth.hasCapability(
Capability.personalize
)}
>
<Link className="nav-link" to="/profile/api-keys">
Set up API keys
</Link>
<Link
className="nav-link"
to="/profile/reset-password"
>
Reset password
</Link>
</ShowIf>
</ShowIf>
<Link
className="nav-link"
Expand All @@ -100,7 +78,7 @@ export default function ProfileDetails({ profile }) {
>
<Link
className="nav-link"
to={`/user/${profile.login}`}
to={`/admin/user/${profile.login}`}
>
<FontAwesomeIcon icon={faUsersCog} />
User settings
Expand Down

0 comments on commit 839d0f0

Please sign in to comment.