Skip to content

Commit cfcdbaf

Browse files
committed
Merge branch 'master' into css-refactor
2 parents e8fe1a7 + 6ecc775 commit cfcdbaf

15 files changed

+795
-12
lines changed

catalog/config.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
"url": "https://discuss.newrelic.com/t/workload-geoops-nerdpack/99478"
1414
}
1515
},
16-
"whatsNew": "\n-Fix: Maps now function with workloads that contain > 25 entities\n-Fix: Multiple usability issues have been addressed for multi-account environments\n-Feat: Published guide to automating configuration using the newrelic-cli"
16+
"whatsNew": "\n-Fix: Maps now function with workloads that contain > 25 entities\n-Fix: Multiple usability issues have been addressed for multi-account environments\n-Feat: map refreshes on a 15s. interval"
1717
}

docs/CHANGELOG.md

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# [1.2.0](https://github.com/newrelic/nr1-workload-geoops/compare/v1.1.7...v1.2.0) (2020-07-02)
2+
3+
4+
### Bug Fixes
5+
6+
* added missing dependency ([22a1d8b](https://github.com/newrelic/nr1-workload-geoops/commit/22a1d8b32a4711886048c2b0182cb4cedc8143ba))
7+
8+
9+
### Features
10+
11+
* introduced data polling for map view and refactored whole view, dividing it on couple smaller components ([b4838dd](https://github.com/newrelic/nr1-workload-geoops/commit/b4838dd0c9530c2006709fa42df25e73960334c0))
12+
113
## [1.1.7](https://github.com/newrelic/nr1-workload-geoops/compare/v1.1.6...v1.1.7) (2020-07-01)
214

315

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { get } from 'lodash';
2+
3+
const composeEntitySummary = activeMapLocation => {
4+
if (activeMapLocation) {
5+
const activeLocationEntities = get(activeMapLocation, 'entities', []);
6+
7+
if (activeLocationEntities.length > 0) {
8+
return activeLocationEntities.map(entity => {
9+
return {
10+
name: entity.name,
11+
alertSeverity: entity.alertSeverity || 'NOT_CONFIGURED',
12+
type: entity.type,
13+
guid: entity.guid
14+
};
15+
});
16+
} else {
17+
return [
18+
{
19+
name: 'hi',
20+
alertSeverity: 'NOT_ALERTING',
21+
type: 'APPLICATION'
22+
}
23+
];
24+
}
25+
} else {
26+
return [
27+
{
28+
name: 'hi',
29+
alertSeverity: 'NOT_ALERTING',
30+
type: 'APPLICATION'
31+
}
32+
];
33+
}
34+
};
35+
36+
export default composeEntitySummary;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Stack, StackItem } from 'nr1';
4+
5+
const LocationMetadata = ({ activeMapLocation }) => {
6+
const keys = Object.keys(activeMapLocation);
7+
8+
const items = keys.map(key => {
9+
if (typeof activeMapLocation[key] !== 'object') {
10+
return (
11+
<li key={key} className="detail-panel-metadata-item">
12+
<Stack fullWidth>
13+
<StackItem className="detail-panel-metadata-item-key" title={key}>
14+
{key}
15+
</StackItem>
16+
<StackItem
17+
grow
18+
className="detail-panel-metadata-item-value"
19+
title={activeMapLocation[key]}
20+
>
21+
{activeMapLocation[key]}
22+
</StackItem>
23+
</Stack>
24+
</li>
25+
);
26+
} else if (key === 'location') {
27+
const locationKeys = Object.keys(activeMapLocation[key]);
28+
29+
return locationKeys.map(locationKey => {
30+
return (
31+
<li key={locationKey} className="detail-panel-metadata-item">
32+
<Stack fullWidth>
33+
<StackItem
34+
className="detail-panel-metadata-item-key"
35+
title={locationKey}
36+
>
37+
{locationKey}
38+
</StackItem>
39+
<StackItem
40+
grow
41+
className="detail-panel-metadata-item-value"
42+
title={activeMapLocation[key][locationKey]}
43+
>
44+
{activeMapLocation[key][locationKey]}
45+
</StackItem>
46+
</Stack>
47+
</li>
48+
);
49+
});
50+
} else {
51+
return '';
52+
}
53+
});
54+
55+
return <ul className="detail-panel-metadata-list">{items}</ul>;
56+
};
57+
58+
LocationMetadata.propTypes = {
59+
activeMapLocation: PropTypes.object
60+
};
61+
62+
export default LocationMetadata;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import React from 'react';
2+
import { format } from 'date-fns';
3+
import { Icon, navigation } from 'nr1';
4+
import PropTypes from 'prop-types';
5+
import { kebabCase, lowerCase } from 'lodash';
6+
import { PACKAGE_UUID } from '../../shared/constants';
7+
8+
const Timeline = ({ activeMapLocation }) => {
9+
const iconType = alertSeverity => {
10+
switch (alertSeverity) {
11+
case 'CRITICAL':
12+
return Icon.TYPE.HARDWARE_AND_SOFTWARE__SOFTWARE__APPLICATION__S_ERROR;
13+
case 'WARNING':
14+
return Icon.TYPE
15+
.HARDWARE_AND_SOFTWARE__SOFTWARE__APPLICATION__S_WARNING;
16+
case 'NOT_ALERTING':
17+
return Icon.TYPE
18+
.HARDWARE_AND_SOFTWARE__SOFTWARE__APPLICATION__A_CHECKED;
19+
case 'NOT_CONFIGURED':
20+
return Icon.TYPE.HARDWARE_AND_SOFTWARE__SOFTWARE__APPLICATION__S_OK;
21+
}
22+
};
23+
24+
const iconColor = alertSeverity => {
25+
switch (alertSeverity) {
26+
case 'CRITICAL':
27+
return '#BF0016';
28+
case 'WARNING':
29+
return '#9C5400';
30+
case 'NOT_ALERTING':
31+
return '#3CA653';
32+
case 'NOT_CONFIGURED':
33+
return '#464e4e';
34+
}
35+
};
36+
37+
const timelineItems = activeMapLocation.recentViolations.map(violation => {
38+
return (
39+
<div
40+
className={`timeline-item impact-${kebabCase(violation.alertSeverity)}`}
41+
key={violation.violationId}
42+
onClick={() => {
43+
navigation.openStackedNerdlet({
44+
id: `${PACKAGE_UUID}.recent-incidents`,
45+
urlState: {
46+
recentViolations: activeMapLocation.recentViolations,
47+
clickedViolation: violation
48+
}
49+
});
50+
}}
51+
>
52+
<div className="timeline-item-timestamp">
53+
<span className="timeline-timestamp-date">
54+
{format(violation.openedAt, 'MM/dd/yy')}
55+
</span>
56+
<span className="timeline-timestamp-time">
57+
{format(violation.openedAt, 'p')}
58+
</span>
59+
</div>
60+
<div className="timeline-item-dot" />
61+
<div className="timeline-item-body">
62+
<div className="timeline-item-body-header">
63+
<div
64+
className="timeline-item-symbol"
65+
title={`Impact: ${lowerCase(violation.alertSeverity)}`}
66+
>
67+
<Icon
68+
type={iconType(violation.alertSeverity)}
69+
color={iconColor(violation.alertSeverity)}
70+
/>
71+
</div>
72+
<div className="timeline-item-title">{violation.label}</div>
73+
</div>
74+
</div>
75+
</div>
76+
);
77+
});
78+
79+
return (
80+
<div className="timeline-container mini-timeline">{timelineItems}</div>
81+
);
82+
};
83+
84+
Timeline.propTypes = {
85+
activeMapLocation: PropTypes.object
86+
};
87+
88+
export default Timeline;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Dropdown, DropdownItem } from 'nr1';
4+
import { groupBy } from 'lodash';
5+
import styled from 'styled-components';
6+
7+
import { ToolbarItem } from '../../../shared/components/Toolbar';
8+
9+
const StyledToolbarItem = styled(ToolbarItem)`
10+
flex-direction: column;
11+
align-items: flex-start;
12+
13+
> span {
14+
margin-bottom: 7px;
15+
letter-spacing: 1.25px;
16+
text-transform: uppercase;
17+
color: #8e9494;
18+
font-size: 10px;
19+
}
20+
21+
> h4 {
22+
max-width: 308px;
23+
white-space: nowrap;
24+
text-overflow: ellipsis;
25+
overflow: hidden;
26+
font-size: 20px;
27+
text-transform: none;
28+
color: #464e4e;
29+
}
30+
`;
31+
32+
const LeftToolbar = ({
33+
map,
34+
mapLocations,
35+
onFilter,
36+
regionFilter,
37+
favoriteFilter,
38+
alertFilter
39+
}) => {
40+
const regions = Object.keys(
41+
groupBy(mapLocations, i => (i.location ? i.location.region : null))
42+
);
43+
44+
const favoriteOptions = [
45+
{ name: 'All', value: null },
46+
{ name: 'Favorites', value: true },
47+
{ name: 'Unfavorited', value: false }
48+
];
49+
50+
const selectedFavorite = favoriteOptions.find(
51+
o => o.value === favoriteFilter
52+
);
53+
54+
const alertStatusOptions = [
55+
{ name: 'All', value: null },
56+
{ name: 'CRITICAL', value: 'CRITICAL' },
57+
{ name: 'NOT_ALERTING', value: 'NOT_ALERTING' },
58+
{ name: 'NOT_CONFIGURED', value: 'NOT_CONFIGURED' },
59+
{ name: 'WARNING', value: 'WARNING' }
60+
];
61+
62+
const selectedAlertStatus = alertStatusOptions.find(
63+
o => o.value === alertFilter
64+
);
65+
66+
return (
67+
<>
68+
<StyledToolbarItem hasSeparator>
69+
<span>Current Map</span>
70+
<h4>{map.title}</h4>
71+
</StyledToolbarItem>
72+
<ToolbarItem>
73+
<Dropdown label="Regions" title={regionFilter || 'Filter by Region'}>
74+
<DropdownItem
75+
key={0}
76+
onClick={() => {
77+
onFilter({
78+
filter: { name: 'regionFilter', value: null }
79+
});
80+
}}
81+
>
82+
All
83+
</DropdownItem>
84+
{regions.map(r => {
85+
return (
86+
<DropdownItem
87+
key={r}
88+
onClick={() => {
89+
onFilter({
90+
filter: { name: 'regionFilter', value: r }
91+
});
92+
}}
93+
>
94+
{r}
95+
</DropdownItem>
96+
);
97+
})}
98+
</Dropdown>
99+
</ToolbarItem>
100+
<ToolbarItem>
101+
<Dropdown label="Favorites" title={selectedFavorite.name}>
102+
{favoriteOptions.map(r => {
103+
return (
104+
<DropdownItem
105+
key={r.value}
106+
onClick={() => {
107+
onFilter({
108+
filter: { name: 'favoriteFilter', value: r.value }
109+
});
110+
}}
111+
>
112+
{r.name}
113+
</DropdownItem>
114+
);
115+
})}
116+
</Dropdown>
117+
</ToolbarItem>
118+
<ToolbarItem>
119+
<Dropdown label="Alerting Status" title={selectedAlertStatus.name}>
120+
{alertStatusOptions.map(r => {
121+
return (
122+
<DropdownItem
123+
key={r.value}
124+
onClick={() => {
125+
onFilter({
126+
filter: { name: 'alertFilter', value: r.value }
127+
});
128+
}}
129+
>
130+
{r.name}
131+
</DropdownItem>
132+
);
133+
})}
134+
</Dropdown>
135+
</ToolbarItem>
136+
</>
137+
);
138+
};
139+
140+
LeftToolbar.propTypes = {
141+
map: PropTypes.object,
142+
mapLocations: PropTypes.array,
143+
onFilter: PropTypes.func,
144+
regionFilter: PropTypes.string,
145+
favoriteFilter: PropTypes.bool,
146+
alertFilter: PropTypes.string
147+
};
148+
149+
export default LeftToolbar;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { Button } from 'nr1';
4+
5+
import { ToolbarItem } from '../../../shared/components/Toolbar';
6+
7+
const RightToolbar = ({ navigation }) => {
8+
return (
9+
<>
10+
<ToolbarItem hasSeparator>
11+
<Button
12+
type={Button.TYPE.NORMAL}
13+
onClick={() => navigation.router({ to: 'createMap' })}
14+
iconType={Button.ICON_TYPE.INTERFACE__SIGN__PLUS}
15+
>
16+
New Map
17+
</Button>
18+
</ToolbarItem>
19+
<ToolbarItem>
20+
<Button
21+
type={Button.TYPE.NORMAL}
22+
onClick={() => navigation.router({ to: 'mapList' })}
23+
iconType={Button.ICON_TYPE.INTERFACE__OPERATIONS__GROUP}
24+
>
25+
View all maps
26+
</Button>
27+
</ToolbarItem>
28+
</>
29+
);
30+
};
31+
32+
RightToolbar.propTypes = {
33+
navigation: PropTypes.object
34+
};
35+
36+
export default RightToolbar;

0 commit comments

Comments
 (0)