Compare commits

..

5 Commits

Author SHA1 Message Date
Matthias Nannt
f7165272f1 docs: fix js import in developer docs 2024-10-24 22:01:41 +02:00
Matthias Nannt
cd523daa73 fix build errors 2024-10-24 15:52:05 +02:00
Matthias Nannt
d646f82a4a move attributes to root level in local storage 2024-10-24 15:40:02 +02:00
Matthias Nannt
bacabca29f fix build issues 2024-10-24 15:18:08 +02:00
Matthias Nannt
646247024a chore: remove attributes from identify call 2024-10-24 15:11:38 +02:00
2107 changed files with 53849 additions and 91362 deletions

View File

@@ -1,11 +1,11 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.2.0/schema.json",
"access": "public",
"baseBranch": "main",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"ignore": ["@formbricks/demo", "@formbricks/web"],
"linked": [],
"updateInternalDependencies": "patch"
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@formbricks/demo", "@formbricks/web"]
}

16
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
ARG VARIANT=20
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT}
# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
# [Optional] Uncomment if you want to install an additional version of node using nvm
# ARG EXTRA_NODE_VERSION=10
# RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}"
# [Optional] Uncomment if you want to install more global node modules
# RUN su node -c "npm install -g <your-package-list-here>"
RUN su node -c "npm install -g pnpm"

View File

@@ -1,6 +1,29 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/javascript-node-postgres
// Update the VARIANT arg in docker-compose.yml to pick a Node.js version
{
"features": {},
"image": "mcr.microsoft.com/devcontainers/universal:2",
"postAttachCommand": "pnpm go",
"postCreateCommand": "cp .env.example .env && sed -i '/^ENCRYPTION_KEY=/c\\ENCRYPTION_KEY='$(openssl rand -hex 32) .env && sed -i '/^NEXTAUTH_SECRET=/c\\NEXTAUTH_SECRET='$(openssl rand -hex 32) .env && sed -i '/^CRON_SECRET=/c\\CRON_SECRET='$(openssl rand -hex 32) .env && pnpm install && pnpm db:migrate:dev"
"name": "Node.js & PostgreSQL",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Add the IDs of extensions you want installed when the container is created.
"extensions": ["dbaeumer.vscode-eslint"]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or with the host.
"forwardPorts": [3000, 5432, 8025],
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "cp .env.example .env && sed -i '/^ENCRYPTION_KEY=/c\\ENCRYPTION_KEY='$(openssl rand -hex 32) .env && sed -i '/^NEXTAUTH_SECRET=/c\\NEXTAUTH_SECRET='$(openssl rand -hex 32) .env && sed -i '/^CRON_SECRET=/c\\CRON_SECRET='$(openssl rand -hex 32) .env && pnpm install && pnpm db:migrate:dev",
"postAttachCommand": "pnpm dev --filter=@formbricks/web... --filter=@formbricks/demo...",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "node"
}

View File

@@ -0,0 +1,51 @@
version: "3.8"
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
# Update 'VARIANT' to pick an LTS version of Node.js: 20, 18, 16, 14.
# Append -bullseye or -buster to pin to an OS version.
# Use -bullseye variants on local arm64/Apple Silicon.
VARIANT: "20"
volumes:
- ..:/workspace:cached
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
network_mode: service:db
# Uncomment the next line to use a non-root user for all processes.
# user: node
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)
db:
image: pgvector/pgvector:pg17
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: formbricks
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)
mailhog:
image: mailhog/mailhog
network_mode: service:app
logging:
driver:
"none" # disable saving logs
# ports:
# - 8025:8025 # web ui
# 1025:1025 # smtp server
volumes:
postgres-data: null

View File

@@ -46,9 +46,6 @@ SMTP_SECURE_ENABLED=0
SMTP_USER=smtpUser
SMTP_PASSWORD=smtpPassword
# If set to 0, the server will not require SMTP_USER and SMTP_PASSWORD(default is 1)
# SMTP_AUTHENTICATED=
# If set to 0, the server will accept connections without requiring authorization from the list of supplied CAs (default is 1).
# SMTP_REJECT_UNAUTHORIZED_TLS=0
@@ -104,11 +101,6 @@ PASSWORD_RESET_DISABLED=1
PRIVACY_URL=
TERMS_URL=
IMPRINT_URL=
IMPRINT_ADDRESS=
# Configure Turnstile in signup flow
# NEXT_PUBLIC_TURNSTILE_SITE_KEY=
# TURNSTILE_SECRET_KEY=
# Configure Github Login
GITHUB_ID=
@@ -165,11 +157,11 @@ ENTERPRISE_LICENSE_KEY=
# Insert an existing organization id or generate a valid CUID for a new one at https://www.getuniqueid.com/cuid (e.g. cjld2cjxh0000qzrmn831i7rn)
# (Role Management is an Enterprise feature)
# DEFAULT_ORGANIZATION_ID=
# DEFAULT_ORGANIZATION_ROLE=owner
# DEFAULT_ORGANIZATION_ROLE=admin
# Send new users to Brevo
# BREVO_API_KEY=
# BREVO_LIST_ID=
# Send new users to customer.io
# CUSTOMER_IO_API_KEY=
# CUSTOMER_IO_SITE_ID=
# Ignore Rate Limiting across the Formbricks app
# RATE_LIMITING_DISABLED=1
@@ -193,7 +185,4 @@ UNSPLASH_ACCESS_KEY=
# AI_AZURE_RESSOURCE_NAME=
# AI_AZURE_API_KEY=
# AI_AZURE_EMBEDDINGS_DEPLOYMENT_ID=
# AI_AZURE_LLM_DEPLOYMENT_ID=
# NEXT_PUBLIC_INTERCOM_APP_ID=
# INTERCOM_SECRET_KEY=
# AI_AZURE_LLM_DEPLOYMENT_ID=

View File

@@ -1,31 +1,81 @@
name: Bug report
description: "Found a bug? Please fill out the sections below. \U0001F44D"
type: bug
title: "[BUG]"
labels: bug
assignees: []
body:
- type: textarea
id: issue-summary
attributes:
label: Issue Summary
description: A summary of the issue. This needs to be a clear detailed-rich summary.
validations:
required: true
- type: textarea
id: issue-expected-behavior
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: false
- type: textarea
id: other-information
attributes:
label: Other information (incl. screenshots, Formbricks version, steps to reproduce,...)
validations:
required: false
- type: dropdown
id: environment
attributes:
label: Your Environment
options:
- Formbricks Cloud (app.formbricks.com)
- Self-hosted Formbricks
- type: textarea
id: issue-summary
attributes:
label: Issue Summary
description: A summary of the issue. This needs to be a clear detailed-rich summary.
validations:
required: true
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to Reproduce
value: |
1. (for example) Went to ...
2. Clicked on...
3. ...
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
id: other-information
attributes:
label: Other information
validations:
required: false
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: If applicable, add screenshots to help explain your problem.
validations:
required: false
- type: checkboxes
id: environment
attributes:
label: Environment
options:
- label: Formbricks Cloud (app.formbricks.com)
- label: Self-hosted Formbricks
- type: textarea
id: desktop-version
attributes:
label: Desktop (please complete the following information)
description: |
examples:
- **OS**: [e.g. iOS]
- **Browser**: [e.g. chrome, safari]
- **Version**: [e.g. 22]
value: |
- OS:
- Node:
- npm:
render: markdown
validations:
required: true
- type: markdown
id: nodejs-version
attributes:
value: |
#### Node.JS version
[e.g. v18.15.0]
- type: markdown
id: anything-else
attributes:
value: |
#### Anything else?
- Screen recording, console logs, network requests: You can make a recording with [Loom](https://www.loom.com).
- Anything else that you think could be an issue?

View File

@@ -1,5 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Questions
url: https://github.com/formbricks/formbricks/discussions
about: Need help selfhosting or ask a general question about the project? Open a discussion
url: https://formbricks.com/discord
about: Ask a general question about the project on our Discord server

View File

@@ -1,6 +1,8 @@
name: Feature request
description: "Suggest an idea for this project \U0001F680"
type: feature
title: "[FEATURE]"
labels: enhancement
assignees: []
body:
- type: textarea
id: problem-description
@@ -16,6 +18,13 @@ body:
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternate-solution-description
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: false
- type: textarea
id: additional-context
attributes:
@@ -24,9 +33,15 @@ body:
validations:
required: false
- type: markdown
id: formbricks-info
attributes:
value: |
### Additional resources 🤓
### How we code at Formbricks 🤓
- Check out our [Contributor Docs](https://formbricks.com/docs/developer-docs/contributing/get-started)
- Anything unclear? [Ask in Github Discussions](https://github.com/formbricks/formbricks/discussions)
- Follow Best Practices lined out in our [Contributor Docs](https://formbricks.com/docs/contributing/how-we-code)
- First time: Please read our [introductory blog post](https://formbricks.com/blog/join-the-formtribe)
- All UI components are in the package `formbricks/ui`
- Run `pnpm go` to find a demo app to test in-app surveys at `localhost:3002`
- Everything is type-safe.
- We use **chatGPT** to help refactor code.
- Anything unclear? [Ask in Discord](https://formbricks.com/discord)

View File

@@ -0,0 +1,33 @@
name: oss.gg hack submission 🕹️
description: "Submit your contribution for the for the oss.gg hackathon"
title: "[🕹️]"
labels: 🕹️ oss.gg, player submission, hacktoberfest
assignees: []
body:
- type: textarea
id: contribution-name
attributes:
label: What side quest or challenge are you solving?
description: Add the name of the side quest or challenge.
validations:
required: true
- type: textarea
id: points
attributes:
label: Points
description: How many points are assigned to this contribution?
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: What's the task your performed?
validations:
- type: textarea
id: proof
attributes:
label: Provide proof that you've completed the task
description: Screenshots, loom recordings, links to the content you shared or interacted with.
validations:
required: true

View File

@@ -1,11 +0,0 @@
name: Task (internal)
description: "Template for creating a task. Used by the Formbricks Team only \U0001f4e5"
type: task
body:
- type: textarea
id: task-summary
attributes:
label: Task description
description: A clear detailed-rich description of the task.
validations:
required: true

View File

@@ -4,7 +4,7 @@
<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
Fixes #(issue)
Fixes # (issue)
<!-- Please provide a screenshots or a loom video for visual changes to speed up reviews
Loom Video: https://www.loom.com/

View File

@@ -1,10 +1,6 @@
name: Build Docs
on:
workflow_call:
permissions:
contents: read
jobs:
build:
name: Build Docs

View File

@@ -1,10 +1,6 @@
name: Build Web
on:
workflow_call:
permissions:
contents: read
jobs:
build:
name: Build Formbricks-web

View File

@@ -4,13 +4,9 @@ on:
workflow_dispatch:
# "Scheduled workflows run on the latest commit on the default or base branch."
# — https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule
schedule:
# Runs "At 00:00." (see https://crontab.guru)
- cron: "0 0 * * *"
permissions:
contents: read
# schedule:
# Runs At 00:00. (see https://crontab.guru)
# - cron: "0 0 * * *"
jobs:
cron-weeklySummary:
env:

View File

@@ -9,8 +9,6 @@ on:
- cron: "0 8 * * 1"
jobs:
cron-weeklySummary:
permissions:
contents: read
env:
APP_URL: ${{ secrets.APP_URL }}
CRON_SECRET: ${{ secrets.CRON_SECRET }}

View File

@@ -1,28 +1,9 @@
name: E2E Tests
on:
workflow_call:
secrets:
AZURE_CLIENT_ID:
required: false
AZURE_TENANT_ID:
required: false
AZURE_SUBSCRIPTION_ID:
required: false
PLAYWRIGHT_SERVICE_URL:
required: false
# Add other secrets if necessary
workflow_dispatch:
env:
TELEMETRY_DISABLED: 1
permissions:
id-token: write
contents: read
actions: read
checks: write
jobs:
build:
name: Run E2E Tests
@@ -79,8 +60,7 @@ jobs:
- name: Apply Prisma Migrations
run: |
# pnpm prisma migrate deploy
pnpm db:migrate:dev
pnpm prisma migrate deploy
- name: Run App
run: |
@@ -102,35 +82,11 @@ jobs:
- name: Install Playwright
run: pnpm exec playwright install --with-deps
- name: Set Azure Secret Variables
run: |
if [[ -n "${{ secrets.AZURE_CLIENT_ID }}" && -n "${{ secrets.AZURE_TENANT_ID }}" && -n "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]]; then
echo "AZURE_ENABLED=true" >> $GITHUB_ENV
else
echo "AZURE_ENABLED=false" >> $GITHUB_ENV
fi
- name: Azure login
if: env.AZURE_ENABLED == 'true'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Run E2E Tests (Azure)
if: env.AZURE_ENABLED == 'true'
env:
PLAYWRIGHT_SERVICE_URL: ${{ secrets.PLAYWRIGHT_SERVICE_URL }}
run: |
pnpm test-e2e:azure
- name: Run E2E Tests (Local)
if: env.AZURE_ENABLED == 'false'
- name: Run E2E Tests
run: |
pnpm test:e2e
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report

View File

@@ -1,10 +1,6 @@
name: Lint
on:
workflow_call:
permissions:
contents: read
jobs:
build:
name: Linters
@@ -12,16 +8,16 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- name: Setup Node.js 20.x
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
uses: actions/setup-node@v3
with:
node-version: 20.x
- name: Install pnpm
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
uses: pnpm/action-setup@v4
- name: Install dependencies
run: pnpm install --config.platform=linux --config.architecture=x64

View File

@@ -1,13 +1,5 @@
name: PR Update
# Update permissions to include all necessary ones
permissions:
contents: read
pull-requests: read
actions: read
checks: write
id-token: write
on:
pull_request:
branches:
@@ -20,28 +12,55 @@ concurrency:
cancel-in-progress: true
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
has-files-requiring-all-checks: ${{ steps.filter.outputs.has-files-requiring-all-checks }}
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
has-files-requiring-all-checks:
- "!(**.md|.github/CODEOWNERS)"
test:
name: Run Unit Tests
needs: [changes]
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/test.yml
secrets: inherit
lint:
name: Run Linters
needs: [changes]
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/lint.yml
secrets: inherit
build:
name: Build Formbricks-web
needs: [changes]
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/build-web.yml
secrets: inherit
docs:
name: Build Docs
needs: [changes]
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/build-docs.yml
secrets: inherit
e2e-test:
name: Run E2E Tests
needs: [changes]
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/e2e.yml
secrets: inherit
@@ -50,10 +69,6 @@ jobs:
needs: [lint, test, build, e2e-test, docs]
if: always()
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
statuses: write
steps:
- name: fail if conditional jobs failed
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped') || contains(needs.*.result, 'cancelled')

View File

@@ -1,62 +0,0 @@
name: Prepare release
run-name: Prepare release ${{ inputs.next_version }}
on:
workflow_dispatch:
inputs:
next_version:
required: true
type: string
description: "Version name"
permissions:
contents: write
pull-requests: write
jobs:
prepare_release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: ./.github/actions/dangerous-git-checkout
- name: Configure git
run: |
git config --local user.email "github-actions@github.com"
git config --local user.name "GitHub Actions"
- name: Setup Node.js 20.x
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with:
node-version: 20.x
- name: Install pnpm
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
- name: Install dependencies
run: pnpm install --config.platform=linux --config.architecture=x64
- name: Bump version
run: |
cd apps/web
pnpm version ${{ inputs.next_version }} --no-workspaces-update
- name: Commit changes and create a branch
run: |
branch_name="release-v${{ inputs.next_version }}"
git checkout -b "$branch_name"
git add .
git commit -m "chore: release v${{ inputs.next_version }}"
git push origin "$branch_name"
- name: Create pull request
run: |
gh pr create \
--base main \
--head "release-v${{ inputs.next_version }}" \
--title "chore: bump version to v${{ inputs.next_version }}" \
--body "This PR contains the changes for the v${{ inputs.next_version }} release."
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -6,11 +6,6 @@ on:
# branches:
# - main
permissions:
contents: write
pull-requests: write
packages: write
concurrency: ${{ github.workflow }}-${{ github.ref }}
env:

View File

@@ -0,0 +1,62 @@
name: Docker for Data Migrations
on:
workflow_dispatch:
push:
tags:
- "v*"
env:
REGISTRY: ghcr.io
IMAGE_NAME: formbricks/data-migrations
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/formbricks?schema=public"
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@v3.5.0
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=tag
type=raw,value=${{ github.ref_name }}
type=raw,value=latest
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
file: ./packages/database/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
DATABASE_URL=${{ env.DATABASE_URL }}
- name: Sign the published Docker image
if: ${{ github.event_name != 'pull_request' }}
run: |
cosign sign --yes ghcr.io/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
cosign sign --yes ghcr.io/${{ env.IMAGE_NAME }}:latest

View File

@@ -8,8 +8,6 @@ on:
jobs:
release-image-on-dockerhub:
name: Release on Dockerhub
permissions:
contents: read
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}

View File

@@ -1,52 +0,0 @@
name: SonarQube
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
jobs:
sonarqube:
name: SonarQube
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Setup Node.js 20.x
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with:
node-version: 20.x
- name: Install pnpm
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
- name: Install dependencies
run: pnpm install --config.platform=linux --config.architecture=x64
- name: create .env
run: cp .env.example .env
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
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
run: |
cd apps/web
pnpm test:coverage
cd ../../
# The Vitest coverage config is in your vite.config.mts
- 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

@@ -6,8 +6,6 @@ jobs:
name: Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@v3

View File

@@ -1,39 +0,0 @@
name: Check Missing Translations
permissions:
contents: read
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened]
jobs:
check-missing-translations:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install Tolgee CLI
run: npm install -g @tolgee/cli
- name: Compare Tolgee Keys
id: compare
run: |
tolgee compare --api-key ${{ secrets.TOLGEE_API_KEY }} > compare_output.txt
cat compare_output.txt
- name: Check for Missing Translations
run: |
if grep -q "new key found" compare_output.txt; then
echo "New keys found that may require translations:"
exit 1
else
echo "No new keys found."
fi

View File

@@ -1,42 +0,0 @@
name: Tolgee Tagging on PR Merge
permissions:
contents: read
on:
push:
branches:
- main
jobs:
tag-production-keys:
name: Tag Production Keys
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18 # Ensure compatibility with your project
- name: Install Tolgee CLI
run: npm install -g @tolgee/cli
- name: Tag Production Keys
run: |
BRANCH_NAME=${GITHUB_REF##*/}
npx tolgee tag \
--api-key ${{ secrets.TOLGEE_API_KEY }} \
--filter-extracted \
--filter-tag "draft: ${BRANCH_NAME}" \
--tag production \
--untag "draft: ${BRANCH_NAME}"
- name: Tag Deprecated Keys
run: |
npx tolgee tag \
--api-key ${{ secrets.TOLGEE_API_KEY }} \
--filter-not-extracted --filter-tag production \
--tag deprecated --untag production

View File

@@ -3,7 +3,7 @@ name: "Welcome new contributors"
on:
issues:
types: opened
pull_request_target:
pull_request:
types: opened
permissions:
@@ -22,6 +22,6 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |-
Thank you so much for making your first Pull Request and taking the time to improve Formbricks! 🚀🙏❤️
Feel free to join the conversation on [Github Discussions](https://github.com/formbricks/formbricks/discussions) if you need any help or have any questions. 😊
Feel free to join the conversation at [Discord](https://formbricks.com/discord)
issue-message: |
Thank you for opening your first issue! 🙏❤️ One of our team members will review it and get back to you as soon as it possible. 😊

6
.gitignore vendored
View File

@@ -35,6 +35,9 @@ yarn-error.log*
!packages/database/.env
!apps/web/.env
# Prisma generated files
packages/database/zod
# turbo
.turbo
@@ -57,6 +60,3 @@ packages/lib/uploads
# js compiled assets
apps/web/public/js
packages/database/migrations

6
.gitpod.Dockerfile vendored Normal file
View File

@@ -0,0 +1,6 @@
FROM gitpod/workspace-full
# Install custom tools, runtime, etc.
RUN brew install yq
RUN pnpm install turbo --global

74
.gitpod.yml Normal file
View File

@@ -0,0 +1,74 @@
tasks:
- name: demo
init: |
gp sync-await init-install &&
bash .gitpod/setup-demo.bash
command: |
cd apps/demo &&
cp .env.example .env &&
sed -i -r "s#^(NEXT_PUBLIC_FORMBRICKS_API_HOST=).*#\1 $(gp url 3000)#" .env &&
gp sync-await init &&
turbo --filter "@formbricks/demo" go
- name: Init Formbricks
init: |
cp .env.example .env &&
bash .gitpod/init.bash &&
turbo --filter "@formbricks/js" build &&
gp sync-done init-install
command: |
gp sync-done init &&
gp tasks list &&
gp ports await 3002 && gp ports await 3000 && gp open apps/demo/.env && gp preview $(gp url 3002) --external
- name: web
init: |
gp sync-await init-install &&
bash .gitpod/setup-web.bash &&
turbo --filter "@formbricks/database" db:down
command: |
gp sync-await init &&
cp .env.example .env &&
sed -i -r "s#^(WEBAPP_URL=).*#\1 $(gp url 3000)#" .env &&
RANDOM_ENCRYPTION_KEY=$(openssl rand -hex 32)
sed -i 's/^ENCRYPTION_KEY=.*/ENCRYPTION_KEY='"$RANDOM_ENCRYPTION_KEY"'/' .env
turbo --filter "@formbricks/web" go
image:
file: .gitpod.Dockerfile
ports:
- port: 3000
visibility: public
onOpen: open-browser
- port: 3001
visibility: public
onOpen: ignore
- port: 3002
visibility: public
onOpen: ignore
- port: 5432
visibility: public
onOpen: ignore
- port: 1025
visibility: public
onOpen: ignore
- port: 8025
visibility: public
onOpen: open-browser
github:
prebuilds:
master: true
pullRequests: true
addComment: true
vscode:
extensions:
- "ban.spellright"
- "bradlc.vscode-tailwindcss"
- "DavidAnson.vscode-markdownlint"
- "dbaeumer.vscode-eslint"
- "esbenp.prettier-vscode"
- "Prisma.prisma"
- "yzhang.markdown-all-in-one"

View File

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

View File

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

View File

@@ -1,3 +1 @@
pnpm lint-staged
pnpm tolgee-pull || true
git add packages/lib/messages/*.json
pnpm lint-staged

2
.nvmrc
View File

@@ -1 +1 @@
22.1.0
20.18.0

View File

@@ -2,10 +2,5 @@ const baseConfig = require("./packages/config-prettier/prettier-preset");
module.exports = {
...baseConfig,
plugins: [
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss",
"prettier-plugin-sort-json",
],
jsonRecursiveSort: true,
plugins: ["@trivago/prettier-plugin-sort-imports", "prettier-plugin-tailwindcss"],
};

View File

@@ -1,31 +0,0 @@
{
"$schema": "https://docs.tolgee.io/cli-schema.json",
"format": "JSON_TOLGEE",
"patterns": ["./apps/web/**/*.ts?(x)"],
"projectId": 10304,
"pull": {
"path": "./packages/lib/messages"
},
"push": {
"files": [
{
"language": "en-US",
"path": "./packages/lib/messages/en-US.json"
},
{
"language": "de-DE",
"path": "./packages/lib/messages/de-DE.json"
},
{
"language": "fr-FR",
"path": "./packages/lib/messages/fr-FR.json"
},
{
"language": "pt-BR",
"path": "./packages/lib/messages/pt-BR.json"
}
],
"forceMode": "OVERRIDE"
},
"strictNamespace": false
}

18
.vscode/launch.json vendored
View File

@@ -1,21 +1,21 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch localhost:3002",
"reAttach": true,
"request": "launch",
"type": "firefox",
"request": "launch",
"reAttach": true,
"url": "http://localhost:3002/",
"webRoot": "${workspaceFolder}"
},
{
"name": "Attach",
"request": "attach",
"type": "firefox"
"type": "firefox",
"request": "attach"
}
],
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0"
]
}

View File

@@ -1,4 +1,4 @@
{
"typescript.preferences.importModuleSpecifier": "non-relative",
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.importModuleSpecifier": "non-relative"
}

View File

@@ -18,13 +18,13 @@ Ready to dive into the code and make a real impact? Here's your path:
1. **Read our Best Practices**: [It takes 5 minutes](https://formbricks.com/docs/developer-docs/contributing/get-started) but will help you save hours 🤓
1. **Fork the Repository:** Fork our repository or use [Gitpod](https://gitpod.io) or use [Github Codespaces](https://github.com/features/codespaces) to get started instantly.
1. **Fork the Repository:** Fork our repository or use [Gitpod](https://formbricks.com/docs/developer-docs/contributing/gitpod) or use [Codespaces](https://formbricks.com/docs/developer-docs/contributing/codespaces)
1. **Tweak and Transform:** Work your coding magic and apply your changes.
1. **Pull Request Act:** If you're ready to go, craft a new pull request closely following our PR template 🙏
Would you prefer a chat before you dive into a lot of work? [Github Discussions](https://github.com/formbricks/formbricks/discussions) is your harbor. Share your thoughts, and we'll meet you there with open arms. We're responsive and friendly, promise!
Would you prefer a chat before you dive into a lot of work? Our [Discord server](https://formbricks.com/discord) is your harbor. Share your thoughts, and we'll meet you there with open arms. We're responsive and friendly, promise!
## 🚀 Aspiring Features

View File

@@ -2,7 +2,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/ee/", "apps/web/modules/ee" & "apps/web/app/(ee)" directories of this repository, if these directories exist, is licensed under the license defined in "packages/ee/LICENSE".
- 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

@@ -15,12 +15,12 @@
<p align="center">
Harvest user-insights, build irresistible experiences.
<br />
<a href="https://formbricks.com/">Website</a>
<a href="https://formbricks.com/">Website</a> | <a href="https://formbricks.com/discord">Join Discord community</a>
</p>
</p>
<p align="center">
<a href="https://github.com/formbricks/formbricks/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-AGPL-purple" alt="License"></a> <a href="https://github.com/formbricks/formbricks/stargazers"><img src="https://img.shields.io/github/stars/formbricks/formbricks?logo=github" alt="Github Stars"></a>
<a href="https://github.com/formbricks/formbricks/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-AGPL-purple" alt="License"></a> <a href="https://formbricks.com/discord"><img src="https://img.shields.io/discord/979077669410979880?label=Discord&logo=discord&logoColor=%23fff" alt="Join Formbricks Discord"></a> <a href="https://github.com/formbricks/formbricks/stargazers"><img src="https://img.shields.io/github/stars/formbricks/formbricks?logo=github" alt="Github Stars"></a>
<a href="https://news.ycombinator.com/item?id=32303986"><img src="https://img.shields.io/badge/Hacker%20News-122-%23FF6600" alt="Hacker News"></a>
<a href="[https://www.producthunt.com/products/formbricks](https://www.producthunt.com/posts/formbricks)"><img src="https://img.shields.io/badge/Product%20Hunt-455-orange?logo=producthunt&logoColor=%23fff" alt="Product Hunt"></a>
<a href="https://github.blog/2023-04-12-github-accelerator-our-first-cohort-and-whats-next/"><img src="https://img.shields.io/badge/2023-blue?logo=github&label=Github%20Accelerator" alt="Github Accelerator"></a>
@@ -228,14 +228,14 @@ The Formbricks core application is licensed under the [AGPLv3 Open Source Licens
### The Enterprise Edition
Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/apps/web/modules/ee) and [license](https://github.com/formbricks/formbricks/blob/main/apps/web/modules/ee/LICENSE) for the enterprise functionality can be found in the `/apps/web/modules/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/docs/self-hosting/enterprise) to unlock it.
Additional to the AGPL licensed Formbricks core, this repository contains code licensed under an Enterprise license. The [code](https://github.com/formbricks/formbricks/tree/main/packages/ee) and [license](https://github.com/formbricks/formbricks/blob/main/packages/ee/LICENSE) for the enterprise functionality can be found in the `/packages/ee` folder of this repository. This additional functionality is not part of the AGPLv3 licensed Formbricks core and is designed to meet the needs of larger teams and enterprises. This advanced functionality is already included in the Docker images, but you need an [Enterprise License Key](https://formbricks.com/docs/self-hosting/enterprise) to unlock it.
### White-Labeling Formbricks and Other Licensing Needs
We currently do not offer Formbricks white-labeled. That means that we don't sell a license which let's other companies resell Formbricks to third parties under their name nor take parts (like the survey editor) out of Formbricks to add to their own software products. Any other needs? [Send us an email](mailto:hola@formbricks.com).
We currently do not offer Formbricks white-labeled. Any other needs? [Send us an email](mailto:hola@formbricks.com).
### Why charge for Enterprise Features?
The Enterprise Edition allows us to fund the development of Formbricks sustainably. It guarantees that the free and open-source surveying infrastructure we're building will be around for decades to come.
The Enterprise Edition and White-Label Licenses allow us to fund the development of Formbricks sustainably. It guarantees that the open-source surveying infrastructure we're building will be around for decades to come.
<p align="right"><a href="#top">🔼 Back to top</a></p>

View File

@@ -1,7 +1,7 @@
# Security Policy of Formbricks
This is Formbrick's security policy. Please reach out to us
on Github or, if privately, via <security@formbricks.com>
on our Discord or, if privately, via <security@formbricks.com>
## Introduction
@@ -40,7 +40,7 @@ We invite you to report if:
Avoid reporting if:
- Assistance is needed to optimize Formbricks for security please engage on Github Discussions for this.
- Assistance is needed to optimize Formbricks for security please engage on our Discord for this.
- Help is required for applying security-related updates.
- The concern is not related to security.
@@ -51,13 +51,13 @@ In the interest of responsibly managing vulnerabilities, please adhere to the fo
> Do not reveal the problem to others until it has been resolved.
1. **Send a Detailed Report**:
- Raise a security report on [Github](https://github.com/formbricks/formbricks/issues/new/choose) or send an email to [security@formbricks.com](mailto:security@formbricks.com).
- Address emails to [security@formbricks.com](mailto:security@formbricks.com).
- Include:
- Problem description.
- Detailed, reproducible steps, with screenshots where possible.
- Affected version(s).
- Known possible mitigations.
- Your preferred contact method.
- Your Discord username or preferred contact method.
2. **Acknowledgement of Receipt**:
- Our security team will acknowledge receipt and provide an initial response within 48 hours.
- Following verification of the vulnerability and the fix, a release plan will be formulated, with the fix deployed between 7 to 28 days, depending on the severity and complexity.

View File

@@ -1,2 +1,2 @@
EXPO_PUBLIC_APP_URL=http://192.168.0.197:3000
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=cm5p0cs7r000819182b32j0a1
EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq

View File

@@ -1,33 +1,32 @@
{
"expo": {
"android": {
"adaptiveIcon": {
"backgroundColor": "#ffffff",
"foregroundImage": "./assets/adaptive-icon.png"
}
},
"assetBundlePatterns": ["**/*"],
"name": "react-native-demo",
"slug": "react-native-demo",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "Take pictures for certain activities.",
"NSMicrophoneUsageDescription": "Need microphone access for recording videos.",
"NSPhotoLibraryUsageDescription": "Select pictures for certain activities."
},
"supportsTablet": true
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"jsEngine": "hermes",
"name": "react-native-demo",
"newArchEnabled": true,
"orientation": "portrait",
"slug": "react-native-demo",
"splash": {
"backgroundColor": "#ffffff",
"image": "./assets/splash.png",
"resizeMode": "contain"
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true,
"infoPlist": {
"NSCameraUsageDescription": "Take pictures for certain activities.",
"NSPhotoLibraryUsageDescription": "Select pictures for certain activities.",
"NSMicrophoneUsageDescription": "Need microphone access for recording videos."
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"userInterfaceStyle": "light",
"version": "1.0.0",
"web": {
"favicon": "./assets/favicon.png"
}

View File

@@ -13,18 +13,16 @@
"dependencies": {
"@formbricks/js": "workspace:*",
"@formbricks/react-native": "workspace:*",
"@react-native-async-storage/async-storage": "2.1.0",
"expo": "52.0.28",
"expo-status-bar": "2.0.1",
"expo": "51.0.26",
"expo-status-bar": "1.12.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.6",
"react-native-webview": "13.12.5"
"react-native": "0.74.4",
"react-native-webview": "13.8.6"
},
"devDependencies": {
"@babel/core": "7.26.0",
"@types/react": "18.3.18",
"typescript": "5.7.2"
"@babel/core": "7.25.2",
"@types/react": "18.3.11",
"typescript": "5.3.3"
},
"private": true
}

View File

@@ -1,14 +1,6 @@
import { StatusBar } from "expo-status-bar";
import React, { type JSX } from "react";
import { Button, LogBox, StyleSheet, Text, View } from "react-native";
import Formbricks, {
logout,
setAttribute,
setAttributes,
setLanguage,
setUserId,
track,
} from "@formbricks/react-native";
import Formbricks, { track } from "@formbricks/react-native";
LogBox.ignoreAllLogs();
@@ -17,92 +9,35 @@ export default function App(): JSX.Element {
throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required");
}
if (!process.env.EXPO_PUBLIC_APP_URL) {
throw new Error("EXPO_PUBLIC_APP_URL is required");
if (!process.env.EXPO_PUBLIC_API_HOST) {
throw new Error("EXPO_PUBLIC_API_HOST is required");
}
const config = {
environmentId: process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
apiHost: process.env.EXPO_PUBLIC_API_HOST,
userId: "random-user-id",
attributes: {
language: "en",
testAttr: "attr-test",
},
};
return (
<View style={styles.container}>
<Text>Formbricks React Native SDK Demo</Text>
<View
style={{
display: "flex",
flexDirection: "column",
gap: 10,
}}>
<Button
title="Trigger Code Action"
onPress={() => {
track("code").catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error tracking event:", error);
});
}}
/>
<Button
title="Set User Id"
onPress={() => {
setUserId("random-user-id").catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error setting user id:", error);
});
}}
/>
<Button
title="Set User Attributess (multiple)"
onPress={() => {
setAttributes({
testAttr: "attr-test",
testAttr2: "attr-test-2",
testAttr3: "attr-test-3",
testAttr4: "attr-test-4",
}).catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error setting user attributes:", error);
});
}}
/>
<Button
title="Set User Attributes (single)"
onPress={() => {
setAttribute("testSingleAttr", "testSingleAttr").catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error setting user attributes:", error);
});
}}
/>
<Button
title="Logout"
onPress={() => {
logout().catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error logging out:", error);
});
}}
/>
<Button
title="Set Language (de)"
onPress={() => {
setLanguage("de").catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error setting language:", error);
});
}}
/>
</View>
<StatusBar style="auto" />
<Formbricks
appUrl={process.env.EXPO_PUBLIC_APP_URL as string}
environmentId={process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID as string}
<Button
title="Trigger Code Action"
onPress={() => {
track("code").catch((error: unknown) => {
// eslint-disable-next-line no-console -- logging is allowed in demo apps
console.error("Error tracking event:", error);
});
}}
/>
<StatusBar style="auto" />
<Formbricks initConfig={config} />
</View>
);
}

View File

@@ -1,6 +1,6 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true
},
"extends": "expo/tsconfig.base"
}
}

View File

@@ -1,7 +1,3 @@
module.exports = {
extends: ["@formbricks/eslint-config/next.js"],
parserOptions: {
project: "tsconfig.json",
tsconfigRootDir: __dirname,
},
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -1,6 +1,6 @@
import { Sidebar } from "./sidebar";
import { Sidebar } from "./Sidebar";
export function LayoutApp({ children }: { children: React.ReactNode }): React.JSX.Element {
export const LayoutApp = ({ children }: { children: React.ReactNode }) => {
return (
<div className="min-h-full">
{/* Static sidebar for desktop */}
@@ -10,4 +10,4 @@ export function LayoutApp({ children }: { children: React.ReactNode }): React.JS
<div className="flex flex-1 flex-col lg:pl-64">{children}</div>
</div>
);
}
};

View File

@@ -25,7 +25,7 @@ const secondaryNavigation = [
{ name: "Privacy", href: "#", icon: ShieldCheckIcon },
];
export function Sidebar(): React.JSX.Element {
export const Sidebar = () => {
return (
<div className="flex flex-grow flex-col overflow-y-auto bg-cyan-700 pb-4 pt-5">
<nav
@@ -62,4 +62,4 @@ export function Sidebar(): React.JSX.Element {
</nav>
</div>
);
}
};

View File

@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -1,3 +1,3 @@
export function classNames(...classes: string[]): string {
export const classNames = (...classes: any) => {
return classes.filter(Boolean).join(" ");
}
};

View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.

View File

@@ -4,21 +4,22 @@
"private": true,
"scripts": {
"clean": "rimraf .turbo node_modules .next",
"dev": "next dev -p 3002 --turbopack",
"go": "next dev -p 3002 --turbopack",
"dev": "next dev -p 3002 --turbo",
"go": "next dev -p 3002 --turbo",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@formbricks/js": "workspace:*",
"lucide-react": "0.468.0",
"next": "15.1.2",
"react": "19.0.0",
"react-dom": "19.0.0"
"@formbricks/ui": "workspace:*",
"lucide-react": "0.452.0",
"next": "14.2.15",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*"
"@formbricks/eslint-config": "workspace:*",
"@formbricks/config-typescript": "workspace:*"
}
}

View File

@@ -1,8 +1,8 @@
import type { AppProps } from "next/app";
import Head from "next/head";
import "../globals.css";
import "@formbricks/ui/globals.css";
export default function App({ Component, pageProps }: AppProps): React.JSX.Element {
const App = ({ Component, pageProps }: AppProps) => {
return (
<>
<Head>
@@ -17,4 +17,6 @@ export default function App({ Component, pageProps }: AppProps): React.JSX.Eleme
<Component {...pageProps} />
</>
);
}
};
export default App;

View File

@@ -1,6 +1,6 @@
import { Head, Html, Main, NextScript } from "next/document";
export default function Document(): React.JSX.Element {
const Document = () => {
return (
<Html lang="en" className="h-full bg-slate-50">
<Head />
@@ -10,4 +10,6 @@ export default function Document(): React.JSX.Element {
</body>
</Html>
);
}
};
export default Document;

View File

@@ -4,9 +4,9 @@ import { useEffect, useState } from "react";
import formbricks from "@formbricks/js";
import fbsetup from "../public/fb-setup.png";
declare const window: Window;
declare const window: any;
export default function AppPage(): React.JSX.Element {
const AppPage = ({}) => {
const [darkMode, setDarkMode] = useState(false);
const router = useRouter();
@@ -19,53 +19,44 @@ export default function AppPage(): React.JSX.Element {
}, [darkMode]);
useEffect(() => {
const initFormbricks = () => {
// enable Formbricks debug mode by adding formbricksDebug=true GET parameter
const addFormbricksDebugParam = (): void => {
const urlParams = new URLSearchParams(window.location.search);
if (!urlParams.has("formbricksDebug")) {
urlParams.set("formbricksDebug", "true");
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
window.history.replaceState({}, "", newUrl);
}
};
addFormbricksDebugParam();
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
const userId = "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING";
const userInitAttributes = {
language: "de",
"Init Attribute 1": "eight",
"Init Attribute 2": "two",
};
void formbricks.init({
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
userId,
attributes: userInitAttributes,
});
}
// Connect next.js router to Formbricks
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
const handleRouteChange = formbricks.registerRouteChange;
router.events.on("routeChangeComplete", () => {
void handleRouteChange();
});
return () => {
router.events.off("routeChangeComplete", () => {
void handleRouteChange();
});
};
// enable Formbricks debug mode by adding formbricksDebug=true GET parameter
const addFormbricksDebugParam = () => {
const urlParams = new URLSearchParams(window.location.search);
if (!urlParams.has("formbricksDebug")) {
urlParams.set("formbricksDebug", "true");
const newUrl = `${window.location.pathname}?${urlParams.toString()}`;
window.history.replaceState({}, "", newUrl);
}
};
initFormbricks();
}, [router.events]);
addFormbricksDebugParam();
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
const userId = "THIS-IS-A-VERY-LONG-USER-ID-FOR-TESTING";
const userInitAttributes = {
language: "de",
"Init Attribute 1": "eight",
"Init Attribute 2": "two",
};
formbricks.init({
environmentId: process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID,
apiHost: process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST,
userId,
attributes: userInitAttributes,
});
}
// Connect next.js router to Formbricks
if (process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID && process.env.NEXT_PUBLIC_FORMBRICKS_API_HOST) {
const handleRouteChange = formbricks?.registerRouteChange;
router.events.on("routeChangeComplete", handleRouteChange);
return () => {
router.events.off("routeChangeComplete", handleRouteChange);
};
}
}, []);
return (
<div className="min-h-screen bg-white px-12 py-6 dark:bg-slate-800">
@@ -83,11 +74,8 @@ export default function AppPage(): React.JSX.Element {
</div>
<button
type="button"
className="mt-2 rounded-lg bg-slate-200 px-6 py-1 dark:bg-slate-700 dark:text-slate-100"
onClick={() => {
setDarkMode(!darkMode);
}}>
onClick={() => setDarkMode(!darkMode)}>
{darkMode ? "Toggle Light Mode" : "Toggle Dark Mode"}
</button>
</div>
@@ -108,8 +96,8 @@ export default function AppPage(): React.JSX.Element {
{process.env.NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID}
</strong>
<span className="relative ml-2 flex h-3 w-3">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-500 opacity-75" />
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500" />
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-green-500 opacity-75"></span>
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
</span>
</div>
</div>
@@ -120,6 +108,9 @@ export default function AppPage(): React.JSX.Element {
Look at the logs to understand how the widget works.{" "}
<strong className="dark:text-white">Open your browser console</strong> to see the logs.
</p>
{/* <div className="max-h-[40vh] overflow-y-auto py-4">
<LogsContainer />
</div> */}
</div>
</div>
@@ -134,9 +125,8 @@ export default function AppPage(): React.JSX.Element {
</p>
<button
className="my-4 rounded-lg bg-slate-500 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
type="button"
onClick={() => {
void formbricks.reset();
formbricks.reset();
}}>
Reset
</button>
@@ -148,9 +138,7 @@ export default function AppPage(): React.JSX.Element {
<div className="p-6">
<div>
<button
type="button"
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
<button className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
No-Code Action
</button>
</div>
@@ -159,7 +147,6 @@ export default function AppPage(): React.JSX.Element {
This button sends a{" "}
<a
href="https://formbricks.com/docs/actions/no-code"
rel="noopener noreferrer"
className="underline dark:text-blue-500"
target="_blank">
No Code Action
@@ -167,7 +154,6 @@ export default function AppPage(): React.JSX.Element {
as long as you created it beforehand in the Formbricks App.{" "}
<a
href="https://formbricks.com/docs/actions/no-code"
rel="noopener noreferrer"
target="_blank"
className="underline dark:text-blue-500">
Here are instructions on how to do it.
@@ -178,9 +164,8 @@ export default function AppPage(): React.JSX.Element {
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setAttribute("Plan", "Free");
formbricks.setAttribute("Plan", "Free");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Plan to &apos;Free&apos;
@@ -192,7 +177,6 @@ export default function AppPage(): React.JSX.Element {
<a
href="https://formbricks.com/docs/attributes/custom-attributes"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
attribute
</a>{" "}
@@ -203,9 +187,8 @@ export default function AppPage(): React.JSX.Element {
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setAttribute("Plan", "Paid");
formbricks.setAttribute("Plan", "Paid");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Plan to &apos;Paid&apos;
@@ -217,7 +200,6 @@ export default function AppPage(): React.JSX.Element {
<a
href="https://formbricks.com/docs/attributes/custom-attributes"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
attribute
</a>{" "}
@@ -228,9 +210,8 @@ export default function AppPage(): React.JSX.Element {
<div className="p-6">
<div>
<button
type="button"
onClick={() => {
void formbricks.setEmail("test@web.com");
formbricks.setEmail("test@web.com");
}}
className="mb-4 rounded-lg bg-slate-800 px-6 py-3 text-white hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600">
Set Email
@@ -242,7 +223,6 @@ export default function AppPage(): React.JSX.Element {
<a
href="https://formbricks.com/docs/attributes/identify-users"
target="_blank"
rel="noopener noreferrer"
className="underline dark:text-blue-500">
user email
</a>{" "}
@@ -254,4 +234,6 @@ export default function AppPage(): React.JSX.Element {
</div>
</div>
);
}
};
export default AppPage;

View File

@@ -3,4 +3,4 @@ module.exports = {
tailwindcss: {},
autoprefixer: {},
},
};
}

View File

@@ -1,5 +1,5 @@
{
"exclude": ["node_modules"],
"extends": "@formbricks/config-typescript/nextjs.json",
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

View File

@@ -1,19 +1,3 @@
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",
},
},
},
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -1,173 +0,0 @@
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
Learn about the different organization-level and team-level roles and how they affect permissions in Formbricks.
## Memberships
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`.
</Note>
Here are the different access permissions, ranked from highest to lowest access
1. Owner
2. Manager
3. Billing
4. Member
### Organisational level
All users and their organization-level roles are listed in **Organization Settings > General**. Users can hold any of the following org-level roles:
- **Billing** users can manage payment and compliance details in the organization.
- **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.
### Team-level Roles
- **Team Contributors** can view and act on surveys and responses.
- **Team Admins** have additional permissions to manage their team's membership and projects. These permissions are granted at the team-level, and don't apply to teams where they're not a Team Admin.
For more information on user roles & permissions, see below:
| | Owner | Manager | Billing | Member |
| -------------------------------- | ----- | ------- | ------- | ------ |
| **Organization** | | | | |
| Update organization | ✅ | ❌ | ❌ | ❌ |
| Delete organization | ✅ | ❌ | ❌ | ❌ |
| 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 | ✅ | ✅ | ❌ | ❌ |
| **Surveys** | | | | |
| Create New Survey | ✅ | ✅ | ❌ | ✅\* |
| Edit Survey | ✅ | ✅ | ❌ | ✅\* |
| Delete Survey | ✅ | ✅ | ❌ | ✅\* |
| View survey results | ✅ | ✅ | ❌ | ✅ |
| **Response** | | | | |
| Delete response | ✅ | ✅ | ❌ | ✅\* |
| Add tags on response | ✅ | ✅ | ❌ | ✅\* |
| Edit tags on response | ✅ | ✅ | ❌ | ✅\* |
| **Actions** | | | | |
| Create Action | ✅ | ✅ | ❌ | ✅\* |
| Update Action | ✅ | ✅ | ❌ | ✅\* |
| Delete Action | ✅ | ✅ | ❌ | ✅\* |
| **API Keys** | | | | |
| Create API key | ✅ | ✅ | ❌ | ✅\*\* |
| Update API key | ✅ | ✅ | ❌ | ✅\*\* |
| Delete API key | ✅ | ✅ | ❌ | ✅\*\* |
| **Tags** | | | | |
| Create tags | ✅ | ✅ | ❌ | ✅\* |
| Update tags | ✅ | ✅ | ❌ | ✅\* |
| Delete tags | ✅ | ✅ | ❌ | ✅\*\* |
| **People** | | | | |
| Delete Person | ✅ | ✅ | ❌ | ✅\* |
| **Integrations** | | | | |
| Manage Integrations | ✅ | ✅ | ❌ | ✅\* |
\* - for the read & write permissions team members
\*\* - for the manage permissions team members
## Inviting organization members
There are two ways to invite organization members: One by one or in bulk.
### Invite organization members one by one
1. Go to the `Organization Settings` page via the menu in the lower right corner:
<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:
<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:
<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`.
</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.
### Invite organization members in bulk
1. Go to the `Organization Settings` page via the menu in the lower right corner:
<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:
<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:
<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 ✅
Formbricks sends an email to each organization member in the CSV. The member can accept the invitation or create a new account by clicking on the link.
---

View File

@@ -1,48 +0,0 @@
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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,48 +0,0 @@
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,83 +0,0 @@
import { MdxImage } from "@/components/mdx-image";
import SurveyEmbed from "@/components/survey-embed";
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: "Recall Data in Formbricks Surveys",
description:
"Personalize your surveys by dynamically inserting data from URL parameters or previous answers into questions and descriptions. The Recall Data feature helps create engaging, adaptive survey experiences tailored to each respondent.",
};
# Recall Data
Personalize your surveys by dynamically inserting data from URL parameters or previous answers into questions and descriptions. The Recall Data feature helps create engaging, adaptive survey experiences tailored to each respondent.
## Recall Sources
You can recall data from the following sources:
- From a previous question
- From a [Hidden Field](/docs/link-surveys/global/hidden-fields)
- From a [Variable](/docs/link-surveys/global/variables)
## Recalling from a previous question
<Note>
The recall functionality is disabled on the first question of the survey since theres no preceding question to recall data from.
</Note>
### **Pre-requisite**
Ensure the answer you wish to recall precedes the question in which it will be recalled. Heres an example of setting up the first question:
<MdxImage
src={StepThree}
alt="Survey setup example with link survey template"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### **Step 1: Recall Data**
Type **`@`** in the question or description field where you want to insert a recall. This triggers a dropdown menu listing all preceding questions. Select the question you want to recall data from.
<MdxImage
src={StepTwo}
alt="Dropdown menu for recalling data in survey"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
### **Step 2: Set a Fallback**
To ensure the survey remains coherent when a response is missing (or the question is optional), you should set a fallback option.
<MdxImage
src={StepOne}
alt="Setting fallback option in survey question"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## Recalling from the URL
1. Create a hidden field, [here is how →](/docs/global/hidden-fields)
2. Use the `@` symbol in a question or description to recall the value of the hidden field
3. Set a fallback in case the hidden field is not being filled by a URL parameter
4. Use [Data Prefilling](/docs/link-surveys/data-prefilling) to set the hidden field value when the survey is accessed
## Recalling from a Variable
1. Create a variable, [here is how →](/docs/link-surveys/global/variables)
2. Use the `@` symbol in a question or description to recall the value of the variable
3. Set a fallback in case the variable is not being filled by a URL parameter
## Live Demo
<SurveyEmbed surveyUrl="https://app.formbricks.com/s/cm393eiiq0001kxphzc6lbbku" />
## **Conclusion**
Recall Data in Formbricks surveys allows for a conversational and customized survey experience that enhances engagement and improves the quality of feedback. By integrating previous responses into new questions, you create a more interactive and relevant environment for respondents.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,88 +0,0 @@
import { MdxImage } from "@/components/mdx-image";
import CreatedVariables from "./images/created-variables.webp";
import InputVariables from "./images/input-variables.webp";
import LogicWithVariables from "./images/logic-with-variables.webp";
import VariablesCard from "./images/variables-card.webp";
import VariablesUsage from "./images/variables-usage.webp";
export const metadata = {
title: "Variables",
description: "Add variabeles to your surveys to transform your survey into a quiz",
};
# Variables
Variables are a powerful feature in Formbricks that allows you to keep track of data variables when user fills a form. This feature is especially useful when you want to use your survey as a quiz.
## Types of Variables
There are two types of variables you can add to your survey:
1. **Text**: You can add text variables to your survey to capture text data.
2. **Number**: You can add number variables to your survey to capture number data.
## How to Add Variables
1. Edit the survey you want to add variables to & switch to the Questions tab and scroll down to the bottom of the page. You will see a section called **Variables**.
<MdxImage
src={VariablesCard}
alt="Variables card"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. Now click on it to add a new variable ID. You can add as many variables as you want. You can also choose the type of variable you want to add along with the default value.
<MdxImage
src={InputVariables}
alt="add variables"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<MdxImage
src={CreatedVariables}
alt="created variables"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
## Use case:
- **Quiz**: You can use variables to create a quiz. For example, you can add a variable `score` and update it every time a user answers a question. You can also use the variable for recall to show the final score to the user.
- **Personalisation**: You can use variables to store user data and personalise the survey experience. For example, you can add a variable `full_name` and update it every time a user fills in their first name and last name. You can use the variable to personalise the survey experience by addressing the user with their full name.
## How is it different from Hidden Fields?
Variables are different from hidden fields in the following ways:
1. **Setting**: Hidden fields can be set through query parameters or `formbricks.init`, but the variables can only be set either during creation or dynamically by using logic actions.
2. **Updating**: Hidden fields cannot be set again, but the value of variables can be updated while the user fills the survey.
3. **Type**: Hidden fields can only store text data, but variables can store both text and number data.
## How to use Variables
1. Once you have added the variables to your survey, you'll be able to access them in the logic editor. You can use the variables to create logic actions and conditions.
<MdxImage
src={VariablesUsage}
alt="Variables usage"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
2. You can create logic based on the variables and questions in your survey and can update the variables based on the user's response.
<MdxImage
src={LogicWithVariables}
alt="Logic with variables"
quality="100"
className="max-w-full rounded-lg sm:max-w-3xl"
/>
<Note>
To know more about how to use logic in Formbricks, check out the [Conditional Logic](/global/conditional-logic).
</Note>

View File

@@ -1,68 +0,0 @@
"use client";
import { useTheme } from "next-themes";
import { useState } from "react";
import { RedocStandalone } from "redoc";
import { LoadingSpinner } from "@/components/icons/loading-spinner";
import { Button } from "@/components/button";
import "./style.css";
export function ApiDocs() {
const { resolvedTheme } = useTheme();
const redocTheme = {
hideDownloadButton: true,
hideLoading: true,
nativeScrollbars: true,
theme: {
sidebar: {
backgroundColor: "transparent",
textColor: resolvedTheme === "dark" ? "rgb(203, 213, 225)" : "rgb(51, 51, 51)",
activeTextColor: "#2dd4bf",
},
rightPanel: {
backgroundColor: "transparent",
},
colors: {
primary: { main: "#2dd4bf" },
text: {
primary: resolvedTheme === "dark" ? "#ffffff" : "rgb(51, 51, 51)",
},
responses: {
success: { color: "#22c55e" },
error: { color: "#ef4444" },
info: { color: "#3b82f6" },
},
},
typography: {
fontSize: "16px",
lineHeight: "2rem",
fontFamily: "Jost, system-ui, -apple-system, sans-serif",
headings: {
fontFamily: "Jost, system-ui, -apple-system, sans-serif",
fontWeight: "600",
},
code: {
fontSize: "16px",
fontFamily: "ui-monospace, monospace",
},
},
codeBlock: {
backgroundColor: "rgb(24, 35, 58)",
},
spacing: { unit: 5 },
},
};
const [loading, setLoading] = useState(true);
return (
<div className="px-4">
<Button href="/developer-docs/rest-api" arrow="left" className="mb-4 mt-8">
Back to docs
</Button>
<RedocStandalone specUrl="/docs/openapi.yaml" onLoaded={() => { setLoading(false); }} options={redocTheme} />
{loading ? <LoadingSpinner /> : null}
</div>
);
}

View File

@@ -1,86 +0,0 @@
:root[data-theme="light"] {
--text-color: rgb(51, 65, 85);
}
:root[data-theme="dark"] {
--text-color: rgb(203, 213, 225);
}
h5,
.sc-dhCplO,
.sc-dpBQxM {
color: var(--text-color) !important;
}
.tab-success,
.react-tabs__tab,
.tab-error {
background-color: transparent !important;
border: 1px solid var(--text-color) !important;
margin: 10px 5px !important;
}
.sc-dwGkES,
.sc-ePpfBx {
background-color: rgb(24, 24, 27) !important;
border: 1px solid var(--text-color) !important;
}
.sc-ePpfBx,
.corVrN {
background-color: rgb(24, 24, 27) !important;
border: none !important;
}
.cqdCbT {
display: none !important;
}
.kiMaJz, .iZNUDY {
align-items: center !important;
}
.react-tabs__tab-panel > div {
padding: 0 !important;
border-radius: 8px !important;
overflow: hidden !important;
}
.sc-Rjrgp {
background-color: rgb(15, 23, 42) !important;
display: flex !important;
justify-content: center !important;
flex-direction: column !important;
padding: 8px 16px !important;
}
.cugBNu {
position: static !important;
}
.daIHdK {
padding: 0 !important;
}
.kqHNPM {
margin-top: 0px !important;
}
.sc-iwXfZk,
.redoc-json {
padding: 1rem !important;
background-color: rgb(24, 35, 58) !important;
}
.sc-uYFMi {
background-color: rgb(24, 35, 58) !important;
padding: 0.5rem 0 !important;
color: #2dd4bf !important;
font-weight: 600 !important;
}
.token {
color: #2dd4bf !important;
}
.property {
color: rgb(203, 213, 225) !important;
}
.sc-iPHsxv {
background-color: rgb(15, 23, 42) !important;
border-radius: 8px !important;
}
.sc-hSyjfr {
background-color: rgb(15, 23, 42) !important;
}

View File

@@ -1,5 +0,0 @@
import { ApiDocs } from "./components/api-docs";
export default function ApiDocsPage() {
return <ApiDocs />;
}

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -1,7 +1,7 @@
import { MdxImage } from "@/components/mdx-image";
import { MdxImage } from "@/components/MdxImage";
import I1 from "./images/i1.webp";
import I2 from "./images/i2.webp";
import I1 from "./images/I1.webp";
import I2 from "./images/I2.webp";
export const metadata = {
title: "Using Actions in Formbricks",

View File

@@ -8,7 +8,7 @@ export const metadata = {
Advanced Targeting allows you to show surveys to the right group of people. You can target surveys based on user attributes, device type, and more instead of spraying and praying. This helps you get more relevant feedback and make data-driven decisions. All of this without writing a single line of code.
## How to setup Advanced Targeting
# How to setup Advanced Targeting
<Note>Advanced Targeting is only available on the Pro plan!</Note>

View File

@@ -1,45 +1,45 @@
import Image from "next/image";
import { Button } from "@/components/button";
import { Button } from "@/components/Button";
import logoHtml from "@/images/frameworks/html5.svg";
import logoNextjs from "@/images/frameworks/nextjs.svg";
import logoReactJs from "@/images/frameworks/reactjs.svg";
import logoVueJs from "@/images/frameworks/vuejs.svg";
import Image from "next/image";
const libraries = [
{
href: "#html",
name: "HTML",
description: "All you need to do is add 3 lines of code to your HTML script and thats it, you're done!",
logo: logoHtml as string,
logo: logoHtml,
},
{
href: "#react-js",
name: "React.js",
description: "Load the our Js library with your environment ID and you're ready to go!",
logo: logoReactJs as string,
logo: logoReactJs,
},
{
href: "#next-js",
name: "Next.js",
description:
"Natively add us to your NextJs project with support for both App as well as Pages project structure!",
logo: logoNextjs as string,
logo: logoNextjs,
},
{
href: "#vue-js",
name: "Vue.js",
description: "Simply add us to your router change and sit back!",
logo: logoVueJs as string,
logo: logoVueJs,
},
{
href: "#react-native",
name: "React Native",
description: "Easily integrate our SDK with your React Native app for seamless survey support!",
logo: logoReactJs as string,
logo: logoReactJs,
},
];
export function Libraries() {
export const Libraries = () => {
return (
<div className="my-16 xl:max-w-none">
<div className="not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-slate-900/5 xl:max-w-none xl:grid-cols-2 2xl:grid-cols-3 dark:border-white/5">
@@ -63,4 +63,4 @@ export function Libraries() {
</div>
</div>
);
}
};

View File

@@ -1,6 +1,6 @@
import { MdxImage } from "@/components/mdx-image";
import { MdxImage } from "@/components/MdxImage";
import { Libraries } from "./components/libraries";
import { Libraries } from "./components/Libraries";
import ReactApp from "./images/react-in-app-survey-app-popup-form.webp";
import WidgetConnected from "./images/widget-connected.webp";
@@ -15,7 +15,7 @@ export const metadata = {
# Framework Guides
One can integrate Formbricks App Survey SDK into their app using multiple options! Checkout the options below that we provide! If you are looking
for something else, please [open a new Github Discussion](https://github.com/formbricks/formbricks/discussions) and we would be glad to help.
for something else, please [join our Discord!](https://formbricks.com/discord) and we would be glad to help.
<Libraries />
@@ -60,7 +60,7 @@ All you need to do is copy a `<script>` tag to your HTML head, and thats abou
</Property>
</Properties>
Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
Refer to our [Example HTML project](https://github.com/formbricks/examples/tree/main/html) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
---
@@ -118,7 +118,7 @@ export default App;
</Property>
</Properties>
Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
Refer to our [Example ReactJs project](https://github.com/formbricks/examples/tree/main/reactjs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
---
@@ -200,6 +200,8 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</CodeGroup>
</Col>
Refer to our [Example NextJS App Directory project](https://github.com/formbricks/examples/tree/main/nextjs-app) for more help!
### Pages Directory
<Col>
@@ -237,6 +239,7 @@ export default function App({ Component, pageProps }: AppProps) {
</CodeGroup>
</Col>
Refer to our [Example NextJS Pages Directory project](https://github.com/formbricks/examples/tree/main/nextjs-pages) for more help!
### Required customizations to be made
@@ -329,7 +332,7 @@ router.afterEach((to, from) => {
</Property>
</Properties>
Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
Refer to our [Example VueJs project](https://github.com/formbricks/examples/tree/main/vuejs) for more help! Now visit the [Validate your Setup](#validate-your-setup) section to verify your setup!
## React Native
@@ -357,11 +360,17 @@ Now, update your App.js/App.tsx file to initialize Formbricks:
// other imports
import Formbricks from "@formbricks/react-native";
const config = {
environmentId: "<environment-id>",
apiHost: "<api-host>",
userId: "<user-id>",
};
export default function App() {
return (
<>
{/* Your app content */}
<Formbricks appUrl="https://app.formbricks.com" environmentId="your-environment-id" />
<Formbricks initConfig={config} />
</>
);
}
@@ -375,7 +384,7 @@ export default function App() {
<Property name="environment-id" type="string">
Formbricks Environment ID.
</Property>
<Property name="app-url" type="string">
<Property name="api-host" type="string">
URL of the hosted Formbricks instance.
</Property>
</Properties>
@@ -442,6 +451,6 @@ Debug log messages provide insights into:
- Event tracking, survey triggers and form interactions.
- Initialization errors.
**Cant figure it out?**: **[Get help in Github Discussions](https://github.com/formbricks/formbricks/discussions)**
**Cant figure it out? [Join our Discord!](https://formbricks.com/discord)**
---

View File

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 149 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

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