fix profile save diff, masksAndZones delete, and config sync

This commit is contained in:
Josh Hawkins 2026-03-11 12:04:35 -05:00
parent 7925d120ae
commit 3cdb40610f
4 changed files with 50 additions and 7 deletions

View File

@ -112,7 +112,7 @@ export function ProfileSectionDropdown({
return (
<>
<DropdownMenu>
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="h-9 gap-2 font-normal">
{editingProfile ? (

View File

@ -1098,6 +1098,7 @@ export default function Settings() {
async (camera: string, section: string, profile: string) => {
try {
await axios.put("config/set", {
requires_restart: 0,
config_data: {
cameras: {
[camera]: {
@ -1197,16 +1198,31 @@ export default function Settings() {
if (currentSectionKey === "masksAndZones") {
try {
const profileData =
config?.cameras?.[selectedCamera]?.profiles?.[profileName];
if (!profileData) return;
// Only delete top-level keys that exist in the profile
const deletePayload: Record<string, string> = {};
if (profileData.zones !== undefined) {
deletePayload.zones = "";
}
if (profileData.motion !== undefined) {
deletePayload.motion = "";
}
if (profileData.objects !== undefined) {
deletePayload.objects = "";
}
if (Object.keys(deletePayload).length === 0) return;
await axios.put("config/set", {
requires_restart: 0,
config_data: {
cameras: {
[selectedCamera]: {
profiles: {
[profileName]: {
zones: "",
motion: { mask: "" },
objects: { mask: "", filters: "" },
},
[profileName]: deletePayload,
},
},
},
@ -1229,6 +1245,7 @@ export default function Settings() {
[
selectedCamera,
currentSectionKey,
config,
handleSelectProfile,
handleDeleteProfileSection,
t,

View File

@ -6,6 +6,7 @@
import get from "lodash/get";
import cloneDeep from "lodash/cloneDeep";
import merge from "lodash/merge";
import unset from "lodash/unset";
import isEqual from "lodash/isEqual";
import mergeWith from "lodash/mergeWith";
@ -497,9 +498,33 @@ export function prepareSectionSavePayload(opts: {
);
// Compute rawFormData (the current stored value for this section)
// For profiles, merge base camera config with profile overrides (matching
// what BaseSection displays in the form) so the diff only contains actual
// user changes, not every field from the merged view.
let rawSectionValue: unknown;
if (level === "camera" && cameraName) {
rawSectionValue = get(config.cameras?.[cameraName], sectionPath);
if (profileInfo.isProfile) {
const baseValue = get(
config.cameras?.[cameraName],
profileInfo.actualSection,
);
const profileOverrides = get(config.cameras?.[cameraName], sectionPath);
if (
profileOverrides &&
typeof profileOverrides === "object" &&
baseValue &&
typeof baseValue === "object"
) {
rawSectionValue = merge(
cloneDeep(baseValue),
cloneDeep(profileOverrides),
);
} else {
rawSectionValue = baseValue;
}
} else {
rawSectionValue = get(config.cameras?.[cameraName], sectionPath);
}
} else {
rawSectionValue = get(config, sectionPath);
}

View File

@ -171,6 +171,7 @@ export default function ProfilesView({
if (Object.keys(configData).length > 0) {
await axios.put("config/set", {
requires_restart: 0,
config_data: { cameras: configData },
});
}