From 8ce62f4417bbe20b718de3436f5d6e67b733f62d Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 14 Feb 2020 16:09:00 +0100 Subject: [PATCH 01/27] patients are charged using fhir.js --- src/components/patients/patientTable/index.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index 74f61ec..e79007c 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -23,6 +23,12 @@ const PatientTable = () => { ); + const calculateAge = (birthday: any) => { + // birthday is a date + var ageDifMs = Date.now() - birthday; + var ageDate = new Date(ageDifMs); // miliseconds from epoch + return Math.abs(ageDate.getUTCFullYear() - 1970); + }; React.useEffect(() => { const fetchPatients = async () => { @@ -45,6 +51,13 @@ const PatientTable = () => { enableRowResizing={false} numRows={patients.length} > + + renderPatientAttribute("id", index) + } + /> Date: Fri, 14 Feb 2020 17:40:18 +0100 Subject: [PATCH 02/27] isolating API call --- .../patients/patientTable/index.tsx | 6 -- src/services/ApiCalls.tsx | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/services/ApiCalls.tsx diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index e79007c..6f0a3b3 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -23,12 +23,6 @@ const PatientTable = () => { ); - const calculateAge = (birthday: any) => { - // birthday is a date - var ageDifMs = Date.now() - birthday; - var ageDate = new Date(ageDifMs); // miliseconds from epoch - return Math.abs(ageDate.getUTCFullYear() - 1970); - }; React.useEffect(() => { const fetchPatients = async () => { diff --git a/src/services/ApiCalls.tsx b/src/services/ApiCalls.tsx new file mode 100644 index 0000000..e531e78 --- /dev/null +++ b/src/services/ApiCalls.tsx @@ -0,0 +1,56 @@ +import React from "react"; + +const calculateAge = (birthday: any) => { + // birthday is a date + var ageDifMs = Date.now() - birthday; + var ageDate = new Date(ageDifMs); // miliseconds from epoch + return Math.abs(ageDate.getUTCFullYear() - 1970); +}; +interface Patient { + firstName: String; + lastName: String; + age: number; + id: String; +} + +export const CallApi = () => { + let mkFhir = require("fhir.js"); + let client = mkFhir({ + baseUrl: "http://hapi.fhir.org/baseR4/" + }); + + let patientPromise = client + .search({ type: "Patient", query: { birthdate: "1995" } }) + .then(function(res: any) { + let pats = res.data.entry.map((x: any) => { + let patient = { + age: 0, + id: x.resource.id, + firstName: undefined, + lastName: undefined + }; + if (x.resource.name) { + if (x.resource.name[0].given) + patient.firstName = x.resource.name[0].given.join(", "); + if (x.resource.name[0].family) + patient.lastName = x.resource.name[0].family; + } + if (x.resource.birthDate) + patient.age = calculateAge(new Date(x.resource.birthDate)); + return patient; + }); + return pats; + }) + .catch(function(res: any) { + //Error responses + if (res.status) { + console.log("Error", res.status); + } + + //Errors + if (res.message) { + console.log("Error", res.message); + } + }); + return patientPromise; +}; From ae6c931d772bf66a530e1a3cba2cb9d7388cd768 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Wed, 19 Feb 2020 10:00:09 +0100 Subject: [PATCH 03/27] creating type file to isolate interfaces, improving api.tsx --- src/services/ApiCalls.tsx | 56 --------------------------------------- src/services/api.tsx | 1 - 2 files changed, 57 deletions(-) delete mode 100644 src/services/ApiCalls.tsx diff --git a/src/services/ApiCalls.tsx b/src/services/ApiCalls.tsx deleted file mode 100644 index e531e78..0000000 --- a/src/services/ApiCalls.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from "react"; - -const calculateAge = (birthday: any) => { - // birthday is a date - var ageDifMs = Date.now() - birthday; - var ageDate = new Date(ageDifMs); // miliseconds from epoch - return Math.abs(ageDate.getUTCFullYear() - 1970); -}; -interface Patient { - firstName: String; - lastName: String; - age: number; - id: String; -} - -export const CallApi = () => { - let mkFhir = require("fhir.js"); - let client = mkFhir({ - baseUrl: "http://hapi.fhir.org/baseR4/" - }); - - let patientPromise = client - .search({ type: "Patient", query: { birthdate: "1995" } }) - .then(function(res: any) { - let pats = res.data.entry.map((x: any) => { - let patient = { - age: 0, - id: x.resource.id, - firstName: undefined, - lastName: undefined - }; - if (x.resource.name) { - if (x.resource.name[0].given) - patient.firstName = x.resource.name[0].given.join(", "); - if (x.resource.name[0].family) - patient.lastName = x.resource.name[0].family; - } - if (x.resource.birthDate) - patient.age = calculateAge(new Date(x.resource.birthDate)); - return patient; - }); - return pats; - }) - .catch(function(res: any) { - //Error responses - if (res.status) { - console.log("Error", res.status); - } - - //Errors - if (res.message) { - console.log("Error", res.message); - } - }); - return patientPromise; -}; diff --git a/src/services/api.tsx b/src/services/api.tsx index f04a776..e136276 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -2,7 +2,6 @@ import { URL_SERVER } from "../constants"; import { Patient } from "types"; import newFhirClient from "fhir.js"; -//const mkFhir = require("fhir.js"); const client = newFhirClient({ baseUrl: URL_SERVER }); From b837f1d81a87f8c709b0ea4d21527de03085ef2e Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Wed, 19 Feb 2020 11:40:28 +0100 Subject: [PATCH 04/27] patient card id, name and age is loaded from the server --- src/components/patient/patientCard/index.tsx | 15 +++-- src/services/api.tsx | 69 +++++++++++++++++++- 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/src/components/patient/patientCard/index.tsx b/src/components/patient/patientCard/index.tsx index 5e3215c..ba2f450 100644 --- a/src/components/patient/patientCard/index.tsx +++ b/src/components/patient/patientCard/index.tsx @@ -2,8 +2,9 @@ import React from "react"; import { Callout, Icon, H3, H5 } from "@blueprintjs/core"; import PatientInfo from "components/patient/patientCard/patientInfo"; -import { DATA_TEST } from "../../../constants"; import { Patient } from "types"; +import { getPatientData } from "services/api"; + import "./style.css"; interface Props { @@ -11,11 +12,15 @@ interface Props { } const PatientCard = ({ patientId }: Props) => { - const [patientsData, setPatientsData] = React.useState([] as Patient[]); + const [patientData, setPatientData] = React.useState({} as Patient); React.useEffect(() => { - setPatientsData(DATA_TEST); - }, []); + const fetchPatientData = async () => { + const patient: Patient = await getPatientData(patientId); + setPatientData(patient); + }; + fetchPatientData(); + }, [patientId]); /* Function getPatientCard @@ -25,8 +30,6 @@ const PatientCard = ({ patientId }: Props) => { Return page content with patient data. */ const getPatientCard = (patientId: any) => { - const patientData = patientsData.find(p => p.id === patientId); - if (!patientData) { return ( <> diff --git a/src/services/api.tsx b/src/services/api.tsx index e136276..8633228 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -19,15 +19,45 @@ const getAge = (birthDate: Date) => { return age; }; +const callApi = ( + resourceType: string, + queryParameters: object, + patientId?: string +) => { + let searchObject = {}; + if (patientId) { + searchObject = { + type: resourceType, + patient: patientId, + query: queryParameters + }; + } else { + searchObject = { + type: resourceType, + query: queryParameters + }; + } + const patient = client.search(searchObject); + return patient; +}; + +const getCount = async (resource: string, queryParameters: object) => { + // getCount function returns the number of resources of a type"; + // _summary: "count" must be added to queryParameters + const fetchCount = async () => { + const resultData = await callApi(resource, queryParameters); + return resultData.data.total; + }; + + return fetchCount(); +}; + export const getPatients = async () => { const response = await client.search({ type: "Patient", query: { _count: 100, _page: 1 } }); - // The research bundle (res.data) shoud have a res.data.total attribute to get the total number of results. - // see https://www.hl7.org/fhir/bundle.html - return response.data.entry.map( ({ resource: { id, birthDate, name } }: any) => { const patient: Patient = { @@ -42,3 +72,36 @@ export const getPatients = async () => { } ); }; + +export const getPatientData = (patientId: string) => { + const getPatientData = async () => { + var count = await getCount("Patient", { _summary: "count" }); + console.log("count : ", count); + + const resultData = await callApi("Patient", {}, patientId); + // The research bundle (res.data) shoud have a res.data.total attribute to get the total number of results. + // see https://www.hl7.org/fhir/bundle.html + + console.log(resultData); + const patientData = resultData.data.entry[0]; + + const patient: Patient = { + id: patientData.resource.id, + firstName: undefined, + lastName: undefined, + age: patientData.resource.birthDate + ? getAge(new Date(patientData.resource.birthDate)) + : NaN + }; + if (patientData.resource.name) { + if (patientData.resource.name[0].given) + patient.firstName = patientData.resource.name[0].given.join(", "); + if (patientData.resource.name[0].family) + patient.lastName = patientData.resource.name[0].family; + } + console.log("observations : ", resultData); + return patient; + }; + + return getPatientData(); +}; From 77ef15952dad899d9e7b634411b788bd2f7cb832 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Thu, 20 Feb 2020 11:22:42 +0100 Subject: [PATCH 05/27] rebase with connectToFHIRAPI/PatientTable branch --- src/services/api.tsx | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/services/api.tsx b/src/services/api.tsx index 8633228..426d91b 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -20,6 +20,7 @@ const getAge = (birthDate: Date) => { }; const callApi = ( + // To be deleted resourceType: string, queryParameters: object, patientId?: string @@ -44,12 +45,13 @@ const callApi = ( const getCount = async (resource: string, queryParameters: object) => { // getCount function returns the number of resources of a type"; // _summary: "count" must be added to queryParameters - const fetchCount = async () => { - const resultData = await callApi(resource, queryParameters); - return resultData.data.total; - }; - return fetchCount(); + const response = await client.search({ + type: resource, + query: queryParameters + }); + + return response.data.total; }; export const getPatients = async () => { @@ -78,12 +80,15 @@ export const getPatientData = (patientId: string) => { var count = await getCount("Patient", { _summary: "count" }); console.log("count : ", count); - const resultData = await callApi("Patient", {}, patientId); + const response = await client.search({ + type: "Patient", + patient: patientId, + query: {} + }); + // The research bundle (res.data) shoud have a res.data.total attribute to get the total number of results. // see https://www.hl7.org/fhir/bundle.html - - console.log(resultData); - const patientData = resultData.data.entry[0]; + const patientData = response.data.entry[0]; const patient: Patient = { id: patientData.resource.id, @@ -99,7 +104,7 @@ export const getPatientData = (patientId: string) => { if (patientData.resource.name[0].family) patient.lastName = patientData.resource.name[0].family; } - console.log("observations : ", resultData); + console.log("observations : ", response); return patient; }; From 16342333fa8e53780a9413a297fad744ad377d09 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 21 Feb 2020 11:13:43 +0100 Subject: [PATCH 06/27] renaming patient to patientPage, centralising Patient interface in types, calling the API to get the observation, allergies and condition number for a patient --- .../header/headerPatientName/index.tsx | 10 +- src/components/header/index.tsx | 15 +-- src/components/patient/index.tsx | 31 ------ src/components/patient/patientCard/index.tsx | 84 -------------- .../hospitSummary/index.tsx | 0 .../hospitSummary/style.css | 0 src/components/patientPage/index.tsx | 44 ++++++++ .../patientPage/patientCard/index.tsx | 103 ++++++++++++++++++ .../patientCard/patientInfo/index.tsx | 0 .../patientCard/patientInfo/style.css | 0 .../patientCard/style.css | 0 .../{patient => patientPage}/style.css | 0 .../timelinePatient/index.tsx | 0 .../timelinePatient/style.css | 0 .../patients/patientTable/index.tsx | 16 ++- .../patients/patientTable/style.css | 8 +- src/routes.tsx | 4 +- src/services/api.tsx | 85 +++++++++------ src/types.tsx | 4 + 19 files changed, 235 insertions(+), 169 deletions(-) delete mode 100644 src/components/patient/index.tsx delete mode 100644 src/components/patient/patientCard/index.tsx rename src/components/{patient => patientPage}/hospitSummary/index.tsx (100%) rename src/components/{patient => patientPage}/hospitSummary/style.css (100%) create mode 100644 src/components/patientPage/index.tsx create mode 100644 src/components/patientPage/patientCard/index.tsx rename src/components/{patient => patientPage}/patientCard/patientInfo/index.tsx (100%) rename src/components/{patient => patientPage}/patientCard/patientInfo/style.css (100%) rename src/components/{patient => patientPage}/patientCard/style.css (100%) rename src/components/{patient => patientPage}/style.css (100%) rename src/components/{patient => patientPage}/timelinePatient/index.tsx (100%) rename src/components/{patient => patientPage}/timelinePatient/style.css (100%) diff --git a/src/components/header/headerPatientName/index.tsx b/src/components/header/headerPatientName/index.tsx index 0434133..4723086 100644 --- a/src/components/header/headerPatientName/index.tsx +++ b/src/components/header/headerPatientName/index.tsx @@ -1,16 +1,20 @@ import * as React from "react"; import { Icon, Navbar as BPNavbar } from "@blueprintjs/core"; +import { Patient } from "types"; interface Props { - patientName: string; + patient: Patient; } -const HeaderPatientName = ({ patientName }: Props) => { +const HeaderPatientName = ({ patient }: Props) => { return ( <> {/* TODO : charge name dynamically */} -
{patientName}
+
+ {patient.lastName && patient.lastName + " "} + {patient.firstName && patient.firstName} +
); }; diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx index f868c64..78d11e1 100644 --- a/src/components/header/index.tsx +++ b/src/components/header/index.tsx @@ -2,17 +2,16 @@ import { Alignment, Button, Icon, Navbar as BPNavbar } from "@blueprintjs/core"; import * as React from "react"; import { Link } from "react-router-dom"; import HeaderPatientName from "components/header/headerPatientName"; -import { DATA_TEST, ROUTE_HOME } from "../../constants"; +import { ROUTE_HOME } from "../../constants"; +import { Patient } from "types"; import "./style.css"; interface Props { - patientId?: string; + patient?: Patient; } -const Header = ({ patientId }: Props) => { - const patient = DATA_TEST.find(p => p.id === patientId); - +const Header = ({ patient }: Props) => { return ( @@ -26,11 +25,7 @@ const Header = ({ patientId }: Props) => {

TIMELINE

- {patient && ( - - )} + {patient && }
diff --git a/src/components/patient/index.tsx b/src/components/patient/index.tsx deleted file mode 100644 index 1bfb848..0000000 --- a/src/components/patient/index.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from "react"; -import Header from "components/header"; -import PatientCard from "components/patient/patientCard"; -import HospitSummary from "components/patient/hospitSummary"; -import TimelinePatient from "components/patient/timelinePatient"; -import "./style.css"; - -interface Props { - patientId: string; -} - -const Patient = ({ patientId }: Props) => { - return ( - <> -
-
-
-
- -
-
- -
-
- -
- - ); -}; - -export default Patient; diff --git a/src/components/patient/patientCard/index.tsx b/src/components/patient/patientCard/index.tsx deleted file mode 100644 index ba2f450..0000000 --- a/src/components/patient/patientCard/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from "react"; - -import { Callout, Icon, H3, H5 } from "@blueprintjs/core"; -import PatientInfo from "components/patient/patientCard/patientInfo"; -import { Patient } from "types"; -import { getPatientData } from "services/api"; - -import "./style.css"; - -interface Props { - patientId: string; -} - -const PatientCard = ({ patientId }: Props) => { - const [patientData, setPatientData] = React.useState({} as Patient); - - React.useEffect(() => { - const fetchPatientData = async () => { - const patient: Patient = await getPatientData(patientId); - setPatientData(patient); - }; - fetchPatientData(); - }, [patientId]); - - /* - Function getPatientCard - Get the PatientData object corresponding to patientId and generate jsx. - TODO: adapt this function to get data from the rest API - - Return page content with patient data. - */ - const getPatientCard = (patientId: any) => { - if (!patientData) { - return ( - <> - - - ); - } else { - // case : rendering, patient found - return ( - <> -
- {patientData.lastName && ( -
- {patientData.lastName.toUpperCase()} -
- )} - {patientData.firstName && ( - {patientData.firstName} - )} -
- - - {patientData.age && ( - - )} - {patientData.medicalHistory && ( - - )} - {patientData.allergies && ( - - )} - - ); - } - }; - - return ( - <> -
-

- Informations générales -

- {getPatientCard(patientId)} -
- - ); -}; - -export default PatientCard; diff --git a/src/components/patient/hospitSummary/index.tsx b/src/components/patientPage/hospitSummary/index.tsx similarity index 100% rename from src/components/patient/hospitSummary/index.tsx rename to src/components/patientPage/hospitSummary/index.tsx diff --git a/src/components/patient/hospitSummary/style.css b/src/components/patientPage/hospitSummary/style.css similarity index 100% rename from src/components/patient/hospitSummary/style.css rename to src/components/patientPage/hospitSummary/style.css diff --git a/src/components/patientPage/index.tsx b/src/components/patientPage/index.tsx new file mode 100644 index 0000000..8b032c0 --- /dev/null +++ b/src/components/patientPage/index.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import Header from "components/header"; +import PatientCard from "components/patientPage/patientCard"; +import HospitSummary from "components/patientPage/hospitSummary"; +import TimelinePatient from "components/patientPage/timelinePatient"; +import { Patient } from "types"; +import { getPatientData } from "services/api"; + +import "./style.css"; + +interface Props { + patientId: string; +} + +const PatientPage = ({ patientId }: Props) => { + const [patientData, setPatientData] = React.useState({} as Patient); + + React.useEffect(() => { + const fetchPatientData = async () => { + const patient: Patient = await getPatientData(patientId); + setPatientData(patient); + }; + fetchPatientData(); + }, [patientId]); + + return ( + <> +
+
+
+
+ +
+
+ +
+
+ +
+ + ); +}; + +export default PatientPage; diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx new file mode 100644 index 0000000..ecabc0e --- /dev/null +++ b/src/components/patientPage/patientCard/index.tsx @@ -0,0 +1,103 @@ +import React from "react"; + +import { Callout, Icon, H3, H5 } from "@blueprintjs/core"; +import PatientInfo from "components/patientPage/patientCard/patientInfo"; +import { Patient } from "types"; + +import "./style.css"; + +interface Props { + patient: Patient; +} + +const PatientCard = ({ patient }: Props) => { + /* + Function getPatientCard + Get the PatientData object corresponding to patientId and generate jsx. + TODO: adapt this function to get data from the rest API + + Return page content with patient data. + */ + + const getAllergiesCard = () => { + if (patient.allergiesNumber !== undefined) + return ( + + ); + }; + + const getObservationsCard = () => { + if (patient.observationsNumber !== undefined) + return ( + + ); + }; + + const getConditionsCard = () => { + if (patient.conditionsNumber !== undefined) + return ( + + ); + }; + + const getPatientCard = () => { + if (!patient) { + return ( + <> + + + ); + } else { + // case : rendering, patient found + return ( + <> +
+ {patient.lastName && ( +
{patient.lastName.toUpperCase()}
+ )} + {patient.firstName && ( + {patient.firstName} + )} +
+ + {patient.age && ( + + )} + {patient.medicalHistory && ( + + )} + {patient.allergies && ( + + )} + {getAllergiesCard()} + + {getObservationsCard()} + + {getConditionsCard()} + + ); + } + }; + + return ( + <> +
+

+ Informations générales +

+ {getPatientCard()} +
+ + ); +}; + +export default PatientCard; diff --git a/src/components/patient/patientCard/patientInfo/index.tsx b/src/components/patientPage/patientCard/patientInfo/index.tsx similarity index 100% rename from src/components/patient/patientCard/patientInfo/index.tsx rename to src/components/patientPage/patientCard/patientInfo/index.tsx diff --git a/src/components/patient/patientCard/patientInfo/style.css b/src/components/patientPage/patientCard/patientInfo/style.css similarity index 100% rename from src/components/patient/patientCard/patientInfo/style.css rename to src/components/patientPage/patientCard/patientInfo/style.css diff --git a/src/components/patient/patientCard/style.css b/src/components/patientPage/patientCard/style.css similarity index 100% rename from src/components/patient/patientCard/style.css rename to src/components/patientPage/patientCard/style.css diff --git a/src/components/patient/style.css b/src/components/patientPage/style.css similarity index 100% rename from src/components/patient/style.css rename to src/components/patientPage/style.css diff --git a/src/components/patient/timelinePatient/index.tsx b/src/components/patientPage/timelinePatient/index.tsx similarity index 100% rename from src/components/patient/timelinePatient/index.tsx rename to src/components/patientPage/timelinePatient/index.tsx diff --git a/src/components/patient/timelinePatient/style.css b/src/components/patientPage/timelinePatient/style.css similarity index 100% rename from src/components/patient/timelinePatient/style.css rename to src/components/patientPage/timelinePatient/style.css diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index 6f0a3b3..a08022a 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -3,16 +3,17 @@ import { Link } from "react-router-dom"; import { Cell, Column, Table } from "@blueprintjs/table"; import { Icon, H3 } from "@blueprintjs/core"; import { ROUTE_PATIENT } from "../../../constants"; -import { getPatients } from "services/api"; +import { getPatients, getCount } from "services/api"; import { Patient } from "types"; import "./style.css"; const PatientTable = () => { const [patients, setPatients] = React.useState([] as Patient[]); + const [patientCount, setPatientCount] = React.useState(""); const renderPatientAttribute = ( - attribute: "id" | "firstName" | "lastName" | "age", + attribute: "id" | "identifier" | "firstName" | "lastName" | "age", index: number ) => ( @@ -28,6 +29,8 @@ const PatientTable = () => { const fetchPatients = async () => { const patients: Patient[] = await getPatients(); setPatients(patients); + const count = await getCount("Patient", { _summary: "count" }); + setPatientCount(count); }; fetchPatients(); }, []); @@ -47,16 +50,16 @@ const PatientTable = () => { > renderPatientAttribute("id", index) } /> - renderPatientAttribute("id", index) + renderPatientAttribute("identifier", index) } /> { /> +
+ {patientCount && `${patientCount} patients identifiés`} +
); }; diff --git a/src/components/patients/patientTable/style.css b/src/components/patients/patientTable/style.css index 43c7bf7..ee6697a 100644 --- a/src/components/patients/patientTable/style.css +++ b/src/components/patients/patientTable/style.css @@ -1,6 +1,12 @@ .table { - height: 95%; + height: 90%; } + .icon-title { transform: translateY(-20%); } + +.infoPatient { + text-align: right; + margin-top: 20px; +} \ No newline at end of file diff --git a/src/routes.tsx b/src/routes.tsx index 7c5f4b1..e8878c8 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -3,7 +3,7 @@ import { Route } from "react-router"; import { BrowserRouter, Switch } from "react-router-dom"; import Patients from "components/patients"; -import Patient from "components/patient"; +import PatientPage from "components/patientPage"; import { ROUTE_HOME, ROUTE_PATIENT } from "./constants"; @@ -18,7 +18,7 @@ const Routes = () => ( match: { params: { id } } - }: any) => } + }: any) => } /> diff --git a/src/services/api.tsx b/src/services/api.tsx index 426d91b..ee4126b 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -19,30 +19,7 @@ const getAge = (birthDate: Date) => { return age; }; -const callApi = ( - // To be deleted - resourceType: string, - queryParameters: object, - patientId?: string -) => { - let searchObject = {}; - if (patientId) { - searchObject = { - type: resourceType, - patient: patientId, - query: queryParameters - }; - } else { - searchObject = { - type: resourceType, - query: queryParameters - }; - } - const patient = client.search(searchObject); - return patient; -}; - -const getCount = async (resource: string, queryParameters: object) => { +export const getCount = async (resource: string, queryParameters: object) => { // getCount function returns the number of resources of a type"; // _summary: "count" must be added to queryParameters @@ -61,7 +38,7 @@ export const getPatients = async () => { }); return response.data.entry.map( - ({ resource: { id, birthDate, name } }: any) => { + ({ resource: { id, identifier, birthDate, name } }: any) => { const patient: Patient = { id: id, age: birthDate && getAge(new Date(birthDate)) @@ -70,6 +47,14 @@ export const getPatients = async () => { if (name[0].given) patient.firstName = name[0].given.join(", "); if (name[0].family) patient.lastName = name[0].family; } + if (identifier) { + patient.identifier = identifier + .map((e: any) => { + return e.value; + }) + .join(", "); + } + return patient; } ); @@ -77,26 +62,24 @@ export const getPatients = async () => { export const getPatientData = (patientId: string) => { const getPatientData = async () => { - var count = await getCount("Patient", { _summary: "count" }); - console.log("count : ", count); + // var count = await getCount("Patient", { _summary: "count" }); + // console.log("count : ", count); - const response = await client.search({ + let response = await client.search({ type: "Patient", patient: patientId, query: {} }); - // The research bundle (res.data) shoud have a res.data.total attribute to get the total number of results. // see https://www.hl7.org/fhir/bundle.html const patientData = response.data.entry[0]; const patient: Patient = { id: patientData.resource.id, - firstName: undefined, - lastName: undefined, + identifier: patientData.resource.identifier[0], age: patientData.resource.birthDate ? getAge(new Date(patientData.resource.birthDate)) - : NaN + : undefined }; if (patientData.resource.name) { if (patientData.resource.name[0].given) @@ -104,9 +87,45 @@ export const getPatientData = (patientId: string) => { if (patientData.resource.name[0].family) patient.lastName = patientData.resource.name[0].family; } - console.log("observations : ", response); + + if (patientData.resource.identifier) { + patient.identifier = patientData.resource.identifier + .map((e: any) => { + return e.value; + }) + .join(", "); + } + + response = await getAllergies(patientId); + patient.allergiesNumber = response.data.total; + + response = await getObservations(patientId); + patient.observationsNumber = response.data.total; + + response = await getConditions(patientId); + patient.conditionsNumber = response.data.total; + return patient; }; return getPatientData(); }; +export const getObservations = (patientId: string) => { + return client.search({ + type: "Observation", + query: { subject: { $type: "Patient", $id: patientId } } + }); +}; +export const getConditions = (patientId: string) => { + return client.search({ + type: "Condition", + query: { subject: { $type: "Patient", $id: patientId } } + }); +}; +export const getAllergies = (patientId: string) => { + return client.search({ + type: "AllergyIntolerance", + patient: patientId, + query: {} + }); +}; diff --git a/src/types.tsx b/src/types.tsx index 4903992..008771a 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -1,8 +1,12 @@ export interface Patient { id: string; + identifier?: string; firstName?: string; lastName?: string; age?: number; medicalHistory?: string; allergies?: string; + allergiesNumber?: number; + observationsNumber?: number; + conditionsNumber?: number; } From 6867e964e04be6b6b6f928497bb3922478ec898d Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 21 Feb 2020 14:10:56 +0100 Subject: [PATCH 07/27] adding id search on toolsearch and adding number of allergies, observations, conditions and hospitalisations on patient table. Clicking on the number will print them on the console. --- .../patientPage/patientCard/index.tsx | 83 ++++++++++++------- src/constants/index.tsx | 10 +++ src/services/api.tsx | 63 +++++++++----- src/types.tsx | 5 +- 4 files changed, 104 insertions(+), 57 deletions(-) diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index ecabc0e..28c6372 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -3,6 +3,7 @@ import React from "react"; import { Callout, Icon, H3, H5 } from "@blueprintjs/core"; import PatientInfo from "components/patientPage/patientCard/patientInfo"; import { Patient } from "types"; +import { getPatientResources, getSubjectResources } from "services/api"; import "./style.css"; @@ -19,34 +20,46 @@ const PatientCard = ({ patient }: Props) => { Return page content with patient data. */ - const getAllergiesCard = () => { - if (patient.allergiesNumber !== undefined) - return ( - - ); - }; - - const getObservationsCard = () => { - if (patient.observationsNumber !== undefined) - return ( - - ); + const getPatientNumberCard = (resourceName: string, writtenName: string) => { + if (patient.number !== undefined) + if (patient.number[resourceName] !== undefined) + return ( +
{ + const response = await getPatientResources( + resourceName, + patient.id + ); + console.log(writtenName + " : ", response.data.entry); + }} + > + +
+ ); }; - const getConditionsCard = () => { - if (patient.conditionsNumber !== undefined) - return ( - - ); + const getSubjectNumberCard = (resourceName: string, writtenName: string) => { + if (patient.number !== undefined) + if (patient.number[resourceName] !== undefined) + return ( +
{ + const response = await getSubjectResources( + resourceName, + patient.id + ); + console.log(writtenName + " : ", response.data.entry); + }} + > + +
+ ); }; const getPatientCard = () => { @@ -68,9 +81,12 @@ const PatientCard = ({ patient }: Props) => { {patient.firstName} )} - - {patient.age && ( - + {patient.identifier && ( + + )} + + {patient.birthDate && ( + )} {patient.medicalHistory && ( @@ -78,11 +94,14 @@ const PatientCard = ({ patient }: Props) => { {patient.allergies && ( )} - {getAllergiesCard()} - {getObservationsCard()} + {getSubjectNumberCard("AllergyIntolerance", "Allergies")} + + {getPatientNumberCard("Observation", "Observations")} + + {getPatientNumberCard("Condition", "Conditions")} - {getConditionsCard()} + {getSubjectNumberCard("EpisodeOfCare", "Hospitalisations")} ); } diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 236f081..96c70c1 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -15,6 +15,16 @@ const OPERATION_TEXT = ["Commence par", "Contient", "Exact"]; const OPERATION_BOOLEAN = ["Oui", "Non"]; export const SEARCH_FIELDS = [ + { + name: "Logical id", + operations: OPERATION_TEXT, + isInputText: true + }, + { + name: "Identifier", + operations: OPERATION_TEXT, + isInputText: true + }, { name: "Nom", operations: OPERATION_TEXT, diff --git a/src/services/api.tsx b/src/services/api.tsx index ee4126b..b58c1ef 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -34,7 +34,7 @@ export const getCount = async (resource: string, queryParameters: object) => { export const getPatients = async () => { const response = await client.search({ type: "Patient", - query: { _count: 100, _page: 1 } + query: { _count: 30, _page: 1 } }); return response.data.entry.map( @@ -75,12 +75,22 @@ export const getPatientData = (patientId: string) => { const patientData = response.data.entry[0]; const patient: Patient = { - id: patientData.resource.id, - identifier: patientData.resource.identifier[0], - age: patientData.resource.birthDate - ? getAge(new Date(patientData.resource.birthDate)) - : undefined + id: patientData.resource.id }; + + // Completing patient information with available data + if (patientData.resource.identifier) { + patient.identifier = patientData.resource.identifier[0]; + } + + if (patientData.resource.birthDate) { + patient.age = getAge(new Date(patientData.resource.birthDate)); + patient.birthDate = + patientData.resource.birthDate + + " (" + + patient.age.toString() + + " ans)"; + } if (patientData.resource.name) { if (patientData.resource.name[0].given) patient.firstName = patientData.resource.name[0].given.join(", "); @@ -96,35 +106,44 @@ export const getPatientData = (patientId: string) => { .join(", "); } - response = await getAllergies(patientId); - patient.allergiesNumber = response.data.total; + const responseAI = await getPatientResources( + "AllergyIntolerance", + patientId + ); - response = await getObservations(patientId); - patient.observationsNumber = response.data.total; + const responseO = await getSubjectResources("Observation", patientId); + const responseC = await getSubjectResources("Condition", patientId); + const responseEoC = await getPatientResources("EpisodeOfCare", patientId); - response = await getConditions(patientId); - patient.conditionsNumber = response.data.total; + patient.number = { + AllergyIntolerance: responseAI.data.total, + Observation: responseO.data.total, + Condition: responseC.data.total, + EpisodeOfCare: responseEoC.data.total + }; return patient; }; return getPatientData(); }; -export const getObservations = (patientId: string) => { - return client.search({ - type: "Observation", - query: { subject: { $type: "Patient", $id: patientId } } - }); -}; -export const getConditions = (patientId: string) => { + +export const getSubjectResources = ( + resourceType: string, + patientId: string +) => { return client.search({ - type: "Condition", + type: resourceType, query: { subject: { $type: "Patient", $id: patientId } } }); }; -export const getAllergies = (patientId: string) => { + +export const getPatientResources = ( + resourceType: string, + patientId: string +) => { return client.search({ - type: "AllergyIntolerance", + type: resourceType, patient: patientId, query: {} }); diff --git a/src/types.tsx b/src/types.tsx index 008771a..19444a7 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -4,9 +4,8 @@ export interface Patient { firstName?: string; lastName?: string; age?: number; + birthDate?: string; medicalHistory?: string; allergies?: string; - allergiesNumber?: number; - observationsNumber?: number; - conditionsNumber?: number; + number?: any; } From 5abf07ed97615983915346e9802ea5717b85cbec Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 21 Feb 2020 14:31:41 +0100 Subject: [PATCH 08/27] cleaning --- .../patientPage/patientCard/index.tsx | 14 +++-- src/constants/index.tsx | 56 ------------------- src/services/api.tsx | 1 - 3 files changed, 8 insertions(+), 63 deletions(-) diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index 28c6372..ad4205f 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -13,13 +13,8 @@ interface Props { const PatientCard = ({ patient }: Props) => { /* - Function getPatientCard - Get the PatientData object corresponding to patientId and generate jsx. - TODO: adapt this function to get data from the rest API - - Return page content with patient data. + getPatientNumberCard and getSubjectNumberCard are now rendering PatientInfo elements with click option which print the results on the console. */ - const getPatientNumberCard = (resourceName: string, writtenName: string) => { if (patient.number !== undefined) if (patient.number[resourceName] !== undefined) @@ -63,6 +58,13 @@ const PatientCard = ({ patient }: Props) => { }; const getPatientCard = () => { + /* + Function getPatientCard + Get the PatientData object corresponding to patientId and generate jsx. + TODO: adapt this function to get data from the rest API + + Return page content with patient data. + */ if (!patient) { return ( <> diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 96c70c1..2703410 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -66,59 +66,3 @@ export const SEARCH_FIELDS = [ isInputText: false } ]; - -export const DATA_TEST = [ - { - firstName: "Martin", - lastName: "Perrier", - id: "1", - age: 5, - medicalHistory: "Trisomie 21", - allergies: "Non" - }, - { - firstName: "Bernard", - lastName: "Lebrun", - id: "2", - age: 63, - medicalHistory: "Non", - allergies: "Graminées, pénicilline" - }, - { - firstName: "Thomas", - lastName: "Besson", - id: "3", - age: 56, - medicalHistory: "Non", - allergies: "Non" - }, - { - firstName: "Laurent", - lastName: "Perrot", - id: "4", - age: 36, - medicalHistory: "Fracture de la hanche", - allergies: "Non" - }, - { - firstName: "Jean", - lastName: "Dupont", - id: "5", - age: 17, - medicalHistory: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", - allergies: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum, Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum" - }, - { - firstName: "Simon", - lastName: "Langlois", - id: "6", - age: 96, - medicalHistory: "Diabète de type 2, AVC", - allergies: "Non" - } -]; -/* - Todo : test with long names - */ diff --git a/src/services/api.tsx b/src/services/api.tsx index b58c1ef..3037690 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -110,7 +110,6 @@ export const getPatientData = (patientId: string) => { "AllergyIntolerance", patientId ); - const responseO = await getSubjectResources("Observation", patientId); const responseC = await getSubjectResources("Condition", patientId); const responseEoC = await getPatientResources("EpisodeOfCare", patientId); From 664962a8e56b607543834bf289a7673eeb0ee19c Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 21 Feb 2020 17:26:12 +0100 Subject: [PATCH 09/27] correcting bug when Patient is not found --- src/components/patientPage/index.tsx | 2 +- src/components/patientPage/patientCard/index.tsx | 2 +- src/services/api.tsx | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/patientPage/index.tsx b/src/components/patientPage/index.tsx index 8b032c0..6b6dc24 100644 --- a/src/components/patientPage/index.tsx +++ b/src/components/patientPage/index.tsx @@ -17,7 +17,7 @@ const PatientPage = ({ patientId }: Props) => { React.useEffect(() => { const fetchPatientData = async () => { - const patient: Patient = await getPatientData(patientId); + const patient: any = await getPatientData(patientId); setPatientData(patient); }; fetchPatientData(); diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index ad4205f..516dd5b 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -68,7 +68,7 @@ const PatientCard = ({ patient }: Props) => { if (!patient) { return ( <> - + ); } else { diff --git a/src/services/api.tsx b/src/services/api.tsx index 3037690..9f2cb4a 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -72,6 +72,7 @@ export const getPatientData = (patientId: string) => { }); // The research bundle (res.data) shoud have a res.data.total attribute to get the total number of results. // see https://www.hl7.org/fhir/bundle.html + if (!response.data.entry) return; //patient not found const patientData = response.data.entry[0]; const patient: Patient = { From 64f95f9a9a24cf7ad7272558b476dbdc9eb8edd8 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 21 Feb 2020 17:59:17 +0100 Subject: [PATCH 10/27] commenting and cleaning --- .../patientPage/patientCard/index.tsx | 4 ++++ .../patients/patientTable/index.tsx | 2 -- src/services/api.tsx | 20 ++++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index 516dd5b..73bd573 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -79,10 +79,12 @@ const PatientCard = ({ patient }: Props) => { {patient.lastName && (
{patient.lastName.toUpperCase()}
)} + {patient.firstName && ( {patient.firstName} )} + {patient.identifier && ( )} @@ -90,9 +92,11 @@ const PatientCard = ({ patient }: Props) => { {patient.birthDate && ( )} + {patient.medicalHistory && ( )} + {patient.allergies && ( )} diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index a08022a..8e22255 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -44,8 +44,6 @@ const PatientTable = () => { { export const getCount = async (resource: string, queryParameters: object) => { // getCount function returns the number of resources of a type"; - // _summary: "count" must be added to queryParameters - const response = await client.search({ type: resource, query: queryParameters }); - return response.data.total; }; export const getPatients = async () => { const response = await client.search({ type: "Patient", - query: { _count: 30, _page: 1 } + query: { _count: 35 } }); return response.data.entry.map( @@ -61,17 +58,16 @@ export const getPatients = async () => { }; export const getPatientData = (patientId: string) => { + /* + getPatientData requests for data from Patient resourcce of id patientId + return a Patient object. + */ const getPatientData = async () => { - // var count = await getCount("Patient", { _summary: "count" }); - // console.log("count : ", count); - let response = await client.search({ type: "Patient", patient: patientId, query: {} }); - // The research bundle (res.data) shoud have a res.data.total attribute to get the total number of results. - // see https://www.hl7.org/fhir/bundle.html if (!response.data.entry) return; //patient not found const patientData = response.data.entry[0]; @@ -132,6 +128,9 @@ export const getSubjectResources = ( resourceType: string, patientId: string ) => { + /* + Function getSubjectResources returns all resources of type resourceType where attribute subject is a Patient of type patientId + */ return client.search({ type: resourceType, query: { subject: { $type: "Patient", $id: patientId } } @@ -142,6 +141,9 @@ export const getPatientResources = ( resourceType: string, patientId: string ) => { + /* + Function getPatientResources returns all resources of type resourceType where attribute patient has id patientId + */ return client.search({ type: resourceType, patient: patientId, From 5679b5d79b1289cf7e1678dd75841da2f3da4aff Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Mon, 24 Feb 2020 11:44:20 +0100 Subject: [PATCH 11/27] moving api calls from patientTable to patients, first working version of the search tool (search by name with exact and start with) --- src/components/patients/index.tsx | 62 ++++++++++++++++- .../patients/patientTable/index.tsx | 21 ++---- src/components/patients/searchTool/index.tsx | 8 ++- src/constants/index.tsx | 25 +++---- src/services/api.tsx | 67 ++++++++++++------- 5 files changed, 123 insertions(+), 60 deletions(-) diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index 4d98253..6fcb85e 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -2,18 +2,76 @@ import React from "react"; import Header from "components/header"; import PatientTable from "components/patients/patientTable"; import SearchTool from "components/patients/searchTool"; +import { getPatients, getCount } from "services/api"; +import { Patient } from "types"; import "./style.css"; +interface Props { + onSearch: Function; + searchItem: any; +} + const Patients = () => { + const [patients, setPatients] = React.useState([] as Patient[]); + const [patientCount, setPatientCount] = React.useState(""); + + const handleSearch = (searchParams: any) => { + console.log("searching with parameters : ", searchParams); + const params: { [k: string]: any } = { _count: 35 }; + searchParams.map((x: any) => { + switch (x.label) { + case "Nom": //TODO : find a better solution than hard coded values + switch (x.symbol) { + case "Contient": + params.name = { $contains: x.text }; //not working + break; + case "Exact": + params.name = { $exact: x.text }; //work + break; + default: + params.name = x.text; //work + break; + } + return {}; + default: + console.log(x.label + " non reconnu"); + } + return {}; + }); + + console.log("params : ", params); + const fetchPatients = async () => { + const patients: Patient[] = await getPatients(params); + setPatients(patients); + + params._summary = "count"; + const count = await getCount("Patient", params); + setPatientCount(count); + }; + + fetchPatients(); + }; + + React.useEffect(() => { + const fetchPatients = async () => { + const patients: Patient[] = await getPatients(); + setPatients(patients); + + const count = await getCount("Patient", { _summary: "count" }); + setPatientCount(count); + }; + fetchPatients(); + }, []); + return ( <>
- +
- +
diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index 8e22255..fad2fcd 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -3,15 +3,16 @@ import { Link } from "react-router-dom"; import { Cell, Column, Table } from "@blueprintjs/table"; import { Icon, H3 } from "@blueprintjs/core"; import { ROUTE_PATIENT } from "../../../constants"; -import { getPatients, getCount } from "services/api"; import { Patient } from "types"; import "./style.css"; -const PatientTable = () => { - const [patients, setPatients] = React.useState([] as Patient[]); - const [patientCount, setPatientCount] = React.useState(""); +interface Props { + patients: Patient[]; + patientCount: string; +} +const PatientTable = ({ patients, patientCount }: Props) => { const renderPatientAttribute = ( attribute: "id" | "identifier" | "firstName" | "lastName" | "age", index: number @@ -25,16 +26,6 @@ const PatientTable = () => { ); - React.useEffect(() => { - const fetchPatients = async () => { - const patients: Patient[] = await getPatients(); - setPatients(patients); - const count = await getCount("Patient", { _summary: "count" }); - setPatientCount(count); - }; - fetchPatients(); - }, []); - return ( <>

@@ -84,7 +75,7 @@ const PatientTable = () => {

- {patientCount && `${patientCount} patients identifiés`} + {patientCount !== undefined && `${patientCount} patients identifiés`}
); diff --git a/src/components/patients/searchTool/index.tsx b/src/components/patients/searchTool/index.tsx index 1f8c841..124b08b 100644 --- a/src/components/patients/searchTool/index.tsx +++ b/src/components/patients/searchTool/index.tsx @@ -9,7 +9,11 @@ interface searchForm { text: string; } -const SearchTool = () => { +interface Props { + onSearch: Function; +} + +const SearchTool = ({ onSearch }: Props) => { const newSearchForm: searchForm = { label: "", symbol: "", @@ -37,7 +41,7 @@ const SearchTool = () => { const search = () => { //This function will search for patients corresponding to the request and will show the patient list on the patient table. // For now, it only show the search parameters. - console.log(searchForms); + onSearch(searchForms); }; return ( diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 2703410..4f8aae8 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -15,26 +15,11 @@ const OPERATION_TEXT = ["Commence par", "Contient", "Exact"]; const OPERATION_BOOLEAN = ["Oui", "Non"]; export const SEARCH_FIELDS = [ - { - name: "Logical id", - operations: OPERATION_TEXT, - isInputText: true - }, - { - name: "Identifier", - operations: OPERATION_TEXT, - isInputText: true - }, { name: "Nom", operations: OPERATION_TEXT, isInputText: true }, - { - name: "Prénom", - operations: OPERATION_TEXT, - isInputText: true - }, { name: "Age", operations: OPERATION_NUMBER, @@ -64,5 +49,15 @@ export const SEARCH_FIELDS = [ name: "Diabète", operations: OPERATION_BOOLEAN, isInputText: false + }, + { + name: "Logical id", + operations: OPERATION_TEXT, + isInputText: true + }, + { + name: "Identifier", + operations: OPERATION_TEXT, + isInputText: true } ]; diff --git a/src/services/api.tsx b/src/services/api.tsx index b2ea3f3..0620f01 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -28,33 +28,48 @@ export const getCount = async (resource: string, queryParameters: object) => { return response.data.total; }; -export const getPatients = async () => { - const response = await client.search({ - type: "Patient", - query: { _count: 35 } - }); - - return response.data.entry.map( - ({ resource: { id, identifier, birthDate, name } }: any) => { - const patient: Patient = { - id: id, - age: birthDate && getAge(new Date(birthDate)) - }; - if (name) { - if (name[0].given) patient.firstName = name[0].given.join(", "); - if (name[0].family) patient.lastName = name[0].family; - } - if (identifier) { - patient.identifier = identifier - .map((e: any) => { - return e.value; - }) - .join(", "); +export const getPatients = async (param?: {}) => { + /* + getPatients return the lists of patients depending on param. + If param is empty ill return the whole list. + */ + let response; + if (param) { + response = await client.search({ + type: "Patient", + query: param + }); + } else { + response = await client.search({ + type: "Patient", + query: { _count: 35 } + }); + } + + console.log(response); + if (!response.data.entry) return {}; + else + return response.data.entry.map( + ({ resource: { id, identifier, birthDate, name } }: any) => { + const patient: Patient = { + id: id, + age: birthDate && getAge(new Date(birthDate)) + }; + if (name) { + if (name[0].given) patient.firstName = name[0].given.join(", "); + if (name[0].family) patient.lastName = name[0].family; + } + if (identifier) { + patient.identifier = identifier + .map((e: any) => { + return e.value; + }) + .join(", "); + } + + return patient; } - - return patient; - } - ); + ); }; export const getPatientData = (patientId: string) => { From ce1aa48848b4bb13641995cd1918b26e88fe02b1 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Tue, 25 Feb 2020 11:17:12 +0100 Subject: [PATCH 12/27] replacing fhir.js by custom get requests --- .../patientPage/patientCard/index.tsx | 6 +- src/components/patients/index.tsx | 47 +++++++++--- .../patients/patientTable/index.tsx | 5 +- src/services/api.tsx | 72 ++++++++++++------- 4 files changed, 89 insertions(+), 41 deletions(-) diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index 73bd573..7d26118 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -21,11 +21,11 @@ const PatientCard = ({ patient }: Props) => { return (
{ - const response = await getPatientResources( + const response: any = await getPatientResources( resourceName, patient.id ); - console.log(writtenName + " : ", response.data.entry); + console.log(writtenName + " : ", response.entry); }} > { return (
{ - const response = await getSubjectResources( + const response: any = await getSubjectResources( resourceName, patient.id ); diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index 6fcb85e..b395363 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -16,36 +16,61 @@ const Patients = () => { const [patientCount, setPatientCount] = React.useState(""); const handleSearch = (searchParams: any) => { - console.log("searching with parameters : ", searchParams); - const params: { [k: string]: any } = { _count: 35 }; + let params = "_count=35"; searchParams.map((x: any) => { switch (x.label) { case "Nom": //TODO : find a better solution than hard coded values + params += "&name"; switch (x.symbol) { case "Contient": - params.name = { $contains: x.text }; //not working + params += ":contains"; //work break; case "Exact": - params.name = { $exact: x.text }; //work + params += ":exact"; //work break; - default: - params.name = x.text; //work + } + params += "=" + x.text; + return {}; + case "Age": //TODO : find a better solution than hard coded values + params += "&birthdate"; + const correspondingDate: Date = new Date(); + correspondingDate.setFullYear( + correspondingDate.getFullYear() - parseInt(x.text) + ); + + const yyyy = correspondingDate.getFullYear(); + const mm = + (correspondingDate.getMonth() + 1 > 9 ? "" : "0") + + (correspondingDate.getMonth() + 1); + const dd = + (correspondingDate.getDate() > 9 ? "" : "0") + + correspondingDate.getDate(); + + const fhirDateformat = `${yyyy}-${mm}-${dd}`; + switch (x.symbol) { + case "=": + params += ":contains"; + break; + case ">": + params += "=lt"; + break; + case "<": + params += "=gt"; break; } + params += fhirDateformat; return {}; default: - console.log(x.label + " non reconnu"); + console.info(`Paramètre ${x.label} non reconnu`); } return {}; }); - console.log("params : ", params); const fetchPatients = async () => { const patients: Patient[] = await getPatients(params); setPatients(patients); - params._summary = "count"; - const count = await getCount("Patient", params); + const count = await getCount("Patient", params); //todo : adapt after PR merge setPatientCount(count); }; @@ -57,7 +82,7 @@ const Patients = () => { const patients: Patient[] = await getPatients(); setPatients(patients); - const count = await getCount("Patient", { _summary: "count" }); + const count = await getCount("Patient", "_summary=count"); setPatientCount(count); }; fetchPatients(); diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index fad2fcd..b7336e4 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -20,7 +20,10 @@ const PatientTable = ({ patients, patientCount }: Props) => { - {patients[index][attribute] || "unknown"} + {patients[index][attribute] !== undefined + ? patients[index][attribute] + : "Inconnu"} + {/* Have to do !== undefined because if patients[index][attribute] (ex: patient age = 0), it will display "Inconnu" */} diff --git a/src/services/api.tsx b/src/services/api.tsx index 0620f01..14232e8 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -6,6 +6,27 @@ const client = newFhirClient({ baseUrl: URL_SERVER }); +const makeRequest = async (resource: string, parameters: string) => { + return new Promise((resolve, reject) => { + let url = URL_SERVER + resource + "?" + parameters; + console.log("url : ", url); + let xmlhttp = new XMLHttpRequest(); + + xmlhttp.open("GET", url); + xmlhttp.onload = () => { + if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { + let patients = JSON.parse(xmlhttp.response); + resolve(patients); + } + }; + xmlhttp.onerror = function() { + console.log("error"); + reject({ status: xmlhttp.status, statusText: xmlhttp.statusText }); + }; + xmlhttp.send(); + }); +}; + const getAge = (birthDate: Date) => { const today = new Date(); const monthDifference = today.getMonth() - birthDate.getMonth(); @@ -19,37 +40,34 @@ const getAge = (birthDate: Date) => { return age; }; -export const getCount = async (resource: string, queryParameters: object) => { +export const getCount = async (resource: string, queryParameters: string) => { // getCount function returns the number of resources of a type"; - const response = await client.search({ - type: resource, - query: queryParameters - }); - return response.data.total; + + const response: any = await makeRequest(resource, queryParameters); + console.log("in getcount : ", response); + return response.total; }; -export const getPatients = async (param?: {}) => { +export const getPatients = async (param?: string) => { /* getPatients return the lists of patients depending on param. If param is empty ill return the whole list. */ - let response; + + /* + test + */ + let response: any; if (param) { - response = await client.search({ - type: "Patient", - query: param - }); + //To be adapted ! + response = await makeRequest("Patient", param); } else { - response = await client.search({ - type: "Patient", - query: { _count: 35 } - }); + response = await makeRequest("Patient", "_count=30"); } - console.log(response); - if (!response.data.entry) return {}; + if (!response.entry) return {}; else - return response.data.entry.map( + return response.entry.map( ({ resource: { id, identifier, birthDate, name } }: any) => { const patient: Patient = { id: id, @@ -78,13 +96,15 @@ export const getPatientData = (patientId: string) => { return a Patient object. */ const getPatientData = async () => { - let response = await client.search({ - type: "Patient", - patient: patientId, - query: {} - }); - if (!response.data.entry) return; //patient not found - const patientData = response.data.entry[0]; + let response: any = await makeRequest("Patient", "_id=" + patientId); + + // let response = await client.search({ + // type: "Patient", + // patient: patientId, + // query: {} + // }); + if (!response.entry) return; //patient not found + const patientData = response.entry[0]; const patient: Patient = { id: patientData.resource.id From 5f0ed131b39f370c0cdea004bade20712e274eba Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Tue, 3 Mar 2020 14:12:29 +0100 Subject: [PATCH 13/27] =?UTF-8?q?working=20search=20by=20age=20and=20name?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../patientPage/hospitSummary/index.tsx | 29 +++++++++---------- .../patientPage/patientCard/index.tsx | 7 ++--- src/components/patients/index.tsx | 29 ++++++++++++------- .../patients/patientTable/index.tsx | 3 +- src/constants/index.tsx | 6 ++-- 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/components/patientPage/hospitSummary/index.tsx b/src/components/patientPage/hospitSummary/index.tsx index 7a7cd03..6d214e7 100644 --- a/src/components/patientPage/hospitSummary/index.tsx +++ b/src/components/patientPage/hospitSummary/index.tsx @@ -4,23 +4,20 @@ import "./style.css"; const HospitSummary = () => { return ( - <> -
-

- Hospitalisation -

-
-
{"Chirurgie cardiaque".toUpperCase()}
- 15/05/2019 - 19/05/2019 -
- Les informations sur l'hospitalisation sélectionnée seront indiquées - ici. -
-
- Par défaut, cette carte affichera les infos de la dernière - hospitalisation. +
+

+ Hospitalisation +

+
+
{"Chirurgie cardiaque".toUpperCase()}
+ 15/05/2019 - 19/05/2019
- + Les informations sur l'hospitalisation sélectionnée seront indiquées ici. +
+
+ Par défaut, cette carte affichera les infos de la dernière + hospitalisation. +
); }; diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index 7d26118..e58b815 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -80,10 +80,9 @@ const PatientCard = ({ patient }: Props) => {
{patient.lastName.toUpperCase()}
)} - {patient.firstName && ( - {patient.firstName} - )} -
+ {/* {patient.identifier && ( + + )} */} {patient.identifier && ( diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index b395363..1e86a72 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -20,19 +20,19 @@ const Patients = () => { searchParams.map((x: any) => { switch (x.label) { case "Nom": //TODO : find a better solution than hard coded values - params += "&name"; switch (x.symbol) { case "Contient": - params += ":contains"; //work + params += `&name:contains=${x.text}`; break; case "Exact": - params += ":exact"; //work + params += `&name:exact=${x.text}`; + break; + default: + params += `&name=${x.text}`; break; } - params += "=" + x.text; return {}; case "Age": //TODO : find a better solution than hard coded values - params += "&birthdate"; const correspondingDate: Date = new Date(); correspondingDate.setFullYear( correspondingDate.getFullYear() - parseInt(x.text) @@ -46,20 +46,29 @@ const Patients = () => { (correspondingDate.getDate() > 9 ? "" : "0") + correspondingDate.getDate(); - const fhirDateformat = `${yyyy}-${mm}-${dd}`; switch (x.symbol) { case "=": - params += ":contains"; + params += `&birthdate=lt${yyyy}-${mm}-${dd}`; + params += `&birthdate=gt${yyyy - 1}-${mm}-${dd}`; + break; + case "≠": //not working for now + params += `&birthdate=gt${yyyy}-${mm}-${dd},lt${yyyy - + 1}-${mm}-${dd}`; break; case ">": - params += "=lt"; + params += `&birthdate=lt${yyyy}-${mm}-${dd}`; break; case "<": - params += "=gt"; + params += `&birthdate=gt${yyyy}-${mm}-${dd}`; break; } - params += fhirDateformat; return {}; + case "Logical id": + params += `&_id=${x.text}`; + break; + case "Identifier": + params += `&identifier=${x.text}`; + break; default: console.info(`Paramètre ${x.label} non reconnu`); } diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index b7336e4..0b82c4e 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -78,7 +78,8 @@ const PatientTable = ({ patients, patientCount }: Props) => {
- {patientCount !== undefined && `${patientCount} patients identifiés`} + {patientCount !== undefined && + `${patientCount} patient-e-s identifié-e-s`}
); diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 4f8aae8..0815679 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -12,6 +12,8 @@ const OPERATION_NUMBER = [">", "<", "=", "≠"]; const OPERATION_TEXT = ["Commence par", "Contient", "Exact"]; +const OPERATION_ID = ["Exact"]; + const OPERATION_BOOLEAN = ["Oui", "Non"]; export const SEARCH_FIELDS = [ @@ -52,12 +54,12 @@ export const SEARCH_FIELDS = [ }, { name: "Logical id", - operations: OPERATION_TEXT, + operations: OPERATION_ID, isInputText: true }, { name: "Identifier", - operations: OPERATION_TEXT, + operations: OPERATION_ID, isInputText: true } ]; From e8c81ae62cc7952209bb82f406bf6a0818ca0faf Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Tue, 3 Mar 2020 14:13:35 +0100 Subject: [PATCH 14/27] adding div --- src/components/patientPage/patientCard/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index e58b815..40f77f4 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -79,8 +79,9 @@ const PatientCard = ({ patient }: Props) => { {patient.lastName && (
{patient.lastName.toUpperCase()}
)} +
- {/* {patient.identifier && ( + {/* {patient.identifier && ( )} */} From 93d5a1dac77106a0841def273c76fbcb13bcf8ad Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Mon, 24 Feb 2020 16:15:13 +0100 Subject: [PATCH 15/27] rebase on PatientCard branch --- .../header/headerPatientName/index.tsx | 11 +- src/components/patientPage/index.tsx | 13 +- .../patientPage/patientCard/index.tsx | 158 +++++++++--------- .../patientCard/patientAgeInfo/index.tsx | 35 ++++ .../patientCard/patientAgeInfo/style.css | 25 +++ .../patientCard/patientGeneralInfo/index.tsx | 31 ++++ .../style.css | 6 + .../patientCard/patientInfo/index.tsx | 22 --- .../patientPage/timelinePatient/index.tsx | 5 +- src/components/patients/index.tsx | 9 +- src/services/api.tsx | 132 ++++++--------- src/types.tsx | 18 +- 12 files changed, 264 insertions(+), 201 deletions(-) create mode 100644 src/components/patientPage/patientCard/patientAgeInfo/index.tsx create mode 100644 src/components/patientPage/patientCard/patientAgeInfo/style.css create mode 100644 src/components/patientPage/patientCard/patientGeneralInfo/index.tsx rename src/components/patientPage/patientCard/{patientInfo => patientGeneralInfo}/style.css (84%) delete mode 100644 src/components/patientPage/patientCard/patientInfo/index.tsx diff --git a/src/components/header/headerPatientName/index.tsx b/src/components/header/headerPatientName/index.tsx index 4723086..c22e400 100644 --- a/src/components/header/headerPatientName/index.tsx +++ b/src/components/header/headerPatientName/index.tsx @@ -1,20 +1,23 @@ import * as React from "react"; import { Icon, Navbar as BPNavbar } from "@blueprintjs/core"; + import { Patient } from "types"; + interface Props { patient: Patient; } const HeaderPatientName = ({ patient }: Props) => { + let patientName; + if (!patient.lastName && !patient.firstName) patientName = "Nom inconnu"; + else patientName = `${patient.lastName} ${patient.firstName}`; + return ( <> {/* TODO : charge name dynamically */} -
- {patient.lastName && patient.lastName + " "} - {patient.firstName && patient.firstName} -
+
{patientName}
); }; diff --git a/src/components/patientPage/index.tsx b/src/components/patientPage/index.tsx index 6b6dc24..6a65cc5 100644 --- a/src/components/patientPage/index.tsx +++ b/src/components/patientPage/index.tsx @@ -1,9 +1,12 @@ import React from "react"; +import { Card, Elevation } from "@blueprintjs/core"; + +import { Patient } from "types"; + import Header from "components/header"; import PatientCard from "components/patientPage/patientCard"; import HospitSummary from "components/patientPage/hospitSummary"; import TimelinePatient from "components/patientPage/timelinePatient"; -import { Patient } from "types"; import { getPatientData } from "services/api"; import "./style.css"; @@ -28,12 +31,12 @@ const PatientPage = ({ patientId }: Props) => {
-
+ -
-
+ + -
+
diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index 40f77f4..bd1b7dc 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -1,9 +1,9 @@ import React from "react"; - import { Callout, Icon, H3, H5 } from "@blueprintjs/core"; -import PatientInfo from "components/patientPage/patientCard/patientInfo"; + +import PatientGeneralInfo from "components/patientPage/patientCard/patientGeneralInfo"; +import PatientAgeInfo from "components/patientPage/patientCard/patientAgeInfo"; import { Patient } from "types"; -import { getPatientResources, getSubjectResources } from "services/api"; import "./style.css"; @@ -15,48 +15,61 @@ const PatientCard = ({ patient }: Props) => { /* getPatientNumberCard and getSubjectNumberCard are now rendering PatientInfo elements with click option which print the results on the console. */ - const getPatientNumberCard = (resourceName: string, writtenName: string) => { - if (patient.number !== undefined) - if (patient.number[resourceName] !== undefined) - return ( -
{ - const response: any = await getPatientResources( - resourceName, - patient.id - ); - console.log(writtenName + " : ", response.entry); - }} - > - -
- ); + const getPatientNumberCard = ( + object: "observations" | "conditions", + writtenName: string + ) => { + if (patient[object]) { + return ( +
{ + console.log(writtenName + " : ", patient[object].entry); + }} + > + +
+ ); + } }; - const getSubjectNumberCard = (resourceName: string, writtenName: string) => { - if (patient.number !== undefined) - if (patient.number[resourceName] !== undefined) - return ( -
{ - const response: any = await getSubjectResources( - resourceName, - patient.id - ); - console.log(writtenName + " : ", response.data.entry); - }} - > - -
- ); + const getSubjectNumberCard = ( + object: "allergyIntolerances" | "episodesOfCare", + writtenName: string + ) => { + if (patient[object]) + return ( +
{ + console.log(writtenName + " : ", patient[object].entry); + }} + > + +
+ ); }; + const getSubjectNameDiv = () => { + if (!patient.lastName && !patient.firstName) + return ( +
+
Nom inconnu
+
+ ); + + return ( +
+
{patient.lastName}
+ + {patient.firstName} +
+ ); + }; const getPatientCard = () => { /* Function getPatientCard @@ -66,58 +79,41 @@ const PatientCard = ({ patient }: Props) => { Return page content with patient data. */ if (!patient) { - return ( - <> - - - ); - } else { - // case : rendering, patient found - return ( - <> -
- {patient.lastName && ( -
{patient.lastName.toUpperCase()}
- )} -
- - {/* {patient.identifier && ( - - )} */} - - {patient.identifier && ( - - )} - - {patient.birthDate && ( - - )} + return ; + } + // case : rendering, patient found + return ( + <> + {getSubjectNameDiv()} - {patient.medicalHistory && ( - - )} + {patient.identifier && ( + + )} - {patient.allergies && ( - - )} + { + + } - {getSubjectNumberCard("AllergyIntolerance", "Allergies")} + {getSubjectNumberCard("allergyIntolerances", "Allergies")} - {getPatientNumberCard("Observation", "Observations")} + {getPatientNumberCard("observations", "Observations")} - {getPatientNumberCard("Condition", "Conditions")} + {getPatientNumberCard("conditions", "Conditions")} - {getSubjectNumberCard("EpisodeOfCare", "Hospitalisations")} - - ); - } + {getSubjectNumberCard("episodesOfCare", "Hospitalisations")} + + ); }; return ( <>

- Informations générales + Informations générales

{getPatientCard()}
diff --git a/src/components/patientPage/patientCard/patientAgeInfo/index.tsx b/src/components/patientPage/patientCard/patientAgeInfo/index.tsx new file mode 100644 index 0000000..bc6c077 --- /dev/null +++ b/src/components/patientPage/patientCard/patientAgeInfo/index.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { Tag } from "@blueprintjs/core"; + +import "./style.css"; + +interface Props { + type: string; + birthDate?: string; + age?: number; +} + +const PatientAgeInfo = ({ type, birthDate, age }: Props) => { + if (birthDate) + return ( +
+
+ {type} +
+
+ {birthDate}{" "} +
({age?.toString()} ans)
+
+
+ ); + return ( +
+
+ {type} +
+
Inconnue
+
+ ); +}; + +export default PatientAgeInfo; diff --git a/src/components/patientPage/patientCard/patientAgeInfo/style.css b/src/components/patientPage/patientCard/patientAgeInfo/style.css new file mode 100644 index 0000000..d09170e --- /dev/null +++ b/src/components/patientPage/patientCard/patientAgeInfo/style.css @@ -0,0 +1,25 @@ +.patientInfo { + margin-top: 5px; + display: flex; +} + +.patientTag { + text-align: right; + width: 20%; + margin-right: 5px; +} + +.patientInfoContent { + width: 70%; + text-align: justify; + display: flex; +} + +.secondContent { + color: grey; + margin-left: 5px; +} + +.unknownValue { + color: grey; +} \ No newline at end of file diff --git a/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx b/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx new file mode 100644 index 0000000..ddc6aa8 --- /dev/null +++ b/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { Tag } from "@blueprintjs/core"; + +import "./style.css"; + +interface Props { + type: string; + content?: string; +} + +const PatientGeneralInfo = ({ type, content }: Props) => { + if (content) + return ( +
+
+ {type} +
+
{content}
+
+ ); + return ( +
+
+ {type} +
+
Inconnu
+
+ ); +}; + +export default PatientGeneralInfo; diff --git a/src/components/patientPage/patientCard/patientInfo/style.css b/src/components/patientPage/patientCard/patientGeneralInfo/style.css similarity index 84% rename from src/components/patientPage/patientCard/patientInfo/style.css rename to src/components/patientPage/patientCard/patientGeneralInfo/style.css index 973a28f..c27f6ea 100644 --- a/src/components/patientPage/patientCard/patientInfo/style.css +++ b/src/components/patientPage/patientCard/patientGeneralInfo/style.css @@ -2,12 +2,18 @@ margin-top: 5px; display: flex; } + .patientTag { text-align: right; width: 20%; margin-right: 5px; } + .patientInfoContent { width: 70%; text-align: justify; } + +.unknownValue { + color: grey; +} \ No newline at end of file diff --git a/src/components/patientPage/patientCard/patientInfo/index.tsx b/src/components/patientPage/patientCard/patientInfo/index.tsx deleted file mode 100644 index 8d7b7bc..0000000 --- a/src/components/patientPage/patientCard/patientInfo/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from "react"; - -import { Tag } from "@blueprintjs/core"; -import "./style.css"; - -interface Props { - type: string; - content: string; -} - -const PatientInfo = ({ type, content }: Props) => { - return ( -
-
- {type} -
-
{content}
-
- ); -}; - -export default PatientInfo; diff --git a/src/components/patientPage/timelinePatient/index.tsx b/src/components/patientPage/timelinePatient/index.tsx index 98c6343..2a199a6 100644 --- a/src/components/patientPage/timelinePatient/index.tsx +++ b/src/components/patientPage/timelinePatient/index.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { Icon, H3, Colors } from "@blueprintjs/core"; +import { Card, Elevation } from "@blueprintjs/core"; import * as am4core from "@amcharts/amcharts4/core"; import * as am4charts from "@amcharts/amcharts4/charts"; @@ -146,13 +147,13 @@ const TimelinePatient = () => { }, [chart]); return ( -
+

Timeline

{/* The timeline will be drawn by amcharts in the timelineP div */}
-
+ ); }; diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index 1e86a72..ad16963 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -4,6 +4,7 @@ import PatientTable from "components/patients/patientTable"; import SearchTool from "components/patients/searchTool"; import { getPatients, getCount } from "services/api"; import { Patient } from "types"; +import { Card, Elevation } from "@blueprintjs/core"; import "./style.css"; interface Props { @@ -101,12 +102,12 @@ const Patients = () => { <>
-
+ -
-
+ + -
+
); diff --git a/src/services/api.tsx b/src/services/api.tsx index 14232e8..811af1a 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -9,7 +9,6 @@ const client = newFhirClient({ const makeRequest = async (resource: string, parameters: string) => { return new Promise((resolve, reject) => { let url = URL_SERVER + resource + "?" + parameters; - console.log("url : ", url); let xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", url); @@ -20,7 +19,6 @@ const makeRequest = async (resource: string, parameters: string) => { } }; xmlhttp.onerror = function() { - console.log("error"); reject({ status: xmlhttp.status, statusText: xmlhttp.statusText }); }; xmlhttp.send(); @@ -44,7 +42,6 @@ export const getCount = async (resource: string, queryParameters: string) => { // getCount function returns the number of resources of a type"; const response: any = await makeRequest(resource, queryParameters); - console.log("in getcount : ", response); return response.total; }; @@ -90,98 +87,73 @@ export const getPatients = async (param?: string) => { ); }; -export const getPatientData = (patientId: string) => { +export const getPatientData = async (patientId: string) => { /* getPatientData requests for data from Patient resourcce of id patientId return a Patient object. */ - const getPatientData = async () => { - let response: any = await makeRequest("Patient", "_id=" + patientId); - - // let response = await client.search({ - // type: "Patient", - // patient: patientId, - // query: {} - // }); - if (!response.entry) return; //patient not found - const patientData = response.entry[0]; - - const patient: Patient = { - id: patientData.resource.id - }; + let response: any = await makeRequest("Patient", "_id=" + patientId); + + // let response = await client.search({ + // type: "Patient", + // patient: patientId, + // query: {} + // }); + if (!response.entry) return; //patient not found + const patientData = response.entry[0]; + + const patient: Patient = { + id: patientData.resource.id + }; - // Completing patient information with available data - if (patientData.resource.identifier) { - patient.identifier = patientData.resource.identifier[0]; - } - - if (patientData.resource.birthDate) { - patient.age = getAge(new Date(patientData.resource.birthDate)); - patient.birthDate = - patientData.resource.birthDate + - " (" + - patient.age.toString() + - " ans)"; - } - if (patientData.resource.name) { - if (patientData.resource.name[0].given) - patient.firstName = patientData.resource.name[0].given.join(", "); - if (patientData.resource.name[0].family) - patient.lastName = patientData.resource.name[0].family; - } - - if (patientData.resource.identifier) { - patient.identifier = patientData.resource.identifier - .map((e: any) => { - return e.value; - }) - .join(", "); - } - - const responseAI = await getPatientResources( - "AllergyIntolerance", - patientId - ); - const responseO = await getSubjectResources("Observation", patientId); - const responseC = await getSubjectResources("Condition", patientId); - const responseEoC = await getPatientResources("EpisodeOfCare", patientId); - - patient.number = { - AllergyIntolerance: responseAI.data.total, - Observation: responseO.data.total, - Condition: responseC.data.total, - EpisodeOfCare: responseEoC.data.total - }; + // Completing patient information with available data + if (patientData.resource.identifier) { + patient.identifier = patientData.resource.identifier + .map((e: any) => { + return e.value; + }) + .join(", "); + } - return patient; - }; + if (patientData.resource.birthDate) { + patient.age = getAge(new Date(patientData.resource.birthDate)); + patient.birthDate = patientData.resource.birthDate; + } + if (patientData.resource.name) { + if (patientData.resource.name[0].given) + patient.firstName = patientData.resource.name[0].given.join(", "); + if (patientData.resource.name[0].family) + patient.lastName = patientData.resource.name[0].family; + } + + response = await getPatientResources("AllergyIntolerance", patientId); + patient.allergyIntolerances = response.data; - return getPatientData(); + response = await getSubjectResources("Observation", patientId); + patient.observations = response.data; + + response = await getSubjectResources("Condition", patientId); + patient.conditions = response.data; + + response = await getPatientResources("EpisodeOfCare", patientId); + patient.episodesOfCare = response.data; + + return patient; }; -export const getSubjectResources = ( - resourceType: string, - patientId: string -) => { - /* +/* Function getSubjectResources returns all resources of type resourceType where attribute subject is a Patient of type patientId */ - return client.search({ +export const getSubjectResources = (resourceType: string, patientId: string) => + client.search({ type: resourceType, query: { subject: { $type: "Patient", $id: patientId } } }); -}; - -export const getPatientResources = ( - resourceType: string, - patientId: string -) => { - /* +/* Function getPatientResources returns all resources of type resourceType where attribute patient has id patientId */ - return client.search({ +export const getPatientResources = (resourceType: string, patientId: string) => + client.search({ type: resourceType, - patient: patientId, - query: {} + patient: patientId }); -}; diff --git a/src/types.tsx b/src/types.tsx index 19444a7..ec46ec6 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -1,11 +1,23 @@ export interface Patient { + /* logical id of the Patient resource */ id: string; + /* patient identifier(s) (ex: NIP) */ identifier?: string; + /* patient first name */ firstName?: string; + /* patient last name */ lastName?: string; + /* patient age name */ age?: number; + /* patient birth date */ birthDate?: string; - medicalHistory?: string; - allergies?: string; - number?: any; + + /* list of allergy intolerance resources linked to this patient*/ + allergyIntolerances?: any; + /* list of condition resources linked to this patient*/ + conditions?: any; + /* list of observation resources linked to this patient*/ + observations?: any; + /* list of episode of care resources linked to this patient*/ + episodesOfCare?: any; } From 49439056569d5acd455b3b34187686241a395586 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Thu, 5 Mar 2020 11:13:03 +0100 Subject: [PATCH 16/27] modifying search tool, adding an unique input text to search by name or id, removing patientTable array which are replaced by Patient Cards --- .../patientCard/patientAgeInfo/index.tsx | 2 +- src/components/patients/index.tsx | 117 ++++++++---------- .../patients/patientTable/index.tsx | 68 +--------- .../patientTable/patientCardTable/index.tsx | 70 +++++++++++ .../patientTable/patientCardTable/style.css | 18 +++ .../patients/patientTable/style.css | 4 +- src/components/patients/searchTool/index.tsx | 51 ++++++-- .../patients/searchTool/searchItem/style.css | 8 +- .../patients/searchTool/searchName/index.tsx | 46 +++++++ .../patients/searchTool/searchName/style.css | 14 +++ src/components/patients/searchTool/style.css | 17 ++- src/constants/index.tsx | 28 ++--- src/services/api.tsx | 17 +-- 13 files changed, 288 insertions(+), 172 deletions(-) create mode 100644 src/components/patients/patientTable/patientCardTable/index.tsx create mode 100644 src/components/patients/patientTable/patientCardTable/style.css create mode 100644 src/components/patients/searchTool/searchName/index.tsx create mode 100644 src/components/patients/searchTool/searchName/style.css diff --git a/src/components/patientPage/patientCard/patientAgeInfo/index.tsx b/src/components/patientPage/patientCard/patientAgeInfo/index.tsx index bc6c077..019d7af 100644 --- a/src/components/patientPage/patientCard/patientAgeInfo/index.tsx +++ b/src/components/patientPage/patientCard/patientAgeInfo/index.tsx @@ -17,7 +17,7 @@ const PatientAgeInfo = ({ type, birthDate, age }: Props) => { {type}
- {birthDate}{" "} + {birthDate}
({age?.toString()} ans)
diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index ad16963..8287c80 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -5,6 +5,8 @@ import SearchTool from "components/patients/searchTool"; import { getPatients, getCount } from "services/api"; import { Patient } from "types"; import { Card, Elevation } from "@blueprintjs/core"; +import { PATIENT_SHOWN } from "../../constants"; + import "./style.css"; interface Props { @@ -16,75 +18,66 @@ const Patients = () => { const [patients, setPatients] = React.useState([] as Patient[]); const [patientCount, setPatientCount] = React.useState(""); - const handleSearch = (searchParams: any) => { - let params = "_count=35"; - searchParams.map((x: any) => { - switch (x.label) { - case "Nom": //TODO : find a better solution than hard coded values - switch (x.symbol) { - case "Contient": - params += `&name:contains=${x.text}`; - break; - case "Exact": - params += `&name:exact=${x.text}`; - break; - default: - params += `&name=${x.text}`; - break; - } - return {}; - case "Age": //TODO : find a better solution than hard coded values - const correspondingDate: Date = new Date(); - correspondingDate.setFullYear( - correspondingDate.getFullYear() - parseInt(x.text) - ); + const handleSearch = async (searchNameParams: any, searchParams: any) => { + let params = "_count=" + PATIENT_SHOWN; - const yyyy = correspondingDate.getFullYear(); - const mm = - (correspondingDate.getMonth() + 1 > 9 ? "" : "0") + - (correspondingDate.getMonth() + 1); - const dd = - (correspondingDate.getDate() > 9 ? "" : "0") + - correspondingDate.getDate(); + if (searchNameParams.text) { + params = "_count=10000"; + searchNameParams.text.split(" ").map((x: string) => { + params += "&name=" + x; + return params; + }); + let patients: Patient[] = await getPatients(params); + patients = patients.concat(await getPatients(params)); + patients = patients.concat(await getPatients(params)); - switch (x.symbol) { - case "=": - params += `&birthdate=lt${yyyy}-${mm}-${dd}`; - params += `&birthdate=gt${yyyy - 1}-${mm}-${dd}`; - break; - case "≠": //not working for now - params += `&birthdate=gt${yyyy}-${mm}-${dd},lt${yyyy - - 1}-${mm}-${dd}`; - break; - case ">": - params += `&birthdate=lt${yyyy}-${mm}-${dd}`; - break; - case "<": - params += `&birthdate=gt${yyyy}-${mm}-${dd}`; - break; - } - return {}; - case "Logical id": - params += `&_id=${x.text}`; - break; - case "Identifier": - params += `&identifier=${x.text}`; - break; - default: - console.info(`Paramètre ${x.label} non reconnu`); - } - return {}; - }); + setPatients(patients.slice(0, PATIENT_SHOWN)); - const fetchPatients = async () => { + setPatientCount(patients.length.toString()); + } else { + searchParams.map((x: any) => { + switch (x.label) { + case "Age": + const correspondingDate: Date = new Date(); + correspondingDate.setFullYear( + correspondingDate.getFullYear() - parseInt(x.text) + ); + const yyyy = correspondingDate.getFullYear(); + const mm = + (correspondingDate.getMonth() + 1 > 9 ? "" : "0") + + (correspondingDate.getMonth() + 1); + const dd = + (correspondingDate.getDate() > 9 ? "" : "0") + + correspondingDate.getDate(); + + switch (x.symbol) { + case "=": + params += `&birthdate=lt${yyyy}-${mm}-${dd}`; + params += `&birthdate=gt${yyyy - 1}-${mm}-${dd}`; + break; + case "≠": //not working for now + params += `&birthdate=gt${yyyy}-${mm}-${dd},lt${yyyy - + 1}-${mm}-${dd}`; + break; + case ">": + params += `&birthdate=lt${yyyy}-${mm}-${dd}`; + break; + case "<": + params += `&birthdate=gt${yyyy}-${mm}-${dd}`; + break; + } + return {}; + default: + console.info(`Paramètre ${x.label} non reconnu`); + } + return {}; + }); const patients: Patient[] = await getPatients(params); setPatients(patients); - const count = await getCount("Patient", params); //todo : adapt after PR merge + const count = await getCount("Patient", params); setPatientCount(count); - }; - - fetchPatients(); + } }; React.useEffect(() => { diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index 0b82c4e..14007ae 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -1,9 +1,7 @@ import React from "react"; -import { Link } from "react-router-dom"; -import { Cell, Column, Table } from "@blueprintjs/table"; import { Icon, H3 } from "@blueprintjs/core"; -import { ROUTE_PATIENT } from "../../../constants"; import { Patient } from "types"; +import PatientCardTable from "components/patients/patientTable/patientCardTable"; import "./style.css"; @@ -13,70 +11,16 @@ interface Props { } const PatientTable = ({ patients, patientCount }: Props) => { - const renderPatientAttribute = ( - attribute: "id" | "identifier" | "firstName" | "lastName" | "age", - index: number - ) => ( - - - - {patients[index][attribute] !== undefined - ? patients[index][attribute] - : "Inconnu"} - {/* Have to do !== undefined because if patients[index][attribute] (ex: patient age = 0), it will display "Inconnu" */} - - - - ); - return ( <>

Résultats

-
- - - renderPatientAttribute("id", index) - } - /> - - renderPatientAttribute("identifier", index) - } - /> - - renderPatientAttribute("firstName", index) - } - /> - - renderPatientAttribute("lastName", index) - } - /> - - renderPatientAttribute("age", index) - } - /> -
-
+ + {patients.map(x => ( + + ))} +
{patientCount !== undefined && `${patientCount} patient-e-s identifié-e-s`} diff --git a/src/components/patients/patientTable/patientCardTable/index.tsx b/src/components/patients/patientTable/patientCardTable/index.tsx new file mode 100644 index 0000000..11b46dd --- /dev/null +++ b/src/components/patients/patientTable/patientCardTable/index.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import { Icon, Card, H5, Tag } from "@blueprintjs/core"; +import { Link } from "react-router-dom"; +import { ROUTE_PATIENT } from "../../../../constants"; + +import { Patient } from "types"; + +import "./style.css"; + +interface Props { + patient: Patient; +} + +const PatientCardTable = ({ patient }: Props) => { + const getSubjectNameDiv = () => { + if (!patient.lastName && !patient.firstName) + return ( +
+ +
Nom inconnu
+
+ ); + + return ( +
+ +
{patient.lastName}
+ {patient.firstName} +
+ ); + }; + const getPatientCard = () => { + /* + Function getPatientCard + Get the PatientData object corresponding to patientId and generate jsx. + TODO: adapt this function to get data from the rest API + + Return page content with patient data. + */ + // case : rendering, patient found + return ( + <> + {getSubjectNameDiv()} + + {patient.identifier && ( +
+ NIP {patient.identifier}{" "} +
+ )} + + {patient.birthDate && ( +
+ {" "} + Date de naissance {patient.birthDate}{" "} +
+ )} + + ); + }; + + return ( + + + {getPatientCard()} + + + ); +}; + +export default PatientCardTable; diff --git a/src/components/patients/patientTable/patientCardTable/style.css b/src/components/patients/patientTable/patientCardTable/style.css new file mode 100644 index 0000000..b444b82 --- /dev/null +++ b/src/components/patients/patientTable/patientCardTable/style.css @@ -0,0 +1,18 @@ +.aligned { + display: flex; +} + +.card { + margin: 5px; + padding: 2px; +} + +.disabled-link-style:hover { + color: inherit; + text-decoration: none; +} + +.disabled-link-style { + color: inherit; + text-decoration: none; +} \ No newline at end of file diff --git a/src/components/patients/patientTable/style.css b/src/components/patients/patientTable/style.css index ee6697a..1a8a647 100644 --- a/src/components/patients/patientTable/style.css +++ b/src/components/patients/patientTable/style.css @@ -8,5 +8,7 @@ .infoPatient { text-align: right; - margin-top: 20px; + position: absolute; + right: 20px; + bottom: 20px; } \ No newline at end of file diff --git a/src/components/patients/searchTool/index.tsx b/src/components/patients/searchTool/index.tsx index 124b08b..7e97aa6 100644 --- a/src/components/patients/searchTool/index.tsx +++ b/src/components/patients/searchTool/index.tsx @@ -2,6 +2,7 @@ import React from "react"; import { Icon, H3, Button } from "@blueprintjs/core"; import "./style.css"; import SearchItem from "components/patients/searchTool/searchItem"; +import SearchName from "components/patients/searchTool/searchName"; interface searchForm { label: string; @@ -23,6 +24,12 @@ const SearchTool = ({ onSearch }: Props) => { newSearchForm ] as searchForm[]); + const [advancedSearchStyle, setAdvancedSearchStyle] = React.useState( + "hidden" + ); + + let nameSearch = { text: "" }; + const addSearchForm = () => { const newSearchForm: searchForm = { label: "", @@ -38,10 +45,16 @@ const SearchTool = ({ onSearch }: Props) => { setSearchForms([...searchForms]); }; + const changeStyle = () => { + if (advancedSearchStyle === "hidden") + setAdvancedSearchStyle("advancedSearch"); + else setAdvancedSearchStyle("hidden"); + }; + const search = () => { //This function will search for patients corresponding to the request and will show the patient list on the patient table. // For now, it only show the search parameters. - onSearch(searchForms); + onSearch(nameSearch, searchForms); }; return ( @@ -49,18 +62,32 @@ const SearchTool = ({ onSearch }: Props) => {

Recherche

-
- {searchForms.map((searchForm, index) => ( -
- -
- ))} +
+ +
+
+
+
+
+ {searchForms.map((searchForm, index) => ( +
+ +
+ ))} +
diff --git a/src/components/patients/searchTool/searchItem/style.css b/src/components/patients/searchTool/searchItem/style.css index 88a515c..bd67f26 100644 --- a/src/components/patients/searchTool/searchItem/style.css +++ b/src/components/patients/searchTool/searchItem/style.css @@ -1,22 +1,28 @@ .searchItem { display: flex; + width: 100%; } + .buttonDelete { margin-left: 5px; } + .searchCard { width: 95%; padding: 5px; display: flex; } + .formElement-label { width: 25%; margin-right: 2px; } + .formElement-symbol { width: 25%; margin-right: 2px; } + .formElement-input { display: inline-block; width: 50%; @@ -33,4 +39,4 @@ .formElement-input-invisible { display: none; -} +} \ No newline at end of file diff --git a/src/components/patients/searchTool/searchName/index.tsx b/src/components/patients/searchTool/searchName/index.tsx new file mode 100644 index 0000000..cf85372 --- /dev/null +++ b/src/components/patients/searchTool/searchName/index.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { InputGroup } from "@blueprintjs/core"; + +import "./style.css"; + +interface Props { + nameSearch: any; + launchSearch: Function; +} + +const SearchName = ({ nameSearch, launchSearch }: Props) => { + const [inputText, setInputText] = React.useState(""); + + /* + onFormChange updates the searchItem object with current label, symbol and inputText. + */ + const onFormChange = (inputText: string) => { + nameSearch.text = inputText; + }; + + const enterPressed = (event: any) => { + let code = event.keyCode || event.which; + if (code === 13) { + //enter keycode + launchSearch(); + } + }; + + return ( +
+
+ { + setInputText(evt.target.value); + onFormChange(evt.target.value); + }} + onKeyPress={enterPressed} + /> +
+
+ ); +}; + +export default SearchName; diff --git a/src/components/patients/searchTool/searchName/style.css b/src/components/patients/searchTool/searchName/style.css new file mode 100644 index 0000000..11e2e62 --- /dev/null +++ b/src/components/patients/searchTool/searchName/style.css @@ -0,0 +1,14 @@ +.searchItem { + display: flex; +} + +.searchCard { + width: 95%; + padding: 5px; + display: flex; +} + +.nameItem { + display: inline-block; + width: 100%; +} \ No newline at end of file diff --git a/src/components/patients/searchTool/style.css b/src/components/patients/searchTool/style.css index 6a5fd5d..9f87e25 100644 --- a/src/components/patients/searchTool/style.css +++ b/src/components/patients/searchTool/style.css @@ -9,14 +9,27 @@ float: right; } -.div-searchItem { +.searchItem { margin: 3px; } -.div-searchItems { +.searchItems { height: 90%; overflow-x: auto; } + .icon-title { transform: translateY(-20%); } + +.hidden { + display: none; +} + +.advancedResearchButton { + float: right; +} + +.smaller { + width: 95% +} \ No newline at end of file diff --git a/src/constants/index.tsx b/src/constants/index.tsx index 0815679..f486f16 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -1,3 +1,12 @@ +/* + Parameters +*/ + +export const PATIENT_SHOWN = 15; + +/* + Routes and URLs +*/ export const ROUTE_HOME = "/"; export const ROUTE_PATIENT = "/patient"; @@ -8,20 +17,13 @@ export const URL_SERVER = "http://hapi.fhir.org/baseR4/"; Search and types are described in FHIR documentation : https://www.hl7.org/fhir/search.html#ptype */ -const OPERATION_NUMBER = [">", "<", "=", "≠"]; +const OPERATION_NUMBER = [">", "<", "="]; const OPERATION_TEXT = ["Commence par", "Contient", "Exact"]; -const OPERATION_ID = ["Exact"]; - const OPERATION_BOOLEAN = ["Oui", "Non"]; export const SEARCH_FIELDS = [ - { - name: "Nom", - operations: OPERATION_TEXT, - isInputText: true - }, { name: "Age", operations: OPERATION_NUMBER, @@ -51,15 +53,5 @@ export const SEARCH_FIELDS = [ name: "Diabète", operations: OPERATION_BOOLEAN, isInputText: false - }, - { - name: "Logical id", - operations: OPERATION_ID, - isInputText: true - }, - { - name: "Identifier", - operations: OPERATION_ID, - isInputText: true } ]; diff --git a/src/services/api.tsx b/src/services/api.tsx index 811af1a..fe3e20b 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -1,4 +1,4 @@ -import { URL_SERVER } from "../constants"; +import { URL_SERVER, PATIENT_SHOWN } from "../constants"; import { Patient } from "types"; import newFhirClient from "fhir.js"; @@ -51,23 +51,20 @@ export const getPatients = async (param?: string) => { If param is empty ill return the whole list. */ - /* - test - */ let response: any; if (param) { - //To be adapted ! response = await makeRequest("Patient", param); } else { - response = await makeRequest("Patient", "_count=30"); + response = await makeRequest("Patient", "_count=" + PATIENT_SHOWN); } - if (!response.entry) return {}; + if (!response.entry) return []; else return response.entry.map( ({ resource: { id, identifier, birthDate, name } }: any) => { const patient: Patient = { id: id, + birthDate: birthDate, age: birthDate && getAge(new Date(birthDate)) }; if (name) { @@ -81,7 +78,6 @@ export const getPatients = async (param?: string) => { }) .join(", "); } - return patient; } ); @@ -94,11 +90,6 @@ export const getPatientData = async (patientId: string) => { */ let response: any = await makeRequest("Patient", "_id=" + patientId); - // let response = await client.search({ - // type: "Patient", - // patient: patientId, - // query: {} - // }); if (!response.entry) return; //patient not found const patientData = response.entry[0]; From 1b23b23963bd858eaea9366b65af4aa4e2de0501 Mon Sep 17 00:00:00 2001 From: Nicolas Riss Date: Fri, 6 Mar 2020 10:27:42 +0100 Subject: [PATCH 17/27] adapting header to Arkhn colors --- src/components/header/index.tsx | 6 +++--- src/components/header/style.css | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/header/index.tsx b/src/components/header/index.tsx index 78d11e1..de8cb6f 100644 --- a/src/components/header/index.tsx +++ b/src/components/header/index.tsx @@ -13,7 +13,7 @@ interface Props { const Header = ({ patient }: Props) => { return ( - + @@ -32,8 +32,8 @@ const Header = ({ patient }: Props) => {
Nom médecin
- +
{patientCount !== undefined && `${patientCount} patient-e-s identifié-e-s`} diff --git a/src/components/patients/patientTable/patientCardTable/index.tsx b/src/components/patients/patientTable/patientCardTable/index.tsx index 11b46dd..158b270 100644 --- a/src/components/patients/patientTable/patientCardTable/index.tsx +++ b/src/components/patients/patientTable/patientCardTable/index.tsx @@ -40,20 +40,25 @@ const PatientCardTable = ({ patient }: Props) => { // case : rendering, patient found return ( <> - {getSubjectNameDiv()} +
{getSubjectNameDiv()}
- {patient.identifier && ( -
- NIP {patient.identifier}{" "} -
- )} +
+ {patient.identifier && ( + <> + NIP +
{patient.identifier}
+ + )} +
- {patient.birthDate && ( -
- {" "} - Date de naissance {patient.birthDate}{" "} -
- )} +
+ {patient.birthDate && ( + <> + Date de naissance +
{patient.birthDate}
+ + )} +
); }; diff --git a/src/components/patients/patientTable/patientCardTable/style.css b/src/components/patients/patientTable/patientCardTable/style.css index b444b82..7827f46 100644 --- a/src/components/patients/patientTable/patientCardTable/style.css +++ b/src/components/patients/patientTable/patientCardTable/style.css @@ -1,10 +1,13 @@ .aligned { display: flex; + align-content: space-between; } .card { margin: 5px; padding: 2px; + vertical-align: middle; + height: 40px; } .disabled-link-style:hover { @@ -15,4 +18,30 @@ .disabled-link-style { color: inherit; text-decoration: none; +} + +.nipCard { + width: 40%; + display: flex; +} + +.nipText { + overflow-y: auto; + width: 80% +} + +.nameCard { + text-align: left; + overflow-y: auto; + width: 30%; +} + +.birthDateCard { + width: 30%; + display: flex; +} + +.patientList { + height: 90%; + overflow-x: auto; } \ No newline at end of file diff --git a/src/components/patients/patientTable/style.css b/src/components/patients/patientTable/style.css index 1a8a647..3c07993 100644 --- a/src/components/patients/patientTable/style.css +++ b/src/components/patients/patientTable/style.css @@ -1,7 +1,3 @@ -.table { - height: 90%; -} - .icon-title { transform: translateY(-20%); } @@ -11,4 +7,12 @@ position: absolute; right: 20px; bottom: 20px; +} + +.leftButton { + text-align: left; +} + +.rightButton { + text-align: right } \ No newline at end of file diff --git a/src/components/patients/searchTool/index.tsx b/src/components/patients/searchTool/index.tsx index 7e97aa6..05bde1f 100644 --- a/src/components/patients/searchTool/index.tsx +++ b/src/components/patients/searchTool/index.tsx @@ -15,20 +15,12 @@ interface Props { } const SearchTool = ({ onSearch }: Props) => { - const newSearchForm: searchForm = { - label: "", - symbol: "", - text: "" - }; - const [searchForms, setSearchForms] = React.useState([ - newSearchForm - ] as searchForm[]); - + const [searchForms, setSearchForms] = React.useState([] as searchForm[]); const [advancedSearchStyle, setAdvancedSearchStyle] = React.useState( "hidden" ); - let nameSearch = { text: "" }; + let [nameSearch] = React.useState({ text: "" }); const addSearchForm = () => { const newSearchForm: searchForm = { @@ -65,19 +57,13 @@ const SearchTool = ({ onSearch }: Props) => {
-
- -
+
{searchForms.map((searchForm, index) => ( -
+
))} diff --git a/src/components/patients/searchTool/searchItem/style.css b/src/components/patients/searchTool/searchItem/style.css index bd67f26..5480475 100644 --- a/src/components/patients/searchTool/searchItem/style.css +++ b/src/components/patients/searchTool/searchItem/style.css @@ -8,7 +8,7 @@ } .searchCard { - width: 95%; + width: 80%; padding: 5px; display: flex; } @@ -28,6 +28,10 @@ width: 50%; } +.searchItem { + margin: 3px; +} + .formElement-wo-input-symbol { width: 50%; } diff --git a/src/components/patients/searchTool/style.css b/src/components/patients/searchTool/style.css index 9f87e25..17e1313 100644 --- a/src/components/patients/searchTool/style.css +++ b/src/components/patients/searchTool/style.css @@ -9,12 +9,8 @@ float: right; } -.searchItem { - margin: 3px; -} - .searchItems { - height: 90%; + height: 80%; overflow-x: auto; } @@ -24,12 +20,4 @@ .hidden { display: none; -} - -.advancedResearchButton { - float: right; -} - -.smaller { - width: 95% } \ No newline at end of file diff --git a/src/constants/index.tsx b/src/constants/index.tsx index f486f16..518c7df 100644 --- a/src/constants/index.tsx +++ b/src/constants/index.tsx @@ -2,7 +2,8 @@ Parameters */ -export const PATIENT_SHOWN = 15; +export const PATIENT_SHOWN = 17; +export const PATIENT_REQUESTED = 100; /* Routes and URLs diff --git a/src/services/api.tsx b/src/services/api.tsx index fe3e20b..608bc89 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -1,21 +1,26 @@ -import { URL_SERVER, PATIENT_SHOWN } from "../constants"; -import { Patient } from "types"; -import newFhirClient from "fhir.js"; +import { URL_SERVER, PATIENT_REQUESTED } from "../constants"; +import { Patient, Bundle, PatientBundle } from "types"; -const client = newFhirClient({ - baseUrl: URL_SERVER -}); +const makeRequest = async ( + resource: string, + total?: boolean, + parameters?: string +) => { + const url: string = `${URL_SERVER}${resource}?${parameters || ""}`; -const makeRequest = async (resource: string, parameters: string) => { - return new Promise((resolve, reject) => { - let url = URL_SERVER + resource + "?" + parameters; + let response = await makeRequestByURL(url, total); + return response; +}; + +const makeRequestByURL = async (url: string, total?: boolean) => { + let response = await new Promise((resolve, reject) => { let xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", url); xmlhttp.onload = () => { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { - let patients = JSON.parse(xmlhttp.response); - resolve(patients); + let resources = JSON.parse(xmlhttp.response); + resolve(resources); } }; xmlhttp.onerror = function() { @@ -23,6 +28,35 @@ const makeRequest = async (resource: string, parameters: string) => { }; xmlhttp.send(); }); + + let result: Bundle = { + entry: response.entry || [] + }; + + if (response.link) { + response.link.map((x: any) => { + if (x.relation === "next") { + result.nextLink = x.url; + return false; + } + return false; + }); + } + + /* + if total : get the total number of matches + */ + if (total) + if (response.data && response.data.total) + result.total = response.data.total; + else { + const responseEntryNumber = await makeRequestByURL( + url + "&_summary=count" + ); + result.total = responseEntryNumber.total || 0; + } + + return result; }; const getAge = (birthDate: Date) => { @@ -38,11 +72,111 @@ const getAge = (birthDate: Date) => { return age; }; -export const getCount = async (resource: string, queryParameters: string) => { - // getCount function returns the number of resources of a type"; +export const getPatientsPerQuery = async ( + searchNameParams: any, + searchParams: any +) => { + let bundles: Bundle[] = []; + let params: string; + + if (searchNameParams.text) { + params = "_count=10000"; + + searchNameParams.text.split(" ").map((x: string) => { + params += "&name=" + x; + return params; + }); + let bundlePatient = await makeRequest("Patient", false, params); + + let entries = bundlePatient.entry; + params = "_count=10000"; + searchNameParams.text.split(" ").map((x: string) => { + params += "&identifier=" + x; + return params; + }); + + bundlePatient = await makeRequest("Patient", false, params); + + bundlePatient.entry = bundlePatient.entry.concat(entries); + + bundles.push(bundlePatient); + } + + /* + Working but need perfs improvement + */ + bundles = bundles.concat( + await Promise.all( + searchParams.map((x: any) => { + switch (x.label) { + case "Age": + params = "_count=10000"; + const correspondingDate: Date = new Date(); + correspondingDate.setFullYear( + correspondingDate.getFullYear() - parseInt(x.text) + ); + const yyyy = correspondingDate.getFullYear(); + const mm = + (correspondingDate.getMonth() + 1 > 9 ? "" : "0") + + (correspondingDate.getMonth() + 1); + const dd = + (correspondingDate.getDate() > 9 ? "" : "0") + + correspondingDate.getDate(); + + switch (x.symbol) { + case "=": + params += `&birthdate=lt${yyyy}-${mm}-${dd}`; + params += `&birthdate=gt${yyyy - 1}-${mm}-${dd}`; + return getPatients(params); + case ">": + params += `&birthdate=lt${yyyy}-${mm}-${dd}`; + return getPatients(params); + case "<": + params += `&birthdate=gt${yyyy}-${mm}-${dd}`; + return getPatients(params); + } + return {}; + case "Diabète": + return getPatientPerCondition("73211009"); + default: + console.info(`Paramètre ${x.label} non reconnu`); + } + return []; + }) + ) + ); + + let finalBundle: Bundle = bundles[0]; + bundles.map((bundle: Bundle) => { + if (finalBundle !== bundle && bundle.entry.length > 0) { + let listId = bundle.entry.map((x: any) => x.resource.id); + finalBundle.entry = finalBundle.entry.filter( + (entry: any) => listId.indexOf(entry.resource.id) >= 0 + ); + } + return false; + }); + finalBundle.total = finalBundle.entry.length; + + return setPatients(finalBundle); +}; + +export const requestNextPatients = async (bundle: PatientBundle) => { + if (!bundle.nextLink) { + console.info("no link available"); + return; + } else { + let newBundle: PatientBundle = (await makeRequestByURL( + bundle.nextLink + )) as PatientBundle; + + bundle.entry = bundle.entry.concat(newBundle.entry); - const response: any = await makeRequest(resource, queryParameters); - return response.total; + newBundle = setPatients(newBundle); + bundle.patients = bundle.patients.concat(newBundle.patients); + bundle.nextLink = newBundle.nextLink; + return bundle; + } }; export const getPatients = async (param?: string) => { @@ -51,44 +185,59 @@ export const getPatients = async (param?: string) => { If param is empty ill return the whole list. */ - let response: any; - if (param) { - response = await makeRequest("Patient", param); - } else { - response = await makeRequest("Patient", "_count=" + PATIENT_SHOWN); - } + let response: PatientBundle = (await makeRequest( + "Patient", + true, + param + )) as PatientBundle; - if (!response.entry) return []; - else - return response.entry.map( - ({ resource: { id, identifier, birthDate, name } }: any) => { - const patient: Patient = { - id: id, - birthDate: birthDate, - age: birthDate && getAge(new Date(birthDate)) - }; - if (name) { - if (name[0].given) patient.firstName = name[0].given.join(", "); - if (name[0].family) patient.lastName = name[0].family; - } - if (identifier) { - patient.identifier = identifier - .map((e: any) => { - return e.value; - }) - .join(", "); - } - return patient; - } - ); + return setPatients(response); +}; + +export const setPatients = (bundle: Bundle) => { + /* + setPatients transforms a bundle in a PatientBundle (generate Patient objects) + */ + + let response: PatientBundle = bundle as PatientBundle; + response.patients = response.entry.map((entry: any) => { + return generatePatientFromResource(entry.resource); + }); + + return response; +}; + +export const generatePatientFromResource = ({ + id, + identifier, + birthDate, + name +}: any) => { + const patient: Patient = { + id: id, + birthDate: birthDate, + age: birthDate && getAge(new Date(birthDate)) + }; + if (name) { + if (name[0].given) patient.firstName = name[0].given.join(", "); + if (name[0].family) patient.lastName = name[0].family; + } + if (identifier) { + patient.identifier = identifier + .map((e: any) => { + return e.value; + }) + .join(", "); + } + return patient; }; -export const getPatientData = async (patientId: string) => { +export const getPatientData = async (patientId: string, detailed?: boolean) => { /* getPatientData requests for data from Patient resourcce of id patientId return a Patient object. */ - let response: any = await makeRequest("Patient", "_id=" + patientId); + let response: any = await makeRequest("Patient", false, "_id=" + patientId); if (!response.entry) return; //patient not found const patientData = response.entry[0]; @@ -117,34 +266,67 @@ export const getPatientData = async (patientId: string) => { patient.lastName = patientData.resource.name[0].family; } - response = await getPatientResources("AllergyIntolerance", patientId); - patient.allergyIntolerances = response.data; + if (detailed) { + response = await getPatientResources("AllergyIntolerance", patientId); + patient.allergyIntolerances = response.entry; - response = await getSubjectResources("Observation", patientId); - patient.observations = response.data; + response = await getSubjectResources("Observation", patientId); + patient.observations = response.entry; - response = await getSubjectResources("Condition", patientId); - patient.conditions = response.data; + response = await getSubjectResources("Condition", patientId); + patient.conditions = response.entry; - response = await getPatientResources("EpisodeOfCare", patientId); - patient.episodesOfCare = response.data; + response = await getPatientResources("EpisodeOfCare", patientId); + patient.episodesOfCare = response.entry; + } return patient; }; /* - Function getSubjectResources returns all resources of type resourceType where attribute subject is a Patient of type patientId + Return the list of patients affected by a condition + Example : 73211009 = diabetes +*/ +export const getPatientPerCondition = async (conditionId: string) => { + /* + get all conditions + http://hapi.fhir.org/baseR4/Condition?code=73211009 */ -export const getSubjectResources = (resourceType: string, patientId: string) => - client.search({ - type: resourceType, - query: { subject: { $type: "Patient", $id: patientId } } + let response: any = await makeRequest( + "Condition", + true, + "_count=10000&code=" + conditionId + ); + if (!response) return; + + const refsList = response.map((x: any) => { + return x.resource.subject.reference.replace("Patient/", ""); }); + + return await Promise.all( + refsList.map((x: string) => getPatientData(x, false)) + ); +}; + +/* + Function getSubjectResources returns all resources of type resourceType where attribute subject is a Patient of type patientId + */ +export const getSubjectResources = async ( + resourceType: "Observation" | "Condition", + patientId: string +) => { + return await makeRequest( + resourceType, + true, + "subject:Patient._id=" + patientId + ); +}; /* Function getPatientResources returns all resources of type resourceType where attribute patient has id patientId */ -export const getPatientResources = (resourceType: string, patientId: string) => - client.search({ - type: resourceType, - patient: patientId - }); +export const getPatientResources = async ( + resourceType: "AllergyIntolerance" | "EpisodeOfCare", + patientId: string +) => { + return await makeRequest(resourceType, true, "patient=" + patientId); +}; diff --git a/src/types.tsx b/src/types.tsx index ec46ec6..88c9b65 100644 --- a/src/types.tsx +++ b/src/types.tsx @@ -21,3 +21,20 @@ export interface Patient { /* list of episode of care resources linked to this patient*/ episodesOfCare?: any; } + +export interface Bundle { + /* list of entries */ + entry: any[]; + /* total number of patients matching the criteria */ + total?: number; + + /* parameters leading to this request */ + parameters?: any; + + /* link to get more entries */ + nextLink?: string; +} + +export interface PatientBundle extends Bundle { + patients: Patient[]; +} From f56f3d55d79dce5553316833f822b2220ab2e961 Mon Sep 17 00:00:00 2001 From: "nicolas.riss22" Date: Tue, 10 Mar 2020 16:36:16 +0100 Subject: [PATCH 20/27] =?UTF-8?q?disabling=20pr=C3=A9c=C3=A9dent=20button?= =?UTF-8?q?=20if=20it=20is=20the=20first=20page,=20API=20cleaning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/patients/index.tsx | 5 +- .../patients/patientTable/index.tsx | 22 +- .../patientTable/patientCardTable/style.css | 3 +- src/components/patients/searchTool/index.tsx | 6 +- .../patients/searchTool/searchItem/index.tsx | 12 +- src/services/api.tsx | 192 +++++++++++------- 6 files changed, 148 insertions(+), 92 deletions(-) diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index c0eb170..03eb379 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -15,7 +15,6 @@ interface Props { const Patients = () => { const [patientBundle, setPatientBundle] = React.useState({} as PatientBundle); - const [patientCount, setPatientCount] = React.useState(""); const handleSearch = async (searchNameParams: any, searchParams: any) => { const bundle: PatientBundle = await getPatientsPerQuery( @@ -23,14 +22,12 @@ const Patients = () => { searchParams ); setPatientBundle(bundle); - if (bundle.total) setPatientCount(bundle.total.toString()); }; React.useEffect(() => { const fetchPatients = async () => { const bundle: PatientBundle = await getPatients(); setPatientBundle(bundle); - if (bundle.total) setPatientCount(bundle.total.toString()); }; fetchPatients(); }, []); @@ -43,7 +40,7 @@ const Patients = () => { - +
diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index bb7db9f..450ee96 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -9,11 +9,12 @@ import "./style.css"; interface Props { bundle: PatientBundle; - patientCount: string; } -const PatientTable = ({ bundle, patientCount }: Props) => { +const PatientTable = ({ bundle }: Props) => { const [pageIndex, setPageIndex] = React.useState(0); + const [leftDisabled, setLeftDisabled] = React.useState(true); + const [rightDisabled, setRightDisabled] = React.useState(false); const [patientBundle, setPatientBundle] = React.useState(bundle); React.useEffect(() => { @@ -21,15 +22,20 @@ const PatientTable = ({ bundle, patientCount }: Props) => { }, [bundle]); const getNextPage = async () => { - if ((pageIndex + 2) * PATIENT_SHOWN > bundle.patients.length) { + if ((pageIndex + 2) * PATIENT_SHOWN >= bundle.patients.length) { const patBundle = (await requestNextPatients(bundle)) as PatientBundle; - setPatientBundle(patBundle); + if (patBundle) setPatientBundle(patBundle); } setPageIndex(pageIndex + 1); + setLeftDisabled(false); }; const getPreviousPage = async () => { - if (pageIndex > 0) setPageIndex(pageIndex - 1); + if (pageIndex > 0) { + setPageIndex(pageIndex - 1); + setRightDisabled(false); + } + if (pageIndex <= 0) setLeftDisabled(true); }; return ( <> @@ -44,6 +50,7 @@ const PatientTable = ({ bundle, patientCount }: Props) => { className="leftButton" icon="direction-left" onClick={() => getPreviousPage()} + disabled={leftDisabled} > Précédent @@ -51,12 +58,13 @@ const PatientTable = ({ bundle, patientCount }: Props) => { className="rightButton" rightIcon="direction-right" onClick={() => getNextPage()} + disabled={rightDisabled} > Suivant
- {patientCount !== undefined && - `${patientCount} patient-e-s identifié-e-s`} + {bundle.total !== undefined && + `${bundle.total} patient-e-s identifié-e-s`}
); diff --git a/src/components/patients/patientTable/patientCardTable/style.css b/src/components/patients/patientTable/patientCardTable/style.css index 7827f46..968a2e5 100644 --- a/src/components/patients/patientTable/patientCardTable/style.css +++ b/src/components/patients/patientTable/patientCardTable/style.css @@ -23,11 +23,12 @@ .nipCard { width: 40%; display: flex; + overflow-wrap: break-word; } .nipText { overflow-y: auto; - width: 80% + width: 80%; } .nameCard { diff --git a/src/components/patients/searchTool/index.tsx b/src/components/patients/searchTool/index.tsx index 05bde1f..4b94a79 100644 --- a/src/components/patients/searchTool/index.tsx +++ b/src/components/patients/searchTool/index.tsx @@ -64,7 +64,11 @@ const SearchTool = ({ onSearch }: Props) => {
{searchForms.map((searchForm, index) => (
- +
))}
-
- {patientCount && `${patientCount} patients identifiés`} -
); }; From 48faaec7d375d95b12c00b6902344a53ed886f2c Mon Sep 17 00:00:00 2001 From: "nicolas.riss22" Date: Fri, 13 Mar 2020 15:08:08 +0100 Subject: [PATCH 22/27] taking jason comments into account for PR#22 --- .../patientPage/patientCard/index.tsx | 13 ++------ .../patientCard/patientGeneralInfo/index.tsx | 3 +- src/components/patients/index.tsx | 17 ++++++++--- .../patients/patientTable/index.tsx | 30 +++++++++---------- src/components/patients/searchTool/index.tsx | 10 +++---- .../patients/searchTool/searchName/index.tsx | 13 ++------ src/constants/index.tsx | 2 +- src/services/api.tsx | 13 ++++---- 8 files changed, 45 insertions(+), 56 deletions(-) diff --git a/src/components/patientPage/patientCard/index.tsx b/src/components/patientPage/patientCard/index.tsx index c745ef9..e5463e0 100644 --- a/src/components/patientPage/patientCard/index.tsx +++ b/src/components/patientPage/patientCard/index.tsx @@ -23,17 +23,10 @@ const PatientCard = ({ patient }: Props) => { | "episodesOfCare", writtenName: string ) => { - let resourceNumber: string = ""; - if (patient[object]) { - resourceNumber = patient[object].length; - } else resourceNumber = "0"; + const resourceNumber = patient[object] ? patient[object].length : 0; return ( -
{ - console.log(writtenName + " : ", patient[object]); - }} - > +
); @@ -83,7 +76,6 @@ const PatientCard = ({ patient }: Props) => { /> } - {getPatientNumberCard("allergyIntolerances", "Allergies")} {getPatientNumberCard("observations", "Observations")} @@ -91,7 +83,6 @@ const PatientCard = ({ patient }: Props) => { {getPatientNumberCard("conditions", "Conditions")} {getPatientNumberCard("episodesOfCare", "Hospitalisations")} - ); }; diff --git a/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx b/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx index ddc6aa8..0e856c2 100644 --- a/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx +++ b/src/components/patientPage/patientCard/patientGeneralInfo/index.tsx @@ -9,7 +9,8 @@ interface Props { } const PatientGeneralInfo = ({ type, content }: Props) => { - if (content) + if (content !== undefined) + //Avoid to write "Inconnu" instead of 0 return (
diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index d835c82..4cc39a6 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -5,6 +5,7 @@ import SearchTool from "components/patients/searchTool"; import { getPatients, getPatientsPerQuery } from "services/api"; import { PatientBundle } from "types"; import { Card, Elevation } from "@blueprintjs/core"; +import { requestNextPatients } from "services/api"; import "./style.css"; @@ -16,9 +17,15 @@ interface Props { const Patients = () => { const [patientBundle, setPatientBundle] = React.useState({} as PatientBundle); - const handleSearch = async (searchNameParams: any, searchParams: any) => { + const getNextPatients = async () => { + const patBundle = (await requestNextPatients( + patientBundle + )) as PatientBundle; + setPatientBundle(patBundle); + }; + const handleSearch = async (searchName: String, searchParams: any) => { const bundle: PatientBundle = await getPatientsPerQuery( - searchNameParams, + searchName, searchParams ); setPatientBundle(bundle); @@ -40,8 +47,10 @@ const Patients = () => { - - +
diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index 450ee96..d1fda6b 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -3,37 +3,39 @@ import { Icon, H3, Button } from "@blueprintjs/core"; import { PatientBundle } from "types"; import PatientCardTable from "components/patients/patientTable/patientCardTable"; import { PATIENT_SHOWN } from "../../../constants"; -import { requestNextPatients } from "services/api"; import "./style.css"; interface Props { bundle: PatientBundle; + updateNextPatients: Function; } -const PatientTable = ({ bundle }: Props) => { +const PatientTable = ({ bundle, updateNextPatients }: Props) => { const [pageIndex, setPageIndex] = React.useState(0); const [leftDisabled, setLeftDisabled] = React.useState(true); - const [rightDisabled, setRightDisabled] = React.useState(false); - const [patientBundle, setPatientBundle] = React.useState(bundle); - - React.useEffect(() => { - setPatientBundle(bundle); - }, [bundle]); const getNextPage = async () => { if ((pageIndex + 2) * PATIENT_SHOWN >= bundle.patients.length) { - const patBundle = (await requestNextPatients(bundle)) as PatientBundle; - if (patBundle) setPatientBundle(patBundle); + updateNextPatients(); } setPageIndex(pageIndex + 1); setLeftDisabled(false); }; + const getPatientCardTable = () => { + const patientcards = + Object.keys(bundle).length !== 0 && + bundle.patients + .slice(pageIndex * PATIENT_SHOWN, (pageIndex + 1) * PATIENT_SHOWN) + .map(x => ); + + return patientcards; + }; + const getPreviousPage = async () => { if (pageIndex > 0) { setPageIndex(pageIndex - 1); - setRightDisabled(false); } if (pageIndex <= 0) setLeftDisabled(true); }; @@ -42,10 +44,7 @@ const PatientTable = ({ bundle }: Props) => {

Résultats

- {Object.keys(patientBundle).length !== 0 && - patientBundle.patients - .slice(pageIndex * PATIENT_SHOWN, (pageIndex + 1) * PATIENT_SHOWN) - .map(x => )} + {getPatientCardTable()} diff --git a/src/components/patients/searchTool/index.tsx b/src/components/patients/searchTool/index.tsx index 4b94a79..c2d95c7 100644 --- a/src/components/patients/searchTool/index.tsx +++ b/src/components/patients/searchTool/index.tsx @@ -20,7 +20,7 @@ const SearchTool = ({ onSearch }: Props) => { "hidden" ); - let [nameSearch] = React.useState({ text: "" }); + let [nameSearch, setNameSearch] = React.useState(""); const addSearchForm = () => { const newSearchForm: searchForm = { @@ -38,9 +38,9 @@ const SearchTool = ({ onSearch }: Props) => { }; const changeStyle = () => { - if (advancedSearchStyle === "hidden") - setAdvancedSearchStyle("advancedSearch"); - else setAdvancedSearchStyle("hidden"); + advancedSearchStyle === "hidden" + ? setAdvancedSearchStyle("advancedSearch") + : setAdvancedSearchStyle("hidden"); }; const search = () => { @@ -55,7 +55,7 @@ const SearchTool = ({ onSearch }: Props) => { Recherche
- +
- +
+

+ Résultats +

+ {getPatientCardTable()} +
+
+ + +
{bundle.total !== undefined && `${bundle.total} patient-e-s identifié-e-s`} diff --git a/src/components/patients/patientTable/patientCardTable/style.css b/src/components/patients/patientTable/patientCardTable/style.css index 968a2e5..32c2c26 100644 --- a/src/components/patients/patientTable/patientCardTable/style.css +++ b/src/components/patients/patientTable/patientCardTable/style.css @@ -7,7 +7,7 @@ margin: 5px; padding: 2px; vertical-align: middle; - height: 40px; + height: 38px; } .disabled-link-style:hover { @@ -43,6 +43,5 @@ } .patientList { - height: 90%; overflow-x: auto; } \ No newline at end of file diff --git a/src/components/patients/patientTable/style.css b/src/components/patients/patientTable/style.css index 3c07993..7418c1f 100644 --- a/src/components/patients/patientTable/style.css +++ b/src/components/patients/patientTable/style.css @@ -11,8 +11,16 @@ .leftButton { text-align: left; + bottom: 20px; + left: 50px; } .rightButton { - text-align: right + text-align: right; + bottom: 20px; + right: 50px; +} + +.patientArray { + height: 98% } \ No newline at end of file diff --git a/src/services/api.tsx b/src/services/api.tsx index 5755c2e..5b16462 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -224,12 +224,12 @@ export const requestNextPatients = async (bundle: PatientBundle) => { bundle.nextLink )) as PatientBundle; - bundle.entry = bundle.entry.concat(newBundle.entry); + newBundle.entry = bundle.entry.concat(newBundle.entry); newBundle = addPatientsToBundle(newBundle); - bundle.patients = bundle.patients.concat(newBundle.patients); - bundle.nextLink = newBundle.nextLink; - return bundle; + newBundle.patients = bundle.patients.concat(newBundle.patients); + newBundle.total = bundle.total; + return newBundle; } }; From e8c2e3bbcc01f66ced370a4ee95aa2e4f0bffb75 Mon Sep 17 00:00:00 2001 From: "nicolas.riss22" Date: Sun, 15 Mar 2020 18:00:37 +0100 Subject: [PATCH 24/27] bug correction on diabete patient search --- src/services/api.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/services/api.tsx b/src/services/api.tsx index 5b16462..7798217 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -329,17 +329,18 @@ export const getPatientPerCondition = async (conditionId: string) => { let response: any = await makeRequest( "Condition", true, - `&code=${conditionId}` + `&code=${conditionId}`, + 1000 ); if (!response) return; - const refsList = response.map((x: any) => { + const refList = response.entry.map((x: any) => { return x.resource.subject.reference.replace("Patient/", ""); }); - return await Promise.all( - refsList.map((x: string) => getPatientData(x, false)) - ); + response = await makeRequest("Patient", true, `?_id=${refList.join(",")}`); + + return response; }; /* From 2476f418f84f90445040c0aad48aded4e7368265 Mon Sep 17 00:00:00 2001 From: "nicolas.riss22" Date: Mon, 16 Mar 2020 09:14:04 +0100 Subject: [PATCH 25/27] adding some comments and removing nextlink bug --- src/components/patients/patientTable/index.tsx | 17 ++++++++++++++++- src/services/api.tsx | 4 ++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/components/patients/patientTable/index.tsx b/src/components/patients/patientTable/index.tsx index 677b76b..73c01fb 100644 --- a/src/components/patients/patientTable/index.tsx +++ b/src/components/patients/patientTable/index.tsx @@ -16,14 +16,26 @@ const PatientTable = ({ bundle, updateNextPatients }: Props) => { const [leftDisabled, setLeftDisabled] = React.useState(true); const getNextPage = async () => { + /* + * Show next patient table page + */ if ((pageIndex + 2) * PATIENT_SHOWN >= bundle.patients.length) { - updateNextPatients(); + /* + * fetch next bundle page (nexLink) to get more patients + */ + if (bundle.nextLink) { + updateNextPatients(); + } } setPageIndex(pageIndex + 1); setLeftDisabled(false); }; const getPatientCardTable = () => { + /* + * getPatientCardTable function + * reander each PatientCardTable for each patient + */ const patientcards = Object.keys(bundle).length !== 0 && bundle.patients @@ -34,6 +46,9 @@ const PatientTable = ({ bundle, updateNextPatients }: Props) => { }; const getPreviousPage = async () => { + /* + * Show previous patient table page + */ if (pageIndex > 0) { setPageIndex(pageIndex - 1); } diff --git a/src/services/api.tsx b/src/services/api.tsx index 7798217..1019182 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -11,7 +11,7 @@ const makeRequest = async ( * Function makeRequest * resource: the resource name we want to fetch * parameters (optional) - * count: number of maximum resource we want to fetch + * count: number of maximum resource we want to fetch (todo:make this func able to request for all patients) */ const url: string = `${URL_SERVER}${resource}?${parameters || ""}`; @@ -330,7 +330,7 @@ export const getPatientPerCondition = async (conditionId: string) => { "Condition", true, `&code=${conditionId}`, - 1000 + 10000 ); if (!response) return; From cb574acaea528d5a75282ff40476c19088ddf840 Mon Sep 17 00:00:00 2001 From: "nicolas.riss22" Date: Wed, 18 Mar 2020 13:32:58 +0100 Subject: [PATCH 26/27] applying Jason comments for PR #22 --- src/components/patients/index.tsx | 13 +++++++------ src/services/api.tsx | 27 +++++++++++---------------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index 4cc39a6..0b576f4 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -2,10 +2,13 @@ import React from "react"; import Header from "components/header"; import PatientTable from "components/patients/patientTable"; import SearchTool from "components/patients/searchTool"; -import { getPatients, getPatientsPerQuery } from "services/api"; +import { + getPatients, + getPatientsPerQuery, + requestNextPatients +} from "services/api"; import { PatientBundle } from "types"; import { Card, Elevation } from "@blueprintjs/core"; -import { requestNextPatients } from "services/api"; import "./style.css"; @@ -18,10 +21,8 @@ const Patients = () => { const [patientBundle, setPatientBundle] = React.useState({} as PatientBundle); const getNextPatients = async () => { - const patBundle = (await requestNextPatients( - patientBundle - )) as PatientBundle; - setPatientBundle(patBundle); + const patBundle = await requestNextPatients(patientBundle); + if (patBundle) setPatientBundle(patBundle); }; const handleSearch = async (searchName: String, searchParams: any) => { const bundle: PatientBundle = await getPatientsPerQuery( diff --git a/src/services/api.tsx b/src/services/api.tsx index 1019182..c6d50c7 100644 --- a/src/services/api.tsx +++ b/src/services/api.tsx @@ -1,5 +1,6 @@ import { URL_SERVER, PATIENT_REQUESTED } from "../constants"; import { Patient, Bundle, PatientBundle } from "types"; +import { join } from "path"; const makeRequest = async ( resource: string, @@ -130,23 +131,20 @@ export const getPatientsPerQuery = async ( * TODO : for now, this function is limited by the server response to 500 resources. Must find a way to improve it (searching computation done by the server ideally ?) */ let bundles: Bundle[] = []; - let params: string; + let params: string = ""; if (searchName) { - params = ""; - - searchName.split(" ").map((x: string) => { - params += "&name=" + x; - return params; - }); + params += searchName + .split(" ") + .map((x: string) => `&name=${x}`) + .join(); let bundlePatient = await makeRequest("Patient", false, params, 10000); let entries = bundlePatient.entry; - params = ""; - searchName.split(" ").map((x: string) => { - params += "&identifier=" + x; - return params; - }); + params = searchName + .split(" ") + .map((x: string) => `&identifier=${x}`) + .join(); bundlePatient = await makeRequest("Patient", false, params, 10000); @@ -160,7 +158,6 @@ export const getPatientsPerQuery = async ( searchParams.map((x: any) => { switch (x.label) { case "Age": - params = ""; const correspondingDate: Date = new Date(); correspondingDate.setFullYear( correspondingDate.getFullYear() - parseInt(x.text) @@ -254,9 +251,7 @@ export const addPatientsToBundle = (bundle: Bundle) => { } if (entry.resource.identifier) { patient.identifier = entry.resource.identifier - .map((e: any) => { - return e.value; - }) + .map((e: any) => e.value) .join(", "); } return patient; From 548e377846faee0743c6ba6eab39ab9e2c2d5118 Mon Sep 17 00:00:00 2001 From: "nicolas.riss22" Date: Thu, 19 Mar 2020 09:25:57 +0100 Subject: [PATCH 27/27] adding toaster and renaming api.tsx to api.ts --- src/components/patients/index.tsx | 1 - src/services/{api.tsx => api.ts} | 23 +++++++++++++---------- src/services/toaster.ts | 7 +++++++ 3 files changed, 20 insertions(+), 11 deletions(-) rename src/services/{api.tsx => api.ts} (94%) create mode 100644 src/services/toaster.ts diff --git a/src/components/patients/index.tsx b/src/components/patients/index.tsx index 0b576f4..b43efb3 100644 --- a/src/components/patients/index.tsx +++ b/src/components/patients/index.tsx @@ -19,7 +19,6 @@ interface Props { const Patients = () => { const [patientBundle, setPatientBundle] = React.useState({} as PatientBundle); - const getNextPatients = async () => { const patBundle = await requestNextPatients(patientBundle); if (patBundle) setPatientBundle(patBundle); diff --git a/src/services/api.tsx b/src/services/api.ts similarity index 94% rename from src/services/api.tsx rename to src/services/api.ts index c6d50c7..2d1b33e 100644 --- a/src/services/api.tsx +++ b/src/services/api.ts @@ -1,6 +1,6 @@ import { URL_SERVER, PATIENT_REQUESTED } from "../constants"; import { Patient, Bundle, PatientBundle } from "types"; -import { join } from "path"; +import { AppToaster } from "services/toaster"; const makeRequest = async ( resource: string, @@ -131,16 +131,19 @@ export const getPatientsPerQuery = async ( * TODO : for now, this function is limited by the server response to 500 resources. Must find a way to improve it (searching computation done by the server ideally ?) */ let bundles: Bundle[] = []; - let params: string = ""; + let params = ""; if (searchName) { - params += searchName + // First API call : searching for names + params = searchName .split(" ") .map((x: string) => `&name=${x}`) .join(); let bundlePatient = await makeRequest("Patient", false, params, 10000); let entries = bundlePatient.entry; + + // Second API call : searching for identifier params = searchName .split(" ") .map((x: string) => `&identifier=${x}`) @@ -172,15 +175,15 @@ export const getPatientsPerQuery = async ( switch (x.symbol) { case "=": - params += `&birthdate=lt${yyyy}-${mm}-${dd}`; + params = `&birthdate=lt${yyyy}-${mm}-${dd}`; params += `&birthdate=gt${yyyy - 1}-${mm}-${dd}`; - return getPatients(params, 1000); + return makeRequest("Patient", false, params, 10000); case ">": - params += `&birthdate=lt${yyyy}-${mm}-${dd}`; - return getPatients(params, 1000); + params = `&birthdate=lt${yyyy}-${mm}-${dd}`; + return makeRequest("Patient", false, params, 10000); case "<": - params += `&birthdate=gt${yyyy}-${mm}-${dd}`; - return getPatients(params, 1000); + params = `&birthdate=gt${yyyy}-${mm}-${dd}`; + return makeRequest("Patient", false, params, 10000); } return {}; case "Diabète": @@ -214,7 +217,7 @@ export const requestNextPatients = async (bundle: PatientBundle) => { * Return the same bundle with more patients, fetched from the nextLink attribute. */ if (!bundle.nextLink) { - console.info("no link available"); + AppToaster.show({ message: "No link available." }); return; } else { let newBundle: PatientBundle = (await makeRequestByURL( diff --git a/src/services/toaster.ts b/src/services/toaster.ts new file mode 100644 index 0000000..397c065 --- /dev/null +++ b/src/services/toaster.ts @@ -0,0 +1,7 @@ +import { Position, Toaster } from "@blueprintjs/core"; + +/** Singleton toaster instance. Create separate instances for different options. */ +export const AppToaster = Toaster.create({ + className: "recipe-toaster", + position: Position.TOP +});