Merge branch 'main' of https://github.com/formbricks/formbricks into shubham/for-1089-formulate-faqs-and-create-page

This commit is contained in:
ShubhamPalriwala
2023-09-28 15:17:15 +05:30
171 changed files with 4004 additions and 1854 deletions

View File

@@ -57,7 +57,7 @@ ports:
onOpen: ignore
- port: 8025
visibility: public
onOpen: ignore
onOpen: open-browser
github:
prebuilds:

View File

@@ -63,15 +63,53 @@ Formbricks helps you apply best practices from data-driven work and experience m
## 🚀 Getting started
We've got several options depending on your need to help you quickly get started with Formbricks
### ☁️ Cloud Version
Formbricks has a hosted cloud offering with a generous free plan to get you up and running as quickly as possible. To get started, please visit [formbricks.com](https://formbricks.com)
### 🐳 Self-hosted version
Formbricks is available Open-Source under AGPLv3 license. You can host Formbricks on your own servers using Docker without a subscription. To get started with self-hosting, take a look at our [self-hosting docs](https://formbricks.com/docs/self-hosting/deployment).
Formbricks is available Open-Source under AGPLv3 license. You can host Formbricks on your own servers using Docker without a subscription.
(In the future we may develop additional features that aren't in the free Open-Source version)
(In the future we may develop additional features that aren't in the free Open-Source version).
If you opt for self-hosting Formbricks, here are a few options to consider:
#### Docker
To get started with self-hosting with Docker, take a look at our [self-hosting docs](https://formbricks.com/docs/self-hosting/deployment).
#### Community managed One Click Hosting
##### Railway
You can deploy Formbricks on [Railway](https://railway.app) using the button below.
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new/template/PPDzCd)
### 👨‍💻 Development
#### Prerequisites
Here is what you need to be able to run Formbricks
- Node.js (Version: >=18.x)
- [Pnpm](https://pnpm.io/)
- [Docker](https://www.docker.com/) - to run PostgreSQL and MailHog
#### Local Setup
To get started locally, we've got a [guide to help you](https://formbricks.com/docs/contributing/setup).
#### Gitpod Setup
1. Click the button below to open this project in Gitpod.
2. This will open a fully configured workspace in your browser with all the necessary dependencies already installed.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/formbricks/formbricks)
## ✍️ Contribution

View File

@@ -13,7 +13,7 @@
"dependencies": {
"@formbricks/js": "workspace:*",
"@heroicons/react": "^2.0.18",
"next": "13.4.19",
"next": "13.5.3",
"react": "18.2.0",
"react-dom": "18.2.0"
},

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Code Actions",
title: "Implementing Code Actions in Formbricks | Real-time User Action Tracking",
description:
"Integrate code actions in Formbricks using formbricks.track() to trigger surveys based on user actions, like button clicks, for precise insights. All open-source.",
"Dive into the world of Formbricks' code actions. Learn how to seamlessly integrate formbricks.track() method into your codebase, enabling real-time tracking of user actions like button clicks, visiting a specfic URL. Up your survey game with precise and exact triggers.",
};
#### Actions

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "No-Code Actions",
title: "Implementing No-Code Actions in Formbricks | Real-time User Action Tracking",
description:
"Utilize Formbricks' No-Code Actions like Page URL, innerText, and CSS Selector for easy survey triggers and enhanced user insights.",
"Discover the power of Formbricks' No-Code Actions. Easily set up triggers based on Page URL, innerText, and CSS Selectors without touching a line of code. Inccrease user engagement and get insights at precise moments in the user journey.",
};
#### Actions

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "What are actions and why are they useful?",
title: "Using Actions in Formbricks | Fine-tuning User Moments",
description:
"Actions in Formbricks enable targeted survey displays during specific user journey moments. Enhance user segmentation by tracking actions for granular surveying.",
"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.",
};
#### Actions

View File

@@ -4,9 +4,9 @@ import AddApiKey from "./add-api-key.webp";
import ApiKeySecret from "./api-key-secret.webp";
export const meta = {
title: "API Key Setup",
title: "Formbricks API Key: Setup and Testing",
description:
"Generate, store, and delete personal API keys for secure Formbricks access. Ensure safekeeping to prevent unauthorized account control.",
"This guide provides step-by-step instructions to generate, store, and delete API keys, ensuring safe and authenticated access to your Formbricks account.",
};
#### API

View File

@@ -1,9 +1,9 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Responses API",
title: "Formbricks Public Client API Guide: Manage Survey Displays & Responses",
description:
"Explore the Formbricks Public Client API for client-side tasks and integration into your website.",
"Dive deep into Formbricks' Public Client API designed for customisation. This comprehensive guide provides detailed instructions on how to mark surveys as displayed as well as responded for individual persons, ensuring seamless client-side interactions without compromising data security.",
};
#### Management API

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "API Overview",
title: "Formbricks API Overview: Public Client & Management API Breakdown",
description:
"Explore Formbricks' APIs: Public Client API for client-side tasks, and Management API for account management with secure API Key authentication.",
"Get a detailed understanding of Formbricks' dual API offerings: the unauthenticated Public Client API optimized for client-side tasks and the secured Management API for advanced account operations. Choose the perfect fit for your integration needs and ensure robust data handling",
};
#### API

View File

@@ -1,9 +1,9 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Responses API",
title: "Formbricks People API: Fetch or Create Person Overview",
description:
"Explore the Formbricks Public Client API for client-side tasks and integration into your website.",
"Dive into Formbricks' People API within the Public Client API suite, designed to work without authentication requirements. Seamlessly fetch or create a person by their userId and environmentId, optimizing client-side interactions while maintaining data privacy.",
};
#### Management API

View File

@@ -1,9 +1,9 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Responses API",
title: "Formbricks Responses API Documentation - Manage Your Survey Data Seamlessly",
description:
"Explore the Formbricks Public Client API for client-side tasks and integration into your website.",
"Unlock the full potential of Formbricks' Responses API. From fetching to updating survey responses, our comprehensive guide helps you integrate and manage survey data efficiently without compromising security. Ideal for client-side interactions.",
};
#### Management API
@@ -14,7 +14,7 @@ The Public Client API is designed for the JavaScript SDK and does not require au
---
## List all responses {{ tag: 'GET', label: '/api/v1/responses' }}
## List all responses {{ tag: 'GET', label: '/api/v1/management/responses' }}
<Row>
<Col>
@@ -39,11 +39,11 @@ The Public Client API is designed for the JavaScript SDK and does not require au
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/api/v1/responses">
<CodeGroup title="Request" tag="GET" label="/api/v1/management/responses">
```bash {{ title: 'cURL' }}
curl --location \
'https://app.formbricks.com/api/v1/responses' \
'https://app.formbricks.com/api/v1/management/responses' \
--header \
'x-api-key: <your-api-key>'
```

View File

@@ -1,9 +1,9 @@
import { Fence } from "@/components/shared/Fence";
export const meta = {
title: "Surveys API",
title: "Formbricks Surveys API Documentation - How to Retrieve All Surveys",
description:
"Explore the Formbricks Public Client API for client-side tasks and integration into your website.",
"Explore the comprehensive guide to the Formbricks Surveys API. Learn how to effectively retrieve all the surveys in your environment with the necessary headers and API key setup. Includes sample request and response formats.",
};
#### Management API
@@ -14,7 +14,7 @@ The Survey API currently has one endpoint that allows you to get all the surveys
---
## List all surveys {{ tag: 'GET', label: '/api/v1/surveys' }}
## List all surveys {{ tag: 'GET', label: '/api/v1/management/surveys' }}
<Row>
<Col>
@@ -32,11 +32,11 @@ The Survey API currently has one endpoint that allows you to get all the surveys
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/api/v1/surveys">
<CodeGroup title="Request" tag="GET" label="/api/v1/management/surveys">
```bash {{ title: 'cURL' }}
curl --location \
'https://app.formbricks.com/api/v1/surveys' \
'https://app.formbricks.com/api/v1/management/surveys' \
--header \
'x-api-key: <your-api-key>'
```

View File

@@ -1,6 +1,6 @@
export const meta = {
title: "Webhook API Overview",
description: "Learn how to use the Formbricks Webhook API.",
title: "Formbricks Webhook API Documentation - List, Retrieve, Create, and Delete Webhooks",
description: "Explore the comprehensive guide to the Formbricks Webhooks API. This is all you need to interact and play with the Formbricks Webhooks and integrate them into any third party app of your choice",
};
#### Webhook API

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Setting attributes with code",
title: "Guide for Setting Custom Attributes | Formbricks Documentation",
description:
"Set attributes in code using setAttribute function. Enhance user segmentation, target surveys effectively, and gather valuable insights for better decisions. All open-source.",
"Learn how to set attributes in code using setAttribute function. Enhance user segmentation, target surveys effectively, and gather valuable insights for better decisions. Easily send user-specific details for better survey segmentation and gain deeper insights.",
};
#### Attributes

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Identifying Users",
title: "User Identification in Formbricks | Enhancing Survey Feedback",
description:
"Identify users with Formbricks by setting User ID, email, and custom attributes. Enhance survey targeting and recontacting while maintaining user privacy.",
"A comprehensive guide on identifying users in Formbricks without compromising privacy. Learn how to set User ID, email, and custom attributes to optimize survey targeting, recontact users, and control survey intervals, all while respecting user anonymity.",
};
#### Attributes

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "What are attributes and why are they useful?",
title: "Understanding User Attributes in Formbricks Surveys",
description:
"How to use attributes for user segmentation, enhancing survey targeting & results. Improve feedback quality and make data-driven decisions.",
"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.",
};
#### Attributes

View File

@@ -11,8 +11,8 @@ import PublishSurvey from "./publish-survey.webp";
import SelectAction from "./select-action.webp";
export const meta = {
title: "Learn from Churn",
description: "To know how to decrease churn, you have to understand it. Use a micro-survey.",
title: "Mastering Churn Surveys with Formbricks | Essential Tips & Steps",
description: "Learn how to effectively utilize Formbricks' Churn Surveys to gain deeper insights into user departures. Dive into a step-by-step guide to craft, trigger, and optimize your churn surveys, ensuring you capture invaluable feedback at critical junctures",
};
#### Best Practices

View File

@@ -11,8 +11,8 @@ import WhenToAsk from "./when-to-ask.webp";
import CopyIds from "./copy-ids.webp";
export const meta = {
title: "Docs Feedback",
description: "Docs Feedback allows you to measure how clear your documentation is.",
title: "Integrate Docs Feedback in Your Website: A Step-by-Step Guide on getting feedback on your Documentation with Formbricks",
description: "Learn the step-by-step process to effectively measure the clarity of your documentation using Formbricks Cloud. Dive into best practices, setting up cloud environments, integrating feedback widgets on your frontend, and connecting to the Formbricks API for a seamless user experience.",
};
#### Best Practices

View File

@@ -10,8 +10,8 @@ import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
title: "Feature Chaser",
description: "Follow up with users who used a specific feature. Gather feedback and improve your product.",
title: "Setting Up Feature Chaser Surveys with Formbricks: A Comprehensive Guide",
description: "Learn how to harness the power of Formbricks to gather targeted user feedback on specific features. Dive deep into creating, triggering, and publishing the Feature Chaser survey to enhance your product with actionable insights for specific users.",
};
#### Best Practices

View File

@@ -12,8 +12,8 @@ import SelectAction from "./select-feedback-button-action.webp";
import RecontactOptions from "./set-recontact-options.webp";
export const meta = {
title: "Feedback Box",
description: "The Feedback Box gives your users a direct channel to share their feedback and feel heard.",
title: "Implementing the Feedback Box with Formbricks: A Step-by-Step Tutorial",
description: "Unlock user insights effortlessly! Discover how to set up the Feedback Box in your app using Formbricks, allowing your users to provide real-time feedback. Follow our comprehensive guide to enhance user experience and respond rapidly to feedback",
};
#### Best Practices

View File

@@ -10,8 +10,8 @@ import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
title: "Improve Trial Conversion",
description: "Understand how to improve the trial conversions to get more paying customers.",
title: "Boost Your Trial Conversion Rates with Formbricks: Comprehensive Guide",
description: "Unlock the secret to converting more trial users into paying customers using Formbricks. Understand insights behind trial cancellations and tailor your offering to fit user needs. Dive into our step-by-step tutorial and improve your conversion strategy today",
};
#### Best Practices

View File

@@ -13,8 +13,8 @@ import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
title: "In-app Interview Prompt",
description: "Invite only power users to schedule an interview with your product team.",
title: "Maximize User Interview Participation with In-app Interview Prompts",
description: "Engage with your power users seamlessly using Formbricks' In-app Interview Prompt. Ditch traditional email invites and experience way more more respondents. Dive into our comprehensive guide on setting up auto-scheduled interviews today and enhance your user understanding",
};
#### Best Practices

View File

@@ -10,8 +10,8 @@ import RecontactOptions from "./recontact-options.webp";
import SelectAction from "./select-action.webp";
export const meta = {
title: "Product-Market Fit Survey",
description: "The Product-Market Fit survey helps you measure, well, Product-Market Fit (PMF).",
title: "How to Set Up a Product-Market Fit Survey Using Formbricks - Step-by-Step Guide",
description: "Learn to leverage Formbricks to create and implement a Product-Market Fit survey in your web app. Follow our detailed step-by-step guide to measure and understand your PMF effectively. Ensure high data quality, efficient triggers, and actionable insights.",
};
#### Best Practices

View File

@@ -3,8 +3,8 @@ import Image from "next/image";
import DemoApp from "./demoapp.webp";
export const meta = {
title: "Demo App",
description: "To test in-app surveys, trigger actions and set attributes, you can use the Demo App.",
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

View File

@@ -1,6 +1,6 @@
export const meta = {
title: "Contribution Guide",
description: "How to contribute to Formbricks",
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

View File

@@ -1,6 +1,6 @@
export const meta = {
title: "Setup Dev Environment",
description: "Setup a development environment for Formbricks.",
title: "Formbricks Development Setup: Complete Guide to Local Environment Configuration for Dev",
description: "Step-by-step guide to setting up your local development environment for Formbricks. Includes installing essential tools like Node.JS, pnpm, and Docker, and accessing the entire Formbricks stack including the Demo app and the main website",
};
#### Contributing

View File

@@ -5,9 +5,9 @@ import UncaughtPromise from "./uncaught-promise.webp";
import Logout from "./logout.webp";
export const meta = {
title: "Troubleshooting",
title: "Formbricks Troubleshooting Guide: How to Solve & Debug Common Issues",
description:
"Formbricks is a complex application in constant development. Sometimes, things don't go as planned. Here are some tips to help you troubleshoot.",
"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

View File

@@ -6,8 +6,8 @@ import WidgetConnected from "./widget-connected.webp";
import ReactApp from "./react-in-app-survey-app-popup-form.webp";
export const metadata = {
title: "Framework Guides",
description: "Explore all the possible ways you can integrate Formbricks into your application.",
title: "Integrate Formbricks: Comprehensive Framework Guide & Integration Tutorial",
description: "Master the integration of Formbricks into your application with our detailed guides. From HTML to ReactJS, NextJS, and VueJS, get step-by-step instructions and ensure seamless setup.",
};
# Framework Guides

View File

@@ -14,8 +14,8 @@ import I11 from "./11-survey-logs-in-app-survey-popup.webp";
import ReactApp from "../framework-guides/react-in-app-survey-app-popup-form.webp";
export const meta = {
title: "Collect in app survey responses in 10 minutes",
description: "Get your first in app survey response in 10 minutes.",
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,5 +1,12 @@
import { Fence } from "@/components/shared/Fence";
import { Callout } from "@/components/shared/Callout";
import IntegrationTab from "./integrations-tab.webp";
import ConnectWithGoogle from "./connect-with-google.webp";
import GoogleConnected from "./google-connected.webp";
import LinkSurveyWithSheet from "./link-survey-with-sheet.webp";
import LinkWithQuestions from "./link-with-questions.webp";
import ListLinkedSurveys from "./list-linked-surveys.webp";
import DeleteConnection from "./delete-connection.webp";
import Image from "next/image";
export const meta = {
@@ -18,12 +25,144 @@ The Google Sheets integration allows you to automatically send responses to a Go
self-hosted version of Formbricks.
</Note>
## Formbricks Cloud
1. Go to the Integrations tab in your [Formbricks Cloud dashboard](https://app.formbricks.com/) and click on the "Connect" button under Google Sheets integration.
<Image
src={IntegrationTab}
alt="Formbricks Integrations Tab"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. Now click on the "Connect with Google" button to authenticate yourself with Google.
<Image
src={ConnectWithGoogle}
alt="Connect Formbricks with your Google"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. You will now be taken to the Google OAuth page where you can select the Google account you want to use for the integration.
4. Once you have selected the account and completed the authentication process, you will be taken back to Formbricks Cloud and see the connected status as below:
<Image
src={GoogleConnected}
alt="Formbricks is now connected with Google"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<Note>
Before the next step, make sure that you have a Formbricks Survey with at least one question and a Google Sheet in the Google account you integrated.
</Note>
6. Now click on the "Link New Sheet" button to link a new Google Sheet with Formbricks and a modal will open up.
<Image
src={LinkSurveyWithSheet}
alt="Link Formbricks with a Google Sheet"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
7. Select the Google Sheet you want to link with Formbricks and the Survey. On doing so, you will be asked with what questions' responses you want to feed in the Google Sheet. Select the questions and click on the "Link Sheet" button.
<Image
src={LinkWithQuestions}
alt="Select question to link with Google Sheet"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
8. On submitting, the modal will close and you will see the linked Google Sheet in the list of linked Google Sheets.
<Image
src={ListLinkedSurveys}
alt="List of linked Google Sheets"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
Congratulations! You have successfully linked a Google Sheet with Formbricks. Now whenever a response is submitted for the linked Survey, it will be automatically added to the linked Google Sheet.
## Setup in self-hosted Formbricks
Enabling the Google Sheets Integration in a self-hosted environment isn't easy and requires a setup using Google Cloud and changing the environment variables of your Formbricks instance.
The environment variables you need to set are:
<Note>This process is really complicated and we recommend using Formbricks Cloud for this feature.</Note>
We will first create a Google Cloud Project and then enable the Google Sheets API for it. Then we will create an OAuth Client ID and Client Secret for our Formbricks instance and set them as environment variables.
1. Go to the [Google Cloud Console](https://console.cloud.google.com/) and **create a new project**.
2. Now select the project you just created and go to the "**APIs & Services**" section.
3. Click on the "**Enable APIs and Services**" button and search for "**Google Sheets API**" and enable it.
4. Now go to the "**OAuth Consent screen**" section and select the **"External" User Type** if you want any Google User to be able to use the integration or select "Internal" if you want only the users of your Google Workspace to be able to use the integration.
5. Now provide it the details such as
- App name (Will **show up in the OAuth modal** when the user is asked to authenticate with Google)
- User support email (ideally should be **your email** for any support queries by the Users or Google)
- Developer contact information (ideally should be **your email** for any **support queries by Google**)
6. Now click on the "Save and Continue" button and you will be taken to the Scopes step.
7. Click on the "**Add or Remove Scopes**" button and add the scopes `https://www.googleapis.com/auth/userinfo.email`, `https://www.googleapis.com/auth/spreadsheets` & `https://www.googleapis.com/auth/drive` and click on the "Update" button:
8. Now Verify the scopes and click on the "Save and Continue" button.
9. Now go to the **"Test Users" section, skip the step**, and click the "Save and Continue" button.
10. You will now be shown a summary of the OAuth Consent Screen. Verify the details and Click on the "**Back to Dashboard**" button.
11. Now go to the "**Credentials**" section and click on the "**Create Credentials**" button and select "**OAuth Client ID**".
12. Select "**Web Application**" as the Application Type and provide it a name (this name will **not be visible** to your end users).
13. Now add your **public facing URL** in the "**Authorized JavaScript Origins**" section:
- https://`<your-public-facing-url`>
14. Now add the following URL in the "**Authorized redirect URIs**" section and click on the "**Create**" button:
- https://`<your-public-facing-url`>/api/google-sheet/callback
15. You will now be shown the **Client ID** and **Client Secret**. Copy them and set them as the **environment variables** in your Formbricks instance as:
- `GOOGLE_SHEETS_CLIENT_ID` - Client ID
- `GOOGLE_SHEETS_CLIENT_SECRET` - Client Secret
16. Also use the **same Authorized redirect URI** in the `GOOGLE_SHEETS_REDIRECT_URL` environment variable.
### By now, your environment variables should include the below ones as well:
- `GOOGLE_SHEETS_CLIENT_ID`
- `GOOGLE_SHEETS_CLIENT_SECRET`
- `GOOGLE_SHEETS_REDIRECT_URL`
Voila! You have successfully enabled the Google Sheets integration in your self-hosted Formbricks instance. Now you can follow the steps mentioned in the [Formbricks Cloud](#formbricks-cloud) section to link a Google Sheet with Formbricks.
## Remove Integration with Google Account
To remove the integration with Google Account,
1. Visit the Integrations tab in your Formbricks Cloud dashboard.
2. Select "Manage" button in the Google Sheets card.
3. Click on the "Connected with `<your-email-here`>" just before the "Link new Sheet" button.
4. It will now ask for a confirmation to remove the integration. Click on the "Delete" button to remove the integration. You can always come back and connect again with the same Google Account.
<Image
src={DeleteConnection}
alt="Delete Google Integration with Formbricks"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## What data do you need?
- Your **Email ID** for authentication (We use this to identify you)
- Your **Google Sheets Names and IDs** (We fetch this to list and show you the options of choosing a sheet to integrate with)
- Write access to **selected Google Sheet** (The google sheet you choose to integrate it with, we write survey responses to it)
For the above, we ask for:
1. **User Email**: To identify you (that's it, nothing else, we're opensource, see this in our codebase [here](https://github.com/formbricks/formbricks/blob/main/apps/web/app/api/google-sheet/callback/route.ts#L47C17-L47C25))
1. **Google Drive API**: To list all your google sheets (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/services/googleSheet.ts#L13))
1. **Google Spreadsheet API**: To write to the spreadsheet you select (that's it, nothing else, we're opensource, see this method in our codebase [here](https://github.com/formbricks/formbricks/blob/main/packages/lib/services/googleSheet.ts#L70))
<Note>
We do not store any other information of yours! We value Privacy more than you and rest assured you're safe
with us!
</Note>
Still struggling or something not working as expected? [Join our Discord!](https://formbricks.com/discord) and we'd be glad to assist you!

View File

@@ -15,8 +15,8 @@ import Result from "./result.webp";
import SelectAction from "./select-action.webp";
export const meta = {
title: "Make.com Setup",
description: "Wire up Formbricks with Make and 1000+ other apps",
title: "Formbricks Integration with Make.com: A Step-by-Step Guide",
description: "Discover how to seamlessly integrate Formbricks with Make.com. Dive into our comprehensive guide to set up scenarios, connect with a plethora of apps, and send your survey data to more than 1000 platforms.",
};
#### Integrations

View File

@@ -19,8 +19,8 @@ import SuccessConnection from "./success-connection.png";
import UpdateQuestionId from "./update-question-id.png";
export const meta = {
title: "n8n Setup",
description: "Wire up Formbricks with n8n and 350+ other apps",
title: "Comprehensive Guide to Integrating Formbricks with n8n",
description: "Unlock the potential of combining Formbricks with n8n for a streamlined workflow experience. Dive into our step-by-step guide and send your survey data effortlessly to 350+ applications. Streamline your data processes now!",
};
#### Integrations

View File

@@ -15,8 +15,8 @@ import UpdateQuestionId from "./update-question-id.webp";
import ZapierMessage from "./zapier-message.webp";
export const meta = {
title: "Zapier Setup",
description: "Wire up Formbricks with Zapier and 5000+ other apps",
title: "Step-by-Step Guide to Integrating Formbricks with Zapier",
description: "Master the integration of Formbricks with Zapier using our detailed guide. Seamlessly connect your surveys to 5000+ apps, automate data transfers, and enhance feedback management. Start optimizing your workflow today.",
};
#### Integrations

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "How Formbricks works",
title: "Inside Look: Formbricks In-Product Micro-Surveys",
description:
"Master in-product micro-surveys with Formbricks: user-friendly form builder, precise targeting, seamless integration, and insightful analytics for SaaS products.",
"Unlock the full potential of Formbricks: From intuitive form-building and event-based triggers to effortless integrations and deep analytics. Master the art of in-product surveys for your SaaS or digital platform.",
};
#### Introduction

View File

@@ -4,9 +4,9 @@ import { HeroPattern } from "@/components/docs/HeroPattern";
import { Button } from "@/components/docs/Button";
export const metadata = {
title: "Formbricks Open Source Experience Management",
title: "Formbricks: Privacy-first Experience Management",
description:
"Natively embed qualitative user research into your B2B SaaS. Leverage Best Practices for user discovery to increase Product-Market Fit",
"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 = [

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Why is Formbricks better?",
title: "Formbricks vs. Generic Survey Tools: A Comparative Insight",
description:
"The ultimate survey solution for SaaS, offering in-depth micro-surveys, precise targeting, and seamless integrations. Discover the difference today!",
"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

View File

@@ -3,8 +3,8 @@ import Image from "next/image";
import QuestionId from "./question-id.webp";
export const meta = {
title: "Data Prefilling in Link Surveys",
description: "Prefill data in your surveys to make it easier for your users to provide feedback.",
title: "URL Data Prefilling for Link Surveys in Formbricks",
description: "Master the art of data prefilling in Formbricks link surveys. Dive into our guide on how to use URL parameters to prepopulate answers, boosting conversion rates and enhancing user experience. Learn through examples and ensure correct validation for each question type.",
};
#### Link Surveys

View File

@@ -3,9 +3,9 @@ import Image from "next/image";
import PeopleView from "./people-view.webp";
export const meta = {
title: "User Identification in Link Surveys",
title: "Effective User Identification in Formbricks Link Surveys",
description:
"Identify users in link surveys via URL parameter. Connect responses to existing users in Formbricks.",
"Discover how to seamlessly connect responses from Formbricks link surveys to existing users in your database. Learn the intricacies of the userId URL parameter to enhance user tracking, profiling, and segmentation, ensuring more personalized interactions and data-driven decisions.",
};
#### Link Surveys

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Deploying Formbricks to production",
title: "Comprehensive Guide to Self-Hosting Formbricks",
description:
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
"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

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Deploying Formbricks with Docker",
title: "Guide to Deploying Formbricks Using Docker",
description:
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
"Step-by-step tutorial on how to effortlessly set up and run Formbricks via Docker. Explore the quick deployment process with Docker-Compose, learn how to update Formbricks, and troubleshoot common issues. Ideal for those looking for a hassle-free Formbricks experience",
};
#### Self-Hosting

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Deploying Formbricks from Source Code",
title: "Formbricks Self-Hosting Guide: Deploy from Source Code for Complete Customization",
description:
"Build the Formbricks Image right from the open-sourced codebase and make changes however needed. Deploy it then with the freedom of customizing even the compile-time environment variables.",
"Build the Formbricks Image right from the open-sourced codebase and make changes however needed. Deploy it then with the freedom of customizing even the compile-time environment variables. Gain more control and flexibility by customizing compile-time environment variables and configuring your instance. Dive into our comprehensive guide for building and running Formbricks with Docker.",
};
#### Self-Hosting

View File

@@ -1,7 +1,7 @@
export const meta = {
title: "Deploying Formbricks to production",
title: "Step by Step Guide on Deploying Formbricks to Production on Ubuntu",
description:
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
"Master the swift deployment of Formbricks on an Ubuntu server with our step-by-step guide. Use a single command to automate Docker, Postgres DB, SSL certificate configuration, and more. Encounter issues? Dive into our troubleshooting steps or join our community on Discord for assistance.",
};
#### Self-Hosting

View File

@@ -1,43 +0,0 @@
import { Button } from "@formbricks/ui";
import { Popover } from "@headlessui/react";
import { usePlausible } from "next-plausible";
import Link from "next/link";
import { useRouter } from "next/router";
import { FooterLogo } from "./Logo";
export default function HeaderLight() {
const plausible = usePlausible();
const router = useRouter();
return (
<Popover className="relative" as="header">
<div className="mx-auto flex items-center justify-between py-6 sm:px-2 md:justify-start lg:px-8 xl:px-12 ">
<div className="flex w-0 flex-1 justify-start">
<Link href="/">
<span className="sr-only">Formbricks</span>
<FooterLogo className="ml-7 h-8 w-auto sm:h-10" />
</Link>
</div>
<div className="hidden flex-1 items-center justify-end md:flex">
<Button
variant="secondary"
onClick={() => {
router.push("https://cal.com/johannes/onboarding");
plausible("Demo_CTA_TalkToUs");
}}>
Talk to us
</Button>
<Button
variant="highlight"
className="ml-2"
onClick={() => {
router.push("https://app.formbricks.com/auth/signup");
plausible("Demo_CTA_TryForFree");
}}>
Start for free
</Button>
</div>
</div>
</Popover>
);
}

View File

@@ -5,7 +5,7 @@ import Image from "next/image";
export default function WaitlistForm() {
return (
<div className="not-prose text-md mx-auto mt-12 max-w-7xl rounded-lg bg-slate-200 p-10 text-slate-500 shadow-lg dark:bg-slate-800 dark:text-slate-400">
<p className="my-0 text-2xl font-bold text-slate-600 dark:text-slate-300">Build in public</p>
<p className="my-0 text-2xl font-bold text-slate-600 dark:text-slate-300">Stay in the loop!</p>
Get all the juicy details of our journey building Formbricks in public 👇
<div className="mt-8 gap-4 md:grid md:grid-cols-2">
<form method="post" action="https://listmonk.formbricks.com/subscription/form">

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -24,14 +24,14 @@
"@mapbox/rehype-prism": "^0.8.0",
"@mdx-js/loader": "^2.3.0",
"@mdx-js/react": "^2.3.0",
"@next/mdx": "13.4.19",
"@next/mdx": "13.5.3",
"@paralleldrive/cuid2": "^2.2.2",
"@sindresorhus/slugify": "^2.2.1",
"@tailwindcss/typography": "^0.5.10",
"@types/node": "20.6.0",
"@types/react-highlight-words": "^0.16.4",
"@types/node": "20.7.0",
"@types/react-highlight-words": "^0.16.5",
"acorn": "^8.10.0",
"autoprefixer": "^10.4.15",
"autoprefixer": "^10.4.16",
"clsx": "^2.0.0",
"fast-glob": "^3.3.1",
"flexsearch": "^0.7.31",
@@ -39,13 +39,13 @@
"lottie-web": "^5.12.2",
"mdast-util-to-string": "^4.0.0",
"mdx-annotations": "^0.1.3",
"next": "13.4.19",
"next": "13.5.3",
"next-plausible": "^3.11.1",
"next-seo": "^6.1.0",
"next-sitemap": "^4.2.3",
"next-themes": "^0.2.1",
"node-fetch": "^3.3.2",
"prism-react-renderer": "^2.0.6",
"prism-react-renderer": "^2.1.0",
"prismjs": "^1.29.0",
"react": "18.2.0",
"react-dom": "18.2.0",
@@ -56,7 +56,7 @@
"remark": "^14.0.3",
"remark-gfm": "^3.0.1",
"remark-mdx": "^2.3.0",
"sharp": "^0.32.5",
"sharp": "^0.32.6",
"shiki": "^0.14.4",
"simple-functional-loader": "^1.2.1",
"tailwindcss": "^3.3.3",

View File

@@ -98,6 +98,12 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
description: "Open-source monitoring platform with beautiful status pages",
href: "https://www.openstatus.dev",
},
{
name: "Papermark",
description:
"Open-Source Docsend Alternative to securely share documents with real-time analytics.",
href: "https://www.papermark.io/",
},
{
name: "Requestly",
description:

View File

@@ -1,11 +1,11 @@
import LayoutWaitlist from "@/components/shared/LayoutLight";
import DemoView from "@/components/dummyUI/DemoView";
import LayoutWaitlist from "@/pages/formtribe/LayoutLight";
export default function DemoPage() {
return (
<LayoutWaitlist
title="Formbricks Demo"
description="Leverage 30+ templates to kick-start your experience management.">
description="Play around with our pre-defined 30+ templates and them to kick-start your survey & experience management.">
<DemoView />
</LayoutWaitlist>
);

View File

@@ -7,7 +7,7 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function DocsFeedbackPage() {
return (
<Layout
title="Docs Feedback"
title="Get User Feedback in the easiest way possible with Formbricks"
description="The better your docs, the higher your user adoption. Measure granularly how clear your documentation is.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -7,8 +7,8 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function FeatureChaserPage() {
return (
<Layout
title="Feature Chaser"
description="Show a survey about a new feature shown only to people who used it.">
title="Feature Chaser with Formbricks"
description="Show a survey about a new feature shown only to people who used it and gain insightful data.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">
<UseCaseHeader title="Feature Chaser" difficulty="Easy" setupMinutes="10" />

View File

@@ -7,7 +7,7 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function FeedbackBoxPage() {
return (
<Layout
title="Feedback Box"
title="Feedback Box with Formbricks"
description="Open a direct channel to your users by allowing them to share feedback with your team.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -0,0 +1,74 @@
import Logo from "@/images/formtribe/formtribe-logo.png";
import { Button, Popover, PopoverContent, PopoverTrigger } from "@formbricks/ui";
import { Bars3Icon } from "@heroicons/react/24/solid";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
const navigation = [
{ name: "How it works", href: "#how" },
{ name: "Prizes", href: "#prizes" },
{ name: "Leaderboard", href: "#leaderboard" },
{ name: "FAQ", href: "#faq" },
];
export default function HeaderLight() {
const [mobileNavMenuOpen, setMobileNavMenuOpen] = useState(false);
return (
<div className="mx-auto flex w-full items-center justify-between py-6 sm:px-2 ">
<div className="flex items-center justify-start">
<Link href="/">
<span className="sr-only">FormTribe</span>
<Image alt="Formtribe Logo" src={Logo} className="ml-7 h-8 w-auto sm:h-10" />
</Link>
<Link
href="https://formbricks.com/github"
target="_blank"
className="ml-6 mt-1 text-sm text-slate-500 hover:scale-105">
Star us
</Link>
</div>
{/* Desktop Menu */}
<div className="hidden items-center gap-x-8 text-slate-700 md:flex">
{navigation.map((navItem) => (
<Link key={navItem.name} href={navItem.href} className="hover:scale-105">
{navItem.name}
</Link>
))}
<Button
variant="highlight"
className="font-kablammo ml-2 bg-gradient-to-br from-[#032E1E] via-[#032E1E] to-[#013C27] text-xl"
href="#join">
Join
</Button>
</div>
{/* Mobile Menu */}
<div className="flex items-center md:hidden">
<Popover open={mobileNavMenuOpen} onOpenChange={setMobileNavMenuOpen}>
<PopoverTrigger onClick={() => setMobileNavMenuOpen(!mobileNavMenuOpen)}>
<span>
<Bars3Icon className="h-8 w-8 rounded-md bg-slate-200 p-1 text-slate-600" />
</span>
</PopoverTrigger>
<PopoverContent className="mr-4 bg-slate-100 shadow">
<div className="flex flex-col">
{navigation.map((navItem) => (
<Link key={navItem.name} href={navItem.href}>
<div
onClick={() => setMobileNavMenuOpen(false)}
className="flex items-center space-x-2 rounded-md p-2">
<span className="font-medium text-slate-600">{navItem.name}</span>
</div>
</Link>
))}
</div>
</PopoverContent>
</Popover>
</div>
</div>
);
}

View File

@@ -1,5 +1,5 @@
import Footer from "./Footer";
import MetaInformation from "./MetaInformation";
import Footer from "../../components/shared/Footer";
import MetaInformation from "../../components/shared/MetaInformation";
import HeaderLight from "./HeaderLight";
interface LayoutProps {
@@ -10,11 +10,11 @@ interface LayoutProps {
export default function Layout({ title, description, children }: LayoutProps) {
return (
<div className="flex h-screen flex-col justify-between">
<div className="max-w-8xl mx-auto">
<MetaInformation title={title} description={description} />
<HeaderLight />
{
<main className="relative mx-auto mb-auto flex w-full flex-col justify-center sm:px-2 lg:px-8 xl:px-12">
<main className="relative mx-auto flex w-full max-w-6xl flex-col justify-center px-2 lg:px-8 xl:px-12">
{children}
</main>
}

View File

@@ -0,0 +1,662 @@
import LayoutLight from "@/pages/formtribe/LayoutLight";
import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@formbricks/ui";
import Head from "next/head";
import Image from "next/image";
import Dhru from "@/images/formtribe/dhru.jpeg";
import Jojo from "@/images/formtribe/jojo.jpeg";
import Matti from "@/images/formtribe/matti.jpeg";
import OSSLoop from "@/images/formtribe/oss-loop.png";
import Mac from "@/images/formtribe/package.jpeg";
import Pandey from "@/images/formtribe/pandeyman.jpeg";
import Shubham from "@/images/formtribe/shubham.jpeg";
import Timeline from "@/images/formtribe/timeline.png";
import { useEffect } from "react";
const HowTo = [
{
step: "1",
header: "Pick an issue from the list below (or start with a side quest)",
},
{
step: "2",
header: "Comment on the issue to signal that you started working on it.",
},
{
step: "3",
header: "Join our Discord to ask questions and submit side quests.",
link: "https://formbricks.com/discord",
},
{
step: "4",
header: "Code and open a PR with your contribution. ",
},
{
step: "5",
header: "Get your PR merged and collect points.",
},
{
step: "6",
header: "Tweet about your contribution and tag @formbricks",
},
{
step: "7",
header: "Solve side quests to increase your chances on the MacBook 👀",
link: "#prizes",
},
];
const SideQuests = [
{
points: "100 Points:",
quest: "You think you're smart removing the blur to see the side quests first?",
},
{
points: "150 Points:",
quest:
"You are! Take a screenshot of this and share it in the 'side-quest' channel on Discord to get 100 points.",
},
{
points: "200 Points:",
quest: "The rest of the side quests will be released on the 1st of October.",
},
{
points: "250 Points:",
quest: "Follow us on Twitter and join us on Discord to be the first to know!",
},
{
points: "Pushmaster Prime | +500 Points + Hoodie:",
quest: "Merge the highest amount of Formbricks PRs in October.",
},
{
points: "Guidance Guru | +500 Points + Hoodie:",
quest: "Most active and helpful in the community helping other contributors.",
},
{
points: "Buzz Builder Guru | +500 Points + Hoodie:",
quest: "Marketing Genie with great and effective ideas to spread the word about FormTribe",
},
];
const TheDeal = [
{
os: "100% free",
free: "Unlimited Surveys",
pro: "Custom URL",
},
{
os: "All features included",
free: "Unlimited Submissions",
pro: "Remove Branding",
},
{
os: "It's your storage, go nuts!",
free: "Upload Limit 10 MB",
pro: "Unlimited Uploads",
},
{
os: "Hook up your own Stripe",
free: "Payments with 2% Mark Up",
pro: "Remove Mark Up from Payments",
},
{
os: "Your server, your rules",
free: "Invite Team Members",
pro: "",
},
{
os: "The 'Do what you want' plan",
free: "Verify Email before Submission",
pro: "",
},
{
os: "at this point I'm just filling rows",
free: "Partial Submissions",
pro: "",
},
{
os: "I should stop",
free: "Custom Thank You Page",
pro: "",
},
{
os: "ok one more",
free: "Close Survey after Submission Limit",
pro: "",
},
{
os: "no flavor like free flavor",
free: "Custom Survey Closed Message",
pro: "",
},
{
os: "...",
free: "Close Survey on Date",
pro: "",
},
{
free: "Redirect on Completion",
pro: "",
},
{
free: "+ all upcoming community-built features",
pro: "",
},
];
const FAQ = [
{
question: "Is this part of Formbricks or a different tool?",
answer:
"The link survey is part of the Formbricks core product. The code is managed in the Formbricks mono repository.",
},
{
question: "Why are there only 30ish issues to work on?",
answer:
"We believe in Quality over Quantity. Were a small team and want to be able to pay enough attention to each of the contributors.",
},
{
question: "Can everyone win?",
answer:
"There are 64 prizes. Every contributor can only win 1 prize, so there will be 64 winners. Every contributor has a chance to win. The more points you make, the higher your chance to win.",
},
{
question: "Why do I have to sign a CLA?",
answer:
"To assure this project to be financially viable, we have to be able to relicense the code for enterprise customers and governments. To be able to do so, we are legally obliged to have you sign a CLA.",
},
{
question: "Where will this be hosted?",
answer:
"We offer a Formbricks Cloud with a generous free plan but you can also easily self-host using Docker.",
},
{
question: "What is the FormTribe?",
answer: "The FormTribe is what we call our community of contributors.",
},
{
question: "Why is there a Commercial plan?",
answer:
"The commercial plan is for features who break the OSS WIN-WIN Loop or incur additional cost. We charge 29$ if you want a custom domain, remove Formbricks branding, collect large files in surveys or collect payments. We think thats fair :)",
},
{
question: "Are your in app surveys also free forever?",
answer:
"The in app surveys you can run with Formbricks are not part of this Deal. We offer a generous free plan but keep full control over the pricing in the long run. In app surveys are really powerful for products with thousands of users and something has to bring in the dollars.",
},
{
question: "Do I need to pay duty for the SWAG?",
answer: "No, we cover that.",
},
{
question: "How long will it take to receive the swag?",
answer: "30-60 days.",
},
{
question: "How does it work?",
answer: "Here are detailed instructions, please read through them.",
},
{
question: "What's in it for me?",
answer: "A Macbook Air M2 or AirPods if your lucky, and life long friends for sure.",
},
{
question: "When is the event happening?",
answer: "1st of October until 31st of October",
},
{
question: "Can everyone participate?",
answer:
"Yes! Even when you dont know how to write code you can take part completing side quests. As long as you can open a PR you are very welcome to take part irrespective of your age, gender, nationality, food preferences, taste in clothing and favorite Pokemon.",
},
];
export default function FormTribeHackathon() {
// dark mode fix
useEffect(() => {
document.documentElement.classList.remove("dark");
}, []);
return (
<LayoutLight
title="FormTribe Hackathon"
description="Can we ship an Open Source Typeform alternative in 30 days?">
<Head>
<link
href="https://fonts.googleapis.com/css2?family=Kablammo:wght@400;500&display=swap"
rel="stylesheet"
/>
</Head>
{/* Header */}
<div className="px-4 pb-16 pt-16 text-center sm:px-6 lg:px-8 lg:pb-32 lg:pt-20">
<a href="#how" className=" rounded-full border bg-slate-100 px-4 py-1.5 text-sm text-slate-500">
Write code, win a Macbook 🔥
</a>
<h1 className="mt-10 text-3xl font-bold tracking-tight text-slate-800 dark:text-slate-200 sm:text-4xl md:text-5xl">
<span className="xl:inline">Let&apos;s ship Open Source Typeform in Hacktoberfest</span>
</h1>
<p className="xs:max-w-none mx-auto mt-3 max-w-xs text-base text-slate-500 dark:text-slate-400 sm:text-lg md:mt-6 md:text-xl">
Can we build an open source Typeform alternative in 30 days?
</p>
</div>
{/* Video + Nutshell */}
<div className="flex flex-col p-4 md:flex-row">
{/* Left Column: YouTube Video */}
<div className="mb-4 overflow-hidden rounded-lg md:mb-0 md:w-1/2">
<iframe
width="100%"
height="315"
src="https://www.youtube-nocookie.com/embed/zed8IIQWZLk?si=pfma3e9isiviKq2j&amp;controls=0"
title="Formbricks goes Hacktoberfest"></iframe>
</div>
{/* Right Column: Headline + Ordered List */}
<div className="flex items-center justify-center sm:pl-12 md:w-1/2">
<div className="space-y-5">
<h1 className="font-kablammo text-3xl font-bold text-slate-800">In a nutshell</h1>
<ol className="list-inside list-decimal space-y-3 text-slate-700">
<li>
<strong>As a community,</strong> we will ship all link survey features for a Typeform like
experience in 30 days <span className="text-lg"> 🚢</span>
</li>
<li>
All code and non-code contributors have a chance to win a <strong>MacBook Air M2</strong>
<span className="text-lg"> 💻</span>
</li>
<li>
The link surveys will be <strong>100% free to use</strong> - from the community, for the
community <span className="text-lg"> 🫶</span>
</li>
</ol>
<Button
variant="highlight"
href="#join"
className="mt-4 bg-gradient-to-br from-[#032E1E] via-[#032E1E] to-[#013C27]">
Join the Tribe
</Button>
</div>
</div>
</div>
{/* Note */}
<div className="sm:mt-32v mt-24 rounded-lg shadow-2xl ">
<div className="flex h-10 w-full items-center rounded-t-lg bg-slate-200 ">
<div className="flex space-x-1 pl-3">
<div className="h-4 w-4 rounded-full bg-red-400"></div>
<div className="h-4 w-4 rounded-full bg-amber-400"></div>
<div className="h-4 w-4 rounded-full bg-green-400"></div>
</div>
</div>
<div className=" w-full items-center space-y-5 rounded-b-lg bg-amber-50 px-8 py-6 text-slate-800 sm:px-24 sm:py-12">
<h3 className="text-xl font-bold text-slate-800 sm:text-2xl">What is this? (And are we 🥜?)</h3>
<p>
Charlie Munger famously said <strong>Show me the incentives and I show you the outcome.</strong>
</p>
<p>
The beauty of Open Source Software (and the reason for its inevitable domination) is that
incentives between the different groups of users and developers are perfectly aligned.
</p>
<p>Lets have a look:</p>
<Image src={OSSLoop} alt="oss loop" />
<p>With open-source software, everyone wins:</p>
<p>
The community of contributors gets to learn on the job working on a real product with real users.
The free users get to use a feature-complete product free of charge. we dont have to pay insane
sales people salaries to sell our enterprise solution in the future.{" "}
<strong>Welcome to the OSS win-win loop</strong>
</p>
<p>
This is why we have decided to spend the complete month of Hacktoberfest hacking away with you,
our community!
</p>
<p className="text-lg font-semibold sm:text-xl">But why would you hack with us?</p>
<p>Theres a couple of reasons, but lets first nail this down:</p>
<p>
The time for an elegant <strong>👏</strong>, open source <strong>👏</strong> survey builder has
come. It has been tried before, but it has never been maintained long enough because its hard to
make money with a free tool.
</p>
<p>This is why the FormTribe is coming together this October!</p>
<p>And you can be a part of it!</p>
<p>
Imagine yourself flexing on a date that your code is used by thousands and soon millions of
people!
</p>
<p className="text-lg font-semibold sm:text-xl">
Apart from you dating life levelling up, here are some proper reasons:
</p>
<p></p>
<ul className="list-disc space-y-1 pl-4">
<li>
You can win a new <b>MacBook Air M2 💻</b>
</li>
<li>
You can win limited <b>hoodies, t-shirts and stickers!</b>
</li>
<li>
You can complete official <b>Hacktoberfest PRs</b>
</li>
<li>Youll earn your spot of honour on our Community page on formbricks.com</li>
<li>
Youll be working closely with the Formbricks team, shipping features which have been requested
since this was called snoopForms
</li>
<li>Youll join an active community of open source fans, connect and learn together</li>
</ul>
<p></p>
<p>Doesnt this sound like fun?</p>
<p>It does! And I almost forgot the most important part:</p>
<p className="text-lg font-semibold sm:text-xl">The grande finale is a ProductHunt launch 🚀</p>
<p>What are you waiting for?</p>
<p>Roll up your sleeves, pick one of the issues linked below and join us!</p>
<p className="font-kablammo pt-6 text-2xl text-slate-800">
Matti, Johannes, Anshuman, Shubham & Dhruwang
</p>
<div className="flex space-x-2">
<Image src={Matti} alt="matti" className="h-12 w-12 rounded-full " />
<Image src={Jojo} alt="jojo" className="h-12 w-12 rounded-full " />
<Image src={Pandey} alt="pandey" className="h-12 w-12 rounded-full " />
<Image src={Shubham} alt="shubh" className="h-12 w-12 rounded-full " />
<Image src={Dhru} alt="dhru" className="h-12 w-12 rounded-full " />
</div>
</div>
</div>
{/* Breaker 1 */}
<Breaker icon="🤸" title="Dont miss the kick-off!" />
{/* Prizes */}
<SectionHeading
id="prizes"
subTitle="🏆"
title="Prizes"
description="We give back to our community with the slickest laptop made by human kind: A MacBook Air M2!"
/>
<div className="grid-cols-2 sm:grid">
<Image src={Mac} alt="macbook air m2" className="rounded-lg p-10" />
<div className="flex items-center justify-center space-y-5 p-2">
<ul className="list-inside space-y-3 text-center text-2xl sm:text-left ">
<li>🎉 1 x MacBook Air M2</li>
<li>🎉 3 x Limited FormTribe Premium Hoodie</li>
<li>🎉 10 x Limited FormTribe Premium Shirt</li>
<li>🎉 50 x Sets of Formbricks Stickers</li>
</ul>
</div>
</div>
<div className="rounded-lg-12 mt-6 grid-cols-6 rounded-lg border border-slate-200 bg-slate-100 py-12 sm:grid">
<div className="col-span-1 mr-8 flex items-center justify-center sm:justify-end">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-white text-3xl">🍀</div>
</div>
<div className="col-span-5 px-6">
<h3 className="my-4 text-lg font-semibold text-slate-700 sm:my-0">
Every participant can win! How?
</h3>
<p className="pr-16 text-slate-500">
For every point you make, your name will be added to a virtual jar. From this jar we will draw the
winners. An example:
</p>
<ul className="list-disc py-4 pl-5 text-slate-500 ">
<li>
Lola makes 200 points for a PR and a total of 100 points with side quests. Lolas name will be
added 300x to the jar.{" "}
</li>
<li>Bricky completes side quests for 50 points, so his name will be added 50x to the jar.</li>
</ul>
<p className="pr-16 text-slate-500">
In a live stream we will pull a name from the jar for each of the prizes. Since Lola has 6x more
points than Bricky, her chance of winning is 6x higher.
</p>
</div>
</div>
{/* Here is how */}
<div className="relative">
<SectionHeading
id="how"
subTitle="💻"
title="Write code, win a MacBook"
description="We want to give back to our community of developers. What would be better than the best laptop ever designed by human kind? A MacBook Air M2!"
/>
</div>
<div className="mx-auto max-w-2xl">
{HowTo.map((offer) => (
<div key={offer.step} className="mb-2 flex items-center gap-x-4">
<div className=" flex h-10 w-10 items-center justify-center rounded-full bg-slate-100 p-7 text-2xl font-semibold text-slate-500">
<p className="font-kablammo bg-gradient-to-br from-[#032E1E] via-[#032E1E] to-[#013C27] bg-clip-text text-transparent">
{offer.step}
</p>
</div>
<div>
{offer.link ? (
<a
href={offer.link}
className="text-lg text-slate-700 decoration-[#013C27] underline-offset-4 hover:underline">
{offer.header}
</a>
) : (
<h4 className="text-lg text-slate-700">{offer.header}</h4>
)}
</div>
</div>
))}
</div>
<div className="mt-12 flex h-64 items-center justify-center rounded-lg bg-slate-200 text-slate-600">
<div className="text-center">
<p>No issues released yet.</p>
<a
href="https://formbricks.com/discord"
target="_blank"
className="text-slate-700 underline decoration-[#013C27] underline-offset-4">
Join Discord to get notified first.
</a>
</div>
</div>
{/* Side Quests */}
<div className="mt-16">
<h3 className="font-kablammo my-4 text-4xl font-bold text-slate-800">
🏰 Side Quests: Increase your chances
</h3>
<p className="w-3/4 text-slate-600">
While code contributions are what gives the most points, everyone gets to bump up their chance of
winning. Here is a list of side quests you can complete:{" "}
</p>
<div className="mt-8 blur">
{SideQuests.map((quest) => (
<div key={quest.points} className="mb-2 flex select-none items-center gap-x-4">
<div className="text-2xl"></div>
<div>
<p className="text-lg font-bold text-slate-700">
{quest.points} <span className="font-normal">{quest.quest}</span>
</p>
</div>
</div>
))}
</div>
</div>
{/* The Leaderboard */}
<SectionHeading
id="leaderboard"
subTitle="👀"
title="The Leaderboard"
description="We keep track of all contributions and side quests in Discord. Join to take part!"
/>
<div className="mt-12 flex h-64 items-center justify-center rounded-lg bg-slate-200 text-slate-600">
<div className="text-center">
<p>Not live yet.</p>
<a href="#join" className="pl-2 text-slate-700 underline decoration-[#013C27] underline-offset-4">
Sign up to get notified on kick-off.
</a>
</div>
</div>
{/* The Timeline */}
<SectionHeading
id="timeline"
subTitle="📅"
title="The Timeline"
description="(Germans do nothing without one)"
/>
<Image src={Timeline} alt="timeline" />
{/* Breaker 1 */}
<Breaker icon="🚀" title="Dont miss the launch!" />
{/* The Deal */}
<div className=" mx-auto max-w-4xl">
<SectionHeading
id="deal"
subTitle="🤝"
title="The Deal"
description="We're kinda making a handshake agreement here. Lets outline the terms:"
/>
<div>
<div className="grid grid-cols-3 items-end rounded-t-lg border border-slate-200 bg-slate-100 px-6 py-3 text-sm font-bold text-slate-800 sm:text-base">
<div>Self-hosted</div>
<div>Formbricks Cloud</div>
<TooltipProvider delayDuration={50}>
<Tooltip>
<TooltipTrigger asChild>
<div>
Formbricks Cloud Pro{" "}
<span className="ml-1 hidden rounded-full bg-slate-700 px-2 text-xs font-normal text-white sm:inline">
Why tho?
</span>
</div>
</TooltipTrigger>
<TooltipContent sideOffset={5} className="max-w-lg font-normal">
You can always self-host to get all features free.
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
{TheDeal.map((feature) => (
<div
key={feature.free}
className="grid grid-cols-3 gap-x-2 border-x border-b border-slate-200 px-6 py-3 text-sm text-slate-900 last:rounded-b-lg">
<div>{feature.os}</div>
<div>{feature.free}</div>
<div>{feature.pro}</div>
</div>
))}
</div>
<div className="rounded-lg-12 mt-6 grid-cols-6 rounded-lg bg-slate-100 py-12 sm:grid">
<div className="col-span-1 mr-8 flex items-center justify-center sm:justify-end">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-white text-3xl">
🤓
</div>
</div>
<div className="col-span-5 px-8 sm:px-0">
<h3 className="mt-4 text-lg font-semibold text-slate-700 sm:mt-0">
Are Formbricks in-app surveys also free?
</h3>
<p className="text-slate-500 sm:pr-16">
Just a heads-up: this deal doesn&apos;t cover Formbricks&apos; in-app surveys. We&apos;ve got a
solid free plan, but we&apos;ve gotta keep some control over pricing to keep things running
long-term.
</p>
</div>
</div>
</div>
{/* FAQ */}
<div className="mt-32" id="faq">
<h3 className="font-kablammo my-4 text-4xl font-bold text-slate-800">FAQ</h3>
<p className="w-3/4 text-slate-600">Anything unclear?</p>
<div className="mt-8">
{FAQ.map((question) => (
<div key={question.question} className="">
<div>
<h3 className="mt-6 text-lg font-bold text-slate-700">{question.question} </h3>
<p className="text-slate-600">{question.answer}</p>
</div>
</div>
))}
</div>
</div>
<Button className="mt-4 " variant="secondary" href="https://formbricks.com/discord" target="_blank">
Join Discord and ask away
</Button>
{/* Breaker 3 */}
<Breaker icon="👋" title="Join the Tribe!" />
</LayoutLight>
);
}
const SectionHeading = ({ title, subTitle, description, id }) => {
return (
<div id={id} className="lg:pt-18 mt-32 px-4 pb-12 text-center sm:px-6 lg:px-8 ">
<p className=" text-[3rem] text-slate-500">{subTitle}</p>
<h1 className="font-kablammo mb-8 mt-4 bg-gradient-to-br from-[#032E1E] via-[#032E1E] to-[#013C27] bg-clip-text text-6xl text-transparent">
{title}
</h1>
<p className="mx-auto mt-4 text-slate-700 sm:w-3/4">{description}</p>
</div>
);
};
const Breaker = ({ icon, title }) => {
return (
<div
id="join"
className="rounded-lg-12 mt-12 rounded-lg bg-slate-200 px-4 py-12 shadow-inner sm:mt-20 sm:grid sm:grid-cols-6">
<div className="col-span-2 mr-8 flex items-center justify-center sm:justify-end">
<div className="h-24 w-24 rounded-full bg-white"></div>
<div className="absolute -mt-4 animate-bounce text-[6rem]">{icon}</div>
</div>
<div className="col-span-4">
<h3 className="mt-8 text-xl font-bold sm:mt-0">{title}</h3>
<p className="mb-4 mt-1 text-slate-500">Get notified on launch plus a weekly update:</p>
<form method="post" action="https://listmonk.formbricks.com/subscription/form">
<div className="hidden">
<input type="hidden" name="nonce" />
<input id="5d65b" type="checkbox" name="l" checked value="5d65bc6e-e685-4694-8c8e-9b20d7be6c40" />
</div>
<div className="mt-2 sm:flex">
<div className="">
<input
type="email"
name="email"
placeholder="Your email"
aria-placeholder="your-email"
required
className="block h-12 w-full rounded-lg border-0 px-3 py-2 text-sm text-slate-900 sm:mr-4 sm:h-full sm:w-64"
/>
</div>
<Button
variant="highlight"
type="submit"
className="mt-2 inline w-full justify-center bg-gradient-to-br from-[#032E1E] via-[#032E1E] to-[#013C27] text-white sm:ml-2 sm:mt-0 sm:w-40 ">
Join the Tribe
</Button>
</div>
</form>
</div>
</div>
);
};

View File

@@ -3,6 +3,7 @@ import { Callout } from "@/components/shared/Callout";
export const meta = {
title: "How to create a GDPR compliant form",
description: "Steps to understand how GDPR might be needed for you with Formbricks.",
};
<Callout title="No legal advice" type="note">

View File

@@ -3,6 +3,7 @@ import { Callout } from "@/components/shared/Callout";
export const meta = {
title: "GDPR FAQs",
description: "Frequently asked questions about GDPR and Formbricks",
};
<Callout title="No legal advice" type="note">

View File

@@ -7,7 +7,7 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function MissedTrialPagePage() {
return (
<Layout
title="Improve Trial Conversion"
title="Improve Trial Conversion with Formbricks"
description="Take the guessing out, convert more trials to paid users with insights.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -7,7 +7,7 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function InterviewPromptPage() {
return (
<Layout
title="Interview Prompt"
title="Interview Prompt with Formbricks"
description="Ask only power users users to book a time in your calendar. Get those juicy details.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -7,7 +7,7 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function LearnFromChurnPage() {
return (
<Layout
title="Learn from Churn"
title="Learn from Churn with Formbricks"
description="Churn is hard, but insightful. Learn from users who changed their mind.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -15,7 +15,7 @@ import Image from "next/image";
export default function MeasurePMFPage() {
return (
<Layout
title="Product-Market Fit Survey"
title="Product-Market Fit Survey with Formbricks"
description="Measure Product-Market Fit to understand how to develop your product further.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -6,7 +6,7 @@ import UseCaseHeader from "@/components/shared/UseCaseHeader";
export default function OnboardingSegmentationPage() {
return (
<Layout
title="Onboarding Segmentation"
title="Onboarding Segmentation with Formbricks"
description="Add a survey to your onboarding to loop in Formbricks right from the start.">
<div className="grid grid-cols-1 items-center md:grid-cols-2 md:gap-12 md:py-20">
<div className="p-6 md:p-0">

View File

@@ -1,6 +1,6 @@
import headlessuiPlugin from "@headlessui/tailwindcss";
import typographyPlugin from "@tailwindcss/typography";
import forms from "@tailwindcss/forms";
import typographyPlugin from "@tailwindcss/typography";
import { type Config } from "tailwindcss";
import defaultTheme from "tailwindcss/defaultTheme";
import typographyStyles from "./typography";
@@ -64,6 +64,7 @@ export default {
fontFamily: {
sans: ["Poppins", ...defaultTheme.fontFamily.sans],
display: ["Lexend", ...defaultTheme.fontFamily.sans],
kablammo: ["Kablammo", "sans"],
},
screens: {
xs: "430px",

3
apps/web/.gitignore vendored
View File

@@ -37,3 +37,6 @@ next-env.d.ts
# Sentry Auth Token
.sentryclirc
# Google Sheets Token File
token.json

View File

@@ -6,8 +6,14 @@ import n8nLogo from "@/images/n8n.png";
import MakeLogo from "@/images/make-small.png";
import { Card } from "@formbricks/ui";
import Image from "next/image";
import { getIntegrations } from "@formbricks/lib/services/integrations";
export default async function IntegrationsPage({ params }) {
const integrations = await getIntegrations(params.environmentId);
const containsGoogleSheetIntegration = integrations.some(
(integration) => integration.type === "googleSheets"
);
export default function IntegrationsPage({ params }) {
return (
<div>
<h1 className="my-2 text-3xl font-bold text-slate-800">Integrations</h1>
@@ -45,7 +51,7 @@ export default function IntegrationsPage({ params }) {
/>
<Card
connectHref={`/environments/${params.environmentId}/integrations/google-sheets`}
connectText="Connect"
connectText={`${containsGoogleSheetIntegration ? "Manage Sheets" : "Connect"}`}
connectNewTab={false}
docsHref="https://formbricks.com/docs/integrations/google-sheets"
docsText="Docs"

View File

@@ -2,17 +2,35 @@ export const revalidate = REVALIDATION_INTERVAL;
import EmptySpaceFiller from "@/components/shared/EmptySpaceFiller";
import { truncateMiddle } from "@/lib/utils";
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
import { getPeople } from "@formbricks/lib/services/person";
import { PEOPLE_PER_PAGE, REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
import { getPeople, getPeopleCount } from "@formbricks/lib/services/person";
import { TPerson } from "@formbricks/types/v1/people";
import { PersonAvatar } from "@formbricks/ui";
import { Pagination, PersonAvatar } from "@formbricks/ui";
import Link from "next/link";
const getAttributeValue = (person: TPerson, attributeName: string) =>
person.attributes[attributeName]?.toString();
export default async function PeoplePage({ params }) {
const people = await getPeople(params.environmentId);
export default async function PeoplePage({
params,
searchParams,
}: {
params: { environmentId: string };
searchParams: { [key: string]: string | string[] | undefined };
}) {
const pageNumber = searchParams.page ? parseInt(searchParams.page as string) : 1;
const totalPeople = await getPeopleCount(params.environmentId);
const maxPageNumber = Math.ceil(totalPeople / PEOPLE_PER_PAGE);
let hidePagination = false;
let people: TPerson[] = [];
if (pageNumber < 1 || pageNumber > maxPageNumber) {
people = [];
hidePagination = true;
} else {
people = await getPeople(params.environmentId, pageNumber);
}
return (
<>
@@ -64,6 +82,14 @@ export default async function PeoplePage({ params }) {
))}
</div>
)}
{hidePagination ? null : (
<Pagination
baseUrl={`/environments/${params.environmentId}/people`}
currentPage={pageNumber}
totalItems={totalPeople}
itemsPerPage={PEOPLE_PER_PAGE}
/>
)}
</>
);
}

View File

@@ -0,0 +1,57 @@
function Pagination({ environmentId, currentPage, totalItems, itemsPerPage }) {
const totalPages = Math.ceil(totalItems / itemsPerPage);
const previousPageLink =
currentPage === 1 ? "#" : `/environments/${environmentId}/people?page=${currentPage - 1}`;
const nextPageLink =
currentPage === totalPages ? "#" : `/environments/${environmentId}/people?page=${currentPage + 1}`;
return (
<nav aria-label="Page navigation" className="flex justify-center">
<ul className="mt-4 inline-flex -space-x-px text-sm">
<li>
<a
href={previousPageLink}
className={`ml-0 flex h-8 items-center justify-center rounded-l-lg border border-gray-300 bg-white px-3 text-gray-500 ${
currentPage === 1
? "cursor-not-allowed opacity-50"
: "hover:bg-gray-100 hover:text-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
}`}>
Previous
</a>
</li>
{Array.from({ length: totalPages }).map((_, idx) => {
const pageNum = idx + 1;
const pageLink = `/environments/${environmentId}/people?page=${pageNum}`;
return (
<li key={pageNum} className="hidden sm:block">
<a
href={pageNum === currentPage ? "#" : pageLink}
className={`flex h-8 items-center justify-center px-3 ${
pageNum === currentPage ? "bg-blue-50 text-green-500" : "bg-white text-gray-500"
} border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white`}>
{pageNum}
</a>
</li>
);
})}
<li>
<a
href={nextPageLink}
className={`ml-0 flex h-8 items-center justify-center rounded-r-lg border border-gray-300 bg-white px-3 text-gray-500 ${
currentPage === totalPages
? "cursor-not-allowed opacity-50"
: "hover:bg-gray-100 hover:text-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
}`}>
Next
</a>
</li>
</ul>
</nav>
);
}
export default Pagination;

View File

@@ -0,0 +1,32 @@
import React from "react";
import { Unplug } from "lucide-react";
import { Button } from "@formbricks/ui";
import Link from "next/link";
type TEmptyInAppSurveysProps = {
environmentId: string;
};
const EmptyInAppSurveys = ({ environmentId }: TEmptyInAppSurveysProps) => {
return (
<div className="flex w-full items-center justify-center gap-8 bg-slate-100 py-12">
<div className="flex h-20 w-20 items-center justify-center rounded-full border border-slate-200 bg-white">
<Unplug size={48} className="text-amber-500" absoluteStrokeWidth />
</div>
<div className="flex flex-col">
<h1 className="text-xl font-semibold text-slate-900">You&apos;re not plugged in yet!</h1>
<p className="mt-2 text-sm text-slate-600">Connect your app with Formbricks to run in app surveys.</p>
<Link className="mt-2" href={`/environments/${environmentId}/settings/setup`}>
<Button variant="darkCTA" size="sm" className="flex w-[120px] justify-center">
Connect
</Button>
</Link>
</div>
</div>
);
};
export default EmptyInAppSurveys;

View File

@@ -1,8 +1,8 @@
"use client";
import CustomFilter from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/CustomFilter";
import SummaryHeader from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/SummaryHeader";
import SurveyResultsTabs from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/SurveyResultsTabs";
import ResponseTimeline from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/ResponseTimeline";
import SurveyResultsTabs from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/SurveyResultsTabs";
import ResponseTimeline from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTimeline";
import ContentWrapper from "@/components/shared/ContentWrapper";
import { useResponseFilter } from "@/app/(app)/environments/[environmentId]/ResponseFilterContext";
import { getFilterResponses } from "@/lib/surveys/surveys";

View File

@@ -1,6 +1,6 @@
"use client";
import TagsCombobox from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/TagsCombobox";
import TagsCombobox from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/TagsCombobox";
import { removeTagFromResponse, useAddTagToResponse, useCreateTag } from "@/lib/tags/mutateTags";
import { useTagsForEnvironment } from "@/lib/tags/tags";
import React, { useEffect, useState } from "react";

View File

@@ -5,6 +5,7 @@ import { TSurvey } from "@formbricks/types/v1/surveys";
import { createId } from "@paralleldrive/cuid2";
import { useMemo } from "react";
import SingleResponse from "./SingleResponse";
import EmptyInAppSurveys from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/EmptyInAppSurveys";
interface ResponseTimelineProps {
environmentId: string;
@@ -40,7 +41,7 @@ export default function ResponseTimeline({
// iterate over survey questions and build the updated response
for (const question of survey.questions) {
const answer = response.data[question.id];
if (answer) {
if (answer !== null && answer !== undefined) {
updatedResponse.push({
id: createId(),
question: question.headline,
@@ -66,7 +67,8 @@ export default function ResponseTimeline({
return (
<div className="space-y-4">
{responses.length === 0 ? (
{survey.type === "web" && responses.length === 0 && <EmptyInAppSurveys environmentId={environmentId} />}
{survey.type !== "web" && responses.length === 0 ? (
<EmptySpaceFiller
type="response"
environmentId={environmentId}

View File

@@ -14,9 +14,9 @@ import Link from "next/link";
import { useRouter } from "next/navigation";
import { ReactNode, useState } from "react";
import toast from "react-hot-toast";
import { RatingResponse } from "../RatingResponse";
import ResponseNote from "./ResponseNote";
import ResponseTagsWrapper from "./ResponseTagsWrapper";
import { RatingResponse } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/RatingResponse";
export interface OpenTextSummaryProps {
environmentId: string;

View File

@@ -1,12 +1,12 @@
export const revalidate = REVALIDATION_INTERVAL;
import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
import ResponsePage from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/ResponsePage";
import ResponsePage from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponsePage";
import { getAnalysisData } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/data";
import { getServerSession } from "next-auth";
import ResponsesLimitReachedBanner from "../ResponsesLimitReachedBanner";
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
import { SURVEY_BASE_URL } from "@formbricks/lib/constants";
import ResponsesLimitReachedBanner from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/ResponsesLimitReachedBanner";
export default async function Page({ params }) {
const surveyBaseUrl = SURVEY_BASE_URL;

View File

@@ -1,6 +1,6 @@
"use client";
import LinkSurveyModal from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/LinkSurveyModal";
import LinkSurveyModal from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/LinkSurveyModal";
import { TSurvey } from "@formbricks/types/v1/surveys";
import { Button } from "@formbricks/ui";
import { ShareIcon } from "@heroicons/react/24/outline";

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