diff --git a/.github/actions/cache-build-web/action.yml b/.github/actions/cache-build-web/action.yml index 151d6e3c9d..185bf49fbd 100644 --- a/.github/actions/cache-build-web/action.yml +++ b/.github/actions/cache-build-web/action.yml @@ -1,5 +1,13 @@ name: Build & Cache Web App +on: + workflow_dispatch: + inputs: + e2e_testing_mode: + description: "Set E2E Testing Mode" + required: false + default: "0" + runs: using: "composite" steps: @@ -41,6 +49,11 @@ runs: run: cp .env.example .env shell: bash + - name: Add E2E Testing Mode + run: | + echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> $GITHUB_ENV + shell: bash + - name: Generate Random ENCRYPTION_KEY run: | SECRET=$(openssl rand -hex 32) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index fbd1364007..85493a8339 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -13,6 +13,8 @@ jobs: - name: Build & Cache Web Binaries uses: ./.github/actions/cache-build-web + with: + e2e_testing_mode: "1" - name: Install pnpm uses: pnpm/action-setup@v2 diff --git a/.github/workflows/release-docker-github-data-migration.yml b/.github/workflows/release-docker-github-data-migration.yml index 263068860c..74bfa98657 100644 --- a/.github/workflows/release-docker-github-data-migration.yml +++ b/.github/workflows/release-docker-github-data-migration.yml @@ -33,9 +33,7 @@ jobs: - name: Install cosign if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 - with: - cosign-release: "v2.1.1" + uses: sigstore/cosign-installer@v3.5.0 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 diff --git a/.github/workflows/release-docker-github.yml b/.github/workflows/release-docker-github.yml index b51156b034..c6b045ac66 100644 --- a/.github/workflows/release-docker-github.yml +++ b/.github/workflows/release-docker-github.yml @@ -41,9 +41,7 @@ jobs: # https://github.com/sigstore/cosign-installer - name: Install cosign if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 - with: - cosign-release: "v2.1.1" + uses: sigstore/cosign-installer@v3.5.0 # Login against a Docker registry except on PR # https://github.com/docker/login-action diff --git a/README.md b/README.md index 03d3b5afa0..0dca1381d7 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,7 @@ Additional to the AGPL licensed Formbricks core, this repository contains code l ### White-Labeling Formbricks and Other Licensing Needs -If you have other licensing requirements such as White-Labeling please [send us an email](mailto:hola@formbricks.com). +We currently do not offer Formbricks white-labeled. Any other needs? [Send us an email](mailto:hola@formbricks.com). ### Why charge for Enterprise Features? diff --git a/apps/demo/components/SurveySwitch.tsx b/apps/demo/components/SurveySwitch.tsx index 002ddaa433..b1a72b26a7 100644 --- a/apps/demo/components/SurveySwitch.tsx +++ b/apps/demo/components/SurveySwitch.tsx @@ -13,12 +13,16 @@ export const SurveySwitch = ({ value, formbricks }: SurveySwitchProps) => { formbricks.logout(); window.location.href = `/${v}`; }}> - + - Website Surveys - App Surveys + + Website Surveys + + + App Surveys + ); diff --git a/apps/demo/package.json b/apps/demo/package.json index 2a5da44f85..0cbfa36cc2 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -13,10 +13,10 @@ "dependencies": { "@formbricks/js": "workspace:*", "@formbricks/ui": "workspace:*", - "lucide-react": "^0.373.0", + "lucide-react": "^0.378.0", "next": "14.2.3", - "react": "18.2.0", - "react-dom": "18.2.0" + "react": "18.3.1", + "react-dom": "18.3.1" }, "devDependencies": { "eslint-config-formbricks": "workspace:*", diff --git a/apps/demo/pages/app/index.tsx b/apps/demo/pages/app/index.tsx index acc2cec63a..1c8d816398 100644 --- a/apps/demo/pages/app/index.tsx +++ b/apps/demo/pages/app/index.tsx @@ -115,7 +115,7 @@ export default function AppPage({}) {
-
+

Reset person / pull data from Formbricks app

@@ -136,26 +136,6 @@ export default function AppPage({}) {

-
-
- -
-
-

- This button sends a{" "} - - Code Action - {" "} - to the Formbricks API called 'Code Action'. You will find it in the Actions Tab. -

-
-
-
+

Reset person / pull data from Formbricks app

@@ -135,60 +135,6 @@ export default function AppPage({}) { try again.

- -
-
- -
-
-

- This button sends an Action to the Formbricks API called 'New Session'. You will - find it in the Actions Tab. -

-
-
- -
-
- -
-
-

- This button sends an Action to the Formbricks API called 'Exit Intent'. You can also - move your mouse to the top of the browser to trigger the exit intent. -

-
-
- -
-
- -
-
-

- This button sends an Action to the Formbricks API called '50% Scroll'. You can also - scroll down to trigger the 50% scroll. -

-
-
diff --git a/apps/formbricks-com/.env.example b/apps/docs/.env.example similarity index 100% rename from apps/formbricks-com/.env.example rename to apps/docs/.env.example diff --git a/apps/formbricks-com/.eslintrc.js b/apps/docs/.eslintrc.js similarity index 100% rename from apps/formbricks-com/.eslintrc.js rename to apps/docs/.eslintrc.js diff --git a/apps/formbricks-com/.gitignore b/apps/docs/.gitignore similarity index 95% rename from apps/formbricks-com/.gitignore rename to apps/docs/.gitignore index a324a42299..aeb7219b23 100644 --- a/apps/formbricks-com/.gitignore +++ b/apps/docs/.gitignore @@ -35,3 +35,4 @@ yarn-error.log* next-env.d.ts public/sitemap*.xml +public/robots.txt \ No newline at end of file diff --git a/apps/formbricks-com/LICENSE.md b/apps/docs/LICENSE.md similarity index 100% rename from apps/formbricks-com/LICENSE.md rename to apps/docs/LICENSE.md diff --git a/apps/docs/app/app-surveys/actions/images/I1.webp b/apps/docs/app/app-surveys/actions/images/I1.webp new file mode 100644 index 0000000000..88c128dc54 Binary files /dev/null and b/apps/docs/app/app-surveys/actions/images/I1.webp differ diff --git a/apps/docs/app/app-surveys/actions/images/I2.webp b/apps/docs/app/app-surveys/actions/images/I2.webp new file mode 100644 index 0000000000..87058593e6 Binary files /dev/null and b/apps/docs/app/app-surveys/actions/images/I2.webp differ diff --git a/apps/docs/app/app-surveys/actions/page.mdx b/apps/docs/app/app-surveys/actions/page.mdx new file mode 100644 index 0000000000..41670b5e69 --- /dev/null +++ b/apps/docs/app/app-surveys/actions/page.mdx @@ -0,0 +1,116 @@ +import { MdxImage } from "@/components/MdxImage"; + +import I1 from "./images/I1.webp"; +import I2 from "./images/I2.webp"; + +export const metadata = { + title: "Using Actions in Formbricks | Fine-tuning User Moments", + description: + "Dive deep into how actions in Formbricks help products and teams to engage users at precise moments in their journey. Discover the power of actions, from coding to no-code setups, to refine user targeting and generate richer, more detailed user insights.", +}; + +#### App Surveys + +# Actions & Targeting + +Understanding user thoughts and feelings at critical moments in their journey is pivotal. To achieve this, Formbricks uses user-centric actions that trigger surveys at precisely the right time. Actions are essentially notifications sent from your application to Formbricks when predefined user activities occur, making it possible to gather insights during key interactions. + + + Ensure that you’ve **initialized Formbricks with a userId** to fully utilize this feature along with other + app survey capabilities. + + +## **How Do Actions Work?** + +Actions in Formbricks App Surveys are deeply integrated with user activities within your app. When a user performs a specified action, the Formbricks widget detects this activity and can present a survey to that specific user if the trigger conditions match of that survey, while also recording the event. This capability ensures that surveys are not only triggered at the right time but are also tailored to the user’s recent interactions within the app. You can set up these actions through a user-friendly No-Code interface within the Formbricks dashboard. + +## **Why Are Actions Useful?** + +Actions are invaluable for enhancing survey relevance and effectiveness: + +- **Personalized Engagement**: Surveys triggered by user actions ensure content is highly relevant and engaging, matching each user’s current context. +- **User Attributes**: By tying surveys to specific user attributes, such as activity levels or feature usage, you can customize the survey experience to reflect individual user profiles. +- **User Segments**: Analyze action data to create detailed user segments, targeting specific groups with surveys that are pertinent to their behaviors or interactions within the app. +- **User Targeting**: Precise targeting based on user actions and attributes ensures that surveys are shown only to users who meet certain criteria, enhancing the relevance and effectiveness of each survey. + +## **Setting Up No-Code Actions** + +Formbricks offers an intuitive No-Code interface that allows you to configure actions without needing to write any code. + +To add a No-Code Action: + +1. Visit the Formbricks Dashboard & switch to the Actions tab: + + + +2. Now click on “Add Action” + + + +Here are three types of No-Code actions you can set up: + +### **1. Page URL Action** + +This action is triggered when a user visits a specific page within your application. You can define the URL match conditions as follows: + +- **exactMatch**: Triggers the action when the URL exactly matches the specified string. +- **contains**: Activates when the URL contains the specified substring. +- **startsWith**: Fires when the URL starts with the specified string. +- **endsWith**: Executes when the URL ends with the specified string. +- **notMatch**: Triggers when the URL does not match the specified condition. +- **notContains**: Activates when the URL does not contain the specified substring. + +### **2. innerText Action** + +Checks if the innerText of a clicked HTML element, like a button label, matches a specific text. This action allows you to display a survey based on text interactions within your application. + +### **3. CSS Selector Action** + +This action verifies if a clicked HTML element matches a provided CSS selector, such as a class, ID, or any other CSS selector used in your website. It enables survey triggers based on element interactions. + +You can have an action use combination of the 3 types as you wish + +## **Setting Up Code Actions** + +For more granular control, you can implement actions directly in your codebase: + +1. **Configure the Action**: First, add the action via the Formbricks web interface to make it available for survey configuration. + After that you can fire an action using `formbricks.track()` + +2. **Track an Action**: Use formbricks.track() to send an action event to Formbricks. + + + + +```javascript +formbricks.track("Action Name"); +``` + + + +Here is an example of how to fire an action when a user clicks a button: + + + +```javascript +const handleClick = () => { + formbricks.track("Button Clicked"); +}; + +return ; +``` + + + + +This documentation frames actions around user interactions, emphasizing the connection between the user's activities and the survey experience. By leveraging user-centric actions, you can create highly targeted and timely surveys that resonate with users and yield valuable insights. diff --git a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/germans-gpt.webp b/apps/docs/app/app-surveys/advanced-targeting/germans-gpt.webp similarity index 100% rename from apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/germans-gpt.webp rename to apps/docs/app/app-surveys/advanced-targeting/germans-gpt.webp diff --git a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/hni.webp b/apps/docs/app/app-surveys/advanced-targeting/hni.webp similarity index 100% rename from apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/hni.webp rename to apps/docs/app/app-surveys/advanced-targeting/hni.webp diff --git a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/page.mdx b/apps/docs/app/app-surveys/advanced-targeting/page.mdx similarity index 90% rename from apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/page.mdx rename to apps/docs/app/app-surveys/advanced-targeting/page.mdx index faf91e577c..57b90b3bb6 100644 --- a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/page.mdx +++ b/apps/docs/app/app-surveys/advanced-targeting/page.mdx @@ -1,4 +1,5 @@ -import { MdxImage } from "@/components/shared/MdxImage"; +import { MdxImage } from "@/components/MdxImage"; +import { ResponsiveVideo } from "@/components/ResponsiveVideo"; import GermansGpt from "./germans-gpt.webp"; import Hni from "./hni.webp"; @@ -12,19 +13,15 @@ export const metadata = { "Advanced Targeting allows you to show surveys to just the right group of people. You can target surveys based on user attributes, user events, metadata , literally anything! This helps you get more relevant feedback and make data-driven decisions. All of this without writing a single line of code.", }; -#### In-App Surveys +#### App Surveys # Advanced Targeting Advanced Targeting allows you to show surveys to the right group of people. You can target surveys based on user attributes, user events, and more instead of spraying and praying. This helps you get more relevant feedback and make data-driven decisions. All of this without writing a single line of code. - + + ## How to setup Advanced Targeting diff --git a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/power-users.webp b/apps/docs/app/app-surveys/advanced-targeting/power-users.webp similarity index 100% rename from apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/power-users.webp rename to apps/docs/app/app-surveys/advanced-targeting/power-users.webp diff --git a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/ride-hailing.webp b/apps/docs/app/app-surveys/advanced-targeting/ride-hailing.webp similarity index 100% rename from apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/ride-hailing.webp rename to apps/docs/app/app-surveys/advanced-targeting/ride-hailing.webp diff --git a/apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/upsell-miro.webp b/apps/docs/app/app-surveys/advanced-targeting/upsell-miro.webp similarity index 100% rename from apps/formbricks-com/app/docs/in-app-surveys/advanced-targeting/upsell-miro.webp rename to apps/docs/app/app-surveys/advanced-targeting/upsell-miro.webp diff --git a/apps/formbricks-com/components/docs/Libraries.tsx b/apps/docs/app/app-surveys/framework-guides/components/Libraries.tsx similarity index 87% rename from apps/formbricks-com/components/docs/Libraries.tsx rename to apps/docs/app/app-surveys/framework-guides/components/Libraries.tsx index b5dd274ee9..09a380d8db 100644 --- a/apps/formbricks-com/components/docs/Libraries.tsx +++ b/apps/docs/app/app-surveys/framework-guides/components/Libraries.tsx @@ -1,11 +1,10 @@ -import logoHtml from "@/images/logos/html5.svg"; -import logoNextjs from "@/images/logos/nextjs.svg"; -import logoReactJs from "@/images/logos/reactjs.svg"; -import logoVueJs from "@/images/logos/vuejs.svg"; +import { Button } from "@/components/Button"; +import logoHtml from "@/images/frameworks/html5.svg"; +import logoNextjs from "@/images/frameworks/nextjs.svg"; +import logoReactJs from "@/images/frameworks/reactjs.svg"; +import logoVueJs from "@/images/frameworks/vuejs.svg"; import Image from "next/image"; -import { Button } from "./Button"; - const libraries = [ { href: "#html", diff --git a/apps/docs/app/app-surveys/framework-guides/images/env-id.webp b/apps/docs/app/app-surveys/framework-guides/images/env-id.webp new file mode 100644 index 0000000000..1c148f2e64 Binary files /dev/null and b/apps/docs/app/app-surveys/framework-guides/images/env-id.webp differ diff --git a/apps/formbricks-com/app/docs/getting-started/framework-guides/react-in-app-survey-app-popup-form.webp b/apps/docs/app/app-surveys/framework-guides/images/react-in-app-survey-app-popup-form.webp similarity index 100% rename from apps/formbricks-com/app/docs/getting-started/framework-guides/react-in-app-survey-app-popup-form.webp rename to apps/docs/app/app-surveys/framework-guides/images/react-in-app-survey-app-popup-form.webp diff --git a/apps/formbricks-com/app/docs/getting-started/framework-guides/widget-connected.webp b/apps/docs/app/app-surveys/framework-guides/images/widget-connected.webp similarity index 100% rename from apps/formbricks-com/app/docs/getting-started/framework-guides/widget-connected.webp rename to apps/docs/app/app-surveys/framework-guides/images/widget-connected.webp diff --git a/apps/formbricks-com/app/docs/getting-started/framework-guides/widget-not-connected.webp b/apps/docs/app/app-surveys/framework-guides/images/widget-not-connected.webp similarity index 100% rename from apps/formbricks-com/app/docs/getting-started/framework-guides/widget-not-connected.webp rename to apps/docs/app/app-surveys/framework-guides/images/widget-not-connected.webp diff --git a/apps/formbricks-com/app/docs/getting-started/framework-guides/page.mdx b/apps/docs/app/app-surveys/framework-guides/page.mdx similarity index 73% rename from apps/formbricks-com/app/docs/getting-started/framework-guides/page.mdx rename to apps/docs/app/app-surveys/framework-guides/page.mdx index 6a04df4bcb..279db721a1 100644 --- a/apps/formbricks-com/app/docs/getting-started/framework-guides/page.mdx +++ b/apps/docs/app/app-surveys/framework-guides/page.mdx @@ -1,10 +1,10 @@ -import { Libraries } from "@/components/docs/Libraries"; -import { MdxImage } from "@/components/shared/MdxImage"; +import { MdxImage } from "@/components/MdxImage"; -import SetupChecklist from "./env-id.webp"; -import ReactApp from "./react-in-app-survey-app-popup-form.webp"; -import WidgetConnected from "./widget-connected.webp"; -import WidgetNotConnected from "./widget-not-connected.webp"; +import { Libraries } from "./components/Libraries"; + +import ReactApp from "./images/react-in-app-survey-app-popup-form.webp"; +import WidgetConnected from "./images/widget-connected.webp"; +import WidgetNotConnected from "./images/widget-not-connected.webp"; export const metadata = { title: "Integrate Formbricks: Comprehensive Framework Guide & Integration Tutorial", @@ -14,8 +14,8 @@ export const metadata = { # Framework Guides -One can integrate Formbricks into their app using multiple options! Checkout the options below that we provide! If you are looking -for something else, please [join our Discord!](https://formbricks.com/discord) and we would be glad to help. {{ className: 'lead' }} +One can integrate Formbricks App Survey SDK into their app using multiple options! Checkout the options below that we provide! If you are looking +for something else, please [join our Discord!](https://formbricks.com/discord) and we would be glad to help. @@ -25,15 +25,8 @@ for something else, please [join our Discord!](https://formbricks.com/discord) a Before getting started, make sure you have: -1. A web application in your desired framework is set up and running. -2. A Formbricks account with access to your environment ID and API host. You can find these in the **Setup Checklist** in the Settings: - - +1. A web application (behind your user authentication system) in your desired framework is set up and running. +2. A Formbricks account with access to your environment ID and API host. You can find these in the **Setup Checklist** in the Settings. --- @@ -46,7 +39,11 @@ All you need to do is copy a ` ``` @@ -58,11 +55,12 @@ All you need to do is copy a ` + +``` + + +### Required customizations to be made + + + + Formbricks Environment ID. + + + + + URL of the hosted Formbricks instance. + + + +Refer to our [Example HTML project](https://github.com/formbricks/examples/tree/main/html) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup! + +--- + +## ReactJS + +Install the Formbricks SDK using one of the package managers ie `npm`,`pnpm`,`yarn`. + + + +```shell {{ title: 'npm' }} +npm install @formbricks/js +``` +```shell {{ title: 'pnpm' }} +pnpm add @formbricks/js +``` +```shell {{ title: 'yarn' }} +yarn add @formbricks/js +``` + + + +Now, update your App.js/ts file to initialise Formbricks. + + + +```js +// other imports +import formbricks from "@formbricks/js/website"; + +if (typeof window !== "undefined") { + formbricks.init({ + environmentId: "", + apiHost: "", + }); +} + +function App() { + // your own app +} + +export default App; +``` + + + +### Required customizations to be made + + + + Formbricks Environment ID. + + + + + URL of the hosted Formbricks instance. + + + +Refer to our [Example ReactJs project](https://github.com/formbricks/examples/tree/main/reactjs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup! + +--- + +## NextJS + +NextJs projects typically follow two main conventions: the App Directory and the Pages Directory. +To ensure smooth integration with the Formbricks SDK, which operates solely on the client side, follow the +guidelines for each convention below: + +- App directory: You will have to define a new component in `app/formbricks.tsx` file and call it in your `app/layout.tsx` file. +- Pages directory: You will have to visit your `_app.tsx` and just initialise Formbricks there. + +Code snippets for the integration for both conventions are provided to further assist you. + + + +```shell {{ title: 'npm' }} +npm install @formbricks/js +``` +```shell {{ title: 'pnpm' }} +pnpm add @formbricks/js +``` +```shell {{ title: 'yarn' }} +yarn add @formbricks/js +``` + + + + +### App Directory + + + + +```tsx {{title: 'Typescript'}} +"use client"; + +import { usePathname, useSearchParams } from "next/navigation"; +import { useEffect } from "react"; + +import formbricks from "@formbricks/js/website"; + +export default function FormbricksProvider() { + const pathname = usePathname(); + const searchParams = useSearchParams(); + + useEffect(() => { + formbricks.init({ + environmentId: "", + apiHost: "", + }); + }, []); + + useEffect(() => { + formbricks?.registerRouteChange(); + }, [pathname, searchParams]); + + return null; +} +``` + + + + +```tsx {{title: 'Typescript'}} +// other imports +import FormbricksProvider from "./formbricks"; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + ); +} +``` + + + + +Refer to our [Example NextJS App Directory project](https://github.com/formbricks/examples/tree/main/nextjs-app) for more help! + +### Pages Directory + + + + +```tsx {{ title: 'Typescript' }} +// other import +import { useRouter } from "next/router"; +import { useEffect } from "react"; + +import formbricks from "@formbricks/js/website"; + +if (typeof window !== "undefined") { + formbricks.init({ + environmentId: "", + apiHost: "", + }); +} + +export default function App({ Component, pageProps }: AppProps) { + const router = useRouter(); + + useEffect(() => { + // Connect next.js router to Formbricks + const handleRouteChange = formbricks?.registerRouteChange; + router.events.on("routeChangeComplete", handleRouteChange); + + return () => { + router.events.off("routeChangeComplete", handleRouteChange); + }; + }, []); + return ; +} +``` + + + +Refer to our [Example NextJS Pages Directory project](https://github.com/formbricks/examples/tree/main/nextjs-pages) for more help! + +### Required customizations to be made + + + + Formbricks Environment ID. + + + + + URL of the hosted Formbricks instance. + + + +First initialize the Formbricks SDK, ensure that it only runs on the client side. +To connect the Next.js router to Formbricks and ensure the SDK can keep track of every page change, we are registering the route change event. + +Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup! + +--- + +## VueJs + +Integrating the Formbricks SDK with Vue.js is a straightforward process. +We will make sure the SDK is only loaded and used on the client side, as it's not intended for server-side usage. + + + +```shell {{ title: 'npm' }} +npm install @formbricks/js +``` + +```shell {{ title: 'pnpm' }} +pnpm add @formbricks/js +``` + +```shell {{ title: 'yarn' }} +yarn add @formbricks/js +``` + + + + + +```js +import formbricks from "@formbricks/js/website"; + +if (typeof window !== "undefined") { + formbricks.init({ + environmentId: "", + apiHost: "", + }); +} + +export default formbricks; +``` + + + + + +```js +// other imports +import formbricks from "@/formbricks"; + +const app = createApp(App); + +app.use(router); + +app.mount("#app"); + +router.afterEach((to, from) => { + if (typeof formbricks !== "undefined") { + formbricks.registerRouteChange(); + } +}); +``` + + + +### Required customizations to be made + + + + Formbricks Environment ID. + + + + + URL of the hosted Formbricks instance. + + + +Refer to our [Example VueJs project](https://github.com/formbricks/examples/tree/main/vuejs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup! + +## Validate your setup + +Once you have completed the steps above, you can validate your setup by checking the **Setup Checklist** in the Settings. Your widget status indicator should go from this: + + + +To this: + + + +## Debugging Formbricks Integration + +Enabling Formbricks debug mode in your browser is a useful troubleshooting step for identifying and resolving complex issues. This section outlines how to activate debug mode, covers common use cases, and provides insights into specific debug log messages. + +### Activate Debug Mode + +To activate Formbricks debug mode: + +1. **Via URL Parameter:** + + - Enable debug mode mode by adding `?formbricksDebug=true` to your application's URL (e.g. `https://example.com?formbricksDebug=true` or `https://example.com?page=123&formbricksDebug=true`). This parameter will enable debugging for the current page. + +2. **View Debug Logs:** + + - Open your browser's developer tools by pressing `F12` or right-clicking and selecting "Inspect." + - Navigate to the "Console" tab to view Formbricks debugging information. + + **How to Open Browser Console:** + + - **Google Chrome:** Press `F12` or right-click, select "Inspect," and go to the "Console" tab. + - **Firefox:** Press `F12` or right-click, select "Inspect Element," and go to the "Console" tab. + - **Safari:** Press `Option + Command + C` to open the developer tools and navigate to the "Console" tab. + - **Edge:** Press `F12` or right-click, select "Inspect Element," and go to the "Console" tab. + +### Common Use Cases + +Debug mode is beneficial for scenarios such as: + +- Verifying Formbricks initialization. +- Identifying survey trigger issues. +- Troubleshooting unexpected behavior. + +### Debug Log Messages + +Debug log messages provide insights into: + +- API calls and responses. +- Event tracking, survey triggers and form interactions. +- Initialization errors. + +**Can’t figure it out? [Join our Discord!](https://formbricks.com/discord)** + +--- diff --git a/apps/docs/app/website-surveys/quickstart/images/I1.webp b/apps/docs/app/website-surveys/quickstart/images/I1.webp new file mode 100644 index 0000000000..b607cc0ec8 Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I1.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I2.webp b/apps/docs/app/website-surveys/quickstart/images/I2.webp new file mode 100644 index 0000000000..4102110058 Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I2.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I3.webp b/apps/docs/app/website-surveys/quickstart/images/I3.webp new file mode 100644 index 0000000000..3e2d7fbedc Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I3.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I4.webp b/apps/docs/app/website-surveys/quickstart/images/I4.webp new file mode 100644 index 0000000000..f7bae96938 Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I4.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I5.webp b/apps/docs/app/website-surveys/quickstart/images/I5.webp new file mode 100644 index 0000000000..33c217013f Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I5.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I6.webp b/apps/docs/app/website-surveys/quickstart/images/I6.webp new file mode 100644 index 0000000000..fc7945d1b4 Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I6.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I7.webp b/apps/docs/app/website-surveys/quickstart/images/I7.webp new file mode 100644 index 0000000000..4a3a352d2d Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I7.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/images/I8.webp b/apps/docs/app/website-surveys/quickstart/images/I8.webp new file mode 100644 index 0000000000..e5c0ab7442 Binary files /dev/null and b/apps/docs/app/website-surveys/quickstart/images/I8.webp differ diff --git a/apps/docs/app/website-surveys/quickstart/page.mdx b/apps/docs/app/website-surveys/quickstart/page.mdx new file mode 100644 index 0000000000..52adcc69fd --- /dev/null +++ b/apps/docs/app/website-surveys/quickstart/page.mdx @@ -0,0 +1,117 @@ +import { MdxImage } from "@/components/MdxImage"; + +import I1 from "./images/I1.webp"; +import I2 from "./images/I2.webp"; +import I3 from "./images/I3.webp"; +import I4 from "./images/I4.webp"; +import I5 from "./images/I5.webp"; +import I6 from "./images/I6.webp"; +import I7 from "./images/I7.webp"; +import I8 from "./images/I8.webp"; + +export const metadata = { + title: "Formbricks Quickstart Guide: Website Surveys Made Easier & Faster", + description: + "Formbricks is the easiest way to create and manage website surveys. This quickstart guide will show you how to create your first website survey in under 5 minutes.", +}; + +#### Website Surveys + +# Quickstart + +Website Surveys make it easy for your public website visitors to give you feedback. They are a great way to get feedback from your users, without interrupting their workflow. This quickstart guide will show you how to create your first website survey in under 5 minutes. + + + Website Surveys are ideal for **public facing websites**. If you are looking to run surveys in your app + where you have user identification & want advanced user targeting, head over to the [App Surveys Quickstart + Guide](/app-surveys/quickstart). + + +1. **Create a free Formbricks Cloud account**: While you can [self-host](/self-hosting/deployment) Formbricks, but the quickest and easiest way to get started is with the free Cloud plan. Just [sign up here](https://app.formbricks.com/auth/signup) and you'll be guided to our onboarding like below: + + + Website & App Surveys have the same integration process. The difference will come when we setup our survey. + + + + +2. **Connect your App/Website**: Once you get through a couple of onboarding steps, you’ll be asked to connect your app or website. This is where you’ll find the code snippet for both HTML as well as the npm package which you need to embed in your app: + + + +Paste the code snippet in your app and reload the page. You should now see the Formbricks widget in the lower right corner of your app! The integration is now complete. + + + +Onboarding is complete! Now let’s create our first survey as you should see templates to choose from after clicking on **Next**: + + + +3. **Create your first survey**: To be able to see a survey in your app, you need to create one. We’ll choose one of the templates and head over to the survey settings: + +Pick the Survey Type as **Website Survey**. + + + +1. **Set Trigger for the Survey**: Scroll down to Survey Trigger and click on **+ Add action**, choose **New Session**. This will cause this survey to appear when the Formbricks Widget tracks a new session: + + + +5. **Set Recontact Options for debugging**: In Recontact Options we choose the following settings, so that we can play around with the survey more easily. By default, each survey will be shown only once for each session to prevent survey fatigue: + + + Please change this setting later on after testing your survey to prevent survey fatigue for your users. + + + + +6. **Publish your survey**: Now hit **Publish** and you’ll be forwarded to the Summary Page. This is where you’ll find the responses to this survey. + + + +--- + +- We offer framework guides for various frontend tech, head over to the the [Website Survey Framework Guides](/website-survey/framework-guides) to get started with your public facing website surveys. +- Head over to our Website Survey SDK documentation to get started with the [Website Survey JS SDK](/developer-docs/website-survey-sdk). + +Still struggling or something not working as expected? [Join our Discord!](https://formbricks.com/discord) and we'd be glad to assist you! diff --git a/apps/formbricks-com/components/docs/Button.tsx b/apps/docs/components/Button.tsx similarity index 88% rename from apps/formbricks-com/components/docs/Button.tsx rename to apps/docs/components/Button.tsx index 836d0f4e64..e9fdfa791f 100644 --- a/apps/formbricks-com/components/docs/Button.tsx +++ b/apps/docs/components/Button.tsx @@ -16,7 +16,7 @@ function ArrowIcon(props: React.ComponentPropsWithoutRef<"svg">) { const variantStyles = { primary: - "rounded-full bg-slate-900 py-1 px-3 text-white hover:bg-slate-700 dark:bg-teal-400/10 dark:text-teal-400 dark:ring-1 dark:ring-inset dark:ring-teal-400/20 dark:hover:bg-teal-400/10 dark:hover:text-teal-300 dark:hover:ring-teal-300", + "rounded-full bg-slate-900 py-1 px-3 text-white hover:text-white hover:bg-slate-700 dark:bg-teal-400/10 dark:text-teal-400 dark:ring-1 dark:ring-inset dark:ring-teal-400/20 dark:hover:bg-teal-400/10 dark:hover:text-teal-300 dark:hover:ring-teal-300", secondary: "rounded-full bg-slate-100 py-1 px-3 text-slate-900 hover:bg-slate-200 dark:bg-slate-800/40 dark:text-slate-400 dark:ring-1 dark:ring-inset dark:ring-slate-800 dark:hover:bg-slate-800 dark:hover:text-slate-300", filled: @@ -39,7 +39,7 @@ export function Button({ variant = "primary", className, children, arrow, ...pro "inline-flex gap-0.5 justify-center items-center overflow-hidden font-medium transition text-center", variantStyles[variant], className, - "px-5 py-2.5 text-xs" + "px-5 py-2.5 text-sm" ); let arrowIcon = ( diff --git a/apps/formbricks-com/components/docs/Code.tsx b/apps/docs/components/Code.tsx similarity index 97% rename from apps/formbricks-com/components/docs/Code.tsx rename to apps/docs/components/Code.tsx index 03ffcd77b5..4529e5173c 100644 --- a/apps/formbricks-com/components/docs/Code.tsx +++ b/apps/docs/components/Code.tsx @@ -1,6 +1,6 @@ "use client"; -import { Tag } from "@/components/docs/Tag"; +import { Tag } from "@/components/Tag"; import { Tab } from "@headlessui/react"; import clsx from "clsx"; import { Children, createContext, isValidElement, useContext, useEffect, useRef, useState } from "react"; @@ -62,7 +62,7 @@ function CopyButton({ code }: { code: string }) { className={clsx( "group/button text-2xs absolute right-4 top-3.5 overflow-hidden rounded-full py-1 pl-2 pr-3 font-medium opacity-0 backdrop-blur transition focus:opacity-100 group-hover:opacity-100", copied - ? "bg-emerald-400/10 ring-1 ring-inset ring-emerald-400/20" + ? "bg-teal-400/10 ring-1 ring-inset ring-teal-400/20" : "hover:bg-white/7.5 dark:bg-white/2.5 bg-white/5 dark:hover:bg-white/5" )} onClick={() => { @@ -82,7 +82,7 @@ function CopyButton({ code }: { code: string }) { Copied! @@ -168,7 +168,7 @@ function CodeGroupHeader({ className={clsx( "ui-not-focus-visible:outline-none border-b py-3 transition", childIndex === selectedIndex - ? "border-emerald-500 text-emerald-400" + ? "border-teal-500 text-teal-400" : "border-transparent text-slate-400 hover:text-slate-300" )}> {getPanelTitle(isValidElement(child) ? child.props : {})} diff --git a/apps/formbricks-com/components/docs/Feedback.tsx b/apps/docs/components/Feedback.tsx similarity index 62% rename from apps/formbricks-com/components/docs/Feedback.tsx rename to apps/docs/components/Feedback.tsx index 109f7d5d69..1a116aaea0 100644 --- a/apps/formbricks-com/components/docs/Feedback.tsx +++ b/apps/docs/components/Feedback.tsx @@ -1,11 +1,8 @@ "use client"; import { Transition } from "@headlessui/react"; -import { usePathname } from "next/navigation"; import { Fragment, forwardRef, useState } from "react"; -import { handleFeedbackSubmit } from "../../lib/handleFeedbackSubmit"; - function CheckIcon(props: React.ComponentPropsWithoutRef<"svg">) { return (

Was this page helpful?

-
- Yes 👍 -
- No 👎 +

Was this page helpful?

+
+ Yes +
+ No
); @@ -53,8 +50,8 @@ const FeedbackForm = forwardRef< const FeedbackThanks = forwardRef>(function FeedbackThanks(_props, ref) { return (
-
- +
+ Thanks for your feedback!
@@ -62,13 +59,14 @@ const FeedbackThanks = forwardRef>(function FeedbackThan }); export function Feedback() { - const path = usePathname(); let [submitted, setSubmitted] = useState(false); - async function onSubmit(event: any) { + function onSubmit(event: React.FormEvent) { event.preventDefault(); - const wasHelpful: string = event.nativeEvent.submitter.dataset.response; - await handleFeedbackSubmit(wasHelpful, path); + + // event.nativeEvent.submitter.dataset.response + // => "yes" or "no" + setSubmitted(true); } diff --git a/apps/formbricks-com/components/docs/Footer.tsx b/apps/docs/components/Footer.tsx similarity index 87% rename from apps/formbricks-com/components/docs/Footer.tsx rename to apps/docs/components/Footer.tsx index 89ce8d562a..2259d829d2 100644 --- a/apps/formbricks-com/components/docs/Footer.tsx +++ b/apps/docs/components/Footer.tsx @@ -1,11 +1,13 @@ "use client"; +import { navigation } from "@/lib/navigation"; import Link from "next/link"; import { usePathname } from "next/navigation"; -import { FaDiscord, FaGithub, FaXTwitter } from "react-icons/fa6"; import { Button } from "./Button"; -import { navigation } from "./Navigation"; +import { DiscordIcon } from "./icons/DiscordIcon"; +import { GithubIcon } from "./icons/GithubIcon"; +import { TwitterIcon } from "./icons/TwitterIcon"; function PageLink({ label, @@ -38,7 +40,11 @@ function PageLink({ function PageNavigation() { let pathname = usePathname(); - let allPages = navigation.flatMap((group) => group.links); + let allPages = navigation.flatMap((group) => { + return group.links.flatMap((link) => { + return link.children ? link.children : link; + }); + }); let currentPageIndex = allPages.findIndex((page) => page.href === pathname); if (currentPageIndex === -1) { @@ -94,13 +100,13 @@ function SmallPrint() { Formbricks GmbH © {currentYear}. All rights reserved.

- + Follow us on Twitter - + Follow us on GitHub - + Join our Discord server
diff --git a/apps/formbricks-com/components/docs/GridPattern.tsx b/apps/docs/components/GridPattern.tsx similarity index 100% rename from apps/formbricks-com/components/docs/GridPattern.tsx rename to apps/docs/components/GridPattern.tsx diff --git a/apps/docs/components/Guides.tsx b/apps/docs/components/Guides.tsx new file mode 100644 index 0000000000..da9cf61208 --- /dev/null +++ b/apps/docs/components/Guides.tsx @@ -0,0 +1,48 @@ +import { Button } from "@/components/Button"; +import { Heading } from "@/components/Heading"; + +const guides = [ + { + href: "/authentication", + name: "Authentication", + description: "Learn how to authenticate your API requests.", + }, + { + href: "/pagination", + name: "Pagination", + description: "Understand how to work with paginated responses.", + }, + { + href: "/errors", + name: "Errors", + description: "Read about the different types of errors returned by the API.", + }, + { + href: "/webhooks", + name: "Webhooks", + description: "Learn how to programmatically configure webhooks for your app.", + }, +]; + +export function Guides() { + return ( +
+ + Guides + +
+ {guides.map((guide) => ( +
+

{guide.name}

+

{guide.description}

+

+ +

+
+ ))} +
+
+ ); +} diff --git a/apps/formbricks-com/components/docs/Header.tsx b/apps/docs/components/Header.tsx similarity index 95% rename from apps/formbricks-com/components/docs/Header.tsx rename to apps/docs/components/Header.tsx index 55104bc82c..7d4fa1e2cb 100644 --- a/apps/formbricks-com/components/docs/Header.tsx +++ b/apps/docs/components/Header.tsx @@ -1,7 +1,7 @@ "use client"; -import { FooterLogo } from "@/components/shared/Logo"; -import { Search } from "@/components/shared/Search"; +import { Logo } from "@/components/Logo"; +import { Search } from "@/components/Search"; import clsx from "clsx"; import { motion, useScroll, useTransform } from "framer-motion"; import Link from "next/link"; @@ -63,7 +63,7 @@ export const Header = forwardRef, { className?: string }
- +
diff --git a/apps/formbricks-com/components/docs/Heading.tsx b/apps/docs/components/Heading.tsx similarity index 64% rename from apps/formbricks-com/components/docs/Heading.tsx rename to apps/docs/components/Heading.tsx index 5537e85cfe..b8dea83679 100644 --- a/apps/formbricks-com/components/docs/Heading.tsx +++ b/apps/docs/components/Heading.tsx @@ -1,14 +1,12 @@ "use client"; +import { useSectionStore } from "@/components/SectionProvider"; +import { Tag } from "@/components/Tag"; import { remToPx } from "@/lib/remToPx"; import { useInView } from "framer-motion"; import Link from "next/link"; -import { usePathname } from "next/navigation"; import { useEffect, useRef } from "react"; -import { useSectionStore } from "./SectionProvider"; -import { Tag } from "./Tag"; - function AnchorIcon(props: React.ComponentPropsWithoutRef<"svg">) { return (
{tag && {tag}} - {tag && label && } - {label && {label}} + {tag && label && } + {label && {label}}
); } @@ -36,8 +34,8 @@ function Anchor({ id, inView, children }: { id: string; inView: boolean; childre {inView && (
-
- +
+
)} @@ -46,7 +44,7 @@ function Anchor({ id, inView, children }: { id: string; inView: boolean; childre ); } -export function HeadingDocs({ +export function Heading({ children, tag, label, @@ -91,40 +89,3 @@ export function HeadingDocs({ ); } - -export function HeadingContent({ - children, - tag, - label, - level, - anchor = true, - ...props -}: React.ComponentPropsWithoutRef<`h${Level}`> & { - id: string; - tag?: string; - label?: string; - level?: Level; - anchor?: boolean; -}) { - level = level ?? (2 as Level); - let Component = `h${level}` as "h2" | "h3"; - let ref = useRef(null); - - return ( - <> - - - {children} - - - ); -} - -export function Heading(props: any) { - const pathname = usePathname(); - if (pathname?.startsWith("/docs")) { - return ; - } else { - return ; - } -} diff --git a/apps/formbricks-com/components/docs/HeroPattern.tsx b/apps/docs/components/HeroPattern.tsx similarity index 100% rename from apps/formbricks-com/components/docs/HeroPattern.tsx rename to apps/docs/components/HeroPattern.tsx diff --git a/apps/formbricks-com/components/docs/Layout.tsx b/apps/docs/components/Layout.tsx similarity index 89% rename from apps/formbricks-com/components/docs/Layout.tsx rename to apps/docs/components/Layout.tsx index d2d456940d..88c97876af 100644 --- a/apps/formbricks-com/components/docs/Layout.tsx +++ b/apps/docs/components/Layout.tsx @@ -1,7 +1,7 @@ "use client"; -import { Navigation } from "@/components/docs/Navigation"; -import { FooterLogo } from "@/components/shared/Logo"; +import { Logo } from "@/components/Logo"; +import { Navigation } from "@/components/Navigation"; import { motion } from "framer-motion"; import Link from "next/link"; import { usePathname } from "next/navigation"; @@ -28,7 +28,7 @@ export function Layout({
- +
diff --git a/apps/docs/components/Logo.tsx b/apps/docs/components/Logo.tsx new file mode 100644 index 0000000000..0267e77450 --- /dev/null +++ b/apps/docs/components/Logo.tsx @@ -0,0 +1,16 @@ +import logoDark from "@/images/logo/logo-dark.svg"; +import logoLight from "@/images/logo/logo-light.svg"; +import Image from "next/image"; + +export function Logo({ className }: { className?: string }) { + return ( +
+
+ Formbricks Open source Forms & Surveys Logo +
+
+ Formbricks Open source Forms & Surveys Logo +
+
+ ); +} diff --git a/apps/formbricks-com/components/shared/MdxImage.tsx b/apps/docs/components/MdxImage.tsx similarity index 100% rename from apps/formbricks-com/components/shared/MdxImage.tsx rename to apps/docs/components/MdxImage.tsx diff --git a/apps/formbricks-com/components/docs/MobileNavigation.tsx b/apps/docs/components/MobileNavigation.tsx similarity index 87% rename from apps/formbricks-com/components/docs/MobileNavigation.tsx rename to apps/docs/components/MobileNavigation.tsx index ce428fe402..dde6625fb3 100644 --- a/apps/formbricks-com/components/docs/MobileNavigation.tsx +++ b/apps/docs/components/MobileNavigation.tsx @@ -1,14 +1,13 @@ "use client"; -import { Header } from "@/components/docs/Header"; +import { Header } from "@/components/Header"; +import { Navigation } from "@/components/Navigation"; import { Dialog, Transition } from "@headlessui/react"; import { motion } from "framer-motion"; import { usePathname, useSearchParams } from "next/navigation"; import { Fragment, Suspense, createContext, useContext, useEffect, useRef } from "react"; import { create } from "zustand"; -import { Navigation } from "./Navigation"; - function MenuIcon(props: React.ComponentPropsWithoutRef<"svg">) { return (
+
@@ -90,7 +89,7 @@ function MobileNavigationDialog({ isOpen, close }: { isOpen: boolean; close: () leaveTo="-translate-x-full"> + className="ring-zinc-900/7.5 fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 min-[416px]:max-w-sm sm:px-6 sm:pb-10 dark:bg-zinc-900 dark:ring-zinc-800"> @@ -125,10 +124,10 @@ export function MobileNavigation() { {!isInsideMobileNavigation && ( diff --git a/apps/docs/components/Navigation.tsx b/apps/docs/components/Navigation.tsx new file mode 100644 index 0000000000..cdacd41979 --- /dev/null +++ b/apps/docs/components/Navigation.tsx @@ -0,0 +1,254 @@ +"use client"; + +import { navigation } from "@/lib/navigation"; +import { remToPx } from "@/lib/remToPx"; +import clsx from "clsx"; +import { AnimatePresence, motion, useIsPresent } from "framer-motion"; +import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { useRef, useState } from "react"; + +import { Button } from "./Button"; +import { useIsInsideMobileNavigation } from "./MobileNavigation"; +import { useSectionStore } from "./SectionProvider"; + +export interface BaseLink { + title: string; +} + +export interface LinkWithHref extends BaseLink { + href: string; + children?: never; // Ensure that 'children' cannot coexist with 'href' +} + +export interface LinkWithChildren extends BaseLink { + href?: never; // Ensure that 'href' cannot coexist with 'children' + children: Array<{ + title: string; + href: string; + }>; +} + +export interface NavGroup { + title: string; + links: Array; +} + +function useInitialValue(value: T, condition = true) { + let initialValue = useRef(value).current; + return condition ? initialValue : value; +} + +function NavLink({ + href, + children, + active = false, + isAnchorLink = false, +}: { + href: string; + children: React.ReactNode; + active: boolean; + isAnchorLink?: boolean; +}) { + return ( + + {children} + + ); +} + +function VisibleSectionHighlight({ group, pathname }: { group: NavGroup; pathname: string }) { + let [sections, visibleSections] = useInitialValue( + [useSectionStore((s) => s.sections), useSectionStore((s) => s.visibleSections)], + useIsInsideMobileNavigation() + ); + + let isPresent = useIsPresent(); + let firstVisibleSectionIndex = Math.max( + 0, + [{ id: "_top" }, ...sections].findIndex((section) => section.id === visibleSections[0]) + ); + let itemHeight = remToPx(2); + let activePageIndex = group.links.findIndex( + (link) => + (link.href && pathname.startsWith(link.href)) || + (link.children && link.children.some((child) => pathname.startsWith(child.href))) + ); + + let height = isPresent ? Math.max(1, visibleSections.length) * itemHeight : itemHeight; + let top = activePageIndex * itemHeight + firstVisibleSectionIndex * itemHeight; + if (activePageIndex === -1) return null; + + return ( + + ); +} + +function ActivePageMarker({ group, pathname }: { group: NavGroup; pathname: string }) { + let itemHeight = remToPx(2); + let offset = remToPx(0.25); + let activePageIndex = group.links.findIndex( + (link) => + (link.href && pathname.startsWith(link.href)) || + (link.children && link.children.some((child) => pathname.startsWith(child.href))) + ); + if (activePageIndex === -1) return null; + let top = offset + activePageIndex * itemHeight; + + return ( + + ); +} + +function NavigationGroup({ + group, + className, + activeGroup, + setActiveGroup, + openGroups, + setOpenGroups, +}: { + group: NavGroup; + className?: string; + activeGroup: NavGroup | null; + setActiveGroup: (group: NavGroup | null) => void; + openGroups: string[]; + setOpenGroups: (groups: string[]) => void; +}) { + const isInsideMobileNavigation = useIsInsideMobileNavigation(); + const pathname = usePathname(); + const isActiveGroup = activeGroup?.title === group.title; + + const toggleParentTitle = (title: string) => { + if (openGroups.includes(title)) { + setOpenGroups(openGroups.filter((t) => t !== title)); + } else { + setOpenGroups([...openGroups, title]); + } + setActiveGroup(group); + }; + + const isParentOpen = (title: string) => openGroups.includes(title); + + return ( +
  • + + {group.title} + +
    + + {isActiveGroup && } + + + + {isActiveGroup && } + +
      + {group.links.map((link) => ( + + {link.href ? ( + + {link.title} + + ) : ( +
      toggleParentTitle(link.title)}> + pathname.startsWith(child.href)) + ) + }> + + {link.title} + {isParentOpen(link.title) ? ( + + ) : ( + + )} + + +
      + )} + + {link.children && isParentOpen(link.title) && ( + + {link.children.map((child) => ( +
    • + + {child.title} + +
    • + ))} +
      + )} +
      +
      + ))} +
    +
    +
  • + ); +} + +export function Navigation(props: React.ComponentPropsWithoutRef<"nav">) { + const [activeGroup, setActiveGroup] = useState(navigation[0]); + const [openGroups, setOpenGroups] = useState([]); + + return ( + + ); +} diff --git a/apps/docs/components/Prose.tsx b/apps/docs/components/Prose.tsx new file mode 100644 index 0000000000..7266bc8371 --- /dev/null +++ b/apps/docs/components/Prose.tsx @@ -0,0 +1,24 @@ +import clsx from "clsx"; + +export function Prose({ + as, + className, + ...props +}: Omit, "as" | "className"> & { + as?: T; + className?: string; +}) { + let Component = as ?? "div"; + + return ( + *)` is used to select all direct children without an increase in specificity like you'd get from just `& > *` + "[html_:where(&>*)]:mx-auto [html_:where(&>*)]:max-w-2xl [html_:where(&>*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(&>*)]:lg:max-w-3xl" + )} + {...props} + /> + ); +} diff --git a/apps/formbricks-com/components/docs/BestPractices.tsx b/apps/docs/components/Resources.tsx similarity index 54% rename from apps/formbricks-com/components/docs/BestPractices.tsx rename to apps/docs/components/Resources.tsx index 9159b1d401..79d8a1dd52 100644 --- a/apps/formbricks-com/components/docs/BestPractices.tsx +++ b/apps/docs/components/Resources.tsx @@ -1,16 +1,15 @@ "use client"; -import { ChatBubbleIcon } from "@/components/docs/icons/ChatBubbleIcon"; -import { EnvelopeIcon } from "@/components/docs/icons/EnvelopeIcon"; -import { UserIcon } from "@/components/docs/icons/UserIcon"; -import { UsersIcon } from "@/components/docs/icons/UsersIcon"; +import { GridPattern } from "@/components/GridPattern"; +import { Heading } from "@/components/Heading"; +import { ChatBubbleIcon } from "@/components/icons/ChatBubbleIcon"; +import { EnvelopeIcon } from "@/components/icons/EnvelopeIcon"; +import { UserIcon } from "@/components/icons/UserIcon"; +import { UsersIcon } from "@/components/icons/UsersIcon"; import { type MotionValue, motion, useMotionTemplate, useMotionValue } from "framer-motion"; import Link from "next/link"; -import { GridPattern } from "./GridPattern"; -import { Heading } from "./Heading"; - -interface BestPractice { +interface Resource { href: string; name: string; description: string; @@ -18,11 +17,12 @@ interface BestPractice { pattern: Omit, "width" | "height" | "x">; } -const bestPractices: Array = [ +const resources: Array = [ { - href: "/docs/best-practices/cancel-subscription", - name: "Learn from Churn", - description: "Churn is hard, but can teach you a lot. These insights are pure gold to reduce churn.", + href: "/contacts", + name: "Contacts", + description: + "Learn about the contact model and how to create, retrieve, update, delete, and list contacts.", icon: UserIcon, pattern: { y: 16, @@ -33,10 +33,10 @@ const bestPractices: Array = [ }, }, { - href: "/docs/best-practices/pmf-survey", - name: "Product Market Fit", + href: "/conversations", + name: "Conversations", description: - "Measuring and understanding your PMF helps you understand what users like, what they’re missing and what to build next.", + "Learn about the conversation model and how to create, retrieve, update, delete, and list conversations.", icon: ChatBubbleIcon, pattern: { y: -6, @@ -47,9 +47,10 @@ const bestPractices: Array = [ }, }, { - href: "/docs/best-practices/improve-trial-cr", - name: "Improve Trial Conversion", - description: "When a user doesn't convert, you want to know why.", + href: "/messages", + name: "Messages", + description: + "Learn about the message model and how to create, retrieve, update, delete, and list messages.", icon: EnvelopeIcon, pattern: { y: 32, @@ -60,9 +61,9 @@ const bestPractices: Array = [ }, }, { - href: "/docs/best-practices/feedback-box", - name: "Feedback Box", - description: "The Feedback Box gives your users a direct channel to share their feedback and feel heard.", + href: "/groups", + name: "Groups", + description: "Learn about the group model and how to create, retrieve, update, delete, and list groups.", icon: UsersIcon, pattern: { y: 22, @@ -71,19 +72,19 @@ const bestPractices: Array = [ }, ]; -function BestPracticeIcon({ icon: Icon }: { icon: BestPractice["icon"] }) { +function ResourceIcon({ icon: Icon }: { icon: Resource["icon"] }) { return ( -
    - +
    +
    ); } -function BestPracticePattern({ +function ResourcePattern({ mouseX, mouseY, ...gridProps -}: BestPractice["pattern"] & { +}: Resource["pattern"] & { mouseX: MotionValue; mouseY: MotionValue; }) { @@ -102,7 +103,7 @@ function BestPracticePattern({ />
    - -
    + className="dark:bg-white/2.5 group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:hover:shadow-black/5"> + +
    - -

    + +

    {resource.name}

    -

    {resource.description}

    +

    {resource.description}

    ); } -export default function BestPractices() { +export function Resources() { return (
    - Best Practices + Resources -
    - {bestPractices.map((resource) => ( - +
    + {resources.map((resource) => ( + ))}
    diff --git a/apps/docs/components/ResponsiveVideo.tsx b/apps/docs/components/ResponsiveVideo.tsx new file mode 100644 index 0000000000..a828a86425 --- /dev/null +++ b/apps/docs/components/ResponsiveVideo.tsx @@ -0,0 +1,15 @@ +// ResponsiveVideo.js +export function ResponsiveVideo({ src, title }) { + return ( +
    + +
    + ); +} diff --git a/apps/formbricks-com/components/shared/Search.tsx b/apps/docs/components/Search.tsx similarity index 90% rename from apps/formbricks-com/components/shared/Search.tsx rename to apps/docs/components/Search.tsx index 096aa9f8e8..47385e7e08 100644 --- a/apps/formbricks-com/components/shared/Search.tsx +++ b/apps/docs/components/Search.tsx @@ -53,13 +53,14 @@ export function Search() { const style = document.createElement("style"); style.innerHTML = ` :root { - --docsearch-primary-color: ${isLightMode ? "#029E94" : "#1F7066"}; - --docsearch-modal-background: ${isLightMode ? "#FFFFFF" : "#121212"}; + --docsearch-primary-color: ${isLightMode ? "#00C4B8" : "#00C4B8"}; + --docsearch-modal-background: ${isLightMode ? "#f8fafc" : "#0f172a"}; --docsearch-text-color: ${isLightMode ? "#121212" : "#FFFFFF"}; --docsearch-hit-background: ${isLightMode ? "#FFFFFF" : "#111111"}; --docsearch-footer-background: ${isLightMode ? "#EEEEEE" : "#121212"}; - --docsearch-searchbox-focus-background: ${isLightMode ? "#D8F6F4" : "#121212"}; - --docsearch-modal-shadow: ${isLightMode ? "inset 1px 1px 0 0 hsla(0,0%,100%,0.5), 0 3px 8px 0 #D8F6F4" : "inset 1px 1px 0 0 hsla(0,0%,100%,0.5), 0 3px 8px 0 #808080"}; + --docsearch-searchbox-focus-background: ${isLightMode ? "#f1f5f9" : "#1e293b"}; + --docsearch-modal-shadow: ""; + --DocSearch-Input: ${isLightMode ? "#000000" : "#FFFFFF"}; } .DocSearch-Hit-title { color: ${isLightMode ? "#000000" : "#FFFFFF"}; @@ -82,6 +83,12 @@ export function Search() { .DocSearch-Screen-Icon { visibility: hidden; } + #docsearch-input { + background-color: transparent; + } + .DocSearch-Footer { + display: none !important; + } `; document.head.appendChild(style); diff --git a/apps/formbricks-com/components/docs/SectionProvider.tsx b/apps/docs/components/SectionProvider.tsx similarity index 98% rename from apps/formbricks-com/components/docs/SectionProvider.tsx rename to apps/docs/components/SectionProvider.tsx index ca3b156102..37d9533198 100644 --- a/apps/formbricks-com/components/docs/SectionProvider.tsx +++ b/apps/docs/components/SectionProvider.tsx @@ -58,7 +58,7 @@ function useVisibleSections(sectionStore: StoreApi) { useEffect(() => { function checkVisibleSections() { let { innerHeight, scrollY } = window; - let newVisibleSections: string[] = []; // Explicitly type the array here + let newVisibleSections: string[] = []; for (let sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { let { id, headingRef, offsetRem = 0 } = sections[sectionIndex]; diff --git a/apps/formbricks-com/components/docs/Tag.tsx b/apps/docs/components/Tag.tsx similarity index 74% rename from apps/formbricks-com/components/docs/Tag.tsx rename to apps/docs/components/Tag.tsx index 4c6c4fe0a9..3e5c7339f9 100644 --- a/apps/formbricks-com/components/docs/Tag.tsx +++ b/apps/docs/components/Tag.tsx @@ -6,10 +6,9 @@ const variantStyles = { }; const colorStyles = { - emerald: { - small: "text-emerald-500 dark:text-emerald-400", - medium: - "ring-emerald-300 dark:ring-emerald-400/30 bg-emerald-400/10 text-emerald-500 dark:text-emerald-400", + teal: { + small: "text-teal-500 dark:text-teal-400", + medium: "ring-teal-300 dark:ring-teal-400/30 bg-teal-400/10 text-teal-500 dark:text-teal-400", }, sky: { small: "text-sky-500", @@ -26,15 +25,15 @@ const colorStyles = { medium: "ring-rose-200 bg-rose-50 text-red-500 dark:ring-rose-500/20 dark:bg-rose-400/10 dark:text-rose-400", }, - slate: { - small: "text-slate-400 dark:text-slate-500", + zinc: { + small: "text-zinc-400 dark:text-zinc-500", medium: - "ring-slate-200 bg-slate-50 text-slate-500 dark:ring-slate-500/20 dark:bg-slate-400/10 dark:text-slate-400", + "ring-zinc-200 bg-zinc-50 text-zinc-500 dark:ring-zinc-500/20 dark:bg-zinc-400/10 dark:text-zinc-400", }, }; const valueColorMap = { - GET: "emerald", + GET: "teal", POST: "sky", PUT: "amber", DELETE: "rose", @@ -43,7 +42,7 @@ const valueColorMap = { export function Tag({ children, variant = "medium", - color = valueColorMap[children] ?? "emerald", + color = valueColorMap[children] ?? "teal", }: { children: keyof typeof valueColorMap & (string | {}); variant?: keyof typeof variantStyles; diff --git a/apps/formbricks-com/components/docs/TellaVideo.tsx b/apps/docs/components/TellaVideo.tsx similarity index 100% rename from apps/formbricks-com/components/docs/TellaVideo.tsx rename to apps/docs/components/TellaVideo.tsx diff --git a/apps/formbricks-com/components/docs/ThemeToggle.tsx b/apps/docs/components/ThemeToggle.tsx similarity index 90% rename from apps/formbricks-com/components/docs/ThemeToggle.tsx rename to apps/docs/components/ThemeToggle.tsx index d1cbd39cb5..89025534d3 100644 --- a/apps/formbricks-com/components/docs/ThemeToggle.tsx +++ b/apps/docs/components/ThemeToggle.tsx @@ -1,5 +1,3 @@ -"use client"; - import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; @@ -35,10 +33,10 @@ export function ThemeToggle() { return ( ); diff --git a/apps/formbricks-com/components/docs/icons/BellIcon.tsx b/apps/docs/components/icons/BellIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/BellIcon.tsx rename to apps/docs/components/icons/BellIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/BoltIcon.tsx b/apps/docs/components/icons/BoltIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/BoltIcon.tsx rename to apps/docs/components/icons/BoltIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/BookIcon.tsx b/apps/docs/components/icons/BookIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/BookIcon.tsx rename to apps/docs/components/icons/BookIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/CalendarIcon.tsx b/apps/docs/components/icons/CalendarIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/CalendarIcon.tsx rename to apps/docs/components/icons/CalendarIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/CartIcon.tsx b/apps/docs/components/icons/CartIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/CartIcon.tsx rename to apps/docs/components/icons/CartIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/ChatBubbleIcon.tsx b/apps/docs/components/icons/ChatBubbleIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/ChatBubbleIcon.tsx rename to apps/docs/components/icons/ChatBubbleIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/CheckIcon.tsx b/apps/docs/components/icons/CheckIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/CheckIcon.tsx rename to apps/docs/components/icons/CheckIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/ChevronRightLeftIcon.tsx b/apps/docs/components/icons/ChevronRightLeftIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/ChevronRightLeftIcon.tsx rename to apps/docs/components/icons/ChevronRightLeftIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/ClipboardIcon.tsx b/apps/docs/components/icons/ClipboardIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/ClipboardIcon.tsx rename to apps/docs/components/icons/ClipboardIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/CogIcon.tsx b/apps/docs/components/icons/CogIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/CogIcon.tsx rename to apps/docs/components/icons/CogIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/CopyIcon.tsx b/apps/docs/components/icons/CopyIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/CopyIcon.tsx rename to apps/docs/components/icons/CopyIcon.tsx diff --git a/apps/docs/components/icons/DiscordIcon.tsx b/apps/docs/components/icons/DiscordIcon.tsx new file mode 100644 index 0000000000..f2781c2b0b --- /dev/null +++ b/apps/docs/components/icons/DiscordIcon.tsx @@ -0,0 +1,15 @@ +export const DiscordIcon: React.FC> = (props) => { + return ( + + + + ); +}; diff --git a/apps/formbricks-com/components/docs/icons/DocumentIcon.tsx b/apps/docs/components/icons/DocumentIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/DocumentIcon.tsx rename to apps/docs/components/icons/DocumentIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/EnvelopeIcon.tsx b/apps/docs/components/icons/EnvelopeIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/EnvelopeIcon.tsx rename to apps/docs/components/icons/EnvelopeIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/FaceSmileIcon.tsx b/apps/docs/components/icons/FaceSmileIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/FaceSmileIcon.tsx rename to apps/docs/components/icons/FaceSmileIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/FolderIcon.tsx b/apps/docs/components/icons/FolderIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/FolderIcon.tsx rename to apps/docs/components/icons/FolderIcon.tsx diff --git a/apps/docs/components/icons/GithubIcon.tsx b/apps/docs/components/icons/GithubIcon.tsx new file mode 100644 index 0000000000..6628c7a618 --- /dev/null +++ b/apps/docs/components/icons/GithubIcon.tsx @@ -0,0 +1,15 @@ +export const GithubIcon: React.FC> = (props) => { + return ( + + + + ); +}; diff --git a/apps/formbricks-com/components/docs/icons/LinkIcon.tsx b/apps/docs/components/icons/LinkIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/LinkIcon.tsx rename to apps/docs/components/icons/LinkIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/ListIcon.tsx b/apps/docs/components/icons/ListIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/ListIcon.tsx rename to apps/docs/components/icons/ListIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/MagnifyingGlassIcon.tsx b/apps/docs/components/icons/MagnifyingGlassIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/MagnifyingGlassIcon.tsx rename to apps/docs/components/icons/MagnifyingGlassIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/MapPinIcon.tsx b/apps/docs/components/icons/MapPinIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/MapPinIcon.tsx rename to apps/docs/components/icons/MapPinIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/PackageIcon.tsx b/apps/docs/components/icons/PackageIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/PackageIcon.tsx rename to apps/docs/components/icons/PackageIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/PaperAirplaneIcon.tsx b/apps/docs/components/icons/PaperAirplaneIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/PaperAirplaneIcon.tsx rename to apps/docs/components/icons/PaperAirplaneIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/PaperClipIcon.tsx b/apps/docs/components/icons/PaperClipIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/PaperClipIcon.tsx rename to apps/docs/components/icons/PaperClipIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/ShapesIcon.tsx b/apps/docs/components/icons/ShapesIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/ShapesIcon.tsx rename to apps/docs/components/icons/ShapesIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/ShirtIcon.tsx b/apps/docs/components/icons/ShirtIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/ShirtIcon.tsx rename to apps/docs/components/icons/ShirtIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/SquaresPlusIcon.tsx b/apps/docs/components/icons/SquaresPlusIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/SquaresPlusIcon.tsx rename to apps/docs/components/icons/SquaresPlusIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/TagIcon.tsx b/apps/docs/components/icons/TagIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/TagIcon.tsx rename to apps/docs/components/icons/TagIcon.tsx diff --git a/apps/docs/components/icons/TwitterIcon.tsx b/apps/docs/components/icons/TwitterIcon.tsx new file mode 100644 index 0000000000..46887d0017 --- /dev/null +++ b/apps/docs/components/icons/TwitterIcon.tsx @@ -0,0 +1,15 @@ +export const TwitterIcon: React.FC> = (props) => { + return ( + + + + ); +}; diff --git a/apps/formbricks-com/components/docs/icons/UserIcon.tsx b/apps/docs/components/icons/UserIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/UserIcon.tsx rename to apps/docs/components/icons/UserIcon.tsx diff --git a/apps/formbricks-com/components/docs/icons/UsersIcon.tsx b/apps/docs/components/icons/UsersIcon.tsx similarity index 100% rename from apps/formbricks-com/components/docs/icons/UsersIcon.tsx rename to apps/docs/components/icons/UsersIcon.tsx diff --git a/apps/formbricks-com/components/docs/mdx.tsx b/apps/docs/components/mdx.tsx similarity index 75% rename from apps/formbricks-com/components/docs/mdx.tsx rename to apps/docs/components/mdx.tsx index f421d93f4b..4d9cd85fe2 100644 --- a/apps/formbricks-com/components/docs/mdx.tsx +++ b/apps/docs/components/mdx.tsx @@ -1,18 +1,17 @@ -import { Feedback } from "@/components/docs/Feedback"; +import { Feedback } from "@/components/Feedback"; +import { Heading } from "@/components/Heading"; +import { Prose } from "@/components/Prose"; import clsx from "clsx"; import Link from "next/link"; -import { Heading } from "./Heading"; -import { Prose } from "./Prose"; - export const a = Link; - -export { CodeGroup, Code as code, Pre as pre } from "./Code"; +export { Button } from "@/components/Button"; +export { CodeGroup, Code as code, Pre as pre } from "@/components/Code"; export function wrapper({ children }: { children: React.ReactNode }) { return (
    - {children} + {children}
    @@ -42,8 +41,8 @@ function InfoIcon(props: React.ComponentPropsWithoutRef<"svg">) { export function Note({ children }: { children: React.ReactNode }) { return ( -
    - +
    +
    {children}
    ); @@ -70,7 +69,7 @@ export function Properties({ children }: { children: React.ReactNode }) {
      + className="m-0 max-w-[calc(theme(maxWidth.lg)-theme(spacing.8))] list-none divide-y divide-zinc-900/5 p-0 dark:divide-white/5"> {children}
    @@ -96,7 +95,7 @@ export function Property({ {type && ( <>
    Type
    -
    {type}
    +
    {type}
    )}
    Description
    diff --git a/apps/formbricks-com/images/logos/go.svg b/apps/docs/images/frameworks/go.svg similarity index 100% rename from apps/formbricks-com/images/logos/go.svg rename to apps/docs/images/frameworks/go.svg diff --git a/apps/formbricks-com/images/logos/html5.svg b/apps/docs/images/frameworks/html5.svg similarity index 100% rename from apps/formbricks-com/images/logos/html5.svg rename to apps/docs/images/frameworks/html5.svg diff --git a/apps/formbricks-com/images/logos/nextjs.svg b/apps/docs/images/frameworks/nextjs.svg similarity index 100% rename from apps/formbricks-com/images/logos/nextjs.svg rename to apps/docs/images/frameworks/nextjs.svg diff --git a/apps/formbricks-com/images/logos/node.svg b/apps/docs/images/frameworks/node.svg similarity index 100% rename from apps/formbricks-com/images/logos/node.svg rename to apps/docs/images/frameworks/node.svg diff --git a/apps/formbricks-com/images/logos/php.svg b/apps/docs/images/frameworks/php.svg similarity index 100% rename from apps/formbricks-com/images/logos/php.svg rename to apps/docs/images/frameworks/php.svg diff --git a/apps/formbricks-com/images/logos/python.svg b/apps/docs/images/frameworks/python.svg similarity index 100% rename from apps/formbricks-com/images/logos/python.svg rename to apps/docs/images/frameworks/python.svg diff --git a/apps/formbricks-com/images/logos/reactjs.svg b/apps/docs/images/frameworks/reactjs.svg similarity index 100% rename from apps/formbricks-com/images/logos/reactjs.svg rename to apps/docs/images/frameworks/reactjs.svg diff --git a/apps/formbricks-com/images/logos/ruby.svg b/apps/docs/images/frameworks/ruby.svg similarity index 100% rename from apps/formbricks-com/images/logos/ruby.svg rename to apps/docs/images/frameworks/ruby.svg diff --git a/apps/formbricks-com/images/logos/vuejs.svg b/apps/docs/images/frameworks/vuejs.svg similarity index 100% rename from apps/formbricks-com/images/logos/vuejs.svg rename to apps/docs/images/frameworks/vuejs.svg diff --git a/apps/formbricks-com/images/blog/logo/footerlogo-dark.svg b/apps/docs/images/logo/logo-dark.svg similarity index 100% rename from apps/formbricks-com/images/blog/logo/footerlogo-dark.svg rename to apps/docs/images/logo/logo-dark.svg diff --git a/apps/formbricks-com/images/blog/logo/footerlogo.svg b/apps/docs/images/logo/logo-light.svg similarity index 100% rename from apps/formbricks-com/images/blog/logo/footerlogo.svg rename to apps/docs/images/logo/logo-light.svg diff --git a/apps/docs/lib/navigation.ts b/apps/docs/lib/navigation.ts new file mode 100644 index 0000000000..59396fccb7 --- /dev/null +++ b/apps/docs/lib/navigation.ts @@ -0,0 +1,136 @@ +import type { NavGroup } from "@/components/Navigation"; + +export const navigation: Array = [ + { + title: "Introduction", + links: [ + { title: "What is Formbricks?", href: "/introduction/what-is-formbricks" }, + { title: "Why open source?", href: "/introduction/why-open-source" }, + { title: "How does it work?", href: "/introduction/how-it-works" }, + { + title: "Best Practices", + children: [ + { title: "Learn from Churn", href: "/best-practices/cancel-subscription" }, + { title: "Interview Prompt", href: "/best-practices/interview-prompt" }, + { title: "Product-Market Fit", href: "/best-practices/pmf-survey" }, + { title: "Trial Conversion", href: "/best-practices/improve-trial-cr" }, + { title: "Feature Chaser", href: "/best-practices/feature-chaser" }, + { title: "Feedback Box", href: "/best-practices/feedback-box" }, + { title: "Docs Feedback", href: "/best-practices/docs-feedback" }, + { title: "Improve Email Content", href: "/best-practices/improve-email-content" }, + ], + }, + ], + }, + { + title: "App Surveys", + links: [ + { title: "Quickstart", href: "/app-surveys/quickstart" }, + { title: "Framework Guides", href: "/app-surveys/framework-guides" }, + { + title: "Features", + children: [ + { title: "Identify Users", href: "/app-surveys/user-identification" }, + { title: "Actions", href: "/app-surveys/actions" }, + { title: "Advanced Targeting", href: "/app-surveys/advanced-targeting" }, + { title: "Show Survey to % of users", href: "/global/show-survey-to-percent-of-users" }, // app and website + { title: "Recontact Options", href: "/app-surveys/recontact" }, + { title: "Multi Language Surveys", href: "/global/multi-language-surveys" }, // global + { title: "User Metadata", href: "/global/metadata" }, // global + { title: "Custom Styling", href: "/global/custom-styling" }, // global + { title: "Conditional Logic", href: "/global/conditional-logic" }, // global + { title: "Custom Start & End Conditions", href: "/global/custom-start-end-conditions" }, // global + { title: "Recall Functionality", href: "/global/recall" }, // global + { title: "Partial Submissions", href: "/global/partial-submissions" }, // global + ], + }, + ], + }, + { + title: "Website Surveys", + links: [ + { title: "Quickstart", href: "/website-surveys/quickstart" }, + { title: "Framework Guides", href: "/website-surveys/framework-guides" }, + { + title: "Features", + children: [ + { title: "Actions & Targeting", href: "/website-surveys/actions-and-targeting" }, + { title: "Show Survey to % of users", href: "/global/show-survey-to-percent-of-users" }, // app and website + { title: "Recontact Options", href: "/app-surveys/recontact" }, + { title: "Multi Language Surveys", href: "/global/multi-language-surveys" }, // global + { title: "User Metadata", href: "/global/metadata" }, // global + { title: "Custom Styling", href: "/global/custom-styling" }, // global + { title: "Conditional Logic", href: "/global/conditional-logic" }, // global + { title: "Custom Start & End Conditions", href: "/global/custom-start-end-conditions" }, // global + { title: "Recall Functionality", href: "/global/recall" }, // global + { title: "Partial Submissions", href: "/global/partial-submissions" }, // global + ], + }, + ], + }, + { + title: "Link Surveys", + links: [ + { title: "Quickstart", href: "/link-surveys/quickstart" }, + { + title: "Features", + children: [ + { title: "Data Prefilling", href: "/link-surveys/data-prefilling" }, + { title: "Identify Users", href: "/link-surveys/user-identification" }, + { title: "Single Use Links", href: "/link-surveys/single-use-links" }, + { title: "Source Tracking", href: "/link-surveys/source-tracking" }, + { title: "Hidden Fields", href: "/link-surveys/hidden-fields" }, + { title: "Start At Question", href: "/link-surveys/start-at-question" }, + { title: "Embed Surveys Anywhere", href: "/link-surveys/embed-surveys" }, + { title: "Multi Language Surveys", href: "/global/multi-language-surveys" }, + { title: "User Metadata", href: "/global/metadata" }, + { title: "Custom Styling", href: "/global/custom-styling" }, + { title: "Conditional Logic", href: "/global/conditional-logic" }, + { title: "Custom Start & End Conditions", href: "/global/custom-start-end-conditions" }, + { title: "Recall Functionality", href: "/global/recall" }, + { title: "Verify Email before Survey", href: "/link-surveys/verify-email-before-survey" }, + { title: "PIN Protected Surveys", href: "/link-surveys/pin-protected-surveys" }, + { title: "Partial Submissions", href: "/global/partial-submissions" }, + ], + }, + ], + }, + { + title: "Self-Hosting", + links: [ + { title: "Overview", href: "/self-hosting/overview" }, + { title: "One-Click Setup", href: "/self-hosting/one-click" }, + { title: "Docker Setup", href: "/self-hosting/docker" }, + { title: "Migration Guide", href: "/self-hosting/migration-guide" }, + { title: "Configuration", href: "/self-hosting/configuration" }, + { title: "Integrations", href: "/self-hosting/integrations" }, + { title: "License", href: "/self-hosting/license" }, + ], + }, + { + title: "Developer Docs", + links: [ + { title: "Overview", href: "/developer-docs/overview" }, + { + title: "Integrations", + children: [ + { title: "Overview", href: "/developer-docs/integrations/overview" }, + { title: "Airtable", href: "/developer-docs/integrations/airtable" }, + { title: "Google Sheets", href: "/developer-docs/integrations/google-sheets" }, + { title: "Make", href: "/developer-docs/integrations/make" }, + { title: "n8n", href: "/developer-docs/integrations/n8n" }, + { title: "Notion", href: "/developer-docs/integrations/notion" }, + { title: "Slack", href: "/developer-docs/integrations/slack" }, + { title: "Wordpress", href: "/developer-docs/integrations/wordpress" }, + { title: "Zapier", href: "/developer-docs/integrations/zapier" }, + ], + }, + { title: "SDK: App Survey", href: "/developer-docs/app-survey-sdk" }, + { title: "SDK: Website Survey", href: "/developer-docs/website-survey-sdk" }, + { title: "SDK: Formbricks API", href: "/developer-docs/api-sdk" }, + { title: "REST API", href: "/developer-docs/rest-api" }, + { title: "Webhooks", href: "/developer-docs/webhooks" }, + { title: "Contributing", href: "/developer-docs/contributing" }, + ], + }, +]; diff --git a/apps/formbricks-com/lib/remToPx.ts b/apps/docs/lib/remToPx.ts similarity index 100% rename from apps/formbricks-com/lib/remToPx.ts rename to apps/docs/lib/remToPx.ts diff --git a/apps/formbricks-com/mdx-components.tsx b/apps/docs/mdx-components.tsx similarity index 75% rename from apps/formbricks-com/mdx-components.tsx rename to apps/docs/mdx-components.tsx index d3a29ccb82..56a0fedca5 100644 --- a/apps/formbricks-com/mdx-components.tsx +++ b/apps/docs/mdx-components.tsx @@ -1,4 +1,4 @@ -import * as mdxComponents from "@/components/docs/mdx"; +import * as mdxComponents from "@/components/mdx"; import { type MDXComponents } from "mdx/types"; export function useMDXComponents(components: MDXComponents) { diff --git a/apps/formbricks-com/mdx/recma.mjs b/apps/docs/mdx/recma.mjs similarity index 100% rename from apps/formbricks-com/mdx/recma.mjs rename to apps/docs/mdx/recma.mjs diff --git a/apps/formbricks-com/mdx/rehype.mjs b/apps/docs/mdx/rehype.mjs similarity index 100% rename from apps/formbricks-com/mdx/rehype.mjs rename to apps/docs/mdx/rehype.mjs diff --git a/apps/formbricks-com/mdx/remark.mjs b/apps/docs/mdx/remark.mjs similarity index 100% rename from apps/formbricks-com/mdx/remark.mjs rename to apps/docs/mdx/remark.mjs diff --git a/apps/formbricks-com/mdx/search.mjs b/apps/docs/mdx/search.mjs similarity index 100% rename from apps/formbricks-com/mdx/search.mjs rename to apps/docs/mdx/search.mjs diff --git a/apps/formbricks-com/next-sitemap.config.js b/apps/docs/next-sitemap.config.js similarity index 100% rename from apps/formbricks-com/next-sitemap.config.js rename to apps/docs/next-sitemap.config.js diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs new file mode 100644 index 0000000000..1abf537db4 --- /dev/null +++ b/apps/docs/next.config.mjs @@ -0,0 +1,118 @@ +import nextMDX from "@next/mdx"; + +import { recmaPlugins } from "./mdx/recma.mjs"; +import { rehypePlugins } from "./mdx/rehype.mjs"; +import { remarkPlugins } from "./mdx/remark.mjs"; +import withSearch from "./mdx/search.mjs"; + +const withMDX = nextMDX({ + options: { + remarkPlugins, + rehypePlugins, + recmaPlugins, + }, +}); + +/** @type {import('next').NextConfig} */ +const nextConfig = { + basePath: "/docs", + pageExtensions: ["js", "jsx", "ts", "tsx", "mdx"], + transpilePackages: ["@formbricks/ui", "@formbricks/lib"], + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "avatars.githubusercontent.com", + port: "", + }, + ], + }, + async redirects() { + return [ + { + source: "/", + destination: "/introduction/what-is-formbricks", + permanent: true, + }, + { + source: "/introduction/why-is-it-better", + destination: "/introduction/why-open-source", + permanent: true, + }, + // Redirects for Docs 2.0 + // Self Hosting + { + source: "/self-hosting/deployment", + destination: "/self-hosting/overview", + permanent: true, + }, + { + source: "/self-hosting/production", + destination: "/self-hosting/one-click", + permanent: true, + }, + { + source: "/self-hosting/external-auth-providers", + destination: "/self-hosting/configure", + permanent: true, + }, + { + source: "/self-hosting/enterprise", + destination: "/self-hosting/license", + permanent: true, + }, + // Developer Docs + { + source: "/contributing/:path", + destination: "/developer-docs/contributing", + permanent: true, + }, + { + source: "/additional-features/api", + destination: "/developer-docs/rest-api", + permanent: true, + }, + { + source: "/in-app-surveys/developer-quickstart", + destination: "/developer-docs/overview", + permanent: true, + }, + + // Link Survey + { + source: "/link-surveys/embed-in-email", + destination: "/link-surveys/embed-surveys", + permanent: true, + }, + // App & Website Survey + { + source: "/getting-started/quickstart-in-app-survey", + destination: "/app-surveys/quickstart", + permanent: true, + }, + { + source: "/getting-started/framework-guides", + destination: "/app-surveys/framework-guides", + permanent: true, + }, + { + source: "/in-app-surveys/:path", + destination: "/app-surveys/:path", + permanent: true, + }, + { + source: "/app-surveys/attributes", + destination: "/app-surveys/user-identification", + permanent: true, + }, + // Integrations + { + source: "/integrations/:path", + destination: "/developer-docs/integrations/:path", + permanent: true, + }, + ]; + }, +}; + +export default withSearch(withMDX(nextConfig)); diff --git a/apps/formbricks-com/package.json b/apps/docs/package.json similarity index 83% rename from apps/formbricks-com/package.json rename to apps/docs/package.json index 442b94a6f2..6dad3ddb7c 100644 --- a/apps/formbricks-com/package.json +++ b/apps/docs/package.json @@ -1,5 +1,5 @@ { - "name": "@formbricks/formbricks-com", + "name": "@formbricks/docs", "version": "1.0.0", "private": true, "scripts": { @@ -19,27 +19,27 @@ "@formbricks/lib": "workspace:*", "@formbricks/types": "workspace:*", "@formbricks/ui": "workspace:*", - "@headlessui/react": "^1.7.19", + "@headlessui/react": "^2.0.3", "@headlessui/tailwindcss": "^0.2.0", "@mapbox/rehype-prism": "^0.9.0", "@mdx-js/loader": "^3.0.1", "@mdx-js/react": "^3.0.1", - "@next/mdx": "14.2.1", + "@next/mdx": "14.2.3", "@paralleldrive/cuid2": "^2.2.2", "@sindresorhus/slugify": "^2.2.1", - "@tailwindcss/typography": "^0.5.12", + "@tailwindcss/typography": "^0.5.13", "acorn": "^8.11.3", "autoprefixer": "^10.4.19", - "clsx": "^2.1.0", + "clsx": "^2.1.1", "fast-glob": "^3.3.2", "flexsearch": "^0.7.43", - "framer-motion": "11.1.1", + "framer-motion": "11.1.9", "lottie-web": "^5.12.2", - "lucide": "^0.368.0", - "lucide-react": "^0.368.0", + "lucide": "^0.378.0", + "lucide-react": "^0.378.0", "mdast-util-to-string": "^4.0.0", "mdx-annotations": "^0.1.4", - "next": "14.1.3", + "next": "14.2.3", "next-plausible": "^3.12.0", "next-seo": "^6.5.0", "next-sitemap": "^4.2.3", @@ -47,10 +47,9 @@ "node-fetch": "^3.3.2", "prism-react-renderer": "^2.3.1", "prismjs": "^1.29.0", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "18.3.1", + "react-dom": "18.3.1", "react-highlight-words": "^0.20.0", - "react-icons": "^5.1.0", "react-markdown": "^9.0.1", "react-responsive-embed": "^2.1.0", "remark": "^15.0.1", diff --git a/apps/formbricks-com/postcss.config.js b/apps/docs/postcss.config.js similarity index 96% rename from apps/formbricks-com/postcss.config.js rename to apps/docs/postcss.config.js index 33ad091d26..12a703d900 100644 --- a/apps/formbricks-com/postcss.config.js +++ b/apps/docs/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/apps/formbricks-com/public/robots.txt b/apps/docs/public/robots.txt similarity index 100% rename from apps/formbricks-com/public/robots.txt rename to apps/docs/public/robots.txt diff --git a/apps/docs/robots.ts b/apps/docs/robots.ts new file mode 100644 index 0000000000..fc42c63945 --- /dev/null +++ b/apps/docs/robots.ts @@ -0,0 +1,10 @@ +import { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + }, + }; +} diff --git a/apps/formbricks-com/styles/tailwind.css b/apps/docs/styles/tailwind.css similarity index 72% rename from apps/formbricks-com/styles/tailwind.css rename to apps/docs/styles/tailwind.css index 52e86045d2..5a00a35a50 100644 --- a/apps/formbricks-com/styles/tailwind.css +++ b/apps/docs/styles/tailwind.css @@ -1,13 +1,13 @@ @layer base { :root { --shiki-color-text: theme("colors.white"); - --shiki-token-constant: theme("colors.emerald.300"); - --shiki-token-string: theme("colors.emerald.300"); + --shiki-token-constant: theme("colors.teal.300"); + --shiki-token-string: theme("colors.teal.300"); --shiki-token-comment: theme("colors.slate.500"); --shiki-token-keyword: theme("colors.sky.300"); --shiki-token-parameter: theme("colors.pink.300"); --shiki-token-function: theme("colors.violet.300"); - --shiki-token-string-expression: theme("colors.emerald.300"); + --shiki-token-string-expression: theme("colors.teal.300"); --shiki-token-punctuation: theme("colors.slate.200"); } diff --git a/apps/formbricks-com/tailwind.config.ts b/apps/docs/tailwind.config.ts similarity index 89% rename from apps/formbricks-com/tailwind.config.ts rename to apps/docs/tailwind.config.ts index c5d038de50..c34cac903d 100644 --- a/apps/formbricks-com/tailwind.config.ts +++ b/apps/docs/tailwind.config.ts @@ -10,13 +10,10 @@ export default { trailingSlash: true, content: [ // app content - "./app/**/*.{js,mjs,jsx,ts,tsx,mdx}", // Note the addition of the `app` directory. - "./pages/**/*.{js,mjs,jsx,ts,tsx,mdx}", + "./app/**/*.{js,mjs,jsx,ts,tsx,mdx}", "./components/**/*.{js,mjs,jsx,ts,tsx,mdx}", "./lib/**/*.{js,mjs,jsx,ts,tsx,mdx}", "./mdx/**/*.{js,mjs,jsx,ts,tsx,mdx}", - // include packages if not transpiling - "../../packages/ui/components/**/*.{js,ts,jsx,tsx}", ], darkMode: "class", theme: { @@ -66,13 +63,12 @@ export default { light: "#00C4B8", dark: "#00C4B8", }, - black: { DEFAULT: "#0F172A", }, }, fontFamily: { - sans: ["Poppins", ...defaultTheme.fontFamily.sans], + sans: ["Jost", ...defaultTheme.fontFamily.sans], display: ["Lexend", ...defaultTheme.fontFamily.sans], kablammo: ["Kablammo", "sans"], }, diff --git a/apps/formbricks-com/tsconfig.json b/apps/docs/tsconfig.json similarity index 88% rename from apps/formbricks-com/tsconfig.json rename to apps/docs/tsconfig.json index d956201bcd..c36afde61e 100644 --- a/apps/formbricks-com/tsconfig.json +++ b/apps/docs/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@formbricks/tsconfig/nextjs.json", "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "../../packages/types/*.d.ts"], - "exclude": ["../../.env"], + "exclude": ["../../.env", "node_modules"], "compilerOptions": { "baseUrl": ".", "paths": { diff --git a/apps/formbricks-com/types.d.ts b/apps/docs/types.d.ts similarity index 100% rename from apps/formbricks-com/types.d.ts rename to apps/docs/types.d.ts diff --git a/apps/formbricks-com/typography.ts b/apps/docs/typography.ts similarity index 99% rename from apps/formbricks-com/typography.ts rename to apps/docs/typography.ts index e07e8a9d41..1c429c48e6 100644 --- a/apps/formbricks-com/typography.ts +++ b/apps/docs/typography.ts @@ -42,7 +42,7 @@ export default function typographyStyles({ theme }: PluginUtils) { // Base color: "var(--tw-prose-body)", - fontSize: theme("fontSize.sm")[0], + fontSize: theme("fontSize.base")[0], lineHeight: theme("lineHeight.7"), // Layout @@ -192,14 +192,14 @@ export default function typographyStyles({ theme }: PluginUtils) { color: "var(--tw-prose-headings)", fontWeight: "600", fontSize: theme("fontSize.lg")[0], - ...theme("fontSize.lg")[1], + ...theme("fontSize.xl")[1], marginTop: theme("spacing.16"), marginBottom: theme("spacing.2"), }, h3: { color: "var(--tw-prose-headings)", fontSize: theme("fontSize.base")[0], - ...theme("fontSize.base")[1], + ...theme("fontSize.lg")[1], fontWeight: "600", marginTop: theme("spacing.10"), marginBottom: theme("spacing.2"), diff --git a/apps/formbricks-com/app/[surveyId]/route.ts b/apps/formbricks-com/app/[surveyId]/route.ts deleted file mode 100644 index bdde1107ba..0000000000 --- a/apps/formbricks-com/app/[surveyId]/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -export async function GET(_: Request, { params }: { params: { surveyId: string } }) { - const surveyId = params.surveyId; - // redirect to Formbricks Cloud - return Response.redirect(`https://app.formbricks.com/s/${surveyId}`, 301); -} diff --git a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/select-action.webp b/apps/formbricks-com/app/docs/best-practices/cancel-subscription/select-action.webp deleted file mode 100644 index 73ad11d8aa..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/select-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-css-selector.webp b/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-css-selector.webp deleted file mode 100644 index 3a25060152..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-css-selector.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-inner-text.webp b/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-inner-text.webp deleted file mode 100644 index 1d6d6a1eba..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-inner-text.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-page-url.webp b/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-page-url.webp deleted file mode 100644 index 86899f7a9a..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/cancel-subscription/trigger-page-url.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/docs-feedback/add-action.webp b/apps/formbricks-com/app/docs/best-practices/docs-feedback/add-action.webp deleted file mode 100644 index 36bbb7141d..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/docs-feedback/add-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/docs-feedback/select-nonevent.webp b/apps/formbricks-com/app/docs/best-practices/docs-feedback/select-nonevent.webp deleted file mode 100644 index e4cc697896..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/docs-feedback/select-nonevent.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/docs-feedback/when-to-ask.webp b/apps/formbricks-com/app/docs/best-practices/docs-feedback/when-to-ask.webp deleted file mode 100644 index a60180875b..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/docs-feedback/when-to-ask.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feature-chaser/action-css.webp b/apps/formbricks-com/app/docs/best-practices/feature-chaser/action-css.webp deleted file mode 100644 index caed1d464a..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feature-chaser/action-css.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feature-chaser/action-text.webp b/apps/formbricks-com/app/docs/best-practices/feature-chaser/action-text.webp deleted file mode 100644 index bf6e4de67a..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feature-chaser/action-text.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feature-chaser/select-action.webp b/apps/formbricks-com/app/docs/best-practices/feature-chaser/select-action.webp deleted file mode 100644 index 1b135d2c38..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feature-chaser/select-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feedback-box/add-action.webp b/apps/formbricks-com/app/docs/best-practices/feedback-box/add-action.webp deleted file mode 100644 index c79172199a..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feedback-box/add-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feedback-box/add-css-action.webp b/apps/formbricks-com/app/docs/best-practices/feedback-box/add-css-action.webp deleted file mode 100644 index 77a817fc0d..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feedback-box/add-css-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feedback-box/add-html-action.webp b/apps/formbricks-com/app/docs/best-practices/feedback-box/add-html-action.webp deleted file mode 100644 index 8a8d56a506..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feedback-box/add-html-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/feedback-box/select-feedback-button-action.webp b/apps/formbricks-com/app/docs/best-practices/feedback-box/select-feedback-button-action.webp deleted file mode 100644 index 2272220a9f..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/feedback-box/select-feedback-button-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/action-innertext.webp b/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/action-innertext.webp deleted file mode 100644 index b33216ca62..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/action-innertext.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/action-pageurl.webp b/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/action-pageurl.webp deleted file mode 100644 index 1ea56f32ce..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/action-pageurl.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/select-action.webp b/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/select-action.webp deleted file mode 100644 index d71d75c428..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/improve-trial-cr/select-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-css.webp b/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-css.webp deleted file mode 100644 index 97909b3e93..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-css.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-innertext.webp b/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-innertext.webp deleted file mode 100644 index 1ab648d6fb..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-innertext.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-pageurl.webp b/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-pageurl.webp deleted file mode 100644 index 5d97f2bed2..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/interview-prompt/action-pageurl.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/interview-prompt/add-action.webp b/apps/formbricks-com/app/docs/best-practices/interview-prompt/add-action.webp deleted file mode 100644 index d9ef879b4a..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/interview-prompt/add-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/interview-prompt/select-action.webp b/apps/formbricks-com/app/docs/best-practices/interview-prompt/select-action.webp deleted file mode 100644 index c0ffaebc51..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/interview-prompt/select-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/pmf-survey/action-css.webp b/apps/formbricks-com/app/docs/best-practices/pmf-survey/action-css.webp deleted file mode 100644 index 97909b3e93..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/pmf-survey/action-css.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/pmf-survey/action-pageurl.webp b/apps/formbricks-com/app/docs/best-practices/pmf-survey/action-pageurl.webp deleted file mode 100644 index 5d97f2bed2..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/pmf-survey/action-pageurl.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/best-practices/pmf-survey/select-action.webp b/apps/formbricks-com/app/docs/best-practices/pmf-survey/select-action.webp deleted file mode 100644 index c0ffaebc51..0000000000 Binary files a/apps/formbricks-com/app/docs/best-practices/pmf-survey/select-action.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/contributing/creating-a-service/page.mdx b/apps/formbricks-com/app/docs/contributing/creating-a-service/page.mdx deleted file mode 100644 index f07b37cd5f..0000000000 --- a/apps/formbricks-com/app/docs/contributing/creating-a-service/page.mdx +++ /dev/null @@ -1,278 +0,0 @@ -import { MdxImage } from "@/components/shared/MdxImage"; - -import UnstableCache from "./unstable-cache-documentation.webp"; - -export const metadata = { - title: "Formbricks Code Contribution Guide: How to create a service in Formbricks", - description: - "Services are the core backbone of the Formbricks codebase. This is the complete guide to help you create a service in Formbricks.", -}; - -#### Contributing - -# How to Create a Service - -In this guide, you will learn how to create a new service in Formbricks codebase. To begin let’s define what we mean when we use the word `Service` - - -A service is an abstraction of database calls related to a specific model in the database which comprises of cached functions that can perform generic database level functionalities. - - - -Let’s break down some of the jargon in that definition: - -**Abstraction of database calls** - -From our guide on [How we Code at Formbricks](https://formbricks.com/docs/contributing/how-we-code), we mention that database calls should not be made directly from components or other places other than a **service**. This means that if you need to make a request to the database to fetch some data, let’s say “get the **surveys** of the current user in the current **environment**”, you would need a function in the surveys service like `getSurveysByEnvironmentId`. It is also worth mentioning that we use [Prisma](https://prisma.io/) as a database abstraction layer to perform database calls. - -**Comprises of cached functions** - -A service consists of multiple functions that can be easily reused in server actions. The other important part of this is that the output of a function in a service MUST be cached so we don’t have make unnecessary database calls for data that hasn’t changed. We will talk more about caching in services a bit later. - -**Generic database level functionalities** - -By generic we mean that if in the `survey` service there is a function that only gets a survey and now you want a function to get both survey and all its responses, you should not create another function specifically for that. Instead use the `getSurvey` function and then a `getResponsesBySurveyId` function in the `response` service to get this data. The functions need to be generic so that they can be reused for cases like this where you need to combine multiple cached functions to get what you need. - -## Do you need a new service? - -Firstly you must note that you almost won’t need to create a new service unless a new model was created. If you think that you need a new service or a new function in an existing service, first double check if you can combine one or two existing functions in an existing service to achieve what you want. If you still think that it doesn’t meet your need, please discuss with Matti first with your specific use-case to get the green light to create a new service or function in a service. - -This is critical to us as a project because services are a key part of our project and we want to make them as organised, minimal, easy to change and use as possible. This is important to us as a team to move quickly and still keep a good and maintainable codebase. - -## Steps to creating a new service - -Below is a break down on how to create a new service, if you ned to implement a function in an existing service you can jump to Step 3: - -### Step 1: Create the service folder in `packages/lib` - -For the sake of this section, let’s say we just added a new model called `ApiKey`, (note this model already exists) - - - - -```sql -model ApiKey { - id String @id @unique @default(cuid()) - createdAt DateTime @default(now()) - lastUsedAt DateTime? - label String? - hashedKey String @unique() - environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade) - environmentId String -} -``` - - - - -**Step 1a**: The first thing you need to do is go to `packages/lib` and create a new folder called `apiKey`, note that this is the camel cased version of the Model name. - -**Step 1b**: We need to create the types for our service once we have the model. To do that you go to `packages/types` and create a file called `apiKey.ts`. - -In the type file, we must first create a Zod type that matches the Prisma model called`ZApiKey` (note here that it MUST begin with `Z` (indicating a Zod type) then the service name in pascal case). Next from this Zod type, we create a derived Typescript type called `TApiKey` (this MUST begin with a `T` and then the service name in pascal case). - -The reason we need both of them is because the Zod type is used for validating arguments passed into a service and we use the Typescript type to specify what data type a service function returns. - -### Step 2: Create `service.ts` and `cache.ts` in the service folder. - -The 2 required files are `service.ts` and `cache.ts`, note they are in singular form. - -`service.ts` - Where all the reusable cached functions are placed. - -`cache.ts` - Where the caching functionality for that service is abstracted to. - -### Step 3: Writing your functions in `service.ts` . - -A function in a service must have the following requirements: - -1. Follow the same naming pattern as we have in other services - - If using Prisma’s `findUnique` then the name should be `get` + `ServiceName` (in singular), e.g `getApiKey` - - If using Prisma’s `findMany` then the name should be `get` + `ServiceName` (in plural), e.g `getApiKeys` - - If your function's primary purpose is to retrieve or manipulate data based on a specific attribute or property of a resource, use "`by`" followed by the attribute name. For example: - - **`getMembersByTeamId`**: This function retrieves members filtered by the team's ID. - - **`getMembershipByUserIdTeamId`**: It retrieves a membership by the user's and team's IDs. - - If using Prisma’s `create` then `createApiKey` - - If using Prisma’s `update` then `updateApiKey` - - if using Prisma’s `delete` then `deleteApiKey` -2. All its arguments must be properly typed. -3. It should have a return type. -4. The arguments should be validated using `validateInputs` (reference the code to see how it is used) -5. Every function must return the standardised data types (`TApiKey`), including create or delete functions. -6. Handle errors in the function and return specific error types for DatabaseErrors. - - - A standardised data type is the derived Typescript type in this case `TApiKey` that matches the model of the - service. - - -Here is an example of a function that gets an api key by id: - - - - -```ts -export const getApiKey = async (apiKeyId: string): Promise => { - validateInputs([apiKeyId, ZString]); - - try { - const apiKeyData = await prisma.apiKey.findUnique({ - where: { - id: apiKeyId, - }, - }); - - if (!apiKeyData) { - throw new ResourceNotFoundError("API Key from ID", apiKeyId); - } - - return apiKeyData; - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseError(error.message); - } - - throw error; - } -}; -``` - - - - -### Step 4: Implementing caching for your function - -**Step 4a**: Firstly in the cache.ts file, you need to follow this structure: - - - - -```ts -import { revalidateTag } from "next/cache"; - -interface RevalidateProps { - id?: string; - environmentId?: string; -} - -export const apiKeyCache = { - tag: { - // Tags can be different depending on your use case - byId(id: string) { - return `apiKeys-${id}`; - }, - byEnvironmentId(environmentId: string) { - return `environments-${environmentId}-apiKeys`; - }, - }, - revalidate({ id, environmentId }: RevalidateProps): void { - if (id) { - revalidateTag(this.tag.byId(id)); - } - - if (environmentId) { - revalidateTag(this.tag.byEnvironmentId(environmentId)); - } - }, -}; -``` - - - - -_Breakdown of the above code._ - -1. **apiKeyCache**: The name of this object is `serviceName` + `Cache`, which is why this is called `apiKeyCache` . -2. **tag**: This object is where all the tags for the service cache will be stored. Read below for the definition of a tag -3. **byId**: This is the required tag, since every service must query by Id at some point, `byId` is a must have in each tag. It is used to revalidate the cache of a single item, e.g. `getApiKey(id)`. If there is a good reason not to query by id, you can avoid creating this tag. The returned string of this function needs to begin with the service name in plural then a dash and the id (which must be passed in). -4. **byEnvironmentId**: It is used to revalidate the cache of a list of items of the same parent, e.g. `getApiKeys(environmentId)`. For parent dependencies used to query this service, you should add the plural of the name in this case `environments` plus the id of the parent dependency plus the name of the service you are working with in plural, in this case `apiKeys` which results to `environments-${environmentId}-apiKeys`. -5. **revalidate**: This function receives an object with optional keys. Depending on the key that is passed in, we optionally call the `revalidateTag` from `next/cache` on the appropriate tag. Note each key passed into this function has to match a `tag`. - - - A tag is a label or metadata identifier attached to a piece of data, content, or an object to categorize, - classify, or organize it for easier retrieval, grouping, or management. In the context of revalidation, tags - are used to associate groups of cached data with specific events or triggers. When an event occurs, such as - a form submission or content update, the tags are used to identify and revalidate all the cached data items - associated with that tag. This ensures that the latest and most up-to-date data is retrieved and displayed - in response to the event, contributing to the effective management and real-time updating of cached content. - - - - We have a [script](https://gist.github.com/rotimi-best/7bd7e4ebda09a68ff0a1dc8ae6fa0009) that can help you - auto-generate the `cache.ts` file with the basic structure. - - -**Step 4b:** Now that you have the `cache.ts`, it is time to actually use the tags and revalidate method in your `service.ts`. - -We will rewrite the function `getApiKey` we created in the `service.ts` file to support caching: - - - - -```ts -import { cache } from "@formbricks/lib/cache"; - -import { apiKeyCache } from "./cache"; - -export const getApiKey = (apiKeyId: string): Promise => - cache( - async () => { - validateInputs([apiKeyId, ZString]); - - try { - const apiKeyData = await prisma.apiKey.findUnique({ - where: { - id: apiKeyId, - }, - }); - - if (!apiKeyData) { - throw new ResourceNotFoundError("API Key from ID", apiKeyId); - } - - return apiKeyData; - } catch (error) { - if (error instanceof Prisma.PrismaClientKnownRequestError) { - throw new DatabaseError(error.message); - } - - throw error; - } - }, - [`getApiKey-${apiKeyId}`], - { - tags: [apiKeyCache.tag.byId(apiKeyId)], - } - )(); -``` - - - - -_Breakdown of the above code._ - -In the above code we only introduce something new called `unstable_cache`, read more about it [here](https://nextjs.org/docs/app/api-reference/functions/unstable_cache#parameters). In a nutshell these are its parameters: - - - -From the screenshot above we see that `unstable_cache` receives 3 arguments: - -1. `fetchData`: In our case this is the exact function of your service without caching (step 3) -2. `keyParts`: As a rule of thumb, the key must consist of the name of the function and the arguments passed into the function, all separated by a dash. In our case it is called `getApiKey-${apiKeyId}` because the function name is `getApiKey` and we receive only one argument called `apiKeyId` -3. `options`: which consists of **tags** that controls the revalidation of the cache. This is where the tags you created in step 4a comes in, tags are created solely based on the arguments passed to the function. (please reference existing services in `packages/lib` to see more variations of this when dealing with more than one argument) - - -In create, update and delete requests, you don’t need caching however these are the places where the revalidate method is called. For example when the apiKey is deleted we want to call the revalidate method and pass in the id and environmentId, so we invalidate every cached function with `id` and `environmentId` tags. -`apiKeyCache.revalidate({ id: [apiKey.id](http://apikey.id/), environmentId: apiKey.environmentId });` - - - -### Step 5: Check if you need to add these 2 optional files (`auth.ts` and `util.ts`) - -`auth.ts` - Is for verifying if the user is authorised to access the service. Typically it has only one function with this naming `canUserAccessApiKey`. Please note that ApiKey at the end of the name is specific to the service name. - -`util.ts` - This file holds any helper function that is used in that specific service. For example one common use case for this files is for converting Date fields from string to Date. The reason for this is that when we cache a function using `unstable_cache`, [it does not support deserialisation of dates](https://github.com/vercel/next.js/issues/51613). We therefore need to manually deserialise date fields by writing a function that receives the data of a service and we check for its date fields that are in strings and we convert them into Date. diff --git a/apps/formbricks-com/app/docs/contributing/creating-a-service/unstable-cache-documentation.webp b/apps/formbricks-com/app/docs/contributing/creating-a-service/unstable-cache-documentation.webp deleted file mode 100644 index b2d7119704..0000000000 Binary files a/apps/formbricks-com/app/docs/contributing/creating-a-service/unstable-cache-documentation.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/contributing/demo/demoapp.webp b/apps/formbricks-com/app/docs/contributing/demo/demoapp.webp deleted file mode 100644 index 8e325cdcbf..0000000000 Binary files a/apps/formbricks-com/app/docs/contributing/demo/demoapp.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/contributing/demo/page.mdx b/apps/formbricks-com/app/docs/contributing/demo/page.mdx deleted file mode 100644 index b22a905453..0000000000 --- a/apps/formbricks-com/app/docs/contributing/demo/page.mdx +++ /dev/null @@ -1,98 +0,0 @@ -import { MdxImage } from "@/components/shared/MdxImage"; - -import DemoApp from "./demoapp.webp"; - -export const metadata = { - title: "Formbricks Demo App Guide: Play around with Formbricks", - description: - "To test in-app surveys, trigger actions and set attributes, you can use the Demo App. This guide provides hands-on examples of sending both code and no-code actions", -}; - -#### Contributing - -# Demo App - -To play around with the in-app [User Actions](/docs/actions/why), you can use the Demo App. It's a simple React app that you can run locally and use to trigger actions and set [Attributes](/docs/attributes/why). - - - -## Functionality - -### Code Action - -This button sends a Code Action to the Formbricks API called 'Code Action'. You will find it in the Actions Tab. - - - - -```javascript -formbricks.track("Code Action"); -``` - - - -### No Code Action - -This button sends a No Code Action as long as you created it beforehand in the Formbricks App. For it to work, you need to add the No Code Action within Formbricks. - - - - -```tsx - -``` - - - -### Set Plan to "Free" - -This button sets the attribute 'Plan' to 'Free'. If the attribute does not exist, it creates it. - - - - -```tsx -formbricks.setAttribute("Plan", "Free"); -``` - - - -### Set Plan to "Paid" - -This button sets the attribute 'Plan' to 'Paid'. If the attribute does not exist, it creates it. - - - - -```tsx -formbricks.setAttribute("Plan", "Paid"); -``` - - - -### Set Email - -This button sets the user email 'test@web.com' - - - - -```tsx -formbricks.setEmail("test@web.com"); -``` - - - -### Set UserId - -This button sets an external user ID in the Formbricks init call to 'THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING' - - - - -```tsx -userId: "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING"; -``` - - - diff --git a/apps/formbricks-com/app/docs/contributing/how-we-code/cors-handling-in-api.webp b/apps/formbricks-com/app/docs/contributing/how-we-code/cors-handling-in-api.webp deleted file mode 100644 index 10ced089d6..0000000000 Binary files a/apps/formbricks-com/app/docs/contributing/how-we-code/cors-handling-in-api.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/contributing/how-we-code/page.mdx b/apps/formbricks-com/app/docs/contributing/how-we-code/page.mdx deleted file mode 100644 index 7406ba3ef1..0000000000 --- a/apps/formbricks-com/app/docs/contributing/how-we-code/page.mdx +++ /dev/null @@ -1,141 +0,0 @@ -import { MdxImage } from "@/components/shared/MdxImage"; - -import CorsHandling from "./cors-handling-in-api.webp"; - -export const metadata = { - title: "Formbricks Code Contribution Guide: Best Practices and Standards", - description: - "Effortlessly Navigate Your Contribution Journey with Formbricks' Coding Guidelines and PR Review Process", -}; - -#### Contributing - -# How we Code at Formbricks - -Thank you for choosing to contribute to Formbricks. Before you start, please familiarize yourself with our coding standards and best practices, as these are key criteria for pull request reviews. Your contributions are greatly valued, and if you have any questions about these guidelines, please don't hesitate to ask. - -**Table of content** - -- Use Typescript types throughout the application -- Always prioritise Server components -- Fetch data only in server components -- Use Server-Action for mutations -- Use service abstraction instead of direct database calls -- Handle authentication and CORS in management APIs -- Always Document API changes -- Constants should be in the packages folder -- Types should be in the packages folder -- How we handle Pull Requests -- Read server-side environment variables from `constants.ts` - ---- - -## Use Typescript types throughout the application - -The entire codebase is written in TypeScript, and it is crucial that every new piece of code is thoroughly and accurately typed. Instead of resorting to using the `any` type for variables, please ensure that you explicitly specify the appropriate type. - -## Always prioritise Server components - -When it comes to prioritizing the development of our components, our main focus is on building them as server components. This ensures that they are optimized for server-side rendering and can handle any necessary interactivity seamlessly. However, in cases where a component requires client-side interactivity, we are able to adapt it into a client component. If you would like to learn more about the advantages and benefits of server components, we highly recommend reading the comprehensive documentation provided by Next.js, which can be accessed [here](https://nextjs.org/docs/app/building-your-application/rendering/server-components). - -## Fetch data only in server components - -In order to ensure that both data fetching and rendering take place on the server, it is expected that actions to fetch data from the server will be performed within server components. This approach is prioritized as discussed in the previous point, which provides further details on the benefits and importance of server components. - -**Note**: Data fetching is done in the `page.tsx` of the route or the corresponding server component that needs this data. - -## Use Server-Action for mutations - -Server actions are used to perform server actions in client components. For example, a button click (client-side) that should change something in the database. Server actions should be placed in an `actions.ts` file within the specific route to maintain code organization and facilitate efficient development. - -## Use service abstraction instead of direct database calls - -We utilize [prisma](https://www.prisma.io/) as our Object-Relational Mapping (ORM) tool to interact with the database. This implies that when you need to fetch or modify data in the database, you will be utilizing prisma. All prisma calls should be written in the services folder `packages/lib`, and before creating a new service, please ensure that one does not already exist. - -## Handle authentication and CORS in management APIs - -We have two APIs: Management API and Client API. - -The public endpoints of the Client API are used by the link survey and `formbricks-js` to send responses and displays to formbricks. Client APIs can be found in `apps/web/app/api/v1/client` - -The Management API offers the same functionality as the management frontend and can be used to create surveys, view responses or change account settings. The endpoints require an api key that the user can obtain in the management frontend. Management APIs can be found in `apps/web/app/api/v1/management`. - -Please keep the following in mind: - -- When dealing with Management APIs always make sure to require authentication via API keys and a sufficient authorization check. -- Make sure to handle CORS requests in any new Client API endpoint you create as these are called from the browser in link surveys or `formbricks-js`. Example below: - - - -## Always Document API changes - -It is imperative that any and all modifications or updates made to the client API are thoroughly and comprehensively documented. This documentation should provide clear and detailed information about the nature of the changes, their impact on existing functionality, and any new features or improvements introduced. This practice not only ensures transparency and accountability but also aids developers, both internal and external, in understanding and effectively utilizing the API, ultimately fostering a more robust and user-friendly development ecosystem. - -## Constants should be in the packages folder - -You should store constants in `packages/lib/constants` - -## Types should be in the packages folder - -You should store type in `packages/types` - -## Read server-side environment variables from `constants.ts` - -Server-side environment variables (`process.env`) shouldn’t be accessed directly but included into the `constants.ts` file and read from there. This way we can assure they are used only on the server side and are also type-safe. - -## How we handle Pull Requests - -We have a number of requirements for PRs to ensure they are as easy to review as possible and to ensure that they are up to standard with the code. - -### Code change - -When submitting a pull request, it is important to avoid combining multiple changes or issues into a single PR. By keeping each PR focused on a specific change or issue, it becomes easier and faster to review. Additionally, separating changes into individual PRs makes it easier to test each change independently. This allows for more efficient and thorough testing, ensuring that each change is properly validated before merging. - -### Title & Content - -We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). You should provide a short and concise title. Don’t put something generic (e.g. bug fixes), and instead mention more specifically what your PR achieves, for instance “fix: dropdown not expanding on survey page”. - -For the PR description, you should go into much greater detail about what your PR adds or fixes. Firstly, the description should include a link to any relevant issues or discussions surrounding the feature or bug that your PR addresses. - -### Feature PRs - -Give a functional overview of how your feature works, including how the user can use the feature. Then, share any technical details in an overview of how the PR works (e.g. “Once the user enters their password, the password is hashed using BCrypt and stored in the Users database field”). - -### Bug Fix PRs - -Give an overview of how your PR fixes the bug both as a high-level overview and a technical explanation of what caused the issue and how your PR resolves this. - -Add a short video or screenshots of what your PR achieves. Loom is a great way of sharing short videos however you can upload your videos directly to the PR description. - -### Code Quality & Styling - -It's really important to keep our code styles consistent so that the repository is easy to read and work with. - -We rely on various style guides from other amazing companies because they are widely used and we genuinely enjoy working with them. - -While we don't expect you to memorize every rule in these style guides, they serve as valuable references for how your code should be styled. However, if your code style significantly deviates from these guides, we may have to reject your pull request. - -#### ESLint & Prettier - -Formbricks uses the ESLint and Prettier formatting tools, and the repository comes with defined rules for each tool. We recommend setting up both tools and using these to help automatically style your code to our guidelines. - -#### HTML & CSS - -We use the **[Google HTML/CSS Style Guide](https://google.github.io/styleguide/htmlcssguide.html)** for any HTML and CSS markup. However, exceptions to the HTML guide apply where JSX differentiates from standard HTML. - -**Note:** We will reject pull requests that differ significantly from our standardised code styles. All code is automatically checked by Github actions, and will notify you if there are any issues with the code that you submit. We require that code passes these quality checks before merging. - -### PR review process - -At the moment Matti is responsible for approving and merging pull requests, so please make sure to request his review when opening the PR and make the changes he requests in order to merge the PR. - -### Making a Pull Request - -- Be sure to **[check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)** while creating your PR. -- If your PR refers to or fixes an issue, be sure to add **`refs #XXX`** or **`fixes #XXX`** to the PR description. Replacing **`XXX`** with the respective issue number. See more about **[Linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)** . -- Be sure to fill the PR Template accordingly. diff --git a/apps/formbricks-com/app/docs/contributing/introduction/page.mdx b/apps/formbricks-com/app/docs/contributing/introduction/page.mdx deleted file mode 100644 index 145d59f246..0000000000 --- a/apps/formbricks-com/app/docs/contributing/introduction/page.mdx +++ /dev/null @@ -1,45 +0,0 @@ -export const metadata = { - title: "Formbricks Open Source Contribution Guide: How to Enhance yourself and Contribute to Formbricks", - description: - "Join the Formbricks community and learn how to effectively contribute. From raising issues and feature requests to creating PRs, discover the best practices and communicate with our responsive team on Discord", -}; - -#### Contributing - -# Contribution Guide - -We are so happy that you are interested in contributing to Formbricks 🤗 - -There are many ways to contribute to Formbricks with writing Issues, fixing bugs, building new features or updating the docs. - -## Issues - -Spotted a bug? Has deployment gone wrong? Do you have user feedback? [Raise an issue](https://github.com/formbricks/formbricks/issues/new/choose) for the fastest response. - -... or pick up and fix an issue if you want to do a Pull Request. - -## Feature requests - -Raise an issue for these and tag it as an Enhancement. We love every idea. Please give us as much context on the why as possible. - -## Creating a PR - -Please fork the repository, make your changes and create a new pull request if you want to make an update. - -If you want to speak to us before doing lots of work, please join our [Discord server](https://formbricks.com/discord) and tell us what you would like to work on - we're very responsive and friendly! - -For QA of your Pull-Request, you can also get in touch with Matti on Discord. But we will also get to your PR without you taking additional action ;-) - -## Features - -We are currently working on having a clear [Roadmap](https://github.com/formbricks/formbricks) for the next steps ahead. - -But you can also pick a feature that is not already on the roadmap if you think it creates a positive impact for Formbricks. - -If you are at all unsure, just raise it as an enhancement issue first and tell us that you like to work on it, and we'll very quickly respond. - -## Contributor License Agreement (CLA) - -To be able to keep working on Formbricks over the coming years, we need to collect a CLA from all relevant contributors. - -Once you open a PR, you will get a message from the CLA bot to fill out the form. Please note that we can only get your contribution merged when we have a CLA signed by you. diff --git a/apps/formbricks-com/app/docs/contributing/troubleshooting/page.mdx b/apps/formbricks-com/app/docs/contributing/troubleshooting/page.mdx deleted file mode 100644 index 43c3ce2a49..0000000000 --- a/apps/formbricks-com/app/docs/contributing/troubleshooting/page.mdx +++ /dev/null @@ -1,84 +0,0 @@ -import { MdxImage } from "@/components/shared/MdxImage"; - -import ClearAppData from "./clear-app-data.webp"; -import Logout from "./logout.webp"; -import UncaughtPromise from "./uncaught-promise.webp"; - -export const metadata = { - title: "Formbricks Troubleshooting Guide: How to Solve & Debug Common Issues", - description: - "Facing issues with Formbricks? This troubleshooting guide covers frequently encountered problems, from Prisma migrations to package errors and more. Detailed solutions, accompanied by visual aids, ensure a smoother user experience with Formbricks", -}; - -#### Contributing - -# Troubleshooting - -Here you'll find help with frequently recurring problems - -## "The app doesn't work after doing a prisma migration" - -This can happen but fear not, the fix is easy: Delete the application storage of your browser and reload the page. This will force the app to re-fetch the data from the server: - - - -## "I ran 'pnpm i' but there seems to be an error with the packages" - -If nothing helps, run `pnpm clean` and then `pnpm i` again. This solves a lot. - -## "I get a full-screen error with cryptic strings" - -This usually happens when the Formbricks Widget wasn't correctly or completely built. - - - - -```bash -pnpm build --filter=js - -// Run the app again -pnpm dev -``` - - - -## My machine struggles with the repository - -Since we're working with a monorepo structure, the repository can get quite big. If you're having trouble working with the repository, try the following: - - - - -```bash {{ title: 'Formbricks Web-App' }} -pnpm dev --filter=web... -``` - -```bash {{ title: 'Formbricks Landing Page' }} -pnpm dev --filter=formbricks-com... -``` - -```bash {{ title: 'Formbricks Demo App' }} -pnpm dev --filter=demo... -``` - - - -However, in our experience it's better to run `pnpm dev` than having two terminals open (one with the Formbricks app and one with the demo). - -## Uncaught (in promise) SyntaxError: Unexpected token !DOCTYPE ... is not valid JSON - - - -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. - - diff --git a/apps/formbricks-com/app/docs/faq/page.mdx b/apps/formbricks-com/app/docs/faq/page.mdx deleted file mode 100644 index 4fc3b2a70e..0000000000 --- a/apps/formbricks-com/app/docs/faq/page.mdx +++ /dev/null @@ -1,14 +0,0 @@ -import FAQ from "@/components/docs/docsFaq"; - -export const metadata = { - 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). - - diff --git a/apps/formbricks-com/app/docs/getting-started/framework-guides/env-id.webp b/apps/formbricks-com/app/docs/getting-started/framework-guides/env-id.webp deleted file mode 100644 index ba69fb0f28..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/framework-guides/env-id.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/1-in-app-survey-or-popup-survey-setup.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/1-in-app-survey-or-popup-survey-setup.webp deleted file mode 100644 index 5d3240735d..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/1-in-app-survey-or-popup-survey-setup.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/2-settings-for-survey-popup-in-app-for-feedback.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/2-settings-for-survey-popup-in-app-for-feedback.webp deleted file mode 100644 index abd4cbb206..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/2-settings-for-survey-popup-in-app-for-feedback.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/3-web-app-survey-settings-for-in-app-survey-popup.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/3-web-app-survey-settings-for-in-app-survey-popup.webp deleted file mode 100644 index 20d7d194b6..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/3-web-app-survey-settings-for-in-app-survey-popup.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/4-in-app-survey-trigger-for-popup-survey.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/4-in-app-survey-trigger-for-popup-survey.webp deleted file mode 100644 index 9baca0597f..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/4-in-app-survey-trigger-for-popup-survey.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/5-options-survey-popup-in-app-for-feedback.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/5-options-survey-popup-in-app-for-feedback.webp deleted file mode 100644 index 6d167916f1..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/5-options-survey-popup-in-app-for-feedback.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/6-setup-in-app-survey-popup-feedback-box.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/6-setup-in-app-survey-popup-feedback-box.webp deleted file mode 100644 index 923625b83c..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/6-setup-in-app-survey-popup-feedback-box.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/7-in-app-survey-popup-for-feedback.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/7-in-app-survey-popup-for-feedback.webp deleted file mode 100644 index c66eba04af..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/7-in-app-survey-popup-for-feedback.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/8-pop-up-form-in-web-app-survey.webp b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/8-pop-up-form-in-web-app-survey.webp deleted file mode 100644 index b01dd5da8e..0000000000 Binary files a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/8-pop-up-form-in-web-app-survey.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/page.mdx b/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/page.mdx deleted file mode 100644 index 86005633e2..0000000000 --- a/apps/formbricks-com/app/docs/getting-started/quickstart-in-app-survey/page.mdx +++ /dev/null @@ -1,128 +0,0 @@ -import { MdxImage } from "@/components/shared/MdxImage"; - -import ReactApp from "../framework-guides/react-in-app-survey-app-popup-form.webp"; -import I1 from "./1-in-app-survey-or-popup-survey-setup.webp"; -import I2 from "./2-settings-for-survey-popup-in-app-for-feedback.webp"; -import I3 from "./3-web-app-survey-settings-for-in-app-survey-popup.webp"; -import I4 from "./4-in-app-survey-trigger-for-popup-survey.webp"; -import I5 from "./5-options-survey-popup-in-app-for-feedback.webp"; -import I6 from "./6-setup-in-app-survey-popup-feedback-box.webp"; -import I7 from "./7-in-app-survey-popup-for-feedback.webp"; -import I8 from "./8-pop-up-form-in-web-app-survey.webp"; - -export const metadata = { - title: "Formbricks Quickstart Guide: In-App Surveys Made Simple", - description: - "Launch your first in-app survey effortlessly. Dive into our step-by-step guide to set up, integrate, and debug Formbricks in your web app in under 15 minutes.", -}; - -#### Getting Started - -# Quickstart - -In-app surveys have 6-10x better conversion rates than emailed out surveys. This tutorial explains how to run an in-app survey in your web app in 10 to 15 minutes. Let’s go! - -## Create a free Formbricks Cloud account - -While you can [self-host](/docs/self-hosting/deployment) Formbricks, the quickest and easiest way to get started is with the free Cloud plan. Just [sign up here](https://app.formbricks.com/auth/signup) and click through the onboarding, until you’re here: - - - -## Create your first survey - -To be able to see a survey in your app, you need to create one. We’ll choose one of the templates and head over to the survey settings: - - - -As you can see in the orange note here, we have not yet connected Formbricks Cloud with our app. We will do so in just a minute, let’s first setup the survey correctly. - -Select “Web App” in the How to ask settings: - - - -Scroll down to Survey Trigger and choose “New Session”. This will cause this survey to appear when the Formbricks Widget tracks a new user session: - - - -In **Recontact Options** we choose the following settings, so that we can play around with the survey more easily. By default, each survey will be shown only once to each user to prevent survey fatigue: - - - -Now hit **Publish** and you’ll be forwarded to the Summary Page. This is where you’ll find the responses to this survey. On the Summary Page click through to the Setup Checklist: - - - -## Set up the Formbricks Widget in your app - -On the Setup Checklist you have two elements. At the top you find the Widget Status Indicator. Once your app is connected to Formbricks Cloud successfully, this will turn green: - - - -In the manual below, this code snippet contains all the information you need: - -- The **Environment ID** of your current Formbricks workspace -- The **API Host** which is ‘https://app.formbricks.com’ for Cloud users - - - -If you like to use the user identification feature, please follow the [user identification guide](/docs/attributes/identify-users). - -## Load Formbricks widget in your app - -In a local instance of your app, you'll embed the Formbricks Widget. Dependent on your frontend tech, the setup differs a bit: - -- [Next.JS App Directory](/docs/getting-started/framework-guides#next-js) -- [Next.JS Pages Directory](/docs/getting-started/framework-guides#next-js) -- [React (Create React App)](/docs/getting-started/framework-guides#react-js) -- [Vue.JS](/docs/getting-started/framework-guides#vue-js) - -## Restart your app and open browser console - -Now, restart your app in your terminal to make sure the widget is loaded. Once its loaded, open the browser console to see the Formbricks debug logs. If you did everything right, you should now see your survey in the lower right corner: - - diff --git a/apps/formbricks-com/app/docs/getting-started/troubleshooting/page.mdx b/apps/formbricks-com/app/docs/getting-started/troubleshooting/page.mdx deleted file mode 100644 index 339a176a1b..0000000000 --- a/apps/formbricks-com/app/docs/getting-started/troubleshooting/page.mdx +++ /dev/null @@ -1,96 +0,0 @@ -import { MdxImage } from "@/components/shared/MdxImage"; - -import I1 from "./1-set-up-in-app-micro-survey-popup.webp"; -import I2 from "./2-micro-survey-pop-up-in-app.webp"; -import I3 from "./3-survey-logs-in-app-survey-popup.webp"; - -export const metadata = { - title: "Formbricks Troubleshooting Guide", - description: - "Troubleshoot your Formbricks integration and get your in-app surveys up and running in no time. Most frequent errors and how to debug them.", -}; - -#### Getting Started - -# Troubleshooting Guide - -In case you don’t see your survey right away, here's what you can do. Go through these to find the error fast: - -## Formbricks Cloud and your app are not connected properly. - -Go back to [app.formbricks.com](http://app.formbricks.com) or your self-hosted instance's URL and go to the Setup Checklist in the Settings. If the status is still indicated as “Not connected” your app hasn't yet pinged the Formbricks Cloud: - - -**How to fix it:** - -1. Check if your app loads the Formbricks widget correctly. -2. Make sure you have `debug` mode enabled in your integration and you should see the Formbricks debug logs in your browser console while being in your app (right click in the browser, `Inspect`, switch to the console tab). If you don’t see them, double check your integration. - ---- - -## Survey not loaded - -If your app is connected with Formbricks Cloud, the survey might have not been loaded properly. Check the debug logs and search for the list of surveys loaded. It should look like so: - - - -**How to fix it:** - -The widget only loads surveys which are **public** and **in progress**. Go to Formbricks Cloud and to the Survey Summary page. Check if your survey is live: - - - ---- - -## Survey not triggered - -If the survey is loaded by the widget it might not have been triggered properly. - -**How to fix:** - -1. Open your local app in an incognito tab or window. The New Session event is only fired if a user was inactive for 60 minutes or was logged out of Formbricks via formrbicks.logout(). -2. Check the debug logs for “Event ‘New Session” tracked”. If you see it in the logs and the survey still did not get displayed, [please let us know.](mailto:support@formbricks.com) - ---- - -## Survey not displayed in HTML page - -If the survey is loaded by the widget in the HTML page, try the below steps: - -**How to fix:** - -1. Make sure you have added the [script](/docs/getting-started/framework-guides#html) in the head of the HTML page. -2. Verify that you have set the \ and \ as per your Formbricks instance. -3. Verify that you have the latest version of the JS Package. -4. Check the debug logs to see if you still see any errors. - ---- - -## Cannot read undefined of .init() - -If you see this error in the console, it means that the Formbricks JS package is not loaded properly. - -**How to fix:** - -1. Update to the latest version of the JS Package. -2. Verify this wherever you call initialise the Formbricks instance in your code. -3. It should now start working. - ---- - -If you are still facing issues, please [Open an Issue on GitHub](https://github.com/formbricks/formbricks/issues) or [join our Discord](https://formbricks.com/discord)! We’re more than happy to help you get started 😊 diff --git a/apps/formbricks-com/app/docs/in-app-surveys/actions/page.mdx b/apps/formbricks-com/app/docs/in-app-surveys/actions/page.mdx deleted file mode 100644 index be55037f49..0000000000 --- a/apps/formbricks-com/app/docs/in-app-surveys/actions/page.mdx +++ /dev/null @@ -1,73 +0,0 @@ -export const metadata = { - title: "Using Actions in Formbricks | Fine-tuning User Moments", - description: - "Dive deep into how actions in Formbricks help products and teams to engage users at precise moments in their journey. Discover the power of actions, from coding to no-code setups, to refine user targeting and generate richer, more detailed user insights.", -}; - -#### In-App Surveys - -# Actions - -You want to understand what your users think and feel during specific moments in the user journey. To be able to ask at exactly the right point in time, you need actions. - -## How do Actions work? - -Actions are a little notification sent from your application to Formbricks. When a predefined action happens in your app, the Formbricks widget notices. This action can then trigger a survey to be shown to the user and is stored in the database. -You decide which actions are sent either in your [Code](#code-actions) or by setting up a [No-Code](#no-code-actions) action within Formbricks. - -### Why are actions useful? - -Actions help you to display your surveys at the right time. Later on, you will be able to segment your users based on the actions they have triggered in the past. This way, you can create much more granular user segments, e.g. only target users that already have used a specific feature. - -## No-Code Actions - -No-Code actions can be set up within Formbricks with just a few clicks. There are three types of No-Code actions: - -1. **Page URL Action** - -The page URL action is triggered, when a user visits a specific page in your application. There are several match conditions: - -- `exactMatch`: The URL should exactly match the provided string. -- `contains`: The URL should contain the specified string as a substring. -- `startsWith`: The URL should start with the specified string. -- `endsWith`: The URL should end with the specified string. -- `notMatch`: The URL should not match the specified condition. -- `notContains`: The URL should not contain the specified string as a substring. - -2. **innerText Action** - -The innerText action checks if the `innerText` of a clicked HTML element matches a specific text, e.g. the label of a button. Display a survey on any button click! - -3. **CSS Selector Action** - -The CSS Selector action checks if the provided CSS selector matches the selector of a clicked HTML element. The CSS selector can be a class, id or any other CSS selector within your website. Display a survey on any element click! - -## Code Actions - -Actions can also be set in the codebase to trigger surveys. Please add the code action first in the Formbricks web interface to be able to configure your surveys to use this action. - -After that you can fire an action using `formbricks.track()` - - - - -```javascript -formbricks.track("Action Name"); -``` - - - -Here is an example of how to fire an action when a user clicks a button: - - - -```javascript -const handleClick = () => { - formbricks.track("Button Clicked"); -}; - -return ; -``` - - - diff --git a/apps/formbricks-com/app/docs/in-app-surveys/attributes/page.mdx b/apps/formbricks-com/app/docs/in-app-surveys/attributes/page.mdx deleted file mode 100644 index 31b1afdfe2..0000000000 --- a/apps/formbricks-com/app/docs/in-app-surveys/attributes/page.mdx +++ /dev/null @@ -1,118 +0,0 @@ -export const metadata = { - title: "Understanding User Attributes in Formbricks Surveys", - description: - "Dive into the importance of attributes in surveys. Learn how key-value pairs can significantly improve survey targeting, enhance feedback quality, and guide data-driven decisions with Formbricks.", -}; - -#### In-App Surveys - -# Attributes - -Surveying your user base without segmentation leads to weak results and survey fatigue. Attributes help you segment your users into groups. - -## How do Attributes work? - -Attributes are **key-value pairs** that you can set for each person individually. -Attributes are sent from your application to Formbricks and are associated with the current user. We store it in our database and allow you to use it the next time you create a survey. They help show surveys to the right group of people. - - - At Formbricks, we value user privacy. By default, Formbricks doesn't collect or store any personal information from your users. - - -## Identifying Users -To use the User Identification feature of Formbricks, target surveys to specific user segments and see more information about the user who responded to a survey, you can identify users by setting a User ID, email, and custom attributes. Below is how to do that. - -### Setting User ID - -To enable the User identification feature you need to set the `userId` in the init() call of Formbricks. Only when the `userId` is set the person will be visible in the Formbricks dashboard. The `userId` can be any string and it's best to use the default identifier you use in your app (e.g. unique id from database or the email address if it's unique) but you can also anonymize these as long as they are unique for every user. - - - - -```javascript -formbricks.init({ - environmentId: "", - apiHost: "", - userId: "", -}); -``` - - - - -### Enhanced Initialization with User Attributes - -In addition to setting the `userId`, Formbricks allows you to set user attributes right at the initialization. This ensures that your user data is seamlessly integrated from the start. Here's how you can include user attributes in the `init()` function: - - - - -```javascript -formbricks.init({ - environmentId: "", - apiHost: "", - userId: "", - attributes: { - // your custom attributes - Plan: "premium", - }, -}); -``` - - - - -### Setting User Email - -The `userId` is the main identifier used in Formbricks and user identification is only enabled when it is set. In addition to the userId you can also set attributes that describes the user better. The email address can be set using the setEmail function: - - - - -```javascript -formbricks.setEmail("user@example.com"); -``` - - - - -## Setting Custom User Attributes - -You can use the setAttribute function to set any custom attribute for the user (e.g. name, plan, etc.): - - - - -```javascript -formbricks.setAttribute("Plan", "free"); -``` - - - - -Generally speaking, the setAttribute function works like this: - - - -```javascript -formbricks.setAttribute("attribute_key", "attribute_value"); -``` - - - -Where `attributeName` is the name of the attribute you want to set, and `attributeValue` is the value of the attribute you want to set. - - -### Logging Out Users - -When a user logs out of your webpage, make sure to log them out of Formbricks as well. This will prevent new activity from being associated with an incorrect user. Use the logout function: - - - - -```javascript -formbricks.logout(); -``` - - - diff --git a/apps/formbricks-com/app/docs/in-app-surveys/developer-quickstart/page.mdx b/apps/formbricks-com/app/docs/in-app-surveys/developer-quickstart/page.mdx deleted file mode 100644 index fdbc457c3f..0000000000 --- a/apps/formbricks-com/app/docs/in-app-surveys/developer-quickstart/page.mdx +++ /dev/null @@ -1,184 +0,0 @@ -import DemoPreview from "@/components/dummyUI/DemoPreview"; -import { MdxImage } from "@/components/shared/MdxImage"; - -export const metadata = { - title: "Formbricks Functions for Frontend Developers: Quickstart Guide", - description: - "An overview of all available options for frontend developers to integrate and display surveys in web applications. Learn the key methods, configuration settings, and best practices.", -}; - -# Formbricks Functions for Frontend Developers: Quickstart Guide - -An overview of all available options for frontend developers to integrate and display surveys in web applications. Learn the key methods, configuration settings, and best practices. - -## Formbricks Integration - -Integrating Formbricks into your web application is straightforward, but it's important to follow the correct steps to ensure proper setup and functionality. For detailed instructions tailored to your development environment, please refer to our [Framework Guides](/docs/getting-started/framework-guides). - -## Check Availability - -Before using Formbricks, it's best practice to confirm it's available on the current web page. You can use a simple check to ensure Formbricks is not null: - - - - -```javascript -if (window.formbricks !== null) { - console.log("Formbricks is available"); -} else { - console.error("Formbricks is not available"); -} -``` - - - - -If Formbricks is not available, ensure it's properly integrated into your web application according to the installation guide. - -## Initializing Formbricks - -To start using Formbricks features, you must initialize it with the necessary environment information. This can be done with or without user identification. -For comprehensive guidance on setting up user identification, please consult our [User Identification Guide](/docs/in-app-surveys/user-identification). - -### Initialization Without User ID - -If you do not need user-specific tracking (e.g. on public facing websites with a lot of traffic), you can initialize Formbricks with just the `environmentId` and `apiHost`: - - - - -```javascript -formbricks.init({ - environmentId: "", - apiHost: "", -}); -``` - - - - -### Initialization With User ID - -If you need user-specific tracking, initialize Formbricks with a `userId` in addition to the `environmentId` and `apiHost`: - - - - -```javascript -formbricks.init({ - environmentId: "", - apiHost: "", - userId: "", -}); -``` - - - - -This setup allows you to associate interactions with specific users, enabling detailed tracking and user-specific functionality. - -## Enabling Debug Mode - -To enable debug mode in Formbricks, add `?formbricksDebug=true` to your URL. This activates detailed debug messages in the browser console, providing deeper insights into Formbricks' operation and potential issues. - -For comprehensive guidance on using debug mode, refer to our [Debug Mode Documentation](/docs/getting-started/framework-guides#debugging-formbricks-integration). It covers additional troubleshooting tips and information on analyzing Formbricks' integration. -## Tracking Code Actions - -Formbricks allows you to track code-based actions, which is useful for logging events or user interactions in your application. -For detailed information on using code-based actions, please visit our [Code Actions Guide](/docs/in-app-surveys/actions#code-actions). - - - - -```javascript -formbricks.track("Code Action"); -``` - - - - -The parameter "Code Action" can be customized to represent various actions, providing flexibility for different tracking scenarios. - -## Setting User Attributes - - - Please note that this function requires user identification to be active. Ensure you've initialized Formbricks with a `userId` before attempting to use this function. - - -If user identification is active, you can set custom attributes for the identified user. This can be helpful for segmenting users based on specific characteristics or properties. -To learn how to set custom user attributes, please check out our [User Attributes Guide](/docs/in-app-surveys/attributes#setting-custom-user-attributes). - - - - -```javascript -formbricks.setAttribute("Plan", "Paid"); -``` - - - - -In this example, the "Plan" attribute is set to "Paid", indicating the user's subscription status. You can set multiple attributes to capture additional user information. These attributes can be used to target surveys to specific users. You cannot set `userId` via this function. - - - -## Setting User Email - - - Please note that this function requires user identification to be active. Ensure you've initialized Formbricks with a `userId` before attempting to use this function. - - -To associate an email address with an identified user, you can use the `setEmail` function. This is useful for linking a user's email with their interactions: - - - - -```javascript -formbricks.setEmail("test@example.com"); -``` - - - - -Setting the email can facilitate user communication and integration with other systems, enabling a more seamless user experience. - -## Logout - -To log out and deinitialize Formbricks, use the `formbricks.logout()` function. This action clears the current initialization configuration and erases stored frontend information, such as the surveys a user has viewed or completed. It's an important step when a user logs out of your application or when you want to reset Formbricks. - - - - -```javascript -formbricks.logout(); -``` - - - - - - After calling `formbricks.logout()`, you'll need to reinitialize Formbricks before using any of its features again. Ensure that you properly reinitialize Formbricks to avoid unexpected errors or behavior in your application. - - -## Resetting Formbricks - -The `formbricks.reset()` function is a convenient way to clear current Formbricks data, and then reinitialize Formbricks. This action not only removes stored frontend information, such as the surveys a user has viewed or completed, but also reinitializes Formbricks with the settings used during the original initialization. - - - - -```javascript -formbricks.reset(); -``` - - - - -After calling `formbricks.reset()`, Formbricks is reinitialized, meaning you can continue using its features without additional setup. Be aware that any custom attributes, tracking, or user-specific data will be cleared and restored to the initial state. - -## Deprecated Function - -Please be aware that the following function is deprecated and should not be used in new code or maintained projects: - -- `formbricks.setUserId()`: Instead of using this function, set the `userId` during the initialization of Formbricks. This change ensures a more consistent approach to user identification and reduces the risk of errors due to inconsistent user context. - diff --git a/apps/formbricks-com/app/docs/integrations/n8n/add-api-key.png b/apps/formbricks-com/app/docs/integrations/n8n/add-api-key.png deleted file mode 100644 index 3ae426aedd..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/add-api-key.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/add-discord.png b/apps/formbricks-com/app/docs/integrations/n8n/add-discord.png deleted file mode 100644 index 12a1dc46ab..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/add-discord.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/add-formbricks-trigger.png b/apps/formbricks-com/app/docs/integrations/n8n/add-formbricks-trigger.png deleted file mode 100644 index c02c24ec31..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/add-formbricks-trigger.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/create-new-credential-btn.png b/apps/formbricks-com/app/docs/integrations/n8n/create-new-credential-btn.png deleted file mode 100644 index 35135a45c5..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/create-new-credential-btn.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/discord-response.png b/apps/formbricks-com/app/docs/integrations/n8n/discord-response.png deleted file mode 100644 index ded4daf03b..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/discord-response.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/duplicate-survey.png b/apps/formbricks-com/app/docs/integrations/n8n/duplicate-survey.png deleted file mode 100644 index 4d586acf54..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/duplicate-survey.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/fill-discord-details.png b/apps/formbricks-com/app/docs/integrations/n8n/fill-discord-details.png deleted file mode 100644 index 8b9f4b0b2c..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/fill-discord-details.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/listen-for-event.png b/apps/formbricks-com/app/docs/integrations/n8n/listen-for-event.png deleted file mode 100644 index 97bc5e2e0b..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/listen-for-event.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/select-event.png b/apps/formbricks-com/app/docs/integrations/n8n/select-event.png deleted file mode 100644 index a029f4772c..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/select-event.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/select-survey.png b/apps/formbricks-com/app/docs/integrations/n8n/select-survey.png deleted file mode 100644 index 22f4cf27ca..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/select-survey.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/selected-surveys.png b/apps/formbricks-com/app/docs/integrations/n8n/selected-surveys.png deleted file mode 100644 index 65571488b1..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/selected-surveys.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/submit-test-response.png b/apps/formbricks-com/app/docs/integrations/n8n/submit-test-response.png deleted file mode 100644 index 2476a8f07d..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/submit-test-response.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/success-connection.png b/apps/formbricks-com/app/docs/integrations/n8n/success-connection.png deleted file mode 100644 index 1a21bcb8e2..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/success-connection.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/test-response-success.png b/apps/formbricks-com/app/docs/integrations/n8n/test-response-success.png deleted file mode 100644 index c8f2d4b307..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/test-response-success.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/n8n/update-question-id.png b/apps/formbricks-com/app/docs/integrations/n8n/update-question-id.png deleted file mode 100644 index 21705f00c3..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/n8n/update-question-id.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/connect-with-notion.png b/apps/formbricks-com/app/docs/integrations/notion/images/connect-with-notion.png deleted file mode 100644 index 56ba5885dd..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/connect-with-notion.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/delete-connection.png b/apps/formbricks-com/app/docs/integrations/notion/images/delete-connection.png deleted file mode 100644 index e9cf8e2adf..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/delete-connection.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/integrations-tab.png b/apps/formbricks-com/app/docs/integrations/notion/images/integrations-tab.png deleted file mode 100644 index 820cb41ed1..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/integrations-tab.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/link-survey-with-database.png b/apps/formbricks-com/app/docs/integrations/notion/images/link-survey-with-database.png deleted file mode 100644 index 0ae7085f8b..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/link-survey-with-database.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/link-with-databases.png b/apps/formbricks-com/app/docs/integrations/notion/images/link-with-databases.png deleted file mode 100644 index 25d6c52594..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/link-with-databases.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/list-linked-databases.png b/apps/formbricks-com/app/docs/integrations/notion/images/list-linked-databases.png deleted file mode 100644 index 08e71f4b3b..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/list-linked-databases.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/notion/images/notion-connected.png b/apps/formbricks-com/app/docs/integrations/notion/images/notion-connected.png deleted file mode 100644 index 6c7a3d7303..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/notion/images/notion-connected.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/connect-with-slack.png b/apps/formbricks-com/app/docs/integrations/slack/images/connect-with-slack.png deleted file mode 100644 index dd023b31af..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/connect-with-slack.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/delete-connection.png b/apps/formbricks-com/app/docs/integrations/slack/images/delete-connection.png deleted file mode 100644 index 90a443f418..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/delete-connection.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/integrations-tab.png b/apps/formbricks-com/app/docs/integrations/slack/images/integrations-tab.png deleted file mode 100644 index 4bd6fa70a6..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/integrations-tab.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/link-survey-with-channel.png b/apps/formbricks-com/app/docs/integrations/slack/images/link-survey-with-channel.png deleted file mode 100644 index efe514a0e1..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/link-survey-with-channel.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/link-with-questions.png b/apps/formbricks-com/app/docs/integrations/slack/images/link-with-questions.png deleted file mode 100644 index d7321cfd98..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/link-with-questions.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/list-linked-surveys.png b/apps/formbricks-com/app/docs/integrations/slack/images/list-linked-surveys.png deleted file mode 100644 index ae5acf7e03..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/list-linked-surveys.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/slack-auth.png b/apps/formbricks-com/app/docs/integrations/slack/images/slack-auth.png deleted file mode 100644 index 6a5c8b59e0..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/slack-auth.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/integrations/slack/images/slack-connected.png b/apps/formbricks-com/app/docs/integrations/slack/images/slack-connected.png deleted file mode 100644 index 8509e8896e..0000000000 Binary files a/apps/formbricks-com/app/docs/integrations/slack/images/slack-connected.png and /dev/null differ diff --git a/apps/formbricks-com/app/docs/introduction/how-it-works/targeting.webp b/apps/formbricks-com/app/docs/introduction/how-it-works/targeting.webp deleted file mode 100644 index 96e04d3b55..0000000000 Binary files a/apps/formbricks-com/app/docs/introduction/how-it-works/targeting.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/introduction/how-it-works/trigger.webp b/apps/formbricks-com/app/docs/introduction/how-it-works/trigger.webp deleted file mode 100644 index cee42447bd..0000000000 Binary files a/apps/formbricks-com/app/docs/introduction/how-it-works/trigger.webp and /dev/null differ diff --git a/apps/formbricks-com/app/docs/introduction/what-is-formbricks/page.mdx b/apps/formbricks-com/app/docs/introduction/what-is-formbricks/page.mdx deleted file mode 100644 index 295904cd09..0000000000 --- a/apps/formbricks-com/app/docs/introduction/what-is-formbricks/page.mdx +++ /dev/null @@ -1,43 +0,0 @@ -import { GettingStarted } from "@/components/docs/GettingStarted"; -import { HeroPattern } from "@/components/docs/HeroPattern"; -import { Button } from "@/components/docs/Button"; - -export const metadata = { - title: "Formbricks: Privacy-first Experience Management", - description: - "Enhance your product with Formbricks – the leading open-source solution for in-product micro-surveys. Dive deep into user research, amplify product-market fit, and uncover the 'why' behind your analytics.", -}; - -export const sections = []; - - - -# Formbricks – Open Source Experience Management - -Welcome to Formbricks, your go-to solution for in-product micro-surveys that will supercharge your product experience! 🚀 {{ className: 'lead' }} - -
    -
    - -## Why Formbricks? 🤔 - -Natively embed qualitative user research into your B2B SaaS. Leverage Best Practices for user discovery to increase Product-Market Fit. {{ className: 'lead' }} - -- 🎯 **Tailor-made for SaaS & digital products**: Craft stunning, highly configurable surveys that enable better product decisions, deep user segmentation, and personalization. -- 🌐 **Platform agnostic**: Seamlessly integrate Formbricks surveys into web, mobile, or desktop applications. -- 📊 **Complete the analytics puzzle**: Answer the "why" behind your product analytics with insightful data analysis and visualization tools. -- 🧪 **Smart triggering**: Show the right survey at the right time with event-based triggers for accurate research and well-defined priorities. -- 🎉 **Open-source and self-hosted**: Enjoy full control over your data and infrastructure with our AGPL-licensed solution, and stay tuned for our upcoming cloud version! - -
    -
    - - diff --git a/apps/formbricks-com/app/docs/introduction/why-is-it-better/page.mdx b/apps/formbricks-com/app/docs/introduction/why-is-it-better/page.mdx deleted file mode 100644 index d388f12752..0000000000 --- a/apps/formbricks-com/app/docs/introduction/why-is-it-better/page.mdx +++ /dev/null @@ -1,24 +0,0 @@ -export const metadata = { - title: "Formbricks vs. Generic Survey Tools: A Comparative Insight", - description: - "Discover how Formbricks excels as a specialized in-product micro-survey platform for SaaS. Get unmatched targeting, seamless integrations, and make informed decisions with our open-source advantage.", -}; - -#### Introduction - -# Why is Formbricks better? - -Formbricks outshines other survey tools by specializing in in-product micro-surveys for SaaS and digital products. With Formbricks, you're better equipped to understand user behavior, improve your product, and make data-driven decisions. Let's see how Formbricks compares to generic survey tools. - -| Feature | Generic Survey Tool | Formbricks In-Product Surveys | -| --------------------------- | ------------------- | ----------------------------- | -| Designed for SaaS | ❌ | ✅ | -| In-product micro-surveys | ⚠️ (limited) | ✅ | -| Customizable forms | ✅ | ✅ | -| Fine-grained user targeting | ❌ | ✅ | -| Seamless integrations | ⚠️ (limited) | ✅ | -| Open-source | ❌ | ✅ | -| Event-based triggers | ❌ | ✅ | -| User segmentation | ❌ | ✅ | - -With Formbricks, you're not just getting another survey tool, but an in-depth, data-driven solution tailor-made for digital products 🎉 diff --git a/apps/formbricks-com/app/docs/layout.tsx b/apps/formbricks-com/app/docs/layout.tsx deleted file mode 100644 index 6be68ac6af..0000000000 --- a/apps/formbricks-com/app/docs/layout.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { Providers } from "@/app/providers"; -import { Layout } from "@/components/docs/Layout"; -import { type Section } from "@/components/docs/SectionProvider"; -import glob from "fast-glob"; -import { type Metadata } from "next"; - -export const metadata: Metadata = { - title: { - template: "%s - Formbricks Docs", - default: "Formbricks Docs", - }, -}; - -export default async function RootLayout({ children }: { children: React.ReactNode }) { - let pages = await glob("**/*.mdx", { cwd: "app/docs" }); - let allSectionsEntries = (await Promise.all( - pages.map(async (filename) => [ - "/docs/" + filename.replace(/(^|\/)page\.mdx$/, ""), - (await import(`./${filename}`)).sections, - ]) - )) as Array<[string, Array
    ]>; - let allSections = Object.fromEntries(allSectionsEntries); - - return ( - -
    - {children} -
    -
    - ); -} diff --git a/apps/formbricks-com/app/docs/link-surveys/embed-in-email/page.mdx b/apps/formbricks-com/app/docs/link-surveys/embed-in-email/page.mdx deleted file mode 100644 index eea532e387..0000000000 --- a/apps/formbricks-com/app/docs/link-surveys/embed-in-email/page.mdx +++ /dev/null @@ -1,146 +0,0 @@ -import { TellaVideo } from "@/components/docs/TellaVideo"; -import { MdxImage } from "@/components/shared/MdxImage"; - -import EmailContentWithSurvey from "./email-content-with-survey.webp"; -import EmailContentWithoutSurvey from "./email-content-without-survey.webp"; -import JoSignature from "./jo-signature.webp"; -import PluginAddSurvey from "./plugin-add-survey.webp"; -import PluginSourceTab from "./plugin-source-tab.webp"; - -export const metadata = { - title: "Embed Surveys in Your Emails", - description: "Embed Formbricks surveys seamlessly into your emails to collect feedback from your users.", -}; - -#### Link Surveys - -# Embed Surveys in Your Email - -Embedding Formbricks surveys directly into your emails allows you to collect valuable feedback from your users at every touchpoint. Seamlessly integrate interactive surveys into your email campaigns to gather insights and improve user experience. - -## Generate an Embed Code - -To embed a Formbricks survey in an email, first, create a survey and publish it. Follow these steps to generate the embed code: - -1. **Create and Publish a Survey**: Start by creating a link survey and publish it once ready. -2. **Access the Share Options**: Go to the survey summary page and click the Share button (see below). -3. **Get the Embed Code**: Click on Embed Survey at the bottom of the share modal, navigate to the `Embed in an Email` tab, and click `Copy Code`. -4. **Preview and Test**: Before sending it to your users, click on Send Preview to receive a test email. This helps ensure the survey appears correctly. - - - -## Embedding the Survey in Emails - -Different email clients have different support for HTML and CSS. We recommend testing the email in different email clients to ensure the survey looks good in all of them. - -Below are some of the methods and services that we know that allows HTML embedding and how you can use them: - - - Please use the below methods at your own discretion. We do not officially endorse any of the services - mentioned below. - - -### 1. Gmail - -Gmail does not support HTML embedding natively. It's a WYSIWYG (What You See Is What You Get) editor. However, you can use a free plugin like [HTML Editor for Gmail by cloudHQ](https://chromewebstore.google.com/detail/free-html-editor-for-gmai/ioinaaeeacahcmbgfmeaaofhfkijpdeb) to embed HTML in your emails. - - - Install the plugin from the Chrome Web Store. - - Open Gmail and compose a new email. - - Write your email content after which you want to embed the survey. - - - - - Right next to the Send button you will see a new button called **HTML Editor**. Click on it. - - This will open a new window with the **Design** tab active. Switch to the **Source** tab. - - - - - Now paste the copied HTML code from Formbricks into this window. On the right, you will see a preview of how the email will look. - - - - - Click on the **Close Editor** button to save the changes & close the editor. - - - - - Voila! You have successfully embedded the survey in your email. - -### 2. Sendgrid - -Sendgrid supports HTML content in emails directly: - - - Compose Your Email: Use Sendgrid's email composition tools to embed the HTML directly into your email body. - - Reference: For detailed steps, refer to Sendgrid's official documentation [here](https://sendgrid.com/en-us/solutions/email-marketing/email-design) or API docs [here](https://sendgrid.com/docs/for-developers/sending-email/api-getting-started/) - -### 3. Mailchimp - -Available in their Standard plan and above, Mailchimp allows HTML content embedding: - - - Use the Code Block: Drag a code block into your email template and paste the HTML code for the survey. - - Reference: Check out Mailchimp's guide on pasting in custom HTML [here](https://mailchimp.com/help/paste-in-html-to-create-an-email/) - -### 4. Notemailer - -Nodemailer is a Node.js module that allows you to send emails with HTML content. - - - If you use Node.js to send emails, just attach the `html` parameter in your email message. - - Reference: Take a look at Nodemailer's official message documentation [here](https://nodemailer.com/message/) - - - Please note that the above methods are not exhaustive and there are many other ways to embed HTML in emails. - - -## Example: Email Footer Survey - -Embed a survey link in your email signature to collect feedback subtly yet effectively. Here’s how: - - - -1. Create a Survey: Adjust an existing survey or create a new one. -2. Scroll down & enable the **Hidden Fields** option. -3. Add a new hidden field with the name **helpful**. -4. Now Publish the survey as a Link Survey & copy the link. -5. Embed in Your Signature: Add this HTML snippet to your email signature in your email client settings. - - - - -```html -Was our conversation helpful? Yes 👍 | No 👎 -``` - - - - -Replace `YOUR_SURVEY_LINK` with the actual survey link. - -PS: If you do not see any signature settings, just use one of the methods we've mentioned above to embed the HTML code in your email. - ---- - -**Can’t figure it out?** [Join our Discord!](https://formbricks.com/discord) diff --git a/apps/formbricks-com/app/docs/link-surveys/embed-surveys/page.mdx b/apps/formbricks-com/app/docs/link-surveys/embed-surveys/page.mdx deleted file mode 100644 index 5ffff7c22e..0000000000 --- a/apps/formbricks-com/app/docs/link-surveys/embed-surveys/page.mdx +++ /dev/null @@ -1,81 +0,0 @@ -import { TellaVideo } from "@/components/docs/TellaVideo"; - -export const metadata = { - title: "Embed Surveys in Your Web Page", - description: "Embed Formbricks surveys seamlessly into your website or web application using an iframe.", -}; - -#### Embed Surveys - -# Embed Surveys in Your Web Page - -Embedding Formbricks surveys directly into your web pages allows you to integrate interactive surveys without redirecting users to a separate survey site. This method ensures a seamless integration and maintains the aesthetic continuity of your website or application. - -## How to Use it? - - - -1. Create and publish a link survey. - -2. Open survey summary page and click on **share** button on the top right. - -3. In the survey share modal, click on **Embed survey** button. - -4. Navigate to **Embed in a Web Page** tab and click on Copy code - -5. Paste the copied iframe code into the HTML of your web page where you want the survey to appear. - -### Example of Embedding a Survey - - - - -```html -
    - -
    -``` - -
    - - -## Iframe Events - -The iframe fires a **formbricksSurveyCompleted** event when a user finishes a survey within the embedded iframe. This event can be captured through a message listener in your webpage's JavaScript - -### How to Use it? - -1. Embed the Formbricks survey on your webpage using the iframe method as described above. - -2. Add an event listener to your webpage’s JavaScript that listens for `message` events from the iframe. - -3. Check if the received message indicates that the survey is completed by comparing the `event.data` with the value `formbricksSurveyCompleted`. - - - It is important to verify the origin of the message to ensure it comes from the iframe containing your - survey, enhancing the security of your event handling. - - -4. Implement your custom actions within the callback function based on the survey completion. - -### Example of Handling Survey Completion Events - - - - -```javascript -window.addEventListener("message", (event) => { - // Replace 'https://app.formbricks.com' with the actual web app url - if (event.origin === "https://app.formbricks.com" && event.data === "formbricksSurveyCompleted") { - console.log("Survey completed!"); - // Implement your custom actions here - } -}); -``` - - - diff --git a/apps/formbricks-com/app/docs/self-hosting/deployment/page.mdx b/apps/formbricks-com/app/docs/self-hosting/deployment/page.mdx deleted file mode 100644 index 0553c02608..0000000000 --- a/apps/formbricks-com/app/docs/self-hosting/deployment/page.mdx +++ /dev/null @@ -1,21 +0,0 @@ -export const metadata = { - title: "Comprehensive Guide to Self-Hosting Formbricks", - description: - "Discover versatile options to deploy Formbricks tailored to your expertise level. From Ubuntu setups using shell scripts, swift Docker deployments, to manual source configurations, harness the flexibility and power of Formbricks to fit your unique hosting needs. Dive in today!", -}; - -#### Self-Hosting - -# Self Hosting Formbricks - -At Formbricks, we understand that different users have different needs, and we strive to cater to a wide variety of situations. This is why we currently provide two ways of running our application: - -1. **Production Instance Setup with Shell Script on Ubuntu**: If you want to quickly set up a production instance of Formbricks on a server running Ubuntu, we've got you covered! This method utilizes a convenient shell script that takes care of everything, including Docker, Postgres DB, and SSL certificate configuration. The shell script will automatically install all the required dependencies and configure your server, making the process a breeze. Visit the [Production Instance Setup with a Bash Script Documentation](/docs/self-hosting/production). - -2. **Fast Setup with a Pre-built Docker Image:** This method is designed for those who want to quickly set up and start using Formbricks without getting into the technicalities of Docker or the build process. When you choose this method, you're using an image that we've already built for you. The pre-built image is ready-to-run, and it only requires minimal configuration on your part. This approach is perfect for getting a functional instance of Formbricks up and running with minimal hassle. It's as easy as downloading the Docker image and firing up the container. Visit the [Docker Setup Documentation](/docs/self-hosting/docker). - -Please note that regardless of the method you choose, Formbricks is designed to be easy-to-use and flexible. So choose the method that best fits your comfort level and requirements, and start leveraging the **power of Formbricks** today! - -Running Formbricks requires at least **1 vCPU**, **2 GBs of RAM**, **8 GBs of SSD** - -Looking for something not mentioned here? [Join our Discord!](https://formbricks.com/discord) and we'd be glad to assist you! diff --git a/apps/formbricks-com/app/docs/self-hosting/enterprise/page.mdx b/apps/formbricks-com/app/docs/self-hosting/enterprise/page.mdx deleted file mode 100644 index 64c3dae5a5..0000000000 --- a/apps/formbricks-com/app/docs/self-hosting/enterprise/page.mdx +++ /dev/null @@ -1,45 +0,0 @@ -export const metadata = { - title: "Enterprise License to unlock advanced functionality", - description: "Request a enterprise licenses to unlock advanced enterprise functionality", -}; - -#### Self-Hosting - -# Enterprise License Request (Beta) - -We're handing out free enterprise license keys during our beta. - -Additional to the AGPL licensed Formbricks core, the Formbricks repository contains code licensed under an [Enterprise license](https://github.com/formbricks/formbricks/blob/main/packages/ee/LICENSE). This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/docs/self-hosting/license) to unlock it. - -### When do I need an Enterprise License? - -| | Community Edition | Enterprise License | -| --- | --- | --- | -| Self-host for commercial purposes | ✅ | No EE license needed | -| To make changes to the code base (happy to publish them) | ✅ | No EE license needed | -| Unlimited responses | ✅ | No EE license needed | -| Unlimited surveys | ✅ | No EE license needed | -| Remove branding | ✅ | No EE license needed | -| SSO | ✅ | No EE license needed | -| Use any of the other 100 features | ✅ | No EE license needed | -| Team roles | ❌ | ✅ | -| Multi-language surveys | ❌ | ✅ | -| Advanced targeting / Segments | ❌ | ✅ | -| Make code changes and keep private | ❌ | ✅ | - -**Please note:** Sooner than later we will introduce a enterprise license pricing. For a free beta key, fill out this form: - -
    - -
    - -**Can’t figure it out?**: [Join our Discord!](https://formbricks.com/discord) diff --git a/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx b/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx deleted file mode 100644 index 4f0bc8518d..0000000000 --- a/apps/formbricks-com/app/docs/self-hosting/external-auth-providers/page.mdx +++ /dev/null @@ -1,200 +0,0 @@ -export const metadata = { - title: "Configure Formbricks with External auth providers", - description: - "Set up and integrate multiple external authentication providers with Formbricks. Our step-by-step guide covers Google OAuth and more, ensuring a seamless login experience for your users.", -}; - -#### Self-Hosting - -# Configure - -## Google OAuth Authentication - -Integrating Google OAuth with your Formbricks instance allows users to log in using their Google credentials, ensuring a secure and streamlined user experience. This guide will walk you through the process of setting up Google OAuth for your Formbricks instance. - -### Requirements - -- A Google Cloud Platform (GCP) account. -- A Formbricks instance running and accessible. - -### Steps - -1. **Create a GCP Project**: - - - Navigate to the [GCP Console](https://console.cloud.google.com/). - - From the projects list, select a project or create a new one. - -2. **Setting up OAuth 2.0**: - - - If the **APIs & services** page isn't already open, open the console left side menu and select **APIs & services**. - - On the left, click **Credentials**. - - Click **Create Credentials**, then select **OAuth client ID**. - -3. **Configure OAuth Consent Screen**: - - - If this is your first time creating a client ID, configure your consent screen by clicking **Consent Screen**. - - Fill in the necessary details and under **Authorized domains**, add the domain where your Formbricks instance is hosted. - -4. **Create OAuth 2.0 Client IDs**: - - Select the application type **Web application** for your project and enter any additional information required. - - Ensure to specify authorized JavaScript origins and authorized redirect URIs. - - - - ``` {{ title: "Redirect & Origin URLs" }} - Authorized JavaScript origins: {WEBAPP_URL} - Authorized redirect URIs: {WEBAPP_URL}/api/auth/callback/google ``` - - - -5. **Update Environment Variables in Docker**: - - To integrate the Google OAuth, you have two options: either update the environment variables in the docker-compose file or directly add them to the running container. - - In your Docker setup directory, open the `.env` file, and add or update the following lines with the `Client ID` and `Client Secret` obtained from Google Cloud Platform: - - Alternatively, you can add the environment variables directly to the running container using the following commands (replace `container_id` with your actual Docker container ID): - - - - -```sh {{ title: 'Shell commands' }} -docker exec -it container_id /bin/bash -export GOOGLE_CLIENT_ID=your-client-id-here -export GOOGLE_CLIENT_SECRET=your-client-secret-here -exit -``` - -```sh {{ title: 'env file' }} -GOOGLE_CLIENT_ID=your-client-id-here -GOOGLE_CLIENT_SECRET=your-client-secret-here -``` - - - - -6. **Restart Your Formbricks Instance**: - - **Note:** Restarting your Docker containers may cause a brief period of downtime. Plan accordingly. - - Once the environment variables have been updated, it's crucial to restart your Docker containers to apply the changes. This ensures that your Formbricks instance can utilize the new Google OAuth configuration for user authentication. Here's how you can do it: - - Navigate to your Docker setup directory where your `docker-compose.yml` file is located. - - Run the following command to bring down your current Docker containers and then bring them back up with the updated environment configuration: - -## Azure SSO Integration - -Have an Azure Active Directory (AAD) instance? Integrate it with your Formbricks instance to allow users to log in using their existing AAD credentials. This guide will walk you through the process of setting up Azure SSO for your Formbricks instance. - -### Requirements - -- An Azure Active Directory (AAD) instance. -- A Formbricks instance running and accessible. - -### Steps - -1. Create a new Tenant in Azure Active Directory as per their [official documentation](https://learn.microsoft.com/en-us/entra/fundamentals/create-new-tenant). -2. Add Users & Groups to your AAD instance. -3. Now we need to fill the below environment variables in our Formbricks instance so get them from your AD configuration: - - `AZUREAD_CLIENT_ID` - - `AZUREAD_CLIENT_SECRET` - - `AZUREAD_TENANT_ID` -4. Update these environment variables in your `docker-compose.yml` or pass it like your other environment variables to the Formbricks container. -5. Restart your Formbricks instance. -6. You're all set! Users can now signup & log in using their AAD credentials. - -## OpenID Integration - -Integrating your own OIDC (OpenID Connect) instance with your Formbricks instance allows users to log in using their OIDC credentials, ensuring a secure and streamlined user experience. Please follow the steps below to set up OIDC for your Formbricks instance. - -1. Configure your OIDC provider & get the following variables: - - `OIDC_CLIENT_ID` - - `OIDC_CLIENT_SECRET` - - `OIDC_ISSUER` - - `OIDC_SIGNING_ALGORITHM` - - -Make sure the Redirect URI for your OIDC Client is set to `{WEBAPP_URL}/api/auth/callback/openid`. - - -2. Update these environment variables in your `docker-compose.yml` or pass it directly to the running container. - -An example configuration for a FusionAuth OpenID Connect in Formbricks would look like: - - -```yml {{ title: '.env' }} -OIDC_CLIENT_ID=59cada54-56d4-4aa8-a5e7-5823bbe0e5b7 -OIDC_CLIENT_SECRET=4f4dwP0ZoOAqMW8fM9290A7uIS3E8Xg29xe1umhlB_s -OIDC_ISSUER=http://localhost:9011 -OIDC_DISPLAY_NAME=FusionAuth -OIDC_SIGNING_ALGORITHM=HS256 -``` - - - -3. Set an environment variable `OIDC_DISPLAY_NAME` to the display name of your OIDC provider. - -4. Restart your Formbricks instance. - -5. You're all set! Users can now signup & log in using their OIDC credentials. - -## Important Run-time Variables - -These variables can be provided at the runtime i.e. in your docker-compose file. - -| Variable | Description | Required | Default | -|-----------------------------|----------------------------------------------------------------------------------------------|---------------------------------------------------------|---------------------------| -| WEBAPP_URL | Base URL of the site. | required | `http://localhost:3000` | -| DATABASE_URL | Database URL with credentials. | required | | -| NEXTAUTH_SECRET | Secret for NextAuth, used for session signing and encryption. | required | (Generated by the user) | -| ENCRYPTION_KEY | Secret for used by Formbricks for data encryption | required | (Generated by the user) | -| NEXTAUTH_URL | Location of the auth server. By default, this is the Formbricks docker instance itself. | required | `http://localhost:3000` | -| UPLOADS_DIR | Local directory for storing uploads. | optional | `./uploads` | -| S3_ACCESS_KEY | Access key for S3. | optional | (resolved by the AWS SDK) | -| S3_SECRET_KEY | Secret key for S3. | optional | (resolved by the AWS SDK) | -| S3_REGION | Region for S3. | optional | (resolved by the AWS SDK) | -| S3_BUCKET_NAME | S3 bucket name for data storage. Formbricks enables S3 storage when this is set. | optional (required if S3 is enabled) | | -| S3_ENDPOINT | Endpoint for S3. | optional | (resolved by the AWS SDK) | -| PRIVACY_URL | URL for privacy policy. | optional | | -| TERMS_URL | URL for terms of service. | optional | | -| IMPRINT_URL | URL for imprint. | optional | | -| SIGNUP_DISABLED | Disables the ability for new users to create an account if set to `1`. | optional | | -| EMAIL_AUTH_DISABLED | Disables the ability for users to signup or login via email and password if set to `1`. | optional | | -| PASSWORD_RESET_DISABLED | Disables password reset functionality if set to `1`. | optional | | -| EMAIL_VERIFICATION_DISABLED | Disables email verification if set to `1`. | optional | | -| RATE_LIMITING_DISABLED | Disables rate limiting if set to `1`. | optional | | -| INVITE_DISABLED | Disables the ability for invited users to create an account if set to `1`. | optional | | -| MAIL_FROM | Email address to send emails from. | optional (required if email services are to be enabled) | | -| SMTP_HOST | Host URL of your SMTP server. | optional (required if email services are to be enabled) | | -| SMTP_PORT | Host Port of your SMTP server. | optional (required if email services are to be enabled) | | -| SMTP_USER | Username for your SMTP Server. | optional (required if email services are to be enabled) | | -| SMTP_PASSWORD | Password for your SMTP Server. | optional (required if email services are to be enabled) | | -| SMTP_SECURE_ENABLED | SMTP secure connection. For using TLS, set to `1` else to `0`. | optional (required if email services are to be enabled) | | -| GITHUB_ID | Client ID for GitHub. | optional (required if GitHub auth is enabled) | | -| GITHUB_SECRET | Secret for GitHub. | optional (required if GitHub auth is enabled) | | -| GOOGLE_CLIENT_ID | Client ID for Google. | optional (required if Google auth is enabled) | | -| GOOGLE_CLIENT_SECRET | Secret for Google. | optional (required if Google auth is enabled) | | -| CRON_SECRET | API Secret for running cron jobs. | optional | | -| STRIPE_SECRET_KEY | Secret key for Stripe integration. | optional | | -| STRIPE_WEBHOOK_SECRET | Webhook secret for Stripe integration. | optional | | -| TELEMETRY_DISABLED | Disables telemetry if set to `1`. | optional | | -| INSTANCE_ID | Instance ID for Formbricks Cloud to be sent to Telemetry. | optional | | -| INTERNAL_SECRET | Internal Secret (Currently we overwrite the value with a random value). | optional | | -| DEFAULT_BRAND_COLOR | Default brand color for your app (Can be overwritten from the UI as well). | optional | `#64748b` | -| DEFAULT_TEAM_ID | Automatically assign new users to a specific team when joining | optional | | -| DEFAULT_TEAM_ROLE | Role of the user in the default team. | optional | `admin` | -| ONBOARDING_DISABLED | Disables onboarding for new users if set to `1` | optional | | -| OIDC_DISPLAY_NAME | Display name for Custom OpenID Connect Provider | optional | | -| OIDC_CLIENT_ID | Client ID for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | | -| OIDC_CLIENT_SECRET | Secret for Custom OpenID Connect Provider | optional (required if OIDC auth is enabled) | | -| OIDC_ISSUER | Issuer URL for Custom OpenID Connect Provider (should have `.well-known` configured at this) | optional (required if OIDC auth is enabled) | | -| OIDC_SIGNING_ALGORITHM | Signing Algorithm for Custom OpenID Connect Provider | optional | `RS256` | -| OPENTELEMETRY_LISTENER_URL | URL for OpenTelemetry listener inside Formbricks. | optional | | | - -## Build-time Variables - -These variables must be provided at the time of the docker build and would require rebuilding the image. - -| Variable | Description | Required | Default | -| ------------------------------------- | ----------------------------------------------------- | -------- | ------- | -| NEXT_PUBLIC_FORMBRICKS_API_HOST | Host for the Formbricks API to use inside Formbricks. | optional | | -| NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID | Formbricks environment ID for use inside Formbricks. | optional | | -| NEXT_PUBLIC_POSTHOG_API_KEY | API key to use PostHog analytics inside Formbricks. | optional | | -| NEXT_PUBLIC_POSTHOG_API_HOST | Host to use PostHog analytics inside Formbricks. | optional | | -| NEXT_PUBLIC_SENTRY_DSN | DSN for Sentry error tracking inside Formbricks. | optional | | - -Still facing issues? [Join our Discord!](https://formbricks.com/discord) and we'd be glad to assist you! diff --git a/apps/formbricks-com/app/docs/self-hosting/license/page.mdx b/apps/formbricks-com/app/docs/self-hosting/license/page.mdx deleted file mode 100644 index 4868f9ed88..0000000000 --- a/apps/formbricks-com/app/docs/self-hosting/license/page.mdx +++ /dev/null @@ -1,27 +0,0 @@ -export const metadata = { - title: "About the Formbricks Open-Source License", - description: - "The Formbricks core is available under the AGPLv3 license", -}; - -#### Self-Hosting - -# License - -## The AGPL Formbricks Core - -The Formbricks core application is licensed under the [AGPLv3 Open Source License](https://github.com/formbricks/formbricks/blob/main/LICENSE). The core application is fully functional and includes everything you need to design & run link surveys, website surveys and in-app surveys. You can use the software for free for personal and commercial use. You're also allowed to create and distribute modified versions as long as you document the changes you make incl. date. The AGPL license requires you to publish your modified version under the AGPLv3 license as well. - -## The Enterprise Edition - -Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/packages/ee) and [license](https://github.com/formbricks/formbricks/blob/main/packages/ee/LICENSE) for the enterprise functionality can be found in the `/packages/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/docs/self-hosting/enterprise) to unlock it. - -## White-Labeling Formbricks and Other Licensing Needs - -If you have other licensing requirements such as White-Labeling please [send us an email](mailto:hola@formbricks.com). - -## Why charge for Enterprise Features? - -The Enterprise Edition and White-Label Licenses allow us to fund the development of Formbricks sustainably. It guarantees that the open-source surveying infrastructure we're building will be around for decades to come. - -**Having more questions?**: [Join our Discord!](https://formbricks.com/discord) diff --git a/apps/formbricks-com/app/layout.tsx b/apps/formbricks-com/app/layout.tsx deleted file mode 100644 index 65fa221adf..0000000000 --- a/apps/formbricks-com/app/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import "@/styles/globals.css"; - -export default function RootLayout({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); -} diff --git a/apps/formbricks-com/components/docs/DocsFeedback.tsx b/apps/formbricks-com/components/docs/DocsFeedback.tsx deleted file mode 100644 index 697cd5df3b..0000000000 --- a/apps/formbricks-com/components/docs/DocsFeedback.tsx +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; - -import { useState } from "react"; - -import { Button } from "@formbricks/ui/Button"; -import { Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui/Popover"; - -export const DocsFeedback: React.FC = () => { - const [isOpen, setIsOpen] = useState(false); - const [sharedFeedback, setSharedFeedback] = useState(false); - const [freeText, setFreeText] = useState(""); - - return ( -
    - {!sharedFeedback ? ( -
    - Is everything on this page clear? - -
    - {["Yes 👍", "No 👎"].map((option) => ( - - {option} - - ))} -
    - -
    - -
    -
    -
    - -
    - - ); -} diff --git a/apps/formbricks-com/components/dummyUI/PreviewModal.tsx b/apps/formbricks-com/components/dummyUI/PreviewModal.tsx deleted file mode 100644 index 0bdcf5e568..0000000000 --- a/apps/formbricks-com/components/dummyUI/PreviewModal.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import clsx from "clsx"; -import { ReactNode, useEffect, useState } from "react"; - -export const Modal: React.FC<{ children: ReactNode; isOpen: boolean }> = ({ children, isOpen }) => { - const [show, setShow] = useState(false); - - useEffect(() => { - setShow(isOpen); - }, [isOpen]); - return ( -
    -
    -
    - {children} -
    -
    -
    - ); -}; - -export default Modal; diff --git a/apps/formbricks-com/components/dummyUI/PreviewSurvey.tsx b/apps/formbricks-com/components/dummyUI/PreviewSurvey.tsx deleted file mode 100644 index 8a6a2d11ca..0000000000 --- a/apps/formbricks-com/components/dummyUI/PreviewSurvey.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { useState } from "react"; - -import Modal from "./Modal"; -import QuestionConditional from "./QuestionConditional"; -import ThankYouCard from "./ThankYouCard"; -import { TSurvey, TSurveyQuestion } from "./types"; - -interface PreviewSurveyProps { - localSurvey?: TSurvey; - setActiveQuestionId: (id: string | null) => void; - activeQuestionId?: string | null; - questions: TSurveyQuestion[]; - brandColor: string; -} - -export default function PreviewSurvey({ - localSurvey, - setActiveQuestionId, - activeQuestionId, - questions, - brandColor, -}: PreviewSurveyProps) { - const [isModalOpen, setIsModalOpen] = useState(true); - - const gotoNextQuestion = () => { - const currentIndex = questions.findIndex((q) => q.id === activeQuestionId); - if (currentIndex < questions.length - 1) { - setActiveQuestionId(questions[currentIndex + 1].id); - } else { - if (localSurvey?.thankYouCard?.enabled) { - setActiveQuestionId("thank-you-card"); - } else { - setIsModalOpen(false); - setTimeout(() => { - setActiveQuestionId(questions[0].id); - setIsModalOpen(true); - }, 500); - if (localSurvey?.thankYouCard?.enabled) { - setActiveQuestionId("thank-you-card"); - } else { - setIsModalOpen(false); - setTimeout(() => { - setActiveQuestionId(questions[0].id); - setIsModalOpen(true); - }, 500); - } - } - } - }; - - const resetPreview = () => { - setIsModalOpen(false); - setTimeout(() => { - setActiveQuestionId(questions[0].id); - setIsModalOpen(true); - }, 500); - }; - - if (!activeQuestionId) { - return null; - } - - return ( - <> - - {activeQuestionId == "thank-you-card" ? ( - - ) : ( - questions.map( - (question, idx) => - activeQuestionId === question.id && ( - - ) - ) - )} - - - ); -} diff --git a/apps/formbricks-com/components/dummyUI/Progress.tsx b/apps/formbricks-com/components/dummyUI/Progress.tsx deleted file mode 100644 index 16f75f378f..0000000000 --- a/apps/formbricks-com/components/dummyUI/Progress.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export const Progress: React.FC<{ progress: number; brandColor: string }> = ({ progress, brandColor }) => { - return ( -
    -
    -
    - ); -}; - -export default Progress; diff --git a/apps/formbricks-com/components/dummyUI/QuestionConditional.tsx b/apps/formbricks-com/components/dummyUI/QuestionConditional.tsx deleted file mode 100644 index a24a784d2a..0000000000 --- a/apps/formbricks-com/components/dummyUI/QuestionConditional.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import CTAQuestion from "./CTAQuestion"; -import MultipleChoiceMultiQuestion from "./MultipleChoiceMultiQuestion"; -import MultipleChoiceSingleQuestion from "./MultipleChoiceSingleQuestion"; -import NPSQuestion from "./NPSQuestion"; -import OpenTextQuestion from "./OpenTextQuestion"; -import RatingQuestion from "./RatingQuestion"; -import { TSurveyQuestion, TSurveyQuestionType } from "./types"; - -interface QuestionConditionalProps { - question: TSurveyQuestion; - onSubmit: (data: { [x: string]: any }) => void; - lastQuestion: boolean; - brandColor: string; -} - -export default function QuestionConditional({ - question, - onSubmit, - lastQuestion, - brandColor, -}: QuestionConditionalProps) { - return question.type === TSurveyQuestionType.OpenText ? ( - - ) : question.type === TSurveyQuestionType.MultipleChoiceSingle ? ( - - ) : question.type === TSurveyQuestionType.MultipleChoiceMulti ? ( - - ) : question.type === TSurveyQuestionType.NPS ? ( - - ) : question.type === TSurveyQuestionType.CTA ? ( - - ) : question.type === TSurveyQuestionType.Rating ? ( - - ) : null; -} diff --git a/apps/formbricks-com/components/dummyUI/RatingQuestion.tsx b/apps/formbricks-com/components/dummyUI/RatingQuestion.tsx deleted file mode 100644 index 6e8c72c584..0000000000 --- a/apps/formbricks-com/components/dummyUI/RatingQuestion.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState } from "react"; - -import { cn } from "@formbricks/lib/cn"; - -import Headline from "./Headline"; -import Subheader from "./Subheader"; -import { TSurveyRatingQuestion } from "./types"; - -interface RatingQuestionProps { - question: TSurveyRatingQuestion; - onSubmit: (data: { [x: string]: any }) => void; - lastQuestion: boolean; - brandColor: string; -} - -export default function RatingQuestion({ - question, - onSubmit, - lastQuestion, - brandColor, -}: RatingQuestionProps) { - const [selectedChoice, setSelectedChoice] = useState(null); - - const handleSelect = (number: number) => { - setSelectedChoice(number); - if (question.required) { - onSubmit({ - [question.id]: number, - }); - setSelectedChoice(null); // reset choice - } - }; - - return ( -
    { - e.preventDefault(); - - const data = { - [question.id]: selectedChoice, - }; - - setSelectedChoice(null); // reset choice - - onSubmit(data); - }}> - - -
    -
    - Options -
    - {Array.from({ length: question.range }, (_, i) => i + 1).map((number) => ( - - ))} -
    -
    -

    {question.lowerLabel}

    -

    {question.upperLabel}

    -
    -
    -
    - {!question.required && ( -
    -
    - -
    - )} - - ); -} diff --git a/apps/formbricks-com/components/dummyUI/Subheader.tsx b/apps/formbricks-com/components/dummyUI/Subheader.tsx deleted file mode 100644 index 2fa4c32353..0000000000 --- a/apps/formbricks-com/components/dummyUI/Subheader.tsx +++ /dev/null @@ -1,14 +0,0 @@ -export const Subheader: React.FC<{ subheader?: string; questionId: string }> = ({ - subheader, - questionId, -}) => { - return ( - - ); -}; - -export default Subheader; diff --git a/apps/formbricks-com/components/dummyUI/TemplateList.tsx b/apps/formbricks-com/components/dummyUI/TemplateList.tsx deleted file mode 100644 index eec24c4173..0000000000 --- a/apps/formbricks-com/components/dummyUI/TemplateList.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useEffect, useState } from "react"; - -import { cn } from "@formbricks/lib/cn"; - -import { templates } from "./templates"; -import { TTemplate } from "./types"; - -type TemplateList = { - onTemplateClick: (template: TTemplate) => void; - activeTemplate: TTemplate | null; -}; - -const ALL_CATEGORY_NAME = "All"; - -export default function TemplateList({ onTemplateClick, activeTemplate }: TemplateList) { - const [selectedFilter, setSelectedFilter] = useState(ALL_CATEGORY_NAME); - - const [categories, setCategories] = useState>([]); - - useEffect(() => { - const defaultCategories = [ - /* ALL_CATEGORY_NAME, */ - ...(Array.from(new Set(templates.map((template) => template.category))) as string[]), - ]; - - const fullCategories = [ALL_CATEGORY_NAME, ...defaultCategories]; - - setCategories(fullCategories); - - const activeFilter = ALL_CATEGORY_NAME; - setSelectedFilter(activeFilter); - }, []); - - return ( -
    -
    - {categories.map((category) => ( - - ))} -
    -
    - {templates - .filter((template) => selectedFilter === ALL_CATEGORY_NAME || template.category === selectedFilter) - .map((template: TTemplate) => ( -
    { - onTemplateClick(template); // Pass the 'template' object instead of 'activeTemplate' - }} - className={cn( - activeTemplate?.name === template.name && "ring-brand ring-2", - "duration-120 group relative rounded-lg bg-white p-6 shadow transition-all duration-150 hover:scale-105 dark:bg-slate-700" - )}> -
    - {template.category} -
    - -

    - {template.name} -

    -

    {template.description}

    -
    - ))} -
    -
    - ); -} diff --git a/apps/formbricks-com/components/dummyUI/ThankYouCard.tsx b/apps/formbricks-com/components/dummyUI/ThankYouCard.tsx deleted file mode 100644 index 9d34466083..0000000000 --- a/apps/formbricks-com/components/dummyUI/ThankYouCard.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import Headline from "./Headline"; -import Subheader from "./Subheader"; - -interface ThankYouCardProps { - headline: string; - subheader: string; - brandColor: string; -} - -export default function ThankYouCard({ headline, subheader, brandColor }: ThankYouCardProps) { - return ( -
    -
    - - - -
    - - - -
    - - -
    - - {/* - -
    -

    - Powered by{" "} - - - Formbricks - - -

    -
    */} -
    - ); -} diff --git a/apps/formbricks-com/components/dummyUI/templates.ts b/apps/formbricks-com/components/dummyUI/templates.ts deleted file mode 100644 index 3227a56802..0000000000 --- a/apps/formbricks-com/components/dummyUI/templates.ts +++ /dev/null @@ -1,1288 +0,0 @@ -import { createId } from "@paralleldrive/cuid2"; - -import { - AppPieChartIcon, - ArrowRightCircleIcon, - ArrowUpRightIcon, - BaseballIcon, - CancelSubscriptionIcon, - CashCalculatorIcon, - CheckMarkIcon, - CodeBookIcon, - DashboardIcon, - DogChaserIcon, - DoorIcon, - EmailIcon, - FeedbackIcon, - GaugeSpeedFastIcon, - HeartCommentIcon, - InterviewPromptIcon, - LoadingBarIcon, - OnboardingIcon, - PMFIcon, - TaskListSearchIcon, - UserSearchGlasIcon, - VideoTabletAdjustIcon, -} from "@formbricks/ui/icons"; - -import { TSurveyQuestionType, TTemplate } from "./types"; - -const thankYouCardDefault = { - enabled: true, - headline: "Thank you!", - subheader: "TWe appreciate your feedback.", -}; - -const welcomeCardDefault = { - enabled: true, - headline: "Welcome!", - timeToFinish: false, - showResponseCount: false, -}; - -export const customSurvey: TTemplate = { - name: "Start from scratch", - description: "Create a survey without template.", - icon: null, - preset: { - name: "New Survey", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Custom Survey", - subheader: "This is an example survey.", - placeholder: "Type your answer here...", - inputType: "text", - longAnswer: true, - required: true, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, -}; - -export const templates: TTemplate[] = [ - { - name: "Product Market Fit (Superhuman)", - icon: PMFIcon, - category: "Product Experience", - - description: "Measure PMF by assessing how disappointed users would be if your product disappeared.", - preset: { - name: "Product Market Fit (Superhuman)", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How disappointed would you be if you could no longer use Formbricks?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Not at all disappointed", - }, - { - id: createId(), - label: "Somewhat disappointed", - }, - { - id: createId(), - label: "Very disappointed", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What is your role?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Founder", - }, - { - id: createId(), - label: "Executive", - }, - { - id: createId(), - label: "Product Manager", - }, - { - id: createId(), - label: "Product Owner", - }, - { - id: createId(), - label: "Software Engineer", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "What type of people do you think would most benefit from Formbricks?", - inputType: "text", - longAnswer: true, - required: true, - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "What is the main benefit you receive from Formbricks?", - inputType: "text", - longAnswer: true, - required: true, - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "How can we improve our service for you?", - inputType: "text", - subheader: "Please be as specific as possible.", - longAnswer: true, - required: true, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - - { - name: "Onboarding Segmentation", - icon: OnboardingIcon, - category: "Product Experience", - description: "Learn more about who signed up to your product and why.", - preset: { - name: "Onboarding Segmentation", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What is your role?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Founder", - }, - { - id: createId(), - label: "Executive", - }, - { - id: createId(), - label: "Product Manager", - }, - { - id: createId(), - label: "Product Owner", - }, - { - id: createId(), - label: "Software Engineer", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What's your company size?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "only me", - }, - { - id: createId(), - label: "1-5 employees", - }, - { - id: createId(), - label: "6-10 employees", - }, - { - id: createId(), - label: "11-100 employees", - }, - { - id: createId(), - label: "over 100 employees", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How did you hear about us first?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Recommendation", - }, - { - id: createId(), - label: "Social Media", - }, - { - id: createId(), - label: "Ads", - }, - { - id: createId(), - label: "Google Search", - }, - { - id: createId(), - label: "In a Podcast", - }, - ], - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Uncover Strengths & Weaknesses", - icon: TaskListSearchIcon, - category: "Growth", - description: "Find out what users like and don't like about your product or offering.", - preset: { - name: "Uncover Strengths & Weaknesses", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What do you value most about our service?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Ease of use", - }, - { - id: createId(), - label: "Good value for money", - }, - { - id: createId(), - label: "It's open-source", - }, - { - id: createId(), - label: "The founders are pretty", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What should we improve on?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Documentation", - }, - { - id: createId(), - label: "Customizability", - }, - { - id: createId(), - label: "Pricing", - }, - { - id: createId(), - label: "Humbleness of founders", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Would you like to add something?", - subheader: "Feel free to speak your mind, we do too.", - inputType: "text", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Marketing Attribution", - icon: AppPieChartIcon, - category: "Growth", - description: "How did you first hear about us?", - preset: { - name: "Marketing Attribution", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How did you hear about us first?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Recommendation", - }, - { - id: createId(), - label: "Social Media", - }, - { - id: createId(), - label: "Ads", - }, - { - id: createId(), - label: "Google Search", - }, - { - id: createId(), - label: "In a Podcast", - }, - ], - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Churn Survey", - icon: CancelSubscriptionIcon, - category: "Increase Revenue", - description: "Find out why people cancel their subscriptions. These insights are pure gold!", - preset: { - name: "Churn Survey", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Why did you cancel your subscription?", - subheader: "We're sorry to see you leave. Please help us do better:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "I didn't get much value out of it", - }, - { - id: createId(), - label: "It's too expensive", - }, - { - id: createId(), - label: "I am missing a feature", - }, - { - id: createId(), - label: "Poor customer service", - }, - { - id: createId(), - label: "I just didn't need it anymore", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "How can we win you back?", - subheader: "Feel free to speak your mind, we do too.", - inputType: "text", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Improve Trial Conversion", - icon: BaseballIcon, - category: "Increase Revenue", - description: "Find out why people stopped their trial. These insights help you improve your funnel.", - preset: { - name: "Improve Trial Conversion", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Why did you stop your trial?", - subheader: "Help us understand you better:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "I didn't get much value out of it", - }, - { - id: createId(), - label: "I expected something else", - }, - { - id: createId(), - label: "It's too expensive for what it does", - }, - { - id: createId(), - label: "I am missing a feature", - }, - { - id: createId(), - label: "I was just looking around", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Any details to share?", - inputType: "text", - longAnswer: true, - required: false, - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "How are you solving your problem instead?", - inputType: "text", - subheader: "Please name alternative tools:", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Changing subscription experience", - icon: CashCalculatorIcon, - category: "Increase Revenue", - description: "Find out what goes through peoples minds when changing their subscriptions.", - preset: { - name: "Changing subscription experience", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How easy was it to change your plan?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Extremely difficult", - }, - { - id: createId(), - label: "It took a while, but I got it", - }, - { - id: createId(), - label: "It was alright", - }, - { - id: createId(), - label: "Quite easy", - }, - { - id: createId(), - label: "Very easy, love it!", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Is the pricing information easy to understand?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Yes, very clear.", - }, - { - id: createId(), - label: "I was confused at first, but found what I needed.", - }, - { - id: createId(), - label: "Quite complicated.", - }, - ], - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Measure Task Accomplishment", - icon: CheckMarkIcon, - category: "Product Experience", - description: "See if people get their 'Job To Be Done' done. Successful people are better customers.", - preset: { - name: "Measure Task Accomplishment", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Were you able to accomplish what you came here to do today?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Yes", - }, - { - id: createId(), - label: "Working on it, boss", - }, - { - id: createId(), - label: "No", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.Rating, - headline: "How easy was it to achieve your goal?", - required: true, - lowerLabel: "Very difficult", - upperLabel: "Very easy", - range: 5, - scale: "number", - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "What did you come here to do today?", - inputType: "text", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Identify Customer Goals", - icon: ArrowRightCircleIcon, - category: "Product Experience", - description: - "Better understand if your messaging creates the right expectations of the value your product provides.", - preset: { - name: "Identify Customer Goals", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What's your primary goal for using Formbricks?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Understand my user base deeply", - }, - { - id: createId(), - label: "Identify upselling opportunities", - }, - { - id: createId(), - label: "Build the best possible product", - }, - { - id: createId(), - label: "Rule the world to make everyone breakfast brussels sprouts.", - }, - ], - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Feature Chaser", - icon: DogChaserIcon, - category: "Product Experience", - description: "Follow up with users who just used a specific feature.", - preset: { - name: "Feature Chaser", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.Rating, - headline: "How easy was it to achieve your goal?", - required: true, - lowerLabel: "Very difficult", - upperLabel: "Very easy", - range: 5, - scale: "number", - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Wanna add something?", - inputType: "text", - subheader: "This really helps us do better!", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Fake Door Follow-Up", - icon: DoorIcon, - category: "Exploration", - description: "Follow up with users who ran into one of your Fake Door experiments.", - preset: { - name: "Fake Door Follow-Up", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.Rating, - headline: "How important is this feature for you?", - required: true, - lowerLabel: "Not important", - upperLabel: "Very important", - range: 5, - scale: "number", - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Product Market Fit Survey (short)", - icon: PMFIcon, - category: "Product Experience", - - description: "Measure PMF by assessing how disappointed users would be if your product disappeared.", - preset: { - name: "Product Market Fit Survey (short)", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How disappointed would you be if you could no longer use Formbricks?", - subheader: "Please select one of the following options:", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Not at all disappointed", - }, - { - id: createId(), - label: "Somewhat disappointed", - }, - { - id: createId(), - label: "Very disappointed", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "How can we improve our service for you?", - inputType: "text", - subheader: "Please be as specific as possible.", - longAnswer: true, - required: true, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Feedback Box", - icon: FeedbackIcon, - category: "Product Experience", - description: "Give your users the chance to seamlessly share what's on their minds.", - preset: { - name: "Feedback Box", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "What's on your mind, boss?", - subheader: "Thanks for sharing. We'll get back to you asap.", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Bug report 🐞", - }, - { - id: createId(), - label: "Feature Request 💡", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Give us the juicy details:", - inputType: "text", - longAnswer: true, - required: true, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Integration usage survey", - icon: DashboardIcon, - category: "Product Experience", - description: "Evaluate how easily users can add integrations to your product. Find blind spots.", - preset: { - name: "Integration Usage Survey", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How easy was it to set this integration up?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Extremely difficult", - }, - { - id: createId(), - label: "It took a while, but I got it", - }, - { - id: createId(), - label: "It was alright", - }, - { - id: createId(), - label: "Quite easy", - }, - { - id: createId(), - label: "Very easy, love it!", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Which product would you like to integrate next?", - inputType: "text", - subheader: "We keep building integrations. Yours can be next:", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "New integration survey", - icon: DashboardIcon, - category: "Exploration", - description: "Find out which integrations your users would like to see next.", - preset: { - name: "New integration survey", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Which other tools are you using?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "PostHog", - }, - { - id: createId(), - label: "Segment", - }, - { - id: createId(), - label: "Hubspot", - }, - { - id: createId(), - label: "Twilio", - }, - { - id: createId(), - label: "Other", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "If you chose other, please clarify:", - inputType: "text", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Docs Feedback", - icon: CodeBookIcon, - category: "Product Experience", - description: "Measure how clear each page of your developer documentation is.", - preset: { - name: "Formbricks Docs Feedback", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Was this page helpful?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Yes 👍", - }, - { - id: createId(), - label: "No 👎", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "Please elaborate:", - inputType: "text", - longAnswer: true, - required: false, - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - inputType: "url", - headline: "Page URL", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Interview Prompt", - icon: InterviewPromptIcon, - category: "Exploration", - description: "Invite a specific subset of your users to schedule an interview with your product team.", - preset: { - name: "Interview Prompt", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.CTA, - headline: "Do you have 15 min to talk to us? 🙏", - html: "You're one of our power users. We would love to interview you briefly!", - buttonLabel: "Book interview", - buttonUrl: "https://cal.com/johannes/onboarding?duration=25", - buttonExternal: true, - required: false, - dismissButtonLabel: "Maybe later", - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Review Prompt", - icon: HeartCommentIcon, - category: "Growth", - description: "Invite users who love your product to review it publicly.", - preset: { - name: "Review Prompt", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.CTA, - headline: "You're one of our most valued customers! Please write a review for us.", - buttonLabel: "Write review", - buttonUrl: "https://formbricks.com/github", - buttonExternal: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Net Promoter Score (NPS)", - icon: GaugeSpeedFastIcon, - category: "Customer Success", - description: "Measure the Net Promoter Score of your product.", - preset: { - name: "Formbricks NPS", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.NPS, - headline: "How likely are you to recommend Formbricks to a friend or colleague?", - required: false, - lowerLabel: "Not likely", - upperLabel: "Very likely", - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Identify upsell opportunities", - icon: ArrowUpRightIcon, - category: "Increase Revenue", - description: "Find out how much time your product saves your user. Use it to upsell.", - preset: { - name: "Identify upsell opportunities", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "How many hours does your team save per week by using Formbricks?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Less than 1 hour", - }, - { - id: createId(), - label: "1 to 2 hours", - }, - { - id: createId(), - label: "3 to 5 hours", - }, - { - id: createId(), - label: "5+ hours", - }, - ], - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Build Product Roadmap", - icon: LoadingBarIcon, - category: "Product Experience", - description: "Ask how users rate your product. Identify blind spots to build your roadmap.", - preset: { - name: "Build Product Roadmap", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.Rating, - headline: "How satisfied are you with the features of Formbricks?", - required: true, - lowerLabel: "Not satisfied", - upperLabel: "Very satisfied", - scale: "number", - range: 5, - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "What's the #1 thing you'd like to change in Formbricks?", - inputType: "text", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Gauge Feature Satisfaction", - icon: UserSearchGlasIcon, - category: "Product Experience", - description: "Evaluate the satisfaction of specific features of your product.", - preset: { - name: "Gauge Feature Satisfaction", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.Rating, - headline: "How easy was it to achieve ... ?", - required: true, - lowerLabel: "Not easy", - upperLabel: "Very easy", - scale: "number", - range: 5, - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "What is one thing we could do better?", - inputType: "text", - longAnswer: true, - required: false, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Marketing Site Clarity", - icon: VideoTabletAdjustIcon, - category: "Growth", - description: "Identify users dropping off your marketing site. Improve your messaging.", - preset: { - name: "Marketing Site Clarity", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.MultipleChoiceSingle, - headline: "Do you have all the info you need to give Formbricks a try?", - required: true, - shuffleOption: "none", - choices: [ - { - id: createId(), - label: "Yes, totally", - }, - { - id: createId(), - label: "Kind of...", - }, - { - id: createId(), - label: "No, not at all", - }, - ], - }, - { - id: createId(), - type: TSurveyQuestionType.OpenText, - headline: "What’s missing or unclear to you about Formbricks?", - inputType: "text", - longAnswer: true, - required: false, - }, - { - id: createId(), - type: TSurveyQuestionType.CTA, - headline: "Thanks for your answer! Get 25% off your first 6 months:", - required: false, - buttonLabel: "Get discount", - buttonUrl: "https://app.formbricks.com/auth/signup", - buttonExternal: true, - }, - { - id: createId(), - type: TSurveyQuestionType.FileUpload, - headline: "Upload file", - required: false, - allowMultipleFiles: false, - maxSizeInMB: 10, - }, - ], - thankYouCard: thankYouCardDefault, - welcomeCard: welcomeCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, - { - name: "Improve Newsletter Content", - icon: EmailIcon, - category: "Growth", - description: "Find out how your subscribers like your newsletter content.", - objectives: ["increase_conversion", "sharpen_marketing_messaging"], - preset: { - name: "Improve Newsletter Content", - questions: [ - { - id: createId(), - type: TSurveyQuestionType.Rating, - logic: [ - { value: "5", condition: "equals", destination: "l2q1chqssong8n0xwaagyl8g" }, - { value: "5", condition: "lessThan", destination: "k3s6gm5ivkc5crpycdbpzkpa" }, - ], - range: 5, - scale: "smiley", - headline: "How would you rate this weeks newsletter?", - required: true, - subheader: "", - lowerLabel: "Meh", - upperLabel: "Great", - }, - { - id: "k3s6gm5ivkc5crpycdbpzkpa", - type: TSurveyQuestionType.OpenText, - logic: [ - { condition: "submitted", destination: "end" }, - { condition: "skipped", destination: "end" }, - ], - headline: "What would have made this weeks newsletter more helpful?", - required: false, - placeholder: "Type your answer here...", - inputType: "text", - }, - { - id: "l2q1chqssong8n0xwaagyl8g", - html: '

    Who thinks like you? You\'d do us a huge favor if you\'d share this weeks episode with your brain friend!

    ', - type: TSurveyQuestionType.CTA, - headline: "Thanks! ❤️ Spread the love with ONE friend.", - required: false, - buttonUrl: "https://formbricks.com", - buttonLabel: "Happy to help!", - buttonExternal: true, - dismissButtonLabel: "Find your own friends", - }, - ], - welcomeCard: welcomeCardDefault, - thankYouCard: thankYouCardDefault, - hiddenFields: { - enabled: false, - }, - }, - }, -]; - -export const findTemplateByName = (name: string): TTemplate | undefined => { - return templates.find((template) => template.name === name); -}; diff --git a/apps/formbricks-com/components/dummyUI/types.ts b/apps/formbricks-com/components/dummyUI/types.ts deleted file mode 100644 index e08a97dc48..0000000000 --- a/apps/formbricks-com/components/dummyUI/types.ts +++ /dev/null @@ -1,500 +0,0 @@ -import z from "zod"; - -export enum TSurveyQuestionType { - FileUpload = "fileUpload", - OpenText = "openText", - MultipleChoiceSingle = "multipleChoiceSingle", - MultipleChoiceMulti = "multipleChoiceMulti", - NPS = "nps", - CTA = "cta", - Rating = "rating", - Consent = "consent", - PictureSelection = "pictureSelection", - Cal = "cal", - Date = "date", -} - -export const ZAllowedFileExtension = z.enum([ - "png", - "jpeg", - "jpg", - "pdf", - "doc", - "docx", - "xls", - "xlsx", - "ppt", - "pptx", - "plain", - "csv", - "mp4", - "mov", - "avi", - "mkv", - "webm", - "zip", - "rar", - "7z", - "tar", -]); - -export type TAllowedFileExtension = z.infer; - -export const ZUserObjective = z.enum([ - "increase_conversion", - "improve_user_retention", - "increase_user_adoption", - "sharpen_marketing_messaging", - "support_sales", - "other", -]); - -export type TUserObjective = z.infer; - -export const ZSurveyWelcomeCard = z.object({ - enabled: z.boolean(), - headline: z.string().optional(), - html: z.string().optional(), - fileUrl: z.string().optional(), - buttonLabel: z.string().optional(), - timeToFinish: z.boolean().default(true), - showResponseCount: z.boolean().default(false), -}); - -export type TSurveyWelcomeCard = z.infer; - -export const ZSurveyThankYouCard = z.object({ - enabled: z.boolean(), - headline: z.optional(z.string()), - subheader: z.optional(z.string()), - buttonLabel: z.optional(z.string()), - buttonLink: z.optional(z.string()), - imageUrl: z.string().optional(), -}); - -export type TSurveyThankYouCard = z.infer; - -export const ZSurveyHiddenFields = z.object({ - enabled: z.boolean(), - fieldIds: z.optional(z.array(z.string())), -}); - -export type TSurveyHiddenFields = z.infer; - -export const ZSurveyChoice = z.object({ - id: z.string(), - label: z.string(), -}); - -export type TSurveyChoice = z.infer; - -export const ZSurveyPictureChoice = z.object({ - id: z.string(), - imageUrl: z.string(), -}); - -export type TSurveyPictureChoice = z.infer; - -export const ZSurveyLogicCondition = z.enum([ - "accepted", - "clicked", - "submitted", - "skipped", - "equals", - "notEquals", - "lessThan", - "lessEqual", - "greaterThan", - "greaterEqual", - "includesAll", - "includesOne", - "uploaded", - "notUploaded", - "booked", -]); - -export type TSurveyLogicCondition = z.infer; - -export const ZSurveyLogicBase = z.object({ - condition: ZSurveyLogicCondition.optional(), - value: z.union([z.string(), z.array(z.string())]).optional(), - destination: z.union([z.string(), z.literal("end")]).optional(), -}); - -export const ZSurveyFileUploadLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["uploaded", "notUploaded"]).optional(), - value: z.undefined(), -}); - -export const ZSurveyOpenTextLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["submitted", "skipped"]).optional(), - value: z.undefined(), -}); - -export const ZSurveyConsentLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["skipped", "accepted"]).optional(), - value: z.undefined(), -}); - -export const ZSurveyMultipleChoiceSingleLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["submitted", "skipped", "equals", "notEquals"]).optional(), - value: z.string().optional(), -}); - -export const ZSurveyMultipleChoiceMultiLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["submitted", "skipped", "includesAll", "includesOne", "equals"]).optional(), - value: z.union([z.array(z.string()), z.string()]).optional(), -}); - -export const ZSurveyNPSLogic = ZSurveyLogicBase.extend({ - condition: z - .enum([ - "equals", - "notEquals", - "lessThan", - "lessEqual", - "greaterThan", - "greaterEqual", - "submitted", - "skipped", - ]) - .optional(), - value: z.union([z.string(), z.number()]).optional(), -}); - -const ZSurveyCTALogic = ZSurveyLogicBase.extend({ - // "submitted" condition is legacy and should be removed later - condition: z.enum(["clicked", "submitted", "skipped"]).optional(), - value: z.undefined(), -}); - -const ZSurveyRatingLogic = ZSurveyLogicBase.extend({ - condition: z - .enum([ - "equals", - "notEquals", - "lessThan", - "lessEqual", - "greaterThan", - "greaterEqual", - "submitted", - "skipped", - ]) - .optional(), - value: z.union([z.string(), z.number()]).optional(), -}); - -const ZSurveyPictureSelectionLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["submitted", "skipped"]).optional(), - value: z.undefined(), -}); - -const ZSurveyCalLogic = ZSurveyLogicBase.extend({ - condition: z.enum(["booked", "skipped"]).optional(), - value: z.undefined(), -}); - -export const ZSurveyLogic = z.union([ - ZSurveyOpenTextLogic, - ZSurveyConsentLogic, - ZSurveyMultipleChoiceSingleLogic, - ZSurveyMultipleChoiceMultiLogic, - ZSurveyNPSLogic, - ZSurveyCTALogic, - ZSurveyRatingLogic, - ZSurveyPictureSelectionLogic, - ZSurveyFileUploadLogic, - ZSurveyCalLogic, -]); - -export type TSurveyLogic = z.infer; - -const ZSurveyQuestionBase = z.object({ - id: z.string(), - type: z.string(), - headline: z.string(), - subheader: z.string().optional(), - imageUrl: z.string().optional(), - required: z.boolean(), - buttonLabel: z.string().optional(), - backButtonLabel: z.string().optional(), - scale: z.enum(["number", "smiley", "star"]).optional(), - range: z.union([z.literal(5), z.literal(3), z.literal(4), z.literal(7), z.literal(10)]).optional(), - logic: z.array(ZSurveyLogic).optional(), - isDraft: z.boolean().optional(), -}); - -export const ZSurveyOpenTextQuestionInputType = z.enum(["text", "email", "url", "number", "phone"]); -export type TSurveyOpenTextQuestionInputType = z.infer; - -export const ZSurveyOpenTextQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.OpenText), - placeholder: z.string().optional(), - longAnswer: z.boolean().optional(), - logic: z.array(ZSurveyOpenTextLogic).optional(), - inputType: ZSurveyOpenTextQuestionInputType.optional().default("text"), -}); - -export type TSurveyOpenTextQuestion = z.infer; - -export const ZSurveyConsentQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.Consent), - html: z.string().optional(), - label: z.string(), - placeholder: z.string().optional(), - logic: z.array(ZSurveyConsentLogic).optional(), -}); - -export type TSurveyConsentQuestion = z.infer; - -export const ZSurveyMultipleChoiceSingleQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.MultipleChoiceSingle), - choices: z.array(ZSurveyChoice), - logic: z.array(ZSurveyMultipleChoiceSingleLogic).optional(), - shuffleOption: z.enum(["none", "all", "exceptLast"]).optional(), - otherOptionPlaceholder: z.string().optional(), -}); - -export type TSurveyMultipleChoiceSingleQuestion = z.infer; - -export const ZSurveyMultipleChoiceMultiQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.MultipleChoiceMulti), - choices: z.array(ZSurveyChoice), - logic: z.array(ZSurveyMultipleChoiceMultiLogic).optional(), - shuffleOption: z.enum(["none", "all", "exceptLast"]).optional(), - otherOptionPlaceholder: z.string().optional(), -}); - -export type TSurveyMultipleChoiceMultiQuestion = z.infer; - -export const ZSurveyNPSQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.NPS), - lowerLabel: z.string(), - upperLabel: z.string(), - logic: z.array(ZSurveyNPSLogic).optional(), -}); - -export type TSurveyNPSQuestion = z.infer; - -export const ZSurveyCTAQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.CTA), - html: z.string().optional(), - buttonUrl: z.string().optional(), - buttonExternal: z.boolean(), - dismissButtonLabel: z.string().optional(), - logic: z.array(ZSurveyCTALogic).optional(), -}); - -export type TSurveyCTAQuestion = z.infer; - -// export const ZSurveyWelcomeQuestion = ZSurveyQuestionBase.extend({ -// type: z.literal(TSurveyQuestionType.Welcome), -// html: z.string().optional(), -// fileUrl: z.string().optional(), -// buttonUrl: z.string().optional(), -// timeToFinish: z.boolean().default(false), -// logic: z.array(ZSurveyCTALogic).optional(), -// }); - -// export type TSurveyWelcomeQuestion = z.infer; - -export const ZSurveyRatingQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.Rating), - scale: z.enum(["number", "smiley", "star"]), - range: z.union([z.literal(5), z.literal(3), z.literal(4), z.literal(7), z.literal(10)]), - lowerLabel: z.string(), - upperLabel: z.string(), - logic: z.array(ZSurveyRatingLogic).optional(), -}); - -export const ZSurveyDateQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.Date), - html: z.string().optional(), - format: z.enum(["M-d-y", "d-M-y", "y-M-d"]), -}); - -export type TSurveyDateQuestion = z.infer; - -export type TSurveyRatingQuestion = z.infer; - -export const ZSurveyPictureSelectionQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.PictureSelection), - allowMulti: z.boolean().optional().default(false), - choices: z.array(ZSurveyPictureChoice), - logic: z.array(ZSurveyPictureSelectionLogic).optional(), -}); - -export type TSurveyPictureSelectionQuestion = z.infer; - -export const ZSurveyFileUploadQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.FileUpload), - allowMultipleFiles: z.boolean(), - maxSizeInMB: z.number().optional(), - allowedFileExtensions: z.array(ZAllowedFileExtension).optional(), - logic: z.array(ZSurveyFileUploadLogic).optional(), -}); - -export type TSurveyFileUploadQuestion = z.infer; - -export const ZSurveyCalQuestion = ZSurveyQuestionBase.extend({ - type: z.literal(TSurveyQuestionType.Cal), - calUserName: z.string(), - logic: z.array(ZSurveyCalLogic).optional(), -}); - -export type TSurveyCalQuestion = z.infer; - -export const ZSurveyQuestion = z.union([ - ZSurveyOpenTextQuestion, - ZSurveyConsentQuestion, - ZSurveyMultipleChoiceSingleQuestion, - ZSurveyMultipleChoiceMultiQuestion, - ZSurveyNPSQuestion, - ZSurveyCTAQuestion, - ZSurveyRatingQuestion, - ZSurveyPictureSelectionQuestion, - ZSurveyDateQuestion, - ZSurveyFileUploadQuestion, - ZSurveyCalQuestion, -]); - -export type TSurveyQuestion = z.infer; - -export const ZSurveyQuestions = z.array(ZSurveyQuestion); - -export type TSurveyQuestions = z.infer; - -export const ZSurveyClosedMessage = z - .object({ - enabled: z.boolean().optional(), - heading: z.string().optional(), - subheading: z.string().optional(), - }) - .nullable() - .optional(); - -export type TSurveyClosedMessage = z.infer; - -export const ZSurveyAttributeFilter = z.object({ - attributeClassId: z.string().cuid2(), - condition: z.enum(["equals", "notEquals"]), - value: z.string(), -}); - -export type TSurveyAttributeFilter = z.infer; - -export const ZSurveyType = z.enum(["web", "email", "link", "mobile"]); - -export type TSurveyType = z.infer; - -const ZSurveyStatus = z.enum(["draft", "inProgress", "paused", "completed"]); - -export type TSurveyStatus = z.infer; - -const ZSurveyDisplayOption = z.enum(["displayOnce", "displayMultiple", "respondMultiple"]); - -export type TSurveyDisplayOption = z.infer; - -export const ZColor = z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/); - -export const ZPlacement = z.enum(["bottomLeft", "bottomRight", "topLeft", "topRight", "center"]); - -export type TPlacement = z.infer; - -export const ZSurveyProductOverwrites = z.object({ - brandColor: ZColor.nullish(), - highlightBorderColor: ZColor.nullish(), - placement: ZPlacement.nullish(), - clickOutsideClose: z.boolean().nullish(), - darkOverlay: z.boolean().nullish(), -}); - -export type TSurveyProductOverwrites = z.infer; - -export const ZSurveyStylingBackground = z.object({ - bg: z.string().nullish(), - bgType: z.enum(["animation", "color", "image"]).nullish(), - brightness: z.number().nullish(), -}); - -export type TSurveyStylingBackground = z.infer; - -export const ZSurveyStyling = z.object({ - background: ZSurveyStylingBackground.nullish(), - hideProgressBar: z.boolean().nullish(), -}); - -export type TSurveyStyling = z.infer; - -export const ZSurveySingleUse = z - .object({ - enabled: z.boolean(), - heading: z.optional(z.string()), - subheading: z.optional(z.string()), - isEncrypted: z.boolean(), - }) - .nullable(); - -export type TSurveySingleUse = z.infer; - -export const ZSurveyVerifyEmail = z - .object({ - name: z.optional(z.string()), - subheading: z.optional(z.string()), - }) - .optional(); - -export type TSurveyVerifyEmail = z.infer; - -export const ZSurvey = z.object({ - id: z.string().cuid2(), - createdAt: z.date(), - updatedAt: z.date(), - name: z.string(), - type: ZSurveyType, - environmentId: z.string(), - createdBy: z.string().nullable(), - status: ZSurveyStatus, - attributeFilters: z.array(ZSurveyAttributeFilter), - displayOption: ZSurveyDisplayOption, - autoClose: z.number().nullable(), - triggers: z.array(z.string()), - redirectUrl: z.string().url().nullable(), - recontactDays: z.number().nullable(), - welcomeCard: ZSurveyWelcomeCard, - questions: ZSurveyQuestions, - thankYouCard: ZSurveyThankYouCard, - hiddenFields: ZSurveyHiddenFields, - delay: z.number(), - autoComplete: z.number().nullable(), - closeOnDate: z.date().nullable(), - productOverwrites: ZSurveyProductOverwrites.nullable(), - styling: ZSurveyStyling.nullable(), - surveyClosedMessage: ZSurveyClosedMessage.nullable(), - singleUse: ZSurveySingleUse.nullable(), - verifyEmail: ZSurveyVerifyEmail.nullable(), - pin: z.string().nullable().optional(), - resultShareKey: z.string().nullable(), - displayPercentage: z.number().min(1).max(100).nullable(), -}); - -export type TSurvey = z.infer; - -export const ZTemplate = z.object({ - name: z.string(), - description: z.string(), - icon: z.any().optional(), - category: z - .enum(["Product Experience", "Exploration", "Growth", "Increase Revenue", "Customer Success"]) - .optional(), - objectives: z.array(ZUserObjective).optional(), - preset: z.object({ - name: z.string(), - welcomeCard: ZSurveyWelcomeCard, - questions: ZSurveyQuestions, - thankYouCard: ZSurveyThankYouCard, - hiddenFields: ZSurveyHiddenFields, - }), -}); - -export type TTemplate = z.infer; diff --git a/apps/formbricks-com/components/home/Faq.tsx b/apps/formbricks-com/components/home/Faq.tsx deleted file mode 100644 index 2fae25717b..0000000000 --- a/apps/formbricks-com/components/home/Faq.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import HeadingCentered from "@/components/shared/HeadingCentered"; -import SeoFaq from "@/components/shared/seo/SeoFaq"; - -const FAQs = [ - { - question: "What is Formbricks?", - answer: - "Formbricks is an experience management platform built on top of the fastest growing open source survey infrastructure out there. It aims to assist businesses in capturing and understanding customer insights and emotions towards their products and services. Designed to integrate seamlessly with various platforms, Formbricks focuses on user research, emphasizing data privacy and requiring minimal development effort for integration.", - }, - { - question: "How do I integrate Formbricks into my application?", - answer: - "Integrating Formbricks into an application is effortless. For web applications, it involves adding a simple script tag to the HTML head. For applications built with modern frameworks such as React, Vue, or Svelte, Formbricks can be installed via NPM. Initialization with specific environment details completes the setup. Detailed instructions and framework guides are readily available in the detailed Formbricks documentation.", - }, - { - question: "Is Formbricks GDPR compliant?", - answer: - "Indeed, Formbricks ensures full GDPR compliance, emphasizing the protection of user data privacy. It offers both cloud-based solutions and self-hosting options, adhering to data privacy regulations and making it a trusted choice for secure open source survey tool deployment.", - }, - { - question: "Can I self-host Formbricks?", - answer: - "Certainly! Formbricks encourages self-hosting, providing users with greater control over their data and compliance. This option underscores Formbricks' commitment to offering versatile and free open source experience management software, ensuring users can adapt the platform to their unique requirements. Detailed self-hosting documentation is available for users seeking to leverage this capability.", - }, - { - question: "How does Formbricks pricing work?", - answer: - "Formbricks introduces a 'Free forever' plan, showcasing its commitment to making open source survey platforms universally accessible. This plan features unlimited surveys and in-product surveys, among other functionalities. Self-hosting users can enjoy all the benefits of the free plan with additional features at no extra cost. For those seeking advanced features Formbricks invites you to explore the pricing section for more information.", - }, - { - question: "How does Formbricks make money?", - answer: - "Formbricks employs the 'Open Core' business model. The core of the Formbricks application is offered for free. Formbricks monetizes by providing advanced features and services, typically catering to the needs of larger clients, thereby generating revenue.", - }, - { - question: "What is the best open source survey software available?", - answer: - "Identifying the best open source survey software requires evaluating features, flexibility, and support. Formbricks is a noteworthy contender, offering comprehensive experience management solutions. This platform excels in enabling businesses to delve into customer insights and feedback, offering versatility and ease of system integration.", - }, - { - question: "Can open source survey platforms be customized for my business needs?", - answer: - "Definitely. Platforms like Formbricks exemplify the customizability of open source survey tools, allowing for extensive tailoring to meet specific business requirements. Access to the source code enables deep customization, from branding adjustments to complex integrations with existing systems, underscoring the flexibility of open source experience management solutions.", - }, - { - question: - "What advantages does using an experience management platform offer over traditional survey tools?", - answer: - "Experience management platforms, especially those built on open source foundations, offer a more holistic view of customer interactions compared to traditional survey tools. They enable real-time collection, analysis, and application of customer feedback, ensuring a thorough understanding of the customer journey. This comprehensive insight facilitates informed decision-making and boosts customer satisfaction.", - }, -]; - -export default function FAQ() { - return ( -
    - - -
    - ); -} diff --git a/apps/formbricks-com/components/home/Features.tsx b/apps/formbricks-com/components/home/Features.tsx deleted file mode 100644 index faf704d1b0..0000000000 --- a/apps/formbricks-com/components/home/Features.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { CodeFileIcon, EyeIcon, HandPuzzleIcon } from "@formbricks/ui/icons"; - -import HeadingCentered from "../shared/HeadingCentered"; - -const features = [ - { - id: "compliance", - name: "Smoothly compliant", - description: "Use our GDPR-compliant Cloud or self-host the entire solution.", - icon: EyeIcon, - }, - { - id: "customizable", - name: "Fully customizable", - description: "Full customizability and extendability. Integrate with your stack easily.", - icon: HandPuzzleIcon, - }, - { - id: "independent", - name: "Stay independent", - description: "The code is open source. Do with it what your organization needs.", - icon: CodeFileIcon, - }, -]; -export const Features: React.FC = () => { - return ( -
    - - -
      - {features.map((feature) => { - const IconComponent: React.ElementType = feature.icon; - - return ( -
    • -
      -
      - -
      -
      -
      -

      - {feature.name} -

      -
      -
      Description
      -
      {feature.description}
      -
      -
      -
    • - ); - })} -
    -
    - ); -}; - -export default Features; diff --git a/apps/formbricks-com/components/home/GitHubSponsorship.tsx b/apps/formbricks-com/components/home/GitHubSponsorship.tsx deleted file mode 100644 index 3cdf43c482..0000000000 --- a/apps/formbricks-com/components/home/GitHubSponsorship.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import Link from "next/link"; - -export const GitHubSponsorship: React.FC = () => { - return ( - -
    - {/* GitHub Sponsors Formbricks badge - GitHub Sponsors Formbricks badge */} - -
    -

    - We are live on ProductHunt today 🚀 -

    -

    - Support our open source project with an upvote and comment. - - - View launch post. - - -

    -
    -
    - {/* Product Hunt Logo */} -
    -
    - - ); -}; - -export default GitHubSponsorship; diff --git a/apps/formbricks-com/components/home/Hero.tsx b/apps/formbricks-com/components/home/Hero.tsx deleted file mode 100644 index a000687903..0000000000 --- a/apps/formbricks-com/components/home/Hero.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import LogoBar from "@/components/salespage/LogoBar"; -import { ShieldCheckIcon, StarIcon } from "lucide-react"; -import { usePlausible } from "next-plausible"; -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -export const Hero: React.FC = ({}) => { - const plausible = usePlausible(); - const router = useRouter(); - return ( -
    -
    -
    -

    - Privacy-first -

    - - - Star us on GitHub - -
    -

    - - Turn customer insights -
    - into irresistible experiences -
    -

    -

    - Formbricks is an Experience Management Suite built on the largest open source survey stack - worldwide. Gracefully gather feedback at every step of the customer journey to{" "} - - know what your customers need. - -

    - - -
    - - -
    -
    -
    - ); -}; - -export default Hero; diff --git a/apps/formbricks-com/components/home/HeroAnimation.tsx b/apps/formbricks-com/components/home/HeroAnimation.tsx deleted file mode 100644 index 87312b46bd..0000000000 --- a/apps/formbricks-com/components/home/HeroAnimation.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import type { LottiePlayer } from "lottie-web"; -import Image from "next/image"; -import { useEffect, useRef, useState } from "react"; - -export const HeroAnimation: React.FC = ({ fallbackImage, ...props }) => { - const [loaded, setLoaded] = useState(false); - const ref = useRef(null); - const [lottie, setLottie] = useState(null); - - useEffect(() => { - import("lottie-web").then((Lottie) => setLottie(Lottie.default)); - }, []); - - useEffect(() => { - if (lottie && ref.current) { - const animation = lottie.loadAnimation({ - container: ref.current, - renderer: "svg", - loop: true, - autoplay: true, - path: "/animations/opensource-xm-platform-formbricks.json", - }); - - animation.addEventListener("DOMLoaded", () => { - setLoaded(true); - }); - - return () => animation.destroy(); - } - }, [lottie]); - - return ( -
    -
    - {!loaded && ( -
    - Fallback Image -
    - )} -
    - ); -}; - -export default HeroAnimation; diff --git a/apps/formbricks-com/components/home/Highlights.tsx b/apps/formbricks-com/components/home/Highlights.tsx deleted file mode 100644 index 863d83d3d4..0000000000 --- a/apps/formbricks-com/components/home/Highlights.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import ImageAttributesDark from "@/images/attributes-dark.svg"; -import ImageAttributesLight from "@/images/attributes-light.svg"; -import ImageEventTriggerDark from "@/images/event-trigger-dark.svg"; -import ImageEventTriggerLight from "@/images/event-trigger-light.svg"; -import Image from "next/image"; - -export const Highlights: React.FC = ({}) => { - return ( -
    -
    -
    -

    - Ask at the right moment, -
    - get the data you need. -

    -

    - Follow up emails are so 2010. Ask users as they experience your product - and leverage a - significantly higher conversion rate. -

    -
    -
    - react library - react library -
    -
    - -
    -
    - react library - react library -
    -
    -

    - Don't ‘Spray and pray’. -
    - Pre-segment granularly. -

    -

    - Pre-segment who sees your survey based on custom attributes. Keep the signal, cancel out the - noise. -

    -
    -
    -
    - ); -}; - -export default Highlights; diff --git a/apps/formbricks-com/components/home/ScrollToTop.tsx b/apps/formbricks-com/components/home/ScrollToTop.tsx deleted file mode 100644 index 8fc0cff88d..0000000000 --- a/apps/formbricks-com/components/home/ScrollToTop.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import throttle from "lodash/throttle"; -import { ArrowUpIcon } from "lucide-react"; -import { useCallback, useEffect, useState } from "react"; - -import { Button } from "@formbricks/ui/Button"; - -const ScrollToTopButton = () => { - const [visible, setVisible] = useState(false); - - const scrollToTop = useCallback(() => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }, []); - - useEffect(() => { - const toggleVisible = () => { - const scrolled = document.documentElement.scrollTop; - if (scrolled > 500) { - setVisible(true); - } else if (scrolled <= 500) { - setVisible(false); - } - }; - - const throttledToggleVisible = throttle(toggleVisible, 200); - - window.addEventListener("scroll", throttledToggleVisible); - - return () => window.removeEventListener("scroll", throttledToggleVisible); - }, []); - - return ( -
    - -
    - ); -}; - -export default ScrollToTopButton; diff --git a/apps/formbricks-com/components/home/SetupTabs.tsx b/apps/formbricks-com/components/home/SetupTabs.tsx deleted file mode 100644 index 2a2c9d2264..0000000000 --- a/apps/formbricks-com/components/home/SetupTabs.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import clsx from "clsx"; -import { useState } from "react"; -import { IoLogoHtml5, IoLogoNpm } from "react-icons/io5"; - -import CodeBlock from "../shared/CodeBlock"; - -interface SecondNavbarProps { - tabs: { id: string; label: string; icon?: React.ReactNode }[]; - activeId: string; - setActiveId: (id: string) => void; -} - -export const TabBar: React.FC = ({ tabs, activeId, setActiveId }) => { - return ( -
    - -
    - ); -}; - -const tabs = [ - { id: "npm", label: "NPM", icon: }, - { id: "html", label: "HTML", icon: }, -]; - -export const SetupInstructions: React.FC = ({}) => { - const [activeTab, setActiveTab] = useState(tabs[0].id); - - return ( -
    - -
    - {activeTab === "npm" ? ( - <> - npm install @formbricks/js - - {`import formbricks from "@formbricks/js"; - -if (typeof window !== "undefined") { - formbricks.init({ - environmentId: "claV2as2kKAqF28fJ8", - apiHost: "https://app.formbricks.com", - }); -}`} - - ) : activeTab === "html" ? ( - {``} - ) : null} -
    -
    - ); -}; - -export default SetupInstructions; diff --git a/apps/formbricks-com/components/home/Steps.tsx b/apps/formbricks-com/components/home/Steps.tsx deleted file mode 100644 index dc527adea6..0000000000 --- a/apps/formbricks-com/components/home/Steps.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import DemoPreview from "@/components/dummyUI/DemoPreview"; -import DashboardMockupDark from "@/images/dashboard-mockup-dark.png"; -import DashboardMockup from "@/images/dashboard-mockup.png"; -import { MousePointerClickIcon } from "lucide-react"; -import Image from "next/image"; - -import { Button } from "@formbricks/ui/Button"; - -import AddEventDummy from "../dummyUI/AddEventDummy"; -import HeadingCentered from "../shared/HeadingCentered"; -import SetupTabs from "./SetupTabs"; - -export const Steps: React.FC = () => { - return ( -
    - -
    -
    -
    -

    Step 1

    -

    - Copy + Paste -

    -

    - Simply copy a <script> tag to your HTML head - that’s about it. Or use NPM to install - Formbricks for React, Vue, Svelte, etc. -

    -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Step 2

    -

    - No-Code: Track User Actions -

    -

    - Set up user actions which can trigger your survey without writing a single line of code. Surveys - can be triggered on specific pages or after an element is clicked. -

    -
    -
    - -
    -
    -

    Step 3

    -

    - Create your survey -

    -

    - Start from a template - or from scratch. Ask what you want, in any language. You can also adjust - the look and feel of your survey. -

    -
    -
    - -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Step 4

    -

    - Set segment and trigger -

    -

    - Create a custom segment for each survey. Use attributes and past user actions to only survey the - people who have answers. Trigger your survey on any user action in your app. -

    -
    -
    - -
    -
    -

    Step 5

    -

    - Make better decisions -

    -

    - Gather all insights you can - including partial submissions. Build conviction for the next - product decision. Better data, better business. -

    -
    -
    - Data Pipelines - Data Pipelines -
    -
    -
    -
    - ); -}; - -export default Steps; diff --git a/apps/formbricks-com/components/home/SurveyTypeSelection.tsx b/apps/formbricks-com/components/home/SurveyTypeSelection.tsx deleted file mode 100644 index 54a556e592..0000000000 --- a/apps/formbricks-com/components/home/SurveyTypeSelection.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import InappMockup from "@/images/survey-type-inapp.png"; -import LinkMockup from "@/images/survey-type-link.webp"; -import WebsiteMockup from "@/images/survey-type-website.png"; -import Image from "next/image"; -import { useRouter } from "next/router"; - -import { OptionCard } from "@formbricks/ui/OptionCard"; - -import HeadingCentered from "../shared/HeadingCentered"; - -export const SurveyTypeSelection: React.FC = () => { - const router = useRouter(); - - return ( -
    - - -
    - { - router.push("/website-survey"); - }}> - - - { - router.push("/open-source-form-builder"); - }}> - - - { - router.push("/in-app-survey"); - }}> - - -
    -
    - ); -}; - -export default SurveyTypeSelection; diff --git a/apps/formbricks-com/components/home/VideoWalkThrough.tsx b/apps/formbricks-com/components/home/VideoWalkThrough.tsx deleted file mode 100644 index 4c40872d68..0000000000 --- a/apps/formbricks-com/components/home/VideoWalkThrough.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Modal } from "@formbricks/ui/Modal"; -import { ResponsiveVideo } from "@formbricks/ui/ResponsiveVideo"; - -interface VideoWalkThroughProps { - open: boolean; - setOpen: (v: boolean) => void; -} - -export const VideoWalkThrough: React.FC = ({ open, setOpen }) => { - return ( - -
    - -
    -
    - ); -}; diff --git a/apps/formbricks-com/components/salespage/FeatureCard.tsx b/apps/formbricks-com/components/salespage/FeatureCard.tsx deleted file mode 100644 index ca7df8a51f..0000000000 --- a/apps/formbricks-com/components/salespage/FeatureCard.tsx +++ /dev/null @@ -1,19 +0,0 @@ -interface TestimonialProps { - title: string; - text: string; - Icon: React.ElementType; -} - -export default function SalesTestimonial({ title, text, Icon }: TestimonialProps) { - return ( -
    -
    - -
    -
    -

    {title}

    -

    {text}

    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/HeaderLight.tsx b/apps/formbricks-com/components/salespage/HeaderLight.tsx deleted file mode 100644 index f1aa665346..0000000000 --- a/apps/formbricks-com/components/salespage/HeaderLight.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { Popover, Transition } from "@headlessui/react"; -import { Menu, X } from "lucide-react"; -import { usePlausible } from "next-plausible"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { Fragment } from "react"; - -import { Button } from "@formbricks/ui/Button"; - -import { FooterLogo } from "../shared/Logo"; - -const mainNav = [ - { name: "Link Surveys", href: "/open-source-form-builder", status: true }, - { name: "Website Surveys", href: "/website-survey", status: true }, - { name: "In-app Surveys", href: "/in-app-survey", status: true }, - { name: "Pricing", href: "/pricing", status: true }, -]; - -export default function HeaderLight() { - const plausible = usePlausible(); - const router = useRouter(); - return ( -
    - - Formbricks - - - -
    - {mainNav.map((item) => ( - - {item.name} - - ))} -
    - - - - - Open menu -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/LayoutLight.tsx b/apps/formbricks-com/components/salespage/LayoutLight.tsx deleted file mode 100644 index 1ee05e9bbf..0000000000 --- a/apps/formbricks-com/components/salespage/LayoutLight.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import Footer from "../shared/Footer"; -import MetaInformation from "../shared/MetaInformation"; -import HeaderLight from "./HeaderLight"; - -interface LayoutProps { - children: React.ReactNode; - title: string; - description: string; -} - -export default function LayoutLight({ title, description, children }: LayoutProps) { - return ( -
    - - -
    - {children} -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/LogoBar.tsx b/apps/formbricks-com/components/salespage/LogoBar.tsx deleted file mode 100644 index 9ff86f4093..0000000000 --- a/apps/formbricks-com/components/salespage/LogoBar.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import CalLogoLight from "@/images/clients/cal-logo-light.svg"; -import CrowdLogoLight from "@/images/clients/crowd-logo-light.svg"; -import FlixbusLogo from "@/images/clients/flixbus-white.svg"; -import GumtreeLogo from "@/images/clients/gumtree.png"; -import LelyLogo from "@/images/clients/lely-logo.webp"; -import NILogoDark from "@/images/clients/niLogoDark.svg"; -import OpinodoLogo from "@/images/clients/opinodo.png"; -import OptimoleLogo from "@/images/clients/optimole-logo.svg"; -import ThemeisleLogo from "@/images/clients/themeisle-logo.webp"; -import Image from "next/image"; - -interface LogoBarProps { - hideTeamsClaim?: boolean; -} - -export default function LogoBar({ hideTeamsClaim = false }: LogoBarProps) { - return ( -
    - {!hideTeamsClaim && ( -

    - 10,000+ teams at the world’s best companies trust Formbricks -

    - )} -
    -
    -
    - {/* List of logos, each wrapped in a div with specific width and flex properties */} - {[ - ThemeisleLogo, - CalLogoLight, - FlixbusLogo, - GumtreeLogo, - LelyLogo, - OpinodoLogo, - CrowdLogoLight, - OptimoleLogo, - NILogoDark, - ].map((src, index) => ( -
    - Formbricks Client Logo -
    - ))} - {/* Repeat the logos for a seamless loop */} - {[ - ThemeisleLogo, - CalLogoLight, - FlixbusLogo, - GumtreeLogo, - LelyLogo, - OpinodoLogo, - CrowdLogoLight, - OptimoleLogo, - NILogoDark, - ].map((src, index) => ( -
    - Formbricks Client Logo -
    - ))} -
    -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/SalesBreaker.tsx b/apps/formbricks-com/components/salespage/SalesBreaker.tsx deleted file mode 100644 index 798232f7fe..0000000000 --- a/apps/formbricks-com/components/salespage/SalesBreaker.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import SalesCTA from "@/components/salespage/SalesCTA"; - -interface Props { - headline: string; - subheadline: string; -} - -export default function SalesBreaker({ headline, subheadline }: Props) { - return ( -
    -
    -
    - -
    -

    {headline}

    -

    - {subheadline} -

    -
    - -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/SalesCTA.tsx b/apps/formbricks-com/components/salespage/SalesCTA.tsx deleted file mode 100644 index 530ff3b699..0000000000 --- a/apps/formbricks-com/components/salespage/SalesCTA.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { usePlausible } from "next-plausible"; -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -export default function SalesCTA() { - const plausible = usePlausible(); - const router = useRouter(); - return ( - - ); -} diff --git a/apps/formbricks-com/components/salespage/SalesPageFeature.tsx b/apps/formbricks-com/components/salespage/SalesPageFeature.tsx deleted file mode 100644 index 8213ef9b8b..0000000000 --- a/apps/formbricks-com/components/salespage/SalesPageFeature.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import SalesCTA from "@/components/salespage/SalesCTA"; -import Image, { StaticImageData } from "next/image"; - -interface SalesPageFeatureProps { - imgSrc: StaticImageData; - imgAlt: string; - headline: string; - subheadline: string; - imgLeft?: boolean; -} - -export default function SalesPageFeature({ - imgSrc, - imgAlt, - headline, - subheadline, - imgLeft, -}: SalesPageFeatureProps) { - return ( -
    -
    -

    {headline}

    -

    {subheadline}

    - -
    -
    - {imgAlt} -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/SalesPageHero.tsx b/apps/formbricks-com/components/salespage/SalesPageHero.tsx deleted file mode 100644 index 732f34bdd8..0000000000 --- a/apps/formbricks-com/components/salespage/SalesPageHero.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import SalesCTA from "@/components/salespage/SalesCTA"; -import Image, { StaticImageData } from "next/image"; - -interface SalesPageHeroProps { - imgSrc: StaticImageData; - imgAlt: string; - headline: React.ReactNode; - subheadline: string; -} - -export default function SalesPageHero({ imgSrc, imgAlt, headline, subheadline }: SalesPageHeroProps) { - return ( -
    -
    -

    {headline}

    -

    {subheadline}

    - -
    -
    - {imgAlt} -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/SalesSteps.tsx b/apps/formbricks-com/components/salespage/SalesSteps.tsx deleted file mode 100644 index aa32f34344..0000000000 --- a/apps/formbricks-com/components/salespage/SalesSteps.tsx +++ /dev/null @@ -1,32 +0,0 @@ -interface SalesStepsProps { - steps: Array<{ id: string; name: string; description: string }>; -} - -export default function SalesSteps({ steps }: SalesStepsProps) { - return ( -
    -
      - {steps.map((step) => { - return ( -
    • -
      -
      - {step.id} -
      -
      -
      -

      {step.name}

      -
      -
      Description
      -
      {step.description}
      -
      -
      -
    • - ); - })} -
    -
    - ); -} diff --git a/apps/formbricks-com/components/salespage/SalesTestimonial.tsx b/apps/formbricks-com/components/salespage/SalesTestimonial.tsx deleted file mode 100644 index c584c33792..0000000000 --- a/apps/formbricks-com/components/salespage/SalesTestimonial.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import Image, { StaticImageData } from "next/image"; - -interface TestimonialProps { - quote: string; - author: string; - imgSrc: StaticImageData; - imgAlt: string; - textSize: "base" | "large"; -} - -export default function SalesTestimonial({ - quote, - author, - imgAlt, - imgSrc, - textSize = "base", -}: TestimonialProps) { - return ( -
    -

    - {quote} -

    -

    {author}

    - {imgAlt} -
    - ); -} diff --git a/apps/formbricks-com/components/shared/APILayout.tsx b/apps/formbricks-com/components/shared/APILayout.tsx deleted file mode 100644 index 3e067dcbd1..0000000000 --- a/apps/formbricks-com/components/shared/APILayout.tsx +++ /dev/null @@ -1,216 +0,0 @@ -"use client"; - -import clsx from "clsx"; -import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; -import { useState } from "react"; - -interface APICallProps { - method: "GET" | "POST"; - url: string; - description: string; - headers: { - label: string; - type: string; - description: string; - required?: boolean; - }[]; - 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, headers, bodies, responses, example }: APICallProps) { - const [switchState, setSwitchState] = useState(true); - function handleOnChange() { - setSwitchState(!switchState); - } - - return ( -
    - {switchState ? ( -
    - ); -} - -interface ParaProps { - label: string; - type: string; - description: string; - required?: boolean; -} - -function Parameter({ label, type, description, required }: ParaProps) { - return ( - <> -
    -
    - {label} - {required &&

    *

    } -
    -
    {type}
    -
    {description}
    -
    - - ); -} - -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 ( -
    -
    -
    -   -
    -
    {statusCode}
    -
    -
    -
    {description}
    -
    - {example && - (toggleExample ? ( -
    -
    - {example && toggleExample && ( -
    - {example} -
    - )} -
    - ); -} diff --git a/apps/formbricks-com/components/shared/AuthorBox.tsx b/apps/formbricks-com/components/shared/AuthorBox.tsx deleted file mode 100644 index b01544a2f0..0000000000 --- a/apps/formbricks-com/components/shared/AuthorBox.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import AuthorJohannes from "@/images/blog/johannes-co-founder-formbricks-small.jpg"; -import AuthorShubham from "@/images/blog/shubham-engineer.png"; -import AuthorSudhanshu from "@/images/blog/sudhanshu-engineer.jpeg"; -import Image from "next/image"; -import Link from "next/link"; - -interface AuthorBoxProps { - name: string; - title: string; - date: string; - duration: string; - author: string; -} - -const authorImages = { - Johannes: AuthorJohannes, - Shubham: AuthorShubham, - Sudhanshu: AuthorSudhanshu, -}; - -export default function AuthorBox({ name, title, date, duration, author }: AuthorBoxProps) { - return ( -
    - {name} -
    -
    - -

    {name}

    - -

    {title}

    -
    -
    -

    {duration} Minutes

    -

    {date}

    -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/BestPracticeNavigation.tsx b/apps/formbricks-com/components/shared/BestPracticeNavigation.tsx deleted file mode 100644 index 636d938871..0000000000 --- a/apps/formbricks-com/components/shared/BestPracticeNavigation.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import clsx from "clsx"; -import Link from "next/link"; - -import { - BaseballIcon, - CancelSubscriptionIcon, - CodeBookIcon, - DogChaserIcon, - FeedbackIcon, - InterviewPromptIcon, - OnboardingIcon, - PMFIcon, -} from "@formbricks/ui/icons"; - -export default function BestPracticeNavigation() { - const BestPractices = [ - { - name: "Interview Prompt", - href: "/interview-prompt", - status: true, - icon: InterviewPromptIcon, - description: "Ask only power users to book a time in your calendar. Get those juicy details.", - category: "Understand Users", - }, - { - name: "Product-Market Fit Survey", - href: "/measure-product-market-fit", - status: true, - icon: PMFIcon, - description: "Find out how disappointed people would be if they could not use your service any more.", - category: "Understand Users", - }, - { - name: "Onboarding Segments", - href: "/onboarding-segmentation", - status: false, - icon: OnboardingIcon, - description: - "Get to know your users right from the start. Ask a few questions early, let us enrich the profile.", - category: "Understand Users", - }, - { - name: "Learn from Churn", - href: "/learn-from-churn", - status: true, - icon: CancelSubscriptionIcon, - description: "Churn is hard, but insightful. Learn from users who changed their mind.", - category: "Increase Revenue", - }, - { - name: "Improve Trial CR", - href: "/improve-trial-conversion", - status: true, - icon: BaseballIcon, - description: "Take guessing out, convert more trials to paid users with insights.", - category: "Increase Revenue", - }, - { - name: "Docs Feedback", - href: "/docs-feedback", - status: true, - icon: CodeBookIcon, - description: "Clear docs lead to more adoption. Understand granularly what's confusing.", - category: "Boost Retention", - }, - { - name: "Feature Chaser", - href: "/feature-chaser", - status: true, - icon: DogChaserIcon, - description: "Show a survey about a new feature shown only to people who used it.", - category: "Boost Retention", - }, - { - name: "Feedback Box", - href: "/feedback-box", - status: true, - icon: FeedbackIcon, - description: "Give users the chance to share feedback in a single click.", - category: "Boost Retention", - }, - ]; - - return ( -
    - {BestPractices.map((bestPractice) => ( - -
    -
    - {bestPractice.category} -
    -
    - -
    -

    - {bestPractice.name} -

    -

    - {bestPractice.description} -

    -
    - - ))} -
    - ); -} diff --git a/apps/formbricks-com/components/shared/BestPractices.tsx b/apps/formbricks-com/components/shared/BestPractices.tsx deleted file mode 100644 index 25b4f6ad45..0000000000 --- a/apps/formbricks-com/components/shared/BestPractices.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import HeadingCentered from "@/components/shared/HeadingCentered"; - -import BestPracticeNavigation from "./BestPracticeNavigation"; - -export default function InsightOppos() { - return ( -
    - - -
    - ); -} diff --git a/apps/formbricks-com/components/shared/BreakerCTA.tsx b/apps/formbricks-com/components/shared/BreakerCTA.tsx deleted file mode 100644 index 53fe1325b8..0000000000 --- a/apps/formbricks-com/components/shared/BreakerCTA.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import clsx from "clsx"; -import { usePlausible } from "next-plausible"; -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -interface Props { - teaser: string; - headline: string; - subheadline: string; - cta: string; - href: string; - inverted?: boolean; -} - -export default function BreakerCTA({ inverted = false, teaser, headline, subheadline, cta, href }: Props) { - const router = useRouter(); - const plausible = usePlausible(); - return ( -
    -
    -
    - -
    -

    - {teaser} -

    -

    - {headline} -

    -

    - {subheadline} -

    -
    - -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/CTA.tsx b/apps/formbricks-com/components/shared/CTA.tsx deleted file mode 100644 index a2068cd98b..0000000000 --- a/apps/formbricks-com/components/shared/CTA.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -import HeadingCentered from "./HeadingCentered"; - -export default function CTA() { - const router = useRouter(); - return ( - <> -
    - - -
    -
    -

    Self-hosted

    -

    Run locally e.g. with docker-compose.

    - -
    -
    -

    Cloud

    -

    Use our free managed service.

    - -
    -
    -
    - - ); -} diff --git a/apps/formbricks-com/components/shared/Callout.tsx b/apps/formbricks-com/components/shared/Callout.tsx deleted file mode 100644 index 4996f840bc..0000000000 --- a/apps/formbricks-com/components/shared/Callout.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Icon } from "@/components/shared/Icon"; -import clsx from "clsx"; - -const styles = { - note: { - container: "bg-slate-100 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10", - title: "text-slate-900 dark:text-slate-400", - body: "text-slate-800 [--tw-prose-background:theme(colors.slate.50)] prose-a:text-slate-900 prose-code:text-slate-900 dark:text-slate-300 dark:prose-code:text-slate-300", - }, - warning: { - container: "bg-amber-50 dark:bg-slate-800/60 dark:ring-1 dark:ring-slate-300/10", - title: "text-amber-900 dark:text-amber-500", - body: "text-amber-800 [--tw-prose-underline:theme(colors.amber.400)] [--tw-prose-background:theme(colors.amber.50)] prose-a:text-amber-900 prose-code:text-amber-900 dark:text-slate-300 dark:[--tw-prose-underline:theme(colors.slate.700)] dark:prose-code:text-slate-300", - }, -}; - -const icons = { - note: (props: any) => , - warning: (props: any) => , -}; - -interface CalloutProps { - type: "note" | "warning"; - title: string; - children: React.ReactNode; -} - -export function Callout({ type = "note", title, children }: CalloutProps) { - let IconComponent = icons[type]; - - return ( -
    - -
    -

    {title}

    -
    {children}
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/Card.jsx b/apps/formbricks-com/components/shared/Card.jsx deleted file mode 100644 index 46daeb69d7..0000000000 --- a/apps/formbricks-com/components/shared/Card.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import Link from "next/link"; -import clsx from "clsx"; - -function ChevronRightIcon(props) { - return ( - - ); -} - -export function Card({ as: Component = "div", className, children }) { - return ( - {children} - ); -} - -Card.Link = function CardLink({ children, ...props }) { - return ( - <> -
    - - - {children} - - - ); -}; - -Card.Title = function CardTitle({ as: Component = "h2", href, children }) { - return ( - - {href ? {children} : children} - - ); -}; - -Card.Description = function CardDescription({ children }) { - return

    {children}

    ; -}; - -Card.Cta = function CardCta({ children }) { - return ( - - ); -}; - -Card.Eyebrow = function CardEyebrow({ - as: Component = "p", - decorate = false, - className, - children, - ...props -}) { - return ( - - {decorate && ( - - ); -}; diff --git a/apps/formbricks-com/components/shared/CodeBlock.tsx b/apps/formbricks-com/components/shared/CodeBlock.tsx deleted file mode 100644 index 52fe63bf55..0000000000 --- a/apps/formbricks-com/components/shared/CodeBlock.tsx +++ /dev/null @@ -1,49 +0,0 @@ -// components/ui/CodeBlock.tsx -import Prism from "prismjs"; -import "prismjs/themes/prism.css"; -import React, { CSSProperties, useEffect } from "react"; - -interface CodeBlockProps { - children: React.ReactNode; -} - -const styles: Record = { - div: { - position: "relative", - marginTop: "1rem", - borderRadius: "0.375rem", - fontSize: "0.875rem", - fontWeight: "normal", - color: "#e5e7eb", - }, - pre: { - background: "none", - }, - code: { - textShadow: "none", - color: "#333333", - }, -}; - -const CodeBlock: React.FC = ({ children }) => { - useEffect(() => { - Prism.highlightAll(); - }, [children]); - - return ( -
    -
    -        
    -          {children}
    -        
    -      
    - -
    - ); -}; - -export default CodeBlock; diff --git a/apps/formbricks-com/components/shared/EarlyBirdDeal.tsx b/apps/formbricks-com/components/shared/EarlyBirdDeal.tsx deleted file mode 100644 index 29f4d7a2db..0000000000 --- a/apps/formbricks-com/components/shared/EarlyBirdDeal.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import EarlyBird from "@/images/early bird deal for open source jotform alternative typeform and surveymonkey_v2.svg"; -import { usePlausible } from "next-plausible"; -import Image from "next/image"; - -import { Button } from "@formbricks/ui/Button"; - -export default function EarlyBirdDeal() { - const plausible = usePlausible(); - return ( -
    -
    -

    - 50% off for Early Birds. -

    -

    - Limited deal: Only{" "} - 14 left. -

    - -
    - -
    -

    - This saves you $588 every year. -

    -
    - formbricks favicon open source forms typeform alternative -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/EnterpriseEditionInfo.tsx b/apps/formbricks-com/components/shared/EnterpriseEditionInfo.tsx deleted file mode 100644 index 0d71da0c45..0000000000 --- a/apps/formbricks-com/components/shared/EnterpriseEditionInfo.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Button } from "@formbricks/ui/Button"; - -export const EnterpriseEditionInfo = () => { - return ( -
    -
    -
    -

    - Enterprise Edition (Self-hosting) -

    - -

    - All features which are not available in the Community Edition belong to the Formbricks Enterprise - Edition. If you would like to use these features on a self-hosted instance, you need to purchase - an Enterprise license from us. -

    -
    - - -
    -
    -
    -
    - ); -}; diff --git a/apps/formbricks-com/components/shared/FeatureHighlight.tsx b/apps/formbricks-com/components/shared/FeatureHighlight.tsx deleted file mode 100644 index e79b61c5c1..0000000000 --- a/apps/formbricks-com/components/shared/FeatureHighlight.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import clsx from "clsx"; -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -interface Props { - featureTitle: string; - text: string; - img: React.ReactNode; - isImgLeft?: boolean; - cta?: string; - href?: string; - disabled?: boolean; -} - -export default function FeatureHighlights({ - featureTitle, - text, - img, - isImgLeft, - cta, - href, - disabled, -}: Props) { - const router = useRouter(); - - return ( -
    -
    -
    -
    -

    - {featureTitle} -

    -
    - {text} -
    -
    - {cta && href && ( - - )} -
    -
    - {img} -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/Fence.jsx b/apps/formbricks-com/components/shared/Fence.jsx deleted file mode 100644 index dab11c6b8e..0000000000 --- a/apps/formbricks-com/components/shared/Fence.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import { Fragment } from "react"; -import { Highlight } from "prism-react-renderer"; - -export function Fence({ children, language }) { - return ( - - {({ className, style, tokens, getTokenProps }) => ( -
    -          
    -            {tokens.map((line, lineIndex) => (
    -              
    -                {line
    -                  .filter((token) => !token.empty)
    -                  .map((token, tokenIndex) => (
    -                    
    -                  ))}
    -                {"\n"}
    -              
    -            ))}
    -          
    -        
    - )} -
    - ); -} diff --git a/apps/formbricks-com/components/shared/Footer.tsx b/apps/formbricks-com/components/shared/Footer.tsx deleted file mode 100644 index 0d383c95d8..0000000000 --- a/apps/formbricks-com/components/shared/Footer.tsx +++ /dev/null @@ -1,160 +0,0 @@ -import CCPALogo from "@/images/ccpa.svg"; -import GPDRLogo from "@/images/gdpr.svg"; -import Image from "next/image"; -import Link from "next/link"; -import { FaDiscord, FaGithub, FaXTwitter } from "react-icons/fa6"; - -import { FooterLogo } from "./Logo"; -import SourceForgeBadge from "./SourceForgeBadge"; - -const navigation = { - products: [ - { name: "Link Surveys", href: "/open-source-form-builder", status: true }, - { name: "Website Surveys", href: "/website-survey", status: true }, - { name: "In-app Surveys", href: "/in-app-survey", status: true }, - ], - comparisons: [ - { name: "vs. Google Forms", href: "/vs-google-forms", status: true }, - { name: "vs. Formspree", href: "/vs-formspree", status: true }, - { name: "vs. OhMyForm", href: "/vs-ohmyform", status: true }, - ], - footernav: [ - { name: "Community", href: "/community", status: true }, - { name: "Pricing", href: "/pricing", status: true }, - { name: "Blog", href: "/blog", status: true }, - { name: "Docs", href: "/docs", status: true }, - ], - legal: [ - { name: "Imprint", href: "/imprint", status: true }, - { name: "Privacy Policy", href: "/privacy", status: true }, - { name: "Terms", href: "/terms", status: true }, - { name: "GDPR FAQ", href: "/gdpr", status: true }, - { name: "GDPR Guide", href: "/gdpr-guide", status: true }, - { name: "Feedback Icon", href: "/feedback-icon", status: true }, - ], - bestPractices: [ - { name: "Interview Prompt", href: "/interview-prompt", status: true }, - { name: "PMF Survey", href: "/measure-product-market-fit", status: true }, - { name: "Onboarding Segments", href: "/onboarding-segmentation", status: true }, - { name: "Learn from Churn", href: "/learn-from-churn", status: true }, - { name: "Improve Trial CR", href: "/improve-trial-conversion", status: true }, - { name: "Docs Feedback", href: "/docs-feedback", status: true }, - { name: "Feature Chaser", href: "/feature-chaser", status: true }, - { name: "Feedback Box", href: "/feedback-box", status: true }, - ], - social: [ - { - name: "Twitter", - href: "https://twitter.com/formbricks", - icon: FaXTwitter, - }, - { - name: "GitHub", - href: "https://github.com/formbricks/formbricks", - icon: FaGithub, - }, - { - name: "Discord", - href: "https://formbricks.com/discord", - icon: FaDiscord, - }, - ], -}; -export default function Footer() { - const currentYear = new Date().getFullYear(); - - return ( -
    - -
    -
    - - Formbricks - - -

    Privacy-first Experience Management

    -
    -

    - Formbricks GmbH © {currentYear}. All rights reserved. -
    - Imprint | Privacy Policy |{" "} - Terms | OSS Friends -

    -
    -
    - {navigation.social.map((item) => ( - - {item.name} -
    -
    - - GDPR Logo - CCPA Logo -
    -
    -
    -
    -

    Formbricks

    - {navigation.footernav.map((item) => ( - - {item.name} - - ))} -
    -
    -

    Product

    - {navigation.products.map((item) => ( - - {item.name} - - ))} -

    Comparison

    - {navigation.comparisons.map((item) => ( - - {item.name} - - ))} -
    -
    -

    Best Practices

    - {navigation.bestPractices.map((item) => ( - - {item.name} - - ))} -
    -
    -

    Legal

    - {navigation.legal.map((item) => ( - - {item.name} - - ))} -
    -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/Header.tsx b/apps/formbricks-com/components/shared/Header.tsx deleted file mode 100644 index 4157569620..0000000000 --- a/apps/formbricks-com/components/shared/Header.tsx +++ /dev/null @@ -1,403 +0,0 @@ -import GitHubMarkWhite from "@/images/github-mark-white.svg"; -import GitHubMarkDark from "@/images/github-mark.svg"; -import { Popover, Transition } from "@headlessui/react"; -import clsx from "clsx"; -import { ChevronDownIcon, ChevronRightIcon, MenuIcon, XIcon } from "lucide-react"; -import { usePlausible } from "next-plausible"; -import Image from "next/image"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { Fragment, useEffect, useState } from "react"; - -import { Button } from "@formbricks/ui/Button"; -import { - BaseballIcon, - CancelSubscriptionIcon, - CodeBookIcon, - DogChaserIcon, - FeedbackIcon, - InterviewPromptIcon, - OnboardingIcon, - PMFIcon, -} from "@formbricks/ui/icons"; - -import { FooterLogo } from "./Logo"; - -function GitHubIcon(props: any) { - return ( - - ); -} - -const UnderstandUsers = [ - { - name: "Interview Prompt", - href: "/interview-prompt", - status: true, - icon: InterviewPromptIcon, - description: "Interview invites on auto-pilot", - }, - { - name: "Measure PMF", - href: "/measure-product-market-fit", - status: true, - icon: PMFIcon, - description: "Improve Product-Market Fit", - }, - { - name: "Onboarding Segments", - href: "/onboarding-segmentation", - status: true, - icon: OnboardingIcon, - description: "Get it right from the start", - }, -]; - -const IncreaseRevenue = [ - { - name: "Learn from Churn", - href: "/learn-from-churn", - status: true, - icon: CancelSubscriptionIcon, - description: "Churn is hard, but insightful", - }, - { - name: "Improve Trial CR", - href: "/improve-trial-conversion", - status: true, - icon: BaseballIcon, - description: "Take guessing out, hit it right", - }, -]; - -const BoostRetention = [ - { - name: "Feedback Box", - href: "/feedback-box", - status: true, - icon: FeedbackIcon, - description: "Always keep an ear open", - }, - { - name: "Docs Feedback", - href: "/docs-feedback", - status: true, - icon: CodeBookIcon, - description: "Clear docs, more adoption", - }, - { - name: "Feature Chaser", - href: "/feature-chaser", - status: true, - icon: DogChaserIcon, - description: "Follow up, improve", - }, -]; - -export default function Header() { - const [mobileSubOpen, setMobileSubOpen] = useState(false); - const plausible = usePlausible(); - const router = useRouter(); - const [stickyNav, setStickyNav] = useState(false); - - useEffect(() => { - const handleScroll = () => { - if (window.scrollY > 250) { - setStickyNav(true); - } else { - setStickyNav(false); - } - }; - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, []); - - const stickyNavClass = stickyNav - ? `bg-transparent dark:bg-slate-900/[0.8] shadow-md backdrop-blur-lg fixed top-0 z-30 w-full` - : "relative"; - return ( - - {/* -
    - We're live on Product Hunt - Show your support for Open Source 🚀 -
    -
    */} -
    -
    - - Formbricks - - -
    -
    - - Open menu - -
    - - - {({ open }) => ( - <> - - Best Practices - - - - -
    -
    -
    -

    - Understand Users -

    - {UnderstandUsers.map((brick) => ( - -
    -
    -
    -

    - {brick.name} -

    -

    {brick.description}

    -
    - - ))} -
    -
    -

    - Increase Revenue -

    - {IncreaseRevenue.map((brick) => ( - -
    -
    -
    -

    - {brick.name} -

    -

    {brick.description}

    -
    - - ))} -
    -
    -

    - Boost Retention -

    - {BoostRetention.map((brick) => ( - -
    -
    -
    -

    - {brick.name} -

    -

    {brick.description}

    -
    - - ))} -
    -
    -
    -
    -
    - - )} -
    - - Pricing - - - Community - - - Docs - - - Blog {/*

    1

    */} - -
    -
    - - -
    -
    - - - -
    -
    -
    -
    - -
    -
    - - Close menu - -
    -
    -
    -
    -
    -
    - {mobileSubOpen ? ( - - ) : ( - - )} - -
    - {mobileSubOpen && ( -
    - {UnderstandUsers.map((brick) => ( - - {brick.name} - - ))} - {IncreaseRevenue.map((brick) => ( - - {brick.name} - - ))} - {BoostRetention.map((brick) => ( - - {brick.name} - - ))} -
    -
    - )} - Community - Pricing - Docs - Blog - {/* Careers */} - - -
    -
    -
    -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/HeadingCentered.tsx b/apps/formbricks-com/components/shared/HeadingCentered.tsx deleted file mode 100644 index 1ce9a2a76f..0000000000 --- a/apps/formbricks-com/components/shared/HeadingCentered.tsx +++ /dev/null @@ -1,21 +0,0 @@ -interface Props { - teaser?: string; - heading: React.ReactNode; - subheading?: string; -} - -export default function HeadingCentered({ teaser, heading, subheading }: Props) { - return ( -
    -

    - {teaser} -

    -

    - {heading} -

    -

    - {subheading} -

    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/HeroAnimation.tsx b/apps/formbricks-com/components/shared/HeroAnimation.tsx deleted file mode 100644 index bfbd3d6c9e..0000000000 --- a/apps/formbricks-com/components/shared/HeroAnimation.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import type { LottiePlayer } from "lottie-web"; -import { useEffect, useRef, useState } from "react"; - -export default function HeroAnimation(props: any) { - const ref = useRef(null); - const [lottie, setLottie] = useState(null); - - useEffect(() => { - import("lottie-web").then((Lottie) => setLottie(Lottie.default)); - }, []); - - useEffect(() => { - if (lottie && ref.current) { - const animation = lottie.loadAnimation({ - container: ref.current, - renderer: "svg", - loop: true, - autoplay: true, - // path to your animation file, place it inside public folder - path: "/animations/xm-hero-v1.json", - }); - - return () => animation.destroy(); - } - }, [lottie]); - - return
    ; -} diff --git a/apps/formbricks-com/components/shared/HeroTitle.tsx b/apps/formbricks-com/components/shared/HeroTitle.tsx deleted file mode 100644 index 6006124b82..0000000000 --- a/apps/formbricks-com/components/shared/HeroTitle.tsx +++ /dev/null @@ -1,25 +0,0 @@ -interface Props { - headingPt1: string; - headingTeal?: string; - headingPt2?: string; - subheading?: string; - children?: React.ReactNode; -} - -export default function HeroTitle({ headingPt1, headingTeal, headingPt2, subheading, children }: Props) { - return ( -
    -

    - {headingPt1}{" "} - - {headingTeal} - {" "} - {headingPt2} -

    -

    - {subheading} -

    -
    {children}
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/Icon.jsx b/apps/formbricks-com/components/shared/Icon.jsx deleted file mode 100644 index acde8da647..0000000000 --- a/apps/formbricks-com/components/shared/Icon.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useId } from "react"; -import clsx from "clsx"; - -import { InstallationIcon } from "@/components/shared/icons/InstallationIcon"; -import { LightbulbIcon } from "@/components/shared/icons/LightbulbIcon"; -import { PluginsIcon } from "@/components/shared/icons/PluginsIcon"; -import { PresetsIcon } from "@/components/shared/icons/PresetsIcon"; -import { ThemingIcon } from "@/components/shared/icons/ThemingIcon"; -import { WarningIcon } from "@/components/shared/icons/WarningIcon"; - -const icons = { - installation: InstallationIcon, - presets: PresetsIcon, - plugins: PluginsIcon, - theming: ThemingIcon, - lightbulb: LightbulbIcon, - warning: WarningIcon, -}; - -const iconStyles = { - slate: "[--icon-foreground:theme(colors.slate.900)] [--icon-background:theme(colors.white)]", - amber: "[--icon-foreground:theme(colors.amber.900)] [--icon-background:theme(colors.amber.100)]", -}; - -export function Icon({ color = "slate", icon, className, ...props }) { - let id = useId(); - let IconComponent = icons[icon]; - - return ( - - ); -} - -const gradients = { - slate: [ - { stopColor: "#0EA5E9" }, - { stopColor: "#22D3EE", offset: ".527" }, - { stopColor: "#818CF8", offset: 1 }, - ], - amber: [ - { stopColor: "#FDE68A", offset: ".08" }, - { stopColor: "#F59E0B", offset: ".837" }, - ], -}; - -export function Gradient({ color = "slate", ...props }) { - return ( - - {gradients[color].map((stop, stopIndex) => ( - - ))} - - ); -} - -export function LightMode({ className, ...props }) { - return ; -} - -export function DarkMode({ className, ...props }) { - return ; -} diff --git a/apps/formbricks-com/components/shared/Layout.tsx b/apps/formbricks-com/components/shared/Layout.tsx deleted file mode 100644 index 5ae080c336..0000000000 --- a/apps/formbricks-com/components/shared/Layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import HeaderLight from "../salespage/HeaderLight"; -import Footer from "./Footer"; -import MetaInformation from "./MetaInformation"; - -interface LayoutProps { - children: React.ReactNode; - title: string; - description: string; -} - -export default function Layout({ title, description, children }: LayoutProps) { - return ( -
    - - - { -
    - {children} -
    - } -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/LayoutMdx.tsx b/apps/formbricks-com/components/shared/LayoutMdx.tsx deleted file mode 100644 index 83724df0d6..0000000000 --- a/apps/formbricks-com/components/shared/LayoutMdx.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import HeaderLight from "@/components/salespage/HeaderLight"; -import SlideInBanner from "@/components/shared/SlideInBanner"; -import { useEffect } from "react"; - -import Footer from "./Footer"; -import MetaInformation from "./MetaInformation"; -import { Prose } from "./Prose"; - -const useExternalLinks = (selector: string) => { - useEffect(() => { - const links = document.querySelectorAll(selector); - - links.forEach((link) => { - link.setAttribute("target", "_blank"); - link.setAttribute("rel", "noopener noreferrer"); - }); - - return () => { - links.forEach((link) => { - link.removeAttribute("target"); - link.removeAttribute("rel"); - }); - }; - }, [selector]); -}; - -interface Props { - meta: { - title: string; - description: string; - publishedTime: string; - authors: string[]; - section: string; - tags: string[]; - ogImage: string; - }; - children: JSX.Element; -} - -export default function LayoutMdx({ meta, children }: Props) { - useExternalLinks(".prose a"); - return ( -
    - - -
    -
    - {meta.title && ( -
    - {meta.title && ( -

    - {meta.title} -

    - )} -
    - )} - - {children} - -
    - -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/Logo.tsx b/apps/formbricks-com/components/shared/Logo.tsx deleted file mode 100644 index 8c80f4bac5..0000000000 --- a/apps/formbricks-com/components/shared/Logo.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import footerLogoDark from "@/images/logo/footerlogo-dark.svg"; -import footerLogo from "@/images/logo/footerlogo.svg"; -import logo from "@/images/logo/logo.svg"; -import logoDark from "@/images/logo/logo_dark.svg"; -import logomark from "@/images/logo/logomark.svg"; -import Image from "next/image"; - -export function Logomark(props: any) { - return Formbricks Open source Forms & Surveys Logomark; -} - -export function Logo(props: any) { - return ( -
    -
    - Formbricks Open source Forms & Surveys Logo -
    -
    - Formbricks Open source Forms & Surveys Logo -
    -
    - ); -} - -export function FooterLogo(props: any) { - return ( -
    -
    - Formbricks Open source Forms & Surveys Logo -
    -
    - Formbricks Open source Forms & Surveys Logo -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/MdxCTA.tsx b/apps/formbricks-com/components/shared/MdxCTA.tsx deleted file mode 100644 index 8764b47541..0000000000 --- a/apps/formbricks-com/components/shared/MdxCTA.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -export default function CTA() { - const router = useRouter(); - return ( - <> -
    -

    - It's free & open-source -

    -

    - Try Formbricks right now! -

    -
    -
    -

    Self-hosted

    -

    Run locally with docker-compose.

    - -
    -
    -

    Cloud

    -

    Use our free managed service.

    - -
    -
    -
    - - ); -} diff --git a/apps/formbricks-com/components/shared/MdxTryItCTA.tsx b/apps/formbricks-com/components/shared/MdxTryItCTA.tsx deleted file mode 100644 index 10d906ecde..0000000000 --- a/apps/formbricks-com/components/shared/MdxTryItCTA.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { CopyIcon } from "lucide-react"; -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -export default function HeadingCentered() { - const router = useRouter(); - return ( -
    -
    -

    - What are you waiting for? -

    -

    - Try it right now! -

    -

    - Dive right in or browse docs for examples. Questions? Join our Discord, we’re happy to help -

    - - -
    -
    -
    -

    npm install @formbricks/react

    - -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/MetaInformation.tsx b/apps/formbricks-com/components/shared/MetaInformation.tsx deleted file mode 100644 index 72caff62c8..0000000000 --- a/apps/formbricks-com/components/shared/MetaInformation.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import Head from "next/head"; -import { useRouter } from "next/router"; - -interface Props { - title: string; - description: string; - publishedTime?: string; - updatedTime?: string; - authors?: string[]; - section?: string; - tags?: string[]; - ogImage?: string; -} - -export default function MetaInformation({ - title, - description, - publishedTime, - updatedTime, - authors, - section, - tags, - ogImage, -}: Props) { - const router = useRouter(); - const pageTitle = `${title}`; - const BASE_URL = `formbricks.com`; - const canonicalLink = `https://${BASE_URL}${router.asPath}`; - return ( - - {pageTitle} - - - - - - - - - - - - - - - - - - {publishedTime && } - {updatedTime && } - {authors && } - {section && } - {tags && } - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/MobileNavigation.jsx b/apps/formbricks-com/components/shared/MobileNavigation.jsx deleted file mode 100644 index a5d8766116..0000000000 --- a/apps/formbricks-com/components/shared/MobileNavigation.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { useEffect, useState } from "react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { Dialog } from "@headlessui/react"; - -import { Logomark } from "@/components/shared/Logo"; -import { Navigation } from "@/components/shared/Navigation"; - -function MenuIcon(props) { - return ( - - ); -} - -function CloseIcon(props) { - return ( - - ); -} - -export function MobileNavigation({ navigation }) { - let router = useRouter(); - let [isOpen, setIsOpen] = useState(false); - - useEffect(() => { - if (!isOpen) return; - - function onRouteChange() { - setIsOpen(false); - } - - router.events.on("routeChangeComplete", onRouteChange); - router.events.on("routeChangeError", onRouteChange); - - return () => { - router.events.off("routeChangeComplete", onRouteChange); - router.events.off("routeChangeError", onRouteChange); - }; - }, [router, isOpen]); - - return ( - <> - - - -
    - - - - -
    - -
    -
    - - ); -} diff --git a/apps/formbricks-com/components/shared/Navigation.tsx b/apps/formbricks-com/components/shared/Navigation.tsx deleted file mode 100644 index cf71204be1..0000000000 --- a/apps/formbricks-com/components/shared/Navigation.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import clsx from "clsx"; -import Link from "next/link"; -import { useRouter } from "next/router"; - -interface NavigationProps { - navigation: { - title: string; - links: { - title: string; - href: string; - }[]; - }[]; - className: string; - preserveScroll: () => void; - linkRef: React.RefObject; -} - -export function Navigation({ navigation, className, preserveScroll, linkRef }: NavigationProps) { - let router = useRouter(); - - return ( - - ); -} diff --git a/apps/formbricks-com/components/shared/NewsletterSignup.tsx b/apps/formbricks-com/components/shared/NewsletterSignup.tsx deleted file mode 100644 index 8d0b90f517..0000000000 --- a/apps/formbricks-com/components/shared/NewsletterSignup.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import Friends from "@/images/newsletter-signup-gif.gif"; -import Image from "next/image"; - -import { Button } from "@formbricks/ui/Button"; - -export default function WaitlistForm() { - return ( -
    -

    Stay in the loop!

    - Get all the juicy details of our journey building Formbricks in public 👇 -
    -
    -
    -
    - -
    -
    - - -
    -
    - - -
    -
    - - -
    - -
    -
    - - Sign up to newsletter -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/OpenSourceInfo.tsx b/apps/formbricks-com/components/shared/OpenSourceInfo.tsx deleted file mode 100644 index 3b0edbd08c..0000000000 --- a/apps/formbricks-com/components/shared/OpenSourceInfo.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Button } from "@formbricks/ui/Button"; - -export const OpenSourceInfo = () => { - return ( -
    -
    -
    -

    - Open Source -

    - -

    - Formbricks is an open source project. You can self-host the Community Edition for free. We provide - multiple easy deployment options as per your customisation needs. We have documented the process - of self-hosting Formbricks on your own server using Docker & Bash Scripting. -

    -
    - - -
    -
    -
    -
    - ); -}; diff --git a/apps/formbricks-com/components/shared/PricingCalculator.tsx b/apps/formbricks-com/components/shared/PricingCalculator.tsx deleted file mode 100644 index cc4ca82ef2..0000000000 --- a/apps/formbricks-com/components/shared/PricingCalculator.tsx +++ /dev/null @@ -1,188 +0,0 @@ -import { Slider } from "@/components/shared/Slider"; -import { useState } from "react"; - -const LinkSurveySlider = ({ label, usersCount, price, onSliderChange }) => ( -
    -
    -
    - {label} -
    -
    - {Math.round(usersCount).toLocaleString()} Submissions -
    -
    - ${price.toFixed(2)} -
    -
    -
    - -
    - {[3, 4, 5, 6].map((mark) => ( - - {mark === 3 ? "1K" : mark === 4 ? "10K" : mark === 5 ? "100K" : "1M"} - - ))} -
    -
    -
    -); - -const InAppSlider = ({ label, usersCount, price, onSliderChange }) => ( -
    -
    -
    - {label} -
    -
    - {Math.round(usersCount).toLocaleString()} Submissions -
    -
    - ${price.toFixed(2)} -
    -
    -
    - -
    - {[3, 4, 5, 6].map((mark) => ( - - {mark === 3 ? "1K" : mark === 4 ? "10K" : mark === 5 ? "100K" : "1M"} - - ))} -
    -
    -
    -); - -const UserSegmentationSlider = ({ label, usersCount, price, onSliderChange }) => ( -
    -
    -
    - {label} -
    -
    - {Math.round(usersCount).toLocaleString()} Submissions -
    -
    - ${price.toFixed(2)} -
    -
    -
    - -
    - {[3, 4, 5, 6].map((mark) => ( - - {mark === 3 ? "1K" : mark === 4 ? "10K" : mark === 5 ? "100K" : "1M"} - - ))} -
    -
    -
    -); - -const Headers = () => ( -
    -

    Product

    -

    - Subtotal -

    -
    -); - -const MonthlyEstimate = ({ price }) => ( -
    - - Monthly estimate: - -
    - - ${price.toFixed(2)} - - - {" "} - / month - -
    -
    -); - -export const PricingCalculator = () => { - const [inProductSlider, setInProductSlider] = useState(Math.log10(1000)); - const [linkSlider, setLinkSlider] = useState(Math.log10(1000)); - - const transformToLog = (value) => Math.pow(10, value); - - const calculatePrice = (users) => { - if (users <= 5000) { - return 0; - } else { - return users * 0.005; - } - }; - - const usersCountForInProductSlider = transformToLog(inProductSlider); - const productSurveysPrice = calculatePrice(usersCountForInProductSlider); - - return ( -
    -

    - Pricing Calculator -

    - -
    -
    - - -
    - - setLinkSlider(value[0])} - /> - -
    - - setLinkSlider(value[0])} - /> - -
    - - setInProductSlider(value[0])} - /> - - -
    -
    -
    - ); -}; diff --git a/apps/formbricks-com/components/shared/PricingGetStarted.tsx b/apps/formbricks-com/components/shared/PricingGetStarted.tsx deleted file mode 100644 index 2a0aa307b8..0000000000 --- a/apps/formbricks-com/components/shared/PricingGetStarted.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Button } from "@formbricks/ui/Button"; - -export const GetStartedWithPricing = ({ showDetailed }: { showDetailed: boolean }) => { - return ( - <> -
    -
    -
    -

    - Community Edition - Free -

    - - {showDetailed && ( -

    - Covers 95% of all features. Great for startups and to get started. Free forever. -

    - )} - - -
    -
    -

    - {" "} - Enterprise Edition{" "} - - Freemium - -

    - {showDetailed && ( -

    - Includes all features with unlimited usage. Free credits every month to get started. -

    - )} - - -
    -
    -
    - - ); -}; diff --git a/apps/formbricks-com/components/shared/PricingTable.tsx b/apps/formbricks-com/components/shared/PricingTable.tsx deleted file mode 100644 index ef377241cf..0000000000 --- a/apps/formbricks-com/components/shared/PricingTable.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { CheckIcon, XIcon } from "lucide-react"; - -import { Button } from "@formbricks/ui/Button"; - -export const PricingTable = ({ leadRow, pricing, endRow }) => { - return ( -
    -
    -
    -
    - {leadRow.title} - {/* - {leadRow.comparison} - */} -
    -
    - {leadRow.free} -
    - -
    - {leadRow.paid} -
    -
    -
    - -
    - {pricing.map((feature) => ( -
    -
    - {feature.name} - {feature.addOnText && ( - - Addon - - )} - {feature.comingSoon && ( - - coming soon - - )} -
    -
    - {typeof feature.free === "string" ? ( - - {feature.free} - - ) : feature.free ? ( - - ) : ( - - )} -
    -
    - {typeof feature.free === "string" ? ( - - {feature.paid} - - ) : feature.paid ? ( - - ) : ( - - )} -
    -
    - ))} -
    - -
    -
    -
    -
    - {endRow.free} - -
    - -
    - {endRow.paid} - -
    -
    -
    -
    - ); -}; diff --git a/apps/formbricks-com/components/shared/Prose.jsx b/apps/formbricks-com/components/shared/Prose.jsx deleted file mode 100644 index 6a816f2419..0000000000 --- a/apps/formbricks-com/components/shared/Prose.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import clsx from "clsx"; - -export function Prose({ as: Component = "div", className, ...props }) { - return ( - - ); -} diff --git a/apps/formbricks-com/components/shared/SlideInBanner.tsx b/apps/formbricks-com/components/shared/SlideInBanner.tsx deleted file mode 100644 index 6124fd5bea..0000000000 --- a/apps/formbricks-com/components/shared/SlideInBanner.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import LFGLuigi from "@/images/blog/lfg-luigi-200px.webp"; -import { XIcon } from "lucide-react"; -import Image from "next/image"; -import React, { useEffect, useState } from "react"; - -import { Button } from "@formbricks/ui/Button"; - -interface Props { - delay?: number; - scrollPercentage?: number; - UTMSource: string; -} - -const SlideInBanner: React.FC = ({ delay = 5000, scrollPercentage = 10, UTMSource }) => { - const [showBanner, setShowBanner] = useState(false); - const [isDismissed, setIsDismissed] = useState(false); - const [isExiting, setIsExiting] = useState(false); - - useEffect(() => { - if (isDismissed) return; - - const timer = setTimeout(() => { - setShowBanner(true); - }, delay); - - const handleScroll = () => { - let currentScrollPercentage = - (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100; - if (currentScrollPercentage > scrollPercentage) { - setShowBanner(true); - } - }; - - window.addEventListener("scroll", handleScroll); - - return () => { - clearTimeout(timer); - window.removeEventListener("scroll", handleScroll); - }; - }, [delay, scrollPercentage, isDismissed]); - - if (isDismissed) return null; - - return ( -
    -
    - LFG Luigi -
    -
    -

    - Did you know? Formbricks is the only open source Experience Management solution: free & - privacy-first! -

    - - -
    -
    - ); -}; - -export default SlideInBanner; diff --git a/apps/formbricks-com/components/shared/Slider.tsx b/apps/formbricks-com/components/shared/Slider.tsx deleted file mode 100644 index 4879826aba..0000000000 --- a/apps/formbricks-com/components/shared/Slider.tsx +++ /dev/null @@ -1,24 +0,0 @@ -"use client"; - -import * as SliderPrimitive from "@radix-ui/react-slider"; -import * as React from "react"; - -import { cn } from "@formbricks/lib/cn"; - -const Slider = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - - - -)); -Slider.displayName = SliderPrimitive.Root.displayName; - -export { Slider }; diff --git a/apps/formbricks-com/components/shared/SourceForgeBadge.tsx b/apps/formbricks-com/components/shared/SourceForgeBadge.tsx deleted file mode 100644 index de00fbe66a..0000000000 --- a/apps/formbricks-com/components/shared/SourceForgeBadge.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useEffect } from "react"; - -const SourceForgeBadge: React.FC = () => { - useEffect(() => { - const script = document.createElement("script"); - script.async = true; - script.src = "https://b.sf-syn.com/badge_js?sf_id=3747607&variant_id=sf"; - const firstScript = document.getElementsByTagName("script")[0]; - firstScript.parentNode?.insertBefore(script, firstScript); - - return () => { - // Clean up the script when the component unmounts - firstScript.parentNode?.removeChild(script); - }; - }, []); - - return ( - - ); -}; - -export default SourceForgeBadge; diff --git a/apps/formbricks-com/components/shared/ThemeSelector.jsx b/apps/formbricks-com/components/shared/ThemeSelector.jsx deleted file mode 100644 index 40af61d4a0..0000000000 --- a/apps/formbricks-com/components/shared/ThemeSelector.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useEffect, useState } from "react"; - -function LightIcon(props) { - return ( - - ); -} - -function DarkIcon(props) { - return ( - - ); -} - -export function ThemeSelector(props) { - // Initialize the state with a default value - const [isDarkMode, setIsDarkMode] = useState(true); - - // Use a useEffect to run code on the client side - useEffect(() => { - // Check the system theme on the client side - const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches; - - // Update the state based on the system theme - setIsDarkMode(systemTheme); - - // Set the HTML attribute based on the initial state - document.documentElement.setAttribute("data-theme", systemTheme ? "dark" : "light"); - }, []); - - const toggleTheme = () => { - setIsDarkMode(!isDarkMode); - document.documentElement.setAttribute("data-theme", isDarkMode ? "light" : "dark"); - }; - - return ( -
    - {isDarkMode ? ( - - ) : ( - - )} -
    - ); -} diff --git a/apps/formbricks-com/components/shared/TryItCTA.tsx b/apps/formbricks-com/components/shared/TryItCTA.tsx deleted file mode 100644 index fa3cbbec3f..0000000000 --- a/apps/formbricks-com/components/shared/TryItCTA.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { CopyIcon } from "lucide-react"; -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -export default function HeadingCentered() { - const router = useRouter(); - return ( -
    -
    -

    - What are you waiting for? -

    -

    - Try it right now! -

    -

    - Dive right in or browse docs for examples. -
    - Questions? Join our Discord, we’re happy to help! -

    - - -
    -
    -
    -

    npm install @formbricks/react

    - -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/UseCaseCTA.tsx b/apps/formbricks-com/components/shared/UseCaseCTA.tsx deleted file mode 100644 index 09c9d00280..0000000000 --- a/apps/formbricks-com/components/shared/UseCaseCTA.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useRouter } from "next/router"; - -import { Button } from "@formbricks/ui/Button"; - -interface UseCaseCTAProps { - href: string; -} - -export default function UseCaseHeader({ href }: UseCaseCTAProps) { - /* const plausible = usePlausible(); */ - const router = useRouter(); - return ( -
    - -
    - -

    It's free

    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/UseCaseHeader.tsx b/apps/formbricks-com/components/shared/UseCaseHeader.tsx deleted file mode 100644 index 8fe2c14c2e..0000000000 --- a/apps/formbricks-com/components/shared/UseCaseHeader.tsx +++ /dev/null @@ -1,26 +0,0 @@ -interface UseCaseHeaderProps { - title: string; - - difficulty: string; - setupMinutes: string; -} - -export default function UseCaseHeader({ title, difficulty, setupMinutes }: UseCaseHeaderProps) { - return ( -
    -
    -

    - {title} -

    -
    -
    - {difficulty} -
    -
    - {setupMinutes} minutes -
    -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/WhyFormbricks.tsx b/apps/formbricks-com/components/shared/WhyFormbricks.tsx deleted file mode 100644 index a1a03dc92c..0000000000 --- a/apps/formbricks-com/components/shared/WhyFormbricks.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { BlocksIcon, BoxIcon, LockIcon, SwatchBookIcon, TerminalIcon, UsersIcon } from "lucide-react"; - -const features = [ - { - name: "Futureproof", - description: "Form needs change. With Formbricks you’ll avoid island solutions right from the start.", - icon: BoxIcon, - }, - { - name: "Privacy by design", - description: "Self-host the entire product and fly through privacy compliance reviews.", - icon: LockIcon, - }, - { - name: "Community driven", - description: "We're building for you. If you need something specific, we’re happy to build it!", - icon: UsersIcon, - }, - { - name: "Great DX", - description: "We love a solid developer experience. We felt your pain and do our best to avoid it.", - icon: TerminalIcon, - }, - { - name: "Customizable", - description: "We have to build opinionated. If it doesn't suit your need, just change it up.", - icon: SwatchBookIcon, - }, - { - name: "Extendable", - description: "Even though we try, we cannot build every single integration. With Formbricks, you can.", - icon: BlocksIcon, - }, -]; - -export default function FeatureTable({}) { - return ( -
    -
    -

    - Why Formbricks? -

    -

    - The only complete open source option. -

    -

    - We experienced how form needs develop as companies grow. We could'nt find a solution which ticked - all of the boxes. Now we're building it. -

    -
    - {features.map((feature) => ( -
    -
    - - -
    -
    -

    {feature.name}

    -

    - {feature.description} -

    -
    -
    - ))} -
    -
    -
    - ); -} diff --git a/apps/formbricks-com/components/shared/icons/InstallationIcon.jsx b/apps/formbricks-com/components/shared/icons/InstallationIcon.jsx deleted file mode 100644 index b36601e0d3..0000000000 --- a/apps/formbricks-com/components/shared/icons/InstallationIcon.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { DarkMode, Gradient, LightMode } from "@/components/shared/Icon"; - -export function InstallationIcon({ id, color }) { - return ( - <> - - - - - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/icons/LightbulbIcon.jsx b/apps/formbricks-com/components/shared/icons/LightbulbIcon.jsx deleted file mode 100644 index 1f6ab70d23..0000000000 --- a/apps/formbricks-com/components/shared/icons/LightbulbIcon.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import { DarkMode, Gradient, LightMode } from "@/components/shared/Icon"; - -export function LightbulbIcon({ id, color }) { - return ( - <> - - - - - - - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/icons/PluginsIcon.jsx b/apps/formbricks-com/components/shared/icons/PluginsIcon.jsx deleted file mode 100644 index ae8f05e78c..0000000000 --- a/apps/formbricks-com/components/shared/icons/PluginsIcon.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import { DarkMode, Gradient, LightMode } from "@/components/shared/Icon"; - -export function PluginsIcon({ id, color }) { - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/icons/PresetsIcon.jsx b/apps/formbricks-com/components/shared/icons/PresetsIcon.jsx deleted file mode 100644 index 2464ddc913..0000000000 --- a/apps/formbricks-com/components/shared/icons/PresetsIcon.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import { DarkMode, Gradient, LightMode } from "@/components/shared/Icon"; - -export function PresetsIcon({ id, color }) { - return ( - <> - - - - - - - - - - - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/icons/ThemingIcon.jsx b/apps/formbricks-com/components/shared/icons/ThemingIcon.jsx deleted file mode 100644 index 476c9f022b..0000000000 --- a/apps/formbricks-com/components/shared/icons/ThemingIcon.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { DarkMode, Gradient, LightMode } from "@/components/shared/Icon"; - -export function ThemingIcon({ id, color }) { - return ( - <> - - - - - - - - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/icons/WarningIcon.jsx b/apps/formbricks-com/components/shared/icons/WarningIcon.jsx deleted file mode 100644 index aa5108664a..0000000000 --- a/apps/formbricks-com/components/shared/icons/WarningIcon.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { DarkMode, Gradient, LightMode } from "@/components/shared/Icon"; - -export function WarningIcon({ id, color }) { - return ( - <> - - - - - - - - - - - - - - - ); -} diff --git a/apps/formbricks-com/components/shared/icons/XCircleIcon.jsx b/apps/formbricks-com/components/shared/icons/XCircleIcon.jsx deleted file mode 100644 index d1573c7733..0000000000 --- a/apps/formbricks-com/components/shared/icons/XCircleIcon.jsx +++ /dev/null @@ -1,13 +0,0 @@ - - -; diff --git a/apps/formbricks-com/components/shared/seo/SeoFaq.tsx b/apps/formbricks-com/components/shared/seo/SeoFaq.tsx deleted file mode 100644 index 2cfb78d462..0000000000 --- a/apps/formbricks-com/components/shared/seo/SeoFaq.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import Script from "next/script"; -import { FAQPage, WithContext } from "schema-dts"; - -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@formbricks/ui/Accordion"; - -interface Answer { - "@type": "Answer"; - text: string; -} - -interface Question { - "@type": "Question"; - name: string; - acceptedAnswer: Answer; -} - -interface FAQ { - question: string; - answer: string; -} - -interface FAQSchemaProps { - faqs: FAQ[]; - headline: string; - description: string; - datePublished: string; - dateModified: string; -} - -const SeoFaq: React.FC = ({ faqs, headline, description, datePublished, dateModified }) => { - const FAQMainEntity: Question[] = faqs.map((faq) => ({ - "@type": "Question", - name: faq.question, - acceptedAnswer: { - "@type": "Answer", - text: faq.answer, - }, - })); - - const FAQjsonld: WithContext = { - "@context": "https://schema.org", - "@type": "FAQPage", - name: `Frequently Asked Questions around ${headline}`, - mainEntity: FAQMainEntity, - headline, - description, - author: { - "@type": "Person", - name: "Johannes Dancker", - url: "https://formbricks.com", - }, - image: "", - datePublished, - dateModified, - }; - - return ( - <> -