From 0aede04ff458c5fffb68c45a1320d38797d6e2db Mon Sep 17 00:00:00 2001 From: wadii Date: Tue, 11 Aug 2026 16:23:44 +0200 Subject: [PATCH] feat: create segment sources fake door modal --- .../e2e/helpers/e2e-helpers.playwright.ts | 5 +- frontend/web/components/icons/Icon.tsx | 28 +++ .../CreateSegmentSourcesModal.scss | 9 + .../CreateSegmentSourcesModal.tsx | 180 ++++++++++++++++++ .../modals/CreateSegmentSourcesModal/index.ts | 1 + .../web/components/pages/SegmentsPage.tsx | 15 +- frontend/web/styles/project/_modals.scss | 3 + 7 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 frontend/web/components/modals/CreateSegmentSourcesModal/CreateSegmentSourcesModal.scss create mode 100644 frontend/web/components/modals/CreateSegmentSourcesModal/CreateSegmentSourcesModal.tsx create mode 100644 frontend/web/components/modals/CreateSegmentSourcesModal/index.ts diff --git a/frontend/e2e/helpers/e2e-helpers.playwright.ts b/frontend/e2e/helpers/e2e-helpers.playwright.ts index e1bd2be30608..f332559af082 100644 --- a/frontend/e2e/helpers/e2e-helpers.playwright.ts +++ b/frontend/e2e/helpers/e2e-helpers.playwright.ts @@ -537,8 +537,11 @@ export class E2EHelpers { topLevelRuleType: 'ALL' | 'ANY' = 'ALL', ) { await this.click(byId('show-create-segment-btn')); - await this.setText(byId('segmentID'), name); const flagsmith = await getFlagsmith(); + if (flagsmith.hasFeature('create_segment_with_external_sources')) { + await this.click(byId('create-segment-manually')); + } + await this.setText(byId('segmentID'), name); if (flagsmith.hasFeature('segment_any_rule_type')) { await this.click(byId(`top-level-rule-type-${topLevelRuleType}`)); } diff --git a/frontend/web/components/icons/Icon.tsx b/frontend/web/components/icons/Icon.tsx index a4b8cc2292e1..c47b4e0ede1a 100644 --- a/frontend/web/components/icons/Icon.tsx +++ b/frontend/web/components/icons/Icon.tsx @@ -17,6 +17,7 @@ export type IconName = | 'clock' | 'close' | 'close-circle' + | 'cloud-upload' | 'code' | 'copy' | 'copy-outlined' @@ -86,6 +87,33 @@ const Icon: FC = ({ ...rest }) => { switch (name) { + case 'cloud-upload': { + return ( + + + + + ) + } case 'code': { return ( void +} + +const CreateSegmentSourcesModal: FC = ({ + onManual, +}) => { + const [selected, setSelected] = useState(null) + const [requested, setRequested] = useState([]) + + const trackSourceEvent = (event: string, source: SegmentSource) => { + flagsmith.trackEvent(event, { + metadata: { + email: AccountStore.getUser()?.email, + organisation: AccountStore.getOrganisation()?.name, + source: source.key, + }, + }) + } + + const openManual = () => { + closeModal() + onManual() + } + + const selectSource = (source: SegmentSource) => { + if (selected?.key === source.key) { + return + } + setSelected(source) + trackSourceEvent('segment_source_clicked', source) + } + + const requestAccess = () => { + if (!selected) { + return + } + trackSourceEvent('segment_source_beta_requested', selected) + setRequested((prev) => [...prev, selected.key]) + } + + const hasRequested = !!selected && requested.includes(selected.key) + + return ( +
+

+ How do you want to define your segment? +

+ + + + +
+
Manually
+
+ Build rules based on traits and context values +
+
+
+
+ {SOURCES.map((source) => { + const isSelected = selected?.key === source.key + return ( +
+ selectSource(source)} + className={classNames( + 'create-segment-sources__source w-100 rounded border-1 p-3 h-100 d-flex align-items-start gap-3', + { 'border-primary bg-primary-opacity-5': isSelected }, + )} + > + {source.image ? ( + {source.name} + ) : ( + + + + )} +
+
{source.name}
+
+ {source.description} +
+
+ + + Beta + +
+
+ ) + })} +
+ {!!selected && ( +
+ {hasRequested && ( + + Thank you! 🎉 We'll be in touch. + + )} + +
+ )} +
+ ) +} + +export default CreateSegmentSourcesModal diff --git a/frontend/web/components/modals/CreateSegmentSourcesModal/index.ts b/frontend/web/components/modals/CreateSegmentSourcesModal/index.ts new file mode 100644 index 000000000000..4825121ef85e --- /dev/null +++ b/frontend/web/components/modals/CreateSegmentSourcesModal/index.ts @@ -0,0 +1 @@ +export { default } from './CreateSegmentSourcesModal' diff --git a/frontend/web/components/pages/SegmentsPage.tsx b/frontend/web/components/pages/SegmentsPage.tsx index 34f15809ad41..d6b0a32d3609 100644 --- a/frontend/web/components/pages/SegmentsPage.tsx +++ b/frontend/web/components/pages/SegmentsPage.tsx @@ -13,6 +13,7 @@ import { useHasPermission } from 'common/providers/Permission' import API from 'project/api' import Button from 'components/base/forms/Button' import CreateSegmentModal from 'components/modals/CreateSegment' +import CreateSegmentSourcesModal from 'components/modals/CreateSegmentSourcesModal' import PanelSearch from 'components/PanelSearch' import JSONReference from 'components/JSONReference' @@ -85,7 +86,7 @@ const SegmentsPage: FC = () => { ) }, [showFeatureSpecific, history]) - const newSegment = () => { + const openCreateSegmentDrawer = () => { openModal( 'New Segment', { ) } + const newSegment = () => { + if (Utils.getFlagsmithHasFeature('create_segment_with_external_sources')) { + openModal( + 'Create Segment', + , + 'p-0 modal--wide', + ) + } else { + openCreateSegmentDrawer() + } + } + const { permission: manageSegmentsPermission } = useHasPermission({ id: projectId, level: 'project', diff --git a/frontend/web/styles/project/_modals.scss b/frontend/web/styles/project/_modals.scss index bc3dc69e3f71..2459465c393d 100644 --- a/frontend/web/styles/project/_modals.scss +++ b/frontend/web/styles/project/_modals.scss @@ -257,6 +257,9 @@ $side-width: 800px; .side-modal--narrow .modal-dialog { width: 640px !important; } + .modal--wide .modal-dialog { + max-width: 860px; + } } @media (max-width: 600px) {