Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add weekly working hours field for users #5981

Merged
merged 2 commits into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 117 additions & 19 deletions src/Components/Users/ManageUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";

import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover";
import ButtonV2 from "../Common/components/ButtonV2";
import ButtonV2, { Submit } from "../Common/components/ButtonV2";
import CareIcon from "../../CAREUI/icons/CareIcon";
import ConfirmHomeFacilityUpdateDialog from "./ConfirmHomeFacilityUpdateDialog";
import CountBlock from "../../CAREUI/display/Count";
Expand All @@ -36,6 +36,7 @@ import useFilters from "../../Common/hooks/useFilters";
import useWindowDimensions from "../../Common/hooks/useWindowDimensions";
import CircularProgress from "../Common/components/CircularProgress.js";
import Page from "../Common/components/Page.js";
import TextFormField from "../Form/FormFields/TextFormField.js";

const Loading = loadable(() => import("../Common/Loading"));

Expand All @@ -59,10 +60,12 @@ export default function ManageUsers() {
const [districtName, setDistrictName] = useState<string>();
const [expandFacilityList, setExpandFacilityList] = useState(false);
const [selectedUser, setSelectedUser] = useState<any | null>(null);
const [expandWorkingHours, setExpandWorkingHours] = useState(false);
const state: any = useSelector((state) => state);
const { currentUser } = state;
const isSuperuser = currentUser.data.is_superuser;
const userType = currentUser.data.user_type;
const [weeklyHours, setWeeklyHours] = useState<any>(0);
const userIndex = USER_TYPES.indexOf(userType);
const userTypes = isSuperuser
? [...USER_TYPES]
Expand Down Expand Up @@ -143,6 +146,30 @@ export default function ManageUsers() {
setUserData({ show: false, username: "", name: "" });
};

const handleWorkingHourSubmit = async () => {
const username = selectedUser;
if (!username || weeklyHours < 0 || weeklyHours > 168) return;
const res = await dispatch(
partialUpdateUser(username, {
weekly_working_hours: weeklyHours,
})
);

if (res?.data) {
Notification.Success({
msg: "Working hours updated successfully",
});
setExpandWorkingHours(false);
setSelectedUser(null);
} else {
Notification.Error({
msg: "Error while updating working hours: " + (res.data.detail || ""),
});
}
setWeeklyHours(0);
fetchData({ aborted: false });
};

const handleSubmit = async () => {
const username = userData.username;
const res = await dispatch(deleteUser(username));
Expand Down Expand Up @@ -194,10 +221,13 @@ export default function ManageUsers() {
id={`usr_${idx}`}
className=" mt-6 w-full md:px-4 lg:w-1/2 xl:w-1/3"
>
<div className="relative block h-full cursor-pointer overflow-visible rounded-lg bg-white shadow hover:border-primary-500">
<div className="flex h-full flex-col justify-between pb-36 sm:pb-28 md:pb-24">
<div className="relative block h-full overflow-visible rounded-lg bg-white shadow hover:border-primary-500">
<div className="flex h-full flex-col justify-between">
<div className="px-6 py-4">
<div className="flex flex-col flex-wrap justify-between gap-3 lg:flex-row">
<div
className="flex-wra p flex
flex-col justify-between gap-3 lg:flex-row"
>
{user.username && (
<div
id="username"
Expand Down Expand Up @@ -247,6 +277,7 @@ export default function ManageUsers() {
variant="danger"
ghost
border
className="float-right"
onClick={() => handleDelete(user)}
>
Delete
Expand All @@ -259,10 +290,10 @@ export default function ManageUsers() {
isExtremeSmallScreen
? " flex-wrap "
: " flex-col justify-between md:flex-row "
} gap-2 md:grid md:grid-cols-4`}
} gap-2 md:grid md:grid-cols-2`}
>
{user.user_type && (
<div className="col-span-2">
<div className="col-span-1">
<UserDetails id="role" title="Role">
<div className="break-all font-semibold">
{user.user_type}
Expand All @@ -271,7 +302,7 @@ export default function ManageUsers() {
</div>
)}
{user.district_object && (
<div className="col-span-2">
<div className="col-span-1">
<UserDetails id="district" title="District">
<div className="font-semibold">
{user.district_object.name}
Expand All @@ -281,7 +312,7 @@ export default function ManageUsers() {
)}
{user.user_type === "Doctor" && (
<>
<div className="col-span-2">
<div className="col-span-1">
<UserDetails
id="doctor-qualification"
title="Qualification"
Expand All @@ -295,7 +326,7 @@ export default function ManageUsers() {
)}
</UserDetails>
</div>
<div className="col-span-2">
<div className="col-span-1">
<UserDetails id="doctor-experience" title="Experience">
{user.doctor_experience_commenced_on ? (
<span className="font-semibold">
Expand Down Expand Up @@ -339,11 +370,11 @@ export default function ManageUsers() {
className={`${
isExtremeSmallScreen
? "flex flex-wrap "
: "grid grid-cols-4 "
: "grid grid-cols-2 "
}`}
>
{user.created_by && (
<div className="col-span-2">
<div className="col-span-1">
<UserDetails id="created_by" title="Created by">
<div className="break-all font-semibold">
{user.created_by}
Expand All @@ -352,7 +383,7 @@ export default function ManageUsers() {
</div>
)}
{user.username && (
<div className="col-span-2">
<div className="col-span-1">
<UserDetails id="home_facility" title="Home Facility">
<span className="block font-semibold">
{user.home_facility_object?.name ||
Expand All @@ -362,8 +393,21 @@ export default function ManageUsers() {
</div>
)}
</div>
{user.username && (
<div className="absolute bottom-0 left-0 flex w-full flex-col justify-between gap-2 p-4 sm:bottom-6 md:flex-row">
<div>
<UserDetails id="working-hours" title="Weekly working hours">
{user.weekly_working_hours ? (
<span className="font-semibold">
{user.weekly_working_hours} hours
</span>
) : (
<span className="text-gray-600">-</span>
)}
</UserDetails>
</div>
</div>
{user.username && (
<div className="mb-0 mt-auto flex w-full flex-col justify-between gap-2 p-4">
<div className="flex flex-col md:flex-row">
<ButtonV2
id="facilities"
className="flex w-full items-center md:w-1/2"
Expand All @@ -375,6 +419,7 @@ export default function ManageUsers() {
<CareIcon className="care-l-hospital text-lg" />
<p>Linked Facilities</p>
</ButtonV2>
<div className="mx-1 my-2 sm:my-0"></div>
<ButtonV2
id="skills"
className="flex w-full items-center md:w-1/2"
Expand All @@ -387,8 +432,24 @@ export default function ManageUsers() {
<p>Linked Skills</p>
</ButtonV2>
</div>
)}
</div>
{["DistrictAdmin", "StateAdmin"].includes(userType) && (
<div className="flex-col md:flex-row">
<ButtonV2
id="skills"
className="flex w-full items-center md:w-full"
onClick={() => {
setExpandWorkingHours(true);
setSelectedUser(user.username);
setWeeklyHours(user.weekly_working_hours);
}}
>
<CareIcon className="care-l-clock text-xl" />
<p>Set weekly working hours</p>
</ButtonV2>
</div>
)}
</div>
)}
</div>
</div>
</div>
Expand All @@ -397,7 +458,7 @@ export default function ManageUsers() {

if (isLoading || !users) {
manageUsers = <Loading />;
} else if (users && users.length) {
} else if (users?.length) {
manageUsers = (
<div>
<div className="flex flex-wrap md:-mx-4">{userList}</div>
Expand Down Expand Up @@ -433,6 +494,43 @@ export default function ManageUsers() {
>
<UserFacilities user={selectedUser} />
</SlideOverCustom>
<SlideOverCustom
open={expandWorkingHours}
setOpen={setExpandWorkingHours}
slideFrom="right"
title="Weekly working hours"
dialogClass="md:w-[400px]"
onCloseClick={() => {
setWeeklyHours(0);
}}
>
<div className="px-2">
<dt className="mb-3 text-sm font-medium leading-5 text-black">
Set weekly working hours for {selectedUser}
</dt>
<TextFormField
name="weekly_working_hours"
id="weekly_working_hours"
value={weeklyHours}
onChange={(e) => {
setWeeklyHours(e.value);
}}
error={
weeklyHours < 0 || weeklyHours > 168
? "Weekly working hours should be between 0 and 168"
: ""
}
required
label=""
type="number"
min={0}
max={168}
/>
<div className="mt-2 text-right">
<Submit onClick={handleWorkingHourSubmit} label="Update" />
</div>
</div>
</SlideOverCustom>

<div className="m-4 mt-5 grid grid-cols-1 sm:grid-cols-3 md:gap-5 md:px-2">
<CountBlock
Expand Down Expand Up @@ -628,7 +726,7 @@ function UserFacilities(props: { user: any }) {
{user?.home_facility_object && (
<div className="mt-2">
<div className="mb-2 ml-2 text-lg font-bold">Home Facility</div>
<div className="relative cursor-pointer rounded p-2 transition hover:bg-gray-200 focus:bg-gray-200 md:rounded-lg">
<div className="relative rounded p-2 transition hover:bg-gray-200 focus:bg-gray-200 md:rounded-lg">
<div className="flex items-center justify-between">
<span>{user?.home_facility_object?.name}</span>
<div className="flex items-center gap-2">
Expand Down Expand Up @@ -672,7 +770,7 @@ function UserFacilities(props: { user: any }) {
id={`facility_${i}`}
key={`facility_${i}`}
className={classNames(
"relative cursor-pointer rounded p-2 transition hover:bg-gray-200 focus:bg-gray-200 md:rounded-lg"
"relative rounded p-2 transition hover:bg-gray-200 focus:bg-gray-200 md:rounded-lg"
)}
>
<div className="flex items-center justify-between">
Expand Down
71 changes: 53 additions & 18 deletions src/Components/Users/UserProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type EditForm = {
doctor_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;
Expand All @@ -57,6 +58,7 @@ const initForm: EditForm = {
doctor_qualification: undefined,
doctor_experience_commenced_on: undefined,
doctor_medical_council_registration: undefined,
weekly_working_hours: undefined,
};

const initError: EditForm = Object.assign(
Expand Down Expand Up @@ -151,6 +153,7 @@ export default function UserProfile() {
),
doctor_medical_council_registration:
res.data.doctor_medical_council_registration,
weekly_working_hours: res.data.weekly_working_hours,
};
dispatch({
type: "set_form",
Expand Down Expand Up @@ -251,6 +254,20 @@ export default function UserProfile() {
invalidForm = true;
}
return;
case "weekly_working_hours":
if (!states.form[field]) {
errors[field] = "This field is required";
invalidForm = true;
} else if (
Number(states.form[field]) < 0 ||
Number(states.form[field]) > 168 ||
!/^\d+$/.test(states.form[field] ?? "")
) {
errors[field] =
"Weekly working hours must be a number between 0 and 168";
invalidForm = true;
}
return;
}
});
dispatch({ type: "set_error", errors });
Expand Down Expand Up @@ -306,6 +323,7 @@ export default function UserProfile() {
details.user_type === "Doctor"
? states.form.doctor_medical_council_registration
: undefined,
weekly_working_hours: states.form.weekly_working_hours,
};
const res = await dispatchAction(partialUpdateUser(username, data));
if (res && res.data) {
Expand Down Expand Up @@ -513,25 +531,33 @@ export default function UserProfile() {
{details.state_object?.name || "-"}
</dd>
</div>
<div className="my-2 sm:col-span-1">
<dt className="text-sm font-medium leading-5 text-black">
Skills
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<div className="flex flex-wrap gap-2">
{details.skills && details.skills.length
? details.skills?.map((skill: SkillObjectModel) => {
return (
<span className="flex items-center gap-2 rounded-full border-gray-300 bg-gray-200 px-3 text-xs text-gray-700">
<p className="py-1.5">{skill.name}</p>
</span>
);
})
: "-"}
</div>
</dd>
</div>
<div className="my-2 sm:col-span-1">
<dt className="text-sm font-medium leading-5 text-black">
Weekly working hours
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
{details.weekly_working_hours ?? "-"}
</dd>
</div>
</dl>
<div className="my-2 sm:col-span-1">
<dt className="text-sm font-medium leading-5 text-black">
Skills
</dt>
<dd className="mt-1 text-sm leading-5 text-gray-900">
<div className="flex flex-wrap gap-2">
{details.skills && details.skills.length
? details.skills?.map((skill: SkillObjectModel) => {
return (
<span className="flex items-center gap-2 rounded-full border-gray-300 bg-gray-200 px-3 text-xs text-gray-700">
<p className="py-1.5">{skill.name}</p>
</span>
);
})
: "-"}
</div>
</dd>
</div>
</div>
)}

Expand Down Expand Up @@ -622,6 +648,15 @@ export default function UserProfile() {
/>
</>
)}
<TextFormField
{...fieldProps("weekly_working_hours")}
required
label="Weekly working hours"
className="col-span-6 sm:col-span-3"
type="number"
min={0}
max={168}
/>
</div>
</div>
<div className="bg-gray-50 px-4 py-3 text-right sm:px-6">
Expand Down
Loading