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

Public Facilities Filters #9748

Merged
merged 22 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
faa4abe
WIP: organization filter
Jacobjeevan Jan 2, 2025
099e91e
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 4, 2025
3abdfd9
Organization Filter
Jacobjeevan Jan 4, 2025
2349c84
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 5, 2025
db940f2
minor fixes
Jacobjeevan Jan 5, 2025
bc0560a
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 6, 2025
a57dfa6
Tweak logic to work with levels from BE than hardcoded
Jacobjeevan Jan 6, 2025
c4ca814
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 6, 2025
be5bcaa
Update organisation filter and search bar styling on public facilitie…
vinutv Jan 7, 2025
2b62571
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 8, 2025
5ff2f0f
added query for root level, styling changes
Jacobjeevan Jan 8, 2025
31986af
moved logic to sep file&hook as per cursor suggestion
Jacobjeevan Jan 8, 2025
5537247
coderabbit suggestions
Jacobjeevan Jan 8, 2025
3ca311d
additional styling changes
Jacobjeevan Jan 8, 2025
def05a4
mobile responsiveness edits
Jacobjeevan Jan 8, 2025
f1f9b55
Merge branch 'develop' into organizationFilter
nihal467 Jan 9, 2025
04b8c10
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 11, 2025
06dba7b
switch to debounced query
Jacobjeevan Jan 11, 2025
cfd445c
styling and fixes
Jacobjeevan Jan 11, 2025
c3eb888
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 11, 2025
e0f4527
Merge branch 'develop' into organizationFilter
Jacobjeevan Jan 13, 2025
03ee501
disable facility type filter if location is not selected
Jacobjeevan Jan 13, 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
4 changes: 3 additions & 1 deletion public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,6 @@
"facility_not_found": "Facility Not Found",
"facility_organizations": "Facility Organizations",
"facility_preference": "Facility preference",
"facility_search_placeholder": "Search by Facility / District Name",
"facility_search_placeholder_text": "Search by Facility name",
"facility_type": "Facility Type",
"facility_updated_success": "Facility updated successfully",
Expand Down Expand Up @@ -1762,6 +1761,7 @@
"select_eligible_policy": "Select an Eligible Insurance Policy",
"select_facility": "Select Facility",
"select_facility_for_discharged_patients_warning": "Facility needs to be selected to view discharged patients.",
"select_facility_type": "Select Facility Type",
"select_for_administration": "Select for Administration",
"select_frequency": "Select frequency",
"select_groups": "Select Groups",
Expand All @@ -1770,12 +1770,14 @@
"select_investigation_groups": "Select Investigation Groups",
"select_investigations": "Select Investigations",
"select_local_body": "Select Local Body",
"select_location_first": "Select Location First",
"select_method": "Select method",
"select_new_role": "Select New Role",
"select_patient": "Select Patient",
"select_policy": "Select an Insurance Policy",
"select_policy_to_add_items": "Select a Policy to Add Items",
"select_practitioner": "Select Practicioner",
"select_previous": "Select Previous Fields",
"select_prn_reason": "Select reason for PRN",
"select_register_patient": "Select/Register Patient",
"select_role": "Select Role",
Expand Down
10 changes: 5 additions & 5 deletions src/components/Common/SearchByMultipleFields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const SearchByMultipleFields: React.FC<SearchByMultipleFieldsProps> = ({
value: searchValue,
onChange: handleSearchChange,
className: cn(
"flex-grow border-none shadow-none focus-visible:ring-0 h-10",
"flex-grow border-none shadow-none focus-visible:ring-0",
inputClassName,
),
} as const;
Expand Down Expand Up @@ -222,7 +222,7 @@ const SearchByMultipleFields: React.FC<SearchByMultipleFieldsProps> = ({
<PopoverTrigger asChild>
<Button
variant="ghost"
className="focus:ring-0 px-2 ml-1"
className="px-2 ml-1 focus:ring-0"
size="sm"
onClick={() => setOpen(true)}
>
Expand All @@ -242,7 +242,7 @@ const SearchByMultipleFields: React.FC<SearchByMultipleFieldsProps> = ({
"hover:bg-secondary-100": true,
})}
>
<CareIcon icon="l-search" className="mr-2 h-4 w-4" />
<CareIcon icon="l-search" className="w-4 h-4 mr-2" />
<span className="flex-1">{t(option.key)}</span>
<kbd className="ml-auto text-xs text-gray-400">
{option.shortcutKey}
Expand All @@ -257,12 +257,12 @@ const SearchByMultipleFields: React.FC<SearchByMultipleFieldsProps> = ({
<div className="w-full">{renderSearchInput}</div>
</div>
{error && (
<div className="error-text px-2 mb-1 text-xs font-medium tracking-wide text-danger-500 transition-opacity duration-300">
<div className="px-2 mb-1 text-xs font-medium tracking-wide transition-opacity duration-300 error-text text-danger-500">
{t("invalid_phone_number")}
</div>
)}
{enableOptionButtons && (
<div className="flex flex-wrap gap-2 rounded-b-lg bg-gray-50 border-t border-t-gray-100 p-2">
<div className="flex flex-wrap gap-2 p-2 border-t rounded-b-lg bg-gray-50 border-t-gray-100">
{options.map((option, i) => (
<Button
key={option.key}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Common/UserSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function UserSelector({
</Button>
</PopoverTrigger>
<PopoverContent
className="p-0 w-[var(--radix-popover-trigger-width)] max-w-[--radix-popover-content-available-width]"
className="p-0 w-[var(--radix-popover-trigger-width)]"
align="start"
sideOffset={4}
>
Expand Down
11 changes: 9 additions & 2 deletions src/components/ui/autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ interface AutocompleteProps {
placeholder?: string;
noOptionsMessage?: string;
disabled?: boolean;
align?: "start" | "center" | "end";
popoverClassName?: string;
"data-cy"?: string;
}

Expand All @@ -42,13 +44,15 @@ export default function Autocomplete({
placeholder = "Select...",
noOptionsMessage = "No options found",
disabled,
align = "center",
popoverClassName,
"data-cy": dataCy,
}: AutocompleteProps) {
const [open, setOpen] = React.useState(false);

return (
<Popover open={open} onOpenChange={setOpen} modal={true}>
<PopoverTrigger asChild>
<PopoverTrigger asChild className={popoverClassName}>
<Button
title={
value
Expand All @@ -70,7 +74,10 @@ export default function Autocomplete({
<CaretSortIcon className="ml-2 size-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-full p-0 pointer-events-auto">
<PopoverContent
className="sm:w-full p-0 pointer-events-auto w-[var(--radix-popover-trigger-width)]"
align={align}
>
<Command>
<CommandInput
placeholder="Search option..."
Expand Down
4 changes: 2 additions & 2 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ const buttonVariants = cva(
destructive:
"bg-red-500 text-gray-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-gray-50 dark:hover:bg-red-900/90",
outline:
"border border-gray-200 bg-white shadow-sm hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50",
"border border-gray-400/75 bg-white shadow-sm hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50",
primary:
"bg-primary-700 text-white shadow hover:bg-primary-700/90 dark:bg-primary-100 dark:text-primary-900 dark:hover:bg-primary-100/90",
secondary:
"bg-gray-100 text-gray-900 shadow-sm hover:bg-gray-100/80 dark:bg-gray-800 dark:text-gray-50 dark:hover:bg-gray-800/80",
ghost:
"hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-50",
"underline hover:bg-gray-100 hover:text-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-50",
link: "text-gray-900 underline-offset-4 hover:underline dark:text-gray-50",
outline_primary:
"border border-primary-700 text-primary-700 bg-white shadow-sm hover:bg-primary-700 hover:text-white dark:border-primary-700 dark:bg-primary-700 dark:text-white",
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
<input
type={type}
className={cn(
"flex h-9 w-full rounded-md border border-gray-200 bg-white px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-gray-950 placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:border-gray-800 dark:file:text-gray-50 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300",
"flex w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-gray-950 placeholder:text-gray-500 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:border-gray-800 dark:file:text-gray-50 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300",
className,
)}
ref={ref}
Expand Down
10 changes: 5 additions & 5 deletions src/components/ui/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-gray-200 bg-white px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-gray-500 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-900 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300 [&>span]:line-clamp-1",
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-gray-400/75 bg-white px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-gray-500 focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-900 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-800 dark:bg-gray-950 dark:ring-offset-gray-950 dark:placeholder:text-gray-400 dark:focus-visible:ring-gray-300 [&>span]:line-clamp-1",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDownIcon className="ml-1 h-4 w-4 opacity-50" />
<ChevronDownIcon className="w-4 h-4 ml-1 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
Expand All @@ -46,7 +46,7 @@ const SelectScrollUpButton = React.forwardRef<
)}
{...props}
>
<ChevronUpIcon className="h-4 w-4" />
<ChevronUpIcon className="w-4 h-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
Expand All @@ -63,7 +63,7 @@ const SelectScrollDownButton = React.forwardRef<
)}
{...props}
>
<ChevronDownIcon className="h-4 w-4" />
<ChevronDownIcon className="w-4 h-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName =
Expand Down Expand Up @@ -127,7 +127,7 @@ const SelectItem = React.forwardRef<
>
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<CheckIcon className="h-4 w-4" />
<CheckIcon className="w-4 h-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
Expand Down
91 changes: 91 additions & 0 deletions src/hooks/useOrganizationLevel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { useQuery } from "@tanstack/react-query";
import { useEffect, useMemo, useState } from "react";

import { FilterState } from "@/hooks/useFilters";

import query from "@/Utils/request/query";
import { Organization } from "@/types/organization/organization";
import organizationApi from "@/types/organization/organizationApi";

interface UseOrganizationLevelProps {
index: number;
skip: boolean;
selectedLevels: Organization[];
setOrgTypes: React.Dispatch<React.SetStateAction<string[]>>;
onChange: (Filter: FilterState, index?: number) => void;
getParentId: (index: number) => string;
}

export function useOrganizationLevel({
index,
skip,
selectedLevels,
setOrgTypes,
onChange,
getParentId,
}: UseOrganizationLevelProps) {
const [levelSearch, setLevelSearch] = useState("");

const { data: availableOrgs } = useQuery<{ results: Organization[] }>({
queryKey: ["organizations-available", getParentId(index), levelSearch],
queryFn: query.debounced(organizationApi.getPublicOrganizations, {
queryParams: {
...(index > 0 && { parent: getParentId(index) }),
...(index === 0 && { level_cache: 1 }),
name: levelSearch || undefined,
},
}),
enabled:
!skip &&
index <= selectedLevels.length &&
(index === 0 || selectedLevels[index - 1] !== undefined),
});

// Update org types when we get new data for this level
useEffect(() => {
if (selectedLevels[index]) {
const currentOrg = selectedLevels[index];
if (currentOrg?.metadata?.govt_org_children_type) {
setOrgTypes((prevTypes) => {
const newTypes = [...prevTypes];
// Update next level type
if (currentOrg.metadata?.govt_org_children_type) {
if (index === newTypes.length) {
newTypes.push(currentOrg.metadata.govt_org_children_type);
} else {
newTypes[index + 1] = currentOrg.metadata.govt_org_children_type;
}
}
return newTypes;
});
}
}
}, [selectedLevels, setOrgTypes, index]);

const options = useMemo(() => {
return (
availableOrgs?.results?.map((org) => ({
label: org.name,
value: org.id,
})) || []
);
}, [availableOrgs?.results]);

const handleChange = (value: string) => {
const selectedOrg = availableOrgs?.results.find((org) => org.id === value);
if (selectedOrg) {
onChange({ organization: selectedOrg.id }, index);
setLevelSearch("");
}
};
Comment on lines +74 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add type safety to the handleChange function.

The function assumes the selected organization exists but doesn't handle the case where it might not be found.

-  const handleChange = (value: string) => {
+  const handleChange = (value: string | undefined) => {
+    if (!value) {
+      onChange({ organization: undefined }, index);
+      setLevelSearch("");
+      return;
+    }
     const selectedOrg = availableOrgs?.results.find((org) => org.id === value);
     if (selectedOrg) {
       onChange({ organization: selectedOrg.id }, index);
       setLevelSearch("");
     }
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleChange = (value: string) => {
const selectedOrg = availableOrgs?.results.find((org) => org.id === value);
if (selectedOrg) {
onChange({ organization: selectedOrg.id }, index);
setLevelSearch("");
}
};
const handleChange = (value: string | undefined) => {
if (!value) {
onChange({ organization: undefined }, index);
setLevelSearch("");
return;
}
const selectedOrg = availableOrgs?.results.find((org) => org.id === value);
if (selectedOrg) {
onChange({ organization: selectedOrg.id }, index);
setLevelSearch("");
}
};


const handleSearch = (value: string) => setLevelSearch(value);

return {
options,
levelSearch,
handleChange,
handleSearch,
availableOrgs,
};
}
Loading
Loading