diff --git a/web/src/components/filter/CameraGroupSelector.tsx b/web/src/components/filter/CameraGroupSelector.tsx index a700981b6..349c07fee 100644 --- a/web/src/components/filter/CameraGroupSelector.tsx +++ b/web/src/components/filter/CameraGroupSelector.tsx @@ -87,6 +87,8 @@ type CameraGroupSelectorProps = { export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { const { t } = useTranslation(["components/camera"]); const { data: config } = useSWR("config"); + const allowedCameras = useAllowedCameras(); + const isCustomRole = useIsCustomRole(); // tooltip @@ -119,10 +121,22 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { return []; } - return Object.entries(config.camera_groups).sort( - (a, b) => a[1].order - b[1].order, - ); - }, [config]); + const allGroups = Object.entries(config.camera_groups); + + // If custom role, filter out groups where user has no accessible cameras + if (isCustomRole) { + return allGroups + .filter(([, groupConfig]) => { + // Check if user has access to at least one camera in this group + return groupConfig.cameras.some((cameraName) => + allowedCameras.includes(cameraName), + ); + }) + .sort((a, b) => a[1].order - b[1].order); + } + + return allGroups.sort((a, b) => a[1].order - b[1].order); + }, [config, allowedCameras, isCustomRole]); // add group @@ -139,6 +153,7 @@ export function CameraGroupSelector({ className }: CameraGroupSelectorProps) { activeGroup={group} setGroup={setGroup} deleteGroup={deleteGroup} + isCustomRole={isCustomRole} />
setAddGroup(true)} - > - - + {!isCustomRole && ( + + )} {isMobile && }
@@ -228,6 +245,7 @@ type NewGroupDialogProps = { activeGroup?: string; setGroup: (value: string | undefined, replace?: boolean | undefined) => void; deleteGroup: () => void; + isCustomRole?: boolean; }; function NewGroupDialog({ open, @@ -236,6 +254,7 @@ function NewGroupDialog({ activeGroup, setGroup, deleteGroup, + isCustomRole, }: NewGroupDialogProps) { const { t } = useTranslation(["components/camera"]); const { mutate: updateConfig } = useSWR("config"); @@ -371,28 +390,30 @@ function NewGroupDialog({ > {t("group.label")} {t("group.edit")} -
- -
+ + + )}
{currentGroups.map((group) => ( @@ -401,6 +422,7 @@ function NewGroupDialog({ group={group} onDeleteGroup={() => onDeleteGroup(group[0])} onEditGroup={() => onEditGroup(group)} + isReadOnly={isCustomRole} /> ))}
@@ -512,12 +534,14 @@ type CameraGroupRowProps = { group: [string, CameraGroupConfig]; onDeleteGroup: () => void; onEditGroup: () => void; + isReadOnly?: boolean; }; export function CameraGroupRow({ group, onDeleteGroup, onEditGroup, + isReadOnly, }: CameraGroupRowProps) { const { t } = useTranslation(["components/camera"]); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); @@ -564,7 +588,7 @@ export function CameraGroupRow({ - {isMobile && ( + {isMobile && !isReadOnly && ( <> @@ -589,7 +613,7 @@ export function CameraGroupRow({ )} - {!isMobile && ( + {!isMobile && !isReadOnly && (