diff --git a/package.json b/package.json index bd09650..cd6c394 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@emotion/jest": "^11.2.1", "@emotion/react": "^11.1.5", "@emotion/styled": "^10.0.27", - "@orfium/ictinus": "^2.63.0", + "@orfium/ictinus": "^2.70.2", "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^13.1.3", @@ -30,6 +30,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-final-form": "^6.5.3", + "react-final-form-listeners": "^1.0.3", "react-query": "^3.16.0", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", diff --git a/server.js b/server.js index 666a56a..2722dfa 100644 --- a/server.js +++ b/server.js @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -const express = require('express'); const path = require('path'); -const serverPort = process.env.PORT || 3000; + +const express = require('express'); +const serverPort = process.env.PORT || 3001; const environment = process.env.NODE_ENV || 'development'; // construct a mini server diff --git a/src/App.tsx b/src/App.tsx index 23250d2..05caf4c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,12 +4,13 @@ import { ThemeProvider } from '@orfium/ictinus'; import { QueryClient, QueryClientProvider } from 'react-query'; import { BrowserRouter as Router } from 'react-router-dom'; - import { AppWrapper } from './App.style'; import Routes from './routing/Routes'; import theme from 'theme/globals'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { queries: { refetchOnWindowFocus: false, refetchOnReconnect: false } }, +}); const App: React.FC = () => { return ( diff --git a/src/api/patientsAPI/index.ts b/src/api/patientsAPI/index.ts new file mode 100644 index 0000000..6d4d2a8 --- /dev/null +++ b/src/api/patientsAPI/index.ts @@ -0,0 +1,5 @@ +import patientsAPI from './patientsAPI'; + +export default { + single: patientsAPI, +}; diff --git a/src/api/patientsAPI/patientsAPI.tsx b/src/api/patientsAPI/patientsAPI.tsx new file mode 100644 index 0000000..fe027e8 --- /dev/null +++ b/src/api/patientsAPI/patientsAPI.tsx @@ -0,0 +1,9 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { RegisterPatientPayload } from '../../models/apiTypes'; +import { METHODS, request } from '../axiosInstances'; + +export default { + getHospitals: () => request(METHODS.GET, '/hospitals/', {}), + registerPatient: (params: RegisterPatientPayload) => + request(METHODS.POST, '/patients/', { params }), +}; diff --git a/src/hooks/api/patientHooks.ts b/src/hooks/api/patientHooks.ts new file mode 100644 index 0000000..72ed0ba --- /dev/null +++ b/src/hooks/api/patientHooks.ts @@ -0,0 +1,57 @@ +import { AxiosError } from 'axios'; +import { ReactQueryKeys } from 'hooks/constants'; +import { useMutation, useQuery } from 'react-query'; +import { useHistory } from 'react-router-dom'; + +import patientsAPI from '../../api/patientsAPI'; +import { HospitalsResponse, RegisterPatientPayload } from '../../models/apiTypes'; +import { RegisterPatientFormType } from '../../pages/RegisterPatient/types'; +import urls from '../../routing/urls'; + +export const useGetHospitals = () => { + return useQuery( + ReactQueryKeys.HospitalsQuery, + async () => { + const { request } = patientsAPI.single.getHospitals(); + const data = await request(); + + return data; + }, + { + onError: (errors) => { + console.log(errors); + }, + + retry: false, + } + ); +}; + +export const useRegisterPatient = () => { + const history = useHistory(); + + return useMutation( + (params) => { + const { request } = patientsAPI.single.registerPatient({ + full_name: params.name, + address: params.address, + age: params.age, + year_of_birth: params.yearOfBirth, + phone_1: params.phone1, + phone_2: params.phone2, + hospital_id: params.center.value, + national_id: params.nationalId, + gender: params.gender, + }); + return request(); + }, + { + onSuccess: () => { + history.replace(urls.patients()); + }, + onError: (errors) => { + console.log(errors); + }, + } + ); +}; diff --git a/src/hooks/constants.ts b/src/hooks/constants.ts new file mode 100644 index 0000000..34cab88 --- /dev/null +++ b/src/hooks/constants.ts @@ -0,0 +1,4 @@ +export const ReactQueryKeys = { + PatientsQuery: 'patientsQuery', + HospitalsQuery: 'hospitalsQuery', +}; diff --git a/src/models/apiTypes.tsx b/src/models/apiTypes.tsx index c35a57e..4d43830 100644 --- a/src/models/apiTypes.tsx +++ b/src/models/apiTypes.tsx @@ -1,3 +1,19 @@ +export interface PaginationParams { + offset?: number; // Number of results to return per page + limit?: number; // The initial index from which to return the results +} + +export interface PaginationResponse { + count: number; + next: string; + previous: string; +} + +export type SelectOption = { + label: string; + value: number; +}; + export interface LoginFormType { email: string; password: string; @@ -9,3 +25,26 @@ export interface LoginResponse { password: string; token?: string; } + +export type HospitalsAPI = { + id: number; + name: string; + address: string; + patient_hospital_id: number; +}; + +export interface HospitalsResponse extends PaginationResponse, PaginationParams { + results: HospitalsAPI[]; +} + +export interface RegisterPatientPayload { + full_name: string; + national_id: string; + age: number; + year_of_birth: number; + gender: string; + phone_1: string; + phone_2: string; + address: string; + hospital_id: number; +} diff --git a/src/pages/Layout/Layout.style.ts b/src/pages/Layout/Layout.style.ts index d5d2dcb..76043da 100644 --- a/src/pages/Layout/Layout.style.ts +++ b/src/pages/Layout/Layout.style.ts @@ -1,7 +1,6 @@ import styled from '@emotion/styled'; import { flexCenter } from '@orfium/ictinus/dist/theme/functions'; - import { scrollBar } from '../../common.style'; import { ResponsiveProps } from '../types'; import { flex } from 'theme/functions'; @@ -14,7 +13,6 @@ const getWidth = ({ isLargeDesktop, isXLargeDesktop, }: ResponsiveProps) => { - console.log(isSmallDesktop, isMediumDesktop, isLargeDesktop, isXLargeDesktop); return isSmallDesktop || isMediumDesktop || isLargeDesktop || isXLargeDesktop ? SIDEBAR_WIDTH_COLLAPSED : 0; @@ -30,7 +28,7 @@ export const Main = styled.main` ${scrollBar}; overflow-x: hidden; - overflow-y: auto; + overflow-y: hidden; ${flex} `; diff --git a/src/pages/Login/components/LoginForm/LoginForm.tsx b/src/pages/Login/components/LoginForm/LoginForm.tsx index bf4bae3..59d6f80 100644 --- a/src/pages/Login/components/LoginForm/LoginForm.tsx +++ b/src/pages/Login/components/LoginForm/LoginForm.tsx @@ -84,7 +84,7 @@ const SignIn: React.FC = () => { - diff --git a/src/pages/RegisterPatient/RegisterPatient.tsx b/src/pages/RegisterPatient/RegisterPatient.tsx index 8c624e5..05c4a10 100644 --- a/src/pages/RegisterPatient/RegisterPatient.tsx +++ b/src/pages/RegisterPatient/RegisterPatient.tsx @@ -4,36 +4,54 @@ import React from 'react'; import { Button } from '@orfium/ictinus'; import { Form } from 'react-final-form'; -import { LoginFormType } from '../../models/apiTypes'; +import { useGetHospitals, useRegisterPatient } from '../../hooks/api/patientHooks'; import { ButtonContainer } from '../Login/components/LoginForm/LoginForm.style'; import RegisterPatientForm from './components/RegisterPatientForm'; import { FormHeading, ButtonsContainer } from './RegisterPatient.style'; +import { RegisterPatientFormType } from './types'; const RegisterPatient = () => { - const handleSubmit = (form: LoginFormType) => { - // mutate(form); - console.log(form); + const { data: hospitals } = useGetHospitals(); + const { mutate, isLoading } = useRegisterPatient(); + + const handleSubmit = (form: RegisterPatientFormType) => { + mutate(form); }; return ( <> Fill in Patient Details
- {({ handleSubmit }) => ( + {({ handleSubmit, values, form }) => ( - + - - diff --git a/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.style.tsx b/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.style.tsx index 16eb1ee..ee127cc 100644 --- a/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.style.tsx +++ b/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.style.tsx @@ -16,7 +16,7 @@ export const FormHeadingContainer = styled.div` export const FormContainer = styled.div` flex-grow: 1; - margin-bottom: 120px; + height: 100%; overflow-y: auto; padding: 18px; diff --git a/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.tsx b/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.tsx index b65e5e1..686a651 100644 --- a/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.tsx +++ b/src/pages/RegisterPatient/components/RegisterPatientForm/RegisterPatientForm.tsx @@ -4,6 +4,7 @@ import React, { ChangeEvent } from 'react'; import { Radio, RadioGroup, Select, TextField } from '@orfium/ictinus'; import { omit } from 'lodash'; import { Field } from 'react-final-form'; +import { OnBlur } from 'react-final-form-listeners'; import { FieldsContainer, @@ -11,13 +12,21 @@ import { LongFieldWrapper, RadioText, } from '../../../../common.style'; +import { HospitalsAPI } from '../../../../models/apiTypes'; +import { RegisterPatientFormType } from '../../types'; +import { getHospitalOptions } from '../../utils'; import { FormContainer, FormHeadingContainer, FormSectionHeading, } from './RegisterPatientForm.style'; -const RegisterPatientForm = () => { +type Props = { + values: RegisterPatientFormType; + hospitals: HospitalsAPI[]; +}; + +const RegisterPatientForm: React.FC = ({ values, hospitals }) => { return ( @@ -33,7 +42,7 @@ const RegisterPatientForm = () => { size="md" required hintMsg={props.meta.error} - options={[{ label: 'Hospital Number 1', value: 1 }]} + options={getHospitalOptions(hospitals)} {...omit(props.input, ['onFocus'])} handleSelectedOption={props.input.onChange} /> @@ -53,7 +62,8 @@ const RegisterPatientForm = () => { return ( { return ( { + + {({ input: { onChange } }) => ( + + {() => { + const age = new Date().getFullYear() - values.yearOfBirth; + onChange(age < 150 ? age : ''); + }} + + )} + value}> {(props) => { return ( @@ -123,7 +146,15 @@ const RegisterPatientForm = () => { value}> {(props) => { return ( - + ); }} @@ -132,21 +163,27 @@ const RegisterPatientForm = () => { Gender - ) => { - console.log(e.target.value); + + {({ input: { onChange } }) => { + return ( + ) => { + onChange(e.target.value); + }} + > +
+ + Male +
+
+ + Female +
+
+ ); }} - > -
- - Male -
-
- - Female -
-
+
@@ -159,6 +196,7 @@ const RegisterPatientForm = () => { return ( { return ( { + return hospitals.map((hospital) => ({ + label: hospital.name, + value: hospital.id, + })); +}; diff --git a/yarn.lock b/yarn.lock index f8a107f..b551958 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2515,10 +2515,10 @@ dependencies: mkdirp "^1.0.4" -"@orfium/ictinus@^2.63.0": - version "2.63.0" - resolved "https://registry.yarnpkg.com/@orfium/ictinus/-/ictinus-2.63.0.tgz#e6b424b4c1db68370fccfa72eb6ce16bd8b6d70a" - integrity sha512-ZpqXUCpGZpK2Qr1p1RymTR74N2nUtrAHuFL1iyodzr/Yw4mimqdnWfeUwLZDa1uaVKAhiW3hr7kF5q6MT/fw3A== +"@orfium/ictinus@^2.70.2": + version "2.70.2" + resolved "https://registry.yarnpkg.com/@orfium/ictinus/-/ictinus-2.70.2.tgz#1da99d286fb49f0828daa829d730228650248928" + integrity sha512-JTKYgst4TWLNLBnidPLtjDZgDFeHVxUxR9pipsiczx//B9LUbqwkwNOHffOEtrdAcnqQKa2u48GOT7kAQlCXwg== dependencies: "@emotion/core" "^10.0.35" dayjs "^1.8.34" @@ -11092,6 +11092,13 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== +react-final-form-listeners@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/react-final-form-listeners/-/react-final-form-listeners-1.0.3.tgz#88e0bd6af06edc9a74b81ddad2ce0ded53f3331e" + integrity sha512-OrdCNxSS4JQS/EXD+R530kZKFqaPfa+WcXPgVro/h4BpaBDF/Ja+BtHyCzDezCIb5rWaGGdOJIj+tN2YdtvrXg== + dependencies: + "@babel/runtime" "^7.12.5" + react-final-form@^6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-6.5.3.tgz#b60955837fe9d777456ae9d9c48e3e2f21547d29"