Skip to content

Feature: Contact Segmentation (#310)#385

Open
BenoitPrmt wants to merge 2 commits intousesend:mainfrom
BenoitPrmt:feat/contact-segmentation
Open

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

Conversation

@BenoitPrmt
Copy link
Copy Markdown

@BenoitPrmt BenoitPrmt commented Mar 30, 2026

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

Linked to issue #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.
    • Campaign editor: pick a segment (resets on contact book change) and see a live audience badge; campaign details show segment and recipient count.
    • 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 c02baad. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features
    • Create and manage custom contact segments based on specific criteria and filters
    • Target individual campaigns to specific contact segments for precise audience control
    • View audience size when selecting a segment for your campaign
    • Filter contact lists by segment for better organization and management

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: e1b9b106-26ce-40e2-b87c-aabd834a6dff

📥 Commits

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

📒 Files selected for processing (3)
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/contact-list.tsx
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/contact-segments-manager.tsx
  • apps/web/src/server/service/contact-segment-filter.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/web/src/server/service/contact-segment-filter.ts
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/contact-segments-manager.tsx
  • apps/web/src/app/(dashboard)/contacts/[contactBookId]/contact-list.tsx

Walkthrough

This PR adds contact segment support: a new ContactSegment table and Prisma model with indexes and relations; campaign records gain an optional contactSegmentId. Backend services and routers provide CRUD for segments, validation and normalization of segment definitions, and conversion of definitions into Prisma where clauses; campaign/service logic and queries are updated to compute campaign audiences using segments. Frontend adds a ContactSegmentsManager UI, integrates segment selection and audience counts in campaign pages, and wires segment filtering into contacts listing and export. Tests and unit helpers for normalization, matching, and description were added.

Possibly related PRs

Suggested reviewers

  • KMKoushik
🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "Feature: Contact Segmentation (#310)" clearly and specifically describes the main feature being added—contact segmentation functionality—which is the primary focus of all changes across the PR.

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


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

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.

1 participant