"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, });