Skip to content
This repository has been archived by the owner on Dec 13, 2022. It is now read-only.

feat(ui): Implement Unified view downtime and acknowledge tooltips #8385

Merged
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
32320f0
Start implementing Resources page
bdauria Feb 18, 2020
5127085
Merge remote-tracking branch 'origin/master' into skeleton-unified-vi…
bdauria Feb 19, 2020
a2aea5b
Add Listing to Resources
bdauria Feb 19, 2020
faf3d18
Merge branch 'MON-4789-unified-view' into skeleton-unified-view-page
bdauria Feb 19, 2020
a77b594
Implement more columns
bdauria Feb 20, 2020
f3d28b4
Finish up Listing columns
bdauria Feb 21, 2020
d53e854
Add Downtime Icon
bdauria Feb 21, 2020
db10fac
Implement sorting and filtering
bdauria Feb 24, 2020
800ae09
Implement pagination and limit
bdauria Feb 25, 2020
6250d43
Add pagination tests
bdauria Feb 26, 2020
816f19e
Finish up listing tests
bdauria Feb 26, 2020
b1c77ac
Fix TypeScript errors
bdauria Feb 26, 2020
826b56d
Remove mock endpoint
bdauria Feb 26, 2020
3bfa7c4
Merge branch 'MON-4789-unified-view' into skeleton-unified-view-page
bdauria Feb 26, 2020
e28e836
Update packages
bdauria Feb 26, 2020
9cf4b4e
Add SearchField in Resource page
bdauria Feb 27, 2020
8f704c3
Change api to beta
bdauria Feb 28, 2020
63932fd
change API to beta
bdauria Feb 28, 2020
2eb9e74
param made single element instead of array
bdauria Feb 28, 2020
5ac983b
Re-order imports
bdauria Feb 28, 2020
a1b12ae
Remove jest config
bdauria Feb 28, 2020
fecafc6
Merge branch 'skeleton-unified-view-page' into unified-view-search
bdauria Feb 28, 2020
92f813c
Implement search
bdauria Feb 28, 2020
2a4de7d
Implement search unit tests
bdauria Feb 28, 2020
60f5e3d
Implement help tooltip for search
bdauria Feb 28, 2020
bfc2d9b
set api version to beta
bdauria Feb 28, 2020
7ca1d50
Merge remote-tracking branch 'origin/MON-4789-unified-view' into unif…
bdauria Feb 28, 2020
94c4a8d
Update colors
bdauria Feb 28, 2020
e4925f2
Add downtime and acknowledge color conditions
bdauria Mar 1, 2020
8298c59
Add Downtime hover details
bdauria Mar 3, 2020
4926d7d
Update www/front_src/src/Resources/SearchHelpTooltip.tsx
bdauria Mar 3, 2020
15274db
Update www/front_src/src/Resources/api/searchObjects.ts
bdauria Mar 3, 2020
852371d
Update www/front_src/src/Resources/api/searchObjects.ts
bdauria Mar 3, 2020
00cb39d
Update www/front_src/src/Resources/translatedLabels.ts
bdauria Mar 3, 2020
c2f90f0
Fix tests and increase search help tooltip font size
bdauria Mar 3, 2020
7bc7e17
Merge branch 'unified-view-search' into unified-view-downtime-acknowl…
bdauria Mar 3, 2020
4284b0f
Implement ackwnoledge and downtime tooltips
bdauria Mar 3, 2020
cd1b487
fix API baseUrl
bdauria Mar 3, 2020
474aa16
Merge remote-tracking branch 'origin/MON-4789-unified-view' into unif…
bdauria Mar 3, 2020
4cdb692
Update www/front_src/src/Resources/index.test.tsx
bdauria Mar 5, 2020
b791afa
Update www/front_src/src/Resources/columns/State/DetailsTable/Acknowl…
bdauria Mar 5, 2020
e3384d4
Update www/front_src/src/Resources/columns/State/DetailsTable/Acknowl…
bdauria Mar 5, 2020
902c35e
Update www/front_src/src/Resources/columns/State/DetailsTable/Acknowl…
kduret Mar 5, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ const merge = require('lodash/merge');

module.exports = merge(require('@centreon/frontend-core/jest'), {
roots: ['<rootDir>/www/front_src/src/'],
setupFilesAfterEnv: [
'@testing-library/jest-dom/extend-expect',
'<rootDir>/setupTest.js',
],
});
563 changes: 390 additions & 173 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,11 @@
"@centreon/ui": "centreon/centreon-ui",
"@material-ui/core": "^4.9.4",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.45",
"axios": "^0.19.2",
"classnames": "^2.2.6",
"connected-react-router": "^6.7.0",
"date-fns": "^2.10.0",
"dom-serializer": "^0.2.2",
"install": "^0.13.0",
"loaders.css": "^0.1.2",
Expand Down
8 changes: 8 additions & 0 deletions setupTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
document.createRange = () => ({
setStart: () => {},
setEnd: () => {},
commonAncestorContainer: {
nodeName: 'BODY',
ownerDocument: document,
},
});
4 changes: 2 additions & 2 deletions www/front_src/src/Resources/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const api = axios.create({
baseURL: './api/beta/',
});

const getData = ({ endpoint, requestParams }): Promise<ResourceListing> =>
const getData = <TData>({ endpoint, requestParams }): Promise<TData> =>
api.get(endpoint, requestParams).then(({ data }) => data);

const listResources = (
Expand All @@ -16,4 +16,4 @@ const listResources = (
): Promise<ResourceListing> =>
getData({ endpoint: buildResourcesEndpoint(endpointParams), requestParams });

export { listResources };
export { listResources, getData };
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';

import {
labelAuthor,
labelComment,
labelEntryTime,
labelPersistent,
labelSticky,
} from '../../../translatedLabels';
import DetailsTable, {
getFormattedDate,
DetailsTableProps,
getYesNoLabel,
} from '.';

interface AcknoweldgementDetails {
author_name: string;
entry_time: string;
is_persistent: string;
bdauria marked this conversation as resolved.
Show resolved Hide resolved
is_sticky: string;
comment: string;
}

type Props = Pick<DetailsTableProps, 'endpoint'>;

const AcknowledgementDetailsTable = ({ endpoint }: Props): JSX.Element => {
const columns = [
{
label: labelAuthor,
getFormattedString: ({ author_name }): string => author_name,
},
{
label: labelEntryTime,
getFormattedString: ({ entry_time }): string =>
getFormattedDate(entry_time),
},
{
label: labelPersistent,
getFormattedString: ({ is_persistent }): string =>
kduret marked this conversation as resolved.
Show resolved Hide resolved
getYesNoLabel(is_persistent),
bdauria marked this conversation as resolved.
Show resolved Hide resolved
},
{
label: labelSticky,
getFormattedString: ({ is_sticky }): string => getYesNoLabel(is_sticky),
},

{
label: labelComment,
getFormattedString: ({ comment }): string => comment,
},
];

return (
<DetailsTable<AcknoweldgementDetails>
columns={columns}
endpoint={endpoint}
/>
);
};

export default AcknowledgementDetailsTable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';

import {
labelAuthor,
labelFixed,
labelYes,
labelNo,
labelStartTime,
labelEndTime,
labelComment,
} from '../../../translatedLabels';
import DetailsTable, { getFormattedDate, DetailsTableProps } from '.';

interface DowntimeDetails {
author_name: string;
is_fixed: boolean;
start_time: string;
end_time: string;
ccoment: string;
}

type Props = Pick<DetailsTableProps, 'endpoint'>;

const DowntimeDetailsTable = ({ endpoint }: Props): JSX.Element => {
const columns = [
{
label: labelAuthor,
getFormattedString: ({ author_name }): string => author_name,
},
{
label: labelFixed,
getFormattedString: ({ is_fixed }): string =>
is_fixed ? labelYes : labelNo,
},
{
label: labelStartTime,
getFormattedString: ({ start_time }): string =>
getFormattedDate(start_time),
},
{
label: labelEndTime,
getFormattedString: ({ end_time }): string => getFormattedDate(end_time),
},

{
label: labelComment,
getFormattedString: ({ comment }): string => comment,
},
];

return (
<DetailsTable<DowntimeDetails> columns={columns} endpoint={endpoint} />
);
};

export default DowntimeDetailsTable;
105 changes: 105 additions & 0 deletions www/front_src/src/Resources/columns/State/DetailsTable/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React, { useState, useEffect } from 'react';

import format from 'date-fns/format';
import parseISO from 'date-fns/parseISO';
kduret marked this conversation as resolved.
Show resolved Hide resolved

import {
TableContainer,
TableRow,
Paper,
Table,
TableHead,
TableCell,
TableBody,
} from '@material-ui/core';
import { Skeleton } from '@material-ui/lab';

import { getData } from '../../../api';
import {
labelSomethingWentWrong,
labelYes,
labelNo,
} from '../../../translatedLabels';
import useCancelTokenSource from '../../../useCancelTokenSource';

const getFormattedDate = (isoDate): string =>
format(parseISO(isoDate), 'MM/dd/yyyy H:m');

const getYesNoLabel = (value): string => (value ? labelYes : labelNo);

const columnMaxWidth = 150;

interface Column {
getFormattedString: (details) => string;
label: string;
}

export interface DetailsTableProps {
endpoint: string;
columns: Array<Column>;
}

const DetailsTable = <TDetails extends {}>({
endpoint,
columns,
}: DetailsTableProps): JSX.Element => {
const [details, setDetails] = useState<TDetails | null>();
const { cancel, token } = useCancelTokenSource();

useEffect(() => {
getData<TDetails>({ endpoint, requestParams: { cancelToken: token } })
.then((retrievedDetails) => setDetails(retrievedDetails))
.catch(() => {
setDetails(null);
});

return (): void => cancel();
}, []);

const loading = details === undefined;
const error = details === null;
const success = !loading && !error;

const tableMaxWidth = columns.length * columnMaxWidth;

return (
<TableContainer component={Paper}>
<Table size="small">
<TableHead>
<TableRow>
{columns.map(({ label }) => (
<TableCell key={label}>{label}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
<TableRow>
{loading && (
<TableCell colSpan={columns.length}>
<Skeleton height={20} animation="wave" width={tableMaxWidth} />
</TableCell>
)}
{success &&
columns.map(({ label, getFormattedString }) => (
<TableCell style={{ maxWidth: columnMaxWidth }} key={label}>
<span>{getFormattedString(details)}</span>
</TableCell>
))}
{error && (
<TableCell
style={{ width: tableMaxWidth }}
align="center"
colSpan={columns.length}
>
<span>{labelSomethingWentWrong}</span>
</TableCell>
)}
</TableRow>
</TableBody>
</Table>
</TableContainer>
);
};

export { getFormattedDate, getYesNoLabel };
export default DetailsTable;
118 changes: 118 additions & 0 deletions www/front_src/src/Resources/columns/State/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';

import { Grid, Avatar, makeStyles, fade, Tooltip } from '@material-ui/core';
import { Person as IconAcknowledged } from '@material-ui/icons';
import { lime, purple } from '@material-ui/core/colors';

import IconDowntime from '../icons/Downtime';
import { ColumnProps } from '..';
import DowntimeDetailsTable from './DetailsTable/Downtime';
import AcknowledgementDetailsTable from './DetailsTable/Acknowledgement';
import { labelInDowntime, labelAcknowledged } from '../../translatedLabels';
import { Resource } from '../../models';

const useStyles = makeStyles((theme) => ({
stateChip: {
width: theme.spacing(4),
height: theme.spacing(4),
},
acknowledged: {
backgroundColor: fade(lime[900], 0.1),
color: lime[900],
},
downtime: {
backgroundColor: fade(purple[500], 0.1),
color: purple[500],
},
tooltip: {
maxWidth: 'none',
backgroundColor: 'transparent',
},
}));

interface StateChipProps {
endpoint: string;
className: string;
Icon: React.SFC;
DetailsTable: React.SFC<{ endpoint: string }>;
ariaLabel: string;
}

const StateChip = ({
endpoint,
className,
Icon,
DetailsTable,
ariaLabel,
}: StateChipProps): JSX.Element => {
const classes = useStyles();

return (
<Tooltip
placement="left"
title={<DetailsTable endpoint={endpoint} />}
classes={{ tooltip: classes.tooltip }}
enterDelay={0}
>
<Avatar
className={`${classes.stateChip} ${className}`}
aria-label={ariaLabel}
>
<Icon />
</Avatar>
</Tooltip>
);
};

const DowntimeChip = ({ resource }: { resource: Resource }): JSX.Element => {
const classes = useStyles();

return (
<StateChip
endpoint={resource.downtime_endpoint as string}
className={classes.downtime}
ariaLabel={`${resource.name} ${labelInDowntime}`}
DetailsTable={DowntimeDetailsTable}
Icon={IconDowntime}
/>
);
};

const AcknowledgedChip = ({
resource,
}: {
resource: Resource;
}): JSX.Element => {
const classes = useStyles();

return (
<StateChip
endpoint={resource.acknowledgement_endpoint as string}
className={classes.acknowledged}
ariaLabel={`${resource.name} ${labelAcknowledged}`}
DetailsTable={AcknowledgementDetailsTable}
Icon={IconAcknowledged}
/>
);
};

const StateColumn = ({ Cell, row }: ColumnProps): JSX.Element => {
return (
<Cell width={80}>
<Grid container spacing={1}>
{row.in_downtime && (
<Grid item>
<DowntimeChip resource={row} />
</Grid>
)}
{row.acknowledged && (
<Grid item>
<AcknowledgedChip resource={row} />
</Grid>
)}
</Grid>
</Cell>
);
};

export default StateColumn;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

const Downtime = (): JSX.Element => <span>Downtime</span>;

export default Downtime;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading