Compare commits

..

2 Commits

Author SHA1 Message Date
pandeymangg
f803022d7c Merge branch 'main' into fix/environment-route-cache 2025-02-13 12:09:02 +05:30
pandeymangg
f476693f0d fix 2025-02-11 18:18:14 +05:30
1556 changed files with 28704 additions and 56084 deletions

View File

@@ -1,56 +1,39 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
**/node_modules
# **/node_modules
.pnp
.pnp.js
.pnpm-store/
# testing
**/coverage
coverage
# next.js
**/.next/
**/out/
**/.next
**/out
**/build
# node
**/dist/
**/dist
# misc
**/.DS_Store
.DS_Store
*.pem
Zone.Identifier
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
**/.env
**/.env.local
**/.env.development.local
**/.env.test.local
**/.env.production.local
!packages/database/.env
!apps/web/.env
# build tools
# turbo
.turbo
**/*vite.config.*.timestamp-*
# environment specific
# nixos stuff
.direnv
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
.vscode
.github
**/.turbo
# project specific
packages/lib/uploads
apps/web/public/js
packages/database/migrations
branch.json
.env

View File

@@ -130,9 +130,6 @@ AZUREAD_TENANT_ID=
# OIDC_DISPLAY_NAME=
# OIDC_SIGNING_ALGORITHM=
# Configure SAML SSO
# SAML_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/formbricks-saml
# Configure this when you want to ship JS & CSS files from a complete URL instead of the current domain
# ASSET_PREFIX_URL=
@@ -189,9 +186,6 @@ UNSPLASH_ACCESS_KEY=
# The below is used for Rate Limiting (uses In-Memory LRU Cache if not provided) (You can use a service like Webdis for this)
# REDIS_HTTP_URL:
# The below is used for Rate Limiting for management API
UNKEY_ROOT_KEY=
# Disable custom cache handler if necessary (e.g. if deployed on Vercel)
# CUSTOM_CACHE_DISABLED=1

32
.github/workflows/build-docs.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Build Docs
on:
workflow_call:
permissions:
contents: read
jobs:
build:
name: Build Docs
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- name: Setup Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install dependencies
run: pnpm install --config.platform=linux --config.architecture=x64
shell: bash
- run: |
pnpm build --filter=@formbricks/docs...
shell: bash

View File

@@ -84,7 +84,7 @@ jobs:
- name: Run App
run: |
NODE_ENV=test pnpm start --filter=@formbricks/web | tee app.log 2>&1 &
NODE_ENV=test pnpm start --filter=@formbricks/web &
sleep 10 # Optional: gives some buffer for the app to start
for attempt in {1..10}; do
if [ $(curl -o /dev/null -s -w "%{http_code}" http://localhost:3000/health) -eq 200 ]; then
@@ -136,13 +136,3 @@ jobs:
name: playwright-report
path: playwright-report/
retention-days: 30
- uses: actions/upload-artifact@v4
if: failure()
with:
name: app-logs
path: app.log
- name: Output App Logs
if: failure()
run: cat app.log

View File

@@ -35,6 +35,11 @@ jobs:
uses: ./.github/workflows/build-web.yml
secrets: inherit
docs:
name: Build Docs
uses: ./.github/workflows/build-docs.yml
secrets: inherit
e2e-test:
name: Run E2E Tests
uses: ./.github/workflows/e2e.yml
@@ -42,7 +47,7 @@ jobs:
required:
name: PR Check Summary
needs: [lint, test, build, e2e-test]
needs: [lint, test, build, e2e-test, docs]
if: always()
runs-on: ubuntu-latest
permissions:

View File

@@ -1,76 +0,0 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: "17 17 * * 6"
push:
branches: ["main"]
workflow_dispatch:
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Add this permission
actions: write # Required for artifact upload
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: sarif
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif

View File

@@ -1,134 +0,0 @@
name: SonarQube Report
on:
workflow_dispatch:
push:
branches:
- main
workflow_run:
workflows: ["SonarQube Analysis"]
types:
- completed
permissions:
# Needed to download artifacts from previous workflow
actions: read
contents: read
jobs:
report:
name: SonarCloud Report
runs-on: ubuntu-latest
# Only run if the previous workflow was successful
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# Download artifacts only for workflow_run events
- name: Create temporary directory for artifacts
if: ${{ github.event_name == 'workflow_run' }}
run: mkdir -p ${{ runner.temp }}/artifacts/
- name: Download analysis package
if: ${{ github.event_name == 'workflow_run' }}
uses: actions/github-script@v7
with:
script: |
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }}
})
const matchArtifact = artifacts.data.artifacts.find((artifact) => {
return artifact.name == "sonarqube-analysis"
})
if (!matchArtifact) {
throw new Error('No SonarQube analysis artifact found')
}
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip'
})
const fs = require('fs')
const path = require('path')
const tempDir = process.env.RUNNER_TEMP
const artifactPath = path.join(tempDir, 'artifacts', 'sonarqube-analysis.zip')
fs.writeFileSync(artifactPath, Buffer.from(download.data))
# Extract the analysis package
- name: Extract analysis package
if: ${{ github.event_name == 'workflow_run' }}
run: |
unzip -q ${{ runner.temp }}/artifacts/sonarqube-analysis.zip -d ${{ runner.temp }}/artifacts/
tar -xzf ${{ runner.temp }}/artifacts/sonarqube-analysis.tar.gz -C ${{ runner.temp }}/artifacts/
# For direct push to main or manual runs, set up environment and run tests
- name: Setup Node.js 20.x
if: ${{ github.event_name != 'workflow_run' }}
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with:
node-version: 20.x
- name: Install pnpm
if: ${{ github.event_name != 'workflow_run' }}
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
- name: Install dependencies
if: ${{ github.event_name != 'workflow_run' }}
run: pnpm install --config.platform=linux --config.architecture=x64
- name: Create .env
if: ${{ github.event_name != 'workflow_run' }}
run: cp .env.example .env
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
if: ${{ github.event_name != 'workflow_run' }}
run: |
RANDOM_KEY=$(openssl rand -hex 32)
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
- name: Run tests with coverage
if: ${{ github.event_name != 'workflow_run' }}
run: |
# Verify repository integrity before running tests
git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin ${{ github.sha }}
git checkout --progress --force ${{ github.sha }}
# Verify that only expected files exist in the test directory
echo "Verifying test directory integrity..."
UNEXPECTED_FILES=$(find apps/web -type f -not -path "*/node_modules/*" -not -path "*/\.*" -not -path "*/coverage/*" | grep -v -E '\.tsx?$|\.jsx?$|package\.json|tsconfig\.json|\.html$|\.css$|\.svg$')
if [ -n "$UNEXPECTED_FILES" ]; then
echo "Warning: Unexpected files detected in apps/web directory:"
echo "$UNEXPECTED_FILES"
exit 1
fi
# Run tests in a more restricted environment
cd apps/web
NODE_OPTIONS="--max-old-space-size=4096 --max-http-header-size=8192" pnpm test:coverage
cd ../../
# The Vitest coverage config is in your vite.config.mts
# Run SonarQube scan
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL || 'https://sonarcloud.io' }}
# Optional: Get the Quality Gate status
- name: SonarQube Quality Gate check
id: sonarqube-quality-gate-check
uses: sonarsource/sonarqube-quality-gate-action@master
# Force the job to fail if Quality Gate fails
# timeout-minutes: 5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL || 'https://sonarcloud.io' }}

View File

@@ -1,18 +1,19 @@
name: SonarQube Analysis
name: SonarQube
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
merge_group:
permissions:
contents: read
jobs:
analyze:
name: Analyze
sonarqube:
name: SonarQube
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
@@ -27,7 +28,7 @@ jobs:
- name: Install dependencies
run: pnpm install --config.platform=linux --config.architecture=x64
- name: Create .env
- name: create .env
run: cp .env.example .env
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
@@ -44,48 +45,8 @@ jobs:
cd ../../
# The Vitest coverage config is in your vite.config.mts
- name: Prepare SonarQube scan info
id: sonarqube-prep
# We'll collect info about the PR without running the actual scan
run: |
echo "PR_NUMBER=${{ github.event.pull_request.number || 'main' }}" >> $GITHUB_OUTPUT
echo "PR_SOURCE=${{ github.event.pull_request.head.repo.full_name || github.repository }}" >> $GITHUB_OUTPUT
echo "PR_BRANCH=${{ github.event.pull_request.head.ref || github.ref_name }}" >> $GITHUB_OUTPUT
echo "PR_BASE=${{ github.event.pull_request.base.ref || '' }}" >> $GITHUB_OUTPUT
# Create analysis properties file
- name: Generate sonar-scanner properties
run: |
{
echo "sonar.projectKey=${{ github.repository_owner }}_formbricks"
echo "sonar.organization=${{ github.repository_owner }}-github"
# If this is a PR, add PR specific properties
if [[ ! -z "${{ steps.sonarqube-prep.outputs.PR_NUMBER }}" && "${{ steps.sonarqube-prep.outputs.PR_NUMBER }}" != "main" ]]; then
echo "sonar.pullrequest.key=${{ steps.sonarqube-prep.outputs.PR_NUMBER }}"
echo "sonar.pullrequest.branch=${{ steps.sonarqube-prep.outputs.PR_BRANCH }}"
echo "sonar.pullrequest.base=${{ steps.sonarqube-prep.outputs.PR_BASE }}"
else
echo "sonar.branch.name=${{ github.ref_name }}"
fi
} > sonar-scanner.properties
# Prepare the project for analysis and save all necessary files
- name: Package project for SonarQube analysis
run: |
# Create a tar file containing all necessary files
tar -czf sonarqube-analysis.tar.gz \
--exclude=node_modules \
--exclude=.git \
sonar-scanner.properties \
sonar-project.properties \
apps/web/coverage \
$(find . -type f -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx")
# Upload the packaged project as an artifact
- name: Upload SonarQube analysis package
uses: actions/upload-artifact@v4
with:
name: sonarqube-analysis
path: sonarqube-analysis.tar.gz
retention-days: 1
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

View File

@@ -5,7 +5,7 @@ permissions:
on:
workflow_dispatch:
pull_request_target:
pull_request:
types: [opened, synchronize, reopened]
jobs:
@@ -14,13 +14,6 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.ref }}
- name: Checkout PR
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -3,8 +3,7 @@ permissions:
contents: read
on:
pull_request_target:
types: [closed]
push:
branches:
- main
@@ -12,28 +11,10 @@ jobs:
tag-production-keys:
name: Tag Production Keys
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # This ensures we get the full git history
- name: Get source branch name
id: branch-name
run: |
RAW_BRANCH="${{ github.head_ref }}"
SOURCE_BRANCH=$(echo "$RAW_BRANCH" | sed 's/[^a-zA-Z0-9._\/-]//g')
# Safely add to environment variables using GitHub's recommended method
# This prevents environment variable injection attacks
echo "SOURCE_BRANCH<<EOF" >> $GITHUB_ENV
echo "$SOURCE_BRANCH" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "Detected source branch: $SOURCE_BRANCH"
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -45,41 +26,17 @@ jobs:
- name: Tag Production Keys
run: |
BRANCH_NAME=${GITHUB_REF##*/}
npx tolgee tag \
--api-key ${{ secrets.TOLGEE_API_KEY }} \
--filter-extracted \
--filter-tag "draft:${SOURCE_BRANCH}" \
--filter-tag "draft: ${BRANCH_NAME}" \
--tag production \
--untag "draft:${SOURCE_BRANCH}"
--verbose
--untag "draft: ${BRANCH_NAME}"
- name: Tag unused production keys as Deprecated
- name: Tag Deprecated Keys
run: |
npx tolgee tag \
--api-key ${{ secrets.TOLGEE_API_KEY }} \
--filter-not-extracted --filter-tag production \
--tag deprecated --untag production
--verbose
- name: Tag unused draft:current-branch keys as Deprecated
run: |
npx tolgee tag \
--api-key ${{ secrets.TOLGEE_API_KEY }} \
--filter-not-extracted --filter-tag "draft:${SOURCE_BRANCH}" \
--tag deprecated --untag "draft:${SOURCE_BRANCH}"
--verbose
- name: Sync with backup
run: |
npx tolgee sync \
--api-key ${{ secrets.TOLGEE_API_KEY }} \
--backup ./tolgee-backup \
--continue-on-warning \
--yes
- name: Upload backup as artifact
uses: actions/upload-artifact@v4
with:
name: tolgee-backup-${{ github.sha }}
path: ./tolgee-backup
retention-days: 90

45
.gitignore vendored
View File

@@ -1,26 +1,25 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
**/node_modules
node_modules
.pnp
.pnp.js
.pnpm-store/
# testing
**/coverage
coverage
# next.js
**/.next/
**/out/
**/build
.next/
out/
build
# node
**/dist/
dist/
# misc
**/.DS_Store
.DS_Store
*.pem
Zone.Identifier
# debug
npm-debug.log*
@@ -28,30 +27,36 @@ yarn-debug.log*
yarn-error.log*
# local env files
**/.env
**/.env.local
**/.env.development.local
**/.env.test.local
**/.env.production.local
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
!packages/database/.env
!apps/web/.env
# build tools
# turbo
.turbo
**/*vite.config.*.timestamp-*
# environment specific
# nixos stuff
.direnv
Zone.Identifier
# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
# project specific
# uploads
packages/lib/uploads
# Vite Timestamps
*vite.config.*.timestamp-*
# js compiled assets
apps/web/public/js
packages/database/migrations
branch.json
.vercel
packages/database/migrations

View File

@@ -1,2 +1 @@
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ./branch.json
prettier --write ./branch.json
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json

1
.husky/post-commit Normal file
View File

@@ -0,0 +1 @@
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json

View File

@@ -1,21 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Load environment variables from .env files
if [ -f .env ]; then
set -a
. .env
set +a
fi
pnpm lint-staged
pnpm tolgee-pull || true
echo "{\"branchName\": \"main\"}" > ../branch.json
git add branch.json packages/lib/messages/*.json
# Run tolgee-pull if branch.json exists and NEXT_PUBLIC_TOLGEE_API_KEY is not set
if [ -f branch.json ]; then
if [ -z "$NEXT_PUBLIC_TOLGEE_API_KEY" ]; then
echo "Skipping tolgee-pull: NEXT_PUBLIC_TOLGEE_API_KEY is not set"
else
pnpm run tolgee-pull
git add packages/lib/messages
fi
fi

View File

@@ -23,14 +23,6 @@
{
"language": "pt-BR",
"path": "./packages/lib/messages/pt-BR.json"
},
{
"language": "zh-Hant-TW",
"path": "./packages/lib/messages/zh-Hant-TW.json"
},
{
"language": "pt-PT",
"path": "./packages/lib/messages/pt-PT.json"
}
],
"forceMode": "OVERRIDE"

View File

@@ -6,8 +6,6 @@
"dbaeumer.vscode-eslint", // eslint plugin
"esbenp.prettier-vscode", // prettier plugin
"Prisma.prisma", // syntax|format|completion for prisma
"yzhang.markdown-all-in-one", // nicer markdown support
"vitest.explorer", // run tests directly from the code window
"sonarsource.sonarlint-vscode" // sonarqube linter for vscode
"yzhang.markdown-all-in-one" // nicer markdown support
]
}

View File

@@ -3,7 +3,7 @@ Copyright (c) 2024 Formbricks GmbH
Portions of this software are licensed as follows:
- All content that resides under the "apps/web/modules/ee" directory of this repository, if these directories exist, is licensed under the license defined in "apps/web/modules/ee/LICENSE".
- All content that resides under the "packages/js/", "packages/react-native/", "packages/android/", "packages/ios/" and "packages/api/" directories of this repository, if that directories exist, is licensed under the "MIT" license as defined in the "LICENSE" files of these packages.
- All content that resides under the "packages/js/", "packages/react-native/" and "packages/api/" directories of this repository, if that directories exist, is licensed under the "MIT" license as defined in the "LICENSE" files of these packages.
- All third party components incorporated into the Formbricks Software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.

View File

@@ -13,7 +13,7 @@
<h3 align="center">Formbricks</h3>
<p align="center">
The Open Source Qualtrics Alternative
Harvest user-insights, build irresistible experiences.
<br />
<a href="https://formbricks.com/">Website</a>
</p>

10
apps/docs/.env.example Normal file
View File

@@ -0,0 +1,10 @@
NEXT_PUBLIC_FORMBRICKS_COM_API_HOST=http://localhost:3000
NEXT_PUBLIC_FORMBRICKS_COM_ENVIRONMENT_ID=
NEXT_PUBLIC_FORMBRICKS_COM_DOCS_FEEDBACK_SURVEY_ID=
# Strapi API Key
STRAPI_API_KEY=
NEXT_PUBLIC_DOCSEARCH_APP_ID=
NEXT_PUBLIC_DOCSEARCH_API_KEY=
NEXT_PUBLIC_DOCSEARCH_INDEX_NAME=

19
apps/docs/.eslintrc.js Normal file
View File

@@ -0,0 +1,19 @@
module.exports = {
root: true,
extends: ["@formbricks/eslint-config/next.js"],
parserOptions: {
project: "tsconfig.json",
tsconfigRootDir: __dirname,
},
rules: {
"@typescript-eslint/restrict-template-expressions": "off",
"import/no-cycle": "off",
},
settings: {
"import/resolver": {
typescript: {
project: "tsconfig.json",
},
},
},
};

38
apps/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
public/sitemap*.xml
public/robots.txt

129
apps/docs/LICENSE.md Normal file
View File

@@ -0,0 +1,129 @@
# Tailwind UI License
## Personal License
Tailwind Labs Inc. grants you an on-going, non-exclusive license to use the Components and Templates.
The license grants permission to **one individual** (the Licensee) to access and use the Components and Templates.
You **can**:
- Use the Components and Templates to create unlimited End Products.
- Modify the Components and Templates to create derivative components and templates. Those components and templates are subject to this license.
- Use the Components and Templates to create unlimited End Products for unlimited Clients.
- Use the Components and Templates to create End Products where the End Product is sold to End Users.
- Use the Components and Templates to create End Products that are open source and freely available to End Users.
You **cannot**:
- Use the Components and Templates to create End Products that are designed to allow an End User to build their own End Products using the Components and Templates or derivatives of the Components and Templates.
- Re-distribute the Components and Templates or derivatives of the Components and Templates separately from an End Product, neither in code or as design assets.
- Share your access to the Components and Templates with any other individuals.
- Use the Components and Templates to produce anything that may be deemed by Tailwind Labs Inc, in their sole and absolute discretion, to be competitive or in conflict with the business of Tailwind Labs Inc.
### Example usage
Examples of usage **allowed** by the license:
- Creating a personal website by yourself.
- Creating a website or web application for a client that will be owned by that client.
- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application.
- Creating a commercial self-hosted web application that is sold to end users for a one-time fee.
- Creating a web application where the primary purpose is clearly not to simply re-distribute the components (like a conference organization app that uses the components for its UI for example) that is free and open source, where the source code is publicly available.
Examples of usage **not allowed** by the license:
- Creating a repository of your favorite Tailwind UI components or templates (or derivatives based on Tailwind UI components or templates) and publishing it publicly.
- Creating a React or Vue version of Tailwind UI and making it available either for sale or for free.
- Create a Figma or Sketch UI kit based on the Tailwind UI component designs.
- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Tailwind UI.
- Creating a theme, template, or project starter kit using the components or templates and making it available either for sale or for free.
- Creating an admin panel tool (like [Laravel Nova](https://nova.laravel.com/) or [ActiveAdmin](https://activeadmin.info/)) that is made available either for sale or for free.
In simple terms, use Tailwind UI for anything you like as long as it doesn't compete with Tailwind UI.
### Personal License Definitions
Licensee is the individual who has purchased a Personal License.
Components and Templates are the source code and design assets made available to the Licensee after purchasing a Tailwind UI license.
End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates.
End User is a user of an End Product.
Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document.
## Team License
Tailwind Labs Inc. grants you an on-going, non-exclusive license to use the Components and Templates.
The license grants permission for **up to 25 Employees and Contractors of the Licensee** to access and use the Components and Templates.
You **can**:
- Use the Components and Templates to create unlimited End Products.
- Modify the Components and Templates to create derivative components and templates. Those components and templates are subject to this license.
- Use the Components and Templates to create unlimited End Products for unlimited Clients.
- Use the Components and Templates to create End Products where the End Product is sold to End Users.
- Use the Components and Templates to create End Products that are open source and freely available to End Users.
You **cannot**:
- Use the Components or Templates to create End Products that are designed to allow an End User to build their own End Products using the Components or Templates or derivatives of the Components or Templates.
- Re-distribute the Components or Templates or derivatives of the Components or Templates separately from an End Product.
- Use the Components or Templates to create End Products that are the property of any individual or entity other than the Licensee or Clients of the Licensee.
- Use the Components or Templates to produce anything that may be deemed by Tailwind Labs Inc, in their sole and absolute discretion, to be competitive or in conflict with the business of Tailwind Labs Inc.
### Example usage
Examples of usage **allowed** by the license:
- Creating a website for your company.
- Creating a website or web application for a client that will be owned by that client.
- Creating a commercial SaaS application (like an invoicing app for example) where end users have to pay a fee to use the application.
- Creating a commercial self-hosted web application that is sold to end users for a one-time fee.
- Creating a web application where the primary purpose is clearly not to simply re-distribute the components or templates (like a conference organization app that uses the components or a template for its UI for example) that is free and open source, where the source code is publicly available.
Examples of use **not allowed** by the license:
- Creating a repository of your favorite Tailwind UI components or template (or derivatives based on Tailwind UI components or templates) and publishing it publicly.
- Creating a React or Vue version of Tailwind UI and making it available either for sale or for free.
- Creating a "website builder" project where end users can build their own websites using components or templates included with or derived from Tailwind UI.
- Creating a theme or template using the components or templates and making it available either for sale or for free.
- Creating an admin panel tool (like [Laravel Nova](https://nova.laravel.com/) or [ActiveAdmin](https://activeadmin.info/)) that is made available either for sale or for free.
- Creating any End Product that is not the sole property of either your company or a client of your company. For example your employees/contractors can't use your company Tailwind UI license to build their own websites or side projects.
### Team License Definitions
Licensee is the business entity who has purchased a Team License.
Components and Templates are the source code and design assets made available to the Licensee after purchasing a Tailwind UI license.
End Product is any artifact produced that incorporates the Components or Templates or derivatives of the Components or Templates.
End User is a user of an End Product.
Employee is a full-time or part-time employee of the Licensee.
Contractor is an individual or business entity contracted to perform services for the Licensee.
Client is an individual or entity receiving custom professional services directly from the Licensee, produced specifically for that individual or entity. Customers of software-as-a-service products are not considered clients for the purpose of this document.
## Enforcement
If you are found to be in violation of the license, access to your Tailwind UI account will be terminated, and a refund may be issued at our discretion. When license violation is blatant and malicious (such as intentionally redistributing the Components or Templates through private warez channels), no refund will be issued.
The copyright of the Components and Templates is owned by Tailwind Labs Inc. You are granted only the permissions described in this license; all other rights are reserved. Tailwind Labs Inc. reserves the right to pursue legal remedies for any unauthorized use of the Components or Templates outside the scope of this license.
## Liability
Tailwind Labs Inc.s liability to you for costs, damages, or other losses arising from your use of the Components or Templates — including third-party claims against you — is limited to a refund of your license fee. Tailwind Labs Inc. may not be held liable for any consequential damages related to your use of the Components or Templates.
This Agreement is governed by the laws of the Province of Ontario and the applicable laws of Canada. Legal proceedings related to this Agreement may only be brought in the courts of Ontario. You agree to service of process at the e-mail address on your original order.
## Questions?
Unsure which license you need, or unsure if your use case is covered by our licenses?
Email us at [support@tailwindui.com](mailto:support@tailwindui.com) with your questions.

View File

@@ -1,8 +1,15 @@
---
title: "User Management"
description: "Assign different roles to organization members to grant them specific rights like creating surveys, viewing responses, or managing organization members."
icon: "user"
---
import { MdxImage } from "@/components/mdx-image";
import AddMember from "./images/add-member.webp";
import BulkInvite from "./images/bulk-invite.webp";
import IndvInvite from "./images/individual-invite.webp";
import MenuItem from "./images/organization-settings-menu.webp";
export const metadata = {
title: "User Management",
description:
"Assign different roles to organization members to grant them specific rights like creating surveys, viewing responses, or managing organization members.",
};
# Organization Access Roles
@@ -13,8 +20,7 @@ Learn about the different organization-level and team-level roles and how they a
Permissions in Formbricks are broadly handled using organization-level roles, which apply to all teams and projects in the organization. Users on a self-hosting and Enterprise plan, have access to team-level roles, which enable more granular permissions.
<Note>
Access Roles is a feature of the **Enterprise Edition**. In the **Community Edition** and on the **Free**
and **Startup** plan in the Cloud you can invite unlimited organization members as `Owner`.
Access Roles is a feature of the **Enterprise Edition**. In the **Community Edition** and on the **Free** and **Startup** plan in the Cloud you can invite unlimited organization members as `Owner`.
</Note>
Here are the different access permissions, ranked from highest to lowest access
@@ -28,16 +34,16 @@ Here are the different access permissions, ranked from highest to lowest access
All users and their organization-level roles are listed in **Organization Settings > General**. Users can hold any of the following org-level roles:
- **Owner** have full access to the organization, its data, and settings. Org Owners can perform Team Admin actions without needing to join the team.
- **Manager** have full management access to all teams and projects. They can also manage the organization's membership. Org Managers can perform Team Admin actions without needing to join the team. They cannot change other organization settings.
- **Billing** users can manage payment and compliance details in the organization.
- **Member** can view most data in the organization and act in the projects they are members of. They cannot join projects on their own and need to be assigned.
- **Org Members** can view most data in the organization and act in the projects they are members of. They cannot join projects on their own and need to be assigned.
- **Org Managers** have full management access to all teams and projects. They can also manage the organization's membership. Org Managers can perform Team Admin actions without needing to join the team. They cannot change other organization settings.
- **Org Owners** have full access to the organization, its data, and settings. Org Owners can perform Team Admin actions without needing to join the team.
### Permissions at project level
- **Read**: Read access to all resources (except settings) in the project.
- **Read & write**: Read & write access to all resources (except settings) in the project.
- **Manage**: Read & write access to all resources including settings in the project.
- **read**: read access to all resources (except settings) in the project.
- **read & write**: read & write access to all resources (except settings) in the project.
- **manage**: read & write access to all resources including settings in the project.
### Team-level Roles
@@ -51,32 +57,31 @@ For more information on user roles & permissions, see below:
| **Organization** | | | | |
| Update organization | ✅ | ❌ | ❌ | ❌ |
| Delete organization | ✅ | ❌ | ❌ | ❌ |
| Add new member | ✅ | ✅ | ❌ | ❌ |
| Delete member | ✅ | ✅ | ❌ | ❌ |
| Update member access | ✅ | ✅ | ❌ | ❌ |
| Update billing | ✅ | ✅ | ✅ | ❌ |
| Add new Member | ✅ | ✅ | ❌ | ❌ |
| Delete Member | ✅ | ✅ | ❌ | ❌ |
| Update Member Access | ✅ | ✅ | ❌ | ❌ |
| Update Billing | ✅ | ✅ | ✅ | ❌ |
| **Project** | | | | |
| Create project | ✅ | ✅ | ❌ | ❌ |
| Update project name | ✅ | ✅ | ❌ | ✅\*\* |
| Update project recontact options | ✅ | ✅ | ❌ | ✅\*\* |
| Update look & feel | ✅ | ✅ | ❌ | ✅\*\* |
| Update survey languages | ✅ | ✅ | ❌ | ✅\*\* |
| Delete project | ✅ | ✅ | ❌ | ❌ |
| Create Project | ✅ | ✅ | ❌ | ❌ |
| Update Project Name | ✅ | ✅ | ❌ | ✅\*\* |
| Update Project Recontact Options | ✅ | ✅ | ❌ | ✅\*\* |
| Update Look & Feel | ✅ | ✅ | ❌ | ✅\*\* |
| Update Survey Languages | ✅ | ✅ | ❌ | ✅\*\* |
| Delete Project | ✅ | ✅ | ❌ | ❌ |
| **Surveys** | | | | |
| Create new survey | ✅ | ✅ | ❌ | ✅\* |
| Edit survey | ✅ | ✅ | ❌ | ✅\* |
| Delete survey | ✅ | ✅ | ❌ | ✅\* |
| Create New Survey | ✅ | ✅ | ❌ | ✅\* |
| Edit Survey | ✅ | ✅ | ❌ | ✅\* |
| Delete Survey | ✅ | ✅ | ❌ | ✅\* |
| View survey results | ✅ | ✅ | ❌ | ✅ |
| **Response** | | | | |
| Delete response | ✅ | ✅ | ❌ | ✅\* |
| Add tags on response | ✅ | ✅ | ❌ | ✅\* |
| Edit tags on response | ✅ | ✅ | ❌ | ✅\* |
| Download survey responses (CSV) | ✅ | ✅ | ❌ | ✅\* |
| **Actions** | | | | |
| Create action | ✅ | ✅ | ❌ | ✅\* |
| Update action | ✅ | ✅ | ❌ | ✅\* |
| Delete action | ✅ | ✅ | ❌ | ✅\* |
| **API keys** | | | | |
| Create Action | ✅ | ✅ | ❌ | ✅\* |
| Update Action | ✅ | ✅ | ❌ | ✅\* |
| Delete Action | ✅ | ✅ | ❌ | ✅\* |
| **API Keys** | | | | |
| Create API key | ✅ | ✅ | ❌ | ✅\*\* |
| Update API key | ✅ | ✅ | ❌ | ✅\*\* |
| Delete API key | ✅ | ✅ | ❌ | ✅\*\* |
@@ -84,10 +89,10 @@ For more information on user roles & permissions, see below:
| Create tags | ✅ | ✅ | ❌ | ✅\* |
| Update tags | ✅ | ✅ | ❌ | ✅\* |
| Delete tags | ✅ | ✅ | ❌ | ✅\*\* |
| **Contacts** | | | | |
| Delete contact | ✅ | ✅ | ❌ | ✅\* |
| **People** | | | | |
| Delete Person | ✅ | ✅ | ❌ | ✅\* |
| **Integrations** | | | | |
| Manage integrations | ✅ | ✅ | ❌ | ✅\* |
| Manage Integrations | ✅ | ✅ | ❌ | ✅\* |
\* - for the read & write permissions team members
@@ -101,19 +106,33 @@ There are two ways to invite organization members: One by one or in bulk.
1. Go to the `Organization Settings` page via the menu in the lower right corner:
![Where to find the Menu Item for Organization Settings](/images/xm-and-surveys/core-features/access-roles/organization-settings-menu.webp)
<MdxImage
src={MenuItem}
alt="Where to find the Menu Item for Organization Settings"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. Click on the `Add member` button:
![Add member Button Position](/images/xm-and-surveys/core-features/access-roles/add-member.webp)
<MdxImage
src={AddMember}
alt="Add member Button Position"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. In the modal, add the Name, Email and Role of the organization member you want to invite:
![Individual Invite Modal Tab](/images/xm-and-surveys/core-features/access-roles/individual-invite.webp)
<MdxImage
src={IndvInvite}
alt="Individual Invite Modal Tab"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<Note>
Access Roles is a feature of the **Enterprise Edition**. In the **Community Edition** and on the **Free**
and **Startup** plan in the Cloud you can invite unlimited organization members as `Owners`.
Access Roles is a feature of the **Enterprise Edition**. In the **Community Edition** and on the **Free** and **Startup** plan in the Cloud you can invite unlimited organization members as `Owners`.
</Note>
Formbricks sends an email to the organization member with an invitation link. The organization member can accept the invitation or create a new account by clicking on the link.
@@ -122,15 +141,30 @@ Formbricks sends an email to the organization member with an invitation link. Th
1. Go to the `Organization Settings` page via the menu in the lower right corner:
![Where to find the Menu Item for Organization Settings](/images/xm-and-surveys/core-features/access-roles/organization-settings-menu.webp)
<MdxImage
src={MenuItem}
alt="Where to find the Menu Item for Organization Settings"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. Click on the `Add member` button:
![Add member Button Position](/images/xm-and-surveys/core-features/access-roles/add-member.webp)
<MdxImage
src={AddMember}
alt="Add member Button Position"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. In the modal, switch to `Bulk Invite`. You can download an example .CSV file to fill in the Name, Email and Role of the organization members you want to invite:
![Individual Invite Modal Tab](/images/xm-and-surveys/core-features/access-roles/bulk-invite.webp)
<MdxImage
src={BulkInvite}
alt="Individual Invite Modal Tab"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
4. Upload the filled .CSV file and invite the organization members in bulk ✅

View File

@@ -0,0 +1,48 @@
import { MdxImage } from "@/components/mdx-image";
import SurveyEmbed from "@/components/survey-embed";
import AddImageOrVideoToQuestionImage from "./images/add-image-or-video-to-question-image.webp";
import AddImageOrVideoToQuestionVideo from "./images/add-image-or-video-to-question-video.webp";
import AddImageOrVideoToQuestion from "./images/add-image-or-video-to-question.webp";
# Add Image or Video to a Question
Enhance your questions by adding images or videos. This makes instructions clearer and the survey more engaging.
## How to Add Images
Click the icon on the right side of the question to add an image or video:
<MdxImage
src={AddImageOrVideoToQuestion}
alt="Overview of adding image or video to question"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
Upload an image by clicking the upload icon or dragging the file:
<MdxImage
src={AddImageOrVideoToQuestionImage}
alt="Overview of adding image to question"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## How to Add Videos
Toggle to add a video via link:
<MdxImage
src={AddImageOrVideoToQuestionVideo}
alt="Overview of adding video to question"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### Supported Video Platforms
We support YouTube, Vimeo, and Loom URLs.
<Note>
**YouTube Privacy Mode**: This option reduces tracking by converting YouTube URLs to no-cookie URLs. It only works with YouTube.
</Note>

View File

@@ -0,0 +1,169 @@
import { MdxImage } from "@/components/mdx-image";
import ActionCalculateOperators from "./images/action-calculate-operators.webp";
import ActionCalculateValue from "./images/action-calculate-value.webp";
import ActionCalculateVariables from "./images/action-calculate-variables.webp";
import ActionCalculate from "./images/action-calculate.webp";
import ActionJump from "./images/action-jump.webp";
import ActionOptions from "./images/action-options.webp";
import ActionRequire from "./images/action-require.webp";
import AddLogic from "./images/add-logic.webp";
import ConditionChaining from "./images/condition-chaining.webp";
import ConditionOperators from "./images/condition-operators.webp";
import ConditionOptions from "./images/condition-options.webp";
import ConditionValue from "./images/condition-value.webp";
import Conditions from "./images/conditions.webp";
import Editor from "./images/editor.webp";
import QuestionLogic from "./images/question-logic.webp";
export const metadata = {
title: "Conditional Logic",
description:
"Create complex survey logic with the Logic Editor. Use conditions, actions, and variables to create a personalized survey experience.",
};
# Conditional Logic
Create complex survey logic with the Logic Editor. Use conditions, actions, and variables to create a personalized survey experience.
<MdxImage src={Editor} alt="Logic Editor" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
## Terminology
- **Condition**: A rule that determines when an action should be executed.
- **Action**: A task that is executed when a condition is met.
## **Creating Logic**
1. **Add a Logic Block**: Click the `Add logic +` button to add a new logic block.
<MdxImage src={AddLogic} alt="Add Logic" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
<Note>
You can add multiple logic blocks to a survey. Logic blocks are executed in the order they are added. You can rearrange the order of logic blocks.
</Note>
2. **Add Conditions**: Add conditions to the logic block. Conditions are rules that determine when an action should be executed.
<MdxImage
src={Conditions}
alt="Add Conditions"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
Conditons can be based on:
- **Question**: The answer to a question.
- **Variable**: A variable value.
- **Hidden Field**: The value of a hidden field.
2.a **Condition Options**: Choose from a list of available conditions.
<MdxImage
src={ConditionOptions}
alt="Condition Options"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2.b **Condition Operators**: Choose an operator to compare the condition value.
<MdxImage
src={ConditionOperators}
alt="Condition Operators"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2.c **Condition Value**: Enter a value to compare the condition against.
Comparisons can be made against a fixed value or a dynamic value.
Dynamic values can be based on a question, variable, or hidden field.
<MdxImage
src={ConditionValue}
alt="Condition Value"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<Note>
- Conditions can be grouped. - Conditions can be combined using AND or OR operators. You can add multiple conditions to a logic block. Conditions are evaluated in the order they are added.
</Note>
<MdxImage
src={ConditionChaining}
alt="Condition Chaining"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. **Add Actions**: Add actions to the logic block. Actions are tasks that are executed when a condition is met.
<Note>You can add multiple actions to a logic block. Actions are executed in the order they are added.</Note>
- 3.a **Action Options**: Choose from a list of available actions.
<MdxImage
src={ActionOptions}
alt="Add Actions"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
Action is of the following types:
- **Calculate**: Perform a calculation. These variables are then available for use in other questions.
- Calculations can be performed on variables.
- Calculations can be based on fixed values or dynamic values.
<MdxImage
src={ActionCalculateVariables}
alt="Action Calculate Variables"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<MdxImage
src={ActionCalculateOperators}
alt="Action Calculate Variables"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<MdxImage
src={ActionCalculateValue}
alt="Action Calculate Variables"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<MdxImage
src={ActionCalculate}
alt="Action Calculate"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
- **Require Answer**: Make a question required. Only the optional questions can be marked as required while filling the survey.
<MdxImage
src={ActionRequire}
alt="Action Require"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
- **Jump to Question**: Skip to a specific question. The user will be redirected to the specified question based on the condition.
<MdxImage
src={ActionJump}
alt="Action Jump"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
4. **Save Logic**: Click the `Save` button to save the logic block.
# Question Logic
This logic is executed when the user answers the question. Logic can be as simple as showing a follow-up question based on the answer or as complex as calculating a score based on multiple answers.
<MdxImage
src={QuestionLogic}
alt="Question Logic"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>

View File

@@ -0,0 +1,48 @@
import { MdxImage } from "@/components/mdx-image";
import EmailCustomizationSettings from "./email-customization-card.webp";
import EmailSample from "./email-sample.webp";
import UpdatedLogo from "./updated-logo.webp";
export const metadata = {
title: "Email Customization",
description: "Customize the email that is sent to your users!",
};
# Email Customization
Email customization is a white-label feature that allows you to customize the email that is sent to your users. You can upload a logo of your company and use it in the email.
<Note>
This feature is a white-label feature. It is only available for users on paid plans or have an enterprise license.
</Note>
## How to Upload a Logo
1. Go to the Organization Settings page.
2. You will see a card called **Email Customization** under the **General** section.
<MdxImage
src={EmailCustomizationSettings}
alt="Email Customization Settings"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. Upload a logo of your company.
4. Click on the **Save** button.
<MdxImage src={UpdatedLogo} alt="Updated Logo" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
## Viewing the Logo in the Email
You can click on the **Send test email** button to get a test email with the logo.
<MdxImage src={EmailSample} alt="Email Sample" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
<Note>Only the owner and managers of the organization can modify the logo.</Note>
## Use Cases
- **White-labeling**: You can use this feature to white-label your emails to your users.
- **Branding**: You can use this feature to add your logo to your emails to increase brand recognition.

View File

@@ -0,0 +1,110 @@
import { MdxImage } from "@/components/mdx-image";
import FilledHiddenFields from "./filled-hidden-fields.webp";
import HiddenFieldResponses from "./hidden-field-responses.webp";
import HiddenFields from "./hidden-fields.webp";
import InputHiddenFields from "./input-hidden-fields.webp";
export const metadata = {
title: "Hidden Fields",
description: "Add hidden fields to your surveys to capture additional data without requiring user inputs!",
};
# Hidden Fields
Hidden fields are a powerful feature in Formbricks that allows you to add data to a submission without asking the user to type it in. This feature is especially useful when you already have information about a user that you want to use in the analysis of the survey results (e.g. `payment plan` or `email`)
## How to Add Hidden Fields
### Enable Hidden Fields
1. Edit the survey you want to add hidden fields to & switch to the Questions tab and scroll down to the bottom of the page. You will see a section called **Hidden Fields**. Make sure to enable it by toggling the switch.
<MdxImage
src={HiddenFields}
alt="Enable Hidden Fields"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### Add Hidden Field IDs
2. Now click on it to add a new hidden field ID. You can add as many hidden fields as you want.
<MdxImage
src={InputHiddenFields}
alt="Add Hidden Fields"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<MdxImage
src={FilledHiddenFields}
alt="Filled Hidden Fields"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## Set Hidden Field in Responses
### Link Surveys
Single Hidden Field:
<Col>
<CodeGroup title="Example Screen from which the User filled it">
```sh
https://formbricks.com/clin3dxja02k8l80hpwmx4bjy?screen=pricing
```
</CodeGroup>
</Col>
Multiple Hidden Fields:
<Col>
<CodeGroup title="Example Screen from which the User filled it">
```sh
https://formbricks.com/clin3dxja02k8l80hpwmx4bjy?screen=landing_page&job=Founder
```
</CodeGroup>
</Col>
### App & Website Surveys
For in-product surveys, you can set hidden fields in the response by adding them to the `formbricks.track` call:
<Col>
<CodeGroup title="Example Screen from which the User filled it">
```sh
formbricks.track("my event", {
hiddenFields: {
screen: "landing_page",
job: "Founder"
},
});
```
</CodeGroup>
</Col>
## View Hidden Fields in Responses
These hidden fields will now be visible in the responses tab just like other fields in the Summary as well as the Response Cards, and you can use them to filter and analyze your responses.
<MdxImage
src={HiddenFieldResponses}
alt="Hidden Field Responses"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## Use Cases
- **Tracking Source**: You can add a hidden field to track the source of the survey. For a detailed guide on Source Tracking, check out the [Source Tracking](/link-surveys/source-tracking) guide.
- **User Metadata**: You can add hidden fields to capture user metadata such as user ID, email, or any other user-specific information.
- **Survey Metadata**: You can add hidden fields to capture other metadata, e.g. the screen from which the survey was filled, or any other app specific information.

View File

@@ -0,0 +1,27 @@
import { MdxImage } from "@/components/mdx-image";
import StepOne from "./images/step-one.webp";
import StepThree from "./images/step-three.webp";
import StepTwo from "./images/step-two.webp";
export const metadata = {
title: "Set a Maximum Number of Submissions for Surveys",
description: "Limit the number of responses your survey can receive.",
};
# Limit by Number of Submissions
Automatically close your survey after a specific number of responses with Formbricks. This feature is perfect for limited offers, exclusive surveys, or when you need a precise sample size for statistical significance.
- **How to**: Open the Survey Editor, switch to the Settings tab. Scroll down to Response Options, Toggle the “Close survey on response limit”.
{" "}
<MdxImage
src={StepTwo}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
- **Details**: Set a specific number of responses after which the survey automatically closes.
- **Use Case**: Perfect for limited offers, exclusive surveys, or when you need a precise sample size for statistical significance.
---

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -0,0 +1,65 @@
import { MdxImage } from "@/components/mdx-image";
import { TellaVideo } from "@/components/tella-video";
import Filters from "./filters.webp";
import MetadataCard from "./metadata-card.webp";
export const metadata = {
title: "User Metadata | Formbricks",
description:
"Understand the metadata of your users when they fill a Formbricks survey (all website, app & link). We currently capture the user's source information, URL, browser, and device details, along with the country & the action that triggered.",
};
# User Metadata
Formbricks captures metadata of your users for you when they fill a survey.
This metadata is useful for understanding the context in which the user filled the survey. For example, if you are running a marketing campaign, you can understand which source is driving the most responses. Or if you are running a survey on a specific page, you can understand the user's behavior on that page.
<TellaVideo tellaVideoIdentifier="clvdr5lv300u90fjq7fmnhs0k" />
## Metadata Captured
- **Source**: The source from where the user filled the survey. This could be an App, Link, or a Webpage.
- **URL**: The URL of the page where the user filled the survey.
- **Browser**: The browser used by the user to fill the survey.
- **OS**: The operating system of the device used by the user to fill the survey.
- **Device**: The device used by the user to fill the survey.
- **Country**: The country of the user.
- **Action**: The action that triggered the survey. This is only available for App surveys.
## View Response Metadata
1. Go to the Responses tab of your survey.
2. Hover over the profile icon of the user on the response card & you should see a tooltip opening up with the metadata details.
<MdxImage
src={MetadataCard}
alt="Metadata Card on Response Tab"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## Filter Responses by Metadata
1. Go to the Responses tab of your survey.
2. Click on the Filter button.
3. Scroll down & Select the metadata field you want to filter by.
4. Select the condition & the value you want to filter by.
<MdxImage
src={Filters}
alt="Apply Filters on Metadata"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
5. Now you should see the responses filtered based on the metadata you selected. If you want to see a walkthrough, view the video above to see how you can view & filter responses by metadata.
## Export Metadata
You can export the metadata of your responses along with the response data. When you export responses, you will see the metadata fields in the exported CSV file.
---
**Cant figure it out?**: **[Get help in Github Discussions](https://github.com/formbricks/formbricks/discussions)**

View File

@@ -0,0 +1,166 @@
import { MdxImage } from "@/components/mdx-image";
import { ResponsiveVideo } from "@/components/responsive-video";
import AddLanguageInSurvey from "./add-language-in-survey.webp";
import AddLanguages from "./add-languages.webp";
import EditMultiLang from "./edit-multi-lang.webp";
import EnableMultiLang from "./enable-multi-lang.webp";
import SeeSurveyInLanguage from "./see-survey-in-language.webp";
import SurveyLanguagesFromHome from "./survey-languages-from-home.webp";
import SurveyLanguageSettings from "./survey-languague-settings.webp";
import SurveySharing from "./survey-sharing.webp";
import SurveysHome from "./surveys-home.webp";
import TranslateAsPerLanguage from "./translate-as-per-language.webp";
export const metadata = {
title: "Multi-language Surveys | Formbricks",
description:
"Create multi-language link & app surveys with Formbricks. Get feedback from your users in their preferred language without writing a single line of code.",
};
# Multi-language Surveys
Multi-Language Surveys allow you to create surveys that support multiple languages using translations. This makes it easier to reach a diverse audience without creating separate surveys for each language. This feature simplifies the creation, delivery, and analysis of surveys for a multilingual audience.
How to deliver a specific language depends on the survey type (app or link survey):
- App & Website survey: Set a `language` attribute for the user. [See the guide below for App Surveys](#app-surveys-configuration)
- Link survey: Add a `lang` parameter in the survey URL. [See the guide below for Link Surveys](#link-surveys-configuration)
<ResponsiveVideo
title="Formbricks Multi-language Surveys"
src="https://www.youtube-nocookie.com/embed/Vol5zjYIoos?si=bdP2y3uk8-xR0uSD&amp;controls=0"
/>
---
## Creating a Multi-language Survey
1. Open the **Survey Languages** page in the Formbricks settings via the top-right menu:
<MdxImage
src={SurveyLanguagesFromHome}
alt="Formbricks Home"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. Click on the **Edit languages** button, to add a new language to your survey
<MdxImage
src={SurveyLanguageSettings}
alt="Formbricks Home"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. Select the preferred language from the dropdown and assign an identifier Alias. Click the **Add language** button to add the language to your project.
<MdxImage
src={AddLanguages}
alt="Add Multiple Languages to your Project"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
You can come back to this page anytime to add more languages or remove existing ones.
4. Now, return to the dashboard to create a new survey or edit an existing one.
<MdxImage
src={SurveysHome}
alt="Add Multiple Languages to your Project"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
5. In the survey editor, scroll down to the **Multiple Languages** section at the bottom and enable the toggle next to it.
<MdxImage
src={EnableMultiLang}
alt="Enable Multi-language for a survey"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
6. Now choose a **Default Language** for your survey. This is the language that will be shown to users who have not selected a preferred language.
<Note>Changing the default language will reset all the translations you have made for the survey.</Note>
7. Now, add the languages from the dropdown that you want to support in your survey.
<MdxImage
src={AddLanguageInSurvey}
alt="Enable Multi-language for a survey"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
8. You can now see the survey in the selected language by clicking on the language dropdown in any of the questions.
<MdxImage
src={SeeSurveyInLanguage}
alt="Enable Multi-language for a survey"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
9. You can now translate all survey content, including questions, options, and button placeholders, into the selected language.
<MdxImage
src={TranslateAsPerLanguage}
alt="Enable Multi-language for a survey"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
10. Once you are done, click on the **Publish** button to save the survey.
## App Surveys Configuration
1. When you initialise the Formbricks SDK for your user, you can pass a `language` attribute with the language code. This can be either the ISO identifier or the Alias you set when creating the language. The `language` attribute makes sure that this user only sees surveys with a translation in this specific language available.
<Col>
<CodeGroup title="Configuring Formbricks SDK with Multi-language">
```js
Formbricks.init({
environmentId: "<environment-id>",
apiHost: "<api-host>",
userId: "<user_id>",
attributes: {
language: "de", // ISO identifier or Alias set when creating language
},
});
```
</CodeGroup>
</Col>
<Note>
If a user has a language assigned, a survey has multi-language activate and it is missing a translation in the language of the user, the survey will not be displayed.
</Note>
2. That's it! Now, users with the language attribute set will see the survey in their preferred language. You can start collecting responses in multiple languages and filter them by language on the summary page.
---
## Link Surveys Configuration
For link surveys, the translation delivery is dependent on the `land` URL parameter.
After publishing the survey, just copy the survey link and append the `lang` query parameter with the language alias you have set.
For example, if you have set the alias for French as `fr`, you can share the survey link as
`https://your-survey-url.com?lang=fr`
Here are two examples:
- English: https://app.Formbricks.com/s/clptfos2i1pj516pvhxqyu3bn?lang=en
- German: https://app.Formbricks.com/s/clptfos2i1pj516pvhxqyu3bn?lang=de
Without the `lang` parameter, Formbricks will show the survey in the default language you have set.
You can now start collecting responses in multiple languages!
**Cant figure it out?**: **[Get help in Github Discussions](https://github.com/formbricks/formbricks/discussions)**

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -0,0 +1,132 @@
import { MdxImage } from "@/components/mdx-image";
import StepEleven from "./images/step-eleven.webp";
import StepNine from "./images/step-nine.webp";
import StepTen from "./images/step-ten.webp";
import Doggo from "./images/doggo.webp";
import HipsterLiving from "./images/hipster-living.webp";
import Mario from "./images/mario.webp";
import WindowsXp from "./images/windows-xp.webp";
export const metadata = {
title: "Overwrite Styling Theme on Individual Surveys",
description:
"Overwrite the global styling theme for individual surveys to create unique styles for each survey.",
};
# Overwrite Styling Theme on Individual Surveys
Overwrite the global styling theme for individual surveys to create unique styles for each survey.
<Note>
To set a styling theme for all surveys, please see the [Styling Theme](/core-features/global/styling-theme) manual.
</Note>
### Overwrite Styling Theme
1. In the Survey Editor of the survey you want to style, navigate to the **Styling** tab:
<MdxImage
src={StepNine}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. Activate the **Add Custom Styles** toggle to override the default project styling:
<MdxImage
src={StepTen}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
3. Customize your survey's style as needed:
<MdxImage
src={StepEleven}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
Voila! just hit the save button to apply your changes. Your survey is now ready to impress with its unique look!
## Overwrite CSS Styles for App & Website Surveys
You can overwrite the default CSS styles for the app & website surveys by adding the following CSS to your global CSS file (eg. `globals.css`):
Make sure that you do not change the CSS variable names as they are used by Formbricks to identify the CSS variables. You can change the values to your liking. We have filled in some sample values for you to change according to your desired appearance.
<Col>
<CodeGroup title="Overwrite Formbricks CSS">
```css
/* Formbricks CSS */
--fb-brand-color: red;
--fb-brand-text-color: white;
--fb-border-color: green;
--fb-border-color-highlight: rgb(13, 13, 12);
--fb-focus-color: red;
--fb-heading-color: yellow;
--fb-subheading-color: green;
--fb-info-text-color: orange;
--fb-signature-text-color: blue;
--fb-survey-background-color: black;
--fb-accent-background-color: rgb(13, 13, 12);
--fb-accent-background-color-selected: red;
--fb-placeholder-color: white;
--fb-shadow-color: var(--fb-brand-color);
--fb-rating-fill: rgb(13, 13, 12);
--fb-rating-hover: green;
--fb-back-btn-border: blue;
--fb-submit-btn-border: transparent;
--fb-rating-selected: black;
```
</CodeGroup>
</Col>
We have an example of this in our [Demo project](https://github.com/formbricks/formbricks/blob/main/apps/demo/styles/globals.css) here.
## Funky Survey Examples
- **Super Mario:** I guess he won't let himself be limited by radio buttons and do all three things
<MdxImage
src={Mario}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
- **Hipster Living**: Does your monstera get enough water?
<MdxImage
src={HipsterLiving}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
- **Windows XP**: Hach, nostalgia. Made us wanna play Mafia.
<MdxImage
src={WindowsXp}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
- **Whosagooooodbooooy**: Things you've likely said to your dog.
<MdxImage
src={Doggo}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
---

View File

@@ -1,8 +1,16 @@
---
title: "Partial Submissions"
description: "Capture and analyze partial submissions, providing detailed insights into how participants interact with each question within your survey. This feature is crucial for identifying specific points where respondents may disengage, allowing you to understand and address drop-offs effectively."
icon: "circle-half"
---
import { MdxImage } from "@/components/mdx-image";
import StepOne from "./images/step-one.webp";
export const metadata = {
title: "Collect Partial Submissions with Formbricks",
description:
"Capture and analyze partial submissions in Formbricks to understand respondent behavior and improve survey engagement. Learn how to enable and access partial submissions data.",
};
# Partial Submissions
Formbricks enables you to capture and analyze partial submissions, providing detailed insights into how participants interact with each question within your survey. This feature is crucial for identifying specific points where respondents may disengage, allowing you to understand and address drop-offs effectively. This makes sure you do not lose any of your meaningful insights from a question in a survey.
## **Understanding Partial Submissions**
@@ -15,37 +23,34 @@ Tracking of partial submissions is automatically enabled for all Formbricks surv
### **Types of Data Captured**
1. **Display Created**: Tracks when a survey is initially opened.
2. **Questions Partially Answered**: Records after every question & the data is handled, noting which questions were last interacted with before the survey was exited.
### **Benefits of Tracking Partial Submissions**
- **Identifies Drop-Off Points**: Pinpoints specific questions where respondents are likely to stop answering, providing critical insights into potential issues within the survey.
- **Improves Survey Design**: Data from partial submissions can guide adjustments to question complexity, survey length, or formatting to enhance respondent engagement.
- **Enhances Completion Rates**: Understanding where and why respondents are dropping off allows for targeted interventions to improve overall engagement and completion rates.
## **Analyzing Partial Submission Data**
Formbricks provides detailed analytics for partial submissions, including a per-question analysis of respondent behavior.
![Partial Submissions](/images/xm-and-surveys/surveys/general-features/partial-submissions/step-one.webp)
<MdxImage
src={StepOne}
alt="Choose a link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### **Survey Summary Analytics**
The "Analyze Drop-Offs" toggle in the survey summary reveals a comprehensive table with detailed metrics, enabling a deep dive into how respondents interact with each question:
- **Summary Metrics**:
- **Impressions**: Total number of times the survey was viewed.
- **Starts**: Percentage of impressions that the survey was started.
- **Responses**: Total number of completed responses.
- **Drop-Offs**: Percentage of starts that did not lead to a complete response.
- **Time to Complete**: Average time taken by respondents to complete the survey.
### **Detailed Question Analysis**
@@ -53,9 +58,7 @@ The "Analyze Drop-Offs" toggle in the survey summary reveals a comprehensive tab
Each question is analyzed for:
- **Time to Complete**: Average time taken by respondents spent on this question.
- **Impressions**: Number of times the question was viewed.
- **Drop-Offs**: Number of times respondents left the survey at this question, with a percentage indicating the drop-off rate.
This data is invaluable for pinpointing problems with specific questions and understanding the overall flow and engagement levels within the survey.
@@ -65,7 +68,6 @@ This data is invaluable for pinpointing problems with specific questions and und
Partial submissions tracking is particularly valuable for:
- Surveys experiencing high drop-off rates, where detailed question analysis can inform necessary adjustments.
- Studies requiring in-depth engagement metrics for each survey question to optimize content and survey structure.
## **Conclusion**

View File

@@ -0,0 +1,39 @@
import { MdxImage } from "@/components/mdx-image";
import SurveyEmbed from "@/components/survey-embed";
import Address from "./images/address.webp";
#### Question Type
# Address
The Address question type allows respondents to input their address details, including multiple fields such as address lines, city, state, and country. You can configure the question by adding a title, an optional description, and toggling specific fields to be required.
<SurveyEmbed surveyUrl="https://app.formbricks.com/s/m8w91e8wi52pdao8un1f4twu" />
## Elements
<MdxImage
src={Address}
alt="Overview of Address question type"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### Question
Provide a question to describe the address information you are requesting.
### Description
Optionally, add a description to guide the user.
### Toggle Fields
You can choose to show and require any or all of the following fields in the form:
- Address Line 1
- Address Line 2
- City
- State
- Zip Code
- Country

View File

@@ -0,0 +1,32 @@
import { MdxImage } from "@/components/mdx-image";
import SurveyEmbed from "@/components/survey-embed";
import Consent from "./images/consent.webp";
#### Question Type
# Consent
The Consent card is used to obtain user agreement regarding a product, service, or policy. It features a bold statement or question as the title, followed by a brief description. At the end of the card, users can confirm their consent by checking a checkbox to indicate their agreement.
<SurveyEmbed surveyUrl="https://app.formbricks.com/s/orxp15pca6x2nfr3v8pttpwm" />
## Elements
<MdxImage
src={Consent}
alt="Overview of Consent question type"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### Title
A bold statement or question asking for user consent, displayed prominently at the top of the card.
### Description
A short explanation or additional context for the consent request, displayed below the title. The text can be formatted, and hyperlinks are allowed within the description.
### Checkbox
At the bottom of the card, users can confirm their agreement by checking the box, indicating their consent to the question or statement above. The label for the checkbox is also editable.

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