From 3bd9325bee973dd7c890ea475134df814bcf320b Mon Sep 17 00:00:00 2001 From: Matthew Meszaros Date: Sun, 30 Aug 2026 00:11:19 -0700 Subject: [PATCH] feat: address review on segments: gate the add-to-campaign and from-segment actions behind the manage-campaigns permission on the segment page, the segments list and the campaign Leads tab so the dashboard never offers an enrolment the API would refuse, trim the segment package and migration comments to the one invariant they carry, and drop the em dash from the editor comment --- internal/app/segment/service.go | 5 ++--- .../infrastructure/db/migrations/000110_segments.up.sql | 7 ++----- web/src/app/app/segments/[id]/page.tsx | 4 +++- web/src/app/app/segments/page.tsx | 4 +++- web/src/components/app/contacts/ContactsTable.tsx | 5 ++++- web/src/components/app/segments/SegmentEditor.tsx | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) diff --git a/internal/app/segment/service.go b/internal/app/segment/service.go index 8c6a5537..17275c72 100644 --- a/internal/app/segment/service.go +++ b/internal/app/segment/service.go @@ -1,6 +1,5 @@ -// Package segment manages saved contact audiences (issue #266). A segment is -// a filter definition plus manual overrides; membership is computed at read -// time by the repository's SQL compiler, so nothing here schedules work. +// Package segment manages saved contact audiences; membership is computed at +// read time, so nothing here schedules work. package segment import ( diff --git a/internal/infrastructure/db/migrations/000110_segments.up.sql b/internal/infrastructure/db/migrations/000110_segments.up.sql index 839751b6..dc8ba5c3 100644 --- a/internal/infrastructure/db/migrations/000110_segments.up.sql +++ b/internal/infrastructure/db/migrations/000110_segments.up.sql @@ -1,8 +1,5 @@ --- Contact segments (issue #266): saved, reusable audiences. A segment is a --- filter tree over contacts (properties, categories, campaign activity, --- engagement, other segments) plus per-contact manual overrides. Membership --- is evaluated at read time, so it is always current and never needs a --- recompute job; only the definition and the overrides are stored. +-- Contact segments (issue #266). Membership is evaluated at read time; only +-- the definition and the manual overrides are stored. CREATE TABLE public.segments ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY, organization_id uuid NOT NULL REFERENCES public.organizations(id) ON DELETE CASCADE, diff --git a/web/src/app/app/segments/[id]/page.tsx b/web/src/app/app/segments/[id]/page.tsx index 1bf071e8..6131f752 100644 --- a/web/src/app/app/segments/[id]/page.tsx +++ b/web/src/app/app/segments/[id]/page.tsx @@ -37,6 +37,8 @@ function SegmentDetail() { const navigate = useNavigate(); const confirm = useConfirm(); const write = useWriteGuard("MANAGE_CONTACTS"); + // Enrolment writes campaign leads, so it takes the campaign permission. + const campaigns = useWriteGuard("MANAGE_CAMPAIGNS"); const segment = useSegment(id); const fields = useSegmentFields(); const remove = useDeleteSegment(); @@ -104,7 +106,7 @@ function SegmentDetail() {