Skip to content

Commit de201f4

Browse files
committed
refactor: extract logic and clean up
1 parent a148b36 commit de201f4

File tree

11 files changed

+251
-98
lines changed

11 files changed

+251
-98
lines changed

frontend/web/components/modals/AssociatedSegmentOverrides.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ class TheComponent extends Component {
157157
/>
158158
<div>
159159
<InputGroup
160+
className="col-4"
160161
component={
161162
<EnvironmentSelect
162163
projectId={this.props.projectId}

frontend/web/components/modals/CreateSegmentRulesTabForm.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,8 @@ const CreateSegmentRulesTabForm: React.FC<CreateSegmentRulesTabFormProps> = ({
132132
</div>
133133
)}
134134
<div className='d-flex flex-md-row flex-sm-column gap-2'>
135-
<div className='col-md-6'>
135+
<div className='d-flex flex-column col-md-6'>
136136
<label htmlFor='segmentID'>Name*</label>
137-
<Flex>
138137
<Input
139138
data-test='segmentID'
140139
name='id'
@@ -153,7 +152,6 @@ const CreateSegmentRulesTabForm: React.FC<CreateSegmentRulesTabFormProps> = ({
153152
type='text'
154153
placeholder='E.g. power_users'
155154
/>
156-
</Flex>
157155
</div>
158156
{!condensed && (
159157
<InputGroup

frontend/web/components/modals/CreateSegmentUsersTabContent.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ const CreateSegmentUsersTabContent: React.FC<
9898
<FormGroup>
9999
<InputGroup
100100
title='Environment'
101+
className="col-4"
101102
component={
102103
<EnvironmentSelect
103104
projectId={`${projectId}`}

frontend/web/components/segments/Rule/Rule.tsx

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
splitIfValue,
1717
isInvalidPercentageSplit,
1818
} from './utils'
19+
import { useConditionCache } from './hooks/useConditionCache'
1920

2021
interface RuleProps {
2122
index: number
@@ -41,6 +42,8 @@ const Rule: React.FC<RuleProps> = ({
4142
}) => {
4243
const { conditions: rules } = rule
4344

45+
const { cacheValue, getCachedValue } = useConditionCache()
46+
4447
const updateCondition = (
4548
conditionIndex: number,
4649
updates: Partial<SegmentCondition>,
@@ -56,7 +59,15 @@ const Rule: React.FC<RuleProps> = ({
5659
}
5760

5861
const setConditionProperty = (conditionIndex: number, property: string) => {
59-
updateCondition(conditionIndex, { property })
62+
const condition = rule.conditions[conditionIndex]
63+
64+
if (condition.property) {
65+
cacheValue(conditionIndex, condition.operator, condition.property, condition.value)
66+
}
67+
68+
const cachedValue = getCachedValue(conditionIndex, condition.operator, property)
69+
70+
updateCondition(conditionIndex, { property, value: cachedValue })
6071
}
6172

6273
const setConditionOperator = (
@@ -71,6 +82,10 @@ const Rule: React.FC<RuleProps> = ({
7182
)
7283
const newOperator = operators.find((op) => op.value === operatorValue)
7384

85+
if (condition.operator && condition.property) {
86+
cacheValue(conditionIndex, condition.operator, condition.property, condition.value)
87+
}
88+
7489
// Split the append part of the operator as not handled by backend
7590
const updates: Partial<SegmentCondition> = {
7691
operator: operatorValue?.split(':')[0],
@@ -103,6 +118,22 @@ const Rule: React.FC<RuleProps> = ({
103118
}
104119
}
105120

121+
const finalProperty = updates.property !== undefined ? updates.property : condition.property
122+
const finalOperator = operatorValue?.split(':')[0]
123+
124+
if (finalProperty && !newOperator?.hideValue && operatorValue !== 'PERCENTAGE_SPLIT') {
125+
const cachedValue = getCachedValue(conditionIndex, finalOperator, finalProperty)
126+
127+
if (cachedValue !== '') {
128+
// Use cached value if one exists (restore previous input)
129+
updates.value = cachedValue
130+
} else if (!updates.value) {
131+
// No cache and no value set by other logic - clear the input
132+
updates.value = ''
133+
}
134+
// Otherwise: preserve value set by append logic or existing value
135+
}
136+
106137
updateCondition(conditionIndex, updates)
107138
}
108139

frontend/web/components/segments/Rule/components/RuleConditionRow.tsx

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo, useState } from 'react'
1+
import React from 'react'
22
import Icon from 'components/Icon'
33
import Utils from 'common/utils/utils'
44
import {
@@ -13,8 +13,6 @@ import RuleConditionPropertySelect from './RuleConditionPropertySelect'
1313
import RuleConditionValueInput from './RuleConditionValueInput'
1414
import { RuleContextValues } from 'common/types/rules.types'
1515
import { useRuleOperator, useRuleContext } from 'components/segments/Rule/hooks'
16-
import MultiSelect from 'components/base/select/MultiSelect'
17-
import { useGetEnvironmentsQuery } from 'common/services/useEnvironment'
1816

1917
interface RuleConditionRowProps {
2018
rule: SegmentCondition
@@ -49,25 +47,20 @@ const RuleConditionRow: React.FC<RuleConditionRowProps> = ({
4947
setRuleProperty,
5048
showDescription,
5149
}) => {
52-
const [selectedEnvironments, setSelectedEnvironments] = useState<string[]>([])
5350
const lastIndex = rules.reduce((acc, v, i) => {
5451
if (!v.delete) {
5552
return i
5653
}
5754
return acc
5855
}, 0)
59-
const { data } = useGetEnvironmentsQuery({ projectId: projectId?.toString() })
60-
const environments = data?.results
61-
62-
const environmentOptions = useMemo(() => environments ? environments?.map(({ name }) => ({ label: name, value: name })) : [],[environments])
6356

6457
const isLastRule = ruleIndex === lastIndex
6558
const hasOr = ruleIndex > 0
6659

6760
const { displayValue, operator, operatorObj, valuePlaceholder } =
6861
useRuleOperator(rule, operators)
6962

70-
const { allowedContextValues, isValueFromContext, showEnvironmentDropdown } =
63+
const { allowedContextValues, isValueFromContext } =
7164
useRuleContext(operator, rule.property)
7265

7366
if (rule.delete) {
@@ -79,9 +72,6 @@ const RuleConditionRow: React.FC<RuleConditionRowProps> = ({
7972
operator === 'PERCENTAGE_SPLIT' &&
8073
rule.property === RuleContextValues.IDENTITY_KEY
8174

82-
83-
console.log('showEnvironmentDropdown', showEnvironmentDropdown)
84-
console.log('rule', rule)
8575
return (
8676
<div className='rule__row reveal' key={ruleIndex}>
8777
{hasOr && (
@@ -124,23 +114,15 @@ const RuleConditionRow: React.FC<RuleConditionRowProps> = ({
124114
className="col-10 col-md-3"
125115
/>
126116
)}
127-
{operator === 'IN' && rule.property === RuleContextValues.ENVIRONMENT_NAME ? (
128-
<MultiSelect
129-
selectedValues={selectedEnvironments}
130-
onSelectionChange={(selectedValues: string[]) => setSelectedEnvironments(selectedValues)}
131-
options={environmentOptions}
132-
className='col-10 col-md-4'
133-
/>
134-
) : (
135117
<RuleConditionValueInput
136118
readOnly={readOnly}
137119
data-test={`${dataTest}-value-${ruleIndex}`}
138120
value={displayValue || ''}
139121
placeholder={valuePlaceholder}
140122
disabled={operatorObj && operatorObj.hideValue}
141123
projectId={projectId}
142-
showEnvironmentDropdown={showEnvironmentDropdown}
143124
operator={operator}
125+
property={rule.property}
144126
onChange={(value: string) => {
145127
setRuleProperty(ruleIndex, 'value', {
146128
value:
@@ -152,7 +134,6 @@ const RuleConditionRow: React.FC<RuleConditionRowProps> = ({
152134
isValid={Utils.validateRule(rule) && !ruleErrors?.value}
153135
className='col-10 col-md-4'
154136
/>
155-
)}
156137
</div>
157138
<div className='d-flex flex-sm-column flex-md-row gap-2'>
158139
{isLastRule && !readOnly ? (

frontend/web/components/segments/Rule/components/RuleConditionValueInput.tsx

Lines changed: 106 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react'
1+
import React, { useMemo } from 'react'
22
import Input from 'components/base/forms/Input'
33
import Icon from 'components/Icon'
44
import Utils from 'common/utils/utils'
@@ -7,6 +7,11 @@ import { getDarkMode } from 'project/darkMode'
77
import EnvironmentSelectDropdown from './EnvironmentSelectDropdown'
88
import TextAreaModal from './TextAreaModal'
99
import { checkWhitespaceIssues } from 'components/segments/Rule/utils'
10+
import MultiSelect from 'components/base/select/MultiSelect'
11+
import { safeParseArray } from 'components/segments/Rule/utils/arrayUtils'
12+
import { useGetEnvironmentsQuery } from 'common/services/useEnvironment'
13+
import { useConditionInputType } from 'components/segments/Rule/hooks/useConditionInputType'
14+
import { OperatorValue } from 'common/types/rules.types'
1015

1116
type RuleConditionValueInputProps = {
1217
'data-test'?: string
@@ -18,21 +23,66 @@ type RuleConditionValueInputProps = {
1823
readOnly?: boolean
1924
isValid?: boolean
2025
projectId?: number
21-
showEnvironmentDropdown?: boolean
22-
operator?: string
26+
operator?: OperatorValue
27+
property?: string
2328
className?: string
2429
}
2530

2631
const RuleConditionValueInput: React.FC<RuleConditionValueInputProps> = ({
2732
isValid,
2833
onChange,
2934
operator,
35+
property,
3036
projectId,
31-
showEnvironmentDropdown,
3237
value,
3338
className,
3439
...props
3540
}) => {
41+
const { data } = useGetEnvironmentsQuery(
42+
{ projectId: projectId?.toString() || '' },
43+
{ skip: !projectId },
44+
)
45+
const environmentOptions = useMemo(
46+
() =>
47+
data?.results
48+
? data.results.map(({ name }) => ({ label: name, value: name }))
49+
: [],
50+
[data],
51+
)
52+
53+
const { showMultiEnvironmentSelect, showSingleEnvironmentSelect } = useConditionInputType(
54+
operator,
55+
property,
56+
)
57+
58+
if (showMultiEnvironmentSelect) {
59+
return (
60+
<div className={className}>
61+
<MultiSelect
62+
selectedValues={safeParseArray(value)}
63+
onSelectionChange={(selectedValues: string[]) => {
64+
onChange?.(JSON.stringify(selectedValues))
65+
}}
66+
options={environmentOptions}
67+
className='w-100'
68+
/>
69+
</div>
70+
)
71+
}
72+
73+
if (showSingleEnvironmentSelect && projectId) {
74+
return (
75+
<div className={className}>
76+
<EnvironmentSelectDropdown
77+
value={value}
78+
onChange={(value: string) => onChange?.(value)}
79+
projectId={projectId}
80+
dataTest={props['data-test']}
81+
/>
82+
</div>
83+
)
84+
}
85+
3686
const whitespaceCheck = checkWhitespaceIssues(value, operator)
3787
const hasWarning = !!whitespaceCheck
3888
const isLongText = String(value).length >= 10
@@ -52,70 +102,59 @@ const RuleConditionValueInput: React.FC<RuleConditionValueInputProps> = ({
52102

53103
return (
54104
<div className={`relative ${className}`}>
55-
{showEnvironmentDropdown && projectId ? (
56-
<EnvironmentSelectDropdown
57-
value={value}
58-
onChange={(value: string) => onChange?.(value)}
59-
projectId={projectId}
60-
dataTest={props['data-test']}
61-
/>
62-
) : (
63-
<>
64-
<Input
65-
type='text'
66-
data-test={props['data-test']}
67-
name='rule-condition-value-input'
68-
aria-label='Rule condition value input'
69-
value={value}
70-
className="w-100"
71-
inputClassName={
72-
showIcon ? `pr-5 ${hasWarning ? 'border-warning' : ''}` : ''
73-
}
74-
style={{ width: '100%' }}
75-
onChange={(e: InputEvent) => {
76-
const value = Utils.safeParseEventValue(e)
77-
onChange?.(value)
78-
}}
79-
/>
80-
{showIcon && (
81-
<div style={{ position: 'absolute', right: 5, top: 9 }}>
82-
<Tooltip
83-
title={
84-
<div
85-
className={`rule-value-icon flex ${
86-
isDarkMode ? 'bg-white' : 'bg-black'
87-
} bg-opacity-10 rounded-2 p-1 cursor-pointer`}
88-
onClick={() => {
89-
openModal2(
90-
'Edit Value',
91-
<TextAreaModal
92-
value={value}
93-
onChange={onChange}
94-
operator={operator}
95-
/>,
96-
)
97-
}}
98-
>
99-
<Icon
100-
name={hasWarning ? 'warning' : 'expand'}
101-
fill={
102-
hasWarning
103-
? undefined
104-
: `${isDarkMode ? '#fff' : '#1A2634'}`
105-
}
106-
width={18}
107-
height={18}
108-
/>
109-
</div>
110-
}
111-
place='top'
112-
effect='solid'
105+
<Input
106+
type='text'
107+
data-test={props['data-test']}
108+
name='rule-condition-value-input'
109+
aria-label='Rule condition value input'
110+
value={value}
111+
className='w-100'
112+
inputClassName={
113+
showIcon ? `pr-5 ${hasWarning ? 'border-warning' : ''}` : ''
114+
}
115+
style={{ width: '100%' }}
116+
onChange={(e: InputEvent) => {
117+
const value = Utils.safeParseEventValue(e)
118+
onChange?.(value)
119+
}}
120+
/>
121+
{showIcon && (
122+
<div style={{ position: 'absolute', right: 5, top: 9 }}>
123+
<Tooltip
124+
title={
125+
<div
126+
className={`rule-value-icon flex ${
127+
isDarkMode ? 'bg-white' : 'bg-black'
128+
} bg-opacity-10 rounded-2 p-1 cursor-pointer`}
129+
onClick={() => {
130+
openModal2(
131+
'Edit Value',
132+
<TextAreaModal
133+
value={value}
134+
onChange={onChange}
135+
operator={operator}
136+
/>,
137+
)
138+
}}
113139
>
114-
{validate()}
115-
</Tooltip>
116-
</div>
117-
)}
118-
</>
140+
<Icon
141+
name={hasWarning ? 'warning' : 'expand'}
142+
fill={
143+
hasWarning
144+
? undefined
145+
: `${isDarkMode ? '#fff' : '#1A2634'}`
146+
}
147+
width={18}
148+
height={18}
149+
/>
150+
</div>
151+
}
152+
place='top'
153+
effect='solid'
154+
>
155+
{validate()}
156+
</Tooltip>
157+
</div>
119158
)}
120159
</div>
121160
)

0 commit comments

Comments
 (0)