diff --git a/frontend/__snapshots__/scenes-app-surveys--new-survey.png b/frontend/__snapshots__/scenes-app-surveys--new-survey.png index 42ecadfbd13a9..7919d71d4d68e 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--new-survey.png and b/frontend/__snapshots__/scenes-app-surveys--new-survey.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--surveys-list.png b/frontend/__snapshots__/scenes-app-surveys--surveys-list.png index c376b70a4e31f..8f5f6642dbecc 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--surveys-list.png and b/frontend/__snapshots__/scenes-app-surveys--surveys-list.png differ diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.ts index aeb4b9471f764..5f33ae64bd556 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.ts @@ -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', diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodeSurvey.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodeSurvey.tsx index 700e1ca60a190..f58e709422962 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodeSurvey.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodeSurvey.tsx @@ -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' diff --git a/frontend/src/scenes/surveys/Survey.tsx b/frontend/src/scenes/surveys/Survey.tsx index 0dd3b5a4667f7..65f9af689976d 100644 --- a/frontend/src/scenes/surveys/Survey.tsx +++ b/frontend/src/scenes/surveys/Survey.tsx @@ -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' @@ -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' @@ -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, }, ] diff --git a/frontend/src/scenes/surveys/SurveyAPIEditor.tsx b/frontend/src/scenes/surveys/SurveyAPIEditor.tsx index 192d35c73b2b1..02fc80e8708ba 100644 --- a/frontend/src/scenes/surveys/SurveyAPIEditor.tsx +++ b/frontend/src/scenes/surveys/SurveyAPIEditor.tsx @@ -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 { diff --git a/frontend/src/scenes/surveys/SurveyAppearance.tsx b/frontend/src/scenes/surveys/SurveyAppearance.tsx index 65e2b4fa9bb81..c8643df296bef 100644 --- a/frontend/src/scenes/surveys/SurveyAppearance.tsx +++ b/frontend/src/scenes/surveys/SurveyAppearance.tsx @@ -7,7 +7,7 @@ import { SurveyQuestionType, MultipleSurveyQuestion, } from '~/types' -import { defaultSurveyAppearance } from './surveyLogic' +import { defaultSurveyAppearance } from './constants' import { cancel, check, diff --git a/frontend/src/scenes/surveys/SurveyView.tsx b/frontend/src/scenes/surveys/SurveyView.tsx index 20aae5ae7af40..7f4fb0e241eab 100644 --- a/frontend/src/scenes/surveys/SurveyView.tsx +++ b/frontend/src/scenes/surveys/SurveyView.tsx @@ -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' @@ -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' @@ -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: [ @@ -339,7 +340,7 @@ function SurveyNPSResults({ survey }: { survey: Survey }): JSX.Element { ], }, { - event: surveyEventName, + event: SURVEY_EVENT_NAME, kind: NodeKind.EventsNode, custom_name: 'Passives', properties: [ @@ -352,7 +353,7 @@ function SurveyNPSResults({ survey }: { survey: Survey }): JSX.Element { ], }, { - event: surveyEventName, + event: SURVEY_EVENT_NAME, kind: NodeKind.EventsNode, custom_name: 'Detractors', properties: [ diff --git a/frontend/src/scenes/surveys/Surveys.tsx b/frontend/src/scenes/surveys/Surveys.tsx index de69d4c030811..19f987bbeb5b0 100644 --- a/frontend/src/scenes/surveys/Surveys.tsx +++ b/frontend/src/scenes/surveys/Surveys.tsx @@ -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' @@ -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' @@ -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 ( -
+
@@ -84,10 +97,13 @@ export function Surveys(): JSX.Element { /> 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] && ( @@ -111,25 +127,81 @@ export function Surveys(): JSX.Element { ) : null}
)} - {surveysLoading ? ( - - ) : ( - <> - {(shouldShowEmptyState || !user?.has_seen_product_intro_for?.[ProductKey.SURVEYS]) && ( - router.actions.push(urls.survey('new'))} - isEmpty={surveys.length === 0} - productKey={ProductKey.SURVEYS} - /> - )} - {!shouldShowEmptyState && ( + + <> + {(shouldShowEmptyState || !user?.has_seen_product_intro_for?.[ProductKey.SURVEYS]) && ( + router.actions.push(urls.survey('new'))} + isEmpty={surveys.length === 0} + productKey={ProductKey.SURVEYS} + /> + )} + {!shouldShowEmptyState && ( + <> +
+
+ +
+ + Status + + { + setSurveysFilters({ status }) + }} + options={[ + { label: 'Any', value: 'any' }, + { label: 'Draft', value: 'draft' }, + { label: 'Running', value: 'running' }, + { label: 'Complete', value: 'complete' }, + ]} + value={filters.status} + /> + + Created by + + { + 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} + /> +
+
+
() as LemonTableColumn, createdAtColumn() as LemonTableColumn, @@ -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?" /> - )} - - )} + + )} +
) } diff --git a/frontend/src/scenes/surveys/constants.ts b/frontend/src/scenes/surveys/constants.ts new file mode 100644 index 0000000000000..fa14c9310288c --- /dev/null +++ b/frontend/src/scenes/surveys/constants.ts @@ -0,0 +1,137 @@ +import { FeatureFlagFilters, Survey, SurveyQuestionType, SurveyType } from '~/types' + +export const SURVEY_EVENT_NAME = 'survey sent' +export const SURVEY_RESPONSE_PROPERTY = '$survey_response' + +export const SurveyQuestionLabel = { + [SurveyQuestionType.Open]: 'Open text', + [SurveyQuestionType.Rating]: 'Rating', + [SurveyQuestionType.Link]: 'Link', + [SurveyQuestionType.SingleChoice]: 'Single choice select', + [SurveyQuestionType.MultipleChoice]: 'Multiple choice select', +} + +export const defaultSurveyAppearance = { + backgroundColor: 'white', + textColor: 'black', + submitButtonText: 'Submit', + submitButtonColor: '#2c2c2c', + ratingButtonColor: '#e0e2e8', + descriptionTextColor: '#4b4b52', + whiteLabel: false, + displayThankYouMessage: true, + placeholder: '', + position: 'right', + thankYouMessageHeader: 'Thank you for your feedback!', +} + +export const defaultSurveyFieldValues = { + [SurveyQuestionType.Open]: { + questions: [ + { + question: 'Give us feedback on our product!', + description: '', + }, + ], + appearance: { + submitButtonText: 'Submit', + thankYouMessageHeader: 'Thank you for your feedback!', + }, + }, + [SurveyQuestionType.Link]: { + questions: [ + { + question: 'Do you want to join our upcoming webinar?', + description: '', + }, + ], + appearance: { + submitButtonText: 'Register', + thankYouMessageHeader: 'Redirecting ...', + }, + }, + [SurveyQuestionType.Rating]: { + questions: [ + { + question: 'How likely are you to recommend us to a friend?', + description: '', + display: 'number', + scale: 10, + lowerBoundLabel: 'Unlikely', + upperBoundLabel: 'Very likely', + }, + ], + appearance: { + thankYouMessageHeader: 'Thank you for your feedback!', + }, + }, + [SurveyQuestionType.SingleChoice]: { + questions: [ + { + question: 'Have you found this tutorial useful?', + description: '', + choices: ['Yes', 'No'], + }, + ], + appearance: { + submitButtonText: 'Submit', + thankYouMessageHeader: 'Thank you for your feedback!', + }, + }, + [SurveyQuestionType.MultipleChoice]: { + questions: [ + { + question: 'Which types of content would you like to see more of?', + description: '', + choices: ['Tutorials', 'Customer case studies', 'Product announcements'], + }, + ], + appearance: { + submitButtonText: 'Submit', + thankYouMessageHeader: 'Thank you for your feedback!', + }, + }, +} + +export interface NewSurvey + extends Pick< + Survey, + | 'name' + | 'description' + | 'type' + | 'conditions' + | 'questions' + | 'start_date' + | 'end_date' + | 'linked_flag' + | 'targeting_flag' + | 'archived' + | 'appearance' + > { + id: 'new' + linked_flag_id: number | undefined + targeting_flag_filters: Pick | undefined +} + +export const NEW_SURVEY: NewSurvey = { + id: 'new', + name: '', + description: '', + questions: [ + { + type: SurveyQuestionType.Open, + question: defaultSurveyFieldValues[SurveyQuestionType.Open].questions[0].question, + description: defaultSurveyFieldValues[SurveyQuestionType.Open].questions[0].description, + }, + ], + type: SurveyType.Popover, + linked_flag_id: undefined, + targeting_flag_filters: undefined, + linked_flag: null, + targeting_flag: null, + start_date: null, + end_date: null, + conditions: null, + archived: false, + appearance: defaultSurveyAppearance, +} diff --git a/frontend/src/scenes/surveys/surveyLogic.tsx b/frontend/src/scenes/surveys/surveyLogic.tsx index b881f1e57b1ef..65e93c8c31956 100644 --- a/frontend/src/scenes/surveys/surveyLogic.tsx +++ b/frontend/src/scenes/surveys/surveyLogic.tsx @@ -8,7 +8,6 @@ import { urls } from 'scenes/urls' import { Breadcrumb, ChartDisplayType, - FeatureFlagFilters, PluginType, PropertyFilterType, PropertyOperator, @@ -26,135 +25,13 @@ import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' import { featureFlagLogic as enabledFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' - -export interface NewSurvey - extends Pick< - Survey, - | 'name' - | 'description' - | 'type' - | 'conditions' - | 'questions' - | 'start_date' - | 'end_date' - | 'linked_flag' - | 'targeting_flag' - | 'archived' - | 'appearance' - > { - id: 'new' - linked_flag_id: number | undefined - targeting_flag_filters: Pick | undefined -} - -export const defaultSurveyAppearance = { - backgroundColor: '#eeeded', - submitButtonText: 'Submit', - submitButtonColor: 'black', - ratingButtonColor: 'white', - ratingButtonActiveColor: 'black', - borderColor: '#c9c6c6', - placeholder: '', - whiteLabel: false, - displayThankYouMessage: true, - thankYouMessageHeader: 'Thank you for your feedback!', - position: 'right', -} - -export const defaultSurveyFieldValues = { - [SurveyQuestionType.Open]: { - questions: [ - { - question: 'Give us feedback on our product!', - description: '', - }, - ], - appearance: { - submitButtonText: 'Submit', - thankYouMessageHeader: 'Thank you for your feedback!', - }, - }, - [SurveyQuestionType.Link]: { - questions: [ - { - question: 'Do you want to join our upcoming webinar?', - description: '', - }, - ], - appearance: { - submitButtonText: 'Register', - thankYouMessageHeader: 'Redirecting ...', - }, - }, - [SurveyQuestionType.Rating]: { - questions: [ - { - question: 'How likely are you to recommend us to a friend?', - description: '', - display: 'number', - scale: 10, - lowerBoundLabel: 'Unlikely', - upperBoundLabel: 'Very likely', - }, - ], - appearance: { - thankYouMessageHeader: 'Thank you for your feedback!', - }, - }, - [SurveyQuestionType.SingleChoice]: { - questions: [ - { - question: 'Have you found this tutorial useful?', - description: '', - choices: ['Yes', 'No'], - }, - ], - appearance: { - submitButtonText: 'Submit', - thankYouMessageHeader: 'Thank you for your feedback!', - }, - }, - [SurveyQuestionType.MultipleChoice]: { - questions: [ - { - question: 'Which types of content would you like to see more of?', - description: '', - choices: ['Tutorials', 'Customer case studies', 'Product announcements'], - }, - ], - appearance: { - submitButtonText: 'Submit', - thankYouMessageHeader: 'Thank you for your feedback!', - }, - }, -} - -export const NEW_SURVEY: NewSurvey = { - id: 'new', - name: '', - description: '', - questions: [ - { - type: SurveyQuestionType.Open, - question: defaultSurveyFieldValues[SurveyQuestionType.Open].questions[0].question, - description: defaultSurveyFieldValues[SurveyQuestionType.Open].questions[0].description, - }, - ], - type: SurveyType.Popover, - linked_flag_id: undefined, - targeting_flag_filters: undefined, - linked_flag: null, - targeting_flag: null, - start_date: null, - end_date: null, - conditions: null, - archived: false, - appearance: defaultSurveyAppearance, -} - -export const surveyEventName = 'survey sent' - -const SURVEY_RESPONSE_PROPERTY = '$survey_response' +import { + defaultSurveyFieldValues, + SURVEY_EVENT_NAME, + SURVEY_RESPONSE_PROPERTY, + NEW_SURVEY, + NewSurvey, +} from './constants' export interface SurveyLogicProps { id: string | 'new' @@ -443,7 +320,7 @@ export const surveyLogic = kea([ value: survey.id, }, ], - series: [{ event: surveyEventName, kind: NodeKind.EventsNode }], + series: [{ event: SURVEY_EVENT_NAME, kind: NodeKind.EventsNode }], trendsFilter: { display: ChartDisplayType.ActionsBarValue }, breakdown: { breakdown: '$survey_response', breakdown_type: 'event' }, }, diff --git a/frontend/src/scenes/surveys/surveysLogic.tsx b/frontend/src/scenes/surveys/surveysLogic.tsx index 0fc81064f9619..fa25749343280 100644 --- a/frontend/src/scenes/surveys/surveysLogic.tsx +++ b/frontend/src/scenes/surveys/surveysLogic.tsx @@ -1,6 +1,7 @@ -import { afterMount, connect, kea, listeners, path, selectors } from 'kea' +import { afterMount, connect, kea, listeners, path, selectors, actions, reducers } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' +import Fuse from 'fuse.js' import { AvailableFeature, Breadcrumb, ProgressStatus, Survey } from '~/types' import { urls } from 'scenes/urls' @@ -9,6 +10,7 @@ import { lemonToast } from '@posthog/lemon-ui' import { userLogic } from 'scenes/userLogic' import { router } from 'kea-router' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { LemonSelectOption } from 'lib/lemon-ui/LemonSelect' export function getSurveyStatus(survey: Survey): ProgressStatus { if (!survey.start_date) { @@ -19,11 +21,25 @@ export function getSurveyStatus(survey: Survey): ProgressStatus { return ProgressStatus.Complete } +export interface SurveysFilters { + status: string + created_by: string + archived: boolean +} + +interface SurveysCreators { + [id: string]: string +} + export const surveysLogic = kea([ path(['scenes', 'surveys', 'surveysLogic']), connect(() => ({ values: [pluginsLogic, ['loading as pluginsLoading', 'enabledPlugins'], userLogic, ['user']], })), + actions({ + setSearchTerm: (searchTerm: string) => ({ searchTerm }), + setSurveysFilters: (filters: Partial, replace?: boolean) => ({ filters, replace }), + }), loaders(({ values }) => ({ surveys: { __default: [] as Survey[], @@ -48,7 +64,24 @@ export const surveysLogic = kea([ }, }, })), - listeners(() => ({ + reducers({ + searchTerm: { + setSearchTerm: (_, { searchTerm }) => searchTerm, + }, + filters: [ + { + archived: false, + status: 'any', + created_by: 'any', + } as Partial, + { + setSurveysFilters: (state, { filters }) => { + return { ...state, ...filters } + }, + }, + ], + }), + listeners(({ actions }) => ({ deleteSurveySuccess: () => { lemonToast.success('Survey deleted') router.actions.push(urls.surveys()) @@ -56,8 +89,49 @@ export const surveysLogic = kea([ updateSurveySuccess: () => { lemonToast.success('Survey updated') }, + setSurveysFilters: () => { + actions.loadSurveys() + actions.loadResponsesCount() + }, })), selectors({ + searchedSurveys: [ + (selectors) => [selectors.surveys, selectors.searchTerm, selectors.filters], + (surveys, searchTerm, filters) => { + let searchedSurveys = surveys + + if (!searchTerm && Object.keys(filters).length === 0) { + return searchedSurveys + } + + if (searchTerm) { + searchedSurveys = new Fuse(searchedSurveys, { + keys: ['key', 'name'], + threshold: 0.3, + }) + .search(searchTerm) + .map((result) => result.item) + } + + const { status, created_by, archived } = filters + if (status !== 'any') { + searchedSurveys = searchedSurveys.filter((survey) => getSurveyStatus(survey) === status) + } + if (created_by !== 'any') { + searchedSurveys = searchedSurveys.filter( + (survey) => survey.created_by?.id === (created_by ? parseInt(created_by) : '') + ) + } + + if (archived) { + searchedSurveys = searchedSurveys.filter((survey) => survey.archived) + } else { + searchedSurveys = searchedSurveys.filter((survey) => !survey.archived) + } + + return searchedSurveys + }, + ], breadcrumbs: [ () => [], (): Breadcrumb[] => [ @@ -67,13 +141,23 @@ export const surveysLogic = kea([ }, ], ], - nonArchivedSurveys: [ - (s) => [s.surveys], - (surveys: Survey[]): Survey[] => surveys.filter((survey) => !survey.archived), - ], - archivedSurveys: [ - (s) => [s.surveys], - (surveys: Survey[]): Survey[] => surveys.filter((survey) => survey.archived), + uniqueCreators: [ + (selectors) => [selectors.surveys], + (surveys) => { + const creators: SurveysCreators = {} + for (const survey of surveys) { + if (survey.created_by) { + if (!creators[survey.created_by.id]) { + creators[survey.created_by.id] = survey.created_by.first_name + } + } + } + const response: LemonSelectOption[] = [ + { label: 'Any user', value: 'any' }, + ...Object.entries(creators).map(([id, first_name]) => ({ label: first_name, value: id })), + ] + return response + }, ], whitelabelAvailable: [ (s) => [s.user], @@ -86,8 +170,8 @@ export const surveysLogic = kea([ }, ], }), - afterMount(async ({ actions }) => { - await actions.loadSurveys() - await actions.loadResponsesCount() + afterMount(({ actions }) => { + actions.loadSurveys() + actions.loadResponsesCount() }), ])