diff --git a/frontend/src/lib/components/InternalSurvey/InternalMultipleChoiceSurvey.tsx b/frontend/src/lib/components/InternalSurvey/InternalMultipleChoiceSurvey.tsx
new file mode 100644
index 0000000000000..dee3b2f30cda8
--- /dev/null
+++ b/frontend/src/lib/components/InternalSurvey/InternalMultipleChoiceSurvey.tsx
@@ -0,0 +1,80 @@
+/**
+ * @fileoverview A component that displays an interactive survey within a session recording. It handles survey display, user responses, and submission
+ */
+import { LemonButton, LemonCheckbox, LemonTextArea } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+
+import { SurveyQuestion, SurveyQuestionType } from '~/types'
+
+import { internalMultipleChoiceSurveyLogic } from './internalMultipleChoiceSurveyLogic'
+
+interface InternalSurveyProps {
+ surveyId: string
+}
+
+export function InternalMultipleChoiceSurvey({ surveyId }: InternalSurveyProps): JSX.Element {
+ const logic = internalMultipleChoiceSurveyLogic({ surveyId })
+ const { survey, surveyResponse, showThankYouMessage, thankYouMessage, openChoice } = useValues(logic)
+ const { handleChoiceChange, handleSurveyResponse, setOpenChoice } = useActions(logic)
+
+ if (!survey) {
+ return <>>
+ }
+
+ return (
+
+
+ {survey.questions.map((question: SurveyQuestion) => (
+
+ {showThankYouMessage && thankYouMessage}
+ {!showThankYouMessage && (
+ <>
+
{question.question}
+ {question.type === SurveyQuestionType.MultipleChoice && (
+
+ {question.choices.map((choice, index) => {
+ // Add an open choice text area if the last choice is an open choice
+ if (index === question.choices.length - 1 && question.hasOpenChoice) {
+ return (
+
+ {choice}
+
+
+ )
+ }
+ return (
+ -
+ handleChoiceChange(choice, checked)}
+ label={choice}
+ className="font-normal"
+ />
+
+ )
+ })}
+
+ )}
+
+ {question.buttonText ?? 'Submit'}
+
+ >
+ )}
+
+ ))}
+
+
+ )
+}
diff --git a/frontend/src/lib/components/InternalSurvey/internalMultipleChoiceSurveyLogic.ts b/frontend/src/lib/components/InternalSurvey/internalMultipleChoiceSurveyLogic.ts
new file mode 100644
index 0000000000000..9c756539ebb78
--- /dev/null
+++ b/frontend/src/lib/components/InternalSurvey/internalMultipleChoiceSurveyLogic.ts
@@ -0,0 +1,95 @@
+import { actions, afterMount, kea, key, listeners, path, props, reducers } from 'kea'
+import posthog from 'posthog-js'
+
+import { Survey } from '~/types'
+
+import type { internalMultipleChoiceSurveyLogicType } from './internalMultipleChoiceSurveyLogicType'
+
+export interface InternalSurveyLogicProps {
+ surveyId: string
+}
+
+export const internalMultipleChoiceSurveyLogic = kea([
+ path(['lib', 'components', 'InternalSurvey', 'internalMultipleChoiceSurveyLogicType']),
+ props({} as InternalSurveyLogicProps),
+ key((props) => props.surveyId),
+ actions({
+ getSurveys: () => ({}),
+ setSurvey: (survey: Survey) => ({ survey }),
+ handleSurveys: (surveys: Survey[]) => ({ surveys }),
+ handleSurveyResponse: () => ({}),
+ handleChoiceChange: (choice: string, isAdded: boolean) => ({ choice, isAdded }),
+ setShowThankYouMessage: (showThankYouMessage: boolean) => ({ showThankYouMessage }),
+ setThankYouMessage: (thankYouMessage: string) => ({ thankYouMessage }),
+ setOpenChoice: (openChoice: string) => ({ openChoice }),
+ }),
+ reducers({
+ survey: [
+ null as Survey | null,
+ {
+ setSurvey: (_, { survey }) => survey,
+ },
+ ],
+ thankYouMessage: [
+ 'Thank you for your feedback!',
+ {
+ setThankYouMessage: (_, { thankYouMessage }) => thankYouMessage,
+ },
+ ],
+ showThankYouMessage: [
+ false as boolean,
+ {
+ setShowThankYouMessage: (_, { showThankYouMessage }) => showThankYouMessage,
+ },
+ ],
+ openChoice: [
+ null as string | null,
+ {
+ setOpenChoice: (_, { openChoice }) => openChoice,
+ },
+ ],
+ surveyResponse: [
+ [] as string[],
+ {
+ handleChoiceChange: (state, { choice, isAdded }) =>
+ isAdded ? [...state, choice] : state.filter((c: string) => c !== choice),
+ },
+ ],
+ }),
+ listeners(({ actions, values, props }) => ({
+ /** When surveyId is set, get the list of surveys for the user */
+ setSurveyId: () => {},
+ /** Callback for the surveys response. Filter it to the surveyId and set the survey */
+ handleSurveys: ({ surveys }) => {
+ const survey = surveys.find((s: Survey) => s.id === props.surveyId)
+ if (survey) {
+ posthog.capture('survey shown', {
+ $survey_id: props.surveyId,
+ })
+ actions.setSurvey(survey)
+
+ if (survey.appearance?.thankYouMessageHeader) {
+ actions.setThankYouMessage(survey.appearance?.thankYouMessageHeader)
+ }
+ }
+ },
+ /** When the survey response is sent, capture the response and show the thank you message */
+ handleSurveyResponse: () => {
+ const payload = {
+ $survey_id: props.surveyId,
+ $survey_response: values.surveyResponse,
+ }
+ if (values.openChoice) {
+ payload.$survey_response.push(values.openChoice)
+ }
+ posthog.capture('survey sent', payload)
+
+ actions.setShowThankYouMessage(true)
+ setTimeout(() => actions.setSurvey(null as unknown as Survey), 5000)
+ },
+ })),
+ afterMount(({ actions }) => {
+ /** When the logic is mounted, set the surveyId from the props */
+ posthog.getSurveys((surveys) => actions.handleSurveys(surveys as unknown as Survey[]))
+ }),
+])
diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx
index e2a84d413715f..8963d698a6bbe 100644
--- a/frontend/src/lib/constants.tsx
+++ b/frontend/src/lib/constants.tsx
@@ -317,6 +317,7 @@ export const SESSION_REPLAY_MINIMUM_DURATION_OPTIONS: LemonSelectOptions(false)
+
+ /**
+ * Handle the opt in change
+ * @param checked
+ */
+ const handleOptInChange = (checked: boolean): void => {
+ updateCurrentTeam({
+ session_recording_opt_in: checked,
+ })
+
+ //If the user opts out, we show the survey
+ setShowSurvey(!checked)
+ }
return (
@@ -521,16 +537,13 @@ export function ReplayGeneral(): JSX.Element {
{
- updateCurrentTeam({
- // when switching replay on or off,
- // we set defaults for some of the other settings
- session_recording_opt_in: checked,
- })
+ handleOptInChange(checked)
}}
label="Record user sessions"
bordered
checked={!!currentTeam?.session_recording_opt_in}
/>
+ {showSurvey && }