))}
diff --git a/apps/formbricks-com/lib/docsNavigation.ts b/apps/formbricks-com/lib/docsNavigation.ts
index 604660a71f..e6a4d16f15 100644
--- a/apps/formbricks-com/lib/docsNavigation.ts
+++ b/apps/formbricks-com/lib/docsNavigation.ts
@@ -78,6 +78,7 @@ const navigation = [
{ title: "Get Webhook", href: "/docs/webhook-api/get-webhook" },
{ title: "Create Webhook", href: "/docs/webhook-api/create-webhook" },
{ title: "Delete Webhook", href: "/docs/webhook-api/delete-webhook" },
+ { title: "Webhook Payload", href: "/docs/webhook-api/webhook-payload" },
],
},
{
diff --git a/apps/formbricks-com/package.json b/apps/formbricks-com/package.json
index 6744f6212c..fc1d6087ad 100644
--- a/apps/formbricks-com/package.json
+++ b/apps/formbricks-com/package.json
@@ -11,22 +11,22 @@
"lint": "next lint"
},
"dependencies": {
- "@calcom/embed-react": "^1.2.2",
+ "@calcom/embed-react": "^1.3.0",
"@docsearch/react": "^3.5.1",
"@formbricks/lib": "workspace:*",
"@formbricks/types": "workspace:*",
"@formbricks/ui": "workspace:*",
- "@headlessui/react": "^1.7.15",
+ "@headlessui/react": "^1.7.16",
"@heroicons/react": "^2.0.18",
"@mapbox/rehype-prism": "^0.8.0",
"@mdx-js/loader": "^2.3.0",
"@mdx-js/react": "^2.3.0",
- "@next/mdx": "^13.4.10",
+ "@next/mdx": "^13.4.12",
"@paralleldrive/cuid2": "^2.2.1",
"clsx": "^2.0.0",
"lottie-web": "^5.12.2",
- "next": "13.4.10",
- "next-plausible": "^3.10.0",
+ "next": "13.4.12",
+ "next-plausible": "^3.10.1",
"next-sitemap": "^4.1.8",
"prism-react-renderer": "^2.0.6",
"prismjs": "^1.29.0",
@@ -35,7 +35,7 @@
"react-icons": "^4.10.1",
"react-responsive-embed": "^2.1.0",
"remark-gfm": "^3.0.1",
- "sharp": "^0.32.3"
+ "sharp": "^0.32.4"
},
"devDependencies": {
"@formbricks/tsconfig": "workspace:*",
diff --git a/apps/formbricks-com/pages/api/oss-friends/index.ts b/apps/formbricks-com/pages/api/oss-friends/index.ts
index 581b701295..5ea74d85c0 100644
--- a/apps/formbricks-com/pages/api/oss-friends/index.ts
+++ b/apps/formbricks-com/pages/api/oss-friends/index.ts
@@ -1,10 +1,134 @@
-import { OSSFriends } from "@/pages/oss-friends";
import type { NextApiRequest, NextApiResponse } from "next";
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
// GET
if (req.method === "GET") {
- return res.status(200).json({ data: OSSFriends });
+ return res.status(200).json({
+ data: [
+ {
+ name: "Appsmith",
+ description: "Build build custom software on top of your data.",
+ href: "https://www.appsmith.com",
+ },
+ {
+ name: "BoxyHQ",
+ description:
+ "BoxyHQ’s suite of APIs for security and privacy helps engineering teams build and ship compliant cloud applications faster.",
+ href: "https://boxyhq.com",
+ },
+ {
+ name: "Cal.com",
+ description:
+ "Cal.com is a scheduling tool that helps you schedule meetings without the back-and-forth emails.",
+ href: "https://cal.com",
+ },
+ {
+ name: "Crowd.dev",
+ description:
+ "Centralize community, product, and customer data to understand which companies are engaging with your open source project.",
+ href: "https://www.crowd.dev",
+ },
+ {
+ name: "Documenso",
+ description:
+ "The Open-Source DocuSign Alternative. We aim to earn your trust by enabling you to self-host the platform and examine its inner workings.",
+ href: "https://documenso.com",
+ },
+ {
+ name: "Erxes",
+ description:
+ "The Open-Source HubSpot Alternative. A single XOS enables to create unique and life-changing experiences that work for all types of business.",
+ href: "https://erxes.io",
+ },
+ {
+ name: "Formbricks",
+ description:
+ "Survey granular user segments at any point in the user journey. Gather up to 6x more insights with targeted micro-surveys. All open-source.",
+ href: "https://formbricks.com",
+ },
+ {
+ name: "GitWonk",
+ description:
+ "GitWonk is an open-source technical documentation tool, designed and built focusing on the developer experience.",
+ href: "https://gitwonk.com",
+ },
+ {
+ name: "Hanko",
+ description:
+ "Open-source authentication and user management for the passkey era. Integrated in minutes, for web and mobile apps.",
+ href: "https://www.hanko.io",
+ },
+ {
+ name: "HTMX",
+ description:
+ "HTMX is a dependency-free JavaScript library that allows you to access AJAX, CSS Transitions, WebSockets, and Server Sent Events directly in HTML.",
+ href: "https://htmx.org",
+ },
+ {
+ name: "Infisical",
+ description:
+ "Open source, end-to-end encrypted platform that lets you securely manage secrets and configs across your team, devices, and infrastructure.",
+ href: "https://infisical.com",
+ },
+ {
+ name: "Mockoon",
+ description: "Mockoon is the easiest and quickest way to design and run mock REST APIs.",
+ href: "https://mockoon.com",
+ },
+ {
+ name: "Novu",
+ description:
+ "The open-source notification infrastructure for developers. Simple components and APIs for managing all communication channels in one place.",
+ href: "https://novu.co",
+ },
+ {
+ name: "OpenBB",
+ description:
+ "Democratizing investment research through an open source financial ecosystem. The OpenBB Terminal allows everyone to perform investment research, from everywhere.",
+ href: "https://openbb.co",
+ },
+ {
+ name: "Sniffnet",
+ description:
+ "Sniffnet is a network monitoring tool to help you easily keep track of your Internet traffic.",
+ href: "https://www.sniffnet.net",
+ },
+ {
+ name: "Tolgee",
+ description: "Software localization from A to Z made really easy.",
+ href: "https://tolgee.io/",
+ },
+ {
+ name: "Trigger.dev",
+ description:
+ "Create long-running Jobs directly in your codebase with features like API integrations, webhooks, scheduling and delays.",
+ href: "https://trigger.dev",
+ },
+ {
+ name: "Typebot",
+ description:
+ "Typebot gives you powerful blocks to create unique chat experiences. Embed them anywhere on your apps and start collecting results like magic.",
+ href: "https://typebot.io",
+ },
+ {
+ name: "Twenty",
+ description:
+ "A modern CRM offering the flexibility of open-source, advanced features and sleek design.",
+ href: "https://twenty.com",
+ },
+ {
+ name: "Webiny",
+ description:
+ "Open-source enterprise-grade serverless CMS. Own your data. Scale effortlessly. Customize everything.",
+ href: "https://www.webiny.com",
+ },
+ {
+ name: "Webstudio",
+ description: "Webstudio is an open source alternative to Webflow",
+ href: "https://webstudio.is",
+ },
+ ],
+ });
}
// Unknown HTTP Method
diff --git a/apps/formbricks-com/pages/docs-feedback/index.tsx b/apps/formbricks-com/pages/docs-feedback/index.tsx
index 191b56a3cf..c50f2570c8 100644
--- a/apps/formbricks-com/pages/docs-feedback/index.tsx
+++ b/apps/formbricks-com/pages/docs-feedback/index.tsx
@@ -7,7 +7,7 @@ import BestPracticeNavigation from "@/components/shared/BestPracticeNavigation";
export default function DocsFeedbackPage() {
return (
diff --git a/apps/formbricks-com/pages/docs/api/api-key-setup/index.mdx b/apps/formbricks-com/pages/docs/api/api-key-setup/index.mdx
index 1f7a208101..e19805ccf6 100644
--- a/apps/formbricks-com/pages/docs/api/api-key-setup/index.mdx
+++ b/apps/formbricks-com/pages/docs/api/api-key-setup/index.mdx
@@ -33,7 +33,7 @@ The API requests are authorized with a personal API key. This API key gives you
### Delete a personal API key
-1. Go to settings on [app.formbricks.com](https://app.formbricks.com/me/settings).
+1. Go to settings on [app.formbricks.com](https://app.formbricks.com/).
2. Go to page “API keys”.
3. Find the key you wish to revoke and select “Delete”.
4. Your API key will stop working immediately.
diff --git a/apps/formbricks-com/pages/docs/self-hosting/deployment/index.mdx b/apps/formbricks-com/pages/docs/self-hosting/deployment/index.mdx
index 9dbd8a00a1..669ddd0c76 100644
--- a/apps/formbricks-com/pages/docs/self-hosting/deployment/index.mdx
+++ b/apps/formbricks-com/pages/docs/self-hosting/deployment/index.mdx
@@ -8,24 +8,56 @@ export const meta = {
"Utilize Docker-Compose for easy deployment on your machine. Clone the repo, configure settings, and build the image to access the app on localhost.",
};
-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 provide two ways of running our application using Docker:
+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 provide three ways of running our application:
-1. **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.
+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.
-2. **Manual Setup by Building the Docker Image from Source:** This approach provides the flexibility to configure every aspect of your Formbricks instance, including environment variables that need to be set at build time. While we don't recommend changing the source code of Formbricks, this method allows you to set your own configuration that might be necessary for specific deployment needs. Keep in mind that this method requires a more in-depth understanding of Docker and the build process. However, the trade-off is the additional control and flexibility you gain, making it worth considering if you're a more advanced user or have very specific configuration needs.
+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.
+
+3. **Manual Setup by Building the Docker Image from Source:** This approach provides the flexibility to configure every aspect of your Formbricks instance, including environment variables that need to be set at build time. While we don't recommend changing the source code of Formbricks, this method allows you to set your own configuration that might be necessary for specific deployment needs. Keep in mind that this method requires a more in-depth understanding of Docker and the build process. However, the trade-off is the additional control and flexibility you gain, making it worth considering if you're a more advanced user or have very specific configuration needs.
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!
---
-## Requirements
+## (Production: Ubuntu) Running the Shell Script
+
+This is the quickest way to get Formbricks up and running on an Ubuntu server. The shell script will automatically install all the required dependencies and configure your server, making the process a breeze.
+
+### Requirements
+
+Before you proceed, make sure you have the following:
+
+- A Linux Ubuntu Virtual Machine deployed with SSH access.
+
+- An A record set up to connect a custom domain to your instance. Formbricks will automatically create an SSL certificate for your domain using Let's Encrypt.
+
+## Single Command Setup
+
+Copy and paste the following command into your terminal:
+
+```bash
+/bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/formbricks/formbricks/main/docker/production.sh)"
+```
+
+The script will prompt you for the following information:
+
+1. **Overwriting Docker GPG Keys**: If Docker GPG keys already exist, the script will ask if you want to overwrite them.
+
+2. **Email Address**: Provide your email address for SSL certificate registration with Let's Encrypt.
+
+3. **Domain Name**: Enter the domain name that Traefik will use to create the SSL certificate and forward requests to Formbricks.
+
+That's it! After running the command and providing the required information, visit the domain name you entered, and you should see the Formbricks home wizard!
+
+## (Most users: Local Setup) Running the pre-built Docker Image
+
+### Requirements
Ensure `docker` & `docker compose` are installed on your server/system. Both are typically included with Docker utilities, like Docker Desktop and Rancher Desktop.
**Note**: `docker compose` without the hyphen is now the primary method of using docker-compose, according to the Docker documentation.
-## (Most users) Running the pre-built Docker Image
-
This is suitable for those who are testing Formbricks or running it with minimal to no modifications. For this we use the [public Docker image](https://hub.docker.com/r/formbricks/formbricks) and a simple docker-compose file.
1. **Create a New Directory for Formbricks**
@@ -89,6 +121,12 @@ This is suitable for those who are testing Formbricks or running it with minimal
## (Advanced users) Build and Run Formbricks
+### Requirements
+
+Ensure `docker` & `docker compose` are installed on your server/system. Both are typically included with Docker utilities, like Docker Desktop and Rancher Desktop.
+
+**Note**: `docker compose` without the hyphen is now the primary method of using docker-compose, according to the Docker documentation.
+
1. Clone the repository:
```bash
@@ -164,4 +202,16 @@ docker compose logs -f
You can close the logs again with `CTRL + C`.
+## Troubleshooting for the Shell Script Setup
+
+If you encounter any issues, consider the following steps:
+
+- **Inbound Rules**: Make sure you have added inbound rules for Port 80 and 443 in your VM's Security Group.
+
+- **A Record**: Verify that you have set up an A record for your domain, pointing to your VM's IP address.
+
+- **Check Docker Instances**: Run `docker ps` to check the status of the Docker instances.
+
+- **Check Formbricks Logs**: Run `cd formbricks && docker compose logs` to check the logs of the Formbricks stack.
+
export default ({ children }) => {children};
diff --git a/apps/formbricks-com/pages/docs/webhook-api/webhook-payload/index.mdx b/apps/formbricks-com/pages/docs/webhook-api/webhook-payload/index.mdx
new file mode 100644
index 0000000000..6dedb30726
--- /dev/null
+++ b/apps/formbricks-com/pages/docs/webhook-api/webhook-payload/index.mdx
@@ -0,0 +1,69 @@
+import { Layout } from "@/components/docs/Layout";
+import { Fence } from "@/components/shared/Fence";
+
+export const meta = {
+ title: "Webhook Payload",
+ description: "Learn how to handle the Formbricks API payload.",
+};
+
+This documentation helps understand the payload structure that will be received when the webhook is triggered in Formbricks.
+
+## An example webhook payload
+
+```
+{
+ "webhookId": "cljwxvjos0003qhnvj2jg4k5i",
+ "event": "responseCreated",
+ "data": {
+ "id": "cljwy2m8r0001qhclco1godnu",
+ "createdAt": "2023-07-10T14:14:17.115Z",
+ "updatedAt": "2023-07-10T14:14:17.115Z",
+ "surveyId": "cljsf3d7a000019cv9apt2t27",
+ "finished": false,
+ "data": {
+ "qumbk3fkr6cky8850bvvq5z1": "Executive"
+ },
+ "meta": {
+ "userAgent": {
+ "os": "Mac OS",
+ "browser": "Chrome"
+ }
+ },
+ "personAttributes": {
+ "email": "test@web.com",
+ "userId": "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING"
+ },
+ "person": {
+ "id": "cljold01t0000qh8ewzigzmjk",
+ "attributes": {
+ "email": "test@web.com",
+ "userId": "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING"
+ },
+ "createdAt": "2023-07-04T17:56:17.154Z",
+ "updatedAt": "2023-07-04T17:56:17.154Z"
+ },
+ "notes": [],
+ "tags": []
+ }
+}
+
+```
+
+| Variable | Type | Description |
+| --------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| webhookId | String | Webhook's Id |
+| event | String | The name of the trigger event [responseCreated, responseUpdated, responseFinished] |
+| data | Object | Contains the details of the newly created response. |
+| data.id | String | Formbricks Response ID. |
+| data.createdAt | String | The timestamp when the response was created. |
+| data.updatedAt | String | The timestamp when the response was last updated. |
+| data.surveyId | String | The identifier of the survey associated with this response. |
+| data.finished | Boolean | A boolean value indicating whether the survey response is marked as finished. |
+| data.data | Object | An object containing the response data, where keys are question identifiers, and values are the corresponding answers given by the respondent. |
+| data.meta | Object | Additional metadata related to the response, such as the user's operating system and browser information. |
+| data.personAttributes | Object | An object with attributes related to the respondent, such as their email and a user ID (if available). |
+| data.person | Object | Information about the respondent, including their unique id, attributes, and creation/update timestamps. |
+| data.notes | Array | An array of notes associated with the response (if any). |
+| data.tags | Array | An array of tags assigned to the response (if any). |
+
+export default ({ children }) => {children};
diff --git a/apps/formbricks-com/pages/imprint.mdx b/apps/formbricks-com/pages/imprint.mdx
index 0557b392a1..acdd0eb5f9 100644
--- a/apps/formbricks-com/pages/imprint.mdx
+++ b/apps/formbricks-com/pages/imprint.mdx
@@ -2,6 +2,7 @@ import Layout from "@/components/shared/LayoutMdx";
export const meta = {
title: "Imprint",
+ description: "Imprint of formbricks.com",
};
## Information according to § 5 TMG
@@ -17,19 +18,24 @@ E-Mail: hola@formbricks.com
## EU dispute resolution
-The European Commission provides a platform for online dispute resolution (OS): https://ec.europa.eu/consumers/odr.\
-You can find our e-mail address in the imprint above.\
-Consumer dispute resolution/universal dispute resolution body\
+The European Commission provides a platform for online dispute resolution (OS): https://ec.europa.eu/consumers/odr
+
+You can also reach out via the e-mail address in the imprint above.
+
+### Consumer dispute resolution/universal dispute resolution body
+
We are not willing or obliged to participate in dispute resolution proceedings before a consumer arbitration board.
## Liability for contents
As a service provider, we are responsible for our own content on these pages in accordance with § 7 paragraph 1 TMG under the general laws. According to §§ 8 to 10 TMG, we are not obligated to monitor transmitted or stored information or to investigate circumstances that indicate illegal activity.\
+
Obligations to remove or block the use of information under the general laws remain unaffected. However, liability in this regard is only possible from the point in time at which a concrete infringement of the law becomes known. If we become aware of any such infringements, we will remove the relevant content immediately.
## Liability for links
Our offer contains links to external websites of third parties, on whose contents we have no influence. Therefore, we cannot assume any liability for these external contents. The respective provider or operator of the sites is always responsible for the content of the linked sites. The linked pages were checked for possible legal violations at the time of linking. Illegal contents were not recognizable at the time of linking.\
+
However, a permanent control of the contents of the linked pages is not reasonable without concrete evidence of a violation of the law. If we become aware of any infringements, we will remove such links immediately.
## Copyright
diff --git a/apps/formbricks-com/pages/oss-friends.tsx b/apps/formbricks-com/pages/oss-friends.tsx
index fb1521b075..eea674fc0a 100644
--- a/apps/formbricks-com/pages/oss-friends.tsx
+++ b/apps/formbricks-com/pages/oss-friends.tsx
@@ -2,105 +2,17 @@ import Layout from "@/components/shared/Layout";
import HeroTitle from "@/components/shared/HeroTitle";
import { Button } from "@formbricks/ui";
-export const OSSFriends = [
- {
- name: "BoxyHQ",
- description:
- "BoxyHQ’s suite of APIs for security and privacy helps engineering teams build and ship compliant cloud applications faster.",
- href: "https://boxyhq.com",
- },
- {
- name: "Cal.com",
- description:
- "Cal.com is a scheduling tool that helps you schedule meetings without the back-and-forth emails.",
- href: "https://cal.com",
- },
- {
- name: "Crowd.dev",
- description:
- "Centralize community, product, and customer data to understand which companies are engaging with your open source project.",
- href: "https://www.crowd.dev",
- },
- {
- name: "Documenso",
- description:
- "The Open-Source DocuSign Alternative. We aim to earn your trust by enabling you to self-host the platform and examine its inner workings.",
- href: "https://documenso.com",
- },
- {
- name: "Erxes",
- description:
- "The Open-Source HubSpot Alternative. A single XOS enables to create unique and life-changing experiences that work for all types of business.",
- href: "https://erxes.io",
- },
- {
- name: "Formbricks",
- description:
- "Survey granular user segments at any point in the user journey. Gather up to 6x more insights with targeted micro-surveys. All open-source.",
- href: "https://formbricks.com",
- },
- {
- name: "GitWonk",
- description:
- "GitWonk is an open-source technical documentation tool, designed and built focusing on the developer experience.",
- href: "https://gitwonk.com",
- },
- {
- name: "Hanko",
- description:
- "Open-source authentication and user management for the passkey era. Integrated in minutes, for web and mobile apps.",
- href: "https://www.hanko.io",
- },
- {
- name: "HTMX",
- description:
- "HTMX is a dependency-free JavaScript library that allows you to access AJAX, CSS Transitions, WebSockets, and Server Sent Events directly in HTML.",
- href: "https://htmx.org",
- },
- {
- name: "Infisical",
- description:
- "Open source, end-to-end encrypted platform that lets you securely manage secrets and configs across your team, devices, and infrastructure.",
- href: "https://infisical.com",
- },
- {
- name: "Novu",
- description:
- "The open-source notification infrastructure for developers. Simple components and APIs for managing all communication channels in one place.",
- href: "https://novu.co",
- },
- {
- name: "OpenBB",
- description:
- "Democratizing investment research through an open source financial ecosystem. The OpenBB Terminal allows everyone to perform investment research, from everywhere.",
- href: "https://openbb.co",
- },
- {
- name: "Sniffnet",
- description:
- "Sniffnet is a network monitoring tool to help you easily keep track of your Internet traffic.",
- href: "https://www.sniffnet.net",
- },
- {
- name: "Typebot",
- description:
- "Typebot gives you powerful blocks to create unique chat experiences. Embed them anywhere on your apps and start collecting results like magic.",
- href: "https://typebot.io",
- },
- {
- name: "Webiny",
- description:
- "Open-source enterprise-grade serverless CMS. Own your data. Scale effortlessly. Customize everything.",
- href: "https://www.webiny.com",
- },
- {
- name: "Webstudio",
- description: "Webstudio is an open source alternative to Webflow",
- href: "https://webstudio.is",
- },
-];
+type OSSFriend = {
+ href: string;
+ name: string;
+ description: string;
+};
-export default function OSSFriendsPage() {
+type Props = {
+ OSSFriends: OSSFriend[];
+};
+
+export default function OSSFriendsPage({ OSSFriends }: Props) {
return (
@@ -122,3 +34,16 @@ export default function OSSFriendsPage() {
);
}
+
+export async function getStaticProps() {
+ const res = await fetch("https://formbricks.com/api/oss-friends");
+ const data = await res.json();
+
+ // By returning { props: { OSSFriends } }, the OSSFriendsPage component
+ // will receive `OSSFriends` as a prop at build time
+ return {
+ props: {
+ OSSFriends: data.data,
+ },
+ };
+}
diff --git a/apps/formbricks-com/pages/privacy-policy.mdx b/apps/formbricks-com/pages/privacy-policy.mdx
index 966f5f2f55..dae5a83c9a 100644
--- a/apps/formbricks-com/pages/privacy-policy.mdx
+++ b/apps/formbricks-com/pages/privacy-policy.mdx
@@ -2,6 +2,7 @@ import Layout from "@/components/shared/LayoutMdx";
export const meta = {
title: "Privacy Policy",
+ description: "Formbricks Privacy Policy",
};
## **1. Introduction**
diff --git a/apps/formbricks-com/pages/terms.mdx b/apps/formbricks-com/pages/terms.mdx
index 8ed7704142..f42962b758 100644
--- a/apps/formbricks-com/pages/terms.mdx
+++ b/apps/formbricks-com/pages/terms.mdx
@@ -3,6 +3,7 @@ import { Callout } from "@/components/shared/Callout";
export const meta = {
title: "Terms of Service",
+ description: "Terms of Service of Formbricks Cloud.",
};
These Terms of Use constitute a legally binding agreement made between you, whether personally or on behalf of an entity (“you”) and Formbricks ("**Company**", “**we**”, “**us**”, or “**our**”), concerning your access to and use of the https://formbricks.com website as well as any other media form, media channel, mobile website or mobile application related, linked, or otherwise connected thereto (collectively, the “Site”). You agree that by accessing the Site, you have read, understood, and agree to be bound by all of these Terms of Use. If you do not agree with all of these terms of use, then you are expressly prohibited from using the site and you must discontinue use immediately.
diff --git a/apps/web/.gitignore b/apps/web/.gitignore
index c87c9b392c..65d82cc1cd 100644
--- a/apps/web/.gitignore
+++ b/apps/web/.gitignore
@@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# Sentry Auth Token
+.sentryclirc
diff --git a/apps/web/CHANGELOG.md b/apps/web/CHANGELOG.md
new file mode 100644
index 0000000000..a25499918d
--- /dev/null
+++ b/apps/web/CHANGELOG.md
@@ -0,0 +1,15 @@
+# @formbricks/web
+
+## 0.1.2
+
+### Patch Changes
+
+- Updated dependencies [a1b447ca]
+ - @formbricks/js@1.0.2
+
+## 0.1.1
+
+### Patch Changes
+
+- Updated dependencies [3d0d633b]
+ - @formbricks/js@1.0.1
diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile
index b22ebe0016..8b0cf565eb 100644
--- a/apps/web/Dockerfile
+++ b/apps/web/Dockerfile
@@ -36,6 +36,8 @@ COPY --from=installer --chown=nextjs:nodejs /app/packages/database/migrations ./
ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET
+EXPOSE 3000
+
CMD if [ "$NEXTAUTH_SECRET" != "RANDOM_STRING" ]; then \
pnpm dlx prisma migrate deploy && node apps/web/server.js; \
else \
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/PreviewSurvey.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/PreviewSurvey.tsx
index 1410aaa64e..0fb77a681e 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/PreviewSurvey.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/PreviewSurvey.tsx
@@ -41,6 +41,11 @@ export default function PreviewSurvey({
const [widgetSetupCompleted, setWidgetSetupCompleted] = useState(false);
const [lastActiveQuestionId, setLastActiveQuestionId] = useState("");
const [showFormbricksSignature, setShowFormbricksSignature] = useState(false);
+ const [finished, setFinished] = useState(false);
+ const [storedResponseValue, setStoredResponseValue] = useState();
+ const [storedResponse, setStoredResponse] = useState>({});
+
+ const showBackButton = progress !== 0 && !finished;
useEffect(() => {
if (product) {
@@ -129,54 +134,54 @@ export default function PreviewSurvey({
}
}, [activeQuestionId, surveyType, questions, setActiveQuestionId, thankYouCard]);
- function evaluateCondition(logic: Logic, answerValue: any): boolean {
+ function evaluateCondition(logic: Logic, responseValue: any): boolean {
switch (logic.condition) {
case "equals":
return (
- (Array.isArray(answerValue) && answerValue.length === 1 && answerValue.includes(logic.value)) ||
- answerValue.toString() === logic.value
+ (Array.isArray(responseValue) && responseValue.length === 1 && responseValue.includes(logic.value)) ||
+ responseValue.toString() === logic.value
);
case "notEquals":
- return answerValue !== logic.value;
+ return responseValue !== logic.value;
case "lessThan":
- return logic.value !== undefined && answerValue < logic.value;
+ return logic.value !== undefined && responseValue < logic.value;
case "lessEqual":
- return logic.value !== undefined && answerValue <= logic.value;
+ return logic.value !== undefined && responseValue <= logic.value;
case "greaterThan":
- return logic.value !== undefined && answerValue > logic.value;
+ return logic.value !== undefined && responseValue > logic.value;
case "greaterEqual":
- return logic.value !== undefined && answerValue >= logic.value;
+ return logic.value !== undefined && responseValue >= logic.value;
case "includesAll":
return (
- Array.isArray(answerValue) &&
+ Array.isArray(responseValue) &&
Array.isArray(logic.value) &&
- logic.value.every((v) => answerValue.includes(v))
+ logic.value.every((v) => responseValue.includes(v))
);
case "includesOne":
return (
- Array.isArray(answerValue) &&
+ Array.isArray(responseValue) &&
Array.isArray(logic.value) &&
- logic.value.some((v) => answerValue.includes(v))
+ logic.value.some((v) => responseValue.includes(v))
);
case "accepted":
- return answerValue === "accepted";
+ return responseValue === "accepted";
case "clicked":
- return answerValue === "clicked";
+ return responseValue === "clicked";
case "submitted":
- if (typeof answerValue === "string") {
- return answerValue !== "dismissed" && answerValue !== "" && answerValue !== null;
- } else if (Array.isArray(answerValue)) {
- return answerValue.length > 0;
- } else if (typeof answerValue === "number") {
- return answerValue !== null;
+ if (typeof responseValue === "string") {
+ return responseValue !== "dismissed" && responseValue !== "" && responseValue !== null;
+ } else if (Array.isArray(responseValue)) {
+ return responseValue.length > 0;
+ } else if (typeof responseValue === "number") {
+ return responseValue !== null;
}
return false;
case "skipped":
return (
- (Array.isArray(answerValue) && answerValue.length === 0) ||
- answerValue === "" ||
- answerValue === null ||
- answerValue === "dismissed"
+ (Array.isArray(responseValue) && responseValue.length === 0) ||
+ responseValue === "" ||
+ responseValue === null ||
+ responseValue === "dismissed"
);
default:
return false;
@@ -191,14 +196,14 @@ export default function PreviewSurvey({
const currentQuestionIndex = questions.findIndex((q) => q.id === activeQuestionId);
if (currentQuestionIndex === -1) throw new Error("Question not found");
- const answerValue = answer[activeQuestionId];
+ const responseValue = answer[activeQuestionId];
const currentQuestion = questions[currentQuestionIndex];
if (currentQuestion.logic && currentQuestion.logic.length > 0) {
for (let logic of currentQuestion.logic) {
if (!logic.destination) continue;
- if (evaluateCondition(logic, answerValue)) {
+ if (evaluateCondition(logic, responseValue)) {
return logic.destination;
}
}
@@ -207,11 +212,13 @@ export default function PreviewSurvey({
}
const gotoNextQuestion = (data) => {
+ setStoredResponse({ ...storedResponse, ...data });
const nextQuestionId = getNextQuestion(data);
-
+ setStoredResponseValue(storedResponse[nextQuestionId]);
if (nextQuestionId !== "end") {
setActiveQuestionId(nextQuestionId);
} else {
+ setFinished(true);
if (thankYouCard?.enabled) {
setActiveQuestionId("thank-you-card");
setProgress(1);
@@ -225,6 +232,15 @@ export default function PreviewSurvey({
}
};
+ function goToPreviousQuestion(data: any) {
+ setStoredResponse({ ...storedResponse, ...data });
+ const currentQuestionIndex = questions.findIndex((q) => q.id === activeQuestionId);
+ if (currentQuestionIndex === -1) throw new Error("Question not found");
+ const previousQuestionId = questions[currentQuestionIndex - 1].id;
+ setStoredResponseValue(storedResponse[previousQuestionId]);
+ setActiveQuestionId(previousQuestionId);
+ }
+
useEffect(() => {
if (environment && environment.widgetSetupCompleted) {
setWidgetSetupCompleted(true);
@@ -280,6 +296,10 @@ export default function PreviewSurvey({
brandColor={brandColor}
lastQuestion={idx === questions.length - 1}
onSubmit={gotoNextQuestion}
+ storedResponseValue={storedResponseValue}
+ goToNextQuestion={gotoNextQuestion}
+ goToPreviousQuestion={showBackButton ? goToPreviousQuestion : undefined}
+ autoFocus={false}
/>
) : null
)
@@ -307,6 +327,10 @@ export default function PreviewSurvey({
brandColor={brandColor}
lastQuestion={idx === questions.length - 1}
onSubmit={gotoNextQuestion}
+ storedResponseValue={storedResponseValue}
+ goToNextQuestion={gotoNextQuestion}
+ goToPreviousQuestion={showBackButton ? goToPreviousQuestion : undefined}
+ autoFocus={false}
/>
) : null
)
diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/SummaryHeader.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/SummaryHeader.tsx
index e8fc53fa19..04113ca12a 100644
--- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/SummaryHeader.tsx
+++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/SummaryHeader.tsx
@@ -4,11 +4,6 @@ import { useEnvironment } from "@/lib/environments/environments";
import { useProduct } from "@/lib/products/products";
import { TSurvey } from "@formbricks/types/v1/surveys";
import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
- SelectValue,
Button,
DropdownMenu,
DropdownMenuContent,
@@ -22,13 +17,7 @@ import {
DropdownMenuTrigger,
ErrorComponent,
} from "@formbricks/ui";
-import {
- CheckCircleIcon,
- PauseCircleIcon,
- PlayCircleIcon,
- PencilSquareIcon,
- EllipsisHorizontalIcon,
-} from "@heroicons/react/24/solid";
+import { PencilSquareIcon, EllipsisHorizontalIcon } from "@heroicons/react/24/solid";
import SurveyStatusIndicator from "@/components/shared/SurveyStatusIndicator";
import { useSurveyMutation } from "@/lib/surveys/mutateSurveys";
import toast from "react-hot-toast";
@@ -36,6 +25,7 @@ import { useRouter } from "next/navigation";
import SuccessMessage from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/SuccessMessage";
import LinkSurveyShareButton from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/LinkModalButton";
import LoadingSpinner from "@/components/shared/LoadingSpinner";
+import SurveyStatusDropdown from "@/components/shared/SurveyStatusDropdown";
interface SummaryHeaderProps {
surveyId: string;
@@ -48,6 +38,10 @@ const SummaryHeader = ({ surveyId, environmentId, survey }: SummaryHeaderProps)
const { environment, isLoadingEnvironment, isErrorEnvironment } = useEnvironment(environmentId);
const { triggerSurveyMutate } = useSurveyMutation(environmentId, surveyId);
+ const isCloseOnDateEnabled = survey.closeOnDate !== null;
+ const closeOnDate = survey.closeOnDate ? new Date(survey.closeOnDate) : null;
+ const isStatusChangeDisabled = (isCloseOnDateEnabled && closeOnDate && closeOnDate < new Date()) ?? false;
+
if (isLoadingProduct || isLoadingEnvironment) {
return ;
}
@@ -64,53 +58,7 @@ const SummaryHeader = ({ surveyId, environmentId, survey }: SummaryHeaderProps)