@openmrs/esm-billing-app 1.0.2-pre.112 → 1.0.2-pre.1124

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 (241) hide show
  1. package/.eslintrc +16 -2
  2. package/README.md +54 -9
  3. package/__mocks__/bills.mock.ts +12 -0
  4. package/__mocks__/react-i18next.js +6 -5
  5. package/dist/1119.js +1 -1
  6. package/dist/1146.js +1 -2
  7. package/dist/1146.js.map +1 -1
  8. package/dist/1197.js +1 -1
  9. package/dist/1537.js +1 -0
  10. package/dist/1537.js.map +1 -0
  11. package/dist/1856.js +1 -0
  12. package/dist/1856.js.map +1 -0
  13. package/dist/2146.js +1 -1
  14. package/dist/2177.js +1 -1
  15. package/dist/2177.js.map +1 -1
  16. package/dist/2524.js +1 -0
  17. package/dist/2524.js.map +1 -0
  18. package/dist/2690.js +1 -1
  19. package/dist/3099.js +1 -1
  20. package/dist/3584.js +1 -1
  21. package/dist/3717.js +2 -0
  22. package/dist/3717.js.map +1 -0
  23. package/dist/4055.js +1 -1
  24. package/dist/4132.js +1 -1
  25. package/dist/4300.js +1 -1
  26. package/dist/4335.js +1 -1
  27. package/dist/439.js +1 -0
  28. package/dist/4618.js +1 -1
  29. package/dist/4652.js +1 -1
  30. package/dist/4724.js +1 -0
  31. package/dist/4724.js.map +1 -0
  32. package/dist/4739.js +1 -1
  33. package/dist/4739.js.map +1 -1
  34. package/dist/4944.js +1 -1
  35. package/dist/5173.js +1 -1
  36. package/dist/5241.js +1 -1
  37. package/dist/5442.js +1 -1
  38. package/dist/5661.js +1 -1
  39. package/dist/6022.js +1 -1
  40. package/dist/6468.js +1 -1
  41. package/dist/6540.js +1 -1
  42. package/dist/6540.js.map +1 -1
  43. package/dist/6589.js +1 -0
  44. package/dist/6679.js +1 -1
  45. package/dist/6840.js +1 -1
  46. package/dist/6859.js +1 -1
  47. package/dist/7097.js +1 -1
  48. package/dist/7159.js +1 -1
  49. package/dist/723.js +1 -1
  50. package/dist/7255.js +1 -1
  51. package/dist/7255.js.map +1 -1
  52. package/dist/7617.js +1 -1
  53. package/dist/795.js +1 -1
  54. package/dist/8163.js +1 -1
  55. package/dist/8349.js +1 -1
  56. package/dist/8371.js +1 -0
  57. package/dist/8572.js +1 -0
  58. package/dist/8572.js.map +1 -0
  59. package/dist/8618.js +1 -1
  60. package/dist/8708.js +2 -0
  61. package/dist/{6557.js.LICENSE.txt → 8708.js.LICENSE.txt} +22 -0
  62. package/dist/8708.js.map +1 -0
  63. package/dist/890.js +1 -1
  64. package/dist/9214.js +1 -1
  65. package/dist/9538.js +1 -1
  66. package/dist/9569.js +1 -1
  67. package/dist/961.js +1 -1
  68. package/dist/961.js.map +1 -1
  69. package/dist/986.js +1 -1
  70. package/dist/9879.js +1 -1
  71. package/dist/9895.js +1 -1
  72. package/dist/9900.js +1 -1
  73. package/dist/9913.js +1 -1
  74. package/dist/main.js +1 -1
  75. package/dist/main.js.map +1 -1
  76. package/dist/openmrs-esm-billing-app.js +1 -1
  77. package/dist/openmrs-esm-billing-app.js.buildmanifest.json +321 -249
  78. package/dist/openmrs-esm-billing-app.js.map +1 -1
  79. package/dist/routes.json +1 -1
  80. package/e2e/README.md +19 -18
  81. package/e2e/commands/billing-operations.ts +96 -0
  82. package/e2e/commands/index.ts +3 -0
  83. package/e2e/commands/patient-operations.ts +82 -0
  84. package/e2e/commands/types.ts +73 -0
  85. package/e2e/core/test.ts +12 -1
  86. package/e2e/fixtures/api.ts +2 -3
  87. package/e2e/pages/billing-dashboard-page.ts +84 -0
  88. package/e2e/pages/billing-form-page.ts +114 -0
  89. package/e2e/pages/index.ts +4 -1
  90. package/e2e/pages/invoice-page.ts +113 -0
  91. package/e2e/pages/payment-page.ts +104 -0
  92. package/e2e/specs/billing-dashboard.spec.ts +203 -0
  93. package/e2e/specs/billing-patient-chart.spec.ts +738 -0
  94. package/e2e/support/github/Dockerfile +1 -1
  95. package/e2e/support/github/run-e2e-docker-env.sh +29 -32
  96. package/example.env +15 -1
  97. package/package.json +19 -16
  98. package/playwright.config.ts +4 -2
  99. package/src/bill-history/bill-history.component.tsx +15 -17
  100. package/src/bill-history/bill-history.scss +4 -94
  101. package/src/bill-history/bill-history.test.tsx +37 -77
  102. package/src/bill-item-actions/bill-item-actions.scss +21 -5
  103. package/src/bill-item-actions/edit-bill-item.modal.tsx +227 -0
  104. package/src/bill-item-actions/edit-bill-item.test.tsx +233 -40
  105. package/src/billable-services/bill-waiver/bill-selection.component.tsx +5 -5
  106. package/src/billable-services/bill-waiver/bill-waiver-form.component.tsx +37 -39
  107. package/src/billable-services/bill-waiver/patient-bills.component.tsx +7 -7
  108. package/src/billable-services/bill-waiver/utils.ts +13 -3
  109. package/src/billable-services/{create-edit/add-billable-service.scss → billable-service-form/billable-service-form.scss} +32 -64
  110. package/src/billable-services/billable-service-form/billable-service-form.test.tsx +1099 -0
  111. package/src/billable-services/billable-service-form/billable-service-form.workspace.tsx +516 -0
  112. package/src/billable-services/billable-service.resource.ts +71 -27
  113. package/src/billable-services/billable-services-home.component.tsx +13 -42
  114. package/src/billable-services/billable-services-left-panel-link.component.tsx +50 -0
  115. package/src/billable-services/billable-services-left-panel-menu.component.tsx +48 -0
  116. package/src/billable-services/billable-services-menu-item/item.component.tsx +5 -4
  117. package/src/billable-services/billable-services.component.tsx +156 -152
  118. package/src/billable-services/billable-services.scss +29 -0
  119. package/src/billable-services/billable-services.test.tsx +6 -49
  120. package/src/billable-services/cash-point/add-cash-point.modal.tsx +170 -0
  121. package/src/billable-services/cash-point/cash-point-configuration.component.tsx +19 -193
  122. package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
  123. package/src/billable-services/dashboard/dashboard.component.tsx +0 -2
  124. package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +77 -0
  125. package/src/billable-services/payment-modes/payment-mode-form.modal.tsx +131 -0
  126. package/src/billable-services/payment-modes/payment-modes-config.component.tsx +139 -0
  127. package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -4
  128. package/src/billable-services-admin-card-link.component.test.tsx +2 -2
  129. package/src/billable-services-admin-card-link.component.tsx +1 -1
  130. package/src/billing-dashboard/billing-dashboard.scss +1 -1
  131. package/src/billing-form/billing-checkin-form.component.tsx +29 -29
  132. package/src/billing-form/billing-checkin-form.scss +0 -7
  133. package/src/billing-form/billing-checkin-form.test.tsx +100 -27
  134. package/src/billing-form/billing-form.component.tsx +226 -284
  135. package/src/billing-form/billing-form.scss +143 -0
  136. package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +47 -41
  137. package/src/billing-form/visit-attributes/visit-attributes-form.scss +16 -3
  138. package/src/billing.resource.test.ts +503 -0
  139. package/src/billing.resource.ts +68 -76
  140. package/src/bills-table/bills-table.component.tsx +17 -24
  141. package/src/bills-table/bills-table.test.tsx +98 -54
  142. package/src/config-schema.ts +84 -31
  143. package/src/dashboard.meta.ts +5 -3
  144. package/src/helpers/functions.ts +5 -4
  145. package/src/index.ts +75 -13
  146. package/src/invoice/invoice-table.component.tsx +36 -70
  147. package/src/invoice/invoice-table.scss +8 -5
  148. package/src/invoice/invoice-table.test.tsx +273 -62
  149. package/src/invoice/invoice.component.tsx +44 -38
  150. package/src/invoice/invoice.scss +11 -4
  151. package/src/invoice/invoice.test.tsx +324 -120
  152. package/src/invoice/payments/invoice-breakdown/invoice-breakdown.scss +9 -9
  153. package/src/invoice/payments/payment-form/payment-form.component.tsx +45 -36
  154. package/src/invoice/payments/payment-form/payment-form.scss +5 -6
  155. package/src/invoice/payments/payment-form/payment-form.test.tsx +217 -67
  156. package/src/invoice/payments/payment-history/payment-history.component.tsx +6 -4
  157. package/src/invoice/payments/payment-history/payment-history.test.tsx +9 -14
  158. package/src/invoice/payments/payments.component.tsx +56 -68
  159. package/src/invoice/payments/payments.scss +4 -3
  160. package/src/invoice/payments/payments.test.tsx +282 -0
  161. package/src/invoice/payments/utils.ts +15 -27
  162. package/src/invoice/printable-invoice/print-receipt.component.tsx +3 -3
  163. package/src/invoice/printable-invoice/print-receipt.test.tsx +14 -25
  164. package/src/invoice/printable-invoice/printable-footer.component.tsx +2 -2
  165. package/src/invoice/printable-invoice/printable-footer.test.tsx +4 -13
  166. package/src/invoice/printable-invoice/printable-invoice-header.component.tsx +20 -11
  167. package/src/invoice/printable-invoice/printable-invoice-header.test.tsx +95 -16
  168. package/src/invoice/printable-invoice/printable-invoice.component.tsx +21 -35
  169. package/src/left-panel-link.component.tsx +9 -8
  170. package/src/left-panel-link.test.tsx +1 -4
  171. package/src/metrics-cards/metrics-cards.component.tsx +16 -6
  172. package/src/metrics-cards/metrics-cards.scss +4 -0
  173. package/src/metrics-cards/metrics-cards.test.tsx +18 -5
  174. package/src/modal/require-payment-modal.test.tsx +27 -22
  175. package/src/modal/{require-payment-modal.component.tsx → require-payment.modal.tsx} +18 -19
  176. package/src/routes.json +44 -20
  177. package/src/types/index.ts +87 -24
  178. package/translations/am.json +140 -80
  179. package/translations/ar.json +141 -81
  180. package/translations/ar_SY.json +141 -81
  181. package/translations/bn.json +143 -83
  182. package/translations/cs.json +230 -0
  183. package/translations/de.json +141 -81
  184. package/translations/en.json +142 -81
  185. package/translations/en_US.json +141 -81
  186. package/translations/es.json +140 -80
  187. package/translations/es_MX.json +141 -81
  188. package/translations/fr.json +224 -164
  189. package/translations/he.json +140 -80
  190. package/translations/hi.json +141 -81
  191. package/translations/hi_IN.json +141 -81
  192. package/translations/id.json +141 -81
  193. package/translations/it.json +166 -106
  194. package/translations/ka.json +141 -81
  195. package/translations/km.json +140 -80
  196. package/translations/ku.json +141 -81
  197. package/translations/ky.json +141 -81
  198. package/translations/lg.json +141 -81
  199. package/translations/ne.json +141 -81
  200. package/translations/pl.json +141 -81
  201. package/translations/pt.json +141 -81
  202. package/translations/pt_BR.json +141 -81
  203. package/translations/qu.json +141 -81
  204. package/translations/ro_RO.json +225 -165
  205. package/translations/ru_RU.json +141 -81
  206. package/translations/si.json +141 -81
  207. package/translations/sq.json +230 -0
  208. package/translations/sw.json +141 -81
  209. package/translations/sw_KE.json +141 -81
  210. package/translations/tr.json +141 -81
  211. package/translations/tr_TR.json +141 -81
  212. package/translations/uk.json +141 -81
  213. package/translations/uz.json +141 -81
  214. package/translations/uz@Latn.json +141 -81
  215. package/translations/uz_UZ.json +141 -81
  216. package/translations/vi.json +141 -81
  217. package/translations/zh.json +141 -81
  218. package/translations/zh_CN.json +171 -111
  219. package/translations/zh_TW.json +230 -0
  220. package/dist/1146.js.LICENSE.txt +0 -21
  221. package/dist/2352.js +0 -1
  222. package/dist/2352.js.map +0 -1
  223. package/dist/246.js +0 -1
  224. package/dist/246.js.map +0 -1
  225. package/dist/4689.js +0 -2
  226. package/dist/4689.js.map +0 -1
  227. package/dist/6557.js +0 -2
  228. package/dist/6557.js.map +0 -1
  229. package/dist/8638.js +0 -1
  230. package/dist/8638.js.map +0 -1
  231. package/dist/9968.js +0 -1
  232. package/dist/9968.js.map +0 -1
  233. package/e2e/pages/home-page.ts +0 -9
  234. package/e2e/specs/sample-test.spec.ts +0 -11
  235. package/src/bill-item-actions/edit-bill-item.component.tsx +0 -221
  236. package/src/billable-services/create-edit/add-billable-service.component.tsx +0 -401
  237. package/src/billable-services/create-edit/add-billable-service.test.tsx +0 -154
  238. package/src/billable-services/dashboard/service-metrics.component.tsx +0 -41
  239. package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +0 -280
  240. package/src/invoice/payments/payments.component.test.tsx +0 -121
  241. /package/dist/{4689.js.LICENSE.txt → 3717.js.LICENSE.txt} +0 -0
@@ -1,5 +1,5 @@
1
1
  # syntax=docker/dockerfile:1.3
2
- FROM --platform=$BUILDPLATFORM node:18-alpine as dev
2
+ FROM --platform=$BUILDPLATFORM node:22-alpine as dev
3
3
 
4
4
  ARG APP_SHELL_VERSION=next
5
5
 
@@ -4,46 +4,43 @@
4
4
  script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
5
5
  # create a temporary working directory
6
6
  working_dir=$(mktemp -d "${TMPDIR:-/tmp/}openmrs-e2e-frontends.XXXXXXXXXX")
7
- # get a list of all the apps in this workspace
8
- apps=$(yarn workspaces list --json | jq -r 'if ((.location == ".") or (.location | test("form-engine-app")) or (.location | test("-app") | not)) then halt else .name end')
9
- # this array will hold all of the packed app names
10
- app_names=()
7
+ # cleanup temp directory on exit
8
+ trap 'rm -rf "$working_dir"' EXIT
9
+ # get the app name
10
+ app_name=$(jq -r '.name' "$script_dir/../../../package.json")
11
11
 
12
- echo "Creating packed archives of apps..."
13
- # for each app
14
- for app in $apps
15
- do
16
- # @openmrs/esm-whatever -> _openmrs_esm_whatever
17
- app_name=$(echo "$app" | tr '[:punct:]' '_');
18
- # add to our array
19
- app_names+=("$app_name.tgz");
20
- # run yarn pack for our app and add it to the working directory
21
- yarn workspace "$app" pack -o "$working_dir/$app_name.tgz" >/dev/null;
22
- done;
23
- echo "Created packed app archives"
12
+ echo "Creating packed archive of the app for Docker build..."
13
+ # @openmrs/esm-whatever -> _openmrs_esm_whatever
14
+ packed_app_name=$(echo "$app_name" | tr '[:punct:]' '_');
15
+ # run yarn pack for our app and add it to the working directory
16
+ yarn pack -o "$working_dir/$packed_app_name.tgz" >/dev/null;
17
+ echo "Successfully created packed app archive: $packed_app_name.tgz"
24
18
 
25
- echo "Creating dynamic spa-assemble-config.json..."
26
- # dynamically assemble our list of frontend modules, prepending the login app and
27
- # primary navigation apps; apps will all be in the /app directory of the Docker
19
+ echo "Creating dynamic spa-assemble-config.json with frontend modules..."
20
+ # dynamically assemble our list of frontend modules, prepending the primary navigation
21
+ # and patient banner apps; apps will all be in the /app directory of the Docker
28
22
  # container
29
23
  jq -n \
30
- --arg apps "$apps" \
31
- --arg app_names "$(echo ${app_names[@]})" \
32
- '{"@openmrs/esm-primary-navigation-app": "next", "@openmrs/esm-home-app": "next"} + (
33
- ($apps | split("\n")) as $apps | ($app_names | split(" ") | map("/app/" + .)) as $app_files
34
- | [$apps, $app_files]
35
- | transpose
36
- | map({"key": .[0], "value": .[1]})
37
- | from_entries
38
- )' | jq '{"frontendModules": .}' > "$working_dir/spa-assemble-config.json"
39
- echo "Created dynamic spa-assemble-config.json"
24
+ --arg app_name "$app_name" \
25
+ --arg app_file "/app/$packed_app_name.tgz" \
26
+ '{
27
+ "@openmrs/esm-home-app": "next",
28
+ "@openmrs/esm-patient-banner-app": "next",
29
+ "@openmrs/esm-patient-chart-app": "next",
30
+ "@openmrs/esm-patient-forms-app": "next",
31
+ "@openmrs/esm-primary-navigation-app": "next",
32
+ "@openmrs/esm-system-admin-app": "next",
33
+ } + {
34
+ ($app_name): $app_file
35
+ }' | jq '{"frontendModules": .}' > "$working_dir/spa-assemble-config.json"
36
+ echo "Successfully created spa-assemble-config.json with frontend modules"
40
37
 
41
- echo "Copying Docker configuration..."
38
+ echo "Copying Docker configuration files to working directory..."
42
39
  cp "$script_dir/Dockerfile" "$working_dir/Dockerfile"
43
40
  cp "$script_dir/docker-compose.yml" "$working_dir/docker-compose.yml"
44
41
 
45
- cd $working_dir
46
- echo "Starting Docker containers..."
42
+ cd "$working_dir"
43
+ echo "Building and starting Docker containers for e2e testing..."
47
44
  # CACHE_BUST to ensure the assemble step is always run
48
45
  docker compose build --build-arg CACHE_BUST=$(date +%s) frontend
49
46
  docker compose up -d
package/example.env CHANGED
@@ -2,5 +2,19 @@
2
2
  E2E_BASE_URL=http://localhost:8080/openmrs
3
3
  E2E_USER_ADMIN_USERNAME=admin
4
4
  E2E_USER_ADMIN_PASSWORD=Admin123
5
+
6
+ # "Outpatient Clinic" location UUID
5
7
  E2E_LOGIN_DEFAULT_LOCATION_UUID=44c3efb0-2583-4c80-a79e-1f756a03c0a1
6
- # The above location UUID is for the "Outpatient Clinic" location in the reference application
8
+
9
+ # Billing Module Test Configuration
10
+ # Default UUIDs from openmrs-content-referenceapplication-demo
11
+ # These should work if you're using the reference application demo data
12
+
13
+ # Cash Point UUID - OPD Cash Point from reference application demo
14
+ E2E_CASH_POINT_UUID=54065383-b4d4-42d2-af4d-d250a1fd2590
15
+
16
+ # Admin User UUID
17
+ E2E_USER_ADMIN_UUID=82f18b44-6814-11e8-923f-e9a88dcb533f
18
+
19
+ # Billable Service UUID - OPD Consultation from reference application demo
20
+ E2E_TEST_SERVICE_UUID=360fab13-d92b-4a9f-ad4e-0ac223e7f54c
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-billing-app",
3
- "version": "1.0.2-pre.112",
3
+ "version": "1.0.2-pre.1124",
4
4
  "description": "O3 frontend module for handling billing concerns in healthcare settings",
5
5
  "browser": "dist/openmrs-esm-billing-app.js",
6
6
  "main": "src/index.ts",
@@ -13,7 +13,7 @@
13
13
  "build": "webpack --mode production",
14
14
  "coverage": "yarn test --coverage",
15
15
  "debug": "npm run serve",
16
- "extract-translations": "i18next 'src/**/*.component.tsx' 'src/index.ts' --config ./tools/i18next-parser.config.js",
16
+ "extract-translations": "i18next 'src/**/*.component.tsx' 'src/**/*.workspace.tsx' 'src/**/*.modal.tsx' 'src/index.ts' --config ./tools/i18next-parser.config.js",
17
17
  "lint": "eslint src --ext ts,tsx --max-warnings=0",
18
18
  "postinstall": "husky install",
19
19
  "prettier": "prettier --config prettier.config.js --write \"src/**/*.{ts,tsx,css,scss}\" \"e2e/**/*.ts\"",
@@ -47,22 +47,22 @@
47
47
  "fuzzy": "^0.1.3",
48
48
  "lodash-es": "^4.17.21",
49
49
  "react-hook-form": "^7.49.3",
50
- "react-to-print": "^2.14.15",
50
+ "react-to-print": "^3.1.1",
51
51
  "zod": "^3.22.4"
52
52
  },
53
53
  "peerDependencies": {
54
- "@openmrs/esm-framework": "6.x",
55
- "@openmrs/esm-patient-common-lib": "10.x",
54
+ "@openmrs/esm-framework": "8.x",
55
+ "@openmrs/esm-patient-common-lib": "11.x",
56
56
  "react": "18.x",
57
57
  "react-dom": "18.x",
58
- "react-i18next": "11.x",
58
+ "react-i18next": "16.x",
59
59
  "rxjs": "6.x",
60
60
  "swr": "2.x"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@openmrs/esm-framework": "next",
64
64
  "@openmrs/esm-patient-common-lib": "next",
65
- "@playwright/test": "^1.49.0",
65
+ "@playwright/test": "^1.53.2",
66
66
  "@swc/cli": "^0.3.2",
67
67
  "@swc/core": "^1.3.106",
68
68
  "@swc/jest": "^0.2.31",
@@ -72,8 +72,8 @@
72
72
  "@testing-library/user-event": "^14.5.2",
73
73
  "@types/jest": "^29.5.11",
74
74
  "@types/lodash-es": "^4.17.12",
75
- "@types/react": "^18.2.48",
76
- "@types/react-dom": "^18.2.18",
75
+ "@types/react": "^18.3.21",
76
+ "@types/react-dom": "^18.3.0",
77
77
  "@types/webpack-env": "^1.18.4",
78
78
  "@typescript-eslint/eslint-plugin": "^6.19.1",
79
79
  "@typescript-eslint/parser": "^6.19.1",
@@ -86,10 +86,13 @@
86
86
  "dotenv": "^16.4.1",
87
87
  "eslint": "^8.56.0",
88
88
  "eslint-plugin-import": "^2.31.0",
89
+ "eslint-plugin-jest-dom": "^5.5.0",
90
+ "eslint-plugin-playwright": "^2.2.2",
89
91
  "eslint-plugin-react-hooks": "^5.0.0",
92
+ "eslint-plugin-testing-library": "^7.13.1",
90
93
  "husky": "^9.0.6",
91
- "i18next": "^23.7.20",
92
- "i18next-parser": "^8.12.0",
94
+ "i18next": "^25.0.0",
95
+ "i18next-parser": "^9.3.0",
93
96
  "identity-obj-proxy": "^3.0.0",
94
97
  "jest": "^29.7.0",
95
98
  "jest-cli": "^29.7.0",
@@ -99,16 +102,16 @@
99
102
  "openmrs": "next",
100
103
  "pinst": "^3.0.0",
101
104
  "prettier": "^3.2.4",
102
- "react": "^18.2.0",
103
- "react-dom": "^18.2.0",
104
- "react-i18next": "^14.0.1",
105
+ "react": "^18.3.1",
106
+ "react-dom": "^18.3.1",
107
+ "react-i18next": "^16.0.0",
105
108
  "react-router-dom": "^6.21.3",
106
109
  "rxjs": "^6.6.7",
107
110
  "sass": "^1.70.0",
108
111
  "swc-loader": "^0.2.3",
109
112
  "swr": "^2.2.4",
110
113
  "turbo": "^2.5.2",
111
- "typescript": "^4.9.5",
114
+ "typescript": "^5.0.0",
112
115
  "webpack": "^5.99.9",
113
116
  "webpack-cli": "^6.0.1",
114
117
  "webpack-dev-server": "^5.2.1"
@@ -117,6 +120,6 @@
117
120
  "*.{ts,tsx}": "eslint --cache --fix --max-warnings 0",
118
121
  "*.{css,scss,ts,tsx}": "prettier --write --list-different"
119
122
  },
120
- "packageManager": "yarn@4.9.1",
123
+ "packageManager": "yarn@4.10.3",
121
124
  "stableVersion": "1.0.1"
122
125
  }
@@ -1,4 +1,4 @@
1
- import { devices, PlaywrightTestConfig } from '@playwright/test';
1
+ import { devices, type PlaywrightTestConfig } from '@playwright/test';
2
2
  import * as dotenv from 'dotenv';
3
3
  dotenv.config();
4
4
 
@@ -7,7 +7,7 @@ const config: PlaywrightTestConfig = {
7
7
  testDir: './e2e/specs',
8
8
  timeout: 3 * 60 * 1000,
9
9
  expect: {
10
- timeout: 40 * 1000,
10
+ timeout: 20 * 1000,
11
11
  },
12
12
  fullyParallel: true,
13
13
  forbidOnly: !!process.env.CI,
@@ -16,7 +16,9 @@ const config: PlaywrightTestConfig = {
16
16
  globalSetup: require.resolve('./e2e/core/global-setup'),
17
17
  use: {
18
18
  baseURL: `${process.env.E2E_BASE_URL}/spa/`,
19
+ locale: 'en-US',
19
20
  storageState: 'e2e/storageState.json',
21
+ trace: 'retain-on-failure',
20
22
  video: 'retain-on-failure',
21
23
  },
22
24
  projects: [
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useState } from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import {
4
4
  Button,
@@ -19,7 +19,7 @@ import {
19
19
  Tile,
20
20
  } from '@carbon/react';
21
21
  import { Add } from '@carbon/react/icons';
22
- import { isDesktop, launchWorkspace, useConfig, useLayoutType, usePagination } from '@openmrs/esm-framework';
22
+ import { launchWorkspace, useConfig, usePagination } from '@openmrs/esm-framework';
23
23
  import { CardHeader, EmptyDataIllustration, ErrorState, usePaginationInfo } from '@openmrs/esm-patient-common-lib';
24
24
  import { convertToCurrency } from '../helpers';
25
25
  import { useBills } from '../billing.resource';
@@ -33,11 +33,9 @@ interface BillHistoryProps {
33
33
  const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
34
34
  const { t } = useTranslation();
35
35
  const { bills, isLoading, error } = useBills(patientUuid);
36
- const layout = useLayoutType();
37
- const responsiveSize = isDesktop(layout) ? 'sm' : 'lg';
38
36
  const { paginated, goTo, results, currentPage } = usePagination(bills);
39
37
  const { pageSize, defaultCurrency } = useConfig();
40
- const [currentPageSize, setCurrentPageSize] = React.useState(pageSize);
38
+ const [currentPageSize, setCurrentPageSize] = useState(pageSize);
41
39
  const { pageSizes } = usePaginationInfo(pageSize, bills?.length, currentPage, results?.length);
42
40
 
43
41
  const headerData = [
@@ -74,7 +72,7 @@ const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
74
72
  if (isLoading) {
75
73
  return (
76
74
  <div className={styles.loaderContainer}>
77
- <DataTableSkeleton showHeader={false} showToolbar={false} zebra size={responsiveSize} />
75
+ <DataTableSkeleton showHeader={false} showToolbar={false} zebra size="md" />
78
76
  </div>
79
77
  );
80
78
  }
@@ -83,7 +81,7 @@ const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
83
81
  return (
84
82
  <div className={styles.errorContainer}>
85
83
  <Layer>
86
- <ErrorState error={error} headerTitle={t('billsList', 'Bill list')} />
84
+ <ErrorState error={error} headerTitle={t('billList', 'Bill list')} />
87
85
  </Layer>
88
86
  </div>
89
87
  );
@@ -109,22 +107,22 @@ const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
109
107
  <div>
110
108
  <CardHeader title={t('billingHistory', 'Billing History')}>
111
109
  <Button renderIcon={Add} onClick={() => launchWorkspace('billing-form-workspace', {})} kind="ghost">
112
- {t('addBill', 'Add bill item(s)')}
110
+ {t('addBill', 'Add bill items')}
113
111
  </Button>
114
112
  </CardHeader>
115
113
  <div className={styles.billHistoryContainer}>
116
- <DataTable isSortable rows={rowData} headers={headerData} size={responsiveSize} useZebraStyles>
114
+ <DataTable isSortable rows={rowData} headers={headerData} size="md" useZebraStyles>
117
115
  {({
118
- rows,
119
- headers,
120
116
  getExpandHeaderProps,
121
- getTableProps,
122
- getTableContainerProps,
123
117
  getHeaderProps,
124
118
  getRowProps,
119
+ getTableContainerProps,
120
+ getTableProps,
121
+ headers,
122
+ rows,
125
123
  }) => (
126
124
  <TableContainer {...getTableContainerProps}>
127
- <Table className={styles.table} {...getTableProps()} aria-label="Bill list">
125
+ <Table className={styles.table} {...getTableProps()} aria-label={t('billList', 'Bill list')}>
128
126
  <TableHead>
129
127
  <TableRow>
130
128
  <TableExpandHeader enableToggle {...getExpandHeaderProps()} />
@@ -153,9 +151,9 @@ const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
153
151
  ))}
154
152
  </TableExpandRow>
155
153
  {row.isExpanded ? (
156
- <TableExpandedRow className={styles.expandedRow} colSpan={headers.length + 1}>
154
+ <TableExpandedRow colSpan={headers.length + 1}>
157
155
  <div className={styles.container} key={i}>
158
- <InvoiceTable bill={currentBill} isSelectable={false} />
156
+ <InvoiceTable bill={currentBill} />
159
157
  </div>
160
158
  </TableExpandedRow>
161
159
  ) : (
@@ -178,7 +176,7 @@ const BillHistory: React.FC<BillHistoryProps> = ({ patientUuid }) => {
178
176
  pageSizes={pageSizes}
179
177
  totalItems={bills.length}
180
178
  className={styles.pagination}
181
- size={responsiveSize}
179
+ size="md"
182
180
  onChange={({ page: newPage, pageSize }) => {
183
181
  if (newPage !== currentPage) {
184
182
  goTo(newPage);
@@ -3,7 +3,7 @@
3
3
  @use '@openmrs/esm-styleguide/src/vars' as *;
4
4
 
5
5
  .container {
6
- margin: layout.$spacing-07 0;
6
+ margin: layout.$spacing-03 0;
7
7
  }
8
8
 
9
9
  .emptyStateContainer,
@@ -21,27 +21,8 @@
21
21
  padding-bottom: 0;
22
22
  }
23
23
 
24
- .headerContainer {
25
- display: flex;
26
- justify-content: space-between;
27
- align-items: center;
28
- padding: layout.$spacing-04 0 layout.$spacing-04 layout.$spacing-05;
29
- background-color: $ui-02;
30
- }
31
-
32
- .backgroundDataFetchingIndicator {
33
- align-items: center;
34
- display: flex;
35
- flex: 1 1 0%;
36
- justify-content: center;
37
- }
38
-
39
- .tableContainer section {
40
- position: relative;
41
- }
42
-
43
- .tableContainer a {
44
- text-decoration: none;
24
+ .errorContainer {
25
+ @extend .container;
45
26
  }
46
27
 
47
28
  .pagination {
@@ -56,34 +37,6 @@
56
37
  display: none;
57
38
  }
58
39
 
59
- .emptyRow {
60
- padding: 0 layout.$spacing-05;
61
- display: flex;
62
- align-items: center;
63
- }
64
-
65
- .visitSummaryContainer {
66
- width: 100%;
67
- max-width: 768px;
68
- margin: layout.$spacing-05 auto;
69
- }
70
-
71
- .expandedActiveVisitRow > td > div {
72
- max-height: max-content !important;
73
- }
74
-
75
- .expandedActiveVisitRow td {
76
- padding: 0 layout.$spacing-07;
77
- }
78
-
79
- .expandedActiveVisitRow th[colspan] td[colspan] > div:first-child {
80
- padding: 0 layout.$spacing-05;
81
- }
82
-
83
- .action {
84
- margin-bottom: layout.$spacing-03;
85
- }
86
-
87
40
  .illo {
88
41
  margin-top: layout.$spacing-05;
89
42
  }
@@ -95,55 +48,11 @@
95
48
  margin-bottom: layout.$spacing-03;
96
49
  }
97
50
 
98
- .desktopHeading,
99
- .tabletHeading {
100
- text-align: left;
101
- text-transform: capitalize;
102
-
103
- h4 {
104
- @include type.type-style('heading-compact-02');
105
- color: $text-02;
106
-
107
- &:after {
108
- content: '';
109
- display: block;
110
- width: layout.$spacing-07;
111
- padding-top: 3px;
112
- border-bottom: 0.375rem solid;
113
- @include brand-03(border-bottom-color);
114
- }
115
- }
116
- }
117
-
118
51
  .tile {
119
52
  text-align: center;
120
53
  border: 1px solid $ui-03;
121
54
  }
122
55
 
123
- .filterEmptyState {
124
- display: flex;
125
- justify-content: center;
126
- align-items: center;
127
- padding: layout.$spacing-05;
128
- margin: layout.$spacing-09;
129
- text-align: center;
130
- }
131
-
132
- .filterEmptyStateTile {
133
- margin: auto;
134
- }
135
-
136
- .filterEmptyStateContent {
137
- @include type.type-style('heading-compact-02');
138
- color: $text-02;
139
- margin-bottom: 0.5rem;
140
- }
141
-
142
- .filterEmptyStateHelper {
143
- @include type.type-style('body-compact-01');
144
- color: $text-02;
145
- }
146
-
147
56
  .table {
148
57
  width: 100%;
149
58
  table-layout: fixed; // This helps with uniform column sizing
@@ -156,6 +65,7 @@
156
65
  text-overflow: unset;
157
66
  }
158
67
  }
68
+
159
69
  :global(tr.cds--parent-row.cds--expandable-row + tr[data-child-row]) td {
160
70
  padding-left: layout.$spacing-05;
161
71
  }
@@ -1,17 +1,23 @@
1
1
  import React from 'react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import { render, screen } from '@testing-library/react';
4
+ import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework';
5
+ import { configSchema, type BillingConfig } from '../config-schema';
4
6
  import { useBills } from '../billing.resource';
5
7
  import BillHistory from './bill-history.component';
6
8
 
7
- // Mock i18next
8
- jest.mock('react-i18next', () => ({
9
- useTranslation: () => ({
10
- t: (key: string) => key,
11
- }),
9
+ const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
10
+ const mockUseBills = jest.mocked(useBills);
11
+
12
+ jest.mock('../billing.resource', () => ({
13
+ useBills: jest.fn(() => ({
14
+ bills: mockBillData,
15
+ isLoading: false,
16
+ isValidating: false,
17
+ error: null,
18
+ })),
12
19
  }));
13
20
 
14
- // Mock window.i18next
15
21
  window.i18next = {
16
22
  language: 'en-US',
17
23
  } as any;
@@ -20,9 +26,7 @@ const testProps = {
20
26
  patientUuid: 'some-uuid',
21
27
  };
22
28
 
23
- const mockbills = useBills as jest.MockedFunction<typeof useBills>;
24
-
25
- const mockBillsData = [
29
+ const mockBillData = [
26
30
  { uuid: '1', patientName: 'John Doe', identifier: '12345678', billingService: 'Checkup', totalAmount: 500 },
27
31
  { uuid: '2', patientName: 'John Doe', identifier: '12345678', billingService: 'Consulatation', totalAmount: 600 },
28
32
  { uuid: '3', patientName: 'John Doe', identifier: '12345678', billingService: 'Child services', totalAmount: 700 },
@@ -37,69 +41,19 @@ const mockBillsData = [
37
41
  { uuid: '12', patientName: 'John Doe', identifier: '12345678', billingService: 'MCH', totalAmount: 1300 },
38
42
  ];
39
43
 
40
- // Mock the invoice table component
41
- jest.mock('../invoice/invoice-table.component', () => jest.fn(() => <div>Invoice table</div>));
42
-
43
- // Mock the billing resource
44
- jest.mock('../billing.resource', () => ({
45
- useBills: jest.fn(() => ({
46
- bills: mockBillsData,
47
- isLoading: false,
48
- isValidating: false,
49
- error: null,
50
- })),
51
- }));
52
-
53
- // Mock esm-patient-common-lib
54
- jest.mock('@openmrs/esm-patient-common-lib', () => ({
55
- CardHeader: jest.fn(({ children }) => <div>{children}</div>),
56
- EmptyDataIllustration: jest.fn(() => <div>Empty state illustration</div>),
57
- ErrorState: jest.fn(({ error }) => <div>Error: {error?.message}</div>),
58
- usePaginationInfo: jest.fn(() => ({
59
- pageSizes: [10, 20, 30],
60
- currentPage: 1,
61
- })),
62
- }));
63
-
64
- // Mock esm-framework
65
- jest.mock('@openmrs/esm-framework', () => ({
66
- useLayoutType: jest.fn(() => 'small-desktop'),
67
- isDesktop: jest.fn(() => true),
68
- usePagination: jest.fn().mockImplementation((data) => ({
69
- currentPage: 1,
70
- goTo: jest.fn(),
71
- results: data,
72
- paginated: true,
73
- })),
74
- showToast: jest.fn(),
75
- showNotification: jest.fn(),
76
- createErrorHandler: jest.fn(),
77
- createGlobalStore: jest.fn(),
78
- getGlobalStore: jest.fn(() => ({
79
- subscribe: jest.fn(),
80
- getState: jest.fn(),
81
- setState: jest.fn(),
82
- })),
83
- useConfig: jest.fn(() => ({
84
- pageSize: 10,
85
- defaultCurrency: 'USD',
86
- })),
87
- useSession: jest.fn(() => ({
88
- sessionLocation: { uuid: 'some-uuid', display: 'Location' },
89
- })),
90
- formatDate: jest.fn((date) => date?.toString() ?? ''),
91
- formatDatetime: jest.fn((date) => date?.toString() ?? ''),
92
- parseDate: jest.fn((dateString) => new Date(dateString)),
93
- ExtensionSlot: jest.fn(({ children }) => <>{children}</>),
94
- }));
95
-
96
44
  describe('BillHistory', () => {
97
- afterEach(() => {
98
- jest.clearAllMocks();
45
+ beforeEach(() => {
46
+ mockUseConfig.mockReturnValue({ ...getDefaultsFromConfigSchema(configSchema), defaultCurrency: 'USD' });
99
47
  });
100
48
 
101
49
  test('should render loading datatable skeleton', () => {
102
- mockbills.mockReturnValueOnce({ isLoading: true, isValidating: false, error: null, bills: [], mutate: jest.fn() });
50
+ mockUseBills.mockReturnValueOnce({
51
+ isLoading: true,
52
+ isValidating: false,
53
+ error: null,
54
+ bills: [],
55
+ mutate: jest.fn(),
56
+ });
103
57
  render(<BillHistory {...testProps} />);
104
58
  const loadingSkeleton = screen.getByRole('table');
105
59
  expect(loadingSkeleton).toBeInTheDocument();
@@ -107,7 +61,7 @@ describe('BillHistory', () => {
107
61
  });
108
62
 
109
63
  test('should render error state when API call fails', () => {
110
- mockbills.mockReturnValueOnce({
64
+ mockUseBills.mockReturnValueOnce({
111
65
  isLoading: false,
112
66
  isValidating: false,
113
67
  error: new Error('some error'),
@@ -115,31 +69,31 @@ describe('BillHistory', () => {
115
69
  mutate: jest.fn(),
116
70
  });
117
71
  render(<BillHistory {...testProps} />);
118
- const errorState = screen.getByText('Error: some error');
72
+ const errorState = screen.getByText(/Error/);
119
73
  expect(errorState).toBeInTheDocument();
120
74
  });
121
75
 
122
76
  test('should render bills table', async () => {
123
77
  const user = userEvent.setup();
124
- mockbills.mockReturnValueOnce({
78
+ mockUseBills.mockReturnValueOnce({
125
79
  isLoading: false,
126
80
  isValidating: false,
127
81
  error: null,
128
- bills: mockBillsData as any,
82
+ bills: mockBillData as any,
129
83
  mutate: jest.fn(),
130
84
  });
131
85
  render(<BillHistory {...testProps} />);
132
86
 
133
87
  // Verify headers
134
- expect(screen.getByText('visitTime')).toBeInTheDocument();
135
- expect(screen.getByText('identifier')).toBeInTheDocument();
88
+ expect(screen.getByText('Visit time')).toBeInTheDocument();
89
+ expect(screen.getByText('Identifier')).toBeInTheDocument();
136
90
 
137
91
  const tableRowGroup = screen.getAllByRole('rowgroup');
138
92
  expect(tableRowGroup).toHaveLength(2);
139
93
 
140
94
  // Page navigation should work as expected
141
- const nextPageButton = screen.getByRole('button', { name: /nextPage/ });
142
- const prevPageButton = screen.getByRole('button', { name: /previousPage/ });
95
+ const nextPageButton = screen.getByRole('button', { name: /Next page/ });
96
+ const prevPageButton = screen.getByRole('button', { name: /Previous page/ });
143
97
 
144
98
  expect(nextPageButton).toBeInTheDocument();
145
99
  expect(prevPageButton).toBeInTheDocument();
@@ -155,7 +109,13 @@ describe('BillHistory', () => {
155
109
  });
156
110
 
157
111
  test('should render empty state view when there are no bills', () => {
158
- mockbills.mockReturnValueOnce({ isLoading: false, isValidating: false, error: null, bills: [], mutate: jest.fn() });
112
+ mockUseBills.mockReturnValueOnce({
113
+ isLoading: false,
114
+ isValidating: false,
115
+ error: null,
116
+ bills: [],
117
+ mutate: jest.fn(),
118
+ });
159
119
  render(<BillHistory {...testProps} />);
160
120
  const emptyState = screen.getByText(/There are no bills to display./);
161
121
  expect(emptyState).toBeInTheDocument();