mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-23 22:18:53 -05:00
feat: add prolific info and docs page (#2853)
This commit is contained in:
+34
-79
@@ -1,18 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowLeftIcon, BellRing, BlocksIcon, Code2Icon, LinkIcon, MailIcon } from "lucide-react";
|
||||
import { BellRing, BlocksIcon, Code2Icon, LinkIcon, MailIcon, UsersRound } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Badge } from "@formbricks/ui/Badge";
|
||||
import { Dialog, DialogContent } from "@formbricks/ui/Dialog";
|
||||
import { ShareSurveyLink } from "@formbricks/ui/ShareSurveyLink";
|
||||
import { EmailTab } from "./shareEmbedTabs/EmailTab";
|
||||
import { LinkTab } from "./shareEmbedTabs/LinkTab";
|
||||
import { WebpageTab } from "./shareEmbedTabs/WebpageTab";
|
||||
import { EmbedView } from "./shareEmbedModal/EmbedView";
|
||||
import { PanelInfoView } from "./shareEmbedModal/PanelInfoView";
|
||||
|
||||
interface ShareEmbedSurveyProps {
|
||||
survey: TSurvey;
|
||||
@@ -21,6 +19,7 @@ interface ShareEmbedSurveyProps {
|
||||
webAppUrl: string;
|
||||
user: TUser;
|
||||
}
|
||||
|
||||
export const ShareEmbedSurvey = ({ survey, open, setOpen, webAppUrl, user }: ShareEmbedSurveyProps) => {
|
||||
const router = useRouter();
|
||||
const environmentId = survey.environmentId;
|
||||
@@ -34,26 +33,26 @@ export const ShareEmbedSurvey = ({ survey, open, setOpen, webAppUrl, user }: Sha
|
||||
];
|
||||
|
||||
const [activeId, setActiveId] = useState(tabs[0].id);
|
||||
const [showInitialPage, setShowInitialPage] = useState(true);
|
||||
const [showView, setShowView] = useState("start");
|
||||
const [surveyUrl, setSurveyUrl] = useState("");
|
||||
|
||||
const handleOpenChange = (open: boolean) => {
|
||||
setActiveId(tabs[0].id);
|
||||
setOpen(open);
|
||||
setShowInitialPage(open); // Reset to initial page when modal opens
|
||||
setShowView(open ? "start" : ""); // Reset to initial page when modal opens or closes
|
||||
|
||||
// fetch latest responses
|
||||
router.refresh();
|
||||
};
|
||||
|
||||
const handleInitialPageButton = () => {
|
||||
setShowInitialPage(!showInitialPage);
|
||||
setShowView("start");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="w-full max-w-xl bg-white p-0 md:max-w-3xl lg:h-[700px] lg:max-w-5xl">
|
||||
{showInitialPage ? (
|
||||
{showView === "start" ? (
|
||||
<div className="h-full max-w-full overflow-hidden">
|
||||
<div className="flex h-[200px] w-full flex-col items-center justify-center space-y-6 p-8 text-center lg:h-2/5">
|
||||
<p className="pt-2 text-xl font-semibold text-slate-800">Your survey is public 🎉</p>
|
||||
@@ -66,10 +65,10 @@ export const ShareEmbedSurvey = ({ survey, open, setOpen, webAppUrl, user }: Sha
|
||||
</div>
|
||||
<div className="flex h-[300px] flex-col items-center justify-center gap-8 rounded-b-lg bg-slate-50 px-8 lg:h-3/5">
|
||||
<p className="-mt-8 text-sm text-slate-500">What's next?</p>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<div className="grid grid-cols-4 gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleInitialPageButton}
|
||||
onClick={() => setShowView("embed")}
|
||||
className="flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-sm text-slate-500 hover:border-slate-200 md:p-8">
|
||||
<Code2Icon className="h-6 w-6 text-slate-700" />
|
||||
Embed survey
|
||||
@@ -86,76 +85,32 @@ export const ShareEmbedSurvey = ({ survey, open, setOpen, webAppUrl, user }: Sha
|
||||
<BlocksIcon className="h-6 w-6 text-slate-700" />
|
||||
Setup integrations
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowView("panel")}
|
||||
className="relative flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-sm text-slate-500 hover:border-slate-200 md:p-8">
|
||||
<UsersRound className="h-6 w-6 text-slate-700" />
|
||||
Send to panel
|
||||
<Badge size="tiny" type="success" text="New" className="absolute right-3 top-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-full overflow-hidden">
|
||||
<div className="border-b border-slate-200 py-2">
|
||||
<Button
|
||||
variant="minimal"
|
||||
className="focus:ring-0"
|
||||
onClick={handleInitialPageButton}
|
||||
StartIcon={ArrowLeftIcon}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid h-full grid-cols-4">
|
||||
<div className="col-span-1 hidden flex-col gap-3 border-r border-slate-200 p-4 lg:flex">
|
||||
{tabs.map((tab) => (
|
||||
<Button
|
||||
StartIcon={tab.icon}
|
||||
startIconClassName="h-4 w-4"
|
||||
variant="minimal"
|
||||
key={tab.id}
|
||||
onClick={() => setActiveId(tab.id)}
|
||||
className={cn(
|
||||
"rounded-md border px-4 py-2 text-slate-600",
|
||||
// "focus:ring-0 focus:ring-offset-0", // enable these classes to remove the focus rings on buttons
|
||||
tab.id === activeId
|
||||
? "border-slate-200 bg-slate-100 font-semibold text-slate-900"
|
||||
: "border-transparent text-slate-500 hover:text-slate-700"
|
||||
)}
|
||||
aria-current={tab.id === activeId ? "page" : undefined}>
|
||||
{tab.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="col-span-4 h-full overflow-y-auto bg-slate-50 px-4 py-6 lg:col-span-3 lg:p-6">
|
||||
<div>
|
||||
{activeId === "email" ? (
|
||||
<EmailTab surveyId={survey.id} email={email} />
|
||||
) : activeId === "webpage" ? (
|
||||
<WebpageTab surveyUrl={surveyUrl} />
|
||||
) : activeId === "link" ? (
|
||||
<LinkTab
|
||||
survey={survey}
|
||||
webAppUrl={webAppUrl}
|
||||
surveyUrl={surveyUrl}
|
||||
setSurveyUrl={setSurveyUrl}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-2 rounded-md p-3 text-center lg:hidden">
|
||||
{tabs.slice(0, 2).map((tab) => (
|
||||
<Button
|
||||
variant="minimal"
|
||||
key={tab.id}
|
||||
onClick={() => setActiveId(tab.id)}
|
||||
className={cn(
|
||||
"rounded-md px-4 py-2",
|
||||
tab.id === activeId
|
||||
? "bg-white text-slate-900 shadow-sm"
|
||||
: "border-transparent text-slate-700 hover:text-slate-900"
|
||||
)}>
|
||||
{tab.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
) : showView === "embed" ? (
|
||||
<EmbedView
|
||||
handleInitialPageButton={handleInitialPageButton}
|
||||
tabs={tabs}
|
||||
activeId={activeId}
|
||||
setActiveId={setActiveId}
|
||||
survey={survey}
|
||||
email={email}
|
||||
surveyUrl={surveyUrl}
|
||||
setSurveyUrl={setSurveyUrl}
|
||||
webAppUrl={webAppUrl}
|
||||
/>
|
||||
) : showView === "panel" ? (
|
||||
<PanelInfoView handleInitialPageButton={handleInitialPageButton} />
|
||||
) : null}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowLeftIcon } from "lucide-react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { EmailTab } from "./EmailTab";
|
||||
import { LinkTab } from "./LinkTab";
|
||||
import { WebpageTab } from "./WebpageTab";
|
||||
|
||||
interface EmbedViewProps {
|
||||
handleInitialPageButton: () => void;
|
||||
tabs: Array<{ id: string; label: string; icon: any }>;
|
||||
activeId: string;
|
||||
setActiveId: React.Dispatch<React.SetStateAction<string>>;
|
||||
survey: any;
|
||||
email: string;
|
||||
surveyUrl: string;
|
||||
setSurveyUrl: React.Dispatch<React.SetStateAction<string>>;
|
||||
webAppUrl: string;
|
||||
}
|
||||
|
||||
export const EmbedView = ({
|
||||
handleInitialPageButton,
|
||||
tabs,
|
||||
activeId,
|
||||
setActiveId,
|
||||
survey,
|
||||
email,
|
||||
surveyUrl,
|
||||
setSurveyUrl,
|
||||
webAppUrl,
|
||||
}: EmbedViewProps) => {
|
||||
return (
|
||||
<div className="h-full overflow-hidden">
|
||||
<div className="border-b border-slate-200 py-2">
|
||||
<Button
|
||||
variant="minimal"
|
||||
className="focus:ring-0"
|
||||
onClick={handleInitialPageButton}
|
||||
StartIcon={ArrowLeftIcon}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid h-full grid-cols-4">
|
||||
<div className="col-span-1 hidden flex-col gap-3 border-r border-slate-200 p-4 lg:flex">
|
||||
{tabs.map((tab) => (
|
||||
<Button
|
||||
StartIcon={tab.icon}
|
||||
startIconClassName="h-4 w-4"
|
||||
variant="minimal"
|
||||
key={tab.id}
|
||||
onClick={() => setActiveId(tab.id)}
|
||||
className={cn(
|
||||
"rounded-md border px-4 py-2 text-slate-600",
|
||||
// "focus:ring-0 focus:ring-offset-0", // enable these classes to remove the focus rings on buttons
|
||||
tab.id === activeId
|
||||
? "border-slate-200 bg-slate-100 font-semibold text-slate-900"
|
||||
: "border-transparent text-slate-500 hover:text-slate-700"
|
||||
)}
|
||||
aria-current={tab.id === activeId ? "page" : undefined}>
|
||||
{tab.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
<div className="col-span-4 h-full overflow-y-auto bg-slate-50 px-4 py-6 lg:col-span-3 lg:p-6">
|
||||
<div>
|
||||
{activeId === "email" ? (
|
||||
<EmailTab surveyId={survey.id} email={email} />
|
||||
) : activeId === "webpage" ? (
|
||||
<WebpageTab surveyUrl={surveyUrl} />
|
||||
) : activeId === "link" ? (
|
||||
<LinkTab
|
||||
survey={survey}
|
||||
webAppUrl={webAppUrl}
|
||||
surveyUrl={surveyUrl}
|
||||
setSurveyUrl={setSurveyUrl}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-2 rounded-md p-3 text-center lg:hidden">
|
||||
{tabs.slice(0, 2).map((tab) => (
|
||||
<Button
|
||||
variant="minimal"
|
||||
key={tab.id}
|
||||
onClick={() => setActiveId(tab.id)}
|
||||
className={cn(
|
||||
"rounded-md px-4 py-2",
|
||||
tab.id === activeId
|
||||
? "bg-white text-slate-900 shadow-sm"
|
||||
: "border-transparent text-slate-700 hover:text-slate-900"
|
||||
)}>
|
||||
{tab.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
"use client";
|
||||
|
||||
import ProlificLogo from "@/images/prolific-logo.webp";
|
||||
import ProlificUI from "@/images/prolific-screenshot.webp";
|
||||
import { ArrowLeftIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
|
||||
interface PanelInfoViewProps {
|
||||
handleInitialPageButton: () => void;
|
||||
}
|
||||
|
||||
export const PanelInfoView = ({ handleInitialPageButton }: PanelInfoViewProps) => {
|
||||
return (
|
||||
<div className="h-full overflow-hidden text-slate-900">
|
||||
<div className="border-b border-slate-200 py-2">
|
||||
<Button
|
||||
variant="minimal"
|
||||
className="focus:ring-0"
|
||||
onClick={handleInitialPageButton}
|
||||
StartIcon={ArrowLeftIcon}>
|
||||
Back
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid h-full grid-cols-2">
|
||||
<div className="flex flex-col gap-y-6 border-r border-slate-200 p-8">
|
||||
<Image src={ProlificUI} alt="Prolific panel selection UI" className="rounded-lg shadow-lg" />
|
||||
<div>
|
||||
<p className="text-md font-semibold">What is a panel?</p>
|
||||
<p className="text-slate-600">
|
||||
A panel is a group of participants selected based on characteristics such as age, profession,
|
||||
gender, etc.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-md font-semibold">When do I need it?</p>
|
||||
<p className="text-slate-600">
|
||||
If you don’t have access to enough people who match your target audience, it makes sense to pay
|
||||
for access to a panel.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-md font-semibold">What is Prolific?</p>
|
||||
<p className="text-slate-600">
|
||||
We’re partnering with Prolific to offer you access to a pool of 200.000 participant to do
|
||||
research with.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex flex-col gap-y-6 bg-slate-50 p-8">
|
||||
<Image
|
||||
src={ProlificLogo}
|
||||
alt="Prolific panel selection UI"
|
||||
className="absolute right-8 top-8 w-32"
|
||||
/>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold">How to create a panel</h3>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-md font-semibold">Step 1: Create an account with Prolific</p>
|
||||
<p className="text-slate-600">
|
||||
We partner with Prolific to give you access to a pool of over 200.000 vetted participants.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-md font-semibold">Step 2: Create a study</p>
|
||||
<p className="text-slate-600">
|
||||
At Prolific, you create a new study where you can pick your preferred audience based on hundreds
|
||||
of characteristics.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-md font-semibold">Step 3: Connect your survey</p>
|
||||
<p className="text-slate-600">
|
||||
Set up hidden fields in your Formbricks survey to track which participant provided which answer.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-md font-semibold">Step 4: Launch your study</p>
|
||||
<p className="text-slate-600">
|
||||
Once everything is setup, you can launch your study. Within a few hours you’ll receive the first
|
||||
responses.
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="darkCTA"
|
||||
className="justify-center"
|
||||
href="https://formbricks.com/docs/link-surveys/market-research-panel"
|
||||
target="_blank">
|
||||
Get started
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Reference in New Issue
Block a user