@palladium-ethiopia/esm-admin-app 5.4.2-pre.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/.turbo/turbo-build.log +15 -0
  2. package/README.md +12 -0
  3. package/dist/117.js +1 -0
  4. package/dist/152.js +1 -0
  5. package/dist/152.js.map +1 -0
  6. package/dist/209.js +1 -0
  7. package/dist/209.js.map +1 -0
  8. package/dist/41.js +1 -0
  9. package/dist/41.js.map +1 -0
  10. package/dist/442.js +1 -0
  11. package/dist/442.js.map +1 -0
  12. package/dist/466.js +1 -0
  13. package/dist/466.js.map +1 -0
  14. package/dist/555.js +1 -0
  15. package/dist/555.js.map +1 -0
  16. package/dist/61.js +1 -0
  17. package/dist/61.js.map +1 -0
  18. package/dist/672.js +15 -0
  19. package/dist/672.js.map +1 -0
  20. package/dist/689.js +1 -0
  21. package/dist/689.js.map +1 -0
  22. package/dist/710.js +1 -0
  23. package/dist/710.js.map +1 -0
  24. package/dist/712.js +1 -0
  25. package/dist/712.js.map +1 -0
  26. package/dist/771.js +1 -0
  27. package/dist/771.js.map +1 -0
  28. package/dist/789.js +1 -0
  29. package/dist/789.js.map +1 -0
  30. package/dist/806.js +1 -0
  31. package/dist/826.js +1 -0
  32. package/dist/826.js.map +1 -0
  33. package/dist/914.js +27 -0
  34. package/dist/914.js.map +1 -0
  35. package/dist/926.js +17 -0
  36. package/dist/926.js.map +1 -0
  37. package/dist/ethiopia-esm-admin-app.js +6 -0
  38. package/dist/ethiopia-esm-admin-app.js.buildmanifest.json +556 -0
  39. package/dist/ethiopia-esm-admin-app.js.map +1 -0
  40. package/dist/main.js +32 -0
  41. package/dist/main.js.map +1 -0
  42. package/dist/routes.json +1 -0
  43. package/jest.config.js +8 -0
  44. package/package.json +52 -0
  45. package/rspack.config.js +1 -0
  46. package/src/components/confirm-modal/confirmation-operation-modal.component.tsx +43 -0
  47. package/src/components/confirm-modal/confirmation-operation.test.tsx +69 -0
  48. package/src/components/dashboard/dashboard.component.tsx +131 -0
  49. package/src/components/dashboard/dashboard.scss +38 -0
  50. package/src/components/dashboard/etl-dashboard.component.tsx +11 -0
  51. package/src/components/empty-state/empty-state-log.components.tsx +20 -0
  52. package/src/components/empty-state/empty-state-log.scss +28 -0
  53. package/src/components/empty-state/empty-state-log.test.tsx +24 -0
  54. package/src/components/facility-setup/card.component.tsx +16 -0
  55. package/src/components/facility-setup/facility-info.component.tsx +142 -0
  56. package/src/components/facility-setup/facility-info.scss +87 -0
  57. package/src/components/facility-setup/facility-setup.component.tsx +21 -0
  58. package/src/components/facility-setup/facility-setup.resource.tsx +7 -0
  59. package/src/components/facility-setup/facility-setup.scss +38 -0
  60. package/src/components/facility-setup/header/header.component.tsx +23 -0
  61. package/src/components/facility-setup/header/header.scss +19 -0
  62. package/src/components/header/header-illustration.component.tsx +13 -0
  63. package/src/components/header/header.component.tsx +28 -0
  64. package/src/components/header/header.scss +19 -0
  65. package/src/components/hook/healthWorkerAdapter.ts +213 -0
  66. package/src/components/hook/useFacilityInfo.tsx +37 -0
  67. package/src/components/hook/useSystemRoleSetting.tsx +33 -0
  68. package/src/components/locations/auto-suggest/autosuggest.component.tsx +149 -0
  69. package/src/components/locations/auto-suggest/autosuggest.scss +61 -0
  70. package/src/components/locations/auto-suggest/location-autosuggest.component.tsx +94 -0
  71. package/src/components/locations/auto-suggest/location-autosuggest.scss +48 -0
  72. package/src/components/locations/common/results-tile.component.tsx +45 -0
  73. package/src/components/locations/common/results-tile.scss +86 -0
  74. package/src/components/locations/forms/add-location/add-location.workspace.scss +34 -0
  75. package/src/components/locations/forms/add-location/add-location.workspace.tsx +200 -0
  76. package/src/components/locations/forms/search-location/search-location.workspace.scss +79 -0
  77. package/src/components/locations/forms/search-location/search-location.workspace.tsx +215 -0
  78. package/src/components/locations/header/header.component.tsx +48 -0
  79. package/src/components/locations/header/header.scss +58 -0
  80. package/src/components/locations/helpers/index.ts +16 -0
  81. package/src/components/locations/home/home-locations.component.tsx +18 -0
  82. package/src/components/locations/home/home-locations.scss +8 -0
  83. package/src/components/locations/hooks/UseFacilityLocations.ts +12 -0
  84. package/src/components/locations/hooks/useLocation.ts +18 -0
  85. package/src/components/locations/hooks/useLocationTags.ts +15 -0
  86. package/src/components/locations/tables/locations-table.component.tsx +243 -0
  87. package/src/components/locations/tables/locations-table.resource.ts +26 -0
  88. package/src/components/locations/tables/locations-table.scss +115 -0
  89. package/src/components/locations/types/index.ts +120 -0
  90. package/src/components/locations/utils/index.ts +5 -0
  91. package/src/components/logs-table/operation-log-resource.ts +41 -0
  92. package/src/components/logs-table/operation-log-table.component.tsx +120 -0
  93. package/src/components/logs-table/operation-log.scss +10 -0
  94. package/src/components/logs-table/operation-log.test.tsx +47 -0
  95. package/src/components/modal/hwr-confirmation.modal.scss +21 -0
  96. package/src/components/modal/hwr-confirmation.modal.tsx +170 -0
  97. package/src/components/modal/hwr-empty.modal.component.tsx +54 -0
  98. package/src/components/modal/hwr-sync.modal.scss +30 -0
  99. package/src/components/modal/hwr-sync.modal.tsx +209 -0
  100. package/src/components/modal/hwr-sync.resource.ts +23 -0
  101. package/src/components/provider-banner/provider-banner.component.tsx +106 -0
  102. package/src/components/provider-banner/provider-banner.module.scss +51 -0
  103. package/src/components/provider-banner/provider-banner.resource.ts +29 -0
  104. package/src/components/side-menu/left-panel.scss +42 -0
  105. package/src/components/side-menu/left-pannel.component.tsx +22 -0
  106. package/src/components/users/header/header.scss +90 -0
  107. package/src/components/users/header/user-management-header.component.tsx +42 -0
  108. package/src/components/users/manage-users/hooks/useProviderAttributeMapping.ts +110 -0
  109. package/src/components/users/manage-users/hooks/useUserFormSteps.ts +119 -0
  110. package/src/components/users/manage-users/hooks/useUserFormSubmission.ts +264 -0
  111. package/src/components/users/manage-users/hooks/useUserManagementForm.ts +122 -0
  112. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-list/user-role-scope-list.component.tsx +177 -0
  113. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-fields.scss +117 -0
  114. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope-fields.component.tsx +290 -0
  115. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope.workspace.tsx +316 -0
  116. package/src/components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/userRoleScopeFormSchema.tsx +43 -0
  117. package/src/components/users/manage-users/manage-user.component.tsx +19 -0
  118. package/src/components/users/manage-users/manage-user.scss +31 -0
  119. package/src/components/users/manage-users/provider-autosuggest.component.tsx +117 -0
  120. package/src/components/users/manage-users/provider-search.resource.ts +34 -0
  121. package/src/components/users/manage-users/sections/demographic-section.component.tsx +156 -0
  122. package/src/components/users/manage-users/sections/login-section.component.tsx +88 -0
  123. package/src/components/users/manage-users/sections/provider-section.component.tsx +270 -0
  124. package/src/components/users/manage-users/sections/roles-section.component.tsx +88 -0
  125. package/src/components/users/manage-users/user-details/user-detail.scss +75 -0
  126. package/src/components/users/manage-users/user-details/user-details.component.tsx +182 -0
  127. package/src/components/users/manage-users/user-list/user-list.component.tsx +378 -0
  128. package/src/components/users/manage-users/user-list/user-list.resource.ts +30 -0
  129. package/src/components/users/manage-users/user-list/user-list.scss +37 -0
  130. package/src/components/users/manage-users/user-management.constants.ts +20 -0
  131. package/src/components/users/manage-users/user-management.utils.ts +100 -0
  132. package/src/components/users/manage-users/user-management.workspace.scss +172 -0
  133. package/src/components/users/manage-users/user-management.workspace.tsx +334 -0
  134. package/src/components/users/userManagementFormSchema.tsx +179 -0
  135. package/src/config-schema.ts +142 -0
  136. package/src/constants.ts +50 -0
  137. package/src/declarations.d.ts +2 -0
  138. package/src/index.ts +55 -0
  139. package/src/left-pannel-link.component.tsx +40 -0
  140. package/src/root.component.tsx +39 -0
  141. package/src/root.scss +12 -0
  142. package/src/routes.json +100 -0
  143. package/src/setup-tests.ts +1 -0
  144. package/src/types/index.ts +385 -0
  145. package/src/user-management.resources.ts +232 -0
  146. package/src/utils/utils.ts +20 -0
  147. package/translations/am.json +159 -0
  148. package/translations/en.json +159 -0
  149. package/tsconfig.json +5 -0
@@ -0,0 +1,179 @@
1
+ import { useTranslation } from 'react-i18next';
2
+ import { z } from 'zod';
3
+
4
+ const PHONE_REGEX = /^\+?[\d\s\-()]+$/;
5
+ const MIN_PHONE_DIGITS = 9;
6
+ const MIN_USERNAME_LENGTH = 3;
7
+ const MIN_PASSWORD_LENGTH = 8;
8
+
9
+ const UserManagementFormSchema = (
10
+ existingUsernames: Array<string>,
11
+ isEdit?: boolean,
12
+ shouldValidatePhoneEmail = true,
13
+ isInitialValuesEmpty = true,
14
+ ) => {
15
+ const { t } = useTranslation();
16
+
17
+ const userManagementFormSchema = z
18
+ .object({
19
+ givenName: z.string().nonempty(t('givenNameRequired', 'Given name is required')),
20
+ middleName: z.string().optional(),
21
+ familyName: z.string().nonempty(t('familyNameRequired', 'Family name is required')),
22
+ gender: z.enum(['M', 'F'], {
23
+ errorMap: () => ({
24
+ message: t('genderRequired', 'Gender is required'),
25
+ }),
26
+ }),
27
+ phoneNumber: shouldValidatePhoneEmail
28
+ ? z
29
+ .string()
30
+ .optional()
31
+ .refine(
32
+ (val) => {
33
+ if (!val || val.trim() === '') {
34
+ return true;
35
+ }
36
+ const digitsOnly = val.replace(/\D/g, '');
37
+ return PHONE_REGEX.test(val) && digitsOnly.length >= MIN_PHONE_DIGITS;
38
+ },
39
+ { message: t('invalidPhoneNumber', 'Invalid phone number') },
40
+ )
41
+ : z.string().optional(),
42
+ email: shouldValidatePhoneEmail
43
+ ? z
44
+ .string()
45
+ .optional()
46
+ .refine((val) => !val || val.trim() === '' || z.string().email().safeParse(val).success, {
47
+ message: t('invalidEmail', 'Invalid email address'),
48
+ })
49
+ : z.string().optional(),
50
+ providerIdentifiers: z.boolean().optional(),
51
+ username: z
52
+ .string()
53
+ .optional()
54
+ .transform((s) => s?.trim() ?? '')
55
+ .pipe(
56
+ z
57
+ .string()
58
+ .min(1, t('usernameRequired', 'Username is required'))
59
+ .min(
60
+ MIN_USERNAME_LENGTH,
61
+ t('usernameMinLength', 'Username must be at least {{count}} characters', {
62
+ count: MIN_USERNAME_LENGTH,
63
+ }),
64
+ )
65
+ .refine((value) => !existingUsernames.includes(value), {
66
+ message: t('usernameTaken', 'Username already exists'),
67
+ }),
68
+ ),
69
+ password: isInitialValuesEmpty
70
+ ? z
71
+ .string()
72
+ .optional()
73
+ .transform((s) => s?.trim() ?? '')
74
+ .pipe(
75
+ z
76
+ .string()
77
+ .min(1, t('passwordRequired', 'Password is required'))
78
+ .min(
79
+ MIN_PASSWORD_LENGTH,
80
+ t('passwordMinLength', 'Password must be at least {{count}} characters', {
81
+ count: MIN_PASSWORD_LENGTH,
82
+ }),
83
+ )
84
+ .refine((val) => /[a-z]/.test(val) && /[A-Z]/.test(val) && /[0-9]/.test(val), {
85
+ message: t(
86
+ 'passwordCaseRequired',
87
+ 'Password must contain at least one lowercase letter, one uppercase letter, and one number.',
88
+ ),
89
+ }),
90
+ )
91
+ : z
92
+ .string()
93
+ .optional()
94
+ .transform((s) => s?.trim() ?? ''),
95
+ confirmPassword: isInitialValuesEmpty
96
+ ? z
97
+ .string()
98
+ .optional()
99
+ .transform((s) => s?.trim() ?? '')
100
+ .pipe(z.string().min(1, t('confirmPasswordRequired', 'Please confirm your password')))
101
+ : z
102
+ .string()
103
+ .optional()
104
+ .transform((s) => s?.trim() ?? ''),
105
+ roles: z
106
+ .array(
107
+ z.object({
108
+ uuid: z.string().min(1, 'UUID is required'),
109
+ display: z.string().min(1, 'Role name is required'),
110
+ description: z.string().nullable().optional(),
111
+ }),
112
+ )
113
+ .optional(),
114
+ primaryRole: z.string().optional(),
115
+ systemId: z.string().optional(),
116
+ providerLicense: z.string().optional(),
117
+ licenseExpiryDate: z.date().optional(),
118
+ registrationNumber: z.string().optional(),
119
+ qualification: z.string().optional(),
120
+ nationalId: z.string().optional(),
121
+ passportNumber: z.string().optional(),
122
+ isEditProvider: z.boolean().optional(),
123
+ providerUniqueIdentifier: z.string().optional(),
124
+ })
125
+ .refine((data) => !isInitialValuesEmpty || data.password === data.confirmPassword, {
126
+ message: t('passwordsDoNotMatch', 'Passwords do not match'),
127
+ path: ['confirmPassword'],
128
+ })
129
+ .superRefine((data, ctx) => {
130
+ if (isInitialValuesEmpty) {
131
+ return;
132
+ }
133
+ const p = (data.password ?? '').trim();
134
+ const c = (data.confirmPassword ?? '').trim();
135
+ if (!p && !c) {
136
+ return;
137
+ }
138
+ if (!p) {
139
+ ctx.addIssue({ code: 'custom', message: t('passwordRequired', 'Password is required'), path: ['password'] });
140
+ return;
141
+ }
142
+ if (p.length < MIN_PASSWORD_LENGTH) {
143
+ ctx.addIssue({
144
+ code: 'custom',
145
+ message: t('passwordMinLength', 'Password must be at least {{count}} characters', {
146
+ count: MIN_PASSWORD_LENGTH,
147
+ }),
148
+ path: ['password'],
149
+ });
150
+ }
151
+ if (!/[a-z]/.test(p) || !/[A-Z]/.test(p) || !/[0-9]/.test(p)) {
152
+ ctx.addIssue({
153
+ code: 'custom',
154
+ message: t(
155
+ 'passwordCaseRequired',
156
+ 'Password must contain at least one lowercase letter, one uppercase letter, and one number.',
157
+ ),
158
+ path: ['password'],
159
+ });
160
+ }
161
+ if (!c) {
162
+ ctx.addIssue({
163
+ code: 'custom',
164
+ message: t('confirmPasswordRequired', 'Please confirm your password'),
165
+ path: ['confirmPassword'],
166
+ });
167
+ } else if (p !== c) {
168
+ ctx.addIssue({
169
+ code: 'custom',
170
+ message: t('passwordsDoNotMatch', 'Passwords do not match'),
171
+ path: ['confirmPassword'],
172
+ });
173
+ }
174
+ });
175
+
176
+ return { userManagementFormSchema };
177
+ };
178
+
179
+ export default UserManagementFormSchema;
@@ -0,0 +1,142 @@
1
+ import { fhirBaseUrl, Type } from '@openmrs/esm-framework';
2
+ import dayjs from 'dayjs';
3
+
4
+ export const configSchema = {
5
+ providerNationalIdUuid: {
6
+ _type: Type.String,
7
+ _description: 'UUID for provider national id',
8
+ _default: '3d152c97-2293-4a2b-802e-e0f1009b7b15',
9
+ },
10
+ passportNumberUuid: {
11
+ _type: Type.String,
12
+ _description: 'UUID for passport number identification for provider',
13
+ _default: '5b4b88e8-9db3-41e6-a175-5e39f2c8a9a5',
14
+ },
15
+ providerHieFhirReference: {
16
+ _type: Type.String,
17
+ _description: 'UUID for provider hie fhir reference',
18
+ _default: '67b94e8e-4d61-4810-b0f1-d86497f6e553',
19
+ },
20
+ qualificationUuid: {
21
+ _type: Type.String,
22
+ _description: 'UUID for provider hie qualification',
23
+ _default: '43f99413-6e7f-4812-bc60-066bb1d43f94',
24
+ },
25
+ licenseBodyUuid: {
26
+ _type: Type.String,
27
+ _description: 'UUID for license body',
28
+ _default: 'ba18bb97-d17c-4640-80d2-58e7df90ca4c',
29
+ },
30
+ licenseNumberUuid: {
31
+ _type: Type.String,
32
+ _description: 'UUID for license number',
33
+ _default: 'bcaaa67b-cc72-4662-90c2-e1e992ceda66',
34
+ },
35
+ licenseExpiryDateUuid: {
36
+ _type: Type.String,
37
+ _description: 'UUID for license expiry date',
38
+ _default: '00539959-a1c7-4848-a5ed-8941e9d5e835',
39
+ },
40
+ phoneNumberUuid: {
41
+ _type: Type.String,
42
+ _description: 'UUID for provider phone number',
43
+ _default: '37daed7f-1f4e-4e62-8e83-6048ade18a87',
44
+ },
45
+ providerAddressUuid: {
46
+ _type: Type.String,
47
+ _description: 'UUID for provider address',
48
+ _default: '033ff604-ecf7-464f-b623-5b77c733667f',
49
+ },
50
+ personEmailAttributeUuid: {
51
+ _type: Type.String,
52
+ _description: 'UUID for person email attribute',
53
+ _default: 'b8d0b331-1d2d-4a9a-b741-1816f498bdb6',
54
+ },
55
+ personPhonenumberAttributeUuid: {
56
+ _type: Type.String,
57
+ _description: 'UUID for person phone number attribute',
58
+ _default: 'b2c38640-2603-4629-aebd-3b54f33f1e3a',
59
+ },
60
+ providerUniqueIdentifierAttributeTypeUuid: {
61
+ _type: Type.String,
62
+ _description: 'UUID for provider unique identifier attribute type',
63
+ _default: 'dace9d99-9f29-4653-9eae-c05929f34a32',
64
+ },
65
+ providerExternalIdAttributeTypeUuid: {
66
+ _type: Type.String,
67
+ _description: 'UUID for provider external id attribute type',
68
+ _default: 'bbdf67e8-c020-40ff-8ad6-74ba34893882',
69
+ },
70
+ providerIHRISIdentifierAttributeTypeUuid: {
71
+ _type: Type.String,
72
+ _description: 'UUID for provider IHRIS identifier attribute type',
73
+ _default: '5dd3eb8a-c901-4eab-bb32-a28493781aae',
74
+ },
75
+ identifierTypes: {
76
+ _type: Type.Array,
77
+ _elements: {
78
+ _type: Type.Object,
79
+ properties: {
80
+ key: { _type: Type.String },
81
+ name: { _type: Type.String },
82
+ },
83
+ },
84
+ _default: [
85
+ { key: 'National ID', name: 'National ID' },
86
+ { key: 'registration_number', name: 'Registration Number' },
87
+ { key: 'Passport', name: 'Passport Number' },
88
+ ],
89
+ _description: 'List of identifier types with unique keys for each.',
90
+ },
91
+ regulatorOptions: {
92
+ _type: Type.Array,
93
+ _elements: {
94
+ _type: Type.Object,
95
+ properties: {
96
+ key: { _type: Type.String },
97
+ name: { _type: Type.String },
98
+ },
99
+ },
100
+ _default: [
101
+ { key: 'Clinical Officers Council', name: 'Clinical Officers Council' },
102
+ { key: 'Pharmacy and Poisons Board', name: 'Pharmacy and Poisons Board' },
103
+ { key: 'Nursing Council', name: 'Nursing Council' },
104
+ { key: 'KMPDC', name: 'Kenya Medical Practitioners and Dentists Council (KMPDC)' },
105
+ {
106
+ key: 'Kenya Medical Laboratory Technicians & Technologists Board',
107
+ name: 'Kenya Medical Laboratory Technicians & Technologists Board',
108
+ },
109
+ ],
110
+ _description: 'List of regulator options with unique keys for each.',
111
+ },
112
+ };
113
+
114
+ export interface UserProperties {
115
+ loginAttempts: string;
116
+ lastViewedPatientIds: string;
117
+ }
118
+
119
+ export interface ConfigObject {
120
+ providerNationalIdUuid: string;
121
+ passportNumberUuid: string;
122
+ providerHieFhirReference: string;
123
+ providerUniqueIdentifierAttributeTypeUuid: string;
124
+ phoneNumberUuid: string;
125
+ providerAddressUuid: string;
126
+ qualificationUuid: string;
127
+ licenseBodyUuid: string;
128
+ licenseNumberUuid: string;
129
+ personEmailAttributeUuid: string;
130
+ personPhonenumberAttributeUuid: string;
131
+ licenseExpiryDateUuid: string;
132
+ providerExternalIdAttributeTypeUuid: string;
133
+ providerIHRISIdentifierAttributeTypeUuid: string;
134
+ identifierTypes: Array<{
135
+ key: string;
136
+ name: string;
137
+ }>;
138
+ regulatorOptions: Array<{
139
+ key: string;
140
+ name: string;
141
+ }>;
142
+ }
@@ -0,0 +1,50 @@
1
+ export const moduleName = '@palladium-ethiopia/esm-admin-app';
2
+ export const etlBasePath = `${window.spaBase}`;
3
+
4
+ export const today = () => {
5
+ const date = new Date();
6
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
7
+ };
8
+
9
+ export const DATE_PICKER_CONTROL_FORMAT = 'd/m/Y';
10
+
11
+ export const DATE_PICKER_FORMAT = 'DD/MM/YYYY';
12
+
13
+ export const formatNewDate = (date: Date | null | undefined) => {
14
+ return date ? new Date(date) : '';
15
+ };
16
+
17
+ /**
18
+ * Represents the error response from the Health Care Worker Registry API
19
+ * when no valid credentials are provided or found.
20
+ * @constant {string}
21
+ */
22
+ export const HWR_API_NO_CREDENTIALS = 'NO_API_CREDENNTIALS';
23
+ /**
24
+ * Represents the error response from the Health Care Worker Registry API
25
+ * when when resource is not found
26
+ * @constant {string}
27
+ */
28
+ export const RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND';
29
+ /**
30
+ * Represents the error response from the Health Care Worker Registry API
31
+ * When Uknown error occures
32
+ * @constant {string}
33
+ */
34
+ export const UNKNOWN = 'UNKNOWN';
35
+
36
+ /**
37
+ * Represents the error response from the Health Care Worker Registry API
38
+ * when when prover with given identifier is not found
39
+ * @constant {string}
40
+ */
41
+ export const PROVIDER_NOT_FOUND = 'PROVIDER_NOT_FOUND';
42
+ export const ROLE_CATEGORIES = {
43
+ CORE_INVENTORY: 'Core Inventory Roles',
44
+ };
45
+ export const SECTIONS = {
46
+ LOGIN: 'login',
47
+ ROLES: 'roles',
48
+ DEMOGRAPHIC: 'demographic',
49
+ PROVIDER: 'provider',
50
+ };
@@ -0,0 +1,2 @@
1
+ declare module '*.css';
2
+ declare module '*.scss';
package/src/index.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { defineConfigSchema, getSyncLifecycle } from '@openmrs/esm-framework';
2
+ import { configSchema } from './config-schema';
3
+ import { moduleName } from './constants';
4
+ import OperationConfirmation from './components/confirm-modal/confirmation-operation-modal.component';
5
+ import Root from './root.component';
6
+ import ManageUserWorkspace from './components/users/manage-users/user-management.workspace';
7
+ import { createLeftPanelLink } from './left-pannel-link.component';
8
+ import HWRConfirmModal from './components/modal/hwr-confirmation.modal';
9
+ import HWREmptyModal from './components/modal/hwr-empty.modal.component';
10
+ import UserRoleScopeWorkspace from './components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope.workspace';
11
+ import HWRSyncModal from './components/modal/hwr-sync.modal';
12
+ import AddLocationWorkspace from './components/locations/forms/add-location/add-location.workspace';
13
+ import SearchLocationWorkspace from './components/locations/forms/search-location/search-location.workspace';
14
+ import ProviderBannerTag from './components/provider-banner/provider-banner.component';
15
+
16
+ const options = {
17
+ featureName: 'esm-admin-app',
18
+ moduleName,
19
+ };
20
+
21
+ export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
22
+
23
+ export const root = getSyncLifecycle(Root, options);
24
+
25
+ export function startupApp() {
26
+ defineConfigSchema(moduleName, configSchema);
27
+ }
28
+
29
+ export const operationConfirmationModal = getSyncLifecycle(OperationConfirmation, options);
30
+ export const manageUserWorkspace = getSyncLifecycle(ManageUserWorkspace, options);
31
+ export const userRoleScopeWorkspace = getSyncLifecycle(UserRoleScopeWorkspace, options);
32
+
33
+ export const userManagementLeftPannelLink = getSyncLifecycle(
34
+ createLeftPanelLink({ title: 'Manage Users', name: 'user-management' }),
35
+ options,
36
+ );
37
+
38
+ export const locationsLeftPanelLink = getSyncLifecycle(
39
+ createLeftPanelLink({ title: 'Locations', name: 'locations' }),
40
+ options,
41
+ );
42
+ export const facilitySetupLeftPanelLink = getSyncLifecycle(
43
+ createLeftPanelLink({ title: 'Facility Details', name: 'facility-setup' }),
44
+ options,
45
+ );
46
+
47
+ export const hwrConfirmationModal = getSyncLifecycle(HWRConfirmModal, options);
48
+ export const hwrEmptyModal = getSyncLifecycle(HWREmptyModal, options);
49
+ export const hwrSyncModal = getSyncLifecycle(HWRSyncModal, options);
50
+
51
+ export const addLocation = getSyncLifecycle(AddLocationWorkspace, options);
52
+ export const searchLocationWorkspace = getSyncLifecycle(SearchLocationWorkspace, options);
53
+
54
+ // t('providerBanner', 'Provider banner')
55
+ export const providerBanner = getSyncLifecycle(ProviderBannerTag, options);
@@ -0,0 +1,40 @@
1
+ import React, { useMemo } from 'react';
2
+ import last from 'lodash-es/last';
3
+ import { BrowserRouter, useLocation } from 'react-router-dom';
4
+ import { ConfigurableLink } from '@openmrs/esm-framework';
5
+
6
+ export interface LinkConfig {
7
+ name: string;
8
+ title: string;
9
+ }
10
+
11
+ function LinkExtension({ config }: { config: LinkConfig }) {
12
+ const { name, title } = config;
13
+ const location = useLocation();
14
+
15
+ let urlSegment = useMemo(() => decodeURIComponent(last(location.pathname.split('/'))), [location.pathname]);
16
+
17
+ const isUUID = (value) => {
18
+ const regex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/;
19
+ return regex.test(value);
20
+ };
21
+
22
+ if (isUUID(urlSegment)) {
23
+ urlSegment = 'user-management';
24
+ }
25
+
26
+ return (
27
+ <ConfigurableLink
28
+ to={`${window.getOpenmrsSpaBase()}admin${name && name !== 'admin' ? `/${name}` : ''}`}
29
+ className={`cds--side-nav__link ${name === urlSegment && 'active-left-nav-link'}`}>
30
+ {title}
31
+ </ConfigurableLink>
32
+ );
33
+ }
34
+
35
+ export const createLeftPanelLink = (config: LinkConfig) => () =>
36
+ (
37
+ <BrowserRouter>
38
+ <LinkExtension config={config} />
39
+ </BrowserRouter>
40
+ );
@@ -0,0 +1,39 @@
1
+ import React, { useEffect } from 'react';
2
+ import { BrowserRouter, Route, Routes } from 'react-router-dom';
3
+ import { setLeftNav, unsetLeftNav, WorkspaceContainer } from '@openmrs/esm-framework';
4
+ import styles from './root.scss';
5
+ import LeftPanel from './components/side-menu/left-pannel.component';
6
+ import UserManagentLandingPage from './components/users/manage-users/manage-user.component';
7
+ import EtlAdminDashboard from './components/dashboard/etl-dashboard.component';
8
+ import FacilitySetup from './components/facility-setup/facility-setup.component';
9
+ import HomeComponent from './components/locations/home/home-locations.component';
10
+
11
+ const Root: React.FC = () => {
12
+ const spaBasePath = window.spaBase;
13
+ const adminBasename = window.getOpenmrsSpaBase() + 'admin';
14
+
15
+ useEffect(() => {
16
+ setLeftNav({
17
+ name: 'admin-left-panel-slot',
18
+ basePath: spaBasePath,
19
+ });
20
+ return () => unsetLeftNav('admin-left-panel-slot');
21
+ }, [spaBasePath]);
22
+
23
+ return (
24
+ <BrowserRouter basename={adminBasename}>
25
+ <LeftPanel />
26
+ <main className={styles.container}>
27
+ <Routes>
28
+ <Route path="/" element={<UserManagentLandingPage />} />
29
+ <Route path="/user-management" element={<UserManagentLandingPage />} />
30
+ <Route path="/etl-administration" element={<EtlAdminDashboard />} />
31
+ <Route path="/facility-setup" element={<FacilitySetup />} />
32
+ <Route path="/locations" element={<HomeComponent />} />
33
+ </Routes>
34
+ </main>
35
+ </BrowserRouter>
36
+ );
37
+ };
38
+
39
+ export default Root;
package/src/root.scss ADDED
@@ -0,0 +1,12 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+
5
+ .container {
6
+ background-color: colors.$white-0;
7
+ height: calc(100vh - layout.$spacing-09);
8
+ }
9
+
10
+ :global(.omrs-breakpoint-gt-tablet) .container {
11
+ margin-left: var(--omrs-sidenav-width);
12
+ }
@@ -0,0 +1,100 @@
1
+ {
2
+ "$schema": "https://json.openmrs.org/routes.schema.json",
3
+ "backendDependencies": {
4
+ "kenyaemrCharts": "^1.6.7"
5
+ },
6
+ "extensions": [
7
+ {
8
+ "component": "adminLeftPanelLink",
9
+ "name": "admin-left-panel-link",
10
+ "slot": "admin-left-panel-slot"
11
+ },
12
+ {
13
+ "component": "userManagementLeftPannelLink",
14
+ "name": "user-management-left-panel-link",
15
+ "slot": "admin-left-panel-slot"
16
+ },
17
+ {
18
+ "component": "locationsLeftPanelLink",
19
+ "name": "locations-left-panel-link",
20
+ "slot": "admin-left-panel-slot"
21
+ },
22
+ {
23
+ "component": "facilitySetupLeftPanelLink",
24
+ "name": "facility-setup-left-panel-link",
25
+ "slot": "admin-left-panel-slot"
26
+ },
27
+ {
28
+ "component": "providerBanner",
29
+ "name": "provider-banner",
30
+ "slot": "provider-banner-info-slot",
31
+ "order":1
32
+ }
33
+ ],
34
+ "workspaces": [
35
+ {
36
+ "name": "manage-user-workspace",
37
+ "component": "manageUserWorkspace",
38
+ "title": "Manage User Workspace",
39
+ "type": "other-form",
40
+ "canMaximize": true,
41
+ "width": "extra-wide"
42
+ },
43
+ {
44
+ "name": "user-role-scope-workspace",
45
+ "component": "userRoleScopeWorkspace",
46
+ "title": "User Rple Scope Workspace",
47
+ "type": "other-form",
48
+ "canMaximize": true,
49
+ "width": "extra-wide"
50
+ },
51
+ {
52
+ "name": "add-location-workspace",
53
+ "title": "Add Location",
54
+ "component": "addLocation",
55
+ "type": "workspace"
56
+ },
57
+ {
58
+ "name": "search-location-workspace",
59
+ "title": "Search Location",
60
+ "component": "searchLocationWorkspace",
61
+ "type": "workspace"
62
+ },
63
+ {
64
+ "name": "hwr-sync-workspace",
65
+ "title": "HWR Sync Workspace",
66
+ "component": "hwrSyncWorkspace",
67
+ "type": "other-form"
68
+ },
69
+ {
70
+ "name": "hwr-sync-modal",
71
+ "title": "HWR Sync Modal",
72
+ "component": "hwrSyncModal",
73
+ "type": "modal"
74
+ }
75
+ ],
76
+ "modals": [
77
+ {
78
+ "component": "operationConfirmationModal",
79
+ "name": "operation-confirmation-modal"
80
+ },
81
+ {
82
+ "component": "hwrConfirmationModal",
83
+ "name": "hwr-confirmation-modal"
84
+ },
85
+ {
86
+ "component": "hwrEmptyModal",
87
+ "name": "hwr-empty-modal"
88
+ },
89
+ {
90
+ "component": "hwrSyncModal",
91
+ "name": "hwr-syncing-modal"
92
+ }
93
+ ],
94
+ "pages": [
95
+ {
96
+ "component": "root",
97
+ "route": "admin"
98
+ }
99
+ ]
100
+ }
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom/extend-expect';