refactor: improve design of no integrations found (#1543)

* refactor: improve design of no integrations found

* fix: deepsource issue
This commit is contained in:
Meier Lukas
2024-11-25 17:31:14 +01:00
committed by GitHub
parent b68977c52c
commit 7e349bb04b
5 changed files with 55 additions and 28 deletions

View File

@@ -1,7 +1,7 @@
import Link from "next/link"; import Link from "next/link";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core"; import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
import { IconApps, IconPencil } from "@tabler/icons-react"; import { IconBox, IconPencil } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api"; import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server"; import { api } from "@homarr/api/server";
@@ -12,6 +12,7 @@ import { getI18n, getScopedI18n } from "@homarr/translation/server";
import { ManageContainer } from "~/components/manage/manage-container"; import { ManageContainer } from "~/components/manage/manage-container";
import { MobileAffixButton } from "~/components/manage/mobile-affix-button"; import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { NoResults } from "~/components/no-results";
import { AppDeleteButton } from "./_app-delete-button"; import { AppDeleteButton } from "./_app-delete-button";
export default async function AppsPage() { export default async function AppsPage() {
@@ -113,16 +114,14 @@ const AppNoResults = async () => {
const session = await auth(); const session = await auth();
return ( return (
<Card withBorder bg="transparent"> <NoResults
<Stack align="center" gap="sm"> icon={IconBox}
<IconApps size="2rem" /> title={t("app.page.list.noResults.title")}
<Text fw={500} size="lg"> action={{
{t("app.page.list.noResults.title")} label: t("app.page.list.noResults.action"),
</Text> href: "/manage/apps/new",
{session?.user.permissions.includes("app-create") && ( hidden: !session?.user.permissions.includes("app-create"),
<Anchor href="/manage/apps/new">{t("app.page.list.noResults.action")}</Anchor> }}
)} />
</Stack>
</Card>
); );
}; };

View File

@@ -27,7 +27,7 @@ import {
Text, Text,
Title, Title,
} from "@mantine/core"; } from "@mantine/core";
import { IconChevronDown, IconChevronUp, IconPencil } from "@tabler/icons-react"; import { IconChevronDown, IconChevronUp, IconPencil, IconPlugX } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api"; import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server"; import { api } from "@homarr/api/server";
@@ -40,6 +40,7 @@ import { CountBadge, IntegrationAvatar } from "@homarr/ui";
import { ManageContainer } from "~/components/manage/manage-container"; import { ManageContainer } from "~/components/manage/manage-container";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { NoResults } from "~/components/no-results";
import { ActiveTabAccordion } from "../../../../components/active-tab-accordion"; import { ActiveTabAccordion } from "../../../../components/active-tab-accordion";
import { DeleteIntegrationActionButton } from "./_integration-buttons"; import { DeleteIntegrationActionButton } from "./_integration-buttons";
import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown"; import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown";
@@ -120,7 +121,7 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
const hasFullAccess = session?.user.permissions.includes("integration-full-all") ?? false; const hasFullAccess = session?.user.permissions.includes("integration-full-all") ?? false;
if (integrations.length === 0) { if (integrations.length === 0) {
return <div>{t("page.list.empty")}</div>; return <NoResults icon={IconPlugX} title={t("page.list.noResults.title")} />;
} }
const grouppedIntegrations = integrations.reduce( const grouppedIntegrations = integrations.reduce(

View File

@@ -13,6 +13,7 @@ import { z } from "@homarr/validation";
import { ManageContainer } from "~/components/manage/manage-container"; import { ManageContainer } from "~/components/manage/manage-container";
import { MobileAffixButton } from "~/components/manage/mobile-affix-button"; import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { NoResults } from "~/components/no-results";
import { SearchEngineDeleteButton } from "./_search-engine-delete-button"; import { SearchEngineDeleteButton } from "./_search-engine-delete-button";
const searchParamsSchema = z.object({ const searchParamsSchema = z.object({
@@ -142,16 +143,14 @@ const SearchEngineNoResults = async () => {
const session = await auth(); const session = await auth();
return ( return (
<Card withBorder bg="transparent"> <NoResults
<Stack align="center" gap="sm"> icon={IconSearch}
<IconSearch size="2rem" /> title={t("search.engine.page.list.noResults.title")}
<Text fw={500} size="lg"> action={{
{t("search.engine.page.list.noResults.title")} label: t("search.engine.page.list.noResults.action"),
</Text> href: "/manage/search-engines/new",
{session?.user.permissions.includes("search-engine-create") && ( hidden: !session?.user.permissions.includes("search-engine-create"),
<Anchor href="/manage/search-engines/new">{t("search.engine.page.list.noResults.action")}</Anchor> }}
)} />
</Stack>
</Card>
); );
}; };

View File

@@ -0,0 +1,26 @@
import { Anchor, Card, Stack, Text } from "@mantine/core";
import type { TablerIcon } from "@tabler/icons-react";
interface NoResultsProps {
icon: TablerIcon;
title: string;
action?: {
label: string;
href: string;
hidden?: boolean;
};
}
export const NoResults = ({ icon: Icon, title, action }: NoResultsProps) => {
return (
<Card withBorder bg="transparent">
<Stack align="center" gap="sm">
<Icon size="2rem" />
<Text fw={500} size="lg">
{title}
</Text>
{!action?.hidden && <Anchor href={action?.href}>{action?.label}</Anchor>}
</Stack>
</Card>
);
};

View File

@@ -388,7 +388,7 @@
"list": { "list": {
"title": "Apps", "title": "Apps",
"noResults": { "noResults": {
"title": "There aren't any apps", "title": "There are no apps yet",
"action": "Create your first app" "action": "Create your first app"
} }
}, },
@@ -456,7 +456,9 @@
"list": { "list": {
"title": "Integrations", "title": "Integrations",
"search": "Search integrations", "search": "Search integrations",
"empty": "No integrations found" "noResults": {
"title": "There are no integrations yet"
}
}, },
"create": { "create": {
"title": "New {name} integration", "title": "New {name} integration",
@@ -2676,7 +2678,7 @@
"list": { "list": {
"title": "Search engines", "title": "Search engines",
"noResults": { "noResults": {
"title": "There aren't any search engines", "title": "There are no search engines yet",
"action": "Create your first search engine" "action": "Create your first search engine"
}, },
"interactive": "Interactive, uses an integration" "interactive": "Interactive, uses an integration"