diff --git a/README.md b/README.md index 002afc979bc..7f2c3e926bd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=coronasafe_care_fe&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=coronasafe_care_fe) ![Code scanning - action](https://github.com/coronasafe/care_fe/workflows/Code%20scanning%20-%20action/badge.svg) ![OSSAR](https://github.com/coronasafe/care_fe/workflows/OSSAR/badge.svg) -[![Cypress Tests](https://github.com/coronasafe/care_fe/actions/workflows/cypress.yaml/badge.svg)](https://github.com/coronasafe/care_fe/actions/workflows/cypress.yaml) +[![Cypress Tests](https://img.shields.io/endpoint?url=https://cloud.cypress.io/badge/simple/wf7d2m/develop&style=flat&logo=cypress)](https://cloud.cypress.io/projects/wf7d2m/runs) ![Staging Release](https://github.com/coronasafe/care_fe/workflows/CARE%20Develop%20Registry/badge.svg) ![Production Release](https://github.com/coronasafe/care_fe/workflows/Production%20Release/badge.svg) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/200482ab117e4b5397ff3f5ae5719aa2)](https://www.codacy.com/gh/coronasafe/care_fe?utm_source=github.com&utm_medium=referral&utm_content=coronasafe/care_fe&utm_campaign=Badge_Grade) @@ -50,7 +50,7 @@ Once the development server has started, open [localhost:4000](http://localhost: Authenticate to staging API with any of the following credentials ```yaml -- username: devdistrictadmin +- username: dev-districtadmin password: Coronasafe@123 role: District Admin diff --git a/cypress/e2e/facility_spec/facility.cy.ts b/cypress/e2e/facility_spec/facility.cy.ts deleted file mode 100644 index da06c65ed53..00000000000 --- a/cypress/e2e/facility_spec/facility.cy.ts +++ /dev/null @@ -1,82 +0,0 @@ -// FacilityCreation -import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress"; -import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import LoginPage from "../../pageobject/Login/LoginPage"; - -describe("Facility Creation", () => { - let facilityUrl: string; - const facilityPage = new FacilityPage(); - const loginPage = new LoginPage(); - - before(() => { - loginPage.loginAsDisctrictAdmin(); - cy.saveLocalStorage(); - }); - - beforeEach(() => { - cy.viewport(1280, 720); - cy.restoreLocalStorage(); - cy.awaitUrl("/facility"); - }); - - it("Create a new facility", () => { - facilityPage.visitCreateFacilityPage(); - facilityPage.fillFacilityName("cypress facility"); - facilityPage.fillPincode("682001"); - facilityPage.selectState("Kerala"); - facilityPage.selectDistrict("Ernakulam"); - facilityPage.selectLocalBody("Aluva"); - facilityPage.selectWard("4"); - facilityPage.fillAddress("Cypress Address"); - facilityPage.fillPhoneNumber("9898469865"); - facilityPage.submitForm(); - - facilityPage.selectBedType("Oxygen beds"); - facilityPage.fillTotalCapacity("10"); - facilityPage.fillCurrentlyOccupied("5"); - facilityPage.saveAndExitBedCapacityForm(); - - facilityPage.selectAreaOfSpecialization("General Medicine"); - facilityPage.fillDoctorCount("5"); - facilityPage.saveAndExitDoctorForm(); - facilityPage.verifyfacilitynewurl(); - cy.url().then((newUrl) => { - facilityUrl = newUrl; - }); - }); - - it("Update the existing facility", () => { - facilityPage.visitUpdateFacilityPage(facilityUrl); - facilityPage.clickManageFacilityDropdown(); - facilityPage.clickUpdateFacilityOption(); - facilityPage.clickUpdateFacilityType("Request Approving Center"); - facilityPage.fillFacilityName("cypress facility updated"); - facilityPage.fillAddress("Cypress Facility Updated Address"); - facilityPage.fillOxygenCapacity("100"); - facilityPage.fillExpectedOxygenRequirement("80"); - facilityPage.selectLocation("Kochi, Kerala"); - facilityPage.submitForm(); - - cy.url().should("not.include", "/update"); - }); - - it("Configure the existing facility", () => { - facilityPage.visitUpdateFacilityPage(facilityUrl); - facilityPage.clickManageFacilityDropdown(); - facilityPage.clickConfigureFacilityOption(); - facilityPage.fillMiddleWareAddress("dev_middleware.coronasafe.live"); - facilityPage.clickupdateMiddleWare(); - facilityPage.verifySuccessNotification("Facility updated successfully"); - }); - - it("Delete a facility", () => { - facilityPage.visitUpdateFacilityPage(facilityUrl); - facilityPage.clickManageFacilityDropdown(); - facilityPage.clickDeleteFacilityOption(); - facilityPage.confirmDeleteFacility(); - }); - - afterEach(() => { - cy.saveLocalStorage(); - }); -}); diff --git a/cypress/e2e/facility_spec/facility_creation.cy.ts b/cypress/e2e/facility_spec/facility_creation.cy.ts new file mode 100644 index 00000000000..cc2d1b7f921 --- /dev/null +++ b/cypress/e2e/facility_spec/facility_creation.cy.ts @@ -0,0 +1,304 @@ +// FacilityCreation +import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress"; +import FacilityPage from "../../pageobject/Facility/FacilityCreation"; +import LoginPage from "../../pageobject/Login/LoginPage"; +import FacilityHome from "../../pageobject/Facility/FacilityHome"; +import ManageUserPage from "../../pageobject/Users/ManageUserPage"; +import { UserCreationPage } from "../../pageobject/Users/UserCreation"; + +describe("Facility Creation", () => { + let facilityUrl1: string; + const facilityPage = new FacilityPage(); + const loginPage = new LoginPage(); + const facilityHome = new FacilityHome(); + const manageUserPage = new ManageUserPage(); + const userCreationPage = new UserCreationPage(); + const facilityFeature = [ + "CT Scan", + "X-Ray", + "Maternity Care", + "Neonatal Care", + "Operation Theater", + "Blood Bank", + ]; + const bedCapacity = "10"; + const bedOccupancy = "5"; + const oxygenCapacity = "100"; + const oxygenExpected = "80"; + const totalCapacity = "20"; + const totalOccupancy = "10"; + const doctorCapacity = "5"; + const totalDoctor = "10"; + const facilityName = "cypress facility"; + const facilityName2 = "Dummy Facility 1"; + const facilityAddress = "cypress address"; + const facilityUpdateAddress = "cypress updated address"; + const facilityNumber = "9898469865"; + const triageDate = "02122023"; + const initialTriageValue = "60"; + const modifiedTriageValue = "50"; + const facilityErrorMessage = [ + "Required", + "Invalid Pincode", + "Required", + "Required", + "Required", + "Required", + "Required", + "Required", + "Invalid Phone Number", + ]; + const bedErrorMessage = [ + "Field is required", + "Total capacity cannot be 0", + "Field is required", + ]; + const doctorErrorMessage = ["Field is required", "Field is required"]; + const triageErrorMessage = ["Field is required"]; + + before(() => { + loginPage.loginAsDisctrictAdmin(); + cy.saveLocalStorage(); + }); + + beforeEach(() => { + cy.viewport(1280, 720); + cy.restoreLocalStorage(); + cy.awaitUrl("/facility"); + }); + + it("Verify Facility Triage Function", () => { + // mandatory field error throw + manageUserPage.typeFacilitySearch(facilityName2); + facilityPage.verifyFacilityBadgeContent(facilityName2); + manageUserPage.assertFacilityInCard(facilityName2); + facilityHome.verifyURLContains(facilityName2); + facilityPage.visitAlreadyCreatedFacility(); + facilityPage.scrollToFacilityTriage(); + facilityPage.clickAddFacilityTriage(); + manageUserPage.clickSubmit(); + userCreationPage.verifyErrorMessages(triageErrorMessage); + // create a entry and verify reflection + facilityPage.fillEntryDate(triageDate); + facilityPage.fillTriageEntryFields( + initialTriageValue, + initialTriageValue, + initialTriageValue, + initialTriageValue, + initialTriageValue + ); + manageUserPage.clickSubmit(); + // edit the entry and verify reflection + facilityPage.scrollToFacilityTriage(); + facilityPage.verifyTriageTableContains(initialTriageValue); + facilityPage.clickEditButton(); + facilityPage.fillTriageEntryFields( + modifiedTriageValue, + modifiedTriageValue, + modifiedTriageValue, + modifiedTriageValue, + modifiedTriageValue + ); + manageUserPage.clickSubmit(); + facilityPage.scrollToFacilityTriage(); + facilityPage.verifyTriageTableContains(modifiedTriageValue); + // validate error of filling data on same date already data exist and verify reflection + facilityPage.scrollToFacilityTriage(); + facilityPage.clickAddFacilityTriage(); + facilityPage.fillEntryDate(triageDate); + facilityPage.clickButtonsMultipleTimes("button#submit"); + }); + + it("Create a new facility with multiple bed and doctor capacity", () => { + // create facility with multiple capacity and verify form error message for facility form + facilityPage.visitCreateFacilityPage(); + facilityPage.submitForm(); + userCreationPage.verifyErrorMessages(facilityErrorMessage); + facilityPage.fillFacilityName(facilityName); + facilityPage.clickfacilityfeatureoption(); + facilityFeature.forEach((featureText) => { + cy.get("[role='option']").contains(featureText).click(); + }); + facilityPage.fillPincode("682001"); + facilityPage.selectLocalBody("Aluva"); + facilityPage.selectWard("4"); + facilityPage.fillAddress(facilityAddress); + facilityPage.fillPhoneNumber(facilityNumber); + facilityPage.fillOxygenCapacity(oxygenCapacity); + facilityPage.fillExpectedOxygenRequirement(oxygenExpected); + facilityPage.fillBTypeCylinderCapacity(oxygenCapacity); + facilityPage.fillExpectedBTypeCylinderRequirement(oxygenExpected); + facilityPage.fillCTypeCylinderCapacity(oxygenCapacity); + facilityPage.fillExpectedCTypeCylinderRequirement(oxygenExpected); + facilityPage.fillDTypeCylinderCapacity(oxygenCapacity); + facilityPage.fillExpectedDTypeCylinderRequirement(oxygenExpected); + facilityPage.selectLocation("Kochi, Kerala"); + facilityPage.submitForm(); + // create multiple bed capacity and verify card reflection + facilityPage.selectBedType("Oxygen beds"); + facilityPage.fillTotalCapacity(bedCapacity); + facilityPage.fillCurrentlyOccupied(bedOccupancy); + facilityPage.clickbedcapcityaddmore(); + facilityPage.selectBedType("Ordinary Bed"); + facilityPage.fillTotalCapacity(bedCapacity); + facilityPage.fillCurrentlyOccupied(bedOccupancy); + facilityPage.clickbedcapcityaddmore(); + facilityPage.getTotalBedCapacity().contains(totalCapacity); + facilityPage.getTotalBedCapacity().contains(totalOccupancy); + facilityPage.clickcancelbutton(); + // create multiple bed capacity and verify card reflection + facilityPage.selectAreaOfSpecialization("General Medicine"); + facilityPage.fillDoctorCount(doctorCapacity); + facilityPage.clickdoctorcapacityaddmore(); + facilityPage.selectAreaOfSpecialization("Pulmonology"); + facilityPage.fillDoctorCount(doctorCapacity); + facilityPage.clickdoctorcapacityaddmore(); + facilityPage.getTotalDoctorCapacity().contains(doctorCapacity); + facilityPage.clickcancelbutton(); + facilityPage.verifyfacilitynewurl(); + // verify the facility card + facilityPage.getFacilityName().contains(facilityName).should("be.visible"); + facilityPage + .getAddressDetailsView() + .contains(facilityAddress) + .should("be.visible"); + facilityPage + .getPhoneNumberView() + .contains(facilityNumber) + .should("be.visible"); + facilityPage + .getFacilityAvailableFeatures() + .invoke("text") + .then((text) => { + facilityFeature.forEach((feature) => { + expect(text).to.contain(feature); + }); + }); + facilityPage.getFacilityOxygenInfo().scrollIntoView(); + facilityPage + .getFacilityOxygenInfo() + .contains(oxygenCapacity) + .should("be.visible"); + facilityPage.getFacilityTotalBedCapacity().scrollIntoView(); + facilityPage.getFacilityTotalBedCapacity().contains(totalCapacity); + facilityPage.getFacilityTotalBedCapacity().contains(totalOccupancy); + facilityPage.getFacilityTotalDoctorCapacity().scrollIntoView(); + facilityPage.getFacilityTotalDoctorCapacity().contains(totalDoctor); + }); + + it("Create a new facility with single bed and doctor capacity", () => { + facilityPage.visitCreateFacilityPage(); + facilityPage.fillFacilityName(facilityName); + facilityPage.fillPincode("682001"); + facilityPage.selectLocalBody("Aluva"); + facilityPage.selectWard("4"); + facilityPage.fillAddress(facilityAddress); + facilityPage.fillPhoneNumber(facilityNumber); + facilityPage.submitForm(); + // add the bed capacity + facilityPage.selectBedType("Oxygen beds"); + facilityPage.fillTotalCapacity(oxygenCapacity); + facilityPage.fillCurrentlyOccupied(oxygenExpected); + facilityPage.saveAndExitBedCapacityForm(); + // add the doctor capacity + facilityPage.selectAreaOfSpecialization("General Medicine"); + facilityPage.fillDoctorCount(doctorCapacity); + facilityPage.saveAndExitDoctorForm(); + facilityPage.verifyfacilitynewurl(); + // verify the created facility details + facilityPage.getFacilityName().contains(facilityName).should("be.visible"); + facilityPage + .getAddressDetailsView() + .contains(facilityAddress) + .should("be.visible"); + facilityPage + .getPhoneNumberView() + .contains(facilityNumber) + .should("be.visible"); + // verify the facility homepage + cy.visit("/facility"); + manageUserPage.typeFacilitySearch(facilityName); + facilityPage.verifyFacilityBadgeContent(facilityName); + manageUserPage.assertFacilityInCard(facilityName); + facilityHome.verifyURLContains(facilityName); + }); + + it("Create a new facility with no bed and doctor capacity", () => { + facilityPage.visitCreateFacilityPage(); + facilityPage.fillFacilityName(facilityName); + facilityPage.fillPincode("682001"); + facilityPage.selectLocalBody("Aluva"); + facilityPage.selectWard("4"); + facilityPage.fillAddress(facilityAddress); + facilityPage.fillPhoneNumber(facilityNumber); + facilityPage.submitForm(); + // add no bed capacity and verify form error message + facilityPage.isVisibleselectBedType(); + facilityPage.saveAndExitBedCapacityForm(); + userCreationPage.verifyErrorMessages(bedErrorMessage); + facilityPage.clickcancelbutton(); + // add no doctor capacity and verify form error message + facilityPage.isVisibleAreaOfSpecialization(); + facilityPage.clickdoctorcapacityaddmore(); + userCreationPage.verifyErrorMessages(doctorErrorMessage); + facilityPage.clickcancelbutton(); + cy.url().then((newUrl) => { + facilityUrl1 = newUrl; + }); + // verify the created facility details + facilityPage.getFacilityName().contains(facilityName).should("be.visible"); + facilityPage + .getAddressDetailsView() + .contains(facilityAddress) + .should("be.visible"); + facilityPage + .getPhoneNumberView() + .contains(facilityNumber) + .should("be.visible"); + }); + + it("Update the existing facility", () => { + // update a existing dummy data facility + facilityPage.visitUpdateFacilityPage(facilityUrl1); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickUpdateFacilityOption(); + facilityPage.clickUpdateFacilityType("Govt Hospital"); + facilityPage.fillAddress(facilityUpdateAddress); + facilityPage.fillOxygenCapacity(oxygenCapacity); + facilityPage.fillExpectedOxygenRequirement(oxygenExpected); + facilityPage.selectLocation("Kochi, Kerala"); + facilityPage.submitForm(); + cy.url().should("not.include", "/update"); + // verify the updated data + facilityPage.getFacilityOxygenInfo().scrollIntoView(); + facilityPage + .getFacilityOxygenInfo() + .contains(oxygenCapacity) + .should("be.visible"); + facilityPage.getAddressDetailsView().scrollIntoView(); + facilityPage + .getAddressDetailsView() + .contains(facilityUpdateAddress) + .should("be.visible"); + }); + + it("Configure the existing facility", () => { + facilityPage.visitUpdateFacilityPage(facilityUrl1); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickConfigureFacilityOption(); + facilityPage.fillMiddleWareAddress("dev_middleware.coronasafe.live"); + facilityPage.clickupdateMiddleWare(); + facilityPage.verifySuccessNotification("Facility updated successfully"); + }); + + it("Delete a facility", () => { + facilityPage.visitUpdateFacilityPage(facilityUrl1); + facilityPage.clickManageFacilityDropdown(); + facilityPage.clickDeleteFacilityOption(); + facilityPage.confirmDeleteFacility(); + }); + + afterEach(() => { + cy.saveLocalStorage(); + }); +}); diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index 8c20b808c98..c13f5beac8e 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -70,6 +70,10 @@ class FacilityPage { cy.get("[role='option']").contains(bedType).click(); } + isVisibleselectBedType() { + cy.get("div#bed-type button").should("be.visible"); + } + fillTotalCapacity(capacity: string) { cy.get("input#total-capacity").click().type(capacity); } @@ -87,6 +91,10 @@ class FacilityPage { cy.get("[role='option']").contains(area).click(); } + isVisibleAreaOfSpecialization() { + cy.get("div#area-of-specialization button").should("be.visible"); + } + fillDoctorCount(count: string) { cy.get("input#count").click().type(count); } @@ -99,6 +107,33 @@ class FacilityPage { cy.get("#expected_oxygen_requirement").click().clear().type(requirement); } + fillBTypeCylinderCapacity(capacity: string) { + cy.get("#type_b_cylinders").click().clear().type(capacity); + } + + fillExpectedBTypeCylinderRequirement(requirement: string) { + cy.get("#expected_type_b_cylinders").focus().clear(); + cy.get("#expected_type_b_cylinders").focus().type(requirement); + } + + fillCTypeCylinderCapacity(capacity: string) { + cy.get("#type_c_cylinders").click().clear().type(capacity); + } + + fillExpectedCTypeCylinderRequirement(requirement: string) { + cy.get("#expected_type_c_cylinders").focus().clear(); + cy.get("#expected_type_c_cylinders").focus().type(requirement); + } + + fillDTypeCylinderCapacity(capacity: string) { + cy.get("#type_d_cylinders").click().clear().type(capacity); + } + + fillExpectedDTypeCylinderRequirement(requirement: string) { + cy.get("#expected_type_d_cylinders").focus().clear(); + cy.get("#expected_type_d_cylinders").focus().type(requirement); + } + saveAndExitDoctorForm() { cy.intercept("GET", "**/api/v1/facility/**").as("createFacilities"); cy.get("button#save-and-exit").click(); @@ -138,6 +173,42 @@ class FacilityPage { cy.get("#inventory-management").click(); } + getTotalBedCapacity() { + return cy.get("#total-bed-capacity"); + } + + getFacilityTotalBedCapacity() { + return cy.get("#facility-bed-capacity-details"); + } + + getFacilityTotalDoctorCapacity() { + return cy.get("#facility-doctor-capacity-details"); + } + + getTotalDoctorCapacity() { + return cy.get("#total-doctor-capacity"); + } + + getFacilityName() { + return cy.get("#facility-name"); + } + + getAddressDetailsView() { + return cy.get("#address-details-view"); + } + + getPhoneNumberView() { + return cy.get("#phone-number-view"); + } + + getFacilityAvailableFeatures() { + return cy.get("#facility-available-features"); + } + + getFacilityOxygenInfo() { + return cy.get("#facility-oxygen-info"); + } + clickResourceRequestOption() { cy.get("#resource-request").contains("Resource Request").click(); } @@ -146,6 +217,69 @@ class FacilityPage { cy.get("#delete-facility").contains("Delete Facility").click(); } + scrollToFacilityTriage() { + cy.get("#add-facility-triage").scrollIntoView(); + } + + fillTriageEntryFields( + visited, + homeQuarantine, + isolation, + referred, + confirmedPositive + ) { + cy.get("#num_patients_visited").clear().click().type(visited); + cy.get("#num_patients_home_quarantine") + .clear() + .click() + .type(homeQuarantine); + cy.get("#num_patients_isolation").clear().click().type(isolation); + cy.get("#num_patient_referred").clear().click().type(referred); + cy.get("#num_patient_confirmed_positive") + .clear() + .click() + .type(confirmedPositive); + } + + fillEntryDate(date) { + cy.get("#entry_date").click(); + cy.get("#date-input").click().type(date); + } + + clickEditButton() { + cy.get("#edit-button").click(); + } + + clickButtonsMultipleTimes(selector) { + cy.get(selector).each(($button) => { + cy.wrap($button).click(); + }); + } + + verifyTriageTableContains(value) { + cy.get("#triage-table").contains(value); + } + + clickAddFacilityTriage() { + cy.get("#add-facility-triage").click(); + } + + clickfacilityfeatureoption() { + cy.get("#features").click(); + } + + clickbedcapcityaddmore() { + cy.get("#bed-capacity-save").click(); + } + + clickdoctorcapacityaddmore() { + cy.get("#doctor-save").click(); + } + + clickcancelbutton() { + cy.get("#cancel").click(); + } + verifyfacilitynewurl() { cy.url().should("match", /facility\/[a-z\d-]+/); } diff --git a/public/External-Results-Template.csv b/public/External-Results-Template.csv new file mode 100644 index 00000000000..3ab2afc65ba --- /dev/null +++ b/public/External-Results-Template.csv @@ -0,0 +1,3 @@ +District,srf id,name,age,age in,gender,mobile number,address,ward,local body,local body type,source,Sample Collection Date,result date,test type,lab name,sample type,patient status,Is Repeat,patient category,result +Ernakulam,00/EKM/0000,Bodhi CSN,24,years,m,8888888888,"CSN HQ +Kochi, Kerala ",7,Poothrikka,grama panchayath,Secondary contact aparna,2020-10-14,2020-10-14,Antigen,Karothukuzhi Laboratory,Ag-SD_Biosensor_Standard_Q_COVID-19_Ag_detection_kit,Asymptomatic,NO,Cat 17: All individuals who wish to get themselves tested,Negative \ No newline at end of file diff --git a/public/config.json b/public/config.json index 444362cfb20..74e509fee59 100644 --- a/public/config.json +++ b/public/config.json @@ -21,6 +21,6 @@ "kasp_string": "KASP", "kasp_full_string": "Karunya Arogya Suraksha Padhathi", "sample_format_asset_import": "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=11JaEhNHdyCHth4YQs_44YaRlP77Rrqe81VSEfg1glko&exportFormat=xlsx", - "sample_format_external_result_import": "https://docs.google.com/spreadsheets/d/17VfgryA6OYSYgtQZeXU9mp7kNvLySeEawvnLBO_1nuE/export?format=csv&id=17VfgryA6OYSYgtQZeXU9mp7kNvLySeEawvnLBO_1nuE", + "sample_format_external_result_import": "/External-Results-Template.csv", "enable_abdm": true } \ No newline at end of file diff --git a/src/CAREUI/display/RecordMeta.tsx b/src/CAREUI/display/RecordMeta.tsx index 944ddf27c8f..818553d9207 100644 --- a/src/CAREUI/display/RecordMeta.tsx +++ b/src/CAREUI/display/RecordMeta.tsx @@ -11,6 +11,7 @@ interface Props { time?: string; prefix?: ReactNode; className?: string; + inlineClassName?: string; user?: { first_name: string; last_name: string; @@ -23,7 +24,14 @@ interface Props { * A generic component to display relative time along with a tooltip and a user * if provided. */ -const RecordMeta = ({ time, user, prefix, className, inlineUser }: Props) => { +const RecordMeta = ({ + time, + user, + prefix, + className, + inlineClassName, + inlineUser, +}: Props) => { const isOnline = user && isUserOnline(user); let child = ( @@ -47,11 +55,11 @@ const RecordMeta = ({ time, user, prefix, className, inlineUser }: Props) => { if (prefix || user) { child = ( -
+
{prefix} {child} {user && inlineUser && by} - {user && } + {user && !inlineUser && } {user && inlineUser && ( {formatName(user)} )} diff --git a/src/Components/Common/BedSelect.tsx b/src/Components/Common/BedSelect.tsx index d903d3b62e0..94caded12a3 100644 --- a/src/Components/Common/BedSelect.tsx +++ b/src/Components/Common/BedSelect.tsx @@ -74,7 +74,7 @@ export const BedSelect = (props: BedSelectProps) => { optionLabel={(option: any) => { if (Object.keys(option).length === 0) return ""; return ( - `${option.name} ${option?.location_object?.name || t("unknown")}` || + `${option.name}, ${option?.location_object?.name || t("unknown")}` || option?.location_object?.name ); }} diff --git a/src/Components/Common/SkillSelect.tsx b/src/Components/Common/SkillSelect.tsx index 3257971d03c..941c29790d1 100644 --- a/src/Components/Common/SkillSelect.tsx +++ b/src/Components/Common/SkillSelect.tsx @@ -1,8 +1,8 @@ import { useCallback } from "react"; import { useDispatch } from "react-redux"; -import { getAllSkills, getUserListSkills } from "../../Redux/actions"; +import { getAllSkills } from "../../Redux/actions"; import AutoCompleteAsync from "../Form/AutoCompleteAsync"; -import { SkillObjectModel } from "../Users/models"; +import { SkillModel, SkillObjectModel } from "../Users/models"; interface SkillSelectProps { id?: string; @@ -17,6 +17,7 @@ interface SkillSelectProps { selected: SkillObjectModel | SkillObjectModel[] | null; setSelected: (selected: SkillObjectModel) => void; username?: string; + userSkills?: SkillModel[]; } export const SkillSelect = (props: SkillSelectProps) => { @@ -32,7 +33,8 @@ export const SkillSelect = (props: SkillSelectProps) => { disabled = false, className = "", errors = "", - username, + //username, + userSkills, } = props; const dispatchAction: any = useDispatch(); @@ -47,21 +49,16 @@ export const SkillSelect = (props: SkillSelectProps) => { }; const res = await dispatchAction(getAllSkills(params)); - - const linkedSkills = await dispatchAction( - getUserListSkills({ username: username }) - ); - - const skillsList = linkedSkills?.data?.results; const skillsID: string[] = []; - skillsList.map((skill: any) => skillsID.push(skill.skill_object.id)); + userSkills?.map((skill: SkillModel) => + skillsID.push(skill.skill_object.id) + ); const skills = res?.data?.results.filter( (skill: any) => !skillsID.includes(skill.id) ); - return skills; }, - [dispatchAction, searchAll, showAll] + [dispatchAction, searchAll, userSkills, showAll] ); return ( diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 20a2cec3341..5a9262b2990 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -112,6 +112,8 @@ export default function ExternalResultUpload() { {" "} {t("sample_format")} diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx index bb584ae93e6..0d8a70781da 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -12,13 +12,12 @@ import PrescriptionsTable from "../../Medicine/PrescriptionsTable"; import Chip from "../../../CAREUI/display/Chip"; import { formatAge, formatDate, formatDateTime } from "../../../Utils/utils"; import ReadMore from "../../Common/components/Readmore"; -import { DailyRoundsList } from "../Consultations/DailyRoundsList"; +import DailyRoundsList from "../Consultations/DailyRoundsList"; const PageTitle = lazy(() => import("../../Common/PageTitle")); export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { const dispatch: any = useDispatch(); - const [showAutomatedRounds, setShowAutomatedRounds] = useState(true); const [hl7SocketUrl, setHL7SocketUrl] = useState(); const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState(); const [monitorBedData, setMonitorBedData] = useState(); @@ -674,31 +673,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
-
- -
- setShowAutomatedRounds((s) => !s)} - /> - -
-
- +
diff --git a/src/Components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx b/src/Components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx index 63b5087cff8..ff738f4acb6 100644 --- a/src/Components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx +++ b/src/Components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx @@ -4,10 +4,11 @@ import CareIcon from "../../../../CAREUI/icons/CareIcon"; import ButtonV2 from "../../../Common/components/ButtonV2"; import { DailyRoundsModel } from "../../../Patient/models"; import LogUpdateCardAttribute from "./LogUpdateCardAttribute"; +import { ConsultationModel } from "../../models"; interface Props { round: DailyRoundsModel; - consultationData: any; + consultationData: ConsultationModel; onViewDetails: () => void; onUpdateLog?: () => void; } diff --git a/src/Components/Facility/Consultations/DailyRoundsFilter.tsx b/src/Components/Facility/Consultations/DailyRoundsFilter.tsx new file mode 100644 index 00000000000..62b8d63e824 --- /dev/null +++ b/src/Components/Facility/Consultations/DailyRoundsFilter.tsx @@ -0,0 +1,115 @@ +import { Popover, Transition } from "@headlessui/react"; +import ButtonV2 from "../../Common/components/ButtonV2"; +import { Fragment } from "react"; +import { SelectFormField } from "../../Form/FormFields/SelectFormField"; +import TextFormField from "../../Form/FormFields/TextFormField"; +import CareIcon from "../../../CAREUI/icons/CareIcon"; +import dayjs from "dayjs"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { DailyRoundTypes, DailyRoundsModel } from "../../Patient/models"; +import { FieldChangeEvent } from "../../Form/FormFields/Utils"; + +type FilterState = { + rounds_type?: DailyRoundsModel["rounds_type"]; + taken_at_after?: string; + taken_at_before?: string; +}; + +interface Props { + onApply: (filter: FilterState) => void; +} + +export default function DailyRoundsFilter(props: Props) { + const { t } = useTranslation(); + const [filter, setFilter] = useState({}); + + const field = (name: keyof FilterState) => ({ + name, + value: filter[name], + onChange: (e: FieldChangeEvent) => + setFilter({ ...filter, [e.name]: e.value }), + labelClassName: "text-sm", + errorClassName: "hidden", + }); + + return ( +
+ + + + + {t("filter")} + + + + +
+
+
+ + {t("filter_by")} + +
+
+
+ t(o)} + optionValue={(o) => o} + /> + + + + + { + setFilter({}); + props.onApply({}); + }} + border + className="w-full" + > + {t("clear")} + + + + props.onApply(filter)} + border + className="w-full" + > + {t("apply")} + + +
+
+
+
+
+
+ ); +} diff --git a/src/Components/Facility/Consultations/DailyRoundsList.tsx b/src/Components/Facility/Consultations/DailyRoundsList.tsx index ffc70ddf175..2060d8657e7 100644 --- a/src/Components/Facility/Consultations/DailyRoundsList.tsx +++ b/src/Components/Facility/Consultations/DailyRoundsList.tsx @@ -6,88 +6,84 @@ import { useTranslation } from "react-i18next"; import LoadingLogUpdateCard from "./DailyRounds/LoadingCard"; import routes from "../../../Redux/api"; import PaginatedList from "../../../CAREUI/misc/PaginatedList"; +import PageTitle from "../../Common/PageTitle"; +import DailyRoundsFilter from "./DailyRoundsFilter"; +import { ConsultationModel } from "../models"; +import { useSlugs } from "../../../Common/hooks/useSlug"; -export const DailyRoundsList = (props: any) => { +interface Props { + consultation: ConsultationModel; +} + +export default function DailyRoundsList({ consultation }: Props) { + const [facilityId, patientId, consultationId] = useSlugs( + "facility", + "patient", + "consultation" + ); const { t } = useTranslation(); - const { - facilityId, - patientId, - consultationId, - consultationData, - showAutomatedRounds, - } = props; + + const consultationUrl = `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}`; return ( - {(_) => ( -
-
- - - {t("no_consultation_updates")} - - - - <> - {Array.from({ length: 3 }).map((_, i) => ( - - ))} - - - className="flex grow flex-col gap-3"> - {(item, items) => { - if (item.rounds_type === "AUTOMATED") { + {({ refetch }) => ( + <> +
+ + refetch({ query })} /> +
+ +
+
+ + + {t("no_consultation_updates")} + + + + <> + {Array.from({ length: 3 }).map((_, i) => ( + + ))} + + + className="flex grow flex-col gap-3"> + {(item, items) => { + if (item.rounds_type === "AUTOMATED") { + return ( + + ); + } + + const itemUrl = + item.rounds_type === "NORMAL" + ? `${consultationUrl}/daily-rounds/${item.id}` + : `${consultationUrl}/daily_rounds/${item.id}`; + return ( - navigate(itemUrl)} + onUpdateLog={() => navigate(`${itemUrl}/update`)} /> ); - } - return ( - { - if (item.rounds_type === "NORMAL") { - navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${item.id}` - ); - } else { - navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${item.id}` - ); - } - }} - onUpdateLog={() => { - if (item.rounds_type === "NORMAL") { - navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds/${item.id}/update` - ); - } else { - navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${item.id}/update` - ); - } - }} - /> - ); - }} - -
- + }} + +
+ +
-
+ )} ); -}; +} diff --git a/src/Components/Facility/FacilityBedCapacity.tsx b/src/Components/Facility/FacilityBedCapacity.tsx index 33583840643..0c05fb022cb 100644 --- a/src/Components/Facility/FacilityBedCapacity.tsx +++ b/src/Components/Facility/FacilityBedCapacity.tsx @@ -82,7 +82,7 @@ export const FacilityBedCapacity = (props: any) => { } return ( -
+
Bed Capacity
@@ -114,6 +114,6 @@ export const FacilityBedCapacity = (props: any) => { /> )} -
+
); }; diff --git a/src/Components/Facility/FacilityCreate.tsx b/src/Components/Facility/FacilityCreate.tsx index e742caefb82..8a7887ebc25 100644 --- a/src/Components/Facility/FacilityCreate.tsx +++ b/src/Components/Facility/FacilityCreate.tsx @@ -570,7 +570,10 @@ export const FacilityCreate = (props: FacilityProps) => { }); capacityList = ( -
+
{
{t("doctors_list")}
-
{doctorList}
+
+ {doctorList} +
); diff --git a/src/Components/Facility/FacilityDoctorList.tsx b/src/Components/Facility/FacilityDoctorList.tsx index e6bbc7f7f3b..d9a8b0d1a37 100644 --- a/src/Components/Facility/FacilityDoctorList.tsx +++ b/src/Components/Facility/FacilityDoctorList.tsx @@ -83,7 +83,7 @@ export const FacilityDoctorList = (props: any) => { } return ( -
+
Doctors List
@@ -116,6 +116,6 @@ export const FacilityDoctorList = (props: any) => { /> )} -
+
); }; diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx index e971f0f3b9d..3ad295a3df3 100644 --- a/src/Components/Facility/FacilityHome.tsx +++ b/src/Components/Facility/FacilityHome.tsx @@ -198,7 +198,7 @@ export const FacilityHome = (props: any) => { )} {editCoverImageTooltip}
-
+

{facilityData?.name}

{facilityData?.modified_date && ( {
-
+

Address

@@ -222,7 +222,7 @@ export const FacilityHome = (props: any) => {
-
+

Phone Number

@@ -270,7 +270,10 @@ export const FacilityHome = (props: any) => { ) && (

Available features

)} -
+
{facilityData?.features?.map( (feature: number, i: number) => FACILITY_FEATURE_TYPES.some((f) => f.id === feature) && ( @@ -426,7 +429,10 @@ export const FacilityHome = (props: any) => {

Oxygen Information

-
+
{ ); temp.push( {
Corona Triage
navigate(`/facility/${props.facilityId}/triage`)} authorizeFor={props.NonReadOnlyUsers} @@ -60,7 +62,10 @@ export const FacilityHomeTriage = (props: any) => { Add Triage
-
+
= (option: T) => R; type SelectFormFieldProps = FormFieldBaseProps & { placeholder?: React.ReactNode; - options: T[]; + options: readonly T[]; position?: "above" | "below"; optionLabel: OptionCallback; optionSelectedLabel?: OptionCallback; diff --git a/src/Components/Patient/models.tsx b/src/Components/Patient/models.tsx index 5502bfb51f4..ef5a3a01b95 100644 --- a/src/Components/Patient/models.tsx +++ b/src/Components/Patient/models.tsx @@ -269,6 +269,8 @@ export interface DailyRoundsOutput { quantity: number; } +export const DailyRoundTypes = ["NORMAL", "VENTILATOR", "AUTOMATED"] as const; + export interface DailyRoundsModel { ventilator_spo2?: number; spo2?: string; @@ -290,7 +292,7 @@ export interface DailyRoundsModel { medication_given?: Array; additional_symptoms_text?: string; current_health?: string; - id?: any; + id: string; other_symptoms?: string; admitted_to?: string; patient_category?: PatientCategory; @@ -300,7 +302,7 @@ export interface DailyRoundsModel { modified_date?: string; taken_at?: string; consciousness_level?: string; - rounds_type?: "NORMAL" | "VENTILATOR" | "ICU" | "AUTOMATED"; + rounds_type: (typeof DailyRoundTypes)[number]; last_updated_by_telemedicine?: boolean; created_by_telemedicine?: boolean; created_by?: { @@ -315,6 +317,7 @@ export interface DailyRoundsModel { }; bed?: string; } + export interface FacilityNameModel { id?: string; name?: string; diff --git a/src/Components/Shifting/ShiftDetails.tsx b/src/Components/Shifting/ShiftDetails.tsx index 3d2a1f60b8b..a898b0cde15 100644 --- a/src/Components/Shifting/ShiftDetails.tsx +++ b/src/Components/Shifting/ShiftDetails.tsx @@ -806,13 +806,12 @@ export default function ShiftDetails(props: { id: string }) { {t("created")}
-
- {data?.created_by_object?.first_name} - {data?.created_by_object?.last_name} -
-
- {data?.created_date && formatDateTime(data?.created_date)} -
+
@@ -820,14 +819,12 @@ export default function ShiftDetails(props: { id: string }) { {t("last_edited")}
-
- {data?.last_edited_by_object?.first_name}{" "} - {data?.last_edited_by_object?.last_name} -
-
- {data?.modified_date && - formatDateTime(data?.modified_date)} -
+
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index 94791e55ae1..3b469081754 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -1,17 +1,5 @@ import * as Notification from "../../Utils/Notifications.js"; -import { - addUserFacility, - clearHomeFacility, - deleteUser, - deleteUserFacility, - getDistrict, - getUserList, - getUserListFacility, - partialUpdateUser, -} from "../../Redux/actions"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { lazy, useCallback, useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { lazy, useState } from "react"; import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; import ButtonV2, { Submit } from "../Common/components/ButtonV2"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -36,6 +24,9 @@ import Page from "../Common/components/Page.js"; import dayjs from "dayjs"; import TextFormField from "../Form/FormFields/TextFormField.js"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; +import routes from "../../Redux/api.js"; +import useQuery from "../../Utils/request/useQuery.js"; +import request from "../../Utils/request/request.js"; const Loading = lazy(() => import("../Common/Loading")); @@ -49,19 +40,13 @@ export default function ManageUsers() { advancedFilter, resultsPerPage, } = useFilters({ limit: 18 }); - const dispatch: any = useDispatch(); - const initialData: any[] = []; let manageUsers: any = null; - const [users, setUsers] = useState(initialData); - const [isLoading, setIsLoading] = useState(false); const [expandSkillList, setExpandSkillList] = useState(false); - const [totalCount, setTotalCount] = useState(0); - const [districtName, setDistrictName] = useState(); const [expandFacilityList, setExpandFacilityList] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [expandWorkingHours, setExpandWorkingHours] = useState(false); const authUser = useAuthUser(); - const [weeklyHours, setWeeklyHours] = useState(0); + const [weeklyHours, setWeeklyHours] = useState("0"); const userIndex = USER_TYPES.indexOf(authUser.user_type); const userTypes = authUser.is_superuser ? [...USER_TYPES] @@ -79,58 +64,32 @@ export default function ManageUsers() { const isExtremeSmallScreen = width <= extremeSmallScreenBreakpoint ? true : false; - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const params = { - limit: resultsPerPage, - offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, - username: qParams.username, - first_name: qParams.first_name, - last_name: qParams.last_name, - phone_number: qParams.phone_number, - alt_phone_number: qParams.alt_phone_number, - user_type: qParams.user_type, - district_id: qParams.district_id, - }; - if (qParams.district_id) { - const dis = await dispatch(getDistrict(qParams.district_id)); - if (!status.aborted) { - if (dis && dis.data) { - setDistrictName(dis.data.name); - } - } - } else { - setDistrictName(undefined); - } - const res = await dispatch(getUserList(params)); - if (!status.aborted) { - if (res && res.data) { - setUsers(res.data.results); - setTotalCount(res.data.count); - } - setIsLoading(false); - } + const { + data: userListData, + loading: userListLoading, + refetch: refetchUserList, + } = useQuery(routes.userList, { + query: { + limit: resultsPerPage.toString(), + offset: ( + (qParams.page ? qParams.page - 1 : 0) * resultsPerPage + ).toString(), + username: qParams.username, + first_name: qParams.first_name, + last_name: qParams.last_name, + phone_number: qParams.phone_number, + alt_phone_number: qParams.alt_phone_number, + user_type: qParams.user_type, + district_id: qParams.district_id, }, - [ - resultsPerPage, - qParams.page, - qParams.username, - qParams.first_name, - qParams.last_name, - qParams.phone_number, - qParams.alt_phone_number, - qParams.user_type, - qParams.district_id, - dispatch, - ] - ); + }); - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] + const { data: districtData, loading: districtDataLoading } = useQuery( + routes.getDistrict, + { + prefetch: !!qParams.district_id, + pathParams: { id: qParams.district_id }, + } ); const addUser = ( @@ -150,17 +109,15 @@ export default function ManageUsers() { const handleWorkingHourSubmit = async () => { const username = selectedUser; - if (!username || !weeklyHours || weeklyHours < 0 || weeklyHours > 168) { + if (!username || !weeklyHours || +weeklyHours < 0 || +weeklyHours > 168) { setWeeklyHoursError("Value should be between 0 and 168"); return; } - const res = await dispatch( - partialUpdateUser(username, { - weekly_working_hours: weeklyHours, - }) - ); - - if (res?.data) { + const { res, data, error } = await request(routes.partialUpdateUser, { + pathParams: { username }, + body: { weekly_working_hours: weeklyHours }, + }); + if (res && res.status === 200 && data) { Notification.Success({ msg: "Working hours updated successfully", }); @@ -168,29 +125,30 @@ export default function ManageUsers() { setSelectedUser(null); } else { Notification.Error({ - msg: "Error while updating working hours: " + (res.data.detail || ""), + msg: "Error while updating working hours: " + (error || ""), }); } - setWeeklyHours(0); + setWeeklyHours("0"); setWeeklyHoursError(""); - fetchData({ aborted: false }); + await refetchUserList(); }; const handleSubmit = async () => { - const username = userData.username; - const res = await dispatch(deleteUser(username)); + const { res, error } = await request(routes.deleteUser, { + pathParams: { username: userData.username }, + }); if (res?.status === 204) { Notification.Success({ msg: "User deleted successfully", }); } else { Notification.Error({ - msg: "Error while deleting User: " + (res?.data?.detail || ""), + msg: "Error while deleting User: " + (error || ""), }); } setUserData({ show: false, username: "", name: "" }); - fetchData({ aborted: false }); + await refetchUserList(); }; const handleDelete = (user: any) => { @@ -214,9 +172,9 @@ export default function ManageUsers() { let userList: any[] = []; - users && - users.length && - (userList = users.map((user: any, idx) => { + userListData?.results && + userListData.results.length && + (userList = userListData.results.map((user: any, idx) => { const cur_online = isUserOnline(user); return (
; - } else if (users?.length) { + } else if (userListData?.results.length) { manageUsers = (
{userList}
- +
); - } else if (users && users.length === 0) { + } else if (userListData?.results && userListData?.results.length === 0) { manageUsers = (
No Users Found
@@ -505,7 +463,7 @@ export default function ManageUsers() { open={expandWorkingHours} setOpen={(state) => { setExpandWorkingHours(state); - setWeeklyHours(0); + setWeeklyHours("0"); setWeeklyHoursError(""); }} slideFrom="right" @@ -539,8 +497,8 @@ export default function ManageUsers() {
@@ -574,7 +532,11 @@ export default function ManageUsers() { phoneNumber(), phoneNumber("WhatsApp no.", "alt_phone_number"), badge("Role", "user_type"), - value("District", "district_id", districtName || ""), + value( + "District", + "district_id", + qParams.district_id ? districtData?.name || "" : "" + ), ]} />
@@ -596,8 +558,6 @@ export default function ManageUsers() { function UserFacilities(props: { user: any }) { const { user } = props; const username = user.username; - const dispatch: any = useDispatch(); - const [facilities, setFacilities] = useState([]); const [isLoading, setIsLoading] = useState(false); const [facility, setFacility] = useState(null); const [unlinkFacilityData, setUnlinkFacilityData] = useState<{ @@ -635,62 +595,59 @@ function UserFacilities(props: { user: any }) { }); }; - const fetchFacilities = async () => { - setIsLoading(true); - const res = await dispatch(getUserListFacility({ username })); - if (res && res.data) { - setFacilities(res.data); - } - setIsLoading(false); - }; + const { + data: userFacilities, + loading: userFacilitiesLoading, + refetch: refetchUserFacilities, + } = useQuery(routes.userListFacility, { + pathParams: { username }, + }); const updateHomeFacility = async (username: string, facility: any) => { setIsLoading(true); - const res = await dispatch( - partialUpdateUser(username, { home_facility: facility.id }) - ); + const { res } = await request(routes.partialUpdateUser, { + pathParams: { username }, + body: { home_facility: facility.id.toString() }, + }); if (res && res.status === 200) user.home_facility_object = facility; - fetchFacilities(); + await refetchUserFacilities(); setIsLoading(false); }; const handleUnlinkFacilitySubmit = async () => { setIsLoading(true); if (unlinkFacilityData.isHomeFacility) { - const res = await dispatch( - clearHomeFacility(unlinkFacilityData.userName) - ); + const { res } = await request(routes.clearHomeFacility, { + pathParams: { username }, + }); if (res && res.status === 204) user.home_facility_object = null; } else { - await dispatch( - deleteUserFacility( - unlinkFacilityData.userName, - String(unlinkFacilityData?.facility?.id) - ) - ); + await request(routes.deleteUserFacility, { + pathParams: { username }, + body: { facility: unlinkFacilityData?.facility?.id?.toString() }, + }); } - fetchFacilities(); - setIsLoading(false); + await refetchUserFacilities(); hideUnlinkFacilityModal(); + setIsLoading(false); }; const addFacility = async (username: string, facility: any) => { setIsLoading(true); - const res = await dispatch(addUserFacility(username, String(facility.id))); + const { res } = await request(routes.addUserFacility, { + pathParams: { username }, + body: { facility: facility.id.toString() }, + }); if (res?.status !== 201) { Notification.Error({ msg: "Error while linking facility", }); } + await refetchUserFacilities(); setIsLoading(false); setFacility(null); - fetchFacilities(); }; - useEffect(() => { - fetchFacilities(); - }, []); - return (
{unlinkFacilityData.show && ( @@ -723,7 +680,7 @@ function UserFacilities(props: { user: any }) { Add
- {isLoading ? ( + {isLoading || userFacilitiesLoading ? (
@@ -761,13 +718,13 @@ function UserFacilities(props: { user: any }) { )} {/* Linked Facilities section */} - {facilities.length > 0 && ( + {userFacilities?.length && (
Linked Facilities
- {facilities.map((facility: any, i: number) => { + {userFacilities.map((facility: any, i: number) => { if (user?.home_facility_object?.id === facility.id) { // skip if it's a home facility return null; @@ -831,7 +788,7 @@ function UserFacilities(props: { user: any }) {
)} - {!user?.home_facility_object && facilities.length === 0 && ( + {!user?.home_facility_object && !userFacilities?.length && (
{ /* added const {t} hook here and relevant text to Common.json to avoid eslint error */ const { t } = useTranslation(); - const [skills, setSkills] = useState([]); const [selectedSkill, setSelectedSkill] = useState( null ); const [isLoading, setIsLoading] = useState(false); const [deleteSkill, setDeleteSkill] = useState(null); - const dispatch: any = useDispatch(); - const fetchSkills = useCallback( - async (username: string) => { - setIsLoading(true); - const res = await dispatch(getUserListSkills({ username })); - if (res && res.data) { - setSkills(res.data.results); - } - setIsLoading(false); - }, - [dispatch] - ); + const { + data: skills, + loading: skillsLoading, + refetch: refetchUserSkills, + } = useQuery(routes.userListSkill, { + pathParams: { username }, + }); const addSkill = useCallback( async (username: string, skill: SkillObjectModel | null) => { if (!skill) return; setIsLoading(true); - const res = await dispatch(addUserSkill(username, skill.id)); - if (res?.status !== 201) { + const { res } = await request(routes.addUserSkill, { + pathParams: { username }, + body: { skill: skill.id }, + }); + if (!res?.ok) { Notification.Error({ msg: "Error while adding skill", }); @@ -62,36 +56,32 @@ export default ({ show, setShow, username }: IProps) => { } setSelectedSkill(null); setIsLoading(false); - fetchSkills(username); + await refetchUserSkills(); }, - [dispatch, fetchSkills] + [refetchUserSkills] ); const removeSkill = useCallback( async (username: string, skillId: string) => { - const res = await dispatch(deleteUserSkill(username, skillId)); + const { res } = await request(routes.deleteUserSkill, { + pathParams: { username, id: skillId }, + }); if (res?.status !== 204) { Notification.Error({ msg: "Error while unlinking skill", }); } setDeleteSkill(null); - fetchSkills(username); + await refetchUserSkills(); }, - [dispatch, fetchSkills] + [refetchUserSkills] ); - useEffect(() => { - setIsLoading(true); - if (username) fetchSkills(username); - setIsLoading(false); - }, [username, fetchSkills]); - const authorizeForAddSkill = useIsAuthorized( AuthorizeFor(["DistrictAdmin", "StateAdmin"]) ); - const hasSkills = useMemo(() => skills.length > 0, [skills]); + const hasSkills = skills?.results?.length || 0 > 0; return (
@@ -114,7 +104,7 @@ export default ({ show, setShow, username }: IProps) => { >
- {!isLoading && ( + {(!isLoading || !skillsLoading) && (
{ setSelected={setSelectedSkill} errors="" username={username} + userSkills={skills?.results || []} /> { )}
)} - {isLoading ? ( + {isLoading || skillsLoading ? (
@@ -151,8 +142,8 @@ export default ({ show, setShow, username }: IProps) => {
{hasSkills ? ( diff --git a/src/Components/Users/UserAdd.tsx b/src/Components/Users/UserAdd.tsx index a6553bad01b..7df0089cdac 100644 --- a/src/Components/Users/UserAdd.tsx +++ b/src/Components/Users/UserAdd.tsx @@ -1,26 +1,17 @@ import { Link, navigate } from "raviger"; -import { lazy, useCallback, useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { lazy, useEffect, useState } from "react"; import { GENDER_TYPES, USER_TYPES, USER_TYPE_OPTIONS, } from "../../Common/constants"; -import { statusType, useAbortableEffect } from "../../Common/utils"; +import { useAbortableEffect } from "../../Common/utils"; import { validateEmailAddress, validateName, validatePassword, validateUsername, } from "../../Common/validation"; -import { - addUser, - getDistrictByState, - getLocalbodyByDistrict, - getStates, - getUserListFacility, - checkUsername, -} from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; import { FacilitySelect } from "../Common/FacilitySelect"; import { FacilityModel } from "../Facility/models"; @@ -45,6 +36,9 @@ import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; import dayjs from "../../Utils/dayjs"; import useAuthUser from "../../Common/hooks/useAuthUser"; import { PhoneNumberValidator } from "../Form/FieldValidators"; +import routes from "../../Redux/api"; +import request from "../../Utils/request/request"; +import useQuery from "../../Utils/request/useQuery"; const Loading = lazy(() => import("../Common/Loading")); @@ -163,7 +157,6 @@ export const validateRule = ( export const UserAdd = (props: UserProps) => { const { goBack } = useAppHistory(); - const dispatchAction: any = useDispatch(); const { userId } = props; const [state, dispatch] = useAutoSaveReducer( @@ -171,13 +164,9 @@ export const UserAdd = (props: UserProps) => { initialState ); const [isLoading, setIsLoading] = useState(false); - const [isStateLoading, setIsStateLoading] = useState(false); - const [isDistrictLoading, setIsDistrictLoading] = useState(false); - const [isLocalbodyLoading, setIsLocalbodyLoading] = useState(false); - const [_current_user_facilities, setFacilities] = useState< - Array - >([]); const [states, setStates] = useState([]); + const [selectedStateId, setSelectedStateId] = useState(0); + const [selectedDistrictId, setSelectedDistrictId] = useState(0); const [districts, setDistricts] = useState([]); const [localBodies, setLocalBodies] = useState([]); const [selectedFacility, setSelectedFacility] = useState([]); @@ -198,9 +187,9 @@ export const UserAdd = (props: UserProps) => { const check_username = async (username: string) => { setUsernameExists(userExistsEnums.checking); - const usernameCheck = await dispatchAction( - checkUsername({ username: username }) - ); + const { res: usernameCheck } = await request(routes.checkUsername, { + pathParams: { username }, + }); if (usernameCheck === undefined || usernameCheck.status === 409) setUsernameExists(userExistsEnums.exists); else if (usernameCheck.status === 200) @@ -254,101 +243,45 @@ export const UserAdd = (props: UserProps) => { state.form.user_type === "StaffReadOnly" ); - const fetchDistricts = useCallback( - async (id: number) => { - if (id > 0) { - setIsDistrictLoading(true); - const districtList = await dispatchAction(getDistrictByState({ id })); - if (districtList) { - if (userIndex <= USER_TYPES.indexOf("DistrictAdmin")) { - setDistricts([ - { - id: authUser.district!, - name: authUser.district_object?.name as string, - }, - ]); - } else { - setDistricts(districtList.data); - } - } - setIsDistrictLoading(false); + const { loading: isDistrictLoading } = useQuery(routes.getDistrictByState, { + prefetch: !!(selectedStateId > 0), + pathParams: { id: selectedStateId.toString() }, + onResponse: (result) => { + if (!result || !result.res || !result.data) return; + if (userIndex <= USER_TYPES.indexOf("DistrictAdmin")) { + setDistricts([authUser.district_object!]); + } else { + setDistricts(result.data); } }, - [dispatchAction] - ); - - const fetchLocalBody = useCallback( - async (id: number) => { - if (id > 0) { - setIsLocalbodyLoading(true); - const localBodyList = await dispatchAction( - getLocalbodyByDistrict({ id }) - ); - setIsLocalbodyLoading(false); - if (localBodyList) { - if (userIndex <= USER_TYPES.indexOf("LocalBodyAdmin")) { - setLocalBodies([ - { - id: authUser.local_body!, - name: authUser.local_body_object?.name as string, - }, - ]); - } else { - setLocalBodies(localBodyList.data); - } - } - } - }, - [dispatchAction] - ); - - const fetchStates = useCallback( - async (status: statusType) => { - setIsStateLoading(true); - const statesRes = await dispatchAction(getStates()); - if (!status.aborted && statesRes.data.results) { - if (userIndex <= USER_TYPES.indexOf("StateAdmin")) { - setStates([ - { - id: authUser.state!, - name: authUser.state_object?.name as string, - }, - ]); + }); + + const { loading: isLocalbodyLoading } = useQuery( + routes.getAllLocalBodyByDistrict, + { + prefetch: !!(selectedDistrictId > 0), + pathParams: { id: selectedDistrictId.toString() }, + onResponse: (result) => { + if (!result || !result.res || !result.data) return; + if (userIndex <= USER_TYPES.indexOf("LocalBodyAdmin")) { + setLocalBodies([authUser.local_body_object!]); } else { - setStates(statesRes.data.results); + setLocalBodies(result.data); } - } - setIsStateLoading(false); - }, - [dispatchAction] - ); - - const fetchFacilities = useCallback( - async (status: any) => { - setIsStateLoading(true); - const res = await dispatchAction( - getUserListFacility({ username: authUser.username }) - ); - if (!status.aborted && res && res.data) { - setFacilities(res.data); - } - setIsStateLoading(false); - }, - [dispatchAction, authUser.username] + }, + } ); - useAbortableEffect( - (status: statusType) => { - fetchStates(status); - if ( - authUser.user_type === "Staff" || - authUser.user_type === "StaffReadOnly" - ) { - fetchFacilities(status); + const { loading: isStateLoading } = useQuery(routes.statesList, { + onResponse: (result) => { + if (!result || !result.res || !result.data) return; + if (userIndex <= USER_TYPES.indexOf("StateAdmin")) { + setStates([authUser.state_object!]); + } else { + setStates(result.data.results); } }, - [dispatch] - ); + }); const handleDateChange = (e: FieldChangeEvent) => { if (dayjs(e.value).isValid()) { @@ -605,13 +538,10 @@ export const UserAdd = (props: UserProps) => { : undefined, }; - const res = await dispatchAction(addUser(data)); - if ( - res && - (res.data || res.data === "") && - res.status >= 200 && - res.status < 300 - ) { + const { res } = await request(routes.addUser, { + body: data, + }); + if (res?.ok) { dispatch({ type: "set_form", form: initForm }); if (!userId) { Notification.Success({ @@ -916,7 +846,7 @@ export const UserAdd = (props: UserProps) => { optionValue={(o) => o.id} onChange={(e) => { handleFieldChange(e); - if (e) fetchDistricts(e.value); + if (e) setSelectedStateId(e.value); }} /> )} @@ -934,7 +864,7 @@ export const UserAdd = (props: UserProps) => { optionValue={(o) => o.id} onChange={(e) => { handleFieldChange(e); - if (e) fetchLocalBody(e.value); + if (e) setSelectedDistrictId(e.value); }} /> )} diff --git a/src/Components/Users/UserFilter.tsx b/src/Components/Users/UserFilter.tsx index 3dca52d2463..4544fb8893a 100644 --- a/src/Components/Users/UserFilter.tsx +++ b/src/Components/Users/UserFilter.tsx @@ -1,6 +1,3 @@ -import { useEffect } from "react"; -import { useDispatch } from "react-redux"; -import { getDistrict } from "../../Redux/actions"; import { navigate } from "raviger"; import DistrictSelect from "../Facility/FacilityFilter/DistrictSelect"; import { parsePhoneNumber } from "../../Utils/utils"; @@ -11,6 +8,8 @@ import { USER_TYPE_OPTIONS } from "../../Common/constants"; import useMergeState from "../../Common/hooks/useMergeState"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; const parsePhoneNumberForFilterParam = (phoneNumber: string) => { if (!phoneNumber) return ""; @@ -21,7 +20,6 @@ const parsePhoneNumberForFilterParam = (phoneNumber: string) => { export default function UserFilter(props: any) { const { filter, onChange, closeFilter } = props; - const dispatch: any = useDispatch(); const [filterState, setFilterState] = useMergeState({ first_name: filter.first_name || "", last_name: filter.last_name || "", @@ -69,17 +67,14 @@ export default function UserFilter(props: any) { onChange(data); }; - useEffect(() => { - async function fetchData() { - if (filter.district_id) { - const { data: districtData } = await dispatch( - getDistrict(filter.district_id, "district") - ); - setFilterState({ district_ref: districtData }); - } - } - fetchData(); - }, [dispatch]); + useQuery(routes.getDistrict, { + prefetch: !!filter.district_id, + pathParams: { id: filter.district_id }, + onResponse: (result) => { + if (!result || !result.data || !result.res) return; + setFilterState({ district_ref: result.data }); + }, + }); const handleChange = ({ name, value }: any) => setFilterState({ ...filterState, [name]: value }); diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index 441a6862634..76a94745c1a 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -1,13 +1,5 @@ -import { useState, useCallback, useReducer, lazy, FormEvent } from "react"; -import { statusType, useAbortableEffect } from "../../Common/utils"; +import { useState, useReducer, lazy, FormEvent } from "react"; import { GENDER_TYPES } from "../../Common/constants"; -import { useDispatch } from "react-redux"; -import { - getUserDetails, - getUserListSkills, - partialUpdateUser, - updateUserPassword, -} from "../../Redux/actions"; import { validateEmailAddress } from "../../Common/validation"; import * as Notification from "../../Utils/Notifications.js"; import LanguageSelector from "../../Components/Common/LanguageSelector"; @@ -18,15 +10,32 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; import { FieldChangeEvent } from "../Form/FormFields/Utils"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; -import { SkillModel, SkillObjectModel } from "../Users/models"; +import { GenderType, SkillModel, UpdatePasswordForm } from "../Users/models"; import UpdatableApp, { checkForUpdate } from "../Common/UpdatableApp"; import dayjs from "../../Utils/dayjs"; import useAuthUser from "../../Common/hooks/useAuthUser"; import { PhoneNumberValidator } from "../Form/FieldValidators"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import request from "../../Utils/request/request"; const Loading = lazy(() => import("../Common/Loading")); type EditForm = { + firstName: string; + lastName: string; + age: string; + gender: GenderType; + email: string; + phoneNumber: string; + altPhoneNumber: string; + user_type: string | undefined; + doctor_qualification: string | undefined; + doctor_experience_commenced_on: number | string | undefined; + doctor_medical_council_registration: string | undefined; + weekly_working_hours: string | undefined; +}; +type ErrorForm = { firstName: string; lastName: string; age: string; @@ -34,6 +43,7 @@ type EditForm = { email: string; phoneNumber: string; altPhoneNumber: string; + user_type: string | undefined; doctor_qualification: string | undefined; doctor_experience_commenced_on: number | string | undefined; doctor_medical_council_registration: string | undefined; @@ -41,27 +51,28 @@ type EditForm = { }; type State = { form: EditForm; - errors: EditForm; + errors: ErrorForm; }; type Action = | { type: "set_form"; form: EditForm } - | { type: "set_error"; errors: EditForm }; + | { type: "set_error"; errors: ErrorForm }; const initForm: EditForm = { firstName: "", lastName: "", age: "", - gender: "", + gender: "Male", email: "", phoneNumber: "", altPhoneNumber: "", + user_type: "", doctor_qualification: undefined, doctor_experience_commenced_on: undefined, doctor_medical_council_registration: undefined, weekly_working_hours: undefined, }; -const initError: EditForm = Object.assign( +const initError: ErrorForm = Object.assign( {}, ...Object.keys(initForm).map((k) => ({ [k]: "" })) ); @@ -87,9 +98,9 @@ const editFormReducer = (state: State, action: Action) => { } } }; + export default function UserProfile() { const [states, dispatch] = useReducer(editFormReducer, initialState); - const reduxDispatch: any = useDispatch(); const [updateStatus, setUpdateStatus] = useState({ isChecking: false, isUpdateAvailable: false, @@ -119,57 +130,44 @@ export default function UserProfile() { const [showEdit, setShowEdit] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const dispatchAction: any = useDispatch(); - - const initialDetails: any = [{}]; - const [details, setDetails] = useState(initialDetails); - - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatchAction(getUserDetails(authUser.username)); - const resSkills = await dispatchAction( - getUserListSkills({ username: authUser.username }) - ); - if (!status.aborted) { - if (res && res.data && resSkills) { - res.data.skills = resSkills.data.results.map( - (skill: SkillModel) => skill.skill_object - ); - setDetails(res.data); - const formData: EditForm = { - firstName: res.data.first_name, - lastName: res.data.last_name, - age: res.data.age, - gender: res.data.gender, - email: res.data.email, - phoneNumber: res.data.phone_number, - altPhoneNumber: res.data.alt_phone_number, - doctor_qualification: res.data.doctor_qualification, - doctor_experience_commenced_on: dayjs().diff( - dayjs(res.data.doctor_experience_commenced_on), - "years" - ), - doctor_medical_council_registration: - res.data.doctor_medical_council_registration, - weekly_working_hours: res.data.weekly_working_hours, - }; - dispatch({ - type: "set_form", - form: formData, - }); - } - setIsLoading(false); - } - }, - [dispatchAction, authUser.username] - ); - useAbortableEffect( - (status: statusType) => { - fetchData(status); + const { + data: userData, + loading: isUserLoading, + refetch: refetchUserData, + } = useQuery(routes.getUserDetails, { + pathParams: { username: authUser.username }, + onResponse: (result) => { + if (!result || !result.res || !result.data) return; + const formData: EditForm = { + firstName: result.data.first_name, + lastName: result.data.last_name, + age: result.data.age?.toString() || "", + gender: result.data.gender || "Male", + email: result.data.email, + phoneNumber: result.data.phone_number?.toString() || "", + altPhoneNumber: result.data.alt_phone_number?.toString() || "", + user_type: result.data.user_type, + doctor_qualification: result.data.doctor_qualification, + doctor_experience_commenced_on: dayjs().diff( + dayjs(result.data.doctor_experience_commenced_on), + "years" + ), + doctor_medical_council_registration: + result.data.doctor_medical_council_registration, + weekly_working_hours: result.data.weekly_working_hours, + }; + dispatch({ + type: "set_form", + form: formData, + }); }, - [fetchData] + }); + + const { data: skillsView, loading: isSkillsLoading } = useQuery( + routes.userListSkill, + { + pathParams: { username: authUser.username }, + } ); const validateForm = () => { @@ -244,7 +242,7 @@ export default function UserProfile() { case "doctor_qualification": case "doctor_experience_commenced_on": case "doctor_medical_council_registration": - if (details.user_type === "Doctor" && !states.form[field]) { + if (states.form.user_type === "Doctor" && !states.form[field]) { errors[field] = "Field is required"; invalidForm = true; } @@ -298,13 +296,13 @@ export default function UserProfile() { phone_number: parsePhoneNumber(states.form.phoneNumber) ?? "", alt_phone_number: parsePhoneNumber(states.form.altPhoneNumber) ?? "", gender: states.form.gender, - age: states.form.age, + age: +states.form.age, doctor_qualification: - details.user_type === "Doctor" + states.form.user_type === "Doctor" ? states.form.doctor_qualification : undefined, doctor_experience_commenced_on: - details.user_type === "Doctor" + states.form.user_type === "Doctor" ? dayjs() .subtract( parseInt( @@ -316,34 +314,27 @@ export default function UserProfile() { .format("YYYY-MM-DD") : undefined, doctor_medical_council_registration: - details.user_type === "Doctor" + states.form.user_type === "Doctor" ? states.form.doctor_medical_council_registration : undefined, weekly_working_hours: states.form.weekly_working_hours, }; - const res = await dispatchAction( - partialUpdateUser(authUser.username, data) - ); - if (res && res.data) { + const { res } = await request(routes.partialUpdateUser, { + pathParams: { username: authUser.username }, + body: data, + }); + if (res?.ok) { Notification.Success({ msg: "Details updated successfully", }); - window.location.reload(); - setDetails({ - ...details, - first_name: states.form.firstName, - last_name: states.form.lastName, - age: states.form.age, - gender: states.form.gender, - email: states.form.email, - phone_number: states.form.phoneNumber, - alt_phone_number: states.form.altPhoneNumber, - }); + await refetchUserData(); setShowEdit(false); } } }; + const isLoading = isUserLoading || isSkillsLoading; + if (isLoading) { return ; } @@ -367,7 +358,7 @@ export default function UserProfile() { } }; - const changePassword = (e: any) => { + const changePassword = async (e: any) => { e.preventDefault(); //validating form if ( @@ -377,30 +368,28 @@ export default function UserProfile() { msg: "Passwords are different in the new and the confirmation column.", }); } else { - setIsLoading(true); - const form = { + const form: UpdatePasswordForm = { old_password: changePasswordForm.old_password, username: authUser.username, new_password: changePasswordForm.new_password_1, }; - reduxDispatch(updateUserPassword(form)).then((resp: any) => { - setIsLoading(false); - const res = resp && resp.data; - if (res.message === "Password updated successfully") { - Notification.Success({ - msg: "Password changed!", - }); - } else { - Notification.Error({ - msg: "There was some error. Please try again in some time.", - }); - } - setChangePasswordForm({ - ...changePasswordForm, - new_password_1: "", - new_password_2: "", - old_password: "", + const { res, data } = await request(routes.updatePassword, { + body: form, + }); + if (res?.ok && data?.message === "Password updated successfully") { + Notification.Success({ + msg: "Password changed!", + }); + } else { + Notification.Error({ + msg: "There was some error. Please try again in some time.", }); + } + setChangePasswordForm({ + ...changePasswordForm, + new_password_1: "", + new_password_2: "", + old_password: "", }); } }; @@ -432,7 +421,7 @@ export default function UserProfile() {
- {!showEdit && ( + {!showEdit && !isLoading && (
- {details.username || "-"} + {userData?.username || "-"}
- {details.phone_number || "-"} + {userData?.phone_number || "-"}
@@ -466,7 +455,7 @@ export default function UserProfile() { Whatsapp No
- {details.alt_phone_number || "-"} + {userData?.alt_phone_number || "-"}
- {details.email || "-"} + {userData?.email || "-"}
- {details.first_name || "-"} + {userData?.first_name || "-"}
- {details.last_name || "-"} + {userData?.last_name || "-"}
@@ -507,7 +496,7 @@ export default function UserProfile() { Age
- {details.age || "-"} + {userData?.age || "-"}
@@ -516,7 +505,7 @@ export default function UserProfile() {
{" "} - {details.user_type || "-"} + {userData?.user_type || "-"}
- {details.gender || "-"} + {userData?.gender || "-"}
@@ -535,7 +524,7 @@ export default function UserProfile() { Local Body
- {details.local_body_object?.name || "-"} + {userData?.local_body_object?.name || "-"}
@@ -543,7 +532,7 @@ export default function UserProfile() { District
- {details.district_object?.name || "-"} + {userData?.district_object?.name || "-"}
@@ -551,7 +540,7 @@ export default function UserProfile() { State
- {details.state_object?.name || "-"} + {userData?.state_object?.name || "-"}
@@ -563,11 +552,13 @@ export default function UserProfile() { className="flex flex-wrap gap-2" id="already-linked-skills" > - {details.skills && details.skills.length - ? details.skills?.map((skill: SkillObjectModel) => { + {skillsView?.results?.length + ? skillsView.results?.map((skill: SkillModel) => { return ( -

{skill.name}

+

+ {skill.skill_object.name} +

); }) @@ -583,7 +574,7 @@ export default function UserProfile() { Average weekly working hours
- {details.weekly_working_hours ?? "-"} + {userData?.weekly_working_hours || "-"}
@@ -649,7 +640,7 @@ export default function UserProfile() { required type="email" /> - {details.user_type === "Doctor" && ( + {states.form.user_type === "Doctor" && ( <> { export const signupUser = (params: object) => { return fireRequest("createUser", [], params); }; -export const addUser = (params: object) => { - return fireRequest("addUser", [], params); -}; export const deleteUser = (username: string) => { - return fireRequest("deleteUser", [username], {}); + return fireRequest("deleteUser", [], {}, { username }); }; export const checkResetToken = (params: object) => { @@ -34,10 +31,6 @@ export const postForgotPassword = (form: object) => { return fireRequest("forgotPassword", [], form); }; -export const updateUserPassword = (form: object) => { - return fireRequest("updatePassword", [], form); -}; - export const getUserPnconfig = (pathParams: object) => { return fireRequest("getUserPnconfig", [], {}, pathParams); }; @@ -62,14 +55,6 @@ export const deleteFacilityCoverImage = (id: string) => { export const getUserList = (params: object, key?: string) => { return fireRequest("userList", [], params, null, key); }; - -export const getUserListSkills = (pathParam: object) => { - return fireRequest("userListSkill", [], {}, pathParam); -}; - -export const partialUpdateUser = (username: string, data: any) => { - return fireRequest("partialUpdateUser", [], data, { username }); -}; export const getUserListFacility = (pathParam: object) => { return fireRequest("userListFacility", [], {}, pathParam); }; @@ -95,10 +80,6 @@ export const deleteUserFacility = (username: string, facility: string) => { ); }; -export const clearHomeFacility = (username: string) => { - return fireRequest("clearHomeFacility", [], {}, { username }); -}; - export const getPermittedFacilities = (params: object) => { return fireRequest("getPermittedFacilities", [], params); }; @@ -605,20 +586,6 @@ export const dischargePatient = (params: object, pathParams: object) => { //Profile -export const checkUsername = (params: object) => { - return fireRequest("checkUsername", [], {}, params, undefined, true); -}; - -export const getUserDetails = (username: string, suppress?: boolean) => { - return fireRequest( - "getUserDetails", - [], - {}, - { username: username }, - undefined, - suppress ?? true - ); -}; export const updateUserDetails = (username: string, data: object) => { return fireRequest("updateUserDetails", [username], data); }; diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 4172effe432..2a64d921792 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -33,6 +33,7 @@ import { ConsultationModel, CreateBedBody, CurrentBed, + DistrictModel, DailyRoundsBody, DailyRoundsRes, DoctorModal, @@ -40,9 +41,10 @@ import { IFacilityNotificationRequest, IFacilityNotificationResponse, IUserFacilityRequest, - LocationModel, PatientStatsModel, WardModel, + LocationModel, + StateModel, } from "../Components/Facility/models"; import { IDeleteExternalResult, @@ -52,9 +54,13 @@ import { ILocalBodyByDistrict, IPartialUpdateExternalResult, } from "../Components/ExternalResult/models"; - +import { + SkillModel, + SkillObjectModel, + UpdatePasswordForm, + UserModel, +} from "../Components/Users/models"; import { Prescription } from "../Components/Medicine/models"; -import { UserModel } from "../Components/Users/models"; import { DailyRoundsModel, PatientModel } from "../Components/Patient/models"; import { PaginatedResponse } from "../Utils/request/types"; import { @@ -149,6 +155,8 @@ const routes = { updatePassword: { path: "/api/v1/password_change/", method: "PUT", + TRes: Type>(), + TBody: Type(), }, // User Endpoints currentUser: { @@ -164,11 +172,14 @@ const routes = { userListSkill: { path: "/api/v1/users/{username}/skill/", + method: "GET", + TRes: Type>(), }, userListFacility: { path: "/api/v1/users/{username}/get_facilities/", - TRes: Type(), + method: "GET", + TRes: Type(), }, addUserFacility: { @@ -181,6 +192,8 @@ const routes = { addUserSkill: { path: "/api/v1/users/{username}/skill/", method: "POST", + TBody: Type<{ skill: string }>(), + TRes: Type(), }, deleteUserFacility: { @@ -193,11 +206,13 @@ const routes = { clearHomeFacility: { path: "/api/v1/users/{username}/clear_home_facility/", method: "DELETE", + TRes: Type>(), }, deleteUserSkill: { path: "/api/v1/users/{username}/skill/{id}/", method: "DELETE", + TRes: Type>(), }, createUser: { @@ -214,6 +229,8 @@ const routes = { partialUpdateUser: { path: "/api/v1/users/{username}/", method: "PATCH", + TRes: Type(), + TBody: Type>(), }, deleteUser: { @@ -225,6 +242,7 @@ const routes = { addUser: { path: "/api/v1/users/add_user/", method: "POST", + TRes: Type(), }, searchUser: { @@ -251,6 +269,8 @@ const routes = { getAllSkills: { path: "/api/v1/skill/", + method: "GET", + TRes: Type>(), }, // Facility Endpoints @@ -651,6 +671,8 @@ const routes = { // States statesList: { path: "/api/v1/state/", + method: "GET", + TRes: Type>(), }, getState: { @@ -661,9 +683,13 @@ const routes = { getDistrict: { path: "/api/v1/district/{id}/", + method: "GET", + TRes: Type(), }, getDistrictByState: { path: "/api/v1/state/{id}/districts/", + method: "GET", + TRes: Type(), }, getDistrictByName: { path: "/api/v1/district/", @@ -775,11 +801,13 @@ const routes = { checkUsername: { path: "/api/v1/users/{username}/check_availability/", method: "GET", + TRes: Type>(), }, getUserDetails: { path: "/api/v1/users/{username}/", method: "GET", + TRes: Type(), }, updateUserDetails: { path: "/api/v1/users", diff --git a/src/Utils/request/utils.ts b/src/Utils/request/utils.ts index ec919c79490..f22dca369f2 100644 --- a/src/Utils/request/utils.ts +++ b/src/Utils/request/utils.ts @@ -82,7 +82,10 @@ export function mergeRequestOptions( ...overrides, query: { ...options.query, ...overrides.query }, - body: { ...(options.body ?? {}), ...(overrides.body ?? {}) }, + body: (options.body || overrides.body) && { + ...(options.body ?? {}), + ...(overrides.body ?? {}), + }, pathParams: { ...options.pathParams, ...overrides.pathParams }, onResponse: (res) => {