From 71393d4652d7f3fc8c1ac14ef19291efd495fe7c Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 25 Oct 2023 12:21:56 +0300 Subject: [PATCH 01/32] Add antd pro-design --- app/package.json | 1 + .../package.json | 1 + yarn.lock | 531 +++++++++++++++++- 3 files changed, 523 insertions(+), 10 deletions(-) diff --git a/app/package.json b/app/package.json index 5163622b0..76abec9d3 100644 --- a/app/package.json +++ b/app/package.json @@ -5,6 +5,7 @@ "dependencies": { "@2fd/ant-design-icons": "^2.6.0", "@ant-design/icons": "^4.7.0", + "@ant-design/pro-components": "^2.6.32", "@onaio/connected-private-route": "^0.0.11", "@onaio/connected-reducer-registry": "^0.0.3", "@onaio/gatekeeper": "1.0.0", diff --git a/packages/fhir-keycloak-user-management/package.json b/packages/fhir-keycloak-user-management/package.json index 1087ecafb..3df30c85c 100644 --- a/packages/fhir-keycloak-user-management/package.json +++ b/packages/fhir-keycloak-user-management/package.json @@ -23,6 +23,7 @@ "dist" ], "peerDependencies": { + "@ant-design/pro-components": "^2.6.32", "@opensrp/store": "^0.0.10", "antd": "^5.5.1", "i18next": "^19.8.4", diff --git a/yarn.lock b/yarn.lock index c5ed562e8..8b37b056f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,6 +45,24 @@ __metadata: languageName: node linkType: hard +"@ant-design/cssinjs@npm:^1.11.1": + version: 1.17.2 + resolution: "@ant-design/cssinjs@npm:1.17.2" + dependencies: + "@babel/runtime": ^7.11.1 + "@emotion/hash": ^0.8.0 + "@emotion/unitless": ^0.7.5 + classnames: ^2.3.1 + csstype: ^3.0.10 + rc-util: ^5.35.0 + stylis: ^4.0.13 + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: ed864b4f890038cb132131c25ebe137a421c32a518dbbd0424126ce2603f71946ce0fca91bbe1ad7a629760374266af6246da01ca86fbce8f68acb9ceba6b91f + languageName: node + linkType: hard + "@ant-design/cssinjs@npm:^1.9.1": version: 1.9.1 resolution: "@ant-design/cssinjs@npm:1.9.1" @@ -70,6 +88,13 @@ __metadata: languageName: node linkType: hard +"@ant-design/icons-svg@npm:^4.3.0": + version: 4.3.1 + resolution: "@ant-design/icons-svg@npm:4.3.1" + checksum: 47f0474277366fb3b8bacfeb1691be35052c3f9b28811be7fb25ad219100533d0e31c2eec00a8dee744c34381a4cda7f39b39403e160811a8fd5d33b861e77aa + languageName: node + linkType: hard + "@ant-design/icons@npm:^4.0.0": version: 4.7.0 resolution: "@ant-design/icons@npm:4.7.0" @@ -102,6 +127,22 @@ __metadata: languageName: node linkType: hard +"@ant-design/icons@npm:^5.0.0": + version: 5.2.6 + resolution: "@ant-design/icons@npm:5.2.6" + dependencies: + "@ant-design/colors": ^7.0.0 + "@ant-design/icons-svg": ^4.3.0 + "@babel/runtime": ^7.11.2 + classnames: ^2.2.6 + rc-util: ^5.31.1 + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 2f571699b1903383cd09faa78e4cce34973debb0e7ec6223b9d9a0a6ab2b2f0c876072db62bbd4e6a45e864df5447343315e066abeffaf58aa5b97df3acc89f1 + languageName: node + linkType: hard + "@ant-design/icons@npm:^5.1.0": version: 5.1.4 resolution: "@ant-design/icons@npm:5.1.4" @@ -118,6 +159,246 @@ __metadata: languageName: node linkType: hard +"@ant-design/pro-card@npm:2.5.22": + version: 2.5.22 + resolution: "@ant-design/pro-card@npm:2.5.22" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.2 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + classnames: ^2.3.2 + omit.js: ^2.0.2 + rc-resize-observer: ^1.0.0 + rc-util: ^5.4.0 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + checksum: 728cf6d7ef2a87fd85dcd12b27cd33eb4e44341f3267a001ec5fdebed2ba4226a1fb5611cce672ea28386ae7ac19cfe08fb99f432c69e3337a9ff9bc58822849 + languageName: node + linkType: hard + +"@ant-design/pro-components@npm:^2.6.32": + version: 2.6.32 + resolution: "@ant-design/pro-components@npm:2.6.32" + dependencies: + "@ant-design/pro-card": 2.5.22 + "@ant-design/pro-descriptions": 2.5.17 + "@ant-design/pro-field": 2.13.3 + "@ant-design/pro-form": 2.21.4 + "@ant-design/pro-layout": 7.17.10 + "@ant-design/pro-list": 2.5.31 + "@ant-design/pro-provider": 2.13.2 + "@ant-design/pro-skeleton": 2.1.8 + "@ant-design/pro-table": 3.13.0 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.16.3 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: dffebe569a271911a761b1a8974732ad9911de8556ec94340cd165264f547eb3398497209d75825ea0039293485ccf5141a015dc8854bc16a4c2a6c3c5b49e78 + languageName: node + linkType: hard + +"@ant-design/pro-descriptions@npm:2.5.17": + version: 2.5.17 + resolution: "@ant-design/pro-descriptions@npm:2.5.17" + dependencies: + "@ant-design/pro-field": 2.13.3 + "@ant-design/pro-form": 2.21.4 + "@ant-design/pro-skeleton": 2.1.8 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + rc-resize-observer: ^0.2.3 + rc-util: ^5.0.6 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + checksum: 591b01a6ba4d178f7a7a54e2254dcf1948d830e68fe68c94be9572cb2c7596b1ea73440d292e3ee4b7d4ca138e4b3ba1908068f695d7d5cca0657e316d191096 + languageName: node + linkType: hard + +"@ant-design/pro-field@npm:2.13.3": + version: 2.13.3 + resolution: "@ant-design/pro-field@npm:2.13.3" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.2 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + "@chenshuai2144/sketch-color": ^1.0.8 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.tonumber: ^4.0.3 + omit.js: ^2.0.2 + rc-util: ^5.4.0 + swr: ^2.0.0 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + checksum: e8f153369c5046ad13e0bbcdaf38f8f2ffe98ccf1522279b78ba9c663be71d8b0491126344c98cda6938b7c27e43e7fad6dbfbbdb97886010bfc3b7bb7e3d002 + languageName: node + linkType: hard + +"@ant-design/pro-form@npm:2.21.4": + version: 2.21.4 + resolution: "@ant-design/pro-form@npm:2.21.4" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-field": 2.13.3 + "@ant-design/pro-provider": 2.13.2 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + "@chenshuai2144/sketch-color": ^1.0.7 + "@umijs/use-params": ^1.0.9 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.merge: ^4.6.2 + omit.js: ^2.0.2 + rc-resize-observer: ^1.1.0 + rc-util: ^5.0.6 + peerDependencies: + "@types/lodash.merge": ^4.6.7 + antd: ">=4.23.0 || >=5.0.0" + rc-field-form: ^1.22.0 + react: ">=17.0.0" + react-dom: ">=17.0.0" + peerDependenciesMeta: + "@types/lodash.merge": + optional: true + checksum: a0bdf3c6dfda12f963d76b72b51860083c7eee4272392480d92d87cca86da210c8d2a06cfb44f2cc10785a6ba99b78df721a55f223e8f3f556ec8fb9edb0d078 + languageName: node + linkType: hard + +"@ant-design/pro-layout@npm:7.17.10": + version: 7.17.10 + resolution: "@ant-design/pro-layout@npm:7.17.10" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.2 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + "@umijs/route-utils": ^4.0.0 + "@umijs/use-params": ^1.0.9 + classnames: ^2.3.2 + lodash.merge: ^4.6.2 + omit.js: ^2.0.2 + path-to-regexp: 2.4.0 + rc-resize-observer: ^1.1.0 + rc-util: ^5.0.6 + swr: ^2.0.0 + warning: ^4.0.3 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: d4b902ef3ea5fa8352b41ec0bdd6b8c971df0e72c2d32ace9c66006f09a98d9adc787926fda70b1672057573a92d05c730503c8f0d2543e2739e9cc033647e96 + languageName: node + linkType: hard + +"@ant-design/pro-list@npm:2.5.31": + version: 2.5.31 + resolution: "@ant-design/pro-list@npm:2.5.31" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-card": 2.5.22 + "@ant-design/pro-field": 2.13.3 + "@ant-design/pro-table": 3.13.0 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + classnames: ^2.3.2 + dayjs: ^1.11.10 + rc-resize-observer: ^1.0.0 + rc-util: ^4.19.0 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: f4c227c6f6b2cba3b6949416641b1e7d146790fcad966d93dc8d26149299d588163c25e99a577d00b378ade1563264810ffa8dae4ce953f6541411c042d066e1 + languageName: node + linkType: hard + +"@ant-design/pro-provider@npm:2.13.2": + version: 2.13.2 + resolution: "@ant-design/pro-provider@npm:2.13.2" + dependencies: + "@ant-design/cssinjs": ^1.11.1 + "@babel/runtime": ^7.18.0 + "@ctrl/tinycolor": ^3.4.0 + rc-util: ^5.0.1 + swr: ^2.0.0 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 4c8497cdb52b97ff54c1928ec39dd1f1d417bbc229391e223e463dff69b72c758d5db61998aced382eb49ad849a8e21ebfb001cda41543659fe33cf8d5086d98 + languageName: node + linkType: hard + +"@ant-design/pro-skeleton@npm:2.1.8": + version: 2.1.8 + resolution: "@ant-design/pro-skeleton@npm:2.1.8" + dependencies: + "@babel/runtime": ^7.18.0 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 806753a45ebc585d354dcbd474ac413358ac30ab4a7555cab8e8f4b57b25a091fcd508cb02e11c1e0acd6811ad75b742c5792a453b1aa112f747a770528aafd5 + languageName: node + linkType: hard + +"@ant-design/pro-table@npm:3.13.0": + version: 3.13.0 + resolution: "@ant-design/pro-table@npm:3.13.0" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-card": 2.5.22 + "@ant-design/pro-field": 2.13.3 + "@ant-design/pro-form": 2.21.4 + "@ant-design/pro-provider": 2.13.2 + "@ant-design/pro-utils": 2.14.10 + "@babel/runtime": ^7.18.0 + "@dnd-kit/core": ^6.0.8 + "@dnd-kit/modifiers": ^6.0.1 + "@dnd-kit/sortable": ^7.0.2 + "@dnd-kit/utilities": ^3.2.1 + classnames: ^2.3.2 + dayjs: ^1.11.10 + omit.js: ^2.0.2 + rc-resize-observer: ^1.0.0 + rc-util: ^5.0.1 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + rc-field-form: ^1.22.0 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: bdde2e21b8434b52ddc5a302c275fba50ab3bd16fa98f7c0e6e282662cb5d1fa9eded29f055f81ddb712f89e8dccb990b3ef62cedb2d23b524f80c63e7485475 + languageName: node + linkType: hard + +"@ant-design/pro-utils@npm:2.14.10": + version: 2.14.10 + resolution: "@ant-design/pro-utils@npm:2.14.10" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.2 + "@babel/runtime": ^7.18.0 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.merge: ^4.6.2 + rc-util: ^5.0.6 + safe-stable-stringify: ^2.4.3 + swr: ^2.0.0 + peerDependencies: + antd: ">=4.23.0 || >=5.0.0" + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: c0af1b7670254e422f304d50ae51d4843accf2a831fce90c04b8ee1fb92597d2c12fa0caa03d10b35715d460f10e449670edfed4ddd3f13241881bed5e74c3a5 + languageName: node + linkType: hard + "@ant-design/react-slick@npm:~1.0.0": version: 1.0.1 resolution: "@ant-design/react-slick@npm:1.0.1" @@ -2021,6 +2302,18 @@ __metadata: languageName: node linkType: hard +"@chenshuai2144/sketch-color@npm:^1.0.7, @chenshuai2144/sketch-color@npm:^1.0.8": + version: 1.0.9 + resolution: "@chenshuai2144/sketch-color@npm:1.0.9" + dependencies: + reactcss: ^1.2.3 + tinycolor2: ^1.4.2 + peerDependencies: + react: ">=16.12.0" + checksum: 7337f9a24abc7630f2b839b1cce875ff07061b8b568d0d72c9528db9dc3c0ec7db78242b20180b419c60ecb2766533487d7cd77000f0e24dc7d049f8bc86db50 + languageName: node + linkType: hard + "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -2159,6 +2452,68 @@ __metadata: languageName: node linkType: hard +"@dnd-kit/accessibility@npm:^3.0.0": + version: 3.0.1 + resolution: "@dnd-kit/accessibility@npm:3.0.1" + dependencies: + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + checksum: 0afc2c0fce9a1c107453620ca0da1778f182d340e74ffbc6e369ef0ac8943cafb929d3a6c0891d9b915aa23b2b92137ff4fad958f43118466586d8129a3359d5 + languageName: node + linkType: hard + +"@dnd-kit/core@npm:^6.0.8": + version: 6.0.8 + resolution: "@dnd-kit/core@npm:6.0.8" + dependencies: + "@dnd-kit/accessibility": ^3.0.0 + "@dnd-kit/utilities": ^3.2.1 + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: abe48ff7395f84fd8c15e6c8b13da4df153dc1f1076096d783acd0c25539516c77e4854ea59be6621dde55739cb0df1d62924ad069df3267fe05ad90ef729b2f + languageName: node + linkType: hard + +"@dnd-kit/modifiers@npm:^6.0.1": + version: 6.0.1 + resolution: "@dnd-kit/modifiers@npm:6.0.1" + dependencies: + "@dnd-kit/utilities": ^3.2.1 + tslib: ^2.0.0 + peerDependencies: + "@dnd-kit/core": ^6.0.6 + react: ">=16.8.0" + checksum: cd31715aac81baa2398558dc7c877a483d1c4c41cdb2f466557fdc41bb742db5cd1b34b3b84332b94ac4a2835e7e5a0e28c64e621936e976399788f1dd029ea4 + languageName: node + linkType: hard + +"@dnd-kit/sortable@npm:^7.0.2": + version: 7.0.2 + resolution: "@dnd-kit/sortable@npm:7.0.2" + dependencies: + "@dnd-kit/utilities": ^3.2.0 + tslib: ^2.0.0 + peerDependencies: + "@dnd-kit/core": ^6.0.7 + react: ">=16.8.0" + checksum: 4ce705aceb15766a0deefe25a9d95a87e9413c3fb9088ea3eb0962e57f844895000117fcec7c0944a0d4ae4e1e889cfa69e3d3778164d4d23115fb1edb218283 + languageName: node + linkType: hard + +"@dnd-kit/utilities@npm:^3.2.0, @dnd-kit/utilities@npm:^3.2.1": + version: 3.2.1 + resolution: "@dnd-kit/utilities@npm:3.2.1" + dependencies: + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + checksum: 038fd5cc1328bf4c9dca17cd48046e5a687bbf9d904c7197f851aab869ab52d9dee2734b2e255256fd6158245acd00063a23deed962c7673c0fadfbf061f04ca + languageName: node + linkType: hard + "@emotion/hash@npm:^0.8.0": version: 0.8.0 resolution: "@emotion/hash@npm:0.8.0" @@ -3673,6 +4028,7 @@ __metadata: react-i18next: ^11.8.10 uuid: ^8.3.1 peerDependencies: + "@ant-design/pro-components": ^2.6.32 "@opensrp/store": ^0.0.10 antd: ^5.5.1 i18next: ^19.8.4 @@ -3979,6 +4335,7 @@ __metadata: dependencies: "@2fd/ant-design-icons": ^2.6.0 "@ant-design/icons": ^4.7.0 + "@ant-design/pro-components": ^2.6.32 "@onaio/connected-private-route": ^0.0.11 "@onaio/connected-reducer-registry": ^0.0.3 "@onaio/gatekeeper": 1.0.0 @@ -5942,6 +6299,22 @@ __metadata: languageName: node linkType: hard +"@umijs/route-utils@npm:^4.0.0": + version: 4.0.1 + resolution: "@umijs/route-utils@npm:4.0.1" + checksum: e1ca91cf6da16d7623cc0a8816cf2655e1f8b7fcc2507d455383b31f7e1979e52bc2464453173e68b660d24c4255be44d521d280e580c025af6bed83b5727241 + languageName: node + linkType: hard + +"@umijs/use-params@npm:^1.0.9": + version: 1.0.9 + resolution: "@umijs/use-params@npm:1.0.9" + peerDependencies: + react: "*" + checksum: 7f72436440dff0e1911ee916340bd26c6c9a510bad4af536d3ec9a5a175d37255d42dbd1cb0dc996daf4d0c3b76e44816ac423ac73e6b3c1655b6b839fed175c + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -6464,6 +6837,15 @@ __metadata: languageName: node linkType: hard +"add-dom-event-listener@npm:^1.1.0": + version: 1.1.0 + resolution: "add-dom-event-listener@npm:1.1.0" + dependencies: + object-assign: 4.x + checksum: 7685d32c4a9d78ad7a36641774168d80e195ef0710ae801e7211d849100c0e4499657cf3838bdae5ee9134b5aa9d7f50d68dec73b03e7d0082302804efff6ea6 + languageName: node + linkType: hard + "add-stream@npm:^1.0.0": version: 1.0.0 resolution: "add-stream@npm:1.0.0" @@ -8838,6 +9220,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -10297,6 +10686,13 @@ __metadata: languageName: node linkType: hard +"dayjs@npm:^1.11.10": + version: 1.11.10 + resolution: "dayjs@npm:1.11.10" + checksum: a6b5a3813b8884f5cd557e2e6b7fa569f4c5d0c97aca9558e38534af4f2d60daafd3ff8c2000fed3435cfcec9e805bcebd99f90130c6d1c5ef524084ced588c4 + languageName: node + linkType: hard + "de-indent@npm:^1.0.2": version: 1.0.2 resolution: "de-indent@npm:1.0.2" @@ -17739,7 +18135,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:>=3.5 <5, lodash@npm:^4.0.0, lodash@npm:^4.1.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.7.0": +"lodash@npm:>=3.5 <5, lodash@npm:^4.0.0, lodash@npm:^4.0.1, lodash@npm:^4.1.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -19277,6 +19673,13 @@ __metadata: languageName: node linkType: hard +"object-assign@npm:4.x, object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + "object-assign@npm:^3.0.0": version: 3.0.0 resolution: "object-assign@npm:3.0.0" @@ -19284,13 +19687,6 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f - languageName: node - linkType: hard - "object-copy@npm:^0.1.0": version: 0.1.0 resolution: "object-copy@npm:0.1.0" @@ -19438,6 +19834,13 @@ __metadata: languageName: node linkType: hard +"omit.js@npm:^2.0.2": + version: 2.0.2 + resolution: "omit.js@npm:2.0.2" + checksum: 5d802b9fd7640250aada82f3b9b7243b554b38911f29b3de0d1066c00f24dd4ee72d3b9c94c582e373fb6511bd21e107917d419a7b2a04287f26c31133b48a15 + languageName: node + linkType: hard + "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -20133,6 +20536,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:2.4.0": + version: 2.4.0 + resolution: "path-to-regexp@npm:2.4.0" + checksum: 581175bf2968e51452f2b8c71f10e75c995693668b4ecf7d0b48962fbe0c56830661ca5dd5fd6d8e2f0cc9a045ce07e89af504ab133e1d21887c2712df85b1f4 + languageName: node + linkType: hard + "path-to-regexp@npm:^1.7.0": version: 1.8.0 resolution: "path-to-regexp@npm:1.8.0" @@ -22213,7 +22623,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.5.8, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.5.10, prop-types@npm:^15.5.8, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -23082,6 +23492,21 @@ __metadata: languageName: node linkType: hard +"rc-resize-observer@npm:^0.2.3": + version: 0.2.6 + resolution: "rc-resize-observer@npm:0.2.6" + dependencies: + "@babel/runtime": ^7.10.1 + classnames: ^2.2.1 + rc-util: ^5.0.0 + resize-observer-polyfill: ^1.5.1 + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 1ce6af2c63a60bedcd9edd4b9e4632df36308c59177ff5e0de861e8cadf483f333f71713f584a2c7281691eaed87a1615c2edfeab6938b3c0a3e8bfb2553d600 + languageName: node + linkType: hard + "rc-resize-observer@npm:^1.0.0, rc-resize-observer@npm:^1.1.0, rc-resize-observer@npm:^1.2.0": version: 1.2.0 resolution: "rc-resize-observer@npm:1.2.0" @@ -23485,6 +23910,32 @@ __metadata: languageName: node linkType: hard +"rc-util@npm:^4.19.0": + version: 4.21.1 + resolution: "rc-util@npm:4.21.1" + dependencies: + add-dom-event-listener: ^1.1.0 + prop-types: ^15.5.10 + react-is: ^16.12.0 + react-lifecycles-compat: ^3.0.4 + shallowequal: ^1.1.0 + checksum: f87d321ad2088171eef990f179fe8b34db648cd5ad37439de381093891189e429ad8c791077205c56de9e26e37749fb3f4ecfceba8c35f983f540650c7ed4fb0 + languageName: node + linkType: hard + +"rc-util@npm:^5.0.0, rc-util@npm:^5.35.0": + version: 5.38.0 + resolution: "rc-util@npm:5.38.0" + dependencies: + "@babel/runtime": ^7.18.3 + react-is: ^18.2.0 + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 97a3e1dd94e199c12e69eed1f69e9a9be39b383f2b0d9e1d52d6fcdf676e91e6499fa7be4c48f37b08b6500747f2dc41014f9be54351a39f23b3b5087360690c + languageName: node + linkType: hard + "rc-util@npm:^5.0.1, rc-util@npm:^5.0.6, rc-util@npm:^5.15.0, rc-util@npm:^5.16.1, rc-util@npm:^5.18.1, rc-util@npm:^5.2.0, rc-util@npm:^5.3.0, rc-util@npm:^5.5.0, rc-util@npm:^5.6.1, rc-util@npm:^5.9.4": version: 5.18.1 resolution: "rc-util@npm:5.18.1" @@ -23861,7 +24312,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0": +"react-is@npm:^18.0.0, react-is@npm:^18.2.0": version: 18.2.0 resolution: "react-is@npm:18.2.0" checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e @@ -24323,6 +24774,15 @@ __metadata: languageName: node linkType: hard +"reactcss@npm:^1.2.3": + version: 1.2.3 + resolution: "reactcss@npm:1.2.3" + dependencies: + lodash: ^4.0.1 + checksum: c53e386a0881f1477e1cff661f6a6ad4c662230941f3827862193ac30f9b75cdf7bc7b4c7e5ca543d3e4e80fee1a3e9fa0056c206b1c0423726c41773ab3fe45 + languageName: node + linkType: hard + "reactstrap@npm:^7.1.0": version: 7.1.0 resolution: "reactstrap@npm:7.1.0" @@ -25317,6 +25777,13 @@ __metadata: languageName: node linkType: hard +"safe-stable-stringify@npm:^2.4.3": + version: 2.4.3 + resolution: "safe-stable-stringify@npm:2.4.3" + checksum: 3aeb64449706ee1f5ad2459fc99648b131d48e7a1fbb608d7c628020177512dc9d94108a5cb61bbc953985d313d0afea6566d243237743e02870490afef04b43 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -26930,6 +27397,18 @@ __metadata: languageName: node linkType: hard +"swr@npm:^2.0.0": + version: 2.2.4 + resolution: "swr@npm:2.2.4" + dependencies: + client-only: ^0.0.1 + use-sync-external-store: ^1.2.0 + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + checksum: d1398f89fd68af0e0cb6100a5769b1e17c0dbe8a778898643ad28456acda64add43344754c7d919e3f2ca82854adf86ff7d465aba25a2d555bcb669e457138f8 + languageName: node + linkType: hard + "symbol-observable@npm:1.2.0, symbol-observable@npm:^1.2.0": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" @@ -27328,6 +27807,13 @@ __metadata: languageName: node linkType: hard +"tinycolor2@npm:^1.4.2": + version: 1.6.0 + resolution: "tinycolor2@npm:1.6.0" + checksum: 6df4d07fceeedc0a878d7bac47e2cd47c1ceeb1078340a9eb8a295bc0651e17c750f73d47b3028d829f30b85c15e0572c0fd4142083e4c21a30a597e47f47230 + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -27610,6 +28096,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.0.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0": version: 2.3.1 resolution: "tslib@npm:2.3.1" @@ -28163,6 +28656,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.2.0": + version: 1.2.0 + resolution: "use-sync-external-store@npm:1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 5c639e0f8da3521d605f59ce5be9e094ca772bd44a4ce7322b055a6f58eeed8dda3c94cabd90c7a41fb6fa852210092008afe48f7038792fd47501f33299116a + languageName: node + linkType: hard + "use@npm:^3.1.0": version: 3.1.1 resolution: "use@npm:3.1.1" @@ -28489,6 +28991,15 @@ __metadata: languageName: node linkType: hard +"warning@npm:^4.0.3": + version: 4.0.3 + resolution: "warning@npm:4.0.3" + dependencies: + loose-envify: ^1.0.0 + checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c + languageName: node + linkType: hard + "watchpack-chokidar2@npm:^2.0.1": version: 2.0.1 resolution: "watchpack-chokidar2@npm:2.0.1" From f62cf0237c49d6a6a7e1e7eec82e699d0ef6fb84 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Thu, 26 Oct 2023 17:20:49 +0300 Subject: [PATCH 02/32] Registering a checkmark --- .../UserList/ListView/tableColumns.tsx | 3 +- .../UserList/UserDetails-v2/index.css | 3 + .../UserList/UserDetails-v2/index.tsx | 416 ++++++++++++++++++ .../UserDetails-v2/tests/index.test.tsx | 71 +++ 4 files changed, 492 insertions(+), 1 deletion(-) create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx index ea57d13ae..4374246c2 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx @@ -9,6 +9,7 @@ import { URL_USER_EDIT, KEYCLOAK_URL_USERS, URL_USER_CREDENTIALS, + URL_USER, } from '@opensrp/user-management'; import { Dictionary } from '@onaio/utils'; import { Column } from '@opensrp/react-utils'; @@ -66,7 +67,7 @@ export const getTableColumns = ( key: '1', permissions: [], label: ( - ), diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css new file mode 100644 index 000000000..8fd8d13a9 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css @@ -0,0 +1,3 @@ +.details-full-view{ + background: #f5f5f5; +} \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx new file mode 100644 index 000000000..74ec92088 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -0,0 +1,416 @@ +import React from 'react'; +import { CloseOutlined } from '@ant-design/icons'; +import { Alert, Button, Descriptions, Divider, Table, Tabs, Tag } from 'antd'; +import { Organization } from '@opensrp/team-management'; +import { Spin } from 'antd'; +import { useTranslation } from '../../../mls' +import { useParams } from 'react-router'; +import { BrokenPage, Resource404, TableLayout, getResourcesFromBundle, parseFhirHumanName, useSimpleTabularView, useTabularViewWithLocalSearch } from '@opensrp/react-utils'; +import { PageHeader } from '@ant-design/pro-layout'; +import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS, KeycloakUser, URL_USER, UserGroupDucks } from '@opensrp/user-management'; +import { KeycloakService } from '@opensrp/keycloak-service'; +import { useQuery } from 'react-query'; +import { groupResourceType, practitionerResourceType } from '../../../constants'; +import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; +import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; +import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; +import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; +import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; +import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; +import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; +import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; + + +/** New details view. -> + * + * Page Leader section + * - breadcrumbs + * user details show that as a description list. has attributes. + */ + + + +const useGetKeycloakUser = (userId: string) => { + // Get keycloakUser should contain details. roles. groups. attributes. +} + +const useGetKeycloakUserGroups = (userId: string) => { + // fetch groups assigned to user. +} + +const useGetKeycloakUserRoles = (userId: string) => { + // roles assigned to user by clients. +} + + +// TODO - what if we do not have a linked practitioner. +const useGetPractitioner = (userId: string) => { + // get practitioner include practitionerRoles. Multiple practitioners having multiple practitionerRoles. + // some practitioenerRoles are purely for assignment. Add codes where possible. +} + +const useGetCareTeams = () => { + // get careTeams that reference this practitioner +} + +const useGetOrganization = () => { + // get organizations that reference this practitioner either directly or via a careTeam +} + +const useGetLocations = () => { + // get locations where this practitioner is assigned. +} + +// - if user record not found then show 404 under page details. +// - if practitioner not found, load, keycloak related data and show that. disable + +// remove onclose from type and export the rest +interface UserDetailProps { + fhirBaseURL: string; + keycloakBaseURL: string; +} + +const routes = [{ + path: "admin/users", + breadcrumbName: 'Users', +}, +{ + path: "id", + breadcrumbName: 'View details', +}, +] + +export const UserDetails = (props: UserDetailProps) => { + const { keycloakBaseURL: keycloakBaseUrl, fhirBaseURL: fhirBBaseUrl } = props + console.log({ props }) + const params = useParams<{ id: string }>() + const { id: resourceId } = params; + const { t } = useTranslation(); + + const { + data: user, + isLoading: userIsLoading, + error: userError, + } = useQuery([KEYCLOAK_URL_USERS, resourceId], () => + new KeycloakService(`${KEYCLOAK_URL_USERS}`, keycloakBaseUrl).read(resourceId) + ); + + if (userIsLoading) { + return + } + + // TODO - how to provide this data. + if (userError && !user) { + return + } + + if (user === undefined) { + return + } + + const { id, firstName, lastName, username, email, emailVerified, enabled, attributes } = user + const userDetails = { + ID: id, + 'First Name': firstName, + 'Last Name': lastName, + 'Username': username, + "Email": email, + "Verified": emailVerified ? "True" : "False", + } + const attributesArray = Object.entries(attributes ?? {}); + + return ( +
+ history.back()} + title="View details" + breadcrumb={{ routes }} + subTitle={user.username} + style={{ padding: "24px", background: "white" }} + /> +
+ +
+ Enabled : Disabled} + extra={[, + , + , + ]} + > + + {Object.entries(userDetails).map(([key, value]) => { + return {value} + })} + + + Attributes + + {attributesArray.length === 0 ? : + + + {attributesArray.map(([key, value]) => { + return {JSON.stringify(value)} + })} + + } + +
+
+ }, + { label: "Roles", key: 'Roles', children: }, + { label: "Practitioners", key: 'Practitioners', children: }, + { label: "CareTeams", key: 'CareTeams', children:
}, + { label: "Organizations", key: 'Organizations', children:
}, + { label: "Locations", key: 'Locations', children:
}, + ] + } + /> + + + + + ); +}; + +// const UserDetailsTabView = ({keycloakBaseUrl, resourceId}: {keycloakBaseUrl: string, resourceId: string}) => { + +// // get groups in a searchable way +// // read userGroup +// const { data, isLoading, error, isFetching } = useQuery< +// UserGroupDucks.KeycloakUserGroup[] +// >([groupResourceType, resourceId], () => { +// return new KeycloakService( +// `${KEYCLOAK_URL_USERS}/${resourceId}${KEYCLOAK_URL_USER_GROUPS}`, +// keycloakBaseUrl +// ).list(); +// }); + +// if (error && !data) { +// return ; +// } + +// const tableData = (data?.records ?? []).map((org: IGroup, index: number) => { +// return { +// ...parseGroup(org), +// key: `${index}`, +// }; +// }); + +// const columns = + +// const tableProps = { +// datasource: tableData, +// columns, +// loading: isFetching || isLoading, +// pagination: tablePaginationProps, +// }; + + + + +// } + +interface PractitionerDetail extends Resource { + fhir: { + careteams?: ICareTeam[]; + teams?: IOrganization[]; + locationHierarchyList: any[]; // TODO - import LocationHierarchy + practitionerRoles: IPractitionerRole[]; + groups: IGroup[]; + practitioner: IPractitioner[] + } +} + +function groupResourcesIdDetails(details: PractitionerDetail[]){ + const groupedData = {} + for (const detail of details){ + for(const [_, resources] of Object.entries(detail.fhir)){ + for(const resource of resources){ + + } + } + } +} + +const DetailsTabView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: string, keycloakId: string }) => { + const { t } = useTranslation(); + + /** Get practitioner details and group them, provision them for the tab children, they can consume whichever data they want. + */ + + const practitionerDetailsResourceType = "practitioner-details" + + const extraQueryFilters = { + "keycloak-uuid": keycloakId, + } + + const { + data, isFetching, isLoading, error, + + } = useQuery([practitionerDetailsResourceType, keycloakId], () => new KeycloakService(practitionerDetailsResourceType, fhirBaseUrl).list(extraQueryFilters), { + select: (res) => { + // invariant : expect practitioner-details will always ever be a single record per keycloak user. + return getResourcesFromBundle(res)[0] + } + }); + const resourcesByName = data?.fhir ?? {} + + // loop through records, and for each practitioner figure their practitionerRoles. + const tableData = processPractitionerDetails(data?.records ?? []) + + + const columns = [ + { + title: t('Name'), + dataIndex: 'name', + }, + { + title: t('Active'), + dataIndex: 'active', + render: (value: boolean) => (value === true ? 'Active' : 'Inactive'), + }, + { + title: t('Coding'), + dataIndex: 'concepts', + render: (concepts: CodeableConcept[]) => concepts.map(concept => ), + }, + ]; + + const tableProps = { + datasource: tableData, + columns, + loading: isFetching || isLoading, + pagination: tablePaginationProps, + }; + + return + +} + + +const PractitionerDetailsView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: string, keycloakId: string }) => { + const { t } = useTranslation(); + /** get all practitioners linked to this user. + * where do practitioner role information come in. revInclude practitionerRoles. + */ + + /** get practitioners that have the given secondary identfier, an include practitionerRoles that reference those practitioners. */ + const extraQueryFilters = { + identifier: keycloakId, + _revinclude: "PractitionerRole:practitioner" + } + + const { + queryValues: { data, isFetching, isLoading, error }, + tablePaginationProps, + searchFormProps, + } = useSimpleTabularView(fhirBaseUrl, practitionerResourceType, extraQueryFilters); + + // loop through records, and for each practitioner figure their practitionerRoles. + const tableData = processPractitionerDetails(data?.records ?? []) + + + const columns = [ + { + title: t('Name'), + dataIndex: 'name', + }, + { + title: t('Active'), + dataIndex: 'active', + render: (value: boolean) => (value === true ? 'Active' : 'Inactive'), + }, + { + title: t('Coding'), + dataIndex: 'concepts', + render: (concepts: CodeableConcept[]) => concepts.map(concept => ), + }, + ]; + + const tableProps = { + datasource: tableData, + columns, + loading: isFetching || isLoading, + pagination: tablePaginationProps, + }; + + return + +} + +function processPractitionerDetails(records: (IPractitioner | IPractitionerRole)[]) { + const tableData: Record = {} + const tempPractitionerRoleCodings: Record = {}; + + for (const res of records) { + if (res.resourceType === practitionerResourceType) { + const typedRes = res as IPractitioner + const resName = typedRes.name?.[0] // TODO - use get official name + // add to store + tableData[`${practitionerResourceType}/${typedRes.id}`] = { name: parseFhirHumanName(resName), active: res.active, concepts: [] } + } + else { + // practitionerRole resource + const typedRes = res as IPractitionerRole + const practitionerId = typedRes.practitioner?.reference as string + + // extract the coding + const concepts = (typedRes.code ?? []) + + // have we encountered a corresponding practitioner for this role + if (tableData[practitionerId] === undefined) { + tableData[practitionerId].codings.push(concepts) + } + else if (tempPractitionerRoleCodings[practitionerId] === undefined) { + tempPractitionerRoleCodings[practitionerId] = [] + } else { + tempPractitionerRoleCodings[practitionerId].push(concepts) + } + } + } + + for (const [key, value] of Object.entries(tempPractitionerRoleCodings)) { + // invariant: we should have encountered all possible practitioners whole practitioner Roles records are in tempPractitionerRoleCodings + tableData[key].coding = [...tableData[key].concepts, ...value] + } + return Object.values(tableData) +} + + +// Create a fhir sdk components package for this +export const FhirCodeableConcept = ({ concept }: { concept: CodeableConcept }) => { + // single concept can have text representation, + // Each concept can have several codings, each coding with its own display. + return codeableConcept + +} + + + + + + +// TODO - extract css to own file.- make sure to scope it to current component. \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx new file mode 100644 index 000000000..cde967914 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx @@ -0,0 +1,71 @@ +import { mount } from 'enzyme'; +import React from 'react'; +import { UserDetails } from '../'; +import { Organization } from '@opensrp/team-management'; +import { Practitioner, KeycloakUser } from '../../../ducks/user'; + +describe('components/TeamsDetail', () => { + const props = { + keycloakUser: { + id: 'c1d36d9a-b771-410b-959e-af2c04d132a2', + username: 'allay_allan', + } as KeycloakUser, + practitioner: { + identifier: 'f713620c-076e-407f-98fd-ae655b3dc5ba', + active: true, + } as Practitioner, + assignedTeams: [ + { + identifier: 'b0d21cea-9e0f-4ff8-b3e3-808a2655b9c5', + active: true, + name: 'Goldsmith CHW', + }, + { + identifier: '88e63364-53c1-436f-94b4-92d4bf7fc6c7', + active: true, + name: 'New team test', + }, + ] as Organization[], + }; + it('shows details section with data', () => { + const wrapper = mount(); + + const keycloakUsername = wrapper.find('#username').text(); + expect(keycloakUsername).toEqual(props.keycloakUser.username); + + const keycloakId = wrapper.find('#keycloakId').text(); + expect(keycloakId).toEqual(props.keycloakUser.id); + + const practitionerId = wrapper.find('#practitionerId').text(); + expect(practitionerId).toEqual(props.practitioner.identifier); + + const practitionerStatus = wrapper.find('#practitionerStatus').text(); + expect(practitionerStatus).toMatchInlineSnapshot(`"active"`); + + const assignedTeams = wrapper.find('#assignedTeam').map((team) => team.text()); + expect(assignedTeams).toEqual(props.assignedTeams.map((team) => team.name)); + }); + it('shows no practitioner message when practitioner not available', () => { + const newProps = { ...props, practitioner: undefined }; + const wrapper = mount(); + expect(wrapper.find('#noActivePractitioner').text()).toMatchInlineSnapshot( + `"No Active Practitioner"` + ); + }); + it('shows no team available message when team not available', () => { + const newProps = { ...props, assignedTeams: [] }; + const wrapper = mount(); + expect(wrapper.find('#noAssignedTeams').text()).toMatchInlineSnapshot(`"No Assigned Teams"`); + }); + it('shows spinner', () => { + const newProps = { ...props, keycloakUser: undefined }; + const wrapper = mount(); + expect(wrapper.find('.ant-spin').exists()).toBeTruthy(); + }); + it('removes it self on close', () => { + const wrapper = mount( wrapper.unmount()} />); + expect(wrapper.children()).toHaveLength(1); + wrapper.find('.close-btn button').simulate('click'); + expect(wrapper).toHaveLength(0); + }); +}); From 7d705b91298c387ea935ebeec3367eb5aaaa0508 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Fri, 27 Oct 2023 15:03:15 +0300 Subject: [PATCH 03/32] Add url to new user view details --- app/src/App/fhir-apps.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/src/App/fhir-apps.tsx b/app/src/App/fhir-apps.tsx index 7e05e1926..2da17d292 100644 --- a/app/src/App/fhir-apps.tsx +++ b/app/src/App/fhir-apps.tsx @@ -63,6 +63,7 @@ import { import { CreateEditUser as FHIRConnectedCreateEditUser, UserList as FhirUserList, + UserDetailsV2, } from '@opensrp/fhir-user-management'; import { Home } from '../containers/pages/Home/Home'; import { @@ -224,6 +225,14 @@ const FHIRApps = () => { permissions={['iam_user.read']} component={FhirUserList} /> + Date: Fri, 27 Oct 2023 15:04:07 +0300 Subject: [PATCH 04/32] silence table stripping for a more crisp clean look --- .../src/components/LocationUnitList/LocationUnitList.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css b/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css index 5390eb54a..031a5d525 100644 --- a/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css +++ b/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css @@ -22,6 +22,6 @@ border-left-color: #e9e9e9; } -table tr:nth-child(2n) td { +/* table tr:nth-child(2n) td { background-color: #f4f4f4; -} +} */ From a3f2f156b74015a3b88e90c983222f90a0c99b49 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Fri, 27 Oct 2023 15:04:51 +0300 Subject: [PATCH 05/32] Second iteration ideating data flow --- .../UserList/UserDetails-v2/index.tsx | 475 ++++++++++++------ .../components/UserList/ViewDetails/index.tsx | 4 +- .../src/constants.ts | 1 + .../src/index.tsx | 2 + 4 files changed, 335 insertions(+), 147 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx index 74ec92088..70d501244 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -1,16 +1,17 @@ import React from 'react'; import { CloseOutlined } from '@ant-design/icons'; -import { Alert, Button, Descriptions, Divider, Table, Tabs, Tag } from 'antd'; +import { Alert, Button, Col, Descriptions, Divider, Row, Table, Tabs, Tag } from 'antd'; +import { Tree, generateFhirLocationTree } from '@opensrp/fhir-location-management'; import { Organization } from '@opensrp/team-management'; import { Spin } from 'antd'; import { useTranslation } from '../../../mls' import { useParams } from 'react-router'; -import { BrokenPage, Resource404, TableLayout, getResourcesFromBundle, parseFhirHumanName, useSimpleTabularView, useTabularViewWithLocalSearch } from '@opensrp/react-utils'; +import { BrokenPage, FHIRServiceClass, Resource404, TableLayout, getResourcesFromBundle, parseFhirHumanName, useSimpleTabularView, useTabularViewWithLocalSearch } from '@opensrp/react-utils'; import { PageHeader } from '@ant-design/pro-layout'; import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS, KeycloakUser, URL_USER, UserGroupDucks } from '@opensrp/user-management'; -import { KeycloakService } from '@opensrp/keycloak-service'; +import { KeycloakAPIService, KeycloakService } from '@opensrp/keycloak-service'; import { useQuery } from 'react-query'; -import { groupResourceType, practitionerResourceType } from '../../../constants'; +import { groupResourceType, keycloakRoleMappingsEndpoint, practitionerResourceType } from '../../../constants'; import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; @@ -19,6 +20,9 @@ import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; +import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; +import { Link } from 'react-router-dom'; +import { StringLocale } from 'yup'; /** New details view. -> @@ -42,23 +46,108 @@ const useGetKeycloakUserRoles = (userId: string) => { // roles assigned to user by clients. } - -// TODO - what if we do not have a linked practitioner. -const useGetPractitioner = (userId: string) => { - // get practitioner include practitionerRoles. Multiple practitioners having multiple practitionerRoles. - // some practitioenerRoles are purely for assignment. Add codes where possible. +interface KeycloakGroupDetailsProp { + keycloakBaseUrl: string; + resourceId: string; } -const useGetCareTeams = () => { - // get careTeams that reference this practitioner +const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { + const { t } = useTranslation(); + const { keycloakBaseUrl, resourceId } = props; + + // TODO - useSimpleTabular view but for keycloak requests. + const { data, error, isLoading } = useQuery([], () => new KeycloakService( + `${KEYCLOAK_URL_USERS}/${resourceId}${KEYCLOAK_URL_USER_GROUPS}`, + keycloakBaseUrl + ).list()) + + if (error && !data) { + return ; + } + + // name, path, leave action. + const columns = [ + { + title: t('Name'), + dataIndex: 'name' as const, + }, + { + title: t('Path'), + dataIndex: 'path' as const, + }, + { + title: t(''), + dataIndex: 'id' as const, + render: (id: string) => , + }, + ]; + + const tableProps = { + datasource: data, + columns, + loading: isLoading, + // pagination: true, + }; + + return } -const useGetOrganization = () => { - // get organizations that reference this practitioner either directly or via a careTeam +interface RoleMapping { + id: string; + name: string; + description: string; + composite: boolean; + clientRole: boolean; + containerId: string; +} +interface ClientRoleMapping { + id: string; + client: string; + mappings: RoleMapping[] } +interface KeycloakUserRoleMappings { + realMappings?: RoleMapping[]; + clientMappings?: Record +} +const KeycloakRoleDetails = (props: KeycloakGroupDetailsProp) => { + const { t } = useTranslation(); + const { keycloakBaseUrl, resourceId } = props; -const useGetLocations = () => { - // get locations where this practitioner is assigned. + // TODO - useSimpleTabular view but for keycloak requests. + const { data, error, isLoading } = useQuery([], () => new KeycloakService( + `${KEYCLOAK_URL_USERS}/${resourceId}/${keycloakRoleMappingsEndpoint}`, + keycloakBaseUrl + ).list()) + + if (error && !data) { + return ; + } + + // name, path, leave action. + const columns = [ + { + title: t('Name'), + dataIndex: 'name' as const, + }, + { + title: t('Path'), + dataIndex: 'path' as const, + }, + { + title: t(''), + dataIndex: 'id' as const, + render: (id: string) => , + }, + ]; + + const tableProps = { + datasource: data, + columns, + loading: isLoading, + // pagination: true, + }; + + return } // - if user record not found then show 404 under page details. @@ -81,8 +170,7 @@ const routes = [{ ] export const UserDetails = (props: UserDetailProps) => { - const { keycloakBaseURL: keycloakBaseUrl, fhirBaseURL: fhirBBaseUrl } = props - console.log({ props }) + const { keycloakBaseURL: keycloakBaseUrl, fhirBaseURL: fhirBaseUrl } = props const params = useParams<{ id: string }>() const { id: resourceId } = params; const { t } = useTranslation(); @@ -95,6 +183,23 @@ export const UserDetails = (props: UserDetailProps) => { new KeycloakService(`${KEYCLOAK_URL_USERS}`, keycloakBaseUrl).read(resourceId) ); + const practitionerDetailsResourceType = "practitioner-details" + + const extraQueryFilters = { + "keycloak-uuid": resourceId, + } + + const { + data: practitionerDetails, isLoading: detailsLoading, error: detailsError, + + } = useQuery([practitionerDetailsResourceType, resourceId], () => new FHIRServiceClass(fhirBaseUrl, practitionerDetailsResourceType).list(extraQueryFilters), { + select: (res) => { + // invariant : expect practitioner-details will always ever be a single record per keycloak user. + return getResourcesFromBundle(res)[0] + } + }); + const practDetailsByResName: PractitionerDetail['fhir'] = practitionerDetails?.fhir ?? {} + if (userIsLoading) { return } @@ -179,12 +284,12 @@ export const UserDetails = (props: UserDetailProps) => { size={"middle"} items={ [ - { label: "Groups", key: 'Groups', children:
}, + { label: "Groups", key: 'Groups', children: }, { label: "Roles", key: 'Roles', children:
}, - { label: "Practitioners", key: 'Practitioners', children: }, - { label: "CareTeams", key: 'CareTeams', children:
}, - { label: "Organizations", key: 'Organizations', children:
}, - { label: "Locations", key: 'Locations', children:
}, + { label: "Practitioners", key: 'Practitioners', children: }, + { label: "CareTeams", key: 'CareTeams', children: }, + { label: "Organizations", key: 'Organizations', children: }, + { label: "Locations", key: 'Locations', children: }, ] } /> @@ -195,91 +300,25 @@ export const UserDetails = (props: UserDetailProps) => { ); }; -// const UserDetailsTabView = ({keycloakBaseUrl, resourceId}: {keycloakBaseUrl: string, resourceId: string}) => { - -// // get groups in a searchable way -// // read userGroup -// const { data, isLoading, error, isFetching } = useQuery< -// UserGroupDucks.KeycloakUserGroup[] -// >([groupResourceType, resourceId], () => { -// return new KeycloakService( -// `${KEYCLOAK_URL_USERS}/${resourceId}${KEYCLOAK_URL_USER_GROUPS}`, -// keycloakBaseUrl -// ).list(); -// }); - -// if (error && !data) { -// return ; -// } - -// const tableData = (data?.records ?? []).map((org: IGroup, index: number) => { -// return { -// ...parseGroup(org), -// key: `${index}`, -// }; -// }); - -// const columns = - -// const tableProps = { -// datasource: tableData, -// columns, -// loading: isFetching || isLoading, -// pagination: tablePaginationProps, -// }; - - - - -// } - interface PractitionerDetail extends Resource { fhir: { careteams?: ICareTeam[]; teams?: IOrganization[]; - locationHierarchyList: any[]; // TODO - import LocationHierarchy - practitionerRoles: IPractitionerRole[]; - groups: IGroup[]; - practitioner: IPractitioner[] + locationHierarchyList?: any[]; // TODO - import LocationHierarchy + practitionerRoles?: IPractitionerRole[]; + groups?: IGroup[]; + practitioner?: IPractitioner[] } } -function groupResourcesIdDetails(details: PractitionerDetail[]){ - const groupedData = {} - for (const detail of details){ - for(const [_, resources] of Object.entries(detail.fhir)){ - for(const resource of resources){ - - } - } - } -} -const DetailsTabView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: string, keycloakId: string }) => { +const PractitionerDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { const { t } = useTranslation(); - /** Get practitioner details and group them, provision them for the tab children, they can consume whichever data they want. - */ - const practitionerDetailsResourceType = "practitioner-details" - - const extraQueryFilters = { - "keycloak-uuid": keycloakId, - } - - const { - data, isFetching, isLoading, error, - - } = useQuery([practitionerDetailsResourceType, keycloakId], () => new KeycloakService(practitionerDetailsResourceType, fhirBaseUrl).list(extraQueryFilters), { - select: (res) => { - // invariant : expect practitioner-details will always ever be a single record per keycloak user. - return getResourcesFromBundle(res)[0] - } - }); - const resourcesByName = data?.fhir ?? {} - - // loop through records, and for each practitioner figure their practitionerRoles. - const tableData = processPractitionerDetails(data?.records ?? []) + const practitioners = practitionerDetails.practitioner ?? []; + const practitionerRoles = practitionerDetails.practitionerRoles ?? []; + const tableData = processPractitionerDetails(practitioners, practitionerRoles) const columns = [ @@ -302,8 +341,8 @@ const DetailsTabView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: string, keyc const tableProps = { datasource: tableData, columns, - loading: isFetching || isLoading, - pagination: tablePaginationProps, + loading, + // pagination: true, }; return @@ -311,41 +350,82 @@ const DetailsTabView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: string, keyc } -const PractitionerDetailsView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: string, keycloakId: string }) => { +const CareTeamDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { const { t } = useTranslation(); - /** get all practitioners linked to this user. - * where do practitioner role information come in. revInclude practitionerRoles. - */ - /** get practitioners that have the given secondary identfier, an include practitionerRoles that reference those practitioners. */ - const extraQueryFilters = { - identifier: keycloakId, - _revinclude: "PractitionerRole:practitioner" - } - const { - queryValues: { data, isFetching, isLoading, error }, - tablePaginationProps, - searchFormProps, - } = useSimpleTabularView(fhirBaseUrl, practitionerResourceType, extraQueryFilters); + const careTeams = practitionerDetails.careteams ?? []; + const tableData = careTeams.map(resource => { + const { id, status, name, } = resource + return { + id, status, name + } + }) - // loop through records, and for each practitioner figure their practitionerRoles. - const tableData = processPractitionerDetails(data?.records ?? []) + + // identifier, status, + const columns = [ + { + title: t('Id'), + dataIndex: 'id' as const, + }, + { + title: t('Name'), + dataIndex: 'name' as const, + render: (name: string) => {name} + }, + { + title: t('status'), + dataIndex: 'status' as const, + render: (code: Coding) => , + }, + ]; + + const tableProps = { + datasource: tableData, + columns, + loading, + // pagination: true, + }; + + return + +} + + +const OrganizationDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { + const { t } = useTranslation(); + // get organization Affiliation - use it tag the codings for the organizations. + const organizations = practitionerDetails.teams ?? []; + const tableData = organizations.map(resource => { + const { id, active, type, name } = resource + return { + id, active, type: type ?? [], name + } + }) + + + // identifier, status, const columns = [ + { + title: t('Id'), + dataIndex: 'id' as const, + }, { title: t('Name'), - dataIndex: 'name', + dataIndex: 'name' as const, + render: (name: string) => {name} }, { title: t('Active'), - dataIndex: 'active', - render: (value: boolean) => (value === true ? 'Active' : 'Inactive'), + dataIndex: 'active' as const, + render: (isActive: string) => isActive ? "Active" : "Inactive", }, { - title: t('Coding'), - dataIndex: 'concepts', + title: t('Type'), + dataIndex: 'type' as const, render: (concepts: CodeableConcept[]) => concepts.map(concept => ), }, ]; @@ -353,42 +433,140 @@ const PractitionerDetailsView = ({ fhirBaseUrl, keycloakId }: { fhirBaseUrl: str const tableProps = { datasource: tableData, columns, - loading: isFetching || isLoading, - pagination: tablePaginationProps, + loading, + // pagination: true, }; return } -function processPractitionerDetails(records: (IPractitioner | IPractitionerRole)[]) { + +const LocationDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { + const { t } = useTranslation(); + + + // get organization Affiliation - use it tag the codings for the organizations. + const hierarchies = practitionerDetails.locationHierarchyList ?? []; + const treeData = hierarchies.map(rawTree => generateFhirLocationTree(rawTree)) + // const tableData = hierarchies.map(resource => { + // const tree = + // const {id, active, type,name } = resource + // return { + // id, active, type, name + // } + // }) + + + // identifier, status, + const columns = [ + { + title: t('Id'), + dataIndex: 'id' as const, + }, + { + title: t('Name'), + dataIndex: 'name' as const, + render: (name: string) => {name} + }, + { + title: t('Active'), + dataIndex: 'active' as const, + render: (isActive: string) => isActive ? "Active" : "Inactive", + }, + { + title: t('Type'), + dataIndex: 'type' as const, + render: (concepts: CodeableConcept[]) => concepts.map(concept => ), + }, + ]; + + // const tableProps = { + // datasource: tableData, + // columns, + // loading, + // // pagination: true, + // }; + + return <> + + + { + // dispatch(setSelectedNode(node)); + }} + /> + + {/* +
+
+ {selectedNode ? selectedNode.model.node.name : t('Location Unit')} +
+ +
+ +
+
+
+
+
{ + setDetailId(row.id); + }} + /> + + */} + + + +} + + + +function processPractitionerDetails(practitioners: IPractitioner[], practitionerRoles: IPractitionerRole[]) { const tableData: Record = {} const tempPractitionerRoleCodings: Record = {}; - for (const res of records) { - if (res.resourceType === practitionerResourceType) { - const typedRes = res as IPractitioner - const resName = typedRes.name?.[0] // TODO - use get official name - // add to store - tableData[`${practitionerResourceType}/${typedRes.id}`] = { name: parseFhirHumanName(resName), active: res.active, concepts: [] } + for (const res of practitioners) { + const typedRes = res + const resName = typedRes.name?.[0] // TODO - use get official name + // add to store + tableData[`${practitionerResourceType}/${typedRes.id}`] = { name: parseFhirHumanName(resName), active: res.active, concepts: [] } + } + + for (const res of practitionerRoles) { + // practitionerRole resource + const typedRes = res as IPractitionerRole + const practitionerId = typedRes.practitioner?.reference as string + + // extract the coding + const concepts = (typedRes.code ?? []) + + // have we encountered a corresponding practitioner for this role + if (tableData[practitionerId] === undefined) { + tableData[practitionerId].codings.push(concepts) } - else { - // practitionerRole resource - const typedRes = res as IPractitionerRole - const practitionerId = typedRes.practitioner?.reference as string - - // extract the coding - const concepts = (typedRes.code ?? []) - - // have we encountered a corresponding practitioner for this role - if (tableData[practitionerId] === undefined) { - tableData[practitionerId].codings.push(concepts) - } - else if (tempPractitionerRoleCodings[practitionerId] === undefined) { - tempPractitionerRoleCodings[practitionerId] = [] - } else { - tempPractitionerRoleCodings[practitionerId].push(concepts) - } + else if (tempPractitionerRoleCodings[practitionerId] === undefined) { + tempPractitionerRoleCodings[practitionerId] = [] + } else { + tempPractitionerRoleCodings[practitionerId].push(concepts) } } @@ -409,8 +587,13 @@ export const FhirCodeableConcept = ({ concept }: { concept: CodeableConcept }) = } +export const FhirCoding = ({ coding }: { coding: Coding }) => { + return Coding +} + -// TODO - extract css to own file.- make sure to scope it to current component. \ No newline at end of file +// TODO - extract css to own file.- make sure to scope it to current component. +// TODO - include any other details added to practitionerDetails. \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/index.tsx index 00b666c99..95ca03884 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/index.tsx @@ -70,7 +70,9 @@ export const ViewDetails = (props: ViewDetailsProps) => { () => { return new FHIRServiceClass(fhirBaseUrl, practitionerResourceType) .list({ identifier: resourceId }) - .then((res: IBundle) => getResourcesFromBundle(res)[0]); + + },{ + select:(res: IBundle) => getResourcesFromBundle(res)[0] } ); diff --git a/packages/fhir-keycloak-user-management/src/constants.ts b/packages/fhir-keycloak-user-management/src/constants.ts index 899adc30d..3beb00841 100644 --- a/packages/fhir-keycloak-user-management/src/constants.ts +++ b/packages/fhir-keycloak-user-management/src/constants.ts @@ -3,6 +3,7 @@ export const careTeamResourceType = 'CareTeam'; export const organizationResourceType = 'Organization'; export const groupResourceType = 'Group'; export const practitionerRoleResourceType = 'PractitionerRole'; +export const keycloakRoleMappingsEndpoint = 'role-mappings'; // keycloak endpoints strings export const keycloakCountEndpoint = 'count'; diff --git a/packages/fhir-keycloak-user-management/src/index.tsx b/packages/fhir-keycloak-user-management/src/index.tsx index 5c893c25f..cf5a32740 100644 --- a/packages/fhir-keycloak-user-management/src/index.tsx +++ b/packages/fhir-keycloak-user-management/src/index.tsx @@ -1,3 +1,5 @@ export * from './components/CreateEditUser'; export * from './components/UserList/ListView'; export * from './constants'; +import {UserDetails as UserDetailsV2} from './components/UserList/UserDetails-v2'; +export {UserDetailsV2}; From baf529cd36bb8256e6ab54c5f6edf8e0524cda19 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Tue, 7 Nov 2023 11:04:34 +0300 Subject: [PATCH 06/32] Export fhirDataType components --- .../fhirDataTypes/CodeableConcept/index.tsx | 27 +++++++++++++++++++ .../components/fhirDataTypes/Coding/index.tsx | 22 +++++++++++++++ .../src/components/fhirDataTypes/index.ts | 2 ++ packages/react-utils/src/index.tsx | 1 + 4 files changed, 52 insertions(+) create mode 100644 packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx create mode 100644 packages/react-utils/src/components/fhirDataTypes/Coding/index.tsx create mode 100644 packages/react-utils/src/components/fhirDataTypes/index.ts diff --git a/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx b/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx new file mode 100644 index 000000000..6ad7ca9b7 --- /dev/null +++ b/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx @@ -0,0 +1,27 @@ +import { CodeableConcept as TCodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; +import { Tooltip } from 'antd'; +import { Coding } from '../Coding'; +import { Typography } from 'antd'; +import React from 'react'; + +const { Text } = Typography; + +export interface CodeableConceptProps { + concept: TCodeableConcept; +} + +export const CodeableConcept = (props: CodeableConceptProps) => { + const { concept } = props; + const { coding, text } = concept; + + const codingsTitle = ( + <> + {(coding ?? []).map((coding, index) => ( + <> + ,{' '} + + ))} + + ); + return {text ? {text} : codingsTitle}; +}; diff --git a/packages/react-utils/src/components/fhirDataTypes/Coding/index.tsx b/packages/react-utils/src/components/fhirDataTypes/Coding/index.tsx new file mode 100644 index 000000000..9c5352f18 --- /dev/null +++ b/packages/react-utils/src/components/fhirDataTypes/Coding/index.tsx @@ -0,0 +1,22 @@ +import { Coding as TCoding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; +import { Typography } from 'antd'; +import React from 'react'; + +const { Text } = Typography; + +export interface CodingProps { + coding: TCoding; +} + +export const Coding = (props: CodingProps) => { + const { display, system, code } = props.coding; + let valueStr = ''; + + if (display) { + valueStr += display; + } + if (system) { + valueStr += `(${system}|${code ? code : ''})`; + } + return {valueStr}; +}; diff --git a/packages/react-utils/src/components/fhirDataTypes/index.ts b/packages/react-utils/src/components/fhirDataTypes/index.ts new file mode 100644 index 000000000..ed75eebaa --- /dev/null +++ b/packages/react-utils/src/components/fhirDataTypes/index.ts @@ -0,0 +1,2 @@ +export * from './CodeableConcept'; +export * from './Coding'; diff --git a/packages/react-utils/src/index.tsx b/packages/react-utils/src/index.tsx index 4c42cba8a..e2b73c8c3 100644 --- a/packages/react-utils/src/index.tsx +++ b/packages/react-utils/src/index.tsx @@ -21,3 +21,4 @@ export * from './components/PageHeader'; export * from './components/ButtonLink'; export * from './helpers/test-utils'; export * from './components/PrivateRoute'; +export * from './components/fhirDataTypes'; From c2f7db85d1b894f1012ad22aaabc4b71b5b08dab Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Tue, 7 Nov 2023 11:10:50 +0300 Subject: [PATCH 07/32] Update user management constantts --- packages/fhir-keycloak-user-management/src/constants.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/fhir-keycloak-user-management/src/constants.ts b/packages/fhir-keycloak-user-management/src/constants.ts index 3beb00841..81d16b3a5 100644 --- a/packages/fhir-keycloak-user-management/src/constants.ts +++ b/packages/fhir-keycloak-user-management/src/constants.ts @@ -4,6 +4,9 @@ export const organizationResourceType = 'Organization'; export const groupResourceType = 'Group'; export const practitionerRoleResourceType = 'PractitionerRole'; export const keycloakRoleMappingsEndpoint = 'role-mappings'; +export const practitionerDetailsResourceType = 'practitioner-details'; // keycloak endpoints strings export const keycloakCountEndpoint = 'count'; +export const keycloakGroupEndpoint = 'groups'; +export const keycloakMembersEndpoint = 'members'; From 769918dc85d364c9a97e3107757b79a016877229 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Tue, 7 Nov 2023 11:12:09 +0300 Subject: [PATCH 08/32] Add second iteration of user detail views --- .../ViewDetailResources/CareTeamDetails.tsx | 73 +++ .../ViewDetailResources/GroupDetailView.tsx | 81 +++ .../OrganizationDetailsView.tsx | 74 +++ .../ViewDetailResources/RoleDetailView.tsx | 106 ++++ .../ViewDetailResources/locationDetails.tsx | 136 +++++ .../practitionerDetails.tsx | 155 +++++ .../ViewDetailResources/tests/fixtures.ts | 176 ++++++ .../tests/practitionerDetails.test.tsx | 21 + .../UserList/UserDetails-v2/index.css | 21 + .../UserList/UserDetails-v2/index.tsx | 546 +++--------------- .../UserDetails-v2/tests/index.test.tsx | 291 ++++++++-- .../UserList/UserDetails-v2/types.ts | 49 ++ 12 files changed, 1196 insertions(+), 533 deletions(-) create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx new file mode 100644 index 000000000..78ce3d53a --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { useTranslation } from '../../../../mls'; +import { TableLayout, CodeableConcept as CodeableConceptJsx } from '@opensrp/react-utils'; +import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; +import { Link } from 'react-router-dom'; +import { PractitionerDetail } from '../types'; +import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; +import { Alert } from 'antd'; + +export interface CareTeamDetailsViewProps { + loading: boolean; + practitionerDetails: PractitionerDetail['fhir']; + error?: Error; +} + +export const CareTeamDetailsView = ({ + loading, + practitionerDetails, + error, +}: CareTeamDetailsViewProps) => { + const { t } = useTranslation(); + + if (error) { + return ( + + {'An error occurred while trying to fetch the practitioner details.'} + + ); + } + + const careTeams = practitionerDetails.careteams ?? []; + const tableData = careTeams.map((resource) => { + const { id, status, name, category } = resource; + return { + id, + status, + name, + category: category ?? [], + }; + }); + + const columns = [ + { + title: t('Id'), + dataIndex: 'id' as const, + }, + { + title: t('Name'), + dataIndex: 'name' as const, + }, + { + title: t('Status'), + dataIndex: 'status' as const, + }, + { + title: t('Category'), + dataIndex: 'category' as const, + render: (concepts: CodeableConcept[]) => { + console.log({ concepts }); + return concepts.map((concept) => ); + }, + }, + ]; + + const tableProps = { + datasource: tableData, + columns, + loading, + size: 'small' as const, + }; + + return ; +}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx new file mode 100644 index 000000000..91205900e --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Alert, Button } from 'antd'; +import { useTranslation } from '../../../../mls'; +import { BrokenPage, TableLayout } from '@opensrp/react-utils'; +import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS } from '@opensrp/user-management'; +import { KeycloakService } from '@opensrp/keycloak-service'; +import { QueryClient, useQueryClient, useQuery } from 'react-query'; +import { keycloakGroupEndpoint, keycloakMembersEndpoint } from '../../../../constants'; +import { sendSuccessNotification } from '@opensrp/notifications'; + +export interface KeycloakGroupDetailsProp { + keycloakBaseUrl: string; + resourceId: string; +} + +export const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { + const { t } = useTranslation(); + const { keycloakBaseUrl, resourceId } = props; + const query = useQueryClient(); + + const { data, error, isLoading } = useQuery([KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS], () => + new KeycloakService( + `${KEYCLOAK_URL_USERS}/${resourceId}${KEYCLOAK_URL_USER_GROUPS}`, + keycloakBaseUrl + ).list() + ); + + if (error && !data) { + return ( + + {'Unable to fetch the keycloak groups that the user is assigned to'} + + ); + } + + const columns = [ + { + title: t('Name'), + dataIndex: 'name' as const, + }, + { + title: t('Path'), + dataIndex: 'path' as const, + }, + { + title: t(''), + dataIndex: 'id' as const, + render: (id: string) => ( + + ), + }, + ]; + + const tableProps = { + datasource: data ?? [], + columns, + loading: isLoading, + size: 'small' as const, + }; + + return ; +}; + +export const removeGroupFromUser = async ( + baseUrl: string, + groupId: string, + userId: string, + query: QueryClient +) => { + const endpoint = `${KEYCLOAK_URL_USERS}/${userId}${KEYCLOAK_URL_USER_GROUPS}/${groupId}`; + const server = new KeycloakService(endpoint, baseUrl); + return server.delete().then(() => { + query.refetchQueries([KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS]); + sendSuccessNotification('User was removed from the keycloak group'); + }); +}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx new file mode 100644 index 000000000..688bdaf9d --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { useTranslation } from '../../../../mls'; +import { TableLayout, CodeableConcept as CodeableConceptJsx } from '@opensrp/react-utils'; +import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; +import { Link } from 'react-router-dom'; +import { PractitionerDetail } from '../types'; +import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; +import { Alert } from 'antd'; + +export interface OrganizationDetailsViewProp { + loading: boolean; + practitionerDetails: PractitionerDetail['fhir']; + error?: Error; +} + +export const OrganizationDetailsView = ({ + loading, + practitionerDetails, + error, +}: OrganizationDetailsViewProp) => { + const { t } = useTranslation(); + + if (error) { + return ( + + {'An error occurred while trying to fetch the practitioner details.'} + + ); + } + + // get organization Affiliation - use it tag the codings for the organizations. + const organizations = practitionerDetails.teams ?? []; + const tableData = organizations.map((resource) => { + const { id, active, type, name } = resource; + return { + id, + active, + type: type ?? [], + name, + }; + }); + + // identifier, status, + const columns = [ + { + title: t('Id'), + dataIndex: 'id' as const, + }, + { + title: t('Name'), + dataIndex: 'name' as const, + }, + { + title: t('Active'), + dataIndex: 'active' as const, + render: (isActive: string) => (isActive ? 'Active' : 'Inactive'), + }, + { + title: t('Type'), + dataIndex: 'type' as const, + render: (concepts: CodeableConcept[]) => + concepts.map((concept) => ), + }, + ]; + + const tableProps = { + datasource: tableData, + columns, + loading, + size: 'small' as const, + }; + + return ; +}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx new file mode 100644 index 000000000..5fb2b8c8e --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import { Divider, Typography } from 'antd'; +import { useTranslation } from '../../../../mls'; +import { BrokenPage, TableLayout } from '@opensrp/react-utils'; +import { KEYCLOAK_URL_USERS } from '@opensrp/user-management'; +import { KeycloakService } from '@opensrp/keycloak-service'; +import { useQuery } from 'react-query'; +import { keycloakRoleMappingsEndpoint } from '../../../../constants'; +import { KeycloakUserRoleMappings } from '../types'; + +const { Text } = Typography; + +export interface KeycloakRoleDetailsProps { + keycloakBaseUrl: string; + resourceId: string; +} + +export const KeycloakRoleDetails = (props: KeycloakRoleDetailsProps) => { + const { t } = useTranslation(); + const { keycloakBaseUrl, resourceId } = props; + + const { data, error, isLoading } = useQuery([], () => + new KeycloakService( + `${KEYCLOAK_URL_USERS}/${resourceId}/${keycloakRoleMappingsEndpoint}`, + keycloakBaseUrl + ).list() + ); + + if (error && !data) { + return ; + } + + const { realmMappings, clientMappings } = data ?? {}; + const realmRoles = realmMappings ?? []; + const clientRoles = flattenClientRoles(clientMappings); + + const realmRoleTableColumn = [ + { + title: t('Name'), + dataIndex: 'name' as const, + }, + + { + title: t('Description'), + dataIndex: 'description' as const, + }, + ]; + + const clientRoleTableColumn = [ + { + title: t('Client'), + dataIndex: 'client' as const, + }, + { + title: t('Name'), + dataIndex: 'name' as const, + }, + + { + title: t('Description'), + dataIndex: 'id' as const, + }, + ]; + + console.log({ realmRoles, clientRoles }); + const realmRolesTableProps = { + datasource: [], + columns: realmRoleTableColumn, + loading: isLoading, + size: 'small' as const, + key: 'realms', + }; + + const clientRolesTableProps = { + datasource: [], + columns: clientRoleTableColumn, + loading: isLoading, + size: 'small' as const, + key: 'client', + }; + + return ( + <> + Realm Roles + + + Client Roles + + + ); +}; + +const flattenClientRoles = (clientRoleMapping: KeycloakUserRoleMappings['clientMappings']) => { + const mapping = clientRoleMapping ?? {}; + const rtv = []; + for (const roleObj of Object.values(mapping)) { + const client = roleObj.client; + for (const role of roleObj.mappings) { + rtv.push({ + ...role, + client, + }); + } + } + return rtv; +}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx new file mode 100644 index 000000000..94cfe5699 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx @@ -0,0 +1,136 @@ +// import React from 'react'; +// import { CloseOutlined } from '@ant-design/icons'; +// import { Alert, Button, Col, Descriptions, Divider, Row, Table, Tabs, Tag, Typography } from 'antd'; +// import { Tree, generateFhirLocationTree } from '@opensrp/fhir-location-management'; +// import { Organization } from '@opensrp/team-management'; +// import { Spin } from 'antd'; +// import { useTranslation } from '../../../mls' +// import { useParams } from 'react-router'; +// import { BrokenPage, FHIRServiceClass, Resource404, TableLayout, getResourcesFromBundle, parseFhirHumanName, useSimpleTabularView, useTabularViewWithLocalSearch } from '@opensrp/react-utils'; +// import { PageHeader } from '@ant-design/pro-layout'; +// import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS, KeycloakUser, URL_USER, UserGroupDucks } from '@opensrp/user-management'; +// import { KeycloakAPIService, KeycloakService } from '@opensrp/keycloak-service'; +// import { useQuery } from 'react-query'; +// import { groupResourceType, keycloakRoleMappingsEndpoint, practitionerResourceType } from '../../../constants'; +// import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; +// import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; +// import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; +// import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; +// import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; +// import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; +// import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; +// import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; +// import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; +// import { Link } from 'react-router-dom'; +// import { PractitionerDetail } from './types'; +// import { useSelector } from 'react-redux'; + +// export const LocationDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { +// const { t } = useTranslation(); +// const selectedNode = useSelector((state) => getSelectedNode(state)); + +// // get organization Affiliation - use it tag the codings for the organizations. +// const hierarchies = practitionerDetails.locationHierarchyList ?? []; +// const treeData = hierarchies.map(rawTree => generateFhirLocationTree(rawTree)) +// // const tableData = hierarchies.map(resource => { +// // const tree = +// // const {id, active, type,name } = resource +// // return { +// // id, active, type, name +// // } +// // }) + +// // generate table data; consider if there is a selected node, sorting the data +// const toDispNodes = +// (selectedNode ? (selectedNode.children as TreeNode[]) : treeData.children) ?? []; +// const sortedNodes = [...toDispNodes].sort((a, b) => +// a.model.node.name.localeCompare(b.model.node.name) +// ); +// let tableNodes = sortedNodes; +// // if a node is selected only its children should be selected, the selected node should come first anyway. +// if (selectedNode) { +// tableNodes = [selectedNode, ...sortedNodes]; +// } +// const tableDispData = parseTableData(tableNodes); + +// // identifier, status, +// const columns = [ +// { +// title: t('Id'), +// dataIndex: 'id' as const, +// }, +// { +// title: t('Name'), +// dataIndex: 'name' as const, +// render: (name: string) => {name} +// }, +// { +// title: t('Active'), +// dataIndex: 'active' as const, +// render: (isActive: string) => isActive ? "Active" : "Inactive", +// }, +// { +// title: t('Type'), +// dataIndex: 'type' as const, +// render: (concepts: CodeableConcept[]) => concepts.map(concept => ), +// }, +// ]; + +// // const tableProps = { +// // datasource: tableData, +// // columns, +// // loading, +// // // pagination: true, +// // }; + +// return <> +// +// +// { +// // dispatch(setSelectedNode(node)); +// }} +// /> +// +// {/* +//
+//
+// {selectedNode ? selectedNode.model.node.name : t('Location Unit')} +//
+// +//
+// +//
+//
+//
+//
+//
{ +// setDetailId(row.id); +// }} +// /> +// +// */} +// +// + +// } + +export {}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx new file mode 100644 index 000000000..3ac5b0718 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx @@ -0,0 +1,155 @@ +import React from 'react'; +import { useTranslation } from '../../../../mls'; +import { + TableLayout, + parseFhirHumanName, + Coding as CodingJsx, + CodeableConcept as CodeableConceptJsx, +} from '@opensrp/react-utils'; +import { practitionerResourceType } from '../../../../constants'; +import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; +import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; +import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; +import { PractitionerDetail } from '../types'; +import { Alert } from 'antd'; +import { + PRACTITIONER_USER_TYPE_CODE, + SUPERVISOR_USER_TYPE_CODE, + UserListTypes, + getUserType, + getUserTypeCode, +} from '@opensrp/user-management'; + +export interface PractitionerDetailViewsProps { + loading: boolean; + practitionerDetails: PractitionerDetail['fhir']; + error?: Error; +} + +export const PractitionerDetailsView = (props: PractitionerDetailViewsProps) => { + const { loading, practitionerDetails, error } = props; + const { t } = useTranslation(); + + if (error) { + return ( + + {'An error occurred while trying to fetch the practitioner details.'} + + ); + } + + const practitioners = practitionerDetails.practitioner ?? []; + const practitionerRoles = practitionerDetails.practitionerRoles ?? []; + const tableData = processPractitionerDetails(practitioners, practitionerRoles); + + const columns = [ + { + title: t('Id'), + dataIndex: 'id' as const, + }, + { + title: t('Name'), + dataIndex: 'name' as const, + }, + { + title: t('Active'), + dataIndex: 'active' as const, + render: (value: boolean) => (value === true ? 'Active' : 'Inactive'), + }, + { + title: t('User Type'), + dataIndex: 'userType' as const, + }, + { + title: t('Practitioner Role Coding'), + dataIndex: 'concepts' as const, + render: (concepts: CodeableConcept[]) => { + console.log({ concepts }); + return concepts.map((concept) => ); + }, + }, + ]; + + const tableProps = { + datasource: tableData, + columns, + loading, + size: 'small' as const, + }; + + return ; +}; + +/** + * @param practitioners + * @param practitionerRoles + */ +function processPractitionerDetails( + practitioners: IPractitioner[], + practitionerRoles: IPractitionerRole[] +) { + interface TableData { + id?: string; + name?: string; + active?: boolean; + concepts: CodeableConcept[]; + userType?: string; + } + const tableData: Record = {}; + const tempPractitionerRoleCodings: Record< + string, + { concepts: CodeableConcept[]; userType?: string } + > = {}; + + for (const res of practitioners) { + const typedRes = res; + const resName = typedRes.name?.[0]; // TODO - use get official name + // add to store + tableData[`${practitionerResourceType}/${typedRes.id}`] = { + id: res.id, + name: parseFhirHumanName(resName), + active: res.active, + concepts: [], + userType: undefined, + }; + } + + for (const res of practitionerRoles) { + // practitionerRole resource + const typedRes = res as IPractitionerRole; + const practitionerId = typedRes.practitioner?.reference as string; + + // extract the coding + const concepts = typedRes.code ?? []; + let userType: string | undefined = undefined; + const userTypeCode = getUserTypeCode(res); + if (userTypeCode) { + userType = getUserType( + userTypeCode as typeof PRACTITIONER_USER_TYPE_CODE | typeof SUPERVISOR_USER_TYPE_CODE + ); + } + + // have we encountered a corresponding practitioner for this role + if (tableData[practitionerId] === undefined) { + tableData[practitionerId].concepts = [...tableData[practitionerId].concepts, ...concepts]; + tableData[practitionerId].userType = userType; + } else if (tempPractitionerRoleCodings[practitionerId] === undefined) { + tempPractitionerRoleCodings[practitionerId] = { concepts: [] }; + tempPractitionerRoleCodings[practitionerId].concepts = []; + tempPractitionerRoleCodings[practitionerId].userType = userType; + } + tempPractitionerRoleCodings[practitionerId].concepts = [ + ...tempPractitionerRoleCodings[practitionerId].concepts, + ...concepts, + ]; + } + + for (const [key, value] of Object.entries(tempPractitionerRoleCodings)) { + // invariant: we should have encountered all possible practitioners whole practitioner Roles records are in tempPractitionerRoleCodings + tableData[key].concepts = [...tableData[key].concepts, ...value.concepts]; + tableData[key].userType = value.userType; + } + + console.log({ tableData, practitionerRoles }); + return Object.values(tableData); +} diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts new file mode 100644 index 000000000..f4260d24c --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts @@ -0,0 +1,176 @@ +import { IBundle } from "@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle"; +import { PractitionerDetail } from "../../types"; + +export const practitionerDetailsBundle = { + "resourceType": "Bundle", + "id": "993066c6-1fda-4f91-a30f-96ab57573e87", + "meta": { + "lastUpdated": "2023-10-30T14:30:12.383+00:00" + }, + "type": "searchset", + "total": 1, + "link": [{ + "relation": "self", + "url": "https://fhir.labs.smartregister.org/fhir/practitioner-details/_search?keycloak-uuid=9f72c646-dc1e-4f24-98df-6f04373b9ec6" + }], + "entry": [{ + "fullUrl": "https://fhir.labs.smartregister.org/fhir/practitioner-details/3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad", + "resource": { + "resourceType": "practitioner-details", + "id": "3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad", + "meta": { + "profile": ["http://hl7.org/fhir/profiles/custom-resource"] + }, + "fhir": { + "id": "3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad", + "teams": [{ + "resourceType": "Organization", + "id": "0d7ae048-9b84-4f0c-ba37-8d6c0b97dc84", + "meta": { + "versionId": "1", + "lastUpdated": "2023-10-13T09:59:23.717+00:00", + "source": "#b7fa59921069f37e" + }, + "identifier": [{ + "use": "official", + "value": "7712bfdd-83e3-43cf-b5f0-c8d6a99ed96b" + }], + "active": true, + "type": [{ + "coding": [{ + "system": "http://terminology.hl7.org/CodeSystem/organization-type", + "code": "team" + }] + }], + "name": "e2e-corporation", + "alias": ["e2e-misty"] + }], + "practitionerRoles": [{ + "resourceType": "PractitionerRole", + "id": "dea42f3c-7bc5-4a19-a398-d89f198558ad", + "meta": { + "versionId": "21", + "lastUpdated": "2023-10-13T09:59:24.990+00:00", + "source": "#0f75b3dadea9fe99" + }, + "identifier": [{ + "use": "official", + "value": "dea42f3c-7bc5-4a19-a398-d89f198558ad" + }, { + "use": "secondary", + "value": "9f72c646-dc1e-4f24-98df-6f04373b9ec6" + }], + "active": true, + "practitioner": { + "reference": "Practitioner/3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad", + "display": "test1147 1147" + }, + "organization": { + "reference": "Organization/0d7ae048-9b84-4f0c-ba37-8d6c0b97dc84", + "display": "e2e-corporation" + }, + "code": [{ + "coding": [{ + "system": "http://snomed.info/sct", + "code": "405623001", + "display": "Assigned practitioner" + }] + }] + }], + "groups": [{ + "resourceType": "Group", + "id": "848abc05-33ea-45f7-b909-b3ce465c67da", + "meta": { + "versionId": "3", + "lastUpdated": "2023-08-29T09:21:06.420+00:00", + "source": "#046812ad18d6a6c3" + }, + "identifier": [{ + "use": "official", + "value": "848abc05-33ea-45f7-b909-b3ce465c67da" + }, { + "use": "secondary", + "value": "9f72c646-dc1e-4f24-98df-6f04373b9ec6" + }], + "active": true, + "type": "practitioner", + "actual": true, + "code": { + "coding": [{ + "system": "http://snomed.info/sct", + "code": "405623001", + "display": "Assigned practitioner" + }] + }, + "name": "test1147 1147", + "member": [{ + "entity": { + "reference": "Practitioner/3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad" + } + }] + }], + "practitioner": [{ + "resourceType": "Practitioner", + "id": "3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad", + "meta": { + "versionId": "4", + "lastUpdated": "2023-08-29T09:21:05.863+00:00", + "source": "#8886a1e2a6059631" + }, + "identifier": [{ + "use": "official", + "value": "3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad" + }, { + "use": "secondary", + "value": "9f72c646-dc1e-4f24-98df-6f04373b9ec6" + }], + "active": true, + "name": [{ + "use": "official", + "family": "1147", + "given": ["test1147"] + }], + "telecom": [{ + "system": "email", + "value": "mejay2303@gmail.com" + }] + }] + } + } + }] +} as unknown as IBundle + +export const user1147 = { + "id": "9f72c646-dc1e-4f24-98df-6f04373b9ec6", + "createdTimestamp": 1675179889477, + "username": "1147", + "enabled": true, + "totp": false, + "emailVerified": false, + "firstName": "test1147", + "lastName": "1147", + "email": "mejay2303@gmail.com", + "attributes": { + "fhir_core_app_id": [ + "ecbis" + ] + }, + "disableableCredentialTypes": [], + "requiredActions": [], + "notBefore": 1681810919, + "access": { + "manageGroupMembership": true, + "view": true, + "mapRoles": true, + "impersonate": true, + "manage": true + } +} + +export const user1147Groups = [ + { + "id": "b68e2590-c2ee-4b3c-9184-c4b35a69a271", + "name": "SuperUser", + "path": "/SuperUser" + } +] \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx new file mode 100644 index 000000000..3f37bc73e --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx @@ -0,0 +1,21 @@ +import { PractitionerDetailsView } from '../practitionerDetails'; +import { render, screen } from '@testing-library/react'; +import { practitionerDetailsBundle } from './fixtures'; +import React from 'react'; +import { getResourcesFromBundle } from '@opensrp/react-utils'; +import { PractitionerDetail } from '../../types'; + +const practitionerDetails = + getResourcesFromBundle(practitionerDetailsBundle)[0].fhir; + +test('renders without crashing', () => { + const props = { + loading: false, + practitionerDetails, + }; + render(); + + const tableRows = document.querySelectorAll('table tr'); + const text = [...tableRows].map((tr) => tr.textContent); + expect(text).toEqual(['NameActiveCoding', 'test1147 1147Active']); +}); diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css index 8fd8d13a9..f1bc5f3a6 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css @@ -1,3 +1,24 @@ .details-full-view{ background: #f5f5f5; +} + +.details-full-view .site-page-header{ + padding: 24px; + background: #fff; +} + +.details-full-view .content-body{ + margin: 24px; + display: flex; + gap: 16px; + flex-direction: column; +} + +.details-full-view .content-box{ + background-color: #fff; +} + +.details-full-view .details-tab{ + background: #fff; + padding: 16px; } \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx index 70d501244..e7f99d2fa 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -1,157 +1,24 @@ import React from 'react'; -import { CloseOutlined } from '@ant-design/icons'; -import { Alert, Button, Col, Descriptions, Divider, Row, Table, Tabs, Tag } from 'antd'; -import { Tree, generateFhirLocationTree } from '@opensrp/fhir-location-management'; -import { Organization } from '@opensrp/team-management'; +import { Alert, Breadcrumb, Button, Descriptions, Divider, Popconfirm, Tabs, Tag } from 'antd'; import { Spin } from 'antd'; import { useTranslation } from '../../../mls' -import { useParams } from 'react-router'; -import { BrokenPage, FHIRServiceClass, Resource404, TableLayout, getResourcesFromBundle, parseFhirHumanName, useSimpleTabularView, useTabularViewWithLocalSearch } from '@opensrp/react-utils'; +import { useHistory, useParams } from 'react-router'; +import { BrokenPage, FHIRServiceClass, Resource404, getResourcesFromBundle } from '@opensrp/react-utils'; import { PageHeader } from '@ant-design/pro-layout'; -import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS, KeycloakUser, URL_USER, UserGroupDucks } from '@opensrp/user-management'; -import { KeycloakAPIService, KeycloakService } from '@opensrp/keycloak-service'; -import { useQuery } from 'react-query'; -import { groupResourceType, keycloakRoleMappingsEndpoint, practitionerResourceType } from '../../../constants'; -import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; -import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; -import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; -import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; -import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; -import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; -import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; +import { KEYCLOAK_URL_USERS, KeycloakUser, URL_USER, URL_USER_EDIT } from '@opensrp/user-management'; +import { KeycloakService } from '@opensrp/keycloak-service'; +import { useQuery, useQueryClient } from 'react-query'; import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; -import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; +import { KeycloakGroupDetails } from './ViewDetailResources/GroupDetailView'; +import { PractitionerDetailsView } from './ViewDetailResources/practitionerDetails'; +import { CareTeamDetailsView } from './ViewDetailResources/CareTeamDetails'; +import { OrganizationDetailsView } from './ViewDetailResources/OrganizationDetailsView'; +import { PractitionerDetail } from './types'; import { Link } from 'react-router-dom'; -import { StringLocale } from 'yup'; - - -/** New details view. -> - * - * Page Leader section - * - breadcrumbs - * user details show that as a description list. has attributes. - */ - - - -const useGetKeycloakUser = (userId: string) => { - // Get keycloakUser should contain details. roles. groups. attributes. -} - -const useGetKeycloakUserGroups = (userId: string) => { - // fetch groups assigned to user. -} - -const useGetKeycloakUserRoles = (userId: string) => { - // roles assigned to user by clients. -} - -interface KeycloakGroupDetailsProp { - keycloakBaseUrl: string; - resourceId: string; -} - -const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { - const { t } = useTranslation(); - const { keycloakBaseUrl, resourceId } = props; - - // TODO - useSimpleTabular view but for keycloak requests. - const { data, error, isLoading } = useQuery([], () => new KeycloakService( - `${KEYCLOAK_URL_USERS}/${resourceId}${KEYCLOAK_URL_USER_GROUPS}`, - keycloakBaseUrl - ).list()) - - if (error && !data) { - return ; - } - - // name, path, leave action. - const columns = [ - { - title: t('Name'), - dataIndex: 'name' as const, - }, - { - title: t('Path'), - dataIndex: 'path' as const, - }, - { - title: t(''), - dataIndex: 'id' as const, - render: (id: string) => , - }, - ]; - - const tableProps = { - datasource: data, - columns, - loading: isLoading, - // pagination: true, - }; - - return -} - -interface RoleMapping { - id: string; - name: string; - description: string; - composite: boolean; - clientRole: boolean; - containerId: string; -} -interface ClientRoleMapping { - id: string; - client: string; - mappings: RoleMapping[] -} -interface KeycloakUserRoleMappings { - realMappings?: RoleMapping[]; - clientMappings?: Record -} -const KeycloakRoleDetails = (props: KeycloakGroupDetailsProp) => { - const { t } = useTranslation(); - const { keycloakBaseUrl, resourceId } = props; - - // TODO - useSimpleTabular view but for keycloak requests. - const { data, error, isLoading } = useQuery([], () => new KeycloakService( - `${KEYCLOAK_URL_USERS}/${resourceId}/${keycloakRoleMappingsEndpoint}`, - keycloakBaseUrl - ).list()) - - if (error && !data) { - return ; - } - - // name, path, leave action. - const columns = [ - { - title: t('Name'), - dataIndex: 'name' as const, - }, - { - title: t('Path'), - dataIndex: 'path' as const, - }, - { - title: t(''), - dataIndex: 'id' as const, - render: (id: string) => , - }, - ]; - - const tableProps = { - datasource: data, - columns, - loading: isLoading, - // pagination: true, - }; - - return -} - -// - if user record not found then show 404 under page details. -// - if practitioner not found, load, keycloak related data and show that. disable +import { practitionerDetailsResourceType } from '../../../constants'; +import "./index.css" +import { sendErrorNotification } from '@opensrp/notifications'; +import { deleteUser } from '../ListView/utils'; // remove onclose from type and export the rest interface UserDetailProps { @@ -159,21 +26,35 @@ interface UserDetailProps { keycloakBaseURL: string; } -const routes = [{ - path: "admin/users", - breadcrumbName: 'Users', -}, -{ - path: "id", - breadcrumbName: 'View details', -}, -] +const breadCrumbItems = [{ + title: "Users", + path: URL_USER +}, { + title: "View details", +}] + + + +const breadCrumb = { + const last = items.indexOf(route) === items.length - 1; + return last ? {route.title} : {route.title}; + }} +> + + Users + + View details + export const UserDetails = (props: UserDetailProps) => { const { keycloakBaseURL: keycloakBaseUrl, fhirBaseURL: fhirBaseUrl } = props const params = useParams<{ id: string }>() const { id: resourceId } = params; const { t } = useTranslation(); + const queryClient = useQueryClient() + const history = useHistory() const { data: user, @@ -183,8 +64,6 @@ export const UserDetails = (props: UserDetailProps) => { new KeycloakService(`${KEYCLOAK_URL_USERS}`, keycloakBaseUrl).read(resourceId) ); - const practitionerDetailsResourceType = "practitioner-details" - const extraQueryFilters = { "keycloak-uuid": resourceId, } @@ -204,8 +83,8 @@ export const UserDetails = (props: UserDetailProps) => { return } - // TODO - how to provide this data. if (userError && !user) { + console.log({ userError }) return } @@ -228,68 +107,73 @@ export const UserDetails = (props: UserDetailProps) => {
history.back()} + onBack={() => history.goBack()} title="View details" - breadcrumb={{ routes }} + breadcrumb={breadCrumb} subTitle={user.username} - style={{ padding: "24px", background: "white" }} /> -
+
-
+
Enabled : Disabled} extra={[, - , - + , + , ]} > {Object.entries(userDetails).map(([key, value]) => { - return {value} + return {value} })} Attributes {attributesArray.length === 0 ? : - {attributesArray.map(([key, value]) => { - return {JSON.stringify(value)} + return {JSON.stringify(value)} })} }
-
+
}, - { label: "Roles", key: 'Roles', children:
}, + // { label: "Roles", key: 'Roles', children: }, { label: "Practitioners", key: 'Practitioners', children: }, { label: "CareTeams", key: 'CareTeams', children: }, { label: "Organizations", key: 'Organizations', children: }, - { label: "Locations", key: 'Locations', children: }, + // { label: "Locations", key: 'Locations', children: }, ] } /> @@ -300,296 +184,8 @@ export const UserDetails = (props: UserDetailProps) => { ); }; -interface PractitionerDetail extends Resource { - fhir: { - careteams?: ICareTeam[]; - teams?: IOrganization[]; - locationHierarchyList?: any[]; // TODO - import LocationHierarchy - practitionerRoles?: IPractitionerRole[]; - groups?: IGroup[]; - practitioner?: IPractitioner[] - } -} - - -const PractitionerDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { - const { t } = useTranslation(); - - - const practitioners = practitionerDetails.practitioner ?? []; - const practitionerRoles = practitionerDetails.practitionerRoles ?? []; - const tableData = processPractitionerDetails(practitioners, practitionerRoles) - - const columns = [ - { - title: t('Name'), - dataIndex: 'name', - }, - { - title: t('Active'), - dataIndex: 'active', - render: (value: boolean) => (value === true ? 'Active' : 'Inactive'), - }, - { - title: t('Coding'), - dataIndex: 'concepts', - render: (concepts: CodeableConcept[]) => concepts.map(concept => ), - }, - ]; - const tableProps = { - datasource: tableData, - columns, - loading, - // pagination: true, - }; - - return - -} - - -const CareTeamDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { - const { t } = useTranslation(); - - - const careTeams = practitionerDetails.careteams ?? []; - const tableData = careTeams.map(resource => { - const { id, status, name, } = resource - return { - id, status, name - } - }) - - - // identifier, status, - const columns = [ - { - title: t('Id'), - dataIndex: 'id' as const, - }, - { - title: t('Name'), - dataIndex: 'name' as const, - render: (name: string) => {name} - }, - { - title: t('status'), - dataIndex: 'status' as const, - render: (code: Coding) => , - }, - ]; - - const tableProps = { - datasource: tableData, - columns, - loading, - // pagination: true, - }; - - return - -} - - -const OrganizationDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { - const { t } = useTranslation(); - - - // get organization Affiliation - use it tag the codings for the organizations. - const organizations = practitionerDetails.teams ?? []; - const tableData = organizations.map(resource => { - const { id, active, type, name } = resource - return { - id, active, type: type ?? [], name - } - }) - - - // identifier, status, - const columns = [ - { - title: t('Id'), - dataIndex: 'id' as const, - }, - { - title: t('Name'), - dataIndex: 'name' as const, - render: (name: string) => {name} - }, - { - title: t('Active'), - dataIndex: 'active' as const, - render: (isActive: string) => isActive ? "Active" : "Inactive", - }, - { - title: t('Type'), - dataIndex: 'type' as const, - render: (concepts: CodeableConcept[]) => concepts.map(concept => ), - }, - ]; - - const tableProps = { - datasource: tableData, - columns, - loading, - // pagination: true, - }; - - return - -} - - -const LocationDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { - const { t } = useTranslation(); - - - // get organization Affiliation - use it tag the codings for the organizations. - const hierarchies = practitionerDetails.locationHierarchyList ?? []; - const treeData = hierarchies.map(rawTree => generateFhirLocationTree(rawTree)) - // const tableData = hierarchies.map(resource => { - // const tree = - // const {id, active, type,name } = resource - // return { - // id, active, type, name - // } - // }) - - - // identifier, status, - const columns = [ - { - title: t('Id'), - dataIndex: 'id' as const, - }, - { - title: t('Name'), - dataIndex: 'name' as const, - render: (name: string) => {name} - }, - { - title: t('Active'), - dataIndex: 'active' as const, - render: (isActive: string) => isActive ? "Active" : "Inactive", - }, - { - title: t('Type'), - dataIndex: 'type' as const, - render: (concepts: CodeableConcept[]) => concepts.map(concept => ), - }, - ]; - - // const tableProps = { - // datasource: tableData, - // columns, - // loading, - // // pagination: true, - // }; - - return <> - - - { - // dispatch(setSelectedNode(node)); - }} - /> - - {/* -
-
- {selectedNode ? selectedNode.model.node.name : t('Location Unit')} -
- -
- -
-
-
-
-
{ - setDetailId(row.id); - }} - /> - - */} - - - -} - - - -function processPractitionerDetails(practitioners: IPractitioner[], practitionerRoles: IPractitionerRole[]) { - const tableData: Record = {} - const tempPractitionerRoleCodings: Record = {}; - - for (const res of practitioners) { - const typedRes = res - const resName = typedRes.name?.[0] // TODO - use get official name - // add to store - tableData[`${practitionerResourceType}/${typedRes.id}`] = { name: parseFhirHumanName(resName), active: res.active, concepts: [] } - } - - for (const res of practitionerRoles) { - // practitionerRole resource - const typedRes = res as IPractitionerRole - const practitionerId = typedRes.practitioner?.reference as string - - // extract the coding - const concepts = (typedRes.code ?? []) - - // have we encountered a corresponding practitioner for this role - if (tableData[practitionerId] === undefined) { - tableData[practitionerId].codings.push(concepts) - } - else if (tempPractitionerRoleCodings[practitionerId] === undefined) { - tempPractitionerRoleCodings[practitionerId] = [] - } else { - tempPractitionerRoleCodings[practitionerId].push(concepts) - } - } - - for (const [key, value] of Object.entries(tempPractitionerRoleCodings)) { - // invariant: we should have encountered all possible practitioners whole practitioner Roles records are in tempPractitionerRoleCodings - tableData[key].coding = [...tableData[key].concepts, ...value] - } - return Object.values(tableData) -} - - -// Create a fhir sdk components package for this -export const FhirCodeableConcept = ({ concept }: { concept: CodeableConcept }) => { - // single concept can have text representation, - // Each concept can have several codings, each coding with its own display. - return codeableConcept - -} - - -export const FhirCoding = ({ coding }: { coding: Coding }) => { - return Coding -} diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx index cde967914..61ee2cb14 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx @@ -1,71 +1,246 @@ +import { UserDetails } from '../'; +import { + cleanup, + fireEvent, + prettyDOM, + render, + screen, + waitFor, + waitForElementToBeRemoved, +} from '@testing-library/react'; +import nock from 'nock'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { authenticateUser } from '@onaio/session-reducer'; +import { RoleContext } from '@opensrp/rbac'; +import { superUserRole } from '@opensrp/react-utils'; +import { store } from '@opensrp/store'; +import { + URL_USER, + UserList, + URL_USER_CREDENTIALS, + UserCredentials, + KEYCLOAK_URL_USERS, + KEYCLOAK_URL_USER_GROUPS, +} from '@opensrp/user-management'; import { mount } from 'enzyme'; import React from 'react'; -import { UserDetails } from '../'; -import { Organization } from '@opensrp/team-management'; -import { Practitioner, KeycloakUser } from '../../../ducks/user'; - -describe('components/TeamsDetail', () => { - const props = { - keycloakUser: { - id: 'c1d36d9a-b771-410b-959e-af2c04d132a2', - username: 'allay_allan', - } as KeycloakUser, - practitioner: { - identifier: 'f713620c-076e-407f-98fd-ae655b3dc5ba', - active: true, - } as Practitioner, - assignedTeams: [ +import { Provider } from 'react-redux'; +import { Switch, Route, Router } from 'react-router'; +import { createMemoryHistory } from 'history'; +import { + keycloakGroupEndpoint, + keycloakMembersEndpoint, + practitionerDetailsResourceType, + practitionerResourceType, +} from '../../../../constants'; +import { + practitionerDetailsBundle, + user1147, + user1147Groups, +} from '../ViewDetailResources/tests/fixtures'; +import * as notifications from '@opensrp/notifications'; + +jest.mock('@opensrp/notifications', () => ({ + __esModule: true, + ...Object.assign({}, jest.requireActual('@opensrp/notifications')), +})); + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fetch = require('node-fetch'); +global.fetch = fetch; + +jest.mock('fhirclient', () => { + return jest.requireActual('fhirclient/lib/entry/browser'); +}); + +nock.disableNetConnect(); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + cacheTime: 0, + }, + }, +}); + +const props = { + fhirBaseURL: 'http://test.server.org', + keycloakBaseURL: 'http://test-keycloak.server.org', +}; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const AppWrapper = (props: any) => { + return ( + + + + + {/* + {(routeProps) => } + */} + + {(routeProps) => } + + + + + + ); +}; + +beforeAll(() => { + store.dispatch( + authenticateUser( + true, { - identifier: 'b0d21cea-9e0f-4ff8-b3e3-808a2655b9c5', - active: true, - name: 'Goldsmith CHW', + email: 'bob@example.com', + name: 'Bobbie', + username: 'RobertBaratheon', }, { - identifier: '88e63364-53c1-436f-94b4-92d4bf7fc6c7', - active: true, - name: 'New team test', - }, - ] as Organization[], - }; - it('shows details section with data', () => { - const wrapper = mount(); + api_token: 'hunter2', + oAuth2Data: { + access_token: 'sometoken', + state: 'abcde', + }, + user_id: 'userFixtures[0].id', + } + ) + ); +}); - const keycloakUsername = wrapper.find('#username').text(); - expect(keycloakUsername).toEqual(props.keycloakUser.username); +afterEach(() => { + nock.cleanAll(); + cleanup(); +}); - const keycloakId = wrapper.find('#keycloakId').text(); - expect(keycloakId).toEqual(props.keycloakUser.id); +afterAll(() => { + nock.enableNetConnect(); +}); - const practitionerId = wrapper.find('#practitionerId').text(); - expect(practitionerId).toEqual(props.practitioner.identifier); +/*** + * End of setup. + */ +const userId = 'userId'; +test('Renders without crashing', async () => { + const history = createMemoryHistory(); + history.push(`${URL_USER}/${userId}`); - const practitionerStatus = wrapper.find('#practitionerStatus').text(); - expect(practitionerStatus).toMatchInlineSnapshot(`"active"`); + const successMock = jest + .spyOn(notifications, 'sendSuccessNotification') + .mockImplementation(() => { + return; + }); - const assignedTeams = wrapper.find('#assignedTeam').map((team) => team.text()); - expect(assignedTeams).toEqual(props.assignedTeams.map((team) => team.name)); - }); - it('shows no practitioner message when practitioner not available', () => { - const newProps = { ...props, practitioner: undefined }; - const wrapper = mount(); - expect(wrapper.find('#noActivePractitioner').text()).toMatchInlineSnapshot( - `"No Active Practitioner"` - ); - }); - it('shows no team available message when team not available', () => { - const newProps = { ...props, assignedTeams: [] }; - const wrapper = mount(); - expect(wrapper.find('#noAssignedTeams').text()).toMatchInlineSnapshot(`"No Assigned Teams"`); - }); - it('shows spinner', () => { - const newProps = { ...props, keycloakUser: undefined }; - const wrapper = mount(); - expect(wrapper.find('.ant-spin').exists()).toBeTruthy(); + nock(props.fhirBaseURL) + .get(`/${practitionerDetailsResourceType}/_search`) + .query({ 'keycloak-uuid': userId }) + .reply(200, practitionerDetailsBundle); + + nock(props.keycloakBaseURL).get(`${KEYCLOAK_URL_USERS}/${userId}`).reply(200, user1147); + + nock(props.keycloakBaseURL) + .get(`${KEYCLOAK_URL_USERS}/${userId}${KEYCLOAK_URL_USER_GROUPS}`) + .reply(200, user1147Groups); + + render( + + + + ); + + // this only await the first call to get the users. + await waitForElementToBeRemoved(document.querySelector('.ant-spin')); + + expect(screen.queryByTitle(/View details/i)).toBeInTheDocument(); + + // second page header details. + const userProfile = screen.getByTestId('user-profile'); + const textContent = userProfile.textContent; + expect(textContent).toEqual( + '1147EnabledDeleteEditID9f72c646-dc1e-4f24-98df-6f04373b9ec6First Nametest1147Last Name1147Username1147Emailmejay2303@gmail.comVerifiedFalseAttributesfhir_core_app_id["ecbis"]' + ); + + // have a look at the tabs + + // start with group + const groupTab = screen.getByText('Groups'); + await fireEvent.click(groupTab); + + await waitForElementToBeRemoved(document.querySelector('.ant-spin')); + + // check table has correct number of rows. and try removing user from one group + let detailsTabSection = document.querySelector('.details-tab'); + const groupsTable = detailsTabSection?.querySelector('table'); + + const tableData = [...(groupsTable?.querySelectorAll('tr') ?? [])].map((tr) => tr.textContent); + expect(tableData).toEqual(['NamePath', 'SuperUser/SuperUserLeave']); + + const leaveBtn = screen.getByText('Leave'); + expect(leaveBtn).toMatchInlineSnapshot(` + + Leave + + `); + + nock(props.keycloakBaseURL) + .delete(`${KEYCLOAK_URL_USERS}/${userId}${KEYCLOAK_URL_USER_GROUPS}/${user1147Groups[0].id}`) + .reply(200, user1147Groups); + + await fireEvent.click(leaveBtn); + + await waitFor(() => { + expect(successMock).toHaveBeenCalledWith('User was removed from the keycloak group'); }); - it('removes it self on close', () => { - const wrapper = mount( wrapper.unmount()} />); - expect(wrapper.children()).toHaveLength(1); - wrapper.find('.close-btn button').simulate('click'); - expect(wrapper).toHaveLength(0); + + // go to practitioners + const practTab = screen.getByText('Practitioners'); + await fireEvent.click(practTab); + + // Check that practitioner-details has finished loading. + await waitFor(() => { + expect(screen.getByText('3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad')).toBeInTheDocument(); }); + + // practitioner records + detailsTabSection = document.querySelector('div.ant-tabs-tabpane-active'); + const practitionerTable = detailsTabSection?.querySelector('table'); + + const practitionerData = [...(practitionerTable?.querySelectorAll('tr') ?? [])].map( + (tr) => tr.textContent + ); + expect(practitionerData).toEqual([ + 'IdNameActiveUser TypePractitioner Role Coding', + '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dadtest1147 1147ActivepractitionerAssigned practitioner(http://snomed.info/sct|405623001), ', + ]); + + // go to careTeams + const careTeamsTab = screen.getByText('CareTeams'); + await fireEvent.click(careTeamsTab); + + // practitioner records + detailsTabSection = document.querySelector('div.ant-tabs-tabpane-active'); + const careTeamsTable = detailsTabSection?.querySelector('table'); + + const careTeamsData = [...(careTeamsTable?.querySelectorAll('tr') ?? [])].map( + (tr) => tr.textContent + ); + expect(careTeamsData).toEqual(['IdNameStatusCategory', 'No data']); + + // go to organization + const organizationsTab = screen.getByText('Organizations'); + await fireEvent.click(organizationsTab); + + // practitioner records + detailsTabSection = document.querySelector('div.ant-tabs-tabpane-active'); + const organizationsTable = detailsTabSection?.querySelector('table'); + + const organizationsData = [...(organizationsTable?.querySelectorAll('tr') ?? [])].map( + (tr) => tr.textContent + ); + expect(organizationsData).toEqual([ + 'IdNameActiveType', + '0d7ae048-9b84-4f0c-ba37-8d6c0b97dc84e2e-corporationActive(http://terminology.hl7.org/CodeSystem/organization-type|team), ', + ]); }); diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts new file mode 100644 index 000000000..5c3517b8a --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts @@ -0,0 +1,49 @@ +import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; +import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; +import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; +import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; +import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; +import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; + + +export interface PractitionerDetail extends Resource { + fhir: { + careteams?: ICareTeam[]; + teams?: IOrganization[]; + locationHierarchyList?: any[]; // TODO - import LocationHierarchy + practitionerRoles?: IPractitionerRole[]; + groups?: IGroup[]; + practitioner?: IPractitioner[] + } +} + +export interface RoleMapping { + id: string; + name: string; + description: string; + composite: boolean; + clientRole: boolean; + containerId: string; +} + +export interface ClientRoleMapping { + id: string; + client: string; + mappings: RoleMapping[] +} + +export interface KeycloakUserRoleMappings { + realmMappings?: RoleMapping[]; + clientMappings?: Record +} + +export interface PractitionerDetail extends Resource { + fhir: { + careteams?: ICareTeam[]; + teams?: IOrganization[]; + locationHierarchyList?: any[]; // TODO - import LocationHierarchy + practitionerRoles?: IPractitionerRole[]; + groups?: IGroup[]; + practitioner?: IPractitioner[] + } +} \ No newline at end of file From 047e8ced84e70b4475dc4ed3361d1c2326062bdf Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 09:57:31 +0300 Subject: [PATCH 09/32] Move userDeleteWith popup confirm button to own component --- .../src/components/UserDeleteBtn/index.tsx | 42 +++++++++++++++++++ .../UserList/ListView/tableColumns.tsx | 26 +----------- .../UserList/UserDetails-v2/index.tsx | 27 +++--------- 3 files changed, 50 insertions(+), 45 deletions(-) create mode 100644 packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx diff --git a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx new file mode 100644 index 000000000..eb055a853 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx @@ -0,0 +1,42 @@ +import { Popconfirm, Button } from "antd"; +import { KEYCLOAK_URL_USERS } from "@opensrp/user-management"; +import { sendErrorNotification } from "@opensrp/notifications"; +import { deleteUser } from "../UserList/ListView/utils"; +import { useTranslation } from "../../mls"; +import { useQueryClient } from "react-query"; + +export interface UserDeleteBtnProp { + afterActions?: () => void, + keycloakBaseUrl: string, + fhirBaseUrl: string, + resourceId: string, +} + +export const UserDeleteBtn = (props: UserDeleteBtnProp) => { + const { afterActions, keycloakBaseUrl, fhirBaseUrl, resourceId } = props + const { t } = useTranslation() + const queryClient = useQueryClient() + + return { + await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t); + try { + return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]); + } catch { + return sendErrorNotification( + t('Failed to update data, please refresh the page to see the most recent changes') + ); + } finally { + afterActions?.() + } + }} + > + + +} \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx index 4374246c2..5c9ee06de 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx @@ -18,6 +18,7 @@ import { QueryClient } from 'react-query'; import type { TFunction } from '@opensrp/i18n'; import { RbacCheck, UserRole } from '@opensrp/rbac'; import { History } from 'history'; +import { UserDeleteBtn } from '../../UserDeleteBtn'; /** * Get table columns for user list @@ -75,30 +76,7 @@ export const getTableColumns = ( { key: '2', permissions: ['iam_user.delete'], - label: ( - { - await deleteUser(keycloakBaseUrl, baseUrl, record.id, t); - try { - return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]); - } catch { - return sendErrorNotification( - t('Failed to update data, please refresh the page to see the most recent changes') - ); - } - }} - > - {user_id && - (record.id === user_id ? null : ( - - ))} - - ), + label: , }, { key: '3', diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx index e7f99d2fa..00eee5580 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -19,6 +19,7 @@ import { practitionerDetailsResourceType } from '../../../constants'; import "./index.css" import { sendErrorNotification } from '@opensrp/notifications'; import { deleteUser } from '../ListView/utils'; +import { UserDeleteBtn } from '../../UserDeleteBtn'; // remove onclose from type and export the rest interface UserDetailProps { @@ -53,9 +54,12 @@ export const UserDetails = (props: UserDetailProps) => { const params = useParams<{ id: string }>() const { id: resourceId } = params; const { t } = useTranslation(); - const queryClient = useQueryClient() const history = useHistory() + const userDeleteAfterAction = () => { + history.push(URL_USER) + } + const { data: user, isLoading: userIsLoading, @@ -120,26 +124,7 @@ export const UserDetails = (props: UserDetailProps) => { title={user.username} subTitle={enabled ? Enabled : Disabled} extra={[, - { - await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t); - try { - return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]); - } catch { - return sendErrorNotification( - t('Failed to update data, please refresh the page to see the most recent changes') - ); - } - }} - > - - , + , , From 6f18b10b4105055694903d98d4d67110b55666e7 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 10:12:59 +0300 Subject: [PATCH 10/32] Add react import to user delete btn component file --- .../src/components/UserDeleteBtn/index.tsx | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx index eb055a853..19a5755a2 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx @@ -1,42 +1,45 @@ -import { Popconfirm, Button } from "antd"; -import { KEYCLOAK_URL_USERS } from "@opensrp/user-management"; -import { sendErrorNotification } from "@opensrp/notifications"; -import { deleteUser } from "../UserList/ListView/utils"; -import { useTranslation } from "../../mls"; -import { useQueryClient } from "react-query"; +import { Popconfirm, Button } from 'antd'; +import { KEYCLOAK_URL_USERS } from '@opensrp/user-management'; +import { sendErrorNotification } from '@opensrp/notifications'; +import { deleteUser } from '../UserList/ListView/utils'; +import { useTranslation } from '../../mls'; +import { useQueryClient } from 'react-query'; +import React from 'react'; export interface UserDeleteBtnProp { - afterActions?: () => void, - keycloakBaseUrl: string, - fhirBaseUrl: string, - resourceId: string, + afterActions?: () => void; + keycloakBaseUrl: string; + fhirBaseUrl: string; + resourceId: string; } export const UserDeleteBtn = (props: UserDeleteBtnProp) => { - const { afterActions, keycloakBaseUrl, fhirBaseUrl, resourceId } = props - const { t } = useTranslation() - const queryClient = useQueryClient() + const { afterActions, keycloakBaseUrl, fhirBaseUrl, resourceId } = props; + const { t } = useTranslation(); + const queryClient = useQueryClient(); - return { - await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t); - try { - return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]); - } catch { - return sendErrorNotification( - t('Failed to update data, please refresh the page to see the most recent changes') - ); - } finally { - afterActions?.() - } - }} + return ( + { + await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t); + try { + return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]); + } catch { + return sendErrorNotification( + t('Failed to update data, please refresh the page to see the most recent changes') + ); + } finally { + afterActions?.(); + } + }} > - + -} \ No newline at end of file + ); +}; From e54240b2b538a760c247ec97028c3860e477a3c1 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 10:14:11 +0300 Subject: [PATCH 11/32] Change leave text on group detail view to red --- .../UserDetails-v2/ViewDetailResources/GroupDetailView.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx index 91205900e..fc4438821 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx @@ -49,6 +49,7 @@ export const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { From 6f823ec940f3494cf2844a356468b6256f2a2238 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 10:14:28 +0300 Subject: [PATCH 12/32] Test edit btn works --- .../UserDetails-v2/tests/index.test.tsx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx index 61ee2cb14..999cb4114 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx @@ -244,3 +244,41 @@ test('Renders without crashing', async () => { '0d7ae048-9b84-4f0c-ba37-8d6c0b97dc84e2e-corporationActive(http://terminology.hl7.org/CodeSystem/organization-type|team), ', ]); }); + +test('Edit button works correctly', async () => { + const history = createMemoryHistory(); + history.push(`${URL_USER}/${userId}`); + + const successMock = jest + .spyOn(notifications, 'sendSuccessNotification') + .mockImplementation(() => { + return; + }); + + nock(props.fhirBaseURL) + .get(`/${practitionerDetailsResourceType}/_search`) + .query({ 'keycloak-uuid': userId }) + .reply(200, practitionerDetailsBundle); + + nock(props.keycloakBaseURL).get(`${KEYCLOAK_URL_USERS}/${userId}`).reply(200, user1147); + + nock(props.keycloakBaseURL) + .get(`${KEYCLOAK_URL_USERS}/${userId}${KEYCLOAK_URL_USER_GROUPS}`) + .reply(200, user1147Groups); + + render( + + + + ); + + // this only await the first call to get the users. + await waitForElementToBeRemoved(document.querySelector('.ant-spin')); + + expect(screen.queryByTitle(/View details/i)).toBeInTheDocument(); + + const editBtn = screen.getByRole('button', {name: "Edit"}) + fireEvent.click(editBtn); + + expect(history.location.pathname).toEqual("/admin/users/edit/userId") +}); \ No newline at end of file From d34f105653ac707bb93d187e0ce5ab42110fc74f Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 11:23:02 +0300 Subject: [PATCH 13/32] Fix roleDetail view and add tests --- .../ViewDetailResources/RoleDetailView.tsx | 31 ++++++--- .../ViewDetailResources/tests/fixtures.ts | 69 ++++++++++++++++++- .../UserList/UserDetails-v2/index.tsx | 5 +- .../UserDetails-v2/tests/index.test.tsx | 49 +++++++++++-- 4 files changed, 137 insertions(+), 17 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx index 5fb2b8c8e..737702a40 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Divider, Typography } from 'antd'; +import { Alert, Divider, Space, Typography } from 'antd'; import { useTranslation } from '../../../../mls'; import { BrokenPage, TableLayout } from '@opensrp/react-utils'; import { KEYCLOAK_URL_USERS } from '@opensrp/user-management'; @@ -27,7 +27,7 @@ export const KeycloakRoleDetails = (props: KeycloakRoleDetailsProps) => { ); if (error && !data) { - return ; + return {'Unable to fetch Roles assigned to the user'}; } const { realmMappings, clientMappings } = data ?? {}; @@ -58,13 +58,13 @@ export const KeycloakRoleDetails = (props: KeycloakRoleDetailsProps) => { { title: t('Description'), - dataIndex: 'id' as const, + dataIndex: 'description' as const, }, ]; console.log({ realmRoles, clientRoles }); const realmRolesTableProps = { - datasource: [], + datasource: realmRoles, columns: realmRoleTableColumn, loading: isLoading, size: 'small' as const, @@ -72,7 +72,7 @@ export const KeycloakRoleDetails = (props: KeycloakRoleDetailsProps) => { }; const clientRolesTableProps = { - datasource: [], + datasource: clientRoles, columns: clientRoleTableColumn, loading: isLoading, size: 'small' as const, @@ -81,11 +81,22 @@ export const KeycloakRoleDetails = (props: KeycloakRoleDetailsProps) => { return ( <> - Realm Roles - - - Client Roles - +
+
+ Realm Roles + +
+
+ Client Roles + +
+
); }; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts index f4260d24c..0f673c967 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts @@ -173,4 +173,71 @@ export const user1147Groups = [ "name": "SuperUser", "path": "/SuperUser" } -] \ No newline at end of file +] + +export const user1147Roles = { + "realmMappings": [ + { + "id": "2637bf64-5b3b-4e63-8849-d5b876c4e661", + "name": "POST_LOCATION", + "description": "", + "composite": false, + "clientRole": false, + "containerId": "a9103f9f-4759-47a3-9c22-c2398f9f5e21" + }, + { + "id": "2cf50188-55a6-4143-b293-f009bc25356f", + "name": "GET_LOCATION", + "description": "", + "composite": false, + "clientRole": false, + "containerId": "a9103f9f-4759-47a3-9c22-c2398f9f5e21" + }, + { + "id": "674c5a60-9ed6-478f-9836-290945b40def", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "a9103f9f-4759-47a3-9c22-c2398f9f5e21" + } + ], + "clientMappings": { + "realm-management": { + "id": "9d9637dc-d86c-4669-bc0d-6600c22090f7", + "client": "realm-management", + "mappings": [ + { + "id": "43218a35-baf4-45fd-b926-5923996e9eb7", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "9d9637dc-d86c-4669-bc0d-6600c22090f7" + }, + { + "id": "bcf4874f-760d-45d4-881e-eda109ecf033", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "9d9637dc-d86c-4669-bc0d-6600c22090f7" + } + ] + }, + "account": { + "id": "7b3904aa-2394-4940-8981-03262642f047", + "client": "account", + "mappings": [ + { + "id": "c0dcef4f-eb0d-49f0-9389-96dc4d7316a2", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "clientRole": true, + "containerId": "7b3904aa-2394-4940-8981-03262642f047" + } + ] + } + } +} \ No newline at end of file diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx index 00eee5580..da6df860f 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -20,6 +20,7 @@ import "./index.css" import { sendErrorNotification } from '@opensrp/notifications'; import { deleteUser } from '../ListView/utils'; import { UserDeleteBtn } from '../../UserDeleteBtn'; +import { KeycloakRoleDetails } from './ViewDetailResources/RoleDetailView'; // remove onclose from type and export the rest interface UserDetailProps { @@ -153,8 +154,8 @@ export const UserDetails = (props: UserDetailProps) => { size={"small"} items={ [ - { label: "Groups", key: 'Groups', children: }, - // { label: "Roles", key: 'Roles', children: }, + { label: "User groups", key: 'Groups', children: }, + { label: "User roles", key: 'Roles', children: }, { label: "Practitioners", key: 'Practitioners', children: }, { label: "CareTeams", key: 'CareTeams', children: }, { label: "Organizations", key: 'Organizations', children: }, diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx index 999cb4114..0ac85b9c4 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx @@ -30,6 +30,7 @@ import { createMemoryHistory } from 'history'; import { keycloakGroupEndpoint, keycloakMembersEndpoint, + keycloakRoleMappingsEndpoint, practitionerDetailsResourceType, practitionerResourceType, } from '../../../../constants'; @@ -37,6 +38,7 @@ import { practitionerDetailsBundle, user1147, user1147Groups, + user1147Roles, } from '../ViewDetailResources/tests/fixtures'; import * as notifications from '@opensrp/notifications'; @@ -165,7 +167,7 @@ test('Renders without crashing', async () => { // have a look at the tabs // start with group - const groupTab = screen.getByText('Groups'); + const groupTab = screen.getByText('User groups'); await fireEvent.click(groupTab); await waitForElementToBeRemoved(document.querySelector('.ant-spin')); @@ -215,6 +217,45 @@ test('Renders without crashing', async () => { '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dadtest1147 1147ActivepractitionerAssigned practitioner(http://snomed.info/sct|405623001), ', ]); + // go to roles + nock(props.keycloakBaseURL) + .get(`${KEYCLOAK_URL_USERS}/${userId}/${keycloakRoleMappingsEndpoint}`) + .reply(200, user1147Roles); + + const rolesTab = screen.getByText('User roles'); + await fireEvent.click(rolesTab); + + console.log(prettyDOM(document)); + + // Check that practitioner-details has finished loading. + await waitFor(() => { + expect(screen.getByText('GET_LOCATION')).toBeInTheDocument(); + }); + + // practitioner records + detailsTabSection = document.querySelector('div.ant-tabs-tabpane-active'); + const realmRolesTable = detailsTabSection?.querySelectorAll('table')[0]; + const clientRolesTable = detailsTabSection?.querySelectorAll('table')[1]; + const realmRolesData = [...(realmRolesTable?.querySelectorAll('tr') ?? [])].map( + (tr) => tr.textContent + ); + const clientRolesData = [...(clientRolesTable?.querySelectorAll('tr') ?? [])].map( + (tr) => tr.textContent + ); + expect(realmRolesData).toEqual([ + 'NameDescription', + 'POST_LOCATION', + 'GET_LOCATION', + 'offline_access${role_offline-access}', + ]); + + expect(clientRolesData).toEqual([ + 'ClientNameDescription', + 'realm-managementmanage-realm${role_manage-realm}', + 'realm-managementmanage-users${role_manage-users}', + 'accountmanage-account${role_manage-account}', + ]); + // go to careTeams const careTeamsTab = screen.getByText('CareTeams'); await fireEvent.click(careTeamsTab); @@ -277,8 +318,8 @@ test('Edit button works correctly', async () => { expect(screen.queryByTitle(/View details/i)).toBeInTheDocument(); - const editBtn = screen.getByRole('button', {name: "Edit"}) + const editBtn = screen.getByRole('button', { name: 'Edit' }); fireEvent.click(editBtn); - expect(history.location.pathname).toEqual("/admin/users/edit/userId") -}); \ No newline at end of file + expect(history.location.pathname).toEqual('/admin/users/edit/userId'); +}); From 4b773f5c5d7fb187c560e9f31ff7f94f8bc478d7 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 11:31:33 +0300 Subject: [PATCH 14/32] Temporarily remove location details view --- .../ViewDetailResources/locationDetails.tsx | 136 ------------------ .../UserList/UserDetails-v2/index.tsx | 1 - 2 files changed, 137 deletions(-) delete mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx deleted file mode 100644 index 94cfe5699..000000000 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/locationDetails.tsx +++ /dev/null @@ -1,136 +0,0 @@ -// import React from 'react'; -// import { CloseOutlined } from '@ant-design/icons'; -// import { Alert, Button, Col, Descriptions, Divider, Row, Table, Tabs, Tag, Typography } from 'antd'; -// import { Tree, generateFhirLocationTree } from '@opensrp/fhir-location-management'; -// import { Organization } from '@opensrp/team-management'; -// import { Spin } from 'antd'; -// import { useTranslation } from '../../../mls' -// import { useParams } from 'react-router'; -// import { BrokenPage, FHIRServiceClass, Resource404, TableLayout, getResourcesFromBundle, parseFhirHumanName, useSimpleTabularView, useTabularViewWithLocalSearch } from '@opensrp/react-utils'; -// import { PageHeader } from '@ant-design/pro-layout'; -// import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS, KeycloakUser, URL_USER, UserGroupDucks } from '@opensrp/user-management'; -// import { KeycloakAPIService, KeycloakService } from '@opensrp/keycloak-service'; -// import { useQuery } from 'react-query'; -// import { groupResourceType, keycloakRoleMappingsEndpoint, practitionerResourceType } from '../../../constants'; -// import { IPractitionerRole } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitionerRole'; -// import { IPractitioner } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IPractitioner'; -// import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; -// import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; -// import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; -// import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; -// import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; -// import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; -// import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; -// import { Link } from 'react-router-dom'; -// import { PractitionerDetail } from './types'; -// import { useSelector } from 'react-redux'; - -// export const LocationDetailsView = ({ loading, practitionerDetails }: { loading: boolean, practitionerDetails: PractitionerDetail['fhir'] }) => { -// const { t } = useTranslation(); -// const selectedNode = useSelector((state) => getSelectedNode(state)); - -// // get organization Affiliation - use it tag the codings for the organizations. -// const hierarchies = practitionerDetails.locationHierarchyList ?? []; -// const treeData = hierarchies.map(rawTree => generateFhirLocationTree(rawTree)) -// // const tableData = hierarchies.map(resource => { -// // const tree = -// // const {id, active, type,name } = resource -// // return { -// // id, active, type, name -// // } -// // }) - -// // generate table data; consider if there is a selected node, sorting the data -// const toDispNodes = -// (selectedNode ? (selectedNode.children as TreeNode[]) : treeData.children) ?? []; -// const sortedNodes = [...toDispNodes].sort((a, b) => -// a.model.node.name.localeCompare(b.model.node.name) -// ); -// let tableNodes = sortedNodes; -// // if a node is selected only its children should be selected, the selected node should come first anyway. -// if (selectedNode) { -// tableNodes = [selectedNode, ...sortedNodes]; -// } -// const tableDispData = parseTableData(tableNodes); - -// // identifier, status, -// const columns = [ -// { -// title: t('Id'), -// dataIndex: 'id' as const, -// }, -// { -// title: t('Name'), -// dataIndex: 'name' as const, -// render: (name: string) => {name} -// }, -// { -// title: t('Active'), -// dataIndex: 'active' as const, -// render: (isActive: string) => isActive ? "Active" : "Inactive", -// }, -// { -// title: t('Type'), -// dataIndex: 'type' as const, -// render: (concepts: CodeableConcept[]) => concepts.map(concept => ), -// }, -// ]; - -// // const tableProps = { -// // datasource: tableData, -// // columns, -// // loading, -// // // pagination: true, -// // }; - -// return <> -// -//
-// { -// // dispatch(setSelectedNode(node)); -// }} -// /> -// -// {/* -//
-//
-// {selectedNode ? selectedNode.model.node.name : t('Location Unit')} -//
-// -//
-// -//
-//
-//
-//
-//
{ -// setDetailId(row.id); -// }} -// /> -// -// */} -// -// - -// } - -export {}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx index da6df860f..bc8abfcda 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -159,7 +159,6 @@ export const UserDetails = (props: UserDetailProps) => { { label: "Practitioners", key: 'Practitioners', children: }, { label: "CareTeams", key: 'CareTeams', children: }, { label: "Organizations", key: 'Organizations', children: }, - // { label: "Locations", key: 'Locations', children: }, ] } /> From 00bb9ab4d3e5225826f408a2b21c09ddc05a3a4c Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 12:27:15 +0300 Subject: [PATCH 15/32] Make description list responsive --- .../src/components/UserList/UserDetails-v2/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx index bc8abfcda..d0966d996 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx @@ -131,7 +131,7 @@ export const UserDetails = (props: UserDetailProps) => { , ]} > - + {Object.entries(userDetails).map(([key, value]) => { return {value} })} @@ -140,7 +140,7 @@ export const UserDetails = (props: UserDetailProps) => { Attributes {attributesArray.length === 0 ? : - + {attributesArray.map(([key, value]) => { return {JSON.stringify(value)} })} From 3e2b3aee2431dee63f5af2d815113a2410b685dd Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Wed, 8 Nov 2023 17:33:36 +0300 Subject: [PATCH 16/32] Replace previous view details components --- .../components/UserList/ListView/index.tsx | 25 +- .../UserList/ListView/tableColumns.tsx | 36 +-- .../tests/__snapshots__/index.test.tsx.snap | 8 + .../UserList/ListView/tests/index.test.tsx | 110 +++---- .../ViewDetailResources/tests/fixtures.ts | 243 --------------- .../UserList/UserDetails-v2/index.css | 24 -- .../UserList/UserDetails-v2/index.tsx | 180 ----------- .../components/UserList/ViewDetails/index.tsx | 257 ---------------- .../UserList/ViewDetails/tests/index.test.tsx | 115 ------- .../ViewDetailResources/CareTeamDetails.tsx | 9 +- .../ViewDetailResources/GroupDetailView.tsx | 9 +- .../OrganizationDetailsView.tsx | 6 +- .../PractitionerDetails.tsx} | 19 +- .../ViewDetailResources/RoleDetailView.tsx | 5 +- .../ViewDetailResources/tests/fixtures.ts | 282 ++++++++++++++++++ .../tests/practitionerDetails.test.tsx | 4 +- .../components/UserList/Viewdetails/index.css | 24 ++ .../components/UserList/Viewdetails/index.tsx | 253 ++++++++++++++++ .../tests/index.test.tsx | 41 +-- .../{UserDetails-v2 => Viewdetails}/types.ts | 20 +- .../src/index.tsx | 4 +- 21 files changed, 681 insertions(+), 993 deletions(-) delete mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/fixtures.ts delete mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.css delete mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/index.tsx delete mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/index.tsx delete mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/tests/index.test.tsx rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/ViewDetailResources/CareTeamDetails.tsx (86%) rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/ViewDetailResources/GroupDetailView.tsx (89%) rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/ViewDetailResources/OrganizationDetailsView.tsx (89%) rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2/ViewDetailResources/practitionerDetails.tsx => Viewdetails/ViewDetailResources/PractitionerDetails.tsx} (89%) rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/ViewDetailResources/RoleDetailView.tsx (94%) create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/fixtures.ts rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/ViewDetailResources/tests/practitionerDetails.test.tsx (84%) create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.css create mode 100644 packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/tests/index.test.tsx (91%) rename packages/fhir-keycloak-user-management/src/components/UserList/{UserDetails-v2 => Viewdetails}/types.ts (77%) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/index.tsx index e9aee11be..da7450196 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/index.tsx @@ -6,8 +6,6 @@ import { TableLayout, BrokenPage, searchQuery, - useSearchParams, - viewDetailsQuery, } from '@opensrp/react-utils'; import { PlusOutlined } from '@ant-design/icons'; import { URL_USER_CREATE, KEYCLOAK_URL_USERS } from '@opensrp/user-management'; @@ -20,8 +18,6 @@ import { PageHeader } from '@opensrp/react-utils'; import { getExtraData } from '@onaio/session-reducer'; import { KeycloakUser } from '@opensrp/user-management'; import { useSelector } from 'react-redux'; -import { ViewDetailsWrapper } from '../ViewDetails'; -import { useQueryClient } from 'react-query'; import { useTranslation } from '@opensrp/i18n'; import { RbacCheck, useUserRole } from '@opensrp/rbac'; @@ -41,13 +37,9 @@ export const UserList = (props: OrganizationListProps) => { const history = useHistory(); const match = useRouteMatch(); const extraData = useSelector(getExtraData); - const queryClient = useQueryClient(); const { t } = useTranslation(); const userRole = useUserRole(); - const { sParams, addParam } = useSearchParams(); - const resourceId = sParams.get(viewDetailsQuery) ?? undefined; - const { isLoading, data, error, isFetching } = useQuery([KEYCLOAK_URL_USERS], () => loadKeycloakResources(keycloakBaseURL, KEYCLOAK_URL_USERS) ); @@ -77,17 +69,7 @@ export const UserList = (props: OrganizationListProps) => { }; }); - const onViewDetails = (resourceId: string) => addParam(viewDetailsQuery, resourceId); - const columns = getTableColumns( - keycloakBaseURL, - fhirBaseURL, - extraData, - queryClient, - t, - onViewDetails, - userRole, - history - ); + const columns = getTableColumns(keycloakBaseURL, fhirBaseURL, extraData, t, userRole, history); const searchFormProps = { defaultValue: getQueryParams(location)[searchQuery], @@ -134,11 +116,6 @@ export const UserList = (props: OrganizationListProps) => { - ); diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx index 5c9ee06de..057fd0cfa 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tableColumns.tsx @@ -1,20 +1,16 @@ import * as React from 'react'; -import { Popconfirm, Divider, Dropdown, Button } from 'antd'; +import { Divider, Dropdown, Button } from 'antd'; import type { MenuProps } from 'antd'; import { MoreOutlined } from '@ant-design/icons'; -import { deleteUser } from './utils'; import { Link } from 'react-router-dom'; import { KeycloakUser, URL_USER_EDIT, - KEYCLOAK_URL_USERS, URL_USER_CREDENTIALS, URL_USER, } from '@opensrp/user-management'; import { Dictionary } from '@onaio/utils'; import { Column } from '@opensrp/react-utils'; -import { sendErrorNotification } from '@opensrp/notifications'; -import { QueryClient } from 'react-query'; import type { TFunction } from '@opensrp/i18n'; import { RbacCheck, UserRole } from '@opensrp/rbac'; import { History } from 'history'; @@ -26,9 +22,7 @@ import { UserDeleteBtn } from '../../UserDeleteBtn'; * @param keycloakBaseUrl - keycloak base url * @param baseUrl - server base url * @param extraData - session data about logged in user - * @param queryClient - react query client * @param t - translator function - * @param onViewDetails - callback when view details is clicked. * @param userRole - role of logged in user. * @param history - history object for managing navigation */ @@ -36,9 +30,7 @@ export const getTableColumns = ( keycloakBaseUrl: string, baseUrl: string, extraData: Dictionary, - queryClient: QueryClient, t: TFunction, - onViewDetails: (recordId: string) => void, userRole: UserRole, history: History ): Column[] => { @@ -63,21 +55,16 @@ export const getTableColumns = ( }); const getItems = (record: KeycloakUser): MenuProps['items'] => { - return [ + const items = [ { key: '1', permissions: [], label: ( - ), }, - { - key: '2', - permissions: ['iam_user.delete'], - label: , - }, { key: '3', permissions: ['iam_user.update'], @@ -93,7 +80,22 @@ export const getTableColumns = ( ), }, - ] + ]; + // don't show delete for the current logged in user - back compatibility reasons. + if (user_id && record.id !== user_id) { + items.push({ + key: '2', + permissions: ['iam_user.delete'], + label: ( + + ), + }); + } + return items .filter((item) => userRole.hasPermissions(item.permissions)) .map((item) => { const { permissions, ...rest } = item; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap index e94011a45..fc545d65f 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap @@ -1,5 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`View details navigates correctly: Header title 1`] = ` + +`; + exports[`renders correctly when listing resources: Header title 1`] = ` -
-
- - - ); -}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/tests/index.test.tsx deleted file mode 100644 index bbcc9d533..000000000 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ViewDetails/tests/index.test.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { ViewDetails } from '..'; -import React from 'react'; -import { store } from '@opensrp/store'; -import { Provider } from 'react-redux'; -import { authenticateUser } from '@onaio/session-reducer'; -import { QueryClient, QueryClientProvider } from 'react-query'; -import nock from 'nock'; -import { waitForElementToBeRemoved } from '@testing-library/dom'; -import { cleanup, render, screen, waitFor } from '@testing-library/react'; -import fetch from 'jest-fetch-mock'; -import { KeycloakUser } from '@opensrp/user-management'; -import { practitionerRoleResourceType } from '../../../../constants'; -import { practitionerRoleBundle } from '../../../CreateEditUser/tests/fixtures'; - -jest.mock('fhirclient', () => { - return jest.requireActual('fhirclient/lib/entry/browser'); -}); - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - cacheTime: 0, - staleTime: 0, - }, - }, -}); - -const props = { - fhirBaseUrl: 'http://test.server.org', - keycloakBaseUrl: 'http://test-keycloak.server.org', - resourceId: 405, -}; - -const keycloakUser = { - id: 'c1d36d9a-b771-410b-959e-af2c04d132a2', - username: 'allay_allan', -} as KeycloakUser; - -beforeAll(() => { - nock.disableNetConnect(); - store.dispatch( - authenticateUser( - true, - { - email: 'bob@example.com', - name: 'Bobbie', - username: 'RobertBaratheon', - }, - { api_token: 'hunter2', oAuth2Data: { access_token: 'sometoken', state: 'abcde' } } - ) - ); -}); - -afterEach(() => { - nock.cleanAll(); - cleanup(); -}); - -afterAll(() => { - nock.enableNetConnect(); -}); - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const AppWrapper = (props: any) => { - return ( - - - - - - ); -}; - -test('responds as expected to errors', async () => { - const errorMessage = 'coughid'; - fetch.mockReject(new Error(errorMessage)); - render(); - - await waitForElementToBeRemoved(document.querySelector('.ant-spin')); - - expect(fetch.mock.calls.map((x) => x[0])).toEqual([ - 'http://test-keycloak.server.org/users/405', - 'http://test-keycloak.server.org/users/405/groups', - ]); - - expect(screen.getByText(/coughid/)).toBeInTheDocument(); - - expect(nock.isDone()).toBeTruthy(); -}); - -test('shows user type in details', async () => { - fetch.mockOnce(JSON.stringify(keycloakUser)); - fetch.mockResponseOnce(JSON.stringify([])); - - nock(props.fhirBaseUrl) - .get(`/${practitionerRoleResourceType}/_search`) - .query({ - identifier: props.resourceId, - }) - .reply(200, practitionerRoleBundle); - - const { getByText, getByTestId } = render(); - - expect(getByTestId('custom-spinner')).toBeInTheDocument(); - - await waitFor(() => { - expect(getByText(/Fetching linked practitionerRole/)).toBeInTheDocument(); - }); - - await waitFor(() => { - expect(getByText(/User Type/)).toBeInTheDocument(); - expect(getByTestId('user-type')).toBeInTheDocument(); - }); -}); diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx similarity index 86% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx index 78ce3d53a..59ecac4dd 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/CareTeamDetails.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx @@ -1,8 +1,6 @@ import React from 'react'; import { useTranslation } from '../../../../mls'; import { TableLayout, CodeableConcept as CodeableConceptJsx } from '@opensrp/react-utils'; -import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; -import { Link } from 'react-router-dom'; import { PractitionerDetail } from '../types'; import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; import { Alert } from 'antd'; @@ -10,7 +8,7 @@ import { Alert } from 'antd'; export interface CareTeamDetailsViewProps { loading: boolean; practitionerDetails: PractitionerDetail['fhir']; - error?: Error; + error: Error | null; } export const CareTeamDetailsView = ({ @@ -56,8 +54,9 @@ export const CareTeamDetailsView = ({ title: t('Category'), dataIndex: 'category' as const, render: (concepts: CodeableConcept[]) => { - console.log({ concepts }); - return concepts.map((concept) => ); + return concepts.map((concept, index) => ( + + )); }, }, ]; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx similarity index 89% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx index fc4438821..2c32414e2 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/GroupDetailView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx @@ -1,12 +1,11 @@ import React from 'react'; import { Alert, Button } from 'antd'; import { useTranslation } from '../../../../mls'; -import { BrokenPage, TableLayout } from '@opensrp/react-utils'; +import { TableLayout } from '@opensrp/react-utils'; import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS } from '@opensrp/user-management'; import { KeycloakService } from '@opensrp/keycloak-service'; import { QueryClient, useQueryClient, useQuery } from 'react-query'; -import { keycloakGroupEndpoint, keycloakMembersEndpoint } from '../../../../constants'; -import { sendSuccessNotification } from '@opensrp/notifications'; +import { sendInfoNotification, sendSuccessNotification } from '@opensrp/notifications'; export interface KeycloakGroupDetailsProp { keycloakBaseUrl: string; @@ -76,7 +75,9 @@ export const removeGroupFromUser = async ( const endpoint = `${KEYCLOAK_URL_USERS}/${userId}${KEYCLOAK_URL_USER_GROUPS}/${groupId}`; const server = new KeycloakService(endpoint, baseUrl); return server.delete().then(() => { - query.refetchQueries([KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS]); + query.refetchQueries([KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS]).catch(() => { + sendInfoNotification('Failed to refresh data, please refresh the page'); + }); sendSuccessNotification('User was removed from the keycloak group'); }); }; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/OrganizationDetailsView.tsx similarity index 89% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/OrganizationDetailsView.tsx index 688bdaf9d..faeacd50b 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/OrganizationDetailsView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/OrganizationDetailsView.tsx @@ -1,8 +1,6 @@ import React from 'react'; import { useTranslation } from '../../../../mls'; import { TableLayout, CodeableConcept as CodeableConceptJsx } from '@opensrp/react-utils'; -import { Coding } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/coding'; -import { Link } from 'react-router-dom'; import { PractitionerDetail } from '../types'; import { CodeableConcept } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/codeableConcept'; import { Alert } from 'antd'; @@ -10,7 +8,7 @@ import { Alert } from 'antd'; export interface OrganizationDetailsViewProp { loading: boolean; practitionerDetails: PractitionerDetail['fhir']; - error?: Error; + error: Error | null; } export const OrganizationDetailsView = ({ @@ -59,7 +57,7 @@ export const OrganizationDetailsView = ({ title: t('Type'), dataIndex: 'type' as const, render: (concepts: CodeableConcept[]) => - concepts.map((concept) => ), + concepts.map((concept, index) => ), }, ]; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx similarity index 89% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx index 3ac5b0718..3066dfc8a 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/practitionerDetails.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx @@ -3,7 +3,6 @@ import { useTranslation } from '../../../../mls'; import { TableLayout, parseFhirHumanName, - Coding as CodingJsx, CodeableConcept as CodeableConceptJsx, } from '@opensrp/react-utils'; import { practitionerResourceType } from '../../../../constants'; @@ -15,7 +14,6 @@ import { Alert } from 'antd'; import { PRACTITIONER_USER_TYPE_CODE, SUPERVISOR_USER_TYPE_CODE, - UserListTypes, getUserType, getUserTypeCode, } from '@opensrp/user-management'; @@ -23,7 +21,7 @@ import { export interface PractitionerDetailViewsProps { loading: boolean; practitionerDetails: PractitionerDetail['fhir']; - error?: Error; + error: Error | null; } export const PractitionerDetailsView = (props: PractitionerDetailViewsProps) => { @@ -64,8 +62,9 @@ export const PractitionerDetailsView = (props: PractitionerDetailViewsProps) => title: t('Practitioner Role Coding'), dataIndex: 'concepts' as const, render: (concepts: CodeableConcept[]) => { - console.log({ concepts }); - return concepts.map((concept) => ); + return concepts.map((concept, index) => ( + + )); }, }, ]; @@ -81,8 +80,11 @@ export const PractitionerDetailsView = (props: PractitionerDetailViewsProps) => }; /** - * @param practitioners - * @param practitionerRoles + * Get practitioner table data source form practitioners and practitioner roles + * returned in the practitioner details endpoint + * + * @param practitioners - practitioner resources + * @param practitionerRoles - practitioner role resources */ function processPractitionerDetails( practitioners: IPractitioner[], @@ -130,9 +132,11 @@ function processPractitionerDetails( } // have we encountered a corresponding practitioner for this role + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (tableData[practitionerId] === undefined) { tableData[practitionerId].concepts = [...tableData[practitionerId].concepts, ...concepts]; tableData[practitionerId].userType = userType; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (tempPractitionerRoleCodings[practitionerId] === undefined) { tempPractitionerRoleCodings[practitionerId] = { concepts: [] }; tempPractitionerRoleCodings[practitionerId].concepts = []; @@ -150,6 +154,5 @@ function processPractitionerDetails( tableData[key].userType = value.userType; } - console.log({ tableData, practitionerRoles }); return Object.values(tableData); } diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/RoleDetailView.tsx similarity index 94% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/RoleDetailView.tsx index 737702a40..8a030d364 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/RoleDetailView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/RoleDetailView.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { Alert, Divider, Space, Typography } from 'antd'; +import { Alert, Typography } from 'antd'; import { useTranslation } from '../../../../mls'; -import { BrokenPage, TableLayout } from '@opensrp/react-utils'; +import { TableLayout } from '@opensrp/react-utils'; import { KEYCLOAK_URL_USERS } from '@opensrp/user-management'; import { KeycloakService } from '@opensrp/keycloak-service'; import { useQuery } from 'react-query'; @@ -62,7 +62,6 @@ export const KeycloakRoleDetails = (props: KeycloakRoleDetailsProps) => { }, ]; - console.log({ realmRoles, clientRoles }); const realmRolesTableProps = { datasource: realmRoles, columns: realmRoleTableColumn, diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/fixtures.ts b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/fixtures.ts new file mode 100644 index 000000000..1d543f159 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/fixtures.ts @@ -0,0 +1,282 @@ +import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; + +/* eslint-disable no-template-curly-in-string */ + +export const practitionerDetailsBundle = { + resourceType: 'Bundle', + id: '993066c6-1fda-4f91-a30f-96ab57573e87', + meta: { + lastUpdated: '2023-10-30T14:30:12.383+00:00', + }, + type: 'searchset', + total: 1, + link: [ + { + relation: 'self', + url: 'https://fhir.labs.smartregister.org/fhir/practitioner-details/_search?keycloak-uuid=9f72c646-dc1e-4f24-98df-6f04373b9ec6', + }, + ], + entry: [ + { + fullUrl: + 'https://fhir.labs.smartregister.org/fhir/practitioner-details/3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + resource: { + resourceType: 'practitioner-details', + id: '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + meta: { + profile: ['http://hl7.org/fhir/profiles/custom-resource'], + }, + fhir: { + id: '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + teams: [ + { + resourceType: 'Organization', + id: '0d7ae048-9b84-4f0c-ba37-8d6c0b97dc84', + meta: { + versionId: '1', + lastUpdated: '2023-10-13T09:59:23.717+00:00', + source: '#b7fa59921069f37e', + }, + identifier: [ + { + use: 'official', + value: '7712bfdd-83e3-43cf-b5f0-c8d6a99ed96b', + }, + ], + active: true, + type: [ + { + coding: [ + { + system: 'http://terminology.hl7.org/CodeSystem/organization-type', + code: 'team', + }, + ], + }, + ], + name: 'e2e-corporation', + alias: ['e2e-misty'], + }, + ], + practitionerRoles: [ + { + resourceType: 'PractitionerRole', + id: 'dea42f3c-7bc5-4a19-a398-d89f198558ad', + meta: { + versionId: '21', + lastUpdated: '2023-10-13T09:59:24.990+00:00', + source: '#0f75b3dadea9fe99', + }, + identifier: [ + { + use: 'official', + value: 'dea42f3c-7bc5-4a19-a398-d89f198558ad', + }, + { + use: 'secondary', + value: '9f72c646-dc1e-4f24-98df-6f04373b9ec6', + }, + ], + active: true, + practitioner: { + reference: 'Practitioner/3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + display: 'test1147 1147', + }, + organization: { + reference: 'Organization/0d7ae048-9b84-4f0c-ba37-8d6c0b97dc84', + display: 'e2e-corporation', + }, + code: [ + { + coding: [ + { + system: 'http://snomed.info/sct', + code: '405623001', + display: 'Assigned practitioner', + }, + ], + }, + ], + }, + ], + groups: [ + { + resourceType: 'Group', + id: '848abc05-33ea-45f7-b909-b3ce465c67da', + meta: { + versionId: '3', + lastUpdated: '2023-08-29T09:21:06.420+00:00', + source: '#046812ad18d6a6c3', + }, + identifier: [ + { + use: 'official', + value: '848abc05-33ea-45f7-b909-b3ce465c67da', + }, + { + use: 'secondary', + value: '9f72c646-dc1e-4f24-98df-6f04373b9ec6', + }, + ], + active: true, + type: 'practitioner', + actual: true, + code: { + coding: [ + { + system: 'http://snomed.info/sct', + code: '405623001', + display: 'Assigned practitioner', + }, + ], + }, + name: 'test1147 1147', + member: [ + { + entity: { + reference: 'Practitioner/3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + }, + }, + ], + }, + ], + practitioner: [ + { + resourceType: 'Practitioner', + id: '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + meta: { + versionId: '4', + lastUpdated: '2023-08-29T09:21:05.863+00:00', + source: '#8886a1e2a6059631', + }, + identifier: [ + { + use: 'official', + value: '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dad', + }, + { + use: 'secondary', + value: '9f72c646-dc1e-4f24-98df-6f04373b9ec6', + }, + ], + active: true, + name: [ + { + use: 'official', + family: '1147', + given: ['test1147'], + }, + ], + telecom: [ + { + system: 'email', + value: 'mejay2303@gmail.com', + }, + ], + }, + ], + }, + }, + }, + ], +} as unknown as IBundle; + +export const user1147 = { + id: '9f72c646-dc1e-4f24-98df-6f04373b9ec6', + createdTimestamp: 1675179889477, + username: '1147', + enabled: true, + totp: false, + emailVerified: false, + firstName: 'test1147', + lastName: '1147', + email: 'mejay2303@gmail.com', + attributes: { + fhir_core_app_id: ['ecbis'], + }, + disableableCredentialTypes: [], + requiredActions: [], + notBefore: 1681810919, + access: { + manageGroupMembership: true, + view: true, + mapRoles: true, + impersonate: true, + manage: true, + }, +}; + +export const user1147Groups = [ + { + id: 'b68e2590-c2ee-4b3c-9184-c4b35a69a271', + name: 'SuperUser', + path: '/SuperUser', + }, +]; + +export const user1147Roles = { + realmMappings: [ + { + id: '2637bf64-5b3b-4e63-8849-d5b876c4e661', + name: 'POST_LOCATION', + description: '', + composite: false, + clientRole: false, + containerId: 'a9103f9f-4759-47a3-9c22-c2398f9f5e21', + }, + { + id: '2cf50188-55a6-4143-b293-f009bc25356f', + name: 'GET_LOCATION', + description: '', + composite: false, + clientRole: false, + containerId: 'a9103f9f-4759-47a3-9c22-c2398f9f5e21', + }, + { + id: '674c5a60-9ed6-478f-9836-290945b40def', + name: 'offline_access', + description: '${role_offline-access}', + composite: false, + clientRole: false, + containerId: 'a9103f9f-4759-47a3-9c22-c2398f9f5e21', + }, + ], + clientMappings: { + 'realm-management': { + id: '9d9637dc-d86c-4669-bc0d-6600c22090f7', + client: 'realm-management', + mappings: [ + { + id: '43218a35-baf4-45fd-b926-5923996e9eb7', + name: 'manage-realm', + description: '${role_manage-realm}', + composite: false, + clientRole: true, + containerId: '9d9637dc-d86c-4669-bc0d-6600c22090f7', + }, + { + id: 'bcf4874f-760d-45d4-881e-eda109ecf033', + name: 'manage-users', + description: '${role_manage-users}', + composite: false, + clientRole: true, + containerId: '9d9637dc-d86c-4669-bc0d-6600c22090f7', + }, + ], + }, + account: { + id: '7b3904aa-2394-4940-8981-03262642f047', + client: 'account', + mappings: [ + { + id: 'c0dcef4f-eb0d-49f0-9389-96dc4d7316a2', + name: 'manage-account', + description: '${role_manage-account}', + composite: true, + clientRole: true, + containerId: '7b3904aa-2394-4940-8981-03262642f047', + }, + ], + }, + }, +}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx similarity index 84% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx index 3f37bc73e..380400f52 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/ViewDetailResources/tests/practitionerDetails.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx @@ -1,5 +1,5 @@ -import { PractitionerDetailsView } from '../practitionerDetails'; -import { render, screen } from '@testing-library/react'; +import { PractitionerDetailsView } from '../PractitionerDetails'; +import { render } from '@testing-library/react'; import { practitionerDetailsBundle } from './fixtures'; import React from 'react'; import { getResourcesFromBundle } from '@opensrp/react-utils'; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.css b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.css new file mode 100644 index 000000000..fc38443d6 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.css @@ -0,0 +1,24 @@ +.details-full-view { + background: #f5f5f5; +} + +.details-full-view .site-page-header { + padding: 24px; + background: #fff; +} + +.details-full-view .content-body { + margin: 24px; + display: flex; + gap: 16px; + flex-direction: column; +} + +.details-full-view .content-box { + background-color: #fff; +} + +.details-full-view .details-tab { + background: #fff; + padding: 16px; +} diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx new file mode 100644 index 000000000..621959af6 --- /dev/null +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx @@ -0,0 +1,253 @@ +import React from 'react'; +import { Alert, Breadcrumb, Button, Descriptions, Divider, Tabs, Tag } from 'antd'; +import { Spin } from 'antd'; +import { useTranslation } from '../../../mls'; +import { useHistory, useParams } from 'react-router'; +import { + BrokenPage, + FHIRServiceClass, + Resource404, + getResourcesFromBundle, +} from '@opensrp/react-utils'; +import { PageHeader } from '@ant-design/pro-layout'; +import { + KEYCLOAK_URL_USERS, + KeycloakUser, + URL_USER, + URL_USER_EDIT, +} from '@opensrp/user-management'; +import { KeycloakService } from '@opensrp/keycloak-service'; +import { useQuery } from 'react-query'; +import { IBundle } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IBundle'; +import { KeycloakGroupDetails } from './ViewDetailResources/GroupDetailView'; +import { PractitionerDetailsView } from './ViewDetailResources/PractitionerDetails'; +import { CareTeamDetailsView } from './ViewDetailResources/CareTeamDetails'; +import { OrganizationDetailsView } from './ViewDetailResources/OrganizationDetailsView'; +import { PractitionerDetail } from './types'; +import { Link } from 'react-router-dom'; +import { practitionerDetailsResourceType } from '../../../constants'; +import './index.css'; +import { UserDeleteBtn } from '../../UserDeleteBtn'; +import { KeycloakRoleDetails } from './ViewDetailResources/RoleDetailView'; + +// remove onclose from type and export the rest +interface UserDetailProps { + fhirBaseURL: string; + keycloakBaseURL: string; +} + +const breadCrumbItems = [ + { + title: 'Users', + path: URL_USER, + }, + { + title: 'View details', + }, +]; + +const breadCrumb = ( + { + const last = items.indexOf(route) === items.length - 1; + return last ? ( + {route.title} + ) : ( + {route.title} + ); + }} + > + + Users + + View details + +); + +export const UserDetails = (props: UserDetailProps) => { + const { keycloakBaseURL: keycloakBaseUrl, fhirBaseURL: fhirBaseUrl } = props; + const params = useParams<{ id: string }>(); + const { id: resourceId } = params; + const { t } = useTranslation(); + const history = useHistory(); + + const userDeleteAfterAction = () => { + history.push(URL_USER); + }; + + const { + data: user, + isLoading: userIsLoading, + error: userError, + } = useQuery([KEYCLOAK_URL_USERS, resourceId], () => + new KeycloakService(`${KEYCLOAK_URL_USERS}`, keycloakBaseUrl).read(resourceId) + ); + + const extraQueryFilters = { + 'keycloak-uuid': resourceId, + }; + + const { + data: practitionerDetails, + isLoading: detailsLoading, + error: detailsError, + } = useQuery( + [practitionerDetailsResourceType, resourceId], + () => + new FHIRServiceClass(fhirBaseUrl, practitionerDetailsResourceType).list( + extraQueryFilters + ), + { + select: (res) => { + // invariant : expect practitioner-details will always ever be a single record per keycloak user. + return getResourcesFromBundle(res)[0]; + }, + } + ); + const practDetailsByResName: PractitionerDetail['fhir'] = practitionerDetails?.fhir ?? {}; + + if (userIsLoading) { + return ; + } + + if (userError && !user) { + return ( + + ); + } + + if (user === undefined) { + return ; + } + + const { id, firstName, lastName, username, email, emailVerified, enabled, attributes } = user; + const userDetails = { + ID: id, + 'First Name': firstName, + 'Last Name': lastName, + Username: username, + Email: email, + Verified: emailVerified ? 'True' : 'False', + }; + const attributesArray = Object.entries(attributes ?? {}); + + return ( +
+ history.goBack()} + title="View details" + breadcrumb={breadCrumb} + subTitle={user.username} + /> +
+
+ Enabled : Disabled + } + extra={[ + , + , + ]} + > + + {Object.entries(userDetails).map(([key, value]) => { + return ( + + {value} + + ); + })} + + Attributes + {attributesArray.length === 0 ? ( + + ) : ( + + {attributesArray.map(([key, value]) => { + return ( + + {JSON.stringify(value)} + + ); + })} + + )} + +
+
+ + ), + }, + { + label: 'User roles', + key: 'Roles', + children: ( + + ), + }, + { + label: 'Practitioners', + key: 'Practitioners', + children: ( + + ), + }, + { + label: 'CareTeams', + key: 'CareTeams', + children: ( + + ), + }, + { + label: 'Organizations', + key: 'Organizations', + children: ( + + ), + }, + ]} + /> +
+
+
+ ); +}; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx similarity index 91% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx index 0ac85b9c4..ff1d412d8 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx @@ -2,7 +2,6 @@ import { UserDetails } from '../'; import { cleanup, fireEvent, - prettyDOM, render, screen, waitFor, @@ -14,25 +13,14 @@ import { authenticateUser } from '@onaio/session-reducer'; import { RoleContext } from '@opensrp/rbac'; import { superUserRole } from '@opensrp/react-utils'; import { store } from '@opensrp/store'; -import { - URL_USER, - UserList, - URL_USER_CREDENTIALS, - UserCredentials, - KEYCLOAK_URL_USERS, - KEYCLOAK_URL_USER_GROUPS, -} from '@opensrp/user-management'; -import { mount } from 'enzyme'; +import { URL_USER, KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS } from '@opensrp/user-management'; import React from 'react'; import { Provider } from 'react-redux'; import { Switch, Route, Router } from 'react-router'; import { createMemoryHistory } from 'history'; import { - keycloakGroupEndpoint, - keycloakMembersEndpoint, keycloakRoleMappingsEndpoint, practitionerDetailsResourceType, - practitionerResourceType, } from '../../../../constants'; import { practitionerDetailsBundle, @@ -42,6 +30,8 @@ import { } from '../ViewDetailResources/tests/fixtures'; import * as notifications from '@opensrp/notifications'; +/* eslint-disable no-template-curly-in-string */ + jest.mock('@opensrp/notifications', () => ({ __esModule: true, ...Object.assign({}, jest.requireActual('@opensrp/notifications')), @@ -78,9 +68,6 @@ const AppWrapper = (props: any) => { - {/* - {(routeProps) => } - */} {(routeProps) => } @@ -168,7 +155,7 @@ test('Renders without crashing', async () => { // start with group const groupTab = screen.getByText('User groups'); - await fireEvent.click(groupTab); + fireEvent.click(groupTab); await waitForElementToBeRemoved(document.querySelector('.ant-spin')); @@ -190,7 +177,7 @@ test('Renders without crashing', async () => { .delete(`${KEYCLOAK_URL_USERS}/${userId}${KEYCLOAK_URL_USER_GROUPS}/${user1147Groups[0].id}`) .reply(200, user1147Groups); - await fireEvent.click(leaveBtn); + fireEvent.click(leaveBtn); await waitFor(() => { expect(successMock).toHaveBeenCalledWith('User was removed from the keycloak group'); @@ -198,7 +185,7 @@ test('Renders without crashing', async () => { // go to practitioners const practTab = screen.getByText('Practitioners'); - await fireEvent.click(practTab); + fireEvent.click(practTab); // Check that practitioner-details has finished loading. await waitFor(() => { @@ -223,9 +210,7 @@ test('Renders without crashing', async () => { .reply(200, user1147Roles); const rolesTab = screen.getByText('User roles'); - await fireEvent.click(rolesTab); - - console.log(prettyDOM(document)); + fireEvent.click(rolesTab); // Check that practitioner-details has finished loading. await waitFor(() => { @@ -258,7 +243,7 @@ test('Renders without crashing', async () => { // go to careTeams const careTeamsTab = screen.getByText('CareTeams'); - await fireEvent.click(careTeamsTab); + fireEvent.click(careTeamsTab); // practitioner records detailsTabSection = document.querySelector('div.ant-tabs-tabpane-active'); @@ -271,7 +256,7 @@ test('Renders without crashing', async () => { // go to organization const organizationsTab = screen.getByText('Organizations'); - await fireEvent.click(organizationsTab); + fireEvent.click(organizationsTab); // practitioner records detailsTabSection = document.querySelector('div.ant-tabs-tabpane-active'); @@ -290,11 +275,9 @@ test('Edit button works correctly', async () => { const history = createMemoryHistory(); history.push(`${URL_USER}/${userId}`); - const successMock = jest - .spyOn(notifications, 'sendSuccessNotification') - .mockImplementation(() => { - return; - }); + jest.spyOn(notifications, 'sendSuccessNotification').mockImplementation(() => { + return; + }); nock(props.fhirBaseURL) .get(`/${practitionerDetailsResourceType}/_search`) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/types.ts similarity index 77% rename from packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts rename to packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/types.ts index 5c3517b8a..a49d8ceb3 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/UserDetails-v2/types.ts +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/types.ts @@ -4,17 +4,17 @@ import { Resource } from '@smile-cdr/fhirts/dist/FHIR-R4/classes/resource'; import { ICareTeam } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/ICareTeam'; import { IOrganization } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IOrganization'; import { IGroup } from '@smile-cdr/fhirts/dist/FHIR-R4/interfaces/IGroup'; - +import { LocationHierarchyResource } from '@opensrp/fhir-location-management'; export interface PractitionerDetail extends Resource { fhir: { careteams?: ICareTeam[]; teams?: IOrganization[]; - locationHierarchyList?: any[]; // TODO - import LocationHierarchy + locationHierarchyList?: LocationHierarchyResource[]; practitionerRoles?: IPractitionerRole[]; groups?: IGroup[]; - practitioner?: IPractitioner[] - } + practitioner?: IPractitioner[]; + }; } export interface RoleMapping { @@ -29,21 +29,21 @@ export interface RoleMapping { export interface ClientRoleMapping { id: string; client: string; - mappings: RoleMapping[] + mappings: RoleMapping[]; } export interface KeycloakUserRoleMappings { realmMappings?: RoleMapping[]; - clientMappings?: Record + clientMappings?: Record; } export interface PractitionerDetail extends Resource { fhir: { careteams?: ICareTeam[]; teams?: IOrganization[]; - locationHierarchyList?: any[]; // TODO - import LocationHierarchy + locationHierarchyList?: LocationHierarchyResource[]; practitionerRoles?: IPractitionerRole[]; groups?: IGroup[]; - practitioner?: IPractitioner[] - } -} \ No newline at end of file + practitioner?: IPractitioner[]; + }; +} diff --git a/packages/fhir-keycloak-user-management/src/index.tsx b/packages/fhir-keycloak-user-management/src/index.tsx index cf5a32740..0db49d2fd 100644 --- a/packages/fhir-keycloak-user-management/src/index.tsx +++ b/packages/fhir-keycloak-user-management/src/index.tsx @@ -1,5 +1,5 @@ +import { UserDetails as UserDetailsV2 } from './components/UserList/Viewdetails'; export * from './components/CreateEditUser'; export * from './components/UserList/ListView'; export * from './constants'; -import {UserDetails as UserDetailsV2} from './components/UserList/UserDetails-v2'; -export {UserDetailsV2}; +export { UserDetailsV2 }; From 324ebb770932c4bdd5d8fdd1f4932d51190e6ac0 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Thu, 9 Nov 2023 14:57:35 +0300 Subject: [PATCH 17/32] Wrap microcopy in translator function --- .../ViewDetailResources/CareTeamDetails.tsx | 2 +- .../ViewDetailResources/GroupDetailView.tsx | 14 +-- .../OrganizationDetailsView.tsx | 2 +- .../PractitionerDetails.tsx | 2 +- .../ViewDetailResources/RoleDetailView.tsx | 2 +- .../components/UserList/Viewdetails/index.tsx | 98 ++++++++++--------- 6 files changed, 63 insertions(+), 57 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx index 59ecac4dd..fc6c73218 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/CareTeamDetails.tsx @@ -21,7 +21,7 @@ export const CareTeamDetailsView = ({ if (error) { return ( - {'An error occurred while trying to fetch the practitioner details.'} + {t('An error occurred while trying to fetch the practitioner details.')} ); } diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx index 2c32414e2..3a59c5d51 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx @@ -6,6 +6,7 @@ import { KEYCLOAK_URL_USERS, KEYCLOAK_URL_USER_GROUPS } from '@opensrp/user-mana import { KeycloakService } from '@opensrp/keycloak-service'; import { QueryClient, useQueryClient, useQuery } from 'react-query'; import { sendInfoNotification, sendSuccessNotification } from '@opensrp/notifications'; +import { TFunction } from 'i18n/dist/types'; export interface KeycloakGroupDetailsProp { keycloakBaseUrl: string; @@ -27,7 +28,7 @@ export const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { if (error && !data) { return ( - {'Unable to fetch the keycloak groups that the user is assigned to'} + {t('An error occured while fetching user groups that the user is assigned to')} ); } @@ -42,11 +43,11 @@ export const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { dataIndex: 'path' as const, }, { - title: t(''), + title: t('Actions'), dataIndex: 'id' as const, render: (id: string) => ( + + + ), }, ]; diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx index 380400f52..65f5e98e0 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/tests/practitionerDetails.test.tsx @@ -17,5 +17,8 @@ test('renders without crashing', () => { const tableRows = document.querySelectorAll('table tr'); const text = [...tableRows].map((tr) => tr.textContent); - expect(text).toEqual(['NameActiveCoding', 'test1147 1147Active']); + expect(text).toEqual([ + 'IdNameActiveUser TypePractitioner Role Coding', + '3a801d6e-7bd3-4a5f-bc9c-64758fbb3dadtest1147 1147ActivepractitionerAssigned practitioner(http://snomed.info/sct|405623001), ', + ]); }); diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx index efd937ca1..cc300fd34 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx @@ -29,6 +29,7 @@ import { practitionerDetailsResourceType } from '../../../constants'; import './index.css'; import { UserDeleteBtn } from '../../UserDeleteBtn'; import { KeycloakRoleDetails } from './ViewDetailResources/RoleDetailView'; +import { RbacCheck } from 'rbac/dist/types'; // remove onclose from type and export the rest interface UserDetailProps { @@ -127,20 +128,22 @@ export const UserDetails = (props: UserDetailProps) => { ) } extra={[ - , - , + + + , + + + , ]} > @@ -168,61 +171,69 @@ export const UserDetails = (props: UserDetailProps) => { )} -
- - ), - }, - { - label: t('User roles'), - key: 'Roles', - children: ( - - ), - }, - { - label: t('Practitioners'), - key: 'Practitioners', - children: ( - - ), - }, - { - label: t('CareTeams'), - key: 'CareTeams', - children: ( - - ), - }, - { - label: t('Organizations'), - key: 'Organizations', - children: ( - - ), - }, - ]} - /> -
+ +
+ + ), + }, + { + label: t('User roles'), + key: 'Roles', + children: ( + + ), + }, + { + label: t('Practitioners'), + key: 'Practitioners', + children: ( + + ), + }, + { + label: t('CareTeams'), + key: 'CareTeams', + children: ( + + ), + }, + { + label: t('Organizations'), + key: 'Organizations', + children: ( + + ), + }, + ]} + /> +
+
); diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx index ff1d412d8..cf0c231ed 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/tests/index.test.tsx @@ -148,7 +148,7 @@ test('Renders without crashing', async () => { const userProfile = screen.getByTestId('user-profile'); const textContent = userProfile.textContent; expect(textContent).toEqual( - '1147EnabledDeleteEditID9f72c646-dc1e-4f24-98df-6f04373b9ec6First Nametest1147Last Name1147Username1147Emailmejay2303@gmail.comVerifiedFalseAttributesfhir_core_app_id["ecbis"]' + '1147EnabledDeleteEditId9f72c646-dc1e-4f24-98df-6f04373b9ec6First Nametest1147Last Name1147Username1147Emailmejay2303@gmail.comVerifiedFalseAttributesfhir_core_app_id["ecbis"]' ); // have a look at the tabs @@ -164,7 +164,7 @@ test('Renders without crashing', async () => { const groupsTable = detailsTabSection?.querySelector('table'); const tableData = [...(groupsTable?.querySelectorAll('tr') ?? [])].map((tr) => tr.textContent); - expect(tableData).toEqual(['NamePath', 'SuperUser/SuperUserLeave']); + expect(tableData).toEqual(['NamePathActions', 'SuperUser/SuperUserLeave']); const leaveBtn = screen.getByText('Leave'); expect(leaveBtn).toMatchInlineSnapshot(` From 4cc34d97ed1792034b8860f96603bf433e3441f5 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Thu, 9 Nov 2023 15:30:32 +0300 Subject: [PATCH 20/32] Fix import --- .../src/components/UserList/Viewdetails/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx index cc300fd34..ea47f1bea 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx @@ -29,7 +29,7 @@ import { practitionerDetailsResourceType } from '../../../constants'; import './index.css'; import { UserDeleteBtn } from '../../UserDeleteBtn'; import { KeycloakRoleDetails } from './ViewDetailResources/RoleDetailView'; -import { RbacCheck } from 'rbac/dist/types'; +import { RbacCheck } from '@opensrp/rbac'; // remove onclose from type and export the rest interface UserDetailProps { From 02835090b19578bc6e53502f3d67caa40fcf1839 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Thu, 9 Nov 2023 15:43:53 +0300 Subject: [PATCH 21/32] Fix permission string --- .../src/components/UserList/Viewdetails/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx index ea47f1bea..b232a35fc 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/index.tsx @@ -171,7 +171,7 @@ export const UserDetails = (props: UserDetailProps) => { )} - +
Date: Thu, 9 Nov 2023 15:55:11 +0300 Subject: [PATCH 22/32] Update tests --- .../tests/__snapshots__/index.test.tsx.snap | 206 ------------------ .../UserList/Viewdetails/tests/index.test.tsx | 4 +- 2 files changed, 3 insertions(+), 207 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap index fc545d65f..683c4ba37 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/__snapshots__/index.test.tsx.snap @@ -400,212 +400,6 @@ exports[`renders correctly when listing resources: table row 5 page 1 4`] = ` `; -exports[`renders correctly when listing resources: user details 1`] = ` -
-
-
-
- - Keycloak UUID - -
-
- - 081724e8-5fc1-47dd-8d0c-fa0c6ae6ddf0 - -
-
-
-
-`; - -exports[`renders correctly when listing resources: user details 2`] = ` -
-
-
-
- - Username - -
-
- - april4petertest - -
-
-
-
-`; - -exports[`renders correctly when listing resources: user details 3`] = ` -
-
-
-
- - Practitioner ID - -
-
- - 206 - -
-
-
-
-`; - -exports[`renders correctly when listing resources: user details 4`] = ` -
-
-
-
- - Practitioner UUID - -
-
- - c1d36d9a-b771-410b-959e-af2c04d132a2 - -
-
-
-
-`; - -exports[`renders correctly when listing resources: user details 5`] = ` -
-
-
-
- - Practitioner status - -
-
- - inactive - -
-
-
-
-`; - -exports[`renders correctly when listing resources: user details 6`] = ` -
-
-
-
- - User Type - -
-
- -
    -
  • - practitioner -
  • -
-
-
-
-
-
-`; - exports[`renders correctly when listing resources: yes button 1`] = ` ), diff --git a/packages/fhir-keycloak-user-management/src/constants.ts b/packages/fhir-keycloak-user-management/src/constants.ts index b3af7f5bb..8c7e68a51 100644 --- a/packages/fhir-keycloak-user-management/src/constants.ts +++ b/packages/fhir-keycloak-user-management/src/constants.ts @@ -1,3 +1,5 @@ +import { URL_USER } from '@opensrp/user-management'; + export const practitionerResourceType = 'Practitioner'; export const careTeamResourceType = 'CareTeam'; export const organizationResourceType = 'Organization'; @@ -10,3 +12,6 @@ export const practitionerDetailsResourceType = 'PractitionerDetail'; export const keycloakCountEndpoint = 'count'; export const keycloakGroupEndpoint = 'groups'; export const keycloakMembersEndpoint = 'members'; + +// router urls +export const USER_DETAILS_URL = `${URL_USER}/details/:id`; From acd64b8cfe458cf0330f1ac3c8b65ef66b3d3298 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 10:31:57 +0300 Subject: [PATCH 25/32] Remove surplus pro-components library --- app/package.json | 1 - packages/fhir-keycloak-user-management/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/app/package.json b/app/package.json index 76abec9d3..5163622b0 100644 --- a/app/package.json +++ b/app/package.json @@ -5,7 +5,6 @@ "dependencies": { "@2fd/ant-design-icons": "^2.6.0", "@ant-design/icons": "^4.7.0", - "@ant-design/pro-components": "^2.6.32", "@onaio/connected-private-route": "^0.0.11", "@onaio/connected-reducer-registry": "^0.0.3", "@onaio/gatekeeper": "1.0.0", diff --git a/packages/fhir-keycloak-user-management/package.json b/packages/fhir-keycloak-user-management/package.json index 3df30c85c..1087ecafb 100644 --- a/packages/fhir-keycloak-user-management/package.json +++ b/packages/fhir-keycloak-user-management/package.json @@ -23,7 +23,6 @@ "dist" ], "peerDependencies": { - "@ant-design/pro-components": "^2.6.32", "@opensrp/store": "^0.0.10", "antd": "^5.5.1", "i18next": "^19.8.4", From f7e1d2c805918d2fa34c48dca04533607705e112 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:39:11 +0300 Subject: [PATCH 26/32] Fix user details url route --- app/src/App/fhir-apps.tsx | 2 +- packages/fhir-keycloak-user-management/src/constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/App/fhir-apps.tsx b/app/src/App/fhir-apps.tsx index 57b63e384..3df5289a7 100644 --- a/app/src/App/fhir-apps.tsx +++ b/app/src/App/fhir-apps.tsx @@ -230,7 +230,7 @@ const FHIRApps = () => { redirectPath={APP_CALLBACK_URL} disableLoginProtection={DISABLE_LOGIN_PROTECTION} exact - path={USER_DETAILS_URL} + path={`${USER_DETAILS_URL}/:id`} permissions={['iam_user.read']} component={UserDetailsV2} /> diff --git a/packages/fhir-keycloak-user-management/src/constants.ts b/packages/fhir-keycloak-user-management/src/constants.ts index 8c7e68a51..f9c1abfc0 100644 --- a/packages/fhir-keycloak-user-management/src/constants.ts +++ b/packages/fhir-keycloak-user-management/src/constants.ts @@ -14,4 +14,4 @@ export const keycloakGroupEndpoint = 'groups'; export const keycloakMembersEndpoint = 'members'; // router urls -export const USER_DETAILS_URL = `${URL_USER}/details/:id`; +export const USER_DETAILS_URL = `${URL_USER}/details`; From 5858edb8b07bd6db3d504d334456c9aefc7676ec Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:39:47 +0300 Subject: [PATCH 27/32] Invalidate only query that refetches user queries --- .../src/components/UserDeleteBtn/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx index 19a5755a2..e05dba650 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx @@ -27,7 +27,7 @@ export const UserDeleteBtn = (props: UserDeleteBtnProp) => { onConfirm={async () => { await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t); try { - return await queryClient.invalidateQueries([KEYCLOAK_URL_USERS]); + return await queryClient.invalidateQueries({queryKey: [KEYCLOAK_URL_USERS], exact: true}); } catch { return sendErrorNotification( t('Failed to update data, please refresh the page to see the most recent changes') From 203a63ea6a20a10eb7951e251d5cdccb80306376 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:40:04 +0300 Subject: [PATCH 28/32] Code cleanup --- .../Viewdetails/ViewDetailResources/GroupDetailView.tsx | 4 ++-- .../Viewdetails/ViewDetailResources/PractitionerDetails.tsx | 4 ++-- .../src/components/LocationUnitList/LocationUnitList.css | 4 ---- .../src/components/fhirDataTypes/CodeableConcept/index.tsx | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx index 5db280884..870b16db2 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/GroupDetailView.tsx @@ -29,7 +29,7 @@ export const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { if (error && !data) { return ( - {t('An error occured while fetching user groups that the user is assigned to')} + {t('An error occurred while fetching user groups that the user is assigned to')} ); } @@ -53,7 +53,7 @@ export const KeycloakGroupDetails = (props: KeycloakGroupDetailsProp) => { type="link" danger > - {'Leave'} + {t('Leave')} ), diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx index bed311487..a2909775d 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/Viewdetails/ViewDetailResources/PractitionerDetails.tsx @@ -80,7 +80,7 @@ export const PractitionerDetailsView = (props: PractitionerDetailViewsProps) => }; /** - * Get practitioner table data source form practitioners and practitioner roles + * Get practitioner table data by combining practitioners and practitioner roles * returned in the practitioner details endpoint * * @param practitioners - practitioner resources @@ -149,7 +149,7 @@ function processPractitionerDetails( } for (const [key, value] of Object.entries(tempPractitionerRoleCodings)) { - // invariant: we should have encountered all possible practitioners whole practitioner Roles records are in tempPractitionerRoleCodings + // invariant: we should have encountered all possible practitioners whose practitioner Roles records are in tempPractitionerRoleCodings tableData[key].concepts = [...tableData[key].concepts, ...value.concepts]; tableData[key].userType = value.userType; } diff --git a/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css b/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css index 031a5d525..0008f68e8 100644 --- a/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css +++ b/packages/fhir-location-management/src/components/LocationUnitList/LocationUnitList.css @@ -21,7 +21,3 @@ font-size: 18px; border-left-color: #e9e9e9; } - -/* table tr:nth-child(2n) td { - background-color: #f4f4f4; -} */ diff --git a/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx b/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx index 6ad7ca9b7..b3b38d5e8 100644 --- a/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx +++ b/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx @@ -23,5 +23,5 @@ export const CodeableConcept = (props: CodeableConceptProps) => { ))} ); - return {text ? {text} : codingsTitle}; + return {text ? {text} : codingsTitle}; }; From 7537956a7a4e018fe97dd6fda65588df9e2f78d7 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:44:10 +0300 Subject: [PATCH 29/32] Update tests --- .../src/components/UserList/ListView/tests/index.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx index 5bab7149b..1bbe95c88 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx @@ -13,7 +13,7 @@ import userEvents from '@testing-library/user-event'; import { URL_USER, URL_USER_CREDENTIALS, UserCredentials } from '@opensrp/user-management'; import { practitioner, userFixtures, group } from './fixtures'; import fetch from 'jest-fetch-mock'; -import { practitionerResourceType, practitionerRoleResourceType } from '../../../../constants'; +import { USER_DETAILS_URL, practitionerResourceType, practitionerRoleResourceType } from '../../../../constants'; import flushPromises from 'flush-promises'; import { practitionerRoleBundle } from '../../../CreateEditUser/tests/fixtures'; import { RoleContext } from '@opensrp/rbac'; @@ -332,5 +332,5 @@ test('View details navigates correctly', async () => { `); fireEvent.click(viewDetailsLink); - expect(history.location.pathname).toEqual(`${URL_USER}/081724e8-5fc1-47dd-8d0c-fa0c6ae6ddf0`); + expect(history.location.pathname).toEqual(`${USER_DETAILS_URL}/081724e8-5fc1-47dd-8d0c-fa0c6ae6ddf0`); }); From a687581cd6dcc31dc5a212a4055044a22da1c2ae Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:47:43 +0300 Subject: [PATCH 30/32] Update lock file --- yarn.lock | 531 +----------------------------------------------------- 1 file changed, 10 insertions(+), 521 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8b37b056f..c5ed562e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,24 +45,6 @@ __metadata: languageName: node linkType: hard -"@ant-design/cssinjs@npm:^1.11.1": - version: 1.17.2 - resolution: "@ant-design/cssinjs@npm:1.17.2" - dependencies: - "@babel/runtime": ^7.11.1 - "@emotion/hash": ^0.8.0 - "@emotion/unitless": ^0.7.5 - classnames: ^2.3.1 - csstype: ^3.0.10 - rc-util: ^5.35.0 - stylis: ^4.0.13 - peerDependencies: - react: ">=16.0.0" - react-dom: ">=16.0.0" - checksum: ed864b4f890038cb132131c25ebe137a421c32a518dbbd0424126ce2603f71946ce0fca91bbe1ad7a629760374266af6246da01ca86fbce8f68acb9ceba6b91f - languageName: node - linkType: hard - "@ant-design/cssinjs@npm:^1.9.1": version: 1.9.1 resolution: "@ant-design/cssinjs@npm:1.9.1" @@ -88,13 +70,6 @@ __metadata: languageName: node linkType: hard -"@ant-design/icons-svg@npm:^4.3.0": - version: 4.3.1 - resolution: "@ant-design/icons-svg@npm:4.3.1" - checksum: 47f0474277366fb3b8bacfeb1691be35052c3f9b28811be7fb25ad219100533d0e31c2eec00a8dee744c34381a4cda7f39b39403e160811a8fd5d33b861e77aa - languageName: node - linkType: hard - "@ant-design/icons@npm:^4.0.0": version: 4.7.0 resolution: "@ant-design/icons@npm:4.7.0" @@ -127,22 +102,6 @@ __metadata: languageName: node linkType: hard -"@ant-design/icons@npm:^5.0.0": - version: 5.2.6 - resolution: "@ant-design/icons@npm:5.2.6" - dependencies: - "@ant-design/colors": ^7.0.0 - "@ant-design/icons-svg": ^4.3.0 - "@babel/runtime": ^7.11.2 - classnames: ^2.2.6 - rc-util: ^5.31.1 - peerDependencies: - react: ">=16.0.0" - react-dom: ">=16.0.0" - checksum: 2f571699b1903383cd09faa78e4cce34973debb0e7ec6223b9d9a0a6ab2b2f0c876072db62bbd4e6a45e864df5447343315e066abeffaf58aa5b97df3acc89f1 - languageName: node - linkType: hard - "@ant-design/icons@npm:^5.1.0": version: 5.1.4 resolution: "@ant-design/icons@npm:5.1.4" @@ -159,246 +118,6 @@ __metadata: languageName: node linkType: hard -"@ant-design/pro-card@npm:2.5.22": - version: 2.5.22 - resolution: "@ant-design/pro-card@npm:2.5.22" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-provider": 2.13.2 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - classnames: ^2.3.2 - omit.js: ^2.0.2 - rc-resize-observer: ^1.0.0 - rc-util: ^5.4.0 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - checksum: 728cf6d7ef2a87fd85dcd12b27cd33eb4e44341f3267a001ec5fdebed2ba4226a1fb5611cce672ea28386ae7ac19cfe08fb99f432c69e3337a9ff9bc58822849 - languageName: node - linkType: hard - -"@ant-design/pro-components@npm:^2.6.32": - version: 2.6.32 - resolution: "@ant-design/pro-components@npm:2.6.32" - dependencies: - "@ant-design/pro-card": 2.5.22 - "@ant-design/pro-descriptions": 2.5.17 - "@ant-design/pro-field": 2.13.3 - "@ant-design/pro-form": 2.21.4 - "@ant-design/pro-layout": 7.17.10 - "@ant-design/pro-list": 2.5.31 - "@ant-design/pro-provider": 2.13.2 - "@ant-design/pro-skeleton": 2.1.8 - "@ant-design/pro-table": 3.13.0 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.16.3 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: dffebe569a271911a761b1a8974732ad9911de8556ec94340cd165264f547eb3398497209d75825ea0039293485ccf5141a015dc8854bc16a4c2a6c3c5b49e78 - languageName: node - linkType: hard - -"@ant-design/pro-descriptions@npm:2.5.17": - version: 2.5.17 - resolution: "@ant-design/pro-descriptions@npm:2.5.17" - dependencies: - "@ant-design/pro-field": 2.13.3 - "@ant-design/pro-form": 2.21.4 - "@ant-design/pro-skeleton": 2.1.8 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - rc-resize-observer: ^0.2.3 - rc-util: ^5.0.6 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - checksum: 591b01a6ba4d178f7a7a54e2254dcf1948d830e68fe68c94be9572cb2c7596b1ea73440d292e3ee4b7d4ca138e4b3ba1908068f695d7d5cca0657e316d191096 - languageName: node - linkType: hard - -"@ant-design/pro-field@npm:2.13.3": - version: 2.13.3 - resolution: "@ant-design/pro-field@npm:2.13.3" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-provider": 2.13.2 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - "@chenshuai2144/sketch-color": ^1.0.8 - classnames: ^2.3.2 - dayjs: ^1.11.10 - lodash.tonumber: ^4.0.3 - omit.js: ^2.0.2 - rc-util: ^5.4.0 - swr: ^2.0.0 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - checksum: e8f153369c5046ad13e0bbcdaf38f8f2ffe98ccf1522279b78ba9c663be71d8b0491126344c98cda6938b7c27e43e7fad6dbfbbdb97886010bfc3b7bb7e3d002 - languageName: node - linkType: hard - -"@ant-design/pro-form@npm:2.21.4": - version: 2.21.4 - resolution: "@ant-design/pro-form@npm:2.21.4" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-field": 2.13.3 - "@ant-design/pro-provider": 2.13.2 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - "@chenshuai2144/sketch-color": ^1.0.7 - "@umijs/use-params": ^1.0.9 - classnames: ^2.3.2 - dayjs: ^1.11.10 - lodash.merge: ^4.6.2 - omit.js: ^2.0.2 - rc-resize-observer: ^1.1.0 - rc-util: ^5.0.6 - peerDependencies: - "@types/lodash.merge": ^4.6.7 - antd: ">=4.23.0 || >=5.0.0" - rc-field-form: ^1.22.0 - react: ">=17.0.0" - react-dom: ">=17.0.0" - peerDependenciesMeta: - "@types/lodash.merge": - optional: true - checksum: a0bdf3c6dfda12f963d76b72b51860083c7eee4272392480d92d87cca86da210c8d2a06cfb44f2cc10785a6ba99b78df721a55f223e8f3f556ec8fb9edb0d078 - languageName: node - linkType: hard - -"@ant-design/pro-layout@npm:7.17.10": - version: 7.17.10 - resolution: "@ant-design/pro-layout@npm:7.17.10" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-provider": 2.13.2 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - "@umijs/route-utils": ^4.0.0 - "@umijs/use-params": ^1.0.9 - classnames: ^2.3.2 - lodash.merge: ^4.6.2 - omit.js: ^2.0.2 - path-to-regexp: 2.4.0 - rc-resize-observer: ^1.1.0 - rc-util: ^5.0.6 - swr: ^2.0.0 - warning: ^4.0.3 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: d4b902ef3ea5fa8352b41ec0bdd6b8c971df0e72c2d32ace9c66006f09a98d9adc787926fda70b1672057573a92d05c730503c8f0d2543e2739e9cc033647e96 - languageName: node - linkType: hard - -"@ant-design/pro-list@npm:2.5.31": - version: 2.5.31 - resolution: "@ant-design/pro-list@npm:2.5.31" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-card": 2.5.22 - "@ant-design/pro-field": 2.13.3 - "@ant-design/pro-table": 3.13.0 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - classnames: ^2.3.2 - dayjs: ^1.11.10 - rc-resize-observer: ^1.0.0 - rc-util: ^4.19.0 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: f4c227c6f6b2cba3b6949416641b1e7d146790fcad966d93dc8d26149299d588163c25e99a577d00b378ade1563264810ffa8dae4ce953f6541411c042d066e1 - languageName: node - linkType: hard - -"@ant-design/pro-provider@npm:2.13.2": - version: 2.13.2 - resolution: "@ant-design/pro-provider@npm:2.13.2" - dependencies: - "@ant-design/cssinjs": ^1.11.1 - "@babel/runtime": ^7.18.0 - "@ctrl/tinycolor": ^3.4.0 - rc-util: ^5.0.1 - swr: ^2.0.0 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: 4c8497cdb52b97ff54c1928ec39dd1f1d417bbc229391e223e463dff69b72c758d5db61998aced382eb49ad849a8e21ebfb001cda41543659fe33cf8d5086d98 - languageName: node - linkType: hard - -"@ant-design/pro-skeleton@npm:2.1.8": - version: 2.1.8 - resolution: "@ant-design/pro-skeleton@npm:2.1.8" - dependencies: - "@babel/runtime": ^7.18.0 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: 806753a45ebc585d354dcbd474ac413358ac30ab4a7555cab8e8f4b57b25a091fcd508cb02e11c1e0acd6811ad75b742c5792a453b1aa112f747a770528aafd5 - languageName: node - linkType: hard - -"@ant-design/pro-table@npm:3.13.0": - version: 3.13.0 - resolution: "@ant-design/pro-table@npm:3.13.0" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-card": 2.5.22 - "@ant-design/pro-field": 2.13.3 - "@ant-design/pro-form": 2.21.4 - "@ant-design/pro-provider": 2.13.2 - "@ant-design/pro-utils": 2.14.10 - "@babel/runtime": ^7.18.0 - "@dnd-kit/core": ^6.0.8 - "@dnd-kit/modifiers": ^6.0.1 - "@dnd-kit/sortable": ^7.0.2 - "@dnd-kit/utilities": ^3.2.1 - classnames: ^2.3.2 - dayjs: ^1.11.10 - omit.js: ^2.0.2 - rc-resize-observer: ^1.0.0 - rc-util: ^5.0.1 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - rc-field-form: ^1.22.0 - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: bdde2e21b8434b52ddc5a302c275fba50ab3bd16fa98f7c0e6e282662cb5d1fa9eded29f055f81ddb712f89e8dccb990b3ef62cedb2d23b524f80c63e7485475 - languageName: node - linkType: hard - -"@ant-design/pro-utils@npm:2.14.10": - version: 2.14.10 - resolution: "@ant-design/pro-utils@npm:2.14.10" - dependencies: - "@ant-design/icons": ^5.0.0 - "@ant-design/pro-provider": 2.13.2 - "@babel/runtime": ^7.18.0 - classnames: ^2.3.2 - dayjs: ^1.11.10 - lodash.merge: ^4.6.2 - rc-util: ^5.0.6 - safe-stable-stringify: ^2.4.3 - swr: ^2.0.0 - peerDependencies: - antd: ">=4.23.0 || >=5.0.0" - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: c0af1b7670254e422f304d50ae51d4843accf2a831fce90c04b8ee1fb92597d2c12fa0caa03d10b35715d460f10e449670edfed4ddd3f13241881bed5e74c3a5 - languageName: node - linkType: hard - "@ant-design/react-slick@npm:~1.0.0": version: 1.0.1 resolution: "@ant-design/react-slick@npm:1.0.1" @@ -2302,18 +2021,6 @@ __metadata: languageName: node linkType: hard -"@chenshuai2144/sketch-color@npm:^1.0.7, @chenshuai2144/sketch-color@npm:^1.0.8": - version: 1.0.9 - resolution: "@chenshuai2144/sketch-color@npm:1.0.9" - dependencies: - reactcss: ^1.2.3 - tinycolor2: ^1.4.2 - peerDependencies: - react: ">=16.12.0" - checksum: 7337f9a24abc7630f2b839b1cce875ff07061b8b568d0d72c9528db9dc3c0ec7db78242b20180b419c60ecb2766533487d7cd77000f0e24dc7d049f8bc86db50 - languageName: node - linkType: hard - "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -2452,68 +2159,6 @@ __metadata: languageName: node linkType: hard -"@dnd-kit/accessibility@npm:^3.0.0": - version: 3.0.1 - resolution: "@dnd-kit/accessibility@npm:3.0.1" - dependencies: - tslib: ^2.0.0 - peerDependencies: - react: ">=16.8.0" - checksum: 0afc2c0fce9a1c107453620ca0da1778f182d340e74ffbc6e369ef0ac8943cafb929d3a6c0891d9b915aa23b2b92137ff4fad958f43118466586d8129a3359d5 - languageName: node - linkType: hard - -"@dnd-kit/core@npm:^6.0.8": - version: 6.0.8 - resolution: "@dnd-kit/core@npm:6.0.8" - dependencies: - "@dnd-kit/accessibility": ^3.0.0 - "@dnd-kit/utilities": ^3.2.1 - tslib: ^2.0.0 - peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - checksum: abe48ff7395f84fd8c15e6c8b13da4df153dc1f1076096d783acd0c25539516c77e4854ea59be6621dde55739cb0df1d62924ad069df3267fe05ad90ef729b2f - languageName: node - linkType: hard - -"@dnd-kit/modifiers@npm:^6.0.1": - version: 6.0.1 - resolution: "@dnd-kit/modifiers@npm:6.0.1" - dependencies: - "@dnd-kit/utilities": ^3.2.1 - tslib: ^2.0.0 - peerDependencies: - "@dnd-kit/core": ^6.0.6 - react: ">=16.8.0" - checksum: cd31715aac81baa2398558dc7c877a483d1c4c41cdb2f466557fdc41bb742db5cd1b34b3b84332b94ac4a2835e7e5a0e28c64e621936e976399788f1dd029ea4 - languageName: node - linkType: hard - -"@dnd-kit/sortable@npm:^7.0.2": - version: 7.0.2 - resolution: "@dnd-kit/sortable@npm:7.0.2" - dependencies: - "@dnd-kit/utilities": ^3.2.0 - tslib: ^2.0.0 - peerDependencies: - "@dnd-kit/core": ^6.0.7 - react: ">=16.8.0" - checksum: 4ce705aceb15766a0deefe25a9d95a87e9413c3fb9088ea3eb0962e57f844895000117fcec7c0944a0d4ae4e1e889cfa69e3d3778164d4d23115fb1edb218283 - languageName: node - linkType: hard - -"@dnd-kit/utilities@npm:^3.2.0, @dnd-kit/utilities@npm:^3.2.1": - version: 3.2.1 - resolution: "@dnd-kit/utilities@npm:3.2.1" - dependencies: - tslib: ^2.0.0 - peerDependencies: - react: ">=16.8.0" - checksum: 038fd5cc1328bf4c9dca17cd48046e5a687bbf9d904c7197f851aab869ab52d9dee2734b2e255256fd6158245acd00063a23deed962c7673c0fadfbf061f04ca - languageName: node - linkType: hard - "@emotion/hash@npm:^0.8.0": version: 0.8.0 resolution: "@emotion/hash@npm:0.8.0" @@ -4028,7 +3673,6 @@ __metadata: react-i18next: ^11.8.10 uuid: ^8.3.1 peerDependencies: - "@ant-design/pro-components": ^2.6.32 "@opensrp/store": ^0.0.10 antd: ^5.5.1 i18next: ^19.8.4 @@ -4335,7 +3979,6 @@ __metadata: dependencies: "@2fd/ant-design-icons": ^2.6.0 "@ant-design/icons": ^4.7.0 - "@ant-design/pro-components": ^2.6.32 "@onaio/connected-private-route": ^0.0.11 "@onaio/connected-reducer-registry": ^0.0.3 "@onaio/gatekeeper": 1.0.0 @@ -6299,22 +5942,6 @@ __metadata: languageName: node linkType: hard -"@umijs/route-utils@npm:^4.0.0": - version: 4.0.1 - resolution: "@umijs/route-utils@npm:4.0.1" - checksum: e1ca91cf6da16d7623cc0a8816cf2655e1f8b7fcc2507d455383b31f7e1979e52bc2464453173e68b660d24c4255be44d521d280e580c025af6bed83b5727241 - languageName: node - linkType: hard - -"@umijs/use-params@npm:^1.0.9": - version: 1.0.9 - resolution: "@umijs/use-params@npm:1.0.9" - peerDependencies: - react: "*" - checksum: 7f72436440dff0e1911ee916340bd26c6c9a510bad4af536d3ec9a5a175d37255d42dbd1cb0dc996daf4d0c3b76e44816ac423ac73e6b3c1655b6b839fed175c - languageName: node - linkType: hard - "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -6837,15 +6464,6 @@ __metadata: languageName: node linkType: hard -"add-dom-event-listener@npm:^1.1.0": - version: 1.1.0 - resolution: "add-dom-event-listener@npm:1.1.0" - dependencies: - object-assign: 4.x - checksum: 7685d32c4a9d78ad7a36641774168d80e195ef0710ae801e7211d849100c0e4499657cf3838bdae5ee9134b5aa9d7f50d68dec73b03e7d0082302804efff6ea6 - languageName: node - linkType: hard - "add-stream@npm:^1.0.0": version: 1.0.0 resolution: "add-stream@npm:1.0.0" @@ -9220,13 +8838,6 @@ __metadata: languageName: node linkType: hard -"client-only@npm:^0.0.1": - version: 0.0.1 - resolution: "client-only@npm:0.0.1" - checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 - languageName: node - linkType: hard - "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -10686,13 +10297,6 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.11.10": - version: 1.11.10 - resolution: "dayjs@npm:1.11.10" - checksum: a6b5a3813b8884f5cd557e2e6b7fa569f4c5d0c97aca9558e38534af4f2d60daafd3ff8c2000fed3435cfcec9e805bcebd99f90130c6d1c5ef524084ced588c4 - languageName: node - linkType: hard - "de-indent@npm:^1.0.2": version: 1.0.2 resolution: "de-indent@npm:1.0.2" @@ -18135,7 +17739,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:>=3.5 <5, lodash@npm:^4.0.0, lodash@npm:^4.0.1, lodash@npm:^4.1.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.7.0": +"lodash@npm:>=3.5 <5, lodash@npm:^4.0.0, lodash@npm:^4.1.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -19673,13 +19277,6 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:4.x, object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f - languageName: node - linkType: hard - "object-assign@npm:^3.0.0": version: 3.0.0 resolution: "object-assign@npm:3.0.0" @@ -19687,6 +19284,13 @@ __metadata: languageName: node linkType: hard +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + "object-copy@npm:^0.1.0": version: 0.1.0 resolution: "object-copy@npm:0.1.0" @@ -19834,13 +19438,6 @@ __metadata: languageName: node linkType: hard -"omit.js@npm:^2.0.2": - version: 2.0.2 - resolution: "omit.js@npm:2.0.2" - checksum: 5d802b9fd7640250aada82f3b9b7243b554b38911f29b3de0d1066c00f24dd4ee72d3b9c94c582e373fb6511bd21e107917d419a7b2a04287f26c31133b48a15 - languageName: node - linkType: hard - "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -20536,13 +20133,6 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:2.4.0": - version: 2.4.0 - resolution: "path-to-regexp@npm:2.4.0" - checksum: 581175bf2968e51452f2b8c71f10e75c995693668b4ecf7d0b48962fbe0c56830661ca5dd5fd6d8e2f0cc9a045ce07e89af504ab133e1d21887c2712df85b1f4 - languageName: node - linkType: hard - "path-to-regexp@npm:^1.7.0": version: 1.8.0 resolution: "path-to-regexp@npm:1.8.0" @@ -22623,7 +22213,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.5.10, prop-types@npm:^15.5.8, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.5.8, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -23492,21 +23082,6 @@ __metadata: languageName: node linkType: hard -"rc-resize-observer@npm:^0.2.3": - version: 0.2.6 - resolution: "rc-resize-observer@npm:0.2.6" - dependencies: - "@babel/runtime": ^7.10.1 - classnames: ^2.2.1 - rc-util: ^5.0.0 - resize-observer-polyfill: ^1.5.1 - peerDependencies: - react: ">=16.9.0" - react-dom: ">=16.9.0" - checksum: 1ce6af2c63a60bedcd9edd4b9e4632df36308c59177ff5e0de861e8cadf483f333f71713f584a2c7281691eaed87a1615c2edfeab6938b3c0a3e8bfb2553d600 - languageName: node - linkType: hard - "rc-resize-observer@npm:^1.0.0, rc-resize-observer@npm:^1.1.0, rc-resize-observer@npm:^1.2.0": version: 1.2.0 resolution: "rc-resize-observer@npm:1.2.0" @@ -23910,32 +23485,6 @@ __metadata: languageName: node linkType: hard -"rc-util@npm:^4.19.0": - version: 4.21.1 - resolution: "rc-util@npm:4.21.1" - dependencies: - add-dom-event-listener: ^1.1.0 - prop-types: ^15.5.10 - react-is: ^16.12.0 - react-lifecycles-compat: ^3.0.4 - shallowequal: ^1.1.0 - checksum: f87d321ad2088171eef990f179fe8b34db648cd5ad37439de381093891189e429ad8c791077205c56de9e26e37749fb3f4ecfceba8c35f983f540650c7ed4fb0 - languageName: node - linkType: hard - -"rc-util@npm:^5.0.0, rc-util@npm:^5.35.0": - version: 5.38.0 - resolution: "rc-util@npm:5.38.0" - dependencies: - "@babel/runtime": ^7.18.3 - react-is: ^18.2.0 - peerDependencies: - react: ">=16.9.0" - react-dom: ">=16.9.0" - checksum: 97a3e1dd94e199c12e69eed1f69e9a9be39b383f2b0d9e1d52d6fcdf676e91e6499fa7be4c48f37b08b6500747f2dc41014f9be54351a39f23b3b5087360690c - languageName: node - linkType: hard - "rc-util@npm:^5.0.1, rc-util@npm:^5.0.6, rc-util@npm:^5.15.0, rc-util@npm:^5.16.1, rc-util@npm:^5.18.1, rc-util@npm:^5.2.0, rc-util@npm:^5.3.0, rc-util@npm:^5.5.0, rc-util@npm:^5.6.1, rc-util@npm:^5.9.4": version: 5.18.1 resolution: "rc-util@npm:5.18.1" @@ -24312,7 +23861,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0, react-is@npm:^18.2.0": +"react-is@npm:^18.0.0": version: 18.2.0 resolution: "react-is@npm:18.2.0" checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e @@ -24774,15 +24323,6 @@ __metadata: languageName: node linkType: hard -"reactcss@npm:^1.2.3": - version: 1.2.3 - resolution: "reactcss@npm:1.2.3" - dependencies: - lodash: ^4.0.1 - checksum: c53e386a0881f1477e1cff661f6a6ad4c662230941f3827862193ac30f9b75cdf7bc7b4c7e5ca543d3e4e80fee1a3e9fa0056c206b1c0423726c41773ab3fe45 - languageName: node - linkType: hard - "reactstrap@npm:^7.1.0": version: 7.1.0 resolution: "reactstrap@npm:7.1.0" @@ -25777,13 +25317,6 @@ __metadata: languageName: node linkType: hard -"safe-stable-stringify@npm:^2.4.3": - version: 2.4.3 - resolution: "safe-stable-stringify@npm:2.4.3" - checksum: 3aeb64449706ee1f5ad2459fc99648b131d48e7a1fbb608d7c628020177512dc9d94108a5cb61bbc953985d313d0afea6566d243237743e02870490afef04b43 - languageName: node - linkType: hard - "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -27397,18 +26930,6 @@ __metadata: languageName: node linkType: hard -"swr@npm:^2.0.0": - version: 2.2.4 - resolution: "swr@npm:2.2.4" - dependencies: - client-only: ^0.0.1 - use-sync-external-store: ^1.2.0 - peerDependencies: - react: ^16.11.0 || ^17.0.0 || ^18.0.0 - checksum: d1398f89fd68af0e0cb6100a5769b1e17c0dbe8a778898643ad28456acda64add43344754c7d919e3f2ca82854adf86ff7d465aba25a2d555bcb669e457138f8 - languageName: node - linkType: hard - "symbol-observable@npm:1.2.0, symbol-observable@npm:^1.2.0": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" @@ -27807,13 +27328,6 @@ __metadata: languageName: node linkType: hard -"tinycolor2@npm:^1.4.2": - version: 1.6.0 - resolution: "tinycolor2@npm:1.6.0" - checksum: 6df4d07fceeedc0a878d7bac47e2cd47c1ceeb1078340a9eb8a295bc0651e17c750f73d47b3028d829f30b85c15e0572c0fd4142083e4c21a30a597e47f47230 - languageName: node - linkType: hard - "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -28096,13 +27610,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0": - version: 2.6.2 - resolution: "tslib@npm:2.6.2" - checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad - languageName: node - linkType: hard - "tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0": version: 2.3.1 resolution: "tslib@npm:2.3.1" @@ -28656,15 +28163,6 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.2.0": - version: 1.2.0 - resolution: "use-sync-external-store@npm:1.2.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 5c639e0f8da3521d605f59ce5be9e094ca772bd44a4ce7322b055a6f58eeed8dda3c94cabd90c7a41fb6fa852210092008afe48f7038792fd47501f33299116a - languageName: node - linkType: hard - "use@npm:^3.1.0": version: 3.1.1 resolution: "use@npm:3.1.1" @@ -28991,15 +28489,6 @@ __metadata: languageName: node linkType: hard -"warning@npm:^4.0.3": - version: 4.0.3 - resolution: "warning@npm:4.0.3" - dependencies: - loose-envify: ^1.0.0 - checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c - languageName: node - linkType: hard - "watchpack-chokidar2@npm:^2.0.1": version: 2.0.1 resolution: "watchpack-chokidar2@npm:2.0.1" From 806543ef42bcb614b12b005bff7987976346365a Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:52:50 +0300 Subject: [PATCH 31/32] Revert "Remove surplus pro-components library" This reverts commit acd64b8cfe458cf0330f1ac3c8b65ef66b3d3298. --- app/package.json | 1 + .../package.json | 1 + yarn.lock | 539 +++++++++++++++++- 3 files changed, 531 insertions(+), 10 deletions(-) diff --git a/app/package.json b/app/package.json index 5163622b0..76abec9d3 100644 --- a/app/package.json +++ b/app/package.json @@ -5,6 +5,7 @@ "dependencies": { "@2fd/ant-design-icons": "^2.6.0", "@ant-design/icons": "^4.7.0", + "@ant-design/pro-components": "^2.6.32", "@onaio/connected-private-route": "^0.0.11", "@onaio/connected-reducer-registry": "^0.0.3", "@onaio/gatekeeper": "1.0.0", diff --git a/packages/fhir-keycloak-user-management/package.json b/packages/fhir-keycloak-user-management/package.json index 1087ecafb..3df30c85c 100644 --- a/packages/fhir-keycloak-user-management/package.json +++ b/packages/fhir-keycloak-user-management/package.json @@ -23,6 +23,7 @@ "dist" ], "peerDependencies": { + "@ant-design/pro-components": "^2.6.32", "@opensrp/store": "^0.0.10", "antd": "^5.5.1", "i18next": "^19.8.4", diff --git a/yarn.lock b/yarn.lock index c5ed562e8..d2f4d49a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45,6 +45,24 @@ __metadata: languageName: node linkType: hard +"@ant-design/cssinjs@npm:^1.11.1": + version: 1.18.4 + resolution: "@ant-design/cssinjs@npm:1.18.4" + dependencies: + "@babel/runtime": ^7.11.1 + "@emotion/hash": ^0.8.0 + "@emotion/unitless": ^0.7.5 + classnames: ^2.3.1 + csstype: ^3.1.3 + rc-util: ^5.35.0 + stylis: ^4.0.13 + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: c6028097929a9948d1e9d5441aeab51ff824edacf98976fb06c146397c7cc9b2fc3f88765a0d93345b1fbbf6b766f638260652d5637fcbf6e6bbafe4e79e60bd + languageName: node + linkType: hard + "@ant-design/cssinjs@npm:^1.9.1": version: 1.9.1 resolution: "@ant-design/cssinjs@npm:1.9.1" @@ -70,6 +88,13 @@ __metadata: languageName: node linkType: hard +"@ant-design/icons-svg@npm:^4.3.0": + version: 4.3.1 + resolution: "@ant-design/icons-svg@npm:4.3.1" + checksum: 47f0474277366fb3b8bacfeb1691be35052c3f9b28811be7fb25ad219100533d0e31c2eec00a8dee744c34381a4cda7f39b39403e160811a8fd5d33b861e77aa + languageName: node + linkType: hard + "@ant-design/icons@npm:^4.0.0": version: 4.7.0 resolution: "@ant-design/icons@npm:4.7.0" @@ -102,6 +127,22 @@ __metadata: languageName: node linkType: hard +"@ant-design/icons@npm:^5.0.0": + version: 5.2.6 + resolution: "@ant-design/icons@npm:5.2.6" + dependencies: + "@ant-design/colors": ^7.0.0 + "@ant-design/icons-svg": ^4.3.0 + "@babel/runtime": ^7.11.2 + classnames: ^2.2.6 + rc-util: ^5.31.1 + peerDependencies: + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 2f571699b1903383cd09faa78e4cce34973debb0e7ec6223b9d9a0a6ab2b2f0c876072db62bbd4e6a45e864df5447343315e066abeffaf58aa5b97df3acc89f1 + languageName: node + linkType: hard + "@ant-design/icons@npm:^5.1.0": version: 5.1.4 resolution: "@ant-design/icons@npm:5.1.4" @@ -118,6 +159,247 @@ __metadata: languageName: node linkType: hard +"@ant-design/pro-card@npm:2.5.29": + version: 2.5.29 + resolution: "@ant-design/pro-card@npm:2.5.29" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.5 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + classnames: ^2.3.2 + omit.js: ^2.0.2 + rc-resize-observer: ^1.0.0 + rc-util: ^5.4.0 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + checksum: 23931bb51a3635640dd1cbcfc4f7ae745834115451d7d013a34c0161363c0560ca5a887b73049c6345c0b54645eb8bdabf53833f75d165dac5b90b845c601764 + languageName: node + linkType: hard + +"@ant-design/pro-components@npm:^2.6.32": + version: 2.6.48 + resolution: "@ant-design/pro-components@npm:2.6.48" + dependencies: + "@ant-design/pro-card": 2.5.29 + "@ant-design/pro-descriptions": 2.5.32 + "@ant-design/pro-field": 2.14.5 + "@ant-design/pro-form": 2.24.3 + "@ant-design/pro-layout": 7.17.19 + "@ant-design/pro-list": 2.5.47 + "@ant-design/pro-provider": 2.13.5 + "@ant-design/pro-skeleton": 2.1.10 + "@ant-design/pro-table": 3.14.1 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.16.3 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: aa7f5c4a10339acc44e13148adb480b6ed185d1aa67e7b7cd6ccf30885da0361035c02e962124f157bdc0c406eef93959b090da573bfc2b9b7aa434f261633e5 + languageName: node + linkType: hard + +"@ant-design/pro-descriptions@npm:2.5.32": + version: 2.5.32 + resolution: "@ant-design/pro-descriptions@npm:2.5.32" + dependencies: + "@ant-design/pro-field": 2.14.5 + "@ant-design/pro-form": 2.24.3 + "@ant-design/pro-skeleton": 2.1.10 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + rc-resize-observer: ^0.2.3 + rc-util: ^5.0.6 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + checksum: 8ba73018a7428bb95664405a7eef9f0284a4071606e97b89840b1f9bdbeb18d03aea16349dd41bf46f0ee2c0bc437df66a39fba334c205a48b51b46a7266f4e2 + languageName: node + linkType: hard + +"@ant-design/pro-field@npm:2.14.5": + version: 2.14.5 + resolution: "@ant-design/pro-field@npm:2.14.5" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.5 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + "@chenshuai2144/sketch-color": ^1.0.8 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.tonumber: ^4.0.3 + omit.js: ^2.0.2 + rc-util: ^5.4.0 + swr: ^2.0.0 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + checksum: 7fa505911dea5d5a81074be9feeae5db9639ba1ccc87d72273f0739a44e779da6e866ec2484025e1e0a242dc17f80186ecea4e9afc863e3319b8e8bf08817096 + languageName: node + linkType: hard + +"@ant-design/pro-form@npm:2.24.3": + version: 2.24.3 + resolution: "@ant-design/pro-form@npm:2.24.3" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-field": 2.14.5 + "@ant-design/pro-provider": 2.13.5 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + "@chenshuai2144/sketch-color": ^1.0.7 + "@umijs/use-params": ^1.0.9 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.merge: ^4.6.2 + omit.js: ^2.0.2 + rc-resize-observer: ^1.1.0 + rc-util: ^5.0.6 + peerDependencies: + "@types/lodash.merge": ^4.6.7 + antd: ^4.24.15 || ^5.11.2 + rc-field-form: ^1.22.0 + react: ">=17.0.0" + react-dom: ">=17.0.0" + peerDependenciesMeta: + "@types/lodash.merge": + optional: true + checksum: 6c48404ca267c286c5bbfdb9c96eb26ab33617370f9ee5973544487e21aa861cd14b2f7a506daa183f44613fdd7eadd1c88827f1bd3aa89c90c2b627263d84e5 + languageName: node + linkType: hard + +"@ant-design/pro-layout@npm:7.17.19": + version: 7.17.19 + resolution: "@ant-design/pro-layout@npm:7.17.19" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.5 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + "@umijs/route-utils": ^4.0.0 + "@umijs/use-params": ^1.0.9 + classnames: ^2.3.2 + lodash.merge: ^4.6.2 + omit.js: ^2.0.2 + path-to-regexp: 2.4.0 + rc-resize-observer: ^1.1.0 + rc-util: ^5.0.6 + swr: ^2.0.0 + warning: ^4.0.3 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 6cfb01a99f630f2d73030afc79c24c6321492298416725fa06329a7eed28bc459040ae3d63f5a77b31393f105e540c525426a9489d6e445d7c244e938e1a195a + languageName: node + linkType: hard + +"@ant-design/pro-list@npm:2.5.47": + version: 2.5.47 + resolution: "@ant-design/pro-list@npm:2.5.47" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-card": 2.5.29 + "@ant-design/pro-field": 2.14.5 + "@ant-design/pro-table": 3.14.1 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + classnames: ^2.3.2 + dayjs: ^1.11.10 + rc-resize-observer: ^1.0.0 + rc-util: ^4.19.0 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 65b313ef0b7b8438ad2867a39af58fa4daf32b23742310f40db5f2b1acfe4063a710056caa026e9daf87a3e9e3836d4db5c24de7e594d5aeee58818f647c1669 + languageName: node + linkType: hard + +"@ant-design/pro-provider@npm:2.13.5": + version: 2.13.5 + resolution: "@ant-design/pro-provider@npm:2.13.5" + dependencies: + "@ant-design/cssinjs": ^1.11.1 + "@babel/runtime": ^7.18.0 + "@ctrl/tinycolor": ^3.4.0 + rc-util: ^5.0.1 + swr: ^2.0.0 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 533207977b954f440eac9cdad5019292547d934bd3b5051cc09440853945f2781029dd4a0ec1906af3758f64f081d6d81ce44c4b76cb22c7e037acff5d53fe88 + languageName: node + linkType: hard + +"@ant-design/pro-skeleton@npm:2.1.10": + version: 2.1.10 + resolution: "@ant-design/pro-skeleton@npm:2.1.10" + dependencies: + "@babel/runtime": ^7.18.0 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 4f57286e64e1a049f1cf7294da2c6f29000f1aa2a339e3f07e451e89dbdf82909a6a2a689abf432399b6bec595a3f5b9754021397ffaae4491d803af773cfe95 + languageName: node + linkType: hard + +"@ant-design/pro-table@npm:3.14.1": + version: 3.14.1 + resolution: "@ant-design/pro-table@npm:3.14.1" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-card": 2.5.29 + "@ant-design/pro-field": 2.14.5 + "@ant-design/pro-form": 2.24.3 + "@ant-design/pro-provider": 2.13.5 + "@ant-design/pro-utils": 2.15.4 + "@babel/runtime": ^7.18.0 + "@dnd-kit/core": ^6.0.8 + "@dnd-kit/modifiers": ^6.0.1 + "@dnd-kit/sortable": ^7.0.2 + "@dnd-kit/utilities": ^3.2.1 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.merge: ^4.6.2 + omit.js: ^2.0.2 + rc-resize-observer: ^1.0.0 + rc-util: ^5.0.1 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + rc-field-form: ^1.22.0 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 639df8e94f5b1adad24a588ecdd7b1796f205cd3783d91ef0bf5e8d454a495b223e09c06d5cb1be15df482ac3c39f80a4dc2a8fab929fdfb4f4a55b2093b6182 + languageName: node + linkType: hard + +"@ant-design/pro-utils@npm:2.15.4": + version: 2.15.4 + resolution: "@ant-design/pro-utils@npm:2.15.4" + dependencies: + "@ant-design/icons": ^5.0.0 + "@ant-design/pro-provider": 2.13.5 + "@babel/runtime": ^7.18.0 + classnames: ^2.3.2 + dayjs: ^1.11.10 + lodash.merge: ^4.6.2 + rc-util: ^5.0.6 + safe-stable-stringify: ^2.4.3 + swr: ^2.0.0 + peerDependencies: + antd: ^4.24.15 || ^5.11.2 + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 5cce8d9522aa270eafc1c7f83a02b7ff9d2615188318cfe00952c5505dfb5a5de191efacad13518b9a1b1c97c8c864f712413b38aae79e7e14c913ef24e8ffb4 + languageName: node + linkType: hard + "@ant-design/react-slick@npm:~1.0.0": version: 1.0.1 resolution: "@ant-design/react-slick@npm:1.0.1" @@ -2021,6 +2303,18 @@ __metadata: languageName: node linkType: hard +"@chenshuai2144/sketch-color@npm:^1.0.7, @chenshuai2144/sketch-color@npm:^1.0.8": + version: 1.0.9 + resolution: "@chenshuai2144/sketch-color@npm:1.0.9" + dependencies: + reactcss: ^1.2.3 + tinycolor2: ^1.4.2 + peerDependencies: + react: ">=16.12.0" + checksum: 7337f9a24abc7630f2b839b1cce875ff07061b8b568d0d72c9528db9dc3c0ec7db78242b20180b419c60ecb2766533487d7cd77000f0e24dc7d049f8bc86db50 + languageName: node + linkType: hard + "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -2159,6 +2453,68 @@ __metadata: languageName: node linkType: hard +"@dnd-kit/accessibility@npm:^3.1.0": + version: 3.1.0 + resolution: "@dnd-kit/accessibility@npm:3.1.0" + dependencies: + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + checksum: fcb88c961e2f4c226ab575bc4a13712419884bb0f60761befcaa23bcb6c9939dc2cac6633416f2a07baee9a8830350c6df444039332408cdaaf27cad17c6b64b + languageName: node + linkType: hard + +"@dnd-kit/core@npm:^6.0.8": + version: 6.1.0 + resolution: "@dnd-kit/core@npm:6.1.0" + dependencies: + "@dnd-kit/accessibility": ^3.1.0 + "@dnd-kit/utilities": ^3.2.2 + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 3b8f46d2f4d2723abad4721c7bc4d1df2c4f6f26ce54673243666212cfa6f34f33e3255b53144a847da469dc736c966c19e4c45330f967ce8c01f8f878ec7f5b + languageName: node + linkType: hard + +"@dnd-kit/modifiers@npm:^6.0.1": + version: 6.0.1 + resolution: "@dnd-kit/modifiers@npm:6.0.1" + dependencies: + "@dnd-kit/utilities": ^3.2.1 + tslib: ^2.0.0 + peerDependencies: + "@dnd-kit/core": ^6.0.6 + react: ">=16.8.0" + checksum: cd31715aac81baa2398558dc7c877a483d1c4c41cdb2f466557fdc41bb742db5cd1b34b3b84332b94ac4a2835e7e5a0e28c64e621936e976399788f1dd029ea4 + languageName: node + linkType: hard + +"@dnd-kit/sortable@npm:^7.0.2": + version: 7.0.2 + resolution: "@dnd-kit/sortable@npm:7.0.2" + dependencies: + "@dnd-kit/utilities": ^3.2.0 + tslib: ^2.0.0 + peerDependencies: + "@dnd-kit/core": ^6.0.7 + react: ">=16.8.0" + checksum: 4ce705aceb15766a0deefe25a9d95a87e9413c3fb9088ea3eb0962e57f844895000117fcec7c0944a0d4ae4e1e889cfa69e3d3778164d4d23115fb1edb218283 + languageName: node + linkType: hard + +"@dnd-kit/utilities@npm:^3.2.0, @dnd-kit/utilities@npm:^3.2.1, @dnd-kit/utilities@npm:^3.2.2": + version: 3.2.2 + resolution: "@dnd-kit/utilities@npm:3.2.2" + dependencies: + tslib: ^2.0.0 + peerDependencies: + react: ">=16.8.0" + checksum: 8a5015c2faa52760ab82a64287b2ac6a3d798867a1bca5ccbc1178560dbd9a1f9f1a21faea80f590ba1a4277c3eb7e7c4d3b4a39f1f32171bf6bc8174b370547 + languageName: node + linkType: hard + "@emotion/hash@npm:^0.8.0": version: 0.8.0 resolution: "@emotion/hash@npm:0.8.0" @@ -3673,6 +4029,7 @@ __metadata: react-i18next: ^11.8.10 uuid: ^8.3.1 peerDependencies: + "@ant-design/pro-components": ^2.6.32 "@opensrp/store": ^0.0.10 antd: ^5.5.1 i18next: ^19.8.4 @@ -3979,6 +4336,7 @@ __metadata: dependencies: "@2fd/ant-design-icons": ^2.6.0 "@ant-design/icons": ^4.7.0 + "@ant-design/pro-components": ^2.6.32 "@onaio/connected-private-route": ^0.0.11 "@onaio/connected-reducer-registry": ^0.0.3 "@onaio/gatekeeper": 1.0.0 @@ -5942,6 +6300,22 @@ __metadata: languageName: node linkType: hard +"@umijs/route-utils@npm:^4.0.0": + version: 4.0.1 + resolution: "@umijs/route-utils@npm:4.0.1" + checksum: e1ca91cf6da16d7623cc0a8816cf2655e1f8b7fcc2507d455383b31f7e1979e52bc2464453173e68b660d24c4255be44d521d280e580c025af6bed83b5727241 + languageName: node + linkType: hard + +"@umijs/use-params@npm:^1.0.9": + version: 1.0.9 + resolution: "@umijs/use-params@npm:1.0.9" + peerDependencies: + react: "*" + checksum: 7f72436440dff0e1911ee916340bd26c6c9a510bad4af536d3ec9a5a175d37255d42dbd1cb0dc996daf4d0c3b76e44816ac423ac73e6b3c1655b6b839fed175c + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -6464,6 +6838,15 @@ __metadata: languageName: node linkType: hard +"add-dom-event-listener@npm:^1.1.0": + version: 1.1.0 + resolution: "add-dom-event-listener@npm:1.1.0" + dependencies: + object-assign: 4.x + checksum: 7685d32c4a9d78ad7a36641774168d80e195ef0710ae801e7211d849100c0e4499657cf3838bdae5ee9134b5aa9d7f50d68dec73b03e7d0082302804efff6ea6 + languageName: node + linkType: hard + "add-stream@npm:^1.0.0": version: 1.0.0 resolution: "add-stream@npm:1.0.0" @@ -8838,6 +9221,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -10186,6 +10576,13 @@ __metadata: languageName: node linkType: hard +"csstype@npm:^3.1.3": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 8db785cc92d259102725b3c694ec0c823f5619a84741b5c7991b8ad135dfaa66093038a1cc63e03361a6cd28d122be48f2106ae72334e067dd619a51f49eddf7 + languageName: node + linkType: hard + "csv-parse@npm:^4.4.6": version: 4.16.3 resolution: "csv-parse@npm:4.16.3" @@ -10297,6 +10694,13 @@ __metadata: languageName: node linkType: hard +"dayjs@npm:^1.11.10": + version: 1.11.10 + resolution: "dayjs@npm:1.11.10" + checksum: a6b5a3813b8884f5cd557e2e6b7fa569f4c5d0c97aca9558e38534af4f2d60daafd3ff8c2000fed3435cfcec9e805bcebd99f90130c6d1c5ef524084ced588c4 + languageName: node + linkType: hard + "de-indent@npm:^1.0.2": version: 1.0.2 resolution: "de-indent@npm:1.0.2" @@ -17739,7 +18143,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:>=3.5 <5, lodash@npm:^4.0.0, lodash@npm:^4.1.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.7.0": +"lodash@npm:>=3.5 <5, lodash@npm:^4.0.0, lodash@npm:^4.0.1, lodash@npm:^4.1.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -19277,6 +19681,13 @@ __metadata: languageName: node linkType: hard +"object-assign@npm:4.x, object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + "object-assign@npm:^3.0.0": version: 3.0.0 resolution: "object-assign@npm:3.0.0" @@ -19284,13 +19695,6 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": - version: 4.1.1 - resolution: "object-assign@npm:4.1.1" - checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f - languageName: node - linkType: hard - "object-copy@npm:^0.1.0": version: 0.1.0 resolution: "object-copy@npm:0.1.0" @@ -19438,6 +19842,13 @@ __metadata: languageName: node linkType: hard +"omit.js@npm:^2.0.2": + version: 2.0.2 + resolution: "omit.js@npm:2.0.2" + checksum: 5d802b9fd7640250aada82f3b9b7243b554b38911f29b3de0d1066c00f24dd4ee72d3b9c94c582e373fb6511bd21e107917d419a7b2a04287f26c31133b48a15 + languageName: node + linkType: hard + "on-finished@npm:~2.3.0": version: 2.3.0 resolution: "on-finished@npm:2.3.0" @@ -20133,6 +20544,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:2.4.0": + version: 2.4.0 + resolution: "path-to-regexp@npm:2.4.0" + checksum: 581175bf2968e51452f2b8c71f10e75c995693668b4ecf7d0b48962fbe0c56830661ca5dd5fd6d8e2f0cc9a045ce07e89af504ab133e1d21887c2712df85b1f4 + languageName: node + linkType: hard + "path-to-regexp@npm:^1.7.0": version: 1.8.0 resolution: "path-to-regexp@npm:1.8.0" @@ -22213,7 +22631,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.5.8, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:^15.5.10, prop-types@npm:^15.5.8, prop-types@npm:^15.6.1, prop-types@npm:^15.6.2, prop-types@npm:^15.7.0, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -23082,6 +23500,21 @@ __metadata: languageName: node linkType: hard +"rc-resize-observer@npm:^0.2.3": + version: 0.2.6 + resolution: "rc-resize-observer@npm:0.2.6" + dependencies: + "@babel/runtime": ^7.10.1 + classnames: ^2.2.1 + rc-util: ^5.0.0 + resize-observer-polyfill: ^1.5.1 + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 1ce6af2c63a60bedcd9edd4b9e4632df36308c59177ff5e0de861e8cadf483f333f71713f584a2c7281691eaed87a1615c2edfeab6938b3c0a3e8bfb2553d600 + languageName: node + linkType: hard + "rc-resize-observer@npm:^1.0.0, rc-resize-observer@npm:^1.1.0, rc-resize-observer@npm:^1.2.0": version: 1.2.0 resolution: "rc-resize-observer@npm:1.2.0" @@ -23485,6 +23918,32 @@ __metadata: languageName: node linkType: hard +"rc-util@npm:^4.19.0": + version: 4.21.1 + resolution: "rc-util@npm:4.21.1" + dependencies: + add-dom-event-listener: ^1.1.0 + prop-types: ^15.5.10 + react-is: ^16.12.0 + react-lifecycles-compat: ^3.0.4 + shallowequal: ^1.1.0 + checksum: f87d321ad2088171eef990f179fe8b34db648cd5ad37439de381093891189e429ad8c791077205c56de9e26e37749fb3f4ecfceba8c35f983f540650c7ed4fb0 + languageName: node + linkType: hard + +"rc-util@npm:^5.0.0, rc-util@npm:^5.35.0": + version: 5.38.1 + resolution: "rc-util@npm:5.38.1" + dependencies: + "@babel/runtime": ^7.18.3 + react-is: ^18.2.0 + peerDependencies: + react: ">=16.9.0" + react-dom: ">=16.9.0" + checksum: 40d0411fb5d6b0a187e718ff16c18f3d68eae3d7e4def43a9a9b2690b89cfce639077a69d683aa01302f8132394dd633baf76b07e5a3b8438fb706b1abb31937 + languageName: node + linkType: hard + "rc-util@npm:^5.0.1, rc-util@npm:^5.0.6, rc-util@npm:^5.15.0, rc-util@npm:^5.16.1, rc-util@npm:^5.18.1, rc-util@npm:^5.2.0, rc-util@npm:^5.3.0, rc-util@npm:^5.5.0, rc-util@npm:^5.6.1, rc-util@npm:^5.9.4": version: 5.18.1 resolution: "rc-util@npm:5.18.1" @@ -23861,7 +24320,7 @@ __metadata: languageName: node linkType: hard -"react-is@npm:^18.0.0": +"react-is@npm:^18.0.0, react-is@npm:^18.2.0": version: 18.2.0 resolution: "react-is@npm:18.2.0" checksum: e72d0ba81b5922759e4aff17e0252bd29988f9642ed817f56b25a3e217e13eea8a7f2322af99a06edb779da12d5d636e9fda473d620df9a3da0df2a74141d53e @@ -24323,6 +24782,15 @@ __metadata: languageName: node linkType: hard +"reactcss@npm:^1.2.3": + version: 1.2.3 + resolution: "reactcss@npm:1.2.3" + dependencies: + lodash: ^4.0.1 + checksum: c53e386a0881f1477e1cff661f6a6ad4c662230941f3827862193ac30f9b75cdf7bc7b4c7e5ca543d3e4e80fee1a3e9fa0056c206b1c0423726c41773ab3fe45 + languageName: node + linkType: hard + "reactstrap@npm:^7.1.0": version: 7.1.0 resolution: "reactstrap@npm:7.1.0" @@ -25317,6 +25785,13 @@ __metadata: languageName: node linkType: hard +"safe-stable-stringify@npm:^2.4.3": + version: 2.4.3 + resolution: "safe-stable-stringify@npm:2.4.3" + checksum: 3aeb64449706ee1f5ad2459fc99648b131d48e7a1fbb608d7c628020177512dc9d94108a5cb61bbc953985d313d0afea6566d243237743e02870490afef04b43 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -26930,6 +27405,18 @@ __metadata: languageName: node linkType: hard +"swr@npm:^2.0.0": + version: 2.2.4 + resolution: "swr@npm:2.2.4" + dependencies: + client-only: ^0.0.1 + use-sync-external-store: ^1.2.0 + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + checksum: d1398f89fd68af0e0cb6100a5769b1e17c0dbe8a778898643ad28456acda64add43344754c7d919e3f2ca82854adf86ff7d465aba25a2d555bcb669e457138f8 + languageName: node + linkType: hard + "symbol-observable@npm:1.2.0, symbol-observable@npm:^1.2.0": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" @@ -27328,6 +27815,13 @@ __metadata: languageName: node linkType: hard +"tinycolor2@npm:^1.4.2": + version: 1.6.0 + resolution: "tinycolor2@npm:1.6.0" + checksum: 6df4d07fceeedc0a878d7bac47e2cd47c1ceeb1078340a9eb8a295bc0651e17c750f73d47b3028d829f30b85c15e0572c0fd4142083e4c21a30a597e47f47230 + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -27610,6 +28104,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.0.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0": version: 2.3.1 resolution: "tslib@npm:2.3.1" @@ -28163,6 +28664,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.2.0": + version: 1.2.0 + resolution: "use-sync-external-store@npm:1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 5c639e0f8da3521d605f59ce5be9e094ca772bd44a4ce7322b055a6f58eeed8dda3c94cabd90c7a41fb6fa852210092008afe48f7038792fd47501f33299116a + languageName: node + linkType: hard + "use@npm:^3.1.0": version: 3.1.1 resolution: "use@npm:3.1.1" @@ -28489,6 +28999,15 @@ __metadata: languageName: node linkType: hard +"warning@npm:^4.0.3": + version: 4.0.3 + resolution: "warning@npm:4.0.3" + dependencies: + loose-envify: ^1.0.0 + checksum: 4f2cb6a9575e4faf71ddad9ad1ae7a00d0a75d24521c193fa464f30e6b04027bd97aa5d9546b0e13d3a150ab402eda216d59c1d0f2d6ca60124d96cd40dfa35c + languageName: node + linkType: hard + "watchpack-chokidar2@npm:^2.0.1": version: 2.0.1 resolution: "watchpack-chokidar2@npm:2.0.1" From d891d8ade966eb29bb74659fc04f64153aa7b805 Mon Sep 17 00:00:00 2001 From: Peter Muriuki Date: Mon, 22 Jan 2024 11:58:38 +0300 Subject: [PATCH 32/32] Fix lint issues --- .../src/components/UserDeleteBtn/index.tsx | 5 ++++- .../components/UserList/ListView/tests/index.test.tsx | 10 ++++++++-- .../components/fhirDataTypes/CodeableConcept/index.tsx | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx index e05dba650..14cd3b775 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserDeleteBtn/index.tsx @@ -27,7 +27,10 @@ export const UserDeleteBtn = (props: UserDeleteBtnProp) => { onConfirm={async () => { await deleteUser(keycloakBaseUrl, fhirBaseUrl, resourceId, t); try { - return await queryClient.invalidateQueries({queryKey: [KEYCLOAK_URL_USERS], exact: true}); + return await queryClient.invalidateQueries({ + queryKey: [KEYCLOAK_URL_USERS], + exact: true, + }); } catch { return sendErrorNotification( t('Failed to update data, please refresh the page to see the most recent changes') diff --git a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx index 1bbe95c88..75fc45839 100644 --- a/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx +++ b/packages/fhir-keycloak-user-management/src/components/UserList/ListView/tests/index.test.tsx @@ -13,7 +13,11 @@ import userEvents from '@testing-library/user-event'; import { URL_USER, URL_USER_CREDENTIALS, UserCredentials } from '@opensrp/user-management'; import { practitioner, userFixtures, group } from './fixtures'; import fetch from 'jest-fetch-mock'; -import { USER_DETAILS_URL, practitionerResourceType, practitionerRoleResourceType } from '../../../../constants'; +import { + USER_DETAILS_URL, + practitionerResourceType, + practitionerRoleResourceType, +} from '../../../../constants'; import flushPromises from 'flush-promises'; import { practitionerRoleBundle } from '../../../CreateEditUser/tests/fixtures'; import { RoleContext } from '@opensrp/rbac'; @@ -332,5 +336,7 @@ test('View details navigates correctly', async () => { `); fireEvent.click(viewDetailsLink); - expect(history.location.pathname).toEqual(`${USER_DETAILS_URL}/081724e8-5fc1-47dd-8d0c-fa0c6ae6ddf0`); + expect(history.location.pathname).toEqual( + `${USER_DETAILS_URL}/081724e8-5fc1-47dd-8d0c-fa0c6ae6ddf0` + ); }); diff --git a/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx b/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx index b3b38d5e8..8c2b6bf45 100644 --- a/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx +++ b/packages/react-utils/src/components/fhirDataTypes/CodeableConcept/index.tsx @@ -23,5 +23,9 @@ export const CodeableConcept = (props: CodeableConceptProps) => { ))} ); - return {text ? {text} : codingsTitle}; + return ( + + {text ? {text} : codingsTitle} + + ); };