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

Updated the User Management Design #9954

Merged
merged 30 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c175797
updated the user management design
AdityaJ2305 Jan 14, 2025
68ed9ba
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 14, 2025
16145f2
fix the small screen crash
AdityaJ2305 Jan 14, 2025
b789369
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 14, 2025
ada30bb
minor changes
AdityaJ2305 Jan 14, 2025
d8fda8c
Merge branch 'userList_design_update' of https://github.com/AdityaJ23…
AdityaJ2305 Jan 14, 2025
c1e3bc2
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 14, 2025
26ee976
coderabbit suggestion
AdityaJ2305 Jan 14, 2025
04d7899
Merge branch 'userList_design_update' of https://github.com/AdityaJ23…
AdityaJ2305 Jan 14, 2025
57db8e6
added skeleton view specific
AdityaJ2305 Jan 14, 2025
d801e1c
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 14, 2025
4bf5ff4
descriptive name
AdityaJ2305 Jan 14, 2025
bcdb66b
Merge branch 'userList_design_update' of https://github.com/AdityaJ23…
AdityaJ2305 Jan 14, 2025
e3a0c5d
nitpick sugg
AdityaJ2305 Jan 14, 2025
41b4cc5
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 14, 2025
8744d7c
coderabbit suggestion
AdityaJ2305 Jan 14, 2025
77324c5
coderabbit suggestion
AdityaJ2305 Jan 14, 2025
4204f63
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
a8dfcca
minor changes sugg by jeevan
AdityaJ2305 Jan 15, 2025
8b5ff83
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
0292a70
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
af5f017
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
783215a
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
0d7f6d9
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
4db7098
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 15, 2025
238411e
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 16, 2025
090bd1c
inline fix
AdityaJ2305 Jan 16, 2025
7ef0ea2
Merge branch 'userList_design_update' of https://github.com/AdityaJ23…
AdityaJ2305 Jan 16, 2025
33eb111
Merge branch 'develop' into userList_design_update
AdityaJ2305 Jan 16, 2025
c79b915
changed to 15 limit
AdityaJ2305 Jan 16, 2025
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
1 change: 1 addition & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2122,6 +2122,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",
Expand Down
230 changes: 162 additions & 68 deletions src/components/Facility/FacilityUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,104 +2,198 @@ 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 useFilters from "@/hooks/useFilters";

import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants";

import routes from "@/Utils/request/api";
import query from "@/Utils/request/query";

export default function FacilityUsers(props: { facilityId: string }) {
const { t } = useTranslation();
const { qParams, updateQuery, Pagination } = useFilters({
limit: 18,
limit: RESULTS_PER_PAGE_LIMIT,
AdityaJ2305 marked this conversation as resolved.
Show resolved Hide resolved
cacheBlacklist: ["username"],
});
const [activeTab, setActiveTab] = useState(0);
const [activeTab, setActiveTab] = useState<"card" | "list">("card");
const { facilityId } = props;

let usersList: JSX.Element = <></>;
const UserListSkeleton = () => (
AdityaJ2305 marked this conversation as resolved.
Show resolved Hide resolved
<div className="overflow-x-auto rounded-lg border border-gray-200">
<table className="relative min-w-full divide-y divide-gray-200">
{/* Header Skeleton */}
<thead>
<tr>
<th className="sticky top-0 z-10 bg-gray-100 px-4 py-3 text-left text-sm font-medium text-gray-600">
<Skeleton className="h-4 w-24" />
</th>
<th className="bg-gray-100 px-6 py-3 text-left text-sm font-medium text-gray-600">
<Skeleton className="h-4 w-16" />
</th>
<th className="bg-gray-100 px-10 py-3 text-left text-sm font-medium text-gray-600">
<Skeleton className="h-4 w-20" />
</th>
<th className="bg-gray-100 px-4 py-3 text-left text-sm font-medium text-gray-600">
<Skeleton className="h-4 w-24" />
</th>
<th className="bg-gray-100 px-4 py-3 text-left text-sm font-medium text-gray-600">
<Skeleton className="h-4 w-20" />
</th>
</tr>
</thead>
{/* Body Skeleton */}
<tbody className="divide-y divide-gray-200 bg-white">
{Array.from({ length: 7 }).map((_, i) => (
<tr key={i} className="hover:bg-gray-50">
<td className="sticky left-0 z-10 bg-white px-4 py-4 lg:pr-20">
<div className="flex items-center gap-3">
<Skeleton className="h-10 w-10 rounded-full" />
<div className="flex flex-col">
<Skeleton className="h-4 w-32 mb-1" />
<Skeleton className="h-3 w-20" />
</div>
</div>
</td>
<td className="flex-0 px-6 py-4">
<Skeleton className="h-4 w-16" />
</td>
<td className="px-10 py-4 text-sm">
<Skeleton className="h-4 w-20" />
</td>
<td className="px-4 py-4 text-sm whitespace-nowrap">
<Skeleton className="h-4 w-24" />
</td>
<td className="px-4 py-4">
<Skeleton className="h-8 w-20 rounded-md" />
</td>
</tr>
))}
</tbody>
</table>
</div>
);

const UserCardSkeleton = () => (
<div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<Card key={i} className="h-full">
<CardContent className="p-4 sm:p-6">
<div className="flex flex-col h-full gap-4">
<div className="flex gap-4">
<Skeleton className="h-12 w-12 sm:h-16 sm:w-16 rounded-full flex-shrink-0" />
AdityaJ2305 marked this conversation as resolved.
Show resolved Hide resolved
<div className="flex flex-col min-w-0 flex-1">
<div className="flex flex-col gap-1">
<Skeleton className="h-5 w-32 mb-1" />
<div className="flex items-center gap-2 flex-wrap">
<Skeleton className="h-4 w-20" />
<Skeleton className="h-4 w-16" />
</div>
</div>
</div>
</div>

<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<Skeleton className="h-4 w-16 mb-1" />
<Skeleton className="h-4 w-24" />
</div>
<div>
<Skeleton className="h-4 w-24 mb-1" />
<Skeleton className="h-4 w-16" />
</div>
</div>

<div className="mt-auto pt-2">
<Skeleton className="h-8 w-full rounded-md" />
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
);
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 (
<div className="px-6">
<Skeleton className="h-8 w-32 mb-4" />
<div className="flex items-center mb-4">
<Skeleton className="h-16 w-16 rounded-lg mr-3" />
<div>
<Skeleton className="h-4 w-14 mb-1" />
<Skeleton className="h-12 w-8" />
</div>
</div>
<div className="flex items-center justify-between mb-4">
<Skeleton className="h-10 w-72" />
<div className="flex space-x-2">
<Skeleton className="h-10 w-32" />
<Skeleton className="h-10 w-32" />
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<Card key={i}>
<CardContent className="p-6">
<div className="flex items-start">
<Skeleton className="h-16 w-16 rounded-lg mr-4" />
<div className="flex-1">
<div className="flex justify-between items-start">
<div>
<Skeleton className="h-6 w-24 mb-1" />
<Skeleton className="h-4 w-12" />
</div>
<Skeleton className="h-6 w-16" />
</div>
<div className="mt-2">
<Skeleton className="h-4 w-20 mb-1" />
<Skeleton className="h-4 w-12" />
</div>
</div>
</div>
</CardContent>
</Card>
))}
</div>
if (userListLoading || !userListData) {
usersList =
activeTab === "card" ? <UserCardSkeleton /> : <UserListSkeleton />;
} else {
usersList = (
<div>
<UserListView
users={userListData?.results ?? []}
activeTab={activeTab}
/>
<Pagination totalCount={userListData.count} />
</div>
);
}
if (!userListData) {
return <div>{t("no_users_found")}</div>;
}

return (
<Page title={t("users")} hideBack={true} breadcrumbs={false}>
<CountBlock
text={t("total_users")}
count={userListData.count}
loading={userListLoading}
icon="d-people"
className="my-3 flex flex-col items-center sm:items-start"
/>

<UserListView
users={userListData?.results ?? []}
onSearch={(username) => updateQuery({ username })}
searchValue={qParams.username}
activeTab={activeTab}
onTabChange={setActiveTab}
/>

<Pagination totalCount={userListData.count} />
<Page title={t("users_management")}>
<Badge
className="bg-purple-50 text-purple-700 ml-2 text-sm font-medium rounded-xl px-3 m-3"
variant="outline"
>
{`${userListData ? userListData.count : ""} ${t("users")}`}
</Badge>
AdityaJ2305 marked this conversation as resolved.
Show resolved Hide resolved
<hr className="mt-4"></hr>
<div className="flex items-center justify-between gap-4 m-5 ml-0">
<Input
id="search-by-username"
name="username"
onChange={(e) => updateQuery({ username: e.target.value })}
value={qParams.username}
placeholder={t("search_by_username")}
className="w-full max-w-sm"
/>
<Tabs
value={activeTab}
onValueChange={(value) => setActiveTab(value as "card" | "list")}
className="ml-auto"
>
<TabsList className="flex">
<TabsTrigger value="card" id="user-card-view">
<div className="flex items-center gap-2">
<CareIcon icon="l-credit-card" className="text-lg" />
<span>{t("card")}</span>
</div>
</TabsTrigger>
<TabsTrigger value="list" id="user-list-view">
<div className="flex items-center gap-2">
<CareIcon icon="l-list-ul" className="text-lg" />
<span>{t("list")}</span>
</div>
</TabsTrigger>
</TabsList>
</Tabs>
</div>
<div>{usersList}</div>
</Page>
);
}
Loading
Loading