diff --git a/pwa/package-lock.json b/pwa/package-lock.json
index caea114..9c88623 100644
--- a/pwa/package-lock.json
+++ b/pwa/package-lock.json
@@ -1678,6 +1678,11 @@
"@types/node": "*"
}
},
+ "@types/history": {
+ "version": "4.7.5",
+ "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz",
+ "integrity": "sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw=="
+ },
"@types/istanbul-lib-coverage": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
@@ -1755,6 +1760,25 @@
"@types/react": "*"
}
},
+ "@types/react-router": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.5.tgz",
+ "integrity": "sha512-RZPdCtZympi6X7EkGyaU7ISiAujDYTWgqMF9owE3P6efITw27IWQykcti0BvA5h4Mu1LLl5rxrpO3r8kHyUZ/Q==",
+ "requires": {
+ "@types/history": "*",
+ "@types/react": "*"
+ }
+ },
+ "@types/react-router-dom": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.4.tgz",
+ "integrity": "sha512-LO0z5qqSfWdYtCNsRm8/OMnnkv52hwADJKrAfpKIyfHclORllcgAMGypEA7ajHm38+jOonKLx8nPygNAAZGxtg==",
+ "requires": {
+ "@types/history": "*",
+ "@types/react": "*",
+ "@types/react-router": "*"
+ }
+ },
"@types/react-test-renderer": {
"version": "16.9.2",
"resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-16.9.2.tgz",
@@ -6636,6 +6660,11 @@
"resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
"integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE="
},
+ "gud": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
+ "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
+ },
"gzip-size": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@@ -6771,6 +6800,19 @@
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
},
+ "history": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
+ "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "loose-envify": "^1.2.0",
+ "resolve-pathname": "^3.0.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0",
+ "value-equal": "^1.0.1"
+ }
+ },
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -6781,6 +6823,14 @@
"minimalistic-crypto-utils": "^1.0.1"
}
},
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
"hosted-git-info": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
@@ -9586,6 +9636,16 @@
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.0.tgz",
"integrity": "sha1-z8RcN+nsDY8KDsPdTvf3w6vjklY="
},
+ "mini-create-react-context": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
+ "integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
+ "requires": {
+ "@babel/runtime": "^7.4.0",
+ "gud": "^1.0.0",
+ "tiny-warning": "^1.0.2"
+ }
+ },
"mini-css-extract-plugin": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
@@ -12269,6 +12329,52 @@
"react-tween-state": "^0.1.5"
}
},
+ "react-router": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz",
+ "integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "history": "^4.9.0",
+ "hoist-non-react-statics": "^3.1.0",
+ "loose-envify": "^1.3.1",
+ "mini-create-react-context": "^0.3.0",
+ "path-to-regexp": "^1.7.0",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.6.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "path-to-regexp": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
+ "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ }
+ }
+ },
+ "react-router-dom": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz",
+ "integrity": "sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "history": "^4.9.0",
+ "loose-envify": "^1.3.1",
+ "prop-types": "^15.6.2",
+ "react-router": "5.1.2",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0"
+ }
+ },
"react-scripts": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-3.3.1.tgz",
@@ -12980,6 +13086,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
},
+ "resolve-pathname": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
+ "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
+ },
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -14891,6 +15002,16 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
+ "tiny-invariant": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
+ "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
+ },
+ "tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
"tinycolor2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
@@ -15364,6 +15485,11 @@
"spdx-expression-parse": "^3.0.0"
}
},
+ "value-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
+ "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
+ },
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
diff --git a/pwa/package.json b/pwa/package.json
index 2e874b8..9abda9f 100644
--- a/pwa/package.json
+++ b/pwa/package.json
@@ -11,10 +11,12 @@
"@types/node": "^12.12.26",
"@types/react": "^16.9.19",
"@types/react-dom": "^16.9.5",
+ "@types/react-router-dom": "^5.1.4",
"antd-mobile": "^2.3.1",
"node-sass": "^4.13.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
+ "react-router-dom": "^5.1.2",
"react-scripts": "3.3.1",
"typescript": "^3.7.5"
},
diff --git a/pwa/src/App.tsx b/pwa/src/App.tsx
index 4006c0f..016b820 100644
--- a/pwa/src/App.tsx
+++ b/pwa/src/App.tsx
@@ -1,14 +1,38 @@
import React from "react";
+import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
+
+import Login from "./components/Login";
+import LandingPage from "./components/LandingPage";
+import PatientInfoForm from "./components/PatientInfoForm";
+import Wrapper from "./Wrapper";
+
import "./scss/App.scss";
import "./antd-mobile.css";
-import Login from "./components/Login";
-
const App = () => {
return (
- <>
-
- >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/pwa/src/Wrapper.test.tsx b/pwa/src/Wrapper.test.tsx
new file mode 100644
index 0000000..00848e8
--- /dev/null
+++ b/pwa/src/Wrapper.test.tsx
@@ -0,0 +1,74 @@
+import React from "react";
+import { render, queryByAttribute, fireEvent } from "@testing-library/react";
+import { useHistory } from "react-router-dom";
+import Wrapper from "./Wrapper";
+
+const mockHistoryPush = jest.fn();
+jest.mock("react-router-dom", () => ({
+ useHistory: () => ({
+ push: mockHistoryPush,
+ }),
+}));
+
+test("renders it's inner contents", () => {
+ const dom = render(
+
+ Hello!
+
+ );
+
+ const textContents = dom.getByText("Hello!");
+
+ expect(textContents).toBeInTheDocument();
+});
+
+test("renders navBar when given navTitle", () => {
+ const dom = render(
+
+ Hello!
+
+ );
+
+ const getById = queryByAttribute.bind(null, "id");
+ const renderedNavBar = getById(dom.container, "nav-bar");
+ const navTitle = dom.getByText("CHI");
+
+ expect(renderedNavBar).toBeTruthy();
+ expect(navTitle).toBeInTheDocument();
+});
+
+test("renders no navBar when not given props", () => {
+ const dom = render(
+
+ Hello!
+
+ );
+ const getById = queryByAttribute.bind(null, "id");
+ const renderedNavBar = getById(dom.container, "nav-bar");
+ expect(renderedNavBar).toBeFalsy();
+});
+
+test("renders leftArrowText", () => {
+ const dom = render(
+
+ Hello!
+
+ );
+
+ const leftArrowText = dom.getByText("back");
+
+ expect(leftArrowText).toBeInTheDocument();
+});
+
+test("redirects on left arrow click ", () => {
+ const dom = render(
+
+ Hello!
+
+ );
+
+ const leftArrowText = dom.getByText("back");
+ fireEvent.click(leftArrowText);
+
+ expect(useHistory().push).toBeCalledWith("/");
+});
diff --git a/pwa/src/Wrapper.tsx b/pwa/src/Wrapper.tsx
new file mode 100644
index 0000000..4a76bc0
--- /dev/null
+++ b/pwa/src/Wrapper.tsx
@@ -0,0 +1,42 @@
+import React from "react";
+import { NavBar, Icon } from "antd-mobile";
+import { useHistory } from "react-router-dom";
+
+import "./scss/Wrapper.scss";
+
+type props = {
+ navTitle?: string;
+ leftArrowText?: string;
+ leftArrowRoute?: string;
+ children: React.ReactNode;
+};
+
+const Wrapper = (props: props) => {
+ const { navTitle, leftArrowText, leftArrowRoute } = props;
+ const hist = useHistory();
+ const needNavBar = navTitle || leftArrowText || leftArrowRoute;
+ const leftContentArr = leftArrowText
+ ? [, `${leftArrowText}`]
+ : [];
+ const leftRoute = leftArrowRoute ? `${leftArrowRoute}` : "/";
+
+ return (
+
+
+ {needNavBar && (
+ hist.push(leftRoute)}
+ leftContent={leftContentArr}
+ >
+ {`${navTitle}`}
+
+ )}
+ {props.children}
+
+
+ );
+};
+
+export default Wrapper;
diff --git a/pwa/src/components/LandingPage.tsx b/pwa/src/components/LandingPage.tsx
index 3a787c4..b8a0df4 100644
--- a/pwa/src/components/LandingPage.tsx
+++ b/pwa/src/components/LandingPage.tsx
@@ -1,11 +1,12 @@
import React, { ReactElement } from "react";
import { WhiteSpace, Button } from "antd-mobile";
-import Icon, { UserAddOutlined } from "@ant-design/icons";
+import { UserAddOutlined } from "@ant-design/icons";
import "../scss/LandingPage.scss";
export default function LandingPage(): ReactElement {
/* istanbul ignore next */
+ // TODO: these onClick functions could be mocked in a unit test
const patientLookup = () => {
console.log("Clicked Patient Lookup");
};
diff --git a/pwa/src/components/Login.tsx b/pwa/src/components/Login.tsx
index 5766b06..d181260 100644
--- a/pwa/src/components/Login.tsx
+++ b/pwa/src/components/Login.tsx
@@ -1,31 +1,29 @@
-import React, { ReactElement } from 'react'
-import useApi from '../hooks/useApi'
-import {InputItem, Button} from 'antd-mobile'
-import '../scss/Login.scss'
+import React, { ReactElement } from "react";
+import useApi from "../hooks/useApi";
+import { InputItem, Button } from "antd-mobile";
+import "../scss/Login.scss";
export default function Login(): ReactElement {
- const {response, error, isLoading} = useApi("")
+ const { response, isLoading } = useApi("");
return (
<>
-
-
-

-
-
-
-
-
Loading API Fetch: {isLoading.toString()}
- {response &&
-
API Fetch Result: {JSON.stringify(response)}
- }
+ {response &&
API Fetch Result: {JSON.stringify(response)}
}
>
- )
+ );
}
diff --git a/pwa/src/components/PatientInfoForm.test.tsx b/pwa/src/components/PatientInfoForm.test.tsx
new file mode 100644
index 0000000..b1009fd
--- /dev/null
+++ b/pwa/src/components/PatientInfoForm.test.tsx
@@ -0,0 +1,105 @@
+import React from "react";
+import { render, fireEvent, prettyDOM } from "@testing-library/react";
+import PatientInfoForm from "./PatientInfoForm";
+
+test("female-only div is only displayed if the sex is selected as female", async () => {
+ const femaleOnlyFormFields = ["Pregnant?", "LMP", "Wants Planning?"];
+
+ const dom = render(
);
+
+ // ensure the female-only div is not displayed
+ femaleOnlyFormFields.forEach((fieldText) => {
+ expect(dom.queryByText(fieldText)).toBeNull();
+ });
+
+ // click the radio button for 'female'
+ const femaleButton = dom.getByTestId("female-button");
+ fireEvent.click(femaleButton);
+
+ femaleOnlyFormFields.forEach((fieldText) => {
+ expect(dom.queryByText(fieldText)).toBeTruthy();
+ });
+});
+
+test("sets visit input", () => {
+ const dom = render(
);
+ const visitYesInput = dom.queryByTestId("visit-Yes");
+ const checkBox = visitYesInput.getElementsByTagName("span")[0];
+
+ expect(checkBox).not.toHaveClass("am-radio-checked");
+ fireEvent.click(visitYesInput);
+ expect(checkBox).toHaveClass("am-radio-checked");
+});
+
+test("sets pregnant input", () => {
+ const dom = render(
);
+ const femaleButton = dom.getByTestId("female-button");
+ fireEvent.click(femaleButton);
+
+ const pregnantYesInput = dom.queryByTestId("pregnant-Yes");
+ const checkBox = pregnantYesInput.getElementsByTagName("span")[0];
+
+ expect(checkBox).not.toHaveClass("am-radio-checked");
+ fireEvent.click(pregnantYesInput);
+ expect(checkBox).toHaveClass("am-radio-checked");
+});
+
+test("sets planning input", () => {
+ const dom = render(
);
+ const femaleButton = dom.getByTestId("female-button");
+ fireEvent.click(femaleButton);
+
+ const planningYesInput = dom.queryByTestId("planning-Yes");
+ const checkBox = planningYesInput.getElementsByTagName("span")[0];
+
+ expect(checkBox).not.toHaveClass("am-radio-checked");
+ fireEvent.click(planningYesInput);
+ expect(checkBox).toHaveClass("am-radio-checked");
+});
+
+test("sets albendazole input", () => {
+ const dom = render(
);
+ const femaleButton = dom.getByTestId("female-button");
+
+ const albendazoleYesInput = dom.queryByTestId("albendazole-Yes");
+ const checkBox = albendazoleYesInput.getElementsByTagName("span")[0];
+
+ expect(checkBox).not.toHaveClass("am-radio-checked");
+ fireEvent.click(albendazoleYesInput);
+ expect(checkBox).toHaveClass("am-radio-checked");
+});
+
+test("renders the Patient Info Form fields", () => {
+ const formFields = [
+ "First Name",
+ "First Name",
+ "Last Name",
+ "Last Name",
+ "Sex",
+ "Birth",
+ "Age",
+ "Phone",
+ "Town",
+ "Visit",
+ "Nickname",
+ "BP",
+ "Temp",
+ "Pulse",
+ "Weight",
+ "Height",
+ "Z-Score",
+ "Albendazole",
+ "Alergies",
+ "Medicines",
+ "Chief Complaint",
+ "History",
+ "Exam",
+ ];
+
+ const { getByText } = render(
);
+
+ formFields.forEach((fieldText) => {
+ let re = new RegExp(fieldText, "gi");
+ expect(getByText(re)).toBeInTheDocument();
+ });
+});
diff --git a/pwa/src/components/PatientInfoForm.tsx b/pwa/src/components/PatientInfoForm.tsx
new file mode 100644
index 0000000..980d8e8
--- /dev/null
+++ b/pwa/src/components/PatientInfoForm.tsx
@@ -0,0 +1,264 @@
+import React, { ReactElement, useState } from "react";
+import {
+ DatePicker,
+ InputItem,
+ List,
+ TextareaItem,
+ Button,
+ Radio,
+ WhiteSpace,
+} from "antd-mobile";
+import enUs from "antd-mobile/lib/date-picker/locale/en_US";
+import "../scss/PatientInfoForm.scss";
+
+const RadioItem = Radio.RadioItem;
+
+export default function Form(): ReactElement {
+ // State Hooks for Radio Buttons
+ // useState returns a value, and a function to set that value
+ const [sex, setSex] = useState(null as Number); // Default: null
+ const [albendazole, setAlbendazole] = useState(0); // Default: No
+ const [visit, setVisit] = useState(0); // Default: No
+ const [pregnant, setPregnant] = useState(0); // Default: No
+ const [planning, setPlanning] = useState(0); // Default: No
+
+ // Data for Radio Buttons
+ const sexValues = [
+ { value: 0, label: "Male" },
+ { value: 1, label: "Female" },
+ ];
+
+ const radioYesNo = [
+ { value: 0, label: "No" },
+ { value: 1, label: "Yes" },
+ ];
+
+ /* istanbul ignore next */
+ const onSubmit = () => {
+ // This shows how to access the values of the form
+ const firstNameField: HTMLInputElement = document.getElementById(
+ "firstname"
+ ) as HTMLInputElement;
+ const lastNameField: HTMLInputElement = document.getElementById(
+ "lastname"
+ ) as HTMLInputElement;
+ const nicknameField: HTMLInputElement = document.getElementById(
+ "nickname"
+ ) as HTMLInputElement;
+ const birthdateField: HTMLInputElement = document.getElementById(
+ "birthdate"
+ ) as HTMLInputElement;
+ const complaintField1: HTMLInputElement = document.getElementById(
+ "complaint1"
+ ) as HTMLInputElement;
+ const complaintField2: HTMLInputElement = document.getElementById(
+ "complaint2"
+ ) as HTMLInputElement;
+ const complaintField3: HTMLInputElement = document.getElementById(
+ "complaint3"
+ ) as HTMLInputElement;
+
+ console.log("First name:", firstNameField.value);
+ console.log("Last name:", lastNameField.value);
+ console.log("Nickname:", nicknameField.value);
+ console.log("Birth Date:", birthdateField.value);
+ console.log(
+ "Complaints: ",
+ complaintField1.value +
+ " " +
+ complaintField2.value +
+ " " +
+ complaintField3.value
+ );
+ console.log("Wants Planning?: " + planning);
+
+ };
+
+ return (
+
+
+
+
+
+
+
+
"Sex"}>
+ {sexValues.map((i) => (
+ setSex(i.value)}
+ >
+ {i.label}
+
+ ))}
+
+
+
+
+
+ Birth Date
+
+
+
+ Age{" "}
+
+
+
+ Phone
+
+
+
+
+
"Visit?"}>
+ {radioYesNo.map((i) => (
+ setVisit(i.value)}
+ >
+ {i.label}
+
+ ))}
+
+
+ {/* Only display the 'female-only' div if the patient is female */}
+ {sex ? (
+
+
+ "Pregnant?"}>
+ {radioYesNo.map((i) => (
+ setPregnant(i.value)}
+ >
+ {i.label}
+
+ ))}
+
+
+
+ G
+
+
+
+ P
+
+
+
+ A
+
+
+
+
+ "Wants Planning?"}>
+ {radioYesNo.map((i) => (
+ setPlanning(i.value)}
+ >
+ {i.label}
+
+ ))}
+
+
+ ) : null}
+
+
+
+
+ Weight (kg)
+
+
+
+ Height (cm)
+
+
+
+ BP
+
+
+
+ Temp (F)
+
+
+
+ Pulse
+
+
+
+ Z-Score
+
+
+
"Albendazole?"}>
+ {radioYesNo.map((i) => (
+ setAlbendazole(i.value)}
+ >
+ {i.label}
+
+ ))}
+
+
+
"Alergies"}>
+
+
+
+
"Medicines"}>
+
+
+
+
"Chief Complaint"}>
+
+
+
+
+
+
"History"}>
+
+
+
+
"Exam"}>
+
+
+
+
+
+
+
+ );
+}
diff --git a/pwa/src/hooks/useApi.test.tsx b/pwa/src/hooks/useApi.test.tsx
index 42286bf..aafd6d8 100644
--- a/pwa/src/hooks/useApi.test.tsx
+++ b/pwa/src/hooks/useApi.test.tsx
@@ -1,16 +1,28 @@
-import { renderHook } from '@testing-library/react-hooks'
-import useApi from './useApi'
+import { renderHook } from "@testing-library/react-hooks";
+import useApi from "./useApi";
-test('should return loading and then a response', async () => {
- const { result, waitForNextUpdate } = renderHook(() => useApi(""))
-
- expect(result.current.response).toBeNull()
- expect(result.current.error).toBeNull()
- expect(result.current.isLoading).toBeTruthy()
+test("should return loading and then a response", async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useApi(""));
- await waitForNextUpdate()
+ expect(result.current.response).toBeNull();
+ expect(result.current.error).toBeNull();
+ expect(result.current.isLoading).toBeTruthy();
- expect(result.current.response).toBeTruthy()
- expect(result.current.error).toBeNull()
- expect(result.current.isLoading).toBeFalsy()
-})
\ No newline at end of file
+ await waitForNextUpdate();
+
+ expect(result.current.response).toBeTruthy();
+ expect(result.current.error).toBeNull();
+ expect(result.current.isLoading).toBeFalsy();
+});
+
+test("should return an error on a non existent route", async () => {
+ const { result, waitForNextUpdate } = renderHook(() => useApi("/404"));
+
+ expect(result.current.response).toBeNull();
+ expect(result.current.error).toBeNull();
+
+ await waitForNextUpdate();
+
+ expect(result.current.response).toBeFalsy();
+ expect(result.current.error).toBeTruthy();
+});
diff --git a/pwa/src/hooks/useApi.tsx b/pwa/src/hooks/useApi.tsx
index 50eb0cd..a292d8b 100644
--- a/pwa/src/hooks/useApi.tsx
+++ b/pwa/src/hooks/useApi.tsx
@@ -1,19 +1,18 @@
-import React from 'react'
+import React from "react";
const useApi = (route: string) => {
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
-
+
/* istanbul ignore next */
const getUrl = () => {
- if (process.env.environment === "docker"){
- return "http://api:4000/"
+ if (process.env.environment === "docker") {
+ return "http://api:4000/";
+ } else {
+ return "http://localhost:4000/";
}
- else {
- return "http://localhost:4000/"
- }
- }
+ };
React.useEffect(() => {
const fetchData = async () => {
@@ -22,14 +21,15 @@ const useApi = (route: string) => {
const res = await fetch(getUrl() + route);
const json = await res.json();
setResponse(json);
- setIsLoading(false)
+ setIsLoading(false);
} catch (error) {
setError(error);
}
};
fetchData();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return { response, error, isLoading };
};
-export default useApi
\ No newline at end of file
+export default useApi;
diff --git a/pwa/src/scss/LandingPage.scss b/pwa/src/scss/LandingPage.scss
index 905257e..73ed522 100644
--- a/pwa/src/scss/LandingPage.scss
+++ b/pwa/src/scss/LandingPage.scss
@@ -1,6 +1,4 @@
.button-flex {
- animation-name: fade-in;
- animation-duration: 2s;
display: flex;
justify-content: center;
margin: 2em 0;
diff --git a/pwa/src/scss/Login.scss b/pwa/src/scss/Login.scss
index ea78e88..f2dd399 100644
--- a/pwa/src/scss/Login.scss
+++ b/pwa/src/scss/Login.scss
@@ -21,7 +21,7 @@
#imgWrap {
width: 28%;
margin: auto;
- padding: .2rem;
+ padding: 0.2rem;
background-color: #eee;
border-radius: 5px;
img {
@@ -37,13 +37,13 @@
animation-name: fade-in;
animation-duration: 2s;
}
- #buttonFlex{
+ #buttonFlex {
animation-name: fade-in;
animation-duration: 2s;
display: flex;
justify-content: center;
.am-button {
- background-color: #9BD7FF;
+ background-color: #9bd7ff;
width: 40%;
&.am-button-active {
background-color: #ccebff;
@@ -51,21 +51,29 @@
}
}
}
-}
+ $animationDelay: 1;
+ @for $i from 1 through 15 {
+ .loginGrid :nth-child(#{$i}) {
+ animation-delay: #{0.3+ ($i)/30}s;
+ }
+ }
-$animationDelay: 1;
-@for $i from 1 through 15 {
- .loginGrid :nth-child(#{$i}) {
- animation-delay: #{0.3+($i)/30}s;
+ @keyframes fade-in {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+ }
+
+ @keyframes fade-in-from-up {
+ 0% {
+ opacity: 0;
+ transform: translateY(-100px);
+ }
+ 100% {
+ opacity: 1;
+ }
}
}
-
-@keyframes fade-in {
- 0% { opacity: 0; }
- 100% { opacity: 1; }
-}
-
-@keyframes fade-in-from-up {
- 0% { opacity: 0; transform: translateY(-100px)}
- 100% { opacity: 1; }
-}
\ No newline at end of file
diff --git a/pwa/src/scss/PatientInfoForm.scss b/pwa/src/scss/PatientInfoForm.scss
new file mode 100644
index 0000000..6451bb9
--- /dev/null
+++ b/pwa/src/scss/PatientInfoForm.scss
@@ -0,0 +1,8 @@
+#patient-info-wrapper {
+ display: flex;
+ flex-direction: column;
+
+ height: 100%;
+ width: 100%;
+ }
+
\ No newline at end of file
diff --git a/pwa/src/scss/Wrapper.scss b/pwa/src/scss/Wrapper.scss
new file mode 100644
index 0000000..ed3f753
--- /dev/null
+++ b/pwa/src/scss/Wrapper.scss
@@ -0,0 +1,11 @@
+#primary-wrapper {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: space-around;
+ #wrapper-contents {
+ box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
+ max-width: 50em;
+ max-height: 94.6vh;
+ }
+}