@kenyaemr/esm-bed-management-app 1.0.1-pre.11

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 (137) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintignore +2 -0
  3. package/.eslintrc +37 -0
  4. package/.husky/pre-commit +4 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  6. package/.idea/modules.xml +8 -0
  7. package/.idea/vcs.xml +6 -0
  8. package/.prettierignore +14 -0
  9. package/.turbo.json +18 -0
  10. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +541 -0
  11. package/.yarn/plugins/@yarnpkg/plugin-version.cjs +550 -0
  12. package/.yarn/versions/3d353a50.yml +0 -0
  13. package/LICENSE +373 -0
  14. package/README.md +40 -0
  15. package/dist/207.js +1 -0
  16. package/dist/207.js.map +1 -0
  17. package/dist/26.js +2 -0
  18. package/dist/26.js.LICENSE.txt +32 -0
  19. package/dist/26.js.map +1 -0
  20. package/dist/283.js +1 -0
  21. package/dist/283.js.map +1 -0
  22. package/dist/294.js +2 -0
  23. package/dist/294.js.LICENSE.txt +9 -0
  24. package/dist/294.js.map +1 -0
  25. package/dist/330.js +2 -0
  26. package/dist/330.js.LICENSE.txt +44 -0
  27. package/dist/330.js.map +1 -0
  28. package/dist/404.js +1 -0
  29. package/dist/404.js.map +1 -0
  30. package/dist/455.js +2 -0
  31. package/dist/455.js.LICENSE.txt +9 -0
  32. package/dist/455.js.map +1 -0
  33. package/dist/558.js +2 -0
  34. package/dist/558.js.LICENSE.txt +14 -0
  35. package/dist/558.js.map +1 -0
  36. package/dist/574.js +1 -0
  37. package/dist/629.js +1 -0
  38. package/dist/629.js.map +1 -0
  39. package/dist/637.js +1 -0
  40. package/dist/637.js.map +1 -0
  41. package/dist/707.js +1 -0
  42. package/dist/707.js.map +1 -0
  43. package/dist/800.js +2 -0
  44. package/dist/800.js.LICENSE.txt +3 -0
  45. package/dist/800.js.map +1 -0
  46. package/dist/850.js +1 -0
  47. package/dist/850.js.map +1 -0
  48. package/dist/884.js +1 -0
  49. package/dist/884.js.map +1 -0
  50. package/dist/933.js +1 -0
  51. package/dist/933.js.map +1 -0
  52. package/dist/esm-kenyaemr-bed-management-app.js +1 -0
  53. package/dist/esm-kenyaemr-bed-management-app.js.buildmanifest.json +506 -0
  54. package/dist/esm-kenyaemr-bed-management-app.js.map +1 -0
  55. package/dist/main.js +1 -0
  56. package/dist/main.js.map +1 -0
  57. package/dist/routes.json +1 -0
  58. package/i18next-parser.config.js +89 -0
  59. package/jest.config.js +0 -0
  60. package/package.json +112 -0
  61. package/src/__mocks__/react-i18next.js +55 -0
  62. package/src/admin-card-link.component.tsx +27 -0
  63. package/src/assets/landing-page.png +0 -0
  64. package/src/assets/logo.svg +1 -0
  65. package/src/bed-administration/bed-administration-form.component.tsx +326 -0
  66. package/src/bed-administration/bed-administration-form.scss +0 -0
  67. package/src/bed-administration/bed-administration-table.component.tsx +317 -0
  68. package/src/bed-administration/bed-administration-table.scss +112 -0
  69. package/src/bed-administration/bed-administration-types.ts +20 -0
  70. package/src/bed-administration/bed-administration.resource.ts +59 -0
  71. package/src/bed-administration/edit-bed-form.component.tsx +100 -0
  72. package/src/bed-administration/new-bed-form.component.tsx +112 -0
  73. package/src/bed-admission/active-patients/active-patients-table.component.tsx +299 -0
  74. package/src/bed-admission/active-patients/active-visits.resource.ts +171 -0
  75. package/src/bed-admission/active-patients/admission-action-button-styles.scss +0 -0
  76. package/src/bed-admission/active-patients/admission-action-button.component.tsx +26 -0
  77. package/src/bed-admission/active-patients/index.tsx +15 -0
  78. package/src/bed-admission/active-patients/patient-queues.resource.ts +136 -0
  79. package/src/bed-admission/active-patients/styles.scss +284 -0
  80. package/src/bed-admission/active-patients/view-action-menu.component.tsx +33 -0
  81. package/src/bed-admission/admitted-patients/active-admissions.resource.ts +125 -0
  82. package/src/bed-admission/admitted-patients/admitted-patients-table.component.tsx +280 -0
  83. package/src/bed-admission/admitted-patients/admitted-patients.component.tsx +22 -0
  84. package/src/bed-admission/admitted-patients/location-combo-box.component.tsx +55 -0
  85. package/src/bed-admission/admitted-patients/styles.scss +284 -0
  86. package/src/bed-admission/bed-admission-tabs-styles.scss +30 -0
  87. package/src/bed-admission/bed-admission-tabs.component.tsx +69 -0
  88. package/src/bed-admission/bed-admission.component.tsx +15 -0
  89. package/src/bed-admission/bed-admission.resource.ts +52 -0
  90. package/src/bed-admission/bed-layout/bed-layout-list.component.tsx +101 -0
  91. package/src/bed-admission/bed-layout/bed-layout.component.tsx +64 -0
  92. package/src/bed-admission/bed-layout/bed-layout.scss +118 -0
  93. package/src/bed-admission/bed-layout/min-bed-layout.component.tsx +26 -0
  94. package/src/bed-admission/bed-tag/bed-tag-administration-table.component.tsx +217 -0
  95. package/src/bed-admission/bed-tag/bed-tags-admin-form.component.tsx +131 -0
  96. package/src/bed-admission/bed-tag/edit-tag-form.component.tsx +80 -0
  97. package/src/bed-admission/bed-tag/new-tag-form.component.tsx +83 -0
  98. package/src/bed-admission/bed-type/bed-type-admin-form.component.tsx +173 -0
  99. package/src/bed-admission/bed-type/bed-type-administration-table.component.tsx +222 -0
  100. package/src/bed-admission/bed-type/edit-bed-type.component.tsx +80 -0
  101. package/src/bed-admission/bed-type/new-bed-type-form.component.tsx +87 -0
  102. package/src/bed-admission/createDashboardLink.tsx +47 -0
  103. package/src/bed-admission/discharged-patients/discharged-patients.componet.tsx +19 -0
  104. package/src/bed-admission/helpers/functions.ts +102 -0
  105. package/src/bed-admission/types.ts +133 -0
  106. package/src/config-schema.ts +31 -0
  107. package/src/declarations.d.ts +7 -0
  108. package/src/empty-state/empty-state.component.tsx +69 -0
  109. package/src/empty-state/empty-state.scss +62 -0
  110. package/src/header/header.component.tsx +51 -0
  111. package/src/header/header.scss +72 -0
  112. package/src/header/illustration.component.tsx +13 -0
  113. package/src/home.component.tsx +15 -0
  114. package/src/home.scss +5 -0
  115. package/src/index.ts +78 -0
  116. package/src/left-panel/left-panel.component.tsx +33 -0
  117. package/src/left-panel/left-panel.scss +41 -0
  118. package/src/left-panel-link.component.tsx +49 -0
  119. package/src/root.component.tsx +39 -0
  120. package/src/root.scss +11 -0
  121. package/src/routes.json +56 -0
  122. package/src/setup-tests.ts +1 -0
  123. package/src/summary/summary.component.tsx +74 -0
  124. package/src/summary/summary.resource.ts +211 -0
  125. package/src/summary/summary.scss +72 -0
  126. package/src/types.ts +163 -0
  127. package/src/ward-card/ward-card.component.tsx +41 -0
  128. package/src/ward-card/ward-card.scss +51 -0
  129. package/src/ward-with-beds/ward-with-beds.component.tsx +186 -0
  130. package/src/ward-with-beds/ward-with-beds.scss +27 -0
  131. package/src/workspace/allocate-bed-workspace.component.tsx +188 -0
  132. package/src/workspace/allocate-bed.scss +124 -0
  133. package/src/workspace/overlay.component.tsx +55 -0
  134. package/src/workspace/overlay.scss +96 -0
  135. package/translations/en.json +7 -0
  136. package/tsconfig.json +23 -0
  137. package/webpack.config.js +1 -0
@@ -0,0 +1,89 @@
1
+ module.exports = {
2
+ contextSeparator: "_",
3
+ // Key separator used in your translation keys
4
+
5
+ createOldCatalogs: false,
6
+ // Save the \_old files
7
+
8
+ defaultNamespace: "translations",
9
+ // Default namespace used in your i18next config
10
+
11
+ defaultValue: "",
12
+ // Default value to give to empty keys
13
+ // You may also specify a function accepting the locale, namespace, and key as arguments
14
+
15
+ indentation: 2,
16
+ // Indentation of the catalog files
17
+
18
+ keepRemoved: false,
19
+ // Keep keys from the catalog that are no longer in code
20
+
21
+ keySeparator: ".",
22
+ // Key separator used in your translation keys
23
+ // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
24
+
25
+ // see below for more details
26
+ lexers: {
27
+ hbs: ["HandlebarsLexer"],
28
+ handlebars: ["HandlebarsLexer"],
29
+
30
+ htm: ["HTMLLexer"],
31
+ html: ["HTMLLexer"],
32
+
33
+ mjs: ["JavascriptLexer"],
34
+ js: ["JavascriptLexer"], // if you're writing jsx inside .js files, change this to JsxLexer
35
+ ts: ["JavascriptLexer"],
36
+ jsx: ["JsxLexer"],
37
+ tsx: ["JsxLexer"],
38
+
39
+ default: ["JavascriptLexer"],
40
+ },
41
+
42
+ lineEnding: "auto",
43
+ // Control the line ending. See options at https://github.com/ryanve/eol
44
+
45
+ locales: ["en"],
46
+ // An array of the locales in your applications
47
+
48
+ namespaceSeparator: ":",
49
+ // Namespace separator used in your translation keys
50
+ // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance.
51
+
52
+ output: "$NAMESPACE/$LOCALE.json",
53
+ // Supports $LOCALE and $NAMESPACE injection
54
+ // Supports JSON (.json) and YAML (.yml) file formats
55
+ // Where to write the locale files relative to process.cwd()
56
+
57
+ pluralSeparator: "_",
58
+ // Plural separator used in your translation keys
59
+ // If you want to use plain english keys, separators such as `_` might conflict. You might want to set `pluralSeparator` to a different string that does not occur in your keys.
60
+
61
+ input: undefined,
62
+ // An array of globs that describe where to look for source files
63
+ // relative to the location of the configuration file
64
+
65
+ sort: true,
66
+ // Whether or not to sort the catalog
67
+
68
+ useKeysAsDefaultValue: false,
69
+ // Whether to use the keys as the default value; ex. "Hello": "Hello", "World": "World"
70
+ // This option takes precedence over the `defaultValue` and `skipDefaultValues` options
71
+ // You may also specify a function accepting the locale and namespace as arguments
72
+
73
+ verbose: false,
74
+ // Display info about the parsing including some stats
75
+
76
+ failOnWarnings: false,
77
+ // Exit with an exit code of 1 on warnings
78
+
79
+ customValueTemplate: null,
80
+ // If you wish to customize the value output the value as an object, you can set your own format.
81
+ // ${defaultValue} is the default value you set in your translation function.
82
+ // Any other custom property will be automatically extracted.
83
+ //
84
+ // Example:
85
+ // {
86
+ // message: "${defaultValue}",
87
+ // description: "${maxLength}", // t('my-key', {maxLength: 150})
88
+ // }
89
+ };
package/jest.config.js ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "@kenyaemr/esm-bed-management-app",
3
+ "version": "1.0.1-pre.11",
4
+ "license": "MPL-2.0",
5
+ "description": "A frontend module for managing beds in UgandaEMR+",
6
+ "browser": "dist/esm-kenyaemr-bed-management-app.js",
7
+ "main": "src/index.ts",
8
+ "source": true,
9
+ "scripts": {
10
+ "start": "openmrs develop",
11
+ "serve": "webpack serve --mode=development",
12
+ "build": "webpack --mode production",
13
+ "analyze": "webpack --mode=production --env analyze=true",
14
+ "lint": "TIMING=1 eslint src --ext js,jsx,ts,tsx",
15
+ "prettier": "prettier --write \"src/**/*.{ts,tsx}\"",
16
+ "typescript": "tsc",
17
+ "test": "jest --config jest.config.js --passWithNoTests",
18
+ "verify": "turbo lint typescript coverage",
19
+ "coverage": "yarn test --coverage",
20
+ "prepare": "husky install",
21
+ "extract-translations": "i18next 'src/**/*.component.tsx' --config ./i18next-parser.config.js"
22
+ },
23
+ "husky": {
24
+ "hooks": {
25
+ "pre-commit": "pretty-quick --staged && yarn verify"
26
+ }
27
+ },
28
+ "browserslist": [
29
+ "extends browserslist-config-openmrs"
30
+ ],
31
+ "keywords": [
32
+ "openmrs",
33
+ "microfrontends"
34
+ ],
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/mets-programme/esm-ugandaemr-bed-management-app.git"
38
+ },
39
+ "homepage": "https://github.com/mets-programme/esm-ugandaemr-bed-management-app#readme",
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/mets-programme/esm-ugandaemr-bed-management-app/issues"
45
+ },
46
+ "dependencies": {
47
+ "@carbon/react": "^1.33.1",
48
+ "@hookform/resolvers": "^3.3.1",
49
+ "@openmrs/openmrs-form-engine-lib": "^1.0.0-pre.44",
50
+ "classnames": "^2.3.2",
51
+ "dayjs": "^1.11.9",
52
+ "lodash-es": "^4.17.21",
53
+ "react-hook-form": "^7.47.0",
54
+ "react-image-annotate": "^1.8.0",
55
+ "zod": "^3.22.2"
56
+ },
57
+ "peerDependencies": {
58
+ "@openmrs/esm-framework": "*",
59
+ "@openmrs/openmrs-form-engine-lib": "5.x",
60
+ "react": "18.x",
61
+ "react-i18next": "11.x",
62
+ "react-router-dom": "6.x",
63
+ "rxjs": "6.x"
64
+ },
65
+ "devDependencies": {
66
+ "@openmrs/esm-framework": "next",
67
+ "@openmrs/esm-patient-common-lib": "next",
68
+ "@openmrs/esm-styleguide": "next",
69
+ "@swc/cli": "^0.1.62",
70
+ "@swc/core": "^1.3.68",
71
+ "@swc/jest": "^0.2.26",
72
+ "@testing-library/dom": "^8.20.1",
73
+ "@testing-library/jest-dom": "^5.16.5",
74
+ "@testing-library/react": "^13.4.0",
75
+ "@testing-library/user-event": "^14.4.3",
76
+ "@types/jest": "^28.1.8",
77
+ "@types/react": "^18.2.14",
78
+ "@types/react-dom": "^18.2.6",
79
+ "@types/react-router": "^5.1.20",
80
+ "@types/react-router-dom": "^5.3.3",
81
+ "@types/webpack-env": "^1.18.1",
82
+ "@typescript-eslint/eslint-plugin": "^5.61.0",
83
+ "@typescript-eslint/parser": "^5.61.0",
84
+ "css-loader": "^6.8.1",
85
+ "eslint": "^8.44.0",
86
+ "eslint-config-prettier": "^8.8.0",
87
+ "eslint-config-ts-react-important-stuff": "^3.0.0",
88
+ "eslint-plugin-prettier": "^4.2.1",
89
+ "husky": "^8.0.0",
90
+ "i18next": "^23.2.8",
91
+ "i18next-parser": "^8.0.0",
92
+ "identity-obj-proxy": "^3.0.0",
93
+ "jest": "^28.1.3",
94
+ "jest-cli": "^28.1.3",
95
+ "jest-environment-jsdom": "^28.1.3",
96
+ "openmrs": "next",
97
+ "prettier": "^2.8.8",
98
+ "pretty-quick": "^3.1.3",
99
+ "react": "^18.2.0",
100
+ "react-dom": "^18.2.0",
101
+ "react-i18next": "^11.18.6",
102
+ "react-router-dom": "^6.14.1",
103
+ "rxjs": "^6.6.7",
104
+ "swc-loader": "^0.2.3",
105
+ "turbo": "^1.10.7",
106
+ "typescript": "^4.9.5",
107
+ "webpack": "^5.88.1",
108
+ "webpack-cli": "^5.1.4"
109
+ },
110
+ "packageManager": "yarn@3.6.3",
111
+ "stableVersion": "1.0.0"
112
+ }
@@ -0,0 +1,55 @@
1
+ /** At present, this entire mock is boilerplate. */
2
+ import "react";
3
+ import "react-i18next";
4
+
5
+ const hasChildren = (node) =>
6
+ node && (node.children || (node.props && node.props.children));
7
+
8
+ const getChildren = (node) =>
9
+ node && node.children ? node.children : node.props && node.props.children;
10
+
11
+ const renderNodes = (reactNodes) => {
12
+ if (typeof reactNodes === "string") {
13
+ return reactNodes;
14
+ }
15
+
16
+ return Object.keys(reactNodes).map((key, i) => {
17
+ const child = reactNodes[key];
18
+ const isElement = React.isValidElement(child);
19
+
20
+ if (typeof child === "string") {
21
+ return child;
22
+ }
23
+ if (hasChildren(child)) {
24
+ const inner = renderNodes(getChildren(child));
25
+ return React.cloneElement(child, { ...child.props, key: i }, inner);
26
+ }
27
+ if (typeof child === "object" && !isElement) {
28
+ return Object.keys(child).reduce(
29
+ (str, childKey) => `${str}${child[childKey]}`,
30
+ ""
31
+ );
32
+ }
33
+
34
+ return child;
35
+ });
36
+ };
37
+
38
+ const useMock = [(k) => k, {}];
39
+ useMock.t = (k, o) => (o && o.defaultValue) || (typeof o === "string" ? o : k);
40
+ useMock.i18n = {};
41
+
42
+ module.exports = {
43
+ // this mock makes sure any components using the translate HoC receive the t function as a prop
44
+ Trans: ({ children }) => renderNodes(children),
45
+ Translation: ({ children }) => children((k) => k, { i18n: {} }),
46
+ useTranslation: () => useMock,
47
+
48
+ // mock if needed
49
+ I18nextProvider: reactI18next.I18nextProvider,
50
+ initReactI18next: reactI18next.initReactI18next,
51
+ setDefaults: reactI18next.setDefaults,
52
+ getDefaults: reactI18next.getDefaults,
53
+ setI18n: reactI18next.setI18n,
54
+ getI18n: reactI18next.getI18n,
55
+ };
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ import { useTranslation } from "react-i18next";
3
+ import { Layer, ClickableTile } from "@carbon/react";
4
+ import { ArrowRight } from "@carbon/react/icons";
5
+
6
+ const BedManagementAdminCardLink: React.FC = () => {
7
+ const { t } = useTranslation();
8
+ const header = t("manageBeds", "Manage Beds");
9
+ return (
10
+ <Layer>
11
+ <ClickableTile
12
+ href={window.getOpenmrsSpaBase() + "bed-management/summary"}
13
+ rel="noopener noreferrer"
14
+ >
15
+ <div>
16
+ <div className="heading">{header}</div>
17
+ <div className="content">{t("bedManagement", "Bed Management")}</div>
18
+ </div>
19
+ <div className="iconWrapper">
20
+ <ArrowRight size={16} />
21
+ </div>
22
+ </ClickableTile>
23
+ </Layer>
24
+ );
25
+ };
26
+
27
+ export default BedManagementAdminCardLink;
Binary file
@@ -0,0 +1 @@
1
+ <svg width="320" height="100" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#409184" d="M0 0h320v100H0z"/><g fill-rule="nonzero"><path d="M57.97 55.541c0 6.26-5.85 9.37-12.48 9.37-6.63 0-12.49-3.11-12.49-9.37v-21.19s5.56 0 5.56 2.7v17.88c0 3.73 2.86 5.68 6.93 5.68s6.93-1.95 6.93-5.68v-20.58s5.56 0 5.56 2.7v18.49h-.01Zm48.45-6.14c0-1.49-.83-2.82-4.02-2.82-3.48 0-6.64 1.2-6.64 4.69-1.2-1.62-1.58-2.9-1.58-4.02 0-3.61 4.85-4.69 8.25-4.69 6.1 0 9.33 2.57 9.33 7.09v15.18c-1.08-.54-2.16-.66-3.19-.66-2.07 0-4.15.75-6.51.75-4.27 0-8.46-1.58-8.46-6.51 0-7.76 11.12-5.73 12.82-9.01Zm.12 4.23c-.95 2.2-7.59 1-7.59 4.69 0 1.87 1.37 2.82 3.73 2.82 2.2 0 3.86-.75 3.86-.75v-6.76Zm23.27-3.61c0-2.2-1.04-3.44-3.98-3.44-2.12 0-3.94.58-3.94.58v17.54s-5.39 0-5.39-2.7v-19.44s1.62.87 3.73.87c2.03 0 3.94-.87 6.18-.87 4.27 0 8.79 1.45 8.79 6.84v15.3s-5.39-.08-5.39-2.7v-11.98Zm23.27-17.21c.37 0 5.39-.04 5.39 3.11v28.99c-1.08-.54-2.2-.71-3.32-.71-2.2 0-4.31.71-6.05.71-5.93 0-9.33-2.61-9.33-7.22v-7.96c0-4.48 3.86-7.17 8.29-7.17 3.44 0 5.35 1.62 5.35 1.62s-.33-1.37-.33-2.41v-8.96Zm0 17.13c0-2.03-2.24-3.44-4.48-3.44-1.99 0-3.44 1.16-3.44 3.86v6.72c0 2.61 1 3.9 4.44 3.9 1.87 0 3.48-.5 3.48-.5v-10.54Zm22.44-.54c0-1.49-.83-2.82-4.02-2.82-3.48 0-6.64 1.2-6.64 4.69-1.2-1.62-1.58-2.9-1.58-4.02 0-3.61 4.85-4.69 8.25-4.69 6.1 0 9.33 2.57 9.33 7.09v15.18c-1.08-.54-2.16-.66-3.19-.66-2.07 0-4.15.75-6.51.75-4.27 0-8.46-1.58-8.46-6.51 0-7.76 11.12-5.73 12.82-9.01Zm.12 4.23c-.95 2.2-7.59 1-7.59 4.69 0 1.87 1.37 2.82 3.73 2.82 2.2 0 3.86-.75 3.86-.75v-6.76Zm17.3-2.53v8.29h14.81c0 .29-.08 5.02-3.86 5.02h-12.73s-5.35 0-5.35-3.19v-26.95h21.94s0 5.06-3.86 5.06h-10.95v6.72h7.8c4.11 0 5.35-.66 5.35-.66 0 2.86-.58 5.72-4.69 5.72h-8.46v-.01Zm41.93-.16c0-2.03.54-4.65.54-4.65l-6.3 13.4s-4.31 0-6.3-3.11l-4.44-10.04s.46 2.36.46 4.4v13.89s-7.13 0-7.13-3.19v-27.37h4.4c.25 0 3.36-.04 4.65 2.78l6.1 13.52 7.63-16.3c1.58 0 7.55 0 7.55 3.19v27.37s-7.13 0-7.13-3.19v-10.7h-.03Zm27 .41c5.23 3.44 6.68 8.38 9.75 12.73 0 0-2.53.66-3.53.66-1.74 0-4.15-.54-5.64-2.95-1.7-2.7-3.82-7.92-7.92-9.7v12.73s-7.13-.12-7.13-3.19v-27.36h10.41c6.72 0 11.57 1.91 11.57 7.88v1.24c0 4.35-2.7 7.88-7.51 7.96Zm-7.34-12.03v7.51h3.61c3.03 0 4.11-1.33 4.11-4.06 0-2.7-1.45-3.44-4.44-3.44h-3.28v-.01Z" fill="#FDFFFF"/><path d="M103.92 73.601c0-.59.07-1.22.07-1.22l-3.51 7.12s-.43 0-.83-.64l-3.09-6.52s.07.67.07 1.26v8.17s-.84 0-.84-.64v-9.57h.71s.57 0 .88.68l2.87 6.1 3.26-6.77s1.25.01 1.25.64v9.57s-.84 0-.84-.64v-7.54Zm9.66 6.76c0 1-1.44 1.45-2.73 1.45-1.67 0-2.74-.83-2.74-2.25v-3.01c0-1.41.94-2.25 2.67-2.25s2.73.84 2.73 2.1c0 1.15-.78 2.03-2.8 2.03-.94 0-1.8-.38-1.8-.38v1.29c0 1.09.46 1.68 1.94 1.68 1.32 0 2.39-.52 2.39-1.59.26.35.34.65.34.93Zm-4.66-3.05c-.06.03 1 .33 1.73.33 1.54 0 2.07-.48 2.07-1.25 0-.8-.48-1.35-1.9-1.35-1.54 0-1.9.64-1.9 1.71v.56Zm12.24-6.41c.13 0 .8-.01.8.77v10.03c-.26-.1-.52-.14-.77-.14-.59 0-1.32.25-2.04.25-1.67 0-2.64-.87-2.64-2.2v-3.1c0-1.38 1.09-2.19 2.36-2.19 2.03 0 2.45.9 2.45.9s-.16-.28-.16-1.52v-2.8Zm0 5.67c0-.9-1.06-1.51-2.1-1.51-1 0-1.75.51-1.75 1.65v2.67c0 1.12.51 1.67 2.1 1.67 1.07 0 1.75-.35 1.75-.35v-4.13Zm4.1-4.72h.75c.13 0 .16.12.16.2v.8h-.75c-.09 0-.16-.1-.16-.19v-.81Zm.06 2.52c.09 0 .8.03.8.64v6.73s-.8 0-.8-.64v-6.73Zm9.58 5.97c0 1.01-1.38 1.46-2.68 1.46-1.74 0-2.88-.83-2.88-2.25v-3.01c0-1.41 1.01-2.25 2.74-2.25 1.51 0 2.51.57 2.51 1.44 0 .28-.1.58-.32.87 0-.91-.81-1.52-2.22-1.52-1.48 0-1.91.71-1.91 1.62v2.71c0 .96.68 1.61 2.09 1.61 1.32 0 2.32-.52 2.32-1.59.22.33.35.65.35.91Zm7.22-3.81c0-.84-.36-1.45-1.83-1.45-1.41 0-2.23.61-2.23 1.52-.22-.29-.3-.61-.3-.87 0-.86 1.01-1.44 2.54-1.44 1.8 0 2.62.87 2.62 2.28v5.13c-.26-.1-.64-.14-.88-.14-.45 0-1.17.25-1.78.25-1.81 0-2.74-.86-2.74-2.06-.01-2.71 4.11-1.74 4.6-3.22Zm-1.75 4.54c1.07 0 1.75-.38 1.75-.38v-3.22c-.29 1.01-3.78.39-3.78 2.29 0 .76.59 1.31 2.03 1.31Zm5.76-10.21c.06 0 .8.01.8.64v7.8c0 1.12.25 2.16.94 2.16 0 0-.19.28-.57.28-.81 0-1.17-.88-1.17-2.06v-8.82Zm15.67 3.52c0 1.74-1.3 2.52-3.1 2.45 2.12.67 3.04 3.96 3.65 4.91-.67.09-.94.01-1.35-.77-1.23-2.41-1.87-3.94-4.41-4.13v4.93s-.84-.01-.84-.64v-9.57h2.81c1.87 0 3.23.62 3.23 2.38v.44h.01Zm-5.2-2.03v3.78h2.09c1.68 0 2.28-.71 2.28-1.97 0-1.28-.78-1.81-2.39-1.81h-1.98Zm13.45 8.01c0 1-1.44 1.45-2.73 1.45-1.67 0-2.74-.83-2.74-2.25v-3.01c0-1.41.94-2.25 2.67-2.25s2.73.84 2.73 2.1c0 1.15-.78 2.03-2.8 2.03-.94 0-1.8-.38-1.8-.38v1.29c0 1.09.46 1.68 1.94 1.68 1.32 0 2.39-.52 2.39-1.59.25.35.34.65.34.93Zm-4.67-3.05c-.06.03 1 .33 1.73.33 1.54 0 2.07-.48 2.07-1.25 0-.8-.48-1.35-1.9-1.35-1.54 0-1.9.64-1.9 1.71v.56Zm13.16 3.03c0 1.01-1.38 1.46-2.68 1.46-1.74 0-2.88-.83-2.88-2.25v-3.01c0-1.41 1.01-2.25 2.74-2.25 1.51 0 2.51.57 2.51 1.44 0 .28-.1.58-.32.87 0-.91-.81-1.52-2.22-1.52-1.48 0-1.91.71-1.91 1.62v2.71c0 .96.68 1.61 2.09 1.61 1.32 0 2.32-.52 2.32-1.59.22.33.35.65.35.91Zm8.67-.78c0 1.42-.94 2.25-2.94 2.25-2.01 0-2.96-.83-2.96-2.25v-3.01c0-1.41.94-2.25 2.96-2.25 2 0 2.94.84 2.94 2.25v3.01Zm-.8-2.8c0-1.07-.36-1.71-2.15-1.71-1.8 0-2.16.64-2.16 1.71v2.58c0 1.09.36 1.72 2.16 1.72 1.78 0 2.15-.64 2.15-1.72v-2.58Zm6.45-1.61c-.62 0-1.29.12-1.64.3v6.28s-.8 0-.8-.64v-6.68c.29.14.46.2.7.2.59 0 1.3-.32 2.06-.32 1.19 0 1.64.67 1.12 1.75-.03-.7-.7-.89-1.44-.89Zm8.49-4.25c.13 0 .8-.01.8.77v10.03c-.26-.1-.52-.14-.77-.14-.59 0-1.32.25-2.04.25-1.67 0-2.64-.87-2.64-2.2v-3.1c0-1.38 1.09-2.19 2.36-2.19 2.03 0 2.45.9 2.45.9s-.16-.28-.16-1.52v-2.8Zm0 5.67c0-.9-1.06-1.51-2.1-1.51-1 0-1.75.51-1.75 1.65v2.67c0 1.12.51 1.67 2.1 1.67 1.07 0 1.75-.35 1.75-.35v-4.13Zm3.73-.07c0-1.45.91-2.2 2.67-2.2 1.29 0 2.54.36 2.54 1.39 0 .3-.12.65-.35 1.07.01-.07.01-.16.01-.23 0-.87-.61-1.48-2.2-1.48-1.42 0-1.87.51-1.87 1.38 0 2.1 4.67-.12 4.67 3.25 0 1.25-.94 2.13-2.8 2.13-1.26 0-2.64-.3-2.64-1.46 0-.26.07-.55.22-.9 0 .91.71 1.61 2.42 1.61 1.58 0 2-.68 2-1.45 0-2.31-4.6-.15-4.67-3.11Zm17.1-5.12c1.52 0 3.3.33 3.3 1.67 0 .28-.09.64-.26 1.04 0-1.51-1.57-1.9-3.15-1.9-1.54 0-2.78.42-2.78 1.94 0 2.94 6.51.51 6.51 4.68 0 2.03-1.52 2.99-3.68 2.99-1.52 0-3.48-.45-3.48-1.7 0-.29.07-.65.23-1.06 0 1.46 1.67 1.94 3.33 1.94 1.83 0 2.72-.83 2.72-2.13 0-3.26-6.51-.91-6.51-4.65.01-2.17 1.8-2.82 3.77-2.82Zm11.01 6.16.26-2.45c.06-.59.55-.65.84-.7l-.28 2.8c-.19 1.97-1.26 3.15-2.07 3.77l-.57.44c-1.04.81-1.77 1.33-1.77 2.46 0 .36.12.78.33 1.29-.06.01-.15.03-.22.03-.39 0-.94-.23-.94-1.38 0-1.26 1-2.17 2.06-2.88-.7-.44-1.91-1.7-2.12-3.73l-.29-2.81s.77 0 .84.64l.29 2.52c.2 1.8 1.81 2.94 1.81 2.94s1.65-1.21 1.83-2.94Zm3.45-1.04c0-1.45.91-2.2 2.67-2.2 1.29 0 2.54.36 2.54 1.39 0 .3-.12.65-.35 1.07.01-.07.01-.16.01-.23 0-.87-.61-1.48-2.2-1.48-1.42 0-1.87.51-1.87 1.38 0 2.1 4.67-.12 4.67 3.25 0 1.25-.94 2.13-2.8 2.13-1.26 0-2.64-.3-2.64-1.46 0-.26.07-.55.22-.9 0 .91.71 1.61 2.42 1.61 1.58 0 2-.68 2-1.45 0-2.31-4.6-.15-4.67-3.11Zm8.39-3.93s.8.01.8.64v1.43h3.03s-.01.78-.54.78h-2.49v3.84c0 1.09.59 1.75 1.75 1.75.94 0 1.75-.59 1.75-1.48.19.35.26.61.26.87 0 .94-.99 1.39-2.03 1.39-1.39 0-2.54-.75-2.54-2.17v-7.05h.01Zm12.31 7.79c0 1-1.44 1.45-2.73 1.45-1.67 0-2.74-.83-2.74-2.25v-3.01c0-1.41.94-2.25 2.67-2.25s2.73.84 2.73 2.1c0 1.15-.78 2.03-2.8 2.03-.94 0-1.8-.38-1.8-.38v1.29c0 1.09.46 1.68 1.94 1.68 1.32 0 2.39-.52 2.39-1.59.25.35.34.65.34.93Zm-4.67-3.05c-.06.03 1 .33 1.73.33 1.54 0 2.07-.48 2.07-1.25 0-.8-.48-1.35-1.9-1.35-1.54 0-1.9.64-1.9 1.71v.56Zm12.39-.71c0-1.1-.62-1.52-1.93-1.52-1.22 0-1.93.35-1.93.35v6.31s-.8 0-.8-.62v-6.7s.36.2.65.2c.75 0 1.44-.32 2.28-.32 1.7 0 2.16.87 2.16.87.41-.38 1.2-.86 2.52-.86 1.17 0 2.49.58 2.49 2.09v5.33s-.8-.01-.8-.62v-4.55c0-1.03-.61-1.46-1.94-1.46-.91 0-1.91.46-1.91 1.26v5.38s-.8 0-.8-.62v-4.52h.01Z" fill="#FAFCFC"/><path d="M276.95 30.661c.3-.52.49-1.1.7-1.71.21-.63.43-1.28.78-1.89a5.25 5.25 0 0 1 1.93-1.9v-4.96c0-.63.12-3.99 0-6.2-1.85-.08-6.36 3.4-6.81 6.2v5.91h-5.91c-.63 0-1.14.51-1.14 1.14v4.54c0 .63.51 1.14 1.14 1.14h5.87c1.41-.17 2.74-1.03 3.44-2.27Z" fill="#FFF"/><path d="M286.28 26.811h-5.9c-.5.36-.93.82-1.24 1.36-.31.53-.51 1.12-.72 1.74-.21.62-.42 1.26-.76 1.85-.84 1.47-2.42 2.49-4.1 2.67v12.31c2.37-.49 6.93-2.44 6.81-7.21v-5.91h5.91c.63 0 1.14-.51 1.14-1.14v-4.54c-.01-.63-.52-1.13-1.14-1.13Z" fill="#FDFFFF"/><path d="M66.09 61.081c-1.51-1.46-2.36-4.37-2.36-6.45v-2.64c0-6.67 5.55-9.14 11.22-9.14 3.08 0 6 1.18 8.75 1.18 1.29 0 2.8-.34 4.77-1.12v11.72c0 6.22-4.32 9.93-12.34 9.93-4.21 0-7.07-1.57-7.07-1.57-.17.56-.34 1.46-.34 2.19 0 2.52 2.58 2.92 6.45 2.92h1.85c5.94 0 12.45-.06 12.45 7.96 0 5.94-4.21 9.93-13.35 9.93-7.63 0-12.28-2.41-12.28-6.11 0-1.46.56-2.97 1.91-4.71.5 4.43 5.16 5.5 9.87 5.5 4.99 0 6.73-1.63 6.73-4.21 0-2.63-2.24-2.92-5.5-2.92H74.1c-5.89 0-11.05-.73-11.05-6.39.01-2.25 1.25-3.94 3.04-6.07Zm10.04-1.85c4.15 0 5.1-1.96 5.1-5.44v-4.77s-2.52-.95-4.99-.95c-3.81 0-5.27 1.35-5.27 4.77v.95c0 3.48.95 5.44 5.16 5.44Z" fill="#FFF"/></g></g></svg>
@@ -0,0 +1,326 @@
1
+ import React, { useState } from "react";
2
+ import capitalize from "lodash-es/capitalize";
3
+ import { z } from "zod";
4
+ import { useForm, Controller } from "react-hook-form";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import {
7
+ Button,
8
+ ComboBox,
9
+ ComposedModal,
10
+ Form,
11
+ FormGroup,
12
+ ModalBody,
13
+ ModalFooter,
14
+ ModalHeader,
15
+ NumberInput,
16
+ Select,
17
+ SelectItem,
18
+ Stack,
19
+ TextArea,
20
+ TextInput,
21
+ InlineNotification,
22
+ } from "@carbon/react";
23
+ import { useTranslation } from "react-i18next";
24
+ import { Location } from "@openmrs/esm-framework";
25
+ import type { BedType, InitialData } from "../types";
26
+ import { BedAdministrationData } from "./bed-administration-types";
27
+
28
+ const numberInString = z.string().transform((val, ctx) => {
29
+ const parsed = parseInt(val);
30
+ if (isNaN(parsed) || parsed < 1) {
31
+ ctx.addIssue({
32
+ code: z.ZodIssueCode.custom,
33
+ message: "Please enter a valid number",
34
+ });
35
+ return z.NEVER;
36
+ }
37
+ return val;
38
+ });
39
+
40
+ const BedAdministrationSchema = z.object({
41
+ bedId: z.string().min(5).max(255),
42
+ description: z.string().max(255),
43
+ bedRow: numberInString,
44
+ bedColumn: numberInString,
45
+ location: z
46
+ .object({ display: z.string(), uuid: z.string() })
47
+ .refine((value) => value.display != "", "Please select a valid location"),
48
+ occupancyStatus: z
49
+ .string()
50
+ .refine((value) => value != "", "Please select a valid occupied status"),
51
+ bedType: z
52
+ .string()
53
+ .refine((value) => value != "", "Please select a valid bed type"),
54
+ });
55
+
56
+ interface BedAdministrationFormProps {
57
+ showModal: boolean;
58
+ onModalChange: (showModal: boolean) => void;
59
+ availableBedTypes: Array<BedType>;
60
+ allLocations: Location[];
61
+ handleCreateQuestion?: (formData: BedAdministrationData) => void;
62
+ headerTitle: string;
63
+ occupancyStatuses: string[];
64
+ initialData: InitialData;
65
+ }
66
+
67
+ interface ErrorType {
68
+ message: string;
69
+ }
70
+
71
+ const BedAdministrationForm: React.FC<BedAdministrationFormProps> = ({
72
+ showModal,
73
+ onModalChange,
74
+ availableBedTypes,
75
+ allLocations,
76
+ handleCreateQuestion,
77
+ headerTitle,
78
+ occupancyStatuses,
79
+ initialData,
80
+ }) => {
81
+ const { t } = useTranslation();
82
+ const [occupancyStatus, setOccupancyStatus] = useState(
83
+ capitalize(initialData.status)
84
+ );
85
+ const [selectedBedType] = useState(initialData.bedType.name);
86
+ const [showErrorNotification, setShowErrorNotification] = useState(false);
87
+ const [formStateError, setFormStateError] = useState("");
88
+
89
+ const filterLocationNames = (location) => {
90
+ return (
91
+ location.item.display
92
+ ?.toLowerCase()
93
+ .includes(location?.inputValue?.toLowerCase()) ?? []
94
+ );
95
+ };
96
+
97
+ const {
98
+ handleSubmit,
99
+ control,
100
+ formState: { isDirty },
101
+ } = useForm<BedAdministrationData>({
102
+ mode: "all",
103
+ resolver: zodResolver(BedAdministrationSchema),
104
+ defaultValues: {
105
+ bedId: initialData.bedNumber || "",
106
+ description: initialData.description || "",
107
+ bedRow: initialData.row.toString() || "0",
108
+ bedColumn: initialData.column.toString() || "0",
109
+ location: initialData.location || {},
110
+ occupancyStatus: capitalize(initialData.status) || occupancyStatus,
111
+ bedType: initialData.bedType.name || "",
112
+ },
113
+ });
114
+
115
+ const onSubmit = (formData: BedAdministrationData) => {
116
+ const result = BedAdministrationSchema.safeParse(formData);
117
+ if (result.success) {
118
+ setShowErrorNotification(false);
119
+ handleCreateQuestion(formData);
120
+ }
121
+ };
122
+
123
+ const onError = (error: { [key: string]: ErrorType }) => {
124
+ setFormStateError(Object.entries(error)[0][1].message);
125
+ setShowErrorNotification(true);
126
+ };
127
+
128
+ return (
129
+ <ComposedModal
130
+ open={showModal}
131
+ onClose={() => onModalChange(false)}
132
+ preventCloseOnClickOutside
133
+ >
134
+ <ModalHeader title={headerTitle} />
135
+ <Form onSubmit={handleSubmit(onSubmit, onError)}>
136
+ <ModalBody hasScrollingContent>
137
+ <Stack gap={3}>
138
+ <FormGroup legendText={""}>
139
+ <Controller
140
+ name="bedId"
141
+ control={control}
142
+ render={({ field, fieldState }) => (
143
+ <>
144
+ <TextInput
145
+ id="bedId"
146
+ labelText={t("bedId", "Bed number")}
147
+ placeholder={t("bedIdPlaceholder", "e.g. BMW-201")}
148
+ invalidText={fieldState.error?.message}
149
+ {...field}
150
+ />
151
+ </>
152
+ )}
153
+ />
154
+ </FormGroup>
155
+
156
+ <FormGroup>
157
+ <Controller
158
+ name="description"
159
+ control={control}
160
+ render={({ field, fieldState }) => (
161
+ <>
162
+ <TextArea
163
+ rows={2}
164
+ id="description"
165
+ invalidText={fieldState?.error?.message}
166
+ labelText={t("description", "Bed description")}
167
+ {...field}
168
+ placeholder={t(
169
+ "description",
170
+ "Enter the bed description"
171
+ )}
172
+ />
173
+ </>
174
+ )}
175
+ />
176
+ </FormGroup>
177
+
178
+ <FormGroup>
179
+ <Controller
180
+ name="bedRow"
181
+ control={control}
182
+ render={({ fieldState, field }) => (
183
+ <NumberInput
184
+ hideSteppers
185
+ id="bedRow"
186
+ label="Bed row"
187
+ labelText="Bed row"
188
+ invalidText={fieldState?.error?.message}
189
+ {...field}
190
+ />
191
+ )}
192
+ />
193
+ </FormGroup>
194
+
195
+ <FormGroup>
196
+ <Controller
197
+ name="bedColumn"
198
+ control={control}
199
+ render={({ field, fieldState }) => (
200
+ <NumberInput
201
+ hideSteppers
202
+ id="bedColumn"
203
+ label="Bed column"
204
+ labelText="Bed column"
205
+ invalidText={fieldState.error?.message}
206
+ {...field}
207
+ />
208
+ )}
209
+ />
210
+ </FormGroup>
211
+
212
+ <FormGroup>
213
+ <Controller
214
+ name="location"
215
+ control={control}
216
+ render={({
217
+ fieldState,
218
+ field: { onChange, onBlur, value, ref },
219
+ }) => (
220
+ <ComboBox
221
+ aria-label={t("location", "Location")}
222
+ shouldFilterItem={filterLocationNames}
223
+ id="location"
224
+ label={t("location", "Location")}
225
+ invalidText={fieldState?.error?.message}
226
+ items={allLocations}
227
+ onBlur={onBlur}
228
+ ref={ref}
229
+ selectedItem={value}
230
+ onChange={({ selectedItem }) => onChange(selectedItem)}
231
+ itemToString={(location) => location?.display ?? ""}
232
+ placeholder={t(
233
+ "selectBedLocation",
234
+ "Select a bed location"
235
+ )}
236
+ titleText={t("bedLocation", "Location")}
237
+ />
238
+ )}
239
+ />
240
+ </FormGroup>
241
+
242
+ <FormGroup>
243
+ <Controller
244
+ name="occupancyStatus"
245
+ control={control}
246
+ render={({ field, fieldState }) => (
247
+ <Select
248
+ id="occupancyStatus"
249
+ labelText={t("occupancyStatus", "Occupied Status")}
250
+ invalidText={fieldState.error?.message}
251
+ defaultValue={occupancyStatus}
252
+ onChange={(event) => setOccupancyStatus(event.target.value)}
253
+ value={occupancyStatus}
254
+ {...field}
255
+ >
256
+ <SelectItem
257
+ text={t("chooseOccupiedStatus", "Choose occupied status")}
258
+ value=""
259
+ />
260
+ {occupancyStatuses.map((occupancyStatus, index) => (
261
+ <SelectItem
262
+ text={t("occupancyStatus", `${occupancyStatus}`)}
263
+ value={t("occupancyStatus", `${occupancyStatus}`)}
264
+ key={`occupancyStatus-${index}`}
265
+ />
266
+ ))}
267
+ </Select>
268
+ )}
269
+ />
270
+ </FormGroup>
271
+
272
+ <FormGroup>
273
+ <Controller
274
+ name="bedType"
275
+ control={control}
276
+ render={({ field }) => (
277
+ <Select
278
+ id="bedType"
279
+ labelText={t("bedType", "Bed type")}
280
+ invalidText={t("required", "Required")}
281
+ defaultValue={selectedBedType}
282
+ {...field}
283
+ >
284
+ <SelectItem
285
+ text={t("chooseBedtype", "Choose a bed type")}
286
+ />
287
+ {availableBedTypes.map((bedType, index) => (
288
+ <SelectItem
289
+ text={bedType.name}
290
+ value={bedType.name}
291
+ key={`bedType-${index}`}
292
+ >
293
+ {bedType.name}
294
+ </SelectItem>
295
+ ))}
296
+ </Select>
297
+ )}
298
+ />
299
+ </FormGroup>
300
+ {showErrorNotification && (
301
+ <InlineNotification
302
+ lowContrast
303
+ title={t("error", "Error")}
304
+ style={{ minWidth: "100%", margin: "0rem", padding: "0rem" }}
305
+ role="alert"
306
+ kind="error"
307
+ subtitle={t("pleaseFillField", formStateError) + "."}
308
+ onClose={() => setShowErrorNotification(false)}
309
+ />
310
+ )}
311
+ </Stack>
312
+ </ModalBody>
313
+ <ModalFooter>
314
+ <Button onClick={() => onModalChange(false)} kind="secondary">
315
+ {t("cancel", "Cancel")}
316
+ </Button>
317
+ <Button disabled={!isDirty} type="submit">
318
+ <span>{t("save", "Save")}</span>
319
+ </Button>
320
+ </ModalFooter>
321
+ </Form>
322
+ </ComposedModal>
323
+ );
324
+ };
325
+
326
+ export default BedAdministrationForm;