Skip to content

Commit

Permalink
Status Filters and Search for valueset list (#10841)
Browse files Browse the repository at this point in the history
  • Loading branch information
AdityaJ2305 authored Mar 4, 2025
1 parent c51f133 commit dd17a1d
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 94 deletions.
7 changes: 5 additions & 2 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,7 @@
"create_template": "Create Template",
"create_user": "Create User",
"create_user_and_add_to_org": "Create a new user and add them to the organization.",
"create_valueset": "Create ValueSet",
"created": "Created",
"created_by": "Created By",
"created_date": "Created Date",
Expand Down Expand Up @@ -1412,7 +1413,8 @@
"make_facility_public": "Make this facility public",
"make_facility_public_description": "When enabled, this facility will be visible to the public and can be discovered by anyone using the platform",
"make_multiple_beds_label": "Do you want to make multiple beds?",
"manage_and_view_questionnaires": "Manage and view questionnaires",
"manage_and_view_questionnaires": "Manage and View Questionnaires",
"manage_and_view_valuesets": "Manage and View ValueSets",
"manage_bed_presets": "Manage Presets of Bed",
"manage_facility_users": "Manage encounters",
"manage_my_schedule": "Manage my schedule",
Expand Down Expand Up @@ -2107,6 +2109,7 @@
"search_user": "Search User",
"search_user_description": "Search for a user and assign a role to add them to the patient.",
"search_users": "Search users...",
"search_valuesets": "Search ValueSets",
"searching": "Searching...",
"see_attachments": "See Attachments",
"see_details": "See Details",
Expand Down Expand Up @@ -2502,7 +2505,7 @@
"valid_year_of_birth": "Please enter a valid year of birth (YYYY)",
"value": "Value",
"value_set": "Value Set",
"valuesets": "Valuesets",
"valuesets": "ValueSets",
"vehicle_preference": "Vehicle preference",
"vendor_name": "Vendor Name",
"ventilator_interface": "Respiratory Support Type",
Expand Down
1 change: 1 addition & 0 deletions src/components/Questionnaire/QuestionnaireList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import questionnaireApi from "@/types/questionnaire/questionnaireApi";
export function QuestionnaireList() {
const { qParams, updateQuery, Pagination, resultsPerPage } = useFilters({
limit: 15,
disableCache: true,
});

const navigate = useNavigate();
Expand Down
246 changes: 154 additions & 92 deletions src/components/ValueSet/ValueSetList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { useQuery } from "@tanstack/react-query";
import { PlusIcon } from "lucide-react";
import { Link, useNavigate } from "raviger";
import {
ArchiveIcon,
FileCheckIcon,
HelpCircle,
NotepadTextDashedIcon,
Pencil,
PlusIcon,
Search,
} from "lucide-react";
import { Link } from "raviger";
import { useTranslation } from "react-i18next";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Table,
TableBody,
Expand All @@ -13,6 +22,7 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";

import Loading from "@/components/Common/Loading";

Expand All @@ -23,116 +33,168 @@ import valuesetApi from "@/types/valueset/valuesetApi";

export function ValueSetList() {
const { t } = useTranslation();
const navigate = useNavigate();
const { qParams, Pagination, resultsPerPage } = useFilters({
const { qParams, updateQuery, Pagination, resultsPerPage } = useFilters({
limit: 15,
disableCache: true,
});
const { data: response, isLoading } = useQuery({
queryKey: ["valuesets", qParams],
queryFn: query(valuesetApi.list, {
queryParams: {
limit: resultsPerPage,
offset: ((qParams.page ?? 1) - 1) * resultsPerPage,
name: qParams.name,
status: qParams.status || "active",
},
}),
});

if (isLoading) {
return <Loading />;
}

const valuesets = response?.results || [];

return (
<div className="container mx-auto px-4 py-6">
<div className="mb-6 flex items-center justify-between">
<div>
<div className="mb-4 ">
<div className="mb-2">
<h1 className="text-2xl font-bold">{t("valuesets")}</h1>
<p className="text-gray-600">{t("manage_and_view_valuesets")}</p>
</div>
<Button asChild>
<Link href="/admin/valuesets/create">
<PlusIcon className="mr-2 size-4" />
{t("create_new")}
</Link>
</Button>
</div>

<div className="overflow-hidden rounded-lg bg-white shadow">
<Table className="min-w-full divide-y divide-gray-200">
<TableHeader className="bg-gray-50">
<TableRow>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("name")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("slug")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("status")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("description")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("system")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("actions")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="divide-y divide-gray-200 bg-white">
{valuesets.map((valueset) => (
<TableRow key={valueset.id} className="hover:bg-gray-50">
<TableCell className="whitespace-nowrap px-6 py-4">
<div className="text-sm font-medium text-gray-900">
{valueset.name}
</div>
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{valueset.slug}
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4">
<Badge
className={
{
active:
"bg-green-100 text-green-800 hover:bg-green-200",
draft:
"bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
retired: "bg-red-100 text-red-800 hover:bg-red-200",
unknown: "bg-gray-100 text-gray-800 hover:bg-gray-200",
}[valueset.status]
}
>
{t(valueset.status)}
</Badge>
</TableCell>
<TableCell className="px-6 py-4 truncate text-sm text-gray-900 break-words whitespace-normal">
{valueset.description}
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{valueset.is_system_defined ? t("yes") : t("no")}
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4 text-sm">
{!valueset.is_system_defined && (
<Button
variant="primary"
size="sm"
onClick={() =>
navigate(`/admin/valuesets/${valueset.slug}/edit`)
}
>
{t("edit")}
</Button>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<div className="flex flex-col md:flex-row items-center justify-between mt-8 gap-2">
<div className="flex lg:flex-row flex-col items-center gap-4">
<Tabs
defaultValue="active"
value={qParams.status || "active"}
onValueChange={(value) => updateQuery({ status: value })}
className="w-full"
>
<TabsList>
<TabsTrigger value="active">
<FileCheckIcon className="w-4 h-4 mr-2 " />
{t("active")}
</TabsTrigger>
<TabsTrigger value="draft">
<NotepadTextDashedIcon className="w-4 h-4 mr-2" />
{t("draft")}
</TabsTrigger>
<TabsTrigger value="retired">
<ArchiveIcon className="w-4 h-4 mr-2" />
{t("retired")}
</TabsTrigger>
<TabsTrigger value="unknown">
<HelpCircle className="w-4 h-4 mr-2 " />
{t("unknown")}
</TabsTrigger>
</TabsList>
</Tabs>
<div className="relative md:min-w-80 w-full">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-gray-500" />
<Input
placeholder={t("search_valuesets")}
className="pl-10"
value={qParams.name || ""}
onChange={(e) => updateQuery({ name: e.target.value })}
/>
</div>
</div>
<Button>
<Link
href="/admin/valuesets/create"
className="flex items-center gap-2"
>
<PlusIcon className="w-4 h-4" />
{t("create_valueset")}
</Link>
</Button>
</div>
</div>
<Pagination totalCount={response?.count ?? 0} />

{isLoading ? (
<Loading />
) : (
<>
<div className="overflow-hidden rounded-lg bg-white shadow">
<Table className="min-w-full divide-y divide-gray-200">
<TableHeader className="bg-gray-50">
<TableRow>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("name")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("slug")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("status")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("description")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("system")}
</TableHead>
<TableHead className="px-6 py-3 text-left text-xs font-medium uppercase tracking-wider text-gray-500">
{t("actions")}
</TableHead>
</TableRow>
</TableHeader>
<TableBody className="divide-y divide-gray-200 bg-white">
{valuesets.map((valueset) => (
<TableRow key={valueset.id} className="hover:bg-gray-50">
<TableCell className="whitespace-nowrap px-6 py-4">
<div className="text-sm font-medium text-gray-900">
{valueset.name}
</div>
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{valueset.slug}
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4">
<Badge
className={
{
active:
"bg-green-100 text-green-800 hover:bg-green-200",
draft:
"bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
retired: "bg-red-100 text-red-800 hover:bg-red-200",
unknown:
"bg-gray-100 text-gray-800 hover:bg-gray-200",
}[valueset.status]
}
>
{t(valueset.status)}
</Badge>
</TableCell>
<TableCell className="px-6 py-4 truncate text-sm text-gray-900 break-words whitespace-normal">
{valueset.description}
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4 text-sm text-gray-500">
{valueset.is_system_defined ? t("yes") : t("no")}
</TableCell>
<TableCell className="whitespace-nowrap px-6 py-4 text-sm">
{!valueset.is_system_defined && (
<Button
variant="outline"
size="sm"
className="font-semibold shadow-gray-300 text-gray-950 border-gray-400"
>
<Link
href={`/admin/valuesets/${valueset.slug}/edit`}
className="flex items-center gap-2"
>
<Pencil className="w-4 h-4 mr-0" />
{t("edit")}
</Link>
</Button>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<Pagination totalCount={response?.count ?? 0} />
</>
)}
</div>
);
}
3 changes: 3 additions & 0 deletions src/hooks/useFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ interface FilterBadgeProps {
export default function useFilters({
limit = 14,
cacheBlacklist = [],
disableCache = false,
}: {
limit?: number;
cacheBlacklist?: string[];
disableCache?: boolean;
}) {
const { t } = useTranslation();
const hasPagination = limit > 0;
Expand All @@ -40,6 +42,7 @@ export default function useFilters({
}>({ value: false });

const updateCache = (query: QueryParam) => {
if (disableCache) return;
const blacklist = FILTERS_CACHE_BLACKLIST.concat(cacheBlacklist);
FiltersCache.set(query, blacklist);
};
Expand Down

0 comments on commit dd17a1d

Please sign in to comment.