feat(campaigns): prefix test names with [Campaign] on add
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled
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:
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user