katello 4.1.4 → 4.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (240) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +2 -2
  3. data/app/controllers/katello/api/v2/content_credentials_controller.rb +3 -3
  4. data/app/controllers/katello/api/v2/content_uploads_controller.rb +3 -1
  5. data/app/controllers/katello/api/v2/content_view_components_controller.rb +33 -1
  6. data/app/controllers/katello/api/v2/content_views_controller.rb +12 -0
  7. data/app/controllers/katello/api/v2/host_errata_controller.rb +1 -1
  8. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -2
  9. data/app/controllers/katello/api/v2/products_controller.rb +4 -4
  10. data/app/controllers/katello/api/v2/repositories_bulk_actions_controller.rb +3 -11
  11. data/app/controllers/katello/api/v2/repositories_controller.rb +68 -47
  12. data/app/controllers/katello/api/v2/upstream_subscriptions_controller.rb +0 -28
  13. data/app/controllers/katello/concerns/api/v2/registration_commands_controller_extensions.rb +26 -5
  14. data/app/controllers/katello/concerns/api/v2/registration_controller_extensions.rb +26 -1
  15. data/app/lib/actions/candlepin/environment/destroy.rb +2 -0
  16. data/app/lib/actions/katello/agent_action.rb +2 -2
  17. data/app/lib/actions/katello/capsule_content/sync_capsule.rb +3 -2
  18. data/app/lib/actions/katello/{gpg_key → content_credential}/update.rb +1 -1
  19. data/app/lib/actions/katello/content_view/publish.rb +6 -1
  20. data/app/lib/actions/katello/content_view_version/create_repos.rb +1 -1
  21. data/app/lib/actions/katello/content_view_version/incremental_update.rb +0 -47
  22. data/app/lib/actions/katello/orphan_cleanup/remove_orphans.rb +1 -1
  23. data/app/lib/actions/katello/repository/clone_contents.rb +1 -7
  24. data/app/lib/actions/katello/repository/clone_to_environment.rb +1 -7
  25. data/app/lib/actions/katello/repository/create.rb +4 -8
  26. data/app/lib/actions/katello/repository/create_root.rb +1 -1
  27. data/app/lib/actions/katello/repository/destroy.rb +1 -3
  28. data/app/lib/actions/katello/repository/import_upload.rb +3 -2
  29. data/app/lib/actions/katello/repository/instance_update.rb +1 -1
  30. data/app/lib/actions/katello/repository/metadata_generate.rb +2 -8
  31. data/app/lib/actions/katello/repository/multi_clone_contents.rb +0 -1
  32. data/app/lib/actions/katello/repository/refresh_repository.rb +1 -4
  33. data/app/lib/actions/katello/repository/remove_content.rb +6 -4
  34. data/app/lib/actions/katello/repository/sync.rb +5 -25
  35. data/app/lib/actions/katello/repository/update.rb +1 -2
  36. data/app/lib/actions/katello/repository/update_http_proxy_details.rb +2 -5
  37. data/app/lib/actions/katello/repository/update_redhat_repository.rb +1 -1
  38. data/app/lib/actions/katello/repository/upload_files.rb +8 -3
  39. data/app/lib/actions/katello/repository/upload_package_group.rb +2 -11
  40. data/app/lib/actions/katello/repository/verify_checksum.rb +0 -1
  41. data/app/lib/actions/katello/repository_set/enable_repository.rb +1 -1
  42. data/app/lib/actions/pulp3/orchestration/repository/create.rb +2 -2
  43. data/app/lib/actions/pulp3/repository/create.rb +3 -4
  44. data/app/lib/actions/pulp3/repository/create_remote.rb +1 -6
  45. data/app/lib/actions/pulp3/repository/repair.rb +4 -0
  46. data/app/lib/katello/errors.rb +1 -0
  47. data/app/lib/katello/http_resource.rb +26 -73
  48. data/app/lib/katello/qpid/connection.rb +1 -3
  49. data/app/lib/katello/resources/candlepin/consumer.rb +1 -1
  50. data/app/lib/katello/resources/candlepin/environment.rb +2 -0
  51. data/app/lib/katello/resources/registry.rb +7 -20
  52. data/app/lib/katello/util/http_proxy.rb +0 -3
  53. data/app/lib/katello/validators/gpg_key_content_validator.rb +1 -1
  54. data/app/models/katello/authorization/{gpg_key.rb → content_credential.rb} +1 -1
  55. data/app/models/katello/authorization/product.rb +0 -4
  56. data/app/models/katello/concerns/host_managed_extensions.rb +2 -16
  57. data/app/models/katello/concerns/organization_extensions.rb +1 -1
  58. data/app/models/katello/concerns/pulp_database_unit.rb +13 -5
  59. data/app/models/katello/concerns/smart_proxy_extensions.rb +45 -41
  60. data/app/models/katello/{gpg_key.rb → content_credential.rb} +4 -4
  61. data/app/models/katello/content_view.rb +6 -1
  62. data/app/models/katello/generic_content_unit.rb +16 -0
  63. data/app/models/katello/glue/pulp/repos.rb +9 -25
  64. data/app/models/katello/kt_environment.rb +1 -1
  65. data/app/models/katello/product.rb +4 -4
  66. data/app/models/katello/repository.rb +13 -7
  67. data/app/models/katello/repository_generic_content_unit.rb +7 -0
  68. data/app/models/katello/root_repository.rb +38 -7
  69. data/app/models/setting/content.rb +5 -0
  70. data/app/services/cert/certs.rb +16 -8
  71. data/app/services/katello/applicability/applicable_content_helper.rb +1 -2
  72. data/app/services/katello/candlepin/consumer.rb +6 -0
  73. data/app/services/katello/component_view_presenter.rb +27 -0
  74. data/app/services/katello/pulp/repository.rb +1 -1
  75. data/app/services/katello/pulp/server.rb +2 -2
  76. data/app/services/katello/pulp3/api/core.rb +4 -0
  77. data/app/services/katello/pulp3/api/generic.rb +68 -0
  78. data/app/services/katello/pulp3/generic_content_unit.rb +29 -0
  79. data/app/services/katello/pulp3/pulp_content_unit.rb +5 -1
  80. data/app/services/katello/pulp3/repository/generic.rb +94 -0
  81. data/app/services/katello/pulp3/repository/yum.rb +4 -5
  82. data/app/services/katello/pulp3/repository.rb +27 -12
  83. data/app/services/katello/pulp3/repository_mirror.rb +2 -2
  84. data/app/services/katello/pulp3/smart_proxy_repository.rb +4 -4
  85. data/app/services/katello/registration_manager.rb +18 -7
  86. data/app/services/katello/repository_type.rb +59 -1
  87. data/app/services/katello/repository_type_manager.rb +116 -24
  88. data/app/views/katello/api/v2/content_views/base.json.rabl +4 -4
  89. data/app/views/katello/api/v2/repositories/show.json.rabl +1 -0
  90. data/app/views/smart_proxies/plugins/_pulpcore.html.erb +2 -5
  91. data/app/views/smart_proxies/pulp_status.html.erb +0 -7
  92. data/config/katello.yaml.example +0 -21
  93. data/config/routes/api/v2.rb +2 -1
  94. data/db/functions/deb_version_cmp_v01.sql +200 -0
  95. data/db/migrate/20171110082124_add_ssl_certs_to_products_and_repos.rb +5 -1
  96. data/db/migrate/20200402130013_add_repsoitory_docker_meta_tag_f_key.rb +3 -1
  97. data/db/migrate/20210624221630_katello_generic_content.rb +22 -0
  98. data/db/migrate/20210625095042_add_retain_package_versions_count.rb +9 -0
  99. data/db/migrate/20210628182553_add_generic_remote_options_to_root_repository.rb +5 -0
  100. data/db/migrate/20210714140440_remove_repo_export_permission.rb +5 -0
  101. data/db/migrate/20210721163730_change_gpg_keys_to_content_credentials.rb +8 -0
  102. data/db/migrate/20210728130748_create_function_deb_version_cmp.rb +12 -0
  103. data/db/seeds.d/111-upgrade_tasks.rb +1 -2
  104. data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -1
  105. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +7 -5
  106. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +4 -1
  107. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-views/details/views/content-view-details.html +1 -1
  108. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/apply-errata.controller.js +1 -1
  109. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/views/apply-errata-confirm.html +2 -1
  110. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +25 -33
  111. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-advanced-sync-modal.html +1 -1
  112. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/product-repositories.controller.js +1 -6
  113. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +10 -2
  114. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.filter.js +9 -0
  115. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details.controller.js +0 -2
  116. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-advanced-sync-options.html +1 -25
  117. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-details.html +1 -1
  118. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +31 -13
  119. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +11 -3
  120. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/views/product-repositories.html +0 -6
  121. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/aggregate-task.factory.js +3 -3
  122. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/task.factory.js +1 -1
  123. data/lib/katello/engine.rb +2 -4
  124. data/lib/katello/permission_creator.rb +6 -12
  125. data/lib/katello/plugin.rb +76 -80
  126. data/lib/katello/repository_types/python.rb +37 -0
  127. data/lib/katello/tasks/reimport.rake +0 -9
  128. data/lib/katello/tasks/repository.rake +3 -4
  129. data/lib/katello/version.rb +1 -1
  130. data/locale/action_names.rb +28 -29
  131. data/locale/bn/katello.po +699 -221
  132. data/locale/cs/katello.po +167 -59
  133. data/locale/de/katello.po +585 -352
  134. data/locale/en/katello.po +167 -59
  135. data/locale/es/katello.po +1388 -1189
  136. data/locale/fr/katello.po +1740 -1494
  137. data/locale/gu/katello.po +896 -416
  138. data/locale/hi/katello.po +892 -415
  139. data/locale/it/katello.po +371 -170
  140. data/locale/ja/katello.po +1657 -1439
  141. data/locale/katello.pot +933 -736
  142. data/locale/kn/katello.po +894 -416
  143. data/locale/ko/katello.po +515 -317
  144. data/locale/mr/katello.po +857 -415
  145. data/locale/or/katello.po +894 -416
  146. data/locale/pa/katello.po +874 -411
  147. data/locale/pt/katello.po +347 -154
  148. data/locale/pt_BR/katello.po +1398 -1215
  149. data/locale/ru/katello.po +671 -463
  150. data/locale/ta/katello.po +697 -221
  151. data/locale/te/katello.po +891 -415
  152. data/locale/zh_CN/katello.po +2029 -1845
  153. data/locale/zh_TW/katello.po +735 -407
  154. data/package.json +3 -1
  155. data/webpack/components/EditableTextInput/EditableTextInput.js +3 -3
  156. data/webpack/components/RoutedTabs/RoutedTabs.js +7 -8
  157. data/webpack/components/Table/TableWrapper.js +19 -11
  158. data/webpack/components/Table/helpers.js +1 -1
  159. data/webpack/components/extensions/HostDetails/Tabs/ContentTab.js +42 -0
  160. data/webpack/components/extensions/HostDetails/Tabs/SubscriptionTab.js +12 -0
  161. data/webpack/components/extensions/RegistrationCommands/__tests__/__snapshots__/ActivationKeys.test.js.snap +4 -0
  162. data/webpack/components/extensions/RegistrationCommands/fields/ActivationKeys.js +1 -1
  163. data/webpack/components/extensions/RegistrationCommands/index.js +1 -2
  164. data/webpack/components/pf3Table/formatters/selectionHeaderCellFormatter.js +2 -1
  165. data/webpack/fills_index.js +4 -1
  166. data/webpack/redux/actions/RedHatRepositories/helpers.js +2 -4
  167. data/webpack/redux/reducers/RedHatRepositories/enabled.js +4 -1
  168. data/webpack/scenes/ContentViews/ContentViewsActions.js +16 -1
  169. data/webpack/scenes/ContentViews/ContentViewsConstants.js +15 -0
  170. data/webpack/scenes/ContentViews/ContentViewsPage.js +12 -22
  171. data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +4 -3
  172. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +25 -14
  173. data/webpack/scenes/ContentViews/Create/CreateContentViewModal.js +4 -2
  174. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +153 -0
  175. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentVersion.js +21 -10
  176. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +157 -19
  177. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.fixtures.json +100 -108
  178. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +140 -16
  179. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/publishedContentViewDetails.fixtures.json +367 -0
  180. data/webpack/scenes/ContentViews/Details/ContentViewDetailActions.js +59 -6
  181. data/webpack/scenes/ContentViews/Details/ContentViewDetailSelectors.js +43 -0
  182. data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +44 -13
  183. data/webpack/scenes/ContentViews/Details/Filters/Add/CVFilterAddModal.js +161 -0
  184. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterAdd.test.js +54 -0
  185. data/webpack/scenes/ContentViews/Details/Filters/Add/__tests__/cvFilterCreateResult.fixtures.json +124 -0
  186. data/webpack/scenes/ContentViews/Details/Filters/CVPackageGroupFilterContent.js +8 -6
  187. data/webpack/scenes/ContentViews/Details/Filters/CVRpmFilterContent.js +7 -6
  188. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilterDetails.js +4 -3
  189. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilters.js +71 -12
  190. data/webpack/scenes/ContentViews/Details/Filters/__tests__/contentViewFilters.test.js +77 -0
  191. data/webpack/scenes/ContentViews/Details/Histories/ContentViewHistories.js +13 -12
  192. data/webpack/scenes/ContentViews/Details/Histories/__tests__/contentViewHistory.test.js +2 -2
  193. data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +17 -14
  194. data/webpack/scenes/ContentViews/Details/Repositories/LastSync.js +3 -3
  195. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewAddRemove.test.js +2 -2
  196. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewDetailRepos.test.js +6 -2
  197. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +61 -20
  198. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskInProgressResponse.fixtures.json +71 -0
  199. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewTaskResponse.fixtures.json +75 -0
  200. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +86 -1
  201. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersionsWithTask.fixtures.json +713 -0
  202. data/webpack/scenes/ContentViews/Details/__tests__/contentViewDetail.test.js +3 -0
  203. data/webpack/scenes/ContentViews/Publish/CVPublishFinish.js +184 -0
  204. data/webpack/scenes/ContentViews/Publish/CVPublishForm.js +104 -0
  205. data/webpack/scenes/ContentViews/Publish/CVPublishReview.js +71 -0
  206. data/webpack/scenes/ContentViews/Publish/ContentViewPublishSelectors.js +17 -0
  207. data/webpack/scenes/ContentViews/Publish/PublishContentViewWizard.js +145 -0
  208. data/webpack/scenes/ContentViews/Publish/__tests__/environmentPaths.fixtures.json +352 -0
  209. data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +184 -0
  210. data/webpack/scenes/ContentViews/Publish/__tests__/publishResponse.fixture.json +69 -0
  211. data/webpack/scenes/ContentViews/Publish/cvPublishForm.scss +3 -0
  212. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +75 -48
  213. data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +15 -2
  214. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +6 -10
  215. data/webpack/scenes/ContentViews/components/EnvironmentLabels.js +22 -10
  216. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathActions.js +12 -0
  217. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathConstants.js +2 -0
  218. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPathSelectors.js +16 -0
  219. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +72 -0
  220. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.scss +8 -0
  221. data/webpack/scenes/ContentViews/components/TaskPresenter/TaskPresenter.js +85 -0
  222. data/webpack/scenes/SmartProxy/SmartProxyContentTable.js +9 -8
  223. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +4 -25
  224. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsPage.test.js +0 -3
  225. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +3 -3
  226. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/SubscriptionsTableSchema.js +4 -2
  227. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/__tests__/__snapshots__/SubscriptionsTable.test.js.snap +24 -0
  228. data/webpack/scenes/Subscriptions/components/SubscriptionsTable/components/Table.js +4 -1
  229. data/webpack/scenes/Subscriptions/index.js +1 -4
  230. metadata +74 -39
  231. data/app/lib/actions/candlepin/environment/create.rb +0 -21
  232. data/app/lib/actions/foreman/environment/destroy.rb +0 -23
  233. data/app/lib/actions/katello/content_view/environment_create.rb +0 -21
  234. data/app/lib/actions/katello/repository/export.rb +0 -85
  235. data/app/lib/actions/katello/repository/purge_empty_content.rb +0 -16
  236. data/app/lib/actions/katello/repository/upload_errata.rb +0 -38
  237. data/app/lib/katello/util/proxy_uri.rb +0 -64
  238. data/app/models/katello/rhsm_fact_importer.rb +0 -20
  239. data/app/models/katello/rhsm_fact_name.rb +0 -17
  240. data/app/models/katello/rhsm_fact_parser.rb +0 -120
data/package.json CHANGED
@@ -38,6 +38,7 @@
38
38
  "eslint-plugin-jsx-a11y": "^6.0.2",
39
39
  "eslint-plugin-promise": "^4.2.1",
40
40
  "eslint-plugin-react": "^7.4.0",
41
+ "eslint-plugin-react-hooks": "^4.2.0",
41
42
  "jest": "^24.9.0",
42
43
  "nock": "^12.0.3",
43
44
  "react-test-renderer": "^16.0.0"
@@ -49,6 +50,7 @@
49
50
  "downshift": "^5.4.2",
50
51
  "ngreact": "^0.5.0",
51
52
  "query-string": "^6.1.0",
52
- "react-bootstrap": "^0.32.1"
53
+ "react-bootstrap": "^0.32.1",
54
+ "use-deep-compare-effect": "^1.6.1"
53
55
  }
54
56
  }
@@ -14,13 +14,13 @@ const EditableTextInput = ({
14
14
  const [editing, setEditing] = useState(false);
15
15
  const [submitting, setSubmitting] = useState(false);
16
16
 
17
- // Setting didCancel to avoid actions from happening after component has been unmounted
17
+ // Setting didCancel to prevent actions from happening after component has been unmounted
18
18
  // see https://overreacted.io/a-complete-guide-to-useeffect/#speaking-of-race-conditions
19
19
  useEffect(() => {
20
20
  let didCancel = false;
21
21
 
22
22
  const onSubmit = async () => {
23
- if (submitting) {
23
+ if (submitting) { // no dependency array because this check takes care of it
24
24
  await onEdit(inputValue, attribute);
25
25
  if (!didCancel) {
26
26
  setSubmitting(false);
@@ -33,7 +33,7 @@ const EditableTextInput = ({
33
33
  return () => {
34
34
  didCancel = true;
35
35
  };
36
- }, [submitting]);
36
+ });
37
37
 
38
38
  // Listen for enter and trigger submit workflow on enter
39
39
  useEffect(() => {
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import React, { useEffect, useCallback } from 'react';
2
2
  import { shape, string, number, element, arrayOf } from 'prop-types';
3
3
  import { Tab, Tabs, TabTitleText } from '@patternfly/react-core';
4
4
  import { useHistory, useLocation } from 'react-router-dom';
@@ -17,30 +17,29 @@ const RoutedTabs = ({
17
17
  const { hash } = useLocation();
18
18
  const { hash: tabFromUrl, params: { subContentId } } = paramsFromHash(hash);
19
19
 
20
- const buildLink = tabKey => `${baseUrl}#${tabKey}`;
21
-
22
- const changeTab = (eventKey) => {
20
+ const changeTab = useCallback((eventKey) => {
21
+ const buildLink = tabKey => `${baseUrl}#${tabKey}`;
23
22
  const matchedTab = tabs.find(tab => tab.key === eventKey);
24
23
  if (matchedTab) {
25
24
  history.push(buildLink(matchedTab.key));
26
25
  } else {
27
26
  history.replace(buildLink(tabs[defaultTabIndex].key)); // go to first tab if no tab selected
28
27
  }
29
- };
28
+ }, [tabs, defaultTabIndex, history, baseUrl]);
30
29
 
31
30
  const handleTabSelect = (_event, eventKey) => changeTab(eventKey);
32
31
 
33
- const getActiveTab = () => {
32
+ const getActiveTab = useCallback(() => {
34
33
  const matchedTab = tabs.find(tab => tab.key === tabFromUrl);
35
34
  if (matchedTab) return matchedTab.key;
36
35
 
37
36
  return tabs[defaultTabIndex].key; // Default to first tab
38
- };
37
+ }, [tabs, tabFromUrl, defaultTabIndex]);
39
38
 
40
39
  // Useful when first navigating to the page, switches to default tab in url
41
40
  useEffect(() => {
42
41
  if (tabFromUrl !== getActiveTab()) changeTab(tabFromUrl);
43
- }, [tabFromUrl]);
42
+ }, [tabFromUrl, getActiveTab, changeTab]);
44
43
 
45
44
  // Handle subroutes to show item's detail content while staying on a tab
46
45
  const showContent = (tab) => {
@@ -1,4 +1,5 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useState, useCallback } from 'react';
2
+ import useDeepCompareEffect from 'use-deep-compare-effect';
2
3
  import { Pagination, Flex, FlexItem } from '@patternfly/react-core';
3
4
 
4
5
  import PropTypes from 'prop-types';
@@ -31,27 +32,34 @@ const TableWrapper = ({
31
32
 
32
33
  const updatePagination = (data) => {
33
34
  const { subtotal: newTotal, page: newPage, per_page: newPerPage } = data;
34
- if (newTotal) setTotal(parseInt(newTotal, 10));
35
- if (newPage) setPage(parseInt(newPage, 10));
36
- if (newPerPage) setPerPage(parseInt(newPerPage, 10));
37
- };
38
- const paginationParams = () => ({ per_page: perPage, page });
39
- const fetchWithParams = (allParams = {}) => {
40
- dispatch(fetchItems({ ...paginationParams(), ...allParams }));
35
+ if (newTotal !== undefined) setTotal(parseInt(newTotal, 10));
36
+ if (newPage !== undefined) setPage(parseInt(newPage, 10));
37
+ if (newPerPage !== undefined) setPerPage(parseInt(newPerPage, 10));
41
38
  };
39
+ const paginationParams = useCallback(() => ({ per_page: perPage, page }), [perPage, page]);
42
40
 
43
41
  useEffect(() => updatePagination(metadata), [metadata]);
44
42
 
45
43
  // The search component will update the search query when a search is performed, listen for that
46
44
  // and perform the search so we can be sure the searchQuery is updated when search is performed.
47
- useEffect(() => {
45
+ useDeepCompareEffect(() => {
46
+ const fetchWithParams = (allParams = {}) => {
47
+ dispatch(fetchItems({ ...paginationParams(), ...allParams }));
48
+ };
48
49
  if (searchQuery || activeFilters) {
49
50
  // Reset page back to 1 when filter or search changes
50
51
  fetchWithParams({ search: searchQuery, page: 1 });
51
52
  } else {
52
53
  fetchWithParams();
53
54
  }
54
- }, [searchQuery, ...additionalListeners]);
55
+ }, [
56
+ activeFilters,
57
+ dispatch,
58
+ fetchItems,
59
+ paginationParams,
60
+ searchQuery,
61
+ additionalListeners,
62
+ ]);
55
63
 
56
64
  const getAutoCompleteParams = search => ({
57
65
  endpoint: autocompleteEndpoint,
@@ -63,7 +71,6 @@ const TableWrapper = ({
63
71
 
64
72
  const onPaginationUpdate = (updatedPagination) => {
65
73
  updatePagination(updatedPagination);
66
- dispatch(fetchItems({ ...paginationParams(), ...updatedPagination, search: searchQuery }));
67
74
  };
68
75
 
69
76
  return (
@@ -118,6 +125,7 @@ TableWrapper.propTypes = {
118
125
  additionalListeners: PropTypes.arrayOf(PropTypes.oneOfType([
119
126
  PropTypes.number,
120
127
  PropTypes.string,
128
+ PropTypes.bool,
121
129
  ])),
122
130
  activeFilters: PropTypes.bool,
123
131
  };
@@ -5,7 +5,7 @@ const onSelect = (rows, setRows) => (_event, isSelected, rowId) => {
5
5
  newRows = rows.map(row => ({ ...row, selected: isSelected }));
6
6
  } else {
7
7
  newRows = [...rows];
8
- newRows[rowId].selected = isSelected;
8
+ newRows[rowId] = { ...newRows[rowId], selected: isSelected };
9
9
  }
10
10
 
11
11
  setRows(newRows);
@@ -0,0 +1,42 @@
1
+ import React, { useState } from 'react';
2
+ import EmptyPage from 'foremanReact/components/common/EmptyState/EmptyStatePattern';
3
+ import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+
6
+ const ContentTab = () => {
7
+ const [activeTab, setActiveTab] = useState('packages');
8
+ const handleTabClick = (event, tabIndex) => setActiveTab(tabIndex);
9
+ return (
10
+ <Tabs
11
+ isSecondary
12
+ activeKey={activeTab}
13
+ onSelect={handleTabClick}
14
+ >
15
+ <Tab eventKey="packages" title={<TabTitleText>{ __('Packages')}</TabTitleText>}>
16
+ <EmptyPage
17
+ icon="enterprise"
18
+ header="WIP Packages"
19
+ description="This is a demo for adding content to the new host details page"
20
+ />
21
+ </Tab>
22
+
23
+ <Tab eventKey="errata" title={<TabTitleText>{ __('Errata')}</TabTitleText>}>
24
+ <EmptyPage
25
+ icon="enterprise"
26
+ header="WIP Errata"
27
+ description="This is a demo for adding content to the new host details page"
28
+ />
29
+ </Tab>
30
+
31
+ <Tab eventKey="modulestreams" title={<TabTitleText>{ __('Module Streams')}</TabTitleText>}>
32
+ <EmptyPage
33
+ icon="enterprise"
34
+ header="WIP Module Streams"
35
+ description="This is a demo for module streams on new host details page"
36
+ />
37
+ </Tab>
38
+ </Tabs>
39
+ );
40
+ };
41
+
42
+ export default ContentTab;
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import EmptyPage from 'foremanReact/components/common/EmptyState/EmptyStatePattern';
3
+
4
+ const SubscriptionTab = () => (
5
+ <EmptyPage
6
+ icon="enterprise"
7
+ header="WIP Subscription"
8
+ description="This is a demo for adding content to the new host details page"
9
+ />
10
+ );
11
+
12
+ export default SubscriptionTab;
@@ -15,6 +15,8 @@ exports[`ActivationKeys renders 1`] = `
15
15
  validated="error"
16
16
  >
17
17
  <Select
18
+ aria-describedby=""
19
+ aria-invalid={false}
18
20
  aria-label=""
19
21
  aria-labelledby=""
20
22
  chipGroupComponent={null}
@@ -33,6 +35,7 @@ exports[`ActivationKeys renders 1`] = `
33
35
  isCreatable={false}
34
36
  isDisabled={true}
35
37
  isGrouped={false}
38
+ isInputValuePersisted={false}
36
39
  isOpen={false}
37
40
  isPlain={false}
38
41
  menuAppendTo="inline"
@@ -51,6 +54,7 @@ exports[`ActivationKeys renders 1`] = `
51
54
  toggleIcon={null}
52
55
  toggleId={null}
53
56
  typeAheadAriaLabel=""
57
+ validated="default"
54
58
  variant="typeaheadmulti"
55
59
  width=""
56
60
  />
@@ -40,7 +40,7 @@ const ActivationKeys = ({
40
40
  // Validate field when hostgroup is changed (host group may have some keys)
41
41
  useEffect(() => {
42
42
  handleInvalidField('Activation Keys', akHasValidValue(hostGroupId, pluginValues?.activationKeys, hostGroupActivationKeys));
43
- }, [hostGroupId, hostGroupActivationKeys, pluginValues]);
43
+ }, [hostGroupId, hostGroupActivationKeys, pluginValues]); // eslint-disable-line react-hooks/exhaustive-deps
44
44
 
45
45
  return (
46
46
  <FormGroup
@@ -18,7 +18,7 @@ const RegistrationCommands = ({
18
18
  }) => {
19
19
  useEffect(() => {
20
20
  onChange({ activationKeys: [], lifecycleEnvironmentId: '' });
21
- }, [organizationId, hostGroupId]);
21
+ }, [organizationId, hostGroupId]); // eslint-disable-line react-hooks/exhaustive-deps
22
22
 
23
23
  return (
24
24
  <>
@@ -78,4 +78,3 @@ RegistrationCommands.defaultProps = {
78
78
  };
79
79
 
80
80
  export default RegistrationCommands;
81
-
@@ -1,10 +1,11 @@
1
1
  import React from 'react';
2
2
  import TableSelectionHeaderCell from '../components/TableSelectionHeaderCell';
3
3
 
4
- export default (selectionController, label) => (
4
+ export default (selectionController, label, selectionEnabled) => (
5
5
  <TableSelectionHeaderCell
6
6
  label={label}
7
7
  checked={selectionController.allRowsSelected()}
8
8
  onChange={() => selectionController.selectAllRows()}
9
+ disabled={!selectionEnabled}
9
10
  />
10
11
  );
@@ -4,10 +4,13 @@ import { registerReducer } from 'foremanReact/common/MountingService';
4
4
 
5
5
  import SystemStatuses from './components/extensions/about';
6
6
  import RegistrationCommands from './components/extensions/RegistrationCommands';
7
-
7
+ import ContentTab from './components/extensions/HostDetails/Tabs/ContentTab';
8
+ import SubscriptionTab from './components/extensions/HostDetails/Tabs/SubscriptionTab';
8
9
  import extendReducer from './components/extensions/reducers';
9
10
 
10
11
  registerReducer('katelloExtends', extendReducer);
11
12
 
12
13
  addGlobalFill('aboutFooterSlot', '[katello]AboutSystemStatuses', <SystemStatuses key="katello-system-statuses" />, 100);
13
14
  addGlobalFill('registrationAdvanced', '[katello]RegistrationCommands', <RegistrationCommands key="katello-reg" />, 100);
15
+ addGlobalFill('host-details-page-tabs', 'Content', <ContentTab key="content" />, 100);
16
+ addGlobalFill('host-details-page-tabs', 'Subscription', <SubscriptionTab key="subscription" />, 100);
@@ -19,16 +19,14 @@ const recommendedRepositoriesRHEL = [
19
19
  'rhel-7-server-extras-rpms',
20
20
  'rhel-7-server-eus-rpms',
21
21
  'rhel-7-server-kickstart',
22
- 'rhel-6-server-rpms',
22
+ 'rhel-6-server-els-rpms',
23
23
  'rhel-6-server-kickstart',
24
- 'rhel-5-server-els-rpms',
25
24
  ];
26
25
 
27
26
  const recommendedRepositoriesSatTools = [
28
27
  'satellite-tools-6.9-for-rhel-8-x86_64-rpms',
29
28
  'rhel-7-server-satellite-tools-6.9-rpms',
30
- 'rhel-6-server-satellite-tools-6.9-rpms',
31
- 'rhel-5-server-els-satellite-tools-6.9-rpms',
29
+ 'rhel-6-server-els-satellite-tools-6.9-rpms',
32
30
  'rhel-7-server-satellite-maintenance-6-rpms',
33
31
  ];
34
32
 
@@ -1,6 +1,7 @@
1
1
  import Immutable from 'seamless-immutable';
2
2
  import { isEmpty } from 'lodash';
3
3
 
4
+
4
5
  import {
5
6
  ENABLED_REPOSITORIES_REQUEST,
6
7
  ENABLED_REPOSITORIES_SUCCESS,
@@ -82,7 +83,9 @@ export default (state = initialState, action) => {
82
83
  // server can return per_page: null when there's error in the search query,
83
84
  // don't store it in such case
84
85
  // eslint-disable-next-line camelcase
85
- perPage: Number(per_page || state.pagination.perPage),
86
+ perPage: (per_page || state.pagination.perPage)
87
+ // eslint-disable-next-line camelcase
88
+ && Number(per_page || state.pagination.perPage),
86
89
  },
87
90
  itemCount: Number(subtotal),
88
91
  loading: false,
@@ -1,8 +1,12 @@
1
1
  import { translate as __ } from 'foremanReact/common/I18n';
2
2
  import { API_OPERATIONS, get, post } from 'foremanReact/redux/API';
3
3
  import api, { orgId } from '../../services/api';
4
- import CONTENT_VIEWS_KEY, { CREATE_CONTENT_VIEW_KEY, COPY_CONTENT_VIEW_KEY } from './ContentViewsConstants';
4
+ import CONTENT_VIEWS_KEY, {
5
+ CREATE_CONTENT_VIEW_KEY, COPY_CONTENT_VIEW_KEY,
6
+ cvVersionPublishKey,
7
+ } from './ContentViewsConstants';
5
8
  import { getResponseErrorMsgs } from '../../utils/helpers';
9
+ import { renderTaskStartedToast } from '../Tasks/helpers';
6
10
 
7
11
  export const createContentViewsParams = extraParams => ({
8
12
  organization_id: orgId(),
@@ -44,4 +48,15 @@ export const copyContentView = params => post({
44
48
  successToast: response => cvSuccessToast(response),
45
49
  errorToast: error => cvErrorToast(error),
46
50
  });
51
+
52
+ export const publishContentView = params => post({
53
+ type: API_OPERATIONS.POST,
54
+ key: cvVersionPublishKey(params.id, params.versionCount),
55
+ url: api.getApiUrl(`/content_views/${params.id}/publish`),
56
+ params,
57
+ handleSuccess: response => renderTaskStartedToast(response.data),
58
+ errorToast: error => cvErrorToast(error),
59
+ });
60
+
61
+
47
62
  export default getContentViews;
@@ -1,6 +1,12 @@
1
+ import { translate as __ } from 'foremanReact/common/I18n';
2
+
1
3
  const CONTENT_VIEWS_KEY = 'CONTENT_VIEWS';
2
4
  export const CREATE_CONTENT_VIEW_KEY = 'CONTENT_VIEW_CREATE';
3
5
  export const COPY_CONTENT_VIEW_KEY = 'CONTENT_VIEW_COPY';
6
+ export const CREATE_CONTENT_VIEW_FILTER_KEY = 'CONTENT_VIEW_FILTER_CREATE';
7
+ export const DELETE_CONTENT_VIEW_FILTER_KEY = 'CONTENT_VIEW_FILTER_DELETE';
8
+ export const DELETE_CONTENT_VIEW_FILTERS_KEY = 'CONTENT_VIEW_FILTERS_DELETE';
9
+ export const PUBLISH_CONTENT_VIEW_KEY = 'CONTENT_VIEW_PUBLISH';
4
10
  export const UPDATE_CONTENT_VIEW = 'UPDATE_CONTENT_VIEW';
5
11
  export const UPDATE_CONTENT_VIEW_SUCCESS = 'UPDATE_CONTENT_VIEW_SUCCESS';
6
12
  export const UPDATE_CONTENT_VIEW_FAILURE = 'UPDATE_CONTENT_VIEW_FAILURE';
@@ -14,6 +20,14 @@ export const cvDetailsHistoryKey = cvId => `${CONTENT_VIEWS_KEY}_HISTORIES_${cvI
14
20
  export const cvFilterRulesKey = filterId => `CONTENT_VIEW_FILTER_${filterId}_RULES`;
15
21
  export const cvDetailsComponentKey = cvId => `${CONTENT_VIEWS_KEY}_COMPONENTS_${cvId}`;
16
22
  export const cvDetailsVersionKey = cvId => `${CONTENT_VIEWS_KEY}_VERSIONS_${cvId}`;
23
+ export const cvVersionPublishKey = (cvId, versionCount) => `${PUBLISH_CONTENT_VIEW_KEY}_${cvId}_VERSION_${versionCount}`;
24
+ export const cvAddComponentKey = cvId => `${CONTENT_VIEWS_KEY}_ADD_COMPONENT_${cvId}`;
25
+ export const cvRemoveComponentKey = cvId => `${CONTENT_VIEWS_KEY}_REMOVE_COMPONENT_${cvId}`;
26
+
27
+ export const removeComponentSuccessMessage = size => (size === 1 ? __('Removed component from content view') : __('Removed components from content view'));
28
+
29
+ export const addComponentSuccessMessage = component => (component ? __('Updated component details') : __('Added component to content view'));
30
+
17
31
 
18
32
  // Repo added to content view status display and key
19
33
  export const ADDED = 'Added';
@@ -21,5 +35,6 @@ export const NOT_ADDED = 'Not added';
21
35
  export const ALL_STATUSES = 'All';
22
36
 
23
37
  export const REPOSITORY_TYPES = 'REPOSITORY_TYPES';
38
+ export const FILTER_TYPES = ['rpm', 'package_group', 'erratum', 'docker', 'modulemd'];
24
39
 
25
40
  export default CONTENT_VIEWS_KEY;
@@ -1,29 +1,19 @@
1
1
  import React from 'react';
2
2
  import { translate as __ } from 'foremanReact/common/I18n';
3
- import { useSelector } from 'react-redux';
4
3
  import { Grid, GridItem, TextContent, Text, TextVariants } from '@patternfly/react-core';
5
- import { selectContentViews,
6
- selectContentViewStatus,
7
- selectContentViewError } from './ContentViewSelectors';
8
4
  import ContentViewsTable from './Table/ContentViewsTable';
9
5
 
10
- const ContentViewsPage = () => {
11
- const response = useSelector(selectContentViews);
12
- const status = useSelector(selectContentViewStatus);
13
- const error = useSelector(selectContentViewError);
14
-
15
- return (
16
- <Grid className="grid-with-margin">
17
- <GridItem span={12}>
18
- <TextContent>
19
- <Text component={TextVariants.h1}>{__('Content Views')}</Text>
20
- </TextContent>
21
- </GridItem>
22
- <GridItem span={12}>
23
- <ContentViewsTable {...{ response, status, error }} />
24
- </GridItem>
25
- </Grid>
26
- );
27
- };
6
+ const ContentViewsPage = () => (
7
+ <Grid className="grid-with-margin">
8
+ <GridItem span={12}>
9
+ <TextContent>
10
+ <Text component={TextVariants.h1}>{__('Content Views')}</Text>
11
+ </TextContent>
12
+ </GridItem>
13
+ <GridItem span={12}>
14
+ <ContentViewsTable />
15
+ </GridItem>
16
+ </Grid>
17
+ );
28
18
 
29
19
  export default ContentViewsPage;
@@ -1,5 +1,6 @@
1
1
  import { STATUS } from 'foremanReact/constants';
2
- import React, { useState, useEffect } from 'react';
2
+ import React, { useState } from 'react';
3
+ import useDeepCompareEffect from 'use-deep-compare-effect';
3
4
  import PropTypes from 'prop-types';
4
5
  import { useDispatch, useSelector } from 'react-redux';
5
6
  import { Redirect } from 'react-router-dom';
@@ -19,7 +20,7 @@ const CopyContentViewForm = ({ cvId, setModalOpen }) => {
19
20
  const status = useSelector(selectCopyContentViewStatus);
20
21
  const error = useSelector(selectCopyContentViewError);
21
22
 
22
- useEffect(() => {
23
+ useDeepCompareEffect(() => {
23
24
  const { id } = response;
24
25
  if (id && status === STATUS.RESOLVED) {
25
26
  setSaving(false);
@@ -27,7 +28,7 @@ const CopyContentViewForm = ({ cvId, setModalOpen }) => {
27
28
  } else if (status === STATUS.ERROR) {
28
29
  setSaving(false);
29
30
  }
30
- }, [JSON.stringify(response), status, error]);
31
+ }, [response, status, error]);
31
32
 
32
33
  const onSubmit = () => {
33
34
  setSaving(true);
@@ -1,5 +1,7 @@
1
- import { STATUS } from 'foremanReact/constants';
2
1
  import React, { useState, useEffect } from 'react';
2
+ import useDeepCompareEffect from 'use-deep-compare-effect';
3
+ import { STATUS } from 'foremanReact/constants';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
3
5
  import PropTypes from 'prop-types';
4
6
  import { useDispatch, useSelector } from 'react-redux';
5
7
  import { Redirect } from 'react-router-dom';
@@ -26,15 +28,15 @@ const CreateContentViewForm = ({ setModalOpen }) => {
26
28
  const status = useSelector(selectCreateContentViewStatus);
27
29
  const error = useSelector(selectCreateContentViewError);
28
30
 
29
- useEffect(() => {
31
+ useDeepCompareEffect(() => {
30
32
  const { id } = response;
31
- if (id && status === STATUS.RESOLVED) {
33
+ if (id && status === STATUS.RESOLVED && saving) {
32
34
  setSaving(false);
33
35
  setRedirect(true);
34
36
  } else if (status === STATUS.ERROR) {
35
37
  setSaving(false);
36
38
  }
37
- }, [JSON.stringify(response), status, error]);
39
+ }, [response, status, error, saving]);
38
40
 
39
41
  const onSave = () => {
40
42
  setSaving(true);
@@ -63,7 +65,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
63
65
 
64
66
  return (
65
67
  <Form>
66
- <FormGroup label="Name" isRequired fieldId="name">
68
+ <FormGroup label={__('Name')} isRequired fieldId="name">
67
69
  <TextInput
68
70
  isRequired
69
71
  type="text"
@@ -74,7 +76,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
74
76
  onChange={value => setName(value)}
75
77
  />
76
78
  </FormGroup>
77
- <FormGroup label="Label" isRequired fieldId="label">
79
+ <FormGroup label={__('Label')} isRequired fieldId="label">
78
80
  <TextInput
79
81
  isRequired
80
82
  type="text"
@@ -85,7 +87,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
85
87
  onChange={value => setLabel(value)}
86
88
  />
87
89
  </FormGroup>
88
- <FormGroup label="Description" fieldId="description">
90
+ <FormGroup label={__('Description')} fieldId="description">
89
91
  <TextArea
90
92
  isRequired
91
93
  type="text"
@@ -96,7 +98,7 @@ const CreateContentViewForm = ({ setModalOpen }) => {
96
98
  onChange={value => setDescription(value)}
97
99
  />
98
100
  </FormGroup>
99
- <FormGroup isInline fieldId="type" label="Type">
101
+ <FormGroup isInline fieldId="type" label={__('Type')}>
100
102
  <Grid hasGutter>
101
103
  <GridItem span={6}>
102
104
  <Tile
@@ -104,11 +106,11 @@ const CreateContentViewForm = ({ setModalOpen }) => {
104
106
  aria-label="component_tile"
105
107
  icon={<ContentViewIcon composite={false} />}
106
108
  id="component"
107
- title="Component content view"
109
+ title={__('Component content view')}
108
110
  onClick={() => { setComponent(true); setComposite(false); }}
109
111
  isSelected={component}
110
112
  >
111
- Single content view consisting of repositories
113
+ {__('Single content view consisting of repositories')}
112
114
  </Tile>
113
115
  </GridItem>
114
116
  <GridItem span={6}>
@@ -117,11 +119,11 @@ const CreateContentViewForm = ({ setModalOpen }) => {
117
119
  aria-label="composite_tile"
118
120
  icon={<ContentViewIcon composite />}
119
121
  id="composite"
120
- title="Composite content view"
122
+ title={__('Composite content view')}
121
123
  onClick={() => { setComposite(true); setComponent(false); }}
122
124
  isSelected={composite}
123
125
  >
124
- Consists of component content views
126
+ {__('Consisting of multiple component content views')}
125
127
  </Tile>
126
128
  </GridItem>
127
129
  </Grid>
@@ -157,8 +159,17 @@ const CreateContentViewForm = ({ setModalOpen }) => {
157
159
  />
158
160
  </FormGroup>}
159
161
  <ActionGroup>
160
- <Button aria-label="create_content_view" variant="primary" isDisabled={saving} onClick={() => onSave()}>Create content view</Button>
161
- <Button variant="link" onClick={() => setModalOpen(false)}>Cancel</Button>
162
+ <Button
163
+ aria-label="create_content_view"
164
+ variant="primary"
165
+ isDisabled={saving}
166
+ onClick={() => onSave()}
167
+ >
168
+ {__('Create content view')}
169
+ </Button>
170
+ <Button variant="link" onClick={() => setModalOpen(false)}>
171
+ {__('Cancel')}
172
+ </Button>
162
173
  </ActionGroup>
163
174
  </Form>
164
175
  );
@@ -1,16 +1,18 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
+ import { translate as __ } from 'foremanReact/common/I18n';
3
4
  import { Modal, ModalVariant } from '@patternfly/react-core';
4
5
  import CreateContentViewForm from './CreateContentViewForm';
5
6
 
6
7
  const CreateContentViewModal = ({ show, setIsOpen }) => (
7
8
  <Modal
8
- title="Create content view"
9
+ title={__('Create content view')}
9
10
  variant={ModalVariant.small}
10
11
  isOpen={show}
11
12
  onClose={() => { setIsOpen(false); }}
12
13
  appendTo={document.body}
13
- ><CreateContentViewForm setModalOpen={setIsOpen} />
14
+ >
15
+ <CreateContentViewForm setModalOpen={setIsOpen} />
14
16
  </Modal>
15
17
  );
16
18