Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/e2e/helpers/e2e-helpers.playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`));
}
Expand Down
28 changes: 28 additions & 0 deletions frontend/web/components/icons/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type IconName =
| 'clock'
| 'close'
| 'close-circle'
| 'cloud-upload'
| 'code'
| 'copy'
| 'copy-outlined'
Expand Down Expand Up @@ -86,6 +87,33 @@ const Icon: FC<IconType> = ({
...rest
}) => {
switch (name) {
case 'cloud-upload': {
return (
<svg
width={width || '24'}
height={height || width || '24'}
viewBox='0 0 512 512'
fill='none'
xmlns='http://www.w3.org/2000/svg'
{...rest}
>
<path
d='M320 367.79h76c55 0 100-29.21 100-83.6s-53-81.47-96-83.6c-8.89-85.06-71-136.8-144-136.8-69 0-113.44 45.79-128 91.2-60 5.7-112 43.88-112 106.4s54 106.4 120 106.4h56'
stroke={fill || 'currentColor'}
strokeWidth='32'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path
d='M320 255.79l-64-64-64 64M256 448.21V207.79'
stroke={fill || 'currentColor'}
strokeWidth='32'
strokeLinecap='round'
strokeLinejoin='round'
/>
</svg>
)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
case 'code': {
return (
<svg
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.create-segment-sources {
&__manual {
min-height: 96px;
}

&__source {
min-height: 110px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import React, { FC, useState } from 'react'
import flagsmith from '@flagsmith/flagsmith'
import classNames from 'classnames'
import AccountStore from 'common/stores/account-store'
import { colorIconAction } from 'common/theme/tokens'
import Button from 'components/base/forms/Button'
import BareButton from 'components/base/forms/BareButton'
import Chip from 'components/base/Chip'
import Icon from 'components/icons/Icon'
import './CreateSegmentSourcesModal.scss'
Comment thread
Zaimwa9 marked this conversation as resolved.

type SegmentSource = {
description: string
image?: string
key: string
name: string
}

type SelectedSegmentSource = SegmentSource | null

const SOURCES: SegmentSource[] = [
{
description:
'Upload identifiers to create a managed segment. Update members with a new upload.',
key: 'csv',
name: 'From a CSV list',
},
{
description:
'Sync a behavioural cohort as a managed segment, updated on schedule or real-time.',
image: '/static/images/integrations/amplitude.svg',
key: 'amplitude',
name: 'Amplitude',
},
{
description:
'A managed segment that updates as users enter and exit your Mixpanel cohort.',
image: '/static/images/integrations/mp.svg',
key: 'mixpanel',
name: 'Mixpanel',
},
{
description:
'Activate an Adobe audience as a managed segment, refreshed automatically by Adobe.',
image: '/static/images/integrations/adobe-analytics.png',
key: 'adobe_journey_manager',
name: 'Adobe Journey Manager',
},
]

type CreateSegmentSourcesModalType = {
onManual: () => void
}

const CreateSegmentSourcesModal: FC<CreateSegmentSourcesModalType> = ({
onManual,
}) => {
Comment thread
Zaimwa9 marked this conversation as resolved.
const [selected, setSelected] = useState<SelectedSegmentSource>(null)
const [requested, setRequested] = useState<string[]>([])

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()
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

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 (
<div className='p-4'>
<p className='h6 fw-semibold text-muted mb-3'>
How do you want to define your segment?
</p>
<BareButton
data-test='create-segment-manually'
onClick={openManual}
className='create-segment-sources__manual w-100 rounded border-1 border-primary p-3 d-flex align-items-start gap-3 mb-3'
>
<span className='mt-1 flex-shrink-0 d-inline-flex'>
<Icon name='options-2' width={24} fill={colorIconAction} />
</span>
<div>
<div className='fw-semibold'>Manually</div>
<div className='fs-small text-muted'>
Build rules based on traits and context values
</div>
</div>
</BareButton>
<div className='row g-0'>
{SOURCES.map((source) => {
const isSelected = selected?.key === source.key
return (
<div key={source.key} className='col-md-6 p-1'>
<BareButton
data-test={`segment-source-${source.key}`}
aria-pressed={isSelected}
onClick={() => 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 ? (
<img
alt={source.name}
src={source.image}
width={24}
height={24}
className='mt-1 flex-shrink-0'
/>
) : (
<span className='mt-1 flex-shrink-0 d-inline-flex'>
<Icon
name='cloud-upload'
width={24}
fill={colorIconAction}
/>
</span>
)}
<div className='flex-fill'>
<div className='fw-semibold'>{source.name}</div>
<div className='fs-small text-muted'>
{source.description}
</div>
</div>
<Chip size='xs' variant='accent' className='fw-semibold'>
<Icon name='rocket' width={12} />
Beta
</Chip>
</BareButton>
</div>
)
})}
</div>
{!!selected && (
<div className='d-flex justify-content-end align-items-center gap-3 mt-4'>
{hasRequested && (
<span className='text-muted'>
Thank you! 🎉 We&apos;ll be in touch.
</span>
)}
<Button
data-test='request-beta-access'
disabled={hasRequested}
onClick={requestAccess}
>
Request access to the beta
</Button>
</div>
)}
</div>
)
}

export default CreateSegmentSourcesModal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './CreateSegmentSourcesModal'
15 changes: 14 additions & 1 deletion frontend/web/components/pages/SegmentsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -85,7 +86,7 @@ const SegmentsPage: FC = () => {
)
}, [showFeatureSpecific, history])

const newSegment = () => {
const openCreateSegmentDrawer = () => {
openModal(
'New Segment',
<CreateSegmentModal
Expand All @@ -99,6 +100,18 @@ const SegmentsPage: FC = () => {
)
}

const newSegment = () => {
if (Utils.getFlagsmithHasFeature('create_segment_with_external_sources')) {
openModal(
'Create Segment',
<CreateSegmentSourcesModal onManual={openCreateSegmentDrawer} />,
'p-0 modal--wide',
)
} else {
openCreateSegmentDrawer()
}
}

const { permission: manageSegmentsPermission } = useHasPermission({
id: projectId,
level: 'project',
Expand Down
3 changes: 3 additions & 0 deletions frontend/web/styles/project/_modals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading