Files
homarr/apps/nextjs/src/app/[locale]/(main)/integrations/page.tsx
homarr-renovate[bot] 71521c0768 fix(deps)!: update tanstack-query monorepo (#126)
* fix(deps): update tanstack-query monorepo to ^5.21.2

* fix(deps): update tanstack-query monorepo

* fix: type issue with transformer

* fix: issues with next-auth, updated to next canary

* chore: fix type issue in trpc route

* chore: fix formatting

---------

Co-authored-by: homarr-renovate[bot] <158783068+homarr-renovate[bot]@users.noreply.github.com>
Co-authored-by: Meier Lukas <meierschlumpf@gmail.com>
2024-02-17 12:52:25 +01:00

176 lines
5.0 KiB
TypeScript

import Link from "next/link";
import type { RouterOutputs } from "@homarr/api";
import { objectEntries } from "@homarr/common";
import { getIntegrationName } from "@homarr/definitions";
import type { IntegrationKind } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import {
AccordionControl,
AccordionItem,
AccordionPanel,
ActionIcon,
ActionIconGroup,
Anchor,
Button,
Container,
CountBadge,
Group,
IconChevronDown,
IconPencil,
Menu,
MenuDropdown,
MenuTarget,
Stack,
Table,
TableTbody,
TableTd,
TableTh,
TableThead,
TableTr,
Text,
Title,
} from "@homarr/ui";
import { api } from "~/trpc/server";
import { IntegrationGroupAccordion } from "./_integration-accordion";
import { IntegrationAvatar } from "./_integration-avatar";
import { DeleteIntegrationActionButton } from "./_integration-buttons";
import { IntegrationCreateDropdownContent } from "./new/_integration-new-dropdown";
interface IntegrationsPageProps {
searchParams: {
tab?: IntegrationKind;
};
}
export default async function IntegrationsPage({
searchParams,
}: IntegrationsPageProps) {
const integrations = await api.integration.all();
const t = await getScopedI18n("integration");
return (
<Container>
<Stack>
<Group justify="space-between" align="center">
<Title>{t("page.list.title")}</Title>
<Menu
width={256}
trapFocus
position="bottom-start"
withinPortal
shadow="md"
keepMounted={false}
>
<MenuTarget>
<Button rightSection={<IconChevronDown size={16} stroke={1.5} />}>
{t("action.create")}
</Button>
</MenuTarget>
<MenuDropdown>
<IntegrationCreateDropdownContent />
</MenuDropdown>
</Menu>
</Group>
<IntegrationList
integrations={integrations}
activeTab={searchParams.tab}
/>
</Stack>
</Container>
);
}
interface IntegrationListProps {
integrations: RouterOutputs["integration"]["all"];
activeTab?: IntegrationKind;
}
const IntegrationList = async ({
integrations,
activeTab,
}: IntegrationListProps) => {
const t = await getScopedI18n("integration");
if (integrations.length === 0) {
return <div>{t("page.list.empty")}</div>;
}
const grouppedIntegrations = integrations.reduce(
(acc, integration) => {
if (!acc[integration.kind]) {
acc[integration.kind] = [];
}
acc[integration.kind].push(integration);
return acc;
},
{} as Record<IntegrationKind, RouterOutputs["integration"]["all"]>,
);
return (
<IntegrationGroupAccordion activeTab={activeTab}>
{objectEntries(grouppedIntegrations).map(([kind, integrations]) => (
<AccordionItem key={kind} value={kind}>
<AccordionControl icon={<IntegrationAvatar size="sm" kind={kind} />}>
<Group>
<Text>{getIntegrationName(kind)}</Text>
<CountBadge count={integrations.length} />
</Group>
</AccordionControl>
<AccordionPanel>
<Table>
<TableThead>
<TableTr>
<TableTh>{t("field.name.label")}</TableTh>
<TableTh>{t("field.url.label")}</TableTh>
<TableTh />
</TableTr>
</TableThead>
<TableTbody>
{integrations.map((integration) => (
<TableTr key={integration.id}>
<TableTd>{integration.name}</TableTd>
<TableTd>
<Anchor
href={integration.url}
target="_blank"
rel="noreferrer"
size="sm"
>
{integration.url}
</Anchor>
</TableTd>
<TableTd>
<Group justify="end">
<ActionIconGroup>
<ActionIcon
component={Link}
href={`/integrations/edit/${integration.id}`}
variant="subtle"
color="gray"
aria-label="Edit integration"
>
<IconPencil size={16} stroke={1.5} />
</ActionIcon>
<DeleteIntegrationActionButton
integration={integration}
count={integrations.length}
/>
</ActionIconGroup>
</Group>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</AccordionPanel>
</AccordionItem>
))}
</IntegrationGroupAccordion>
);
};