diff --git a/.eslintrc.json b/.eslintrc.json index cb9f0d0af1b..fa158329d1d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -46,6 +46,9 @@ "*.bs.js", "*.gen.tsx", "*.res", - "*.css" + "*.css", + "*.csv", + "*.stories.mdx", + "Dockerfile" ] } diff --git a/cypress/e2e/external_results_spec/external_result.cy.ts b/cypress/e2e/external_results_spec/external_result.cy.ts index ea1d7a42b26..1da601c9cd5 100644 --- a/cypress/e2e/external_results_spec/external_result.cy.ts +++ b/cypress/e2e/external_results_spec/external_result.cy.ts @@ -38,10 +38,16 @@ describe("Edit Profile Testing", () => { }); it("import", () => { - cy.wait(100); - cy.contains("Import/Export").click().wait(100); - cy.contains("Import Results").click().wait(100); - // TODO: attach file and save + cy.intercept("POST", "/api/v1/external_result/bulk_upsert").as("import"); + cy.get("div").contains("Import/Export").click({ force: true }); + cy.get("div").contains("Import Results").click({ force: true }); + cy.get("[id=result-upload]") + .selectFile("cypress/fixtures/external-result_sample.csv") + .wait(100); + cy.get("button").contains("Save").click({ force: true }); + cy.wait("@import").then((interception) => { + expect(interception.response.statusCode).to.equal(202); + }); }); it("export", () => { diff --git a/cypress/e2e/facility_spec/facility.cy.ts b/cypress/e2e/facility_spec/facility.cy.ts index d76c9fde210..41b54b458c2 100644 --- a/cypress/e2e/facility_spec/facility.cy.ts +++ b/cypress/e2e/facility_spec/facility.cy.ts @@ -5,8 +5,6 @@ class facility { cy.awaitUrl("/facility/create"); this.fillForm({ ...facility, - type: 1, - features: [1, 3], latitude: 800, longitude: 500, }); @@ -20,8 +18,6 @@ class facility { cy.url().should("include", "update"); this.fillForm({ ...facility, - type: 3, - features: [3, 4], latitude: 900, longitude: 600, }); @@ -36,6 +32,10 @@ class facility { address, pincode, tel, + state, + district, + localbody, + ward, oxygen_capacity, oxygen_requirement, type_b_cylinders, @@ -48,68 +48,68 @@ class facility { longitude, }) { cy.get("[id=facility-type] > div > button").click(); - cy.get(`ul > li:nth-child(${type})`).click(); + cy.get("div").contains(type).click(); - cy.get("[id=facility-name]").should("exist").type(name); + cy.get("input[id=facility-name]").should("exist").type(name); cy.get("[id=facility-features] > div > div > button").click(); - cy.get(`ul > li:nth-child(${features[0]})`).click(); - cy.get(`ul > li:nth-child(${features[1]})`).click(); + cy.get("li").contains(features[0]).click(); + cy.get("li").contains(features[1]).click(); cy.get("body").click(); cy.get("[id=facility-state] > div > button").click(); - cy.get("ul > li:nth-child(2)").click(); + cy.get("div").contains(state).click(); cy.get("[id=facility-district] > div > button").click(); - cy.get("ul > li:nth-child(3)").click(); + cy.get("div").contains(district).click(); cy.get("[id=facility-localbody] > div > button").click(); - cy.get("ul > li:nth-child(3)").click(); + cy.get("div").contains(localbody).click(); cy.get("[id=facility-ward] > div > button").click(); - cy.get("ul > li:nth-child(2)").click(); + cy.get("div").contains(ward).click(); - cy.get("[id=facility-address]").type(address); + cy.get("textarea[id=facility-address]").should("exist").type(address); - cy.get("[id=facility-pincode]").should("exist").clear().type(pincode); + cy.get("input[id=facility-pincode]").should("exist").clear().type(pincode); cy.get("input[type=tel]").should("exist").type(tel); - cy.get("[id=facility-oxygen_capacity]").clear().type(oxygen_capacity); - cy.get("[id=facility-expected_oxygen_requirement]") + cy.get("input[id=facility-oxygen_capacity]").clear().type(oxygen_capacity); + cy.get("input[id=facility-expected_oxygen_requirement]") .should("exist") .clear() .type(oxygen_requirement); - cy.get("[id=facility-type_b_cylinders]") + cy.get("input[id=facility-type_b_cylinders]") .should("exist") .clear() .type(type_b_cylinders); - cy.get("[id=facility-expected_type_b_cylinders]") + cy.get("input[id=facility-expected_type_b_cylinders]") .should("exist") .clear() .type(expected_type_b_cylinders); - cy.get("[id=facility-type_c_cylinders]") + cy.get("input[id=facility-type_c_cylinders]") .should("exist") .clear() .type(type_c_cylinders); - cy.get("[id=facility-expected_type_c_cylinders]") + cy.get("input[id=facility-expected_type_c_cylinders]") .should("exist") .clear() .type(expected_type_c_cylinders); - cy.get("[id=facility-type_d_cylinders]") + cy.get("input[id=facility-type_d_cylinders]") .should("exist") .clear() .type(type_d_cylinders); - cy.get("[id=facility-expected_type_d_cylinders]") + cy.get("input[id=facility-expected_type_d_cylinders]") .should("exist") .clear() .type(expected_type_d_cylinders); cy.get("[id=facility-location-button]").click(); - cy.get("body").wait(6000).click(latitude, longitude); + cy.get("body").wait(7000).click(latitude, longitude); cy.get("body").click(); } } @@ -131,11 +131,11 @@ describe("Facility", () => { facility.create({ type: "Private Hospital", name: "cypress facility", - features: [1, 3], + features: ["CT Scan", "X-Ray"], state: "Kerala", district: "Ernakulam", - localbody: "Alangad  Block Panchayat, Ernakulam District", - ward: "1: MANAKKAPADY", + localbody: "Alangad", + ward: "MANAKKAPADY", address: "some address", pincode: "884656", tel: "9985784535", @@ -151,12 +151,14 @@ describe("Facility", () => { longitude: "1.494140625", }); + cy.verifyNotification("Facility added successfully"); + // add bed type cy.url().should("include", "bed"); cy.get("[id=bed-type] > div > button").click(); - cy.get("ul > li:nth-child(2)").click(); - cy.get("[id=total-capacity]").type("150"); - cy.get("[id=currently-occupied]").type("100"); + cy.get("div").contains("Non-Covid Ordinary Beds").click(); + cy.get("input[id=total-capacity]").should("exist").type("150"); + cy.get("input[id=currently-occupied]").should("exist").type("100"); cy.get("[id=bed-capacity-save]").click(); cy.verifyNotification("Bed capacity added successfully"); @@ -185,11 +187,11 @@ describe("Facility", () => { facility.update({ type: "TeleMedicine", name: " update", - features: [1, 4], + features: ["X-Ray", "Neonatal Care"], state: "Kerala", district: "Ernakulam", - localbody: "Alangad  Block Panchayat, Ernakulam District", - ward: "1: MANAKKAPADY", + localbody: "Aikaranad", + ward: "PAZHAMTHOTTAM", address: " update", pincode: "584675", tel: "9985784535", @@ -204,6 +206,7 @@ describe("Facility", () => { latitude: "-16.97274101999901", longitude: "11.77734375", }); + cy.verifyNotification("Facility updated successfully"); cy.url().then((url) => { current_url = url; }); diff --git a/cypress/e2e/users_spec/user_crud.cy.ts b/cypress/e2e/users_spec/user_crud.cy.ts index ca698c7927c..62bfd0a0e8a 100644 --- a/cypress/e2e/users_spec/user_crud.cy.ts +++ b/cypress/e2e/users_spec/user_crud.cy.ts @@ -30,7 +30,20 @@ describe("User management", () => { it("create user", () => { cy.contains("Add New User").click(); - cy.get("[name='user_type']").select("Volunteer"); + cy.get("[id='user_type'] > div > button").click(); + cy.get("div").contains("Ward Admin").click(); + cy.get("[id='state'] > div > button").click(); + cy.get("div").contains("Kerala").click(); + cy.get("[id='district'] > div > button").click(); + cy.get("div").contains("Ernakulam").click(); + cy.get("[id='localbody'] > div > button").click(); + cy.get("div").contains("Aikaranad").click(); + cy.intercept(/\/api\/v1\/facility/).as("facility"); + cy.get("[name='facilities']") + .type("cypress_testing_facility") + .wait("@facility"); + cy.get("[name='facilities']").type("{enter}"); + cy.wait(1000); cy.get("input[type='checkbox']").click(); cy.wait(1000); cy.get("[placeholder='Phone Number']").type(phone_number); @@ -38,22 +51,23 @@ describe("User management", () => { cy.get("[placeholder='WhatsApp Phone Number']").type(alt_phone_number, { force: true, }); - cy.intercept(/\/api\/v1\/facility/).as("facility"); - cy.get("[name='facilities']").type("Mysore").wait("@facility"); - cy.get("[name='facilities']").type("{downarrow}{enter}"); - cy.intercept(/users/).as("checkUsername"); + cy.intercept(/users/).as("check_availability"); + cy.get("[id='date_of_birth']").click(); + cy.get("div").contains("20").click(); + cy.get("[id='year-0']").click(); + cy.get("[id='date-1']").click(); cy.get("[name='username']").type(username, { force: true }); - cy.wait("@checkUsername").its("response.statusCode").should("eq", 200); - cy.get("[name='dob']").type("02/03/2001"); + cy.wait("@check_availability").its("response.statusCode").should("eq", 200); cy.get("[name='password']").type("#@Cypress_test123"); cy.get("[name='c_password']").type("#@Cypress_test123"); cy.get("[name='first_name']").type("Cypress Test"); cy.get("[name='last_name']").type("Tester"); cy.get("[name='email']").type("cypress@tester.com"); - cy.get("[name='gender']").select("Male"); - cy.get("[name='state']").select("Kerala"); - cy.get("[name='district']").select("Ernakulam"); - cy.get("button[type='submit']").contains("Save User").click(); + cy.get("[id='gender'] > div > button").click(); + cy.get("div").contains("Male").click(); + cy.get("button[id='submit']").contains("Save User").click({ + force: true, + }); cy.verifyNotification("User added successfully"); }); @@ -61,11 +75,36 @@ describe("User management", () => { cy.contains("Advanced Filters").click(); cy.get("[name='first_name']").type("Cypress Test"); cy.get("[name='last_name']").type("Tester"); + cy.get("[id='role'] > div > button").click(); + cy.get("div") + .contains(/^Ward Admin$/) + .click(); + cy.get("input[name='district']").type("Ernakulam").wait(1000); + cy.get("input[name='district']").type("{downarrow}{enter}"); cy.get("[placeholder='Phone Number']").type(phone_number); cy.get("[placeholder='WhatsApp Phone Number']").type(alt_phone_number); cy.contains("Apply").click(); + cy.intercept(/\/api\/v1\/users/).as("getUsers"); + cy.wait(1000); cy.get("[name='username']").type(username, { force: true }); - // TODO: some verify task + cy.wait("@getUsers"); + cy.wait(1000); + cy.get("dd[id='count']").contains(/^1$/).click(); + cy.get("div[id='usr_0']").within(() => { + cy.intercept(`/api/v1/users/${username}/get_facilities/`).as( + "userFacility" + ); + cy.get("div[id='role']").contains(/^WardAdmin$/); + cy.get("div[id='name']").contains("Cypress Test Tester"); + cy.get("div[id='district']").contains(/^Ernakulam$/); + cy.get("div[id='local_body']").contains("Aikaranad"); + cy.get("div[id='created_by']").contains(/^devdistrictadmin$/); + cy.get("div[id='home_facility']").contains("No Home Facility"); + cy.get("div[id='facilities'] > div > button").click().wait(1000); + cy.wait("@userFacility").then(() => { + cy.get("div").contains("cypress_testing_facility"); + }); + }); }); it("link facility for user", () => { @@ -138,6 +177,7 @@ describe("Edit Profile Testing", () => { .type("User 1") .trigger("change", { force: true }); cy.get("form").get("button[type='submit']").contains("Update").click(); + cy.wait(1000); cy.get("dt").contains("First Name").siblings().first().contains("User 1"); }); @@ -153,6 +193,7 @@ describe("Edit Profile Testing", () => { .type("User 1") .trigger("change", { force: true }); cy.get("form").get("button[type='submit']").contains("Update").click(); + cy.wait(1000); cy.get("dt").contains("Last Name").siblings().first().contains("User 1"); }); @@ -188,6 +229,7 @@ describe("Edit Profile Testing", () => { .should("have.attr", "value", `+91 ${whatsapp_num}`); cy.wait(1000); cy.get("form").get("button[type='submit']").contains("Update").click(); + cy.wait(1000); cy.get("dt") .contains("Whatsapp No") .siblings() @@ -227,6 +269,7 @@ describe("Edit Profile Testing", () => { .should("have.attr", "value", `+91 ${phone_num}`); cy.wait(1000); cy.get("form").get("button[type='submit']").contains("Update").click(); + cy.wait(1000); cy.get("dt") .contains("Contact No") .siblings() diff --git a/cypress/fixtures/external-result_sample.csv b/cypress/fixtures/external-result_sample.csv new file mode 100644 index 00000000000..b22a56848b2 --- /dev/null +++ b/cypress/fixtures/external-result_sample.csv @@ -0,0 +1,3 @@ +id,name,age,age_in,result,srf_id,gender,address,district,local_body,local_body_type,ward,mobile_number,is_repeat,patient_status,sample_type,test_type,sample_collection_date,result_date,lab_name,source,patient_category +1,Test 1,38,years,Negative,165/EKM/202010122,F,"CSN HQ +Kochi, Kerala",Ernakulam,Karukutty,Grama Panchayath,7,9207054148,NO,Symptomatic,Nasopharyngeal swab/Nasal swab/Oropharyngeal swab/Throat swab/Sputum/BAL/ETA/ Nasopharyngeal Orophar,4,2020-10-14,2020-10-10,Gh Muvattupuzha,,Cat 17: All individuals who wish to get themselves tested diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 4fa57235af6..4dd4dc0a32e 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -32,25 +32,24 @@ export type UserRole = | "StateReadOnlyAdmin" | "StateAdmin"; -const readOnly = true; export const USER_TYPE_OPTIONS: { id: UserRole; role: string; - readOnly?: true | undefined; + readOnly?: boolean; }[] = [ - { id: "Pharmacist", role: "Pharmacist" }, - { id: "Volunteer", role: "Volunteer" }, - { id: "StaffReadOnly", role: "Staff", readOnly }, - { id: "Staff", role: "Staff" }, - { id: "Doctor", role: "Doctor" }, - { id: "WardAdmin", role: "Ward Admin" }, - { id: "LocalBodyAdmin", role: "Local Body Admin" }, - { id: "DistrictLabAdmin", role: "District Lab Admin" }, - { id: "DistrictReadOnlyAdmin", role: "District Admin", readOnly }, - { id: "DistrictAdmin", role: "District Admin" }, - { id: "StateLabAdmin", role: "State Lab Admin" }, - { id: "StateReadOnlyAdmin", role: "State Admin", readOnly }, - { id: "StateAdmin", role: "State Admin" }, + { id: "Pharmacist", role: "Pharmacist", readOnly: false }, + { id: "Volunteer", role: "Volunteer", readOnly: false }, + { id: "StaffReadOnly", role: "Staff", readOnly: true }, + { id: "Staff", role: "Staff", readOnly: false }, + { id: "Doctor", role: "Doctor", readOnly: false }, + { id: "WardAdmin", role: "Ward Admin", readOnly: false }, + { id: "LocalBodyAdmin", role: "Local Body Admin", readOnly: false }, + { id: "DistrictLabAdmin", role: "District Lab Admin", readOnly: false }, + { id: "DistrictReadOnlyAdmin", role: "District Admin", readOnly: true }, + { id: "DistrictAdmin", role: "District Admin", readOnly: false }, + { id: "StateLabAdmin", role: "State Lab Admin", readOnly: false }, + { id: "StateReadOnlyAdmin", role: "State Admin", readOnly: true }, + { id: "StateAdmin", role: "State Admin", readOnly: false }, ]; export const USER_TYPES = USER_TYPE_OPTIONS.map((o) => o.id); diff --git a/src/Components/Common/DateInputV2.tsx b/src/Components/Common/DateInputV2.tsx index a2c1e1ec091..f6e0f0fcb3e 100644 --- a/src/Components/Common/DateInputV2.tsx +++ b/src/Components/Common/DateInputV2.tsx @@ -18,6 +18,7 @@ type DatePickerType = "date" | "month" | "year"; export type DatePickerPosition = "LEFT" | "RIGHT" | "CENTER"; interface Props { + id?: string; className?: string; value: Date | undefined; min?: Date; @@ -31,6 +32,7 @@ interface Props { const DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]; const DateInputV2: React.FC = ({ + id, className, value, min, @@ -188,6 +190,7 @@ const DateInputV2: React.FC = ({ = ({ {type === "date" && ( <>
- {DAYS.map((day) => ( -
+ {DAYS.map((day, i) => ( +
{day}
@@ -268,7 +275,11 @@ const DateInputV2: React.FC = ({ /> ))} {dayCount.map((d, i) => ( -
+
= ({ .map((_, i) => (
= ({ return (
+
{props.title} :
diff --git a/src/Components/Common/components/CheckBox.tsx b/src/Components/Common/components/CheckBox.tsx new file mode 100644 index 00000000000..85318b69656 --- /dev/null +++ b/src/Components/Common/components/CheckBox.tsx @@ -0,0 +1,28 @@ +interface Props { + id?: string; + className?: string; + name?: string; + onCheck: (value: boolean) => void; + checked?: boolean; + error?: string; + required?: boolean; + label?: string; +} + +export default function Checkbox(props: Props) { + return ( +
+
+ props.onCheck(event.target.checked)} + /> + +
+
+ ); +} diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 5650a63bf4c..040fe25b39f 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -2,7 +2,6 @@ import loadable from "@loadable/component"; import _ from "lodash"; import { navigate } from "raviger"; import { useState } from "react"; -//@ts-ignore import CSVReader from "react-csv-reader"; import { useDispatch } from "react-redux"; import { externalResultUploadCsv } from "../../Redux/actions"; @@ -87,6 +86,7 @@ export default function ExternalResultUpload() { { return ( handleChange({ name, value })} max={props.max || (props.disableFuture ? new Date() : undefined)} diff --git a/src/Components/Form/FormFields/SelectFormField.tsx b/src/Components/Form/FormFields/SelectFormField.tsx index b917abc4e66..932b5e10b91 100644 --- a/src/Components/Form/FormFields/SelectFormField.tsx +++ b/src/Components/Form/FormFields/SelectFormField.tsx @@ -11,6 +11,7 @@ type OptionCallback = (option: T) => R; type SelectFormFieldProps = FormFieldBaseProps & { placeholder?: React.ReactNode; options: T[]; + position?: "above" | "below"; optionLabel: OptionCallback; optionSelectedLabel?: OptionCallback; optionDescription?: OptionCallback; @@ -29,6 +30,7 @@ export const SelectFormField = (props: SelectFormFieldProps) => { options={props.options} disabled={props.disabled} value={props.value} + position={props.position} placeholder={props.placeholder} optionLabel={props.optionLabel} optionSelectedLabel={props.optionSelectedLabel} diff --git a/src/Components/Form/FormFields/TextFormField.tsx b/src/Components/Form/FormFields/TextFormField.tsx index 2fce6297b33..3ad4f430422 100644 --- a/src/Components/Form/FormFields/TextFormField.tsx +++ b/src/Components/Form/FormFields/TextFormField.tsx @@ -20,6 +20,8 @@ export type TextFormFieldProps = FormFieldBaseProps & { trailingFocused?: React.ReactNode | undefined; min?: string | number; max?: string | number; + onFocus?: (event: React.FocusEvent) => void; + onBlur?: (event: React.FocusEvent) => void; }; const TextFormField = React.forwardRef((props: TextFormFieldProps, ref) => { @@ -55,6 +57,8 @@ const TextFormField = React.forwardRef((props: TextFormFieldProps, ref) => { max={props.max} autoComplete={props.autoComplete} required={props.required} + onFocus={props.onFocus} + onBlur={props.onBlur} onChange={(event) => { event.preventDefault(); handleChange(event.target); @@ -68,7 +72,7 @@ const TextFormField = React.forwardRef((props: TextFormFieldProps, ref) => { {child}
- - - {options.map((option, index) => ( - - {({ active, selected }) => ( -
-
- {option.label} - {props.optionIcon - ? option.icon - : selected && ( - - )} +
+ + + {options.map((option, index) => ( + + {({ active, selected }) => ( +
+
+ {option.label} + {props.optionIcon + ? option.icon + : selected && ( + + )} +
+ {option.description && ( +

+ {option.description} +

+ )}
- {option.description && ( -

- {option.description} -

- )} -
- )} - - ))} - - + )} + + ))} + + +
)} diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index 4175099e704..96a0c221ab5 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -354,13 +354,14 @@ export default function ManageUsers() { users && users.length && - (userList = users.map((user: any) => { + (userList = users.map((user: any, idx) => { const cur_online = moment() .subtract(5, "minutes") .isBefore(user.last_login); return (
@@ -368,7 +369,10 @@ export default function ManageUsers() {
{user.username && ( -
+
{user.username}
)} @@ -399,7 +403,7 @@ export default function ManageUsers() { )}
-
+
{`${user.first_name} ${user.last_name}`} {user.last_login && cur_online ? ( @@ -428,7 +432,7 @@ export default function ManageUsers() { > {user.user_type && (
- +
{user.user_type}
@@ -437,7 +441,7 @@ export default function ManageUsers() { )} {user.district_object && (
- +
{user.district_object.name}
@@ -446,7 +450,7 @@ export default function ManageUsers() { )}
{user.local_body_object && ( - +
{user.local_body_object.name}
@@ -461,33 +465,16 @@ export default function ManageUsers() { > {user.created_by && (
- +
{user.created_by}
)} - {user.phone_number && ( -
- -
- )} {user.username && (
- + {user.home_facility_object?.name || "No Home Facility"} @@ -496,7 +483,7 @@ export default function ManageUsers() {
)} {user.username && ( -
+

Linked Facilities:

) : ( -
+
{totalCount}
)} diff --git a/src/Components/Users/UserAdd.tsx b/src/Components/Users/UserAdd.tsx index 46ff293e53d..1285c34a2bd 100644 --- a/src/Components/Users/UserAdd.tsx +++ b/src/Components/Users/UserAdd.tsx @@ -1,16 +1,15 @@ -import { - Card, - CardContent, - CircularProgress, - InputLabel, -} from "@material-ui/core"; +import { Card, CardContent, CircularProgress } from "@material-ui/core"; import loadable from "@loadable/component"; import { Link, navigate } from "raviger"; import { parsePhoneNumberFromString } from "libphonenumber-js/max"; import moment from "moment"; import { useCallback, useEffect, useReducer, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { GENDER_TYPES, USER_TYPES } from "../../Common/constants"; +import { + GENDER_TYPES, + USER_TYPES, + USER_TYPE_OPTIONS, +} from "../../Common/constants"; import { statusType, useAbortableEffect } from "../../Common/utils"; import { validateEmailAddress, @@ -28,40 +27,50 @@ import { } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; import { FacilitySelect } from "../Common/FacilitySelect"; -import { - DateInputField, - SelectField, - TextInputField, - CheckboxField, -} from "../Common/HelperInputFields"; +import { PhoneNumberField, ErrorHelperText } from "../Common/HelperInputFields"; import { FacilityModel } from "../Facility/models"; - import { classNames, goBack } from "../../Utils/utils"; import { Cancel, Submit } from "../Common/components/ButtonV2"; -import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; +import SelectMenuV2 from "../Form/SelectMenuV2"; +import { FieldChangeEvent } from "../Form/FormFields/Utils"; +import TextFormField from "../Form/FormFields/TextFormField"; +import DateFormField from "../Form/FormFields/DateFormField"; +import Checkbox from "../Common/components/CheckBox"; +import { SelectFormField } from "../Form/FormFields/SelectFormField"; const Loading = loadable(() => import("../Common/Loading")); const PageTitle = loadable(() => import("../Common/PageTitle")); -const genderTypes = [ - { - id: 0, - text: "Select", - }, - ...GENDER_TYPES, -]; - interface UserProps { userId?: number; } -const initialStates = [{ id: 0, name: "Choose State *" }]; -const initialDistricts = [{ id: 0, name: "Choose District" }]; -const selectStates = [{ id: 0, name: "Please select your state" }]; -const initialLocalbodies = [{ id: 0, name: "Choose Localbody" }]; -const selectDistrict = [{ id: 0, name: "Please select your district" }]; +interface StateObj { + id: number; + name: string; +} -const initForm: any = { +type UserForm = { + user_type: string; + gender: string; + password: string; + c_password: string; + facilities: Array; + home_facility: FacilityModel | null; + username: string; + first_name: string; + last_name: string; + email: string; + phone_number: string; + alt_phone_number: string; + age: number; + date_of_birth: Date | null; + state: number; + district: number; + local_body: number; +}; + +const initForm: UserForm = { user_type: "", gender: "", password: "", @@ -74,11 +83,11 @@ const initForm: any = { email: "", phone_number: "", alt_phone_number: "", - age: "", + age: 0, date_of_birth: null, - state: "", - district: "", - local_body: "", + state: 0, + district: 0, + local_body: 0, }; const initError = Object.assign( @@ -110,6 +119,9 @@ const user_create_reducer = (state = initialState, action: any) => { } }; +const getDate = (value: any) => + value && moment(value).isValid() && moment(value).toDate(); + export const UserAdd = (props: UserProps) => { const dispatchAction: any = useDispatch(); const { userId } = props; @@ -122,12 +134,10 @@ export const UserAdd = (props: UserProps) => { const [_current_user_facilities, setFacilities] = useState< Array >([]); - const [states, setStates] = useState(initialStates); - const [districts, setDistricts] = useState(selectStates); - const [localBody, setLocalBody] = useState(selectDistrict); - const [selectedFacility, setSelectedFacility] = useState< - FacilityModel[] | null - >([]); + const [states, setStates] = useState([]); + const [districts, setDistricts] = useState([]); + const [localBodies, setLocalBodies] = useState([]); + const [selectedFacility, setSelectedFacility] = useState([]); const [phoneIsWhatsApp, setPhoneIsWhatsApp] = useState(true); const [usernameInputInFocus, setUsernameInputInFocus] = useState(false); const [passwordInputInFocus, setPasswordInputInFocus] = useState(false); @@ -183,21 +193,21 @@ export const UserAdd = (props: UserProps) => { const userIndex = USER_TYPES.indexOf(userType); - const defaultAllowedUserTypes = USER_TYPES.slice(0, userIndex + 1); + const readOnlyUsers = USER_TYPE_OPTIONS.filter((user) => user.readOnly); + + const defaultAllowedUserTypes = USER_TYPE_OPTIONS.slice(0, userIndex + 1); const userTypes = isSuperuser - ? [...USER_TYPES] + ? [...USER_TYPE_OPTIONS] : userType === "StaffReadOnly" - ? ["StaffReadOnly"] - : userType === "DistrictReadOnlyAdmin" - ? ["StaffReadOnly", "DistrictReadOnlyAdmin"] - : userType === "StateReadOnlyAdmin" - ? ["StaffReadOnly", "DistrictReadOnlyAdmin", "StateReadOnlyAdmin"] - : userType === "Pharmacist" - ? ["Pharmacist"] - : // Exception to allow Staff to Create Doctors - userType === "Staff" - ? ["Doctor", ...defaultAllowedUserTypes] - : defaultAllowedUserTypes; + ? readOnlyUsers.slice(0, 1) + : userType === "DistrictReadOnlyAdmin" + ? readOnlyUsers.slice(0, 2) + : userType === "StateReadOnlyAdmin" + ? readOnlyUsers.slice(0, 3) + : userType === "Pharmacist" + ? USER_TYPE_OPTIONS.slice(0, 1) + : // Exception to allow Staff to Create Doctors + defaultAllowedUserTypes; const headerText = !userId ? "Add User" : "Update User"; const buttonText = !userId ? "Save User" : "Update Details"; @@ -210,34 +220,31 @@ export const UserAdd = (props: UserProps) => { ); const fetchDistricts = useCallback( - async (id: string) => { - if (Number(id) > 0) { + async (id: number) => { + if (id > 0) { setIsDistrictLoading(true); const districtList = await dispatchAction(getDistrictByState({ id })); if (districtList) { if (userIndex <= USER_TYPES.indexOf("DistrictAdmin")) { setDistricts([ - ...initialDistricts, { id: currentUser.data.district, name: currentUser.data.district_object.name, }, ]); } else { - setDistricts([...initialDistricts, ...districtList.data]); + setDistricts(districtList.data); } } setIsDistrictLoading(false); - } else { - setDistricts(selectStates); } }, [dispatchAction] ); const fetchLocalBody = useCallback( - async (id: string) => { - if (Number(id) > 0) { + async (id: number) => { + if (id > 0) { setIsLocalbodyLoading(true); const localBodyList = await dispatchAction( getLocalbodyByDistrict({ id }) @@ -245,19 +252,16 @@ export const UserAdd = (props: UserProps) => { setIsLocalbodyLoading(false); if (localBodyList) { if (userIndex <= USER_TYPES.indexOf("LocalBodyAdmin")) { - setLocalBody([ - ...initialLocalbodies, + setLocalBodies([ { id: currentUser.data.local_body, name: currentUser.data.local_body_object.name, }, ]); } else { - setLocalBody([...initialLocalbodies, ...localBodyList.data]); + setLocalBodies(localBodyList.data); } } - } else { - setLocalBody(selectDistrict); } }, [dispatchAction] @@ -270,14 +274,13 @@ export const UserAdd = (props: UserProps) => { if (!status.aborted && statesRes.data.results) { if (userIndex <= USER_TYPES.indexOf("StateAdmin")) { setStates([ - ...initialStates, { id: currentUser.data.state, name: currentUser.data.state_object.name, }, ]); } else { - setStates([...initialStates, ...statesRes.data.results]); + setStates(statesRes.data.results); } } setIsStateLoading(false); @@ -307,39 +310,36 @@ export const UserAdd = (props: UserProps) => { [dispatch] ); - const handleChange = (e: any) => { - const { value, name } = e.target; - const form = { ...state.form }; - form[name] = value; - if (name === "username") { - form[name] = value.toLowerCase(); - } - if (name === "state") { - form["district"] = ""; - } - dispatch({ type: "set_form", form }); - }; - - const handleChangeHomeFacility = (e: any) => { - const { value, name } = e.target; - const newValue = value === "" ? null : value; - const form = { ...state.form }; - form[name] = newValue; - dispatch({ type: "set_form", form }); + const handleChange = (e: FieldChangeEvent) => { + dispatch({ + type: "set_form", + form: { + ...state.form, + [e.name]: e.name === "username" ? e.value.toLowerCase() : e.value, + }, + }); }; - const handleDateChange = (date: any, field: string) => { - if (moment(date).isValid()) { - const form = { ...state.form }; - form[field] = date; - dispatch({ type: "set_form", form }); + const handleDateChange = (e: FieldChangeEvent) => { + if (moment(e.value).isValid()) { + dispatch({ + type: "set_form", + form: { + ...state.form, + [e.name]: moment(e.value).format("YYYY-MM-DD"), + }, + }); } }; const handleValueChange = (value: any, name: string) => { - const form = { ...state.form }; - form[name] = value; - dispatch({ type: "set_form", form }); + dispatch({ + type: "set_form", + form: { + ...state.form, + [name]: value, + }, + }); }; useAbortableEffect(() => { @@ -544,7 +544,7 @@ export const UserAdd = (props: UserProps) => { email: state.form.email, state: state.form.state, district: state.form.district, - local_body: state.form.local_body, + local_body: showLocalbody ? state.form.local_body : null, phone_number: parsePhoneNumberFromString( state.form.phone_number )?.format("E.164"), @@ -606,7 +606,7 @@ export const UserAdd = (props: UserProps) => {
handleSubmit(e)}>
- Facilities + { showAll={false} />
-
- User Type* - + User Type + {" *"} + + o.id} + optionLabel={(o) => + o.role + ((o.readOnly && " (Read Only)") || "") + } + onChange={(e) => handleValueChange(e, "user_type")} /> +
- Home Facility - Home Facility + o.name} + optionValue={(o) => o.id} + onChange={(e) => handleValueChange(e, "home_facility")} /> +
-
- + Phone Number + {" *"} + + - handleValueChange(event.value, event.name) + onChange={(value: any) => + handleValueChange(value, "phone_number") } - error={state.errors.phone_number} - onlyIndia + errors={state.errors.phone_number} + onlyIndia={true} /> - { + onCheck={(checked) => { setPhoneIsWhatsApp(checked); !checked && handleValueChange("+91", "alt_phone_number"); }} label="Is the phone number a WhatsApp number?" - className="font-bold" />
-
- Whatsapp Number + - handleValueChange(event.value, event.name) + onChange={(value: any) => + handleValueChange(value, "alt_phone_number") } disabled={phoneIsWhatsApp} - error={state.errors.alt_phone_number} - onlyIndia + errors={state.errors.alt_phone_number} + onlyIndia={true} />
-
- Username* - + Username + {" *"} + + { handleChange(e); - setUsernameInput(e.target.value); + setUsernameInput(e.value); }} - errors={state.errors.username} onFocus={() => setUsernameInputInFocus(true)} - onBlur={() => setUsernameInputInFocus(false)} + onBlur={() => { + setUsernameInputInFocus(false); + }} + error={state.errors.username} /> {usernameInputInFocus && (
@@ -754,34 +752,34 @@ export const UserAdd = (props: UserProps) => {
)}
-
- Date of birth* - handleDateChange(date, "date_of_birth")} - errors={state.errors.date_of_birth} - inputVariant="outlined" - margin="dense" - openTo="year" + +
-
- Password* - + Password + {" *"} + + setPasswordInputInFocus(true)} onBlur={() => setPasswordInputInFocus(false)} /> @@ -807,17 +805,18 @@ export const UserAdd = (props: UserProps) => { )}
- Confirm Password* - + Confirm Password + {" *"} + + setConfirmPasswordInputInFocus(true)} onBlur={() => setConfirmPasswordInputInFocus(false)} /> @@ -829,115 +828,144 @@ export const UserAdd = (props: UserProps) => { )}
- First name* - + First name + {" *"} + +
-
- Last name* - + Last name + {" *"} + +
-
- Email - + Email + {" *"} + +
-
- Gender* - + Gender + {" *"} + + o.text} + optionValue={(o) => o.text} + onChange={(e) => handleValueChange(e.value, "gender")} />
-
- State* + {isStateLoading ? ( ) : ( - [ - handleChange(e), - fetchDistricts(String(e.target.value)), - ]} - errors={state.errors.state} - /> + <> + o.name} + optionValue={(o) => o.id} + value={state.form.state} + onChange={(e) => { + if (e) { + return [ + handleValueChange(e.value, "state"), + fetchDistricts(e.value), + ]; + } + }} + /> + + )}
-
- District* + {isDistrictLoading ? ( ) : ( - [ - handleChange(e), - fetchLocalBody(String(e.target.value)), - ]} - errors={state.errors.district} - /> + <> + o.name} + optionValue={(o) => o.id} + value={state.form.district} + onChange={(e) => { + if (e) { + return [ + handleValueChange(e.value, "district"), + fetchLocalBody(e.value), + ]; + } + }} + /> + + )}
- {showLocalbody && (
- Localbody + {isLocalbodyLoading ? ( ) : ( - + <> + o.name} + optionValue={(o) => o.id} + value={state.form.local_body} + onChange={(e) => + handleValueChange(e.value, "local_body") + } + /> + + )}
)} diff --git a/src/Components/Users/UserFilter.tsx b/src/Components/Users/UserFilter.tsx index 0e6c4f75812..a6579a158de 100644 --- a/src/Components/Users/UserFilter.tsx +++ b/src/Components/Users/UserFilter.tsx @@ -123,6 +123,7 @@ export default function UserFilter(props: any) {
Role o.role + ((o.readOnly && " (Read Only)") || "")}