@truedat/core 7.5.7 → 7.5.10

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 (277) hide show
  1. package/package.json +34 -68
  2. package/src/__tests__/routesTree.spec.js +108 -0
  3. package/src/api/queries.js +10 -0
  4. package/src/api.js +3 -0
  5. package/src/components/AddMemberForm.js +10 -10
  6. package/src/components/AddResourceMember.js +0 -2
  7. package/src/components/AdminMenu.js +0 -1
  8. package/src/components/AiMenu.js +0 -1
  9. package/src/components/Alert.js +1 -1
  10. package/src/components/AlertExporter.js +0 -1
  11. package/src/components/ArrayDecorator.js +0 -1
  12. package/src/components/Authorized.js +2 -2
  13. package/src/components/AvailableFilters.js +1 -2
  14. package/src/components/CSVFileModal.js +1 -1
  15. package/src/components/CardGroupsAccordion.js +1 -1
  16. package/src/components/CatalogMenu.js +1 -2
  17. package/src/components/CommentRow.js +7 -8
  18. package/src/components/Comments.js +3 -4
  19. package/src/components/CommentsForm.js +2 -2
  20. package/src/components/ConfirmModal.js +0 -1
  21. package/src/components/CursorPagination.js +1 -2
  22. package/src/components/DashboardMenu.js +0 -1
  23. package/src/components/Date.js +0 -1
  24. package/src/components/DateFilter.js +1 -1
  25. package/src/components/DateRangeFilter.js +1 -1
  26. package/src/components/DateTime.js +0 -1
  27. package/src/components/DescriptionInput.js +1 -1
  28. package/src/components/DomainSelector.js +2 -3
  29. package/src/components/DropdownMenuItem.js +0 -1
  30. package/src/components/ErrorBoundary.js +8 -7
  31. package/src/components/FieldLabel.js +0 -1
  32. package/src/components/FilterDropdown.js +3 -3
  33. package/src/components/FilterItem.js +0 -1
  34. package/src/components/FilterMultilevelDropdown.js +1 -1
  35. package/src/components/GenericCrumbs.js +3 -4
  36. package/src/components/GlossaryMenu.js +24 -31
  37. package/src/components/GrantMenu.js +0 -1
  38. package/src/components/GroupActions.js +15 -15
  39. package/src/components/Hierarchy.js +7 -10
  40. package/src/components/HierarchyFilterDropdown.js +1 -2
  41. package/src/components/HierarchyNodeFinder.js +4 -4
  42. package/src/components/HierarchySelector.js +1 -2
  43. package/src/components/HistoryBackButton.js +6 -6
  44. package/src/components/IngestMenu.js +0 -1
  45. package/src/components/LanguagesTabs.js +4 -4
  46. package/src/components/LineageMenu.js +0 -1
  47. package/src/components/Loading.js +1 -2
  48. package/src/components/MembersMenu.js +0 -1
  49. package/src/components/ModalSaveFilter.js +1 -1
  50. package/src/components/NodeOpenActions.js +0 -1
  51. package/src/components/OptionGroup.js +1 -2
  52. package/src/components/OptionModal.js +0 -1
  53. package/src/components/Pagination.js +0 -1
  54. package/src/components/QualityMenu.js +0 -1
  55. package/src/components/QxMenu.js +0 -1
  56. package/src/components/Redirector.js +3 -3
  57. package/src/components/ResourceMember.js +1 -2
  58. package/src/components/ResourceMembers.js +3 -3
  59. package/src/components/ResourceMembersActions.js +1 -2
  60. package/src/components/RichTextEditor.js +7 -8
  61. package/src/components/RouteListener.js +11 -32
  62. package/src/components/SafeLink.js +0 -1
  63. package/src/components/ScrollToTop.js +13 -16
  64. package/src/components/SearchFilterDropdown.js +1 -1
  65. package/src/components/SearchInput.js +3 -3
  66. package/src/components/SearchMenu.js +0 -1
  67. package/src/components/SelectedFilters.js +1 -2
  68. package/src/components/SideMenu.js +0 -1
  69. package/src/components/SidebarToggle.js +2 -2
  70. package/src/components/StructureFilterItem.js +0 -1
  71. package/src/components/Submenu.js +8 -7
  72. package/src/components/SystemsLoader.js +27 -0
  73. package/src/components/TaxonomyMenu.js +0 -1
  74. package/src/components/TemplateSelector.js +1 -1
  75. package/src/components/TemplatesLoader.js +24 -0
  76. package/src/components/TreeSelector.js +4 -4
  77. package/src/components/Unauthorized.js +0 -1
  78. package/src/components/UploadModal.js +152 -176
  79. package/src/components/UserFilter.js +0 -2
  80. package/src/components/UserFilters.js +0 -2
  81. package/src/components/__tests__/AddMemberForm.spec.js +26 -38
  82. package/src/components/__tests__/AddResourceMember.spec.js +0 -1
  83. package/src/components/__tests__/AdminMenu.spec.js +0 -1
  84. package/src/components/__tests__/Alert.spec.js +17 -21
  85. package/src/components/__tests__/AvailableFilters.spec.js +19 -14
  86. package/src/components/__tests__/CardGroupsAccordion.spec.js +24 -15
  87. package/src/components/__tests__/CatalogMenu.spec.js +27 -45
  88. package/src/components/__tests__/CommentsLoader.spec.js +17 -14
  89. package/src/components/__tests__/CursorPagination.spec.js +0 -1
  90. package/src/components/__tests__/DashboardMenu.spec.js +0 -1
  91. package/src/components/__tests__/DateFilter.spec.js +6 -28
  92. package/src/components/__tests__/DateTime.spec.js +0 -1
  93. package/src/components/__tests__/DomainSelector.spec.js +4 -4
  94. package/src/components/__tests__/DropdownMenuItem.spec.js +0 -1
  95. package/src/components/__tests__/FilterDropdown.spec.js +28 -27
  96. package/src/components/__tests__/FilterItem.spec.js +19 -20
  97. package/src/components/__tests__/FilterMultilevelDropdown.spec.js +16 -16
  98. package/src/components/__tests__/FiltersLoader.spec.js +25 -20
  99. package/src/components/__tests__/GenericCrumbs.spec.js +0 -1
  100. package/src/components/__tests__/GlossaryMenu.spec.js +0 -1
  101. package/src/components/__tests__/GrantMenu.spec.js +0 -1
  102. package/src/components/__tests__/GroupActions.spec.js +0 -1
  103. package/src/components/__tests__/Hierarchy.spec.js +0 -1
  104. package/src/components/__tests__/HierarchyFilterDropdown.spec.js +16 -14
  105. package/src/components/__tests__/HierarchyNodeFinder.spec.js +0 -1
  106. package/src/components/__tests__/HierarchySelector.spec.js +28 -35
  107. package/src/components/__tests__/HistoryBackButton.spec.js +23 -23
  108. package/src/components/__tests__/IngestMenu.spec.js +0 -1
  109. package/src/components/__tests__/LineageMenu.spec.js +0 -1
  110. package/src/components/__tests__/Loading.spec.js +4 -5
  111. package/src/components/__tests__/MembersMenu.spec.js +0 -1
  112. package/src/components/__tests__/ModalSaveFilter.spec.js +0 -1
  113. package/src/components/__tests__/OptionGroup.spec.js +5 -7
  114. package/src/components/__tests__/Pagination.spec.js +22 -16
  115. package/src/components/__tests__/QualityMenu.spec.js +0 -1
  116. package/src/components/__tests__/Redirector.spec.js +5 -10
  117. package/src/components/__tests__/ResourceMembers.spec.js +36 -27
  118. package/src/components/__tests__/ResourceMembersAction.spec.js +1 -2
  119. package/src/components/__tests__/RouteListener.spec.js +25 -26
  120. package/src/components/__tests__/SafeLink.spec.js +0 -1
  121. package/src/components/__tests__/SearchDateFilter.spec.js +0 -1
  122. package/src/components/__tests__/SearchFilterDropdown.spec.js +32 -24
  123. package/src/components/__tests__/SearchInput.spec.js +5 -5
  124. package/src/components/__tests__/SearchMenu.spec.js +0 -1
  125. package/src/components/__tests__/SelectedFilters.spec.js +50 -47
  126. package/src/components/__tests__/SideMenu.spec.js +5 -8
  127. package/src/components/__tests__/StructureFilterItem.spec.js +13 -11
  128. package/src/components/__tests__/Submenu.spec.js +0 -1
  129. package/src/components/__tests__/SystemsLoader.spec.js +40 -0
  130. package/src/components/__tests__/TaxonomyMenu.spec.js +0 -1
  131. package/src/components/__tests__/TemplateSelector.spec.js +27 -29
  132. package/src/components/__tests__/TemplatesLoader.spec.js +30 -0
  133. package/src/components/__tests__/TreeSelector.spec.js +110 -83
  134. package/src/components/__tests__/UploadModal.spec.js +171 -0
  135. package/src/components/__tests__/UserFilters.spec.js +44 -37
  136. package/src/components/__tests__/__snapshots__/AddMemberForm.spec.js.snap +1 -0
  137. package/src/components/__tests__/__snapshots__/AddResourceMember.spec.js.snap +10 -9
  138. package/src/components/__tests__/__snapshots__/AdminMenu.spec.js.snap +22 -11
  139. package/src/components/__tests__/__snapshots__/Alert.spec.js.snap +29 -17
  140. package/src/components/__tests__/__snapshots__/AvailableFilters.spec.js.snap +1 -1
  141. package/src/components/__tests__/__snapshots__/CatalogMenu.spec.js.snap +18 -8
  142. package/src/components/__tests__/__snapshots__/CursorPagination.spec.js.snap +3 -0
  143. package/src/components/__tests__/__snapshots__/DateFilter.spec.js.snap +165 -107
  144. package/src/components/__tests__/__snapshots__/DomainSelector.spec.js.snap +1 -1
  145. package/src/components/__tests__/__snapshots__/FilterItem.spec.js.snap +13 -20
  146. package/src/components/__tests__/__snapshots__/GlossaryMenu.spec.js.snap +8 -0
  147. package/src/components/__tests__/__snapshots__/GrantMenu.spec.js.snap +14 -7
  148. package/src/components/__tests__/__snapshots__/GroupActions.spec.js.snap +0 -1
  149. package/src/components/__tests__/__snapshots__/Hierarchy.spec.js.snap +0 -1
  150. package/src/components/__tests__/__snapshots__/HierarchySelector.spec.js.snap +1 -1
  151. package/src/components/__tests__/__snapshots__/HistoryBackButton.spec.js.snap +10 -20
  152. package/src/components/__tests__/__snapshots__/IngestMenu.spec.js.snap +3 -1
  153. package/src/components/__tests__/__snapshots__/LineageMenu.spec.js.snap +6 -3
  154. package/src/components/__tests__/__snapshots__/Loading.spec.js.snap +5 -4
  155. package/src/components/__tests__/__snapshots__/MembersMenu.spec.js.snap +6 -3
  156. package/src/components/__tests__/__snapshots__/OptionGroup.spec.js.snap +38 -28
  157. package/src/components/__tests__/__snapshots__/Pagination.spec.js.snap +213 -64
  158. package/src/components/__tests__/__snapshots__/QualityMenu.spec.js.snap +14 -8
  159. package/src/components/__tests__/__snapshots__/ResourceMembers.spec.js.snap +1 -0
  160. package/src/components/__tests__/__snapshots__/ResourceMembersAction.spec.js.snap +2 -1
  161. package/src/components/__tests__/__snapshots__/RouteListener.spec.js.snap +1 -1
  162. package/src/components/__tests__/__snapshots__/SearchInput.spec.js.snap +10 -12
  163. package/src/components/__tests__/__snapshots__/SearchMenu.spec.js.snap +3 -1
  164. package/src/components/__tests__/__snapshots__/SelectedFilters.spec.js.snap +8 -8
  165. package/src/components/__tests__/__snapshots__/SideMenu.spec.js.snap +112 -50
  166. package/src/components/__tests__/__snapshots__/Submenu.spec.js.snap +3 -0
  167. package/src/components/__tests__/__snapshots__/SystemsLoader.spec.js.snap +3 -0
  168. package/src/components/__tests__/__snapshots__/TaxonomyMenu.spec.js.snap +3 -1
  169. package/src/components/__tests__/__snapshots__/TemplateSelector.spec.js.snap +7 -7
  170. package/src/components/common/SearchContextWrapper.js +1 -4
  171. package/src/hooks/__mocks__/useAuthorized.js +2 -0
  172. package/src/hooks/__mocks__/useUserFilters.js +17 -0
  173. package/src/hooks/__tests__/{useAclEntries.spec.js → useAclEntries.spec.js.disabled} +1 -1
  174. package/src/hooks/__tests__/useAuthorized.spec.js +1 -1
  175. package/src/hooks/useAclEntries.js +6 -3
  176. package/src/hooks/useActiveRoute.js +3 -3
  177. package/src/hooks/useActiveRoutes.js +4 -4
  178. package/src/hooks/useHierarchies.js +112 -0
  179. package/src/hooks/useLocales.js +1 -1
  180. package/src/hooks/useMessages.js +1 -1
  181. package/src/hooks/usePath.js +4 -4
  182. package/src/hooks/useUserFilters.js +1 -1
  183. package/src/i18n/components/EditableCell.js +1 -1
  184. package/src/i18n/components/I18nRoutes.js +11 -16
  185. package/src/i18n/components/LangProvider.js +56 -101
  186. package/src/i18n/components/LangProviderWrapper.js +26 -33
  187. package/src/i18n/components/Languages.js +8 -7
  188. package/src/i18n/components/MessageForm.js +9 -23
  189. package/src/i18n/components/Messages.js +5 -4
  190. package/src/i18n/components/MessagesTable.js +0 -1
  191. package/src/i18n/components/NewMessage.js +5 -4
  192. package/src/i18n/components/__tests__/EditableCell.spec.js +18 -20
  193. package/src/i18n/components/__tests__/I18nRoutes.spec.js +39 -5
  194. package/src/i18n/components/__tests__/MessageForm.spec.js +0 -1
  195. package/src/i18n/components/__tests__/Messages.spec.js +9 -21
  196. package/src/i18n/components/__tests__/NewMessage.spec.js +0 -1
  197. package/src/i18n/components/__tests__/__snapshots__/I18nRoutes.spec.js.snap +25 -1
  198. package/src/i18n/components/__tests__/__snapshots__/MessageForm.spec.js.snap +1 -0
  199. package/src/i18n/components/__tests__/__snapshots__/Messages.spec.js.snap +5 -13
  200. package/src/i18n/components/__tests__/__snapshots__/NewMessage.spec.js.snap +2 -0
  201. package/src/reducers/__tests__/comments.spec.js +9 -6
  202. package/src/reducers/__tests__/commentsResource.spec.js +5 -2
  203. package/src/reducers/__tests__/coreMessage.spec.js +1 -1
  204. package/src/reducers/__tests__/dashboardDomains.spec.js +5 -5
  205. package/src/reducers/__tests__/systems.spec.js +42 -0
  206. package/src/reducers/__tests__/systemsLoading.spec.js +22 -0
  207. package/src/reducers/index.js +5 -1
  208. package/src/reducers/systems.js +19 -0
  209. package/src/reducers/systemsLoading.js +14 -0
  210. package/src/router/Loader.js +10 -0
  211. package/src/router/ProtectedRoute.js +11 -0
  212. package/src/router/Unauthorized.js +16 -0
  213. package/src/router/__tests__/ProtectedRoute.spec.js +49 -0
  214. package/src/router/__tests__/Unauthorized.spec.js +15 -0
  215. package/src/router/__tests__/__snapshots__/ProtectedRoute.spec.js.snap +44 -0
  216. package/src/router/__tests__/__snapshots__/Unauthorized.spec.js.snap +26 -0
  217. package/src/router/index.js +5 -0
  218. package/src/routes.js +7 -7
  219. package/src/routesTree.js +93 -0
  220. package/src/routines.js +8 -0
  221. package/src/sagas/__tests__/addComment.spec.js +3 -5
  222. package/src/sagas/__tests__/fetchComments.spec.js +1 -1
  223. package/src/sagas/__tests__/fetchSystems.spec.js +69 -0
  224. package/src/sagas/fetchSystems.js +25 -0
  225. package/src/sagas/index.js +3 -0
  226. package/src/search/FilterDropdown.js +0 -1
  227. package/src/search/FilterItem.js +0 -1
  228. package/src/search/FilterMultilevelDropdown.js +1 -1
  229. package/src/search/FilterQueryDropdown.js +38 -42
  230. package/src/search/HierarchyFilterDropdown.js +3 -4
  231. package/src/search/ModalSaveFilter.js +1 -1
  232. package/src/search/Pagination.js +0 -1
  233. package/src/search/SearchContext.js +9 -11
  234. package/src/search/SearchDateFilter.js +0 -1
  235. package/src/search/SearchFilters.js +1 -2
  236. package/src/search/SearchSelectedFilters.js +4 -4
  237. package/src/search/SearchWidget.js +0 -4
  238. package/src/search/UserFilter.js +0 -2
  239. package/src/search/UserFilters.js +4 -3
  240. package/src/search/__tests__/FilterDropdown.spec.js +54 -51
  241. package/src/search/__tests__/FilterItem.spec.js +20 -15
  242. package/src/search/__tests__/FilterQueryDropdown.spec.js +106 -84
  243. package/src/search/__tests__/ModalSaveFilter.spec.js +4 -5
  244. package/src/search/__tests__/SearchContext.spec.js +3 -4
  245. package/src/search/__tests__/SearchWidget.spec.js +0 -1
  246. package/src/selectors/__tests__/getConceptSubscope.spec.js +37 -0
  247. package/src/selectors/__tests__/getDashboardConfig.spec.js +9 -9
  248. package/src/selectors/__tests__/getMessage.spec.js +1 -1
  249. package/src/selectors/__tests__/getSidemenuGlossarySubscopes.spec.js +17 -0
  250. package/src/selectors/__tests__/makeActiveFiltersSelector.spec.js +2 -2
  251. package/src/selectors/__tests__/makeSearchQuerySelector.spec.js +1 -1
  252. package/src/selectors/__tests__/makeTagOptionsSelector.spec.js +6 -6
  253. package/src/selectors/getConceptSubscope.js +19 -0
  254. package/src/selectors/getMessage.js +2 -2
  255. package/src/selectors/getRecipients.js +34 -0
  256. package/src/selectors/getSidemenuGlossarySubscopes.js +8 -0
  257. package/src/selectors/index.js +5 -0
  258. package/src/selectors/makeActiveFiltersSelector.js +2 -6
  259. package/src/selectors/makeSearchQuerySelector.js +4 -11
  260. package/src/selectors/makeTagOptionsSelector.js +4 -8
  261. package/src/selectors/subscopedTemplates.js +16 -0
  262. package/src/selectors/taxonomy.js +170 -0
  263. package/src/services/__tests__/columnDecorator.spec.js +1 -1
  264. package/src/services/__tests__/dateFilterFormatter.spec.js +2 -2
  265. package/src/services/__tests__/fieldType.spec.js +2 -2
  266. package/src/services/__tests__/filters.spec.js +1 -1
  267. package/src/services/__tests__/message.spec.js +2 -2
  268. package/src/services/__tests__/operators.spec.js +5 -5
  269. package/src/services/__tests__/sort.spec.js +8 -8
  270. package/src/services/__tests__/storage.spec.js +17 -0
  271. package/src/services/arrays.js +16 -14
  272. package/src/services/columnDecoratorComponent.js +2 -2
  273. package/src/services/columnDecorators.js +0 -1
  274. package/src/services/fieldType.js +8 -8
  275. package/src/services/filters.js +5 -5
  276. package/src/services/message.js +1 -4
  277. package/src/services/operators.js +1 -1
@@ -4,6 +4,8 @@ import { commentsResource } from "./commentsResource";
4
4
  import { coreMessage } from "./coreMessage";
5
5
  import { dashboardDomains } from "./dashboardDomains";
6
6
  import { sidebarVisible } from "./sidebarVisible";
7
+ import { systems } from "./systems";
8
+ import { systemsLoading } from "./systemsLoading";
7
9
 
8
10
  export {
9
11
  commentsLoading,
@@ -11,5 +13,7 @@ export {
11
13
  commentsResource,
12
14
  coreMessage,
13
15
  dashboardDomains,
14
- sidebarVisible
16
+ sidebarVisible,
17
+ systems,
18
+ systemsLoading,
15
19
  };
@@ -0,0 +1,19 @@
1
+ import _ from "lodash/fp";
2
+ import { fetchSystems, clearSystems } from "../routines";
3
+
4
+ const initialState = [];
5
+
6
+ const systems = (state = initialState, { type, payload }) => {
7
+ switch (type) {
8
+ case fetchSystems.TRIGGER:
9
+ return initialState;
10
+ case clearSystems.TRIGGER:
11
+ return initialState;
12
+ case fetchSystems.SUCCESS:
13
+ return _.propOr([], "data")(payload);
14
+ default:
15
+ return state;
16
+ }
17
+ };
18
+
19
+ export { systems };
@@ -0,0 +1,14 @@
1
+ import { fetchSystems } from "../routines";
2
+
3
+ const systemsLoading = (state = false, { type }) => {
4
+ switch (type) {
5
+ case fetchSystems.TRIGGER:
6
+ return true;
7
+ case fetchSystems.FULFILL:
8
+ return false;
9
+ default:
10
+ return state;
11
+ }
12
+ };
13
+
14
+ export { systemsLoading };
@@ -0,0 +1,10 @@
1
+ import { Outlet } from "react-router";
2
+
3
+ export default function Loader({ loaders, children }) {
4
+ return (
5
+ <>
6
+ {loaders}
7
+ {children ? children : <Outlet />}
8
+ </>
9
+ );
10
+ }
@@ -0,0 +1,11 @@
1
+ import { Outlet } from "react-router";
2
+ import { useAuthorized } from "@truedat/core/hooks/useAuthorized";
3
+ import Unauthorized from "@truedat/core/router/Unauthorized";
4
+
5
+ export default function ProtectedRoute({ authorization, children }) {
6
+ const isAllowed = useAuthorized(authorization);
7
+ if (!isAllowed) {
8
+ return <Unauthorized />;
9
+ }
10
+ return children ? children : <Outlet />;
11
+ }
@@ -0,0 +1,16 @@
1
+ import { useIntl } from "react-intl";
2
+ import { Message } from "semantic-ui-react";
3
+
4
+ export default function Unauthorized() {
5
+ const { formatMessage } = useIntl();
6
+ return (
7
+ <Message
8
+ icon="warning sign"
9
+ className="center"
10
+ header={formatMessage({ id: "view.unauthorized.head" })}
11
+ content={formatMessage({ id: "view.unauthorized.content" })}
12
+ attached="bottom"
13
+ visible
14
+ />
15
+ );
16
+ }
@@ -0,0 +1,49 @@
1
+ import { render, waitForLoad } from "@truedat/test/render";
2
+ import ProtectedRoute from "../ProtectedRoute";
3
+ import { useAuthorized } from "@truedat/core/hooks/useAuthorized";
4
+
5
+ // Mock the hooks module
6
+ jest.mock("@truedat/core/hooks/useAuthorized", () => ({
7
+ useAuthorized: jest.fn(() => true),
8
+ }));
9
+
10
+ // Mock the Outlet component
11
+ jest.mock("react-router", () => ({
12
+ ...jest.requireActual("react-router"),
13
+ Outlet: () => <div data-testid="outlet">Outlet Content</div>,
14
+ }));
15
+
16
+ describe("ProtectedRoute", () => {
17
+ it("renders unauthorized message when not authorized", async () => {
18
+ useAuthorized.mockReturnValueOnce(false);
19
+
20
+ const rendered = render(<ProtectedRoute authorization="some_permission" />);
21
+ await waitForLoad(rendered);
22
+
23
+ expect(rendered.getByText(/view.unauthorized.head/i)).toBeInTheDocument();
24
+ expect(
25
+ rendered.getByText(/view.unauthorized.content/i),
26
+ ).toBeInTheDocument();
27
+ expect(rendered.container).toMatchSnapshot();
28
+ });
29
+
30
+ it("renders children when authorized and children are provided", async () => {
31
+ const rendered = render(
32
+ <ProtectedRoute authorization="some_permission">
33
+ <div>Protected Content</div>
34
+ </ProtectedRoute>,
35
+ );
36
+ await waitForLoad(rendered);
37
+
38
+ expect(rendered.getByText(/protected content/i)).toBeInTheDocument();
39
+ expect(rendered.container).toMatchSnapshot();
40
+ });
41
+
42
+ it("renders outlet when authorized and no children are provided", async () => {
43
+ const rendered = render(<ProtectedRoute authorization="some_permission" />);
44
+ await waitForLoad(rendered);
45
+
46
+ expect(rendered.getByTestId("outlet")).toBeInTheDocument();
47
+ expect(rendered.container).toMatchSnapshot();
48
+ });
49
+ });
@@ -0,0 +1,15 @@
1
+ import { render, waitForLoad } from "@truedat/test/render";
2
+ import Unauthorized from "../Unauthorized";
3
+
4
+ describe("Unauthorized", () => {
5
+ it("renders unauthorized message", async () => {
6
+ const rendered = render(<Unauthorized />);
7
+ await waitForLoad(rendered);
8
+
9
+ expect(rendered.getByText(/view.unauthorized.head/i)).toBeInTheDocument();
10
+ expect(
11
+ rendered.getByText(/view.unauthorized.content/i),
12
+ ).toBeInTheDocument();
13
+ expect(rendered.container).toMatchSnapshot();
14
+ });
15
+ });
@@ -0,0 +1,44 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ProtectedRoute renders children when authorized and children are provided 1`] = `
4
+ <div>
5
+ <div>
6
+ Protected Content
7
+ </div>
8
+ </div>
9
+ `;
10
+
11
+ exports[`ProtectedRoute renders outlet when authorized and no children are provided 1`] = `
12
+ <div>
13
+ <div
14
+ data-testid="outlet"
15
+ >
16
+ Outlet Content
17
+ </div>
18
+ </div>
19
+ `;
20
+
21
+ exports[`ProtectedRoute renders unauthorized message when not authorized 1`] = `
22
+ <div>
23
+ <div
24
+ class="ui icon visible bottom attached message center"
25
+ >
26
+ <i
27
+ aria-hidden="true"
28
+ class="warning sign icon"
29
+ />
30
+ <div
31
+ class="content"
32
+ >
33
+ <div
34
+ class="header"
35
+ >
36
+ view.unauthorized.head
37
+ </div>
38
+ <p>
39
+ view.unauthorized.content
40
+ </p>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ `;
@@ -0,0 +1,26 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`Unauthorized renders unauthorized message 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui icon visible bottom attached message center"
7
+ >
8
+ <i
9
+ aria-hidden="true"
10
+ class="warning sign icon"
11
+ />
12
+ <div
13
+ class="content"
14
+ >
15
+ <div
16
+ class="header"
17
+ >
18
+ view.unauthorized.head
19
+ </div>
20
+ <p>
21
+ view.unauthorized.content
22
+ </p>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ `;
@@ -0,0 +1,5 @@
1
+ import ProtectedRoute from "./ProtectedRoute";
2
+ import Loader from "./Loader";
3
+ import Unauthorized from "./Unauthorized";
4
+
5
+ export { ProtectedRoute, Unauthorized, Loader };
package/src/routes.js CHANGED
@@ -97,10 +97,10 @@ export const HIERARCHY_CREATE = "/hierarchies/new";
97
97
  export const I18N = "/i18n";
98
98
  export const I18N_MESSAGES = "/i18n/messages";
99
99
  export const I18N_MESSAGES_NEW = "/i18n/messages/new";
100
- export const IMPLEMENTATION = "/implementations/:implementation_id(\\d+)";
100
+ export const IMPLEMENTATION = "/implementations/:implementation_id";
101
101
  export const IMPLEMENTATIONS = "/implementations";
102
- export const IMPLEMENTATIONS_DEPRECATED = "/deprecatedImplementations";
103
- export const IMPLEMENTATIONS_PENDING = "/pendingImplementations";
102
+ export const IMPLEMENTATIONS_DEPRECATED = "/implementations/deprecated";
103
+ export const IMPLEMENTATIONS_PENDING = "/implementations/pending";
104
104
  export const IMPLEMENTATION_CLONE = "/implementations/:implementation_id/clone";
105
105
  export const IMPLEMENTATION_CONCEPT_LINKS =
106
106
  "/implementations/:implementation_id/links/concepts";
@@ -196,7 +196,7 @@ export const RESOURCE_MAPPINGS = "/resource_mappings";
196
196
  export const ROLE = "/roles/:id";
197
197
  export const ROLES = "/roles";
198
198
  export const ROLES_NEW = "/roles/new";
199
- export const RULE = "/rules/:id(\\d+)";
199
+ export const RULE = "/rules/:id";
200
200
  export const RULES = "/rules";
201
201
  export const RULE_EDIT = "/rules/:id/edit";
202
202
  export const RULE_EVENTS = "/rules/:id/events";
@@ -271,7 +271,6 @@ export const TASK = "/reindex/:id";
271
271
  export const TEMPLATE = "/templates/:templateId";
272
272
  export const TEMPLATES = "/templates";
273
273
  export const TEMPLATES_NEW = "/templates/new";
274
- export const TEMPLATE_EDIT = "/templates/:id/edit";
275
274
  export const UNAUTHORIZED = "/unauthorized";
276
275
  export const USER = "/users/:id";
277
276
  export const USERS = "/users";
@@ -500,7 +499,6 @@ const routes = {
500
499
  TEMPLATE,
501
500
  TEMPLATES,
502
501
  TEMPLATES_NEW,
503
- TEMPLATE_EDIT,
504
502
  UNAUTHORIZED,
505
503
  USER,
506
504
  USERS,
@@ -509,5 +507,7 @@ const routes = {
509
507
  USER_EDIT_PASSWORD,
510
508
  };
511
509
 
512
- export const linkTo = _.mapValues(compile)(routes);
510
+ export const linkTo = _.mapValues(
511
+ (route) => (params) => compile(route)(_.mapValues((x) => `${x}`)(params))
512
+ )(routes);
513
513
  export default routes;
@@ -0,0 +1,93 @@
1
+ import _ from "lodash/fp";
2
+
3
+ const routes = [
4
+ "/",
5
+ {
6
+ i18n: [
7
+ "i18n",
8
+ {
9
+ messages: ["messages", { new: ["new"] }],
10
+ },
11
+ ],
12
+ roles: [
13
+ "roles",
14
+ {
15
+ new: ["new"],
16
+ ":id": [":id"],
17
+ },
18
+ ],
19
+ },
20
+ ];
21
+ /**
22
+ * Returns the path for a given location based on the routes structure
23
+ * @param {string} location - A slash separated string representing the location (e.g. "i18n/messages")
24
+ * @returns {string} The path for the specified location
25
+ */
26
+ export function path(location) {
27
+ if (!location) return "/";
28
+
29
+ const segments = _.flow(_.split("/"), _.filter(Boolean))(location);
30
+
31
+ if (_.isEmpty(segments)) return "/";
32
+
33
+ return _.flow(
34
+ () => routes[1],
35
+ (routeObj) =>
36
+ _.reduce((acc, segment) => {
37
+ if (acc === null || !acc[segment]) return null;
38
+
39
+ return acc[segment][1];
40
+ }, routeObj)(segments.slice(0, -1)),
41
+ (finalRouteObj) => {
42
+ if (finalRouteObj === null) return undefined;
43
+ const lastSegment = _.last(segments);
44
+ return finalRouteObj[lastSegment]
45
+ ? finalRouteObj[lastSegment][0]
46
+ : undefined;
47
+ }
48
+ )();
49
+ }
50
+ /**
51
+ * Returns the absolute path for a given location based on the routes structure
52
+ * @param {string} location - A slash separated string representing the location (e.g. "i18n/messages")
53
+ * @returns {string} The absolute path for the specified location, including all parent segments
54
+ */
55
+ export function absolutePath(location) {
56
+ if (!location) return "/";
57
+
58
+ const segments = _.flow(_.split("/"), _.filter(Boolean))(location);
59
+
60
+ if (_.isEmpty(segments)) return "/";
61
+
62
+ let currentPath = "";
63
+ let currentRouteObj = routes[1];
64
+
65
+ for (let i = 0; i < segments.length; i++) {
66
+ const segment = segments[i];
67
+
68
+ if (!currentRouteObj[segment]) return undefined;
69
+
70
+ currentPath += `/${currentRouteObj[segment][0]}`;
71
+
72
+ if (i < segments.length - 1) {
73
+ currentRouteObj = currentRouteObj[segment][1];
74
+ if (!currentRouteObj) return undefined;
75
+ }
76
+ }
77
+
78
+ return currentPath;
79
+ }
80
+
81
+ /**
82
+ * Returns the relative path for a given location based on the routes structure
83
+ * @param {string} location - A slash separated string representing the location (e.g. "i18n/messages")
84
+ * @returns {string} The relative path for the specified location, without the leading slash
85
+ */
86
+ export function relativePath(location) {
87
+ const absolute = absolutePath(location);
88
+
89
+ if (!absolute) return undefined;
90
+ if (absolute === "/") return "";
91
+
92
+ return absolute.substring(1); // Remove the leading slash
93
+ }
package/src/routines.js CHANGED
@@ -31,3 +31,11 @@ export const logError = createRoutine("LOG_ERROR");
31
31
 
32
32
  export const reindexAllGrants = createRoutine("REINDEX_ALL_GRANTS");
33
33
  export const reindexAllStructures = createRoutine("REINDEX_ALL_STRUCTURES");
34
+
35
+ export const clearNavFilter = createRoutine("CLEAR_BUCKET_FILTER");
36
+
37
+ export const clearTemplates = createRoutine("CLEAR_TEMPLATES");
38
+ export const fetchTemplates = createRoutine("FETCH_TEMPLATES");
39
+
40
+ export const fetchSystems = createRoutine("FETCH_SYSTEMS");
41
+ export const clearSystems = createRoutine("CLEAR_SYSTEMS");
@@ -17,9 +17,7 @@ describe("sagas: addCommentRequestSaga", () => {
17
17
 
18
18
  it("should throw exception if an unhandled action is received", () => {
19
19
  expect(() => {
20
- testSaga(addCommentRequestSaga)
21
- .next()
22
- .takeLatest("FOO", addCommentSaga);
20
+ testSaga(addCommentRequestSaga).next().takeLatest("FOO", addCommentSaga);
23
21
  }).toThrow();
24
22
  });
25
23
  });
@@ -31,7 +29,7 @@ describe("sagas: addCommentSaga", () => {
31
29
  const data = {
32
30
  resource_id: id,
33
31
  resource_type: "business_concept",
34
- content: comment
32
+ content: comment,
35
33
  };
36
34
  const payload = { comment: data };
37
35
 
@@ -59,7 +57,7 @@ describe("sagas: addCommentSaga", () => {
59
57
  const data = {
60
58
  resource_id: id,
61
59
  resource_type: "business_concept",
62
- content: comment
60
+ content: comment,
63
61
  };
64
62
  const payload = { comment: data };
65
63
 
@@ -31,7 +31,7 @@ describe("sagas: fetchCommentsSaga", () => {
31
31
  const json_opts = { ...JSON_OPTS, params: payload };
32
32
  const data = [
33
33
  { content: "My first comment", resource_id: id },
34
- { content: "My second comment", resource_id: id }
34
+ { content: "My second comment", resource_id: id },
35
35
  ];
36
36
 
37
37
  it("should put a success action when a response is returned", () => {
@@ -0,0 +1,69 @@
1
+ import { testSaga } from "redux-saga-test-plan";
2
+ import { apiJson, JSON_OPTS } from "@truedat/core/services/api";
3
+ import { fetchSystemsRequestSaga, fetchSystemsSaga } from "../fetchSystems";
4
+ import { fetchSystems } from "../../routines";
5
+ import { API_SYSTEMS } from "../../api";
6
+
7
+ describe("sagas: fetchSystemsRequestSaga", () => {
8
+ it("should invoke fetchSystemsSaga on trigger", () => {
9
+ expect(() => {
10
+ testSaga(fetchSystemsRequestSaga)
11
+ .next()
12
+ .takeLatest(fetchSystems.TRIGGER, fetchSystemsSaga)
13
+ .finish()
14
+ .isDone();
15
+ }).not.toThrow();
16
+ });
17
+
18
+ it("should throw exception if an unhandled action is received", () => {
19
+ expect(() => {
20
+ testSaga(fetchSystemsRequestSaga)
21
+ .next()
22
+ .takeLatest("FOO", fetchSystemsSaga);
23
+ }).toThrow();
24
+ });
25
+ });
26
+
27
+ describe("sagas: fetchSystemsSaga", () => {
28
+ const data = {
29
+ collection: [
30
+ { id: 1, name: "Structure 1" },
31
+ { id: 2, name: "Structure 2" },
32
+ ],
33
+ };
34
+
35
+ it("should put a success action when a response is returned", () => {
36
+ expect(() => {
37
+ testSaga(fetchSystemsSaga)
38
+ .next()
39
+ .put(fetchSystems.request())
40
+ .next()
41
+ .call(apiJson, API_SYSTEMS, JSON_OPTS)
42
+ .next({ data })
43
+ .put(fetchSystems.success(data))
44
+ .next()
45
+ .put(fetchSystems.fulfill())
46
+ .next()
47
+ .isDone();
48
+ }).not.toThrow();
49
+ });
50
+
51
+ it("should put a failure action when the call returns an error", () => {
52
+ const message = "Request failed";
53
+ const error = { message };
54
+
55
+ expect(() => {
56
+ testSaga(fetchSystemsSaga)
57
+ .next()
58
+ .put(fetchSystems.request())
59
+ .next()
60
+ .call(apiJson, API_SYSTEMS, JSON_OPTS)
61
+ .throw(error)
62
+ .put(fetchSystems.failure(message))
63
+ .next()
64
+ .put(fetchSystems.fulfill())
65
+ .next()
66
+ .isDone();
67
+ }).not.toThrow();
68
+ });
69
+ });
@@ -0,0 +1,25 @@
1
+ import { call, put, takeLatest } from "redux-saga/effects";
2
+ import { apiJson, JSON_OPTS } from "@truedat/core/services/api";
3
+ import { fetchSystems } from "../routines";
4
+ import { API_SYSTEMS } from "../api";
5
+
6
+ export function* fetchSystemsSaga() {
7
+ try {
8
+ yield put(fetchSystems.request());
9
+ const { data } = yield call(apiJson, API_SYSTEMS, JSON_OPTS);
10
+ yield put(fetchSystems.success(data));
11
+ } catch (error) {
12
+ if (error.response) {
13
+ const { status, data } = error.response;
14
+ yield put(fetchSystems.failure({ status, data }));
15
+ } else {
16
+ yield put(fetchSystems.failure(error.message));
17
+ }
18
+ } finally {
19
+ yield put(fetchSystems.fulfill());
20
+ }
21
+ }
22
+
23
+ export function* fetchSystemsRequestSaga() {
24
+ yield takeLatest(fetchSystems.TRIGGER, fetchSystemsSaga);
25
+ }
@@ -1,15 +1,18 @@
1
1
  import { fetchCommentsRequestSaga } from "./fetchComments";
2
2
  import { addCommentRequestSaga } from "./addComment";
3
3
  import { reindexAllRequestSaga } from "./reindexAll";
4
+ import { fetchSystemsRequestSaga } from "./fetchSystems";
4
5
 
5
6
  export {
6
7
  fetchCommentsRequestSaga,
7
8
  addCommentRequestSaga,
8
9
  reindexAllRequestSaga,
10
+ fetchSystemsRequestSaga,
9
11
  };
10
12
 
11
13
  export default [
12
14
  fetchCommentsRequestSaga(),
13
15
  addCommentRequestSaga(),
14
16
  reindexAllRequestSaga(),
17
+ fetchSystemsRequestSaga(),
15
18
  ];
@@ -1,5 +1,4 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
3
2
  import { FormattedMessage } from "react-intl";
4
3
  import { Label, Icon, Dropdown, Dimmer, Loader } from "semantic-ui-react";
5
4
  import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
@@ -1,5 +1,4 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
3
2
  import PropTypes from "prop-types";
4
3
  import { FormattedMessage } from "react-intl";
5
4
  import { Icon, Dropdown } from "semantic-ui-react";
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useState, useEffect } from "react";
2
+ import { useState, useEffect } from "react";
3
3
  import { FormattedMessage } from "react-intl";
4
4
  import {
5
5
  Label,
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useState, useEffect } from "react";
2
+ import { useState, useEffect } from "react";
3
3
  import { FormattedMessage } from "react-intl";
4
4
  import {
5
5
  Label,
@@ -88,52 +88,48 @@ export default function FilterQueryDropdown() {
88
88
  open={!_.isEmpty(options)}
89
89
  >
90
90
  <Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
91
- <>
92
- <>
93
- <Input
94
- icon="search"
95
- iconPosition="left"
96
- className="search"
97
- onKeyDown={(e) => {
98
- if (e.key === " ") {
99
- e.stopPropagation();
100
- }
101
- }}
102
- onChange={handleSearch}
103
- value={localQuery}
104
- onClick={(e) => {
105
- e.preventDefault();
106
- e.stopPropagation();
107
- }}
108
- />
109
- {selected.map((option, i) => (
110
- <FilterItem
111
- key={i}
112
- filter={filter}
113
- toggleFilterValue={toggleFilterValue}
114
- option={option}
115
- active={true}
116
- />
117
- ))}
118
- <Dropdown.Divider />
119
- {filteredOptions.map((option, i) => (
120
- <FilterItem
121
- key={i}
122
- filter={filter}
123
- toggleFilterValue={toggleFilterValue}
124
- option={option}
125
- active={_.includes(_.prop("value")(option))(
126
- activeFilterSelectedValues
127
- )}
128
- />
129
- ))}
130
- </>
131
- </>
132
91
  {loading && (
133
92
  <Dimmer active inverted>
134
93
  <Loader size="tiny" />
135
94
  </Dimmer>
136
95
  )}
96
+ <Input
97
+ icon="search"
98
+ iconPosition="left"
99
+ className="search"
100
+ onKeyDown={(e) => {
101
+ if (e.key === " ") {
102
+ e.stopPropagation();
103
+ }
104
+ }}
105
+ onChange={handleSearch}
106
+ value={localQuery}
107
+ onClick={(e) => {
108
+ e.preventDefault();
109
+ e.stopPropagation();
110
+ }}
111
+ />
112
+ {selected.map((option, i) => (
113
+ <FilterItem
114
+ key={i}
115
+ filter={filter}
116
+ toggleFilterValue={toggleFilterValue}
117
+ option={option}
118
+ active={true}
119
+ />
120
+ ))}
121
+ <Dropdown.Divider />
122
+ {filteredOptions.map((option, i) => (
123
+ <FilterItem
124
+ key={i}
125
+ filter={filter}
126
+ toggleFilterValue={toggleFilterValue}
127
+ option={option}
128
+ active={_.includes(_.prop("value")(option))(
129
+ activeFilterSelectedValues
130
+ )}
131
+ />
132
+ ))}
137
133
  </Dimmer.Dimmable>
138
134
  </Dropdown>
139
135
  );