Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(surveys): templates #17904

Merged
merged 26 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
84c00e4
routes for survey templates
liyiy Oct 11, 2023
343a5a4
basic on click setup logic
liyiy Oct 11, 2023
610b11f
Update UI snapshots for `chromium` (2)
github-actions[bot] Oct 11, 2023
725eb46
import type
liyiy Oct 11, 2023
e2f4d00
Merge branch 'master' into surveys/templates
liyiy Oct 11, 2023
d006c61
Apply suggestions from code review
liyiy Oct 11, 2023
f4f0047
Merge branch 'surveys/templates' of https://github.com/PostHog/postho…
liyiy Oct 11, 2023
c729606
address feedback from cory
liyiy Oct 11, 2023
8c63bad
fix logic
liyiy Oct 11, 2023
84a068c
remove unused logic file
liyiy Oct 11, 2023
c9600e8
Update frontend/src/scenes/surveys/constants.tsx
liyiy Oct 12, 2023
1994686
Update frontend/src/scenes/surveys/constants.tsx
liyiy Oct 12, 2023
3732a91
Merge branch 'master' into surveys/templates
liyiy Oct 12, 2023
55f8744
Merge branch 'surveys/templates' of https://github.com/PostHog/postho…
liyiy Oct 12, 2023
d0095eb
address comments
liyiy Oct 12, 2023
ce744a9
Update UI snapshots for `chromium` (2)
github-actions[bot] Oct 12, 2023
0ca4d0e
more fixes
liyiy Oct 12, 2023
3739f7e
Merge branch 'surveys/templates' of https://github.com/PostHog/postho…
liyiy Oct 12, 2023
7febe5d
Update UI snapshots for `webkit` (2)
github-actions[bot] Oct 12, 2023
cd405b5
Update UI snapshots for `webkit` (2)
github-actions[bot] Oct 12, 2023
a617a0e
Update UI snapshots for `chromium` (2)
github-actions[bot] Oct 12, 2023
c3e9196
Merge branch 'master' into surveys/templates
liyiy Oct 16, 2023
3150085
Merge branch 'surveys/templates' of https://github.com/PostHog/postho…
liyiy Oct 16, 2023
92fedf7
move description and title together
liyiy Oct 16, 2023
f53936b
Merge branch 'master' into surveys/templates
liyiy Oct 16, 2023
027562d
fix e2e
liyiy Oct 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified frontend/__snapshots__/scenes-app-surveys--surveys-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/src/scenes/appScenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const appScenes: Record<Scene, () => any> = {
[Scene.EarlyAccessFeature]: () => import('./early-access-features/EarlyAccessFeature'),
[Scene.Surveys]: () => import('./surveys/Surveys'),
[Scene.Survey]: () => import('./surveys/Survey'),
[Scene.SurveyTemplates]: () => import('./surveys/SurveyTemplates'),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the new scene for templates doesn't really make sense anymore, since it's on the surveys page itself?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually still routes to survey_templates which is its own scene, I was thinking a bit about this on whether to just incorporate it directly onto the surveys components or not but I think a separation of routes is the better option. That way users can always be directed to the templates page instead of it being a "state"

[Scene.DataWarehouse]: () => import('./data-warehouse/external/DataWarehouseExternalScene'),
[Scene.DataWarehousePosthog]: () => import('./data-warehouse/posthog/DataWarehousePosthogScene'),
[Scene.DataWarehouseExternal]: () => import('./data-warehouse/external/DataWarehouseExternalScene'),
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/sceneLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const sceneNavAlias: Partial<Record<Scene, Scene>> = {
[Scene.FeatureFlag]: Scene.FeatureFlags,
[Scene.EarlyAccessFeature]: Scene.EarlyAccessFeatures,
[Scene.Survey]: Scene.Surveys,
[Scene.SurveyTemplates]: Scene.Surveys,
[Scene.DataWarehouseTable]: Scene.DataWarehouse,
[Scene.DataWarehousePosthog]: Scene.DataWarehouse,
[Scene.DataWarehouseExternal]: Scene.DataWarehouse,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/scenes/sceneTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export enum Scene {
EarlyAccessFeature = 'EarlyAccessFeature',
Surveys = 'Surveys',
Survey = 'Survey',
SurveyTemplates = 'SurveyTemplates',
DataWarehouse = 'DataWarehouse',
DataWarehousePosthog = 'DataWarehousePosthog',
DataWarehouseExternal = 'DataWarehouseExternal',
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/scenes/scenes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ export const sceneConfigurations: Partial<Record<Scene, SceneConfig>> = {
projectBased: true,
name: 'Survey',
},
[Scene.SurveyTemplates]: {
projectBased: true,
name: 'New survey',
},
[Scene.DataWarehouse]: {
projectBased: true,
name: 'Data Warehouse',
Expand Down Expand Up @@ -429,6 +433,7 @@ export const routes: Record<string, Scene> = {
[urls.earlyAccessFeature(':id')]: Scene.EarlyAccessFeature,
[urls.surveys()]: Scene.Surveys,
[urls.survey(':id')]: Scene.Survey,
[urls.surveyTemplates()]: Scene.SurveyTemplates,
[urls.dataWarehouse()]: Scene.DataWarehouse,
[urls.dataWarehouseTable(':id')]: Scene.DataWarehouseTable,
[urls.dataWarehousePosthog()]: Scene.DataWarehousePosthog,
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/scenes/surveys/SurveyAppearance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface SurveyAppearanceProps {
type: SurveyQuestionType
question: string
appearance: SurveyAppearanceType
surveyQuestionItem: RatingSurveyQuestion | SurveyQuestion | MultipleSurveyQuestion
surveyQuestionItem: SurveyQuestion
description?: string | null
link?: string | null
readOnly?: boolean
Expand Down Expand Up @@ -101,7 +101,7 @@ export function SurveyAppearance({

return (
<>
<h3 className="mb-4 text-center">Preview</h3>
{!readOnly && <h3 className="mb-4 text-center">Preview</h3>}
liyiy marked this conversation as resolved.
Show resolved Hide resolved
{!hideSubmittedSurvey && (
<>
{type === SurveyQuestionType.Rating && (
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/scenes/surveys/SurveyTemplates.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@import '../../styles/mixins';

.SurveyTemplateContainer {
display: flex;
align-items: center;
border: 1px solid var(--border);
border-radius: 6px;
min-height: 300px;
margin-top: 2px;

&:hover {
cursor: pointer;
border-color: var(--primary-light);
}

.SurveyTemplate {
-ms-transform: scale(0.8, 0.8); /* IE 9 */
-webkit-transform: scale(0.8, 0.8); /* Safari */
transform: scale(0.8, 0.8);
}
}
64 changes: 64 additions & 0 deletions frontend/src/scenes/surveys/SurveyTemplates.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { SceneExport } from 'scenes/sceneTypes'
import { SurveyAppearance } from './SurveyAppearance'
import { defaultSurveyTemplates } from './constants'
import { SurveyQuestion } from '~/types'
import './SurveyTemplates.scss'
import { useActions } from 'kea'
import { PageHeader } from 'lib/components/PageHeader'
import { LemonButton } from '@posthog/lemon-ui'
import { urls } from 'scenes/urls'
import { surveyLogic } from './surveyLogic'

export const scene: SceneExport = {
component: SurveyTemplates,
}

export function SurveyTemplates(): JSX.Element {
const { setSurveyTemplateValues } = useActions(surveyLogic({ id: 'new' }))

return (
<>
<PageHeader
title={'New survey'}
buttons={
<LemonButton type="primary" to={urls.survey('new')} data-attr="new-survey">
Create blank survey
</LemonButton>
}
/>
<div className="flex flex-row flex-wrap gap-10 ml-8 mt-8">
{defaultSurveyTemplates.map((template, idx) => {
return (
<div
className="flex flex-col items-center"
key={idx}
onClick={() =>
setSurveyTemplateValues({ name: template.type, questions: template.questions })
}
>
<span className="mb-2 text-md">
<b>{template.type}</b>
</span>
<div className="SurveyTemplateContainer">
<div className="SurveyTemplate">
<SurveyAppearance
key={idx}
type={template.questions[0].type}
question={template.questions[0].question}
appearance={{ whiteLabel: true }}
surveyQuestionItem={template.questions[0] as SurveyQuestion}
onAppearanceChange={() => {}}
readOnly
/>
</div>
</div>
<span className="flex flex-wrap text-xs text-muted max-w-80 font-medium mt-3">
{template.description}
</span>
</div>
)
})}
</div>
</>
)
}
4 changes: 2 additions & 2 deletions frontend/src/scenes/surveys/Surveys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ export function Surveys(): JSX.Element {
}
buttons={
<>
<LemonButton type="primary" to={urls.survey('new')} data-attr="new-survey">
New survey
<LemonButton type="primary" to={urls.surveyTemplates()} data-attr="new-survey">
New
liyiy marked this conversation as resolved.
Show resolved Hide resolved
</LemonButton>
<LemonButton
type="secondary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,103 @@ export const NEW_SURVEY: NewSurvey = {
archived: false,
appearance: defaultSurveyAppearance,
}

export enum SurveyTemplateTypes {
liyiy marked this conversation as resolved.
Show resolved Hide resolved
Interview = 'User interview',
NPS = 'Net promoter score (NPS)',
CSAT = 'Customer satisfaction score (CSAT)',
CES = 'Customer effort score (CES)',
CCR = 'Customer churn rate (CCR)',
Superhuman = 'Product-market fit (Superhuman)',
}

export const defaultSurveyTemplates = [
{
type: SurveyTemplateTypes.Interview,
questions: [
{
type: SurveyQuestionType.Link,
question: 'Would you be interested in participating in a customer interview?',
description: 'We are looking for feedback on our product and would love to hear from you!',
link: 'https://calendly.com/',
},
],
description: <>Send users straight to your calendar.</>,
},
{
type: SurveyTemplateTypes.NPS,
questions: [
{
type: SurveyQuestionType.Rating,
question: 'How likely are you to recommend us to a friend?',
description: '',
display: 'number',
scale: 10,
lowerBoundLabel: 'Unlikely',
upperBoundLabel: 'Very likely',
},
],
description: 'Get an industry-recognized benchmark.',
},
{
type: SurveyTemplateTypes.Superhuman,
questions: [
{
type: SurveyQuestionType.SingleChoice,
question: 'How would you feel if you could no longer use PostHog?',
choices: ['Not disappointed', 'Somewhat disappointed', 'Very disappointed'],
},
],
description: "If 40% say 'very disappointed', you're at product-market fit.",
liyiy marked this conversation as resolved.
Show resolved Hide resolved
},
{
type: SurveyTemplateTypes.CSAT,
questions: [
{
type: SurveyQuestionType.Rating,
question: 'How satisfied are you with PostHog surveys?',
description: '',
display: 'emoji',
scale: 5,
lowerBoundLabel: 'Very dissatisfied',
upperBoundLabel: 'Very satisfied',
},
],
description: 'Works best after a checkout or support flow.',
},
{
type: SurveyTemplateTypes.CES,
questions: [
{
type: SurveyQuestionType.Rating,
question: 'How easy was it to use our product?',
description: '',
display: 'emoji',
scale: 5,
lowerBoundLabel: 'Very difficult',
upperBoundLabel: 'Very easy',
},
],
description: 'Works well with churn surveys.',
},
{
type: SurveyTemplateTypes.CCR,
questions: [
{
type: SurveyQuestionType.MultipleChoice,
question: "We're sorry to see you go. What's your reason for unsubscribing?",
choices: [
'I no longer need the product',
'I found a better product',
'I found the product too difficult to use',
'Other',
],
},
{
type: SurveyQuestionType.Open,
question: "Anything else you'd like to share?",
},
],
description: 'Find out if it was something you said.',
},
]
23 changes: 21 additions & 2 deletions frontend/src/scenes/surveys/surveyLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { lemonToast } from '@posthog/lemon-ui'
import { kea, path, props, key, listeners, afterMount, reducers, actions, selectors, connect } from 'kea'
import { forms } from 'kea-forms'
import { loaders } from 'kea-loaders'
import { router, urlToAction } from 'kea-router'
import { actionToUrl, router, urlToAction } from 'kea-router'
import api from 'lib/api'
import { urls } from 'scenes/urls'
import {
Expand Down Expand Up @@ -96,6 +96,7 @@ export const surveyLogic = kea<surveyLogicType>([
archiveSurvey: true,
setCurrentQuestionIndexAndType: (idx: number, type: SurveyQuestionType) => ({ idx, type }),
setWritingHTMLDescription: (writingHTML: boolean) => ({ writingHTML }),
setSurveyTemplateValues: (template: any) => ({ template }),
}),
loaders(({ props, actions, values }) => ({
survey: {
Expand All @@ -110,7 +111,11 @@ export const surveyLogic = kea<surveyLogicType>([
throw error
}
}
return { ...NEW_SURVEY }
if (props.id === 'new' && router.values.hashParams.fromTemplate) {
return values.survey
} else {
return { ...NEW_SURVEY }
}
},
createSurvey: async (surveyPayload: Partial<Survey>) => {
return await api.surveys.create(sanitizeQuestions(surveyPayload))
Expand Down Expand Up @@ -293,6 +298,12 @@ export const surveyLogic = kea<surveyLogicType>([
},
}
},
setSurveyTemplateValues: (state, { template }) => {
return {
...state,
liyiy marked this conversation as resolved.
Show resolved Hide resolved
...template,
}
},
},
],
currentQuestionIndexAndType: [
Expand Down Expand Up @@ -541,6 +552,14 @@ export const surveyLogic = kea<surveyLogicType>([
}
},
})),
actionToUrl(({ values }) => ({
setSurveyTemplateValues: () => {
const hashParams = router.values.hashParams
hashParams['fromTemplate'] = true

return [urls.survey(values.survey.id), router.values.searchParams, hashParams]
},
})),
afterMount(async ({ props, actions }) => {
if (props.id !== 'new') {
await actions.loadSurvey()
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/scenes/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,13 @@ export const urls = {
earlyAccessFeatures: (): string => '/early_access_features',
earlyAccessFeature: (id: ':id' | 'new' | string): string => `/early_access_features/${id}`,
surveys: (): string => '/surveys',
survey: (id: ':id' | 'new' | string): string => `/surveys/${id}`,
surveyTemplates: (): string => '/survey_templates',
dataWarehouse: (): string => '/warehouse',
dataWarehouseTable: (id: ':id' | 'new' | string): string => `/warehouse/${id}`,
dataWarehousePosthog: (): string => '/data-warehouse/posthog',
dataWarehouseExternal: (): string => '/data-warehouse/external',
dataWarehouseSavedQueries: (): string => '/data-warehouse/views',
survey: (id: ':id' | 'new' | string): string => `/surveys/${id}`,
annotations: (): string => '/annotations',
annotation: (id: AnnotationType['id'] | ':id'): string => `/annotations/${id}`,
projectApps: (tab?: PluginTab): string => `/project/apps${tab ? `?tab=${tab}` : ''}`,
Expand Down
Loading