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: Redesign the User view of Department/Teams #10568

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 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
11 changes: 11 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@
"add_beds_to_configure_presets": "Add beds to this location to configure presets for them.",
"add_consultation": "Add consultation",
"add_consultation_update": "Add Consultation Update",
"add_department_team": "Add Department/Team",
"add_details_of_patient": "Add Details of Patient",
"add_exception": "Add Exception",
"add_facility": "Add Facility",
Expand Down Expand Up @@ -640,13 +641,16 @@
"create_add_more": "Create & Add More",
"create_asset": "Create Asset",
"create_consultation": "Create Consultation",
"create_department_team": "Create Department/Team",
"create_department_team_description": "Create a new department/team in this facility.",
"create_encounter": "Create Encounter",
"create_facility": "Create Facility",
"create_new": "Create New",
"create_new_asset": "Create New Asset",
"create_new_encounter": "Create a new encounter to get started",
"create_new_facility": "Create a new facility and add it to the organization.",
"create_new_tag": "Create New Tag",
"create_organization": "Create Organization",
"create_position_preset": "Create a new position preset",
"create_position_preset_description": "Creates a new position preset in Care from the current position of the camera for the given name",
"create_preset_prerequisite": "To create presets for this bed, you'll need to link the camera to the bed first.",
Expand Down Expand Up @@ -938,6 +942,8 @@
"end_time_before_start_error": "End time cannot be before start time",
"end_time_future_error": "End time cannot be in the future",
"ended": "Ended",
"enter_department_team_description": "Enter department/team description (optional)",
"enter_department_team_name": "Enter department/team name",
"enter_dosage_instructions": "Enter Dosage Instructions",
"enter_file_name": "Enter File Name",
"enter_message": "Start typing...",
Expand Down Expand Up @@ -1522,6 +1528,7 @@
"ordering": "Ordering",
"organization": "Organization",
"organization_access_help": "Organizations help you manage facilities, users, and resources efficiently. Contact your administrator to get access.",
"organization_created_successfully": "Organization created successfully",
"organization_for_care_support": "Organization for Care Support",
"organization_forbidden": "You don't have access to any organizations yet.",
"organization_not_found": "No Organizations Found",
Expand Down Expand Up @@ -1649,6 +1656,7 @@
"please_enter_correct_birth_year": "Please enter the correct birth year to verify the patient details.",
"please_enter_current_password": "Please enter your current password.",
"please_enter_new_password": "Please enter your new password.",
"please_enter_organization_name": "Please enter an organization name",
"please_enter_username": "Please enter the username",
"please_fix_errors": "Please fix the errors in the highlighted fields and try submitting again.",
"please_select_a_facility": "Please select a facility",
Expand Down Expand Up @@ -1921,6 +1929,7 @@
"search_by_patient_no": "Search by Patient Number",
"search_by_phone_number": "Search by Phone Number",
"search_by_resource_title": "Search by resource title",
"search_by_user_name": "Search by user name",
"search_by_username": "Search by username",
"search_country": "Search country...",
"search_encounters": "Search Encounters",
Expand Down Expand Up @@ -1974,6 +1983,7 @@
"select_method": "Select method",
"select_new_role": "Select New Role",
"select_organization": "Select Organization",
"select_organization_type": "Select organization type",
"select_patient": "Select Patient",
"select_policy": "Select an Insurance Policy",
"select_policy_to_add_items": "Select a Policy to Add Items",
Expand Down Expand Up @@ -2164,6 +2174,7 @@
"true": "True",
"try_again_later": "Try again later!",
"try_different_abha_linking_option": "Want to try a different linking option, here are some more:",
"type": "Type",
"type_any_extra_comments_here": "type any extra comments here",
"type_b_cylinders": "B Type Cylinders",
"type_c_cylinders": "C Type Cylinders",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { useQuery } from "@tanstack/react-query";
import { useQueryParams } from "raviger";
import { useState } from "react";
import { useTranslation } from "react-i18next";

import CareIcon from "@/CAREUI/icons/CareIcon";

import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";

import { Avatar } from "@/components/Common/Avatar";
import {
CardGridSkeleton,
CardListSkeleton,
} from "@/components/Common/SkeletonLoading";
import { CardGridSkeleton } from "@/components/Common/SkeletonLoading";
import { UserStatusIndicator } from "@/components/Users/UserListAndCard";

import useFilters from "@/hooks/useFilters";

import routes from "@/Utils/request/api";
import query from "@/Utils/request/query";
import AddUserSheet from "@/pages/Organization/components/AddUserSheet";
Expand All @@ -29,142 +30,182 @@ interface Props {
}

export default function FacilityOrganizationUsers({ id, facilityId }: Props) {
const [qParams, setQueryParams] = useQueryParams<{
const [sheetParams, setSheetParams] = useQueryParams<{
sheet: string;
username: string;
}>();
abhimanyurajeesh marked this conversation as resolved.
Show resolved Hide resolved
const { qParams, Pagination, resultsPerPage } = useFilters({
limit: 12,
});
const [searchQuery, setSearchQuery] = useState("");
const { t } = useTranslation();

const openAddUserSheet = qParams.sheet === "add";
const openLinkUserSheet = qParams.sheet === "link";
const openAddUserSheet = sheetParams.sheet === "add";
const openLinkUserSheet = sheetParams.sheet === "link";

const { data: users, isLoading: isLoadingUsers } = useQuery({
queryKey: ["facilityOrganizationUsers", facilityId, id],
queryFn: query(routes.facilityOrganization.listUsers, {
queryKey: [
"facilityOrganizationUsers",
facilityId,
id,
searchQuery,
qParams,
],
queryFn: query.debounced(routes.facilityOrganization.listUsers, {
pathParams: { facilityId, organizationId: id },
queryParams: {
search: searchQuery || undefined,
...(searchQuery
? {}
: {
limit: resultsPerPage,
offset: ((qParams.page || 1) - 1) * resultsPerPage,
}),
},
}),
enabled: !!id,
});

const filteredUsers = users?.results?.filter((userRole) => {
if (!searchQuery) return true;

const searchLower = searchQuery.toLowerCase();
const fullName =
`${userRole.user.first_name} ${userRole.user.last_name}`.toLowerCase();
const username = userRole.user.username.toLowerCase();

return fullName.includes(searchLower) || username.includes(searchLower);
});
abhimanyurajeesh marked this conversation as resolved.
Show resolved Hide resolved

if (!id) {
return null;
}

if (isLoadingUsers) {
return (
<FacilityOrganizationLayout id={id} facilityId={facilityId}>
<div className="grid gap-4">
<CardListSkeleton count={1} />
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<CardGridSkeleton count={6} />
</div>
</div>
</FacilityOrganizationLayout>
);
}

return (
<FacilityOrganizationLayout id={id} facilityId={facilityId}>
<div className="space-y-6">
<div className="flex justify-between items-center">
<h2 className="text-lg font-semibold">{t("users")}</h2>
<div className="flex justify-between items-center flex-col md:flex-row sm:items-center gap-4 w-full lg:justify-between">
<div className="relative">
<CareIcon
icon="l-search"
className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 h-4 w-4"
/>
<Input
placeholder={t("search_by_user_name")}
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
}}
className="w-full pl-8"
/>
</div>
<div className="flex gap-2">
<AddUserSheet
open={openAddUserSheet}
setOpen={(open) => {
setQueryParams({ sheet: open ? "add" : "", username: "" });
setSheetParams({ sheet: open ? "add" : "", username: "" });
}}
onUserCreated={(user) => {
setQueryParams({ sheet: "link", username: user.username });
setSheetParams({ sheet: "link", username: user.username });
}}
/>
<LinkFacilityUserSheet
facilityId={facilityId}
organizationId={id}
open={openLinkUserSheet}
setOpen={(open) => {
setQueryParams({ sheet: open ? "link" : "", username: "" });
setSheetParams({ sheet: open ? "link" : "", username: "" });
}}
preSelectedUsername={qParams.username}
preSelectedUsername={sheetParams.username}
/>
</div>
</div>

<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-4">
{users?.results?.length === 0 ? (
<Card className="col-span-full">
<CardContent className="p-6 text-center text-gray-500">
{t("no_users_found")}
</CardContent>
</Card>
) : (
users?.results?.map((userRole: OrganizationUserRole) => (
<Card key={userRole.id} className="h-full">
<CardContent className="p-4 sm:p-6">
<div className="flex flex-col h-full gap-4">
<div className="flex gap-4">
<Avatar
name={`${userRole.user.first_name} ${userRole.user.last_name}`}
imageUrl={userRole.user.profile_picture_url}
className="h-12 w-12 sm:h-16 sm:w-16 text-xl sm:text-2xl flex-shrink-0"
/>

<div className="flex flex-col min-w-0 flex-1">
<div className="flex flex-col gap-1">
<h1 className="text-base font-bold break-words pr-2">
{userRole.user.first_name} {userRole.user.last_name}
</h1>
<span className="text-sm text-gray-500">
<span className="mr-2 break-words">
{isLoadingUsers ? (
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 gap-4">
<CardGridSkeleton count={6} />
</div>
) : (
<div className="space-y-4">
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-2 gap-4">
{!filteredUsers?.length ? (
<Card className="col-span-full">
<CardContent className="p-6 text-center text-gray-500">
{t("no_users_found")}
</CardContent>
</Card>
) : (
filteredUsers?.map((userRole: OrganizationUserRole) => (
<Card key={userRole.id} className="h-full">
<CardContent className="p-4 sm:p-6 flex flex-col h-full">
<div className="flex items-start gap-3">
<Avatar
name={`${userRole.user.first_name} ${userRole.user.last_name}`}
imageUrl={userRole.user.profile_picture_url}
className="h-12 w-12 sm:h-14 sm:w-14 text-xl sm:text-2xl flex-shrink-0"
/>

<div className="flex flex-col min-w-0 flex-1">
<div className="flex flex-col gap-1">
<div className="flex items-start justify-between">
<h1 className="text-base font-bold break-words pr-2">
{userRole.user.first_name}{" "}
{userRole.user.last_name}
</h1>
<span className="text-sm text-gray-500">
<UserStatusIndicator user={userRole.user} />
</span>
</div>
<span className="text-sm text-gray-500 mr-2 break-words">
{userRole.user.username}
</span>
<UserStatusIndicator user={userRole.user} />
</span>
</div>
<div className="mt-4 -ml-12 sm:ml-0 grid grid-cols-2 gap-2 text-sm">
<div>
<div className="text-gray-500">{t("role")}</div>
<div className="font-medium truncate">
rithviknishad marked this conversation as resolved.
Show resolved Hide resolved
{userRole.role.name}
</div>
</div>
<div>
<div className="text-gray-500">
{t("phone_number")}
</div>
<div className="font-medium truncate">
{userRole.user.phone_number}
</div>
</div>
</div>
</div>
</div>
</div>

<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<div className="text-gray-500">{t("role")}</div>
<div className="font-medium truncate">
{userRole.role.name}
</div>
</div>
<div>
<div className="text-gray-500">{t("phone_number")}</div>
<div className="font-medium truncate">
{userRole.user.phone_number}
</div>
<div className="mt-2 -mx-2 -mb-2 sm:-mx-4 sm:-mb-4 rounded-md py-4 px-4 bg-gray-50 flex justify-end">
<EditFacilityUserRoleSheet
facilityId={facilityId}
organizationId={id}
userRole={userRole}
trigger={
<Button variant="outline" size="sm">
<span>{t("see_details")}</span>
</Button>
}
/>
</div>
</div>

<div className="mt-auto pt-2">
<EditFacilityUserRoleSheet
facilityId={facilityId}
organizationId={id}
userRole={userRole}
trigger={
<Button
variant="secondary"
size="sm"
className="w-full gap-2"
>
<CareIcon
icon="l-arrow-up-right"
className="h-4 w-4"
/>
<span>{t("more_details")}</span>
</Button>
}
/>
</div>
</div>
</CardContent>
</Card>
))
)}
</div>
</CardContent>
</Card>
))
)}
</div>

{(filteredUsers || []).length > 0 &&
users?.count &&
users.count > resultsPerPage && (
<div className="flex justify-center">
<Pagination totalCount={users.count} />
</div>
)}
</div>
)}
</div>
</FacilityOrganizationLayout>
);
Expand Down
Loading
Loading