Skip to content

Commit

Permalink
Merged in feature/SHIBUI-1978-loading-spinners (pull request #592)
Browse files Browse the repository at this point in the history
Feature/SHIBUI-1978 loading spinners

Approved-by: Dmitriy Kopylenko
Approved-by: Bill Smith
Former-commit-id: 021beb61111732330cb38b1da6d416555e9460fa
  • Loading branch information
rmathis committed Aug 4, 2022
2 parents f47e67a + 3c2cde4 commit 0649df2
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 116 deletions.
37 changes: 22 additions & 15 deletions backend/src/integration/resources/SHIBUI-1732-4.side
Original file line number Diff line number Diff line change
Expand Up @@ -992,28 +992,35 @@
["xpath=//div[3]/div/div/div[3]/button", "xpath:position"]
],
"value": ""
}, {
"id": "14c486b1-bdff-4474-94e3-b4286303a8fd",
"comment": "",
"command": "pause",
"target": "5000",
"targets": [],
"value": ""
}, {
"id": "e3892564-1a1b-4ee6-bbab-49d3cb3079d7",
"comment": "",
"command": "assertElementNotPresent",
"target": "css=table > tbody > tr",
"targets": [],
"value": ""
},{
"id": "4ec2c493-85e4-403b-9b09-031c5728f498",
"comment": "",
"command": "open",
"target": "/api/heheheheheheheWipeout",
"targets": [],
"value": ""
}, {
"id": "e074980a-8f21-4c22-8412-c4b6fcdcd1a4",
"comment": "",
"command": "assertText",
"target": "css=body",
"targets": [],
"value": "yes, you did it"
}]
}, {
"id": "4ec2c493-85e4-403b-9b09-031c5728f498",
"comment": "",
"command": "open",
"target": "/api/heheheheheheheWipeout",
"targets": [],
"value": ""
}, {
"id": "e074980a-8f21-4c22-8412-c4b6fcdcd1a4",
"comment": "",
"command": "assertText",
"target": "css=body",
"targets": [],
"value": "yes, you did it"
}]
}],
"suites": [{
"id": "575d414c-556d-45f7-b2f2-c9971ad51348",
Expand Down
9 changes: 9 additions & 0 deletions ui/src/app/core/components/Spinner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';

export function Spinner ({ size, className }) {
return (<FontAwesomeIcon icon={faSpinner} size={size} spin={true} pulse={true} className={ className } />)
}

export default Spinner;
11 changes: 8 additions & 3 deletions ui/src/app/dashboard/view/ActionsTab.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { MetadataActions } from '../../admin/container/MetadataActions';
import UserActions from '../../admin/container/UserActions';
import Spinner from '../../core/components/Spinner';

import Translate from '../../i18n/components/translate';
import SourceList from '../../metadata/domain/source/component/SourceList';

export function ActionsTab({ sources, users, reloadSources, reloadUsers }) {
export function ActionsTab({ sources, users, reloadSources, reloadUsers, loadingSources, loadingUsers }) {

return (
<>
Expand All @@ -21,7 +22,9 @@ export function ActionsTab({ sources, users, reloadSources, reloadUsers }) {
<div className="p-3">
<MetadataActions type="source">
{(enable) =>
<SourceList entities={sources} onDelete={reloadSources} onEnable={(s, e) => enable(s, e, reloadSources)} />
<SourceList entities={sources} onDelete={reloadSources} onEnable={(s, e) => enable(s, e, reloadSources)}>
{loadingSources && <div className="d-flex justify-content-center text-primary"><Spinner size="4x" /></div> }
</SourceList>
}
</MetadataActions>
</div>
Expand All @@ -36,7 +39,9 @@ export function ActionsTab({ sources, users, reloadSources, reloadUsers }) {
</div>
</div>
</div>
<UserActions users={users} reloadUsers={reloadUsers} />
<UserActions users={users} reloadUsers={reloadUsers}>
{loadingUsers && <div className="d-flex justify-content-center text-primary"><Spinner size="4x" /></div> }
</UserActions>
</div>
</section>
</>
Expand Down
5 changes: 4 additions & 1 deletion ui/src/app/dashboard/view/AdminTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import UserMaintenance from '../../admin/component/UserMaintenance';
import API_BASE_PATH from '../../App.constant';

import Translate from '../../i18n/components/translate';
import Spinner from '../../core/components/Spinner';

export function AdminTab () {

const [users, setUsers] = React.useState([]);

const { get, response } = useFetch(`${API_BASE_PATH}/admin/users`, {
const { get, response, loading } = useFetch(`${API_BASE_PATH}/admin/users`, {
cachePolicy: 'no-cache'
}, []);

Expand Down Expand Up @@ -46,7 +47,9 @@ export function AdminTab () {
onChangeUserRole={onChangeUserRole}
onDeleteUser={onDeleteUser}
onChangeUserGroup={onChangeUserGroup} />}

</UserManagement>
{loading && <div className="d-flex justify-content-center text-primary"><Spinner size="4x" /></div> }
</div>
</div>
</section>
Expand Down
14 changes: 10 additions & 4 deletions ui/src/app/dashboard/view/Dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export function Dashboard () {

const isAdmin = useIsAdmin();

const loading = useCurrentUserLoading();
const loadingUser = useCurrentUserLoading();

const [actions, setActions] = React.useState(0);
const [users, setUsers] = React.useState([]);
const [sources, setSources] = React.useState([]);

const { get, response } = useFetch(`${API_BASE_PATH}`, {
const { get, response, loading } = useFetch(`${API_BASE_PATH}`, {
cachePolicy: 'no-cache'
});

Expand Down Expand Up @@ -64,7 +64,7 @@ export function Dashboard () {

return (
<div className="container-fluid p-3" role="navigation">
{loading ?
{loadingUser ?
<div className="d-flex justify-content-center text-primary mt-5">
<FontAwesomeIcon icon={faSpinner} spin={true} pulse={true} size="3x" />
</div>
Expand Down Expand Up @@ -109,7 +109,13 @@ export function Dashboard () {
} />
<Route path={`${path}/admin/actions`} render={() =>
<ProtectRoute redirectTo="/dashboard">
<ActionsTab sources={sources} users={users} reloadSources={loadSources} reloadUsers={loadUsers} />
<ActionsTab
sources={sources}
users={users}
reloadSources={loadSources}
reloadUsers={loadUsers}
loadingSources={sourceLoader.loading}
loadingUsers={loading} />
</ProtectRoute>
} />
<Route exact path={`${path}/*`}>
Expand Down
7 changes: 5 additions & 2 deletions ui/src/app/dashboard/view/ProvidersTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import { Ordered } from '../component/Ordered';
import { useIsAdmin } from '../../core/user/UserContext';
import Alert from 'react-bootstrap/Alert';
import { MetadataActions } from '../../admin/container/MetadataActions';
import Spinner from '../../core/components/Spinner';
const searchProps = ['name', '@type', 'createdBy'];

export function ProvidersTab () {

const [providers, setProviders] = React.useState([]);

const { get, response } = useMetadataEntities('provider', {
const { get, response, loading } = useMetadataEntities('provider', {
cachePolicy: 'no-cache'
});

Expand Down Expand Up @@ -54,7 +55,9 @@ export function ProvidersTab () {
last={last}
onEnable={(p, e) => enable(p, e, loadProviders)}
onOrderUp={onOrderUp}
onOrderDown={onOrderDown}></ProviderList>
onOrderDown={onOrderDown}>
{loading && <div className="d-flex justify-content-center text-primary"><Spinner size="4x" /></div> }
</ProviderList>
}
</MetadataActions>
}
Expand Down
8 changes: 6 additions & 2 deletions ui/src/app/dashboard/view/SourcesTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import Translate from '../../i18n/components/translate';
import SourceList from '../../metadata/domain/source/component/SourceList';
import { useMetadataEntities, useMetadataEntity } from '../../metadata/hooks/api';
import { Search } from '../component/Search';
import { Spinner } from '../../core/components/Spinner';

import { NotificationContext, createNotificationAction, NotificationTypes } from '../../notifications/hoc/Notifications';


const searchProps = ['serviceProviderName', 'entityId', 'createdBy'];

export function SourcesTab () {
Expand All @@ -16,7 +18,7 @@ export function SourcesTab () {

const [sources, setSources] = React.useState([]);

const { get, response } = useMetadataEntities('source', {
const { get, response, loading } = useMetadataEntities('source', {
cachePolicy: 'no-cache'
});

Expand Down Expand Up @@ -68,7 +70,9 @@ export function SourcesTab () {
entities={searched}
onDelete={(id) => remove(id, loadSources)}
onEnable={(s, e) => enable(s, e, loadSources) }
onChangeGroup={changeSourceGroup} />
onChangeGroup={changeSourceGroup}>
{loading && <div className="d-flex justify-content-center text-primary"><Spinner size="4x" /></div> }
</SourceList>
}
</MetadataActions>
}
Expand Down
154 changes: 79 additions & 75 deletions ui/src/app/metadata/domain/provider/component/ProviderList.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,88 +12,92 @@ import { Scroller } from '../../../../dashboard/component/Scroller';
import { useIsAdmin } from '../../../../core/user/UserContext';
import { useTranslator } from '../../../../i18n/hooks';

export function ProviderList({ entities, reorder = true, first, last, onEnable, onOrderUp, onOrderDown }) {
export function ProviderList({ children, entities, reorder = true, first, last, onEnable, onOrderUp, onOrderDown }) {

const isAdmin = useIsAdmin();
const translator = useTranslator();

return (
<Scroller entities={entities}>
{(limited) => <div className="table-responsive mt-3 provider-list!">
<table className="table table-striped w-100 table-hover">
<thead>
<tr>
<th><Translate value="label.order">Order</Translate></th>
<th className="w-25"><Translate value="label.title">Title</Translate></th>
<th className="w-15"><Translate value="label.provider-type">Provider Type</Translate></th>
<th className="w-15"><Translate value="label.author">Author</Translate></th>
<th className="w-15"><Translate value="label.creation-date">Created Date</Translate></th>
<th className="text-end w-15"><Translate value="label.enabled">Enabled</Translate></th>
</tr>
</thead>
<tbody>
{limited.map((provider, idx) =>
<tr key={idx}>
<td className="align-middle">
<div className="d-flex align-items-center">
{reorder ?
<div className="provider-index text-center text-primary font-weight-bold">{idx + 1}</div>
<React.Fragment>
<Scroller entities={entities}>
{(limited) => <div className="table-responsive mt-3 provider-list!">
<table className="table table-striped w-100 table-hover">
<thead>
<tr>
<th><Translate value="label.order">Order</Translate></th>
<th className="w-25"><Translate value="label.title">Title</Translate></th>
<th className="w-15"><Translate value="label.provider-type">Provider Type</Translate></th>
<th className="w-15"><Translate value="label.author">Author</Translate></th>
<th className="w-15"><Translate value="label.creation-date">Created Date</Translate></th>
<th className="text-end w-15"><Translate value="label.enabled">Enabled</Translate></th>
</tr>
</thead>
<tbody>
{limited.map((provider, idx) =>
<tr key={idx}>
<td className="align-middle">
<div className="d-flex align-items-center">
{reorder ?
<div className="provider-index text-center text-primary font-weight-bold">{idx + 1}</div>
:
<div className="provider-index text-center text-primary font-weight-bold">&mdash;</div>
}
&nbsp;
<Button
onClick={ () => onOrderDown(provider.resourceId) }
variant="link"
className=" px-1"
disabled={provider.resourceId === last || !reorder}
aria-label="Decrease reorder by 1">
<FontAwesomeIcon className="text-info" icon={faChevronCircleDown} size="lg" />
<i className="fa text-info fa-lg fa-chevron-circle-down" aria-hidden="true"></i>
</Button>
<Button
onClick={ () => onOrderUp(provider.resourceId) }
variant="link"
className="px-1"
aria-label="Increase reorder by 1"
disabled={provider.resourceId === first || !reorder}>
<FontAwesomeIcon className="text-info" icon={faChevronCircleUp} size="lg" />
<i className="fa text-info fa-lg fa-chevron-circle-up" aria-hidden="true"></i>
</Button>
</div>
</td>
<td className="align-middle">
<Link to={`/metadata/provider/${provider.resourceId}/configuration/options`}>{provider.name}</Link>
</td>
<td className="align-middle">{ provider['@type'] }</td>
<td className="align-middle">{ provider.createdBy }</td>
<td className="align-middle"><FormattedDate date={provider.createdDate} /></td>
<td className="align-middle">
<span className="d-flex justify-content-end">
{onEnable && isAdmin ?
<Form.Check
size="lg"
type="switch"
id={`enable-switch-${provider.resourceId}`}
aria-label={translator(provider.enabled ? 'label.disable' : 'label.enable')}
onChange={({ target: { checked } }) => onEnable(provider, checked)}
checked={provider.enabled}
>
</Form.Check>
:
<div className="provider-index text-center text-primary font-weight-bold">&mdash;</div>
<Badge variant={provider.enabled ? 'success' : 'danger'}>
<Translate value={provider.enabnled ? 'value.enabled' : 'value.disabled'}></Translate>
</Badge>
}
&nbsp;
<Button
onClick={ () => onOrderDown(provider.resourceId) }
variant="link"
className=" px-1"
disabled={provider.resourceId === last || !reorder}
aria-label="Decrease reorder by 1">
<FontAwesomeIcon className="text-info" icon={faChevronCircleDown} size="lg" />
<i className="fa text-info fa-lg fa-chevron-circle-down" aria-hidden="true"></i>
</Button>
<Button
onClick={ () => onOrderUp(provider.resourceId) }
variant="link"
className="px-1"
aria-label="Increase reorder by 1"
disabled={provider.resourceId === first || !reorder}>
<FontAwesomeIcon className="text-info" icon={faChevronCircleUp} size="lg" />
<i className="fa text-info fa-lg fa-chevron-circle-up" aria-hidden="true"></i>
</Button>
</div>
</td>
<td className="align-middle">
<Link to={`/metadata/provider/${provider.resourceId}/configuration/options`}>{provider.name}</Link>
</td>
<td className="align-middle">{ provider['@type'] }</td>
<td className="align-middle">{ provider.createdBy }</td>
<td className="align-middle"><FormattedDate date={provider.createdDate} /></td>
<td className="align-middle">
<span className="d-flex justify-content-end">
{onEnable && isAdmin ?
<Form.Check
size="lg"
type="switch"
id={`enable-switch-${provider.resourceId}`}
aria-label={translator(provider.enabled ? 'label.disable' : 'label.enable')}
onChange={({ target: { checked } }) => onEnable(provider, checked)}
checked={provider.enabled}
>
</Form.Check>
:
<Badge variant={provider.enabled ? 'success' : 'danger'}>
<Translate value={provider.enabnled ? 'value.enabled' : 'value.disabled'}></Translate>
</Badge>
}
</span>
</td>
</tr>
)}
</tbody>
</table>
</div>
}
</Scroller>
</span>
</td>
</tr>
)}
</tbody>
</table>
</div>
}
</Scroller>
{children}
</React.Fragment>

);
}

Expand Down
Loading

0 comments on commit 0649df2

Please sign in to comment.