frigate/web/src/components/Statusbar.tsx
GuoQing Liu d34533981f
feat: add i18n (translation/localization) (#16877)
* Translation module init

* Add more i18n keys

* fix: fix string wrong

* refactor: use namespace translation file

* chore: add more translation key

* fix: fix some page name error

* refactor: change Trans tag for t function

* chore: fix some key not work

* chore: fix SearchFilterDialog i18n key error

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* chore: fix en i18n file filter missing some keys

* chore: add some i18n keys

* chore: add more i18n keys again

* feat: add search page i18n

* feat: add explore model i18n keys

* Update web/src/components/menu/GeneralSettings.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/components/menu/GeneralSettings.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/components/menu/GeneralSettings.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* feat: add more live i18n keys

* feat: add more search setting i18n keys

* fix: remove some comment

* fix: fix some setting page url error

* Update web/src/views/settings/SearchSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* fix: add system missing keys

* fix: update password update i18n keys

* chore: remove outdate translation.json file

* fix: fix exploreSettings error

* chore: add object setting i18n keys

* Update web/src/views/recording/RecordingView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/public/locales/en/components/filter.json

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/components/overlay/ExportDialog.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* feat: add more i18n keys

* fix: fix motionDetectionTuner html node

* feat: add more page i18n keys

* fix: cameraStream i18n keys error

* feat: add Player i18n keys

* feat: add more toast i18n keys

* feat: change explore setting name

* feat: add more document title i18n keys

* feat: add more search i18n keys

* fix: fix accessDenied i18n keys error

* chore: add objectType i18n

* chore: add  inputWithTags i18n

* chore: add SearchFilterDialog i18n

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* Update web/src/views/settings/ObjectSettingsView.tsx

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>

* chore: add some missing i18n keys

* chore: remove most import { t } from "i18next";

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
2025-03-16 10:36:20 -05:00

176 lines
5.2 KiB
TypeScript

import { useEmbeddingsReindexProgress } from "@/api/ws";
import {
StatusBarMessagesContext,
StatusMessage,
} from "@/context/statusbar-provider";
import useStats, { useAutoFrigateStats } from "@/hooks/use-stats";
import { useContext, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { FaCheck } from "react-icons/fa";
import { IoIosWarning } from "react-icons/io";
import { MdCircle } from "react-icons/md";
import { Link } from "react-router-dom";
export default function Statusbar() {
const { t } = useTranslation(["views/system"]);
const { messages, addMessage, clearMessages } = useContext(
StatusBarMessagesContext,
)!;
const stats = useAutoFrigateStats();
const cpuPercent = useMemo(() => {
const systemCpu = stats?.cpu_usages["frigate.full_system"]?.cpu;
if (!systemCpu || systemCpu == "0.0") {
return null;
}
return parseInt(systemCpu);
}, [stats]);
const { potentialProblems } = useStats(stats);
useEffect(() => {
clearMessages("stats");
potentialProblems.forEach((problem) => {
addMessage(
"stats",
problem.text,
problem.color,
undefined,
problem.relevantLink,
);
});
}, [potentialProblems, addMessage, clearMessages]);
const { payload: reindexState } = useEmbeddingsReindexProgress();
useEffect(() => {
if (reindexState) {
if (reindexState.status == "indexing") {
clearMessages("embeddings-reindex");
addMessage(
"embeddings-reindex",
t("stats.reindexingEmbeddings", {
processed: Math.floor(
(reindexState.processed_objects / reindexState.total_objects) *
100,
),
}),
);
}
if (reindexState.status === "completed") {
clearMessages("embeddings-reindex");
}
}
}, [reindexState, addMessage, clearMessages, t]);
return (
<div className="absolute bottom-0 left-0 right-0 z-10 flex h-8 w-full items-center justify-between border-t border-secondary-highlight bg-background_alt px-4 dark:text-secondary-foreground">
<div className="flex h-full items-center gap-2">
{cpuPercent && (
<Link to="/system#general">
<div className="flex cursor-pointer items-center gap-2 text-sm hover:underline">
<MdCircle
className={`size-2 ${
cpuPercent < 50
? "text-success"
: cpuPercent < 80
? "text-orange-400"
: "text-danger"
}`}
/>
CPU {cpuPercent}%
</div>
</Link>
)}
{Object.entries(stats?.gpu_usages || {}).map(([name, stats]) => {
if (name == "error-gpu") {
return;
}
let gpuTitle;
switch (name) {
case "amd-vaapi":
gpuTitle = "AMD GPU";
break;
case "intel-vaapi":
case "intel-qsv":
gpuTitle = "Intel GPU";
break;
default:
gpuTitle = name;
break;
}
const gpu = parseInt(stats.gpu);
if (isNaN(gpu)) {
return;
}
return (
<Link key={gpuTitle} to="/system#general">
{" "}
<div
key={gpuTitle}
className="flex cursor-pointer items-center gap-2 text-sm hover:underline"
>
<MdCircle
className={`size-2 ${
gpu < 50
? "text-success"
: gpu < 80
? "text-orange-400"
: "text-danger"
}`}
/>
{gpuTitle} {gpu}%
</div>
</Link>
);
})}
</div>
<div className="no-scrollbar flex h-full max-w-[50%] items-center gap-2 overflow-x-auto">
{Object.entries(messages).length === 0 ? (
<div className="flex items-center gap-2 text-sm">
<FaCheck className="size-3 text-green-500" />
{t("stats.healthy")}
</div>
) : (
Object.entries(messages).map(([key, messageArray]) => (
<div key={key} className="flex h-full items-center gap-2">
{messageArray.map(({ text, color, link }: StatusMessage) => {
const message = (
<div
key={text}
className={`flex items-center gap-2 whitespace-nowrap text-sm ${link ? "cursor-pointer hover:underline" : ""}`}
>
<IoIosWarning
className={`size-5 ${color || "text-danger"}`}
/>
{text}
</div>
);
if (link) {
return (
<Link key={text} to={link}>
{message}
</Link>
);
} else {
return message;
}
})}
</div>
))
)}
</div>
</div>
);
}