foreman_patch 1.1.6.alpha5 → 1.2.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/foreman_patch/api/v2/invocations_controller.rb +1 -23
- data/app/controllers/foreman_patch/concerns/hosts_controller_extensions.rb +41 -35
- data/app/helpers/foreman_patch/hosts_helper.rb +1 -1
- data/app/lib/actions/foreman_patch/cycle/complete.rb +41 -0
- data/app/lib/actions/foreman_patch/cycle/create.rb +1 -5
- data/app/lib/actions/foreman_patch/cycle/initiate.rb +1 -5
- data/app/lib/actions/foreman_patch/cycle/plan.rb +66 -0
- data/app/lib/actions/foreman_patch/invocation/action.rb +29 -23
- data/app/lib/actions/foreman_patch/invocation/patch.rb +8 -16
- data/app/lib/actions/foreman_patch/invocation/wait_for_host.rb +29 -4
- data/app/lib/actions/foreman_patch/round/patch.rb +1 -5
- data/app/lib/actions/foreman_patch/round/plan.rb +33 -0
- data/app/lib/actions/foreman_patch/window/plan.rb +43 -0
- data/app/lib/actions/foreman_patch/window/publish.rb +1 -5
- data/app/lib/actions/foreman_patch/window/resolve_hosts.rb +1 -5
- data/app/models/foreman_patch/invocation.rb +4 -4
- data/app/models/foreman_patch/round.rb +23 -4
- data/app/models/setting/patching.rb +57 -0
- data/app/services/foreman_patch/ticket/api.rb +1 -2
- data/app/views/foreman_patch/api/v2/invocations/base.json.rabl +1 -1
- data/app/views/foreman_patch/api/v2/invocations/phase.json.rabl +7 -0
- data/app/views/foreman_patch/api/v2/invocations/show.json.rabl +2 -2
- data/app/views/foreman_patch/api/v2/rounds/base.json.rabl +1 -1
- data/app/views/foreman_patch/api/v2/rounds/status.json.rabl +8 -2
- data/app/views/foreman_patch/groups/index.html.erb +1 -1
- data/app/views/foreman_patch/invocations/show.html.erb +26 -1
- data/app/views/foreman_patch/layouts/react.html.erb +1 -1
- data/app/views/templates/ensure_services.erb +4 -7
- data/config/api_routes.rb +24 -28
- data/config/routes/mount_engine.rb +3 -0
- data/config/routes/overrides.rb +10 -0
- data/config/routes.rb +29 -40
- data/db/seeds.d/100-assign_features_with_templates.rb +12 -6
- data/lib/foreman_patch/engine.rb +46 -11
- data/lib/foreman_patch/plugin.rb +47 -0
- data/lib/foreman_patch/version.rb +1 -1
- data/lib/foreman_patch.rb +2 -3
- data/locale/en/foreman_patch.po +1 -1
- data/locale/foreman_patch.pot +1 -1
- data/locale/gemspec.rb +1 -1
- data/package.json +9 -20
- data/public/assets/foreman_patch/cycle_plans-e5667e178ba389908f5c815b24ec0ea77c340849d56bc39c5ce72bb626bd446a.scss +6 -0
- data/public/assets/foreman_patch/cycle_plans-e5667e178ba389908f5c815b24ec0ea77c340849d56bc39c5ce72bb626bd446a.scss.gz +0 -0
- data/public/assets/foreman_patch/cycle_plans-ff3d252119622a68828ff70f4a97328303963002237dbf850e92d6a706e93667.scss.gz +0 -0
- data/public/assets/foreman_patch/foreman_patch-be2e2ba89548f4a490612e8a6cd1cdebc0473be89f8023a3df7612f05a75d301.css.gz +0 -0
- data/public/assets/foreman_patch/foreman_patch-ce5805a60c0d5f896f557ff5246e5a09172043004c850b39bea54e618df1c485.css +1 -0
- data/public/assets/foreman_patch/foreman_patch-ce5805a60c0d5f896f557ff5246e5a09172043004c850b39bea54e618df1c485.css.gz +0 -0
- data/public/assets/foreman_patch/foreman_patch.json +1 -1
- data/public/assets/foreman_patch/plan_edit_windows-9ba20f84f3ecf2c4eb903acd57d30ee3e16f023a79db30bc614aa22f26442ce3.js.gz +0 -0
- data/public/assets/foreman_patch/plan_edit_windows-e656ba411642a7f983b51958ab30ac49c056322d19295a603cff4d5e6c71c8ed.js +1 -0
- data/public/assets/foreman_patch/plan_edit_windows-e656ba411642a7f983b51958ab30ac49c056322d19295a603cff4d5e6c71c8ed.js.gz +0 -0
- data/public/webpack/foreman_patch/bundle-e45c4bb530e40506f2da.js +6 -0
- data/public/webpack/foreman_patch/bundle-e45c4bb530e40506f2da.js.gz +0 -0
- data/public/webpack/foreman_patch/bundle-e45c4bb530e40506f2da.js.map +1 -0
- data/public/webpack/foreman_patch/bundle-e45c4bb530e40506f2da.js.map.gz +0 -0
- data/public/webpack/foreman_patch/{foreman_patch-8909c3e06f012a43f769.css → foreman_patch-4a4e1a59d74af09c4b8b.css} +1 -1
- data/public/webpack/foreman_patch/{foreman_patch-8909c3e06f012a43f769.css.gz → foreman_patch-4a4e1a59d74af09c4b8b.css.gz} +0 -0
- data/public/webpack/foreman_patch/foreman_patch-4a4e1a59d74af09c4b8b.js +6 -0
- data/public/webpack/foreman_patch/foreman_patch-4a4e1a59d74af09c4b8b.js.gz +0 -0
- data/public/webpack/foreman_patch/foreman_patch-4a4e1a59d74af09c4b8b.js.map +1 -0
- data/public/webpack/foreman_patch/foreman_patch-4a4e1a59d74af09c4b8b.js.map.gz +0 -0
- data/public/webpack/foreman_patch/manifest.json +8 -13
- data/public/webpack/foreman_patch/manifest.json.gz +0 -0
- data/public/webpack/foreman_patch/{vendor-769bd77f6be96c3c37e1.js → vendor-4b77c91f1e9103179596.js} +2 -2
- data/public/webpack/foreman_patch/vendor-4b77c91f1e9103179596.js.gz +0 -0
- data/public/webpack/foreman_patch/{vendor-769bd77f6be96c3c37e1.js.map → vendor-4b77c91f1e9103179596.js.map} +1 -1
- data/public/webpack/foreman_patch/vendor-4b77c91f1e9103179596.js.map.gz +0 -0
- data/webpack/components/Invocation/Invocation.js +47 -0
- data/webpack/{src/Components → components}/Invocation/InvocationSelectors.js +3 -0
- data/webpack/components/Invocation/index.js +36 -0
- data/webpack/components/Invocations/Invocations.js +16 -48
- data/webpack/components/Invocations/InvocationsPage.js +1 -24
- data/webpack/components/Invocations/InvocationsSelectors.js +1 -1
- data/webpack/components/Invocations/components/{InvocationActions.js → InvocationItem.js} +16 -4
- data/webpack/components/Invocations/index.js +12 -95
- data/webpack/components/RoundProgress/AggregateStatus.js +5 -6
- data/webpack/components/RoundProgress/RoundProgress.js +6 -7
- data/webpack/components/RoundProgress/RoundProgressSelectors.js +2 -3
- data/webpack/components/common/Calendar/Calendar.js +4 -5
- data/webpack/components/common/Terminal/OutputLine.js +26 -0
- data/webpack/components/common/Terminal/Terminal.js +115 -0
- data/webpack/components/common/Terminal/Terminal.scss +47 -0
- data/webpack/index.js +0 -3
- metadata +91 -54
- data/app/controllers/foreman_patch/react_controller.rb +0 -12
- data/app/lib/actions/foreman_patch/invocation/process_logging.rb +0 -44
- data/app/lib/actions/foreman_patch/invocation/proxy_action.rb +0 -52
- data/app/models/foreman_patch/event.rb +0 -13
- data/app/views/foreman_patch/api/v2/invocations/event.json.rabl +0 -3
- data/db/migrate/20230706092400_nullify_group_on_delete.rb +0 -11
- data/db/migrate/20230707102800_create_invocation_events.rb +0 -16
- data/lib/foreman_patch/register.rb +0 -119
- data/public/webpack/foreman_patch/bundle-831173d6ae39953b2409.js +0 -6
- data/public/webpack/foreman_patch/bundle-831173d6ae39953b2409.js.gz +0 -0
- data/public/webpack/foreman_patch/bundle-831173d6ae39953b2409.js.map +0 -1
- data/public/webpack/foreman_patch/bundle-831173d6ae39953b2409.js.map.gz +0 -0
- data/public/webpack/foreman_patch/foreman_patch-8909c3e06f012a43f769.js +0 -6
- data/public/webpack/foreman_patch/foreman_patch-8909c3e06f012a43f769.js.gz +0 -0
- data/public/webpack/foreman_patch/foreman_patch-8909c3e06f012a43f769.js.map +0 -1
- data/public/webpack/foreman_patch/foreman_patch-8909c3e06f012a43f769.js.map.gz +0 -0
- data/public/webpack/foreman_patch/foreman_patch:global-d71cc6e1e759f04f631a.css +0 -1
- data/public/webpack/foreman_patch/foreman_patch:global-d71cc6e1e759f04f631a.css.gz +0 -0
- data/public/webpack/foreman_patch/foreman_patch:global-d71cc6e1e759f04f631a.js +0 -6
- data/public/webpack/foreman_patch/foreman_patch:global-d71cc6e1e759f04f631a.js.gz +0 -0
- data/public/webpack/foreman_patch/foreman_patch:global-d71cc6e1e759f04f631a.js.map +0 -1
- data/public/webpack/foreman_patch/foreman_patch:global-d71cc6e1e759f04f631a.js.map.gz +0 -0
- data/public/webpack/foreman_patch/vendor-769bd77f6be96c3c37e1.js.gz +0 -0
- data/public/webpack/foreman_patch/vendor-769bd77f6be96c3c37e1.js.map.gz +0 -0
- data/webpack/components/common/Table/index.js +0 -28
- data/webpack/global_index.js +0 -16
- data/webpack/src/Components/Invocation/Invocation.js +0 -67
- data/webpack/src/Components/Invocation/InvocationLogFooter.js +0 -30
- data/webpack/src/Components/Invocation/InvocationLogToolbar.js +0 -80
- data/webpack/src/Components/Invocation/index.js +0 -62
- data/webpack/src/Components/InvocationStatus.js +0 -50
- data/webpack/src/Components/Loading.js +0 -51
- data/webpack/src/Extends/index.js +0 -15
- data/webpack/src/Router/routes.js +0 -5
- data/webpack/src/reducers.js +0 -7
- /data/public/webpack/foreman_patch/{bundle-831173d6ae39953b2409.css → bundle-e45c4bb530e40506f2da.css} +0 -0
- /data/public/webpack/foreman_patch/{bundle-831173d6ae39953b2409.css.gz → bundle-e45c4bb530e40506f2da.css.gz} +0 -0
- /data/webpack/{src/Components → components}/Invocation/InvocationActions.js +0 -0
- /data/webpack/{src/Components → components}/Invocation/InvocationConsts.js +0 -0
- /data/webpack/components/Invocations/{Invocations.css → InvocationsPage.scss} +0 -0
- /data/webpack/components/common/Calendar/{Calendar.css → Calendar.scss} +0 -0
Binary file
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
import { Tabs, Tab, TabTitleText, LoadingState } from 'patternfly-react';
|
5
|
+
import { STATUS } from 'foremanReact/constants';
|
6
|
+
|
7
|
+
import Terminal from '../common/Terminal/Terminal';
|
8
|
+
|
9
|
+
const Invocation = ({ invocation, status }) => {
|
10
|
+
if (status === STATUS.ERROR) {
|
11
|
+
return (
|
12
|
+
<Alert type="error">
|
13
|
+
{__(
|
14
|
+
'There was an error loading the invocation, try refreshing the page.'
|
15
|
+
)}
|
16
|
+
</Alert>
|
17
|
+
);
|
18
|
+
}
|
19
|
+
|
20
|
+
const [activeTabKey, setActiveTabKey] = useState(0);
|
21
|
+
|
22
|
+
const handleTabClick = (event, tabIndex) => {
|
23
|
+
setActiveTabKey(tabIndex);
|
24
|
+
};
|
25
|
+
|
26
|
+
return (
|
27
|
+
<LoadingState loading={status == STATUS.PENDING}>
|
28
|
+
<Tabs activeKey={activeTabKey} onSelect={handleTabClick}>
|
29
|
+
<Tab eventKey={0} title={<TabTitleText>Overview</TabTitleText>}>
|
30
|
+
TODO: status
|
31
|
+
</Tab>
|
32
|
+
{invocation.phases.map((phase, index) => (
|
33
|
+
<Tab event={index + 1} title={<TabTitleText>{phase.humanized_name}</TabTitleText>}>
|
34
|
+
<Terminal key={phase.label} linesets={phase.live_output}/>
|
35
|
+
</Tab>
|
36
|
+
))}
|
37
|
+
</Tabs>
|
38
|
+
</LoadingState>
|
39
|
+
);
|
40
|
+
};
|
41
|
+
|
42
|
+
Invocation.propTypes = {
|
43
|
+
status: PropTypes.string.isRequired,
|
44
|
+
invocation: PropTypes.object.isRequired,
|
45
|
+
};
|
46
|
+
|
47
|
+
export default Invocation;
|
@@ -7,5 +7,8 @@ import { INVOCATION } from './InvocationConsts';
|
|
7
7
|
export const selectInvocation = state =>
|
8
8
|
selectAPIResponse(state, INVOCATION) || {};
|
9
9
|
|
10
|
+
export const selectState = state =>
|
11
|
+
selectAPIResponse(state, INVOCATION).state;
|
12
|
+
|
10
13
|
export const selectStatus = state =>
|
11
14
|
selectAPIStatus(state, INVOCATION);
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import React { useEffect, useState } from 'react';
|
2
|
+
import { useSelector, useDispatch } from 'react-redux';
|
3
|
+
import { stopInterval } from 'foremanReact/redux/middlewares/IntervalMiddleware';
|
4
|
+
import Invocation from './Invocation';
|
5
|
+
import {
|
6
|
+
selectInvocation,
|
7
|
+
selectState,
|
8
|
+
selectStatus,
|
9
|
+
} from './InvocationSelectors';
|
10
|
+
import { getData } from './InvocationActions';
|
11
|
+
import { INVOCATION } from './InvocationsConsts';
|
12
|
+
|
13
|
+
const WrappedInvocation = () => {
|
14
|
+
const dispatch = useDispatch();
|
15
|
+
const state = useSelector(selectState);
|
16
|
+
const invocation = useSelector(selectInvocation);
|
17
|
+
const status = useSelector(selectStatus);
|
18
|
+
|
19
|
+
useEffect(() => {
|
20
|
+
dispatch(getData());
|
21
|
+
|
22
|
+
return () => {
|
23
|
+
dispatch(stopInterval(INVOCATION));
|
24
|
+
};
|
25
|
+
}, [dispatch]);
|
26
|
+
|
27
|
+
useEffect(() => {
|
28
|
+
if (state === 'stopped') {
|
29
|
+
dispatch(stopInterval(INVOCATION));
|
30
|
+
}
|
31
|
+
}, [state, dispatch]);
|
32
|
+
|
33
|
+
return <Invocation invocation={invocation} status={status}/>
|
34
|
+
};
|
35
|
+
|
36
|
+
export default WrappedInvocation;
|
@@ -2,13 +2,11 @@ import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { translate as __ } from 'foremanReact/common/I18n';
|
4
4
|
import { LoadingState, Alert } from 'patternfly-react';
|
5
|
-
import { TableComposable, Thead, Tr, Th, Tbody, Td } from '@patternfly/react-table';
|
6
5
|
import { STATUS } from 'foremanReact/constants';
|
7
6
|
|
8
|
-
import
|
9
|
-
import InvocationActions from './components/InvocationActions';
|
7
|
+
import InvocationItem from './components/InvocationItem';
|
10
8
|
|
11
|
-
const Invocations = ({ status, items
|
9
|
+
const Invocations = ({ status, items }) => {
|
12
10
|
if (status === STATUS.ERROR) {
|
13
11
|
return (
|
14
12
|
<Alert type="error">
|
@@ -20,51 +18,26 @@ const Invocations = ({ status, items, selectAll, areAllSelected, onSelect, isSel
|
|
20
18
|
}
|
21
19
|
|
22
20
|
const rows = items.length ? (
|
23
|
-
items.map(
|
24
|
-
<Tr key={`invocation-${item.id}`}>
|
25
|
-
<Td
|
26
|
-
select={{
|
27
|
-
rowIndex,
|
28
|
-
onSelect: (_event, isSelecting) => onSelect(item, rowIndex, isSelecting),
|
29
|
-
isSelected: isSelected(item)
|
30
|
-
}}
|
31
|
-
/>
|
32
|
-
<Td className="invocation_name">
|
33
|
-
<a href={`/foreman_patch/invocations/${item.id}`}>{item.name}</a>
|
34
|
-
</Td>
|
35
|
-
<Td>
|
36
|
-
<InvocationStatus status={item.status}/>
|
37
|
-
</Td>
|
38
|
-
<Td>
|
39
|
-
<InvocationActions {...item}/>
|
40
|
-
</Td>
|
41
|
-
</Tr>
|
42
|
-
))
|
21
|
+
items.map(item => (<InvocationItem {...item} />))
|
43
22
|
) : (
|
44
|
-
<
|
45
|
-
<
|
46
|
-
</
|
23
|
+
<tr>
|
24
|
+
<td colSpan="3">{__('No hosts found.')}</td>
|
25
|
+
</tr>
|
47
26
|
);
|
48
27
|
|
49
28
|
return (
|
50
29
|
<LoadingState loading={!items.length && status === STATUS.PENDING}>
|
51
30
|
<div>
|
52
|
-
<
|
53
|
-
<
|
54
|
-
<
|
55
|
-
<
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
<Th className="col-md-2">{__('Status')}</Th>
|
63
|
-
<Th className="col-md-1">{__('Actions')}</Th>
|
64
|
-
</Tr>
|
65
|
-
</Thead>
|
66
|
-
<Tbody>{rows}</Tbody>
|
67
|
-
</TableComposable>
|
31
|
+
<table className="table table-bordered table-striped table-hover">
|
32
|
+
<thead>
|
33
|
+
<tr>
|
34
|
+
<th>{__('Host')}</th>
|
35
|
+
<th className="col-md-2">{__('Status')}</th>
|
36
|
+
<th className="col-md-1">{__('Actions')}</th>
|
37
|
+
</tr>
|
38
|
+
</thead>
|
39
|
+
<tbody>{rows}</tbody>
|
40
|
+
</table>
|
68
41
|
</div>
|
69
42
|
</LoadingState>
|
70
43
|
);
|
@@ -73,15 +46,10 @@ const Invocations = ({ status, items, selectAll, areAllSelected, onSelect, isSel
|
|
73
46
|
Invocations.propTypes = {
|
74
47
|
status: PropTypes.string,
|
75
48
|
items: PropTypes.array.isRequired,
|
76
|
-
selectAll: PropTypes.func.isRequired,
|
77
|
-
areAllSelected: PropTypes.bool,
|
78
|
-
onSelect: PropTypes.func.isRequired,
|
79
|
-
isSelected: PropTypes.func.isRequired,
|
80
49
|
};
|
81
50
|
|
82
51
|
Invocations.defaultProps = {
|
83
52
|
status: null,
|
84
|
-
areAllSelected: false,
|
85
53
|
};
|
86
54
|
|
87
55
|
export default Invocations;
|
@@ -4,7 +4,6 @@ import { Grid } from 'patternfly-react';
|
|
4
4
|
|
5
5
|
import SearchBar from 'foremanReact/components/SearchBar';
|
6
6
|
import Pagination from 'foremanReact/components/Pagination/PaginationWrapper';
|
7
|
-
import { ActionButtons } from 'foremanReact/components/common/ActionButtons/ActionButtons';
|
8
7
|
import { getControllerSearchProps } from 'foremanReact/constants';
|
9
8
|
|
10
9
|
import Invocations from './Invocations';
|
@@ -18,11 +17,6 @@ const InvocationsPage = ({
|
|
18
17
|
pagination,
|
19
18
|
handleSearch,
|
20
19
|
handlePagination,
|
21
|
-
selectAll,
|
22
|
-
areAllSelected,
|
23
|
-
onSelect,
|
24
|
-
isSelected,
|
25
|
-
actions
|
26
20
|
}) => (
|
27
21
|
<div id="patch_invocations">
|
28
22
|
<Grid.Row>
|
@@ -41,19 +35,9 @@ const InvocationsPage = ({
|
|
41
35
|
}}
|
42
36
|
/>
|
43
37
|
</Grid.Col>
|
44
|
-
<Grid.Col md={6} className="title_action">
|
45
|
-
<ActionButtons buttons={[]} />
|
46
|
-
</Grid.Col>
|
47
38
|
</Grid.Row>
|
48
39
|
<br />
|
49
|
-
<Invocations
|
50
|
-
status={status}
|
51
|
-
items={items}
|
52
|
-
selectAll={selectAll}
|
53
|
-
areAllSelected={areAllSelected}
|
54
|
-
onSelect={onSelect}
|
55
|
-
isSelected={isSelected}
|
56
|
-
/>
|
40
|
+
<Invocations status={status} items={items} />
|
57
41
|
<Pagination
|
58
42
|
viewType="table"
|
59
43
|
itemCount={total}
|
@@ -73,17 +57,10 @@ InvocationsPage.propTypes = {
|
|
73
57
|
pagination: PropTypes.object.isRequired,
|
74
58
|
handleSearch: PropTypes.func.isRequired,
|
75
59
|
handlePagination: PropTypes.func.isRequired,
|
76
|
-
selectAll: PropTypes.func.isRequired,
|
77
|
-
areAllSelected: PropTypes.bool,
|
78
|
-
onSelect: PropTypes.func.isRequired,
|
79
|
-
isSelected: PropTypes.func.isRequired,
|
80
|
-
actions: PropTypes.array,
|
81
60
|
};
|
82
61
|
|
83
62
|
InvocationsPage.defaultProps = {
|
84
63
|
status: null,
|
85
|
-
areAllSelected: false,
|
86
|
-
actions: []
|
87
64
|
};
|
88
65
|
|
89
66
|
export default InvocationsPage;
|
@@ -12,7 +12,7 @@ export const selectTotal = state =>
|
|
12
12
|
selectAPIResponse(state, INVOCATIONS).total || 0;
|
13
13
|
|
14
14
|
export const selectAutoRefresh = state =>
|
15
|
-
selectItems(state).some(item => (item.
|
15
|
+
selectItems(state).some(item => (item.result === 'pending'));
|
16
16
|
|
17
17
|
export const selectStatus = state => selectAPIStatus(state, INVOCATIONS);
|
18
18
|
|
@@ -3,7 +3,9 @@ import PropTypes from 'prop-types';
|
|
3
3
|
import { ActionButtons } from 'foremanReact/components/common/ActionButtons/ActionButtons';
|
4
4
|
import { translate as __ } from 'foremanReact/common/I18n';
|
5
5
|
|
6
|
-
|
6
|
+
import InvocationStatus from './InvocationStatus';
|
7
|
+
|
8
|
+
const InvocationItem = ({ id, name, status, task_id, host_id }) => {
|
7
9
|
|
8
10
|
const actions = [
|
9
11
|
{
|
@@ -39,11 +41,21 @@ const InvocationActions = ({ id, name, status, task_id, host_id }) => {
|
|
39
41
|
}
|
40
42
|
|
41
43
|
return (
|
42
|
-
<
|
44
|
+
<tr id={`invocation-${id}`}>
|
45
|
+
<td className="invocation_name">
|
46
|
+
<a href={`/foreman_patch/invocations/${id}`}>{name}</a>
|
47
|
+
</td>
|
48
|
+
<td className="invocation_status">
|
49
|
+
<InvocationStatus status={status} />
|
50
|
+
</td>
|
51
|
+
<td className="invocation_actions">
|
52
|
+
<ActionButtons buttons={[...actions]} />
|
53
|
+
</td>
|
54
|
+
</tr>
|
43
55
|
);
|
44
56
|
};
|
45
57
|
|
46
|
-
|
58
|
+
InvocationItem.propTypes = {
|
47
59
|
id: PropTypes.number.isRequired,
|
48
60
|
name: PropTypes.string.isRequired,
|
49
61
|
status: PropTypes.string.isRequired,
|
@@ -51,4 +63,4 @@ InvocationActions.propTypes = {
|
|
51
63
|
task_id: PropTypes.number.isRequired,
|
52
64
|
};
|
53
65
|
|
54
|
-
export default
|
66
|
+
export default InvocationItem;
|
@@ -1,21 +1,14 @@
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
2
2
|
import { useSelector, useDispatch } from 'react-redux';
|
3
3
|
import PropTypes from 'prop-types';
|
4
|
-
import { Grid } from 'patternfly-react';
|
5
4
|
|
6
|
-
import {
|
5
|
+
import {
|
7
6
|
withInterval,
|
8
7
|
stopInterval
|
9
8
|
} from 'foremanReact/redux/middlewares/IntervalMiddleware';
|
10
9
|
import { get } from 'foremanReact/redux/API';
|
11
10
|
import { useForemanSettings } from 'foremanReact/Root/Context/ForemanContext';
|
12
|
-
import { getControllerSearchProps } from 'foremanReact/constants';
|
13
|
-
import { translate as __ } from 'foremanReact/common/I18n';
|
14
|
-
import SearchBar from 'foremanReact/components/SearchBar';
|
15
|
-
import Pagination from 'foremanReact/components/Pagination/PaginationWrapper';
|
16
|
-
import { ActionButtons } from 'foremanReact/components/common/ActionButtons/ActionButtons';
|
17
11
|
|
18
|
-
import Invocations from './Invocations';
|
19
12
|
import {
|
20
13
|
selectItems,
|
21
14
|
selectTotal,
|
@@ -25,8 +18,7 @@ import {
|
|
25
18
|
} from './InvocationsSelectors';
|
26
19
|
import { getUrl } from './InvocationsHelpers';
|
27
20
|
import { INVOCATIONS } from './InvocationsConstants';
|
28
|
-
|
29
|
-
import './Invocations.css';
|
21
|
+
import InvocationsPage from './InvocationsPage';
|
30
22
|
|
31
23
|
const WrappedInvocations = ({ round }) => {
|
32
24
|
const dispatch = useDispatch();
|
@@ -44,9 +36,6 @@ const WrappedInvocations = ({ round }) => {
|
|
44
36
|
});
|
45
37
|
const [url, setUrl] = useState(getUrl(round, searchQuery, pagination));
|
46
38
|
const intervalExists = useSelector(selectIntervalExists);
|
47
|
-
const [selectedItems, setSelectedItems] = useState([]);
|
48
|
-
const [recentSelectedItemIndex, setRecentSelectedRowIndex] = useState(null);
|
49
|
-
const [shifting, setShifting] = useState(false);
|
50
39
|
|
51
40
|
const handleSearch = query => {
|
52
41
|
const defaultPagination = { page: 1, perPage: pagination.perPage };
|
@@ -63,39 +52,13 @@ const WrappedInvocations = ({ round }) => {
|
|
63
52
|
setUrl(getUrl(round, searchQuery, args));
|
64
53
|
};
|
65
54
|
|
66
|
-
const selectAll = (isSelecting) => setSelectedItems(isSelecting ? items.map(item => item.id) : []);
|
67
|
-
|
68
|
-
const areAllSelected = selectedItems.length == items.length;
|
69
|
-
|
70
|
-
const setItemSelected = (item, isSelecting) =>
|
71
|
-
setSelectedItems(prevSelected => {
|
72
|
-
const otherSelectedItems = prevSelected.filter(i => i !== item.id);
|
73
|
-
return isSelecting ? [...otherSelectedItems, item.id] : otherSelectedItems;
|
74
|
-
});
|
75
|
-
|
76
|
-
const onSelect = (item, rowIndex, isSelecting) => {
|
77
|
-
if (shifting && recentSelectedRowIndex !== null) {
|
78
|
-
const numberSelected = rowIndex - recentSelectedRowIndex;
|
79
|
-
const intermediateIndexes =
|
80
|
-
numberSelected > 0
|
81
|
-
? Array.from(new Array(numberSelected + 1), (_x, i) => i + recentSelectedRowIndex)
|
82
|
-
: Array.from(new Array(Math.abs(numberSelected) + 1), (_x, i) => i + rowIndex);
|
83
|
-
intermediateIndexes.forEach(index, setItemSelected(items[index], isSelecting));
|
84
|
-
} else {
|
85
|
-
setItemSelected(item, isSelecting);
|
86
|
-
}
|
87
|
-
setRecentSelectedRowIndex(rowIndex);
|
88
|
-
};
|
89
|
-
|
90
|
-
const isSelected = item => selectedItems.includes(item.id);
|
91
|
-
|
92
55
|
const stopApiInterval = () => {
|
93
56
|
if (intervalExists) {
|
94
57
|
dispatch(stopInterval(INVOCATIONS));
|
95
58
|
}
|
96
59
|
};
|
97
60
|
|
98
|
-
const getData = url =>
|
61
|
+
const getData = url => withInterval(get({
|
99
62
|
key: INVOCATIONS,
|
100
63
|
url,
|
101
64
|
handleError: () => {
|
@@ -104,73 +67,27 @@ const WrappedInvocations = ({ round }) => {
|
|
104
67
|
}), 5000);
|
105
68
|
|
106
69
|
useEffect(() => {
|
107
|
-
const onKeyDown = (e) => {
|
108
|
-
if (e.key === 'Shift') {
|
109
|
-
setShifting(true);
|
110
|
-
}
|
111
|
-
};
|
112
|
-
const onKeyUp = (e) => {
|
113
|
-
if (e.key === 'Shift') {
|
114
|
-
setShifting(false);
|
115
|
-
}
|
116
|
-
};
|
117
|
-
|
118
70
|
dispatch(getData(url));
|
119
71
|
|
120
72
|
if (!autoRefresh) {
|
121
73
|
dispatch(stopInterval(INVOCATIONS));
|
122
74
|
}
|
123
75
|
|
124
|
-
document.addEventListener('keydown', onKeyDown);
|
125
|
-
document.addEventListener('keyup', onKeyUp);
|
126
|
-
|
127
76
|
return () => {
|
128
77
|
dispatch(stopInterval(INVOCATIONS));
|
129
|
-
document.removeEventListener('keydown', onKeyDown);
|
130
|
-
document.removeEventListener('keyup', onKeyUp);
|
131
78
|
};
|
132
79
|
}, [dispatch, url, autoRefresh]);
|
133
80
|
|
134
81
|
return (
|
135
|
-
<
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
searchQuery,
|
145
|
-
url: '/foreman_patch/invocations/auto_complete_search',
|
146
|
-
useKeyShortcuts: true,
|
147
|
-
},
|
148
|
-
bookmarks: {},
|
149
|
-
}}
|
150
|
-
/>
|
151
|
-
</Grid.Col>
|
152
|
-
<Grid.Col md={6}>
|
153
|
-
<ActionButtons buttons={actions} />
|
154
|
-
</Grid.Col>
|
155
|
-
</Grid.Row>
|
156
|
-
<br />
|
157
|
-
<Invocations
|
158
|
-
status={status}
|
159
|
-
items={items}
|
160
|
-
selectAll={selectAll}
|
161
|
-
areAllSelected={areAllSelected}
|
162
|
-
isSelected={isSelected}
|
163
|
-
onSelect={onSelect}
|
164
|
-
/>
|
165
|
-
<Pagination
|
166
|
-
viewType="table"
|
167
|
-
itemCount={total}
|
168
|
-
pagination={pagination}
|
169
|
-
onChange={handlePagination}
|
170
|
-
dropdownButtonId="invocations-pagination-dropdown"
|
171
|
-
className="invocations-pagination"
|
172
|
-
/>
|
173
|
-
</React.Fragment>
|
82
|
+
<InvocationsPage
|
83
|
+
status={status}
|
84
|
+
items={items}
|
85
|
+
total={total}
|
86
|
+
searchQuery={searchQuery}
|
87
|
+
handleSearch={handleSearch}
|
88
|
+
pagination={pagination}
|
89
|
+
handlePagination={handlePagination}
|
90
|
+
/>
|
174
91
|
);
|
175
92
|
};
|
176
93
|
|
@@ -19,7 +19,7 @@ const AggregateStatus = ({ progress }) => (
|
|
19
19
|
<span className="card-pf-aggregate-status-notification">
|
20
20
|
<span id="failed_count">
|
21
21
|
<span className="pficon pficon-error-circle-o" />
|
22
|
-
{progress.
|
22
|
+
{progress.failed}
|
23
23
|
</span>
|
24
24
|
</span>
|
25
25
|
<span className="card-pf-aggregate-status-notification">
|
@@ -31,7 +31,7 @@ const AggregateStatus = ({ progress }) => (
|
|
31
31
|
<span className="card-pf-aggregate-status-notification">
|
32
32
|
<span id="pending_count">
|
33
33
|
<span className="pficon pficon-pending" />
|
34
|
-
{progress.pending
|
34
|
+
{progress.pending}
|
35
35
|
</span>
|
36
36
|
</span>
|
37
37
|
<span className="card-pf-aggregate-status-notification">
|
@@ -46,12 +46,11 @@ const AggregateStatus = ({ progress }) => (
|
|
46
46
|
|
47
47
|
AggregateStatus.propTypes = {
|
48
48
|
progress: PropTypes.shape({
|
49
|
-
planned: PropTypes.number,
|
50
|
-
pending: PropTypes.number,
|
51
|
-
running: PropTypes.number,
|
52
49
|
success: PropTypes.number,
|
53
50
|
warning: PropTypes.number,
|
54
|
-
|
51
|
+
failed: PropTypes.number,
|
52
|
+
running: PropTypes.number,
|
53
|
+
pending: PropTypes.number,
|
55
54
|
cancelled: PropTypes.number,
|
56
55
|
}).isRequired,
|
57
56
|
};
|
@@ -11,14 +11,14 @@ const RoundProgress = ({ progress }) => {
|
|
11
11
|
const data = [
|
12
12
|
[__('Success'), progress.success, '#5CB85C'],
|
13
13
|
[__('Warning'), progress.warning, '#DB843D'],
|
14
|
-
[__('Failed'), progress.
|
14
|
+
[__('Failed'), progress.failed, '#D9534F'],
|
15
15
|
[__('Running'), progress.running, '#3D96AE'],
|
16
|
-
[__('Pending'), progress.pending
|
16
|
+
[__('Pending'), progress.pending, '#DEDEDE'],
|
17
17
|
[__('Cancelled'), progress.cancelled, '#B7312D'],
|
18
18
|
];
|
19
19
|
|
20
20
|
const iMax = data.reduce((im, e, i, arr) =>
|
21
|
-
(e[1] > arr[im][
|
21
|
+
(e[1] > arr[im][i] ? i : im), 0);
|
22
22
|
|
23
23
|
return (
|
24
24
|
<div id="round_progress">
|
@@ -36,12 +36,11 @@ const RoundProgress = ({ progress }) => {
|
|
36
36
|
|
37
37
|
RoundProgress.propTypes = {
|
38
38
|
progress: PropTypes.shape({
|
39
|
-
pending: PropTypes.number,
|
40
|
-
planned: PropTypes.number,
|
41
|
-
running: PropTypes.number,
|
42
39
|
success: PropTypes.number,
|
43
40
|
warning: PropTypes.number,
|
44
|
-
|
41
|
+
failed: PropTypes.number,
|
42
|
+
running: PropTypes.number,
|
43
|
+
pending: PropTypes.number,
|
45
44
|
cancelled: PropTypes.number,
|
46
45
|
}).isRequired,
|
47
46
|
};
|
@@ -8,10 +8,9 @@ import { ROUND_PROGRESS } from './RoundProgressConstants';
|
|
8
8
|
const defaultProgress = {
|
9
9
|
success: 0,
|
10
10
|
warning: 0,
|
11
|
-
|
11
|
+
failed: 0,
|
12
12
|
running: 0,
|
13
13
|
pending: 0,
|
14
|
-
planned: 0,
|
15
14
|
cancelled: 0,
|
16
15
|
};
|
17
16
|
|
@@ -19,7 +18,7 @@ export const selectProgress = state =>
|
|
19
18
|
selectAPIResponse(state, ROUND_PROGRESS).progress || defaultProgress;
|
20
19
|
|
21
20
|
export const selectAutoRefresh = state =>
|
22
|
-
selectAPIResponse(state, ROUND_PROGRESS).
|
21
|
+
!selectAPIResponse(state, ROUND_PROGRESS).complete;
|
23
22
|
|
24
23
|
export const selectStatus = state =>
|
25
24
|
selectAPIStatus(state, ROUND_PROGRESS);
|
@@ -2,13 +2,12 @@ import React, { useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
3
3
|
import { DndProvider } from 'react-dnd';
|
4
4
|
import HTML5Backend from 'react-dnd-html5-backend';
|
5
|
-
|
6
|
-
import './Calendar.css';
|
5
|
+
import './Calendar.scss';
|
7
6
|
|
8
7
|
import { views, getView } from './View';
|
9
8
|
|
10
9
|
const Calendar = (props) => {
|
11
|
-
const {
|
10
|
+
const {start, end, weekStartsOn, locale, onEventMoved} = props;
|
12
11
|
|
13
12
|
const initialDate = () => {
|
14
13
|
const { date } = props;
|
@@ -28,7 +27,7 @@ const Calendar = (props) => {
|
|
28
27
|
|
29
28
|
const result = events.map(event => {
|
30
29
|
if (event.id === updatedEvent.id) {
|
31
|
-
return {
|
30
|
+
return {...event, ...updatedEvent};
|
32
31
|
}
|
33
32
|
return event;
|
34
33
|
});
|
@@ -79,7 +78,7 @@ Calendar.defaultProps = {
|
|
79
78
|
date: new Date(),
|
80
79
|
view: views.MONTH,
|
81
80
|
events: [],
|
82
|
-
onEventMoved: (event) => {
|
81
|
+
onEventMoved: (event) => {},
|
83
82
|
locale: 'en-US',
|
84
83
|
weekStartsOn: 1,
|
85
84
|
};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import React, { useState } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { classnames } from 'classname';
|
4
|
+
|
5
|
+
const OutputLine = ({index, type, children}) => {
|
6
|
+
return (
|
7
|
+
<div className={classnames('line', type)} >
|
8
|
+
<span className="counter">{index}</span>
|
9
|
+
<div className="content">
|
10
|
+
{children}
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
);
|
14
|
+
};
|
15
|
+
|
16
|
+
OutputLine.propTypes = {
|
17
|
+
index: PropTypes.number.isRequired,
|
18
|
+
type: PropTypes.string,
|
19
|
+
timestamp: PropTypes.number,
|
20
|
+
};
|
21
|
+
|
22
|
+
OutputLine.defaultProps = {
|
23
|
+
type: 'stdout',
|
24
|
+
};
|
25
|
+
|
26
|
+
export default OutputLine;
|