From 2980886b3174d87a3b7490af92447074c3e48fee Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Fri, 23 Jun 2023 11:57:14 +0200 Subject: [PATCH] fix(ui): some ui fixes for feature flags --- .../components/FFlags/FFlagItem/FFlagItem.tsx | 10 +-- frontend/app/components/FFlags/FFlagsList.tsx | 14 ++-- .../app/components/FFlags/NewFFlag/Header.tsx | 2 +- .../FFlags/NewFFlag/Multivariant.tsx | 17 +++-- .../components/FFlags/NewFFlag/NewFFlag.tsx | 71 ++++++++++--------- frontend/app/mstore/featureFlagsStore.ts | 4 ++ frontend/app/mstore/types/FeatureFlag.ts | 13 ++++ 7 files changed, 77 insertions(+), 54 deletions(-) diff --git a/frontend/app/components/FFlags/FFlagItem/FFlagItem.tsx b/frontend/app/components/FFlags/FFlagItem/FFlagItem.tsx index d94d9dd7c..29272f3ba 100644 --- a/frontend/app/components/FFlags/FFlagItem/FFlagItem.tsx +++ b/frontend/app/components/FFlags/FFlagItem/FFlagItem.tsx @@ -13,11 +13,11 @@ function FFlagItem({ flag }: { flag: FeatureFlag }) { const newValue = !flag.isActive flag.setIsEnabled(newValue); featureFlagsStore.updateFlagStatus(flag.featureFlagId, newValue).then(() => { - toast.success('Feature flag updated.'); + toast.success('Feature flag status has been updated.'); }) .catch(() => { flag.setIsEnabled(!newValue); - toast.error('Failed to update flag.') + toast.error('Something went wrong, please try again.') }) } @@ -25,16 +25,16 @@ function FFlagItem({ flag }: { flag: FeatureFlag }) { const flagOwner = flag.updatedBy || flag.createdBy const user = userStore.list.length > 0 ? userStore.list.find(u => parseInt(u.userId) === flagOwner!)?.name : flagOwner; return ( -
+
-
+
{flag.flagKey} {flag.description ? ( - + ) : null}
diff --git a/frontend/app/components/FFlags/FFlagsList.tsx b/frontend/app/components/FFlags/FFlagsList.tsx index 6508db776..2ef815b46 100644 --- a/frontend/app/components/FFlags/FFlagsList.tsx +++ b/frontend/app/components/FFlags/FFlagsList.tsx @@ -1,7 +1,7 @@ import React from 'react'; import FFlagsListHeader from 'Components/FFlags/FFlagsListHeader'; import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG'; -import {Loader, NoContent, Pagination} from 'UI'; +import { Loader, NoContent, Pagination } from 'UI'; import FFlagItem from './FFlagItem'; import { useStore } from 'App/mstore'; import { observer } from 'mobx-react-lite'; @@ -29,14 +29,18 @@ function FFlagsList({ siteId }: { siteId: string }) {
- You haven't created any feature flags yet. + {featureFlagsStore.sort.query === '' + ? "You haven't created any feature flags yet." + : 'No matching results'}
} subtext={ -
- Use feature flags to deploy and rollback new functionality with ease. -
+ featureFlagsStore.sort.query === '' ? ( +
+ Use feature flags to deploy and rollback new functionality with ease. +
+ ) : null } >
diff --git a/frontend/app/components/FFlags/NewFFlag/Header.tsx b/frontend/app/components/FFlags/NewFFlag/Header.tsx index 0758aaac8..b4e76dce9 100644 --- a/frontend/app/components/FFlags/NewFFlag/Header.tsx +++ b/frontend/app/components/FFlags/NewFFlag/Header.tsx @@ -7,7 +7,7 @@ function Header({ current, onCancel, onSave, isNew }: any) { return ( <>
-

{isNew ? 'New Feature Flag' : current.flagKey}

+

{!current.flagKey ? 'New Feature Flag' : current.flagKey}

diff --git a/frontend/app/components/FFlags/NewFFlag/Multivariant.tsx b/frontend/app/components/FFlags/NewFFlag/Multivariant.tsx index 9806fb016..8e3f45173 100644 --- a/frontend/app/components/FFlags/NewFFlag/Multivariant.tsx +++ b/frontend/app/components/FFlags/NewFFlag/Multivariant.tsx @@ -57,13 +57,12 @@ function Multivariant() {
- +
@@ -77,7 +76,7 @@ function Multivariant() {
) => variant.setKey(e.target.value) @@ -86,7 +85,7 @@ function Multivariant() {
) => variant.setDescription(e.target.value) @@ -95,7 +94,7 @@ function Multivariant() {
) => variant.setPayload(e.target.value) @@ -136,12 +135,12 @@ function Multivariant() { ); })}
-
+
{featureFlagsStore.currentFflag!.isRedDistribution ? ( -
Total distribution is less than 100%
+
Total distribution is less than 100%.
) : null}
diff --git a/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx b/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx index 90c6d4063..35e296dd2 100644 --- a/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx +++ b/frontend/app/components/FFlags/NewFFlag/NewFFlag.tsx @@ -5,7 +5,7 @@ import { Input, SegmentSelection, Toggler, Loader, Button, NoContent } from 'UI' import Breadcrumb from 'Shared/Breadcrumb'; import { useModal } from 'App/components/Modal'; import HowTo from 'Components/FFlags/NewFFlag/HowTo'; -import { useHistory } from 'react-router'; +import {Prompt, useHistory} from 'react-router'; import { withSiteId, fflags } from 'App/routes'; import Description from './Description'; import Header from './Header'; @@ -76,6 +76,12 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { const showDescription = Boolean(current.description?.length); return (
+ { + return 'You have unsaved changes. Are you sure you want to leave?'; + }} + />
-
+
@@ -134,20 +135,22 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) { { name: 'Single Variant (Boolean)', value: 'single' }, { name: 'Multi-Variant (String)', value: 'multi' }, ]} - />
{current.isSingleOption ? ( <> -
- Users will be served - true if they match - one or more rollout conditions. -
-
- - -
+
+ Users will be served + true if they match + one or more rollout conditions. +
+
+ + +
) : ( @@ -183,19 +186,17 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) {
- {current.conditions.length === 0 ? null - : ( -
- Indicate the users for whom you intend to make this flag available. Keep in mind that - each set of conditions will be deployed separately from one another. -
- ) - } + {current.conditions.length === 0 ? null : ( +
+ Indicate the users for whom you intend to make this flag available. Keep in mind that + each set of conditions will be deployed separately from one another. +
+ )} +
Indicate the users for whom you intend to make this flag available.
@@ -217,14 +218,16 @@ function NewFFlag({ siteId, fflagId }: { siteId: string; fflagId?: string }) {
OR
))} -
current!.addCondition()} - className={ - 'flex items-center justify-center w-full bg-white rounded border mt-2 p-2' - } - > - -
+ {current.conditions.length <= 10 ? ( +
current!.addCondition()} + className={ + 'flex items-center justify-center w-full bg-white rounded border mt-2 p-2' + } + > + +
+ ) : null}
diff --git a/frontend/app/mstore/featureFlagsStore.ts b/frontend/app/mstore/featureFlagsStore.ts index b67bc51d0..120cb0e6a 100644 --- a/frontend/app/mstore/featureFlagsStore.ts +++ b/frontend/app/mstore/featureFlagsStore.ts @@ -26,6 +26,8 @@ export default class FeatureFlagsStore { this.client = customClient ?? fflagsService } + + setFlagsSearch = (search: string) => { this.flagsSearch = search; }; @@ -122,6 +124,7 @@ export default class FeatureFlagsStore { try { // @ts-ignore const result = await this.client.createFlag(this.currentFflag.toJS()); + this.currentFflag.setHasChanged(false) this.addFlag(new FeatureFlag(result)); } catch (e) { console.error(e); @@ -150,6 +153,7 @@ export default class FeatureFlagsStore { // @ts-ignore const result = await this.client.updateFlag(usedFlag.toJS()); if (!flag) this.setCurrentFlag(new FeatureFlag(result)); + if (!flag) this.currentFflag?.setHasChanged(false) } catch (e) { console.error('getting api error', e); throw e.response; diff --git a/frontend/app/mstore/types/FeatureFlag.ts b/frontend/app/mstore/types/FeatureFlag.ts index 0580031d4..cbd8a5b22 100644 --- a/frontend/app/mstore/types/FeatureFlag.ts +++ b/frontend/app/mstore/types/FeatureFlag.ts @@ -92,6 +92,11 @@ export default class FeatureFlag { payload: SingleFFlag['payload'] flagType: string; variants: Variant[] = []; + hasChanged = false + + setHasChanged = (hasChanged: boolean) => { + this.hasChanged = hasChanged + } constructor(data?: SingleFFlag) { Object.assign( @@ -109,11 +114,13 @@ export default class FeatureFlag { setPayload = (payload: string) => { this.payload = payload; + this.setHasChanged(true) } addVariant = () => { this.variants.push(new Variant(this.variants.length + 1)) this.redistributeVariants() + this.setHasChanged(true) } removeVariant = (index: number) => { @@ -151,6 +158,7 @@ export default class FeatureFlag { addCondition = () => { this.conditions.push(new Conditions()) + this.setHasChanged(true) } removeCondition = (index: number) => { @@ -159,21 +167,26 @@ export default class FeatureFlag { setFlagKey = (flagKey: string) => { this.flagKey = flagKey; + this.setHasChanged(true) } setDescription = (description: string) => { this.description = description; + this.setHasChanged(true) } setIsPersist = (isPersist: boolean) => { this.isPersist = isPersist; + this.setHasChanged(true) } setIsSingleOption = (isSingleOption: boolean) => { this.isSingleOption = isSingleOption; + this.setHasChanged(true) } setIsEnabled = (isEnabled: boolean) => { this.isActive = isEnabled; + this.setHasChanged(true) } } \ No newline at end of file