Skip to content

Commit

Permalink
Adds user type: Nurse and Nurse (readonly) (ohcnetwork#6464)
Browse files Browse the repository at this point in the history
* adds nurse user type, fixes ohcnetwork#6240

* Nurse/Staff can now create doctors

* Staff user: disallow link in patient list

* fixes ohcnetwork#7002; restrict access to external results for Nurse and Staff

* Hide External Results tab and pages from Staff and Nurse

---------

Co-authored-by: Mohammed Nihal <[email protected]>
Co-authored-by: Aakash Singh <[email protected]>
  • Loading branch information
3 people authored Jan 11, 2024
1 parent f62ea5d commit e187977
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 62 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Authenticate to staging API with any of the following credentials

- username: staffdev
password: Coronasafe@123
role: Staff
role: Nurse

- username: doctordev
password: Coronasafe@123
Expand Down
22 changes: 4 additions & 18 deletions src/Common/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export type UserRole =
| "Volunteer"
| "StaffReadOnly"
| "Staff"
| "NurseReadOnly"
| "Nurse"
| "Doctor"
| "WardAdmin"
| "LocalBodyAdmin"
Expand All @@ -47,6 +49,8 @@ export const USER_TYPE_OPTIONS: {
{ id: "Volunteer", role: "Volunteer", readOnly: false },
{ id: "StaffReadOnly", role: "Staff", readOnly: true },
{ id: "Staff", role: "Staff", readOnly: false },
{ id: "NurseReadOnly", role: "Nurse", readOnly: true },
{ id: "Nurse", role: "Nurse", readOnly: false },
{ id: "Doctor", role: "Doctor", readOnly: false },
{ id: "WardAdmin", role: "Ward Admin", readOnly: false },
{ id: "LocalBodyAdmin", role: "Local Body Admin", readOnly: false },
Expand Down Expand Up @@ -418,24 +422,6 @@ export const SAMPLE_FLOW_RULES = {
RECEIVED_AT_LAB: ["COMPLETED"],
};

export const ROLE_STATUS_MAP = {
Staff: ["SENT_TO_COLLECTON_CENTRE"],
DistrictAdmin: [
"APPROVED",
"DENIED",
"SENT_TO_COLLECTON_CENTRE",
"RECEIVED_AND_FORWARED",
],
StateLabAdmin: [
"APPROVED",
"DENIED",
"SENT_TO_COLLECTON_CENTRE",
"RECEIVED_AND_FORWARED",
"RECEIVED_AT_LAB",
"COMPLETED",
],
};

export const DISEASE_STATUS = [
"POSITIVE",
"SUSPECTED",
Expand Down
41 changes: 25 additions & 16 deletions src/Components/Common/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useConfig from "../../../Common/hooks/useConfig";
import SlideOver from "../../../CAREUI/interactive/SlideOver";
import { classNames } from "../../../Utils/utils";
import { Link } from "raviger";
import useAuthUser from "../../../Common/hooks/useAuthUser";

export const SIDEBAR_SHRINK_PREFERENCE_KEY = "sidebarShrinkPreference";

Expand All @@ -27,28 +28,36 @@ type StatelessSidebarProps =
onItemClick: (open: boolean) => void;
};

const NavItems = [
{ text: "Facilities", to: "/facility", icon: "care-l-hospital" },
{ text: "Patients", to: "/patients", icon: "care-l-user-injured" },
{ text: "Assets", to: "/assets", icon: "care-l-shopping-cart-alt" },
{ text: "Sample Test", to: "/sample", icon: "care-l-medkit" },
{ text: "Shifting", to: "/shifting", icon: "care-l-ambulance" },
{ text: "Resource", to: "/resource", icon: "care-l-heart-medical" },
{
text: "External Results",
to: "/external_results",
icon: "care-l-clipboard-notes",
},
{ text: "Users", to: "/users", icon: "care-l-users-alt" },
{ text: "Notice Board", to: "/notice_board", icon: "care-l-meeting-board" },
];

const StatelessSidebar = ({
shrinkable = false,
shrinked = false,
setShrinked,
onItemClick,
}: StatelessSidebarProps) => {
const authUser = useAuthUser();

const NavItems = [
{ text: "Facilities", to: "/facility", icon: "care-l-hospital" },
{ text: "Patients", to: "/patients", icon: "care-l-user-injured" },
{ text: "Assets", to: "/assets", icon: "care-l-shopping-cart-alt" },
{ text: "Sample Test", to: "/sample", icon: "care-l-medkit" },
{ text: "Shifting", to: "/shifting", icon: "care-l-ambulance" },
{ text: "Resource", to: "/resource", icon: "care-l-heart-medical" },
...(!["Nurse", "NurseReadOnly", "Staff", "StaffReadOnly"].includes(
authUser.user_type
)
? [
{
text: "External Results",
to: "/external_results",
icon: "care-l-clipboard-notes",
},
]
: []),
{ text: "Users", to: "/users", icon: "care-l-users-alt" },
{ text: "Notice Board", to: "/notice_board", icon: "care-l-meeting-board" },
];

const { main_logo } = useConfig();
const activeLink = useActiveLink();
const Item = shrinked ? ShrinkedSidebarItem : SidebarItem;
Expand Down
26 changes: 19 additions & 7 deletions src/Components/ExternalResult/ResultList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ import Page from "../Common/components/Page";
import routes from "../../Redux/api";
import useQuery from "../../Utils/request/useQuery";
import { parsePhoneNumber } from "../../Utils/utils";
import useAuthUser from "../../Common/hooks/useAuthUser";
import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
const Loading = lazy(() => import("../Common/Loading"));

export default function ResultList() {
const authUser = useAuthUser();
const {
qParams,
updateQuery,
Expand Down Expand Up @@ -165,6 +168,10 @@ export default function ResultList() {
<td className="whitespace-nowrap px-6 py-4 text-left text-sm leading-5 text-gray-500">
<ButtonV2
variant="primary"
disabled={
authUser.user_type === "Nurse" || authUser.user_type === "Staff"
}
authorizeFor={NonReadOnlyUsers}
border
ghost
onClick={() => {
Expand Down Expand Up @@ -226,13 +233,18 @@ export default function ResultList() {
<ExportMenu
label="Import/Export"
exportItems={[
{
label: "Import Results",
action: () => navigate("/external_results/upload"),
options: {
icon: <CareIcon className="care-l-import" />,
},
},
...(authUser.user_type !== "Nurse" &&
authUser.user_type !== "Staff"
? [
{
label: "Import Results",
action: () => navigate("/external_results/upload"),
options: {
icon: <CareIcon className="care-l-import" />,
},
},
]
: []),
{
label: "Export Results",
action: () =>
Expand Down
4 changes: 2 additions & 2 deletions src/Components/Facility/DoctorVideoSlideover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ export default function DoctorVideoSlideover(props: {
home: true,
},
{
title: "Staff",
user_type: "Staff",
title: "Nurse",
user_type: "Nurse",
home: true,
},
{
Expand Down
22 changes: 16 additions & 6 deletions src/Components/Patient/ManagePatients.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,9 @@ export const PatientManager = () => {
? PATIENT_CATEGORIES.find((c) => c.text === category)?.twClass
: "patient-unknown";

return (
<Link
key={`usr_${patient.id}`}
data-cy="patient"
href={patientUrl}
className={`ring/0 hover:ring/100 group relative w-full cursor-pointer rounded-lg bg-white p-4 pl-5 text-black shadow transition-all duration-200 ease-in-out hover:pl-5 ${categoryClass}-ring overflow-hidden`}
const children = (
<div
className={`ring/0 hover:ring/100 group relative w-full rounded-lg bg-white p-4 pl-5 text-black shadow transition-all duration-200 ease-in-out hover:pl-5 ${categoryClass}-ring overflow-hidden`}
>
<div
className={`absolute inset-y-0 left-0 flex h-full w-1 items-center rounded-l-lg transition-all duration-200 ease-in-out group-hover:w-5 ${categoryClass}`}
Expand Down Expand Up @@ -744,6 +741,19 @@ export const PatientManager = () => {
</div>
)}
</div>
</div>
);

if (
authUser.user_type === "Staff" ||
authUser.user_type === "StaffReadOnly"
) {
return children;
}

return (
<Link key={`usr_${patient.id}`} data-cy="patient" href={patientUrl}>
{children}
</Link>
);
});
Expand Down
6 changes: 6 additions & 0 deletions src/Components/Patient/PatientRegister.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import useConfig from "../../Common/hooks/useConfig";
import { useDispatch } from "react-redux";
import { validatePincode } from "../../Common/validation";
import { FormContextValue } from "../Form/FormContext.js";
import useAuthUser from "../../Common/hooks/useAuthUser.js";

const Loading = lazy(() => import("../Common/Loading"));
const PageTitle = lazy(() => import("../Common/PageTitle"));
Expand Down Expand Up @@ -180,6 +181,7 @@ const patientFormReducer = (state = initialState, action: any) => {
};

export const PatientRegister = (props: PatientRegisterProps) => {
const authUser = useAuthUser();
const { goBack } = useAppHistory();
const { gov_data_api_key, enable_hcx, enable_abdm } = useConfig();
const dispatchAction: any = useDispatch();
Expand Down Expand Up @@ -1173,6 +1175,10 @@ export const PatientRegister = (props: PatientRegisterProps) => {
<div className="mb-2 overflow-visible rounded border border-gray-200 p-4">
<ButtonV2
className="flex items-center gap-2"
disabled={
authUser.user_type === "Nurse" ||
authUser.user_type === "Staff"
}
onClick={(_) => {
setShowImport({
show: true,
Expand Down
31 changes: 20 additions & 11 deletions src/Components/Users/UserAdd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ const initForm: UserForm = {
doctor_medical_council_registration: undefined,
};

const STAFF_OR_NURSE_USER = [
"Staff",
"StaffReadOnly",
"Nurse",
"NurseReadOnly",
];

const initError = Object.assign(
{},
...Object.keys(initForm).map((k) => ({ [k]: "" }))
Expand Down Expand Up @@ -234,15 +241,19 @@ export const UserAdd = (props: UserProps) => {
: // Exception to allow Staff to Create Doctors
defaultAllowedUserTypes;

// TODO: refactor lines 227 through 248 to be more readable. This is messy.
if (authUser.user_type === "Nurse" || authUser.user_type === "Staff") {
userTypes.push(USER_TYPE_OPTIONS[6]); // Temperorily allows creation of users with elevated permissions due to introduction of new roles.
}

const headerText = !userId ? "Add User" : "Update User";
const buttonText = !userId ? "Save User" : "Update Details";
const showLocalbody = !(
state.form.user_type === "Pharmacist" ||
state.form.user_type === "Volunteer" ||
state.form.user_type === "Doctor" ||
state.form.user_type === "Staff" ||
state.form.user_type === "StaffReadOnly"
);
const showLocalbody = ![
"Pharmacist",
"Volunteer",
"Doctor",
...STAFF_OR_NURSE_USER,
].includes(state.form.user_type);

const { loading: isDistrictLoading } = useQuery(routes.getDistrictByState, {
prefetch: !!(selectedStateId > 0),
Expand Down Expand Up @@ -332,10 +343,8 @@ export const UserAdd = (props: UserProps) => {
case "facilities":
if (
state.form[field].length === 0 &&
(authUser.user_type === "Staff" ||
authUser.user_type === "StaffReadOnly") &&
(state.form["user_type"] === "Staff" ||
state.form["user_type"] === "StaffReadOnly")
STAFF_OR_NURSE_USER.includes(authUser.user_type) &&
STAFF_OR_NURSE_USER.includes(state.form.user_type)
) {
errors[field] =
"Please select atleast one of the facilities you are linked to";
Expand Down
11 changes: 10 additions & 1 deletion src/Routers/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import AssetRoutes from "./routes/AssetRoutes";
import ResourceRoutes from "./routes/ResourceRoutes";
import ExternalResultRoutes from "./routes/ExternalResultRoutes";
import { DetailRoute } from "./types";
import useAuthUser from "../Common/hooks/useAuthUser";

const Routes = {
"/": () => <Redirect to="/facility" />,

...AssetRoutes,
...ConsultationRoutes,
...ExternalResultRoutes,
...FacilityRoutes,
...PatientRoutes,
...ResourceRoutes,
Expand All @@ -49,6 +49,7 @@ const Routes = {
};

export default function AppRouter() {
const authUser = useAuthUser();
const { main_logo, enable_hcx } = useConfig();

let routes = Routes;
Expand All @@ -57,6 +58,14 @@ export default function AppRouter() {
routes = { ...routes, ...HCXRoutes };
}

if (
!["Nurse", "NurseReadOnly", "Staff", "StaffReadOnly"].includes(
authUser.user_type
)
) {
routes = { ...routes, ...ExternalResultRoutes };
}

useRedirect("/user", "/users");
const pages = useRoutes(routes) || <Error404 />;
const path = usePath();
Expand Down

0 comments on commit e187977

Please sign in to comment.