"use client";
import React from "react";
import { Box, Center, Group, Progress, ScrollArea, SimpleGrid, Stack, Text } from "@mantine/core";
import { IconDatabase, IconPhoto, IconUsers, IconVideo } from "@tabler/icons-react";
import { getQueryKey } from "@trpc/react-query";
import { clientApi } from "@homarr/api/client";
import { formatBytes } from "@homarr/common";
import { useCurrentIntlLocale, useI18n } from "@homarr/translation/client";
import { WidgetEmptyState } from "../../common/empty-state";
import type { WidgetComponentProps } from "../../definition";
import { getUsableWidgetQueryData, isInitialWidgetQueryPending } from "../../common/query-state";
import { WidgetQueryErrorIndicator, WidgetQueryLoadingState } from "../../common/query-state-indicator";
import { useWidgetRuntimeQueries } from "../../runtime-hooks";
import classes from "./component.module.css";
const MAX_ADVANCED_ALBUMS = 50;
export default function ImmichServerStatsWidget({
integrationIds,
options,
displayMode = "compact",
width,
height,
widgetRuntimeRef,
}: WidgetComponentProps<"immich-serverStats">) {
const t = useI18n("widget.immich-serverStats");
const locale = useCurrentIntlLocale();
const input = { integrationId: integrationIds[0] ?? "" };
const albumsInput = { ...input, limit: MAX_ADVANCED_ALBUMS };
const isAdvanced = displayMode === "advanced";
const albumsEnabled = isAdvanced && integrationIds.length > 0;
const statsQuery = clientApi.widget.immich.getServerStats.useQuery(input);
const stats = getUsableWidgetQueryData(statsQuery);
const albumsQuery = clientApi.widget.immich.getAlbums.useQuery(albumsInput, {
enabled: albumsEnabled,
staleTime: 15 * 60 * 1000,
});
const albums = albumsQuery.data ?? [];
useWidgetRuntimeQueries(widgetRuntimeRef, [
getQueryKey(clientApi.widget.immich.getServerStats, input, "query"),
...(albumsEnabled ? [getQueryKey(clientApi.widget.immich.getAlbums, albumsInput, "query")] : []),
]);
if (isInitialWidgetQueryPending(statsQuery)) return ;
if (!stats) return ;
const statVisibility = getImmichStatVisibility(options, isAdvanced);
const statCount =
Number(statVisibility.showUsers) +
Number(statVisibility.showPhotos) +
Number(statVisibility.showVideos) +
Number(statVisibility.showStorage);
const statsLayout = getImmichStatsLayout(width, height, statCount, isAdvanced);
const statsContent = (
{statVisibility.showUsers && (
}
label={t("users")}
value={stats.userCount.toLocaleString(locale)}
dense={statsLayout.dense}
/>
)}
{statVisibility.showPhotos && (
}
label={t("photos")}
value={stats.photoCount.toLocaleString(locale)}
dense={statsLayout.dense}
/>
)}
{statVisibility.showVideos && (
}
label={t("videos")}
value={stats.videoCount.toLocaleString(locale)}
dense={statsLayout.dense}
/>
)}
{statVisibility.showStorage && (
}
label={t("storage")}
value={formatBytes(stats.totalLibraryUsageInBytes)}
dense={statsLayout.dense}
/>
)}
);
if (!isAdvanced) {
return (
{statsContent}
{statsQuery.error && (
)}
);
}
const sortedAlbums = albums;
const maxAssets = sortedAlbums[0]?.assetCount ?? 1;
return (
{statsContent}
{t("albumLimit", { count: MAX_ADVANCED_ALBUMS })}
{isInitialWidgetQueryPending(albumsQuery) ? (
) : albumsQuery.error && albumsQuery.data === undefined ? (
) : albums.length === 0 ? (
) : (
= 900 ? 2 : 1} spacing="xs">
{sortedAlbums.map((album) => (
{album.albumName}
{album.assetCount.toLocaleString(locale)}
))}
)}
);
}
interface StatItemProps {
icon: React.ReactNode;
label: string;
value: string | number;
dense: boolean;
}
function StatItem({ icon, label, value, dense }: StatItemProps) {
if (dense) {
return (
{value}
{label}
);
}
return (
{icon}
{label}
{value}
);
}
export const getImmichStatsLayout = (width: number, height: number, itemCount: number, isAdvanced = false) => {
const count = Math.max(1, itemCount);
if (isAdvanced) {
return { columns: width >= 720 ? Math.min(count, 4) : width >= 360 ? Math.min(count, 2) : 1, dense: false };
}
const dense = width < 320 || height < 160;
if (height < 140) return { columns: Math.min(count, width >= 360 ? 4 : 2), dense: true };
if (height < 220) return { columns: Math.min(count, 2), dense: true };
return { columns: Math.min(count, width >= 280 ? 2 : 1), dense };
};
export const getImmichStatVisibility = (
options: WidgetComponentProps<"immich-serverStats">["options"],
isAdvanced: boolean,
) => ({
showUsers: isAdvanced || options.showUsers,
showPhotos: isAdvanced || options.showPhotos,
showVideos: isAdvanced || options.showVideos,
showStorage: isAdvanced || options.showStorage,
});