feat(integrations): add ArchiveTeam Warrior widget (#5784)

* feat(integrations): add ArchiveTeam Warrior widget

* feat(integrations): add support for basic auth for archive warrior

* refactor(integrations): address ArchiveTeam Warrior review feedback

* Increase cache duration from 1 to 5 minutes

---------

Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
This commit is contained in:
GlareandManuel authored and GitHub committed 2026-06-16 10:43:55 +02:00
1 parent 2ae83458e6
commit dd57ee2102
14 files changed
+689

No files matched your search

@@ -0,0 +1,100 @@
"use client";
import { Avatar, Badge, Card, Group, SimpleGrid, Stack, Text } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { humanFileSize } from "@homarr/common";
import { getIconUrl } from "@homarr/definitions";
import type { WidgetComponentProps } from "../definition";
export default function ArchiveTeamWarriorWidget({
integrationIds,
options,
}: WidgetComponentProps<"archiveTeamWarrior">) {
const integrationId = integrationIds[0];
if (!integrationId) {
return null;
}
return <ArchiveTeamWarriorWidgetContent integrationId={integrationId} options={options} />;
}
const ArchiveTeamWarriorWidgetContent = ({
integrationId,
options,
}: {
integrationId: string;
options: WidgetComponentProps<"archiveTeamWarrior">["options"];
}) => {
const [data] = clientApi.widget.archiveTeamWarrior.getStatus.useSuspenseQuery({
integrationId,
});
const utils = clientApi.useUtils();
clientApi.widget.archiveTeamWarrior.subscribeStatus.useSubscription(
{ integrationId },
{
onData: (next) => {
utils.widget.archiveTeamWarrior.getStatus.setData({ integrationId }, next);
},
},
);
const status = data.status;
const projectName = status.project?.title ?? status.selectedProject ?? "No project selected";
return (
<Stack p="xs" gap="xs" h="100%">
<Group justify="space-between" align="center" wrap="nowrap">
<Group gap="xs" wrap="nowrap" miw={0}>
<Avatar size="sm" radius="md" src={getIconUrl("archiveTeamWarrior")} />
<Text size="sm" c="dimmed" lineClamp={1}>
{projectName}
</Text>
</Group>
<Badge size="sm" color={status.status === "Running" ? "green" : "gray"}>
{status.status}
</Badge>
</Group>
{options.showBroadcastMessage && status.broadcastMessage && (
<Card withBorder p="xs">
<Text size="xs" lineClamp={3}>
{status.broadcastMessage}
</Text>
</Card>
)}
<SimpleGrid cols={2} spacing="xs">
<Metric label="Running" value={status.counts.running} />
<Metric label="Done" value={status.counts.completed} />
<Metric label="Failed" value={status.counts.failed} />
<Metric label="Canceled" value={status.counts.canceled} />
</SimpleGrid>
{status.bandwidth && (
<SimpleGrid cols={2} spacing="xs">
<Metric label="Download" value={formatBandwidth(status.bandwidth.receiving)} />
<Metric label="Upload" value={formatBandwidth(status.bandwidth.sending)} />
</SimpleGrid>
)}
</Stack>
);
};
const Metric = ({ label, value }: { label: string; value: number | string }) => (
<Stack gap={0} miw={0}>
<Text size="md" fw={700} lineClamp={1}>
{value}
</Text>
<Text size="xs" c="dimmed" lineClamp={1}>
{label}
</Text>
</Stack>
);
const formatBandwidth = (value?: number) => humanFileSize(Math.round(value ?? 0), "/s");
@@ -0,0 +1,16 @@
import { IconArchive } from "@tabler/icons-react";
import { createWidgetDefinition } from "../definition";
import { optionsBuilder } from "../options";
export const { definition, componentLoader } = createWidgetDefinition("archiveTeamWarrior", {
icon: IconArchive,
createOptions() {
return optionsBuilder.from((factory) => ({
showBroadcastMessage: factory.switch({
defaultValue: true,
}),
}));
},
supportedIntegrations: ["archiveTeamWarrior"],
}).withDynamicImport(() => import("./component"));
+2
View File
@@ -10,6 +10,7 @@ import type { SettingsContextProps } from "@homarr/settings/creator";
import * as anchorNote from "./anchor-note";
import * as audioStats from "./audio-stats";
import * as app from "./app";
import * as archiveTeamWarrior from "./archive-team-warrior";
import * as bookmarks from "./bookmarks";
import * as calendar from "./calendar";
import * as clock from "./clock";
@@ -67,6 +68,7 @@ export const widgetImports = {
clock,
weather,
app,
archiveTeamWarrior,
anchorNote,
notebook,
iframe,