feat: faq section on landing page as well as docs

feat: faq section on landing page as well as docs
This commit is contained in:
Johannes
2023-10-04 13:31:56 +05:45
committed by GitHub
13 changed files with 632 additions and 155 deletions
@@ -14,7 +14,7 @@ export const meta = {
# Troubleshooting
Here you'll find help with Frequently Recurring Problems
Here you'll find help with frequently recurring problems
## "The app doesn't work after doing a prisma migration"
@@ -24,7 +24,7 @@ This can happen but fear not, the fix is easy: Delete the application storage of
src={ClearAppData}
alt="Demo App Preview"
quality="100"
className="rounded-lg max-w-full sm:max-w-3xl"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## "I ran 'pnpm i' but there seems to be an error with the packages"
@@ -74,9 +74,9 @@ However, in our experience it's better to run `pnpm dev` than having two termina
src={UncaughtPromise}
alt="Uncaught promise"
quality="100"
className="rounded-lg max-w-full sm:max-w-3xl"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
This happens when you're using the Demo App and delete the Person within the Formbricks app which the widget is currently connected with. We're fixing it, but you can also just logout your test person and reload the page to get rid of it.
<Image src={Logout} alt="Logout Person" quality="100" className="rounded-lg max-w-full sm:max-w-3xl" />
<Image src={Logout} alt="Logout Person" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
+14
View File
@@ -0,0 +1,14 @@
import FAQ from "@/components/docs/docsFaq";
export const meta = {
title: "FAQ",
description: "Frequently Asked Questions about Formbricks and how to use it.",
};
#### FAQ
# Frequently Asked Questions
Here you'll find help with frequently recurring problems. If you can't find an answer to your question, please join our [Discord server](https://formbricks.com/discord).
<FAQ />
@@ -45,5 +45,3 @@ Natively embed qualitative user research into your B2B SaaS. Leverage Best Pract
</div>
<GettingStarted />
<BestPractices />
@@ -258,6 +258,7 @@ export const navigation: Array<NavGroup> = [
{ title: "Gitpod", href: "/docs/contributing/gitpod" },
{ title: "Demo App", href: "/docs/contributing/demo" },
{ title: "Troubleshooting", href: "/docs/contributing/troubleshooting" },
{ title: "FAQ", href: "/docs/faq" },
],
},
{
@@ -0,0 +1,69 @@
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@formbricks/ui";
import FaqJsonLdComponent from "./faQJsonLD";
const FAQ_DATA = [
{
question: "What is an environment ID?",
answer: () => (
<>
The environment ID is a unique identifier associated with each Environment in Formbricks,
distinguishing between different setups like production, development, etc.
</>
),
},
{
question: "How can I implement authentication for the Formbricks API?",
answer: () => (
<>
Formbricks provides 2 types of API keys for each environment ie development and production. You can
generate, view, and manage these keys in the Settings section on the Admin dashboard. Include the API
key in your requests to authenticate and gain access to Formbricks functionalities.
</>
),
},
{
question: "Can I run the deployment shell script on any server?",
answer: () => (
<>
You can run it on any machine you own as long as its running a <b> Linux Ubuntu </b> distribution. And
to forward the requests, make sure you have an <b>A record</b> setup for your domain pointing to the
server.
</>
),
},
{
question: "Can I self-host Formbricks?",
answer: () => (
<>
Absolutely! We provide an option for users to host Formbricks on their own server, ensuring even more
control over data and compliance. And the best part? Self-hosting is available for free, always. For
documentation on self hosting, click{" "}
<a href="/docs/self-hosting/deployment" className="text-brand-dark dark:text-brand-light">
here
</a>
.
</>
),
},
];
export const faqJsonLdData = FAQ_DATA.map((faq) => ({
questionName: faq.question,
acceptedAnswerText: faq.answer(),
}));
export default function FAQ() {
return (
<>
<FaqJsonLdComponent data={faqJsonLdData} />
<Accordion type="single" collapsible>
{FAQ_DATA.map((faq, index) => (
<AccordionItem key={`item-${index}`} value={`item-${index + 1}`} className="not-prose mb-0 mt-0">
<AccordionTrigger>{faq.question}</AccordionTrigger>
<AccordionContent>{faq.answer()}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</>
);
}
@@ -0,0 +1,12 @@
"use client";
import { FAQPageJsonLd } from "next-seo";
export default function FaqJsonLdComponent({ data }) {
const faqEntities = data.map(({ question, answer }) => ({
questionName: question,
acceptedAnswerText: answer,
}));
return <FAQPageJsonLd mainEntity={faqEntities} />;
}
@@ -0,0 +1,87 @@
import { FAQPageJsonLd } from "next-seo";
import HeadingCentered from "@/components/shared/HeadingCentered";
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@formbricks/ui";
const FAQ_DATA = [
{
question: "What is Formbricks?",
answer: () => (
<>
Formbricks is an open-source Experience Management tool that helps businesses understand what
customers think and feel about their products. It integrates natively into your platform to conduct
user research with a focus on data privacy and minimal development intervention.
</>
),
},
{
question: "How do I integrate Formbricks into my application?",
answer: () => (
<>
Integrating Formbricks is a breeze. Simply copy a script tag to your HTML head, or use NPM to install
Formbricks for platforms like React, Vue, Svelte, etc. Once installed, initialize Formbricks with your
environment details. Learn more with our framework guides{" "}
<a href="/docs/getting-started/framework-guides" className="text-brand-dark dark:text-brand-light">
here
</a>
.
</>
),
},
{
question: "Is Formbricks GDPR compliant?",
answer: () => (
<>
Yes, Formbricks is fully GDPR compliant. Whether you use our cloud solution or decide to self-host, we
ensure compliance with all data privacy regulations.
</>
),
},
{
question: "Can I self-host Formbricks?",
answer: () => (
<>
Absolutely! We provide an option for users to host Formbricks on their own server, ensuring even more
control over data and compliance. And the best part? Self-hosting is available for free, always. For
documentation on self hosting, click{" "}
<a href="/docs/self-hosting/deployment" className="text-brand-dark dark:text-brand-light">
here
</a>
.
</>
),
},
{
question: "How does Formbricks pricing work?",
answer: () => (
<>
Formbricks offers a Free forever plan on the cloud that includes unlimited surveys, in-product
surveys, and more. We also provide a self-hosting option which includes all free features and more,
available at no cost. If you require additional features or responses, check out our pricing section
above for more details.
</>
),
},
];
const faqJsonLdData = FAQ_DATA.map((faq) => ({
questionName: faq.question,
acceptedAnswerText: faq.answer(),
}));
export default function FAQ() {
return (
<div className="max-w-7xl py-4 sm:px-6 sm:pb-6 lg:px-8" id="faq">
<FAQPageJsonLd mainEntity={faqJsonLdData} />
<HeadingCentered heading="Frequently Asked Questions" teaser="FAQ" closer />
<Accordion type="single" collapsible className="px-4 sm:px-0">
{FAQ_DATA.map((faq, index) => (
<AccordionItem key={`item-${index}`} value={`item-${index + 1}`}>
<AccordionTrigger>{faq.question}</AccordionTrigger>
<AccordionContent>{faq.answer()}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
);
}
+8 -5
View File
@@ -1,11 +1,12 @@
import Layout from "@/components/shared/Layout";
import Hero from "@/components/home/Hero";
import Faq from "@/components/home/Faq";
import Features from "@/components/home/Features";
import Highlights from "@/components/home/Highlights";
import BreakerCTA from "@/components/shared/BreakerCTA";
import Steps from "@/components/home/Steps";
import GitHubSponsorship from "@/components/home/GitHubSponsorship";
import Hero from "@/components/home/Hero";
import Highlights from "@/components/home/Highlights";
import Steps from "@/components/home/Steps";
import BestPractices from "@/components/shared/BestPractices";
import BreakerCTA from "@/components/shared/BreakerCTA";
import Layout from "@/components/shared/Layout";
const IndexPage = () => (
<Layout
@@ -41,6 +42,8 @@ const IndexPage = () => (
href="https://app.formbricks.com/auth/signup"
inverted
/>
<Faq />
</Layout>
);
@@ -13,6 +13,8 @@ module.exports = {
animation: {
"ping-slow": "ping 2s cubic-bezier(0, 0, 0.2, 1) infinite",
shake: "shake 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both",
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
@@ -49,6 +51,14 @@ module.exports = {
transform: "translate3d(4px, 0, 0)",
},
},
"accordion-down": {
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
},
},
maxWidth: {
"8xl": "88rem",
+53
View File
@@ -0,0 +1,53 @@
"use client";
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronDown } from "lucide-react";
import { cn } from "@formbricks/lib/cn";
const Accordion = AccordionPrimitive.Root;
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item ref={ref} className={cn("border-b", className)} {...props} />
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-center justify-between py-4 text-left font-medium text-slate-800 transition-all hover:underline dark:text-slate-100 [&[data-state=open]>svg]:rotate-180",
className
)}
{...props}>
{children}
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className={cn(
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm text-slate-500 transition-all dark:text-slate-300",
className
)}
{...props}>
<div className="pb-4 pt-0">{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
+3 -2
View File
@@ -1,3 +1,4 @@
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./components/Accordion";
export { AdvancedOptionToggle } from "./components/AdvancedOptionToggle";
export { Alert, AlertDescription, AlertTitle } from "./components/Alert";
export { PersonAvatar, ProfileAvatar } from "./components/Avatars";
@@ -50,6 +51,7 @@ export {
export { ErrorComponent } from "./components/ErrorComponent";
export { Input } from "./components/Input";
export { Label } from "./components/Label";
export { NoMobileOverlay } from "./components/NoMobileOverlay";
export { Pagination } from "./components/Pagination";
export { PasswordInput } from "./components/PasswordInput";
export { Popover, PopoverContent, PopoverTrigger } from "./components/Popover";
@@ -67,12 +69,11 @@ export {
SelectTrigger,
SelectValue,
} from "./components/Select";
export { Skeleton } from "./components/Skeleton";
export { Switch } from "./components/Switch";
export { TabBar } from "./components/TabBar";
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/Tooltip";
export { AddVariablesDropdown, Editor } from "./components/editor";
export { Skeleton } from "./components/Skeleton";
export { NoMobileOverlay } from "./components/NoMobileOverlay";
/* Icons */
export { AngryBirdRage2Icon } from "./components/icons/AngryBirdRage2Icon";
+1
View File
@@ -29,6 +29,7 @@
"@lexical/react": "^0.12.2",
"@lexical/rich-text": "^0.12.2",
"@lexical/table": "^0.12.2",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
+370 -142
View File
File diff suppressed because it is too large Load Diff