Skip to content

Commit

Permalink
feat(surveys): Add support for duplicating survey (#21525)
Browse files Browse the repository at this point in the history

Co-authored-by: Neil Kakkar <[email protected]>
  • Loading branch information
aryanrawlani28 and neilkakkar authored Apr 23, 2024
1 parent a3c1c21 commit e332571
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 8 deletions.
60 changes: 60 additions & 0 deletions cypress/e2e/surveys.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,64 @@ describe('Surveys', () => {
cy.get('[data-attr=surveys-table]').should('contain', name)
cy.get(`[data-row-key="${name}"]`).contains(name).click()
})

it('duplicates a survey', () => {
// create survey
cy.get('[data-attr=new-survey]').click()
cy.get('[data-attr=new-blank-survey]').click()
cy.get('[data-attr=survey-name]').focus().type(name).should('have.value', name)

// Add user targetting criteria
cy.get('.LemonCollapsePanel').contains('Targeting').click()
cy.contains('All users').click()
cy.get('.Popover__content').contains('Users who match').click()
cy.contains('Add user targeting').click()
cy.get('[data-attr="property-select-toggle-0"]').click()
cy.get('[data-attr="prop-filter-person_properties-0"]').click()
cy.get('[data-attr=prop-val] .LemonInput').click({ force: true })
cy.get('[data-attr=prop-val-0]').click({ force: true })
cy.get('[data-attr="rollout-percentage"]').type('100')

cy.get('[data-attr=save-survey]').first().click()

// Launch the survey first, the duplicated one should be in draft
cy.get('[data-attr="launch-survey"]').click()

// try to duplicate survey
cy.get('[data-attr=more-button]').click()
cy.get('[data-attr=duplicate-survey]').click()

// if the survey is duplicated, try to view it & verify a copy is created
cy.get('[data-attr=success-toast]').contains('duplicated').should('exist').siblings('button').click()
cy.get('[data-attr=top-bar-name]').contains(`${name} (copy)`).should('exist')

// check if it launched in a draft state
cy.get('button[data-attr="launch-survey"]').should('have.text', 'Launch')

// check if targetting criteria is copied
cy.contains('Release conditions summary').should('exist')
cy.get('.FeatureConditionCard').should('exist').should('contain.text', 'is_demo equals true')
cy.get('.FeatureConditionCard').should('contain.text', 'Rolled out to 100% of users in this set.')

// delete the duplicated survey
cy.get('[data-attr=more-button]').click()
cy.get('[data-attr=delete-survey]').click()

// Archive the original survey
cy.clickNavMenu('surveys')
cy.get('[data-attr=surveys-table]').find(`[data-row-key="${name}"]`).find('a').click()
cy.get('[data-attr=stop-survey]').click()
cy.get('[data-attr=more-button]').click()
cy.get('[data-attr=archive-survey]').click()

// check if the duplicated survey is created with draft state
cy.get('[data-attr=more-button]').click()
cy.get('[data-attr=duplicate-survey]').click()
cy.clickNavMenu('surveys')
cy.get('[data-attr=surveys-table]')
.find(`[data-row-key="${name} (copy)"]`)
.find('[data-attr=status]')
.contains('DRAFT')
.should('exist')
})
})
5 changes: 3 additions & 2 deletions frontend/src/lib/utils/eventUsageLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
reportSurveyViewed: (survey: Survey) => ({
survey,
}),
reportSurveyCreated: (survey: Survey) => ({ survey }),
reportSurveyCreated: (survey: Survey, isDuplicate?: boolean) => ({ survey, isDuplicate }),
reportSurveyEdited: (survey: Survey) => ({ survey }),
reportSurveyLaunched: (survey: Survey) => ({ survey }),
reportSurveyStopped: (survey: Survey) => ({ survey }),
Expand Down Expand Up @@ -1156,13 +1156,14 @@ export const eventUsageLogic = kea<eventUsageLogicType>([
language,
})
},
reportSurveyCreated: ({ survey }) => {
reportSurveyCreated: ({ survey, isDuplicate }) => {
posthog.capture('survey created', {
name: survey.name,
id: survey.id,
survey_type: survey.type,
questions_length: survey.questions.length,
question_types: survey.questions.map((question) => question.type),
is_duplicate: isDuplicate ?? false,
})
},
reportSurveyLaunched: ({ survey }) => {
Expand Down
34 changes: 30 additions & 4 deletions frontend/src/scenes/surveys/SurveyView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,25 @@ import {

export function SurveyView({ id }: { id: string }): JSX.Element {
const { survey, surveyLoading, selectedQuestion, targetingFlagFilters } = useValues(surveyLogic)
const { editingSurvey, updateSurvey, launchSurvey, stopSurvey, archiveSurvey, resumeSurvey, setSelectedQuestion } =
useActions(surveyLogic)
const {
editingSurvey,
updateSurvey,
launchSurvey,
stopSurvey,
archiveSurvey,
resumeSurvey,
setSelectedQuestion,
duplicateSurvey,
} = useActions(surveyLogic)
const { deleteSurvey } = useActions(surveysLogic)

const [tabKey, setTabKey] = useState(survey.start_date ? 'results' : 'overview')

useEffect(() => {
if (survey.start_date) {
setTabKey('results')
} else {
setTabKey('overview')
}
}, [survey.start_date])

Expand All @@ -66,10 +76,21 @@ export function SurveyView({ id }: { id: string }): JSX.Element {
>
Edit
</LemonButton>
<LemonButton
data-attr="duplicate-survey"
fullWidth
onClick={duplicateSurvey}
>
Duplicate
</LemonButton>
<LemonDivider />
</>
{survey.end_date && !survey.archived && (
<LemonButton onClick={() => archiveSurvey()} fullWidth>
<LemonButton
data-attr="archive-survey"
onClick={() => archiveSurvey()}
fullWidth
>
Archive
</LemonButton>
)}
Expand Down Expand Up @@ -101,7 +122,12 @@ export function SurveyView({ id }: { id: string }): JSX.Element {
</LemonButton>
) : (
!survey.archived && (
<LemonButton type="secondary" status="danger" onClick={() => stopSurvey()}>
<LemonButton
data-attr="stop-survey"
type="secondary"
status="danger"
onClick={() => stopSurvey()}
>
Stop
</LemonButton>
)
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/scenes/surveys/Surveys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function Surveys(): JSX.Element {

const { user } = useValues(userLogic)

const [tab, setSurveyTab] = useState(SurveysTabs.Active)
const [tab, setSurveyTab] = useState(filters.archived ? SurveysTabs.Archived : SurveysTabs.Active)
const shouldShowEmptyState = !surveysLoading && surveys.length === 0

return (
Expand Down Expand Up @@ -368,7 +368,7 @@ export function StatusTag({ survey }: { survey: Survey }): JSX.Element {
} as Record<ProgressStatus, LemonTagType>
const status = getSurveyStatus(survey)
return (
<LemonTag type={statusColors[status]} style={{ fontWeight: 600 }}>
<LemonTag type={statusColors[status]} style={{ fontWeight: 600 }} data-attr="status">
{status.toUpperCase()}
</LemonTag>
)
Expand Down
36 changes: 36 additions & 0 deletions frontend/src/scenes/surveys/surveyLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ export interface QuestionResultsReady {

const getResponseField = (i: number): string => (i === 0 ? '$survey_response' : `$survey_response_${i}`)

function duplicateExistingSurvey(survey: Survey | NewSurvey): Partial<Survey> {
return {
...survey,
id: NEW_SURVEY.id,
name: `${survey.name} (copy)`,
archived: false,
start_date: null,
end_date: null,
targeting_flag_filters: survey.targeting_flag?.filters ?? NEW_SURVEY.targeting_flag_filters,
linked_flag_id: survey.linked_flag?.id ?? NEW_SURVEY.linked_flag_id,
}
}

export const surveyLogic = kea<surveyLogicType>([
props({} as SurveyLogicProps),
key(({ id }) => id),
Expand Down Expand Up @@ -171,6 +184,26 @@ export const surveyLogic = kea<surveyLogicType>([
return await api.surveys.update(props.id, { end_date: null })
},
},
duplicatedSurvey: {
duplicateSurvey: async () => {
const { survey } = values
const payload = duplicateExistingSurvey(survey)
const createdSurvey = await api.surveys.create(sanitizeQuestions(payload))

lemonToast.success('Survey duplicated.', {
toastId: `survey-duplicated-${createdSurvey.id}`,
button: {
label: 'View Survey',
action: () => {
router.actions.push(urls.survey(createdSurvey.id))
},
},
})

actions.reportSurveyCreated(createdSurvey, true)
return survey
},
},
surveyUserStats: {
loadSurveyUserStats: async (): Promise<SurveyUserStats> => {
const { survey } = values
Expand Down Expand Up @@ -413,6 +446,9 @@ export const surveyLogic = kea<surveyLogicType>([
actions.reportSurveyEdited(survey)
actions.loadSurveys()
},
duplicateSurveySuccess: () => {
actions.loadSurveys()
},
launchSurveySuccess: ({ survey }) => {
lemonToast.success(<>Survey {survey.name} launched</>)
actions.loadSurveys()
Expand Down

0 comments on commit e332571

Please sign in to comment.