Skip to content

Feature: Contact Segmentation (#310)#385

Open
BenoitPrmt wants to merge 3 commits into
usesend:mainfrom
BenoitPrmt:feat/contact-segmentation
Open

Feature: Contact Segmentation (#310)#385
BenoitPrmt wants to merge 3 commits into
usesend:mainfrom
BenoitPrmt:feat/contact-segmentation

Conversation

@BenoitPrmt
Copy link
Copy Markdown
Contributor

@BenoitPrmt BenoitPrmt commented Mar 30, 2026

Added availability to create segments on contacts lists and make campaigns over it

Closes #310


Summary by cubic

Add contact segmentation so teams can target subsets of a contact book and see exact audience counts in campaigns.

  • New Features

    • Manage segments per contact book: create, edit, delete with rules (equals, contains, is set, is not set) and live contact counts.
    • Filter contact lists and exports by segment; invalid segment URLs auto-reset.
    • Campaign editor: pick a segment (resets on contact book change) and see a live audience badge; campaign details show segment and recipient count; getCampaign now includes contactSegment.
    • Backend: listSegments, createSegment, updateSegment, deleteSegment, getCampaignAudience; contacts APIs accept segmentId; campaign updates accept contactSegmentId with contact book validation; send/schedule/batching compute audience and totals using segment filters.
  • Migration

    • Adds ContactSegment table and Campaign.contactSegmentId. Run Prisma migrations to apply the new schema and indexes.

Written for commit a1cd608. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Contact Segments: Create, edit, and delete custom segments within contact books using filter rules based on contact properties
    • Segment Filtering: Filter and search contacts by segment in contact lists
    • Campaign Segment Targeting: Target campaigns to specific contact segments instead of all contacts
    • Audience Count Display: View the exact number of recipients for campaigns based on selected segments

Review Change Stack

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 30, 2026

@BenoitPrmt is attempting to deploy a commit to the kmkoushik's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6289d84a-f35d-4641-961f-0e69ee29f7fd

📥 Commits

Reviewing files that changed from the base of the PR and between c02baad and a1cd608.

📒 Files selected for processing (1)
  • apps/web/src/server/service/campaign-service.ts

Walkthrough

This PR implements contact segmentation, allowing users to create segments based on custom contact fields and send campaigns to those segments. The changes introduce a new ContactSegment database table with filter definitions, update campaign sending logic to respect segment constraints, add server-side CRUD operations and query building for segments, and provide UI components for segment management and filtering across campaigns and contact lists.

Possibly related PRs

  • usesend/useSend#274: Both PRs extend the updateCampaign mutation input logic, with this PR adding contactSegmentId support and PR #274 adding html field support.

Suggested reviewers

  • KMKoushik
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Feature: Contact Segmentation (#310)' clearly and concisely summarizes the primary change: adding contact segmentation functionality to the system.
Linked Issues check ✅ Passed All key objectives from issue #310 are implemented: users can define reusable segments based on custom fields, create multiple segments per contact book with flexible rule types (equals, contains, isSet, isNotSet), and campaigns can be sent to specific segments with live audience counts.
Out of Scope Changes check ✅ Passed All code changes are directly aligned with the segmentation feature objectives; no extraneous or unrelated modifications were introduced outside the scope of issue #310.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
apps/web/src/app/(dashboard)/contacts/[contactBookId]/page.tsx (1)

47-47: Use the ~/ alias for this new src import.

contact-segments-manager lives under apps/web/src, so this new import should follow the repo alias convention instead of adding another relative path.

As per coding guidelines, apps/web/src/**/*.{ts,tsx} should "Use the ~/ alias for imports from src in apps/web (e.g., import { x } from "~/utils/x")".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/`(dashboard)/contacts/[contactBookId]/page.tsx at line 47,
The import of ContactSegmentsManager in page.tsx uses a relative path; update
the import to use the apps/web src alias (~/...) per convention. Locate the
import statement for ContactSegmentsManager in page.tsx and replace the relative
path with the aliased path (e.g., import ContactSegmentsManager from "~/...") so
it imports from apps/web/src via the ~/ alias.
apps/web/src/lib/contact-segments.unit.test.ts (1)

8-77: Add one regression test for the server-side filter path.

These tests only cover the in-memory helpers. The new audience counts and contact filtering now depend on buildContactSegmentWhere(), so a mismatch in the Prisma translation can still ship unnoticed. Please add at least one server-side test that exercises isSet / isNotSet through the query path.

As per coding guidelines, apps/web/**/*.{unit,trpc,api,integration}.test.{ts,tsx} should "Add tests when changes impact logic, APIs, or behavior in apps/web".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/contact-segments.unit.test.ts` around lines 8 - 77, Add a
regression test that exercises the server-side filter translation by calling
buildContactSegmentWhere with a segment containing isSet and isNotSet conditions
and asserting the produced Prisma "where" shape (or the actual query result
against a test Prisma client) matches the expected behavior; specifically
construct a segment with conditions like { field: "company", operator: "isSet" }
and { field: "email", operator: "isNotSet" }, call buildContactSegmentWhere(...)
and assert the returned object contains the correct null/not-null checks (or run
a small in-memory/test DB query using that where to verify matching rows). Use
the existing test patterns in contact-segments.unit.test.ts for structure and
reference the buildContactSegmentWhere symbol to locate the code to test.
apps/web/src/server/service/contact-segment-service.ts (1)

9-9: Use the app src alias for this import.

♻️ Suggested change
-import { buildContactSegmentWhere } from "./contact-segment-filter";
+import { buildContactSegmentWhere } from "~/server/service/contact-segment-filter";

As per coding guidelines, apps/web/src/**/*.{ts,tsx}: Use the ~/ alias for imports from src in apps/web.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/server/service/contact-segment-service.ts` at line 9, Replace
the relative import of buildContactSegmentWhere with the app src alias import
(use the ~/ alias) so the module is resolved from the app's src; specifically
update the import in contact-segment-service.ts to import
buildContactSegmentWhere from the aliased path (e.g.,
"~/server/service/contact-segment-filter") instead of
"./contact-segment-filter".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/`(dashboard)/contacts/[contactBookId]/contact-list.tsx:
- Around line 86-89: The URL-stored segmentId must be cleared when the selected
segment no longer exists: after the segmentsQuery
(api.contacts.listSegments.useQuery) returns, check whether the current
segmentId from useUrlState("segment") exists in segmentsQuery.data; if it does
not, call setSegmentId("") (or undefined per useUrlState expectation) to remove
the invalid filter so the contacts/export queries stop sending the deleted id;
implement this check in a useEffect that depends on segmentsQuery.data and
segmentId and reference segmentId, setSegmentId, and segmentsQuery to locate the
code to update.

In
`@apps/web/src/app/`(dashboard)/contacts/[contactBookId]/contact-segments-manager.tsx:
- Around line 168-182: The early return when contactBookVariables.length === 0
hides the segments list; remove that return and instead keep rendering the
segments management UI (the list/rendering code for existing segments) while
showing the informational Card (the existing
Card/CardHeader/CardTitle/CardContent snippet) as a warning/notice; only disable
or hide the segment-creation controls (e.g., the Create/Manage button or
createSegment handler) when contactBookVariables is empty so saved segments
(referenced as segments or whatever list variable the component renders) remain
visible and deletable. Ensure you update any create-segment button or form
(CreateSegmentButton, onCreateSegment, or similar) to be disabled when
contactBookVariables.length === 0 rather than preventing the entire component
from rendering.

In `@apps/web/src/server/service/contact-segment-filter.ts`:
- Around line 44-58: The isSet/isNotSet Prisma filters currently use
Prisma.JsonNull which only matches explicit JSON nulls; update the case handling
in contact-segment-filter (the "isSet" and "isNotSet" branches that build the
properties filter for path) to use Prisma.DbNull instead (for isNotSet use
equals: Prisma.DbNull or for isSet use NOT: { properties: { path, equals:
Prisma.DbNull } } / for the negated case use not: Prisma.DbNull) so missing JSON
keys (SQL NULL) are matched the same way the in-memory matcher does; adjust the
NOT/negation logic accordingly in the code that constructs the properties filter
for path.

---

Nitpick comments:
In `@apps/web/src/app/`(dashboard)/contacts/[contactBookId]/page.tsx:
- Line 47: The import of ContactSegmentsManager in page.tsx uses a relative
path; update the import to use the apps/web src alias (~/...) per convention.
Locate the import statement for ContactSegmentsManager in page.tsx and replace
the relative path with the aliased path (e.g., import ContactSegmentsManager
from "~/...") so it imports from apps/web/src via the ~/ alias.

In `@apps/web/src/lib/contact-segments.unit.test.ts`:
- Around line 8-77: Add a regression test that exercises the server-side filter
translation by calling buildContactSegmentWhere with a segment containing isSet
and isNotSet conditions and asserting the produced Prisma "where" shape (or the
actual query result against a test Prisma client) matches the expected behavior;
specifically construct a segment with conditions like { field: "company",
operator: "isSet" } and { field: "email", operator: "isNotSet" }, call
buildContactSegmentWhere(...) and assert the returned object contains the
correct null/not-null checks (or run a small in-memory/test DB query using that
where to verify matching rows). Use the existing test patterns in
contact-segments.unit.test.ts for structure and reference the
buildContactSegmentWhere symbol to locate the code to test.

In `@apps/web/src/server/service/contact-segment-service.ts`:
- Line 9: Replace the relative import of buildContactSegmentWhere with the app
src alias import (use the ~/ alias) so the module is resolved from the app's
src; specifically update the import in contact-segment-service.ts to import
buildContactSegmentWhere from the aliased path (e.g.,
"~/server/service/contact-segment-filter") instead of
"./contact-segment-filter".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0ae99c22-9183-423c-8b95-b957d36834bf

📥 Commits

Reviewing files that changed from the base of the PR and between bd78ed9 and 3171ffc.

📒 Files selected for processing (15)
  • apps/web/prisma/migrations/20260330193000_add_contact_segments/migration.sql
  • apps/web/prisma/schema.prisma
  • apps/web/src/app/(dashboard)/campaigns/[campaignId]/edit/page.tsx
  • apps/web/src/app/(dashboard)/campaigns/[campaignId]/page.tsx
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/contact-list.tsx
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/contact-segments-manager.tsx
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/page.tsx
  • apps/web/src/lib/contact-segments.ts
  • apps/web/src/lib/contact-segments.unit.test.ts
  • apps/web/src/server/api/routers/campaign.ts
  • apps/web/src/server/api/routers/contacts.ts
  • apps/web/src/server/service/campaign-service.ts
  • apps/web/src/server/service/contact-segment-filter.ts
  • apps/web/src/server/service/contact-segment-service.ts
  • apps/web/src/server/service/webhook-service.unit.test.ts

Comment thread apps/web/src/server/service/contact-segment-filter.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat: Contact Segmentaion

1 participant