diff --git a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png index 30d6848c0dc..ec8494f1cb6 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png index 115f6d83d8d..5496b3c4488 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png and b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png index 4fcefc01064..5a042b0f3b1 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png and b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png index 2e07e4e3103..e10f4cc6a6a 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png and b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png index e83f20e9105..32591170a12 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png index d789a9fe7af..8e6538c1e24 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png index 045db880007..784e386f8b6 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png index e95c74d8a3f..9c8668050ec 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png differ diff --git a/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx b/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx index f246bfc1a98..54936f61f0b 100644 --- a/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx +++ b/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx @@ -8,6 +8,7 @@ import { MultivariateFlagVariant } from '~/types' import { experimentLogic } from '../experimentLogic' import { VariantTag } from './components' +import { VariantScreenshot } from './VariantScreenshot' export function DistributionTable(): JSX.Element { const { experimentId, experiment, experimentResults } = useValues(experimentLogic) @@ -32,6 +33,18 @@ export function DistributionTable(): JSX.Element { return
{`${item.rollout_percentage}%`}
}, }, + { + className: 'w-1/3', + key: 'variant_screenshot', + title: 'Screenshot', + render: function Key(_, item): JSX.Element { + return ( +
+ +
+ ) + }, + }, ] return ( diff --git a/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx b/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx new file mode 100644 index 00000000000..c80bcd574ed --- /dev/null +++ b/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx @@ -0,0 +1,114 @@ +import { IconUpload, IconX } from '@posthog/icons' +import { LemonButton, LemonDivider, LemonFileInput, LemonModal, LemonSkeleton, lemonToast } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { useUploadFiles } from 'lib/hooks/useUploadFiles' +import { useState } from 'react' + +import { experimentLogic } from '../experimentLogic' +import { VariantTag } from './components' + +export function VariantScreenshot({ + variantKey, + rolloutPercentage, +}: { + variantKey: string + rolloutPercentage: number +}): JSX.Element { + const { experiment } = useValues(experimentLogic) + const { updateExperimentVariantImages } = useActions(experimentLogic) + + const [mediaId, setMediaId] = useState(experiment.parameters?.variant_screenshot_media_ids?.[variantKey] || null) + const [isLoadingImage, setIsLoadingImage] = useState(true) + const [isModalOpen, setIsModalOpen] = useState(false) + + const { setFilesToUpload, filesToUpload, uploading } = useUploadFiles({ + onUpload: (_, __, id) => { + setMediaId(id) + if (id) { + const updatedVariantImages = { + ...experiment.parameters?.variant_screenshot_media_ids, + [variantKey]: id, + } + updateExperimentVariantImages(updatedVariantImages) + } + }, + onError: (detail) => { + lemonToast.error(`Error uploading image: ${detail}`) + }, + }) + + return ( +
+ {!mediaId ? ( + + + Upload a preview of this variant's UI + + } + /> + ) : ( +
+
+
setIsModalOpen(true)} className="cursor-zoom-in relative"> +
+ {isLoadingImage && } + setIsLoadingImage(false)} + onLoad={() => setIsLoadingImage(false)} + /> +
+
+ } + onClick={(e) => { + e.stopPropagation() + setMediaId(null) + const updatedVariantImages = { + ...experiment.parameters?.variant_screenshot_media_ids, + } + delete updatedVariantImages[variantKey] + updateExperimentVariantImages(updatedVariantImages) + }} + size="small" + tooltip="Remove" + tooltipPlacement="right" + noPadding + className="group-hover:flex hidden absolute right-0 top-0" + /> +
+
+
+
+ )} + setIsModalOpen(false)} + title={ +
+ Screenshot + + + {rolloutPercentage !== undefined && ( + ({rolloutPercentage}% rollout) + )} +
+ } + > + {`Screenshot: +
+
+ ) +} diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx index bdcd2b51d4f..f93ce8c45ab 100644 --- a/frontend/src/scenes/experiments/experimentLogic.tsx +++ b/frontend/src/scenes/experiments/experimentLogic.tsx @@ -169,6 +169,7 @@ export const experimentLogic = kea([ closeShipVariantModal: true, setCurrentFormStep: (stepIndex: number) => ({ stepIndex }), moveToNextFormStep: true, + updateExperimentVariantImages: (variantPreviewMediaIds: Record) => ({ variantPreviewMediaIds }), }), reducers({ experiment: [ @@ -587,15 +588,12 @@ export const experimentLogic = kea([ }, updateExperimentSuccess: async ({ experiment }) => { actions.updateExperiments(experiment) - if (values.changingGoalMetric) { actions.loadExperimentResults() } - if (values.changingSecondaryMetrics) { actions.loadSecondaryMetricResults() } - if (values.experiment?.start_date) { actions.loadExperimentResults() } @@ -717,6 +715,22 @@ export const experimentLogic = kea([ lemonToast.error(error) actions.closeShipVariantModal() }, + updateExperimentVariantImages: async ({ variantPreviewMediaIds }) => { + try { + const updatedParameters = { + ...values.experiment.parameters, + variant_screenshot_media_ids: variantPreviewMediaIds, + } + await api.update(`api/projects/${values.currentTeamId}/experiments/${values.experimentId}`, { + parameters: updatedParameters, + }) + actions.setExperiment({ + parameters: updatedParameters, + }) + } catch (error) { + lemonToast.error('Failed to update experiment variant images') + } + }, })), loaders(({ actions, props, values }) => ({ experiment: { diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 233cf88a25c..7357eb4f939 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -3219,6 +3219,7 @@ export interface Experiment { feature_flag_variants: MultivariateFlagVariant[] custom_exposure_filter?: FilterType aggregation_group_type_index?: integer + variant_screenshot_media_ids?: Record } start_date?: string | null end_date?: string | null