Compare commits

...

57 Commits

Author SHA1 Message Date
github-actions[bot]
64ad948039 Version Packages (#172)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-08 15:29:26 +01:00
Matthias Nannt
bcecdc0f3d hq - update forms overview, add Nps Type, update charts lib, update react lib 2022-12-08 15:26:17 +01:00
Matthias Nannt
273aad391e improve posthog events in hq 2022-12-07 16:47:57 +01:00
Matthias Nannt
b0554757df move hq from app folder to pages folder, update react lib classNames 2022-12-07 16:38:09 +01:00
Matthias Nannt
3a5e297302 hq - add more posthog events 2022-12-07 14:06:17 +01:00
Matthias Nannt
f6d7a023cd update postgres version in docker compose, reset migrations in hq 2022-12-05 16:07:12 +01:00
Matthias Nannt
cbd74936ca fix pnpm lock, update hq dockerfile 2022-12-05 15:45:19 +01:00
Matthias Nannt
9f902354bb hq add telemetry, add posthog tracking, add single customer page 2022-12-05 13:41:54 +01:00
Matthias Nannt
5954a22ff3 enable search in docs 2022-12-02 16:36:01 +01:00
Matthias Nannt
69320e8119 fix link in blog 2022-12-02 16:22:28 +01:00
Matthias Nannt
a51c884e7f update code in weekly update 2022-12-02 15:44:56 +01:00
Matthias Nannt
4a87ebbec3 clean up charts lib - 0.0.1 release 2022-12-02 15:28:36 +01:00
Johannes
03bd1107cf Weekyl 2nd dec 2022 (#170) 2022-12-02 14:27:00 +01:00
Matthias Nannt
b45a32343e make ui package private 2022-12-02 12:48:45 +01:00
Matthias Nannt
334d95fe2a hq - move auth to client side searchparams to avoid problems with vercel 2022-12-02 12:47:07 +01:00
Matthias Nannt
7e24c065f0 sort submissions in hq, add number of submissions to summary 2022-12-02 12:41:25 +01:00
Matthias Nannt
83f3df4e81 render auth page client-side to avoid vercel bug on searchParams 2022-12-01 16:22:35 +01:00
Johannes
2335c72234 hq favicon (#169)
Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
2022-12-01 16:02:37 +01:00
Matthias Nannt
6d2b26b391 Merge branch 'main' of github.com:snoopForms/snoopforms 2022-12-01 15:59:51 +01:00
Matthias Nannt
23fd92f85c add sendToHq helper to formbricks react, update class merge in react lib, update capture endpoints in hq to support cors 2022-12-01 15:59:33 +01:00
Johannes
17b8bb1324 added docs, fix doc header (#168) 2022-12-01 15:57:15 +01:00
Johannes
515b23859a Api & schema docs (#167)
* api docs images v1

* schema & api docs

Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
2022-12-01 10:53:52 +01:00
Matthias Nannt
cabe59dcea update gitignore 2022-12-01 10:49:46 +01:00
Matthias Nannt
ed5b1cc263 add simple table graph (placeholder), fix build errors in hq 2022-12-01 10:08:45 +01:00
Matthias Nannt
d8a7d1a72e add simple charts library, add simple summary to hq 2022-11-30 21:03:19 +01:00
Matthias Nannt
013fec0fe0 hq - fix toasts 2022-11-30 16:21:12 +01:00
Matthias Nannt
5bd09c13b6 add schema to submission display if available 2022-11-30 16:15:40 +01:00
Matthias Nannt
47dd1d04de hq - add full pipeline functionality 2022-11-30 15:46:21 +01:00
Matthias Nannt
69985f5f39 add basic pipeline view to hq 2022-11-29 21:02:13 +01:00
Matthias Nannt
e0d0458d7f add submissions table 2022-11-29 18:12:37 +01:00
Matthias Nannt
a977cc403b update hq db schema, add form overview page 2022-11-29 15:34:55 +01:00
Matthias Nannt
ec695df017 fix redirect to login not working - formbricks hq 2022-11-28 18:51:11 +01:00
Matthias Nannt
99dd6478d2 change db:push command 2022-11-28 18:41:46 +01:00
Matthias Nannt
0070f9c69d formhq: add simple layout, add forms overview, add customers overview, add api endpoints 2022-11-28 18:31:19 +01:00
Matthias Nannt
c834cbf8ce add dashboard infobox to formbricks hq 2022-11-26 12:37:45 +01:00
Matthias Nannt
8535986ae8 Merge branch 'main' of github.com:snoopForms/snoopforms 2022-11-26 11:56:05 +01:00
Matthias Nannt
8cf06c6ea5 add api key functionality to formbricks HQ 2022-11-26 11:55:57 +01:00
Johannes
43e6e4ad91 Update Image Names (#166)
* updated names
2022-11-25 17:50:40 +01:00
Tomas Valenta
3fe3ca7ee4 Fix typo (#163) 2022-11-25 17:44:48 +01:00
Tomas Valenta
d215371ec4 Make the GitHub link in docs point to the formbricks repo (#164) 2022-11-25 17:42:15 +01:00
Johannes
7e63da2977 Img names (#165)
* weekly-update

* updated names
2022-11-25 17:41:10 +01:00
Johannes
fc58e9badf weekly-update (#162) 2022-11-25 16:53:48 +01:00
Matti Nannt
7c77a36ca1 weekly-251122 (#161)
Co-authored-by: knugget <johannes@knugget.de>
2022-11-25 15:01:33 +01:00
Johannes
3550a51cea Update react intro (#159)
* updated react intro

* docs: icon page

* add name attr to Submit
2022-11-25 11:21:57 +01:00
Matthias Nannt
0f638c3f6b fix get started link on mobile formbricks-com 2022-11-24 13:25:37 +01:00
Matthias Nannt
4a8e1c563d fix typo in docs 2022-11-24 13:07:04 +01:00
Matthias Nannt
32afef77ea add VERCEL_URL to turbo.json 2022-11-24 13:02:19 +01:00
Matthias Nannt
a3db36fef5 update docs intro page 2022-11-24 13:00:09 +01:00
Matthias Nannt
0a17676981 move development of formbricks HQ to monorepo 2022-11-24 12:30:08 +01:00
Matthias Nannt
74b1cb0687 Merge branch 'main' of github.com:snoopForms/snoopforms 2022-11-23 14:59:09 +01:00
Matthias Nannt
3444f5b828 update docs 2022-11-23 14:50:09 +01:00
Matti Nannt
e2f625cd02 Fix Markdown issue 2022-11-23 14:45:42 +01:00
Johannes
af5c9c8785 Update README.md 2022-11-23 07:44:23 -06:00
Johannes
1c962433f3 Update Readme for npm 2022-11-23 07:43:12 -06:00
Matthias Nannt
d8d2ac115d Merge branch 'main' of github.com:snoopForms/snoopforms 2022-11-23 14:13:46 +01:00
Matti Nannt
104a3d22ff React Lib HTML Input Docs (#158)
Co-authored-by: knugget <johannes@knugget.de>
2022-11-23 14:13:20 +01:00
Matthias Nannt
0eadbfce9d update react package version to 0.2.1 2022-11-23 14:09:16 +01:00
264 changed files with 14319 additions and 2065 deletions

51
.env.docker Normal file
View File

@@ -0,0 +1,51 @@
########################################################################
# ------------ MANDATORY (CHANGE ACCORDING TO YOUR SETUP) ------------#
########################################################################
############
# Basics #
############
NEXTAUTH_SECRET=RANDOM_STRING
NEXTAUTH_URL=http://localhost:3000
# This should always be localhost:3000 (or whatever port your app is running on)
NEXTAUTH_URL_INTERNAL=http://localhost:3000
DATABASE_URL='postgresql://postgres:postgres@postgres:5432/postgres?schema=public'
################
# Mail Setup #
################
# Necessary if email verification and password reset are enabled.
# See optional configurations below if you want to disable these features.
# MAIL_FROM=noreply@example.com
# SMTP_HOST=localhost
# SMTP_PORT=1025
# SMTP_SECURE_ENABLED=0 # Enable for TLS (port 465)
# SMTP_USER=smtpUser
# SMTP_PASSWORD=smtpPassword
########################################################################
# ------------------------------ OPTIONAL -----------------------------#
########################################################################
# Uncomment the variables you would like to use and customize the values.
#####################
# Disable Features #
#####################
# Email Verification. If you enable Email Verification you have to setup SMTP-Settings, too.
NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED=1
# Password Reset. If you enable Password Reset functionality you have to setup SMTP-Settings, too.
NEXT_PUBLIC_PASSWORD_RESET_DISABLED=1
# Signup. Disable the ability for new users to create an account.
# NEXT_PUBLIC_SIGNUP_DISABLED=1

55
.env.example Normal file
View File

@@ -0,0 +1,55 @@
########################################################################
# ------------ MANDATORY (CHANGE ACCORDING TO YOUR SETUP) ------------#
########################################################################
############
# Basics #
############
NEXTAUTH_SECRET=RANDOM_STRING
# Set this to your public-facing URL, e.g., https://example.com
NEXTAUTH_URL=http://localhost:3000
# This should always be localhost:3000 (or whatever port your app is running on)
NEXTAUTH_URL_INTERNAL=http://localhost:3000
DATABASE_URL='postgresql://postgres:postgres@localhost:5432/postgres?schema=public'
# For Docker Compose Production Setup use this Database URL:
# DATABASE_URL='postgresql://postgres:postgres@postgres:5432/formbricks?schema=public'
################
# Mail Setup #
################
# Necessary if email verification and password reset are enabled.
# See optional configurations below if you want to disable these features.
MAIL_FROM=noreply@example.com
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE_ENABLED=0 # Enable for TLS (port 465)
SMTP_USER=smtpUser
SMTP_PASSWORD=smtpPassword
########################################################################
# ------------------------------ OPTIONAL -----------------------------#
########################################################################
# Uncomment the variables you would like to use and customize the values.
#####################
# Disable Features #
#####################
# Email Verification. If you enable Email Verification you have to setup SMTP-Settings, too.
# NEXT_PUBLIC_EMAIL_VERIFICATION_DISABLED=1
# Password Reset. If you enable Password Reset functionality you have to setup SMTP-Settings, too.
# NEXT_PUBLIC_PASSWORD_RESET_DISABLED=1
# Signup. Disable the ability for new users to create an account.
# NEXT_PUBLIC_SIGNUP_DISABLED=1

2
.gitignore vendored
View File

@@ -39,4 +39,4 @@ yarn-error.log*
# nixos stuff
.direnv
apps/examples
apps/demo

View File

@@ -56,7 +56,7 @@ export default function WaitlistForm() {
<Text name="firstname" label="What's your first name?" validation="required" />
<Text name="lastname" label="What's your last name?" />
<Textarea name="about" label="About you" help="Please keep it short" />
<Submit label="Submit" />
<Submit name="submit" label="Submit" />
</Form>
);
}

View File

@@ -40,9 +40,9 @@ function Header({ navigation }: any) {
return (
<header
className={clsx(
"sticky top-0 z-50 flex flex-wrap items-center justify-between bg-white px-4 py-5 shadow-md shadow-slate-900/5 transition duration-500 dark:shadow-none sm:px-6 lg:px-8",
"sticky top-0 z-50 flex flex-wrap items-center justify-between bg-slate-100 px-4 py-5 shadow-md shadow-slate-900/5 transition duration-500 dark:shadow-none sm:px-6 lg:px-8",
isScrolled
? "dark:bg-slate-900/95 dark:backdrop-blur dark:[@supports(backdrop-filter:blur(0))]:bg-slate-900/75"
? "bg-slate-100/90 backdrop-blur dark:bg-slate-900/90 [@supports(backdrop-filter:blur(0))]:bg-slate-100/75 dark:[@supports(backdrop-filter:blur(0))]:bg-slate-900/75"
: "dark:bg-transparent"
)}>
<div className="mr-6 flex lg:hidden">
@@ -50,27 +50,24 @@ function Header({ navigation }: any) {
</div>
<div className="relative flex flex-grow basis-0 items-center">
<Link href="/" aria-label="Home page">
<Logomark className="h-9 w-9 lg:hidden" />
<FooterLogo className="hidden h-9 w-auto fill-slate-700 dark:fill-slate-100 lg:block" />
<FooterLogo className="h-8 w-auto sm:h-10" />
</Link>
<div>
<Button
variant="secondary"
onClick={() => router.push("/")}
size="sm"
className="ml-10 hidden justify-center opacity-60 sm:flex">
&larr; Back to Mainpage
</Button>
</div>
</div>
<div className="-my-5 mr-6 sm:mr-8 md:mr-0">
<Search />
</div>
<div className="relative flex basis-0 justify-end gap-6 sm:gap-8 md:flex-grow">
<ThemeSelector className="relative z-10" />
<Link href="https://github.com" className="group" aria-label="GitHub">
<GitHubIcon className="h-6 w-6 fill-slate-400 group-hover:fill-slate-500 dark:group-hover:fill-slate-300" />
</Link>
<div className="hidden items-center justify-end md:flex md:flex-1 lg:w-0">
<ThemeSelector className="relative z-10 mr-5" />
<Button
variant="secondary"
EndIcon={GitHubIcon}
endIconClassName="fill-slate-800 dark:fill-slate-200"
onClick={() => router.push("https://github.com/formbricks/formbricks")}>
View on Github
</Button>
<Button variant="highlight" className="ml-2" onClick={() => router.push("/get-started")}>
Get started
</Button>
</div>
</header>
);

View File

@@ -0,0 +1,194 @@
import { useState, ChangeEvent } from "react";
import { ChevronDownIcon, ChevronRightIcon, ChevronLeftIcon } from "@heroicons/react/20/solid";
import clsx from "clsx";
interface APICallProps {
method: "GET" | "POST";
url: string;
description: string;
queries: {
label: string;
type: string;
description: string;
}[];
bodies: {
label: string;
type: string;
description: string;
required?: boolean;
}[];
responses: {
color: string;
statusCode: string;
description: string;
example?: string;
}[];
example?: string;
}
export function APILayout({ method, url, description, queries, bodies, responses, example }: APICallProps) {
const [switchState, setSwitchState] = useState(false);
function handleOnChange() {
setSwitchState(!switchState);
}
return (
<div className="rounded-lg bg-slate-200 p-8 dark:bg-slate-700">
{switchState ? (
<ChevronDownIcon
className="hover:text-brand-dark dark:hover:text-brand-dark mr-3 inline h-5 w-5 hover:cursor-pointer"
aria-hidden="true"
onClick={handleOnChange}
/>
) : (
<ChevronRightIcon
className="hover:text-brand-dark dark:hover:text-brand-dark mr-3 inline h-5 w-5 hover:cursor-pointer"
aria-hidden="true"
onClick={handleOnChange}
/>
)}
<div
className={clsx(
"mr-3 inline rounded-full p-1 px-3 font-semibold text-white",
method === "POST" && "bg-red-400",
method === "GET" && "bg-green-400"
)}>
{method}
</div>
<div className="inline text-sm text-slate-500">
http://localhost:300/api
<span className="font-bold text-black">{url}</span>
</div>
<div className="mt-4 ml-8 font-bold">{description}</div>
<div>
<div className={clsx(switchState ? "block" : "hidden", "ml-8")}>
<p className="mt-6 mb-2 text-lg font-semibold">Parameters</p>
<div>
<div className="text-base">
<p className="not-prose -mb-1 pt-2 font-bold">Query</p>
<div>
{queries.map((q) => (
<Parameter key={q.label} label={q.label} type={q.type} description={q.description} />
))}
</div>
</div>
<div className="mt-4 text-base">
<p className="not-prose -mb-1 pt-2 font-bold">Body</p>
<div>
{}
{bodies.map((b) => (
<Parameter
key={b.label}
label={b.label}
type={b.type}
description={b.description}
required={b.required}
/>
))}
{example && (
<div>
<p className="not-prose mb-2 pt-2 font-bold">Body Example</p>
<div className="rounded-lg bg-slate-300 p-2 font-mono text-sm">{example}</div>
</div>
)}
</div>
<div className="mt-4 text-base">
<p className="not-prose -mb-1 pt-2 font-bold">Responses</p>
<div>
{responses.map((r) => (
<Response
key={r.color}
color={r.color}
statusCode={r.statusCode}
description={r.description}
example={r.example}
/>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
interface ParaProps {
label: string;
type: string;
description: string;
required?: boolean;
}
function Parameter({ label, type, description, required }: ParaProps) {
return (
<>
<div className="my-2 grid grid-cols-4 text-sm">
<div className="inline font-mono">
{label}
{required && <p className="inline font-bold text-red-500">*</p>}
</div>
<div>{type}</div>
<div className="col-span-2">{description}</div>
</div>
</>
);
}
interface RespProps {
color: string;
statusCode: string;
description: string;
example?: string;
}
function Response({ color, statusCode, description, example }: RespProps) {
const [toggleExample, setSwitchState] = useState(false);
function handleOnChange() {
setSwitchState(!toggleExample);
}
return (
<div className="my-2 grid grid-cols-2 text-sm">
<div className="text-md inline-flex items-center font-semibold">
<div
className={clsx(
"mr-3 inline h-3 w-3 rounded-full",
color === "green" && "bg-green-400",
color === "brown" && "bg-amber-800"
)}>
&nbsp;
</div>
<div>{statusCode}</div>
</div>
<div className="flex items-center justify-between">
<div>{description}</div>
<div className="font-bold">
{example &&
(toggleExample ? (
<ChevronDownIcon
className={clsx(
toggleExample ? "block" : "hidden",
"hover:text-brand-dark dark:hover:text-brand-dark mr-3 inline h-6 w-6 hover:cursor-pointer"
)}
aria-hidden="true"
onClick={handleOnChange}
/>
) : (
<ChevronLeftIcon
className={clsx(
toggleExample ? "hidden" : "block",
"hover:text-brand-dark dark:hover:text-brand-dark mr-3 inline h-6 w-6 hover:cursor-pointer"
)}
aria-hidden="true"
onClick={handleOnChange}
/>
))}
</div>
</div>
{example && toggleExample && (
<div className="col-span-2 my-3 rounded-lg bg-slate-300 p-2 font-mono">{example}</div>
)}
</div>
);
}

View File

@@ -449,7 +449,7 @@ export default function Header() {
</Button>
<Button
variant="primary"
onClick={() => router.push("https://app.formbricks.com")}
onClick={() => router.push("/get-started")}
className="mt-3 flex w-full justify-center">
Get started
</Button>

View File

@@ -0,0 +1,61 @@
import Image from "next/image";
import Button from "@/components/shared/Button";
import Friends from "@/images/newsletter-signup-gif.gif";
export default function WaitlistForm() {
return (
<div className="not-prose text-md mx-auto mt-12 max-w-7xl rounded-lg bg-slate-200 p-10 text-slate-500 shadow-lg dark:bg-slate-800 dark:text-slate-400">
<p className="my-0 text-2xl font-bold text-slate-600 dark:text-slate-300">Build in public</p>
Get all the juicy details of our journey building Formbricks in public 👇
<div className="mt-8 gap-4 md:grid md:grid-cols-2">
<form method="post" action="https://listmonk.formbricks.com/subscription/form">
<div className="p-6 ">
<div>
<input type="hidden" name="nonce" />
</div>
<div>
<input
type="email"
name="email"
required
placeholder="Email"
className="block w-full rounded-xl text-slate-900 shadow-sm focus:border-slate-500 focus:ring-slate-500 sm:text-sm"
/>
<label htmlFor="email" className="ml-2 block text-sm text-slate-400 dark:text-slate-500">
Work or personal
</label>
</div>
<div>
<input
type="text"
name="name"
placeholder="Name"
required
className="mt-4 block w-full rounded-xl text-slate-900 shadow-sm focus:border-slate-500 focus:ring-slate-500 sm:text-sm"
/>
<label htmlFor="name" className="ml-2 block text-sm text-slate-400 dark:text-slate-500">
Optional but appreciated
</label>
</div>
<div className="hidden">
<input
id="e0084"
type="checkbox"
name="l"
checked
value="e0084486-8751-43e4-8cfb-58b7c3f5f318"
readOnly
/>
<label htmlFor="e0084">Build in public</label>
</div>
<Button type="submit" className="mt-5 w-full justify-center">
Subscribe
</Button>
</div>
</form>
<Image src={Friends} alt="Sign up to newsletter" className="not-prose rounded-xl" />
</div>
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

View File

@@ -35,6 +35,26 @@ const navigation = [
title: "Form Wrapper",
href: "/docs/react-form-library/form-wrapper",
},
{
title: "Input: Checkbox",
href: "/docs/react-form-library/input-checkbox",
},
{
title: "Input: Email",
href: "/docs/react-form-library/input-email",
},
{
title: "Input: Number",
href: "/docs/react-form-library/input-number",
},
{
title: "Input: Password",
href: "/docs/react-form-library/input-password",
},
{
title: "Input: Phone",
href: "/docs/react-form-library/input-phone",
},
{
title: "Input: Text",
href: "/docs/react-form-library/input-text",
@@ -47,10 +67,14 @@ const navigation = [
title: "Input: Radio Button",
href: "/docs/react-form-library/input-radio",
},
/* {
title: "Input: Checkbox",
href: "/docs/react-form-library/input-checkbox",
}, */
{
title: "Input: URL",
href: "/docs/react-form-library/input-url",
},
{
title: "Input: Search",
href: "/docs/react-form-library/input-search",
},
{
title: "Input: Submit",
href: "/docs/react-form-library/input-submit",
@@ -63,20 +87,21 @@ const navigation = [
title: "Style with Tailwind",
href: "/docs/react-form-library/style-tailwind",
},
{
title: "Icons",
href: "/docs/react-form-library/icons",
},
],
},
{
title: "Data Pipelines",
title: "Formbricks HQ",
links: [
{ title: "Core API", href: "/docs/data-pipelines/core-api" },
{ title: "Email Notifications", href: "/docs/data-pipelines/email-notifications" },
{ title: "Webhooks", href: "/docs/data-pipelines/webhooks" },
{ title: "Core API", href: "/docs/formbricks-hq/core-api" },
{ title: "Schema", href: "/docs/formbricks-hq/schema" },
{ title: "Email Notifications (UI)", href: "/docs/formbricks-hq/ui/email-notifications" },
{ title: "Webhooks (UI)", href: "/docs/formbricks-hq/ui/webhooks" },
],
},
{
title: "Data Insights",
links: [{ title: "Formbricks HQ", href: "/docs/data-insights/formbricks-hq" }],
},
];
export default navigation;

View File

@@ -1,14 +1,14 @@
import Image from "next/image";
import LayoutMdx from "@/components/shared/LayoutMdx";
import LaunchDay from "./launch react library form builder survey with multi-step form open-source.png";
import LaunchDay from "./launch2-react library form builder survey with multi-step form open-source.png";
import MdxTryItCTA from "@/components/shared/MdxTryItCTA.tsx";
import Styling from "../../docs/react-form-library/style-css/HTML classes of Formbricks React Form Library to custom style surveys and forms.png";
import CodeExample from "./v2 developer experience best react library 2023 forms and surveys open source.png";
export const meta = {
title: "React Form Library launched! 🚀",
title: "React Form Library launched! 🚀 [Updated]",
description: "We are pumped to announce that the Formbricks React Form Library is ready to be used!",
date: "2022-11-22",
date: "2022-11-24",
};
<Image src={LaunchDay} alt="Robin Hood Meme" className="rounded-lg" />
@@ -27,14 +27,24 @@ We want to make forms in React more accessible. To achieve that, we work with:
<Image src={CodeExample} alt="Robin Hood Meme" className="rounded-lg" />
### What does it come with?
### What does it come with? [Updated]
In version 0.1 we layed the foundation for performant, easy to build forms. The library includes:
- Text Input
- Textarea Input
- Submit Button
- Validation
- Checkboxes
- Radio Buttons
- Phone Input
- Email Input
- URL Input
- Password Input
- Search Input
- Submit Button with Icons
And additional features like
- Validation & Error Messages
- Tailwind Support
- Standard CSS Support
@@ -42,7 +52,6 @@ In version 0.1 we layed the foundation for performant, easy to build forms. The
### Whats on the roadmap?
- All HTML input types (checkbox, phone, email, address, etc)
- Common non-HTML question types (Slider, star-rating, NPS, toggle, etc.)
- Easy multi-step forms
- Easy form logic

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,166 @@
import Image from "next/image";
import LayoutMdx from "@/components/shared/LayoutMdx";
import HeaderImage from "../weekly-update-181122/weeklyupdate.png";
import { Callout } from "@/components/shared/Callout";
import NewsletterSignup from "@/components/shared/NewsletterSignup";
import Chart1 from "./chart-1.webp";
import Chart2 from "./chart-2.webp";
import Chart3 from "./chart-3.webp";
import Results1 from "./results-1.webp";
import Results2 from "./results-2.webp";
import Crowd from "./crowd-dev-1.png";
export const meta = {
title: "Weekly Summary - 2nd Dec 2022",
description:
"And it is December! This week was dominated by working on the Formbricks HQ, Formbricks Charts and the YC interview. Lets look at it in detail.",
date: "2022-12-02",
};
_And it is December! This week was dominated by working on the Formbricks HQ, Formbricks Charts and the YC interview. Lets look at it in detail 👇_
<Image src={HeaderImage} alt="Weekly Update" className="rounded-lg" />
# Highlights
- Formbricks HQ almost ready 🚢
- Formbricks Charts 📈
- YC Interview 🍊
Let's dive in!
##
# Product
### Formbricks HQ (launching next week)
We made very good progress with the new Formbricks HQ. We rebuilt the use case of the Swiss government adding native feedback functionality into an existing app - super smooth!
<Callout title="Modular is the way" type="note">
The Swiss pulled snoopForms apart to build their feedback feature. A few more early users did that which led
us to build a modular set of tools: they work independently but are well integrated e.g. by the use of a
shared data schema.
</Callout>
Here is a quick run-through to show how our modular setup plays out:
### Step 1: Build the form with Formbricks React
Within a few minutes we spun up the right form. Look how easy radio buttons can be in React, styled with Tailwind CSS 😍
```tsx
import { Form, Radio, Submit, sendToHq, Textarea } from "@formbricks/react";
...
<Form formId="clb4yr1m90000XXXXXXXX" hqUrl="http://localhost:3000" onSubmit={sendToHq}>
<Radio
name="evaluate"
label="Evaluate the online course you just completed"
legendClassName="mb-3 font-bold text-gray-800 text-xl"
labelClassName="font-regular text-gray-500 text-lg"
options={["Perfect", "Very satisfactory", "Satisfactory", "Not very satisfactory", "Useless"]}
/>
<Textarea
name="feedback"
label="Would you like to send us a comment, an opinion, a correction?"
help="Only you and us can see your answer."
labelClassName="font-bold text-gray-800 text-xl"
innerClassName="mt-3"
cols={50}
rows={4}
/>
<Submit
label="Answer"
inputClassName="flex items-center justify-center rounded-md border border-transparent
bg-gradient-to-r from-purple-600 to-indigo-600 bg-origin-border px-3 py-2 text-base
font-medium text-white shadow-sm hover:from-purple-700 hover:to-indigo-700"
/>
</Form>
```
<Image src={Chart1} alt="Weekly Update Demo Custom Native Feedback Function" className="rounded-lg" />
### Step 2: Pipe it up with Formbricks HQ
We hooked it up with the Formbricks HQ to store the data. The React Lib sends a data schema along with the submission to assure a complete picture of the data. Read more about schemas in our [docs](/docs/formbricks-hq/schema):
```tsx
<Form
formId="clb4yr1m90000yzaXXXXXXXX"
hqUrl="http://localhost:3000"
onSubmit={handleSubmit}
>
```
<Image src={Results1} alt="Weekly Update Demo Custom Native Feedback Function" className="rounded-lg" />
### Step 3: Display the results right after submission
They were looking for a polling functionality so showing the enduser the results right after they submitted their answer. We built Formbricks Charts to enable that with a single line of code:
```tsx
import { Bar } from "@formbricks/charts";
...
<h2 className="mx-auto mb-3 text-lg font-bold text-gray-800">
Thanks a lot for your feedback
</h2>
<p className="mb-5 text-lg text-gray-500">
Here you can see what other people answered.
</p>
/* Visualize Submission using Formbricks Charts Library */
<Bar
submissions="{submissions}"
schema="{schema}"
fieldName="evaluate"
color="#4f46e5"
/>
```
<Image src={Chart3} alt="Weekly Update Demo Custom Native Feedback Function" className="rounded-lg" />
### Formbricks Charts
Formbricks charts is a package to display form data collected with Formbricks React or the upcoming Visual Form Builder. Its leveraging the great [Recharts](https://recharts.org) package.
With a single line of code you can display your form data wherever you need it:
```tsx
<Bar submissions={submissions} schema={schema} fieldName="fieldToVisualize" />
/* or */
<Table submissions={submissions} schema={schema} fieldName="fieldToVisualize" />
```
We also use the same package to display our charts in Formbricks HQ:
<Image src={Results2} alt="Formbricks HQ Demo Custom Native Feedback with Charts" className="rounded-lg" />
# Community
We had a few great in-depth chats with builders looking to use Formbricks. Special shout out to jetsoms, LD and kiran for sharing so much about their pain points with other solutions.
Here is a little overview of our crowds activity on GitHub and Discord over the past week:
<Image src={Crowd} alt="Weekly Update Demo Custom Native Feedback Function" className="rounded-lg" />
## YC Interview
Last night we had our YC interview.
Our experience was surprisingly relaxed, Nicolas just tried to understand what were building, why and what we have learned about our users over the past couple of months.
We talked about how we see Formbricks develop from solving form infrastructure to becoming an open source alternative to Qualitrcs, what happened with snoopForms and more.
Some say you hear from YC within a few hours in case they want to invest. On the website it says they'll get back to us within a day or two - let's see how it shakes out.
Generally, it was super exciting to be invited and look at what we have done and are planning to do through the YC lens 🚀 We got a lot more pumped about size of the opportunity ahead of us!
<NewsletterSignup />
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,11 +1,11 @@
import Image from "next/image";
import LayoutMdx from "@/components/shared/LayoutMdx";
import Button from "@/components/shared/Button";
import Mockup from "./demo mockup for formbricks hq open source form and survey tool.png";
import NewLogo from "./formbricks new logo mockup community feedback.png";
import Community from "./community feedback logo and name.png";
import Storybook from "./storybook for formbricks survey form builder.png";
import HeaderImage from "./weeklyupdate.png";
import NewsletterSignup from "@/components/shared/NewsletterSignup";
export const meta = {
title: "Weekly Summary - 18th Nov 2022",
@@ -91,42 +91,6 @@ Lets hope Elon doesnt break it too soon.
## Have a great weekend!
---
<div className="mx-auto max-w-7xl px-5 py-5 bg-white shadow-lg rounded-lg text-md text-gray-700">
You would like to have Formbricks "Build in Public Updates" like this in your mailbox? Subscribe to our
newsletter 💪
<form method="post" action="https://listmonk.formbricks.com/subscription/form" className="listmonk-form">
<div>
<input type="hidden" name="nonce" />
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700">
Email
</label>
<div className="mt-1">
<input type="email" name="email" required placeholder="you@example.com" className="block w-full rounded-md border-gray-300 shadow-sm focus:border-slate-500 focus:ring-slate-500 sm:text-sm" />
</div>
</div>
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
Name (optional)
</label>
<div className="mt-1">
<input type="text" name="name" required className="block w-full rounded-md border-gray-300 shadow-sm focus:border-slate-500 focus:ring-slate-500 sm:text-sm" />
</div>
</div>
<div className='hidden'>
<input id="e0084" type="checkbox" name="l" checked value="e0084486-8751-43e4-8cfb-58b7c3f5f318" readOnly />
<label htmlFor="e0084">Build in public</label>
</div>
<Button type="submit" className="mt-4 w-full justify-center">Subscribe</Button>
</div>
</form>
</div>
<NewsletterSignup />
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,111 @@
import Image from "next/image";
import LayoutMdx from "@/components/shared/LayoutMdx";
import NewsletterSignup from "@/components/shared/NewsletterSignup";
import HeroAnimation from "@/components/shared/HeroAnimation";
import HeaderImage from "./weeklyupdate.png";
import POC from "./proof-of-concept.png";
export const meta = {
title: "Weekly Summary - 25th Nov 2022",
description:
"Every week, well share an update about all things Formbricks. This week we shipped, wrote docs, talked about a feedback app built on top of snoopForms and prepped for the YC interview! 👉",
date: "2022-11-25",
};
_This week we shipped Formbricks React v0.1 and v0.2, wrote lots of docs, talked about a feedback app built on top of snoopForms and prepped for the upcoming YC interview! 🤸_
<Image src={HeaderImage} alt="Weekly Update" className="rounded-lg" />
# Highlights
- shipped Formbricks React 🚢
- met devs who built a feedback solution with snoopForms 🦝
- got invited to YC interview 🍊
Let's dive in!
##
# Product
### Launched Formbricks React Library 🥳
We launched v0.1 and v0.2 of the Formbricks React Library.
- **Why?** Make it as easy as possible to build forms in React
- **How?** Offering all essential form functionality out of the box
- **What?** Lots of input components, validation, easy styling, easy multi-step forms, i18n, a11y and everything else you need when building forms in React.
Have a look at this example:
```jsx
import { Form, Text, Email, Checkbox, Submit } from "@formbricks/react";
import "@formbricks/react/styles.css";
export default function WaitlistForm() {
return (
<Form onSubmit={({ data, schema }) => console.log("data:", data, "schema:", schema)}>
<Text name="name" label="What's your name?" validation="required" />
<Email
name="email"
label="What's your email address?"
placeholder="you@example.com"
validation="required|email"
/>
<Checkbox
name="terms"
label="Terms & Conditions"
help="To use our service, please accept."
validation="accepted"
/>
<Submit label="Let's go!" />
</Form>
);
}
```
A lot more information in the [Docs, check them out!](/docs/react-form-library/introduction)
### What's next for Formbricks React?
In the upcoming week we're not planning to add more features to it. We'll first ship a version of the Formbricks HQ to offer an end-to-end solution from form creation to submission handling to analysis 👇
## Formbricks HQ Demo
With the YC interview coming up next week, we decided to build an end-to-end demo. We're not focussing on production readiness for now. Expect to get your fingers on it the week after!
<HeroAnimation />
## Feedback tool for the State of Geneva, Switzerland
We were delighted to find out that a digital agency built a POC for a feedback functionality in an e-learning offer from the state of Geneva, Switzerland.
In the call we learned a lot about why they chose snoopForms (privacy-first), how their developer experience has been (pretty good) and what they need in the future.
They are working on a more comprehensive offer for forms & surveys for the Swiss market built on top of Formbricks!
So exciting for us to see that our product and positioning resonates so well 😊
<Image src={POC} alt="SCreenshot from feedback tool built with snoopforms" className="rounded-lg" />
##
# Community
Shoutout to octalpixel and [Akshu](https://twitter.com/Akshu_on_github) from the EddieHub community for sharing feedback on the React Library. Highly appreciated 🙏
## Listmonk
We chose [Listmonk](https://listmonk.app/) as our Newsletter tool as it is a promising open-source option. So far the setup has been pretty good!
You can sign up to our "Build in Public" list at the bottom of this article to not miss a single one of them 😉
## YC Interview
We got invited! This is obviously very exciting so we spent some time getting our answers crisp. We're talking some alumns to learn from their experience 🤞
## Have a great weekend!
<NewsletterSignup />
export default ({ children }) => <LayoutMdx meta={meta}>{children}</LayoutMdx>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,10 +0,0 @@
import { Layout } from "@/components/docs/Layout";
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Formbricks HQ",
};
coming soon
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -1,10 +0,0 @@
import { Layout } from "@/components/docs/Layout";
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Core API",
};
coming soon
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -1,10 +0,0 @@
import { Layout } from "@/components/docs/Layout";
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Email Notifications",
};
coming soon
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -1,10 +0,0 @@
import { Layout } from "@/components/docs/Layout";
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Webhooks",
};
coming soon
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@@ -0,0 +1,189 @@
import { Layout } from "@/components/docs/Layout";
import { Fence } from "@/components/shared/Fence";
import Image from "next/image";
import Architecture from "./api-endpoints.png";
import { Callout } from "@/components/shared/Callout";
import { APILayout } from "@/components/shared/APILayout.tsx";
export const meta = {
title: "Core API - Formbricks Open Source Form & Survey Builder",
};
## Why a form API?
We want to empower you to rapidly build exactly the form and survey functionality that you need. An open API is key to achieve that as it gives you access to every single data model within the application. This allows you to replicate the entire functionality of [Formbricks HQ](/formbricks-hq) in your own app using the API.
## Architecture
Especially large organisations with multiple teams suffer from scatterd qualitative data. This is why we baked in scaleability right from the start. The data models are structured as follows:
<Image src={Architecture} alt="Data model of open source form tool" className="rounded-lg" />
## Auth: Personal API key
The API requests are authorized with a personal API key. This API key gives you the same rights as if you were logged in at formbricks.com - **don't share it around!**
### How to generate an API key
1. Go to your profile at [hq.formbricks.com](https://hq.formbricks.com/app/me/settings).
2. Select “Add key”.
3. Assign it any label (this is just for you).
4. Copy the key. You wont be able to see it again.
<Callout title="Store API key safely" type="warning">
Anyone who has your API key has full control over your account. For security reasons, you cannot view the
API key again.
</Callout>
### Delete a personal API key
1. Go to your profile at [hq.formbricks.com](https://hq.formbricks.com/app/me/settings).
2. Find the key you wish to revoke and select “Delete”.
3. Your API key will stop working immediately.
## User
### Find current user
<APILayout
method="GET"
url="/users/me"
description="Get the current logged in user."
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{ label: "tba", type: "tba", description: "tba" },
{ label: "tba", type: "tba", description: "tba" },
]}
responses={[
{ color: "green", statusCode: "tba", description: "tba" },
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
## Team
### Find memberships
<APILayout
method="GET"
url="/memberships"
description="Get all of my team memberships."
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{ label: "tba", type: "tba", description: "tba" },
{ label: "tba", type: "tba", description: "tba" },
]}
responses={[
{ color: "green", statusCode: "tba", description: "tba" },
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
### Get specific team
<APILayout
method="GET"
url="/teams/[teamId]"
description="Get a specific team by team ID."
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{ label: "tba", type: "tba", description: "tba" },
{ label: "tba", type: "tba", description: "tba" },
]}
responses={[
{ color: "green", statusCode: "tba", description: "tba" },
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
## Forms
### Get all forms of a team
<APILayout
method="GET"
url="/teams/[teamId]/forms"
description="Get all forms of a team by team ID."
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{ label: "tba", type: "tba", description: "tba" },
{ label: "tba", type: "tba", description: "tba" },
]}
responses={[
{ color: "green", statusCode: "tba", description: "tba" },
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
### Create new form
<APILayout
method="POST"
url="/teams/[teamId]/forms"
description="Create a new form"
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{ label: "label", type: "string", description: "The name you want to give your form.", required: true },
{
label: "schema",
type: "JSON",
description: "A valid Formbricks schema to provide provide context for the form data.",
},
]}
responses={[
{ color: "green", statusCode: "tba", description: "tba" },
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
## Submissions
### Get all submission of a form
<APILayout
method="GET"
url="/teams/[teamId]/forms/[formId]/submissions"
description="Request all submissions of a form by ID."
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{ label: "tba", type: "tba", description: "tba" },
{ label: "tba", type: "tba", description: "tba" },
]}
responses={[
{ color: "green", statusCode: "tba", description: "tba" },
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
### Create new submission
<APILayout
method="POST"
url="/capture/forms/[formId]/submissions"
description="Add a new submission to a form by form ID."
queries={[{ label: "apiKey", type: "string", description: "Your API key" }]}
bodies={[
{
label: "customerId",
type: "string",
description: "The customer/user you want to link the submission to.",
},
{
label: "data",
type: "JSON",
description: "The content of the submission.",
},
]}
example="hello"
responses={[
{
color: "green",
statusCode: "200",
description: "tba",
example: "{ // Response }",
},
{ color: "brown", statusCode: "tba", description: "tba" },
]}
/>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -0,0 +1,99 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import Schema from "./data-visualisation-open-source-survey-builder.png";
import NoSchema from "./no-schema-visualisation-of-open-source-data-form-builder-creator-webform.png";
import Question from "./schema-open-source-form-creator-web-form-builder.png";
export const meta = {
title: "Schema Open-source forms & survey infrastructure",
};
## What is a schema?
A schema is like a manual for a form: It provides the context of the form including the questions, all answer options, information about pages and more. Essentially, it is the skeleton of your form stored in a .JSON file.
## Why are schemas helpful?
A schema is essential to get a full picture of the data you are collecting. Here is a simple example. Lets say you have a form with one question like this:
<div className="grid md:grid-cols-2 gap-4">
<Image
className=""
src={Question}
alt="Open Source Form with Question about favorite food"
className="rounded-lg"
/>
<div className="mt-2">
```json
<Radio
name="fav-food"
label="What's your favorite food?"
help="Allday, every day!"
options={["Sushi", "Pasta", "Pizza"]}
/>
```
</div>
</div>
If you POST only the data to an API endpoint, it looks like this:
```json
{
"submission": {
"fav-food": "Pasta"
},
```
If you POST the data along with the schema, it looks like this:
```json
{
"submission": {
"fav-food": "pasta"
},
"schema": {
"type": "form",
"config": {},
"children": [
{
"type": "radio",
"name": "fav-food",
"label": "What's your favorite food?",
"options": ["Sushi", "Pasta", "Pizza"]
}
]
}
}
```
Without the schema, a visualisation of your data looks like this:
<Image src={NoSchema} alt="Open Source Form with Question about favorite food" className="rounded-lg" />
As you can see, the question and answer options which were not selected are missing. Over time, when more submissions with different values come in, the visualisation will get more complete. However, when respondents can skip this question it will be challenging to make correct statements about this data point.
<Callout title="Inconsistent data" type="warning">
The lack of a schema almost always leads to **inconsistent data.**
</Callout>
With the schema, the visualisation looks like this:
<Image src={Schema} alt="Open Source Form with Question about favorite food" className="rounded-lg" />
As you can clearly see, this visualisation keeps track of all answer options as well as the initial question. The quality of your decisions will improve, especially over the course of several surveys.
### Why does it work on Typeform, Jotform, etc.?
If you are using standalone form builder like Typeform, Jotform or Tally they handle the schema internally. When you custom build your form functionality or use a Backend-as-a-Service provider like Formspree you should deal with schemas to get a full picture of your data. Or let us do it 👇
### Formbricks React 🤝 Formbricks HQ
Our React Form Library auto-generates a schema out of the form you have built. On submit it sends it over to the Formbricks HQ along with the submission data. Formbricks HQ understands the schema and can display your data correctly.
<Callout title="Forms how they should be" type="note">
With Formbricks you get the level of integration of a custom-coded solution with the speed and ease of a
SaaS tool 🤍
</Callout>
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,56 @@
import { Layout } from "@/components/docs/Layout";
import Image from "next/image";
import Step1 from "./step-1-send-web-form-to-email-open-source.png";
import Step2 from "./step-2-select-email-survey-response-to-email-send.png";
import Step3 from "./step-3-add-email-to-send-form-data-to-open-source-alternative.png";
import Step4 from "./step-4-set-active-to-send-survey-data-via-mail-to-myself-open-source.png";
export const meta = {
title: "Email Notifications (UI) - Open-source Forms & Surveys",
};
You can send your form and survey data to your email address. To do so you can setup an Email Pipeline in your form settings.
## Send form data to email
### Setup
You need to have a form created to add a data pipeline to it.
### Step 1: Add Pipeline
In the top bar, select “Pipelines”. Click on “Add Pipeline” in the top right corner:
<Image src={Step1} className="rounded-lg" alt="Step 1 of sending form data to your email address" />
### Step 2: Select type of Pipeline
Select Email Notification. You can also set up a custom [Webhook](/docs/formbricks-hq/ui/webhooks).
<Image src={Step2} className="rounded-lg" alt="Step 2 of best webform to email tool" />
### Step 3: Configure settings
To setup your Pipeline you need to fill out two fields:
**Pipeline label:** Give it a name, this is just for you
**Email address:** Type in your email address
Then select “Create”.
<Image src={Step3} className="rounded-lg" alt="Step 3 of how to send form to email" />
### Step 4: Make sure its active
Congrats, you have created your email pipeline! Make sure it is set to “**Active**”.
<Image
src={Step4}
className="rounded-lg"
alt="Step 4 of sending survey data form open source survey to your email"
/>
From now on you will receive an email with the form content whenever someone submits your form. To deactive it, just turn it off. You can also delete it anytime.
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -0,0 +1,60 @@
import { Layout } from "@/components/docs/Layout";
import Image from "next/image";
import Step1 from "../email-notifications/step-1-send-web-form-to-email-open-source.png";
import Step2 from "../email-notifications/step-2-select-email-survey-response-to-email-send.png";
import Step3 from "./step-3-send-form-data-webhook-open-source.png";
import Step4 from "./step-4-webhook-survey-data-opensource.png";
export const meta = {
title: "Webhooks (UI) - Open source Forms & Surveys",
};
You can send your form and survey data to a destination of your choice via a Webhook. To do so you can set up a Webhook Pipeline in your form settings.
## Send form data with Webhook
### Setup
You need to have a form created to add a data pipeline to it.
### Step 1: Add Pipeline
In the top bar, select “Pipelines”. Click on “Add Pipeline” in the top right corner:
<Image src={Step1} className="rounded-lg" alt="Step 1 of sending form data to your email address" />
### Step 2: Select type of Pipeline
Select Webhook. You can also set up an [Email Notification](/docs/formbricks-hq/ui/email-notifications).
<Image src={Step2} className="rounded-lg" alt="Step 2 of best webform to email tool" />
### Step 3: Configure settings
To setup your Webhook you need to fill out three fields:
**Webhook label:** Give it a name to know what this is for.
**Endpoint URL:** Copy your endpoint URL here. This is where the webhook sends the form data to.
**Secret**: To authenticate the data transmission, add a secret. You can generate one [here](https://www.lastpass.com/features/password-generator) or just type something in.
<Image src={Step3} className="rounded-lg" alt="Step 3 of how to send form to email" />
Then select “Create”.
### Step 4: Make sure its active
Congrats, you have created your webhook pipeline! Make sure it is set to “**Active**”.
<Image
src={Step4}
className="rounded-lg"
alt="Step 4 of sending survey data form open source survey to your email"
/>
From now on the webhook will be triggered whenever someone submits your form. All form data will be sent to the
destination.
To deactive it, just turn it off. You can also delete it anytime.
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -9,7 +9,7 @@ The goal is clear: Build the last form tool humanity needs. What does such a sol
### Modular
Form needs differ widely but a one-size-fits all monolyth would fit no one. It would be slow and bloated. Only when it's possible to compose exactly the solution you need with the performance you expect we can compete with current solutions.
Form needs differ widely but a one-size-fits all monolith would fit no one. It would be slow and bloated. Only when it's possible to compose exactly the solution you need with the performance you expect we can compete with current solutions.
### Customizable

View File

@@ -66,7 +66,7 @@ export default function WaitlistForm() {
<Text name="firstname" label="What's your first name?" validation="required" />
<Text name="lastname" label="What's your last name?" />
<Textarea name="about" label="About you" help="Please keep it short" />
<Submit label="Submit" />
<Submit name="submit" label="Submit" />
</Form>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,74 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import FindIcon from "./icon-name-react-library-form-builder.png";
export const meta = {
title: "Icons - Formbricks React Form Library",
};
Icons give forms and surveys not only a professional look & feel, they increase the usability. Formbricks React supports icons in several locations throughout the form.
### Choosing an icon library
In the first step you have to decide which library you like. We like:
- [HeroIcons](https://heroicons.com/),
- [FeatherIcons](https://feathericons.com/) (beautiful but limited),
- [React Icons](https://react-icons.github.io/react-icons/). (huge collection)
### Installing library package
Once you picked yours you have to add it to your repository, e.g. HeroIcons:
```jsx
npm install @heroicons/react
```
### Importing icons
Next you have to import the icons you want to use them in your form:
```jsx
import { PlusIcon } from "@heroicons/react/24/outline";
# or several at once
import { ChatBubbleOvalLeftEllipsisIcon, EnvelopeIcon } from "@heroicons/react/24/solid";
```
If you paid close attention you noticed that we imported `PlusIcon` from the `outline` folder and the latter two from the `solid` folder. You can find the name of the icon you'd like to import on the respective website:
<Image
src={FindIcon}
alt="Illustration of error message and validation handling in best form react lib"
className="rounded-lg"
/>
Now you're ready to use them!
### Using icons
At this early stage, we only support icons in the submit button. You add it **before** and **after** the button text.
Before the button text:
```jsx
<Submit label="Submit" PrefixIcon={EnvelopeIcon} />
```
After the button text:
```jsx
<Submit label="Submit" SuffixIcon={PlusIcon} />
```
### Props & Attributes
| Prop | Type | Default | Description |
| ---------- | ---- | ------- | --------------------------- |
| PrefixIcon | SVG | - | Icon displayed before label |
| SuffixIcon | SVG | - | Icon displayed after label |
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -0,0 +1,154 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "./Checkbox-input-form-survey-react-lib-easy-build-forms-fast-validation-multi-step.png";
export const meta = {
title: "Checkbox - Formbricks React Form Library",
};
The checkbox input uses [HTML's checkbox input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox) and can display one or many options to a user. It's a great way to allow users to select multiple items from a list. Formbricks supports single and multiple checkboxes.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<Checkbox name="terms" />
```
## Single Checkbox
By default the checkbox input will render a single checkbox returning a `boolean` value:
```jsx
<Checkbox
name="newsletter"
label="Monthly Newsletter"
help="Would you like to receive our newsletter?"
validation="accepted"
/>
```
## Multiple checkboxes
There are two ways to add several options to your checkbox input, both using the `options` attribute.
### Options: Array of values
The options attribute can handle an array of values which are interpreted both as label and value.
```jsx
<Checkbox
name="toppings"
label="What do you like on your Pizza?"
help="Take what you like"
options={["Pineapple", "Ananas", "Piña"]}
/>
```
### Options: Array of objects
The options attribute can also handle an array of objects containing label/value pairs. This allows for differences between the label and the data stored. You can also add an optional config object:
```jsx
<Checkbox
name="allergies"
label="What are you allergic to?"
help="Tick all the boxes."
options={[
{
label: "Dogs",
value: "dogs",
config: {
disabled: true,
},
},
{
label: "These nuts",
value: "nuts",
},
{
label: "Bullshit",
value: "bs",
},
]}
/>
```
## Props & Attributes
| Prop | Type | Default | Description | |
| ---------------- | -------------------- | ------- | ------------------------------------------- | ---------------------------------------------------- |
| options | Array | - | Contains the options in your checkbox input | |
| label | String | - | The label of the checkbox input. | |
| legendClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| optionsClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| optionClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-legend | The legend (often a question) |
| formbricks-help | The help text itself. |
| formbricks-options | A wrapper around all options. |
| formbricks-option | A wrapper around each option. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself, here the checkbox button. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<Checkbox
name="toppings"
label="What do you like on your Pizza?"
help="Take what you like"
options={["Pineapple", "Ananas", "Piña"]}
legendClassName="font-bold"
optionClassName="bg-gray rounded-sm"
/>
```
### Options-specific props to style it
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ---------------- | -------------------- | --------- | -------------------- | ---------------------------------------------------- |
| legendClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| optionsClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| optionClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -0,0 +1,79 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "../style-css/html-classes-of-formbricks-react-form-library-to-custom-style-surveys-and-forms.png";
export const meta = {
title: "Email Input - Formbricks React Form Library",
};
The `Email` input uses HTML's [native email input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email). It allows a user to enter an email.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<Email name="email" />
```
In most cases, it looks something like this:
```jsx
<Email name="email" label="What's your email?" placeholder="Email" />
```
### Props & Attributes
| Prop | Type | Default | Description |
| ----------- | ------ | ------- | ------------------------------------------------ |
| placeholder | String | - | Placeholder before respondent clicks into field. |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your email field with adding CSS to the section classes. This is how the classes are named:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<Email name="email" label="What's your email?" inputClassName="bg-gray-800 my-5" />
```
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -0,0 +1,90 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "./Number-input-best-easy-react-form-library-2023-forms-survey.png";
export const meta = {
title: "Number Input - Formbricks React Form Library",
};
The `Number` input uses HTML's [native number input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number). It allows a user to enter a single integer or decimal value.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<Number name="age" />
```
In most cases, it looks something like this:
```jsx
<Number name="age" label="How old are you?" placeholder="42" min="1" max="130" step="1" />
```
### Props & Attributes
| Prop | Type | Default | Description |
| ----------- | ------ | ------- | ------------------------------------------------------------------------------------ |
| min | Number | - | The input has to be at least X |
| max | Number | - | The input cannot be higher than Y |
| step | Number | auto | Specifies the granularity that the value must adhere to when increasing / decreasing |
| placeholder | String | - | Placeholder before respondent clicks into field. |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your number field with adding CSS to the section classes. This is how the classes are named:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<Number
name="age"
label="How old are you?"
placeholder="42"
min="1"
max="130"
step="1"
inputClassName="font-bold"
/>
```
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -0,0 +1,87 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "./Password-input-easy-react-form-library-build-forms-surveys-fast.png";
export const meta = {
title: "Password Input - Formbricks React Form Library",
};
The `Password` input uses HTML's [native password input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/password). It allows a user to enter a password.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<Password name="password" />
```
In most cases, it looks something like this:
```jsx
<Password
name="password"
label="Password"
help="Enter secure password"
placeholder="ilikecoldcoffee"
validation="required"
/>
```
### Props & Attributes
| Prop | Type | Default | Description |
| ----------- | ------ | ------- | ------------------------------------------------ |
| minLength | Number | 0 | Limits min length accepted in the field |
| maxLength | Number | 524288 | Limits max length accepted in the field |
| placeholder | String | - | Placeholder before respondent clicks into field. |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<Password name="password" label="Password" validation="required" outerClassName="bg-gray-800" />
```
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -0,0 +1,81 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "./Phone-react-library-for-surveys-and-form-build-fast-and-easy.png";
export const meta = {
title: "Phone Input - Formbricks React Form Library",
};
The `Phone` input uses HTML's [native tel input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/tel). It allows a user to enter a telephone number.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<Phone name="phone" />
```
In most cases, it looks something like this:
```jsx
<Phone name="phone" label="Phone" help="Enter your phone number." placeholder="+1 123 444 567" />
```
### Props & Attributes
| Prop | Type | Default | Description |
| ----------- | ------ | ------- | ------------------------------------------------ |
| minLength | Number | 0 | Limits min length accepted in the field |
| maxLength | Number | 524288 | Limits max length accepted in the field |
| placeholder | String | - | Placeholder before respondent clicks into field. |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<Phone name="phone" label="Phone" outerClassName="bg-gray-800" />
```
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -100,10 +100,13 @@ label: "Do you agree?", value: "Do you agree?"
## Props & Attributes
| Prop | Type | Default | Description |
| ------- | ------ | ------- | ------------------------------------------------------------------------------------------------------- |
| options | Array | - | Contains the options in your radio input |
| label | String | - | The label (often a question) of the radio input. With no `option` attribute it's also the stored value. |
| Prop | Type | Default | Description | |
| ---------------- | -------------------- | ------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| options | Array | - | Contains the options in your radio input | |
| label | String | - | The label of the radio input. <br/>With no `option` attribute it's also the stored value. | |
| legendClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| optionsClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| optionClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Universal props all inputs have
@@ -124,7 +127,7 @@ label: "Do you agree?", value: "Do you agree?"
### Styling with CSS
You can style your form with adding CSS to the pre-assigned classes. This is how the classes are assigned:
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={Styling}
@@ -162,7 +165,7 @@ You can also use Tailwind to extend the classes, like so:
/>
```
### Options-specific props
### Options-specific props to style it
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ---------------- | -------------------- | --------- | -------------------- | ---------------------------------------------------- |

View File

@@ -0,0 +1,86 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "./Search-input-react-form-library-survey-forms-validation-multi-step-forms.png";
export const meta = {
title: "Search Input - Formbricks React Form Library",
};
The `Search` input uses HTML's [native search input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/search). It allows a user to enter a search term and is interpreted differently than the normal `text` input from the browser, e.g. it adds a little `x` inside the input field to remove the input again.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<Search name="search" />
```
In most cases, it looks something like this:
```jsx
<Search name="search" placeholder="Search..." label="Search" />
```
### Props & Attributes
| Prop | Type | Default | Description |
| ----------- | ------ | ------- | ------------------------------------------------ |
| minLength | Number | 0 | Limits min length accepted in the field |
| maxLength | Number | 524288 | Limits max length accepted in the field |
| placeholder | String | - | Placeholder before respondent clicks into field. |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<Search
name="search"
placeholder="Search..."
label="Search"
innerClassName="border-gray-700 border-2 rounded-full"
/>
```
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -9,11 +9,19 @@ export const meta = {
The submit button component is very straight forward:
```jsx
<Submit label="Submit" />
<Submit name="submit" label="Submit" />
```
To keep it working make sure that there is **only one Submit button** per form.
### Adding an icon
You can add icons **before and after** the button text:
```jsx
<Submit label="Submit" PrefixIcon={BeakerIcon} SuffixIcon={BeakerIcon} />
```
### Props & Attributes
| Prop | Type | Default | Description |

View File

@@ -48,7 +48,7 @@ In most cases, it looks something like this:
### Styling with CSS
You can style your form with adding CSS to the pre-assigned classes. This is how the classes are assigned:
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
@@ -56,17 +56,17 @@ You can style your form with adding CSS to the pre-assigned classes. This is how
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ----------------------------------------------------------------------------------------------- |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — most often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind

View File

@@ -55,7 +55,7 @@ In most cases, it looks something like this:
### Styling with CSS
You can style your form with adding CSS to the pre-assigned classes. This is how the classes are assigned:
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
@@ -63,17 +63,17 @@ You can style your form with adding CSS to the pre-assigned classes. This is how
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ----------------------------------------------------------------------------------------------- |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — most often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind

View File

@@ -0,0 +1,81 @@
import { Layout } from "@/components/docs/Layout";
import { Callout } from "@/components/shared/Callout";
import Image from "next/image";
import StyleClasses from "./URL-field-input-best-easy-react-form-librari-hook-form-survey.png";
export const meta = {
title: "URL Input - Formbricks React Form Library",
};
The `URL` input uses HTML's [native url input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/url). It allows a user to enter a url.
Like all other inputs it needs a `name` prop as an unique identifier:
```jsx
<URL name="url" />
```
<Callout title="Consider using Text input" type="note">
The reason there is an URL input in HTML is the native URL validation. Since Formbricks offers URL
validation out of the box you can remove unneccessary complexity by using the [Text
input](/docs/react-form-library/input-text).
</Callout>
### Props & Attributes
| Prop | Type | Default | Description |
| ----------- | ------ | ------- | ------------------------------------------------ |
| minLength | Number | 0 | Limits min length accepted in the field |
| maxLength | Number | 524288 | Limits max length accepted in the field |
| placeholder | String | - | Placeholder before respondent clicks into field. |
### Universal props all inputs have
| Prop | What is it? | Required? | Shown to respondent? | Comment |
| ----------------- | --------------------------------------- | --------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | name of the field | yes | no | It is not shown to respondents but important as a unique identifier to understand which field holds what content. You cannot have two fields with the same name. |
| label | label of the field | yes | yes | The label above the input |
| help | help text | no | yes | You can add a little help text to each field to provide more detail on how to fill out the field. |
| validation | determines how field input is validated | no | depends on field input | If you want to validate the content of a field before the form is sent, you can do so with the validation prop. [read more](/docs/react-form-library/validation-errors) |
| id | own identifier for input | no | no | To target your input with css or javascript directly |
| outerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| wrapperClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| innerClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| inputClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| helpClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messagesClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
| messageClassName | append styling class | no | - | [read more](/docs/react-form-library/style-tailwind) |
### Styling with CSS
You can style your field with adding CSS to the pre-assigned classes. This is how the classes are assigned:
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### Styling with Tailwind
You can also use Tailwind to extend the classes, like so:
```jsx
<URL name="url" label="Company website" outerClassName="bg-gray-800 my-5" />
```
Here are the in-depth guides for [CSS](/docs/react-form-library/style-css) or [Tailwind](/docs/react-form-library/style-tailwind).
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -1,37 +1,56 @@
import { Layout } from "@/components/docs/Layout";
import { Fence } from "@/components/shared/Fence";
import Image from "next/image";
import StyleClasses from "./sampleform.png";
export const meta = {
title: "React Form Library",
title: "Building React forms just got easy",
};
## Building React forms has never been quicker.
Every developer works with forms, few like their experience. Building Forms, especially in React, can be pretty annoying. State management, validation, form components, accessibility, internationalization and performance are all things you have to deal with, but don't really want to.
We make building - and maintaining - forms easier than ever in the React world.
We make building - and maintaining - forms easier than ever in the world of React.
### Formbricks React Example:
### Example
```jsx
import { Form, Text, Textarea, Submit } from "@formbricks/react";
import { Form, Text, Email, Checkbox, Submit } from "@formbricks/react";
import "@formbricks/react/styles.css";
export default function WaitlistForm() {
return (
<Form onSubmit={({ data, schema }) => console.log("data:", data, "schema:", schema)}>
<Text name="firstname" label="What's your first name?" validation="required" />
<Text name="lastname" label="What's your last name?" />
<Textarea name="about" label="About you" help="Please keep it short" />
<Submit label="Submit" />
<Text name="name" label="What's your name?" validation="required" />
<Email
name="email"
label="What's your email address?"
placeholder="you@example.com"
validation="required|email"
/>
<Checkbox
name="terms"
label="Terms & Conditions"
help="To use our service, please accept."
validation="accepted"
/>
<Submit label="Let's go!" />
</Form>
);
}
```
<div className="grid grid-cols-1 sm:grid-cols-2">
<Image
src={StyleClasses}
alt="Data log of a form created with the fastest react form builder"
className="rounded-lg"
/>
</div>
### Why is this easier already?
- One easy to use syntax for input types including labels, validation and help texts
- One easy to use syntax for all input types
- HTML & non-HTML input types available out of the box
- Easily maintainable with component-based approach
- All characteristics adjustable via props
@@ -43,8 +62,12 @@ export default function WaitlistForm() {
- Multi-page forms
- Accessibility
- Internationalization
- Form Templates
- Form Templates (content & styles)
The Formbricks React Library uses [React Hook Form](https://react-hook-form.com/) to benefit from their easy to use data handling and performance optimizations. The easy to use approach was inspired by the [FormKit for Vue.js](https://formkit.com/) Library.
### Shoutout
The Formbricks React Library is built on top of [React Hook Form](https://react-hook-form.com/) to make from their data handling and performance optimizations.
The developer experience is inspired by the great [FormKit for Vue.js](https://formkit.com/) Library.
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -84,17 +84,17 @@ Each form element component follows the same naming convention. This means that
className="rounded-lg"
/>
| CSS Class | Description |
| ------------------- | ----------------------------------------------------------------------------------------------- |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — most often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
| CSS Class | Description |
| ------------------- | ------------------------------------------------------------------------------------------ |
| formbricks-form | The outermost wrapping element. |
| formbricks-outer | A wrapper around the label input, help text and error messages. |
| formbricks-help | The help text itself. |
| formbricks-wrapper | A wrapper around the label and the input field (no help text). |
| formbricks-label | The label itself. |
| formbricks-inner | A wrapper around the input element. |
| formbricks-input | The input element itself. |
| formbricks-message | The element (or many elements) containing a message — often validation and error messages. |
| formbricks-messages | A wrapper around all the messages. |
### In React app, a simple form with one field looks like this:

View File

@@ -16,16 +16,19 @@ We love Tailwind! This is why Formbricks React natively supports Tailwind. All y
Here are all the props you can use to extend the styling of the different form elements:
| CSS class | Prop | Content |
| ------------------- | ----------------- | --------------------------------------------------- |
| formbricks-form | formClassName | The wrapper around the complete form |
| formbricks-outer | outerClassName | The wrapper around label, input field and help text |
| formbricks-help | helpClassName | The help text |
| formbricks-wrapper | wrapperClassName | The wrapper around the label and the input field |
| formbricks-label | labelClassName | The label |
| formbricks-inner | innerClassName | The input field |
| formbricks-input | inputClassName | The input |
| formbricks-message | messageClassName | The validation / error message itself |
| formbricks-messages | messagesClassName | Wrapper around all error messages |
| CSS class | Prop | Content |
| ------------------- | ----------------- | ------------------------------------------------------------ |
| formbricks-form | formClassName | The wrapper around the complete form |
| formbricks-outer | outerClassName | The wrapper around label, input field and help text |
| formbricks-legend | legendClassName | The label of the options group (only radio & checkbox input) |
| formbricks-help | helpClassName | The help text |
| formbricks-options | optionsClassName | A wrapper around all options (only radio & checkbox input) |
| formbricks-option | optionClassName | A wrapper around each option (only radio & checkbox input) |
| formbricks-wrapper | wrapperClassName | The wrapper around the label and the input field |
| formbricks-label | labelClassName | The label |
| formbricks-inner | innerClassName | The input field |
| formbricks-input | inputClassName | The input |
| formbricks-message | messageClassName | The validation / error message itself |
| formbricks-messages | messagesClassName | Wrapper around all error messages |
export default ({ children }) => <Layout meta={meta}>{children}</Layout>;

View File

@@ -12,7 +12,7 @@ Validation prevents users from submitting missing of false data.
To add a validation to your inputs, add the `validation` prop and write the rules in string syntax. Rules are divided by a pipe (`|`), e.g.:
```bash
```jsx
<Text name="age" label="What's your age?" validation="required|number|min:0|max:100" />
```
@@ -27,12 +27,15 @@ Only if all four requirements are met, the form can be submitted. If not an erro
### Available rules
| Rule | Explanation | Example |
| -------- | -------------------------------------------------------------------------- | ---------- |
| required | Only accepts non-empty fields | “required |
| number | Only accepts fields with a number or float value | “number” |
| min | Only accepts number values that are greater or equal to the value provided | min:10 |
| max | Only accepts number values that are greater or equal to the value provided | max:50 |
| Rule | Explanation | Example |
| ------------ | ------------------------------------------------------------------------------------------- | ---------- |
| **accepted** | Only accepts `true`, `1`, `"on"`, and `"yes"` as values (useful e.g. for single checkboxes) | "accepted" |
| **email** | Only accepts valid email address format | "email" |
| **min** | Only accepts number values that are greater or equal to the value provided | "min:10" |
| **max** | Only accepts number values that are greater or equal to the value provided | "max:50" |
| **number** | Only accepts fields with a number or float value | "number" |
| **required** | Only accepts non-empty fields | "required" |
| **url** | Only accepts valid url format (including protocol) | "url" |
### Message position

View File

@@ -28,9 +28,16 @@ All imported components will be included in the production build - even when you
| Component | Details | Use |
| -------------- | ------------------- | ------------------------------------------- |
| `<Form />` | Form Wrapper | Handles the onSubmit action, wraps the form |
| `<Email />` | HTML Email Input | Email input |
| `<Number />` | HTML Number Input | Integer or decimal value |
| `<Password />` | HTML Password Input | Strings of all kind |
| `<Phone />` | HTML Tel Input | Phone numbers |
| `<Radio />` | Radio Buttons | Single choice |
| `<Search />` | HTML Search Input | Search terms |
| `<Submit />` | Submit Button | Submits the form |
| `<Text />` | HTML Text Input | Single line of text input |
| `<Textarea />` | HTML Textarea Input | Multiple lines of text |
| `<Submit />` | Submit Button | Submits the form |
| `<URL />` | HTML URL Input | Submits the form |
---
@@ -38,13 +45,12 @@ All imported components will be included in the production build - even when you
| Component | Details | Use |
| ------------------ | ----------------------------- | ----------------------- |
| `<Radio />` | Radio Buttons | Single choice |
| `<Checkbox />` | Checkboxes | Multiple choice |
| `<Rating />` | Star-Rating | Simple rating in steps |
| `<Nps />` | Nps Score | Evaluate experience |
| `<Color />` | Color-Picker | Let user choose a color |
| `<Slider />` | Slider | Stepless rating |
| `<Autocomplete />` | Autocomplete possible options | e.g. Tags |
| `<Payments />` | Ready-made Stripe integration | Process Stripe payments |
and many more...

View File

@@ -58,9 +58,9 @@ const features = [
const FormHQPage = () => (
<Layout
title="FormHQ"
title="Formbricks HQ"
description="Manage all form data in one place. Analyze right here or pipe your data where you need it.">
<HeroTitle headingPt1="Form" headingTeal="HQ" />
<HeroTitle headingPt1="Formbricks" headingTeal="HQ" />
<Image
src={ImageFormHQ}
alt="Formbricks HQ user interface to create forms, manage submissions open source."

4
apps/hq/.eslintrc.js Normal file
View File

@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["formbricks"],
};

37
apps/hq/.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel
.vscode

9
apps/hq/CHANGELOG.md Normal file
View File

@@ -0,0 +1,9 @@
# @formbricks/hq
## 0.0.1
### Patch Changes
- Updated dependencies [bcecdc0]
- @formbricks/charts@0.1.0
- @formbricks/react@0.3.0

41
apps/hq/Dockerfile Normal file
View File

@@ -0,0 +1,41 @@
# Add lockfile and package.json's of isolated subworkspace
FROM node:16-alpine AS installer
RUN apk update
RUN apk --no-cache add curl libc6-compat
RUN curl -fsSL "https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linuxstatic-x64" -o /bin/pnpm; chmod +x /bin/pnpm;
WORKDIR /app
# First install the dependencies (as they change less often)
COPY . .
# Copy .env file because Docker don't follow symlinks
COPY .env /app/apps/hq/
RUN pnpm install
# Build the project
RUN pnpm dlx prisma generate
RUN pnpm turbo run build --filter=hq...
FROM node:16-alpine AS runner
RUN apk --no-cache add curl libc6-compat
RUN curl -fsSL "https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linuxstatic-x64" -o /bin/pnpm; chmod +x /bin/pnpm;
# Don't run production as root
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
USER nextjs
WORKDIR /home/nextjs
COPY --from=installer /app/apps/hq/next.config.js .
COPY --from=installer /app/apps/hq/package.json .
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=installer --chown=nextjs:nodejs /app/apps/hq/.next/standalone ./
COPY --from=installer --chown=nextjs:nodejs /app/apps/hq/.next/static ./apps/hq/.next/static
COPY --from=installer --chown=nextjs:nodejs /app/apps/hq/public ./apps/hq/public
COPY --from=installer --chown=nextjs:nodejs /app/packages/database/prisma ./packages/database/prisma
CMD pnpm dlx prisma migrate deploy && node apps/hq/server.js

80
apps/hq/README.md Normal file
View File

@@ -0,0 +1,80 @@
# Formbricks HQ
> still in development
Everything you always wanted (from a form tool)...
The days of scattered response data are counted. Manage all form data in one place. Analyze right here or pipe your data where you need it.
### How to run locally (for development)
To get the project running locally on your machine you need to have the following development tools installed:
- Node.JS (we recommend v16)
- [pnpm](https://pnpm.io/)
- [Docker](https://www.docker.com/) (to run PostgreSQL / MailHog)
1. Clone the project:
```sh
git clone https://github.com/formbricks/formbricks
```
and move into the directory
```sh
cd formbricks
```
2. Install Node.JS packages via pnpm. Don't have pnpm? Get it [here](https://pnpm.io/installation)
```sh
pnpm install
```
3. To make the process of installing a dev dependencies easier, we offer a [`docker-compose.yml`](https://docs.docker.com/compose/) with the following servers:
- a `postgres` container and environment variables preset to reach it,
- a `mailhog` container that acts as a mock SMTP server and shows received mails in a web UI (forwarded to your host's `localhost:8025`)
```sh
docker-compose -f docker-compose.dev.yml up -d
```
4. Create a `.env` file based on `.env.example` and change it according to your setup. If you are using a cloud based database or another mail server, you will need to update the `DATABASE_URL` and SMTP settings in your `.env` accordingly.
```sh
cp .env.example .env
```
5. Make sure your PostgreSQL Database Server is running. Then let prisma set up the database for you:
```sh
pnpm dlx prisma migrate dev
```
6. Start the development server:
```sh
pnpm dev
```
**You can now access the app on [https://localhost:3000](https://localhost:3000)**. You will be automatically redirected to the login. To use your local installation of formbricks, create a new account.
For viewing the confirmation email and other emails the system sends you, you can access mailhog at [https://localhost:8025](https://localhost:8025)
### Build
To build all apps and packages, run the following command:
```sh
pnpm build
```
### Develop
To develop all apps and packages, run the following command:
```sh
pnpm dev
```

5
apps/hq/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

36
apps/hq/next.config.js Normal file
View File

@@ -0,0 +1,36 @@
/**
* @type {import('next').NextConfig}
*/
var path = require("path");
module.exports = {
reactStrictMode: true,
output: "standalone",
experimental: {
outputFileTracingRoot: path.join(__dirname, "../../"),
/* serverComponentsExternalPackages: ["@prisma/client"], */
},
images: {
remotePatterns: [
{
protocol: "https",
hostname: "avatars.githubusercontent.com",
},
],
},
webpack: (config) => {
config.externals = [...(config.externals || []), "@prisma/client"];
// Important: return the modified config
return config;
},
async redirects() {
return [
{
source: "/",
destination: "/app/",
permanent: false,
},
];
},
};

47
apps/hq/package.json Normal file
View File

@@ -0,0 +1,47 @@
{
"private": true,
"name": "@formbricks/hq",
"version": "0.0.1",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@formbricks/charts": "workspace:*",
"@formbricks/react": "workspace:*",
"@formbricks/ui": "workspace:*",
"@headlessui/react": "^1.7.4",
"@heroicons/react": "^2.0.13",
"@vercel/analytics": "^0.1.5",
"bcryptjs": "^2.4.3",
"clsx": "^1.2.1",
"date-fns": "^2.29.3",
"jsonwebtoken": "^8.5.1",
"next": "^13.0.5",
"next-auth": "^4.17.0",
"nextjs-cors": "^2.1.2",
"nodemailer": "^6.8.0",
"prismjs": "^1.29.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.6.0",
"react-loader-spinner": "^5.3.4",
"react-toastify": "^9.1.1",
"swr": "^1.3.0"
},
"devDependencies": {
"@formbricks/database": "workspace:*",
"@formbricks/tailwind-config": "workspace:*",
"@formbricks/tsconfig": "workspace:*",
"@types/node": "^18.11.9",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9",
"autoprefixer": "^10.4.13",
"eslint": "^8.28.0",
"eslint-config-formbricks": "workspace:*",
"postcss": "^8.4.19",
"typescript": "^4.9.3"
}
}

1507
apps/hq/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#0f172a</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M2030 6463 c-392 -63 -688 -348 -758 -728 -4 -22 -7 -269 -7 -550 l0
-510 1815 1 c998 0 1842 3 1875 7 139 14 256 58 386 143 266 175 404 445 395
774 -8 281 -111 488 -340 683 -47 41 -166 105 -246 133 -153 54 -123 53 -1665
52 -786 -1 -1441 -3 -1455 -5z"/>
<path d="M1260 3500 l0 -896 1843 1 c1013 1 1865 5 1892 10 28 4 89 20 137 36
302 98 527 355 595 679 3 14 6 82 8 151 8 263 -74 468 -260 655 -103 104 -207
170 -337 213 -135 45 -91 44 -2025 46 l-1853 2 0 -897z"/>
<path d="M1263 2314 c-6 -17 2 -1036 9 -1059 3 -11 11 -40 17 -65 27 -105 104
-256 171 -334 184 -216 432 -328 708 -321 142 4 171 8 270 41 313 105 532 351
597 672 14 70 16 153 16 573 0 270 -2 494 -6 497 -4 4 -405 7 -892 7 -649 0
-887 -3 -890 -11z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,19 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@@ -0,0 +1,67 @@
"use client";
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline";
import { ArrowDownIcon, ArrowUpIcon } from "@heroicons/react/24/solid";
import clsx from "clsx";
import React from "react";
interface Props {
value: string | number;
label: string;
toolTipText?: string;
trend?: number;
smallerText?: boolean;
}
const AnalyticsCard: React.FC<Props> = ({ value, label, toolTipText, trend, smallerText }) => {
return (
<div className="rounded-md bg-white shadow-md">
<div key={label} className="px-4 py-5 sm:p-6">
<dt className="has-tooltip inline-flex text-base font-normal text-gray-900">
{label}{" "}
{toolTipText && (
<QuestionMarkCircleIcon className="text-red hover:text-ui-gray-dark ml-1 h-4 w-4" />
)}
{toolTipText && (
<span className="tooltip -mt-6 -ml-8 flex grow rounded bg-gray-600 p-1 px-4 text-center text-xs text-white shadow-lg">
{toolTipText}
</span>
)}
</dt>
<dd className="mt-1 flex items-baseline justify-between md:block lg:flex">
<div
className={clsx(
smallerText ? "text-lg" : "text-xl",
"flex items-baseline text-xl font-semibold text-gray-800"
)}>
{value}
</div>
{trend && (
<div
className={clsx(
trend >= 0 ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800",
"inline-flex items-baseline rounded-full px-2.5 py-0.5 text-sm font-medium md:mt-2 lg:mt-0"
)}>
{trend >= 0 ? (
<ArrowUpIcon
className="-ml-1 mr-0.5 h-5 w-5 flex-shrink-0 self-center text-green-500"
aria-hidden="true"
/>
) : (
<ArrowDownIcon
className="-ml-1 mr-0.5 h-5 w-5 flex-shrink-0 self-center text-red-500"
aria-hidden="true"
/>
)}
<span className="sr-only">{trend >= 0 ? "Increased" : "Decreased"} by</span>
{trend} %
</div>
)}
</dd>
</div>
</div>
);
};
export default AnalyticsCard;

View File

@@ -0,0 +1,39 @@
"use client";
import { Button } from "@formbricks/ui";
import React from "react";
interface Props {
children: React.ReactNode;
onClick?: () => void;
alertText: string;
hintText: string;
buttonText?: string;
borderStyles?: string;
hasButton?: boolean;
}
const EmptyPageFiller: React.FC<Props> = ({
children,
onClick = () => {},
alertText,
hintText,
buttonText,
borderStyles,
hasButton = false,
}) => {
return (
<div className={`mx-auto mt-8 rounded-lg border border-slate-200 p-8 text-center ` + borderStyles}>
{children}
<h3 className="mt-5 text-base font-bold text-slate-400">{alertText}</h3>
<p className="mt-1 text-xs font-light text-slate-400">{hintText}</p>
{hasButton && (
<div className="mt-6">
<Button onClick={onClick}>{buttonText}</Button>
</div>
)}
</div>
);
};
export default EmptyPageFiller;

View File

@@ -0,0 +1,7 @@
"use client";
import { TailSpin } from "react-loader-spinner";
export default function LoadingSpinner() {
return <TailSpin color="#1f2937" height={30} width={30} />;
}

Some files were not shown because too many files have changed in this diff Show More