conditionally display actions for admin role only

This commit is contained in:
Josh Hawkins 2025-12-09 14:37:38 -06:00
parent 9cdc10008d
commit 8d5349ca85
4 changed files with 72 additions and 55 deletions

View File

@ -19,6 +19,7 @@ import {
import useKeyboardListener from "@/hooks/use-keyboard-listener"; import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { toast } from "sonner"; import { toast } from "sonner";
import { useIsAdmin } from "@/hooks/use-is-admin";
type ReviewActionGroupProps = { type ReviewActionGroupProps = {
selectedReviews: ReviewSegment[]; selectedReviews: ReviewSegment[];
@ -33,6 +34,7 @@ export default function ReviewActionGroup({
pullLatestData, pullLatestData,
}: ReviewActionGroupProps) { }: ReviewActionGroupProps) {
const { t } = useTranslation(["components/dialog"]); const { t } = useTranslation(["components/dialog"]);
const isAdmin = useIsAdmin();
const onClearSelected = useCallback(() => { const onClearSelected = useCallback(() => {
setSelectedReviews([]); setSelectedReviews([]);
}, [setSelectedReviews]); }, [setSelectedReviews]);
@ -185,6 +187,7 @@ export default function ReviewActionGroup({
</div> </div>
)} )}
</Button> </Button>
{isAdmin && (
<Button <Button
className="flex items-center gap-2 p-2" className="flex items-center gap-2 p-2"
aria-label={t("button.delete", { ns: "common" })} aria-label={t("button.delete", { ns: "common" })}
@ -200,6 +203,7 @@ export default function ReviewActionGroup({
</div> </div>
)} )}
</Button> </Button>
)}
</div> </div>
</div> </div>
</> </>

View File

@ -16,6 +16,7 @@ import {
import useKeyboardListener from "@/hooks/use-keyboard-listener"; import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { toast } from "sonner"; import { toast } from "sonner";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import { useIsAdmin } from "@/hooks/use-is-admin";
type SearchActionGroupProps = { type SearchActionGroupProps = {
selectedObjects: string[]; selectedObjects: string[];
@ -28,6 +29,7 @@ export default function SearchActionGroup({
pullLatestData, pullLatestData,
}: SearchActionGroupProps) { }: SearchActionGroupProps) {
const { t } = useTranslation(["components/filter"]); const { t } = useTranslation(["components/filter"]);
const isAdmin = useIsAdmin();
const onClearSelected = useCallback(() => { const onClearSelected = useCallback(() => {
setSelectedObjects([]); setSelectedObjects([]);
}, [setSelectedObjects]); }, [setSelectedObjects]);
@ -123,6 +125,7 @@ export default function SearchActionGroup({
{t("button.unselect", { ns: "common" })} {t("button.unselect", { ns: "common" })}
</div> </div>
</div> </div>
{isAdmin && (
<div className="flex items-center gap-1 md:gap-2"> <div className="flex items-center gap-1 md:gap-2">
<Button <Button
className="flex items-center gap-2 p-2" className="flex items-center gap-2 p-2"
@ -140,6 +143,7 @@ export default function SearchActionGroup({
)} )}
</Button> </Button>
</div> </div>
)}
</div> </div>
</> </>
); );

View File

@ -31,6 +31,7 @@ import {
import useSWR from "swr"; import useSWR from "swr";
import { Trans, useTranslation } from "react-i18next"; import { Trans, useTranslation } from "react-i18next";
import BlurredIconButton from "../button/BlurredIconButton"; import BlurredIconButton from "../button/BlurredIconButton";
import { useIsAdmin } from "@/hooks/use-is-admin";
type SearchResultActionsProps = { type SearchResultActionsProps = {
searchResult: SearchResult; searchResult: SearchResult;
@ -52,6 +53,7 @@ export default function SearchResultActions({
children, children,
}: SearchResultActionsProps) { }: SearchResultActionsProps) {
const { t } = useTranslation(["views/explore"]); const { t } = useTranslation(["views/explore"]);
const isAdmin = useIsAdmin();
const { data: config } = useSWR<FrigateConfig>("config"); const { data: config } = useSWR<FrigateConfig>("config");
@ -137,7 +139,8 @@ export default function SearchResultActions({
<span>{t("itemMenu.findSimilar.label")}</span> <span>{t("itemMenu.findSimilar.label")}</span>
</MenuItem> </MenuItem>
)} )}
{config?.semantic_search?.enabled && {isAdmin &&
config?.semantic_search?.enabled &&
searchResult.data.type == "object" && ( searchResult.data.type == "object" && (
<MenuItem <MenuItem
aria-label={t("itemMenu.addTrigger.aria")} aria-label={t("itemMenu.addTrigger.aria")}
@ -146,12 +149,14 @@ export default function SearchResultActions({
<span>{t("itemMenu.addTrigger.label")}</span> <span>{t("itemMenu.addTrigger.label")}</span>
</MenuItem> </MenuItem>
)} )}
{isAdmin && (
<MenuItem <MenuItem
aria-label={t("itemMenu.deleteTrackedObject.label")} aria-label={t("itemMenu.deleteTrackedObject.label")}
onClick={() => setDeleteDialogOpen(true)} onClick={() => setDeleteDialogOpen(true)}
> >
<span>{t("button.delete", { ns: "common" })}</span> <span>{t("button.delete", { ns: "common" })}</span>
</MenuItem> </MenuItem>
)}
</> </>
); );

View File

@ -15,6 +15,7 @@ import {
import { HiDotsHorizontal } from "react-icons/hi"; import { HiDotsHorizontal } from "react-icons/hi";
import { SearchResult } from "@/types/search"; import { SearchResult } from "@/types/search";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { useIsAdmin } from "@/hooks/use-is-admin";
type Props = { type Props = {
search: SearchResult | Event; search: SearchResult | Event;
@ -35,6 +36,7 @@ export default function DetailActionsMenu({
const { t } = useTranslation(["views/explore", "views/faceLibrary"]); const { t } = useTranslation(["views/explore", "views/faceLibrary"]);
const navigate = useNavigate(); const navigate = useNavigate();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const isAdmin = useIsAdmin();
const clipTimeRange = useMemo(() => { const clipTimeRange = useMemo(() => {
const startTime = (search.start_time ?? 0) - REVIEW_PADDING; const startTime = (search.start_time ?? 0) - REVIEW_PADDING;
@ -130,7 +132,9 @@ export default function DetailActionsMenu({
</DropdownMenuItem> </DropdownMenuItem>
)} )}
{config?.semantic_search.enabled && search.data.type == "object" && ( {isAdmin &&
config?.semantic_search.enabled &&
search.data.type == "object" && (
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
setIsOpen(false); setIsOpen(false);