fix(ui): re-add flag read view

This commit is contained in:
nick-delirium 2023-06-26 16:47:33 +02:00
parent 282aa19847
commit c9c5e68283
7 changed files with 197 additions and 119 deletions

View file

@ -5,6 +5,7 @@ import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
import { resentOrDate } from 'App/date';
import { toast } from 'react-toastify';
import { fflagRead } from "App/routes";
function FFlagItem({ flag }: { flag: FeatureFlag }) {
const { featureFlagsStore, userStore } = useStore();
@ -32,7 +33,7 @@ function FFlagItem({ flag }: { flag: FeatureFlag }) {
return (
<div className={'w-full py-2 px-6 border-b hover:bg-active-blue'}>
<div className={'flex items-center'}>
<Link style={{ flex: 1 }} to={`feature-flags/${flag.featureFlagId}`}>
<Link style={{ flex: 1 }} to={fflagRead(flag.featureFlagId.toString())}>
<div className={'flex items-center gap-2'}>
<Tooltip delay={150} title={flag.isSingleOption ? 'Single variant' : 'Multivariant'}>
<Icon name={flagIcon} size={32} />

View file

@ -100,10 +100,12 @@ function FlagView({ siteId, fflagId }: { siteId: string; fflagId: string }) {
<React.Fragment key={index}>
<RolloutCondition
set={index + 1}
readonly
index={index}
conditions={condition}
removeCondition={current.removeCondition}
/>
<div className={'mt-2'} />
</React.Fragment>
))}
</div>

View file

@ -12,9 +12,10 @@ interface Props {
conditions: Conditions;
removeCondition: (ind: number) => void;
index: number
readonly?: boolean;
}
function RolloutCondition({ set, conditions, removeCondition, index }: Props) {
function RolloutCondition({ set, conditions, removeCondition, index, readonly }: Props) {
const [forceRender, forceRerender] = React.useState(false);
const onAddFilter = (filter = {}) => {
conditions.filter.addFilter(filter);
@ -47,17 +48,16 @@ function RolloutCondition({ set, conditions, removeCondition, index }: Props) {
<div className={'flex items-center border-b px-4 py-2 gap-2'}>
<div>Condition</div>
<div className={'p-2 rounded bg-gray-lightest'}>Set {set}</div>
<div
className={cn(
'p-2 px-4 cursor-pointer rounded ml-auto',
'hover:bg-teal-light'
)}
onClick={() => removeCondition(index)}
>
<Icon name={'trash'} color={'main'} />
</div>
{readonly ? null : (
<div
className={cn('p-2 px-4 cursor-pointer rounded ml-auto', 'hover:bg-teal-light')}
onClick={() => removeCondition(index)}
>
<Icon name={'trash'} color={'main'} />
</div>
)}
</div>
<div className={'p-2 border-b'}>
<div className={readonly ? 'p-2' : 'p-2 border-b'}>
<div className={conditions.filter.filters.length > 0 ? 'p-2 border-b mb-2' : ''}>
<FilterList
filter={conditions.filter}
@ -66,25 +66,38 @@ function RolloutCondition({ set, conditions, removeCondition, index }: Props) {
onChangeEventsOrder={onChangeEventsOrder}
hideEventsOrder
excludeFilterKeys={nonFlagFilters}
readonly
/>
{readonly && !conditions.filter?.filters?.length ? (
<div className={'p-2'}>No conditions</div>
) : null}
</div>
<FilterSelection
filter={undefined}
onFilterClick={onAddFilter}
excludeFilterKeys={nonFlagFilters}
>
<Button variant="text-primary" icon="plus">Add Condition</Button>
</FilterSelection>
{readonly ? null : (
<FilterSelection
filter={undefined}
onFilterClick={onAddFilter}
excludeFilterKeys={nonFlagFilters}
>
<Button variant="text-primary" icon="plus">
Add Condition
</Button>
</FilterSelection>
)}
</div>
<div className={'px-4 py-2 flex items-center gap-2'}>
<span>Rollout to</span>
<Input
type="text"
width={60}
value={conditions.rolloutPercentage}
onChange={onPercentChange}
leadingButton={<div className={'p-2 text-disabled-text'}>%</div>}
/>
{readonly ? (
<div>{conditions.rolloutPercentage}%</div>
) : (
<Input
type="text"
width={60}
value={conditions.rolloutPercentage}
onChange={onPercentChange}
leadingButton={<div className={'p-2 text-disabled-text'}>%</div>}
/>
)}
<span>of sessions</span>
</div>
</div>

View file

@ -2,112 +2,159 @@ import React from 'react';
import FilterOperator from '../FilterOperator';
import FilterSelection from '../FilterSelection';
import FilterValue from '../FilterValue';
import { Button } from 'UI';
import {Button} from 'UI';
import FilterSource from '../FilterSource';
import { FilterKey, FilterType } from 'App/types/filter/filterType';
import {FilterKey, FilterType} from 'App/types/filter/filterType';
import SubFilterItem from '../SubFilterItem';
import {toJS} from "mobx";
interface Props {
filterIndex: number;
filter: any; // event/filter
onUpdate: (filter: any) => void;
onRemoveFilter: () => void;
isFilter?: boolean;
saveRequestPayloads?: boolean;
disableDelete?: boolean;
excludeFilterKeys?: Array<string>;
filterIndex: number;
filter: any; // event/filter
onUpdate: (filter: any) => void;
onRemoveFilter: () => void;
isFilter?: boolean;
saveRequestPayloads?: boolean;
disableDelete?: boolean;
excludeFilterKeys?: Array<string>;
readonly?: boolean;
}
function FilterItem(props: Props) {
const { isFilter = false, filterIndex, filter, saveRequestPayloads, disableDelete = false, excludeFilterKeys = [] } = props;
const canShowValues = !(filter.operator === 'isAny' || filter.operator === 'onAny' || filter.operator === 'isUndefined');
const isSubFilter = filter.type === FilterType.SUB_FILTERS;
const replaceFilter = (filter: any) => {
props.onUpdate({
...filter,
value: [''],
filters: filter.filters ? filter.filters.map((i: any) => ({ ...i, value: [''] })) : [],
});
};
const {
isFilter = false,
filterIndex,
filter,
saveRequestPayloads,
disableDelete = false,
excludeFilterKeys = []
} = props;
const canShowValues = !(filter.operator === 'isAny' || filter.operator === 'onAny' || filter.operator === 'isUndefined');
const isSubFilter = filter.type === FilterType.SUB_FILTERS;
const replaceFilter = (filter: any) => {
props.onUpdate({
...filter,
value: [''],
filters: filter.filters ? filter.filters.map((i: any) => ({...i, value: ['']})) : [],
});
};
const onOperatorChange = (e: any, { value }: any) => {
props.onUpdate({ ...filter, operator: value });
};
const onOperatorChange = (e: any, {value}: any) => {
props.onUpdate({...filter, operator: value});
};
const onSourceOperatorChange = (e: any, { value }: any) => {
props.onUpdate({ ...filter, sourceOperator: value });
};
const onSourceOperatorChange = (e: any, {value}: any) => {
props.onUpdate({...filter, sourceOperator: value});
};
const onUpdateSubFilter = (subFilter: any, subFilterIndex: any) => {
props.onUpdate({
...filter,
filters: filter.filters.map((i: any, index: any) => {
if (index === subFilterIndex) {
return subFilter;
}
return i;
}),
});
};
const onUpdateSubFilter = (subFilter: any, subFilterIndex: any) => {
props.onUpdate({
...filter,
filters: filter.filters.map((i: any, index: any) => {
if (index === subFilterIndex) {
return subFilter;
}
return i;
}),
});
};
return (
<div className="flex items-center hover:bg-active-blue -mx-5 px-5">
<div className="flex items-start w-full">
{!isFilter && (
<div className="mt-1 flex-shrink-0 border w-6 h-6 text-xs flex items-center justify-center rounded-full bg-gray-light-shade mr-2">
<span>{filterIndex + 1}</span>
</div>
console.log('FilterItem', toJS(filter))
return (
<div className="flex items-center hover:bg-active-blue -mx-5 px-5">
<div className="flex items-start w-full">
{!isFilter && (
<div
className="mt-1 flex-shrink-0 border w-6 h-6 text-xs flex items-center justify-center rounded-full bg-gray-light-shade mr-2">
<span>{filterIndex + 1}</span>
</div>
)}
<FilterSelection
filter={filter}
onFilterClick={replaceFilter}
excludeFilterKeys={excludeFilterKeys}
disabled={disableDelete || props.readonly}
/>
{/* Filter with Source */}
{filter.hasSource && (
<>
<FilterOperator
options={filter.sourceOperatorOptions}
onChange={onSourceOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.sourceOperator}
isDisabled={filter.operatorDisabled || props.readonly}
/>
<FilterSource filter={filter} onUpdate={props.onUpdate}/>
</>
)}
{/* Filter values */}
{!isSubFilter && filter.operatorOptions && (
<>
<FilterOperator
options={filter.operatorOptions}
onChange={onOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.operator}
isDisabled={filter.operatorDisabled || props.readonly}
/>
{canShowValues && (
<>
{props.readonly ? (
<div
className={'px-2 py-1 bg-gray-lightest'}
>
{filter.value.map((val: string) => {
return filter.options && filter.options.length
? filter.options[filter.options.findIndex((i: any) => i.value === val)]?.label
: val
})}
</div>
) : (
<FilterValue filter={filter} onUpdate={props.onUpdate}/>
)}
<FilterSelection filter={filter} onFilterClick={replaceFilter} excludeFilterKeys={excludeFilterKeys} disabled={disableDelete} />
</>
)}
</>
)}
{/* Filter with Source */}
{filter.hasSource && (
<>
<FilterOperator
options={filter.sourceOperatorOptions}
onChange={onSourceOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.sourceOperator}
isDisabled={filter.operatorDisabled}
/>
<FilterSource filter={filter} onUpdate={props.onUpdate} />
</>
)}
{/* Filter values */}
{!isSubFilter && filter.operatorOptions && (
<>
<FilterOperator
options={filter.operatorOptions}
onChange={onOperatorChange}
className="mx-2 flex-shrink-0"
value={filter.operator}
isDisabled={filter.operatorDisabled}
/>
{canShowValues && <FilterValue filter={filter} onUpdate={props.onUpdate} />}
</>
)}
{/* filters */}
{isSubFilter && (
<div className="grid grid-col ml-3 w-full">
{filter.filters
.filter((i: any) => (i.key !== FilterKey.FETCH_REQUEST_BODY && i.key !== FilterKey.FETCH_RESPONSE_BODY) || saveRequestPayloads)
.map((subFilter: any, subFilterIndex: any) => (
<SubFilterItem
filterIndex={subFilterIndex}
filter={subFilter}
onUpdate={(f) => onUpdateSubFilter(f, subFilterIndex)}
onRemoveFilter={props.onRemoveFilter}
/>
))}
</div>
)}
</div>
<div className="flex flex-shrink-0 self-start ml-auto">
<Button disabled={disableDelete} variant="text" icon="trash" onClick={props.onRemoveFilter} size="small" iconSize={14} />
</div>
{/* filters */}
{isSubFilter && (
<div className="grid grid-col ml-3 w-full">
{filter.filters
.filter(
(i: any) =>
(i.key !== FilterKey.FETCH_REQUEST_BODY &&
i.key !== FilterKey.FETCH_RESPONSE_BODY) ||
saveRequestPayloads
)
.map((subFilter: any, subFilterIndex: any) => (
<SubFilterItem
filterIndex={subFilterIndex}
filter={subFilter}
onUpdate={(f) => onUpdateSubFilter(f, subFilterIndex)}
onRemoveFilter={props.onRemoveFilter}
/>
))}
</div>
)}
</div>
{props.readonly ? null :
<div className="flex flex-shrink-0 self-start ml-auto">
<Button
disabled={disableDelete}
variant="text"
icon="trash"
onClick={props.onRemoveFilter}
size="small"
iconSize={14}
/>
</div>
);
}
</div>
);
}
export default FilterItem;

View file

@ -13,6 +13,7 @@ interface Props {
observeChanges?: () => void;
saveRequestPayloads?: boolean;
supportsEmpty?: boolean
readonly?: boolean;
excludeFilterKeys?: Array<string>
}
function FilterList(props: Props) {
@ -84,6 +85,7 @@ function FilterList(props: Props) {
saveRequestPayloads={saveRequestPayloads}
disableDelete={cannotDeleteFilter}
excludeFilterKeys={excludeFilterKeys}
readonly={props.readonly}
/>
) : null
)}
@ -99,6 +101,7 @@ function FilterList(props: Props) {
!filter.isEvent ? (
<FilterItem
key={filterIndex}
readonly={props.readonly}
isFilter={true}
filterIndex={filterIndex}
filter={filter}

View file

@ -202,6 +202,14 @@ export default class API {
return this.featureFlags.reloadFlags()
}
getFeatureFlag(flagName: string): IFeatureFlag | undefined {
return this.featureFlags.getFeatureFlag(flagName)
}
getAllFeatureFlags() {
return this.featureFlags.flags
}
use<T>(fn: (app: App | null, options?: Options) => T): T {
return fn(this.app, this.options)
}

View file

@ -25,6 +25,10 @@ export default class FeatureFlags {
}
}
getFeatureFlag(flagName: string): IFeatureFlag | undefined {
return this.flags.find((flag) => flag.key === flagName)
}
isFlagEnabled(flagName: string): boolean {
return this.flags.findIndex((flag) => flag.key === flagName) !== -1
}