feat: add improved search (#1051)
* feat: add improved search * wip: add support for sorting, rename use-options to use-query-options, add use-options for local usage, add pages search group * feat: add help links from manage layout to help search mode * feat: add additional search engines * feat: add group search details * refactor: improve users search group type * feat: add apps search group, add disabled search interaction * feat: add integrations and boards for search * wip: hook issue with react * fix: hook issue regarding actions and interactions * chore: address pull request feedback * fix: format issues * feat: add additional global actions to search * chore: remove unused code * fix: search engine short key * fix: typecheck issues * fix: deepsource issues * fix: eslint issue * fix: lint issues * fix: unordered dependencies * chore: address pull request feedback
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { Group, Stack, Text } from "@mantine/core";
|
||||
import { IconEye, IconUsersGroup } from "@tabler/icons-react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
|
||||
import { createChildrenOptions } from "../../lib/children";
|
||||
import { createGroup } from "../../lib/group";
|
||||
import { interaction } from "../../lib/interaction";
|
||||
|
||||
// This has to be type so it can be interpreted as Record<string, unknown>.
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
type Group = { id: string; name: string };
|
||||
|
||||
const groupChildrenOptions = createChildrenOptions<Group>({
|
||||
useActions: () => [
|
||||
{
|
||||
key: "detail",
|
||||
component: () => {
|
||||
const t = useI18n();
|
||||
return (
|
||||
<Group mx="md" my="sm">
|
||||
<IconEye stroke={1.5} />
|
||||
<Text>{t("search.mode.userGroup.group.group.children.action.detail.label")}</Text>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
useInteraction: interaction.link(({ id }) => ({ href: `/manage/users/groups/${id}` })),
|
||||
},
|
||||
{
|
||||
key: "manageMember",
|
||||
component: () => {
|
||||
const t = useI18n();
|
||||
return (
|
||||
<Group mx="md" my="sm">
|
||||
<IconUsersGroup stroke={1.5} />
|
||||
<Text>{t("search.mode.userGroup.group.group.children.action.manageMember.label")}</Text>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
useInteraction: interaction.link(({ id }) => ({ href: `/manage/users/groups/${id}/members` })),
|
||||
},
|
||||
{
|
||||
key: "managePermission",
|
||||
component: () => {
|
||||
const t = useI18n();
|
||||
return (
|
||||
<Group mx="md" my="sm">
|
||||
<IconEye stroke={1.5} />
|
||||
<Text>{t("search.mode.userGroup.group.group.children.action.managePermission.label")}</Text>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
useInteraction: interaction.link(({ id }) => ({ href: `/manage/users/groups/${id}/permissions` })),
|
||||
},
|
||||
],
|
||||
detailComponent: ({ options }) => {
|
||||
const t = useI18n();
|
||||
return (
|
||||
<Stack mx="md" my="sm">
|
||||
<Text>{t("search.mode.userGroup.group.group.children.detail.title")}</Text>
|
||||
|
||||
<Group>
|
||||
<Text>{options.name}</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const groupsSearchGroup = createGroup<Group>({
|
||||
keyPath: "id",
|
||||
title: "Groups",
|
||||
component: ({ name }) => (
|
||||
<Group px="md" py="sm">
|
||||
<Text>{name}</Text>
|
||||
</Group>
|
||||
),
|
||||
useInteraction: interaction.children(groupChildrenOptions),
|
||||
useQueryOptions(query) {
|
||||
return clientApi.group.search.useQuery({ query, limit: 5 });
|
||||
},
|
||||
});
|
||||
9
packages/spotlight/src/modes/user-group/index.tsx
Normal file
9
packages/spotlight/src/modes/user-group/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { SearchMode } from "../../lib/mode";
|
||||
import { groupsSearchGroup } from "./groups-search-group";
|
||||
import { usersSearchGroup } from "./users-search-group";
|
||||
|
||||
export const userGroupMode = {
|
||||
modeKey: "userGroup",
|
||||
character: "@",
|
||||
groups: [usersSearchGroup, groupsSearchGroup],
|
||||
} satisfies SearchMode;
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Group, Stack, Text } from "@mantine/core";
|
||||
import { IconEye } from "@tabler/icons-react";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { UserAvatar } from "@homarr/ui";
|
||||
|
||||
import { createChildrenOptions } from "../../lib/children";
|
||||
import { createGroup } from "../../lib/group";
|
||||
import { interaction } from "../../lib/interaction";
|
||||
|
||||
// This has to be type so it can be interpreted as Record<string, unknown>.
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
type User = { id: string; name: string; image: string | null };
|
||||
|
||||
const userChildrenOptions = createChildrenOptions<User>({
|
||||
useActions: () => [
|
||||
{
|
||||
key: "detail",
|
||||
component: () => {
|
||||
const t = useI18n();
|
||||
|
||||
return (
|
||||
<Group mx="md" my="sm">
|
||||
<IconEye stroke={1.5} />
|
||||
<Text>{t("search.mode.userGroup.group.user.children.action.detail.label")}</Text>
|
||||
</Group>
|
||||
);
|
||||
},
|
||||
useInteraction: interaction.link(({ id }) => ({ href: `/manage/users/${id}/general` })),
|
||||
},
|
||||
],
|
||||
detailComponent: ({ options }) => {
|
||||
const t = useI18n();
|
||||
|
||||
return (
|
||||
<Stack mx="md" my="sm">
|
||||
<Text>{t("search.mode.userGroup.group.user.children.detail.title")}</Text>
|
||||
|
||||
<Group>
|
||||
<UserAvatar user={options} size="sm" />
|
||||
<Text>{options.name}</Text>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const usersSearchGroup = createGroup<User>({
|
||||
keyPath: "id",
|
||||
title: (t) => t("search.mode.userGroup.group.user.title"),
|
||||
component: (user) => (
|
||||
<Group px="md" py="sm">
|
||||
<UserAvatar user={user} size="sm" />
|
||||
<Text>{user.name}</Text>
|
||||
</Group>
|
||||
),
|
||||
useInteraction: interaction.children(userChildrenOptions),
|
||||
useQueryOptions(query) {
|
||||
return clientApi.user.search.useQuery({ query, limit: 5 });
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user