feat(campaigns): prefix test names with [Campaign] on add
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

- 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 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-05-29 09:19:07 +02:00
parent 2910aea6b2
commit b19ecc0d5f

View File

@@ -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({
</div>
</div>
<button
onClick={() => addExistingMutation.mutate(test.id)}
disabled={addExistingMutation.isPending && addExistingMutation.variables === test.id}
onClick={() => addExistingMutation.mutate(test)}
disabled={addExistingMutation.isPending && addExistingMutation.variables?.id === test.id}
className="ml-3 flex shrink-0 items-center gap-1.5 rounded-lg border border-cyan-500/30 bg-cyan-500/10 px-3 py-1.5 text-xs font-medium text-cyan-400 hover:bg-cyan-500/20 disabled:opacity-50 transition-colors"
>
{addExistingMutation.isPending && addExistingMutation.variables === test.id ? (
{addExistingMutation.isPending && addExistingMutation.variables?.id === test.id ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Plus className="h-3.5 w-3.5" />