From 9c1cecfc1ec65c7c8f23bb5965dcdd9ea96f154e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 29 Jul 2026 15:35:22 +0000 Subject: [PATCH] Allow deleting completed access review campaigns Completed campaigns were immutable because delete only accepted draft or cancelled status. Extend deletion to completed records and surface delete in the console for that status. Signed-off-by: Cursor Agent Co-authored-by: Bryan FRIMIN --- .../access-reviews/campaigns/AccessReviewCampaignsTab.tsx | 4 ++-- .../access-reviews/campaigns/CampaignDetailPage.tsx | 3 ++- pkg/accessreview/campaign_service.go | 3 ++- pkg/accessreview/errors.go | 2 +- pkg/accessreview/errors_test.go | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/console/src/pages/organizations/access-reviews/campaigns/AccessReviewCampaignsTab.tsx b/apps/console/src/pages/organizations/access-reviews/campaigns/AccessReviewCampaignsTab.tsx index a126edb78..82fc277c6 100644 --- a/apps/console/src/pages/organizations/access-reviews/campaigns/AccessReviewCampaignsTab.tsx +++ b/apps/console/src/pages/organizations/access-reviews/campaigns/AccessReviewCampaignsTab.tsx @@ -132,9 +132,9 @@ export default function AccessReviewCampaignsTab({ queryRef }: Props) { deleteCampaignMutation, ); - // Only DRAFT and CANCELLED campaigns can be deleted (enforced by the backend). + // Only DRAFT, CANCELLED, and COMPLETED campaigns can be deleted (enforced by the backend). const isDeletableStatus = (status: string) => - status === "DRAFT" || status === "CANCELLED"; + status === "DRAFT" || status === "CANCELLED" || status === "COMPLETED"; const handleDelete = (campaignId: string, campaignName: string) => { confirm( diff --git a/apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx b/apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx index 8f3eb2991..1400c471f 100644 --- a/apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx +++ b/apps/console/src/pages/organizations/access-reviews/campaigns/CampaignDetailPage.tsx @@ -217,7 +217,8 @@ export default function CampaignDetailPage({ queryRef }: Props) { const isDraft = campaign.status === "DRAFT"; const isPendingActions = campaign.status === "PENDING_ACTIONS"; const isCancelled = campaign.status === "CANCELLED"; - const canDelete = campaign.canDelete && (isDraft || isCancelled); + const isCompleted = campaign.status === "COMPLETED"; + const canDelete = campaign.canDelete && (isDraft || isCancelled || isCompleted); const campaignIdRef = useRef(campaign.id); diff --git a/pkg/accessreview/campaign_service.go b/pkg/accessreview/campaign_service.go index 4ca5ecb8a..fb979b5d0 100644 --- a/pkg/accessreview/campaign_service.go +++ b/pkg/accessreview/campaign_service.go @@ -206,7 +206,8 @@ func (s *Service) DeleteCampaign( } if campaign.Status != coredata.AccessReviewCampaignStatusDraft && - campaign.Status != coredata.AccessReviewCampaignStatusCancelled { + campaign.Status != coredata.AccessReviewCampaignStatusCancelled && + campaign.Status != coredata.AccessReviewCampaignStatusCompleted { return NewCampaignNotDeletableError(campaign.ID) } diff --git a/pkg/accessreview/errors.go b/pkg/accessreview/errors.go index 3fd1f389b..8e0e29fc4 100644 --- a/pkg/accessreview/errors.go +++ b/pkg/accessreview/errors.go @@ -95,7 +95,7 @@ func NewCampaignNotDeletableError(campaignID gid.GID) error { func (e *CampaignNotDeletableError) Error() string { return fmt.Sprintf( - "access review campaign %q cannot be deleted unless it is draft or cancelled", + "access review campaign %q cannot be deleted unless it is draft, cancelled, or completed", e.CampaignID, ) } diff --git a/pkg/accessreview/errors_test.go b/pkg/accessreview/errors_test.go index 701831a22..e8fe5bc88 100644 --- a/pkg/accessreview/errors_test.go +++ b/pkg/accessreview/errors_test.go @@ -61,7 +61,7 @@ func TestCampaignClientErrors(t *testing.T) { name: "not deletable", err: accessreview.NewCampaignNotDeletableError(campaignID), wantText: fmt.Sprintf( - "access review campaign %q cannot be deleted unless it is draft or cancelled", + "access review campaign %q cannot be deleted unless it is draft, cancelled, or completed", campaignID, ), sentinel: accessreview.ErrCampaignNotDeletable,