mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-17 21:58:22 +03:00
remove profile badge in settings and add profiles to main menu
This commit is contained in:
parent
39500b20a0
commit
5a1ec5d729
@ -168,6 +168,7 @@
|
|||||||
"systemMetrics": "System metrics",
|
"systemMetrics": "System metrics",
|
||||||
"configuration": "Configuration",
|
"configuration": "Configuration",
|
||||||
"systemLogs": "System logs",
|
"systemLogs": "System logs",
|
||||||
|
"profiles": "Profiles",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"configurationEditor": "Configuration Editor",
|
"configurationEditor": "Configuration Editor",
|
||||||
"languages": "Languages",
|
"languages": "Languages",
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import {
|
|||||||
LuActivity,
|
LuActivity,
|
||||||
LuGithub,
|
LuGithub,
|
||||||
LuLanguages,
|
LuLanguages,
|
||||||
|
LuLayers,
|
||||||
LuLifeBuoy,
|
LuLifeBuoy,
|
||||||
LuList,
|
LuList,
|
||||||
LuLogOut,
|
LuLogOut,
|
||||||
@ -69,6 +70,9 @@ import SetPasswordDialog from "../overlay/SetPasswordDialog";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
|
import type { ProfilesApiResponse } from "@/types/profile";
|
||||||
|
import { getProfileColor } from "@/utils/profileColors";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { supportedLanguageKeys } from "@/lib/const";
|
import { supportedLanguageKeys } from "@/lib/const";
|
||||||
|
|
||||||
@ -84,6 +88,8 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
|||||||
const { getLocaleDocUrl } = useDocDomain();
|
const { getLocaleDocUrl } = useDocDomain();
|
||||||
const { data: profile } = useSWR("profile");
|
const { data: profile } = useSWR("profile");
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
const { data: profilesData, mutate: updateProfiles } =
|
||||||
|
useSWR<ProfilesApiResponse>("profiles");
|
||||||
const logoutUrl = config?.proxy?.logout_url || "/api/logout";
|
const logoutUrl = config?.proxy?.logout_url || "/api/logout";
|
||||||
|
|
||||||
// languages
|
// languages
|
||||||
@ -105,6 +111,41 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
|||||||
});
|
});
|
||||||
}, [t]);
|
}, [t]);
|
||||||
|
|
||||||
|
// profiles
|
||||||
|
|
||||||
|
const allProfileNames = useMemo(
|
||||||
|
() => profilesData?.profiles?.map((p) => p.name) ?? [],
|
||||||
|
[profilesData],
|
||||||
|
);
|
||||||
|
|
||||||
|
const profileFriendlyNames = useMemo(() => {
|
||||||
|
const map = new Map<string, string>();
|
||||||
|
profilesData?.profiles?.forEach((p) => map.set(p.name, p.friendly_name));
|
||||||
|
return map;
|
||||||
|
}, [profilesData]);
|
||||||
|
|
||||||
|
const hasProfiles = allProfileNames.length > 0;
|
||||||
|
|
||||||
|
const handleActivateProfile = async (profileName: string | null) => {
|
||||||
|
try {
|
||||||
|
await axios.put("profile/set", { profile: profileName || null });
|
||||||
|
await updateProfiles();
|
||||||
|
toast.success(
|
||||||
|
profileName
|
||||||
|
? t("profiles.activated", {
|
||||||
|
ns: "views/settings",
|
||||||
|
profile: profileFriendlyNames.get(profileName) ?? profileName,
|
||||||
|
})
|
||||||
|
: t("profiles.deactivated", { ns: "views/settings" }),
|
||||||
|
{ position: "top-center" },
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
toast.error(t("profiles.activateFailed", { ns: "views/settings" }), {
|
||||||
|
position: "top-center",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
|
|
||||||
const { language, setLanguage } = useLanguage();
|
const { language, setLanguage } = useLanguage();
|
||||||
@ -285,6 +326,118 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
|||||||
<span>{t("menu.systemLogs")}</span>
|
<span>{t("menu.systemLogs")}</span>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
{hasProfiles && (
|
||||||
|
<SubItem>
|
||||||
|
<SubItemTrigger
|
||||||
|
className={
|
||||||
|
isDesktop
|
||||||
|
? "cursor-pointer"
|
||||||
|
: "flex items-center p-2 text-sm"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<LuLayers className="mr-2 size-4" />
|
||||||
|
<span>{t("menu.profiles")}</span>
|
||||||
|
</SubItemTrigger>
|
||||||
|
<Portal>
|
||||||
|
<SubItemContent
|
||||||
|
className={
|
||||||
|
isDesktop ? "" : "w-[92%] rounded-lg md:rounded-2xl"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{!isDesktop && (
|
||||||
|
<>
|
||||||
|
<DialogTitle className="sr-only">
|
||||||
|
{t("menu.profiles")}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription className="sr-only">
|
||||||
|
{t("menu.profiles")}
|
||||||
|
</DialogDescription>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<span tabIndex={0} className="sr-only" />
|
||||||
|
<MenuItem
|
||||||
|
className={
|
||||||
|
isDesktop
|
||||||
|
? "cursor-pointer"
|
||||||
|
: "flex items-center p-2 text-sm"
|
||||||
|
}
|
||||||
|
aria-label={t("profiles.baseConfig", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
onClick={() => handleActivateProfile(null)}
|
||||||
|
>
|
||||||
|
<div className="flex w-full items-center justify-between gap-2">
|
||||||
|
<span className="ml-6 mr-2">
|
||||||
|
{t("profiles.baseConfig", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
{!profilesData?.active_profile && (
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="text-xs text-primary-variant"
|
||||||
|
>
|
||||||
|
{t("profiles.active", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
|
{allProfileNames.map((profileName) => {
|
||||||
|
const color = getProfileColor(
|
||||||
|
profileName,
|
||||||
|
allProfileNames,
|
||||||
|
);
|
||||||
|
const isActive =
|
||||||
|
profilesData?.active_profile === profileName;
|
||||||
|
return (
|
||||||
|
<MenuItem
|
||||||
|
key={profileName}
|
||||||
|
className={
|
||||||
|
isDesktop
|
||||||
|
? "cursor-pointer"
|
||||||
|
: "flex items-center p-2 text-sm"
|
||||||
|
}
|
||||||
|
aria-label={
|
||||||
|
profileFriendlyNames.get(profileName) ??
|
||||||
|
profileName
|
||||||
|
}
|
||||||
|
onClick={() =>
|
||||||
|
handleActivateProfile(profileName)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex w-full items-center justify-between gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
"ml-2 size-2 shrink-0 rounded-full",
|
||||||
|
color.dot,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{profileFriendlyNames.get(profileName) ??
|
||||||
|
profileName}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{isActive && (
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className="text-xs text-primary-variant"
|
||||||
|
>
|
||||||
|
{t("profiles.active", {
|
||||||
|
ns: "views/settings",
|
||||||
|
})}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</SubItemContent>
|
||||||
|
</Portal>
|
||||||
|
</SubItem>
|
||||||
|
)}
|
||||||
</DropdownMenuGroup>
|
</DropdownMenuGroup>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -99,7 +99,6 @@ import {
|
|||||||
import type { ProfileState, ProfilesApiResponse } from "@/types/profile";
|
import type { ProfileState, ProfilesApiResponse } from "@/types/profile";
|
||||||
import { getProfileColor } from "@/utils/profileColors";
|
import { getProfileColor } from "@/utils/profileColors";
|
||||||
import { ProfileSectionDropdown } from "@/components/settings/ProfileSectionDropdown";
|
import { ProfileSectionDropdown } from "@/components/settings/ProfileSectionDropdown";
|
||||||
import { Badge } from "@/components/ui/badge";
|
|
||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
import RestartDialog from "@/components/overlay/dialog/RestartDialog";
|
import RestartDialog from "@/components/overlay/dialog/RestartDialog";
|
||||||
import SaveAllPreviewPopover, {
|
import SaveAllPreviewPopover, {
|
||||||
@ -1524,24 +1523,6 @@ export default function Settings() {
|
|||||||
<h2 className="ml-2 text-lg">
|
<h2 className="ml-2 text-lg">
|
||||||
{t("menu.settings", { ns: "common" })}
|
{t("menu.settings", { ns: "common" })}
|
||||||
</h2>
|
</h2>
|
||||||
{profilesData?.active_profile && (
|
|
||||||
<Badge
|
|
||||||
className={cn(
|
|
||||||
"ml-2 cursor-pointer text-white",
|
|
||||||
getProfileColor(
|
|
||||||
profilesData.active_profile,
|
|
||||||
allProfileNames,
|
|
||||||
).bg,
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
setPage("profiles");
|
|
||||||
setContentMobileOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{profileFriendlyNames.get(profilesData.active_profile) ??
|
|
||||||
profilesData.active_profile}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1750,18 +1731,6 @@ export default function Settings() {
|
|||||||
<Heading as="h3" className="mb-0">
|
<Heading as="h3" className="mb-0">
|
||||||
{t("menu.settings", { ns: "common" })}
|
{t("menu.settings", { ns: "common" })}
|
||||||
</Heading>
|
</Heading>
|
||||||
{profilesData?.active_profile && (
|
|
||||||
<Badge
|
|
||||||
className={cn(
|
|
||||||
"cursor-pointer text-white",
|
|
||||||
getProfileColor(profilesData.active_profile, allProfileNames)
|
|
||||||
.bg,
|
|
||||||
)}
|
|
||||||
onClick={() => setPage("profiles")}
|
|
||||||
>
|
|
||||||
{profilesData.active_profile}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{hasPendingChanges && (
|
{hasPendingChanges && (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user