From 21c79c9efb59ad8411f748ebd72123a2effa563d Mon Sep 17 00:00:00 2001 From: Matthias Nannt Date: Wed, 15 Feb 2023 21:26:54 +0100 Subject: [PATCH] update Pmf Superhuman view --- apps/web/src/components/LoadingSpinner.tsx | 6 +- .../forms/pmf/SuperhumanApproach.tsx | 154 +++++++++++++----- apps/web/src/lib/superhumanPmf.ts | 7 +- apps/web/src/lib/utils.ts | 1 + 4 files changed, 119 insertions(+), 49 deletions(-) diff --git a/apps/web/src/components/LoadingSpinner.tsx b/apps/web/src/components/LoadingSpinner.tsx index f29dbec739..1434738b1a 100644 --- a/apps/web/src/components/LoadingSpinner.tsx +++ b/apps/web/src/components/LoadingSpinner.tsx @@ -3,5 +3,9 @@ import { TailSpin } from "react-loader-spinner"; export default function LoadingSpinner() { - return ; + return ( +
+ +
+ ); } diff --git a/apps/web/src/components/forms/pmf/SuperhumanApproach.tsx b/apps/web/src/components/forms/pmf/SuperhumanApproach.tsx index 51947d48a1..8c2e5456fa 100644 --- a/apps/web/src/components/forms/pmf/SuperhumanApproach.tsx +++ b/apps/web/src/components/forms/pmf/SuperhumanApproach.tsx @@ -15,12 +15,16 @@ import { useRouter } from "next/router"; import { useMemo, useState } from "react"; import FilterNavigation from "../shared/FilterNavigation"; import { BrainIcon, Button } from "@formbricks/ui"; +import { findRolesWithHighestVeryDisappointedPercentage } from "@/lib/superhumanPmf"; +import { camelToTitle } from "@/lib/utils"; const limitFields = ["role"]; export default function SegmentResults() { const router = useRouter(); const [filteredSubmissions, setFilteredSubmissions] = useState([]); + const [loadingMainBenefit, setLoadingMainBenefit] = useState(false); + const [loadingNextSteps, setLoadingNextSteps] = useState(false); const { submissions, isLoadingSubmissions, isErrorSubmissions } = useSubmissions( router.query.organisationId?.toString(), router.query.formId?.toString() @@ -34,7 +38,6 @@ export default function SegmentResults() { filteredSubmissions.filter((s) => s.data.disappointment === "veryDisappointed" && s.data.mainBenefit), [filteredSubmissions] ); - const improvers = useMemo( () => filteredSubmissions.filter( @@ -49,6 +52,19 @@ export default function SegmentResults() { } }, [form]); + const mostDisappointedSegment = useMemo( + () => submissions && findRolesWithHighestVeryDisappointedPercentage(submissions, 1), + [submissions] + ); + + const mostDisappointedSegmentSubmissions = useMemo( + () => + submissions && mostDisappointedSegment + ? submissions.filter((s) => s.data.role === mostDisappointedSegment.bestRoleCombination[0]) + : [], + [submissions, mostDisappointedSegment] + ); + if (isLoadingSubmissions || isLoadingForm) return ; if (isErrorSubmissions || isErrorForm) @@ -107,23 +123,46 @@ export default function SegmentResults() { happiest users are most likely to be long-term customers:

+ {mostDisappointedSegment && mostDisappointedSegment.bestPercentage > 0 && ( +
+
+ +

Most disappointed segment (AI-powered)

+
+

+ The most disappointed segment is " + {camelToTitle(mostDisappointedSegment.bestRoleCombination[0])}" with{" "} + {Math.round(mostDisappointedSegment.bestPercentage * 100)}% of users answering + "very disappointed". +

+
+ )} +
-

All

-

- ({submissions.length} submissions) -

- -
-
-

Most disappointed segment

+

Current Selection

({filteredSubmissions.length} submissions)

+
+
+

+ {camelToTitle(mostDisappointedSegment.bestRoleCombination[0])} +

+

+ ({mostDisappointedSegmentSubmissions.length} submissions) +

+
@@ -143,22 +182,36 @@ export default function SegmentResults() { (like it happened to Mixpanel).

-
-
- -

Main Benefit Summary (AI-powered)

+ + {form.id === "demo-pmf" && ( +
+ {loadingMainBenefit ? ( + + ) : ( + <> +
+ +

Main Benefit Summary (AI-powered)

+
+

+ The main benefit that can be extracted from the list is "Efficiently track + and manage expenses in one place for better financial control." +

+
+ +
+ + )}
-

- The best is that I can get a quick overview of all my transactions. The best is that I - can get a quick overview of all my transactions. The best is that I can get a quick - overview of all my transactions. -

-
- -
-
+ )}
@@ -226,25 +279,40 @@ export default function SegmentResults() { disappointed' when they could no longer use your product. This helps you craft a product for a wider audience.

-
-
- -

Next Action Steps (AI-powered)

+ + {form.id === "demo-pmf" && ( +
+ {loadingNextSteps ? ( + + ) : ( + <> +
+ +

Next Action Steps (AI-powered)

+
+

+ The top three improvements requested are to provide a mobile app, enable + archiving of old transactions, and allow notes to be added to transactions. + Other requested improvements include customization options, dark mode, credit + score tracking, analytics, educational resources, performance optimization, and + improved security and accessibility features. +

+
+ +
+ + )}
-

- Based on the submissions below, we suggest targeting these three aspects first: -

    -
  • Fix this quick and easy
  • -
  • Fix this quick and easy
  • -
  • Fix this quick and easy
  • -
-

-
- -
-
+ )} +
How can we improve our service for you? diff --git a/apps/web/src/lib/superhumanPmf.ts b/apps/web/src/lib/superhumanPmf.ts index e8f7568013..c882ff095f 100644 --- a/apps/web/src/lib/superhumanPmf.ts +++ b/apps/web/src/lib/superhumanPmf.ts @@ -10,10 +10,7 @@ interface RoleCounts { [role: string]: number; } -export function findRolesWithHighestVeryDisappointedPercentage( - submissions: FormSubmission[], - n: number -): string[] { +export function findRolesWithHighestVeryDisappointedPercentage(submissions: FormSubmission[], n: number) { const roleCounts: RoleCounts = {}; const submissionCounts: RoleCounts = {}; @@ -57,7 +54,7 @@ export function findRolesWithHighestVeryDisappointedPercentage( }); } - return bestRoleCombination; + return { bestRoleCombination, bestPercentage }; } function generateCombinations(elements: T[], combinationLength: number): T[][] { diff --git a/apps/web/src/lib/utils.ts b/apps/web/src/lib/utils.ts index 88fcee06ba..3d70a249cc 100644 --- a/apps/web/src/lib/utils.ts +++ b/apps/web/src/lib/utils.ts @@ -110,6 +110,7 @@ export const capitalizeFirstLetter = (string) => { // camel case to title case export const camelToTitle = (string) => { + if (!string) return ""; return string .replace(/([A-Z])/g, " $1") .replace(/^./, function (str) {