foreman_scc_manager 5.1.0 → 5.3.0

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_scc_manager/locale/de/foreman_scc_manager.js +42 -9
  3. data/app/assets/javascripts/foreman_scc_manager/locale/el/foreman_scc_manager.js +42 -9
  4. data/app/assets/javascripts/foreman_scc_manager/locale/en/foreman_scc_manager.js +38 -5
  5. data/app/assets/javascripts/foreman_scc_manager/locale/fr/foreman_scc_manager.js +43 -10
  6. data/app/assets/javascripts/foreman_scc_manager/locale/ja/foreman_scc_manager.js +43 -10
  7. data/app/assets/javascripts/foreman_scc_manager/locale/ka/foreman_scc_manager.js +43 -10
  8. data/app/assets/javascripts/foreman_scc_manager/locale/ko/foreman_scc_manager.js +43 -10
  9. data/app/assets/javascripts/foreman_scc_manager/locale/zh_CN/foreman_scc_manager.js +43 -10
  10. data/app/controllers/api/v2/scc_accounts_controller.rb +2 -0
  11. data/app/controllers/scc_accounts_controller.rb +0 -3
  12. data/app/views/api/v2/scc_accounts/main.json.rabl +1 -1
  13. data/app/views/scc_accounts/index.html.erb +14 -33
  14. data/lib/foreman_scc_manager/version.rb +1 -1
  15. data/locale/de/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  16. data/locale/de/foreman_scc_manager.po +46 -18
  17. data/locale/el/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  18. data/locale/el/foreman_scc_manager.po +43 -15
  19. data/locale/en/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  20. data/locale/en/foreman_scc_manager.po +38 -11
  21. data/locale/foreman_scc_manager.pot +105 -50
  22. data/locale/fr/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  23. data/locale/fr/foreman_scc_manager.po +47 -19
  24. data/locale/ja/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  25. data/locale/ja/foreman_scc_manager.po +47 -16
  26. data/locale/ka/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  27. data/locale/ka/foreman_scc_manager.po +44 -21
  28. data/locale/ko/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  29. data/locale/ko/foreman_scc_manager.po +46 -18
  30. data/locale/zh_CN/LC_MESSAGES/foreman_scc_manager.mo +0 -0
  31. data/locale/zh_CN/foreman_scc_manager.po +47 -17
  32. data/test/controllers/api/v2/scc_accounts_test.rb +30 -0
  33. data/test/fixtures/models/scc_accounts.yml +2 -0
  34. data/webpack/components/SCCAccountIndex/SCCAccountIndex.scss +26 -0
  35. data/webpack/components/SCCAccountIndex/SCCAccountIndex.test.js +291 -0
  36. data/webpack/components/SCCAccountIndex/SCCAccountIndexActions.js +205 -0
  37. data/webpack/components/SCCAccountIndex/SCCAccountIndexConstants.js +9 -0
  38. data/webpack/components/SCCAccountIndex/index.js +262 -0
  39. data/webpack/components/SCCProductPage/EmptySccProducts.js +10 -7
  40. data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCGenericPicker/index.js +25 -11
  41. data/webpack/components/SCCProductPage/components/SCCProductPicker/components/SCCTreePicker/components/SCCRepoPicker/index.js +1 -1
  42. data/webpack/components/SCCProductPage/components/SCCProductPicker/styles.scss +8 -3
  43. data/webpack/components/SCCProductPage/sccProductPage.scss +5 -0
  44. data/webpack/index.js +6 -0
  45. metadata +8 -3
@@ -0,0 +1,262 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import {
4
+ PageSection,
5
+ Modal,
6
+ Button,
7
+ Dropdown,
8
+ DropdownList,
9
+ DropdownItem,
10
+ MenuToggle,
11
+ } from '@patternfly/react-core';
12
+ import { Table, Thead, Tr, Th, Td, Tbody } from '@patternfly/react-table';
13
+ import { EllipsisVIcon } from '@patternfly/react-icons';
14
+ import { useDispatch } from 'react-redux';
15
+ import { translate as __ } from 'foremanReact/common/I18n';
16
+ import { foremanUrl } from 'foremanReact/common/helpers';
17
+ import { WARN_DELETE } from './SCCAccountIndexConstants';
18
+ import {
19
+ syncSccAccountAction,
20
+ deleteSccAccountAction,
21
+ } from './SCCAccountIndexActions';
22
+ import './SCCAccountIndex.scss';
23
+
24
+ function SccAccountsIndex({ initialAccounts }) {
25
+ const dispatch = useDispatch();
26
+ const [deleteOpen, setDeleteOpen] = useState(false);
27
+ const [openMenuRow, setOpenMenuRow] = useState(null);
28
+ const [accounts, setAccounts] = useState(initialAccounts);
29
+ const [selectedAccountName, setSelectedAccountName] = useState(null);
30
+ const deletingIdRef = useRef(null);
31
+ const taskTimeoutRef = useRef({});
32
+ const lastStateRef = useRef({});
33
+
34
+ const handleSync = (accountId) => {
35
+ syncSccAccountAction(
36
+ dispatch,
37
+ accountId,
38
+ setAccounts,
39
+ taskTimeoutRef,
40
+ lastStateRef
41
+ );
42
+ };
43
+
44
+ const handleDelete = (id) => {
45
+ deleteSccAccountAction(
46
+ dispatch,
47
+ id,
48
+ setAccounts,
49
+ setDeleteOpen,
50
+ deletingIdRef
51
+ );
52
+ };
53
+
54
+ useEffect(
55
+ () => () => {
56
+ Object.values(taskTimeoutRef.current).forEach(clearTimeout);
57
+ taskTimeoutRef.current = {};
58
+ },
59
+ []
60
+ );
61
+
62
+ return (
63
+ <PageSection ouiaId="scc-accounts-index-page-section">
64
+ <div className="scc-account-add-container">
65
+ <Button
66
+ component="a"
67
+ href={foremanUrl('/scc_accounts/new')}
68
+ variant="primary"
69
+ ouiaId="scc-account-add-button"
70
+ >
71
+ {__('Add SCC account')}
72
+ </Button>
73
+ </div>
74
+
75
+ <Table
76
+ aria-label={__('SUSE subscriptions')}
77
+ ouiaId="scc-accounts-table"
78
+ variant="compact"
79
+ >
80
+ <Thead ouiaId="scc-accounts-table-head">
81
+ <Tr ouiaId="scc-accounts-table-header-row">
82
+ <Th ouiaId="scc-accounts-name-header">{__('Name')}</Th>
83
+ <Th ouiaId="scc-accounts-products-header">{__('Products')}</Th>
84
+ <Th ouiaId="scc-accounts-last-synced-header">
85
+ {__('Last synced')}
86
+ </Th>
87
+ <Th width={10} ouiaId="scc-accounts-actions-header">
88
+ {__('Actions')}
89
+ </Th>
90
+ </Tr>
91
+ </Thead>
92
+ <Tbody ouiaId="scc-accounts-table-body">
93
+ {accounts &&
94
+ accounts.map((acc) => {
95
+ const lastSynced = acc.sync_task ? (
96
+ <a
97
+ target="_blank"
98
+ href={foremanUrl(`/foreman_tasks/tasks/${acc.sync_task.id}`)}
99
+ rel="noreferrer"
100
+ >
101
+ {acc.sync_status}
102
+ </a>
103
+ ) : (
104
+ acc.sync_status || __('never synced')
105
+ );
106
+
107
+ return (
108
+ <Tr key={acc.id} ouiaId={`scc-account-row-${acc.id}`}>
109
+ <Td
110
+ dataLabel={__('Name')}
111
+ ouiaId={`scc-account-name-${acc.id}`}
112
+ >
113
+ <a href={`/scc_accounts/${acc.id}/edit`}>{acc.name}</a>
114
+ </Td>
115
+ <Td
116
+ dataLabel={__('Products')}
117
+ ouiaId={`scc-account-products-${acc.id}`}
118
+ >
119
+ {acc.scc_products_with_repos_count}
120
+ </Td>
121
+
122
+ <Td
123
+ dataLabel={__('Last synced')}
124
+ ouiaId={`scc-account-last-synced-${acc.id}`}
125
+ >
126
+ {lastSynced}
127
+ </Td>
128
+ <Td
129
+ dataLabel={__('Actions')}
130
+ ouiaId={`scc-account-actions-${acc.id}`}
131
+ >
132
+ <div className="scc-account-actions">
133
+ <Button
134
+ variant="primary"
135
+ size="sm"
136
+ onClick={() => {
137
+ window.location.href = `/scc_accounts/${acc.id}`;
138
+ }}
139
+ ouiaId={`scc-account-select-products-button-${acc.id}`}
140
+ >
141
+ {__('Select Products')}
142
+ </Button>
143
+
144
+ <Dropdown
145
+ isOpen={openMenuRow === acc.id}
146
+ onSelect={() => setOpenMenuRow(null)}
147
+ onOpenChange={(isOpen) =>
148
+ setOpenMenuRow(isOpen ? acc.id : null)
149
+ }
150
+ ouiaId={`scc-account-actions-dropdown-${acc.id}`}
151
+ toggle={(toggleRef) => (
152
+ <MenuToggle
153
+ ref={toggleRef}
154
+ aria-label={__('Actions menu')}
155
+ variant="plain"
156
+ isExpanded={openMenuRow === acc.id}
157
+ onClick={() =>
158
+ setOpenMenuRow(
159
+ openMenuRow === acc.id ? null : acc.id
160
+ )
161
+ }
162
+ ouiaId={`scc-account-actions-menu-toggle-${acc.id}`}
163
+ >
164
+ <EllipsisVIcon />
165
+ </MenuToggle>
166
+ )}
167
+ >
168
+ <DropdownList
169
+ ouiaId={`scc-account-actions-dropdown-list-${acc.id}`}
170
+ >
171
+ <DropdownItem
172
+ key="sync"
173
+ isDisabled={
174
+ acc.sync_status === 'running' ||
175
+ acc.sync_status === 'planned'
176
+ }
177
+ onClick={() => {
178
+ setOpenMenuRow(null);
179
+ handleSync(acc.id);
180
+ }}
181
+ ouiaId={`scc-account-sync-item-${acc.id}`}
182
+ >
183
+ {acc.sync_status === 'running' ||
184
+ acc.sync_status === 'planned'
185
+ ? __('Syncing...')
186
+ : __('Sync')}
187
+ </DropdownItem>
188
+
189
+ <DropdownItem
190
+ key="delete"
191
+ onClick={() => {
192
+ setOpenMenuRow(null);
193
+ deletingIdRef.current = acc.id;
194
+ setDeleteOpen(true);
195
+ setSelectedAccountName(acc.name);
196
+ }}
197
+ ouiaId={`scc-account-delete-item-${acc.id}`}
198
+ >
199
+ {__('Delete')}
200
+ </DropdownItem>
201
+ </DropdownList>
202
+ </Dropdown>
203
+ </div>
204
+ </Td>
205
+ </Tr>
206
+ );
207
+ })}
208
+ </Tbody>
209
+ </Table>
210
+
211
+ <Modal
212
+ title={__('Delete SCC Account')}
213
+ isOpen={deleteOpen}
214
+ onClose={() => setDeleteOpen(false)}
215
+ variant="small"
216
+ ouiaId="scc-account-delete-modal"
217
+ actions={[
218
+ <Button
219
+ key="confirm"
220
+ variant="danger"
221
+ onClick={() => handleDelete(deletingIdRef.current)}
222
+ ouiaId="scc-account-delete-confirm-button"
223
+ >
224
+ {__('Delete')}
225
+ </Button>,
226
+ <Button
227
+ key="cancel"
228
+ variant="link"
229
+ onClick={() => setDeleteOpen(false)}
230
+ ouiaId="scc-account-delete-cancel-button"
231
+ >
232
+ {__('Cancel')}
233
+ </Button>,
234
+ ]}
235
+ >
236
+ <div className="scc-account-delete-warning">
237
+ {WARN_DELETE.replace('%acc_name', selectedAccountName || '')}
238
+ </div>
239
+ </Modal>
240
+ </PageSection>
241
+ );
242
+ }
243
+
244
+ SccAccountsIndex.propTypes = {
245
+ initialAccounts: PropTypes.arrayOf(
246
+ PropTypes.shape({
247
+ id: PropTypes.number.isRequired,
248
+ name: PropTypes.string.isRequired,
249
+ sync_status: PropTypes.string,
250
+ sync_task: PropTypes.shape({
251
+ id: PropTypes.string,
252
+ }),
253
+ scc_products_with_repos_count: PropTypes.number,
254
+ })
255
+ ),
256
+ };
257
+
258
+ SccAccountsIndex.defaultProps = {
259
+ initialAccounts: [],
260
+ };
261
+
262
+ export default SccAccountsIndex;
@@ -5,6 +5,7 @@ import { Button } from '@patternfly/react-core';
5
5
  import { translate as __ } from 'foremanReact/common/I18n';
6
6
  import EmptyState from 'foremanReact/components/common/EmptyState';
7
7
  import { syncSccAccountAction } from './SCCProductPageActions';
8
+ import './sccProductPage.scss';
8
9
 
9
10
  export const EmptySccProducts = ({ canCreate, sccAccountId }) => {
10
11
  const dispatch = useDispatch();
@@ -27,13 +28,15 @@ export const EmptySccProducts = ({ canCreate, sccAccountId }) => {
27
28
  'https://docs.orcharhino.com/or/docs/sources/usage_guides/managing_sles_systems_guide.html#mssg_adding_scc_accounts',
28
29
  }}
29
30
  />
30
- <Button
31
- onClick={onSyncStart}
32
- ouiaId="scc-manager-welcome-sync-products"
33
- className="btn btn-primary"
34
- >
35
- {__('Synchronize SUSE Account')}
36
- </Button>
31
+ <div className="scc-sync-button-container">
32
+ <Button
33
+ onClick={onSyncStart}
34
+ ouiaId="scc-manager-welcome-sync-products"
35
+ className="btn btn-primary"
36
+ >
37
+ {__('Synchronize SUSE Account')}
38
+ </Button>
39
+ </div>
37
40
  </>
38
41
  );
39
42
  };
@@ -14,6 +14,7 @@ import {
14
14
  } from '@patternfly/react-core';
15
15
  import { TimesIcon } from '@patternfly/react-icons';
16
16
  import { translate as __ } from 'foremanReact/common/I18n';
17
+ import '../../../SCCProductPicker/styles.scss';
17
18
 
18
19
  const GenericSelector = ({
19
20
  initialSelectOptions,
@@ -234,18 +235,31 @@ const GenericSelector = ({
234
235
  }}
235
236
  toggle={toggle}
236
237
  shouldFocusFirstItemOnOpen={false}
238
+ popperProps={{
239
+ direction: 'down',
240
+ appendTo: () => document.body,
241
+ }}
237
242
  >
238
- <SelectList id={initialLabel.concat('select-typeahead-listbox')}>
239
- {selectOptions.map((option, index) => (
240
- <SelectOption
241
- key={option.value || option.children}
242
- isFocused={focusedItemIndex === index}
243
- id={createItemId(option.value)}
244
- className={option.className}
245
- {...option}
246
- ref={null}
247
- />
248
- ))}
243
+ <SelectList
244
+ id={initialLabel.concat('select-typeahead-listbox')}
245
+ isAriaMultiselectable={false}
246
+ className="product-picker__select-list"
247
+ >
248
+ {selectOptions.length > 0 ? (
249
+ selectOptions.map((option, index) => (
250
+ <SelectOption
251
+ key={option.value || option.children}
252
+ isFocused={focusedItemIndex === index}
253
+ id={createItemId(option.value)}
254
+ className={option.className}
255
+ {...option}
256
+ />
257
+ ))
258
+ ) : (
259
+ <SelectOption isDisabled key="no-options">
260
+ {__('No options available')}
261
+ </SelectOption>
262
+ )}
249
263
  </SelectList>
250
264
  </Select>
251
265
  );
@@ -38,7 +38,7 @@ const setRepoSelection = (
38
38
  res = sccRepos.filter(
39
39
  (repo) =>
40
40
  (!repo.name.includes('Debug') &&
41
- !repo.name.includes('Source-Pool') &&
41
+ !repo.name.includes('-Source') &&
42
42
  repo.katello_repository_id === null) ||
43
43
  repo.katello_repository_id !== null
44
44
  );
@@ -3,8 +3,13 @@
3
3
  .pf-v5-c-select__toggle {
4
4
  font-size: var(--pf-v5-global--FontSize--xs);
5
5
  font-weight: var(--pf-v5-global--FontWeight--bold);
6
- };
6
+ }
7
7
 
8
8
  .pf-v5-c-switch__input ~ .pf-v5-c-switch__label {
9
- font-size: var(--pf-v5-global--FontSize--xs);
10
- };
9
+ font-size: var(--pf-v5-global--FontSize--xs);
10
+ }
11
+
12
+ .product-picker__select-list {
13
+ max-height: 200px;
14
+ overflow-y: auto;
15
+ }
@@ -6,3 +6,8 @@
6
6
  --pf-v5-c-tile--PaddingBottom: var(--pf-v5-global--spacer--xs);
7
7
  --pf-v5-c-tile--PaddingLeft: var(--pf-v5-global--spacer--xs);
8
8
  }
9
+
10
+ .scc-sync-button-container {
11
+ display: flex;
12
+ justify-content: center;
13
+ }
data/webpack/index.js CHANGED
@@ -3,12 +3,18 @@ import injectReducer from 'foremanReact/redux/reducers/registerReducer';
3
3
  import SCCProductPage from './components/SCCProductPage';
4
4
  import reducer from './reducer';
5
5
  import SCCAccountForm from './components/SCCAccountForm';
6
+ import SCCAccountIndex from './components/SCCAccountIndex';
6
7
 
7
8
  componentRegistry.register({
8
9
  name: 'SCCAccountForm',
9
10
  type: SCCAccountForm,
10
11
  });
11
12
 
13
+ componentRegistry.register({
14
+ name: 'SCCAccountIndex',
15
+ type: SCCAccountIndex,
16
+ });
17
+
12
18
  componentRegistry.register({
13
19
  name: 'SCCProductPage',
14
20
  type: SCCProductPage,
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_scc_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ATIX AG
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-10-15 00:00:00.000000000 Z
10
+ date: 2026-02-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rdoc
@@ -203,6 +203,11 @@ files:
203
203
  - webpack/components/SCCAccountForm/components/SCCSyncSettingsCard.js
204
204
  - webpack/components/SCCAccountForm/components/SCCTokenRefreshCard.js
205
205
  - webpack/components/SCCAccountForm/index.js
206
+ - webpack/components/SCCAccountIndex/SCCAccountIndex.scss
207
+ - webpack/components/SCCAccountIndex/SCCAccountIndex.test.js
208
+ - webpack/components/SCCAccountIndex/SCCAccountIndexActions.js
209
+ - webpack/components/SCCAccountIndex/SCCAccountIndexConstants.js
210
+ - webpack/components/SCCAccountIndex/index.js
206
211
  - webpack/components/SCCProductPage/EmptySccProducts.js
207
212
  - webpack/components/SCCProductPage/SCCProductPage.js
208
213
  - webpack/components/SCCProductPage/SCCProductPageActions.js
@@ -245,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
250
  - !ruby/object:Gem::Version
246
251
  version: '0'
247
252
  requirements: []
248
- rubygems_version: 3.6.9
253
+ rubygems_version: 4.0.3
249
254
  specification_version: 4
250
255
  summary: Suse Customer Center plugin for Foreman
251
256
  test_files: