frigate/web/src/components/overlay/DeleteRoleDialog.tsx
Josh Hawkins ed1e3a7c9a
Enhance user roles to limit camera access (#20024)
* update config for roles and add validator

* ensure admin and viewer are never overridden

* add class method to user to retrieve all allowed cameras

* enforce config roles in auth api endpoints

* add camera access api dependency functions

* protect review endpoints

* protect preview endpoints

* rename param name for better fastapi injection matching

* remove unneeded

* protect export endpoints

* protect event endpoints

* protect media endpoints

* update auth hook for allowed cameras

* update default app view

* ensure anonymous user always returns all cameras

* limit cameras in explore

* cameras is already a list

* limit cameras in review/history

* limit cameras in live view

* limit cameras in camera groups

* only show face library and classification in sidebar for admin

* remove check in delete reviews

since admin role is required, no need to check camera access. fixes failing test

* pass request with camera access for tests

* more async

* camera access tests

* fix proxy auth tests

* allowed cameras for review tests

* combine event tests and refactor for camera access

* fix post validation for roles

* don't limit roles in create user dialog

* fix triggers endpoints

no need to run require camera access dep since the required role is admin

* fix type

* create and edit role dialogs

* delete role dialog

* fix role change dialog

* update settings view for roles

* i18n changes

* minor spacing tweaks

* docs

* use badges and camera name label component

* clarify docs

* display all cameras badge for admin and viewer

* i18n fix

* use validator to prevent reserved and empty roles from being assigned

* split users and roles into separate tabs in settings

* tweak docs

* clarify docs

* change icon

* don't memoize roles

always recalculate on component render
2025-09-12 05:19:29 -06:00

110 lines
3.1 KiB
TypeScript

import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Trans } from "react-i18next";
import { useTranslation } from "react-i18next";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { useState } from "react";
type DeleteRoleDialogProps = {
show: boolean;
role: string;
onCancel: () => void;
onDelete: () => void;
};
export default function DeleteRoleDialog({
show,
role,
onCancel,
onDelete,
}: DeleteRoleDialogProps) {
const { t } = useTranslation("views/settings");
const [isLoading, setIsLoading] = useState<boolean>(false);
const handleDelete = async () => {
setIsLoading(true);
try {
await onDelete();
} catch (error) {
// Error handled in parent
} finally {
setIsLoading(false);
}
};
return (
<Dialog open={show} onOpenChange={onCancel}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{t("roles.dialog.deleteRole.title")}</DialogTitle>
<DialogDescription>
<Trans
i18nKey="roles.dialog.deleteRole.desc"
ns="views/settings"
values={{ role }}
components={{
strong: <span className="font-medium" />,
}}
/>
</DialogDescription>
</DialogHeader>
<div className="py-3">
<div className="text-sm text-muted-foreground">
<p>
<Trans
ns={"views/settings"}
values={{ role }}
components={{ strong: <span className="font-medium" /> }}
>
roles.dialog.deleteRole.warn
</Trans>
</p>
</div>
</div>
<DialogFooter className="flex gap-2 pt-2 sm:justify-end">
<div className="flex flex-1 flex-col justify-end">
<div className="flex flex-row gap-2 pt-5">
<Button
className="flex flex-1"
aria-label={t("button.cancel", { ns: "common" })}
variant="outline"
disabled={isLoading}
onClick={onCancel}
type="button"
>
{t("button.cancel", { ns: "common" })}
</Button>
<Button
className="flex flex-1"
aria-label={t("button.delete", { ns: "common" })}
variant="destructive"
disabled={isLoading}
onClick={handleDelete}
type="button"
>
{isLoading ? (
<div className="flex flex-row items-center gap-2">
<ActivityIndicator />
<span>{t("roles.dialog.deleteRole.deleting")}</span>
</div>
) : (
t("button.delete", { ns: "common" })
)}
</Button>
</div>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
);
}