Compare commits
13 Commits
feature/ve
...
feature/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7725d3d8a2 | ||
|
|
d722e9de9e | ||
|
|
560b8904f7 | ||
|
|
b68dcdf2dc | ||
|
|
2da2758255 | ||
|
|
b322f014ab | ||
|
|
d408d42f00 | ||
|
|
27875deb44 | ||
|
|
dcc98b671e | ||
|
|
4f5acbcddc | ||
|
|
a9bf8fcdaa | ||
|
|
87f2f8d870 | ||
|
|
4ae38546f0 |
@@ -173,6 +173,9 @@ ENTERPRISE_LICENSE_KEY=
|
||||
# OpenTelemetry URL for tracing
|
||||
# OPENTELEMETRY_LISTENER_URL=http://localhost:4318/v1/traces
|
||||
|
||||
# Unsplash API Key
|
||||
UNSPLASH_ACCESS_KEY=
|
||||
|
||||
# The below is used for Next Caching (uses In-Memory from Next Cache if not provided)
|
||||
# REDIS_URL:
|
||||
|
||||
|
||||
25
apps/demo/components/SurveySwitch.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@formbricks/ui/Select";
|
||||
|
||||
interface SurveySwitchProps {
|
||||
value: "website" | "app";
|
||||
formbricks: any;
|
||||
}
|
||||
|
||||
export const SurveySwitch = ({ value, formbricks }: SurveySwitchProps) => {
|
||||
return (
|
||||
<Select
|
||||
value={value}
|
||||
onValueChange={(v) => {
|
||||
formbricks.logout();
|
||||
window.location.href = `/${v}`;
|
||||
}}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Theme" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="website">Website Surveys</SelectItem>
|
||||
<SelectItem value="app">App Surveys</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
transpilePackages: ["@formbricks/ui"],
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"lucide-react": "^0.368.0",
|
||||
"next": "14.2.1",
|
||||
"react": "18.2.0",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { EarthIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import formbricksApp from "@formbricks/js/app";
|
||||
|
||||
import { SurveySwitch } from "../../components/SurveySwitch";
|
||||
import fbsetup from "../../public/fb-setup.png";
|
||||
|
||||
declare const window: any;
|
||||
@@ -57,27 +57,11 @@ export default function AppPage({}) {
|
||||
}
|
||||
});
|
||||
|
||||
const removeFormbricksContainer = () => {
|
||||
document.getElementById("formbricks-modal-container")?.remove();
|
||||
document.getElementById("formbricks-app-container")?.remove();
|
||||
localStorage.removeItem("formbricks-js-app");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
||||
<div className="flex flex-col justify-between md:flex-row">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="rounded-lg bg-[#038178] p-2 text-white focus:outline-none focus:ring-2 focus:ring-slate-900 focus:ring-offset-1"
|
||||
onClick={() => {
|
||||
removeFormbricksContainer();
|
||||
window.location.href = "/website";
|
||||
}}>
|
||||
<div className="flex items-center gap-2">
|
||||
<EarthIcon className="h-10 w-10" />
|
||||
<span>Website Demo</span>
|
||||
</div>
|
||||
</button>
|
||||
<SurveySwitch value="app" formbricks={formbricksApp} />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">
|
||||
Formbricks In-product Survey Demo App
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
|
||||
|
||||
import formbricksWebsite from "@formbricks/js/website";
|
||||
|
||||
import { SurveySwitch } from "../../components/SurveySwitch";
|
||||
import fbsetup from "../../public/fb-setup.png";
|
||||
|
||||
declare const window: any;
|
||||
@@ -57,27 +58,11 @@ export default function AppPage({}) {
|
||||
}
|
||||
});
|
||||
|
||||
const removeFormbricksContainer = () => {
|
||||
document.getElementById("formbricks-modal-container")?.remove();
|
||||
document.getElementById("formbricks-website-container")?.remove();
|
||||
localStorage.removeItem("formbricks-js-website");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
||||
<div className="flex flex-col items-center justify-between md:flex-row">
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
className="rounded-lg bg-[#038178] p-2 text-white focus:outline-none focus:ring-2 focus:ring-slate-900 focus:ring-offset-1"
|
||||
onClick={() => {
|
||||
removeFormbricksContainer();
|
||||
window.location.href = "/app";
|
||||
}}>
|
||||
<div className="flex items-center gap-2">
|
||||
<MonitorIcon className="h-10 w-10" />
|
||||
<span>App Demo</span>
|
||||
</div>
|
||||
</button>
|
||||
<SurveySwitch value="website" formbricks={formbricksWebsite} />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-slate-900 dark:text-white">
|
||||
Formbricks Website Survey Demo App
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
137
apps/formbricks-com/app/docs/additional-features/api/page.mdx
Normal file
@@ -0,0 +1,137 @@
|
||||
import { MdxImage } from "@/components/shared/MdxImage";
|
||||
import AddApiKey from "./add-api-key.webp";
|
||||
import ApiKeySecret from "./api-key-secret.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks API Overview: Public Client & Management API Breakdown",
|
||||
description:
|
||||
"Formbricks provides a powerful API to manage your surveys, responses, users, displays, actions, attributes & webhooks programmatically. 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
|
||||
|
||||
# API Overview
|
||||
|
||||
Formbricks offers two types of APIs: the **Public Client API** and the **Management API**. Each API serves a different purpose, has different authentication requirements, and provides access to different data and settings.
|
||||
|
||||
View our [API Documentation](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh) in more than 30 frameworks and languages. Or directly try out our APIs in Postman by clicking the button below:
|
||||
|
||||
<div className="max-w-full sm:max-w-3xl">
|
||||
<a target="_blank" href="https://formbricks.postman.co/collection/11026000-927c954f-85a9-4f8f-b0ec-14191b903737?source=rip_html">
|
||||
<img alt="Run in Postman" src="https://run.pstmn.io/button.svg"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Public Client API
|
||||
|
||||
The [Public Client API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#5c981d9e-5e7d-455d-9795-b9c45bc2f930) is designed for our SDKs and **does not require authentication**. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
We currently have the following Client API methods exposed and below is their documentation attached in Postman:
|
||||
|
||||
- [Actions API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#b8f3a10e-1642-4d82-a629-fef0a8c6c86c) - Create actions for a Person
|
||||
- [Displays API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#949272bf-daec-4d72-9b52-47af3d74a62c) - Mark Survey as Displayed or Update an existing Display by linking it with a Response for a Person
|
||||
- [People API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#ee3d2188-4253-4bca-9238-6b76455805a9) - Create & Update a Person (e.g. attributes, email, userId, etc)
|
||||
- [Responses API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#8c773032-536c-483c-a237-c7697347946e) - Create & Update a Response for a Survey
|
||||
|
||||
## Management API
|
||||
|
||||
The [Management API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#98fce5a1-1365-4125-8de1-acdb28206766) provides access to all data and settings that your account has access to in the Formbricks app. This API **requires a personal API Key** for authentication, which can be generated in the Settings section of the Formbricks app. Checkout the [API Key Setup](#how-to-generate-an-api-key) below to generate & manage API Keys.
|
||||
|
||||
We currently have the following Management API methods exposed and below is their documentation attached in Postman:
|
||||
|
||||
- [Action Class API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#81947f69-99fc-41c9-a184-f3260e02be48) - Create, List, and Delete Action Classes
|
||||
- [Attribute Class API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#31089010-d468-4a7c-943e-8ebe71b9a36e) - Create, List, and Delete Attribute Classes
|
||||
- [Me API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#79e08365-641d-4b2d-aea2-9a855e0438ec) - Retrieve Account Information
|
||||
- [People API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#cffc27a6-dafb-428f-8ea7-5165bedb911e) - List and Delete People
|
||||
- [Response API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#e544ec0d-8b30-4e33-8d35-2441cb40d676) - List, List by Survey, Update, and Delete Responses
|
||||
- [Survey API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#953189b2-37b5-4429-a7bd-f4d01ceae242) - List, Create, Update, and Delete Surveys
|
||||
- [Webhook API](https://documenter.getpostman.com/view/11026000/2sA3Bq5XEh#62e6ec65-021b-42a4-ac93-d1434b393c6c) - List, Create, and Delete Webhooks
|
||||
|
||||
## How to Generate an API key
|
||||
|
||||
The API requests are authorized with a personal API key. This API key gives you the same rights as if you were logged in at formbricks.com - **don't share it around!**
|
||||
|
||||
1. Go to your settings on [app.formbricks.com](https://app.formbricks.com).
|
||||
2. Go to page “API keys”
|
||||
<MdxImage src={AddApiKey} alt="Add API Key" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
3. Create a key for the development or production environment.
|
||||
4. Copy the key immediately. You won’t be able to see it again.
|
||||
<MdxImage
|
||||
src={ApiKeySecret}
|
||||
alt="API Key Secret"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
<Note>
|
||||
### Store API key safely!
|
||||
Anyone who has your API key has full control over your account. For security reasons, you cannot view the API key again.
|
||||
</Note>
|
||||
|
||||
### Test your API Key
|
||||
|
||||
Hit the below request to verify that you are authenticated with your API Key and the server is responding.
|
||||
|
||||
## Get My Profile {{ tag: 'GET', label: '/api/v1/me' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get the product details and environment type of your account.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Delete a personal API key
|
||||
|
||||
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.
|
||||
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/me">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/me' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"id": "cll2m30r70004mx0huqkitgqv",
|
||||
"createdAt": "2023-08-08T18:04:59.922Z",
|
||||
"updatedAt": "2023-08-08T18:04:59.922Z",
|
||||
"type": "production",
|
||||
"product": {
|
||||
"id": "cll2m30r60003mx0hnemjfckr",
|
||||
"name": "My Product"
|
||||
},
|
||||
"widgetSetupCompleted": false
|
||||
}
|
||||
```
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
Not authenticated
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
Can’t figure it out? Join our [Discord](https://discord.com/invite/3YFcABF2Ts) and we'd be glad to assist you!
|
||||
|
||||
---
|
||||
@@ -1,94 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Actions API Documentation - Manage Your Survey Data Seamlessly",
|
||||
description:
|
||||
"Unlock the full potential of Formbricks' Client Actions API. Create Actions right from the API.",
|
||||
};
|
||||
|
||||
#### Client API
|
||||
|
||||
# Actions API
|
||||
|
||||
The Public Client API is designed for the JavaScript SDK and does not require authentication. It's primarily used for creating persons, sessions, and responses within the Formbricks platform. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
This API can be used to:
|
||||
|
||||
- [Add Action for User](#add-action-for-user)
|
||||
|
||||
---
|
||||
|
||||
## Add Action for User {{ tag: 'POST', label: '/api/v1/client/<environment-id>/actions' }}
|
||||
|
||||
Adds an Actions for a given User by their User ID
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
### Mandatory Body Fields
|
||||
|
||||
<Properties>
|
||||
<Property name="userId" type="string">
|
||||
The id of the user for whom the action is being created.
|
||||
|
||||
Note: A user with this ID must exist in your environment in Formbricks.
|
||||
</Property>
|
||||
<Property name="name" type="string">
|
||||
The name of the Action being created.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/client/<environment-id>/actions">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST 'https://app.formbricks.com/api/v1/client/<environment-id>/actions' \
|
||||
--data-raw '{
|
||||
"userId": "1",
|
||||
"name": "new_action_v2"
|
||||
|
||||
}'
|
||||
```
|
||||
|
||||
```json {{ title: 'Example Request Body' }}
|
||||
{
|
||||
"userId": "1",
|
||||
"name": "new_action_v3"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "Fields are missing or incorrectly formatted",
|
||||
"details": {
|
||||
"name": "Required"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '500 Internal Server Error' }}
|
||||
{
|
||||
"code": "internal_server_error",
|
||||
"message": "Unable to handle the request: Database operation failed",
|
||||
"details": {}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,145 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Public Client API Guide: Manage Survey Displays & Responses",
|
||||
description:
|
||||
"Dive deep into Formbricks' Public Client API designed for customisation. This comprehensive guide provides detailed instructions on how to mark create and update survey displays for users.",
|
||||
};
|
||||
|
||||
#### Client API
|
||||
|
||||
# Displays API
|
||||
|
||||
The Public Client API is designed for the JavaScript SDK and does not require authentication. It's primarily used for creating persons, sessions, and responses within the Formbricks platform. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
This set of API can be used to
|
||||
- [Create Display](#create-display)
|
||||
- [Update Display](#update-display)
|
||||
|
||||
---
|
||||
|
||||
## Create Display {{ tag: 'POST', label: '/api/v1/client/<environment-id>/diplays' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Create Display of survey for a user
|
||||
|
||||
### Mandatory Request Body JSON Keys
|
||||
<Properties>
|
||||
<Property name="surveyId" type="string">
|
||||
Survey ID to mark as viewed for a person
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Optional Request Body JSON Keys
|
||||
<Properties>
|
||||
<Property name="userId" type="string">
|
||||
Already existing user's ID to mark as viewed for a survey
|
||||
</Property>
|
||||
<Property name="responseId" type="string">
|
||||
Already existing response's ID to link with this new Display
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/client/<environment-id>/displays">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST \
|
||||
'https://app.formbricks.com/api/v1/client/displays' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"surveyId":"<survey-id>",
|
||||
"userId":"<user-id>"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "clphzz6oo00083zdmc7e0nwzi"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "Fields are missing or incorrectly formatted",
|
||||
"details": {
|
||||
"surveyId": "Required"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Update Display {{ tag: 'PUT', label: '/api/v1/client/<environment-id>/diplays/<display-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Update a display by it's ID
|
||||
|
||||
### Optional Request Body JSON Keys
|
||||
<Properties>
|
||||
<Property name="userId" type="string">
|
||||
Already existing user's ID to mark as viewed for a survey
|
||||
</Property>
|
||||
<Property name="responseId" type="string">
|
||||
Already existing response's ID to link with this new Display
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="PUT" label="/api/v1/client/<environment-id>/displays/<display-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST \
|
||||
'https://app.formbricks.com/api/v1/client/<environment-id>/displays/<display-id>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"userId":"<user-id>"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "Fields are missing or incorrectly formatted",
|
||||
"details": {
|
||||
"surveyId": "Required"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,47 +0,0 @@
|
||||
export const metadata = {
|
||||
title: "Formbricks API Overview: Public Client & Management API Breakdown",
|
||||
description:
|
||||
"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
|
||||
|
||||
# API Overview
|
||||
|
||||
Formbricks offers two types of APIs: the **Public Client API** and the **Management API**. Each API serves a different purpose, has different authentication requirements, and provides access to different data and settings.
|
||||
|
||||
Checkout the [API Key Setup](/docs/api/management/api-key-setup) - to generate, store, or delete API Keys.
|
||||
|
||||
## Public Client API
|
||||
|
||||
The Public Client API is designed for the JavaScript SDK and does not require authentication. It's primarily used for creating persons, sessions, and responses within the Formbricks platform. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
- [Actions API](/docs/api/client/actions) - Create actions for a person
|
||||
- [Displays API](/docs/api/client/displays) - Mark Survey as Displayed or Responded for a Person
|
||||
- [People API](/docs/api/client/people) - Create & update people (e.g. attributes)
|
||||
- [Responses API](/docs/api/client/responses) - Create & update responses for a survey
|
||||
|
||||
## Management API
|
||||
|
||||
The Management API provides access to all data and settings that are visible in the Formbricks App. This API requires a personal API Key for authentication, which can be generated in the Settings section of the Formbricks App. With the Management API, you can manage your Formbricks account programmatically, accessing and modifying data and settings as needed.
|
||||
|
||||
**Auth:** Personal API Key
|
||||
|
||||
API requests made to the Management API are authorized using a personal API key. This key grants the same rights and access as if you were logged in at formbricks.com. It's essential to keep your API key secure and not share it with others.
|
||||
|
||||
To generate, store, or delete an API key, follow the instructions provided on the following page [API Key](/docs/api/management/api-key-setup).
|
||||
|
||||
- [Action Class API](/docs/api/management/action-classes) - Create, Update, and Delete Action Classes
|
||||
- [Attribute Class API](/docs/api/management/attribute-classes) - Create, Update, and Delete Attribute Classes
|
||||
- [Me API](/docs/api/management/me) - Retrieve Account Information
|
||||
- [People API](/docs/api/management/people) - Create, Update, and Delete People
|
||||
- [Responses API](/docs/api/management/responses) - Create, Update, and Delete Responses
|
||||
- [Surveys API](/docs/api/management/surveys) - Create, Update, and Delete Surveys
|
||||
- [Webhook API](/docs/api/management/webhooks) - Create, Update, and Delete Webhooks
|
||||
|
||||
<Note>
|
||||
By understanding the differences between these two APIs, you can choose the appropriate one for your needs,
|
||||
ensuring a secure and efficient integration with the Formbricks platform.
|
||||
</Note>
|
||||
|
||||
---
|
||||
@@ -1,130 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Public Client API Guide: Manage Users",
|
||||
description:
|
||||
"Dive deep into Formbricks' Public Client API designed for customisation. This comprehensive guide provides detailed instructions on creating and updating users to help in user identification.",
|
||||
};
|
||||
|
||||
#### Client API
|
||||
|
||||
# People API
|
||||
|
||||
The Public Client API is designed for the JavaScript SDK and does not require authentication. It's primarily used for creating persons, sessions, and responses within the Formbricks platform. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
This set of API can be used to
|
||||
- [Create Person](#create-person)
|
||||
- [Update Person](#update-person)
|
||||
|
||||
---
|
||||
|
||||
## Create Person {{ tag: 'POST', label: '/api/v1/client/<environment-id>/people' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Create User with your own User ID
|
||||
|
||||
### Mandatory Request Body JSON Keys
|
||||
<Properties>
|
||||
<Property name="userId" type="string">
|
||||
User ID which you would like to identify the person with
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/client/<environment-id>/people">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST \
|
||||
'https://app.formbricks.com/api/v1/client/<environment-id>/people' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"userId":"docs_user"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"userId": "docs_user"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "Fields are missing or incorrectly formatted",
|
||||
"details": {
|
||||
"surveyId": "Required"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Update Person {{ tag: 'POST', label: '/api/v1/client/<environment-id>/people/<user-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Update Person by their User ID
|
||||
|
||||
### Mandatory Request Body JSON Keys
|
||||
<Properties>
|
||||
<Property name="attributes" type="JSON">
|
||||
Key Value pairs of attributes to add to the user
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/client/<environment-id>/people/<user-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST \
|
||||
--location \
|
||||
'https://app.formbricks.com/api/v1/client/<environment-id>/people/<user-id>'
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{
|
||||
"attributes":{
|
||||
"welcome_to":"formbricks"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '500 Internal Server Error' }}
|
||||
{
|
||||
"code": "internal_server_error",
|
||||
"message": "Database operation failed",
|
||||
"details": {}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,200 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Responses API Documentation - Manage Your Survey Data Seamlessly",
|
||||
description:
|
||||
"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.",
|
||||
};
|
||||
|
||||
#### Client API
|
||||
|
||||
# Responses API
|
||||
|
||||
The Public Client API is designed for the JavaScript SDK and does not require authentication. It's primarily used for creating persons, sessions, and responses within the Formbricks platform. This API is ideal for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
This set of API can be used to
|
||||
- [Create Response](#create-response)
|
||||
- [Update Response](#update-response)
|
||||
|
||||
---
|
||||
|
||||
## Create Response {{ tag: 'POST', label: '/api/v1/client/<environment-id>/responses' }}
|
||||
|
||||
Add a new response to a survey.
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
### Mandatory Body Fields
|
||||
|
||||
<Properties>
|
||||
<Property name="surveyId" type="string">
|
||||
The id of the survey the response belongs to.
|
||||
</Property>
|
||||
<Property name="finished" type="boolean">
|
||||
Marks whether the response is complete or not.
|
||||
</Property>
|
||||
<Property name="data" type="string">
|
||||
The data of the response as JSON object (key: questionId, value: answer).
|
||||
</Property>
|
||||
|
||||
</Properties>
|
||||
|
||||
### Optional Body Fields
|
||||
|
||||
<Properties>
|
||||
<Property name="userId" type="string" required>
|
||||
Pre-existing User ID to identify the user sending the response
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Parameters Explained
|
||||
|
||||
| field name | required | default | description |
|
||||
| ---------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| data | yes | - | The response data object (answers to the survey). In this object the key is the questionId, the value the answer of the user to this question. |
|
||||
| userId | no | - | The person this response is connected to. |
|
||||
| surveyId | yes | - | The survey this response is connected to. |
|
||||
| finished | yes | false | Mark a response as complete to be able to filter accordingly. |
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/client/<environment-id>/responses">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST 'https://app.formbricks.com/api/v1/client/<environment-id>/responses' \
|
||||
--data-raw '{
|
||||
"surveyId":"cloqzeuu70000z8khcirufo60",
|
||||
"userId": "1",
|
||||
"finished": true,
|
||||
"data": {
|
||||
"clfqjny0v0003yzgscnog1j9i": 10,
|
||||
"clfqjtn8n0070yzgs6jgx9rog": "I love Formbricks"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```json {{ title: 'Example Request Body' }}
|
||||
{
|
||||
"userId": "1",
|
||||
"surveyId": "cloqzeuu70000z8khcirufo60",
|
||||
"finished": true,
|
||||
"data": {
|
||||
"clfqjny0v0003yzgscnog1j9i": 10,
|
||||
"clfqjtn8n0070yzgs6jgx9rog": "I love Formbricks"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "clp84xdld0002px36fkgue5ka",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "surveyId was not provided.",
|
||||
"details": {
|
||||
"surveyId": "This field is required."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Update Response {{ tag: 'PUT', label: '/api/v1/client/<environment-id>/responses/<response-id>' }}
|
||||
|
||||
Update an existing response in a survey.
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
### Mandatory Body Fields
|
||||
|
||||
<Properties>
|
||||
<Property name="finished" type="boolean">
|
||||
Marks whether the response is complete or not.
|
||||
</Property>
|
||||
<Property name="data" type="string">
|
||||
The data of the response as JSON object (key: questionId, value: answer).
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Parameters Explained
|
||||
|
||||
| field name | required | default | description |
|
||||
| ---------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| data | yes | - | The response data object (answers to the survey). In this object the key is the questionId, the value the answer of the user to this question. |
|
||||
| finished | yes | false | Mark a response as complete to be able to filter accordingly. |
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="PUT" label="/api/v1/client/<environment-id>/responses/<response-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request PUT 'https://app.formbricks.com/api/v1/client/<environment-id>/responses/<response-id>' \
|
||||
--data-raw '{
|
||||
"finished":false,
|
||||
"data": {
|
||||
"clfqjny0v0003yzgscnog1j9i": 10,
|
||||
"clfqjtn8n0070yzgs6jgx9rog": "I love Formbricks"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```json {{ title: 'Example Request Body' }}
|
||||
{
|
||||
"finished":false,
|
||||
"data": {
|
||||
"clfqjny0v0003yzgscnog1j9i": 10,
|
||||
"clfqjtn8n0070yzgs6jgx9rog": "I love Formbricks"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "data was not provided.",
|
||||
"details": {
|
||||
"data": "This field is required."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '404 Not Found' }}
|
||||
{
|
||||
"code": "not_found",
|
||||
"message": "Response not found"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -1,297 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import {generateManagementApiMetadata} from "@/lib/utils"
|
||||
export const metadata = generateManagementApiMetadata("Action Class",["Fetch","Create","Delete"])
|
||||
|
||||
#### Management API
|
||||
|
||||
# Action Classes API
|
||||
|
||||
This set of API can be used to
|
||||
- [List Actions](#get-all-action-classes)
|
||||
- [Get Action](#get-action-class-by-id)
|
||||
- [Create Actions](#create-action-class)
|
||||
- [Delete Actions](#delete-action-class)
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## Get all Action Classes {{ tag: 'GET', label: '/api/v1/management/action-classes' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get all the existing action classes in your environment.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/action-classes">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/action-classes' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "cln8k0t47000gz87nw4ibwv35",
|
||||
"createdAt": "2023-10-02T07:13:19.207Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.207Z",
|
||||
"name": "New Session",
|
||||
"description": "Gets fired when a new session is created",
|
||||
"type": "automatic",
|
||||
"noCodeConfig": null,
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
},
|
||||
{
|
||||
"id": "cln8k0t55000uz87noerwdooj",
|
||||
"createdAt": "2023-10-02T07:13:19.241Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.241Z",
|
||||
"name": "Invited Team Member",
|
||||
"description": "Person invited a team member",
|
||||
"type": "noCode",
|
||||
"noCodeConfig": {
|
||||
"type": "innerHtml",
|
||||
"innerHtml": {
|
||||
"value": "Add Team Member"
|
||||
}
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Get Action Class by ID {{ tag: 'GET', label: '/api/v1/management/action-classes/<action-class-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Fetch an action class by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/action-classes/<action-class-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/action-classes/<action-class-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln8k0t55000uz87noerwdooj",
|
||||
"createdAt": "2023-10-02T07:13:19.241Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.241Z",
|
||||
"name": "Invited Team Member",
|
||||
"description": "Person invited a team member",
|
||||
"type": "noCode",
|
||||
"noCodeConfig": {
|
||||
"type": "innerHtml",
|
||||
"innerHtml": {
|
||||
"value": "Add Team Member"
|
||||
}
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Create Action Class {{ tag: 'POST', label: '/api/v1/management/action-classes/' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Create an action class.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Body
|
||||
|
||||
<CodeGroup title="Request Body">
|
||||
```json {{ title: 'cURL' }}
|
||||
{
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"name": "My Action from API",
|
||||
"type": "code"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/management/action-classes/">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST https://app.formbricks.com/api/v1/management/action-classes/ \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <your-api-key>' \
|
||||
-d '{"environmentId": "cln8k0t47000fz87njmmu2bck", "name": "My Action from API", "type": "code"}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln9w1cno0008z8zu79nk5w0c",
|
||||
"createdAt": "2023-10-03T05:37:26.100Z",
|
||||
"updatedAt": "2023-10-03T05:37:26.100Z",
|
||||
"name": "My Action from API",
|
||||
"description": null,
|
||||
"type": "code",
|
||||
"noCodeConfig": null,
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Delete Action Class {{ tag: 'DELETE', label: '/api/v1/management/action-classes/<action-class-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Delete an action class by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/v1/management/action-classes/<action-class-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X DELETE https://app.formbricks.com/api/v1/management/action-classes/<action-class-id> \
|
||||
--header 'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln9w1cno0008z8zu79nk5w0c",
|
||||
"createdAt": "2023-10-03T05:37:26.100Z",
|
||||
"updatedAt": "2023-10-03T05:37:26.100Z",
|
||||
"name": "My Action from API",
|
||||
"description": null,
|
||||
"type": "code",
|
||||
"noCodeConfig": null,
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,103 +0,0 @@
|
||||
import { MdxImage } from "@/components/shared/MdxImage";
|
||||
|
||||
import AddApiKey from "./add-api-key.webp";
|
||||
import ApiKeySecret from "./api-key-secret.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks API Key: Setup and Testing",
|
||||
description:
|
||||
"This guide provides step-by-step instructions to generate, store, and delete API keys, ensuring safe and authenticated access to your Formbricks account.",
|
||||
};
|
||||
|
||||
#### API
|
||||
|
||||
# API Key Setup
|
||||
|
||||
## Auth: Personal API key
|
||||
|
||||
The API requests are authorized with a personal API key. This API key gives you the same rights as if you were logged in at formbricks.com - **don't share it around!**
|
||||
|
||||
### How to generate an API key
|
||||
|
||||
1. Go to your settings on [app.formbricks.com](https://app.formbricks.com).
|
||||
2. Go to page “API keys”
|
||||
<MdxImage src={AddApiKey} alt="Add API Key" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
3. Create a key for the development or production environment.
|
||||
4. Copy the key immediately. You won’t be able to see it again.
|
||||
<MdxImage
|
||||
src={ApiKeySecret}
|
||||
alt="API Key Secret"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
<Note>
|
||||
### Store API key safely!
|
||||
Anyone who has your API key has full control over your account. For security
|
||||
reasons, you cannot view the API key again.
|
||||
</Note>
|
||||
|
||||
### Test your API Key
|
||||
|
||||
Hit the below request to verify that you are authenticated with your API Key and the server is responding.
|
||||
|
||||
## Get My Profile {{ tag: 'GET', label: '/api/v1/me' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get the product details and environment type of your account.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/me">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/me' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"id": "cll2m30r70004mx0huqkitgqv",
|
||||
"createdAt": "2023-08-08T18:04:59.922Z",
|
||||
"updatedAt": "2023-08-08T18:04:59.922Z",
|
||||
"type": "production",
|
||||
"product": {
|
||||
"id": "cll2m30r60003mx0hnemjfckr",
|
||||
"name": "My Product"
|
||||
},
|
||||
"widgetSetupCompleted": false
|
||||
}
|
||||
```
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
Not authenticated
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
### Delete a personal API key
|
||||
|
||||
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.
|
||||
@@ -1,289 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import {generateManagementApiMetadata} from "@/lib/utils"
|
||||
|
||||
export const metadata = generateManagementApiMetadata("Attribute Class",["Fetch","Create","Delete"])
|
||||
|
||||
#### Management API
|
||||
|
||||
# Attribute Classes API
|
||||
|
||||
This set of API can be used to
|
||||
- [List Attributes](#get-all-attribute-classes)
|
||||
- [Get Attributes](#get-attribute-class-by-id)
|
||||
- [Create Attributes](#create-attribute-class)
|
||||
- [Delete Attributes](#delete-attribute-class)
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## Get all Attribute Classes {{ tag: 'GET', label: '/api/v1/management/attribute-classes' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get all the existing attribute classes in your environment.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/attribute-classes">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/attribute-classes' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "cln8k0t47000kz87n3lh23zf0",
|
||||
"createdAt": "2023-10-02T07:13:19.207Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.207Z",
|
||||
"name": "email",
|
||||
"description": "The email of the person",
|
||||
"archived": false,
|
||||
"type": "automatic",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
},
|
||||
{
|
||||
"id": "cln8k0t55000xz87nrtwbo7sf",
|
||||
"createdAt": "2023-10-02T07:13:19.241Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.241Z",
|
||||
"name": "Name",
|
||||
"description": "Full Name of the Person",
|
||||
"archived": false,
|
||||
"type": "code",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Get Attribute Class by ID {{ tag: 'GET', label: '/api/v1/management/attribute-classes/<attribute-class-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Fetch an Attribute class by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/attribute-classes/<attribute-class-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/attribute-classes/<attribute-class-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln8k0t47000jz87nfwcey6mh",
|
||||
"createdAt": "2023-10-02T07:13:19.207Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.207Z",
|
||||
"name": "userId",
|
||||
"description": "The internal ID of the person",
|
||||
"archived": false,
|
||||
"type": "automatic",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Create Attribute Class {{ tag: 'POST', label: '/api/v1/management/attribute-classes/' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Create an Attribute class.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Body
|
||||
|
||||
<CodeGroup title="Request Body">
|
||||
```json {{ title: 'cURL' }}
|
||||
{
|
||||
"environmentId": "clmlmwdqq0003196ufewo6ibg",
|
||||
"name": "My Attribute from API",
|
||||
"type": "code",
|
||||
"description": "My description"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/management/attribute-classes/">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST https://app.formbricks.com/api/v1/management/attribute-classes/ \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <your-api-key>' \
|
||||
-d '{"environmentId": "clmlmwdqq0003196ufewo6ibg", "name": "My Attribute from API", "type": "code", "description":"My description"}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "clna0hd7z0009z8zue2z3a7wy",
|
||||
"createdAt": "2023-10-03T07:41:51.792Z",
|
||||
"updatedAt": "2023-10-03T07:41:51.792Z",
|
||||
"name": "My Attribute from API",
|
||||
"description": null,
|
||||
"archived": false,
|
||||
"type": "code",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Delete Attribute Class {{ tag: 'DELETE', label: '/api/v1/management/attribute-classes/<attribute-class-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Delete an Attribute class by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/v1/management/attribute-classes/<attribute-class-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X DELETE https://app.formbricks.com/api/v1/management/attribute-classes/<attribute-class-id> \
|
||||
--header 'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "clna0hd7z0009z8zue2z3a7wy",
|
||||
"createdAt": "2023-10-03T07:41:51.792Z",
|
||||
"updatedAt": "2023-10-03T07:41:51.792Z",
|
||||
"name": "My Attribute from API",
|
||||
"description": null,
|
||||
"archived": false,
|
||||
"type": "code",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,78 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Me API: Fetch your environment details",
|
||||
description:
|
||||
"Dive into Formbricks' Me API within the Public Client API suite. Seamlessly fetch your own current environment details.",
|
||||
};
|
||||
|
||||
#### Management API
|
||||
|
||||
# Me API
|
||||
|
||||
This API can be used to get your own current environment details.
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## Get Environment {{ tag: 'GET', label: '/api/v1/management/me' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get your current environment details.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/me">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/me' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"id": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.207Z",
|
||||
"updatedAt": "2023-10-02T07:14:14.162Z",
|
||||
"type": "production",
|
||||
"product": {
|
||||
"id": "cln8k0t47000ez87n57aqywvz",
|
||||
"name": "Demo Product"
|
||||
},
|
||||
"widgetSetupCompleted": true
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Unauthorized' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,232 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import {generateManagementApiMetadata} from "@/lib/utils"
|
||||
|
||||
export const metadata = generateManagementApiMetadata("People",["Fetch","Delete"])
|
||||
|
||||
|
||||
#### Management API
|
||||
|
||||
# People API
|
||||
|
||||
This set of API can be used to
|
||||
- [List People](#list-people)
|
||||
- [Get Person](#get-person)
|
||||
- [Delete Person](#delete-person)
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## List People {{ tag: 'GET', label: '/api/v1/management/people' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
List People
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/people">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/people' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "b4wgrzl363dn3zb6yy5gf265",
|
||||
"attributes": {
|
||||
"userId": "CYO618",
|
||||
"email": "sophia@amazon.com",
|
||||
"Name": "Sophia Johnson",
|
||||
"Role": "Designer",
|
||||
"Company": "Amazon",
|
||||
"Experience": "7 years",
|
||||
"Usage Frequency": "Yearly",
|
||||
"Company Size": "1628 employees",
|
||||
"Product Satisfaction Score": "62",
|
||||
"Recommendation Likelihood": "9"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
},
|
||||
{
|
||||
"id": "jrb5iyzqvnkg9322ckhde3j4",
|
||||
"attributes": {
|
||||
"userId": "CYO511",
|
||||
"email": "antonio@ibm.com",
|
||||
"Name": "Antonio García",
|
||||
"Role": "Designer",
|
||||
"Company": "IBM",
|
||||
"Experience": "1 years",
|
||||
"Usage Frequency": "Weekly",
|
||||
"Company Size": "4023 employees",
|
||||
"Product Satisfaction Score": "77",
|
||||
"Recommendation Likelihood": "4"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "Fields are missing or incorrectly formatted",
|
||||
"details": {
|
||||
"userId": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Get Person {{ tag: 'GET', label: '/api/v1/management/people/<person-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get Person by ID
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/people/<person-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/people/<person-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "jrb5iyzqvnkg9322ckhde3j4",
|
||||
"attributes": {
|
||||
"userId": "CYO511",
|
||||
"email": "antonio@ibm.com",
|
||||
"Name": "Antonio García",
|
||||
"Role": "Designer",
|
||||
"Company": "IBM",
|
||||
"Experience": "1 years",
|
||||
"Usage Frequency": "Weekly",
|
||||
"Company Size": "4023 employees",
|
||||
"Product Satisfaction Score": "77",
|
||||
"Recommendation Likelihood": "4"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '404 Not Found' }}
|
||||
{
|
||||
"code": "not_found",
|
||||
"message": "Person not found",
|
||||
"details": {
|
||||
"resource_id": "clmlmykc2000019vz5o3jglsa",
|
||||
"resource_type": "Person"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Delete Person {{ tag: 'DELETE', label: '/api/v1/management/people/<person-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Delete Person by ID
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/v1/management/people/<person-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X DELETE https://app.formbricks.com/api/v1/management/people/<person-id> \
|
||||
--header 'x-api-key: <your-api-key>'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"success": "Person deleted successfully"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '404 Not Found' }}
|
||||
{
|
||||
"code": "not_found",
|
||||
"message": "Person not found",
|
||||
"details": {
|
||||
"resource_id": "clmlmykc2000019vz5o3jglsa",
|
||||
"resource_type": "Person"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,379 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import { generateManagementApiMetadata } from "@/lib/utils";
|
||||
|
||||
export const metadata = generateManagementApiMetadata("Responses", ["Fetch", "Delete"]);
|
||||
|
||||
#### Management API
|
||||
|
||||
# Responses API
|
||||
|
||||
This set of API can be used to
|
||||
|
||||
- [List Responses](#list-all-responses)
|
||||
- [List all Responses by surveyId](#list-all-responses-by-survey-id)
|
||||
- [Get Response](#get-response-by-id)
|
||||
- [Delete Response](#delete-a-response)
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## List all Responses {{ tag: 'GET', label: '/api/v1/management/responses' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Retrieve all the responses you have received in your environment.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/responses">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/responses' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data":[
|
||||
{
|
||||
"id": "cln8k0tqv00pcz87no4qrw333",
|
||||
"createdAt": "2023-10-02T07:13:20.023Z",
|
||||
"updatedAt": "2023-10-02T07:13:20.023Z",
|
||||
"surveyId": "cln8k0tqu00p7z87nqr4thi3k",
|
||||
"finished": true,
|
||||
"data": {
|
||||
"interview-prompt": "clicked"
|
||||
},
|
||||
"meta": {
|
||||
"userAgent": {
|
||||
"os": "MacOS",
|
||||
"browser": "Chrome"
|
||||
}
|
||||
},
|
||||
"personAttributes": null,
|
||||
"person": {
|
||||
"id": "e0x4i5tvsp8puxfztyrwykvn",
|
||||
"attributes": {
|
||||
"userId": "CYO675",
|
||||
"email": "ravi@netflix.com",
|
||||
"Name": "Ravi Kumar",
|
||||
"Role": "Manager",
|
||||
"Company": "Netflix",
|
||||
"Experience": "6 years",
|
||||
"Usage Frequency": "Monthly",
|
||||
"Company Size": "4610 employees",
|
||||
"Product Satisfaction Score": "43",
|
||||
"Recommendation Likelihood": "4"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
},
|
||||
"notes": [],
|
||||
"tags": []
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## List all Responses by surveyId {{ tag: 'GET', label: '/api/v1/management/responses?surveyId=<survey-Id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Retrieve all the responses received in your survey.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/responses?surveyId=<survey-Id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/responses?surveyId=<survey-Id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data":[
|
||||
{
|
||||
"id": "cln8k0tqv00pcz87no4qrw333",
|
||||
"createdAt": "2023-10-02T07:13:20.023Z",
|
||||
"updatedAt": "2023-10-02T07:13:20.023Z",
|
||||
"surveyId": "cln8k0tqu00p7z87nqr4thi3k",
|
||||
"finished": true,
|
||||
"data": {
|
||||
"interview-prompt": "clicked"
|
||||
},
|
||||
"meta": {
|
||||
"userAgent": {
|
||||
"os": "MacOS",
|
||||
"browser": "Chrome"
|
||||
}
|
||||
},
|
||||
"personAttributes": null,
|
||||
"person": {
|
||||
"id": "e0x4i5tvsp8puxfztyrwykvn",
|
||||
"attributes": {
|
||||
"userId": "CYO675",
|
||||
"email": "ravi@netflix.com",
|
||||
"Name": "Ravi Kumar",
|
||||
"Role": "Manager",
|
||||
"Company": "Netflix",
|
||||
"Experience": "6 years",
|
||||
"Usage Frequency": "Monthly",
|
||||
"Company Size": "4610 employees",
|
||||
"Product Satisfaction Score": "43",
|
||||
"Recommendation Likelihood": "4"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
},
|
||||
"notes": [],
|
||||
"tags": []
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Get Response by ID {{ tag: 'GET', label: '/api/v1/management/responses/<response-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Retrieve a response by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/responses/<response-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/responses/<response-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data":
|
||||
{
|
||||
"id": "cln8k0tqv00pbz87nwo5lr72b",
|
||||
"createdAt": "2023-10-02T07:13:20.023Z",
|
||||
"updatedAt": "2023-10-02T07:13:20.023Z",
|
||||
"surveyId": "cln8k0tqu00p7z87nqr4thi3k",
|
||||
"finished": true,
|
||||
"data": {
|
||||
"interview-prompt": "clicked"
|
||||
},
|
||||
"meta": {
|
||||
"userAgent": {
|
||||
"os": "Windows",
|
||||
"browser": "Edge"
|
||||
}
|
||||
},
|
||||
"personAttributes": null,
|
||||
"person": {
|
||||
"id": "hsx38f15v50ua8383uadagq5",
|
||||
"attributes": {
|
||||
"userId": "CYO278",
|
||||
"email": "jorge@facebook.com",
|
||||
"Name": "Jorge Sanchez",
|
||||
"Role": "Product Manager",
|
||||
"Company": "Facebook",
|
||||
"Experience": "10 years",
|
||||
"Usage Frequency": "Daily",
|
||||
"Company Size": "1685 employees",
|
||||
"Product Satisfaction Score": "84",
|
||||
"Recommendation Likelihood": "6"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
},
|
||||
"notes": [],
|
||||
"tags": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Delete a response {{ tag: 'DELETE', label: '/api/v1/client/responses/<response-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Delete Response by ID
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/v1/client/responses/<response-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X DELETE https://app.formbricks.com/api/v1/management/responses/<response-id> \
|
||||
--header 'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln8k0tqv00pbz87nwo5lr72b",
|
||||
"createdAt": "2023-10-02T07:13:20.023Z",
|
||||
"updatedAt": "2023-10-02T07:13:20.023Z",
|
||||
"surveyId": "cln8k0tqu00p7z87nqr4thi3k",
|
||||
"finished": true,
|
||||
"data": {
|
||||
"interview-prompt": "clicked"
|
||||
},
|
||||
"meta": {
|
||||
"userAgent": {
|
||||
"os": "Windows",
|
||||
"browser": "Edge"
|
||||
}
|
||||
},
|
||||
"personAttributes": null,
|
||||
"person": {
|
||||
"id": "hsx38f15v50ua8383uadagq5",
|
||||
"attributes": {
|
||||
"userId": "CYO278",
|
||||
"email": "jorge@facebook.com",
|
||||
"Name": "Jorge Sanchez",
|
||||
"Role": "Product Manager",
|
||||
"Company": "Facebook",
|
||||
"Experience": "10 years",
|
||||
"Usage Frequency": "Daily",
|
||||
"Company Size": "1685 employees",
|
||||
"Product Satisfaction Score": "84",
|
||||
"Recommendation Likelihood": "6"
|
||||
},
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"createdAt": "2023-10-02T07:13:19.444Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.444Z"
|
||||
},
|
||||
"notes": [],
|
||||
"tags": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "surveyId was not provided.",
|
||||
"details": {
|
||||
"surveyId": "This field is required."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,789 +0,0 @@
|
||||
import { Fence } from "@/components/shared/Fence";
|
||||
import { generateManagementApiMetadata } from "@/lib/utils";
|
||||
|
||||
export const metadata = generateManagementApiMetadata("Surveys", ["Fetch", "Create", "Update", "Delete"]);
|
||||
|
||||
#### Management API
|
||||
|
||||
# Surveys API
|
||||
|
||||
This set of API can be used to
|
||||
|
||||
- [List All Surveys](#list-all-surveys)
|
||||
- [Get Survey](#get-survey-by-id)
|
||||
- [Create Survey](#create-survey)
|
||||
- [Update Survey](#update-survey-by-id)
|
||||
- [Delete Survey](#delete-survey-by-id)
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## List all surveys {{ tag: 'GET', label: '/api/v1/management/surveys' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
Retrieve all the surveys you have for the environment with pagination.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Query Parameters
|
||||
<Properties>
|
||||
<Property name="offset" type="number">
|
||||
The number of surveys to skip before returning the results.
|
||||
</Property>
|
||||
|
||||
<Property name="limit" type="number">
|
||||
The number of surveys to return.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/surveys">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/surveys?offset=20&limit=10' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "cllnfy2780fromy0hy7uoxvtn",
|
||||
"createdAt": "2023-08-23T07:56:20.516Z",
|
||||
"updatedAt": "2023-08-23T07:56:26.947Z",
|
||||
"name": "Product Market Fit (Superhuman)",
|
||||
"type": "link",
|
||||
"environmentId": "cll2m30r70004mx0huqkitgqv",
|
||||
"status": "inProgress",
|
||||
"attributeFilters": [],
|
||||
"displayOption": "displayOnce",
|
||||
"autoClose": null,
|
||||
"triggers": [],
|
||||
"redirectUrl": null,
|
||||
"recontactDays": null,
|
||||
"questions": [
|
||||
{
|
||||
"id": "gml6mgy71efgtq8np3s9je5p",
|
||||
"type": "cta",
|
||||
"headline": "You are one of our power users! Do you have 5 minutes?",
|
||||
"required": false,
|
||||
"buttonLabel": "Happy to help!",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "skipped",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We would love to understand your user experience better. Sharing your insight helps a lot.</span></p>",
|
||||
"buttonExternal": false,
|
||||
"dismissButtonLabel": "No, thanks."
|
||||
},
|
||||
{
|
||||
"id": "kp62fbqe8cfzmvy8qwpr81b2",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": "How disappointed would you be if you could no longer use My Product?",
|
||||
"subheader": "Please select one of the following options:",
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "bdgy1hnwd7uwmfxk1ljqp1n5",
|
||||
"label": "Not at all disappointed"
|
||||
},
|
||||
{
|
||||
"id": "poabnvgtwenp8rb2v70gj4hj",
|
||||
"label": "Somewhat disappointed"
|
||||
},
|
||||
{
|
||||
"id": "opfiqyqz8wrqn0i0f7t24d3n",
|
||||
"label": "Very disappointed"
|
||||
}
|
||||
],
|
||||
"shuffleOption": "none"
|
||||
},
|
||||
{
|
||||
"id": "klvpwd4x08x8quesihvw5l92",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": "What is your role?",
|
||||
"subheader": "Please select one of the following options:",
|
||||
"required": true,
|
||||
"choices": [
|
||||
{
|
||||
"id": "c8nerw6l9gpsxcmqkn10f9hy",
|
||||
"label": "Founder"
|
||||
},
|
||||
{
|
||||
"id": "ebjqezei6a2axtuq86cleetn",
|
||||
"label": "Executive"
|
||||
},
|
||||
{
|
||||
"id": "ctiijjblyhlp22snypfamqt1",
|
||||
"label": "Product Manager"
|
||||
},
|
||||
{
|
||||
"id": "ibalyr0mhemfkkr82vypmg40",
|
||||
"label": "Product Owner"
|
||||
},
|
||||
{
|
||||
"id": "fipk606aegslbd0e7yhc0xjx",
|
||||
"label": "Software Engineer"
|
||||
}
|
||||
],
|
||||
"shuffleOption": "none"
|
||||
},
|
||||
{
|
||||
"id": "ryo75306flyg72iaeditbv51",
|
||||
"type": "openText",
|
||||
"headline": "What type of people do you think would most benefit from My Product?",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"id": "lkjaxb73ulydzeumhd51sx9g",
|
||||
"type": "openText",
|
||||
"headline": "What is the main benefit you receive from My Product?",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"id": "ec7agikkr58j8uonhioinkyk",
|
||||
"type": "openText",
|
||||
"headline": "How can we improve My Product for you?",
|
||||
"subheader": "Please be as specific as possible.",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"thankYouCard": {
|
||||
"enabled": true,
|
||||
"headline": "Thank you!",
|
||||
"subheader": "We appreciate your feedback."
|
||||
},
|
||||
"delay": 0,
|
||||
"autoComplete": null,
|
||||
"closeOnDate": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Get Survey by ID {{ tag: 'GET', label: '/api/v1/management/surveys/<survey-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Get a specific survey by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/management/surveys/<survey-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/management/surveys/<survey-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln8k0tjz00n5z87nwq527h3z",
|
||||
"createdAt": "2023-10-02T07:13:19.775Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.775Z",
|
||||
"name": "Churn Survey",
|
||||
"type": "link",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"status": "inProgress",
|
||||
"attributeFilters": [],
|
||||
"displayOption": "displayOnce",
|
||||
"autoClose": null,
|
||||
"triggers": [],
|
||||
"redirectUrl": null,
|
||||
"recontactDays": null,
|
||||
"questions": [
|
||||
{
|
||||
"id": "churn-reason",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": "Why did you cancel your subscription?",
|
||||
"subheader": "We're sorry to see you leave. Help us do better:",
|
||||
"required": true,
|
||||
"logic": [
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "Difficult to use",
|
||||
"destination": "easier-to-use"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "It's too expensive",
|
||||
"destination": "30-off"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "I am missing features",
|
||||
"destination": "missing-features"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "Poor customer service",
|
||||
"destination": "poor-service"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "I just didn't need it anymore",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"choices": [
|
||||
{
|
||||
"id": "isud2xethsw63dlwl89kr4kj",
|
||||
"label": "Difficult to use"
|
||||
},
|
||||
{
|
||||
"id": "opuu4ba3dlele3n0gjkuh27c",
|
||||
"label": "It's too expensive"
|
||||
},
|
||||
{
|
||||
"id": "gnypapo0rhvkt8pwosrphvbl",
|
||||
"label": "I am missing features"
|
||||
},
|
||||
{
|
||||
"id": "wkgsrsrazd9kfunqhzjezx6t",
|
||||
"label": "Poor customer service"
|
||||
},
|
||||
{
|
||||
"id": "pykmgyyw74vg0gaeryj6bo4c",
|
||||
"label": "I just didn't need it anymore"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "easier-to-use",
|
||||
"type": "openText",
|
||||
"headline": "What would have made {{productName}} easier to use?",
|
||||
"subheader": "",
|
||||
"required": true,
|
||||
"buttonLabel": "Send",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "submitted",
|
||||
"destination": "end"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "30-off",
|
||||
"type": "cta",
|
||||
"headline": "Get 30% off for the next year!",
|
||||
"required": true,
|
||||
"buttonLabel": "Get 30% off",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "clicked",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We'd love to keep you as a customer. Happy to offer a 30% discount for the next year.</span></p>",
|
||||
"buttonUrl": "https://formbricks.com",
|
||||
"buttonExternal": true,
|
||||
"dismissButtonLabel": "Skip"
|
||||
},
|
||||
{
|
||||
"id": "missing-features",
|
||||
"type": "openText",
|
||||
"headline": "What features are you missing?",
|
||||
"subheader": "",
|
||||
"required": true,
|
||||
"logic": [
|
||||
{
|
||||
"condition": "submitted",
|
||||
"destination": "end"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "poor-service",
|
||||
"type": "cta",
|
||||
"headline": "So sorry to hear 😔 Talk to our CEO directly!",
|
||||
"required": true,
|
||||
"buttonLabel": "Send email to CEO",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "clicked",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We aim to provide the best possible customer service. Please email our CEO and she will personally handle your issue.</span></p>",
|
||||
"buttonUrl": "mailto:ceo@company.com",
|
||||
"buttonExternal": true,
|
||||
"dismissButtonLabel": "Skip"
|
||||
}
|
||||
],
|
||||
"thankYouCard": {
|
||||
"enabled": false
|
||||
},
|
||||
"delay": 0,
|
||||
"autoComplete": null,
|
||||
"closeOnDate": null,
|
||||
"surveyClosedMessage": null,
|
||||
"verifyEmail": null
|
||||
}
|
||||
}
|
||||
```
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Create Survey {{ tag: 'POST', label: '/api/v1/management/surveys' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Create a survey
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Body
|
||||
|
||||
<CodeGroup title="Request Body">
|
||||
```json {{ title: 'cURL' }}
|
||||
{
|
||||
"environmentId": "clmlmwdqq0003196ufewo6ibg",
|
||||
"type": "link",
|
||||
"name": "My new Survey"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/management/surveys">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST \
|
||||
'https://app.formbricks.com/api/v1/management/surveys' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST https://app.formbricks.com/api/v1/management/surveys/ \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <your-api-key>' \
|
||||
-d '{"environmentId": "cln8k0t47000fz87njmmu2bck", "name": "My Survey from API", "type": "link"}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "clna6bqnz000az8zubq3e757t",
|
||||
"createdAt": "2023-10-03T10:25:26.975Z",
|
||||
"updatedAt": "2023-10-03T10:25:26.975Z",
|
||||
"name": "My new Survey",
|
||||
"redirectUrl": null,
|
||||
"type": "link",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"status": "draft",
|
||||
"questions": [],
|
||||
"thankYouCard": {
|
||||
"enabled": false
|
||||
},
|
||||
"displayOption": "displayOnce",
|
||||
"recontactDays": null,
|
||||
"autoClose": null,
|
||||
"delay": 0,
|
||||
"autoComplete": null,
|
||||
"closeOnDate": null,
|
||||
"surveyClosedMessage": null,
|
||||
"verifyEmail": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Update Survey by ID {{ tag: 'PUT', label: '/api/v1/management/surveys/<survey-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Update a survey by its ID
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Body
|
||||
|
||||
<CodeGroup title="Request Body">
|
||||
```json {{ title: 'cURL' }}
|
||||
{
|
||||
"name": "My renamed Survey",
|
||||
"redirectUrl":"https://formbricks.com",
|
||||
"type":"web"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="PUT" label="/api/v1/management/surveys/<survey-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X PUT https://app.formbricks.com/api/v1/management/surveys/<survey-id> \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'x-api-key: <your-api-key>' \
|
||||
-d '{"name": "My renamed Survey"}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cloqzeuu70000z8khcirufo60",
|
||||
"createdAt": "2023-11-09T09:23:42.367Z",
|
||||
"updatedAt": "2023-11-09T09:23:42.367Z",
|
||||
"name": "My renamed Survey",
|
||||
"redirectUrl": null,
|
||||
"type": "link",
|
||||
"environmentId": "clonzr6vc0009z8md7y06hipl",
|
||||
"status": "inProgress",
|
||||
"welcomeCard": {
|
||||
"html": "Thanks for providing your feedback - let's go!",
|
||||
"enabled": false,
|
||||
"headline": "Welcome!",
|
||||
"timeToFinish": false
|
||||
},
|
||||
"questions": [
|
||||
{
|
||||
"id": "l9rwn5nbk48y44tvnyyjcvca",
|
||||
"type": "openText",
|
||||
"headline": "Why did you leave the platform?",
|
||||
"required": true,
|
||||
"inputType": "text"
|
||||
}
|
||||
],
|
||||
"thankYouCard": {
|
||||
"enabled": true,
|
||||
"headline": "Thank you!",
|
||||
"subheader": "We appreciate your feedback."
|
||||
},
|
||||
"hiddenFields": {
|
||||
"enabled": true,
|
||||
"fieldIds": []
|
||||
},
|
||||
"displayOption": "displayOnce",
|
||||
"recontactDays": null,
|
||||
"autoClose": null,
|
||||
"delay": 0,
|
||||
"autoComplete": 50,
|
||||
"closeOnDate": null,
|
||||
"surveyClosedMessage": null,
|
||||
"productOverwrites": null,
|
||||
"singleUse": {
|
||||
"enabled": false,
|
||||
"isEncrypted": true
|
||||
},
|
||||
"verifyEmail": null,
|
||||
"pin": null,
|
||||
"triggers": [],
|
||||
"attributeFilters": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Delete Survey by ID {{ tag: 'DELETE', label: '/api/v1/management/surveys/<survey-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Delete a survey by its ID.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/v1/management/surveys/<survey-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X DELETE \
|
||||
'https://app.formbricks.com/api/v1/management/surveys/<survey-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{title:'200 Success'}}
|
||||
{
|
||||
"data": {
|
||||
"id": "cln8k0tjz00n5z87nwq527h3z",
|
||||
"createdAt": "2023-10-02T07:13:19.775Z",
|
||||
"updatedAt": "2023-10-02T07:13:19.775Z",
|
||||
"name": "Churn Survey",
|
||||
"type": "link",
|
||||
"environmentId": "cln8k0t47000fz87njmmu2bck",
|
||||
"status": "inProgress",
|
||||
"attributeFilters": [],
|
||||
"displayOption": "displayOnce",
|
||||
"autoClose": null,
|
||||
"triggers": [],
|
||||
"redirectUrl": null,
|
||||
"recontactDays": null,
|
||||
"questions": [
|
||||
{
|
||||
"id": "churn-reason",
|
||||
"type": "multipleChoiceSingle",
|
||||
"headline": "Why did you cancel your subscription?",
|
||||
"subheader": "We're sorry to see you leave. Help us do better:",
|
||||
"required": true,
|
||||
"logic": [
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "Difficult to use",
|
||||
"destination": "easier-to-use"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "It's too expensive",
|
||||
"destination": "30-off"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "I am missing features",
|
||||
"destination": "missing-features"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "Poor customer service",
|
||||
"destination": "poor-service"
|
||||
},
|
||||
{
|
||||
"condition": "equals",
|
||||
"value": "I just didn't need it anymore",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"choices": [
|
||||
{
|
||||
"id": "isud2xethsw63dlwl89kr4kj",
|
||||
"label": "Difficult to use"
|
||||
},
|
||||
{
|
||||
"id": "opuu4ba3dlele3n0gjkuh27c",
|
||||
"label": "It's too expensive"
|
||||
},
|
||||
{
|
||||
"id": "gnypapo0rhvkt8pwosrphvbl",
|
||||
"label": "I am missing features"
|
||||
},
|
||||
{
|
||||
"id": "wkgsrsrazd9kfunqhzjezx6t",
|
||||
"label": "Poor customer service"
|
||||
},
|
||||
{
|
||||
"id": "pykmgyyw74vg0gaeryj6bo4c",
|
||||
"label": "I just didn't need it anymore"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "easier-to-use",
|
||||
"type": "openText",
|
||||
"headline": "What would have made {{productName}} easier to use?",
|
||||
"subheader": "",
|
||||
"required": true,
|
||||
"buttonLabel": "Send",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "submitted",
|
||||
"destination": "end"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "30-off",
|
||||
"type": "cta",
|
||||
"headline": "Get 30% off for the next year!",
|
||||
"required": true,
|
||||
"buttonLabel": "Get 30% off",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "clicked",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We'd love to keep you as a customer. Happy to offer a 30% discount for the next year.</span></p>",
|
||||
"buttonUrl": "https://formbricks.com",
|
||||
"buttonExternal": true,
|
||||
"dismissButtonLabel": "Skip"
|
||||
},
|
||||
{
|
||||
"id": "missing-features",
|
||||
"type": "openText",
|
||||
"headline": "What features are you missing?",
|
||||
"subheader": "",
|
||||
"required": true,
|
||||
"logic": [
|
||||
{
|
||||
"condition": "submitted",
|
||||
"destination": "end"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "poor-service",
|
||||
"type": "cta",
|
||||
"headline": "So sorry to hear 😔 Talk to our CEO directly!",
|
||||
"required": true,
|
||||
"buttonLabel": "Send email to CEO",
|
||||
"logic": [
|
||||
{
|
||||
"condition": "clicked",
|
||||
"destination": "end"
|
||||
}
|
||||
],
|
||||
"html": "<p class=\"fb-editor-paragraph\" dir=\"ltr\"><span>We aim to provide the best possible customer service. Please email our CEO and she will personally handle your issue.</span></p>",
|
||||
"buttonUrl": "mailto:ceo@company.com",
|
||||
"buttonExternal": true,
|
||||
"dismissButtonLabel": "Skip"
|
||||
}
|
||||
],
|
||||
"thankYouCard": {
|
||||
"enabled": false
|
||||
},
|
||||
"delay": 0,
|
||||
"autoComplete": null,
|
||||
"closeOnDate": null,
|
||||
"surveyClosedMessage": null,
|
||||
"verifyEmail": null
|
||||
}
|
||||
}
|
||||
```
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -1,398 +0,0 @@
|
||||
import {generateManagementApiMetadata} from "@/lib/utils"
|
||||
|
||||
export const metadata = generateManagementApiMetadata("Webhook",["Fetch","Create","Delete"])
|
||||
|
||||
#### Management API
|
||||
|
||||
# Webhook API
|
||||
|
||||
Formbricks' Webhook API offers a powerful interface for interacting with webhooks. Webhooks allow you to receive real-time HTTP notifications of changes to specific objects in the Formbricks environment.
|
||||
|
||||
The behavior of the webhooks is determined by their trigger settings. The trigger determines which updates the webhook sends. Current available triggers include "responseCreated", "responseUpdated", and "responseFinished". This allows you to customize your webhooks to only send notifications for the events that are relevant to your application.
|
||||
|
||||
Webhooks are tied to a specific Formbricks environment. Once set, a webhook will receive updates from all surveys within this environment. This makes it easy to manage your data flow and ensure that all relevant updates are caught by the webhook.
|
||||
|
||||
This set of API can be used to
|
||||
- [List All Webhooks](#list-webhooks)
|
||||
- [Get Webhook](#retrieve-webhook-by-id)
|
||||
- [Create Webhook](#create-webhook)
|
||||
- [Delete Webhook](#delete-webhook-by-id)
|
||||
|
||||
And the detailed Webhook Payload is elaborated [here](#webhook-payload).
|
||||
|
||||
These APIs are designed to facilitate seamless integration of Formbricks with third-party systems. By making use of our webhook API, you can automate the process of sending data to these systems whenever significant events occur within your Formbricks environment.
|
||||
|
||||
<Note>You will need an API Key to interact with these APIs.</Note>
|
||||
|
||||
---
|
||||
|
||||
## List Webhooks {{ tag: 'GET', label: '/api/v1/webhooks' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
Learn how to retrieve a list of all webhooks via API.
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/webhooks">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/webhooks' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "cliu1kdza000219zftad4ip6c",
|
||||
"createdAt": "2023-06-13T08:49:04.198Z",
|
||||
"updatedAt": "2023-06-13T08:49:04.198Z",
|
||||
"url": "https://mysystem.com/myendpoint",
|
||||
"environmentId": "clisypjy4000319t4imm289uo",
|
||||
"triggers": [
|
||||
"responseFinished"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Retrieve Webhook by ID {{ tag: 'GET', label: '/api/v1/webhooks/<webhook-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/api/v1/webhooks/<webhook-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location \
|
||||
'https://app.formbricks.com/api/v1/webhooks/<webhook-id>' \
|
||||
--header \
|
||||
'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "cliu167rk000019zfhbo68bar",
|
||||
"createdAt": "2023-06-13T08:38:02.960Z",
|
||||
"updatedAt": "2023-06-13T08:38:02.960Z",
|
||||
"url": "https://mysystem.com/myendpoint",
|
||||
"environmentId": "clisypjy4000319t4imm289uo",
|
||||
"triggers": [
|
||||
"responseFinished"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Create Webhook {{ tag: 'POST', label: '/api/v1/webhooks' }}
|
||||
|
||||
Add a webhook to your product.
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### Request Body Parameters
|
||||
|
||||
<Properties>
|
||||
<Property name="url" type="string" required>
|
||||
The URL where the webhook will send data to.
|
||||
</Property>
|
||||
<Property name="triggers" type="string[]" required>
|
||||
List of events that will trigger the webhook.
|
||||
</Property>
|
||||
<Property name="surveyIds" type="string[]">
|
||||
List of survey IDs that will trigger the webhook. If not provided, the webhook will be triggered for all surveys.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
| field name | required | default | description |
|
||||
| ---------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| url | yes | - | The endpoint that the webhook will send data to |
|
||||
| trigger | yes | - | The event that will trigger the webhook ("responseCreated" or "responseUpdated" or "responseFinished") |
|
||||
| surveyIds | no | - | List of survey IDs that will trigger the webhook. If not provided, the webhook will be triggered for all surveys. |
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/api/v1/webhooks">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request POST 'https://app.formbricks.com/api/v1/webhooks' \
|
||||
--header 'x-api-key: <your-api-key>' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"url": "https://mysystem.com/myendpoint",
|
||||
"triggers": ["responseFinished"]
|
||||
}'
|
||||
```
|
||||
|
||||
```json {{ title: 'Example Request Body' }}
|
||||
{
|
||||
"url": "https://mysystem.com/myendpoint",
|
||||
"triggers": ["responseFinished"]
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "cliu1kdza000219zftad4ip6c",
|
||||
"createdAt": "2023-06-13T08:49:04.198Z",
|
||||
"updatedAt": "2023-06-13T08:49:04.198Z",
|
||||
"url": "https://mysystem.com/myendpoint",
|
||||
"environmentId": "clisypjy4000319t4imm289uo",
|
||||
"triggers": ["responseFinished"],
|
||||
"surveyIds": ["clisypjy4000319t4imm289uo"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '400 Bad Request' }}
|
||||
{
|
||||
"code": "bad_request",
|
||||
"message": "Missing trigger",
|
||||
"details": {
|
||||
"missing_field": "trigger"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Delete Webhook by ID {{ tag: 'DELETE', label: '/api/v1/webhooks/<webhook-id>' }}
|
||||
|
||||
<Row>
|
||||
<Col>
|
||||
|
||||
### Mandatory Headers
|
||||
|
||||
<Properties>
|
||||
<Property name="x-Api-Key" type="string">
|
||||
Your Formbricks API key.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="DELETE" label="/api/v1/webhooks/<webhook-id>">
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request DELETE 'https://app.formbricks.com/api/v1/webhooks/<webhook-id>' \
|
||||
--header 'x-api-key: <your-api-key>'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
|
||||
```json {{ title: '200 Success' }}
|
||||
{
|
||||
"data": {
|
||||
"id": "cliu167rk000019zfhbo68bar",
|
||||
"createdAt": "2023-06-13T08:38:02.960Z",
|
||||
"updatedAt": "2023-06-13T08:38:02.960Z",
|
||||
"url": "https://mysystem.com/myendpoint",
|
||||
"environmentId": "clisypjy4000319t4imm289uo",
|
||||
"triggers": ["responseFinished"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '401 Not Authenticated' }}
|
||||
{
|
||||
"code": "not_authenticated",
|
||||
"message": "Not authenticated",
|
||||
"details": {
|
||||
"x-Api-Key": "Header not provided or API Key invalid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json {{ title: '404 Not Found' }}
|
||||
{
|
||||
"code": "not_found",
|
||||
"message": "Webhook not found.",
|
||||
"details": {
|
||||
"webhookId": "The requested webhook does not exist."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
## Webhook Payload
|
||||
|
||||
This documentation helps understand the payload structure that will be received when the webhook is triggered in Formbricks.
|
||||
|
||||
<Row>
|
||||
<Col sticky>
|
||||
|
||||
| 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). |
|
||||
|
||||
</Col>
|
||||
<Col>
|
||||
|
||||
### An example webhook payload
|
||||
|
||||
<CodeGroup title="Payload">
|
||||
|
||||
```json
|
||||
{
|
||||
"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": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
@@ -95,7 +95,7 @@ Click "Create a webhook":
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Enter the Formbricks API key. Learn how to get one from the [API Key tutorial](/docs/api/management/api-key-setup).
|
||||
Enter the Formbricks API key. Learn how to get one from the [API Key tutorial](/docs/additional-features/api#how-to-generate-an-api-key).
|
||||
|
||||
<MdxImage
|
||||
src={EnterApiKey}
|
||||
|
||||
@@ -90,7 +90,7 @@ Click on Create New Credentail button to add your host and API Key
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Now you need an API key. Please refer to the [API Key Setup](/docs/api/management/api-key-setup) page to learn how to create one.
|
||||
Now you need an API key. Please refer to the [API Key Setup](/docs/additional-features/api#how-to-generate-an-api-key) page to learn how to create one.
|
||||
|
||||
Once you copied it in the API Key field, hit Save button to test the connection and save the credentials.
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ Now, you have to connect Zapier with Formbricks via an API Key:
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Now you need an API key. Please refer to the [API Key Setup](/docs/api/management/api-key-setup) page to learn how to create one.
|
||||
Now you need an API key. Please refer to the [API Key Setup](/docs/additional-features/api#how-to-generate-an-api-key) page to learn how to create one.
|
||||
|
||||
Once you copied it in the newly opened Zapier window, you will be connected:
|
||||
|
||||
|
||||
@@ -215,7 +215,10 @@ export const navigation: Array<NavGroup> = [
|
||||
},
|
||||
{
|
||||
title: "Additional Features",
|
||||
links: [{ title: "Multi Language Surveys", href: "/docs/additional-features/multi-language-surveys" }],
|
||||
links: [
|
||||
{ title: "Multi Language Surveys", href: "/docs/additional-features/multi-language-surveys" },
|
||||
{ title: "API", href: "/docs/additional-features/api" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Best Practices",
|
||||
@@ -267,29 +270,6 @@ export const navigation: Array<NavGroup> = [
|
||||
{ title: "FAQ", href: "/docs/faq" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Client API",
|
||||
links: [
|
||||
{ title: "Overview", href: "/docs/api/client/overview" },
|
||||
{ title: "Actions", href: "/docs/api/client/actions" },
|
||||
{ title: "Displays", href: "/docs/api/client/displays" },
|
||||
{ title: "People", href: "/docs/api/client/people" },
|
||||
{ title: "Responses", href: "/docs/api/client/responses" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Management API",
|
||||
links: [
|
||||
{ title: "API Key Setup", href: "/docs/api/management/api-key-setup" },
|
||||
{ title: "Action Classes", href: "/docs/api/management/action-classes" },
|
||||
{ title: "Attribute Classes", href: "/docs/api/management/attribute-classes" },
|
||||
{ title: "Me", href: "/docs/api/management/me" },
|
||||
{ title: "People", href: "/docs/api/management/people" },
|
||||
{ title: "Responses", href: "/docs/api/management/responses" },
|
||||
{ title: "Surveys", href: "/docs/api/management/surveys" },
|
||||
{ title: "Webhooks", href: "/docs/api/management/webhooks" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function Navigation(props: React.ComponentPropsWithoutRef<"nav">) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import CalLogoLight from "@/images/clients/cal-logo-light.svg";
|
||||
import CrowdLogoLight from "@/images/clients/crowd-logo-light.svg";
|
||||
import FlixbusLogo from "@/images/clients/flixbus-white.svg";
|
||||
import GumtreeLogo from "@/images/clients/gumtree.png";
|
||||
import LelyLogo from "@/images/clients/lely-logo.webp";
|
||||
import NILogoDark from "@/images/clients/niLogoDark.svg";
|
||||
import OpinodoLogo from "@/images/clients/opinodo.png";
|
||||
import OptimoleLogo from "@/images/clients/optimole-logo.svg";
|
||||
@@ -42,10 +43,11 @@ export const Hero: React.FC = ({}) => {
|
||||
know what your customers need.
|
||||
</span>
|
||||
</p>
|
||||
<div className="mx-auto mt-5 max-w-xl items-center px-4 sm:flex sm:justify-center md:mt-6 md:space-x-8 md:px-0 lg:max-w-4xl">
|
||||
<div className="grid grid-cols-2 items-center gap-8 pt-2 md:grid-cols-3 lg:grid-cols-8">
|
||||
<div className="mx-auto mt-5 max-w-xl items-center px-4 sm:flex sm:justify-center md:mt-6 md:space-x-8 md:px-0 lg:max-w-5xl">
|
||||
<div className="grid grid-cols-3 items-center gap-8 pt-2 md:grid-cols-3 lg:grid-cols-9">
|
||||
<Image src={FlixbusLogo} alt="Flixbus Flix Flixtrain Logo" className="rounded-md" width={200} />
|
||||
<Image src={GumtreeLogo} alt="Gumtree Logo" width={200} />
|
||||
<Image src={LelyLogo} alt="Lely Logo" width={200} />
|
||||
<Image src={CalLogoLight} alt="Cal Logo" width={170} />
|
||||
<Image src={ThemeisleLogo} alt="ThemeIsle Logo" width={200} />
|
||||
<Image src={OpinodoLogo} alt="Opinodo.com Logo" width={200} />
|
||||
|
||||
@@ -19,7 +19,7 @@ export const SurveyTypeSelection: React.FC = () => {
|
||||
subheading="Follow individual feedback trails or zoom out for the big picture. All in one place."
|
||||
/>
|
||||
|
||||
<div className="flex space-x-8 text-center">
|
||||
<div className="space-y-6 text-center md:flex md:space-x-8 md:space-y-0">
|
||||
<OptionCard
|
||||
size="lg"
|
||||
title="On your website"
|
||||
@@ -32,7 +32,7 @@ export const SurveyTypeSelection: React.FC = () => {
|
||||
<OptionCard
|
||||
size="lg"
|
||||
title="In emails"
|
||||
description="Create on brand surveys, embedded in your emails."
|
||||
description="Embed branded surveys in your emails."
|
||||
onSelect={() => {
|
||||
router.push("/open-source-form-builder");
|
||||
}}>
|
||||
|
||||
@@ -2,6 +2,7 @@ import CalLogoLight from "@/images/clients/cal-logo-light.svg";
|
||||
import CrowdLogoLight from "@/images/clients/crowd-logo-light.svg";
|
||||
import FlixbusLogo from "@/images/clients/flixbus-white.svg";
|
||||
import GumtreeLogo from "@/images/clients/gumtree.png";
|
||||
import LelyLogo from "@/images/clients/lely-logo.webp";
|
||||
import NILogoDark from "@/images/clients/niLogoDark.svg";
|
||||
import OpinodoLogo from "@/images/clients/opinodo.png";
|
||||
import OptimoleLogo from "@/images/clients/optimole-logo.svg";
|
||||
@@ -15,35 +16,16 @@ export default function LogoBar() {
|
||||
10,000+ teams at the world’s best companies trust Formbricks
|
||||
</p>
|
||||
<div className="mt-5 items-center px-4 sm:flex sm:justify-center md:mt-6 md:space-x-8 md:px-0">
|
||||
<div className="grid grid-cols-2 items-center gap-8 pt-2 md:grid-cols-2 md:gap-10 lg:grid-cols-8">
|
||||
<Image
|
||||
src={FlixbusLogo}
|
||||
alt="Flixbus Flix Flixtrain Logo"
|
||||
className="rounded-lg pb-1 "
|
||||
width={200}
|
||||
/>
|
||||
<Image
|
||||
src={GumtreeLogo}
|
||||
alt="Flixbus Flix Flixtrain Logo"
|
||||
className="rounded-lg pb-1 "
|
||||
width={200}
|
||||
/>
|
||||
<Image src={CalLogoLight} alt="Cal Logo" className="block dark:hidden" width={170} />
|
||||
<Image src={ThemeisleLogo} alt="ThemeIsle Logo" className="pb-1" width={200} />
|
||||
<Image
|
||||
src={OpinodoLogo}
|
||||
alt="Crowd.dev Logo"
|
||||
className="block rounded-lg pb-1 dark:hidden"
|
||||
width={200}
|
||||
/>
|
||||
<Image
|
||||
src={CrowdLogoLight}
|
||||
alt="Crowd.dev Logo"
|
||||
className="block rounded-lg pb-1 dark:hidden"
|
||||
width={200}
|
||||
/>
|
||||
<Image src={OptimoleLogo} alt="Optimole Logo" className="pb-1" width={200} />
|
||||
<Image src={NILogoDark} alt="Neverinstall Logo" className="block pb-1 dark:hidden" width={200} />
|
||||
<div className="grid grid-cols-3 items-center gap-8 pt-2 md:grid-cols-3 md:gap-10 lg:grid-cols-9">
|
||||
<Image src={FlixbusLogo} alt="Flixbus Flix Flixtrain Logo" width={200} />
|
||||
<Image src={GumtreeLogo} alt="Gumtree Logo" width={200} />
|
||||
<Image src={LelyLogo} alt="Lely Logo" width={200} />
|
||||
<Image src={CalLogoLight} alt="Cal Logo" width={200} />
|
||||
<Image src={ThemeisleLogo} alt="ThemeIsle Logo" width={200} />
|
||||
<Image src={OpinodoLogo} alt="Crowd.dev Logo" width={200} />
|
||||
<Image src={CrowdLogoLight} alt="Crowd.dev Logo" width={200} />
|
||||
<Image src={OptimoleLogo} alt="Optimole Logo" width={200} />
|
||||
<Image src={NILogoDark} alt="Neverinstall Logo" width={200} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import CCPALogo from "@/images/ccpa.svg";
|
||||
import GPDRLogo from "@/images/gdpr.svg";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { FaDiscord, FaGithub, FaXTwitter } from "react-icons/fa6";
|
||||
|
||||
import { FooterLogo } from "./Logo";
|
||||
import SourceForgeBadge from "./SourceForgeBadge";
|
||||
|
||||
const navigation = {
|
||||
products: [
|
||||
@@ -89,6 +93,11 @@ export default function Footer() {
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex space-x-4">
|
||||
<SourceForgeBadge />
|
||||
<Image src={GPDRLogo} alt="GDPR Logo" width={50} />
|
||||
<Image src={CCPALogo} alt="CCPA Logo" width={50} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-8 lg:col-span-2 lg:grid-cols-4">
|
||||
<div>
|
||||
|
||||
34
apps/formbricks-com/components/shared/SourceForgeBadge.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
const SourceForgeBadge: React.FC = () => {
|
||||
useEffect(() => {
|
||||
const script = document.createElement("script");
|
||||
script.async = true;
|
||||
script.src = "https://b.sf-syn.com/badge_js?sf_id=3747607&variant_id=sf";
|
||||
const firstScript = document.getElementsByTagName("script")[0];
|
||||
firstScript.parentNode?.insertBefore(script, firstScript);
|
||||
|
||||
return () => {
|
||||
// Clean up the script when the component unmounts
|
||||
firstScript.parentNode?.removeChild(script);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
data-id="3747607"
|
||||
data-badge="heart-badge-white"
|
||||
data-variant-id="sf"
|
||||
style={{ width: "75px" }}
|
||||
className="p-0.5">
|
||||
<a
|
||||
href="https://sourceforge.net/software/product/Formbricks/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Formbricks Reviews
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SourceForgeBadge;
|
||||
11
apps/formbricks-com/images/ccpa.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="32" height="50" viewBox="0 0 32 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.59947 48.1181C9.09296 48.1181 9.38736 47.8022 9.47458 47.4725L9.17472 47.3736C9.10928 47.5934 8.92393 47.8022 8.59947 47.8022C8.26962 47.8022 7.95884 47.5605 7.95884 47.1044C7.95884 46.6318 8.27784 46.3983 8.59677 46.3983C8.92117 46.3983 9.10112 46.5879 9.15834 46.8241L9.46642 46.7198C9.37921 46.3791 9.0875 46.0879 8.59677 46.0879C8.09519 46.0879 7.62354 46.4698 7.62354 47.1044C7.62354 47.739 8.07881 48.1181 8.59947 48.1181ZM10.0062 47.1016C10.0062 46.6319 10.3279 46.3983 10.6523 46.3983C10.9795 46.3983 11.3012 46.6319 11.3012 47.1016C11.3012 47.5715 10.9795 47.805 10.6523 47.805C10.3279 47.805 10.0062 47.5715 10.0062 47.1016ZM9.67091 47.1016C9.67091 47.7418 10.1453 48.1181 10.6523 48.1181C11.1593 48.1181 11.6365 47.7418 11.6365 47.1016C11.6365 46.4643 11.1593 46.0879 10.6523 46.0879C10.1453 46.0879 9.67091 46.4643 9.67091 47.1016ZM14.1244 48.0769V46.1291H13.68L13.0612 47.6154L12.4341 46.1291H11.9979V48.0769H12.3169V46.6483L12.914 48.0769H13.1974L13.7999 46.6428V48.0769H14.1244ZM14.9367 47.0247V46.4203H15.2775C15.4847 46.4203 15.6101 46.5385 15.6101 46.7253C15.6101 46.9093 15.4847 47.0247 15.2775 47.0247H14.9367ZM15.3266 47.316C15.6973 47.316 15.9427 47.066 15.9427 46.7225C15.9427 46.3819 15.6973 46.1291 15.3266 46.1291H14.6096V48.0769H14.9367V47.316H15.3266ZM17.4735 48.0769V47.7637H16.5874V46.1291H16.2603V48.0769H17.4735ZM18.0944 48.0769V46.1291H17.7619V48.0769H18.0944ZM19.8987 48.0769H20.2558L19.5033 46.1291H19.1244L18.372 48.0769H18.7182L18.8981 47.5879H19.716L19.8987 48.0769ZM19.307 46.4863L19.6015 47.283H19.0126L19.307 46.4863ZM22.1554 48.0769V46.1291H21.8282V47.511L20.9504 46.1291H20.5333V48.0769H20.8604V46.6016L21.8146 48.0769H22.1554ZM24.0293 46.4396V46.1291H22.4454V46.4396H23.0724V48.0769H23.3996V46.4396H24.0293Z" fill="#64748b"/>
|
||||
<path d="M7.4822 43.2638C9.40139 43.2638 10.2215 41.9451 10.4221 41.0836L9.10479 40.7055C8.98269 41.1539 8.55521 41.8748 7.4822 41.8748C6.55747 41.8748 5.70255 41.1978 5.70255 40.0287C5.70255 38.7187 6.63596 38.121 7.46476 38.121C8.55521 38.121 8.9478 38.8506 9.04374 39.2814L10.3436 38.8682C10.1429 37.9715 9.32289 36.7671 7.46476 36.7671C5.73744 36.7671 4.27185 38.0858 4.27185 40.0287C4.27185 41.9715 5.70254 43.2638 7.4822 43.2638ZM13.8911 43.2638C15.8104 43.2638 16.6304 41.9451 16.831 41.0836L15.5138 40.7055C15.3916 41.1539 14.9641 41.8748 13.8911 41.8748C12.9664 41.8748 12.1115 41.1978 12.1115 40.0287C12.1115 38.7187 13.0449 38.121 13.8737 38.121C14.9641 38.121 15.3567 38.8506 15.4527 39.2814L16.7525 38.8682C16.5519 37.9715 15.7319 36.7671 13.8737 36.7671C12.1464 36.7671 10.6808 38.0858 10.6808 40.0287C10.6808 41.9715 12.1115 43.2638 13.8911 43.2638ZM18.8345 39.6946V38.1033H19.6371C20.1605 38.1033 20.5182 38.4023 20.5182 38.9033C20.5182 39.3868 20.1605 39.6946 19.6371 39.6946H18.8345ZM19.7767 40.8902C21.0329 40.8902 21.8965 40.0726 21.8965 38.8945C21.8965 37.7341 21.0329 36.899 19.7767 36.899H17.4474V43.1319H18.8258V40.8902H19.7767ZM26.1414 43.1319H27.6419L25.3388 36.899H23.7424L21.4132 43.1319H22.8613L23.3062 41.866H25.6965L26.1414 43.1319ZM24.5188 38.4462L25.2603 40.6H23.7599L24.5188 38.4462Z" fill="#64748b"/>
|
||||
<path d="M14.393 7.2895V0.507324H6.22569V2.47134L5.29443 5.16282L6.22569 6.10842V8.80019L8.08849 10.6914V11.5645L9.73642 14.2559V15.4197L12.4588 18.9114V21.8213H15.3962L19.838 24.3672V26.3315L26.2859 25.3131V20.8757L21.7725 16.0466H12.3824V7.2895H14.393Z" fill="#64748b"/>
|
||||
<path d="M15.1082 23.3091V22.4849H12.3821V23.3091H15.1082Z" fill="#64748b"/>
|
||||
<path d="M15.3815 25.5071V24.4082H16.1993V25.5071H15.3815Z" fill="#64748b"/>
|
||||
<path d="M16.1989 24.4082V23.584H17.0167V24.4082H16.1989Z" fill="#64748b"/>
|
||||
<path d="M13.7455 9.20666C13.7455 8.90341 13.9895 8.65723 14.2907 8.65723H21.3787C21.68 8.65723 21.924 8.90341 21.924 9.20666V14.1284C21.924 14.432 21.68 14.6779 21.3787 14.6779H14.2907C13.9895 14.6779 13.7455 14.432 13.7455 14.1284V9.20666Z" fill="#64748b"/>
|
||||
<path d="M17.0931 12.9479L16.017 11.9471C15.896 11.8345 15.896 11.6528 16.017 11.5402C16.1373 11.4284 16.3327 11.4278 16.4538 11.5386C16.4541 11.5391 16.4546 11.5394 16.4551 11.5399L17.3501 12.3699L19.4863 10.3833C19.6063 10.2718 19.8015 10.2713 19.9223 10.382C19.9231 10.3825 19.9239 10.3833 19.9247 10.3842C20.046 10.4977 20.0458 10.68 19.9239 10.7932L17.6072 12.9479C17.4663 13.079 17.237 13.0795 17.0953 12.9495C17.0944 12.949 17.0939 12.9485 17.0931 12.9479Z" fill="#131212"/>
|
||||
<path d="M19.7595 8.79228V7.27003C19.7595 6.26615 18.9517 5.45215 17.9553 5.45215C16.9589 5.45215 16.1511 6.26615 16.1511 7.27003V8.79228" stroke="#64748b" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
BIN
apps/formbricks-com/images/clients/lely-logo.webp
Normal file
|
After Width: | Height: | Size: 21 KiB |
19
apps/formbricks-com/images/gdpr.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="34" height="50" viewBox="0 0 34 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.1891 49.1803H12.5685L11.7691 47.2431H11.3665L10.567 49.1803H10.9349L11.126 48.694H11.995L12.1891 49.1803ZM11.5606 47.5983L11.8734 48.3907H11.2477L11.5606 47.5983ZM14.1523 49.1803V48.8688H13.2109V47.2431H12.8633V49.1803H14.1523ZM14.8121 49.1803V47.2431H14.4588V49.1803H14.8121ZM17.1461 49.1803V48.1995H16.141V48.4863H16.8188C16.8014 48.653 16.645 48.9208 16.2308 48.9208C15.8774 48.9208 15.553 48.683 15.553 48.2104C15.553 47.7295 15.9006 47.5055 16.2336 47.5055C16.561 47.5055 16.7638 47.6913 16.8332 47.9098L17.1606 47.7923C17.0505 47.4726 16.7319 47.2021 16.2336 47.2021C15.7007 47.2021 15.1997 47.5738 15.1997 48.2104C15.1997 48.8497 15.6746 49.2213 16.2105 49.2213C16.5378 49.2213 16.7492 49.0765 16.839 48.9344L16.8651 49.1803H17.1461ZM19.3023 49.1803V47.2431H18.9547V48.6175L18.022 47.2431H17.5788V49.1803H17.9264V47.7131L18.9402 49.1803H19.3023ZM21.0877 49.1803V48.8743H20.1666V48.3606H21.0008V48.0628H20.1666V47.5491H21.0877V47.2431H19.819V49.1803H21.0877ZM21.8469 48.8798V47.5437H22.2148C22.571 47.5437 22.8636 47.7623 22.8636 48.2158C22.8636 48.6639 22.5681 48.8798 22.2118 48.8798H21.8469ZM22.2234 49.1803C22.7796 49.1803 23.2256 48.8361 23.2256 48.2158C23.2256 47.5929 22.7854 47.2431 22.2263 47.2431H21.4993V49.1803H22.2234Z" fill="#64748b"/>
|
||||
<path d="M11.1576 44.2621V40.9485H7.81151V42.1201H9.78584C9.70237 42.4698 9.24824 43.1343 8.16377 43.1343C7.09784 43.1343 6.18945 42.4611 6.18945 41.1671C6.18945 39.7856 7.23684 39.2261 8.12664 39.2261C9.22037 39.2261 9.65604 39.9256 9.75804 40.3103L11.1669 39.8469C10.8796 38.9813 10.0082 37.9321 8.12664 37.9321C6.27287 37.9321 4.70642 39.2173 4.70642 41.1671C4.70642 43.1256 6.20799 44.3933 8.03397 44.3933C8.97017 44.3933 9.59117 44.0261 9.87851 43.6326L9.97117 44.2621H11.1576ZM13.5866 42.9594V39.366H14.4394C15.4497 39.366 16.2746 39.9256 16.2746 41.1671C16.2746 42.4086 15.4497 42.9594 14.4394 42.9594H13.5866ZM14.495 44.2621C16.4786 44.2621 17.804 43.0818 17.804 41.1671C17.804 39.2523 16.4786 38.0633 14.5043 38.0633H12.1222V44.2621H14.495ZM20.1248 40.8436V39.2611H20.9775C21.5336 39.2611 21.9137 39.5583 21.9137 40.0567C21.9137 40.5376 21.5336 40.8436 20.9775 40.8436H20.1248ZM21.1258 42.0326C22.4606 42.0326 23.3782 41.2195 23.3782 40.0479C23.3782 38.8938 22.4606 38.0633 21.1258 38.0633H18.651V44.2621H20.1155V42.0326H21.1258ZM27.2181 44.2621H28.8309L27.4962 41.7529C28.3026 41.4818 28.7938 40.8436 28.7938 40.0043C28.7938 38.9026 27.9596 38.0633 26.662 38.0633H24.0574V44.2621H25.5219V41.9539H26.041L27.2181 44.2621ZM25.5219 40.7649V39.2611H26.3839C26.9864 39.2611 27.3108 39.5496 27.3108 40.0129C27.3108 40.4501 26.9864 40.7649 26.3839 40.7649H25.5219Z" fill="#64748b"/>
|
||||
<path d="M16.8006 1.87061L17.3072 3.38312H18.9472L17.6206 4.31787L18.1272 5.83039L16.8006 4.89506L15.4739 5.83039L15.9808 4.31787L14.6536 3.38312H16.2936L16.8006 1.87061Z" fill="#64748b"/>
|
||||
<path d="M16.8006 26.7607L17.3072 28.2736H18.9472L17.6206 29.2083L18.1272 30.7206L16.8006 29.7859L15.4739 30.7206L15.9808 29.2083L14.6536 28.2736H16.2936L16.8006 26.7607Z" fill="#64748b"/>
|
||||
<path d="M23.5467 4.13281L24.053 5.6453H25.6933L24.3664 6.58044L24.8733 8.09257L23.5467 7.15744L22.2195 8.09257L22.7264 6.58044L21.3998 5.6453H23.0401L23.5467 4.13281Z" fill="#64748b"/>
|
||||
<path d="M29.0669 8.42285L29.5731 9.93523H31.2135L29.8866 10.8702L30.3935 12.3827L29.0669 11.448L27.7399 12.3827L28.2468 10.8702L26.9199 9.93523H28.5597L29.0669 8.42285Z" fill="#64748b"/>
|
||||
<path d="M29.0669 20.5381L29.5735 22.0512H31.2135L29.8866 22.9859L30.3937 24.498L29.0669 23.5635L27.7399 24.498L28.2468 22.9859L26.9199 22.0512H28.5599L29.0669 20.5381Z" fill="#64748b"/>
|
||||
<path d="M4.53114 8.42285L5.03772 9.93523H6.67777L5.35101 10.8702L5.85758 12.3827L4.53114 11.448L3.20412 12.3827L3.71092 10.8702L2.38416 9.93523H4.02459L4.53114 8.42285Z" fill="#64748b"/>
|
||||
<path d="M30.294 14.3154L30.8006 15.8279H32.4406L31.1138 16.7626L31.6206 18.2751L30.294 17.341L28.9671 18.2751L29.4743 16.7626L28.1471 15.8279H29.7868L30.294 14.3154Z" fill="#64748b"/>
|
||||
<path d="M3.30594 14.3154L3.81249 15.8279H5.45246L4.12549 16.7626L4.63265 18.2751L3.30594 17.341L1.97889 18.2751L2.48578 16.7626L1.15881 15.8279H2.79905L3.30594 14.3154Z" fill="#64748b"/>
|
||||
<path d="M4.53128 20.5381L5.0378 22.0512H6.67777L5.35118 22.9859L5.85764 24.498L4.53128 23.5635L3.20432 24.498L3.71112 22.9859L2.38416 22.0512H4.02448L4.53128 20.5381Z" fill="#64748b"/>
|
||||
<path d="M24.7739 25.6294L25.2805 27.1417H26.9206L25.5934 28.077L26.1006 29.5893L24.7739 28.6546L23.4467 29.5893L23.9539 28.077L22.627 27.1417H24.2673L24.7739 25.6294Z" fill="#64748b"/>
|
||||
<path d="M10.0512 4.13281L10.5576 5.6453H12.198L10.871 6.58044L11.3782 8.09257L10.0512 7.15744L8.7245 8.09257L9.2313 6.58044L7.9043 5.6453H9.5443L10.0512 4.13281Z" fill="#64748b"/>
|
||||
<path d="M8.82574 25.6294L9.33248 27.1425H10.9727L9.64581 28.0767L10.1526 29.5893L8.82574 28.6551L7.49914 29.5893L8.00588 28.0767L6.67908 27.1425H8.31888L8.82574 25.6294Z" fill="#64748b"/>
|
||||
<path d="M12.4551 15.3887C12.4551 15.0869 12.7145 14.8423 13.0344 14.8423H20.5654C20.8855 14.8423 21.1447 15.0869 21.1447 15.3887V20.2604C21.1447 20.5623 20.8855 20.8068 20.5654 20.8068H13.0344C12.7145 20.8068 12.4551 20.5623 12.4551 20.2604V15.3887Z" fill="#64748b"/>
|
||||
<path d="M16.0118 19.1917L14.8686 18.2004C14.7399 18.0887 14.7399 17.9087 14.8686 17.7972C14.9962 17.6865 15.2039 17.6857 15.3325 17.7956C15.333 17.7961 15.3334 17.7964 15.334 17.7969L16.285 18.6193L18.5547 16.6513C18.6822 16.5406 18.8896 16.5401 19.0179 16.6496C19.0188 16.6504 19.0196 16.651 19.0205 16.6518C19.1494 16.7644 19.1491 16.9453 19.0196 17.0573L16.5581 19.1917C16.4084 19.3215 16.1648 19.3223 16.0142 19.1934C16.0133 19.1928 16.0127 19.1923 16.0118 19.1917Z" fill="#64748b"/>
|
||||
<path d="M18.8449 14.9775V13.4766C18.8449 12.478 17.9867 11.6685 16.928 11.6685C15.8693 11.6685 15.0111 12.478 15.0111 13.4766V14.9775" stroke="#64748b" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
@@ -210,6 +210,11 @@ const nextConfig = {
|
||||
destination: "/blog",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/api/:slug*',
|
||||
destination: '/docs/additional-features/api',
|
||||
permanent: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
@@ -20,9 +20,14 @@ import {
|
||||
interface ActivityTabProps {
|
||||
actionClass: TActionClass;
|
||||
environmentId: string;
|
||||
isUserTargetingEnabled: boolean;
|
||||
}
|
||||
|
||||
export default function EventActivityTab({ actionClass, environmentId }: ActivityTabProps) {
|
||||
export default function EventActivityTab({
|
||||
actionClass,
|
||||
environmentId,
|
||||
isUserTargetingEnabled,
|
||||
}: ActivityTabProps) {
|
||||
// const { eventClass, isLoadingEventClass, isErrorEventClass } = useEventClass(environmentId, actionClass.id);
|
||||
|
||||
const [numEventsLastHour, setNumEventsLastHour] = useState<number | undefined>();
|
||||
@@ -46,9 +51,9 @@ export default function EventActivityTab({ actionClass, environmentId }: Activit
|
||||
numEventsLast7DaysData,
|
||||
activeInactiveSurveys,
|
||||
] = await Promise.all([
|
||||
getActionCountInLastHourAction(actionClass.id, environmentId),
|
||||
getActionCountInLast24HoursAction(actionClass.id, environmentId),
|
||||
getActionCountInLast7DaysAction(actionClass.id, environmentId),
|
||||
isUserTargetingEnabled ? getActionCountInLastHourAction(actionClass.id, environmentId) : 0,
|
||||
isUserTargetingEnabled ? getActionCountInLast24HoursAction(actionClass.id, environmentId) : 0,
|
||||
isUserTargetingEnabled ? getActionCountInLast7DaysAction(actionClass.id, environmentId) : 0,
|
||||
getActiveInactiveSurveysAction(actionClass.id, environmentId),
|
||||
]);
|
||||
setNumEventsLastHour(numEventsLastHourData);
|
||||
@@ -62,7 +67,7 @@ export default function EventActivityTab({ actionClass, environmentId }: Activit
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [actionClass.id, environmentId]);
|
||||
}, [actionClass.id, environmentId, isUserTargetingEnabled]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <ErrorComponent />;
|
||||
@@ -70,23 +75,25 @@ export default function EventActivityTab({ actionClass, environmentId }: Activit
|
||||
return (
|
||||
<div className="grid grid-cols-3 pb-2">
|
||||
<div className="col-span-2 space-y-4 pr-6">
|
||||
<div>
|
||||
<Label className="text-slate-500">Ocurrances</Label>
|
||||
<div className="mt-1 grid w-fit grid-cols-3 rounded-lg border-slate-100 bg-slate-50">
|
||||
<div className="border-r border-slate-200 px-4 py-2 text-center">
|
||||
<p className="font-bold text-slate-800">{numEventsLastHour}</p>
|
||||
<p className="text-xs text-slate-500">last hour</p>
|
||||
</div>
|
||||
<div className="border-r border-slate-200 px-4 py-2 text-center">
|
||||
<p className="font-bold text-slate-800">{numEventsLast24Hours}</p>
|
||||
<p className="text-xs text-slate-500">last 24 hours</p>
|
||||
</div>
|
||||
<div className="px-4 py-2 text-center">
|
||||
<p className="font-bold text-slate-800">{numEventsLast7Days}</p>
|
||||
<p className="text-xs text-slate-500">last week</p>
|
||||
{isUserTargetingEnabled && (
|
||||
<div>
|
||||
<Label className="text-slate-500">Ocurrances</Label>
|
||||
<div className="mt-1 grid w-fit grid-cols-3 rounded-lg border-slate-100 bg-slate-50">
|
||||
<div className="border-r border-slate-200 px-4 py-2 text-center">
|
||||
<p className="font-bold text-slate-800">{numEventsLastHour}</p>
|
||||
<p className="text-xs text-slate-500">last hour</p>
|
||||
</div>
|
||||
<div className="border-r border-slate-200 px-4 py-2 text-center">
|
||||
<p className="font-bold text-slate-800">{numEventsLast24Hours}</p>
|
||||
<p className="text-xs text-slate-500">last 24 hours</p>
|
||||
</div>
|
||||
<div className="px-4 py-2 text-center">
|
||||
<p className="font-bold text-slate-800">{numEventsLast7Days}</p>
|
||||
<p className="text-xs text-slate-500">last week</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Label className="text-slate-500">Active surveys</Label>
|
||||
|
||||
@@ -12,15 +12,19 @@ import { ErrorComponent } from "@formbricks/ui/ErrorComponent";
|
||||
import ActionDetailModal from "./ActionDetailModal";
|
||||
import AddNoCodeActionModal from "./AddActionModal";
|
||||
|
||||
interface ActionClassesTableProps {
|
||||
environmentId: string;
|
||||
actionClasses: TActionClass[];
|
||||
children: [JSX.Element, JSX.Element[]];
|
||||
isUserTargetingEnabled: boolean;
|
||||
}
|
||||
|
||||
export default function ActionClassesTable({
|
||||
environmentId,
|
||||
actionClasses,
|
||||
children: [TableHeading, actionRows],
|
||||
}: {
|
||||
environmentId: string;
|
||||
actionClasses: TActionClass[];
|
||||
children: [JSX.Element, JSX.Element[]];
|
||||
}) {
|
||||
isUserTargetingEnabled,
|
||||
}: ActionClassesTableProps) {
|
||||
const [isActionDetailModalOpen, setActionDetailModalOpen] = useState(false);
|
||||
const [isAddActionModalOpen, setAddActionModalOpen] = useState(false);
|
||||
const { membershipRole, isLoading, error } = useMembershipRole(environmentId);
|
||||
@@ -83,6 +87,7 @@ export default function ActionClassesTable({
|
||||
setOpen={setActionDetailModalOpen}
|
||||
actionClass={activeActionClass}
|
||||
membershipRole={membershipRole}
|
||||
isUserTargetingEnabled={isUserTargetingEnabled}
|
||||
/>
|
||||
<AddNoCodeActionModal
|
||||
environmentId={environmentId}
|
||||
|
||||
@@ -13,6 +13,7 @@ interface ActionDetailModalProps {
|
||||
setOpen: (v: boolean) => void;
|
||||
actionClass: TActionClass;
|
||||
membershipRole?: TMembershipRole;
|
||||
isUserTargetingEnabled: boolean;
|
||||
}
|
||||
|
||||
export default function ActionDetailModal({
|
||||
@@ -21,11 +22,18 @@ export default function ActionDetailModal({
|
||||
setOpen,
|
||||
actionClass,
|
||||
membershipRole,
|
||||
isUserTargetingEnabled,
|
||||
}: ActionDetailModalProps) {
|
||||
const tabs = [
|
||||
{
|
||||
title: "Activity",
|
||||
children: <EventActivityTab actionClass={actionClass} environmentId={environmentId} />,
|
||||
children: (
|
||||
<EventActivityTab
|
||||
actionClass={actionClass}
|
||||
environmentId={environmentId}
|
||||
isUserTargetingEnabled={isUserTargetingEnabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
|
||||
@@ -4,16 +4,34 @@ import ActionTableHeading from "@/app/(app)/environments/[environmentId]/(action
|
||||
import { Metadata } from "next";
|
||||
|
||||
import { getActionClasses } from "@formbricks/lib/actionClass/service";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Actions",
|
||||
};
|
||||
|
||||
export default async function ActionClassesComponent({ params }) {
|
||||
let actionClasses = await getActionClasses(params.environmentId);
|
||||
const [actionClasses, team] = await Promise.all([
|
||||
getActionClasses(params.environmentId),
|
||||
getTeamByEnvironmentId(params.environmentId),
|
||||
]);
|
||||
|
||||
if (!team) {
|
||||
throw new Error("Team not found");
|
||||
}
|
||||
|
||||
// On Formbricks Cloud only render the timeline if the user targeting feature is booked
|
||||
const isUserTargetingEnabled = IS_FORMBRICKS_CLOUD
|
||||
? team.billing.features.userTargeting.status === "active"
|
||||
: true;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ActionClassesTable environmentId={params.environmentId} actionClasses={actionClasses}>
|
||||
<ActionClassesTable
|
||||
environmentId={params.environmentId}
|
||||
actionClasses={actionClasses}
|
||||
isUserTargetingEnabled={isUserTargetingEnabled}>
|
||||
<ActionTableHeading />
|
||||
{actionClasses.map((actionClass) => (
|
||||
<ActionClassDataRow key={actionClass.id} actionClass={actionClass} />
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import ActivityTimeline from "@/app/(app)/environments/[environmentId]/(peopleAndSegments)/people/[personId]/components/ActivityTimeline";
|
||||
|
||||
import { getActionsByPersonId } from "@formbricks/lib/action/service";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
|
||||
export default async function ActivitySection({
|
||||
environmentId,
|
||||
@@ -10,9 +12,20 @@ export default async function ActivitySection({
|
||||
environmentId: string;
|
||||
personId: string;
|
||||
}) {
|
||||
const team = await getTeamByEnvironmentId(environmentId);
|
||||
|
||||
if (!team) {
|
||||
throw new Error("Team not found");
|
||||
}
|
||||
|
||||
// On Formbricks Cloud only render the timeline if the user targeting feature is booked
|
||||
const isUserTargetingEnabled = IS_FORMBRICKS_CLOUD
|
||||
? team.billing.features.userTargeting.status === "active"
|
||||
: true;
|
||||
|
||||
const [environment, actions] = await Promise.all([
|
||||
getEnvironment(environmentId),
|
||||
getActionsByPersonId(personId, 1),
|
||||
isUserTargetingEnabled ? getActionsByPersonId(personId, 1) : [],
|
||||
]);
|
||||
|
||||
if (!environment) {
|
||||
@@ -21,7 +34,11 @@ export default async function ActivitySection({
|
||||
|
||||
return (
|
||||
<div className="md:col-span-1">
|
||||
<ActivityTimeline environment={environment} actions={actions.slice(0, 10)} />
|
||||
<ActivityTimeline
|
||||
environment={environment}
|
||||
actions={actions.slice(0, 10)}
|
||||
isUserTargetingEnabled={isUserTargetingEnabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,58 +1,71 @@
|
||||
import { TAction } from "@formbricks/types/actions";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import EmptySpaceFiller from "@formbricks/ui/EmptySpaceFiller";
|
||||
import { UpgradePlanNotice } from "@formbricks/ui/UpgradePlanNotice";
|
||||
|
||||
import { ActivityItemContent, ActivityItemIcon, ActivityItemPopover } from "./ActivityItemComponents";
|
||||
|
||||
interface IActivityTimelineProps {
|
||||
environment: TEnvironment;
|
||||
actions: TAction[];
|
||||
isUserTargetingEnabled: boolean;
|
||||
}
|
||||
|
||||
export default function ActivityTimeline({
|
||||
environment,
|
||||
actions,
|
||||
}: {
|
||||
environment: TEnvironment;
|
||||
actions: TAction[];
|
||||
}) {
|
||||
isUserTargetingEnabled,
|
||||
}: IActivityTimelineProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between pb-6">
|
||||
<h2 className="text-lg font-bold text-slate-700">Actions Timeline</h2>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
{actions.length === 0 ? (
|
||||
<EmptySpaceFiller type={"event"} environment={environment} />
|
||||
) : (
|
||||
<div>
|
||||
{actions.map(
|
||||
(actionItem, index) =>
|
||||
actionItem && (
|
||||
<li key={actionItem.id} className="list-none">
|
||||
<div className="relative pb-12">
|
||||
{index !== actions.length - 1 && (
|
||||
<span
|
||||
className="absolute left-6 top-4 -ml-px h-full w-0.5 bg-slate-200"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
<div className="relative">
|
||||
<ActivityItemPopover actionItem={actionItem}>
|
||||
<div className="flex space-x-3 text-left">
|
||||
<ActivityItemIcon actionItem={actionItem} />
|
||||
<ActivityItemContent actionItem={actionItem} />
|
||||
</div>
|
||||
</ActivityItemPopover>
|
||||
{!isUserTargetingEnabled ? (
|
||||
<UpgradePlanNotice
|
||||
message="Upgrade to the User Targeting plan to store action history."
|
||||
textForUrl="Upgrade now."
|
||||
url={`/environments/${environment.id}/settings/billing`}
|
||||
/>
|
||||
) : (
|
||||
<div className="relative">
|
||||
{actions.length === 0 ? (
|
||||
<EmptySpaceFiller type={"event"} environment={environment} />
|
||||
) : (
|
||||
<div>
|
||||
{actions.map(
|
||||
(actionItem, index) =>
|
||||
actionItem && (
|
||||
<li key={actionItem.id} className="list-none">
|
||||
<div className="relative pb-12">
|
||||
{index !== actions.length - 1 && (
|
||||
<span
|
||||
className="absolute left-6 top-4 -ml-px h-full w-0.5 bg-slate-200"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
<div className="relative">
|
||||
<ActivityItemPopover actionItem={actionItem}>
|
||||
<div className="flex space-x-3 text-left">
|
||||
<ActivityItemIcon actionItem={actionItem} />
|
||||
<ActivityItemContent actionItem={actionItem} />
|
||||
</div>
|
||||
</ActivityItemPopover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
<div className="relative">
|
||||
{actions.length === 10 && (
|
||||
<div className="absolute bottom-0 flex h-56 w-full items-end justify-center bg-gradient-to-t from-slate-50 to-transparent"></div>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
<div className="relative">
|
||||
{actions.length === 10 && (
|
||||
<div className="absolute bottom-0 flex h-56 w-full items-end justify-center bg-gradient-to-t from-slate-50 to-transparent"></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,15 +8,19 @@ import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getTagsByEnvironmentId } from "@formbricks/lib/tag/service";
|
||||
|
||||
export default async function PersonPage({ params }) {
|
||||
const environment = await getEnvironment(params.environmentId);
|
||||
const environmentTags = await getTagsByEnvironmentId(params.environmentId);
|
||||
const product = await getProductByEnvironmentId(params.environmentId);
|
||||
const [environment, environmentTags, product] = await Promise.all([
|
||||
getEnvironment(params.environmentId),
|
||||
getTagsByEnvironmentId(params.environmentId),
|
||||
getProductByEnvironmentId(params.environmentId),
|
||||
]);
|
||||
|
||||
if (!product) {
|
||||
throw new Error("Product not found");
|
||||
}
|
||||
if (!environment) {
|
||||
throw new Error("Environment not found");
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<main className="mx-auto px-4 sm:px-6 lg:px-8">
|
||||
|
||||
@@ -24,9 +24,10 @@ type ThemeStylingProps = {
|
||||
product: TProduct;
|
||||
environmentId: string;
|
||||
colors: string[];
|
||||
isUnsplashConfigured: boolean;
|
||||
};
|
||||
|
||||
export const ThemeStyling = ({ product, environmentId, colors }: ThemeStylingProps) => {
|
||||
export const ThemeStyling = ({ product, environmentId, colors, isUnsplashConfigured }: ThemeStylingProps) => {
|
||||
const router = useRouter();
|
||||
const [localProduct, setLocalProduct] = useState(product);
|
||||
const [previewSurveyType, setPreviewSurveyType] = useState<TSurveyType>("link");
|
||||
@@ -211,6 +212,7 @@ export const ThemeStyling = ({ product, environmentId, colors }: ThemeStylingPro
|
||||
colors={colors}
|
||||
key={styling.background?.bg}
|
||||
hideCheckmark
|
||||
isUnsplashConfigured={isUnsplashConfigured}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
getRemoveLinkBrandingPermission,
|
||||
} from "@formbricks/ee/lib/service";
|
||||
import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { SURVEY_BG_COLORS } from "@formbricks/lib/constants";
|
||||
import { SURVEY_BG_COLORS, UNSPLASH_ACCESS_KEY } from "@formbricks/lib/constants";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
@@ -53,7 +53,12 @@ export default async function ProfileSettingsPage({ params }: { params: { enviro
|
||||
title="Theme"
|
||||
className="max-w-7xl"
|
||||
description="Create a style theme for all surveys. You can enable custom styling for each survey.">
|
||||
<ThemeStyling environmentId={params.environmentId} product={product} colors={SURVEY_BG_COLORS} />
|
||||
<ThemeStyling
|
||||
environmentId={params.environmentId}
|
||||
product={product}
|
||||
colors={SURVEY_BG_COLORS}
|
||||
isUnsplashConfigured={UNSPLASH_ACCESS_KEY ? true : false}
|
||||
/>
|
||||
</SettingsCard>{" "}
|
||||
<SettingsCard title="Logo" description="Upload your company logo to brand surveys and link previews.">
|
||||
<EditLogo product={product} environmentId={params.environmentId} isViewer={isViewer} />
|
||||
|
||||
@@ -95,10 +95,6 @@ const SummaryPage = ({
|
||||
updatedResponseCount = await getResponseCountAction(surveyId, filters);
|
||||
}
|
||||
setResponseCount(updatedResponseCount);
|
||||
if (updatedResponseCount === 0) {
|
||||
setSurveySummary(initialSurveySummary);
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedSurveySummary;
|
||||
if (isSharingPage) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { getServerSession } from "next-auth";
|
||||
|
||||
import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { UNSPLASH_ACCESS_KEY } from "@formbricks/lib/constants";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
import { canUserAccessProduct } from "@formbricks/lib/product/auth";
|
||||
import { getProduct } from "@formbricks/lib/product/service";
|
||||
@@ -199,3 +200,61 @@ export const resetBasicSegmentFiltersAction = async (surveyId: string) => {
|
||||
|
||||
return await resetSegmentInSurvey(surveyId);
|
||||
};
|
||||
|
||||
export async function getImagesFromUnsplashAction(searchQuery: string, page: number = 1) {
|
||||
const baseUrl = "https://api.unsplash.com/search/photos";
|
||||
const params = new URLSearchParams({
|
||||
query: searchQuery,
|
||||
client_id: UNSPLASH_ACCESS_KEY,
|
||||
orientation: "landscape",
|
||||
per_page: "9",
|
||||
page: page.toString(),
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(`${baseUrl}?${params}`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || "Failed to fetch images from Unsplash");
|
||||
}
|
||||
|
||||
const { results } = await response.json();
|
||||
return results.map((result) => {
|
||||
const authorName = encodeURIComponent(result.user.first_name + " " + result.user.last_name);
|
||||
const authorLink = encodeURIComponent(result.user.links.html);
|
||||
|
||||
return {
|
||||
id: result.id,
|
||||
alt_description: result.alt_description,
|
||||
urls: {
|
||||
regularWithAttribution: `${result.urls.regular}&dpr=2&authorLink=${authorLink}&authorName=${authorName}&utm_source=formbricks&utm_medium=referral`,
|
||||
download: result.links.download_location,
|
||||
},
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error("Error getting images from Unsplash");
|
||||
}
|
||||
}
|
||||
|
||||
export async function triggerDownloadUnsplashImageAction(downloadUrl: string) {
|
||||
try {
|
||||
const response = await fetch(`${downloadUrl}/?client_id=${UNSPLASH_ACCESS_KEY}`, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || "Failed to download image from Unsplash");
|
||||
}
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
throw new Error("Error downloading image from Unsplash");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ interface BackgroundStylingCardProps {
|
||||
hideCheckmark?: boolean;
|
||||
disabled?: boolean;
|
||||
environmentId: string;
|
||||
isUnsplashConfigured: boolean;
|
||||
}
|
||||
|
||||
export default function BackgroundStylingCard({
|
||||
@@ -31,6 +32,7 @@ export default function BackgroundStylingCard({
|
||||
hideCheckmark,
|
||||
disabled,
|
||||
environmentId,
|
||||
isUnsplashConfigured,
|
||||
}: BackgroundStylingCardProps) {
|
||||
const { bgType, brightness } = styling?.background ?? {};
|
||||
|
||||
@@ -113,6 +115,7 @@ export default function BackgroundStylingCard({
|
||||
colors={colors}
|
||||
bgType={bgType}
|
||||
environmentId={environmentId}
|
||||
isUnsplashConfigured={isUnsplashConfigured}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ export default function EditThankYouCard({
|
||||
<form>
|
||||
<QuestionFormInput
|
||||
id="headline"
|
||||
label="Headline"
|
||||
value={localSurvey?.thankYouCard?.headline}
|
||||
localSurvey={localSurvey}
|
||||
questionIdx={localSurvey.questions.length}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { FileInput } from "@formbricks/ui/FileInput";
|
||||
|
||||
interface ImageSurveyBgProps {
|
||||
interface UploadImageSurveyBgProps {
|
||||
environmentId: string;
|
||||
handleBgChange: (url: string, bgType: string) => void;
|
||||
background: string;
|
||||
}
|
||||
|
||||
export const ImageSurveyBg = ({ environmentId, handleBgChange, background }: ImageSurveyBgProps) => {
|
||||
export const UploadImageSurveyBg = ({
|
||||
environmentId,
|
||||
handleBgChange,
|
||||
background,
|
||||
}: UploadImageSurveyBgProps) => {
|
||||
return (
|
||||
<div className="mt-2 w-full">
|
||||
<div className="flex w-full items-center justify-center">
|
||||
@@ -16,9 +20,9 @@ export const ImageSurveyBg = ({ environmentId, handleBgChange, background }: Ima
|
||||
environmentId={environmentId}
|
||||
onFileUpload={(url: string[]) => {
|
||||
if (url.length > 0) {
|
||||
handleBgChange(url[0], "image");
|
||||
handleBgChange(url[0], "upload");
|
||||
} else {
|
||||
handleBgChange("", "image");
|
||||
handleBgChange("", "upload");
|
||||
}
|
||||
}}
|
||||
fileUrl={background}
|
||||
|
||||
@@ -23,6 +23,7 @@ type StylingViewProps = {
|
||||
setStyling: React.Dispatch<React.SetStateAction<TSurveyStyling | null>>;
|
||||
localStylingChanges: TSurveyStyling | null;
|
||||
setLocalStylingChanges: React.Dispatch<React.SetStateAction<TSurveyStyling | null>>;
|
||||
isUnsplashConfigured: boolean;
|
||||
};
|
||||
|
||||
export const StylingView = ({
|
||||
@@ -35,6 +36,7 @@ export const StylingView = ({
|
||||
styling,
|
||||
localStylingChanges,
|
||||
setLocalStylingChanges,
|
||||
isUnsplashConfigured,
|
||||
}: StylingViewProps) => {
|
||||
const [overwriteThemeStyling, setOverwriteThemeStyling] = useState(
|
||||
localSurvey?.styling?.overwriteThemeStyling ?? false
|
||||
@@ -162,6 +164,7 @@ export const StylingView = ({
|
||||
environmentId={environment.id}
|
||||
colors={colors}
|
||||
disabled={!overwriteThemeStyling}
|
||||
isUnsplashConfigured={isUnsplashConfigured}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import { TabBar } from "@formbricks/ui/TabBar";
|
||||
|
||||
import { AnimatedSurveyBg } from "./AnimatedSurveyBg";
|
||||
import { ColorSurveyBg } from "./ColorSurveyBg";
|
||||
import { ImageSurveyBg } from "./ImageSurveyBg";
|
||||
import { UploadImageSurveyBg } from "./ImageSurveyBg";
|
||||
import { ImageFromUnsplashSurveyBg } from "./UnsplashImages";
|
||||
|
||||
interface SurveyBgSelectorTabProps {
|
||||
handleBgChange: (bg: string, bgType: string) => void;
|
||||
@@ -14,11 +15,13 @@ interface SurveyBgSelectorTabProps {
|
||||
bgType: string | null | undefined;
|
||||
environmentId: string;
|
||||
styling: TSurveyStyling | TProductStyling | null;
|
||||
isUnsplashConfigured: boolean;
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{ id: "color", label: "Color" },
|
||||
{ id: "animation", label: "Animation" },
|
||||
{ id: "upload", label: "Upload" },
|
||||
{ id: "image", label: "Image" },
|
||||
];
|
||||
|
||||
@@ -28,52 +31,59 @@ export default function SurveyBgSelectorTab({
|
||||
colors,
|
||||
bgType,
|
||||
environmentId,
|
||||
isUnsplashConfigured,
|
||||
}: SurveyBgSelectorTabProps) {
|
||||
const [activeTab, setActiveTab] = useState(bgType || "color");
|
||||
const { background } = styling ?? {};
|
||||
const bgUrl = styling?.background?.bg || "";
|
||||
|
||||
const [colorBackground, setColorBackground] = useState(background?.bg);
|
||||
const [animationBackground, setAnimationBackground] = useState(background?.bg);
|
||||
const [imageBackground, setImageBackground] = useState(background?.bg);
|
||||
const [colorBackground, setColorBackground] = useState(bgUrl);
|
||||
const [animationBackground, setAnimationBackground] = useState(bgUrl);
|
||||
const [uploadBackground, setUploadBackground] = useState(bgUrl);
|
||||
|
||||
useEffect(() => {
|
||||
const bgType = background?.bgType;
|
||||
|
||||
if (bgType === "color") {
|
||||
setColorBackground(background?.bg);
|
||||
setColorBackground(bgUrl);
|
||||
setAnimationBackground("");
|
||||
setImageBackground("");
|
||||
setUploadBackground("");
|
||||
}
|
||||
|
||||
if (bgType === "animation") {
|
||||
setAnimationBackground(background?.bg);
|
||||
setAnimationBackground(bgUrl);
|
||||
setColorBackground("");
|
||||
setImageBackground("");
|
||||
setUploadBackground("");
|
||||
}
|
||||
|
||||
if (bgType === "image") {
|
||||
setImageBackground(background?.bg);
|
||||
if (isUnsplashConfigured && bgType === "image") {
|
||||
setColorBackground("");
|
||||
setAnimationBackground("");
|
||||
setUploadBackground("");
|
||||
}
|
||||
|
||||
if (bgType === "upload") {
|
||||
setUploadBackground(bgUrl);
|
||||
setColorBackground("");
|
||||
setAnimationBackground("");
|
||||
}
|
||||
}, [background?.bg, background?.bgType]);
|
||||
}, [bgUrl, bgType, isUnsplashConfigured]);
|
||||
|
||||
const renderContent = () => {
|
||||
switch (activeTab) {
|
||||
case "color":
|
||||
return (
|
||||
<ColorSurveyBg handleBgChange={handleBgChange} colors={colors} background={colorBackground ?? ""} />
|
||||
);
|
||||
return <ColorSurveyBg handleBgChange={handleBgChange} colors={colors} background={colorBackground} />;
|
||||
case "animation":
|
||||
return <AnimatedSurveyBg handleBgChange={handleBgChange} background={animationBackground ?? ""} />;
|
||||
case "image":
|
||||
return <AnimatedSurveyBg handleBgChange={handleBgChange} background={animationBackground} />;
|
||||
case "upload":
|
||||
return (
|
||||
<ImageSurveyBg
|
||||
<UploadImageSurveyBg
|
||||
environmentId={environmentId}
|
||||
handleBgChange={handleBgChange}
|
||||
background={imageBackground ?? ""}
|
||||
background={uploadBackground}
|
||||
/>
|
||||
);
|
||||
case "image":
|
||||
if (isUnsplashConfigured) {
|
||||
return <ImageFromUnsplashSurveyBg handleBgChange={handleBgChange} />;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -82,7 +92,7 @@ export default function SurveyBgSelectorTab({
|
||||
return (
|
||||
<div className="mt-4 flex flex-col items-center justify-center rounded-lg ">
|
||||
<TabBar
|
||||
tabs={tabs}
|
||||
tabs={tabs.filter((tab) => tab.id !== "image" || isUnsplashConfigured)}
|
||||
activeId={activeTab}
|
||||
setActiveId={setActiveTab}
|
||||
tabStyle="button"
|
||||
|
||||
@@ -34,6 +34,7 @@ interface SurveyEditorProps {
|
||||
isUserTargetingAllowed?: boolean;
|
||||
isMultiLanguageAllowed?: boolean;
|
||||
isFormbricksCloud: boolean;
|
||||
isUnsplashConfigured: boolean;
|
||||
}
|
||||
|
||||
export default function SurveyEditor({
|
||||
@@ -49,6 +50,7 @@ export default function SurveyEditor({
|
||||
isMultiLanguageAllowed,
|
||||
isUserTargetingAllowed = false,
|
||||
isFormbricksCloud,
|
||||
isUnsplashConfigured,
|
||||
}: SurveyEditorProps): JSX.Element {
|
||||
const [activeView, setActiveView] = useState<TSurveyEditorTabs>("questions");
|
||||
const [activeQuestionId, setActiveQuestionId] = useState<string | null>(null);
|
||||
@@ -174,6 +176,7 @@ export default function SurveyEditor({
|
||||
setStyling={setStyling}
|
||||
localStylingChanges={localStylingChanges}
|
||||
setLocalStylingChanges={setLocalStylingChanges}
|
||||
isUnsplashConfigured={isUnsplashConfigured}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
"use client";
|
||||
|
||||
import { debounce } from "lodash";
|
||||
import { SearchIcon } from "lucide-react";
|
||||
import UnsplashImage from "next/image";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { TSurveyBackgroundBgType } from "@formbricks/types/surveys";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { Input } from "@formbricks/ui/Input";
|
||||
import LoadingSpinner from "@formbricks/ui/LoadingSpinner";
|
||||
|
||||
import { getImagesFromUnsplashAction, triggerDownloadUnsplashImageAction } from "../actions";
|
||||
|
||||
interface ImageFromUnsplashSurveyBgProps {
|
||||
handleBgChange: (url: string, bgType: TSurveyBackgroundBgType) => void;
|
||||
}
|
||||
|
||||
interface UnsplashImage {
|
||||
id: string;
|
||||
alt_description: string;
|
||||
urls: {
|
||||
regularWithAttribution: string;
|
||||
download?: string;
|
||||
};
|
||||
authorName?: string;
|
||||
}
|
||||
|
||||
const defaultImages = [
|
||||
{
|
||||
id: "dog-1",
|
||||
alt_description: "Dog",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/dogs.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "pencil",
|
||||
alt_description: "Pencil",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/pencil.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "plant",
|
||||
alt_description: "Plant",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/plant.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "dog-2",
|
||||
alt_description: "Another Dog",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/dog-2.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "kitten-2",
|
||||
alt_description: "Another Kitten",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/kitten-2.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "lollipop",
|
||||
alt_description: "Lollipop",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/lolipop.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "oranges",
|
||||
alt_description: "Oranges",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/oranges.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "flower",
|
||||
alt_description: "Flower",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/flowers.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "supermario",
|
||||
alt_description: "Super Mario",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/supermario.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "shapes",
|
||||
alt_description: "Shapes",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/shapes.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "waves",
|
||||
alt_description: "Waves",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/waves.webp",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "kitten-1",
|
||||
alt_description: "Kitten",
|
||||
urls: {
|
||||
regularWithAttribution: "/image-backgrounds/kittens.webp",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const ImageFromUnsplashSurveyBg = ({ handleBgChange }: ImageFromUnsplashSurveyBgProps) => {
|
||||
const inputFocus = useRef<HTMLInputElement>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [query, setQuery] = useState("");
|
||||
const [images, setImages] = useState<UnsplashImage[]>(defaultImages);
|
||||
const [page, setPage] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async (searchQuery: string, currentPage: number) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const imagesFromUnsplash = await getImagesFromUnsplashAction(searchQuery, currentPage);
|
||||
for (let i = 0; i < imagesFromUnsplash.length; i++) {
|
||||
const authorName = new URL(imagesFromUnsplash[i].urls.regularWithAttribution).searchParams.get(
|
||||
"authorName"
|
||||
);
|
||||
imagesFromUnsplash[i].authorName = authorName;
|
||||
}
|
||||
setImages((prevImages) => [...prevImages, ...imagesFromUnsplash]);
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedFetchData = debounce((q) => fetchData(q, page), 500);
|
||||
|
||||
if (query.trim() !== "") {
|
||||
debouncedFetchData(query);
|
||||
}
|
||||
|
||||
return () => {
|
||||
debouncedFetchData.cancel();
|
||||
};
|
||||
}, [query, page, setImages]);
|
||||
|
||||
useEffect(() => {
|
||||
inputFocus.current?.focus();
|
||||
}, []);
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setQuery(event.target.value);
|
||||
setPage(1);
|
||||
setImages([]);
|
||||
};
|
||||
|
||||
const handleImageSelected = async (imageUrl: string, downloadImageUrl?: string) => {
|
||||
try {
|
||||
handleBgChange(imageUrl, "image");
|
||||
if (downloadImageUrl) {
|
||||
await triggerDownloadUnsplashImageAction(downloadImageUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoadMore = () => {
|
||||
setPage((prevPage) => prevPage + 1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative mt-2 w-full">
|
||||
<div className="relative">
|
||||
<SearchIcon className="absolute left-2 top-1/2 h-6 w-4 -translate-y-1/2 text-slate-500" />
|
||||
<Input
|
||||
value={query}
|
||||
onChange={handleChange}
|
||||
placeholder="Try 'lollipop' or 'mountain'..."
|
||||
className="pl-8"
|
||||
ref={inputFocus}
|
||||
aria-label="Search for images"
|
||||
/>
|
||||
</div>
|
||||
<div className="relative mt-4 grid grid-cols-3 gap-1">
|
||||
{images.length > 0 &&
|
||||
images.map((image) => (
|
||||
<div key={image.id} className="group relative">
|
||||
<UnsplashImage
|
||||
width={300}
|
||||
height={200}
|
||||
src={image.urls.regularWithAttribution}
|
||||
alt={image.alt_description}
|
||||
onClick={() => handleImageSelected(image.urls.regularWithAttribution, image.urls.download)}
|
||||
className="h-full cursor-pointer rounded-lg object-cover"
|
||||
/>
|
||||
{image.authorName && (
|
||||
<span className="absolute bottom-1 right-1 hidden rounded bg-black bg-opacity-75 px-2 py-1 text-xs text-white group-hover:block">
|
||||
{image.authorName}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{isLoading && (
|
||||
<div className="col-span-3 flex items-center justify-center p-3">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
)}
|
||||
{images.length > 0 && !isLoading && query.trim() !== "" && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
className="col-span-3 mt-3 flex items-center justify-center"
|
||||
type="button"
|
||||
onClick={handleLoadMore}>
|
||||
Load More
|
||||
</Button>
|
||||
)}
|
||||
{!isLoading && images.length === 0 && query.trim() !== "" && (
|
||||
<div className="col-span-3 flex items-center justify-center text-sm text-slate-500">
|
||||
No images found for '{query}'
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -4,7 +4,7 @@ import { getAdvancedTargetingPermission, getMultiLanguagePermission } from "@for
|
||||
import { getActionClasses } from "@formbricks/lib/actionClass/service";
|
||||
import { getAttributeClasses } from "@formbricks/lib/attributeClass/service";
|
||||
import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { IS_FORMBRICKS_CLOUD, SURVEY_BG_COLORS } from "@formbricks/lib/constants";
|
||||
import { IS_FORMBRICKS_CLOUD, SURVEY_BG_COLORS, UNSPLASH_ACCESS_KEY } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
@@ -87,6 +87,7 @@ export default async function SurveysEditPage({ params }) {
|
||||
isUserTargetingAllowed={isUserTargetingAllowed}
|
||||
isMultiLanguageAllowed={isMultiLanguageAllowed}
|
||||
isFormbricksCloud={IS_FORMBRICKS_CLOUD}
|
||||
isUnsplashConfigured={UNSPLASH_ACCESS_KEY ? true : false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -260,13 +260,13 @@ export const PreviewSurvey = ({
|
||||
/>
|
||||
</Modal>
|
||||
) : (
|
||||
<div className="w-full px-3">
|
||||
<div className="h-full w-full">
|
||||
<div className="absolute left-5 top-5">
|
||||
{!styling.isLogoHidden && product.logo?.url && (
|
||||
<ClientLogo environmentId={environment.id} product={product} previewSurvey />
|
||||
)}
|
||||
</div>
|
||||
<div className="no-scrollbar z-10 w-full max-w-md overflow-y-auto rounded-lg border border-transparent">
|
||||
<div className="flex h-full items-end">
|
||||
<SurveyInline
|
||||
survey={survey}
|
||||
isBrandingEnabled={product.linkSurveyBranding}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { responses } from "@/app/lib/api/response";
|
||||
import { transformErrorToDetails } from "@/app/lib/api/validator";
|
||||
|
||||
import { createAction } from "@formbricks/lib/action/service";
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { ZActionInput } from "@formbricks/types/actions";
|
||||
|
||||
interface Context {
|
||||
@@ -32,6 +34,16 @@ export async function POST(req: Request, context: Context): Promise<Response> {
|
||||
);
|
||||
}
|
||||
|
||||
// Formbricks Cloud: Make sure environment is part of a paid plan
|
||||
if (IS_FORMBRICKS_CLOUD) {
|
||||
const team = await getTeamByEnvironmentId(context.params.environmentId);
|
||||
if (!team || team.billing.features.userTargeting.status !== "active") {
|
||||
// temporary return status code 200 to avoid CORS issues; will be changed to 400 in the future
|
||||
return responses.successResponse({}, true);
|
||||
//return responses.badRequestResponse("Storing actions is only possible in a paid plan", {}, true);
|
||||
}
|
||||
}
|
||||
|
||||
await createAction(inputValidation.data);
|
||||
|
||||
return responses.successResponse({}, true);
|
||||
|
||||
14
apps/web/app/api/v1/client/[environmentId]/test/route.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { NextRequest } from "next/server";
|
||||
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
|
||||
export async function GET(
|
||||
_: NextRequest,
|
||||
{ params }: { params: { environmentId: string } }
|
||||
): Promise<Response> {
|
||||
const environmentId = params.environmentId;
|
||||
console.log("calling endpoint");
|
||||
const environment = await getEnvironment(environmentId);
|
||||
|
||||
return Response.json(environment);
|
||||
}
|
||||
@@ -20,17 +20,13 @@ export async function POST(request: Request) {
|
||||
if (!EMAIL_AUTH_ENABLED || inviteToken ? INVITE_DISABLED : !SIGNUP_ENABLED) {
|
||||
return Response.json({ error: "Signup disabled" }, { status: 403 });
|
||||
}
|
||||
user = { ...user, ...{ email: user.email.toLowerCase() } };
|
||||
|
||||
let inviteId;
|
||||
|
||||
try {
|
||||
let invite;
|
||||
let isInviteValid = false;
|
||||
|
||||
// create the user
|
||||
user = await createUser(user);
|
||||
|
||||
// User is invited to team
|
||||
if (inviteToken) {
|
||||
let inviteTokenData = await verifyInviteToken(inviteToken);
|
||||
inviteId = inviteTokenData?.inviteId;
|
||||
@@ -45,7 +41,20 @@ export async function POST(request: Request) {
|
||||
if (!invite) {
|
||||
return Response.json({ error: "Invalid invite ID" }, { status: 400 });
|
||||
}
|
||||
isInviteValid = true;
|
||||
}
|
||||
|
||||
user = {
|
||||
...user,
|
||||
...{ email: user.email.toLowerCase() },
|
||||
onboardingCompleted: isInviteValid,
|
||||
};
|
||||
|
||||
// create the user
|
||||
user = await createUser(user);
|
||||
|
||||
// User is invited to team
|
||||
if (isInviteValid) {
|
||||
// assign user to existing team
|
||||
await createMembership(invite.teamId, user.id, {
|
||||
accepted: true,
|
||||
|
||||
@@ -117,6 +117,10 @@ export default function LinkSurvey({
|
||||
if (window.self === window.top) {
|
||||
setAutofocus(true);
|
||||
}
|
||||
// For safari on mobile devices, scroll is a bit off due to dynamic height of address bar, so on inital load, we scroll to the bottom
|
||||
window.scrollTo({
|
||||
top: document.body.scrollHeight,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -181,9 +185,9 @@ export default function LinkSurvey({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center">
|
||||
<div className="flex h-screen items-center justify-center" style={{ height: "90svh" }}>
|
||||
{!determineStyling().isLogoHidden && product.logo?.url && <ClientLogo product={product} />}
|
||||
<ContentWrapper className="w-11/12 p-0 md:max-w-md">
|
||||
<ContentWrapper className="w-full p-0 md:max-w-md">
|
||||
{isPreview && (
|
||||
<div className="fixed left-0 top-0 flex w-full items-center justify-between bg-slate-600 p-2 px-4 text-center text-sm text-white shadow-sm">
|
||||
<div />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
@@ -24,6 +25,7 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
|
||||
}) => {
|
||||
const animatedBackgroundRef = useRef<HTMLVideoElement>(null);
|
||||
const [backgroundLoaded, setBackgroundLoaded] = useState(false);
|
||||
const [authorDetailsForUnsplash, setAuthorDetailsForUnsplash] = useState({ authorName: "", authorURL: "" });
|
||||
|
||||
// get the background from either the survey or the product styling
|
||||
const background = useMemo(() => {
|
||||
@@ -55,7 +57,18 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
|
||||
|
||||
// Cleanup
|
||||
return () => video.removeEventListener("canplaythrough", onCanPlayThrough);
|
||||
} else if (background?.bgType === "image" && background?.bg) {
|
||||
} else if ((background?.bgType === "image" || background?.bgType === "upload") && background?.bg) {
|
||||
if (background?.bgType === "image") {
|
||||
// To not set for Default Images as they have relative URL & are not from Unsplash
|
||||
if (!background?.bg.startsWith("/")) {
|
||||
setAuthorDetailsForUnsplash({
|
||||
authorName: new URL(background?.bg!).searchParams.get("authorName") || "",
|
||||
authorURL: new URL(background?.bg!).searchParams.get("authorLink") || "",
|
||||
});
|
||||
} else {
|
||||
setAuthorDetailsForUnsplash({ authorName: "", authorURL: "" });
|
||||
}
|
||||
}
|
||||
// For images, we create a new Image object to listen for the 'load' event
|
||||
const img = new Image();
|
||||
img.onload = () => setBackgroundLoaded(true);
|
||||
@@ -98,10 +111,40 @@ export const MediaBackground: React.FC<MediaBackgroundProps> = ({
|
||||
</video>
|
||||
);
|
||||
case "image":
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`${baseClasses} ${loadedClass} bg-cover bg-center`}
|
||||
style={{ backgroundImage: `url(${background?.bg})`, filter: `${filterStyle}` }}></div>
|
||||
<div className={`absolute bottom-6 z-10 h-12 w-full lg:bottom-0`}>
|
||||
<div className="mx-auto max-w-full p-3 text-center text-xs text-slate-400 lg:text-right">
|
||||
{authorDetailsForUnsplash.authorName && (
|
||||
<div className="ml-auto w-max">
|
||||
<span>Photo by </span>
|
||||
<Link
|
||||
href={authorDetailsForUnsplash.authorURL + "?utm_source=formbricks&utm_medium=referral"}
|
||||
target="_blank"
|
||||
className="hover:underline">
|
||||
{authorDetailsForUnsplash.authorName}
|
||||
</Link>
|
||||
<span> on </span>
|
||||
<Link
|
||||
href="https://unsplash.com/?utm_source=formbricks&utm_medium=referral"
|
||||
target="_blank"
|
||||
className="hover:underline">
|
||||
Unsplash
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
case "upload":
|
||||
return (
|
||||
<div
|
||||
className={`${baseClasses} ${loadedClass} bg-cover bg-center`}
|
||||
style={{ backgroundImage: `url(${background?.bg})`, filter: `${filterStyle}` }}
|
||||
style={{ backgroundImage: `url(${survey.styling?.background?.bg})`, filter: `${filterStyle}` }}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const jiti = createJiti(fileURLToPath(import.meta.url));
|
||||
const require = createRequire(import.meta.url);
|
||||
// const require = createRequire(import.meta.url);
|
||||
|
||||
jiti("@formbricks/lib/env");
|
||||
|
||||
@@ -53,6 +53,10 @@ const nextConfig = {
|
||||
protocol: "https",
|
||||
hostname: "formbricks-cdn.s3.eu-central-1.amazonaws.com",
|
||||
},
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "images.unsplash.com",
|
||||
},
|
||||
],
|
||||
},
|
||||
async rewrites() {
|
||||
@@ -153,9 +157,9 @@ const nextConfig = {
|
||||
};
|
||||
|
||||
// set custom cache handler
|
||||
if (process.env.CUSTOM_CACHE_DISABLED !== "1") {
|
||||
/* if (process.env.CUSTOM_CACHE_DISABLED !== "1") {
|
||||
nextConfig.cacheHandler = require.resolve("./cache-handler.mjs");
|
||||
}
|
||||
} */
|
||||
|
||||
// set actions allowed origins
|
||||
if (process.env.WEBAPP_URL) {
|
||||
|
||||
@@ -25,32 +25,32 @@
|
||||
"@headlessui/react": "^1.7.19",
|
||||
"@json2csv/node": "^7.0.6",
|
||||
"@opentelemetry/auto-instrumentations-node": "^0.44.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
||||
"@opentelemetry/resources": "^1.23.0",
|
||||
"@opentelemetry/sdk-node": "^0.50.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.23.0",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "^0.51.0",
|
||||
"@opentelemetry/resources": "^1.24.0",
|
||||
"@opentelemetry/sdk-node": "^0.51.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.24.0",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@radix-ui/react-collapsible": "^1.0.3",
|
||||
"@react-email/components": "^0.0.16",
|
||||
"@sentry/nextjs": "^7.110.1",
|
||||
"@sentry/nextjs": "^7.112.2",
|
||||
"@vercel/og": "^0.6.2",
|
||||
"@vercel/speed-insights": "^1.0.10",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"encoding": "^0.1.13",
|
||||
"framer-motion": "11.1.1",
|
||||
"framer-motion": "11.1.7",
|
||||
"googleapis": "^134.0.0",
|
||||
"jiti": "^1.21.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^10.2.0",
|
||||
"lucide-react": "^0.368.0",
|
||||
"mime": "^4.0.1",
|
||||
"next": "14.2.1",
|
||||
"lucide-react": "^0.373.0",
|
||||
"mime": "^4.0.2",
|
||||
"next": "14.2.3",
|
||||
"nodemailer": "^6.9.13",
|
||||
"otplib": "^12.0.1",
|
||||
"papaparse": "^5.4.1",
|
||||
"posthog-js": "^1.125.0",
|
||||
"posthog-js": "^1.129.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "18.2.0",
|
||||
@@ -68,7 +68,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@neshca/cache-handler": "^1.2.1",
|
||||
"@neshca/cache-handler": "^1.3.1",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/lodash": "^4.17.0",
|
||||
"@types/markdown-it": "^14.0.1",
|
||||
|
||||
@@ -187,6 +187,8 @@ test.describe("Survey Create & Submit Response", async () => {
|
||||
await page.getByPlaceholder(surveys.createAndSubmit.address.placeholder).fill("This is my Address");
|
||||
await page.getByRole("button", { name: "Finish" }).click();
|
||||
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Thank You Card
|
||||
await expect(page.getByText(surveys.createAndSubmit.thankYouCard.headline)).toBeVisible();
|
||||
await expect(page.getByText(surveys.createAndSubmit.thankYouCard.description)).toBeVisible();
|
||||
|
||||
@@ -86,7 +86,7 @@ test.describe("Invite, accept and remove team member", async () => {
|
||||
await page.getByRole("link", { name: "Create account" }).click();
|
||||
|
||||
await signupUsingInviteToken(page, name, email, password);
|
||||
await finishOnboarding(page, false);
|
||||
await page.waitForURL(/\/environments\/[^/]+\/surveys/);
|
||||
});
|
||||
|
||||
test("Remove member", async ({ page }) => {
|
||||
|
||||
@@ -275,6 +275,6 @@ export const createSurvey = async (
|
||||
|
||||
// Thank You Card
|
||||
await page.getByText("Thank You CardShown").click();
|
||||
await page.getByLabel("Question").fill(params.thankYouCard.headline);
|
||||
await page.getByLabel("Headline").fill(params.thankYouCard.headline);
|
||||
await page.getByLabel("Description").fill(params.thankYouCard.description);
|
||||
};
|
||||
|
||||
BIN
apps/web/public/image-backgrounds/confetti.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
apps/web/public/image-backgrounds/dog-2.webp
Normal file
|
After Width: | Height: | Size: 196 KiB |
BIN
apps/web/public/image-backgrounds/dogs.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
apps/web/public/image-backgrounds/flowers.webp
Normal file
|
After Width: | Height: | Size: 390 KiB |
BIN
apps/web/public/image-backgrounds/kitten-2.webp
Normal file
|
After Width: | Height: | Size: 153 KiB |
BIN
apps/web/public/image-backgrounds/kittens.webp
Normal file
|
After Width: | Height: | Size: 120 KiB |
BIN
apps/web/public/image-backgrounds/lolipop.webp
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
apps/web/public/image-backgrounds/oranges.webp
Normal file
|
After Width: | Height: | Size: 298 KiB |
BIN
apps/web/public/image-backgrounds/pencil.webp
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
apps/web/public/image-backgrounds/plant.webp
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
apps/web/public/image-backgrounds/shapes.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
apps/web/public/image-backgrounds/supermario.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
apps/web/public/image-backgrounds/waves.webp
Normal file
|
After Width: | Height: | Size: 52 KiB |
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"functions": {
|
||||
"app/api/cron/**/*.ts": {
|
||||
"maxDuration": 30
|
||||
"maxDuration": 30,
|
||||
"memory": 512
|
||||
},
|
||||
"app/api/v1/client/**/*.ts": {
|
||||
"maxDuration": 10,
|
||||
"memory": 300
|
||||
"memory": 200
|
||||
},
|
||||
"app/**/*.ts": {
|
||||
"maxDuration": 10,
|
||||
|
||||
@@ -182,5 +182,6 @@ export const RATE_LIMITING_DISABLED = env.RATE_LIMITING_DISABLED === "1";
|
||||
|
||||
export const CUSTOMER_IO_SITE_ID = env.CUSTOMER_IO_SITE_ID;
|
||||
export const CUSTOMER_IO_API_KEY = env.CUSTOMER_IO_API_KEY;
|
||||
export const UNSPLASH_ACCESS_KEY = env.UNSPLASH_ACCESS_KEY;
|
||||
|
||||
export const STRIPE_API_VERSION = "2024-04-10";
|
||||
|
||||
@@ -86,6 +86,7 @@ export const env = createEnv({
|
||||
UPLOADS_DIR: z.string().min(1).optional(),
|
||||
VERCEL_URL: z.string().optional(),
|
||||
WEBAPP_URL: z.string().url().optional(),
|
||||
UNSPLASH_ACCESS_KEY: z.string().optional(),
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -185,5 +186,6 @@ export const env = createEnv({
|
||||
UPLOADS_DIR: process.env.UPLOADS_DIR,
|
||||
VERCEL_URL: process.env.VERCEL_URL,
|
||||
WEBAPP_URL: process.env.WEBAPP_URL,
|
||||
UNSPLASH_ACCESS_KEY: process.env.UNSPLASH_ACCESS_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -30,6 +30,8 @@ export const getEnvironment = async (environmentId: string): Promise<TEnvironmen
|
||||
async () => {
|
||||
validateInputs([environmentId, ZId]);
|
||||
|
||||
console.log("CALLING WITHOUT CACHE!!!");
|
||||
|
||||
try {
|
||||
const environment = await prisma.environment.findUnique({
|
||||
where: {
|
||||
|
||||
@@ -27,6 +27,7 @@ import { TSurveySummary } from "@formbricks/types/surveys";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
|
||||
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL, WEBAPP_URL } from "../constants";
|
||||
import { displayCache } from "../display/cache";
|
||||
import { deleteDisplayByResponseId, getDisplayCountBySurveyId } from "../display/service";
|
||||
import { createPerson, getPerson, getPersonByUserId, transformPrismaPerson } from "../person/service";
|
||||
import {
|
||||
@@ -632,7 +633,7 @@ export const getSurveySummary = (
|
||||
},
|
||||
[`getSurveySummary-${surveyId}-${JSON.stringify(filterCriteria)}`],
|
||||
{
|
||||
tags: [responseCache.tag.bySurveyId(surveyId)],
|
||||
tags: [responseCache.tag.bySurveyId(surveyId), displayCache.tag.bySurveyId(surveyId)],
|
||||
revalidate: SERVICES_REVALIDATION_INTERVAL,
|
||||
}
|
||||
)();
|
||||
|
||||
@@ -333,7 +333,7 @@ export const Survey = ({
|
||||
<AutoCloseWrapper survey={survey} onClose={onClose}>
|
||||
<div
|
||||
className={cn(
|
||||
"no-scrollbar rounded-custom bg-survey-bg flex h-full w-full flex-col justify-between px-6 pb-3 pt-6",
|
||||
"no-scrollbar md:rounded-custom bg-survey-bg rounded-t-custom flex h-full w-full flex-col justify-between rounded-b-none px-6 py-6 md:pb-3 md:pt-6",
|
||||
isCardBorderVisible ? "border-survey-border border" : "",
|
||||
survey.type === "link" ? "fb-survey-shadow" : ""
|
||||
)}>
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { SurveyInlineProps } from "@formbricks/types/formbricksSurveys";
|
||||
|
||||
import { Survey } from "./Survey";
|
||||
|
||||
export function SurveyInline(props: SurveyInlineProps) {
|
||||
const [isMobile, setIsMobile] = useState(window.innerWidth < 768); // Assuming 768px as a breakpoint for mobile
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
setIsMobile(window.innerWidth < 768);
|
||||
};
|
||||
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => window.removeEventListener("resize", handleResize);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div id="fbjs" className="formbricks-form h-full w-full">
|
||||
<Survey {...props} />
|
||||
{isMobile ? (
|
||||
<div className="flex h-screen w-full flex-col justify-end overflow-hidden">
|
||||
<div className="max-h-[90vh] overflow-auto">
|
||||
<Survey {...props} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Survey {...props} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Button from "@/components/buttons/SubmitButton";
|
||||
import Headline from "@/components/general/Headline";
|
||||
import { LoadingSpinner } from "@/components/general/LoadingSpinner";
|
||||
import { QuestionMedia } from "@/components/general/QuestionMedia";
|
||||
import RedirectCountDown from "@/components/general/RedirectCountdown";
|
||||
import Subheader from "@/components/general/Subheader";
|
||||
@@ -36,57 +37,64 @@ export const ThankYouCard = ({
|
||||
isResponseSendingFinished,
|
||||
isInIframe,
|
||||
}: ThankYouCardProps) => {
|
||||
const media = imageUrl || videoUrl ? <QuestionMedia imgUrl={imageUrl} videoUrl={videoUrl} /> : null;
|
||||
const checkmark = (
|
||||
<div className="text-brand flex flex-col items-center justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="h-24 w-24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span className="bg-brand mb-[10px] inline-block h-1 w-16 rounded-[100%]"></span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="text-center">
|
||||
{imageUrl || videoUrl ? (
|
||||
<QuestionMedia imgUrl={imageUrl} videoUrl={videoUrl} />
|
||||
) : (
|
||||
<div>
|
||||
<div className="text-brand flex items-center justify-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="h-24 w-24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
{isResponseSendingFinished ? (
|
||||
<>
|
||||
{media || checkmark}
|
||||
<Headline
|
||||
alignTextCenter={true}
|
||||
headline={replaceRecallInfo(getLocalizedValue(headline, languageCode))}
|
||||
questionId="thankYouCard"
|
||||
/>
|
||||
<Subheader
|
||||
subheader={replaceRecallInfo(getLocalizedValue(subheader, languageCode))}
|
||||
questionId="thankYouCard"
|
||||
/>
|
||||
<RedirectCountDown redirectUrl={redirectUrl} isRedirectDisabled={isRedirectDisabled} />
|
||||
{buttonLabel && (
|
||||
<div className="mt-6 flex w-full flex-col items-center justify-center space-y-4">
|
||||
<Button
|
||||
buttonLabel={getLocalizedValue(buttonLabel, languageCode)}
|
||||
isLastQuestion={false}
|
||||
focus={!isInIframe}
|
||||
onClick={() => {
|
||||
if (!buttonLink) return;
|
||||
window.location.replace(buttonLink);
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
<p className="text-subheading text-xs">Press Enter ↵</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="my-3">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
<span className="bg-brand mb-[10px] inline-block h-1 w-16 rounded-[100%]"></span>
|
||||
</div>
|
||||
<h1 className="text-brand">Sending responses...</h1>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Headline
|
||||
alignTextCenter={true}
|
||||
headline={replaceRecallInfo(getLocalizedValue(headline, languageCode))}
|
||||
questionId="thankYouCard"
|
||||
/>
|
||||
<Subheader
|
||||
subheader={replaceRecallInfo(getLocalizedValue(subheader, languageCode))}
|
||||
questionId="thankYouCard"
|
||||
/>
|
||||
<RedirectCountDown redirectUrl={redirectUrl} isRedirectDisabled={isRedirectDisabled} />
|
||||
{buttonLabel && isResponseSendingFinished && (
|
||||
<div className="mt-6 flex w-full flex-col items-center justify-center space-y-4">
|
||||
<Button
|
||||
buttonLabel={getLocalizedValue(buttonLabel, languageCode)}
|
||||
isLastQuestion={false}
|
||||
focus={!isInIframe}
|
||||
onClick={() => {
|
||||
if (!buttonLink) return;
|
||||
window.location.replace(buttonLink);
|
||||
}}
|
||||
/>
|
||||
<p class="text-subheading text-xs">Press Enter ↵</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ export const ZCardArrangement = z.object({
|
||||
|
||||
export const ZSurveyStylingBackground = z.object({
|
||||
bg: z.string().nullish(),
|
||||
bgType: z.enum(["animation", "color", "image"]).nullish(),
|
||||
bgType: z.enum(["animation", "color", "image", "upload"]).nullish(),
|
||||
brightness: z.number().nullish(),
|
||||
});
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ export const ZSurveyProductOverwrites = z.object({
|
||||
|
||||
export type TSurveyProductOverwrites = z.infer<typeof ZSurveyProductOverwrites>;
|
||||
|
||||
export const ZSurveyBackgroundBgType = z.enum(["animation", "color", "image"]);
|
||||
export const ZSurveyBackgroundBgType = z.enum(["animation", "color", "upload", "image"]);
|
||||
|
||||
export type TSurveyBackgroundBgType = z.infer<typeof ZSurveyBackgroundBgType>;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ interface ClientLogoProps {
|
||||
export const ClientLogo = ({ environmentId, product, previewSurvey = false }: ClientLogoProps) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(previewSurvey ? "" : "left-5 top-5 md:left-7 md:top-7", "group absolute z-0 rounded-lg")}
|
||||
className={cn(previewSurvey ? "" : "left-5 top-5 md:left-7 md:top-7", "group fixed z-0 rounded-lg")}
|
||||
style={{ backgroundColor: product.logo?.bgColor }}>
|
||||
{previewSurvey && environmentId && (
|
||||
<Link
|
||||
|
||||
@@ -40,5 +40,5 @@ export const SurveyInline = (props: Omit<SurveyInlineProps, "containerId">) => {
|
||||
loadScript();
|
||||
}, [containerId, props, renderInline]);
|
||||
|
||||
return <div id={containerId} className="h-full w-full" />;
|
||||
return <div id={containerId} className="w-full" />;
|
||||
};
|
||||
|
||||
@@ -173,7 +173,7 @@ export const SurveysList = ({
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<span className="mb-4 h-24 w-24 rounded-full bg-slate-100 p-6 text-5xl">🕵️</span>
|
||||
|
||||
<div className="text-slate-600">{isFetching ? "Fetching Surveys" : "No surveys found"}</div>
|
||||
<div className="text-slate-600">{isFetching ? "Fetching surveys..." : "No surveys found"}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
723
pnpm-lock.yaml
generated
@@ -146,7 +146,8 @@
|
||||
"UPLOADS_DIR",
|
||||
"VERCEL",
|
||||
"VERCEL_URL",
|
||||
"WEBAPP_URL"
|
||||
"WEBAPP_URL",
|
||||
"UNSPLASH_ACCESS_KEY"
|
||||
]
|
||||
},
|
||||
"build:dev": {
|
||||
|
||||