Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@steven-tey
Copy link
Collaborator

@steven-tey steven-tey commented Nov 1, 2025

Summary by CodeRabbit

  • New Features

    • "External ID" filter added to customer filters.
    • Search now uses full-text matching for name/email and exact-match for email inputs.
  • UI / UX

    • Search placeholder shortened and input width reduced.
    • Invoice filter is no longer hidden.
    • Empty-state wording changed to "No matching options" and now supports creating options or AI suggestions.
    • Combobox create icon can be customized.
  • Performance

    • Database indexing improved to speed up customer search.

@vercel
Copy link
Contributor

vercel bot commented Nov 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Nov 2, 2025 0:55am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 1, 2025

Walkthrough

Switches customer search endpoints to prefer exact email matches (when applicable) and otherwise use sanitized full-text search on email and name; adds a projectId+email index and a full-text index on (email,name); and makes several UI tweaks to filters, combobox behavior, and search input presentation.

Changes

Cohort / File(s) Summary
API: customer search & count
apps/web/app/(ee)/api/customers/route.ts, apps/web/app/(ee)/api/customers/count/route.ts
Reworked WHERE construction: if search contains "@" match email exactly, otherwise sanitize search with sanitizeFullTextSearch and apply full-text search to email and name. Moved country and linkId to top-level where spreads. Added sanitizeFullTextSearch import and take: 10000 on Prisma groupBy when grouping by linkId.
Database schema: customer indexes
packages/prisma/schema/customer.prisma
Replaced @@index([projectId, email, externalId, name]) with @@index([projectId, email]); added @@fulltext([email, name]).
UI: customer filters & search UI
apps/web/ui/customers/customer-table/use-customer-filters.tsx, apps/web/ui/customers/customer-table/customer-table.tsx, apps/web/lib/swr/use-customers-count.ts
Added External ID filter wiring and included externalId in filter params/activeFilters; search placeholder shortened and input width reduced; use-customers-count now includes externalId in API include.
UI components: Combobox & Filter behavior
packages/ui/src/combobox/index.tsx, packages/ui/src/filter/filter-list.tsx, packages/ui/src/filter/filter-select.tsx
Combobox adds createIcon?: Icon prop and uses it for the create option. filter-list introduces FilterCombobox with internal search and create-on-empty behavior. filter-select expands CommandEmpty signature (selectedFilter, onSelect) and refines empty/loading messaging and rendering.
App-specific filter tweak
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsx
Removed hideInFilterDropdown: true from the Invoice filter option.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API as Customers API
    participant DB as Prisma/DB

    Client->>API: GET /api/customers?search=...&country=...&linkId=...
    activate API
    alt search present
        alt search contains "@"
            API->>API: where = { email: search }
        else
            API->>API: where = { email: { search: sanitizeFullTextSearch(search) }, name: { search: sanitizeFullTextSearch(search) } }
        end
    else no search
        API->>API: where = {}
    end
    API->>API: spread/add top-level filters: country, linkId
    API->>DB: Query (uses projectId+email index and fulltext on email,name)
    activate DB
    DB-->>API: Results
    deactivate DB
    API-->>Client: Response
    deactivate API
Loading

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~45 minutes

  • Areas to focus:
    • Correctness of the revised WHERE branching in both route.ts and count/route.ts (email vs full-text logic and sanitizeFullTextSearch usage).
    • Prisma schema changes: migration impact and full-text index semantics.
    • filter-select CommandEmpty signature change โ€“ verify all consumers compile and behavior matches expectations.
    • FilterCombobox create-on-empty flow and Combobox createIcon prop integration.
    • groupBy with take: 10000 โ€” review performance and intent.

Possibly related PRs

Suggested reviewers

  • TWilson023

Poem

๐Ÿ‡ I nibble through indexes, tidy and bright,
Emails now match swift, fuzzy finds take flight,
Filters bloom, comboboxes learn to create,
Search hops cleaner โ€” the codebase feels great!

Pre-merge checks and finishing touches

โŒ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
โœ… Passed checks (2 passed)
Check name Status Explanation
Description Check โœ… Passed Check skipped - CodeRabbitโ€™s high-level summary is enabled.
Title Check โœ… Passed The PR title "Optimize customers search with FULLTEXT index" directly aligns with the primary changes in this changeset. The core modifications center on adding a FULLTEXT database index on the Customer model and refactoring the search logic in multiple API route files to use full-text search via sanitizeFullTextSearch instead of the previous OR-based startsWith approach. While the PR includes several secondary changes (UI placeholder updates, new filter components, and icon customization), these are all supportive of or related to the main optimization objective. The title is concise, specific, and clearly communicates the primary intentโ€”optimizing customer search performance through database indexing.
โœจ Finishing touches
  • ๐Ÿ“ Generate docstrings
๐Ÿงช Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch optimize-customers

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
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: 1

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between e6644da and 8cabeb8.

๐Ÿ“’ Files selected for processing (2)
  • apps/web/app/(ee)/api/customers/route.ts (2 hunks)
  • packages/prisma/schema/customer.prisma (1 hunks)
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (1)
๐Ÿ“š Learning: 2025-09-24T16:13:00.387Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: packages/prisma/schema/partner.prisma:151-153
Timestamp: 2025-09-24T16:13:00.387Z
Learning: In the Dub codebase, Prisma schemas use single-column indexes without brackets (e.g., `@index(partnerId)`) and multi-column indexes with brackets (e.g., `@index([programId, partnerId])`). This syntax pattern is consistently used throughout their schema files and works correctly with their Prisma version.

Applied to files:

  • packages/prisma/schema/customer.prisma
  • apps/web/app/(ee)/api/customers/route.ts
๐Ÿงฌ Code graph analysis (1)
apps/web/app/(ee)/api/customers/route.ts (1)
packages/prisma/index.ts (1)
  • sanitizeFullTextSearch (19-22)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
๐Ÿ”‡ Additional comments (3)
apps/web/app/(ee)/api/customers/route.ts (1)

15-15: LGTM!

The import of sanitizeFullTextSearch is correctly added and used in the search logic below.

packages/prisma/schema/customer.prisma (2)

28-28: LGTM!

The composite index on [projectId, email] efficiently supports exact email lookups when search contains "@", which is the common case for email-based searches.


34-34: Full-text index is correctly configured for the MySQL database.

The production database is configured as MySQL (in packages/prisma/schema/schema.prisma), which natively supports full-text indexes since version 5.6+. The @@fulltext([email, name, externalId]) index on the Customer model is properly defined for MySQL and will function as intended for full-text search operations.

@steven-tey
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 2, 2025

โœ… Actions performed

Full review triggered.

Copy link
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: 2

โ™ป๏ธ Duplicate comments (1)
apps/web/app/(ee)/api/customers/route.ts (1)

69-74: Full-text search still requires both fields to match

We're still intersecting the full-text predicates (email and name) at the same level, so Prisma issues an AND between them. That means any search term that only matches one field now returns zero rowsโ€”the exact bug flagged in the earlier review. Please wrap those predicates in an OR (and reuse a sanitized token) so that matching either field returns results.

-            : search
-              ? search.includes("@")
-                ? { email: search }
-                : {
-                    email: { search: sanitizeFullTextSearch(search) },
-                    name: { search: sanitizeFullTextSearch(search) },
-                  }
-              : {}),
+            : search
+              ? search.includes("@")
+                ? { email: search }
+                : (() => {
+                    const sanitized = sanitizeFullTextSearch(search);
+                    return sanitized
+                      ? {
+                          OR: [
+                            { email: { search: sanitized } },
+                            { name: { search: sanitized } },
+                          ],
+                        }
+                      : {};
+                  })()
+              : {}),
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 8cabeb8 and 58323fc.

๐Ÿ“’ Files selected for processing (10)
  • apps/web/app/(ee)/api/customers/count/route.ts (3 hunks)
  • apps/web/app/(ee)/api/customers/route.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsx (0 hunks)
  • apps/web/lib/swr/use-customers-count.ts (1 hunks)
  • apps/web/ui/customers/customer-table/customer-table.tsx (1 hunks)
  • apps/web/ui/customers/customer-table/use-customer-filters.tsx (3 hunks)
  • packages/prisma/schema/customer.prisma (1 hunks)
  • packages/ui/src/combobox/index.tsx (3 hunks)
  • packages/ui/src/filter/filter-list.tsx (3 hunks)
  • packages/ui/src/filter/filter-select.tsx (3 hunks)
๐Ÿ’ค Files with no reviewable changes (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsx
๐Ÿšง Files skipped from review as they are similar to previous changes (1)
  • packages/prisma/schema/customer.prisma
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (5)
๐Ÿ“š Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/app/(ee)/api/customers/count/route.ts
๐Ÿ“š Learning: 2025-09-24T16:10:37.349Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/ui/partners/partner-about.tsx:11-11
Timestamp: 2025-09-24T16:10:37.349Z
Learning: In the Dub codebase, the team prefers to import Icon as a runtime value from "dub/ui" and uses Icon as both a type and variable name in component props, even when this creates shadowing. This is their established pattern and should not be suggested for refactoring.

Applied to files:

  • packages/ui/src/filter/filter-list.tsx
๐Ÿ“š Learning: 2025-09-24T16:13:00.387Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: packages/prisma/schema/partner.prisma:151-153
Timestamp: 2025-09-24T16:13:00.387Z
Learning: In the Dub codebase, Prisma schemas use single-column indexes without brackets (e.g., `@index(partnerId)`) and multi-column indexes with brackets (e.g., `@index([programId, partnerId])`). This syntax pattern is consistently used throughout their schema files and works correctly with their Prisma version.

Applied to files:

  • apps/web/app/(ee)/api/customers/route.ts
๐Ÿ“š Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.

Applied to files:

  • apps/web/ui/customers/customer-table/use-customer-filters.tsx
๐Ÿ“š Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.

Applied to files:

  • apps/web/ui/customers/customer-table/use-customer-filters.tsx
๐Ÿงฌ Code graph analysis (4)
apps/web/app/(ee)/api/customers/count/route.ts (1)
packages/prisma/index.ts (1)
  • sanitizeFullTextSearch (19-22)
packages/ui/src/filter/filter-list.tsx (2)
packages/ui/src/combobox/index.tsx (2)
  • ComboboxOption (29-37)
  • Combobox (86-369)
packages/ui/src/icons/index.tsx (1)
  • Icon (80-80)
packages/ui/src/combobox/index.tsx (1)
packages/ui/src/icons/index.tsx (1)
  • Icon (80-80)
apps/web/app/(ee)/api/customers/route.ts (1)
packages/prisma/index.ts (1)
  • sanitizeFullTextSearch (19-22)
โฐ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Copy link
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: 1

โ™ป๏ธ Duplicate comments (3)
packages/ui/src/filter/filter-list.tsx (1)

296-314: Preserve original typed filter values in callbacks.

ComboboxOption.value is always a string (line 180: value: String(opt.value)), but FilterOption["value"] can be string | number | boolean. Passing newOption.value or searchValue directly to onSelect downgrades numeric/boolean filter values to strings, breaking downstream consumers that expect the original typed value.

Apply this diff to look up and preserve the original FilterOption["value"]:

      setSelected={(newOption: ComboboxOption | null) => {
        if (newOption && newOption.value !== String(value)) {
-         // Remove the current value and add the new one
-         onRemove(filterKey, value);
-         onSelect(filterKey, newOption.value);
+         const matchedFilterOption = filter.options?.find((opt) =>
+           typeof opt.value === "string" && typeof newOption.value === "string"
+             ? opt.value.toLowerCase() === newOption.value.toLowerCase()
+             : String(opt.value) === newOption.value,
+         );
+         const typedValue = (matchedFilterOption?.value ?? newOption.value) as FilterOption["value"];
+         
+         onRemove(filterKey, value);
+         onSelect(filterKey, typedValue);
        }
      }}
      options={options}
      onSearchChange={setSearch}
      onCreate={
        hasEmptyOptions && onSelect
          ? async (searchValue: string) => {
-             // Select the search value as a new option
+             const matchedFilterOption = filter.options?.find((opt) =>
+               typeof opt.value === "string"
+                 ? opt.value.toLowerCase() === searchValue.toLowerCase()
+                 : String(opt.value) === searchValue,
+             );
+             const typedValue = (matchedFilterOption?.value ?? searchValue) as FilterOption["value"];
+             
              onRemove(filterKey, value);
-             onSelect(filterKey, searchValue);
+             onSelect(filterKey, typedValue);
              return true;
            }
          : undefined
      }
apps/web/app/(ee)/api/customers/count/route.ts (1)

18-25: Use OR logic for full-text search and guard empty sanitized queries.

The current implementation has two critical issues:

  1. Unintended AND logic: Applying email and name filters in the same object forces Prisma to AND the predicates, requiring both columns to match. This dramatically narrows results compared to the intended "email OR name" behavior.

  2. Unguarded empty query: When sanitizeFullTextSearch strips all characters (e.g., user enters "+++"), Prisma forwards an empty string to Postgres, causing syntax error in tsquery: "".

Apply this diff to fix both issues:

+ const sanitizedSearch =
+   search && !search.includes("@") ? sanitizeFullTextSearch(search) : "";
+
  const commonWhere: Prisma.CustomerWhereInput = {
    projectId: workspace.id,
    ...(email
      ? { email }
      : externalId
        ? { externalId }
        : search
          ? search.includes("@")
            ? { email: search }
-           : {
-               email: { search: sanitizeFullTextSearch(search) },
-               name: { search: sanitizeFullTextSearch(search) },
-             }
+           : sanitizedSearch
+             ? {
+                 OR: [
+                   { email: { search: sanitizedSearch } },
+                   { name: { search: sanitizedSearch } },
+                 ],
+               }
+             : {
+                 OR: [
+                   {
+                     email: {
+                       contains: search,
+                       mode: Prisma.QueryMode.insensitive,
+                     },
+                   },
+                   {
+                     name: {
+                       contains: search,
+                       mode: Prisma.QueryMode.insensitive,
+                     },
+                   },
+                 ],
+               }
          : {}),
apps/web/app/(ee)/api/customers/route.ts (1)

69-75: Restore OR semantics for multi-field full-text search

Prisma combines sibling conditions with AND, so this branch still requires both email and name to match the term. Any record missing one of those fields (common for customers without a name) now drops out of search results โ€” the same regression flagged earlier. Please wrap these search filters in an OR so a match in either column qualifies, and keep the exact-email short circuit.

-            : search
-              ? search.includes("@")
-                ? { email: search }
-                : {
-                    email: { search: sanitizeFullTextSearch(search) },
-                    name: { search: sanitizeFullTextSearch(search) },
-                  }
+            : search
+              ? search.includes("@")
+                ? {
+                    OR: [
+                      { email: search },
+                      { email: { search: sanitizeFullTextSearch(search) } },
+                      { name: { search: sanitizeFullTextSearch(search) } },
+                    ],
+                  }
+                : {
+                    OR: [
+                      { email: { search: sanitizeFullTextSearch(search) } },
+                      { name: { search: sanitizeFullTextSearch(search) } },
+                    ],
+                  }
               : {}),

</blockquote></details>

</blockquote></details>

<details>
<summary>๐Ÿงน Nitpick comments (1)</summary><blockquote>

<details>
<summary>packages/prisma/schema/customer.prisma (1)</summary><blockquote>

`28-34`: **Consider adding index for projectId + externalId queries.**

Removing `@@index([projectId, email, externalId, name])` may degrade performance for queries filtering by `projectId` and `externalId` together. While the `@@unique([projectId, externalId])` constraint exists, it's not optimized for filtering operations.



If queries commonly filter by both `projectId` and `externalId`, consider adding:

```diff
  @@unique([projectId, externalId])
  @@unique([projectConnectId, externalId])
  @@index([projectId, email])
+ @@index([projectId, externalId])
  @@index([projectId, createdAt])
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between e6644da and 58323fc.

๐Ÿ“’ Files selected for processing (10)
  • apps/web/app/(ee)/api/customers/count/route.ts (3 hunks)
  • apps/web/app/(ee)/api/customers/route.ts (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsx (0 hunks)
  • apps/web/lib/swr/use-customers-count.ts (1 hunks)
  • apps/web/ui/customers/customer-table/customer-table.tsx (1 hunks)
  • apps/web/ui/customers/customer-table/use-customer-filters.tsx (3 hunks)
  • packages/prisma/schema/customer.prisma (1 hunks)
  • packages/ui/src/combobox/index.tsx (3 hunks)
  • packages/ui/src/filter/filter-list.tsx (3 hunks)
  • packages/ui/src/filter/filter-select.tsx (3 hunks)
๐Ÿ’ค Files with no reviewable changes (1)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/payouts/use-payout-filters.tsx
๐Ÿงฐ Additional context used
๐Ÿง  Learnings (6)
๐Ÿ“š Learning: 2025-10-15T01:52:37.048Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx:270-303
Timestamp: 2025-10-15T01:52:37.048Z
Learning: In React components with dropdowns or form controls that show modals for confirmation (e.g., role selection, delete confirmation), local state should be reverted to match the prop value when the modal is cancelled. This prevents the UI from showing an unconfirmed change. The solution is to either: (1) pass an onClose callback to the modal that resets the local state, or (2) observe modal visibility state and reset on close. Example context: RoleCell component in apps/web/app/(ee)/partners.dub.co/(dashboard)/profile/members/page-client.tsx where role dropdown should revert to user.role when UpdateUserModal is cancelled.

Applied to files:

  • packages/ui/src/filter/filter-list.tsx
๐Ÿ“š Learning: 2025-09-24T16:10:37.349Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: apps/web/ui/partners/partner-about.tsx:11-11
Timestamp: 2025-09-24T16:10:37.349Z
Learning: In the Dub codebase, the team prefers to import Icon as a runtime value from "dub/ui" and uses Icon as both a type and variable name in component props, even when this creates shadowing. This is their established pattern and should not be suggested for refactoring.

Applied to files:

  • packages/ui/src/filter/filter-list.tsx
  • packages/ui/src/combobox/index.tsx
๐Ÿ“š Learning: 2025-09-24T16:13:00.387Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2872
File: packages/prisma/schema/partner.prisma:151-153
Timestamp: 2025-09-24T16:13:00.387Z
Learning: In the Dub codebase, Prisma schemas use single-column indexes without brackets (e.g., `@index(partnerId)`) and multi-column indexes with brackets (e.g., `@index([programId, partnerId])`). This syntax pattern is consistently used throughout their schema files and works correctly with their Prisma version.

Applied to files:

  • apps/web/app/(ee)/api/customers/route.ts
  • packages/prisma/schema/customer.prisma
๐Ÿ“š Learning: 2025-08-26T15:05:55.081Z
Learnt from: TWilson023
Repo: dubinc/dub PR: 2736
File: apps/web/lib/swr/use-bounty.ts:11-16
Timestamp: 2025-08-26T15:05:55.081Z
Learning: In the Dub codebase, workspace authentication and route structures prevent endless loading states when workspaceId or similar route parameters are missing, so gating SWR loading states on parameter availability is often unnecessary.

Applied to files:

  • apps/web/ui/customers/customer-table/use-customer-filters.tsx
๐Ÿ“š Learning: 2025-10-15T01:05:43.266Z
Learnt from: steven-tey
Repo: dubinc/dub PR: 2958
File: apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx:432-457
Timestamp: 2025-10-15T01:05:43.266Z
Learning: In apps/web/app/app.dub.co/(dashboard)/[slug]/settings/members/page-client.tsx, defer refactoring the custom MenuItem component (lines 432-457) to use the shared dub/ui MenuItem component to a future PR, as requested by steven-tey.

Applied to files:

  • apps/web/ui/customers/customer-table/use-customer-filters.tsx
๐Ÿ“š Learning: 2025-06-06T07:59:03.120Z
Learnt from: devkiran
Repo: dubinc/dub PR: 2177
File: apps/web/lib/api/links/bulk-create-links.ts:66-84
Timestamp: 2025-06-06T07:59:03.120Z
Learning: In apps/web/lib/api/links/bulk-create-links.ts, the team accepts the risk of potential undefined results from links.find() operations when building invalidLinks arrays, because existing links are fetched from the database based on the input links, so matches are expected to always exist.

Applied to files:

  • apps/web/app/(ee)/api/customers/count/route.ts
๐Ÿ”‡ Additional comments (5)
apps/web/lib/swr/use-customers-count.ts (1)

24-24: LGTM!

Adding externalId to the query parameters properly extends the customer count API to support external ID filtering, which aligns with the PR's broader customer search enhancements.

packages/ui/src/combobox/index.tsx (1)

58-58: LGTM!

The new createIcon prop cleanly enables customization of the create-option icon while maintaining backward compatibility with the Plus default.

Also applies to: 98-98, 294-294

packages/ui/src/filter/filter-list.tsx (1)

268-291: LGTM!

The empty-state logic correctly handles different scenarios: prompts users to start typing when the filter has no options, defers to onCreate when a search term exists, and shows "No matching options" for standard filtered lists.

apps/web/app/(ee)/api/customers/count/route.ts (1)

65-65: Verify the 10,000 link limit is sufficient.

The take: 10000 limit prevents unbounded queries when grouping by linkId. Ensure this limit is sufficient for your largest workspaces and won't cause incomplete data in the UI.

packages/prisma/schema/customer.prisma (1)

28-28: Database provider is MySQL, not PostgreSQLโ€”review comment is incorrect.

The schema uses MySQL (datasource db { provider = "mysql" }), not PostgreSQL. The @@fulltext index is properly supported via the enabled "fullTextIndex" preview feature in Prisma. MySQL has native full-text search; the review comment's verification script searches for PostgreSQL-specific GIN indexes (USING gin), which are not applicable here. The schema changes are correctly configured.

Likely an incorrect or invalid review comment.

@steven-tey steven-tey merged commit c8cfa69 into main Nov 2, 2025
8 checks passed
@steven-tey steven-tey deleted the optimize-customers branch November 2, 2025 01:40
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.

2 participants