From 158dae288f8c09d3ce19763d1c6d7a788aabec0f Mon Sep 17 00:00:00 2001 From: Juraj Majerik Date: Thu, 7 Dec 2023 11:55:09 +0100 Subject: [PATCH] feat(schedule feature flags): add plain UI (#19071) --- frontend/src/lib/constants.tsx | 1 + .../src/scenes/feature-flags/FeatureFlag.tsx | 9 ++ .../FeatureFlagReleaseConditions.tsx | 8 +- .../feature-flags/FeatureFlagSchedule.tsx | 86 +++++++++++++++++++ .../scenes/feature-flags/featureFlagLogic.ts | 26 +++++- .../scenes/feature-flags/featureFlagsLogic.ts | 1 + 6 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index 081291b831502..6d5e3767c2f0a 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -185,6 +185,7 @@ export const FEATURE_FLAGS = { NETWORK_PAYLOAD_CAPTURE: 'network-payload-capture', // owner: #team-replay FEATURE_FLAG_COHORT_CREATION: 'feature-flag-cohort-creation', // owner: @neilkakkar #team-feature-success INSIGHT_HORIZONTAL_CONTROLS: 'insight-horizontal-controls', // owner: @benjackwhite + SCHEDULED_CHANGES_FEATURE_FLAGS: 'scheduled-changes-feature-flags', // owner: @jurajmajerik #team-feature-success ALWAYS_SHOW_SEEKBAR_PREVIEW: 'always-show-seekbar-preview', // owner: #team-replay SESSION_REPLAY_MOBILE: 'session-replay-mobile', // owner: #team-replay SESSION_REPLAY_IOS: 'session-replay-ios', // owner: #team-replay diff --git a/frontend/src/scenes/feature-flags/FeatureFlag.tsx b/frontend/src/scenes/feature-flags/FeatureFlag.tsx index 26a0872dbc8f7..fc133f316f7ad 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlag.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlag.tsx @@ -68,6 +68,7 @@ import { featureFlagLogic } from './featureFlagLogic' import { featureFlagPermissionsLogic } from './featureFlagPermissionsLogic' import FeatureFlagProjects from './FeatureFlagProjects' import { FeatureFlagReleaseConditions } from './FeatureFlagReleaseConditions' +import FeatureFlagSchedule from './FeatureFlagSchedule' import { featureFlagsLogic, FeatureFlagsTab } from './featureFlagsLogic' import { RecentFeatureFlagInsights } from './RecentFeatureFlagInsightsCard' @@ -221,6 +222,14 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element { }) } + if (featureFlags[FEATURE_FLAGS.SCHEDULED_CHANGES_FEATURE_FLAGS]) { + tabs.push({ + label: 'Schedule', + key: FeatureFlagsTab.SCHEDULE, + content: , + }) + } + return ( <>
diff --git a/frontend/src/scenes/feature-flags/FeatureFlagReleaseConditions.tsx b/frontend/src/scenes/feature-flags/FeatureFlagReleaseConditions.tsx index d45d04842e5f6..fdad43811dc81 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlagReleaseConditions.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlagReleaseConditions.tsx @@ -29,13 +29,17 @@ interface FeatureFlagReadOnlyProps { readOnly?: boolean isSuper?: boolean excludeTitle?: boolean + usageContext?: string } export function FeatureFlagReleaseConditions({ readOnly, isSuper, excludeTitle, + usageContext, }: FeatureFlagReadOnlyProps): JSX.Element { + const logic = usageContext === 'schedule' ? featureFlagLogic({ id: 'schedule' }) : featureFlagLogic + const { showGroupsOptions, aggregationLabel } = useValues(groupsModel) const { aggregationTargetName, @@ -47,14 +51,14 @@ export function FeatureFlagReleaseConditions({ computeBlastRadiusPercentage, affectedUsers, totalUsers, - } = useValues(featureFlagLogic) + } = useValues(logic) const { setAggregationGroupTypeIndex, updateConditionSet, duplicateConditionSet, removeConditionSet, addConditionSet, - } = useActions(featureFlagLogic) + } = useActions(logic) const { cohortsById } = useValues(cohortsModel) const filterGroups: FeatureFlagGroupType[] = isSuper diff --git a/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx b/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx new file mode 100644 index 0000000000000..9b50a40a96ec6 --- /dev/null +++ b/frontend/src/scenes/feature-flags/FeatureFlagSchedule.tsx @@ -0,0 +1,86 @@ +import { LemonButton, LemonCheckbox, LemonDivider, LemonSelect, LemonTable } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { DatePicker } from 'lib/components/DatePicker' +import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' +import { useEffect } from 'react' + +import { featureFlagLogic } from './featureFlagLogic' +import { FeatureFlagReleaseConditions } from './FeatureFlagReleaseConditions' + +const logic = featureFlagLogic({ id: 'schedule' }) +export const DAYJS_FORMAT = 'MMMM DD, YYYY h:mm A' + +const columns = [createdByColumn() as any, createdAtColumn() as any] + +export default function FeatureFlagSchedule(): JSX.Element { + const { featureFlag, scheduledChanges, scheduleChangeType, scheduleDateMarker } = useValues(logic) + const { setFeatureFlag, loadScheduledChanges, setScheduleDateMarker, setScheduleChangeType } = useActions(logic) + + useEffect(() => { + loadScheduledChanges() + }, []) + + return ( +
+

Add a scheduled change

+
Automatically change flag properties at a future point in time.
+
+
+
Change type
+ setScheduleChangeType(value)} + options={[ + { label: 'Add a condition', value: 'add_condition' }, + { label: 'Change status', value: 'change_status' }, + ]} + /> +
+
+
Date and time
+ setScheduleDateMarker(value)} + className="h-10 w-60" + allowClear={false} + showTime + showSecond={false} + format={DAYJS_FORMAT} + /> +
+
+ +
+ {scheduleChangeType === 'add_condition' && } + {scheduleChangeType === 'change_status' && ( + <> +
+ { + featureFlag.active = value + setFeatureFlag(featureFlag) + }} + checked={featureFlag.active} + /> +
+ + )} +
+ Schedule +
+ +
+ +
+ ) +} diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.ts index c7542d156b1bd..0ab2881a5f01a 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.ts @@ -114,7 +114,7 @@ export function validateFeatureFlagKey(key: string): string | undefined { } export interface FeatureFlagLogicProps { - id: number | 'new' | 'link' + id: number | 'new' | 'link' | 'schedule' } // KLUDGE: Payloads are returned in a : mapping. @@ -230,6 +230,8 @@ export const featureFlagLogic = kea([ generateUsageDashboard: true, enrichUsageDashboard: true, setCopyDestinationProject: (id: number | null) => ({ id }), + setScheduleDateMarker: (dateMarker: any) => ({ dateMarker }), + setScheduleChangeType: (changeType: string | null) => ({ changeType }), }), forms(({ actions, values }) => ({ featureFlag: { @@ -482,11 +484,23 @@ export const featureFlagLogic = kea([ setCopyDestinationProject: (_, { id }) => id, }, ], + scheduleDateMarker: [ + null as any, + { + setScheduleDateMarker: (_, { dateMarker }) => dateMarker, + }, + ], + scheduleChangeType: [ + 'add_condition' as string | null, + { + setScheduleChangeType: (_, { changeType }) => changeType, + }, + ], }), loaders(({ values, props, actions }) => ({ featureFlag: { loadFeatureFlag: async () => { - if (props.id && props.id !== 'new' && props.id !== 'link') { + if (props.id && props.id !== 'new' && props.id !== 'link' && props.id !== 'schedule') { try { const retrievedFlag: FeatureFlagType = await api.featureFlags.get(props.id) return variantKeyToIndexFeatureFlagPayloads(retrievedFlag) @@ -587,7 +601,7 @@ export const featureFlagLogic = kea([ null as CohortType | null, { createStaticCohort: async () => { - if (props.id && props.id !== 'new' && props.id !== 'link') { + if (props.id && props.id !== 'new' && props.id !== 'link' && props.id !== 'schedule') { return (await api.featureFlags.createStaticCohort(props.id)).cohort } return null @@ -627,6 +641,12 @@ export const featureFlagLogic = kea([ } }, }, + scheduledChanges: { + __default: [] as any, + loadScheduledChanges: async () => { + return [] + }, + }, })), listeners(({ actions, values, props }) => ({ submitNewDashboardSuccessWithResult: async ({ result }) => { diff --git a/frontend/src/scenes/feature-flags/featureFlagsLogic.ts b/frontend/src/scenes/feature-flags/featureFlagsLogic.ts index e196a65ac98b5..aa5df8a41afc5 100644 --- a/frontend/src/scenes/feature-flags/featureFlagsLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagsLogic.ts @@ -20,6 +20,7 @@ export enum FeatureFlagsTab { USAGE = 'usage', PERMISSIONS = 'permissions', PROJECTS = 'projects', + SCHEDULE = 'schedule', } export interface FeatureFlagsFilters {