improvement:frontend search filter on organizations
Signed-off-by: manish-singh-bisht <mthefool218@gmail.com>
This commit is contained in:
committed by
Sacha Al Himdani
parent
001a2aa4ac
commit
bb4876fd4f
@@ -16,18 +16,20 @@ import {
|
||||
IconPlusLarge,
|
||||
IconCheckmark1,
|
||||
IconClock,
|
||||
IconMagnifyingGlass,
|
||||
useToast,
|
||||
Logo,
|
||||
Toasts,
|
||||
ConfirmDialog,
|
||||
Avatar,
|
||||
Badge,
|
||||
Input,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useLazyLoadQuery } from "react-relay";
|
||||
import type { EmployeeLayoutQuery as EmployeeLayoutQueryType } from "./__generated__/EmployeeLayoutQuery.graphql";
|
||||
import { Suspense, useState, useEffect, use } from "react";
|
||||
import { Suspense, useState, useEffect, useMemo, use } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { PageError } from "/components/PageError";
|
||||
import { buildEndpoint } from "/providers/RelayProviders";
|
||||
@@ -152,8 +154,18 @@ function OrganizationSelector({
|
||||
const [pendingInvitationsCount, setPendingInvitationsCount] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const filteredOrganizations = useMemo(() => {
|
||||
if (!search.trim()) {
|
||||
return organizations;
|
||||
}
|
||||
return organizations.filter((org) =>
|
||||
org.name.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
}, [organizations, search]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
@@ -218,17 +230,31 @@ function OrganizationSelector({
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
{!isLoading && organizations.length > 0 && (
|
||||
<div className="px-3 py-2 border-b border-border-low">
|
||||
<Input
|
||||
icon={IconMagnifyingGlass}
|
||||
placeholder={__("Search organizations...")}
|
||||
value={search}
|
||||
onValueChange={setSearch}
|
||||
onKeyDown={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="max-h-150 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent hover:scrollbar-thumb-gray-400">
|
||||
{isLoading ? (
|
||||
<div className="px-3 py-2 text-gray-500">
|
||||
{__("Loading organizations...")}
|
||||
</div>
|
||||
) : organizations.length === 0 ? (
|
||||
) : filteredOrganizations.length === 0 ? (
|
||||
<div className="px-3 py-2 text-gray-500">
|
||||
{__("No organizations found")}
|
||||
</div>
|
||||
) : (
|
||||
organizations.map((organization) => {
|
||||
filteredOrganizations.map((organization) => {
|
||||
const isAuthenticated =
|
||||
organization.authStatus === "authenticated";
|
||||
const isExpired = organization.authStatus === "expired";
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
IconKey,
|
||||
IconListStack,
|
||||
IconLock,
|
||||
IconMagnifyingGlass,
|
||||
IconMedal,
|
||||
IconPageTextLine,
|
||||
IconPeopleAdd,
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
IconShield,
|
||||
IconStore,
|
||||
IconTodo,
|
||||
Input,
|
||||
Layout,
|
||||
SidebarItem,
|
||||
Skeleton,
|
||||
@@ -39,7 +41,7 @@ import {
|
||||
UserDropdown as UserDropdownRoot,
|
||||
useToast,
|
||||
} from "@probo/ui";
|
||||
import { Suspense, use, useEffect, useState } from "react";
|
||||
import { Suspense, use, useEffect, useMemo, useState } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { useLazyLoadQuery } from "react-relay";
|
||||
import { Link, Navigate, Outlet, useParams } from "react-router";
|
||||
@@ -364,8 +366,18 @@ function OrganizationSelector({
|
||||
const [pendingInvitationsCount, setPendingInvitationsCount] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [search, setSearch] = useState("");
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const filteredOrganizations = useMemo(() => {
|
||||
if (!search.trim()) {
|
||||
return organizations;
|
||||
}
|
||||
return organizations.filter((org) =>
|
||||
org.name.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
}, [organizations, search]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
@@ -429,18 +441,30 @@ function OrganizationSelector({
|
||||
{isLoading ? __("Loading...") : currentOrganization?.name || ""}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
>
|
||||
<div className="px-3 py-2">
|
||||
<Input
|
||||
icon={IconMagnifyingGlass}
|
||||
placeholder={__("Search organizations...")}
|
||||
value={search}
|
||||
onValueChange={setSearch}
|
||||
onKeyDown={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
<div className="max-h-150 overflow-y-auto scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent hover:scrollbar-thumb-gray-400">
|
||||
{isLoading ? (
|
||||
<div className="px-3 py-2 text-gray-500">
|
||||
{__("Loading organizations...")}
|
||||
</div>
|
||||
) : organizations.length === 0 ? (
|
||||
) : filteredOrganizations.length === 0 ? (
|
||||
<div className="px-3 py-2 text-gray-500">
|
||||
{__("No organizations found")}
|
||||
</div>
|
||||
) : (
|
||||
organizations.map((organization) => {
|
||||
filteredOrganizations.map((organization) => {
|
||||
const isAuthenticated =
|
||||
organization.authStatus === "authenticated";
|
||||
const isExpired = organization.authStatus === "expired";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
import {
|
||||
Avatar,
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
IconCheckmark1,
|
||||
IconLock,
|
||||
IconClock,
|
||||
IconMagnifyingGlass,
|
||||
Badge,
|
||||
Input,
|
||||
} from "@probo/ui";
|
||||
import { usePageTitle } from "@probo/hooks";
|
||||
import { formatDate } from "@probo/helpers";
|
||||
@@ -46,6 +48,16 @@ export default function OrganizationsPage() {
|
||||
const [invitations, setInvitations] = useState<Invitation[]>([]);
|
||||
const [isLoadingInvitations, setIsLoadingInvitations] = useState(true);
|
||||
const [isAccepting, setIsAccepting] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const filteredOrganizations = useMemo(() => {
|
||||
if (!search.trim()) {
|
||||
return organizations;
|
||||
}
|
||||
return organizations.filter((org) =>
|
||||
org.name.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
}, [organizations, search]);
|
||||
|
||||
// Fetch organizations from REST endpoint
|
||||
useEffect(() => {
|
||||
@@ -163,12 +175,28 @@ export default function OrganizationsPage() {
|
||||
{__("Your organizations")}
|
||||
</h2>
|
||||
)}
|
||||
{organizations.map((organization) => (
|
||||
<OrganizationCard
|
||||
key={organization.id}
|
||||
organization={organization}
|
||||
/>
|
||||
))}
|
||||
{organizations.length > 3 && (
|
||||
<div className="w-full">
|
||||
<Input
|
||||
icon={IconMagnifyingGlass}
|
||||
placeholder={__("Search organizations...")}
|
||||
value={search}
|
||||
onValueChange={setSearch}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{filteredOrganizations.length === 0 ? (
|
||||
<div className="text-center text-txt-secondary py-4">
|
||||
{__("No organizations found")}
|
||||
</div>
|
||||
) : (
|
||||
filteredOrganizations.map((organization) => (
|
||||
<OrganizationCard
|
||||
key={organization.id}
|
||||
organization={organization}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<Card padded>
|
||||
|
||||
Reference in New Issue
Block a user