feat(ui): add rule-based segment editing to the console - #2753
Conversation
Segments can now have attribute-based rules in addition to the uploaded user-ID list. Adds a full-page segment create/update editor with a rule builder shared with the flag targeting tab, sends rules on create and a RuleListValue only when rules change on update, shows rule counts on the segment list, warns about impact on connected flags, and makes the targeting tab's segment clause a multi-select. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Bucketeer console UI to support rule-based user segments (in addition to included-user lists), reusing the existing targeting rule/clause model and wiring rule payloads into the segment create/update API calls. It also refactors the segment editor UX from a slide modal to a dedicated full-page editor and updates flag targeting’s SEGMENT clause UI to support multi-select (OR semantics).
Changes:
- Add segment rule form/value mapping utilities (+ unit tests) and send
ruleson segment create/update (with “send only when changed” semantics on update). - Replace the segment create/update slide modal with a full-page create/update flow under nested
UserSegmentsRootroutes. - Extract a shared
RuleClausesFormcomponent and update targeting SEGMENT value display + UI to support multiple selected segments.
Reviewed changes
Copilot reviewed 27 out of 28 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/dashboard/src/pages/user-segments/utils.ts | Adds segment rule form models plus API payload mapping and “changed” detection helpers. |
| ui/dashboard/src/pages/user-segments/utils.test.ts | Adds unit tests for rule mapping, ID preservation, and change detection. |
| ui/dashboard/src/pages/user-segments/user-segment-modal/segment-warning.tsx | Introduces a reusable warning component listing connected flags. |
| ui/dashboard/src/pages/user-segments/user-segment-modal/segment-create-update-form/index.tsx | Removes the old slide-modal segment editor. |
| ui/dashboard/src/pages/user-segments/user-segment-modal/delete-segment-modal/index.tsx | Updates import path for the new SegmentWarning component. |
| ui/dashboard/src/pages/user-segments/types.ts | Extends the segment form type to include rules. |
| ui/dashboard/src/pages/user-segments/page-loader.tsx | Switches add/edit flows from modal+URL actions to navigation-based routes; keeps upload indicator via navigation state. |
| ui/dashboard/src/pages/user-segments/form-schema.ts | Adds Yup validation for segment rules and clauses. |
| ui/dashboard/src/pages/user-segments/collection-layout/data-collection.tsx | Adds a Rules column (rule count) to the segment list table. |
| ui/dashboard/src/pages/feature-flag-details/targeting/utils.ts | Updates segment value label formatting to support multiple segments (comma-joined). |
| ui/dashboard/src/pages/feature-flag-details/targeting/segment-rule/rule.tsx | Refactors to use the extracted shared RuleClausesForm. |
| ui/dashboard/src/pages/create-update-segment/segment-form/segment-rules.tsx | Adds sortable rule-card UI for segment rules in the new full-page editor. |
| ui/dashboard/src/pages/create-update-segment/segment-form/index.tsx | Implements the new create/update segment form page (general info, user list, rules, impact confirmation). |
| ui/dashboard/src/pages/create-update-segment/segment-form/confirm-rules-impact-modal.tsx | Adds confirmation modal when saving rule changes that impact connected flags. |
| ui/dashboard/src/pages/create-update-segment/page-loader.tsx | Loads segment data for update flows and renders the page-level layout/header. |
| ui/dashboard/src/pages/create-update-segment/page-content.tsx | Wraps the form in a page layout container. |
| ui/dashboard/src/pages/create-update-segment/index.tsx | Adds the top-level Create/Update Segment page entry point. |
| ui/dashboard/src/elements/rule-clauses-form/index.tsx | Extracts shared clause-row editor used by targeting and segment rules. |
| ui/dashboard/src/elements/rule-clauses-form/attribute-key-select.tsx | Fixes UserMessage import to match its new location after extraction. |
| ui/dashboard/src/app/routers.tsx | Adds UserSegmentsRoot with nested routes for list/new/:id. |
| ui/dashboard/src/app/index.tsx | Switches the /segments/* route to use UserSegmentsRoot. |
| ui/dashboard/src/@types/user-segment.ts | Adds types for segment rule payloads and RuleListValue wrapper. |
| ui/dashboard/src/@locales/en/form.json | Adds localized strings for segment rule editor + impact copy + list/rules semantics. |
| ui/dashboard/src/@locales/ja/form.json | Adds localized strings for segment rule editor + impact copy + list/rules semantics. |
| ui/dashboard/src/@locales/en/common.json | Adds or localization key. |
| ui/dashboard/src/@locales/ja/common.json | Adds or localization key. |
| ui/dashboard/src/@api/user-segment/user-segment-updater.ts | Extends update payload typing to optionally include rules (RuleListValue). |
| ui/dashboard/src/@api/user-segment/user-segment-creator.ts | Extends create payload typing to optionally include rules. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Allow DATE clauses in the segment rule condition dropdown (the schema, mapping utilities, and server already support them), and await the bulk upload so failures surface as an error toast instead of being swallowed by the FileReader callback. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 28 changed files in this pull request and generated no new comments.
Suppressed comments (3)
ui/dashboard/src/pages/create-update-segment/segment-form/segment-rules.tsx:190
- The segment rule editor currently offers
RuleClauseType.DATEin the context-kind dropdown ([RuleClauseType.COMPARE, RuleClauseType.DATE]). This contradicts the PR description/issue text stating that the date kind is not offered for segment rules. Please align behavior and documentation (either remove DATE support from the segment UI/schema/mappers/tests, or update the PR description if DATE is intended to be supported).
const segmentSituationOptions = useMemo(
() =>
(situationOptions as SituationOption[]).filter(option =>
[RuleClauseType.COMPARE, RuleClauseType.DATE].includes(option.value)
),
ui/dashboard/src/pages/feature-flag-details/targeting/utils.ts:644
getValueLabelforSEGMENTjoins names in the order ofsegmentUsersrather than the order of the selectedvalues. This can display segment names in an unexpected order relative to the user’s selection (and is less stable if the backing list order changes). Prefer mapping invaluesorder and then filtering out unknown IDs.
return segmentUsers
.filter(item => values.includes(item.id))
.map(item => item.name)
.join(', ');
ui/dashboard/src/pages/create-update-segment/segment-form/index.tsx:122
- The download flow creates an object URL via
URL.createObjectURL(...)but never revokes it. For large segment lists this can leak memory until the tab is closed. Revoke the URL after triggering the download.
window.document.body.appendChild(link);
link.click();
if (link.parentNode) {
link.parentNode.removeChild(link);
}
… leak Render selected segment names in selection order in the targeting clause label, and revoke the object URL after triggering the segment user list download. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 27 out of 28 changed files in this pull request and generated no new comments.
Suppressed comments (2)
ui/dashboard/src/pages/feature-flag-details/targeting/utils.ts:645
getValueLabelnow renders multiple selected segments by doingsegmentUsers.find(...)for each selected id. That becomes O(values × segmentUsers) and is called while rendering targeting rules, so it can get noticeably slow if the segment list is large. Consider building an id→name map once and then looking up names in selection order.
// Map in values order so names render in the user's selection order.
return values
.map(value => segmentUsers.find(item => item.id === value)?.name)
.filter(name => !!name)
.join(', ');
ui/dashboard/src/pages/create-update-segment/segment-form/segment-rules.tsx:190
- The PR description says the segment rule builder does not offer the DATE context kind, but the segment editor code currently includes
RuleClauseType.DATEinsegmentSituationOptions. Please confirm the intended behavior and keep the PR description and UI consistent (either allow DATE in the UI everywhere, or remove DATE support consistently).
// Segment rules reject the SEGMENT and FEATURE_FLAG operators server-side;
// only attribute comparisons and date conditions are offered.
const segmentSituationOptions = useMemo(
() =>
(situationOptions as SituationOption[]).filter(option =>
[RuleClauseType.COMPARE, RuleClauseType.DATE].includes(option.value)
),
Fix #2722
feat(ui): add rule-based segment editing to the console
Overview
This PR adds console support for rule-based user segments. A segment can now have
attribute-based rules in addition to (or instead of) its uploaded user-ID list.
A user is a member of the segment if their ID is in the included-user list OR
they match any rule. Within a rule, all conditions are ANDed; across rules, ORed.
The backend already accepts and evaluates segment rules
(
CreateSegmentRequest.rules,UpdateSegmentRequest.rulesasRuleListValue);this PR wires the console up to it. No proto or Go changes.
Segment editor is now a full page
The old slide modal was too narrow for a comfortable rule builder, so segment
create/update moved to a dedicated page (
pages/create-update-segment/),following the same pattern as flag creation: nested routes under
UserSegmentsRoot, a details header with back navigation, and a card-basedform layout (General info / User-ID list / Rules). The URLs are unchanged
(
/segments/new,/segments/:id), so deep links keep working.Editor details:
tab: sortable rule cards (drag or arrow buttons), add/remove rules and
conditions, attribute-key suggestions from SDK-reported keys.
feature-flag operators inside segment rules; the date kind is not offered).
the rules card, reflecting the evaluation semantics.
"A user is included in this segment if… Within a rule, all conditions must match.").
uploading a new list replaces it, and offers a Download User Segment
action (same behavior as the list page's download).
and each condition needs an attribute, an operator, and at least one value.
Shared rule builder
The clause-row editor was extracted from the targeting tab into
elements/rule-clauses-form/(along withattribute-key-select), and thetargeting tab's
segment-rule/rule.tsxis now a thin wrapper around it —the targeting tab UI is unchanged.
API layer
rulesare sent onCreateSegmentwhen present (IDs empty;the server generates them).
RuleListValuewrapper is sent only when the user actuallymodified rules (including reordering); when absent, rules stay unchanged.
Existing rule/clause IDs are preserved when editing.
Warnings
Saving a segment whose rules changed while it is referenced by flags opens a
confirmation modal listing all affected flags, since changing membership
affects targeting in every connected flag.
Segment list
A Rules column shows each segment's rule count alongside the existing
user count and connected-flags info.
Flag targeting tab — segment clause
The segment clause value is now a multi-select (multiple segments in one
clause = OR). Each option shows a short summary (
N users · M rules), and theselected segments are listed under the field with links to their segment pages.
Misc
after a successful save.
saving a segment with a new user-ID file (passed via navigation state).
Testing
preservation, and change detection (
pages/user-segments/utils.test.ts).yarn tsc,yarn lint,yarn prettier --check,yarn test, andyarn buildall pass.mixed segments; AND within a rule / OR across rules / list-OR-rule
evaluation via the gateway.