Skip to content

Commit

Permalink
Implemented user context in navbar
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed Aug 31, 2021
1 parent 57eae7b commit 66a29b8
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 40 deletions.
1 change: 1 addition & 0 deletions backend/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

action.dashboard=Dashboard
action.logout=Logout
action.logged-in=Logged in as {username} ({group})
action.add=Add
action.add-new=Add New
action.add-new-provider=Add a new metadata provider
Expand Down
72 changes: 47 additions & 25 deletions ui/src/app/core/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,65 @@ import Navbar from 'react-bootstrap/Navbar';
import Dropdown from 'react-bootstrap/Dropdown';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTh, faSignOutAlt, faPlusCircle, faCube, faCubes, faUsersCog } from '@fortawesome/free-solid-svg-icons';
import { faTh, faSignOutAlt, faPlusCircle, faCube, faCubes, faUsersCog, faUser, faSpinner } from '@fortawesome/free-solid-svg-icons';

import Translate from '../../i18n/components/translate';
import { useTranslator } from '../../i18n/hooks';

import { brand } from '../../app.brand';
import { useIsAdmin } from '../user/UserContext';
import { useCurrentUser, useCurrentUserLoading, useIsAdmin } from '../user/UserContext';

export function Header () {

const translator = useTranslator();

const isAdmin = useIsAdmin();

const { username, groupId } = useCurrentUser();
const loading = useCurrentUserLoading();

return (
<Navbar expand="md" fixed="top" bg="">
<Navbar.Brand href={brand.logo.link.url} title={brand.logo.link.description}>
<img src={brand.logo.small} width="30" height="30" className="d-inline-block align-top" alt={brand.logo.alt} />
<span className="d-lg-inline d-none"><Translate value={brand.logo.link.label}></Translate></span>
</Navbar.Brand>
<Navbar.Text><Translate value={brand.header.title}></Translate></Navbar.Text>
{loading ?
<div className="d-flex justify-content-end flex-fill">
<FontAwesomeIcon icon={faSpinner} spin={true} pulse={true} size="lg" />
</div>
:
<>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto align-items-center" navbar>
<div className="border-md-right px-2">
<Link to="/dashboard" className="btn btn-link btn-sm" aria-label="Metadata Dashboard">
<i className="fa fa-th fa-fw" aria-hidden="true"></i>
<FontAwesomeIcon icon={faTh} className="mr-2" />
<Translate value="action.dashboard">Dashboard</Translate>
</Link>
</div>
<Dropdown className="border-md-right px-2" id="basic-nav-dropdown">
<Dropdown.Toggle variant="link" id="dropdown-basic" size="sm">
<FontAwesomeIcon icon={faPlusCircle} className="mr-2" />
<Translate value={'action.add-new'} />
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item as={Link} to="/metadata/source/new" className="text-primary py-2">
<FontAwesomeIcon icon={faCube} className="mr-2" />
<Translate value="action.add-new-source" />
</Dropdown.Item>
{isAdmin && <Dropdown.Item as={Link} to="/metadata/provider/new" className="text-primary py-2">
<FontAwesomeIcon icon={faCubes} className="mr-2" />
<Translate value="action.add-new-provider" />
</Dropdown.Item> }
</Dropdown.Menu>
</Dropdown>
{isAdmin &&
<Dropdown className="mr-2" id="basic-nav-dropdown">
<Dropdown.Toggle variant="outline-primary" id="dropdown-basic" size="sm">
<Dropdown className="border-md-right px-2" id="basic-nav-dropdown">
<Dropdown.Toggle variant="link" id="dropdown-basic" size="sm">
<FontAwesomeIcon icon={faPlusCircle} className="mr-2" />
<Translate value={'action.advanced'} />
</Dropdown.Toggle>
Expand All @@ -48,33 +80,23 @@ export function Header () {
</Dropdown.Menu>
</Dropdown>
}
<Dropdown className="" id="basic-nav-dropdown">
<Dropdown.Toggle variant="outline-primary" id="dropdown-basic" size="sm">
<FontAwesomeIcon icon={faPlusCircle} className="mr-2" />
<Translate value={'action.add-new'} />

<Dropdown className="pl-2" id="basic-nav-dropdown">
<Dropdown.Toggle variant="link" id="dropdown-basic" size="sm">
<FontAwesomeIcon icon={faUser} className="mr-2" />
<Translate value={'action.logged-in'} params={{ username, group: groupId }} />
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item as={Link} to="/metadata/source/new" className="text-primary py-2">
<FontAwesomeIcon icon={faCube} className="mr-2" />
<Translate value="action.add-new-source" />
<Dropdown.Menu alignRight={true}>
<Dropdown.Item as={Nav.Link} href="/logout" target="_self" className="text-primary py-2" aria-label={translator('action.logout')}>
<FontAwesomeIcon icon={faSignOutAlt} className="mr-2" />
<Translate value="action.logout">Logout</Translate>
</Dropdown.Item>
{isAdmin && <Dropdown.Item as={Link} to="/metadata/provider/new" className="text-primary py-2">
<FontAwesomeIcon icon={faCubes} className="mr-2" />
<Translate value="action.add-new-provider" />
</Dropdown.Item> }
</Dropdown.Menu>
</Dropdown>
<Link to="/dashboard" className="nav-link" aria-label="Metadata Dashboard">
<i className="fa fa-th fa-fw" aria-hidden="true"></i>
<FontAwesomeIcon icon={faTh} className="mr-2" />
<Translate value="action.dashboard">Dashboard</Translate>
</Link>
<Nav.Link href="/logout" target="_self" aria-label={translator('action.logout')}>
<FontAwesomeIcon icon={faSignOutAlt} className="mr-2" />
<Translate value="action.logout">Logout</Translate>
</Nav.Link>
</Nav>
</Navbar.Collapse>
</>
}
</Navbar>
);
}
Expand Down
22 changes: 16 additions & 6 deletions ui/src/app/core/user/UserContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const path = '/admin/users/current';
/*eslint-disable react-hooks/exhaustive-deps*/
function UserProvider({ children }) {

const { get, response } = useFetch(`${API_BASE_PATH}`, {
const { get, response, loading } = useFetch(`${API_BASE_PATH}`, {
cacheLife: 10000,
cachePolicy: 'cache-first'
});
Expand All @@ -20,18 +20,28 @@ function UserProvider({ children }) {

async function loadUser() {
const user = await get(`${path}`);
if (response.ok) setUser(user);
if (response.ok) {
setUser(user);
}
}

const [user, setUser] = React.useState({});

const providerValue = React.useMemo(() => ({ user, loading }), [user, loading]);

return (
<Provider value={user}>{children}</Provider>
<Provider value={providerValue}>{children}</Provider>
);
}

function useCurrentUser() {
const context = React.useContext(UserContext);
return context;
const { user } = React.useContext(UserContext);
return user;
}

function useCurrentUserLoading() {
const { loading } = React.useContext(UserContext);
return loading;
}

function useIsAdmin() {
Expand All @@ -51,4 +61,4 @@ function useIsAdminOrInGroup() {
}


export { UserContext, UserProvider, Consumer as UserConsumer, useCurrentUser, useIsAdmin, useIsAdminOrInGroup };
export { UserContext, UserProvider, Consumer as UserConsumer, useCurrentUser, useIsAdmin, useIsAdminOrInGroup, useCurrentUserLoading };
17 changes: 13 additions & 4 deletions ui/src/app/dashboard/view/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@ import { SourcesTab } from './SourcesTab';
import { ProvidersTab } from './ProvidersTab';
import { AdminTab } from './AdminTab';
import { ActionsTab } from './ActionsTab';
import { useIsAdmin } from '../../core/user/UserContext';
import { useCurrentUserLoading, useIsAdmin } from '../../core/user/UserContext';
import useFetch from 'use-http';
import API_BASE_PATH from '../../App.constant';
import { useNonAdminSources } from '../../metadata/hooks/api';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';

export function Dashboard () {

const { path } = useRouteMatch();

const isAdmin = useIsAdmin();

const loading = useCurrentUserLoading();

const [actions, setActions] = React.useState(0);
const [users, setUsers] = React.useState([]);
const [sources, setSources] = React.useState([]);
Expand Down Expand Up @@ -58,8 +62,12 @@ export function Dashboard () {

return (
<div className="container-fluid p-3" role="navigation">

<Nav variant="tabs">
{loading ?
<div className="d-flex justify-content-center text-primary mt-5">
<FontAwesomeIcon icon={faSpinner} spin={true} pulse={true} size="3x" />
</div>
:
<><Nav variant="tabs">
<Nav.Item>
<NavLink className="nav-link" to={`${path}/metadata/manager/resolvers`}>
<Translate value="label.metadata-sources">Metadata Sources</Translate>
Expand Down Expand Up @@ -96,7 +104,8 @@ export function Dashboard () {
<Route path={`${path}/admin/actions`}>
<ActionsTab sources={sources} users={users} reloadSources={loadSources} reloadUsers={loadUsers} />
</Route>
</Switch>
</Switch></>
}
</div>
);
}
Expand Down
12 changes: 7 additions & 5 deletions ui/src/app/metadata/wizard/MetadataProviderTypeSelector.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { faArrowCircleRight } from '@fortawesome/free-solid-svg-icons';
import { faArrowCircleRight, faSpinner } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { useForm } from 'react-hook-form';
Expand All @@ -14,7 +14,7 @@ export function MetadataProviderTypeSelector({ type, types = [], children}) {

const translator = useTranslator();

const { data = [] } = useMetadataProviders({cachePolicy: 'no-cache'}, []);
const { data = [], loading } = useMetadataProviders({cachePolicy: 'no-cache'}, []);

const [showSelector, setShowSelector] = React.useState(true);

Expand Down Expand Up @@ -94,13 +94,15 @@ export function MetadataProviderTypeSelector({ type, types = [], children}) {
{errors?.name?.required && <Translate value={`message.service-resolver-name-required`} />}
</Form.Text>
</Form.Group>

<Form.Group>
<Form.Label>
<span><Translate value={'label.metadata-provider-type'} /></span>
<span>
<Translate value={'label.metadata-provider-type'} />
{loading && <FontAwesomeIcon icon={faSpinner} size="lg" spin={true} pulse={true} className="ml-2" /> }
</span>
<InfoIcon value="tooltip.metadata-provider-type" />
</Form.Label>
<Form.Control custom as="select" defaultValue={''} placeholder={translator(`label.select-metadata-type`)} {...register('type', {required: true})}>
<Form.Control disabled={loading} custom as="select" defaultValue={''} placeholder={translator(`label.select-metadata-type`)} {...register('type', {required: true})}>
<option disabled value="">{translator(`label.select-metadata-type`)}</option>
{types.map(t => <option key={t} value={t}>{t}</option>)}
</Form.Control>
Expand Down
12 changes: 12 additions & 0 deletions ui/src/theme/project/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ nav.fixed-top {
color: #00355f;
}

.border-md-right {
border-right: 1px solid silver;
}

@media (max-width: 768px) {

.border-md-right {
border-right: none;
}

}

footer {
background-color: $white;
padding: 0 20px;
Expand Down

0 comments on commit 66a29b8

Please sign in to comment.