update pmf survey dashboard with quick optimizations

This commit is contained in:
Matthias Nannt
2023-02-14 10:53:57 +01:00
parent 8a02ebc9db
commit afdc4f1ee9
15 changed files with 354 additions and 241 deletions
@@ -157,16 +157,16 @@ export default function NewFormModal({ open, setOpen, organisationId }: FormOnbo
],
},
{
id: "userSegmentPage",
id: "rolePage",
config: {
autoSubmit: true,
},
elements: [
{
id: "userSegment",
id: "role",
type: "radio",
name: "userSegment",
label: "What is your job title?",
name: "role",
label: "What is your role?",
options: [
{ label: "Founder", value: "founder" },
{ label: "Executive", value: "executive" },
@@ -189,12 +189,12 @@ export default function NewFormModal({ open, setOpen, organisationId }: FormOnbo
],
},
{
id: "selfSegmentationPage",
id: "benefitingUsers",
elements: [
{
id: "selfSegmentation",
id: "benefitingUsers",
type: "text",
name: "selfSegmentation",
name: "benefitingUsers",
label: "What type of people would benefit most from using our service?",
},
],
@@ -1,13 +1,15 @@
"use client";
import EmptyPageFiller from "@/components/EmptyPageFiller";
import LoadingSpinner from "@/components/LoadingSpinner";
import { useForm } from "@/lib/forms";
import { getOptionLabelMap, useSubmissions } from "@/lib/submissions";
import { Pie } from "@formbricks/charts";
import { NotDisappointedIcon, SomewhatDisappointedIcon, VeryDisappointedIcon } from "@formbricks/ui";
import { InboxIcon } from "@heroicons/react/24/outline";
import { useRouter } from "next/router";
import { useMemo, useState } from "react";
import FilterNavigation from "../shared/FilterNavigation";
import { SubmissionCounter } from "../shared/SubmissionCounter";
export default function OverviewResults() {
const router = useRouter();
@@ -50,7 +52,7 @@ export default function OverviewResults() {
},
{
label: "What type of people would benefit most from using our service?",
name: "selfSegmentation",
name: "benefitingUsers",
},
];
@@ -59,60 +61,84 @@ export default function OverviewResults() {
<div>
<section aria-labelledby="filters" className="max-w-8xl mx-auto py-8 pt-6 pb-24 ">
<div className="grid grid-cols-1 gap-x-16 gap-y-10 lg:grid-cols-4">
<FilterNavigation submissions={submissions} setFilteredSubmissions={setFilteredSubmissions} />
<div>
<SubmissionCounter
numFilteredSubmissions={filteredSubmissions.length}
numTotalSubmissions={submissions.length}
/>
<FilterNavigation submissions={submissions} setFilteredSubmissions={setFilteredSubmissions} />
</div>
{/* Submission grid */}
<div className="max-w-7xl lg:col-span-3">
<div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Overall</h3>
<h3 className="text-xs font-light text-slate-800">({submissions.length} submissions)</h3>
<Pie submissions={submissions} schema={form.schema} fieldName={"disappointment"} />
</div>
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Selected Segment</h3>
<h3 className="text-xs font-light text-slate-800">
({filteredSubmissions.length} submissions)
</h3>
<Pie submissions={filteredSubmissions} schema={form.schema} fieldName={"disappointment"} />
</div>
</div>
{questions.map((question) => (
<div key={question.name} className="my-4 rounded-lg bg-white">
<div className="rounded-t-lg bg-slate-100 p-4 text-lg font-bold text-slate-800">
{question.label}
{submissions.length === 0 ? (
<EmptyPageFiller
alertText="You haven't received any submissions yet."
hintText="Embed the PMF survey on your website to start gathering insights."
borderStyles="border-4 border-dotted border-red">
<InboxIcon className="stroke-thin mx-auto h-24 w-24 text-slate-300" />
</EmptyPageFiller>
) : (
<>
<div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Disappointment Level</h3>
<Pie
submissions={filteredSubmissions}
schema={form.schema}
fieldName={"disappointment"}
/>
</div>
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Role</h3>
<Pie submissions={filteredSubmissions} schema={form.schema} fieldName={"role"} />
</div>
</div>
<div className="grid grid-cols-5 gap-2 border-t border-slate-200 bg-slate-100 px-4 py-2 text-sm font-semibold text-slate-500">
<div className="col-span-3">Response</div>
<div>Feeling</div>
<div>Segment</div>
</div>
{filteredSubmissions
.filter((s) => question.name in s.data)
.map((submission) => (
<div
key={submission.id}
className="grid grid-cols-5 gap-2 border-t border-slate-100 px-4 pt-2 pb-4 text-sm">
<div className="col-span-3">{submission.data[question.name]}</div>
<div>
{submission.data.disappointment === "veryDisappointed" ? (
<VeryDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : submission.data.disappointment === "notDisappointed" ? (
<NotDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : submission.data.disappointment === "somewhatDisappointed" ? (
<SomewhatDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : null}
</div>
<div>
<div className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs text-slate-600">
{labelMap[submission.data.userSegment]}
</div>
</div>
{questions.map((question) => (
<div key={question.name} className="my-4 rounded-lg bg-white">
<div className="rounded-t-lg bg-slate-100 p-4 text-lg font-bold text-slate-800">
{question.label}
</div>
))}
</div>
))}
<div className="grid grid-cols-5 gap-2 border-t border-slate-200 bg-slate-100 px-4 py-2 text-sm font-semibold text-slate-500">
<div className="col-span-3">Response</div>
<div>Disappointment Level</div>
<div>Job</div>
</div>
{console.log(JSON.stringify(question, null, 2))}
{filteredSubmissions
.filter((s) => question.name in s.data)
.map((submission) => (
<div
key={submission.id}
className="grid grid-cols-5 gap-2 border-t border-slate-100 px-4 pt-2 pb-4 text-sm">
<div className="col-span-3">{submission.data[question.name]}</div>
<div>
{submission.data.disappointment === "veryDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800">
very disappointed
</span>
) : submission.data.disappointment === "notDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800">
not disappointed
</span>
) : submission.data.disappointment === "somewhatDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-yellow-100 px-2.5 py-0.5 text-xs font-medium text-yellow-800">
somewhat disappointed
</span>
) : null}
</div>
<div>
<div className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs text-slate-600">
{labelMap[submission.data.role] || <NotProvided />}
</div>
</div>
</div>
))}
</div>
))}
</>
)}
</div>
</div>
</section>
@@ -120,3 +146,7 @@ export default function OverviewResults() {
</div>
);
}
function NotProvided() {
return <span className="text-slate-500">(not provided)</span>;
}
@@ -19,8 +19,8 @@ import SetupInstructions from "./SetupInstructions";
import SuperhumanApproach from "./SuperhumanApproach";
const tabs = [
{ name: "Results", icon: RectangleStackIcon },
{ name: "Overview", icon: ChartPieIcon },
{ name: "Results", icon: RectangleStackIcon },
{ name: "Superhuman Approach", icon: RocketLaunchIcon },
{ name: "Data Pipelines", icon: ShareIcon },
{ name: "Setup Instructions", icon: InformationCircleIcon },
@@ -28,7 +28,7 @@ const tabs = [
export default function PMFPage() {
const router = useRouter();
const [currentTab, setCurrentTab] = useState("Results");
const [currentTab, setCurrentTab] = useState("Overview");
const { form, isLoadingForm, isErrorForm } = useForm(
router.query.formId?.toString(),
router.query.organisationId?.toString()
@@ -7,6 +7,7 @@ import { InboxIcon } from "@heroicons/react/24/outline";
import { useRouter } from "next/router";
import { useState } from "react";
import FilterNavigation from "../shared/FilterNavigation";
import { SubmissionCounter } from "../shared/SubmissionCounter";
import PMFTimeline from "./PMFTimeline";
export default function PMFResults() {
@@ -34,7 +35,13 @@ export default function PMFResults() {
<div>
<section aria-labelledby="filters" className="max-w-8xl mx-auto py-8 pt-6 pb-24">
<div className="grid grid-cols-1 gap-x-16 gap-y-10 lg:grid-cols-4">
<FilterNavigation submissions={submissions} setFilteredSubmissions={setFilteredSubmissions} />
<div>
<SubmissionCounter
numFilteredSubmissions={filteredSubmissions.length}
numTotalSubmissions={submissions.length}
/>
<FilterNavigation submissions={submissions} setFilteredSubmissions={setFilteredSubmissions} />
</div>
{/* Submission grid */}
@@ -1,19 +1,21 @@
"use client";
import EmptyPageFiller from "@/components/EmptyPageFiller";
import LoadingSpinner from "@/components/LoadingSpinner";
import PMFThumb2 from "@/images/pmfthumb-2.webp";
import PMFThumb from "@/images/pmfthumb.webp";
import { useForm } from "@/lib/forms";
import { getOptionLabelMap, useSubmissions } from "@/lib/submissions";
import { Pie } from "@formbricks/charts";
import { NotDisappointedIcon, SomewhatDisappointedIcon, VeryDisappointedIcon } from "@formbricks/ui";
import { InboxIcon } from "@heroicons/react/24/outline";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { useMemo, useState } from "react";
import FilterNavigation from "../shared/FilterNavigation";
import { SubmissionCounter } from "../shared/SubmissionCounter";
const limitFields = ["userSegment"];
const limitFields = ["role"];
export default function SegmentResults() {
const router = useRouter();
@@ -54,16 +56,22 @@ export default function SegmentResults() {
return (
<div>
<div>
<section aria-labelledby="filters" className="max-w-8xl mx-auto py-8">
<section aria-labelledby="filters" className="max-w-8xl mx-auto py-8 pt-6">
<div className="grid grid-cols-1 gap-x-16 gap-y-10 lg:grid-cols-4">
<div>
{/* Segments */}
<div>
<SubmissionCounter
numFilteredSubmissions={filteredSubmissions.length}
numTotalSubmissions={submissions.length}
/>
<FilterNavigation
submissions={submissions}
setFilteredSubmissions={setFilteredSubmissions}
limitFields={limitFields}
/>
</div>
<FilterNavigation
submissions={submissions}
setFilteredSubmissions={setFilteredSubmissions}
limitFields={limitFields}
/>
<div className="mb-2 flex py-2 text-sm font-bold">
<h4 className="text-slate-600">Tutorials</h4>
</div>
@@ -96,140 +104,174 @@ export default function SegmentResults() {
{/* Double down on what they love*/}
<div className=" lg:col-span-3">
<div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Overall</h3>
<h3 className="text-xs font-light text-slate-800">({submissions.length} submissions)</h3>
<Pie submissions={submissions} schema={form.schema} fieldName={"disappointment"} />
</div>
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Selected Segment</h3>
<h3 className="text-xs font-light text-slate-800">
({filteredSubmissions.length} submissions)
</h3>
<Pie submissions={filteredSubmissions} schema={form.schema} fieldName={"disappointment"} />
</div>
</div>
<h2 className="mt-10 mb-4 text-2xl font-bold text-slate-500">Double down on what they love</h2>
<div className="rounded-md bg-teal-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<span className="inline-flex items-center rounded-full bg-teal-100 px-2.5 py-0.5 text-xs font-medium text-teal-800">
How it works
</span>
{submissions.length === 0 ? (
<EmptyPageFiller
alertText="You haven't received any submissions yet."
hintText="Embed the PMF survey on your website to start gathering insights."
borderStyles="border-4 border-dotted border-red">
<InboxIcon className="stroke-thin mx-auto h-24 w-24 text-slate-300" />
</EmptyPageFiller>
) : (
<>
<div className="grid grid-cols-1 gap-4 xl:grid-cols-2">
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">All</h3>
<h3 className="text-xs font-light text-slate-800">
({submissions.length} submissions)
</h3>
<Pie submissions={submissions} schema={form.schema} fieldName={"disappointment"} />
</div>
<div className="flex flex-col items-center justify-center rounded-lg bg-white p-2">
<h3 className="text-sm font-medium text-slate-800">Selected Role</h3>
<h3 className="text-xs font-light text-slate-800">
({filteredSubmissions.length} submissions)
</h3>
<Pie
submissions={filteredSubmissions}
schema={form.schema}
fieldName={"disappointment"}
/>
</div>
</div>
<div className="ml-3 flex-1 md:flex md:justify-between">
<p className="text-sm text-teal-700">
To protect the PMF score from eroding among already very satisfied users, you deepen the
value they experience. To do so, you build what they request in the following answers.
</p>
</div>
</div>
</div>
<div className="my-4 rounded-lg bg-white">
<div className="rounded-t-lg bg-slate-100 p-4 text-lg font-bold text-slate-700">
What is the main benefit you receive from our service?
</div>
<div className="grid grid-cols-5 gap-2 bg-slate-100 px-4 pb-2 text-sm font-semibold text-slate-500">
<div className="col-span-3">Response</div>
<div>Disappointment</div>
<div>Segment</div>
</div>
{lovers.length === 0 ? (
<div className="p-4">
<h3 className="text-center text-sm font-bold text-slate-400">
You dont have any submissions that fit this filter
</h3>
<p className="mt-1 text-center text-xs font-light text-slate-400">
Change your filters or come back when you have more submissions.
</p>
</div>
) : (
<>
{lovers.map((submission) => (
<div className="grid grid-cols-5 gap-2 px-4 pt-2 pb-4 text-sm">
<div className="col-span-3 text-slate-800">
{submission.data.mainBenefit || <NotProvided />}
</div>
<div>
{submission.data.disappointment === "veryDisappointed" ? (
<VeryDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : submission.data.disappointment === "notDisappointed" ? (
<NotDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : submission.data.disappointment === "somewhatDisappointed" ? (
<SomewhatDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : null}
</div>
<div>
<div className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs text-slate-600">
{labelMap[submission.data.userSegment] || <NotProvided />}
</div>
</div>
<h2 className="mt-10 mb-4 text-2xl font-bold text-slate-500">
Double down on what they love
</h2>
<div className="rounded-md bg-teal-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<span className="inline-flex items-center rounded-full bg-teal-100 px-2.5 py-0.5 text-xs font-medium text-teal-800">
How it works
</span>
</div>
))}
</>
)}
</div>
<h2 className="mt-10 mb-4 text-2xl font-bold text-slate-500">Fix whats holding them back</h2>
<div className="rounded-md bg-teal-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<span className="inline-flex items-center rounded-full bg-teal-100 px-2.5 py-0.5 text-xs font-medium text-teal-800">
How it works
</span>
</div>
<div className="ml-3 flex-1 md:flex md:justify-between">
<p className="text-sm text-teal-700">
To make more users very disappointed when your product were to go away, you build
whats holding the somewhat disappointed users back.
</p>
</div>
</div>
</div>
<div className="my-4 rounded-lg bg-white">
<div className="rounded-t-lg bg-slate-100 p-4 text-lg font-bold text-slate-700">
How can we improve our service for you?
</div>
<div className="grid grid-cols-5 gap-2 bg-slate-100 px-4 pb-2 text-sm font-semibold text-slate-500">
<div className="col-span-3">Response</div>
<div>Disappointment</div>
<div>Segment</div>
</div>
{improvers.length === 0 ? (
<div className="p-4">
<h3 className="text-center text-sm font-bold text-slate-400">
You dont have any submissions that fit this filter
</h3>
<p className="mt-1 text-center text-xs font-light text-slate-400">
Change your filters or come back when you have more submissions.
</p>
</div>
) : (
<>
{improvers.map((submission) => (
<div className="grid grid-cols-5 gap-2 px-4 pt-2 pb-4 text-sm">
<div className="col-span-3 text-slate-800">
{submission.data.improvement || <NotProvided />}
</div>
<div>
{submission.data.disappointment === "veryDisappointed" ? (
<VeryDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : submission.data.disappointment === "notDisappointed" ? (
<NotDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : submission.data.disappointment === "somewhatDisappointed" ? (
<SomewhatDisappointedIcon className="h-6 w-6 text-white" aria-hidden="true" />
) : null}
</div>
<div>
<div className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs text-slate-600">
{labelMap[submission.data.userSegment] || <NotProvided />}
</div>
</div>
<div className="ml-3 flex-1 md:flex md:justify-between">
<p className="text-sm text-teal-700">
To protect the PMF score from eroding among already very satisfied users, you deepen
the value they experience. To do so, you build what they request in the following
answers.
</p>
</div>
))}
</>
)}
</div>
</div>
</div>
<div className="my-4 rounded-lg bg-white">
<div className="rounded-t-lg bg-slate-100 p-4 text-lg font-bold text-slate-700">
What is the main benefit you receive from our service?
</div>
<div className="grid grid-cols-5 gap-2 bg-slate-100 px-4 pb-2 text-sm font-semibold text-slate-500">
<div className="col-span-3">Response</div>
<div>Disappointment Level</div>
<div>Role</div>
</div>
{lovers.length === 0 ? (
<div className="p-4">
<h3 className="text-center text-sm font-bold text-slate-400">
You dont have any submissions that fit this filter
</h3>
<p className="mt-1 text-center text-xs font-light text-slate-400">
Change your filters or come back when you have more submissions.
</p>
</div>
) : (
<>
{lovers.map((submission) => (
<div className="grid grid-cols-5 gap-2 px-4 pt-2 pb-4 text-sm">
<div className="col-span-3 text-slate-800">
{submission.data.mainBenefit || <NotProvided />}
</div>
<div>
{submission.data.disappointment === "veryDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800">
very disappointed
</span>
) : submission.data.disappointment === "notDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800">
not disappointed
</span>
) : submission.data.disappointment === "somewhatDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-yellow-100 px-2.5 py-0.5 text-xs font-medium text-yellow-800">
somewhat disappointed
</span>
) : null}
</div>
<div>
<div className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs text-slate-600">
{labelMap[submission.data.role] || <NotProvided />}
</div>
</div>
</div>
))}
</>
)}
</div>
<h2 className="mt-10 mb-4 text-2xl font-bold text-slate-500">
Fix whats holding them back
</h2>
<div className="rounded-md bg-teal-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
<span className="inline-flex items-center rounded-full bg-teal-100 px-2.5 py-0.5 text-xs font-medium text-teal-800">
How it works
</span>
</div>
<div className="ml-3 flex-1 md:flex md:justify-between">
<p className="text-sm text-teal-700">
To make more users very disappointed when your product were to go away, you build
whats holding the somewhat disappointed users back.
</p>
</div>
</div>
</div>
<div className="my-4 rounded-lg bg-white">
<div className="rounded-t-lg bg-slate-100 p-4 text-lg font-bold text-slate-700">
How can we improve our service for you?
</div>
<div className="grid grid-cols-5 gap-2 bg-slate-100 px-4 pb-2 text-sm font-semibold text-slate-500">
<div className="col-span-3">Response</div>
<div>Disappointment Level</div>
<div>Role</div>
</div>
{improvers.length === 0 ? (
<div className="p-4">
<h3 className="text-center text-sm font-bold text-slate-400">
You dont have any submissions that fit this filter
</h3>
<p className="mt-1 text-center text-xs font-light text-slate-400">
Change your filters or come back when you have more submissions.
</p>
</div>
) : (
<>
{improvers.map((submission) => (
<div className="grid grid-cols-5 gap-2 px-4 pt-2 pb-4 text-sm">
<div className="col-span-3 text-slate-800">
{submission.data.improvement || <NotProvided />}
</div>
<div>
{submission.data.disappointment === "veryDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800">
very disappointed
</span>
) : submission.data.disappointment === "notDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-red-100 px-2.5 py-0.5 text-xs font-medium text-red-800">
not disappointed
</span>
) : submission.data.disappointment === "somewhatDisappointed" ? (
<span className="inline-flex items-center rounded-full bg-yellow-100 px-2.5 py-0.5 text-xs font-medium text-yellow-800">
somewhat disappointed
</span>
) : null}
</div>
<div>
<div className="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs text-slate-600">
{labelMap[submission.data.role] || <NotProvided />}
</div>
</div>
</div>
))}
</>
)}
</div>
</>
)}
</div>
</div>
</section>
@@ -102,21 +102,39 @@ export default function FilterNavigation({
}
}, [filters, form, submissions, setFilteredSubmissions]);
const chooseOptionFilter = (filterName, optionValue) => {
const chooseOptionFilter = (filterName, optionValue, optionActive) => {
const newFilters = [...filters];
const filter = newFilters.find((filter) => filter.name === filterName);
if (filter) {
// reset all previous filter options
for (const option of filter.options) {
if (option.value === optionValue) {
option.active = true;
} else {
// activate option & deactivate all others
if (!optionActive) {
// reset all previous filter options
for (const option of filter.options) {
if (option.value === optionValue) {
option.active = true;
} else {
option.active = false;
}
// reset all pins when all is selected
if (optionValue === "all") {
option.pinned = false;
}
}
} else {
// deactivate option if already active
const option = filter.options.find((option) => option.value === optionValue);
if (option) {
option.active = false;
}
// reset all pins when all is selected
if (optionValue === "all") {
option.pinned = false;
// check if something is pinned
const pinnedOption = filter.options.find((option) => option.pinned);
if (!pinnedOption) {
// activate all option if noting is pinned
const option = filter.options.find((option) => option.value === "all");
if (option) {
option.active = true;
}
}
}
}
@@ -191,7 +209,7 @@ export default function FilterNavigation({
key={option.value}
type="button"
onClick={() => {
chooseOptionFilter(filter.name, option.value);
chooseOptionFilter(filter.name, option.value, option.active);
}}
className={clsx(
option.active || option.pinned
@@ -205,7 +223,10 @@ export default function FilterNavigation({
{!["all", "inbox", "archived"].includes(option.value) && (option.active || option.pinned) && (
<button
className="ml-auto"
onClick={() => pinOptionFilter(filter.name, option.value, !option.pinned)}>
onClick={(e) => {
e.stopPropagation();
pinOptionFilter(filter.name, option.value, !option.pinned);
}}>
{option.pinned ? (
<BsPinFill className="h-4 w-4 text-gray-400" />
) : (
@@ -0,0 +1,14 @@
export function SubmissionCounter({ numFilteredSubmissions, numTotalSubmissions }) {
return (
<div className="mb-4 rounded bg-white p-3">
<div className="inline-block text-base font-bold text-slate-600">
{numFilteredSubmissions} responses
</div>
{numFilteredSubmissions !== numTotalSubmissions && (
<div className="ml-1 inline-block text-sm font-medium text-slate-400">
(out of {numTotalSubmissions})
</div>
)}
</div>
);
}
@@ -19,9 +19,9 @@ const output = {
data: {
improvement: "Make it possible to add a note to a transaction",
mainBenefit: "The best is that I can get a quick overview of all my transactions",
userSegment: "founder",
role: "founder",
disappointment: "veryDisappointed",
selfSegmentation: "other founders",
benefitingUsers: "other founders",
},
meta: {
userAgent:
@@ -76,14 +76,14 @@ const output = {
],
},
{
id: "userSegmentPage",
id: "rolePage",
config: {
autoSubmit: true,
},
elements: [
{
id: "userSegment",
name: "userSegment",
id: "role",
name: "role",
type: "radio",
label: "What is your job title?",
options: [
@@ -123,11 +123,11 @@ const output = {
],
},
{
id: "selfSegmentationPage",
id: "benefitingUsersPage",
elements: [
{
id: "selfSegmentation",
name: "selfSegmentation",
id: "benefitingUsers",
name: "benefitingUsers",
type: "text",
label: "What type of people would benefit most from using our service?",
},
+6 -6
View File
@@ -47,14 +47,14 @@ const output = {
],
},
{
id: "userSegmentPage",
id: "rolePage",
config: {
autoSubmit: true,
},
elements: [
{
id: "userSegment",
name: "userSegment",
id: "role",
name: "role",
type: "radio",
label: "What is your job title?",
options: [
@@ -94,11 +94,11 @@ const output = {
],
},
{
id: "selfSegmentationPage",
id: "benefitingUsersPage",
elements: [
{
id: "selfSegmentation",
name: "selfSegmentation",
id: "benefitingUsers",
name: "benefitingUsers",
type: "text",
label: "What type of people would benefit most from using our service?",
},
+4 -4
View File
@@ -20,7 +20,7 @@ const mainBenefits = [
"The notifications when I receive money are great",
];
const userSegments = ["founder", "executive", "productManager", "productOwner", "softwareEngineer"];
const roles = ["founder", "executive", "productManager", "productOwner", "softwareEngineer"];
const disappointments = [
"veryDisappointed",
@@ -31,7 +31,7 @@ const disappointments = [
"notDisappointed",
];
const selfSegmentations = [
const benefitingUserss = [
"Founders",
"Executives",
"Product Managers",
@@ -65,9 +65,9 @@ export const getPmfSubmissions = () => {
data: {
improvement: getRandomItem(improvements),
mainBenefit: getRandomItem(mainBenefits),
userSegment: getRandomItem(userSegments),
role: getRandomItem(roles),
disappointment: getRandomItem(disappointments),
selfSegmentation: getRandomItem(selfSegmentations),
benefitingUsers: getRandomItem(benefitingUserss),
},
meta: {
userAgent:
+1 -2
View File
@@ -71,7 +71,6 @@ function FbPie({ color, submissions, schema, fieldName }: Props) {
return (
<>
<PieChart width={500} height={250}>
{/* <Pie dataKey="value" fill={color || "#00C4B8"} /> */}
<Pie
data={data}
dataKey="value"
@@ -84,7 +83,7 @@ function FbPie({ color, submissions, schema, fieldName }: Props) {
const x = cx + radius * Math.cos(-midAngle * RADIAN);
const y = cy + radius * Math.sin(-midAngle * RADIAN);
return (
return value === 0 ? null : (
<text
x={x}
y={y}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@formbricks/pmf",
"version": "0.1.0",
"version": "0.1.1",
"source": "src/index.ts",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
File diff suppressed because one or more lines are too long
+7 -7
View File
@@ -58,7 +58,7 @@
<div class="formbricks-radio-options">
<div class="formbricks-radio-option">
<input
data-element-name="userSegment"
data-element-name="role"
data-element-value="founder"
id="formbricksPmf-2-0"
name="formbricksPmf-2-0"
@@ -68,7 +68,7 @@
</div>
<div class="formbricks-radio-option">
<input
data-element-name="userSegment"
data-element-name="role"
data-element-value="executive"
id="formbricksPmf-2-1"
name="formbricksPmf-2-1"
@@ -78,7 +78,7 @@
</div>
<div class="formbricks-radio-option">
<input
data-element-name="userSegment"
data-element-name="role"
data-element-value="productManager"
id="formbricksPmf-2-2"
name="formbricksPmf-2-2"
@@ -88,7 +88,7 @@
</div>
<div class="formbricks-radio-option">
<input
data-element-name="userSegment"
data-element-name="role"
data-element-value="productOwner"
id="formbricksPmf-2-3"
name="formbricksPmf-2-3"
@@ -98,7 +98,7 @@
</div>
<div class="formbricks-radio-option">
<input
data-element-name="userSegment"
data-element-name="role"
data-element-value="softwareEngineer"
id="formbricksPmf-2-4"
name="formbricksPmf-2-4"
@@ -122,8 +122,8 @@
<div class="formbricks-element formbricks-hidden" id="formbricks-question-4">
<p class="formbricks-element-label">What type of people would benefit most from using our service?</p>
<form class="formbricks-form" data-element-name="selfSegmentation">
<textarea rows="4" name="selfSegmentation" class="formbricks-textarea" required autofocus></textarea>
<form class="formbricks-form" data-element-name="benefitingUsers">
<textarea rows="4" name="benefitingUsers" class="formbricks-textarea" required autofocus></textarea>
<div class="formbricks-next-button-wrapper">
<button type="submit" class="formbricks-next-button">Submit</button>
</div>
+1 -1
View File
@@ -132,7 +132,7 @@ async function submitElement(name?: string, value?: string) {
const response = await createSubmission(submission);
submissionId = response.id;
} else {
const finished = !!("selfSegmentation" in submission);
const finished = !!("benefitingUsers" in submission);
await updateSubmission(submissionId, submission, finished);
if (finished) {
config.onFinished();