foreman_host_reports 0.0.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +67 -447
  3. data/app/controllers/api/v2/host_reports_controller.rb +39 -20
  4. data/app/controllers/concerns/foreman_host_reports/controller/hosts_controller_extensions.rb +18 -0
  5. data/app/controllers/concerns/foreman_host_reports/controller/parameters/host_report.rb +1 -1
  6. data/app/controllers/host_reports_controller.rb +32 -2
  7. data/app/helpers/concerns/foreman_host_reports/hosts_helper_extensions.rb +25 -0
  8. data/app/models/concerns/foreman_host_reports/host_extensions.rb +6 -0
  9. data/app/models/host_report.rb +15 -0
  10. data/app/models/host_status/host_report_status.rb +185 -0
  11. data/app/views/api/v2/host_reports/main.json.rabl +1 -2
  12. data/config/routes.rb +2 -4
  13. data/db/migrate/20220113064436_rename_status_summaries.rb +12 -0
  14. data/lib/foreman_host_reports/engine.rb +11 -5
  15. data/lib/foreman_host_reports/version.rb +1 -1
  16. data/test/controllers/api/v2/host_reports_controller_test.rb +30 -67
  17. data/test/factories/foreman_host_reports_factories.rb +13 -5
  18. data/test/model/host_report_status_test.rb +204 -0
  19. data/test/test_plugin_helper.rb +4 -2
  20. data/webpack/__mocks__/foremanReact/components/Pagination/index.js +2 -0
  21. data/webpack/fills.js +23 -0
  22. data/webpack/global_index.js +2 -0
  23. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/Formatters/statusFormatter.js +3 -4
  24. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/Components/StatusCell.js +1 -1
  25. data/webpack/src/Router/HostReports/IndexPage/Components/HostReportsTable/HostReportsTable.js +2 -10
  26. data/webpack/src/Router/HostReports/IndexPage/IndexPage.js +0 -1
  27. data/webpack/src/Router/HostReports/IndexPage/IndexPageActions.js +5 -4
  28. data/webpack/src/Router/HostReports/IndexPage/IndexPageHelpers.js +1 -1
  29. data/webpack/src/Router/HostReports/IndexPage/__tests__/HostReportsIndexPage.test.js +3 -4
  30. data/webpack/src/Router/HostReports/IndexPage/__tests__/__snapshots__/HostReportsIndexPage.test.js.snap +4 -6
  31. data/webpack/src/Router/HostReports/IndexPage/constants.js +4 -3
  32. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Ansible.js +62 -27
  33. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Components/EmptyLogsRow.js +27 -0
  34. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Components/RawMsgModal.js +41 -0
  35. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/Puppet.js +38 -37
  36. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogs/index.js +10 -3
  37. data/webpack/src/Router/HostReports/ShowPage/Components/ReportLogsFilter/index.js +56 -65
  38. data/webpack/src/Router/HostReports/ShowPage/ShowPage.js +34 -8
  39. data/webpack/src/Router/HostReports/constants.js +2 -0
  40. data/webpack/src/components/ReportsTab/ReportsTable.js +117 -0
  41. data/webpack/src/components/ReportsTab/helpers.js +155 -0
  42. data/webpack/src/components/ReportsTab/index.js +132 -0
  43. metadata +18 -19
  44. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -4
@@ -1,8 +1,9 @@
1
1
  import { getControllerSearchProps } from 'foremanReact/constants';
2
2
 
3
- export const HOST_REPORTS_SEARCH_PROPS = getControllerSearchProps(
4
- 'host_reports'
5
- );
3
+ export const HOST_REPORTS_SEARCH_PROPS = {
4
+ ...getControllerSearchProps('/host_reports'),
5
+ controller: 'host_reports',
6
+ };
6
7
 
7
8
  export const HOST_REPORTS_PATH = '/host_reports';
8
9
  export const HOST_REPORTS_API_PATH =
@@ -1,16 +1,49 @@
1
1
  import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
- import { Alert, AlertActionCloseButton } from '@patternfly/react-core';
4
+ import { Alert, AlertActionCloseButton, Button } from '@patternfly/react-core';
5
+
6
+ import {
7
+ TableComposable,
8
+ Thead,
9
+ Tbody,
10
+ Tr,
11
+ Th,
12
+ Td,
13
+ } from '@patternfly/react-table';
5
14
 
6
15
  import { translate as __ } from 'foremanReact/common/I18n';
16
+ import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
17
+
18
+ import RawMsgModal from './Components/RawMsgModal';
19
+ import EmptyLogsRow from './Components/EmptyLogsRow';
7
20
 
8
21
  import { msgLevelClasses } from './helpers';
9
22
 
10
- const AnsibleLogs = ({ logs, checkMode }) => {
23
+ import { RAW_MSG_MODAL_ID } from '../../../constants';
24
+
25
+ const AnsibleLogs = ({ logs, checkMode, onClear }) => {
11
26
  const [alertVisibility, setAlertVisibility] = useState(true);
27
+ const [selectedMsg, setSelectedMsg] = useState(0);
28
+ const { setModalOpen: setRawModalOpen } = useForemanModal({
29
+ id: RAW_MSG_MODAL_ID,
30
+ });
31
+
32
+ const rawMsg = idx => {
33
+ const onClick = () => {
34
+ setSelectedMsg(idx);
35
+ setRawModalOpen();
36
+ };
37
+ return (
38
+ <Button isSmall onClick={onClick} variant="secondary">
39
+ {__('Show')}
40
+ </Button>
41
+ );
42
+ };
43
+
12
44
  return (
13
45
  <>
46
+ <RawMsgModal body={logs[selectedMsg]} />
14
47
  {checkMode && alertVisibility ? (
15
48
  <Alert
16
49
  variant="info"
@@ -23,50 +56,52 @@ const AnsibleLogs = ({ logs, checkMode }) => {
23
56
  {__('Notice that ansible roles run in check mode.')}
24
57
  </Alert>
25
58
  ) : null}
26
- <table
27
- id="report_log"
28
- className="table table-bordered table-striped table-hover"
29
- >
30
- <thead>
31
- <tr>
32
- <th className="col col-md"> {__('Level')} </th>
33
- <th className="col col-md-3"> {__('Task')} </th>
34
- <th className="col col-md-9"> {__('Message')} </th>
35
- </tr>
36
- </thead>
37
- <tbody>
59
+ <TableComposable id="report_log" variant="compact">
60
+ <Thead noWrap>
61
+ <Tr>
62
+ <Th> {__('Level')} </Th>
63
+ <Th> {__('Task')} </Th>
64
+ <Th> {__('Message')} </Th>
65
+ <Th> {__('Raw data')} </Th>
66
+ </Tr>
67
+ </Thead>
68
+ <Tbody>
38
69
  {logs.map((log, idx) => (
39
- <tr key={`tr-${idx + 1}`}>
40
- <td>
70
+ <Tr key={`tr-${idx + 1}`}>
71
+ <Td>
41
72
  <span className={msgLevelClasses(log.level)}>{log.level}</span>
42
- </td>
43
- <td className="break-me">{log.task.name}</td>
73
+ </Td>
74
+ <Td>{log.task.name}</Td>
44
75
  {Array.isArray(log.friendlyMessage) ? (
45
- <td>
76
+ <Td>
46
77
  <ul>
47
78
  {log.friendlyMessage.map((msg, i) => (
48
79
  <li key={`li-${i + 1}`}>{msg}</li>
49
80
  ))}
50
81
  </ul>
51
- </td>
82
+ </Td>
52
83
  ) : (
53
- <td className="break-me">{log.friendlyMessage}</td>
84
+ <Td>{log.friendlyMessage}</Td>
54
85
  )}
55
- </tr>
86
+ <Td>{rawMsg(idx)}</Td>
87
+ </Tr>
56
88
  ))}
57
89
  {logs.length === 0 ? (
58
- <tr key="tr-0">
59
- <td colSpan="3">{__('Nothing to show')}</td>
60
- </tr>
90
+ <Tr key="tr-0">
91
+ <Td colSpan={4}>
92
+ <EmptyLogsRow onClear={onClear} />
93
+ </Td>
94
+ </Tr>
61
95
  ) : null}
62
- </tbody>
63
- </table>
96
+ </Tbody>
97
+ </TableComposable>
64
98
  </>
65
99
  );
66
100
  };
67
101
 
68
102
  AnsibleLogs.propTypes = {
69
103
  logs: PropTypes.array.isRequired,
104
+ onClear: PropTypes.func.isRequired,
70
105
  checkMode: PropTypes.bool,
71
106
  };
72
107
 
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
4
+
5
+ import DefaultEmptyState from 'foremanReact/components/common/EmptyState';
6
+ import { translate as __ } from 'foremanReact/common/I18n';
7
+
8
+ const EmptyLogsRow = ({ onClear }) => (
9
+ <DefaultEmptyState
10
+ header={__('No results found')}
11
+ icon={<SearchIcon />}
12
+ description={__(
13
+ 'No results match this filter criteria. Clear all filters and try again.'
14
+ )}
15
+ action={{
16
+ title: __('Clear all filters'),
17
+ url: '#',
18
+ onClick: onClear,
19
+ }}
20
+ />
21
+ );
22
+
23
+ EmptyLogsRow.propTypes = {
24
+ onClear: PropTypes.func.isRequired,
25
+ };
26
+
27
+ export default EmptyLogsRow;
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import JSONTree from 'react-json-tree';
4
+ import Immutable from 'seamless-immutable';
5
+
6
+ import { translate as __ } from 'foremanReact/common/I18n';
7
+ import ForemanModal from 'foremanReact/components/ForemanModal';
8
+
9
+ import { RAW_MSG_MODAL_ID } from '../../../../constants';
10
+
11
+ const RawMsgModal = ({ body }) => {
12
+ const theme = {
13
+ scheme: 'foreman',
14
+ backgroundColor: 'rgba(0, 0, 0, 255)',
15
+ base00: 'rgba(0, 0, 0, 0)',
16
+ };
17
+ return (
18
+ <ForemanModal
19
+ id={RAW_MSG_MODAL_ID}
20
+ title={__('Raw data')}
21
+ backdrop="static"
22
+ enforceFocus
23
+ >
24
+ <JSONTree
25
+ data={Immutable.asMutable(body, { deep: true })}
26
+ hideRoot
27
+ theme={theme}
28
+ />
29
+ </ForemanModal>
30
+ );
31
+ };
32
+
33
+ RawMsgModal.propTypes = {
34
+ body: PropTypes.object,
35
+ };
36
+
37
+ RawMsgModal.defaultProps = {
38
+ body: {},
39
+ };
40
+
41
+ export default RawMsgModal;
@@ -2,13 +2,24 @@ import React from 'react';
2
2
  import { useDispatch } from 'react-redux';
3
3
  import PropTypes from 'prop-types';
4
4
 
5
- import { sprintf, translate as __ } from 'foremanReact/common/I18n';
5
+ import {
6
+ TableComposable,
7
+ Thead,
8
+ Tbody,
9
+ Tr,
10
+ Th,
11
+ Td,
12
+ } from '@patternfly/react-table';
13
+
14
+ import { translate as __ } from 'foremanReact/common/I18n';
6
15
  import * as diffModalActions from 'foremanReact/components/ConfigReports/DiffModal/DiffModalActions';
7
16
  import DiffModal from 'foremanReact/components/ConfigReports/DiffModal';
8
17
 
18
+ import EmptyLogsRow from './Components/EmptyLogsRow';
19
+
9
20
  import { msgLevelClasses } from './helpers';
10
21
 
11
- const PuppetLogs = ({ logs, environment }) => {
22
+ const PuppetLogs = ({ logs, onClear }) => {
12
23
  const dispatch = useDispatch();
13
24
  const showDiff = (e, diff, title) => {
14
25
  e.preventDefault();
@@ -18,31 +29,23 @@ const PuppetLogs = ({ logs, environment }) => {
18
29
  return (
19
30
  <>
20
31
  <DiffModal />
21
- {environment ? (
22
- <p className="ra">
23
- {sprintf(__('Puppet Environment: %s'), environment)}
24
- </p>
25
- ) : null}
26
- <table
27
- id="report_log"
28
- className="table table-bordered table-striped table-hover"
29
- >
30
- <thead>
31
- <tr>
32
- <th className="col col-md"> {__('Level')} </th>
33
- <th className="col col-md-3"> {__('Resource')} </th>
34
- <th className="col col-md-9"> {__('Message')} </th>
35
- </tr>
36
- </thead>
37
- <tbody>
32
+ <TableComposable id="report_log" variant="compact">
33
+ <Thead noWrap>
34
+ <Tr>
35
+ <Th className="col col-md"> {__('Level')} </Th>
36
+ <Th className="col col-md-3"> {__('Resource')} </Th>
37
+ <Th className="col col-md-9"> {__('Message')} </Th>
38
+ </Tr>
39
+ </Thead>
40
+ <Tbody>
38
41
  {logs.map((log, i) => (
39
- <tr key={`tr-${i + 1}`}>
40
- <td>
42
+ <Tr key={`tr-${i + 1}`}>
43
+ <Td>
41
44
  <span className={msgLevelClasses(log[0])}>{log[0]}</span>
42
- </td>
43
- <td className="break-me">{log[1]}</td>
45
+ </Td>
46
+ <Td className="break-me">{log[1]}</Td>
44
47
  {log[2].startsWith('\n---') ? (
45
- <td className="break-me">
48
+ <Td className="break-me">
46
49
  <a
47
50
  onClick={e =>
48
51
  showDiff(e, log[2], /File\[(.*?)\]/.exec(log[1])[1])
@@ -50,30 +53,28 @@ const PuppetLogs = ({ logs, environment }) => {
50
53
  >
51
54
  {__('Show Diff')}
52
55
  </a>
53
- </td>
56
+ </Td>
54
57
  ) : (
55
- <td className="break-me">{log[2]}</td>
58
+ <Td className="break-me">{log[2]}</Td>
56
59
  )}
57
- </tr>
60
+ </Tr>
58
61
  ))}
59
62
  {logs.length === 0 ? (
60
- <tr key="tr-0">
61
- <td colSpan="3">{__('Nothing to show')}</td>
62
- </tr>
63
+ <Tr key="tr-0">
64
+ <Td colSpan={3}>
65
+ <EmptyLogsRow onClear={onClear} />
66
+ </Td>
67
+ </Tr>
63
68
  ) : null}
64
- </tbody>
65
- </table>
69
+ </Tbody>
70
+ </TableComposable>
66
71
  </>
67
72
  );
68
73
  };
69
74
 
70
75
  PuppetLogs.propTypes = {
71
76
  logs: PropTypes.array.isRequired,
72
- environment: PropTypes.string,
73
- };
74
-
75
- PuppetLogs.defaultProps = {
76
- environment: null,
77
+ onClear: PropTypes.func.isRequired,
77
78
  };
78
79
 
79
80
  export default PuppetLogs;
@@ -4,12 +4,18 @@ import PropTypes from 'prop-types';
4
4
  import PuppetLogs from './Puppet';
5
5
  import AnsibleLogs from './Ansible';
6
6
 
7
- const ReportLogs = ({ format, logs, meta }) => {
7
+ const ReportLogs = ({ format, logs, meta, onFilterClear }) => {
8
8
  switch (format) {
9
9
  case 'puppet':
10
- return <PuppetLogs logs={logs} environment={meta.environment} />;
10
+ return <PuppetLogs logs={logs} onClear={onFilterClear} />;
11
11
  case 'ansible':
12
- return <AnsibleLogs logs={logs} checkMode={meta.checkMode} />;
12
+ return (
13
+ <AnsibleLogs
14
+ logs={logs}
15
+ checkMode={meta.checkMode}
16
+ onClear={onFilterClear}
17
+ />
18
+ );
13
19
  default:
14
20
  return <></>;
15
21
  }
@@ -17,6 +23,7 @@ const ReportLogs = ({ format, logs, meta }) => {
17
23
 
18
24
  ReportLogs.propTypes = {
19
25
  format: PropTypes.string.isRequired,
26
+ onFilterClear: PropTypes.func.isRequired,
20
27
  logs: PropTypes.array,
21
28
  meta: PropTypes.object,
22
29
  };
@@ -1,14 +1,23 @@
1
- import React, { useState, useEffect, useCallback } from 'react';
1
+ import React, { useState, useEffect } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
 
4
- import { sprintf, translate as __ } from 'foremanReact/common/I18n';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
5
 
6
- import { ContextSelector, ContextSelectorItem } from '@patternfly/react-core';
6
+ import {
7
+ Select,
8
+ SelectOption,
9
+ SelectVariant,
10
+ Toolbar,
11
+ ToolbarContent,
12
+ ToolbarToggleGroup,
13
+ ToolbarGroup,
14
+ ToolbarItem,
15
+ } from '@patternfly/react-core';
16
+ import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon';
7
17
 
8
18
  import ReportLogs from '../ReportLogs';
9
19
 
10
- const ReportLogsFilter = ({ format, reportedAt, meta }) => {
11
- const reportedAtLocal = new Date(reportedAt);
20
+ const ReportLogsFilter = ({ format, meta }) => {
12
21
  const { logs } = meta;
13
22
  const filterItems = [
14
23
  { text: __('All messages'), accepts: [] },
@@ -19,36 +28,17 @@ const ReportLogsFilter = ({ format, reportedAt, meta }) => {
19
28
  { text: __('Warnings and errors'), accepts: ['warning', 'err'] },
20
29
  { text: __('Errors only'), accepts: ['err'] },
21
30
  ];
22
- const [currentItem, setCurrentItem] = useState(filterItems[0]);
23
- const [currentFilterItems, setCurrentFilterItems] = useState(filterItems);
24
- const [searchValue, setSearchValue] = useState('');
25
- const [isOpen, setIsOpen] = useState(false);
26
31
  const [filteredLogs, setFilteredLogs] = useState(logs);
32
+ const [isOpen, setIsOpen] = useState(false);
33
+ const [selected, setSelected] = useState(filterItems[0]);
27
34
 
28
- /* eslint-disable react-hooks/exhaustive-deps */
29
- const onSearchButtonClick = useCallback(() => {
30
- const filtered =
31
- searchValue === ''
32
- ? filterItems
33
- : filterItems.filter(item =>
34
- item.text.toLowerCase().includes(searchValue.toLowerCase())
35
- );
36
- setCurrentFilterItems(filtered || []);
37
- }, [searchValue]);
38
- /* eslint-enable react-hooks/exhaustive-deps */
39
-
40
- useEffect(() => {
41
- onSearchButtonClick();
42
- }, [searchValue, onSearchButtonClick]);
43
-
44
- const onToggle = (event, newIsOpen) => {
45
- setIsOpen(newIsOpen);
46
- };
47
- const onSelect = () => {
48
- setIsOpen(!isOpen);
35
+ const onToggle = isExpanded => {
36
+ setIsOpen(isExpanded);
49
37
  };
50
- const onSearchInputChange = (value, event) => {
51
- setSearchValue(event.target.value);
38
+ const onSelect = (event, selection) => {
39
+ const item = filterItems.find(i => i.text === selection);
40
+ setSelected(item);
41
+ setIsOpen(false);
52
42
  };
53
43
  const filterLogs = (toFilter, accepts) => {
54
44
  if (!accepts.length) return toFilter;
@@ -57,48 +47,49 @@ const ReportLogsFilter = ({ format, reportedAt, meta }) => {
57
47
  };
58
48
 
59
49
  useEffect(() => {
60
- setFilteredLogs(filterLogs(logs, currentItem.accepts));
61
- }, [logs, currentItem]);
50
+ setFilteredLogs(filterLogs(logs, selected.accepts));
51
+ }, [logs, selected]);
52
+
53
+ const onFilterClear = () => {
54
+ setSelected(filterItems[0]);
55
+ };
62
56
 
63
57
  return (
64
58
  <>
65
- <span>{__('Show log messages:')}</span>
66
- <br />
67
- <ContextSelector
68
- id="report-logs-filter"
69
- toggleText={currentItem.text}
70
- onSearchInputChange={onSearchInputChange}
71
- isOpen={isOpen}
72
- searchInputValue={searchValue}
73
- onToggle={onToggle}
74
- onSelect={onSelect}
75
- onSearchButtonClick={onSearchButtonClick}
76
- screenReaderLabel="Selected Messages:"
77
- >
78
- {currentFilterItems.map((item, i) => (
79
- <ContextSelectorItem
80
- key={i + 1}
81
- id={`select_messages_${i}`}
82
- onClick={() => {
83
- setCurrentItem(item);
84
- }}
85
- isDisabled={item.text === currentItem.text}
86
- >
87
- {item.text}
88
- </ContextSelectorItem>
89
- ))}
90
- </ContextSelector>
91
- <p className="ra">
92
- {sprintf(__('Reported at %s'), reportedAtLocal.toLocaleString())}
93
- </p>
94
- <ReportLogs format={format} logs={filteredLogs} meta={meta} />
59
+ <Toolbar id="logs-toolbar">
60
+ <ToolbarContent>
61
+ <ToolbarToggleGroup toggleIcon={<FilterIcon />} breakpoint="lg">
62
+ <ToolbarGroup variant="filter-group">
63
+ <ToolbarItem variant="label">{__('Message')}</ToolbarItem>
64
+ <ToolbarItem>
65
+ <Select
66
+ variant={SelectVariant.single}
67
+ onToggle={onToggle}
68
+ onSelect={onSelect}
69
+ selections={selected.text}
70
+ isOpen={isOpen}
71
+ >
72
+ {filterItems.map((option, index) => (
73
+ <SelectOption key={index} value={option.text} />
74
+ ))}
75
+ </Select>
76
+ </ToolbarItem>
77
+ </ToolbarGroup>
78
+ </ToolbarToggleGroup>
79
+ </ToolbarContent>
80
+ </Toolbar>
81
+ <ReportLogs
82
+ format={format}
83
+ logs={filteredLogs}
84
+ meta={meta}
85
+ onFilterClear={onFilterClear}
86
+ />
95
87
  </>
96
88
  );
97
89
  };
98
90
 
99
91
  ReportLogsFilter.propTypes = {
100
92
  format: PropTypes.string.isRequired,
101
- reportedAt: PropTypes.string.isRequired,
102
93
  meta: PropTypes.object,
103
94
  };
104
95
 
@@ -1,10 +1,17 @@
1
1
  import React from 'react';
2
2
  import JSONTree from 'react-json-tree';
3
3
  import PropTypes from 'prop-types';
4
- import { Button, Grid, GridItem } from '@patternfly/react-core';
4
+ import {
5
+ Button,
6
+ Grid,
7
+ GridItem,
8
+ Toolbar,
9
+ ToolbarItem,
10
+ ToolbarContent,
11
+ } from '@patternfly/react-core';
5
12
 
6
13
  import PageLayout from 'foremanReact/routes/common/PageLayout/PageLayout';
7
- import { translate as __ } from 'foremanReact/common/I18n';
14
+ import { sprintf, translate as __ } from 'foremanReact/common/I18n';
8
15
  import { foremanUrl } from 'foremanReact/common/helpers';
9
16
  import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
10
17
 
@@ -28,6 +35,7 @@ const HostReportsShowPage = ({
28
35
  isLoading,
29
36
  fetchAndPush,
30
37
  }) => {
38
+ const reportedAtLocal = new Date(reportedAt);
31
39
  const {
32
40
  setModalOpen: setDeleteModalOpen,
33
41
  setModalClosed: setDeleteModalClosed,
@@ -77,7 +85,6 @@ const HostReportsShowPage = ({
77
85
  const meta = {};
78
86
  switch (format) {
79
87
  case 'puppet':
80
- meta.environment = body.environment;
81
88
  meta.logs = body.logs;
82
89
  break;
83
90
  case 'ansible':
@@ -124,11 +131,30 @@ const HostReportsShowPage = ({
124
131
  ) : (
125
132
  <Grid hasGutter>
126
133
  <GridItem>
127
- <ReportLogsFilter
128
- format={format}
129
- reportedAt={reportedAt}
130
- meta={meta}
131
- />
134
+ <Toolbar id="meta-toolbar">
135
+ <ToolbarContent>
136
+ <ToolbarItem>
137
+ {sprintf(
138
+ __('Reported at %s'),
139
+ reportedAtLocal.toLocaleString()
140
+ )}
141
+ </ToolbarItem>
142
+ {format === 'puppet' ? (
143
+ <>
144
+ <ToolbarItem variant="separator" />
145
+ <ToolbarItem>
146
+ {sprintf(
147
+ __('Puppet Environment: %s'),
148
+ body.environment
149
+ )}
150
+ </ToolbarItem>
151
+ </>
152
+ ) : null}
153
+ </ToolbarContent>
154
+ </Toolbar>
155
+ </GridItem>
156
+ <GridItem>
157
+ <ReportLogsFilter format={format} meta={meta} />
132
158
  </GridItem>
133
159
  {format === 'puppet' ? (
134
160
  <GridItem>
@@ -13,3 +13,5 @@ export const HOST_REPORT_REQUEST_KEY = 'HOST_REPORT';
13
13
  export const HOST_REPORTS_API_REQUEST_KEY = 'HOST_REPORTS_API';
14
14
 
15
15
  export const HOST_REPORT_DELETE_MODAL_ID = 'hostReportDeleteModal';
16
+
17
+ export const RAW_MSG_MODAL_ID = 'rawMsgModal';