require all masks and zones to be on the base config

This commit is contained in:
Josh Hawkins 2026-04-02 08:53:20 -05:00
parent a7761225bc
commit b116f61055
3 changed files with 131 additions and 53 deletions

View File

@ -539,6 +539,7 @@
},
"restart_required": "Restart required (masks/zones changed)",
"disabledInConfig": "Item is disabled in the config file",
"addDisabledProfile": "Add to the base config first, then override in the profile",
"profileBase": "(base)",
"profileOverride": "(override)",
"toast": {

View File

@ -154,7 +154,19 @@ export default function PolygonItem({
cameraConfig?.review.alerts.required_zones || [],
cameraConfig?.review.detections.required_zones || [],
);
url = `cameras.${polygon.camera}.zones.${polygon.name}${alertQueries}${detectionQueries}`;
// Also delete from profiles that have overrides for this zone
let profileQueries = "";
if (allProfileNames && cameraConfig) {
for (const profileName of allProfileNames) {
if (
cameraConfig.profiles?.[profileName]?.zones?.[polygon.name] !==
undefined
) {
profileQueries += `&cameras.${polygon.camera}.profiles.${profileName}.zones.${polygon.name}`;
}
}
}
url = `cameras.${polygon.camera}.zones.${polygon.name}${alertQueries}${detectionQueries}${profileQueries}`;
}
await axios
@ -214,11 +226,41 @@ export default function PolygonItem({
},
};
let cameraUpdate: Record<string, unknown>;
if (editingProfile) {
cameraUpdate = { profiles: { [editingProfile]: deleteSection } };
} else {
// Base mode: also delete from profiles that have overrides for this mask
const profileDeletes: Record<string, unknown> = {};
if (allProfileNames && cameraConfig) {
for (const profileName of allProfileNames) {
const profileData = cameraConfig.profiles?.[profileName];
if (!profileData) continue;
const hasMask =
polygon.type === "motion_mask"
? profileData.motion?.mask?.[polygon.name] !== undefined
: polygon.type === "object_mask"
? profileData.objects?.mask?.[polygon.name] !== undefined ||
Object.values(profileData.objects?.filters || {}).some(
(f) => f?.mask?.[polygon.name] !== undefined,
)
: false;
if (hasMask) {
profileDeletes[profileName] = deleteSection;
}
}
}
cameraUpdate =
Object.keys(profileDeletes).length > 0
? { ...deleteSection, profiles: profileDeletes }
: deleteSection;
}
const configUpdate = {
cameras: {
[polygon.camera]: editingProfile
? { profiles: { [editingProfile]: deleteSection } }
: deleteSection,
[polygon.camera]: cameraUpdate,
},
};
@ -271,6 +313,7 @@ export default function PolygonItem({
index,
setLoadingPolygonIndex,
editingProfile,
allProfileNames,
onDeleted,
],
);
@ -430,22 +473,41 @@ export default function PolygonItem({
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{t("masksAndZones.form.polygonDrawing.delete.title")}
{polygon.polygonSource === "override"
? t(
"masksAndZones.form.polygonDrawing.revertOverride.title",
)
: t("masksAndZones.form.polygonDrawing.delete.title")}
</AlertDialogTitle>
</AlertDialogHeader>
<AlertDialogDescription>
<Trans
ns="views/settings"
values={{
type: t(
`masksAndZones.form.polygonDrawing.type.${polygon.type}`,
{ ns: "views/settings" },
),
name: polygon.friendly_name ?? polygon.name,
}}
>
masksAndZones.form.polygonDrawing.delete.desc
</Trans>
{polygon.polygonSource === "override" ? (
<Trans
ns="views/settings"
values={{
type: t(
`masksAndZones.form.polygonDrawing.type.${polygon.type}`,
{ ns: "views/settings" },
),
name: polygon.friendly_name ?? polygon.name,
}}
>
masksAndZones.form.polygonDrawing.revertOverride.desc
</Trans>
) : (
<Trans
ns="views/settings"
values={{
type: t(
`masksAndZones.form.polygonDrawing.type.${polygon.type}`,
{ ns: "views/settings" },
),
name: polygon.friendly_name ?? polygon.name,
}}
>
masksAndZones.form.polygonDrawing.delete.desc
</Trans>
)}
</AlertDialogDescription>
<AlertDialogFooter>
<AlertDialogCancel>

View File

@ -816,20 +816,25 @@ export default function MasksAndZonesView({
</HoverCard>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.zones.add")}
onClick={() => {
setEditPane("zone");
handleNewPolygon("zone");
}}
>
<LuPlus />
</Button>
<span className="inline-flex">
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.zones.add")}
disabled={!!currentEditingProfile}
onClick={() => {
setEditPane("zone");
handleNewPolygon("zone");
}}
>
<LuPlus />
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{t("masksAndZones.zones.add")}
{currentEditingProfile
? t("masksAndZones.addDisabledProfile")
: t("masksAndZones.zones.add")}
</TooltipContent>
</Tooltip>
</div>
@ -891,20 +896,25 @@ export default function MasksAndZonesView({
</HoverCard>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.motionMasks.add")}
onClick={() => {
setEditPane("motion_mask");
handleNewPolygon("motion_mask");
}}
>
<LuPlus />
</Button>
<span className="inline-flex">
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.motionMasks.add")}
disabled={!!currentEditingProfile}
onClick={() => {
setEditPane("motion_mask");
handleNewPolygon("motion_mask");
}}
>
<LuPlus />
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{t("masksAndZones.motionMasks.add")}
{currentEditingProfile
? t("masksAndZones.addDisabledProfile")
: t("masksAndZones.motionMasks.add")}
</TooltipContent>
</Tooltip>
</div>
@ -968,20 +978,25 @@ export default function MasksAndZonesView({
</HoverCard>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.objectMasks.add")}
onClick={() => {
setEditPane("object_mask");
handleNewPolygon("object_mask");
}}
>
<LuPlus />
</Button>
<span className="inline-flex">
<Button
variant="secondary"
className="size-6 rounded-md bg-secondary-foreground p-1 text-background"
aria-label={t("masksAndZones.objectMasks.add")}
disabled={!!currentEditingProfile}
onClick={() => {
setEditPane("object_mask");
handleNewPolygon("object_mask");
}}
>
<LuPlus />
</Button>
</span>
</TooltipTrigger>
<TooltipContent>
{t("masksAndZones.objectMasks.add")}
{currentEditingProfile
? t("masksAndZones.addDisabledProfile")
: t("masksAndZones.objectMasks.add")}
</TooltipContent>
</Tooltip>
</div>