Files
Dash/apps/frontend/src/widgets/immich.tsx
T
Tomas DvorakandDevin e0ef60eca6 feat(ui): phase 5 — graphs, widgets, theming, PWA, boards, i18n
P5a: 24h uptime bar strip + latency chart on monitor detail, iframe
embed widget, generic JSON-path widget, accent/wallpaper/custom CSS
settings, PWA manifest + service worker, demo GIF.

P5b: transport picker + per-transport fields + test button in
settings; per-monitor alert-rule fields in dialogs.

P5c: container cpu/mem columns, SMART/ZFS/GPU sections on system
detail, subdomains section on domain detail.

P5d: login/setup page + auth middleware client handling, board
switcher + /b/:slug routes, full string extraction to typed t() keys
with {param} interpolation, complete cs locale, language select.

Authored By: TDvorak <[email protected]>

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-22 12:49:51 +02:00

37 lines
1.4 KiB
TypeScript

import type { Item } from "@/types";
import type { WidgetData } from "@/widgets/index";
import { t } from "@/i18n";
// Immich — photo/video counts and library footprint.
function num(d: WidgetData | undefined, k: string): number {
const v = d?.[k];
return typeof v === "number" ? v : 0;
}
const fmtNum = (n: number) =>
n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` : n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
const fmtBytes = (b: number) =>
b >= 1 << 30 ? `${(b / (1 << 30)).toFixed(1)} GB` : `${(b / (1 << 20)).toFixed(0)} MB`;
export function ImmichWidget({ item, data, error }: { item: Item; data: WidgetData | undefined; error: string | undefined }) {
if (error) {
return <span className="font-mono text-[11px] text-text-faint">{item.name} — {error}</span>;
}
return (
<div className="flex min-w-0 items-center gap-4">
<div className="flex min-w-0 flex-col gap-0.5">
<span className="font-mono text-[22px] font-medium leading-none tracking-tight tabular-nums">
{data ? fmtNum(num(data, "photos")) : "…"}
</span>
<span className="text-[10.5px] uppercase tracking-[0.1em] text-text-faint">{t("widget.photosLbl")}</span>
</div>
<div className="ml-auto flex flex-col items-end gap-1 font-mono text-[10.5px] text-text-faint">
<span>{t("widget.videos", { n: fmtNum(num(data, "videos")) })}</span>
<span>{fmtBytes(num(data, "usageBytes"))}</span>
</div>
</div>
);
}