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

fix: Limit number of facilities a single user can be linked to #7257

Merged
merged 1 commit into from Mar 4, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
227 changes: 131 additions & 96 deletions src/Components/Users/ManageUsers.tsx
@@ -1,42 +1,45 @@
import * as Notification from "../../Utils/Notifications.js";
import dayjs from "dayjs";
import { navigate } from "raviger";
import { lazy, useState } from "react";
import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover";
import ButtonV2, { Submit } from "../Common/components/ButtonV2";
import CareIcon from "../../CAREUI/icons/CareIcon";
import ConfirmHomeFacilityUpdateDialog from "./ConfirmHomeFacilityUpdateDialog";
import { useTranslation } from "react-i18next";
import CountBlock from "../../CAREUI/display/Count";
import { FacilityModel } from "../Facility/models";
import { FacilitySelect } from "../Common/FacilitySelect";
import SearchInput from "../Form/SearchInput";
import SkillsSlideOver from "./SkillsSlideOver";
import CareIcon from "../../CAREUI/icons/CareIcon";
import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover";
import SlideOverCustom from "../../CAREUI/interactive/SlideOver";
import { USER_TYPES } from "../../Common/constants";
import UnlinkFacilityDialog from "./UnlinkFacilityDialog";
import UserDeleteDialog from "./UserDeleteDialog";
import UserDetails from "../Common/UserDetails";
import UserDetailComponent from "../Common/UserDetailsComponet.js";
import UserFilter from "./UserFilter";
import useAuthUser from "../../Common/hooks/useAuthUser.js";
import useFilters from "../../Common/hooks/useFilters";
import useWindowDimensions from "../../Common/hooks/useWindowDimensions";
import routes from "../../Redux/api.js";
import * as Notification from "../../Utils/Notifications.js";
import request from "../../Utils/request/request.js";
import useQuery from "../../Utils/request/useQuery.js";
import {
classNames,
isUserOnline,
relativeTime,
showUserDelete,
} from "../../Utils/utils";
import { navigate } from "raviger";
import useFilters from "../../Common/hooks/useFilters";
import useWindowDimensions from "../../Common/hooks/useWindowDimensions";
import { FacilitySelect } from "../Common/FacilitySelect";
import Pagination from "../Common/Pagination";
import UserDetails from "../Common/UserDetails";
import UserDetailComponent from "../Common/UserDetailsComponet.js";
import ButtonV2, { Submit } from "../Common/components/ButtonV2";
import CircularProgress from "../Common/components/CircularProgress.js";
import Page from "../Common/components/Page.js";
import dayjs from "dayjs";
import { FacilityModel } from "../Facility/models";
import TextFormField from "../Form/FormFields/TextFormField.js";
import useAuthUser from "../../Common/hooks/useAuthUser.js";
import routes from "../../Redux/api.js";
import useQuery from "../../Utils/request/useQuery.js";
import request from "../../Utils/request/request.js";
import SearchInput from "../Form/SearchInput";
import ConfirmHomeFacilityUpdateDialog from "./ConfirmHomeFacilityUpdateDialog";
import SkillsSlideOver from "./SkillsSlideOver";
import UnlinkFacilityDialog from "./UnlinkFacilityDialog";
import UserDeleteDialog from "./UserDeleteDialog";
import UserFilter from "./UserFilter";

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

export default function ManageUsers() {
const { t } = useTranslation();
const { width } = useWindowDimensions();
const {
qParams,
Expand Down Expand Up @@ -108,7 +111,7 @@ export default function ManageUsers() {
onClick={() => navigate("/users/add")}
>
<CareIcon icon="l-plus" className="text-lg" />
<p>Add New User</p>
<p>{t("add_new_user")}</p>
</ButtonV2>
);

Expand Down Expand Up @@ -434,13 +437,13 @@ export default function ManageUsers() {
} else if (userListData?.results && userListData?.results.length === 0) {
manageUsers = (
<div>
<h5> No Users Found</h5>
<h5> {t("no_users_found")}</h5>
</div>
);
}

return (
<Page title="User Management" hideBack={true} breadcrumbs={false}>
<Page title={t("user_management")} hideBack={true} breadcrumbs={false}>
{expandSkillList && (
<SkillsSlideOver
show={expandSkillList}
Expand All @@ -452,11 +455,8 @@ export default function ManageUsers() {
open={expandFacilityList}
setOpen={setExpandFacilityList}
slideFrom="right"
title="Facilities"
title={t("facilities")}
dialogClass="md:w-[400px]"
onCloseClick={() => {
//fetchData({ aborted: false });
}}
>
<UserFacilities user={selectedUser} />
</SlideOverCustom>
Expand All @@ -468,12 +468,12 @@ export default function ManageUsers() {
setWeeklyHoursError("");
}}
slideFrom="right"
title="Average weekly working hours"
title={t("average_weekly_working_hours")}
dialogClass="md:w-[400px]"
>
<div className="px-2">
<dt className="mb-3 text-sm font-medium leading-5 text-black">
Set Average weekly working hours for {selectedUser}
{t("set_average_weekly_working_hours_for")} {selectedUser}
</dt>
<TextFormField
name="weekly_working_hours"
Expand All @@ -490,7 +490,7 @@ export default function ManageUsers() {
max={168}
/>
<div className="mt-2 text-right">
<Submit onClick={handleWorkingHourSubmit} label="Update" />
<Submit onClick={handleWorkingHourSubmit} label={t("update")} />
</div>
</div>
</SlideOverCustom>
Expand All @@ -510,7 +510,7 @@ export default function ManageUsers() {
name="username"
onChange={(e) => updateQuery({ [e.name]: e.value })}
value={qParams.username}
placeholder="Search by username"
placeholder={t("search_by_username")}
/>
</div>
<div className="flex flex-col gap-2">
Expand Down Expand Up @@ -557,9 +557,14 @@ export default function ManageUsers() {
}

function UserFacilities(props: { user: any }) {
const { t } = useTranslation();
const { user } = props;
const username = user.username;
const limit = 20;
const [isLoading, setIsLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [offset, setOffset] = useState(0);
const [totalCount, setTotalCount] = useState(0);
const [facility, setFacility] = useState<any>(null);
const [unlinkFacilityData, setUnlinkFacilityData] = useState<{
show: boolean;
Expand Down Expand Up @@ -602,8 +607,23 @@ function UserFacilities(props: { user: any }) {
refetch: refetchUserFacilities,
} = useQuery(routes.userListFacility, {
pathParams: { username },
query: {
limit,
offset,
},
onResponse: ({ res, data }) => {
if (res?.status === 200 && data) {
setTotalCount(data.count);
}
},
});

const handlePagination = (page: number, limit: number) => {
const offset = (page - 1) * limit;
setCurrentPage(page);
setOffset(offset);
};

const updateHomeFacility = async (username: string, facility: any) => {
setIsLoading(true);
const { res } = await request(routes.partialUpdateUser, {
Expand Down Expand Up @@ -678,7 +698,7 @@ function UserFacilities(props: { user: any }) {
className="mt-1 h-[45px] w-[74px] text-base"
onClick={() => addFacility(username, facility)}
>
Add
{t("add")}
</ButtonV2>
</div>
{isLoading || userFacilitiesLoading ? (
Expand All @@ -690,7 +710,9 @@ function UserFacilities(props: { user: any }) {
{/* Home Facility section */}
{user?.home_facility_object && (
<div className="mt-2" id="home-facility">
<div className="mb-2 ml-2 text-lg font-bold">Home Facility</div>
<div className="mb-2 ml-2 text-lg font-bold">
{t("home_facility")}
</div>
<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>
Expand All @@ -708,7 +730,7 @@ function UserFacilities(props: { user: any }) {
>
<CareIcon className="care-l-link-broken" />
<span className="tooltip-text tooltip-left">
Clear Home Facility
{t("clear_home_facility")}
</span>
</button>
</div>
Expand All @@ -719,77 +741,90 @@ function UserFacilities(props: { user: any }) {
)}

{/* Linked Facilities section */}
{!!userFacilities?.length && (
{!!userFacilities?.results.length && (
<div className="mt-2" id="linked-facility-list">
<div className="mb-2 ml-2 text-lg font-bold">
Linked Facilities
{t("linked_facilities")}
</div>
<div className="flex flex-col">
{userFacilities.map((facility: any, i: number) => {
if (user?.home_facility_object?.id === facility.id) {
// skip if it's a home facility
return null;
}
return (
<div
id={`facility_${i}`}
key={`facility_${i}`}
className={classNames(
"relative rounded p-2 transition hover:bg-gray-200 focus:bg-gray-200 md:rounded-lg"
)}
>
<div className="flex items-center justify-between">
<span>{facility.name}</span>
<div className="flex items-center gap-2">
<button
className="tooltip text-lg hover:text-primary-500"
id="home-facility-icon"
onClick={() => {
if (user?.home_facility_object) {
// has previous home facility
setReplaceHomeFacility({
{userFacilities.results.map(
(facility: FacilityModel, i: number) => {
if (user?.home_facility_object?.id === facility.id) {
// skip if it's a home facility
return null;
}
return (
<div
id={`facility_${i}`}
key={`facility_${i}`}
className={classNames(
"relative rounded p-2 transition hover:bg-gray-200 focus:bg-gray-200 md:rounded-lg"
)}
>
<div className="flex items-center justify-between">
<span>{facility.name}</span>
<div className="flex items-center gap-2">
<button
className="tooltip text-lg hover:text-primary-500"
id="home-facility-icon"
onClick={() => {
if (user?.home_facility_object) {
// has previous home facility
setReplaceHomeFacility({
show: true,
userName: username,
previousFacility:
user?.home_facility_object,
newFacility: facility,
});
} else {
// no previous home facility
updateHomeFacility(username, facility);
}
}}
>
<CareIcon className="care-l-estate" />
<span className="tooltip-text tooltip-left">
Set as home facility
</span>
</button>
<button
id="unlink-facility-button"
className="tooltip text-lg text-red-600"
onClick={() =>
setUnlinkFacilityData({
show: true,
facility: facility,
userName: username,
previousFacility: user?.home_facility_object,
newFacility: facility,
});
} else {
// no previous home facility
updateHomeFacility(username, facility);
isHomeFacility: false,
})
}
}}
>
<CareIcon className="care-l-estate" />
<span className="tooltip-text tooltip-left">
Set as home facility
</span>
</button>
<button
id="unlink-facility-button"
className="tooltip text-lg text-red-600"
onClick={() =>
setUnlinkFacilityData({
show: true,
facility: facility,
userName: username,
isHomeFacility: false,
})
}
>
<CareIcon className="care-l-link-broken" />
<span className="tooltip-text tooltip-left">
Unlink Facility
</span>
</button>
>
<CareIcon className="care-l-link-broken" />
<span className="tooltip-text tooltip-left">
Unlink Facility
</span>
</button>
</div>
</div>
</div>
</div>
);
})}
);
}
)}
</div>
{totalCount > limit && (
<div className="mt-4 flex w-full justify-center">
<Pagination
cPage={currentPage}
defaultPerPage={limit}
data={{ totalCount }}
onChange={handlePagination}
/>
</div>
)}
</div>
)}
{!user?.home_facility_object && !userFacilities?.length && (
{!user?.home_facility_object && !userFacilities?.results.length && (
<div className="my-2 flex h-96 flex-col content-center justify-center align-middle">
<div className="w-full">
<img
Expand All @@ -799,7 +834,7 @@ function UserFacilities(props: { user: any }) {
/>
</div>
<p className="pt-4 text-center text-lg font-semibold text-primary">
No Linked Facilities
{t("no_linked_facilities")}
</p>
</div>
)}
Expand Down
13 changes: 13 additions & 0 deletions src/Locale/en/Users.json
@@ -0,0 +1,13 @@
{
"user_management": "User Management",
"facilities": "Facilities",
"add_new_user": "Add New User",
"no_users_found": "No Users Found",
"home_facility": "Home Facility",
"clear_home_facility": "Clear Home Facility",
"linked_facilities": "Linked Facilities",
"no_linked_facilities": "No Linked Facilities",
"average_weekly_working_hours": "Average weekly working hours",
"set_average_weekly_working_hours_for": "Set Average weekly working hours for",
"search_by_username": "Search by username"
}