Skip to content

Commit

Permalink
feat(surveys): table view improvements (#17686)
Browse files Browse the repository at this point in the history
* add search, filtering, responses column & other fixes

* Update UI snapshots for `chromium` (2)

* move out constants

* remove async

* fix status dropdown width

* fix import

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Li Yi Yu <[email protected]>
  • Loading branch information
3 people authored Oct 2, 2023
1 parent 36bc0fe commit 7bca1dc
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 198 deletions.
Binary file modified frontend/__snapshots__/scenes-app-surveys--new-survey.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.
2 changes: 1 addition & 1 deletion frontend/src/scenes/feature-flags/featureFlagLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { userLogic } from 'scenes/userLogic'
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
import { dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic'
import { NEW_EARLY_ACCESS_FEATURE } from 'scenes/early-access-features/earlyAccessFeatureLogic'
import { NEW_SURVEY, NewSurvey } from 'scenes/surveys/surveyLogic'
import { NEW_SURVEY, NewSurvey } from 'scenes/surveys/constants'

const getDefaultRollbackCondition = (): FeatureFlagRollbackConditions => ({
operator: 'gt',
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/scenes/notebooks/Nodes/NotebookNodeSurvey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
import { notebookNodeLogic } from './notebookNodeLogic'
import { JSONContent, NotebookNodeViewProps } from '../Notebook/utils'
import { buildFlagContent } from './NotebookNodeFlag'
import { defaultSurveyAppearance, surveyLogic } from 'scenes/surveys/surveyLogic'
import { surveyLogic } from 'scenes/surveys/surveyLogic'
import { defaultSurveyAppearance } from 'scenes/surveys/constants'
import { StatusTag } from 'scenes/surveys/Surveys'
import { SurveyResult } from 'scenes/surveys/SurveyView'
import { SurveyAppearance } from 'scenes/surveys/SurveyAppearance'
Expand Down
22 changes: 16 additions & 6 deletions frontend/src/scenes/surveys/Survey.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SceneExport } from 'scenes/sceneTypes'
import { NewSurvey, defaultSurveyAppearance, defaultSurveyFieldValues, surveyLogic } from './surveyLogic'
import { surveyLogic } from './surveyLogic'
import { BindLogic, useActions, useValues } from 'kea'
import { Form, Group } from 'kea-forms'
import { PageHeader } from 'lib/components/PageHeader'
Expand Down Expand Up @@ -31,6 +31,7 @@ import { SurveyAppearance } from './SurveyAppearance'
import { SurveyAPIEditor } from './SurveyAPIEditor'
import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic'
import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic'
import { defaultSurveyFieldValues, defaultSurveyAppearance, SurveyQuestionLabel, NewSurvey } from './constants'
import { FEATURE_FLAGS } from 'lib/constants'
import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions'

Expand Down Expand Up @@ -139,17 +140,26 @@ export function SurveyForm({ id }: { id: string }): JSX.Element {
)
}}
options={[
{ label: 'Open text', value: SurveyQuestionType.Open },
{ label: 'Link', value: SurveyQuestionType.Link },
{ label: 'Rating', value: SurveyQuestionType.Rating },
{
label: SurveyQuestionLabel[SurveyQuestionType.Open],
value: SurveyQuestionType.Open,
},
{
label: SurveyQuestionLabel[SurveyQuestionType.Link],
value: SurveyQuestionType.Link,
},
{
label: SurveyQuestionLabel[SurveyQuestionType.Rating],
value: SurveyQuestionType.Rating,
},
...(featureFlags[FEATURE_FLAGS.SURVEYS_MULTIPLE_CHOICE]
? [
{
label: 'Single choice select',
label: SurveyQuestionLabel[SurveyQuestionType.SingleChoice],
value: SurveyQuestionType.SingleChoice,
},
{
label: 'Multiple choice select',
label: SurveyQuestionLabel[SurveyQuestionType.MultipleChoice],
value: SurveyQuestionType.MultipleChoice,
},
]
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/surveys/SurveyAPIEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Survey } from '~/types'
import { NewSurvey } from './surveyLogic'
import { NewSurvey } from './constants'
import { CodeSnippet, Language } from 'lib/components/CodeSnippet'

export function SurveyAPIEditor({ survey }: { survey: Survey | NewSurvey }): JSX.Element {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/scenes/surveys/SurveyAppearance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
SurveyQuestionType,
MultipleSurveyQuestion,
} from '~/types'
import { defaultSurveyAppearance } from './surveyLogic'
import { defaultSurveyAppearance } from './constants'
import {
cancel,
check,
Expand Down
9 changes: 5 additions & 4 deletions frontend/src/scenes/surveys/SurveyView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { capitalizeFirstLetter } from 'lib/utils'
import { useState, useEffect } from 'react'
import { pluginsLogic } from 'scenes/plugins/pluginsLogic'
import { Query } from '~/queries/Query/Query'
import { defaultSurveyAppearance, surveyEventName, surveyLogic } from './surveyLogic'
import { surveyLogic } from './surveyLogic'
import { surveysLogic } from './surveysLogic'
import { PageHeader } from 'lib/components/PageHeader'
import { SurveyReleaseSummary } from './Survey'
Expand All @@ -21,6 +21,7 @@ import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { IconOpenInNew } from 'lib/lemon-ui/icons'
import { NodeKind } from '~/queries/schema'
import { dayjs } from 'lib/dayjs'
import { defaultSurveyAppearance, SURVEY_EVENT_NAME } from './constants'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'

Expand Down Expand Up @@ -326,7 +327,7 @@ function SurveyNPSResults({ survey }: { survey: Survey }): JSX.Element {
},
series: [
{
event: surveyEventName,
event: SURVEY_EVENT_NAME,
kind: NodeKind.EventsNode,
custom_name: 'Promoters',
properties: [
Expand All @@ -339,7 +340,7 @@ function SurveyNPSResults({ survey }: { survey: Survey }): JSX.Element {
],
},
{
event: surveyEventName,
event: SURVEY_EVENT_NAME,
kind: NodeKind.EventsNode,
custom_name: 'Passives',
properties: [
Expand All @@ -352,7 +353,7 @@ function SurveyNPSResults({ survey }: { survey: Survey }): JSX.Element {
],
},
{
event: surveyEventName,
event: SURVEY_EVENT_NAME,
kind: NodeKind.EventsNode,
custom_name: 'Detractors',
properties: [
Expand Down
154 changes: 113 additions & 41 deletions frontend/src/scenes/surveys/Surveys.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { LemonButton, LemonTable, LemonDivider, Link, LemonTag, LemonTagType, Spinner } from '@posthog/lemon-ui'
import {
LemonButton,
LemonDivider,
LemonInput,
LemonSelect,
LemonTable,
Link,
LemonTag,
LemonTagType,
Spinner,
} from '@posthog/lemon-ui'
import { PageHeader } from 'lib/components/PageHeader'
import { More } from 'lib/lemon-ui/LemonButton/More'
import stringWithWBR from 'lib/utils/stringWithWBR'
Expand All @@ -14,13 +24,13 @@ import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { useState } from 'react'
import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
import { userLogic } from 'scenes/userLogic'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
import { dayjs } from 'lib/dayjs'
import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner'
import { teamLogic } from 'scenes/teamLogic'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
import { IconSettings } from 'lib/lemon-ui/icons'
import { openSurveysSettingsDialog } from './SurveySettings'
import { SurveyQuestionLabel } from './constants'
import { FEATURE_FLAGS } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'

Expand All @@ -30,34 +40,37 @@ export const scene: SceneExport = {
}

export enum SurveysTabs {
All = 'all',
Active = 'active',
Yours = 'yours',
Archived = 'archived',
}

export function Surveys(): JSX.Element {
const {
nonArchivedSurveys,
archivedSurveys,
surveys,
searchedSurveys,
surveysLoading,
surveysResponsesCount,
surveysResponsesCountLoading,
usingSurveysSiteApp,
searchTerm,
filters,
uniqueCreators,
} = useValues(surveysLogic)

const { deleteSurvey, updateSurvey } = useActions(surveysLogic)
const { deleteSurvey, updateSurvey, setSearchTerm, setSurveysFilters } = useActions(surveysLogic)

const { user } = useValues(userLogic)
const { featureFlags } = useValues(featureFlagLogic)

const { currentTeam } = useValues(teamLogic)
const surveysPopupDisabled = currentTeam && !currentTeam?.surveys_opt_in

const [tab, setSurveyTab] = useState(SurveysTabs.All)
const [tab, setSurveyTab] = useState(SurveysTabs.Active)
const shouldShowEmptyState = !surveysLoading && surveys.length === 0

return (
<div className="mt-10">
<div>
<PageHeader
title={
<div className="flex items-center gap-2">
Expand All @@ -84,10 +97,13 @@ export function Surveys(): JSX.Element {
/>
<LemonTabs
activeKey={tab}
onChange={(newTab) => setSurveyTab(newTab)}
onChange={(newTab) => {
setSurveyTab(newTab)
setSurveysFilters({ ...filters, archived: newTab === SurveysTabs.Archived })
}}
tabs={[
{ key: SurveysTabs.All, label: 'All surveys' },
{ key: SurveysTabs.Archived, label: 'Archived surveys' },
{ key: SurveysTabs.Active, label: 'Active' },
{ key: SurveysTabs.Archived, label: 'Archived' },
]}
/>
{featureFlags[FEATURE_FLAGS.SURVEYS_SITE_APP_DEPRECATION] && (
Expand All @@ -111,25 +127,81 @@ export function Surveys(): JSX.Element {
) : null}
</div>
)}
{surveysLoading ? (
<LemonSkeleton />
) : (
<>
{(shouldShowEmptyState || !user?.has_seen_product_intro_for?.[ProductKey.SURVEYS]) && (
<ProductIntroduction
productName={'Surveys'}
thingName={'survey'}
description={
'Use surveys to gather qualitative feedback from your users on new or existing features.'
}
action={() => router.actions.push(urls.survey('new'))}
isEmpty={surveys.length === 0}
productKey={ProductKey.SURVEYS}
/>
)}
{!shouldShowEmptyState && (

<>
{(shouldShowEmptyState || !user?.has_seen_product_intro_for?.[ProductKey.SURVEYS]) && (
<ProductIntroduction
productName={'Surveys'}
thingName={'survey'}
description={
'Use surveys to gather qualitative feedback from your users on new or existing features.'
}
action={() => router.actions.push(urls.survey('new'))}
isEmpty={surveys.length === 0}
productKey={ProductKey.SURVEYS}
/>
)}
{!shouldShowEmptyState && (
<>
<div>
<div className="flex justify-between mb-4">
<LemonInput
type="search"
placeholder="Search for surveys"
onChange={setSearchTerm}
value={searchTerm || ''}
/>
<div className="flex items-center gap-2">
<span>
<b>Status</b>
</span>
<LemonSelect
dropdownMatchSelectWidth={false}
onChange={(status) => {
setSurveysFilters({ status })
}}
options={[
{ label: 'Any', value: 'any' },
{ label: 'Draft', value: 'draft' },
{ label: 'Running', value: 'running' },
{ label: 'Complete', value: 'complete' },
]}
value={filters.status}
/>
<span className="ml-1">
<b>Created by</b>
</span>
<LemonSelect
onChange={(user) => {
if (user) {
if (user === 'any') {
if (filters) {
const { created_by, ...restFilters } = filters
setSurveysFilters(restFilters, true)
}
} else {
setSurveysFilters({ created_by: user })
}
}
}}
options={uniqueCreators}
value={filters.created_by}
/>
</div>
</div>
</div>
<LemonTable
className="mt-6"
dataSource={searchedSurveys}
defaultSorting={{
columnKey: 'created_at',
order: -1,
}}
nouns={['survey', 'surveys']}
data-attr="surveys-table"
emptyState={
tab === SurveysTabs.Active ? 'No surveys. Create a new survey?' : 'No surveys found'
}
loading={surveysLoading}
columns={[
{
dataIndex: 'name',
Expand Down Expand Up @@ -166,7 +238,15 @@ export function Surveys(): JSX.Element {
},
{
dataIndex: 'type',
title: 'Type',
title: 'Mode',
},
{
title: 'Question type',
render: function RenderResponses(_, survey) {
return survey.questions.length === 1
? SurveyQuestionLabel[survey.questions[0].type]
: 'Multiple'
},
},
createdByColumn<Survey>() as LemonTableColumn<Survey, keyof Survey | undefined>,
createdAtColumn<Survey>() as LemonTableColumn<Survey, keyof Survey | undefined>,
Expand Down Expand Up @@ -280,18 +360,10 @@ export function Surveys(): JSX.Element {
},
},
]}
dataSource={tab === SurveysTabs.Archived ? archivedSurveys : nonArchivedSurveys}
defaultSorting={{
columnKey: 'created_at',
order: -1,
}}
nouns={['survey', 'surveys']}
data-attr="surveys-table"
emptyState="No surveys. Create a new survey?"
/>
)}
</>
)}
</>
)}
</>
</div>
)
}
Expand Down
Loading

0 comments on commit 7bca1dc

Please sign in to comment.