From d9a510198eb6d7b3faf72d3b8265662d7c9e6c2a Mon Sep 17 00:00:00 2001 From: Aditya Jindal Date: Thu, 16 Jan 2025 10:33:47 +0530 Subject: [PATCH] Updated the User Management Design (#9954) --- public/locale/en.json | 3 + src/components/Facility/FacilityUsers.tsx | 236 +++++++++++++++------- src/components/Users/UserListAndCard.tsx | 161 +++++---------- 3 files changed, 217 insertions(+), 183 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index c827a055dd0..10179650df4 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -2112,6 +2112,8 @@ "use_phone_number_for_emergency": "Use this phone number for emergency contact", "user_add_error": "Error while adding User", "user_added_successfully": "User added successfully", + "user_count_one": "{{count}} user", + "user_count_other": "{{count}} users", "user_delete_error": "Error while deleting User", "user_deleted_successfully": "User Deleted Successfully", "user_deleted_successfuly": "User Deleted Successfully", @@ -2137,6 +2139,7 @@ "username_userdetails_not_found": "Unable to fetch details as username or user details not found", "username_valid": "Username is valid", "users": "Users", + "users_management": "Users Management", "vacant": "Vacant", "vaccinated": "Vaccinated", "vaccine_name": "Vaccine name", diff --git a/src/components/Facility/FacilityUsers.tsx b/src/components/Facility/FacilityUsers.tsx index 4d4caed7e6e..696e1d06b9b 100644 --- a/src/components/Facility/FacilityUsers.tsx +++ b/src/components/Facility/FacilityUsers.tsx @@ -2,104 +2,202 @@ import { useQuery } from "@tanstack/react-query"; import { useState } from "react"; import { useTranslation } from "react-i18next"; -import CountBlock from "@/CAREUI/display/Count"; +import CareIcon from "@/CAREUI/icons/CareIcon"; +import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import Page from "@/components/Common/Page"; -import UserListView from "@/components/Users/UserListAndCard"; +import UserListAndCardView from "@/components/Users/UserListAndCard"; import useFilters from "@/hooks/useFilters"; import routes from "@/Utils/request/api"; import query from "@/Utils/request/query"; +const UserCardSkeleton = () => ( +
+
+ {Array.from({ length: 6 }).map((_, i) => ( + + +
+
+ +
+
+ +
+ + +
+
+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+ ))} +
+
+); + +const UserListSkeleton = () => ( +
+ + {/* Header Skeleton */} + + + + + + + + + + {/* Body Skeleton */} + + {Array.from({ length: 7 }).map((_, i) => ( + + + + + + + + ))} + +
+ + + + + + + + + +
+
+ +
+ + +
+
+
+ + + + + + + +
+
+); + export default function FacilityUsers(props: { facilityId: string }) { const { t } = useTranslation(); const { qParams, updateQuery, Pagination } = useFilters({ - limit: 18, + limit: 15, cacheBlacklist: ["username"], }); - const [activeTab, setActiveTab] = useState(0); + const [activeTab, setActiveTab] = useState<"card" | "list">("card"); const { facilityId } = props; + let usersList: JSX.Element = <>; + const { data: userListData, isLoading: userListLoading } = useQuery({ - queryKey: ["facilityUsers", facilityId], - queryFn: query(routes.facility.getUsers, { + queryKey: ["facilityUsers", facilityId, qParams], + queryFn: query.debounced(routes.facility.getUsers, { pathParams: { facility_id: facilityId }, + queryParams: { + username: qParams.username, + limit: qParams.limit, + offset: (qParams.page - 1) * qParams.limit, + }, }), enabled: !!facilityId, }); - if (userListLoading) { - return ( -
- -
- -
- - -
-
-
- -
- - -
-
-
- {Array.from({ length: 6 }).map((_, i) => ( - - -
- -
-
-
- - -
- -
-
- - -
-
-
-
-
- ))} -
+ if (userListLoading || !userListData) { + usersList = + activeTab === "card" ? : ; + } else { + usersList = ( +
+ +
); } - if (!userListData) { - return
{t("no_users_found")}
; - } return ( - - - - updateQuery({ username })} - searchValue={qParams.username} - activeTab={activeTab} - onTabChange={setActiveTab} - /> - - + + {t("user_count", { count: userListData?.count ?? 0 })} + + } + > +
+
+ updateQuery({ username: e.target.value })} + value={qParams.username} + placeholder={t("search_by_username")} + className="w-full max-w-sm" + /> + setActiveTab(value as "card" | "list")} + className="ml-auto" + > + + +
+ + {t("card")} +
+
+ +
+ + {t("list")} +
+
+
+
+
+
{usersList}
); } diff --git a/src/components/Users/UserListAndCard.tsx b/src/components/Users/UserListAndCard.tsx index b1123f55d08..be13992ea2f 100644 --- a/src/components/Users/UserListAndCard.tsx +++ b/src/components/Users/UserListAndCard.tsx @@ -1,18 +1,15 @@ import { navigate } from "raviger"; import { useTranslation } from "react-i18next"; -import Card from "@/CAREUI/display/Card"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Badge } from "@/components/ui/badge"; +import { Card, CardContent } from "@/components/ui/card"; import { Avatar } from "@/components/Common/Avatar"; -import Tabs from "@/components/Common/Tabs"; -import SearchInput from "@/components/Form/SearchInput"; import useAuthUser from "@/hooks/useAuthUser"; import useSlug from "@/hooks/useSlug"; -import useWindowDimensions from "@/hooks/useWindowDimensions"; import { formatName, @@ -25,12 +22,13 @@ import { UserBase } from "@/types/user/user"; const GetDetailsButton = (username: string) => { const { t } = useTranslation(); const facilityId = useSlug("facility"); + return (
); }; -const getNameAndStatusCard = (user: UserBase, showDetailsButton = false) => { - return ( -
-
-
-
-

- {formatName(user)} -

- -
- - {user.username} - -
-
{showDetailsButton && GetDetailsButton(user.username)}
-
-
- ); -}; export const UserStatusIndicator = ({ user, @@ -102,55 +77,57 @@ export const UserStatusIndicator = ({ ); }; const UserCard = ({ user }: { user: UserBase }) => { - const userOnline = isUserOnline(user); - const { width } = useWindowDimensions(); - const mediumScreenBreakpoint = 640; - const isMediumScreen = width <= mediumScreenBreakpoint; - const isLessThanXLargeScreen = width <= 1280; const { t } = useTranslation(); return ( - -
-
-
-
- - {isMediumScreen && getNameAndStatusCard(user, userOnline)} -
-
- {!isMediumScreen && - getNameAndStatusCard(user, !isLessThanXLargeScreen)} -
-
-
{t("role")}
-
- {user.user_type} -
+ + +
+
+ +
+
+

+ {user.first_name} {user.last_name} +

+
+ + {user.username} + +
+ +
+
+
{t("role")}
+
+ {user.user_type ?? "-"} +
+
+
+
{t("phone_number")}
+
+ {formatPhoneNumber(user.phone_number) ?? "-"} +
+
+
+
{GetDetailsButton(user.username)}
- {isLessThanXLargeScreen && ( -
{GetDetailsButton(user.username)}
- )} -
+ ); }; export const UserGrid = ({ users }: { users?: UserBase[] }) => ( -
+
{users?.map((user) => )}
); @@ -236,66 +213,22 @@ export const UserList = ({ users }: { users?: UserBase[] }) => {
); }; -interface UserListViewProps { +interface UserListAndCardViewProps { users: UserBase[]; - onSearch: (username: string) => void; - searchValue: string; - activeTab: number; - onTabChange: (tab: number) => void; + activeTab: "card" | "list"; } -export default function UserListView({ +export default function UserListAndCardView({ users, - onSearch, - searchValue, activeTab, - onTabChange, -}: UserListViewProps) { +}: UserListAndCardViewProps) { const { t } = useTranslation(); return ( <> -
-
- onSearch(e.value)} - value={searchValue} - placeholder={t("search_by_username")} - /> -
- - - {t("card")} -
- ), - value: 0, - id: "user-card-view", - }, - { - text: ( -
- - {t("list")} -
- ), - value: 1, - id: "user-list-view", - }, - ]} - currentTab={activeTab} - onTabChange={(tab) => onTabChange(tab as number)} - className="float-right" - /> -
{users.length > 0 ? ( <> - {activeTab === 0 ? ( + {activeTab === "card" ? ( ) : (