@openshift-assisted/ui-lib 2.9.10-cim → 2.9.11-cim

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 (131) hide show
  1. package/build/@types/cim/index.d.ts +1 -0
  2. package/build/@types/cim/index.d.ts.map +1 -1
  3. package/build/@types/common/api/utils.d.ts +1 -1
  4. package/build/@types/common/api/utils.d.ts.map +1 -1
  5. package/build/@types/common/index.d.ts +1 -0
  6. package/build/@types/common/index.d.ts.map +1 -1
  7. package/build/@types/common/types/index.d.ts +0 -8
  8. package/build/@types/common/types/index.d.ts.map +1 -1
  9. package/build/@types/common/types/resource-ui-state.d.ts +9 -0
  10. package/build/@types/common/types/resource-ui-state.d.ts.map +1 -0
  11. package/build/@types/ocm/components/Routes.d.ts.map +1 -1
  12. package/build/@types/ocm/components/hosts/DeleteHostModal.d.ts.map +1 -1
  13. package/build/@types/ocm/config/external-features.d.ts +8 -0
  14. package/build/@types/ocm/config/external-features.d.ts.map +1 -0
  15. package/build/@types/ocm/config/index.d.ts +1 -1
  16. package/build/@types/ocm/config/{routeBaseBath.d.ts → routeBasePath.d.ts} +1 -1
  17. package/build/@types/ocm/config/routeBasePath.d.ts.map +1 -0
  18. package/build/@types/ocm/hooks/use-feature-detection.d.ts +3 -0
  19. package/build/@types/ocm/hooks/use-feature-detection.d.ts.map +1 -0
  20. package/build/@types/ocm/services/ClustersService.d.ts +3 -3
  21. package/build/@types/ocm/services/ClustersService.d.ts.map +1 -1
  22. package/build/@types/ocm/services/InfraEnvIdsCacheService.d.ts +2 -2
  23. package/build/@types/ocm/services/InfraEnvIdsCacheService.d.ts.map +1 -1
  24. package/build/@types/ocm/store/hooks.d.ts +17 -5
  25. package/build/@types/ocm/store/hooks.d.ts.map +1 -1
  26. package/build/@types/ocm/store/index.d.ts +1 -3
  27. package/build/@types/ocm/store/index.d.ts.map +1 -1
  28. package/build/@types/ocm/store/slices/clusters/selectors.d.ts +6 -3
  29. package/build/@types/ocm/store/slices/clusters/selectors.d.ts.map +1 -1
  30. package/build/@types/ocm/store/slices/clusters/slice.d.ts +2 -2
  31. package/build/@types/ocm/store/slices/clusters/slice.d.ts.map +1 -1
  32. package/build/@types/ocm/store/slices/current-cluster/slice.d.ts +4 -3
  33. package/build/@types/ocm/store/slices/current-cluster/slice.d.ts.map +1 -1
  34. package/build/@types/ocm/store/slices/current-user/slice.d.ts +46 -0
  35. package/build/@types/ocm/store/slices/current-user/slice.d.ts.map +1 -0
  36. package/build/@types/ocm/store/slices/feature-flags/selectors.d.ts +18 -4
  37. package/build/@types/ocm/store/slices/feature-flags/selectors.d.ts.map +1 -1
  38. package/build/@types/ocm/store/slices/feature-flags/slice.d.ts +26 -5
  39. package/build/@types/ocm/store/slices/feature-flags/slice.d.ts.map +1 -1
  40. package/build/@types/ocm/store/slices/infra-envs/selectors.d.ts +2 -1
  41. package/build/@types/ocm/store/slices/infra-envs/selectors.d.ts.map +1 -1
  42. package/build/@types/ocm/store/store-day1.d.ts +4 -2
  43. package/build/@types/ocm/store/store-day1.d.ts.map +1 -1
  44. package/build/@types/ocm/store/types/state-slice.d.ts +16 -0
  45. package/build/@types/ocm/store/types/state-slice.d.ts.map +1 -0
  46. package/build/cjs/cim/index.js +3 -1
  47. package/build/cjs/cim/index.js.map +1 -1
  48. package/build/cjs/common/index.js +3 -0
  49. package/build/cjs/common/index.js.map +1 -1
  50. package/build/cjs/common/types/index.js +0 -10
  51. package/build/cjs/common/types/index.js.map +1 -1
  52. package/build/cjs/common/types/resource-ui-state.js +13 -0
  53. package/build/cjs/common/types/resource-ui-state.js.map +1 -0
  54. package/build/cjs/ocm/components/HostsClusterDetailTab/HostsClusterDetailTab.js +2 -2
  55. package/build/cjs/ocm/components/HostsClusterDetailTab/HostsClusterDetailTab.js.map +1 -1
  56. package/build/cjs/ocm/components/Routes.js +3 -5
  57. package/build/cjs/ocm/components/Routes.js.map +1 -1
  58. package/build/cjs/ocm/components/hosts/DeleteHostModal.js +8 -7
  59. package/build/cjs/ocm/components/hosts/DeleteHostModal.js.map +1 -1
  60. package/build/cjs/ocm/config/external-features.js +10 -0
  61. package/build/cjs/ocm/config/external-features.js.map +1 -0
  62. package/build/cjs/ocm/config/index.js +1 -1
  63. package/build/cjs/ocm/config/{routeBaseBath.js → routeBasePath.js} +1 -1
  64. package/build/cjs/ocm/config/routeBasePath.js.map +1 -0
  65. package/build/cjs/ocm/hooks/use-feature-detection.js +14 -0
  66. package/build/cjs/ocm/hooks/use-feature-detection.js.map +1 -0
  67. package/build/cjs/ocm/services/ClustersService.js +19 -19
  68. package/build/cjs/ocm/services/ClustersService.js.map +1 -1
  69. package/build/cjs/ocm/services/InfraEnvIdsCacheService.js +5 -5
  70. package/build/cjs/ocm/services/InfraEnvIdsCacheService.js.map +1 -1
  71. package/build/cjs/ocm/store/hooks.js +2 -4
  72. package/build/cjs/ocm/store/hooks.js.map +1 -1
  73. package/build/cjs/ocm/store/index.js +1 -5
  74. package/build/cjs/ocm/store/index.js.map +1 -1
  75. package/build/cjs/ocm/store/slices/clusters/slice.js +9 -8
  76. package/build/cjs/ocm/store/slices/clusters/slice.js.map +1 -1
  77. package/build/cjs/ocm/store/slices/current-cluster/slice.js +14 -13
  78. package/build/cjs/ocm/store/slices/current-cluster/slice.js.map +1 -1
  79. package/build/cjs/ocm/store/slices/current-user/slice.js +109 -0
  80. package/build/cjs/ocm/store/slices/current-user/slice.js.map +1 -0
  81. package/build/cjs/ocm/store/slices/feature-flags/selectors.js +1 -4
  82. package/build/cjs/ocm/store/slices/feature-flags/selectors.js.map +1 -1
  83. package/build/cjs/ocm/store/slices/feature-flags/slice.js +79 -6
  84. package/build/cjs/ocm/store/slices/feature-flags/slice.js.map +1 -1
  85. package/build/cjs/ocm/store/slices/infra-envs/thunks.js +1 -1
  86. package/build/cjs/ocm/store/slices/infra-envs/thunks.js.map +1 -1
  87. package/build/cjs/ocm/store/store-day1.js +2 -4
  88. package/build/cjs/ocm/store/store-day1.js.map +1 -1
  89. package/build/cjs/ocm/store/types/state-slice.js +3 -0
  90. package/build/cjs/ocm/store/types/state-slice.js.map +1 -0
  91. package/lib/@types/assets.d.ts +1 -1
  92. package/lib/@types/global.d.ts +1 -0
  93. package/lib/_test-helpers/mock-container.ts +42 -0
  94. package/lib/_test-helpers/mock-server.ts +26 -0
  95. package/lib/_test-helpers/vitest-setup.ts +17 -0
  96. package/lib/cim/index.ts +1 -0
  97. package/lib/common/api/utils.ts +1 -1
  98. package/lib/common/index.ts +1 -0
  99. package/lib/common/types/index.ts +0 -9
  100. package/lib/common/types/resource-ui-state.ts +8 -0
  101. package/lib/ocm/components/HostsClusterDetailTab/HostsClusterDetailTab.tsx +2 -2
  102. package/lib/ocm/components/Routes.tsx +3 -5
  103. package/lib/ocm/components/hosts/DeleteHostModal.tsx +28 -23
  104. package/lib/ocm/config/external-features.ts +12 -0
  105. package/lib/ocm/config/index.ts +1 -1
  106. package/lib/ocm/hooks/_use-feature-detection.test.tsx +125 -0
  107. package/lib/ocm/hooks/use-feature-detection.ts +17 -0
  108. package/lib/ocm/services/ClustersService.ts +9 -9
  109. package/lib/ocm/services/InfraEnvIdsCacheService.ts +2 -2
  110. package/lib/ocm/store/hooks.ts +1 -4
  111. package/lib/ocm/store/index.ts +1 -3
  112. package/lib/ocm/store/slices/clusters/slice.ts +5 -4
  113. package/lib/ocm/store/slices/current-cluster/slice.ts +9 -15
  114. package/lib/ocm/store/slices/current-user/slice.ts +129 -0
  115. package/lib/ocm/store/slices/feature-flags/selectors.ts +6 -7
  116. package/lib/ocm/store/slices/feature-flags/slice.ts +102 -7
  117. package/lib/ocm/store/slices/infra-envs/thunks.ts +1 -1
  118. package/lib/ocm/store/store-day1.ts +2 -4
  119. package/lib/ocm/store/types/state-slice.ts +16 -0
  120. package/package.json +12 -4
  121. package/build/@types/ocm/config/routeBaseBath.d.ts.map +0 -1
  122. package/build/@types/ocm/store/store-day2.d.ts +0 -10
  123. package/build/@types/ocm/store/store-day2.d.ts.map +0 -1
  124. package/build/cjs/ocm/config/routeBaseBath.js.map +0 -1
  125. package/build/cjs/ocm/store/store-day2.js +0 -20
  126. package/build/cjs/ocm/store/store-day2.js.map +0 -1
  127. package/lib/ocm/store/store-day2.ts +0 -19
  128. /package/lib/cim/components/ClusterDeployment/{wizardTransition.test.ts → _wizardTransition.test.ts} +0 -0
  129. /package/lib/common/components/ui/formik/{validationSchemas.test.ts → _validationSchemas.test.ts} +0 -0
  130. /package/lib/ocm/components/clusterWizard/{wizardTransition.test.ts → _wizardTransition.test.ts} +0 -0
  131. /package/lib/ocm/config/{routeBaseBath.ts → routeBasePath.ts} +0 -0
@@ -4,8 +4,5 @@ exports.isFeatureEnabled = exports.selectFeatureFlagsSlice = void 0;
4
4
  const toolkit_1 = require("@reduxjs/toolkit");
5
5
  const selectFeatureFlagsSlice = (state) => state.featureFlags;
6
6
  exports.selectFeatureFlagsSlice = selectFeatureFlagsSlice;
7
- exports.isFeatureEnabled = (0, toolkit_1.createSelector)([exports.selectFeatureFlagsSlice, (_, feature) => feature], (featureFlags, feature) => {
8
- // Configured via Unleash in the OCM
9
- return !!featureFlags[feature];
10
- });
7
+ exports.isFeatureEnabled = (0, toolkit_1.createSelector)(exports.selectFeatureFlagsSlice, (_featureFlags, featureId) => featureId, (featureFlags, featureId) => featureFlags.data[featureId]);
11
8
  //# sourceMappingURL=selectors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../../../../../lib/ocm/store/slices/feature-flags/selectors.ts"],"names":[],"mappings":";;;AAAA,8CAAkD;AAI3C,MAAM,uBAAuB,GAAG,CAAC,KAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;AAAvE,QAAA,uBAAuB,2BAAgD;AAEvE,QAAA,gBAAgB,GAAG,IAAA,wBAAc,EAC5C,CAAC,+BAAuB,EAAE,CAAC,CAAC,EAAE,OAAqC,EAAE,EAAE,CAAC,OAAO,CAAC,EAChF,CAAC,YAAY,EAAE,OAAO,EAAE,EAAE;IACxB,oCAAoC;IACpC,OAAO,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC,CACF,CAAC"}
1
+ {"version":3,"file":"selectors.js","sourceRoot":"","sources":["../../../../../../lib/ocm/store/slices/feature-flags/selectors.ts"],"names":[],"mappings":";;;AAEA,8CAAkD;AAE3C,MAAM,uBAAuB,GAAG,CAAC,KAAoB,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;AAAvE,QAAA,uBAAuB,2BAAgD;AAEvE,QAAA,gBAAgB,GAAG,IAAA,wBAAc,EAC5C,+BAAuB,EACvB,CAAC,aAAyD,EAAE,SAAgC,EAAE,EAAE,CAC9F,SAAS,EACX,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAC1D,CAAC"}
@@ -1,16 +1,89 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.featureFlagsReducer = exports.featureFlagsActions = void 0;
3
+ exports.featureFlagsReducer = exports.featureFlagsActions = exports.featureFlagsAsyncActions = void 0;
4
+ const tslib_1 = require("tslib");
4
5
  const toolkit_1 = require("@reduxjs/toolkit");
6
+ const featureGate_1 = require("../../../../common/features/featureGate");
7
+ const external_features_1 = require("../../../config/external-features");
8
+ const slice_1 = require("../current-user/slice");
9
+ const featureFlagsAsyncActions = {
10
+ detectFeaturesAsync: (0, toolkit_1.createAsyncThunk)('featureFlags/detectFeatures', (featuresOverride = null, thunkApi) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
11
+ var _a, _b, _c;
12
+ yield thunkApi.dispatch(slice_1.currentUserAsyncActions.getCapabilitiesAsync());
13
+ const state = thunkApi.getState();
14
+ for (const { featureId, capabilityId } of external_features_1.externalFeaturesMappings) {
15
+ const capability = (_c = (_b = (_a = state.currentUser.data) === null || _a === void 0 ? void 0 : _a.organization) === null || _b === void 0 ? void 0 : _b.capabilities) === null || _c === void 0 ? void 0 : _c.find((capability) => capability.name === capabilityId);
16
+ if (capability) {
17
+ thunkApi.dispatch(featureFlagsActions.setFeatureFlag({
18
+ featureId,
19
+ isEnabled: capability.value === 'true',
20
+ }));
21
+ }
22
+ }
23
+ if (featuresOverride !== null) {
24
+ for (const [k, v] of Object.entries(featuresOverride)) {
25
+ thunkApi.dispatch(featureFlagsActions.setFeatureFlag({
26
+ featureId: k,
27
+ isEnabled: v,
28
+ }));
29
+ }
30
+ }
31
+ })),
32
+ };
33
+ exports.featureFlagsAsyncActions = featureFlagsAsyncActions;
34
+ const initialState = {
35
+ data: featureGate_1.STANDALONE_DEPLOYMENT_ENABLED_FEATURES,
36
+ error: null,
37
+ meta: {
38
+ status: 'idle',
39
+ updatedAt: null,
40
+ currentRequestId: null,
41
+ },
42
+ };
5
43
  const featureFlagsSlice = (0, toolkit_1.createSlice)({
6
44
  name: 'featureFlags',
7
- initialState: {},
45
+ initialState,
8
46
  reducers: {
9
- setFeatureFlags: (state, action) => {
10
- return Object.assign(state, action.payload);
47
+ setMeta: (state, action) => {
48
+ state.meta.updatedAt = new Date().toUTCString();
49
+ state.meta.status = action.payload.meta.requestStatus;
50
+ state.meta.currentRequestId = action.payload.meta.requestId;
51
+ return state;
52
+ },
53
+ setFeatureFlag: (state, action) => {
54
+ const { featureId, isEnabled } = action.payload;
55
+ state.data[featureId] = isEnabled;
56
+ state.error = null;
57
+ state.meta.updatedAt = new Date().toUTCString();
58
+ return state;
11
59
  },
12
60
  },
61
+ extraReducers: (builder) => {
62
+ for (const { pending, fulfilled, rejected } of Object.values(featureFlagsAsyncActions)) {
63
+ builder.addCase(pending, (state, action) => {
64
+ if (/idle|fulfilled|rejected/.test(state.meta.status)) {
65
+ featureFlagsActions.setMeta(action);
66
+ }
67
+ return state;
68
+ });
69
+ builder.addCase(fulfilled, (state, action) => {
70
+ if (/idle|pending/.test(state.meta.status)) {
71
+ featureFlagsActions.setMeta(action);
72
+ }
73
+ return state;
74
+ });
75
+ builder.addCase(rejected, (state, action) => {
76
+ if (/idle|pending/.test(state.meta.status)) {
77
+ featureFlagsActions.setMeta(action);
78
+ state.error = action.error;
79
+ }
80
+ return state;
81
+ });
82
+ }
83
+ },
13
84
  });
14
- exports.featureFlagsActions = featureFlagsSlice.actions;
15
- exports.featureFlagsReducer = featureFlagsSlice.reducer;
85
+ const featureFlagsActions = featureFlagsSlice.actions;
86
+ exports.featureFlagsActions = featureFlagsActions;
87
+ const featureFlagsReducer = featureFlagsSlice.reducer;
88
+ exports.featureFlagsReducer = featureFlagsReducer;
16
89
  //# sourceMappingURL=slice.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../../../../../lib/ocm/store/slices/feature-flags/slice.ts"],"names":[],"mappings":";;;AAAA,8CAA8D;AAG9D,MAAM,iBAAiB,GAAG,IAAA,qBAAW,EAAC;IACpC,IAAI,EAAE,cAAc;IACpB,YAAY,EAAE,EAAqB;IACnC,QAAQ,EAAE;QACR,eAAe,EAAE,CAAC,KAAK,EAAE,MAAsC,EAAE,EAAE;YACjE,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAC9C,CAAC;KACF;CACF,CAAC,CAAC;AAEU,QAAA,mBAAmB,GAAG,iBAAiB,CAAC,OAAO,CAAC;AAChD,QAAA,mBAAmB,GAAG,iBAAiB,CAAC,OAAO,CAAC"}
1
+ {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../../../../../lib/ocm/store/slices/feature-flags/slice.ts"],"names":[],"mappings":"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"}
@@ -9,7 +9,7 @@ exports.listInfraEnvsByClusterId = (0, toolkit_1.createAsyncThunk)('infraEnvs/li
9
9
  const { data: infraEnvs } = yield apis_1.InfraEnvsAPI.list(clusterId);
10
10
  return infraEnvs;
11
11
  }));
12
- exports.createInfraEnv = (0, toolkit_1.createAsyncThunk)('', (params) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
12
+ exports.createInfraEnv = (0, toolkit_1.createAsyncThunk)('infraEnvs/createInfraEnv', (params) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
13
13
  const { data: infraEnv } = yield apis_1.InfraEnvsAPI.register(params);
14
14
  // dispatch({ type: 'currentCluster/updateStaticNetworkConfig' payload: infraEnv.staticNetworkConfig });
15
15
  return infraEnv;
@@ -1 +1 @@
1
- {"version":3,"file":"thunks.js","sourceRoot":"","sources":["../../../../../../lib/ocm/store/slices/infra-envs/thunks.ts"],"names":[],"mappings":";;;;AAAA,8CAAoD;AAEpD,iDAAsD;AAEtD,2CAA8D;AAQjD,QAAA,wBAAwB,GAAG,IAAA,0BAAgB,EACtD,2BAA2B,EAC3B,CAAO,SAAwB,EAAE,EAAE;IACjC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,mBAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/D,OAAO,SAAS,CAAC;AACnB,CAAC,CAAA,CACF,CAAC;AAEW,QAAA,cAAc,GAAG,IAAA,0BAAgB,EAC5C,EAAE,EACF,CAAO,MAAuD,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,mBAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/D,wGAAwG;IACxG,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAA,CACF,CAAC;AAEW,QAAA,kBAAkB,GAAG,IAAA,0BAAgB,EAAC,qBAAqB,EAAE,CAAO,CAAC,EAAE,QAAQ,EAAE,EAAE;IAC9F,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAmB,CAAC;IACnD,MAAM,WAAW,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,EAAE,CAAC;IACpB,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;QACpC,QAAQ,CAAC,IAAI,CAAC,mBAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;KACpD;IACD,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC,CAAA,CAAC,CAAC;AAEU,QAAA,cAAc,GAAG,IAAA,0BAAgB,EAC5C,kBAAkB,EAClB,CAAO,EAAgF,EAAE,EAAE;QAApF,EAAE,UAAU,OAAoE,EAA/D,MAAM,sBAAvB,cAAyB,CAAF;IAC5B,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,mBAAY,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzE,wGAAwG;IACxG,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAA,CACF,CAAC;AAEW,QAAA,WAAW,GAAG,IAAA,0BAAgB,EACzC,uBAAuB,EACvB,CAAO,eAAgC,EAAE,QAAQ,EAAE,EAAE;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAmB,CAAC;IACnD,kFAAkF;IAClF,mFAAmF;IACnF,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,IAAA,2CAA+B,EAAC,KAAK,EAAE,eAAe,CAAa,CAAC;IACrF,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,mBAAY,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3E,OAAO,YAAY,CAAC;AACtB,CAAC,CAAA,CACF,CAAC"}
1
+ {"version":3,"file":"thunks.js","sourceRoot":"","sources":["../../../../../../lib/ocm/store/slices/infra-envs/thunks.ts"],"names":[],"mappings":";;;;AAAA,8CAAoD;AAEpD,iDAAsD;AAEtD,2CAA8D;AAQjD,QAAA,wBAAwB,GAAG,IAAA,0BAAgB,EACtD,2BAA2B,EAC3B,CAAO,SAAwB,EAAE,EAAE;IACjC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,mBAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/D,OAAO,SAAS,CAAC;AACnB,CAAC,CAAA,CACF,CAAC;AAEW,QAAA,cAAc,GAAG,IAAA,0BAAgB,EAC5C,0BAA0B,EAC1B,CAAO,MAAuD,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,mBAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/D,wGAAwG;IACxG,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAA,CACF,CAAC;AAEW,QAAA,kBAAkB,GAAG,IAAA,0BAAgB,EAAC,qBAAqB,EAAE,CAAO,CAAC,EAAE,QAAQ,EAAE,EAAE;IAC9F,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAmB,CAAC;IACnD,MAAM,WAAW,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,EAAE,CAAC;IACpB,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;QACpC,QAAQ,CAAC,IAAI,CAAC,mBAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;KACpD;IACD,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC,CAAA,CAAC,CAAC;AAEU,QAAA,cAAc,GAAG,IAAA,0BAAgB,EAC5C,kBAAkB,EAClB,CAAO,EAAgF,EAAE,EAAE;QAApF,EAAE,UAAU,OAAoE,EAA/D,MAAM,sBAAvB,cAAyB,CAAF;IAC5B,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,mBAAY,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzE,wGAAwG;IACxG,OAAO,QAAQ,CAAC;AAClB,CAAC,CAAA,CACF,CAAC;AAEW,QAAA,WAAW,GAAG,IAAA,0BAAgB,EACzC,uBAAuB,EACvB,CAAO,eAAgC,EAAE,QAAQ,EAAE,EAAE;IACnD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAmB,CAAC;IACnD,kFAAkF;IAClF,mFAAmF;IACnF,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,IAAA,2CAA+B,EAAC,KAAK,EAAE,eAAe,CAAa,CAAC;IACrF,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,mBAAY,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC3E,OAAO,YAAY,CAAC;AACtB,CAAC,CAAA,CACF,CAAC"}
@@ -6,7 +6,7 @@ const slice_1 = require("./slices/clusters/slice");
6
6
  const slice_2 = require("./slices/current-cluster/slice");
7
7
  const slice_3 = require("./slices/infra-envs/slice");
8
8
  const slice_4 = require("./slices/feature-flags/slice");
9
- const common_1 = require("../../common");
9
+ const slice_5 = require("./slices/current-user/slice");
10
10
  exports.storeDay1 = (0, toolkit_1.configureStore)({
11
11
  devTools: {
12
12
  name: 'Assisted Installer - Day 1',
@@ -16,9 +16,7 @@ exports.storeDay1 = (0, toolkit_1.configureStore)({
16
16
  currentCluster: slice_2.currentClusterReducer,
17
17
  infraEnvs: slice_3.infraEnvsReducer,
18
18
  featureFlags: slice_4.featureFlagsReducer,
19
- },
20
- preloadedState: {
21
- featureFlags: common_1.STANDALONE_DEPLOYMENT_ENABLED_FEATURES,
19
+ currentUser: slice_5.currentUserReducer,
22
20
  },
23
21
  });
24
22
  //# sourceMappingURL=store-day1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store-day1.js","sourceRoot":"","sources":["../../../../lib/ocm/store/store-day1.ts"],"names":[],"mappings":";;;AAAA,8CAAkD;AAClD,mDAA0D;AAC1D,0DAAuE;AACvE,qDAA6D;AAC7D,wDAAmE;AACnE,yCAAsE;AAEzD,QAAA,SAAS,GAAG,IAAA,wBAAc,EAAC;IACtC,QAAQ,EAAE;QACR,IAAI,EAAE,4BAA4B;KACnC;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,uBAAe;QACzB,cAAc,EAAE,6BAAqB;QACrC,SAAS,EAAE,wBAAgB;QAC3B,YAAY,EAAE,2BAAmB;KAClC;IACD,cAAc,EAAE;QACd,YAAY,EAAE,+CAAsC;KACrD;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"store-day1.js","sourceRoot":"","sources":["../../../../lib/ocm/store/store-day1.ts"],"names":[],"mappings":";;;AAAA,8CAAkD;AAClD,mDAA0D;AAC1D,0DAAuE;AACvE,qDAA6D;AAC7D,wDAAmE;AACnE,uDAAiE;AAEpD,QAAA,SAAS,GAAG,IAAA,wBAAc,EAAC;IACtC,QAAQ,EAAE;QACR,IAAI,EAAE,4BAA4B;KACnC;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,uBAAe;QACzB,cAAc,EAAE,6BAAqB;QACrC,SAAS,EAAE,wBAAgB;QAC3B,YAAY,EAAE,2BAAmB;QACjC,WAAW,EAAE,0BAAkB;KAChC;CACF,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=state-slice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-slice.js","sourceRoot":"","sources":["../../../../../lib/ocm/store/types/state-slice.ts"],"names":[],"mappings":""}
@@ -2,4 +2,4 @@
2
2
  * We use this in order to get decent type definitions
3
3
  * for asset files like .svg, .css, .jpg, etc...
4
4
  */
5
- /// <reference types="vite/client" />
5
+ import 'vite/client';
@@ -0,0 +1 @@
1
+ import '@redhat-cloud-services/types';
@@ -0,0 +1,42 @@
1
+ import { unmountComponentAtNode } from 'react-dom';
2
+
3
+ let element: HTMLDivElement | null = null;
4
+
5
+ function getMockContainer() {
6
+ return element;
7
+ }
8
+
9
+ /**
10
+ * Call this in your test if you intend to render a React component
11
+ * @example
12
+ * beforeEach(() => {
13
+ * initMockContainer();
14
+ * });
15
+ */
16
+ function initMockContainer(
17
+ value: HTMLDivElement = document.createElement('div'),
18
+ baseElement: HTMLElement = document.body,
19
+ ) {
20
+ element = value;
21
+ element.id = 'root';
22
+ baseElement.appendChild(element);
23
+ }
24
+
25
+ /**
26
+ * Attempts to unmount and remove the underlying DOM element.
27
+ *
28
+ * Returns true when the underlying DOM element is reset successfully; false otherwise.
29
+ */
30
+ function tryResetMockContainer() {
31
+ let didResetMockContainer = false;
32
+ if (element) {
33
+ unmountComponentAtNode(element);
34
+ element.remove();
35
+ element = null;
36
+ didResetMockContainer = true;
37
+ }
38
+
39
+ return didResetMockContainer;
40
+ }
41
+
42
+ export { getMockContainer, initMockContainer, tryResetMockContainer };
@@ -0,0 +1,26 @@
1
+ import { SetupServer } from 'msw/node';
2
+
3
+ let mockServer: SetupServer | null = null;
4
+
5
+ /**
6
+ * Call this function if you intend to use a mock server.
7
+ * @param value the value returned from calling `import('msw/node').setupServer(handlers)`
8
+ * @see https://mswjs.io/docs/getting-started/mocks/rest-api
9
+ * @example
10
+ * import { setupServer, rest } from 'mws/node';
11
+ *
12
+ * beforeAll(() => {
13
+ * const handlers = []; // <-- Define `rest` handlers there
14
+ * setMockServer(setupServer(...handlers));
15
+ * getMockServer()?.listen({ onUnhandledRequest: 'error' });
16
+ * });
17
+ */
18
+ function setMockServer(value: SetupServer | null) {
19
+ mockServer = value;
20
+ }
21
+
22
+ function getMockServer() {
23
+ return mockServer;
24
+ }
25
+
26
+ export { setMockServer, getMockServer };
@@ -0,0 +1,17 @@
1
+ import { afterAll, afterEach, beforeEach, vi } from 'vitest';
2
+ import { initMockContainer, tryResetMockContainer } from './mock-container';
3
+ import { getMockServer } from './mock-server';
4
+
5
+ afterAll(() => {
6
+ getMockServer()?.close();
7
+ });
8
+
9
+ beforeEach(() => {
10
+ initMockContainer();
11
+ });
12
+
13
+ afterEach(() => {
14
+ getMockServer()?.resetHandlers();
15
+ tryResetMockContainer();
16
+ vi.clearAllMocks();
17
+ });
package/lib/cim/index.ts CHANGED
@@ -15,3 +15,4 @@ export * from '../common/components/clusterWizard/types';
15
15
  export * from '../common/components/clusterDetail';
16
16
  export * from '../common/components/clusterConfiguration';
17
17
  export * from '../common/types/clusters';
18
+ export { ResourceUIState } from '../common';
@@ -1,7 +1,7 @@
1
1
  import Axios, { AxiosError } from 'axios';
2
2
  import * as Sentry from '@sentry/browser';
3
3
  import pick from 'lodash-es/pick.js';
4
- import {
4
+ import type {
5
5
  Error as APIError,
6
6
  InfraError,
7
7
  } from '@openshift-assisted/types/assisted-installer-service';
@@ -9,3 +9,4 @@ export * from './config';
9
9
  export * from './features';
10
10
  export * from './reducers';
11
11
  export * from './selectors';
12
+ export { ResourceUIState } from './types/resource-ui-state';
@@ -1,12 +1,3 @@
1
- export enum ResourceUIState {
2
- LOADING = 'LOADING',
3
- RELOADING = 'RELOADING',
4
- EMPTY = 'EMPTY',
5
- LOADED = 'LOADED',
6
- POLLING_ERROR = 'POLLING_ERROR',
7
- UPDATE_ERROR = 'UPDATE_ERROR',
8
- }
9
-
10
1
  export type WithTestID = {
11
2
  testId?: string;
12
3
  };
@@ -0,0 +1,8 @@
1
+ export enum ResourceUIState {
2
+ LOADING = 'LOADING',
3
+ RELOADING = 'RELOADING',
4
+ EMPTY = 'EMPTY',
5
+ LOADED = 'LOADED',
6
+ POLLING_ERROR = 'POLLING_ERROR',
7
+ UPDATE_ERROR = 'UPDATE_ERROR',
8
+ }
@@ -5,10 +5,10 @@ import { AlertsContextProvider, FeatureGateContextProvider } from '../../../comm
5
5
  import { HostsClusterDetailTabContent } from './HostsClusterDetailTabContent';
6
6
  import { HostsClusterDetailTabProps } from './types';
7
7
  import { storeDay1 } from '../../store';
8
- import { featureFlagsActions } from '../../store/slices/feature-flags/slice';
8
+ import { useFeatureDetection } from '../../hooks/use-feature-detection';
9
9
 
10
10
  const HostsClusterDetailTab = (props: HostsClusterDetailTabProps) => {
11
- storeDay1.dispatch(featureFlagsActions.setFeatureFlags(props.allEnabledFeatures));
11
+ useFeatureDetection();
12
12
 
13
13
  return (
14
14
  <Provider store={storeDay1}>
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from 'react';
1
+ import React from 'react';
2
2
  import { Provider } from 'react-redux';
3
3
  import { Switch, Route, Redirect } from 'react-router-dom';
4
4
  import { Clusters, ClusterPage, NewClusterPage } from './clusters';
@@ -7,15 +7,13 @@ import type { FeatureListType } from '../../common/features/featureGate';
7
7
  import { routeBasePath } from '../config';
8
8
  import { AssistedUILibVersion } from './ui';
9
9
  import { storeDay1 } from '../store';
10
- import { featureFlagsActions } from '../store/slices/feature-flags/slice';
10
+ import { useFeatureDetection } from '../hooks/use-feature-detection';
11
11
 
12
12
  export const Routes: React.FC<{ allEnabledFeatures: FeatureListType }> = ({
13
13
  allEnabledFeatures,
14
14
  children,
15
15
  }) => {
16
- useEffect(() => {
17
- storeDay1.dispatch(featureFlagsActions.setFeatureFlags(allEnabledFeatures));
18
- }, [allEnabledFeatures]);
16
+ useFeatureDetection(allEnabledFeatures);
19
17
 
20
18
  return (
21
19
  <Provider store={storeDay1}>
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import { Modal, ModalVariant, Button, ButtonVariant } from '@patternfly/react-core';
3
+ import { useTranslation } from '../../../common/hooks/use-translation-wrapper';
3
4
 
4
5
  type DeleteHostModalProps = {
5
6
  hostname?: string;
@@ -8,28 +9,32 @@ type DeleteHostModalProps = {
8
9
  onDelete: () => void;
9
10
  };
10
11
 
11
- const DeleteHostModal = ({ isOpen, hostname, onClose, onDelete }: DeleteHostModalProps) => (
12
- <Modal
13
- title="Remove host?"
14
- isOpen={isOpen}
15
- onClose={onClose}
16
- variant={ModalVariant.small}
17
- actions={[
18
- <Button
19
- data-testid="delete-host-submit"
20
- key="confirm"
21
- variant={ButtonVariant.danger}
22
- onClick={onDelete}
23
- >
24
- Remove host
25
- </Button>,
26
- <Button key="cancel" variant={ButtonVariant.link} onClick={onClose}>
27
- Cancel
28
- </Button>,
29
- ]}
30
- >
31
- Host {hostname || ''} will be removed.
32
- </Modal>
33
- );
12
+ const DeleteHostModal = ({ isOpen, hostname, onClose, onDelete }: DeleteHostModalProps) => {
13
+ const { t } = useTranslation();
14
+ return (
15
+ <Modal
16
+ title={t('ai:Remove host?')}
17
+ isOpen={isOpen}
18
+ onClose={onClose}
19
+ variant={ModalVariant.small}
20
+ titleIconVariant="warning"
21
+ actions={[
22
+ <Button
23
+ data-testid="delete-host-submit"
24
+ key="confirm"
25
+ variant={ButtonVariant.danger}
26
+ onClick={onDelete}
27
+ >
28
+ {t('ai:Remove host')}
29
+ </Button>,
30
+ <Button key="cancel" variant={ButtonVariant.link} onClick={onClose}>
31
+ {t('ai:Cancel')}
32
+ </Button>,
33
+ ]}
34
+ >
35
+ {t('ai:Host {{hostname}} will be removed', { hostname: hostname || '' })}
36
+ </Modal>
37
+ );
38
+ };
34
39
 
35
40
  export default DeleteHostModal;
@@ -0,0 +1,12 @@
1
+ import type { FeatureListType } from '../../common/features/featureGate';
2
+
3
+ type FeatureMapping = {
4
+ featureId: keyof FeatureListType;
5
+ capabilityId: string;
6
+ };
7
+ export const externalFeaturesMappings: Array<FeatureMapping> = [
8
+ {
9
+ featureId: 'ASSISTED_INSTALLER_PLATFORM_OCI',
10
+ capabilityId: 'capability.organization.bare_metal_installer_platform_oci',
11
+ },
12
+ ];
@@ -1,2 +1,2 @@
1
- export * from './routeBaseBath';
1
+ export * from './routeBasePath';
2
2
  export * from './constants';
@@ -0,0 +1,125 @@
1
+ import type { Account, Organization } from '@openshift-assisted/types/accounts-management-service';
2
+ import { describe, it, vi, expect } from 'vitest';
3
+ import * as React from 'react';
4
+ import { Provider } from 'react-redux';
5
+ import { render } from 'react-dom';
6
+ import { act } from 'react-dom/test-utils';
7
+ import { featureFlagsActions, FeatureFlagsState } from '../store/slices/feature-flags/slice';
8
+ import { isFeatureEnabled } from '../store/slices/feature-flags/selectors';
9
+ import { RootStateDay1, storeDay1 } from '../store/store-day1';
10
+ import { useFeatureDetection } from './use-feature-detection';
11
+ import { CurrentAccountApi } from '../../common/api/accounts-management-service/current-account-api';
12
+ import { OrganizationsApi } from '../../common/api/accounts-management-service/organizations-api';
13
+ import { getMockContainer } from '../../_test-helpers/mock-container';
14
+
15
+ vi.spyOn(featureFlagsActions, 'setFeatureFlag');
16
+ vi.spyOn(CurrentAccountApi, 'getCurrentAccount').mockImplementation(() => {
17
+ return Promise.resolve({
18
+ username: 'jdoe',
19
+ } as Account);
20
+ });
21
+
22
+ const makeSpyOn$OrganizationsApi$getOrganization = (mockResponse: Organization) =>
23
+ vi.spyOn(OrganizationsApi, 'getOrganization').mockImplementation(() => {
24
+ return Promise.resolve(mockResponse);
25
+ });
26
+
27
+ describe('use-feature-detection.ts', () => {
28
+ it('Sets only external features when no overrides are provided', async () => {
29
+ const DummyComponent: React.FC = () => {
30
+ useFeatureDetection();
31
+ return null;
32
+ };
33
+ makeSpyOn$OrganizationsApi$getOrganization({
34
+ capabilities: [
35
+ {
36
+ name: 'capability.organization.bare_metal_installer_platform_oci',
37
+ value: 'true',
38
+ inherited: true,
39
+ },
40
+ ],
41
+ });
42
+
43
+ await act(() =>
44
+ Promise.resolve(
45
+ render(
46
+ <Provider store={storeDay1}>
47
+ <DummyComponent />
48
+ </Provider>,
49
+ getMockContainer(),
50
+ ),
51
+ ),
52
+ );
53
+
54
+ expect(
55
+ isFeatureEnabled(
56
+ storeDay1.getState() as RootStateDay1 & FeatureFlagsState,
57
+ 'ASSISTED_INSTALLER_PLATFORM_OCI',
58
+ ),
59
+ ).toBe(true);
60
+ });
61
+
62
+ it('Internal features override external features', async () => {
63
+ const featuresOverride = { ASSISTED_INSTALLER_PLATFORM_OCI: true };
64
+ const DummyComponent: React.FC = () => {
65
+ useFeatureDetection(featuresOverride);
66
+ return null;
67
+ };
68
+ makeSpyOn$OrganizationsApi$getOrganization({
69
+ capabilities: [
70
+ {
71
+ name: 'capability.organization.bare_metal_installer_platform_oci',
72
+ value: 'false',
73
+ inherited: true,
74
+ },
75
+ ],
76
+ });
77
+
78
+ await act(() =>
79
+ Promise.resolve(
80
+ render(
81
+ <Provider store={storeDay1}>
82
+ <DummyComponent />
83
+ </Provider>,
84
+ getMockContainer(),
85
+ ),
86
+ ),
87
+ );
88
+
89
+ expect(
90
+ isFeatureEnabled(
91
+ storeDay1.getState() as RootStateDay1 & FeatureFlagsState,
92
+ 'ASSISTED_INSTALLER_PLATFORM_OCI',
93
+ ),
94
+ ).toBe(featuresOverride.ASSISTED_INSTALLER_PLATFORM_OCI);
95
+ });
96
+
97
+ it('Processes only features prefixed with ASSISTED_INSTALLER', async () => {
98
+ const featuresOverride = { NON_ASSISTED_INSTALLER_FEATURE: true };
99
+ const DummyComponent: React.FC = () => {
100
+ useFeatureDetection(featuresOverride);
101
+ return null;
102
+ };
103
+
104
+ await act(() =>
105
+ Promise.resolve(
106
+ render(
107
+ <Provider store={storeDay1}>
108
+ <DummyComponent />
109
+ </Provider>,
110
+ getMockContainer(),
111
+ ),
112
+ ),
113
+ );
114
+
115
+ /* Here we don't mock the call to the user organization API, therefore the
116
+ * capabilities in the currentUser slice should remain undefined, since the list
117
+ * of featuresOverride should get filtered out of features not prefixed with
118
+ * "ASSISTED_INSTALLER" there should be no call to setFeatureFlag.
119
+ */
120
+ expect(featureFlagsActions.setFeatureFlag).not.toHaveBeenCalled();
121
+ expect(
122
+ storeDay1.getState().featureFlags.data['NON_ASSISTED_INSTALLER_FEATURE'],
123
+ ).not.toBeDefined();
124
+ });
125
+ });
@@ -0,0 +1,17 @@
1
+ import type { FeatureListType } from '../../common/features/featureGate';
2
+ import { featureFlagsAsyncActions } from '../store/slices/feature-flags/slice';
3
+ import { storeDay1 } from '../store/store-day1';
4
+ import { useEffect } from 'react';
5
+
6
+ export function useFeatureDetection<T extends Partial<Record<string, boolean> & FeatureListType>>(
7
+ overrides: T | null = null,
8
+ ) {
9
+ useEffect(() => {
10
+ const onlyAssistedInstallerFeatures = Object.fromEntries(
11
+ Object.entries(overrides ?? {}).filter(([k, _v]) => k.startsWith('ASSISTED_INSTALLER')),
12
+ );
13
+ void storeDay1.dispatch(
14
+ featureFlagsAsyncActions.detectFeaturesAsync(onlyAssistedInstallerFeatures),
15
+ );
16
+ }, [overrides]);
17
+ }
@@ -1,21 +1,21 @@
1
- import { ClustersAPI } from '../services/apis';
2
- import HostsService from './HostsService';
3
- import InfraEnvsService from './InfraEnvsService';
4
- import { AI_UI_TAG } from '../../common';
5
- import { isInOcm } from '../api';
6
- import { ClusterCreateParamsWithStaticNetworking } from './types';
7
- import omit from 'lodash-es/omit.js';
8
- import {
1
+ import type { ClusterCreateParamsWithStaticNetworking } from './types';
2
+ import type {
9
3
  CustomManifestValues,
10
4
  ListManifestsExtended,
11
5
  } from '../components/clusterConfiguration/manifestsConfiguration/data/dataTypes';
12
- import {
6
+ import type {
13
7
  Cluster,
14
8
  Host,
15
9
  InfraEnvCreateParams,
16
10
  UpdateManifestParams,
17
11
  V2ClusterUpdateParams,
18
12
  } from '@openshift-assisted/types/assisted-installer-service';
13
+ import omit from 'lodash-es/omit.js';
14
+ import ClustersAPI from '../../common/api/assisted-service/ClustersAPI';
15
+ import HostsService from './HostsService';
16
+ import InfraEnvsService from './InfraEnvsService';
17
+ import { AI_UI_TAG } from '../../common/config/constants';
18
+ import { isInOcm } from '../../common/api/axiosClient';
19
19
 
20
20
  const ClustersService = {
21
21
  findHost(hosts: Cluster['hosts'] = [], hostId: Host['id']) {
@@ -1,5 +1,5 @@
1
- import { CpuArchitecture, getAllCpuArchitectures } from '../../common';
2
- import { Cluster, InfraEnv } from '@openshift-assisted/types/assisted-installer-service';
1
+ import type { Cluster, InfraEnv } from '@openshift-assisted/types/assisted-installer-service';
2
+ import { CpuArchitecture, getAllCpuArchitectures } from '../../common//types/cpuArchitecture';
3
3
 
4
4
  const CACHE_KEY = 'infra-env-ids-cache-v2';
5
5
 
@@ -1,9 +1,6 @@
1
1
  import { useDispatch, useSelector } from 'react-redux';
2
2
  import type { TypedUseSelectorHook } from 'react-redux';
3
3
  import type { DispatchDay1, RootStateDay1 } from './store-day1';
4
- import type { DispatchDay2, RootStateDay2 } from './store-day2';
5
4
 
6
- export const useDispatchDay1: () => DispatchDay1 = useDispatch;
5
+ export const useDispatchDay1 = useDispatch<DispatchDay1>;
7
6
  export const useSelectorDay1: TypedUseSelectorHook<RootStateDay1> = useSelector;
8
- export const useDispatchDay2: () => DispatchDay2 = useDispatch;
9
- export const useSelectorDay2: TypedUseSelectorHook<RootStateDay2> = useSelector;