diff --git a/frontend/src/views/admin/ProjectDetail.test.ts b/frontend/src/views/admin/ProjectDetail.test.ts index bbaadc9e..c40f5e6c 100644 --- a/frontend/src/views/admin/ProjectDetail.test.ts +++ b/frontend/src/views/admin/ProjectDetail.test.ts @@ -50,6 +50,17 @@ describe("ProjectDetail setup draft publish workflow", () => { expect(source.indexOf("applySetupDraft(createDefaultSetupDraft(siteOptions.value));", functionIndex)).toBe(-1); }); + it("does not let legacy empty projectInfo overwrite project master data", () => { + const source = readProjectDetail(); + const functionIndex = source.indexOf("const applySetupDraft = (draft: SetupConfigDraft) => {"); + const emptyGuardIndex = source.indexOf("!isProjectPublishSnapshotEmpty(projectInfo) || isSetupDraftEffectivelyEmpty(setupDraft)", functionIndex); + const fallbackIndex = source.indexOf("setupDraft.projectInfo = buildProjectPublishSnapshot();", emptyGuardIndex); + + expect(functionIndex).toBeGreaterThan(-1); + expect(emptyGuardIndex).toBeGreaterThan(functionIndex); + expect(fallbackIndex).toBeGreaterThan(emptyGuardIndex); + }); + it("keeps early termination out of regular visit schedule options", () => { const source = readProjectDetail(); diff --git a/frontend/src/views/admin/ProjectDetail.vue b/frontend/src/views/admin/ProjectDetail.vue index 615aed33..643fa6fb 100644 --- a/frontend/src/views/admin/ProjectDetail.vue +++ b/frontend/src/views/admin/ProjectDetail.vue @@ -4009,6 +4009,9 @@ const isProjectPublishSnapshotShape = (value: unknown): value is ProjectPublishS ); }; +const isProjectPublishSnapshotEmpty = (snapshot: ProjectPublishSnapshot): boolean => + JSON.stringify(snapshot) === JSON.stringify(emptyProjectInfoDraft()); + const applyProjectInfoDraft = (snapshot: ProjectPublishSnapshot) => { suppressEnrollmentFieldSync.value = true; form.value.code = snapshot.code || ""; @@ -4044,7 +4047,11 @@ const applySetupDraft = (draft: SetupConfigDraft) => { setupDraft.siteEnrollmentPlans = normalizeSiteEnrollmentPlans(clone(draft.siteEnrollmentPlans)); setupDraft.monitoringStrategies = clone(draft.monitoringStrategies); setupDraft.centerConfirm = clone(draft.centerConfirm); - applyProjectInfoDraft(projectInfo); + if (!isProjectPublishSnapshotEmpty(projectInfo) || isSetupDraftEffectivelyEmpty(setupDraft)) { + applyProjectInfoDraft(projectInfo); + } else { + setupDraft.projectInfo = buildProjectPublishSnapshot(); + } formBaselineSnapshot.value = serializeFormForCompare(); suppressDraftWatch.value = false; };