refactor: improve design of no integrations found (#1543)
* refactor: improve design of no integrations found * fix: deepsource issue
This commit is contained in:
@@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
26
apps/nextjs/src/components/no-results.tsx
Normal file
26
apps/nextjs/src/components/no-results.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user