From b19ecc0d5f91da3af4ebf82cbcbabad6171cd0e4 Mon Sep 17 00:00:00 2001 From: kitos Date: Fri, 29 May 2026 09:19:07 +0200 Subject: [PATCH] feat(campaigns): prefix test names with [Campaign] on add MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - From template: name is pre-filled as '[Campaign] {template.name}' (user can edit before confirming). - Existing test: renamed via PATCH /tests/{id} to prepend '[Campaign] ' before being linked to the campaign, consistent with the APT-generated campaign flow. Idempotent — skips rename if the name already starts with '[Campaign]'. Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/AddTestToCampaignModal.tsx | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/AddTestToCampaignModal.tsx b/frontend/src/components/AddTestToCampaignModal.tsx index 99b516e..c0a7391 100644 --- a/frontend/src/components/AddTestToCampaignModal.tsx +++ b/frontend/src/components/AddTestToCampaignModal.tsx @@ -12,10 +12,9 @@ import { ChevronRight, Filter, } from "lucide-react"; -import { getTests } from "../api/tests"; +import { getTests, createTestFromTemplate, updateTest } from "../api/tests"; import { addTestToCampaign } from "../api/campaigns"; import { getTemplates, getTemplateById } from "../api/test-templates"; -import { createTestFromTemplate } from "../api/tests"; import type { Test, TestState, TestTemplateSummary } from "../types/models"; /* ── helpers ─────────────────────────────────────────────────────── */ @@ -130,10 +129,13 @@ export default function AddTestToCampaignModal({ enabled: !!selectedTemplateId, }); - // Pre-fill form when full template loads + // Pre-fill form when full template loads — always prefix with [Campaign] useEffect(() => { if (fullTemplate) { - setFormName(fullTemplate.name); + const baseName = fullTemplate.name.startsWith("[Campaign]") + ? fullTemplate.name + : `[Campaign] ${fullTemplate.name}`; + setFormName(baseName); setFormDescription(fullTemplate.description || ""); setFormPlatform(fullTemplate.platform || ""); setFormProcedure(fullTemplate.attack_procedure || ""); @@ -143,14 +145,20 @@ export default function AddTestToCampaignModal({ // ── mutations ───────────────────────────────────────────────────── - /** Add an existing test directly to the campaign */ + /** Add an existing test to the campaign, renaming it with [Campaign] prefix first */ const addExistingMutation = useMutation({ - mutationFn: (testId: string) => - addTestToCampaign(campaignId, { test_id: testId }), - onSuccess: (_data, testId) => { - setAddedIds((prev) => new Set(prev).add(testId)); + mutationFn: async (test: Test) => { + // Rename the test to add [Campaign] prefix if not already present + if (!test.name.startsWith("[Campaign]")) { + await updateTest(test.id, { name: `[Campaign] ${test.name}` }); + } + return addTestToCampaign(campaignId, { test_id: test.id }); + }, + onSuccess: (_data, test) => { + setAddedIds((prev) => new Set(prev).add(test.id)); setAddedCount((n) => n + 1); queryClient.invalidateQueries({ queryKey: ["campaign", campaignId] }); + queryClient.invalidateQueries({ queryKey: ["tests"] }); onSuccess(); }, }); @@ -528,11 +536,11 @@ export default function AddTestToCampaignModal({