feat: search group members (#2445)

This commit is contained in:
Philip Okugbe
2026-08-27 12:43:08 +01:00
committed by GitHub
parent 579e22a45e
commit d92716d82f
2 changed files with 70 additions and 52 deletions
@@ -12,13 +12,19 @@ import useUserRole from "@/hooks/use-user-role.tsx";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { IUser } from "@/features/user/types/user.types.ts"; import { IUser } from "@/features/user/types/user.types.ts";
import Paginate from "@/components/common/paginate.tsx"; import Paginate from "@/components/common/paginate.tsx";
import { useCursorPaginate } from "@/hooks/use-cursor-paginate"; import { SearchInput } from "@/components/common/search-input.tsx";
import NoTableResults from "@/components/common/no-table-results.tsx";
import { usePaginateAndSearch } from "@/hooks/use-paginate-and-search.tsx";
export default function GroupMembersList() { export default function GroupMembersList() {
const { t } = useTranslation(); const { t } = useTranslation();
const { groupId } = useParams(); const { groupId } = useParams();
const { cursor, goNext, goPrev } = useCursorPaginate(); const { search, cursor, goNext, goPrev, handleSearch } =
const { data, isLoading } = useGroupMembersQuery(groupId, { cursor }); usePaginateAndSearch();
const { data, isLoading } = useGroupMembersQuery(groupId, {
cursor,
query: search,
});
const removeGroupMember = useRemoveGroupMemberMutation(); const removeGroupMember = useRemoveGroupMemberMutation();
const { isAdmin } = useUserRole(); const { isAdmin } = useUserRole();
@@ -48,6 +54,7 @@ export default function GroupMembersList() {
return ( return (
<> <>
<SearchInput onSearch={handleSearch} />
<Table.ScrollContainer minWidth={500}> <Table.ScrollContainer minWidth={500}>
<Table highlightOnHover verticalSpacing="sm"> <Table highlightOnHover verticalSpacing="sm">
<Table.Thead> <Table.Thead>
@@ -59,55 +66,62 @@ export default function GroupMembersList() {
</Table.Thead> </Table.Thead>
<Table.Tbody> <Table.Tbody>
{data?.items.map((user: IUser, index: number) => ( {data?.items.length > 0 ? (
<Table.Tr key={index}> data?.items.map((user: IUser, index: number) => (
<Table.Td> <Table.Tr key={index}>
<Group gap="sm" wrap="nowrap"> <Table.Td>
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name} /> <Group gap="sm" wrap="nowrap">
<div> <CustomAvatar
<Text fz="sm" fw={500} lineClamp={1}> avatarUrl={user.avatarUrl}
{user.name} name={user.name}
</Text> />
<Text fz="xs" c="dimmed"> <div>
{user.email} <Text fz="sm" fw={500} lineClamp={1}>
</Text> {user.name}
</div> </Text>
</Group> <Text fz="xs" c="dimmed">
</Table.Td> {user.email}
<Table.Td> </Text>
<Badge variant="light">{t("Active")}</Badge> </div>
</Table.Td> </Group>
<Table.Td> </Table.Td>
{isAdmin && ( <Table.Td>
<Menu <Badge variant="light">{t("Active")}</Badge>
shadow="xl" </Table.Td>
position="bottom-end" <Table.Td>
offset={20} {isAdmin && (
width={200} <Menu
withArrow shadow="xl"
arrowPosition="center" position="bottom-end"
> offset={20}
<Menu.Target> width={200}
<ActionIcon withArrow
variant="subtle" arrowPosition="center"
c="gray" >
aria-label={t("Member actions for {{name}}", { <Menu.Target>
name: user.name, <ActionIcon
})} variant="subtle"
> c="gray"
<IconDots size={20} stroke={2} /> aria-label={t("Member actions for {{name}}", {
</ActionIcon> name: user.name,
</Menu.Target> })}
<Menu.Dropdown> >
<Menu.Item onClick={() => openRemoveModal(user.id)}> <IconDots size={20} stroke={2} />
{t("Remove group member")} </ActionIcon>
</Menu.Item> </Menu.Target>
</Menu.Dropdown> <Menu.Dropdown>
</Menu> <Menu.Item onClick={() => openRemoveModal(user.id)}>
)} {t("Remove group member")}
</Table.Td> </Menu.Item>
</Table.Tr> </Menu.Dropdown>
))} </Menu>
)}
</Table.Td>
</Table.Tr>
))
) : (
<NoTableResults colSpan={3} />
)}
</Table.Tbody> </Table.Tbody>
</Table> </Table>
</Table.ScrollContainer> </Table.ScrollContainer>
@@ -60,6 +60,10 @@ export class GroupUserRepo {
sql`f_unaccent(users.name)`, sql`f_unaccent(users.name)`,
'ilike', 'ilike',
sql`f_unaccent(${'%' + pagination.query + '%'})`, sql`f_unaccent(${'%' + pagination.query + '%'})`,
).or(
sql`users.email`,
'ilike',
sql`f_unaccent(${'%' + pagination.query + '%'})`,
), ),
); );
} }