mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2026-05-27 07:36:34 +00:00
Added logic in ManageDomains and DomainRow components to check if the domains status
This commit is contained in:
@@ -1,22 +1,26 @@
|
|||||||
import { FC } from 'react';
|
import { FC, useEffect } from 'react';
|
||||||
import { Button, UncontrolledTooltip } from 'reactstrap';
|
import { Button, UncontrolledTooltip } from 'reactstrap';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import {
|
import {
|
||||||
faBan as forbiddenIcon,
|
faBan as forbiddenIcon,
|
||||||
faCheck as defaultDomainIcon,
|
faDotCircle as defaultDomainIcon,
|
||||||
|
faCheck as checkIcon,
|
||||||
|
faCircleNotch as loadingStatusIcon,
|
||||||
faEdit as editIcon,
|
faEdit as editIcon,
|
||||||
} from '@fortawesome/free-solid-svg-icons';
|
} from '@fortawesome/free-solid-svg-icons';
|
||||||
import { ShlinkDomain, ShlinkDomainRedirects } from '../api/types';
|
import { ShlinkDomainRedirects } from '../api/types';
|
||||||
import { useToggle } from '../utils/helpers/hooks';
|
import { useToggle } from '../utils/helpers/hooks';
|
||||||
import { OptionalString } from '../utils/utils';
|
import { OptionalString } from '../utils/utils';
|
||||||
import { SelectedServer } from '../servers/data';
|
import { SelectedServer } from '../servers/data';
|
||||||
import { supportsDefaultDomainRedirectsEdition } from '../utils/helpers/features';
|
import { supportsDefaultDomainRedirectsEdition } from '../utils/helpers/features';
|
||||||
import { EditDomainRedirectsModal } from './helpers/EditDomainRedirectsModal';
|
import { EditDomainRedirectsModal } from './helpers/EditDomainRedirectsModal';
|
||||||
|
import { Domain, DomainStatus } from './data';
|
||||||
|
|
||||||
interface DomainRowProps {
|
interface DomainRowProps {
|
||||||
domain: ShlinkDomain;
|
domain: Domain;
|
||||||
defaultRedirects?: ShlinkDomainRedirects;
|
defaultRedirects?: ShlinkDomainRedirects;
|
||||||
editDomainRedirects: (domain: string, redirects: Partial<ShlinkDomainRedirects>) => Promise<void>;
|
editDomainRedirects: (domain: string, redirects: Partial<ShlinkDomainRedirects>) => Promise<void>;
|
||||||
|
checkDomainHealth: (domain: string) => void;
|
||||||
selectedServer: SelectedServer;
|
selectedServer: SelectedServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,12 +36,27 @@ const DefaultDomain: FC = () => (
|
|||||||
<UncontrolledTooltip target="defaultDomainIcon" placement="right">Default domain</UncontrolledTooltip>
|
<UncontrolledTooltip target="defaultDomainIcon" placement="right">Default domain</UncontrolledTooltip>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
const StatusIcon: FC<{ status: DomainStatus }> = ({ status }) => {
|
||||||
|
if (status === 'validating') {
|
||||||
|
return <FontAwesomeIcon fixedWidth icon={loadingStatusIcon} spin />;
|
||||||
|
}
|
||||||
|
|
||||||
export const DomainRow: FC<DomainRowProps> = ({ domain, editDomainRedirects, defaultRedirects, selectedServer }) => {
|
return status === 'valid'
|
||||||
|
? <FontAwesomeIcon fixedWidth icon={checkIcon} className="text-muted" />
|
||||||
|
: <FontAwesomeIcon fixedWidth icon={forbiddenIcon} className="text-danger" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DomainRow: FC<DomainRowProps> = (
|
||||||
|
{ domain, editDomainRedirects, checkDomainHealth, defaultRedirects, selectedServer },
|
||||||
|
) => {
|
||||||
const [ isOpen, toggle ] = useToggle();
|
const [ isOpen, toggle ] = useToggle();
|
||||||
const { domain: authority, isDefault, redirects } = domain;
|
const { domain: authority, isDefault, redirects, status } = domain;
|
||||||
const canEditDomain = !isDefault || supportsDefaultDomainRedirectsEdition(selectedServer);
|
const canEditDomain = !isDefault || supportsDefaultDomainRedirectsEdition(selectedServer);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
checkDomainHealth(domain.domain);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="responsive-table__row">
|
<tr className="responsive-table__row">
|
||||||
<td className="responsive-table__cell" data-th="Is default domain">{isDefault ? <DefaultDomain /> : ''}</td>
|
<td className="responsive-table__cell" data-th="Is default domain">{isDefault ? <DefaultDomain /> : ''}</td>
|
||||||
@@ -51,6 +70,9 @@ export const DomainRow: FC<DomainRowProps> = ({ domain, editDomainRedirects, def
|
|||||||
<td className="responsive-table__cell" data-th="Invalid short URL redirect">
|
<td className="responsive-table__cell" data-th="Invalid short URL redirect">
|
||||||
{redirects?.invalidShortUrlRedirect ?? <Nr fallback={defaultRedirects?.invalidShortUrlRedirect} />}
|
{redirects?.invalidShortUrlRedirect ?? <Nr fallback={defaultRedirects?.invalidShortUrlRedirect} />}
|
||||||
</td>
|
</td>
|
||||||
|
<td className="responsive-table__cell text-lg-center" data-th="Status">
|
||||||
|
<StatusIcon status={status} />
|
||||||
|
</td>
|
||||||
<td className="responsive-table__cell text-right">
|
<td className="responsive-table__cell text-right">
|
||||||
<span id={!canEditDomain ? 'defaultDomainBtn' : undefined}>
|
<span id={!canEditDomain ? 'defaultDomainBtn' : undefined}>
|
||||||
<Button outline size="sm" disabled={!canEditDomain} onClick={!canEditDomain ? undefined : toggle}>
|
<Button outline size="sm" disabled={!canEditDomain} onClick={!canEditDomain ? undefined : toggle}>
|
||||||
|
|||||||
@@ -13,14 +13,15 @@ interface ManageDomainsProps {
|
|||||||
listDomains: Function;
|
listDomains: Function;
|
||||||
filterDomains: (searchTerm: string) => void;
|
filterDomains: (searchTerm: string) => void;
|
||||||
editDomainRedirects: (domain: string, redirects: Partial<ShlinkDomainRedirects>) => Promise<void>;
|
editDomainRedirects: (domain: string, redirects: Partial<ShlinkDomainRedirects>) => Promise<void>;
|
||||||
|
checkDomainHealth: (domain: string) => void;
|
||||||
domainsList: DomainsList;
|
domainsList: DomainsList;
|
||||||
selectedServer: SelectedServer;
|
selectedServer: SelectedServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = [ '', 'Domain', 'Base path redirect', 'Regular 404 redirect', 'Invalid short URL redirect', '' ];
|
const headers = [ '', 'Domain', 'Base path redirect', 'Regular 404 redirect', 'Invalid short URL redirect', '', '' ];
|
||||||
|
|
||||||
export const ManageDomains: FC<ManageDomainsProps> = (
|
export const ManageDomains: FC<ManageDomainsProps> = (
|
||||||
{ listDomains, domainsList, filterDomains, editDomainRedirects, selectedServer },
|
{ listDomains, domainsList, filterDomains, editDomainRedirects, checkDomainHealth, selectedServer },
|
||||||
) => {
|
) => {
|
||||||
const { filteredDomains: domains, defaultRedirects, loading, error, errorData } = domainsList;
|
const { filteredDomains: domains, defaultRedirects, loading, error, errorData } = domainsList;
|
||||||
const resolvedDefaultRedirects = defaultRedirects ?? domains.find(({ isDefault }) => isDefault)?.redirects;
|
const resolvedDefaultRedirects = defaultRedirects ?? domains.find(({ isDefault }) => isDefault)?.redirects;
|
||||||
@@ -55,6 +56,7 @@ export const ManageDomains: FC<ManageDomainsProps> = (
|
|||||||
key={domain.domain}
|
key={domain.domain}
|
||||||
domain={domain}
|
domain={domain}
|
||||||
editDomainRedirects={editDomainRedirects}
|
editDomainRedirects={editDomainRedirects}
|
||||||
|
checkDomainHealth={checkDomainHealth}
|
||||||
defaultRedirects={resolvedDefaultRedirects}
|
defaultRedirects={resolvedDefaultRedirects}
|
||||||
selectedServer={selectedServer}
|
selectedServer={selectedServer}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Bottle from 'bottlejs';
|
import Bottle from 'bottlejs';
|
||||||
import { ConnectDecorator } from '../../container/types';
|
import { ConnectDecorator } from '../../container/types';
|
||||||
import { filterDomains, listDomains } from '../reducers/domainsList';
|
import { checkDomainHealth, filterDomains, listDomains } from '../reducers/domainsList';
|
||||||
import { DomainSelector } from '../DomainSelector';
|
import { DomainSelector } from '../DomainSelector';
|
||||||
import { ManageDomains } from '../ManageDomains';
|
import { ManageDomains } from '../ManageDomains';
|
||||||
import { editDomainRedirects } from '../reducers/domainRedirects';
|
import { editDomainRedirects } from '../reducers/domainRedirects';
|
||||||
@@ -13,13 +13,14 @@ const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
|||||||
bottle.serviceFactory('ManageDomains', () => ManageDomains);
|
bottle.serviceFactory('ManageDomains', () => ManageDomains);
|
||||||
bottle.decorator('ManageDomains', connect(
|
bottle.decorator('ManageDomains', connect(
|
||||||
[ 'domainsList', 'selectedServer' ],
|
[ 'domainsList', 'selectedServer' ],
|
||||||
[ 'listDomains', 'filterDomains', 'editDomainRedirects' ],
|
[ 'listDomains', 'filterDomains', 'editDomainRedirects', 'checkDomainHealth' ],
|
||||||
));
|
));
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('listDomains', listDomains, 'buildShlinkApiClient');
|
bottle.serviceFactory('listDomains', listDomains, 'buildShlinkApiClient');
|
||||||
bottle.serviceFactory('filterDomains', () => filterDomains);
|
bottle.serviceFactory('filterDomains', () => filterDomains);
|
||||||
bottle.serviceFactory('editDomainRedirects', editDomainRedirects, 'buildShlinkApiClient');
|
bottle.serviceFactory('editDomainRedirects', editDomainRedirects, 'buildShlinkApiClient');
|
||||||
|
bottle.serviceFactory('checkDomainHealth', checkDomainHealth, 'buildShlinkApiClient');
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
export default provideServices;
|
||||||
|
|||||||
@@ -3,14 +3,22 @@ import { Mock } from 'ts-mockery';
|
|||||||
import { Button, UncontrolledTooltip } from 'reactstrap';
|
import { Button, UncontrolledTooltip } from 'reactstrap';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faBan as forbiddenIcon, faEdit as editIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faBan as forbiddenIcon, faEdit as editIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { ShlinkDomain, ShlinkDomainRedirects } from '../../src/api/types';
|
import { ShlinkDomainRedirects } from '../../src/api/types';
|
||||||
import { DomainRow } from '../../src/domains/DomainRow';
|
import { DomainRow } from '../../src/domains/DomainRow';
|
||||||
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
import { ReachableServer, SelectedServer } from '../../src/servers/data';
|
||||||
|
import { Domain } from '../../src/domains/data';
|
||||||
|
|
||||||
describe('<DomainRow />', () => {
|
describe('<DomainRow />', () => {
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const createWrapper = (domain: ShlinkDomain, selectedServer = Mock.all<SelectedServer>()) => {
|
const createWrapper = (domain: Domain, selectedServer = Mock.all<SelectedServer>()) => {
|
||||||
wrapper = shallow(<DomainRow domain={domain} editDomainRedirects={jest.fn()} selectedServer={selectedServer} />);
|
wrapper = shallow(
|
||||||
|
<DomainRow
|
||||||
|
domain={domain}
|
||||||
|
selectedServer={selectedServer}
|
||||||
|
editDomainRedirects={jest.fn()}
|
||||||
|
checkDomainHealth={jest.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
};
|
};
|
||||||
@@ -18,34 +26,34 @@ describe('<DomainRow />', () => {
|
|||||||
afterEach(() => wrapper?.unmount());
|
afterEach(() => wrapper?.unmount());
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[ Mock.of<ShlinkDomain>({ domain: '', isDefault: true }), undefined, 1, 1, 'defaultDomainBtn' ],
|
[ Mock.of<Domain>({ domain: '', isDefault: true }), undefined, 1, 1, 'defaultDomainBtn' ],
|
||||||
[ Mock.of<ShlinkDomain>({ domain: '', isDefault: false }), undefined, 0, 0, undefined ],
|
[ Mock.of<Domain>({ domain: '', isDefault: false }), undefined, 0, 0, undefined ],
|
||||||
[ Mock.of<ShlinkDomain>({ domain: 'foo.com', isDefault: true }), undefined, 1, 1, 'defaultDomainBtn' ],
|
[ Mock.of<Domain>({ domain: 'foo.com', isDefault: true }), undefined, 1, 1, 'defaultDomainBtn' ],
|
||||||
[ Mock.of<ShlinkDomain>({ domain: 'foo.bar.com', isDefault: true }), undefined, 1, 1, 'defaultDomainBtn' ],
|
[ Mock.of<Domain>({ domain: 'foo.bar.com', isDefault: true }), undefined, 1, 1, 'defaultDomainBtn' ],
|
||||||
[ Mock.of<ShlinkDomain>({ domain: 'foo.baz', isDefault: false }), undefined, 0, 0, undefined ],
|
[ Mock.of<Domain>({ domain: 'foo.baz', isDefault: false }), undefined, 0, 0, undefined ],
|
||||||
[
|
[
|
||||||
Mock.of<ShlinkDomain>({ domain: 'foo.baz', isDefault: true }),
|
Mock.of<Domain>({ domain: 'foo.baz', isDefault: true }),
|
||||||
Mock.of<ReachableServer>({ version: '2.10.0' }),
|
Mock.of<ReachableServer>({ version: '2.10.0' }),
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
Mock.of<ShlinkDomain>({ domain: 'foo.baz', isDefault: true }),
|
Mock.of<Domain>({ domain: 'foo.baz', isDefault: true }),
|
||||||
Mock.of<ReachableServer>({ version: '2.9.0' }),
|
Mock.of<ReachableServer>({ version: '2.9.0' }),
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
'defaultDomainBtn',
|
'defaultDomainBtn',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
Mock.of<ShlinkDomain>({ domain: 'foo.baz', isDefault: false }),
|
Mock.of<Domain>({ domain: 'foo.baz', isDefault: false }),
|
||||||
Mock.of<ReachableServer>({ version: '2.9.0' }),
|
Mock.of<ReachableServer>({ version: '2.9.0' }),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
undefined,
|
undefined,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
Mock.of<ShlinkDomain>({ domain: 'foo.baz', isDefault: false }),
|
Mock.of<Domain>({ domain: 'foo.baz', isDefault: false }),
|
||||||
Mock.of<ReachableServer>({ version: '2.10.0' }),
|
Mock.of<ReachableServer>({ version: '2.10.0' }),
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@@ -89,7 +97,7 @@ describe('<DomainRow />', () => {
|
|||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
])('shows expected redirects', (redirects, expectedNoRedirects) => {
|
])('shows expected redirects', (redirects, expectedNoRedirects) => {
|
||||||
const wrapper = createWrapper(Mock.of<ShlinkDomain>({ domain: '', isDefault: true, redirects }));
|
const wrapper = createWrapper(Mock.of<Domain>({ domain: '', isDefault: true, redirects }));
|
||||||
const noRedirects = wrapper.find('Nr');
|
const noRedirects = wrapper.find('Nr');
|
||||||
const cells = wrapper.find('td');
|
const cells = wrapper.find('td');
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,14 @@ import { SelectedServer } from '../../src/servers/data';
|
|||||||
describe('<ManageDomains />', () => {
|
describe('<ManageDomains />', () => {
|
||||||
const listDomains = jest.fn();
|
const listDomains = jest.fn();
|
||||||
const filterDomains = jest.fn();
|
const filterDomains = jest.fn();
|
||||||
const editDomainRedirects = jest.fn();
|
|
||||||
let wrapper: ShallowWrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const createWrapper = (domainsList: DomainsList) => {
|
const createWrapper = (domainsList: DomainsList) => {
|
||||||
wrapper = shallow(
|
wrapper = shallow(
|
||||||
<ManageDomains
|
<ManageDomains
|
||||||
listDomains={listDomains}
|
listDomains={listDomains}
|
||||||
filterDomains={filterDomains}
|
filterDomains={filterDomains}
|
||||||
editDomainRedirects={editDomainRedirects}
|
editDomainRedirects={jest.fn()}
|
||||||
|
checkDomainHealth={jest.fn()}
|
||||||
domainsList={domainsList}
|
domainsList={domainsList}
|
||||||
selectedServer={Mock.all<SelectedServer>()}
|
selectedServer={Mock.all<SelectedServer>()}
|
||||||
/>,
|
/>,
|
||||||
@@ -77,7 +77,7 @@ describe('<ManageDomains />', () => {
|
|||||||
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
|
const wrapper = createWrapper(Mock.of<DomainsList>({ loading: false, error: false, filteredDomains: [] }));
|
||||||
const headerCells = wrapper.find('th');
|
const headerCells = wrapper.find('th');
|
||||||
|
|
||||||
expect(headerCells).toHaveLength(6);
|
expect(headerCells).toHaveLength(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('one row when list of domains is empty', () => {
|
it('one row when list of domains is empty', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user