From 7fff2999f7856fec462dc762c3a39fcc73a9fb26 Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Thu, 17 Oct 2024 08:58:32 +0530 Subject: [PATCH 1/9] Add field validation to resolve the Update Profile Issue --- package-lock.json | 138 +-------------------------- src/Components/Users/UserProfile.tsx | 53 +++++++++- 2 files changed, 54 insertions(+), 137 deletions(-) diff --git a/package-lock.json b/package-lock.json index aa99337d8d6..a0fee9a566c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -115,6 +115,7 @@ "apps/care_livekit_fe": { "name": "care-livekit", "version": "0.0.1", + "extraneous": true, "license": "ISC", "dependencies": { "@livekit/components-react": "^2.6.2", @@ -1779,11 +1780,6 @@ "node": ">=6.9.0" } }, - "node_modules/@bufbuild/protobuf": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz", - "integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==" - }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2647,64 +2643,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@livekit/components-core": { - "version": "0.11.9", - "resolved": "https://registry.npmjs.org/@livekit/components-core/-/components-core-0.11.9.tgz", - "integrity": "sha512-LPE1BZ+YTaqsVqGy/GAlpiO5rEI8XpEaf1TQcGdZN1BCBas9hTHt7/aHMbHQJ0K5xuAFQx8is6dFe451T4qXIQ==", - "dependencies": { - "@floating-ui/dom": "1.6.11", - "loglevel": "1.9.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "livekit-client": "^2.5.7", - "tslib": "^2.6.2" - } - }, - "node_modules/@livekit/components-react": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@livekit/components-react/-/components-react-2.6.5.tgz", - "integrity": "sha512-G3BpBlKy+lWTV9MH3/oBTBC17Z8CWqZ9GnjcG/xmYI0IvqmY89tVWph7cj2Bq0taniA+mD3U9EMPr68fOb1m1g==", - "dependencies": { - "@livekit/components-core": "0.11.9", - "clsx": "2.1.1", - "usehooks-ts": "3.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@livekit/krisp-noise-filter": "^0.2.12", - "livekit-client": "^2.5.7", - "react": ">=18", - "react-dom": ">=18", - "tslib": "^2.6.2" - }, - "peerDependenciesMeta": { - "@livekit/krisp-noise-filter": { - "optional": true - } - } - }, - "node_modules/@livekit/components-styles": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@livekit/components-styles/-/components-styles-1.1.4.tgz", - "integrity": "sha512-QCupn7tQ/dy/WZclrfsgtDe8peiGYS6Ied1IGkKOysaXo04l90t62SIUTKyxgd0dNDhUDC0p34qCggGZs/44lQ==", - "engines": { - "node": ">=18" - } - }, - "node_modules/@livekit/protocol": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.24.0.tgz", - "integrity": "sha512-9dCsqnkMn7lvbI4NGh18zhLDsrXyUcpS++TEFgEk5Xv1WM3R2kT3EzqgL1P/mr3jaabM6rJ8wZA/KJLuQNpF5w==", - "dependencies": { - "@bufbuild/protobuf": "^1.10.0" - } - }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -6195,10 +6133,6 @@ "node": ">=6" } }, - "node_modules/care-livekit": { - "resolved": "apps/care_livekit_fe", - "link": true - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -10932,26 +10866,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/livekit-client": { - "version": "2.5.9", - "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.5.9.tgz", - "integrity": "sha512-oDpK6SKYB1F+mNO+25DA0bF0cD2XoOJeD8ji4YQpzDBQv2IxeyKrQhoqXAqrYgIKuiMNkImSf+yg2v7EHSl4Og==", - "dependencies": { - "@livekit/protocol": "1.24.0", - "events": "^3.3.0", - "loglevel": "^1.8.0", - "sdp-transform": "^2.14.1", - "ts-debounce": "^4.0.0", - "tslib": "2.7.0", - "typed-emitter": "^2.1.0", - "webrtc-adapter": "^9.0.0" - } - }, - "node_modules/livekit-client/node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, "node_modules/load-plugin": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-6.0.3.tgz", @@ -11052,7 +10966,8 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", @@ -11138,18 +11053,6 @@ "node": ">=8" } }, - "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } - }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -16101,14 +16004,6 @@ "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" }, - "node_modules/sdp-transform": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.2.tgz", - "integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==", - "bin": { - "sdp-verify": "checker.js" - } - }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -17329,11 +17224,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-debounce": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ts-debounce/-/ts-debounce-4.0.0.tgz", - "integrity": "sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==" - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -17501,14 +17391,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", - "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", - "optionalDependencies": { - "rxjs": "*" - } - }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -18043,20 +17925,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/usehooks-ts": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.0.tgz", - "integrity": "sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw==", - "dependencies": { - "lodash.debounce": "^4.0.8" - }, - "engines": { - "node": ">=16.15.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index 22f2a0e2607..5ca5c0221b4 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -1,4 +1,4 @@ -import { useState, useReducer, FormEvent } from "react"; +import { useState, useReducer, FormEvent, useMemo } from "react"; import { GENDER_TYPES } from "../../Common/constants"; import { validateEmailAddress } from "../../Common/validation"; import * as Notification from "../../Utils/Notifications.js"; @@ -182,6 +182,51 @@ export default function UserProfile() { }, ); + const hasChanges = useMemo(() => { + if (!userData) return false; + + const fieldsToCheck: (keyof EditForm)[] = [ + "altPhoneNumber", + "date_of_birth", + "doctor_experience_commenced_on", + "doctor_medical_council_registration", + "email", + "firstName", + "gender", + "lastName", + "phoneNumber", + "qualification", + "video_connect_link", + "weekly_working_hours", + ]; + + return fieldsToCheck.some((field) => { + if (field === "date_of_birth") { + return ( + dayjs(states.form[field]).format("YYYY-MM-DD") !== + dayjs(userData[field]).format("YYYY-MM-DD") + ); + } + if (field === "doctor_experience_commenced_on") { + const userDataYears = dayjs().diff(dayjs(userData[field]), "years"); + return Number(states.form[field]) !== Number(userDataYears); + } + if (field === "weekly_working_hours") + return Number(states.form[field]) !== Number(userData[field]); + if (field === "firstName") + return states.form[field] !== userData.first_name; + if (field === "lastName") + return states.form[field] !== userData.last_name; + if (field === "phoneNumber") + return states.form[field] !== userData.phone_number; + if (field === "altPhoneNumber") + return states.form[field] !== userData.alt_phone_number; + if (field === "video_connect_link") + return states.form[field] !== userData[field] && !!states.form[field]; + return states.form[field] !== userData[field]; + }); + }, [states.form, userData]); + const validateNewPassword = (password: string) => { if ( password.length < 8 || @@ -788,7 +833,11 @@ export default function UserProfile() {
- +
From 5fee85e55cb07e6d1132448a6c33369e6ef084be Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Thu, 17 Oct 2024 11:09:40 +0530 Subject: [PATCH 2/9] refactor(profile): migrated change detection from useMemo to useEffect for cleaner comparison logic --- src/Components/Users/UserProfile.tsx | 60 +++++++--------------------- 1 file changed, 14 insertions(+), 46 deletions(-) diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index 5ca5c0221b4..a0739a4db38 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -1,4 +1,4 @@ -import { useState, useReducer, FormEvent, useMemo } from "react"; +import { useState, useReducer, FormEvent, useEffect } from "react"; import { GENDER_TYPES } from "../../Common/constants"; import { validateEmailAddress } from "../../Common/validation"; import * as Notification from "../../Utils/Notifications.js"; @@ -117,6 +117,8 @@ export default function UserProfile() { isChecking: false, isUpdateAvailable: false, }); + const [hasChanges, setHasChanges] = useState(false); + const [isFieldLoaded, setIsFieldLoaded] = useState(false); const authUser = useAuthUser(); @@ -172,6 +174,7 @@ export default function UserProfile() { type: "set_form", form: formData, }); + setHasChanges(false); }, }); @@ -182,51 +185,6 @@ export default function UserProfile() { }, ); - const hasChanges = useMemo(() => { - if (!userData) return false; - - const fieldsToCheck: (keyof EditForm)[] = [ - "altPhoneNumber", - "date_of_birth", - "doctor_experience_commenced_on", - "doctor_medical_council_registration", - "email", - "firstName", - "gender", - "lastName", - "phoneNumber", - "qualification", - "video_connect_link", - "weekly_working_hours", - ]; - - return fieldsToCheck.some((field) => { - if (field === "date_of_birth") { - return ( - dayjs(states.form[field]).format("YYYY-MM-DD") !== - dayjs(userData[field]).format("YYYY-MM-DD") - ); - } - if (field === "doctor_experience_commenced_on") { - const userDataYears = dayjs().diff(dayjs(userData[field]), "years"); - return Number(states.form[field]) !== Number(userDataYears); - } - if (field === "weekly_working_hours") - return Number(states.form[field]) !== Number(userData[field]); - if (field === "firstName") - return states.form[field] !== userData.first_name; - if (field === "lastName") - return states.form[field] !== userData.last_name; - if (field === "phoneNumber") - return states.form[field] !== userData.phone_number; - if (field === "altPhoneNumber") - return states.form[field] !== userData.alt_phone_number; - if (field === "video_connect_link") - return states.form[field] !== userData[field] && !!states.form[field]; - return states.form[field] !== userData[field]; - }); - }, [states.form, userData]); - const validateNewPassword = (password: string) => { if ( password.length < 8 || @@ -369,8 +327,18 @@ export default function UserProfile() { type: "set_form", form: { ...states.form, [event.name]: event.value }, }); + setIsFieldLoaded(true); }; + useEffect(() => { + if (showEdit && isFieldLoaded) { + setHasChanges(true); + } else { + setIsFieldLoaded(false); + setHasChanges(false); + } + }, [handleFieldChange]); + const getDate = (value: any) => value && dayjs(value).isValid() && dayjs(value).toDate(); From 7b775a3482fe299988a27c21c39e411a0dd0e47a Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Thu, 17 Oct 2024 12:52:07 +0530 Subject: [PATCH 3/9] Discard package-lock.json file --- package-lock.json | 138 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a0fee9a566c..aa99337d8d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -115,7 +115,6 @@ "apps/care_livekit_fe": { "name": "care-livekit", "version": "0.0.1", - "extraneous": true, "license": "ISC", "dependencies": { "@livekit/components-react": "^2.6.2", @@ -1780,6 +1779,11 @@ "node": ">=6.9.0" } }, + "node_modules/@bufbuild/protobuf": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz", + "integrity": "sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2643,6 +2647,64 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@livekit/components-core": { + "version": "0.11.9", + "resolved": "https://registry.npmjs.org/@livekit/components-core/-/components-core-0.11.9.tgz", + "integrity": "sha512-LPE1BZ+YTaqsVqGy/GAlpiO5rEI8XpEaf1TQcGdZN1BCBas9hTHt7/aHMbHQJ0K5xuAFQx8is6dFe451T4qXIQ==", + "dependencies": { + "@floating-ui/dom": "1.6.11", + "loglevel": "1.9.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "livekit-client": "^2.5.7", + "tslib": "^2.6.2" + } + }, + "node_modules/@livekit/components-react": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@livekit/components-react/-/components-react-2.6.5.tgz", + "integrity": "sha512-G3BpBlKy+lWTV9MH3/oBTBC17Z8CWqZ9GnjcG/xmYI0IvqmY89tVWph7cj2Bq0taniA+mD3U9EMPr68fOb1m1g==", + "dependencies": { + "@livekit/components-core": "0.11.9", + "clsx": "2.1.1", + "usehooks-ts": "3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@livekit/krisp-noise-filter": "^0.2.12", + "livekit-client": "^2.5.7", + "react": ">=18", + "react-dom": ">=18", + "tslib": "^2.6.2" + }, + "peerDependenciesMeta": { + "@livekit/krisp-noise-filter": { + "optional": true + } + } + }, + "node_modules/@livekit/components-styles": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@livekit/components-styles/-/components-styles-1.1.4.tgz", + "integrity": "sha512-QCupn7tQ/dy/WZclrfsgtDe8peiGYS6Ied1IGkKOysaXo04l90t62SIUTKyxgd0dNDhUDC0p34qCggGZs/44lQ==", + "engines": { + "node": ">=18" + } + }, + "node_modules/@livekit/protocol": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/@livekit/protocol/-/protocol-1.24.0.tgz", + "integrity": "sha512-9dCsqnkMn7lvbI4NGh18zhLDsrXyUcpS++TEFgEk5Xv1WM3R2kT3EzqgL1P/mr3jaabM6rJ8wZA/KJLuQNpF5w==", + "dependencies": { + "@bufbuild/protobuf": "^1.10.0" + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -6133,6 +6195,10 @@ "node": ">=6" } }, + "node_modules/care-livekit": { + "resolved": "apps/care_livekit_fe", + "link": true + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -10866,6 +10932,26 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/livekit-client": { + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/livekit-client/-/livekit-client-2.5.9.tgz", + "integrity": "sha512-oDpK6SKYB1F+mNO+25DA0bF0cD2XoOJeD8ji4YQpzDBQv2IxeyKrQhoqXAqrYgIKuiMNkImSf+yg2v7EHSl4Og==", + "dependencies": { + "@livekit/protocol": "1.24.0", + "events": "^3.3.0", + "loglevel": "^1.8.0", + "sdp-transform": "^2.14.1", + "ts-debounce": "^4.0.0", + "tslib": "2.7.0", + "typed-emitter": "^2.1.0", + "webrtc-adapter": "^9.0.0" + } + }, + "node_modules/livekit-client/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, "node_modules/load-plugin": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/load-plugin/-/load-plugin-6.0.3.tgz", @@ -10966,8 +11052,7 @@ "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", @@ -11053,6 +11138,18 @@ "node": ">=8" } }, + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -16004,6 +16101,14 @@ "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" }, + "node_modules/sdp-transform": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.14.2.tgz", + "integrity": "sha512-icY6jVao7MfKCieyo1AyxFYm1baiM+fA00qW/KrNNVlkxHAd34riEKuEkUe4bBb3gJwLJZM+xT60Yj1QL8rHiA==", + "bin": { + "sdp-verify": "checker.js" + } + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -17224,6 +17329,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-debounce": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ts-debounce/-/ts-debounce-4.0.0.tgz", + "integrity": "sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==" + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -17391,6 +17501,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "optionalDependencies": { + "rxjs": "*" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -17925,6 +18043,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-3.1.0.tgz", + "integrity": "sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", From 391dcab244286e67f17514cc9b1995d93e3cba73 Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Thu, 17 Oct 2024 15:26:45 +0530 Subject: [PATCH 4/9] fix: prevent isDirty from updating on load due to phone number validation --- src/Components/Users/UserProfile.tsx | 29 +++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index a0739a4db38..d4f440f5a94 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -1,4 +1,4 @@ -import { useState, useReducer, FormEvent, useEffect } from "react"; +import { useState, useReducer, FormEvent } from "react"; import { GENDER_TYPES } from "../../Common/constants"; import { validateEmailAddress } from "../../Common/validation"; import * as Notification from "../../Utils/Notifications.js"; @@ -117,8 +117,7 @@ export default function UserProfile() { isChecking: false, isUpdateAvailable: false, }); - const [hasChanges, setHasChanges] = useState(false); - const [isFieldLoaded, setIsFieldLoaded] = useState(false); + const [dirty, setDirty] = useState(false); const authUser = useAuthUser(); @@ -174,7 +173,7 @@ export default function UserProfile() { type: "set_form", form: formData, }); - setHasChanges(false); + setDirty(false); }, }); @@ -323,21 +322,25 @@ export default function UserProfile() { }; const handleFieldChange = (event: FieldChangeEvent) => { + console.log(event); + console.log(userData); dispatch({ type: "set_form", form: { ...states.form, [event.name]: event.value }, }); - setIsFieldLoaded(true); - }; - useEffect(() => { - if (showEdit && isFieldLoaded) { - setHasChanges(true); + const isMobileNumberModified = + (event.name === "phoneNumber" || event.name === "altPhoneNumber") && + (event.value == userData?.alt_phone_number || + event.value == userData?.phone_number); + + if (isMobileNumberModified) { + console.log(states.form); + setDirty(false); } else { - setIsFieldLoaded(false); - setHasChanges(false); + setDirty(true); } - }, [handleFieldChange]); + }; const getDate = (value: any) => value && dayjs(value).isValid() && dayjs(value).toDate(); @@ -804,7 +807,7 @@ export default function UserProfile() { From e39adc5b93d620f510b3ba1520c26f247688771e Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Thu, 17 Oct 2024 15:29:47 +0530 Subject: [PATCH 5/9] removed unnecessary console logging --- src/Components/Users/UserProfile.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index d4f440f5a94..f3e2a78ef57 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -322,8 +322,6 @@ export default function UserProfile() { }; const handleFieldChange = (event: FieldChangeEvent) => { - console.log(event); - console.log(userData); dispatch({ type: "set_form", form: { ...states.form, [event.name]: event.value }, @@ -335,7 +333,6 @@ export default function UserProfile() { event.value == userData?.phone_number); if (isMobileNumberModified) { - console.log(states.form); setDirty(false); } else { setDirty(true); From 1c3c76932fc97b26c9aab6786b3d50f5524cc397 Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Thu, 17 Oct 2024 20:16:49 +0530 Subject: [PATCH 6/9] fix: removed 'useEffect' from 'PhoneNumberFormField.tsx' Component --- .../Form/FormFields/PhoneNumberFormField.tsx | 4 ---- src/Components/Users/UserProfile.tsx | 12 +----------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/Components/Form/FormFields/PhoneNumberFormField.tsx b/src/Components/Form/FormFields/PhoneNumberFormField.tsx index c46d2690ad3..ccdf1799fbc 100644 --- a/src/Components/Form/FormFields/PhoneNumberFormField.tsx +++ b/src/Components/Form/FormFields/PhoneNumberFormField.tsx @@ -92,10 +92,6 @@ export default function PhoneNumberFormField(props: Props) { } }, [setValue]); - useEffect(() => { - setValue(field.value || "+91"); - }, []); - return ( From 7700550ba51f583b97f7415bb1d343d702d90835 Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Tue, 22 Oct 2024 04:12:59 +0530 Subject: [PATCH 7/9] fix: Update a condition to check falsy value --- src/Components/Form/FormFields/PhoneNumberFormField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Form/FormFields/PhoneNumberFormField.tsx b/src/Components/Form/FormFields/PhoneNumberFormField.tsx index ccdf1799fbc..270ad00e9bb 100644 --- a/src/Components/Form/FormFields/PhoneNumberFormField.tsx +++ b/src/Components/Form/FormFields/PhoneNumberFormField.tsx @@ -178,7 +178,7 @@ const formatPhoneNumber = ( value: string | undefined, types: PhoneNumberType[], ) => { - if (value == null) { + if (!value) { return "+91 "; } From 950ce0aa9ad87c9d0e4cb82e07d1038001094ca0 Mon Sep 17 00:00:00 2001 From: JavidSumra Date: Tue, 22 Oct 2024 21:03:59 +0530 Subject: [PATCH 8/9] fix style for overflow export button --- src/Components/Patient/ManagePatients.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index c6bc3352115..5f5f4876e86 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -838,7 +838,7 @@ export const PatientManager = () => {

-
+
{ }} > -

Doctor Connect

+

Doctor Connect

)} @@ -913,7 +913,6 @@ export const PatientManager = () => { className="mr-5 w-full lg:w-fit" > - Export ) : ( Date: Sat, 26 Oct 2024 15:18:45 +0530 Subject: [PATCH 9/9] changing track files --- src/components/Users/UserProfile.tsx | 1023 ++++++++++++++++++++++++++ 1 file changed, 1023 insertions(+) create mode 100644 src/components/Users/UserProfile.tsx diff --git a/src/components/Users/UserProfile.tsx b/src/components/Users/UserProfile.tsx new file mode 100644 index 00000000000..7aa3c9eff34 --- /dev/null +++ b/src/components/Users/UserProfile.tsx @@ -0,0 +1,1023 @@ +import { useState, useReducer, FormEvent } from "react"; +import { GENDER_TYPES, LocalStorageKeys } from "@/common/constants"; +import { validateEmailAddress } from "@/common/validation"; +import * as Notification from "../../Utils/Notifications"; +import LanguageSelector from "@/components/Common/LanguageSelector"; +import TextFormField from "../Form/FormFields/TextFormField"; +import ButtonV2, { Submit } from "@/components/Common/components/ButtonV2"; +import { + classNames, + dateQueryString, + formatDate, + formatDisplayName, + isValidUrl, + parsePhoneNumber, + sleep, +} from "@/Utils/utils"; +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 { GenderType, SkillModel, UpdatePasswordForm } from "./models"; +import UpdatableApp, { checkForUpdate } from "@/components/Common/UpdatableApp"; +import dayjs from "../../Utils/dayjs"; +import useAuthUser, { useAuthContext } 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"; +import DateFormField from "../Form/FormFields/DateFormField"; +import { validateRule } from "./UserAdd"; +import { useTranslation } from "react-i18next"; +import AvatarEditable from "@/components/Common/AvatarEditable"; +import Page from "@/components/Common/components/Page"; +import Loading from "@/components/Common/Loading"; +import AvatarEditModal from "@/components/Common/AvatarEditModal"; +import uploadFile from "@/Utils/request/uploadFile"; +import careConfig from "@careConfig"; + +type EditForm = { + firstName: string; + lastName: string; + date_of_birth: Date | null | string; + gender: GenderType; + email: string; + video_connect_link: string | undefined; + phoneNumber: string; + altPhoneNumber: string; + user_type: string | undefined; + qualification: string | undefined; + doctor_experience_commenced_on: number | string | undefined; + doctor_medical_council_registration: string | undefined; + weekly_working_hours: string | null | undefined; +}; +type ErrorForm = { + firstName: string; + lastName: string; + date_of_birth: string | null; + gender: string; + email: string; + video_connect_link: string | undefined; + phoneNumber: string; + altPhoneNumber: string; + user_type: string | undefined; + qualification: string | undefined; + doctor_experience_commenced_on: number | string | undefined; + doctor_medical_council_registration: string | undefined; + weekly_working_hours: string | undefined; +}; +type State = { + form: EditForm; + errors: ErrorForm; +}; +type Action = + | { type: "set_form"; form: EditForm } + | { type: "set_error"; errors: ErrorForm }; + +const initForm: EditForm = { + firstName: "", + lastName: "", + date_of_birth: null, + gender: "Male", + video_connect_link: "", + email: "", + phoneNumber: "", + altPhoneNumber: "", + user_type: "", + qualification: undefined, + doctor_experience_commenced_on: undefined, + doctor_medical_council_registration: undefined, + weekly_working_hours: undefined, +}; + +const initError: ErrorForm = Object.assign( + {}, + ...Object.keys(initForm).map((k) => ({ [k]: "" })), +); + +const initialState: State = { + form: { ...initForm }, + errors: { ...initError }, +}; + +const editFormReducer = (state: State, action: Action) => { + switch (action.type) { + case "set_form": { + return { + ...state, + form: action.form, + }; + } + case "set_error": { + return { + ...state, + errors: action.errors, + }; + } + } +}; + +export default function UserProfile() { + const { t } = useTranslation(); + const { signOut, refetchUser } = useAuthContext(); + const [states, dispatch] = useReducer(editFormReducer, initialState); + const [editAvatar, setEditAvatar] = useState(false); + const [updateStatus, setUpdateStatus] = useState({ + isChecking: false, + isUpdateAvailable: false, + }); + const [dirty, setDirty] = useState(false); + + const authUser = useAuthUser(); + + const [changePasswordForm, setChangePasswordForm] = useState<{ + username: string; + old_password: string; + new_password_1: string; + new_password_2: string; + }>({ + username: authUser.username, + old_password: "", + new_password_1: "", + new_password_2: "", + }); + + const [changePasswordErrors] = useState<{ + old_password: string; + password_confirmation: string; + }>({ + old_password: "", + password_confirmation: "", + }); + + const [showEdit, setShowEdit] = useState(false); + const { + data: userData, + loading: isUserLoading, + refetch: refetchUserData, + } = useQuery(routes.currentUser, { + onResponse: (result) => { + if (!result || !result.res || !result.data) return; + + const formData: EditForm = { + firstName: result.data.first_name, + lastName: result.data.last_name, + date_of_birth: result.data.date_of_birth || null, + gender: result.data.gender || "Male", + email: result.data.email, + video_connect_link: result.data.video_connect_link, + phoneNumber: result.data.phone_number?.toString() || "", + altPhoneNumber: result.data.alt_phone_number?.toString() || "", + user_type: result.data.user_type, + qualification: result.data.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, + }); + setDirty(false); + }, + }); + + const { data: skillsView, loading: isSkillsLoading } = useQuery( + routes.userListSkill, + { + pathParams: { username: authUser.username }, + }, + ); + + const validateNewPassword = (password: string) => { + if ( + password.length < 8 || + !/\d/.test(password) || + password === password.toUpperCase() || + password === password.toLowerCase() + ) { + return false; + } + return true; + }; + + const validateForm = () => { + const errors = { ...initError }; + let invalidForm = false; + Object.keys(states.form).forEach((field) => { + switch (field) { + case "firstName": + case "lastName": + case "gender": + if (!states.form[field]) { + errors[field] = t("field_required"); + invalidForm = true; + } + return; + case "date_of_birth": + if (!states.form[field]) { + errors[field] = "Enter a valid date of birth"; + invalidForm = true; + } else if ( + !dayjs(states.form[field]).isValid() || + dayjs(states.form[field]).isAfter(dayjs().subtract(17, "year")) + ) { + errors[field] = "Enter a valid date of birth"; + invalidForm = true; + } + return; + case "phoneNumber": + // eslint-disable-next-line no-case-declarations + const phoneNumber = parsePhoneNumber(states.form[field]); + + // eslint-disable-next-line no-case-declarations + let is_valid = false; + if (phoneNumber) { + is_valid = PhoneNumberValidator()(phoneNumber) === undefined; + } + + if (!states.form[field] || !is_valid) { + errors[field] = "Please enter valid phone number"; + invalidForm = true; + } + return; + case "altPhoneNumber": + // eslint-disable-next-line no-case-declarations + let alt_is_valid = false; + if (states.form[field] && states.form[field] !== "+91") { + const altPhoneNumber = parsePhoneNumber(states.form[field]); + if (altPhoneNumber) { + alt_is_valid = + PhoneNumberValidator(["mobile"])(altPhoneNumber) === undefined; + } + } + + if ( + states.form[field] && + states.form[field] !== "+91" && + !alt_is_valid + ) { + errors[field] = "Please enter valid mobile number"; + invalidForm = true; + } + return; + case "email": + if (!states.form[field]) { + errors[field] = t("field_required"); + invalidForm = true; + } else if (!validateEmailAddress(states.form[field])) { + errors[field] = "Enter a valid email address"; + invalidForm = true; + } + return; + case "doctor_experience_commenced_on": + if (states.form.user_type === "Doctor" && !states.form[field]) { + errors[field] = t("field_required"); + invalidForm = true; + } else if ( + (states.form.user_type === "Doctor" && + Number(states.form.doctor_experience_commenced_on) >= 100) || + Number(states.form.doctor_experience_commenced_on) < 0 + ) { + errors[field] = + "Doctor experience should be at least 0 years and less than 100 years."; + invalidForm = true; + } + return; + case "qualification": + if ( + (states.form.user_type === "Doctor" || + states.form.user_type === "Nurse") && + !states.form[field] + ) { + errors[field] = t("field_required"); + invalidForm = true; + } + return; + case "doctor_medical_council_registration": + if (states.form.user_type === "Doctor" && !states.form[field]) { + errors[field] = t("field_required"); + invalidForm = true; + } + return; + case "weekly_working_hours": + if ( + states.form[field] && + (Number(states.form[field]) < 0 || + Number(states.form[field]) > 168 || + !/^\d+$/.test(states.form[field] ?? "")) + ) { + errors[field] = + "Average weekly working hours must be a number between 0 and 168"; + invalidForm = true; + } + return; + case "video_connect_link": + if (states.form[field]) { + if (isValidUrl(states.form[field]) === false) { + errors[field] = "Please enter a valid url"; + invalidForm = true; + } + } + return; + } + }); + dispatch({ type: "set_error", errors }); + return !invalidForm; + }; + + const handleFieldChange = (event: FieldChangeEvent) => { + dispatch({ + type: "set_form", + form: { ...states.form, [event.name]: event.value }, + }); + setDirty(true); + }; + + const getDate = (value: any) => + value && dayjs(value).isValid() && dayjs(value).toDate(); + + const fieldProps = (name: string) => { + return { + name, + id: name, + value: (states.form as any)[name], + onChange: handleFieldChange, + error: (states.errors as any)[name], + }; + }; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + const validForm = validateForm(); + if (validForm) { + const data = { + username: authUser.username, + first_name: states.form.firstName, + last_name: states.form.lastName, + email: states.form.email, + video_connect_link: states.form.video_connect_link, + phone_number: parsePhoneNumber(states.form.phoneNumber) ?? "", + alt_phone_number: parsePhoneNumber(states.form.altPhoneNumber) ?? "", + gender: states.form.gender, + date_of_birth: dateQueryString(states.form.date_of_birth), + qualification: + states.form.user_type === "Doctor" || + states.form.user_type === "Nurse" + ? states.form.qualification + : undefined, + doctor_experience_commenced_on: + states.form.user_type === "Doctor" + ? dayjs() + .subtract( + parseInt( + (states.form.doctor_experience_commenced_on as string) ?? + "0", + ), + "years", + ) + .format("YYYY-MM-DD") + : undefined, + doctor_medical_council_registration: + states.form.user_type === "Doctor" + ? states.form.doctor_medical_council_registration + : undefined, + weekly_working_hours: + states.form.weekly_working_hours && + states.form.weekly_working_hours !== "" + ? states.form.weekly_working_hours + : null, + }; + const { res } = await request(routes.partialUpdateUser, { + pathParams: { username: authUser.username }, + body: data, + }); + if (res?.ok) { + Notification.Success({ + msg: "Details updated successfully", + }); + await refetchUserData(); + setShowEdit(false); + } + } + }; + + const isLoading = isUserLoading || isSkillsLoading; + + if (isLoading) { + return ; + } + + const checkUpdates = async () => { + setUpdateStatus({ ...updateStatus, isChecking: true }); + await new Promise((resolve) => setTimeout(resolve, 500)); + if ((await checkForUpdate()) != null) { + setUpdateStatus({ + isUpdateAvailable: true, + isChecking: false, + }); + } else { + setUpdateStatus({ + isUpdateAvailable: false, + isChecking: false, + }); + Notification.Success({ + msg: "No update available", + }); + } + }; + + const changePassword = async (e: any) => { + e.preventDefault(); + //validating form + if ( + changePasswordForm.new_password_1 !== changePasswordForm.new_password_2 + ) { + Notification.Error({ + msg: "Passwords are different in new password and confirmation password column.", + }); + } else if (!validateNewPassword(changePasswordForm.new_password_1)) { + Notification.Error({ + msg: "Entered New Password is not valid, please check!", + }); + } else if ( + changePasswordForm.new_password_1 === changePasswordForm.old_password + ) { + Notification.Error({ + msg: "New password is same as old password, Please enter a different new password.", + }); + } else { + const form: UpdatePasswordForm = { + old_password: changePasswordForm.old_password, + username: authUser.username, + new_password: changePasswordForm.new_password_1, + }; + const { res, data, error } = await request(routes.updatePassword, { + body: form, + }); + if (res?.ok) { + Notification.Success({ msg: data?.message }); + } else if (!error) { + Notification.Error({ + msg: "There was some error. Please try again in some time.", + }); + } + setChangePasswordForm({ + ...changePasswordForm, + new_password_1: "", + new_password_2: "", + old_password: "", + }); + } + }; + + const handleAvatarUpload = async (file: File, onError: () => void) => { + const formData = new FormData(); + formData.append("profile_picture", file); + const url = `${careConfig.apiUrl}/api/v1/users/${authUser.username}/profile_picture/`; + + uploadFile( + url, + formData, + "POST", + { + Authorization: + "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken), + }, + async (xhr: XMLHttpRequest) => { + if (xhr.status === 200) { + await sleep(1000); + refetchUser(); + Notification.Success({ msg: "Profile picture updated." }); + setEditAvatar(false); + } + }, + null, + () => { + onError(); + }, + ); + }; + + const handleAvatarDelete = async (onError: () => void) => { + const { res } = await request(routes.deleteProfilePicture, { + pathParams: { username: authUser.username }, + }); + if (res?.ok) { + Notification.Success({ msg: "Profile picture deleted" }); + await refetchUser(); + setEditAvatar(false); + } else { + onError(); + } + }; + + return ( + + setEditAvatar(false)} + /> +
+
+

+ {t("local_body")}, {t("district")}, {t("state")}{" "} + {t("are_non_editable_fields")}. +

+
+ setEditAvatar(!editAvatar)} + className="h-20 w-20" + /> +
+

+ {authUser?.first_name} {authUser?.last_name} +

+

+ @{authUser?.username} +

+
+
+
+ setShowEdit(!showEdit)} + type="button" + id="edit-cancel-profile-button" + > + {showEdit ? t("cancel") : t("edit_user_profile")} + + + + {t("sign_out")} + +
+
+
+ {!showEdit && !isLoading && ( +
+
+
+
+ {t("username")} +
+
+ {userData?.username || "-"} +
+
+
+
+ {t("phone_number")} +
+
+ {userData?.phone_number || "-"} +
+
+ +
+
+ {t("whatsapp_number")} +
+
+ {userData?.alt_phone_number || "-"} +
+
+
+
+ {t("email")} +
+
+ {userData?.email || "-"} +
+
+
+
+ {t("first_name")} +
+
+ {userData?.first_name || "-"} +
+
+
+
+ {t("last_name")} +
+
+ {userData?.last_name || "-"} +
+
+
+
+ {t("date_of_birth")} +
+
+ {userData?.date_of_birth + ? formatDate(userData?.date_of_birth) + : "-"} +
+
+
+
+ {t("access_level")} +
+
+ + {userData?.user_type || "-"} +
+
+
+
+ {t("gender")} +
+
+ {userData?.gender || "-"} +
+
+
+
+ {t("local_body")} +
+
+ {userData?.local_body_object?.name || "-"} +
+
+
+
+ {t("district")} +
+
+ {userData?.district_object?.name || "-"} +
+
+
+
+ {t("state")} +
+
+ {userData?.state_object?.name || "-"} +
+
+
+
+ {t("skills")} +
+
+
+ {skillsView?.results?.length + ? skillsView.results?.map((skill: SkillModel) => { + return ( + +

+ {skill.skill_object.name} +

+
+ ); + }) + : "-"} +
+
+
+
+
+ {t("average_weekly_working_hours")} +
+
+ {userData?.weekly_working_hours ?? "-"} +
+
+ +
+
+ )} + {showEdit && ( +
+
+
+
+
+ + + + o.text} + optionValue={(o) => o.text} + options={GENDER_TYPES} + /> + + + + {(states.form.user_type === "Doctor" || + states.form.user_type === "Nurse") && ( + + )} + {states.form.user_type === "Doctor" && ( + <> + + + + )} + + +
+
+
+ +
+
+
+
+
+
+
+ + setChangePasswordForm({ + ...changePasswordForm, + old_password: e.value, + }) + } + error={changePasswordErrors.old_password} + required + /> +
+ { + setChangePasswordForm({ + ...changePasswordForm, + new_password_1: e.value, + }); + }} + required + /> +
+ {validateRule( + changePasswordForm.new_password_1?.length >= 8, + "Password should be atleast 8 characters long", + )} + {validateRule( + changePasswordForm.new_password_1 !== + changePasswordForm.new_password_1.toUpperCase(), + "Password should contain at least 1 lowercase letter", + )} + {validateRule( + changePasswordForm.new_password_1 !== + changePasswordForm.new_password_1.toLowerCase(), + "Password should contain at least 1 uppercase letter", + )} + {validateRule( + /\d/.test(changePasswordForm.new_password_1), + "Password should contain at least 1 number", + )} +
+
+
+ { + setChangePasswordForm({ + ...changePasswordForm, + new_password_2: e.value, + }); + }} + /> + {changePasswordForm.new_password_2.length > 0 && ( +
+ {validateRule( + changePasswordForm.new_password_1 === + changePasswordForm.new_password_2, + "Confirm password should match the new password", + )} +
+ )} +
+
+
+
+ +
+
+
+
+ )} +
+
+ +
+
+
+

+ {t("language_selection")} +

+

+ {t("set_your_local_language")} +

+
+
+
+ +
+
+
+
+
+

+ {t("software_update")} +

+

+ {t("check_for_available_update")} +

+
+
+ {updateStatus.isUpdateAvailable && ( + + +
+ + {t("update_available")} +
+
+
+ )} +
+ {!updateStatus.isUpdateAvailable && ( + + {" "} +
+ + {updateStatus.isChecking + ? t("checking_for_update") + : t("check_for_update")} +
+
+ )} +
+
+
+ ); +}