Skip to content

Commit 2dff072

Browse files
committed
feat: standard segment page tabs for csv cohort segments
1 parent f5db619 commit 2dff072

9 files changed

Lines changed: 176 additions & 115 deletions

File tree

frontend/common/services/useCohort.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,16 @@ export const cohortService = service
6767
{ id: arg.segmentId, type: 'Segment' },
6868
{ id: `LIST${arg.projectId}`, type: 'Segment' },
6969
],
70-
query: (query) => ({
71-
body: {
72-
description: query.description,
73-
name: query.name,
74-
},
70+
query: ({
71+
cohortId,
72+
environmentApiKey,
73+
projectId: _projectId,
74+
segmentId: _segmentId,
75+
...body
76+
}) => ({
77+
body,
7578
method: 'PATCH',
76-
url: `environments/${query.environmentApiKey}/cohorts/${query.cohortId}/`,
79+
url: `environments/${environmentApiKey}/cohorts/${cohortId}/`,
7780
}),
7881
}),
7982
// END OF ENDPOINTS

frontend/common/types/requests.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ export type Req = {
197197
segmentId: number
198198
name?: string
199199
description?: string
200+
metadata?: Metadata[]
200201
}
201202
syncCohortCsv: {
202203
environmentApiKey: string

frontend/web/components/CsvPreview/CsvPreview.scss

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,9 @@
1818
white-space: nowrap;
1919
}
2020

21-
thead th {
22-
background: var(--color-surface-muted);
23-
}
24-
2521
tbody tr {
2622
border-top: 1px solid var(--color-border-default);
2723
color: var(--color-text-secondary);
2824
}
29-
30-
.csv-preview__cell--selected {
31-
background: var(--color-surface-action-tint);
32-
}
3325
}
3426
}

frontend/web/components/CsvPreview/CsvPreview.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ const CsvPreview: FC<CsvPreviewType> = ({
2828
{columns.map((column, i) => (
2929
<th
3030
key={i}
31-
className={classNames('fw-semibold', {
32-
'csv-preview__cell--selected': i === selectedColumn,
33-
})}
31+
className={classNames(
32+
'fw-semibold',
33+
i === selectedColumn
34+
? 'bg-surface-action-tint'
35+
: 'bg-surface-muted',
36+
)}
3437
>
3538
{column}
3639
</th>
@@ -44,7 +47,7 @@ const CsvPreview: FC<CsvPreviewType> = ({
4447
<td
4548
key={j}
4649
className={classNames({
47-
'csv-preview__cell--selected': j === selectedColumn,
50+
'bg-surface-action-tint': j === selectedColumn,
4851
})}
4952
>
5053
{row[j]}

frontend/web/components/CsvUpload/CsvUpload.scss

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22
&__droparea {
33
padding: 32px;
44
border: 1px dashed var(--color-border-action);
5-
6-
&--disabled {
7-
opacity: 0.5;
8-
pointer-events: none;
9-
}
105
}
116

127
&__file-card {

frontend/web/components/CsvUpload/CsvUpload.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const CsvUpload: FC<CsvUploadType> = ({
9898
className={classNames(
9999
'csv-upload__droparea text-center rounded-lg',
100100
{
101-
'csv-upload__droparea--disabled': disabled,
101+
'opacity-50 pe-none': disabled,
102102
},
103103
)}
104104
>

frontend/web/components/modals/CreateSegment.tsx

Lines changed: 130 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
useGetSegmentsQuery,
2727
useUpdateSegmentMutation,
2828
} from 'common/services/useSegment'
29+
import { useUpdateCohortMutation } from 'common/services/useCohort'
2930
import Utils from 'common/utils/utils'
3031
import AssociatedSegmentOverrides from 'components/segments/AssociatedSegmentOverrides'
3132
import CohortSegmentDetail from 'components/segments/CohortSegmentDetail'
@@ -146,6 +147,8 @@ const CreateSegment: FC<CreateSegmentType> = ({
146147
const [segment, setSegment] = useState(_segment || defaultSegment)
147148
// A cohort owns its segment's rules, and the API rejects updating one.
148149
const isCohortManaged = !!_segment?.cohort
150+
// CSV cohorts swap the rules form for the synchronisation detail view.
151+
const isCsvCohort = _segment?.cohort?.source_type === 'csv'
149152
const isReadOnly = readOnly || isCohortManaged
150153
const readOnlyMessage = isCohortManaged
151154
? 'This segment is managed by a cohort. Its rules follow the cohort membership and cannot be edited.'
@@ -193,6 +196,8 @@ const CreateSegment: FC<CreateSegmentType> = ({
193196
},
194197
] = useUpdateSegmentMutation()
195198
const [createChangeRequest] = useCreateProjectChangeRequestMutation({})
199+
const [updateCohort, { isLoading: isSavingCohortMetadata }] =
200+
useUpdateCohortMutation()
196201
const isSaving = creating || updating
197202
const [showDescriptions, setShowDescriptions] = useState(false)
198203
const [tab, setTab] = useState(UserTabs.RULES)
@@ -300,6 +305,27 @@ const CreateSegment: FC<CreateSegmentType> = ({
300305

301306
const [valueChanged, setValueChanged] = useState(false)
302307
const [metadataValueChanged, setMetadataValueChanged] = useState(false)
308+
309+
// A managed segment rejects direct updates; its metadata is saved through
310+
// the cohort instead.
311+
const saveCohortMetadata = async () => {
312+
if (!_segment?.cohort) {
313+
return
314+
}
315+
try {
316+
await updateCohort({
317+
cohortId: _segment.cohort.id,
318+
environmentApiKey: _segment.cohort.environment_api_key,
319+
metadata,
320+
projectId: Number(projectId),
321+
segmentId: _segment.id,
322+
}).unwrap()
323+
setMetadataValueChanged(false)
324+
toast('Updated segment')
325+
} catch (error: any) {
326+
toast(error?.data?.metadata?.[0] || 'Error updating segment', 'danger')
327+
}
328+
}
303329
const onClosing = useCallback(() => {
304330
return new Promise<boolean>((resolve) => {
305331
if (valueChanged) {
@@ -549,6 +575,16 @@ const CreateSegment: FC<CreateSegmentType> = ({
549575
/>
550576
}
551577
/>
578+
{isCsvCohort && metadataValueChanged && !readOnly && (
579+
<div className='text-right'>
580+
<Button
581+
disabled={isSavingCohortMetadata}
582+
onClick={saveCohortMetadata}
583+
>
584+
{isSavingCohortMetadata ? 'Saving...' : 'Save Changes'}
585+
</Button>
586+
</div>
587+
)}
552588
</FormGroup>
553589
)
554590

@@ -570,33 +606,42 @@ const CreateSegment: FC<CreateSegmentType> = ({
570606
>
571607
<TabItem tabLabel='General' isDirty={valueChanged}>
572608
<div className='my-4'>
573-
<CreateSegmentRulesTabForm
574-
is4Eyes={is4Eyes}
575-
onCreateChangeRequest={onCreateChangeRequest}
576-
save={save}
577-
condensed={condensed}
578-
segmentsLimitAlert={segmentsLimitAlert}
579-
name={name}
580-
setName={setName}
581-
setValueChanged={setValueChanged}
582-
description={description}
583-
setDescription={setDescription}
584-
identity={identity}
585-
readOnly={isReadOnly}
586-
readOnlyMessage={readOnlyMessage}
587-
showDescriptions={showDescriptions}
588-
setShowDescriptions={setShowDescriptions}
589-
allWarnings={allWarnings}
590-
rulesEl={rulesEl}
591-
isEdit={isEdit}
592-
segment={segment}
593-
isSaving={isSaving}
594-
isValid={isValid}
595-
isLimitReached={isLimitReached}
596-
onCancel={handleCancel}
597-
topLevelRuleType={topLevelRuleType}
598-
setTopLevelRuleType={setTopLevelRuleType}
599-
/>
609+
{isCsvCohort && _segment ? (
610+
<CohortSegmentDetail
611+
hideHeader
612+
projectId={projectId}
613+
segment={_segment}
614+
readOnly={readOnly}
615+
/>
616+
) : (
617+
<CreateSegmentRulesTabForm
618+
is4Eyes={is4Eyes}
619+
onCreateChangeRequest={onCreateChangeRequest}
620+
save={save}
621+
condensed={condensed}
622+
segmentsLimitAlert={segmentsLimitAlert}
623+
name={name}
624+
setName={setName}
625+
setValueChanged={setValueChanged}
626+
description={description}
627+
setDescription={setDescription}
628+
identity={identity}
629+
readOnly={isReadOnly}
630+
readOnlyMessage={readOnlyMessage}
631+
showDescriptions={showDescriptions}
632+
setShowDescriptions={setShowDescriptions}
633+
allWarnings={allWarnings}
634+
rulesEl={rulesEl}
635+
isEdit={isEdit}
636+
segment={segment}
637+
isSaving={isSaving}
638+
isValid={isValid}
639+
isLimitReached={isLimitReached}
640+
onCancel={handleCancel}
641+
topLevelRuleType={topLevelRuleType}
642+
setTopLevelRuleType={setTopLevelRuleType}
643+
/>
644+
)}
600645
</div>
601646
</TabItem>
602647
<TabItem tabLabel={segment.feature ? 'Feature' : 'Features'}>
@@ -651,10 +696,65 @@ const CreateSegment: FC<CreateSegmentType> = ({
651696
>
652697
{/* Horizontal padding comes from the surrounding tab-item. */}
653698
<div className={className || 'my-3'}>
699+
{isCsvCohort && _segment ? (
700+
<CohortSegmentDetail
701+
projectId={projectId}
702+
segment={_segment}
703+
readOnly={readOnly}
704+
/>
705+
) : (
706+
<CreateSegmentRulesTabForm
707+
save={save}
708+
is4Eyes={is4Eyes}
709+
onCreateChangeRequest={onCreateChangeRequest}
710+
condensed={condensed}
711+
segmentsLimitAlert={segmentsLimitAlert}
712+
name={name}
713+
setName={setName}
714+
setValueChanged={setValueChanged}
715+
description={description}
716+
setDescription={setDescription}
717+
identity={identity}
718+
readOnly={isReadOnly}
719+
readOnlyMessage={readOnlyMessage}
720+
showDescriptions={showDescriptions}
721+
setShowDescriptions={setShowDescriptions}
722+
allWarnings={allWarnings}
723+
rulesEl={rulesEl}
724+
isEdit={isEdit}
725+
segment={segment}
726+
isSaving={isSaving}
727+
isValid={isValid}
728+
isLimitReached={isLimitReached}
729+
onCancel={handleCancel}
730+
topLevelRuleType={topLevelRuleType}
731+
setTopLevelRuleType={setTopLevelRuleType}
732+
/>
733+
)}
734+
</div>
735+
</TabItem>
736+
<TabItem
737+
tabLabelString='Custom Fields'
738+
tabLabel={
739+
<Row className='justify-content-center'>Custom Fields</Row>
740+
}
741+
>
742+
<div className={className || 'my-3'}>{MetadataTab}</div>
743+
</TabItem>
744+
</Tabs>
745+
)}
746+
{!(isEdit && !condensed) &&
747+
!(metadataEnable && segmentContentType?.id) && (
748+
<div className={className || 'my-3 mx-4'}>
749+
{isCsvCohort && _segment ? (
750+
<CohortSegmentDetail
751+
projectId={projectId}
752+
segment={_segment}
753+
readOnly={readOnly}
754+
/>
755+
) : (
654756
<CreateSegmentRulesTabForm
655757
save={save}
656-
is4Eyes={is4Eyes}
657-
onCreateChangeRequest={onCreateChangeRequest}
658758
condensed={condensed}
659759
segmentsLimitAlert={segmentsLimitAlert}
660760
name={name}
@@ -678,46 +778,7 @@ const CreateSegment: FC<CreateSegmentType> = ({
678778
topLevelRuleType={topLevelRuleType}
679779
setTopLevelRuleType={setTopLevelRuleType}
680780
/>
681-
</div>
682-
</TabItem>
683-
<TabItem
684-
tabLabelString='Custom Fields'
685-
tabLabel={
686-
<Row className='justify-content-center'>Custom Fields</Row>
687-
}
688-
>
689-
<div className={className || 'my-3'}>{MetadataTab}</div>
690-
</TabItem>
691-
</Tabs>
692-
)}
693-
{!(isEdit && !condensed) &&
694-
!(metadataEnable && segmentContentType?.id) && (
695-
<div className={className || 'my-3 mx-4'}>
696-
<CreateSegmentRulesTabForm
697-
save={save}
698-
condensed={condensed}
699-
segmentsLimitAlert={segmentsLimitAlert}
700-
name={name}
701-
setName={setName}
702-
setValueChanged={setValueChanged}
703-
description={description}
704-
setDescription={setDescription}
705-
identity={identity}
706-
readOnly={isReadOnly}
707-
readOnlyMessage={readOnlyMessage}
708-
showDescriptions={showDescriptions}
709-
setShowDescriptions={setShowDescriptions}
710-
allWarnings={allWarnings}
711-
rulesEl={rulesEl}
712-
isEdit={isEdit}
713-
segment={segment}
714-
isSaving={isSaving}
715-
isValid={isValid}
716-
isLimitReached={isLimitReached}
717-
onCancel={handleCancel}
718-
topLevelRuleType={topLevelRuleType}
719-
setTopLevelRuleType={setTopLevelRuleType}
720-
/>
781+
)}
721782
</div>
722783
)}
723784
</>
@@ -802,17 +863,6 @@ const LoadingCreateSegment: FC<LoadingCreateSegmentType> = (props) => {
802863
)
803864
}
804865

805-
// CSV cohort segments have no editable rules; show the sync detail view.
806-
if (segmentData?.cohort?.source_type === 'csv') {
807-
return (
808-
<CohortSegmentDetail
809-
projectId={props.projectId}
810-
segment={segmentData}
811-
readOnly={props.readOnly}
812-
/>
813-
)
814-
}
815-
816866
return (
817867
<CreateSegment
818868
{...props}

frontend/web/components/segments/CohortSegmentDetail/CohortSegmentDetail.scss

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
.cohort-segment-detail {
2-
border: 1px solid var(--color-border-default);
2+
&--card {
3+
border: 1px solid var(--color-border-default);
4+
}
35

46
&__header {
57
border-bottom: 1px solid var(--color-border-default);
@@ -31,7 +33,6 @@
3133
height: 6px;
3234
border-radius: 3px;
3335
overflow: hidden;
34-
background: var(--color-surface-default);
3536
}
3637

3738
&__progress-bar {

0 commit comments

Comments
 (0)