Compare commits
255 Commits
codeql2
...
feature/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f88194f770 | ||
|
|
0ebbc67dbe | ||
|
|
cb2a73d569 | ||
|
|
22e8a137ef | ||
|
|
a9fe05d64a | ||
|
|
5219065b8e | ||
|
|
cb8497229d | ||
|
|
25b8920d20 | ||
|
|
9203db88ab | ||
|
|
36378e9c23 | ||
|
|
9c33e77755 | ||
|
|
88cb4c742f | ||
|
|
475cce8253 | ||
|
|
a86c1738d1 | ||
|
|
96a4d02c80 | ||
|
|
bb6df783ab | ||
|
|
26cca5c2f8 | ||
|
|
7e3dd7d624 | ||
|
|
db9a53f923 | ||
|
|
92ae4786f0 | ||
|
|
b35cf14d32 | ||
|
|
14374b55d2 | ||
|
|
5a919018c5 | ||
|
|
6ac73d3f25 | ||
|
|
510fe3902e | ||
|
|
2bc23594ad | ||
|
|
06e00f3066 | ||
|
|
9b3d409695 | ||
|
|
f7f5737abf | ||
|
|
458f135ee1 | ||
|
|
8e116bf62d | ||
|
|
f1d697a83f | ||
|
|
69a7a57f41 | ||
|
|
24de1559a5 | ||
|
|
ec29abfcaf | ||
|
|
eac97db665 | ||
|
|
d8386328e7 | ||
|
|
d28f321aa2 | ||
|
|
e691c076a1 | ||
|
|
ad842e0e80 | ||
|
|
dcf4109c5b | ||
|
|
05287c135e | ||
|
|
6ff8ec21cf | ||
|
|
7b6e22aa04 | ||
|
|
ee56914285 | ||
|
|
a2e9cd3c43 | ||
|
|
359f29a264 | ||
|
|
576b15fec0 | ||
|
|
42434290da | ||
|
|
62c6189dfd | ||
|
|
21c9ebbca3 | ||
|
|
658d4687f9 | ||
|
|
3775453db8 | ||
|
|
edcaf8e639 | ||
|
|
3aa658a64e | ||
|
|
58fc66ad1c | ||
|
|
f68f87645f | ||
|
|
25f99da172 | ||
|
|
5da6faa972 | ||
|
|
02b25138ef | ||
|
|
21644f5ad8 | ||
|
|
d3adc1629c | ||
|
|
7c60c57c60 | ||
|
|
7006a790dc | ||
|
|
2575b649a0 | ||
|
|
8399391aaa | ||
|
|
dfbec20016 | ||
|
|
17ac777e9b | ||
|
|
01edc0e6e0 | ||
|
|
d6b58a5e66 | ||
|
|
517ef9515c | ||
|
|
7d94861db9 | ||
|
|
cb1e4fa583 | ||
|
|
dd5fced6c4 | ||
|
|
01a4d91167 | ||
|
|
2e2f0fdbb5 | ||
|
|
3ca1a72c6a | ||
|
|
9905199055 | ||
|
|
5970ff917f | ||
|
|
d197c91995 | ||
|
|
6e1ee6df12 | ||
|
|
b44d6e60bd | ||
|
|
1d0e49d5b6 | ||
|
|
ced7b2aa2c | ||
|
|
6aa473c316 | ||
|
|
aebe36b9e8 | ||
|
|
71c92766d3 | ||
|
|
a3f5f7645a | ||
|
|
7e8514e7be | ||
|
|
7715789d0f | ||
|
|
a93fed448f | ||
|
|
10738a7af0 | ||
|
|
2c00c55e5d | ||
|
|
80ba02851f | ||
|
|
5fd3190a2d | ||
|
|
117ec317de | ||
|
|
f4f2836bdb | ||
|
|
e194a2feb8 | ||
|
|
93e9ec867c | ||
|
|
1fe625a9b4 | ||
|
|
1c1ef56e00 | ||
|
|
838fe16845 | ||
|
|
7f3c45f85a | ||
|
|
9a4f6721e2 | ||
|
|
a4ffc03e55 | ||
|
|
c4c98bda31 | ||
|
|
63aa2fd307 | ||
|
|
99d0b1786d | ||
|
|
843fed5ffd | ||
|
|
9b8112b478 | ||
|
|
2433d40918 | ||
|
|
2c1f473bbe | ||
|
|
7605a4d835 | ||
|
|
a1e2fddd5c | ||
|
|
74b770a937 | ||
|
|
bd8724c1e2 | ||
|
|
e508159255 | ||
|
|
4649921b1d | ||
|
|
1c58ac3704 | ||
|
|
4676b4cd25 | ||
|
|
15f36651d8 | ||
|
|
7b11ef9b40 | ||
|
|
3b90f085b1 | ||
|
|
36bf445370 | ||
|
|
8936ce928f | ||
|
|
28aec8852b | ||
|
|
5d1224e438 | ||
|
|
a0d02a843e | ||
|
|
08f22983e7 | ||
|
|
07289667c0 | ||
|
|
f49375dce4 | ||
|
|
9910cafe78 | ||
|
|
9996a1579b | ||
|
|
fd913ad1fa | ||
|
|
412a873c47 | ||
|
|
22046f4cfb | ||
|
|
030debe9d9 | ||
|
|
9db1d548a0 | ||
|
|
da167642b7 | ||
|
|
971053c90d | ||
|
|
65d9220df6 | ||
|
|
264ebb1d4c | ||
|
|
3509d3594f | ||
|
|
2a268accff | ||
|
|
37966880fd | ||
|
|
6699c92082 | ||
|
|
9ca1c5c14b | ||
|
|
af1a5f7361 | ||
|
|
c349a3b869 | ||
|
|
8d2edf91a1 | ||
|
|
39b686a13b | ||
|
|
139965d6ca | ||
|
|
e79e692735 | ||
|
|
797c56585b | ||
|
|
e38391ade0 | ||
|
|
f344715381 | ||
|
|
1e95b8cf9e | ||
|
|
8ffaf0748e | ||
|
|
88357a3aeb | ||
|
|
dfe025ab8e | ||
|
|
97a66168c0 | ||
|
|
a9983e1fe0 | ||
|
|
793ce0afd8 | ||
|
|
4d7cc26983 | ||
|
|
c72ce9b446 | ||
|
|
f3c628ba76 | ||
|
|
9a66e26b00 | ||
|
|
8b7f2f102f | ||
|
|
56d8c3f50f | ||
|
|
61f5c66444 | ||
|
|
a2f7b1a780 | ||
|
|
35b2d12e18 | ||
|
|
5dcd32050a | ||
|
|
1099d67bf1 | ||
|
|
69625ae832 | ||
|
|
a1c7c4a310 | ||
|
|
a3bea3b7da | ||
|
|
a3043c1f6d | ||
|
|
d0da7858ec | ||
|
|
7bba09c16d | ||
|
|
78813d53b1 | ||
|
|
35eac02545 | ||
|
|
a5b0d39adf | ||
|
|
f80d1b32b7 | ||
|
|
7598a16b75 | ||
|
|
b83b54eee1 | ||
|
|
eb2621f72a | ||
|
|
44980d21a9 | ||
|
|
e1d2e1357b | ||
|
|
f80c7d03e2 | ||
|
|
4ca6ee358b | ||
|
|
9dad06222d | ||
|
|
37ef6be4c3 | ||
|
|
f0a4fad878 | ||
|
|
06ae035e11 | ||
|
|
91b5177bdb | ||
|
|
30bd427985 | ||
|
|
a92762ff47 | ||
|
|
0a5c98aba0 | ||
|
|
6f041bf693 | ||
|
|
23c9dc304a | ||
|
|
97377fe8bd | ||
|
|
36cf16ce90 | ||
|
|
ab3ef63097 | ||
|
|
7f8549124f | ||
|
|
43b1cb904d | ||
|
|
762a3ca626 | ||
|
|
91f0d00ba2 | ||
|
|
41f42f4427 | ||
|
|
98181bfe6c | ||
|
|
a8ab4aaf2e | ||
|
|
78dca7a2bf | ||
|
|
844ea40c3a | ||
|
|
7a6dedf452 | ||
|
|
b641b37308 | ||
|
|
8c1f8bfb42 | ||
|
|
1f1563401d | ||
|
|
9fd585ee07 | ||
|
|
609dcabf77 | ||
|
|
80d338c998 | ||
|
|
306784c31b | ||
|
|
bcd68e0f19 | ||
|
|
5764148753 | ||
|
|
e7edfe3ba1 | ||
|
|
da6f54eede | ||
|
|
ade5c3d80e | ||
|
|
cc2600cfba | ||
|
|
9b191ef3e4 | ||
|
|
c450c35baf | ||
|
|
b35b82f4ee | ||
|
|
ea52624ab2 | ||
|
|
0484bccfd1 | ||
|
|
d094f63faa | ||
|
|
dc6bc61442 | ||
|
|
5918c42cf9 | ||
|
|
c34a08561e | ||
|
|
7213c726b4 | ||
|
|
f650ac4e76 | ||
|
|
2ff1be2c4a | ||
|
|
61ac306ef3 | ||
|
|
022569e404 | ||
|
|
ebf22df7b6 | ||
|
|
adcc596875 | ||
|
|
1bcdf06b43 | ||
|
|
3be78f0312 | ||
|
|
5633499834 | ||
|
|
88847a153b | ||
|
|
3e7c3a45c3 | ||
|
|
1af1a92fec | ||
|
|
7b38923b7d | ||
|
|
38500e1d79 | ||
|
|
2e82fc3ead | ||
|
|
e88ae4aa3d | ||
|
|
5f55c922dc | ||
|
|
fc3c044e00 |
@@ -1,16 +0,0 @@
|
||||
# [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"
|
||||
@@ -1,28 +1,6 @@
|
||||
// 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
|
||||
{
|
||||
// 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"]
|
||||
}
|
||||
},
|
||||
|
||||
"dockerComposeFile": "docker-compose.yml",
|
||||
// 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],
|
||||
|
||||
"name": "Node.js & PostgreSQL",
|
||||
"postAttachCommand": "pnpm dev --filter=@formbricks/web... --filter=@formbricks/demo...",
|
||||
|
||||
// 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",
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "node",
|
||||
"service": "app",
|
||||
"workspaceFolder": "/workspace"
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
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
|
||||
21
.env.example
@@ -46,6 +46,9 @@ 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
|
||||
|
||||
@@ -101,6 +104,11 @@ 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=
|
||||
@@ -157,11 +165,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=admin
|
||||
# DEFAULT_ORGANIZATION_ROLE=owner
|
||||
|
||||
# Send new users to customer.io
|
||||
# CUSTOMER_IO_API_KEY=
|
||||
# CUSTOMER_IO_SITE_ID=
|
||||
# Send new users to Brevo
|
||||
# BREVO_API_KEY=
|
||||
# BREVO_LIST_ID=
|
||||
|
||||
# Ignore Rate Limiting across the Formbricks app
|
||||
# RATE_LIMITING_DISABLED=1
|
||||
@@ -185,4 +193,7 @@ UNSPLASH_ACCESS_KEY=
|
||||
# AI_AZURE_RESSOURCE_NAME=
|
||||
# AI_AZURE_API_KEY=
|
||||
# AI_AZURE_EMBEDDINGS_DEPLOYMENT_ID=
|
||||
# AI_AZURE_LLM_DEPLOYMENT_ID=
|
||||
# AI_AZURE_LLM_DEPLOYMENT_ID=
|
||||
|
||||
# NEXT_PUBLIC_INTERCOM_APP_ID=
|
||||
# INTERCOM_SECRET_KEY=
|
||||
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Bug report
|
||||
description: "Found a bug? Please fill out the sections below. \U0001F44D"
|
||||
labels:
|
||||
- bug
|
||||
type: bug
|
||||
body:
|
||||
- type: textarea
|
||||
id: issue-summary
|
||||
@@ -10,6 +9,13 @@ body:
|
||||
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:
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: Feature request
|
||||
description: "Suggest an idea for this project \U0001F680"
|
||||
labels:
|
||||
- enhancement
|
||||
type: feature
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem-description
|
||||
@@ -30,4 +29,4 @@ body:
|
||||
### Additional resources 🤓
|
||||
|
||||
- Check out our [Contributor Docs](https://formbricks.com/docs/developer-docs/contributing/get-started)
|
||||
- Anything unclear? [Ask in Discord](https://formbricks.com/discord)
|
||||
- Anything unclear? [Ask in Github Discussions](https://github.com/formbricks/formbricks/discussions)
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/task.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
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
|
||||
4
.github/workflows/build-docs.yml
vendored
@@ -1,6 +1,10 @@
|
||||
name: Build Docs
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Docs
|
||||
|
||||
4
.github/workflows/build-web.yml
vendored
@@ -1,6 +1,10 @@
|
||||
name: Build Web
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Formbricks-web
|
||||
|
||||
92
.github/workflows/codeql.yml
vendored
@@ -1,92 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '17 1 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
10
.github/workflows/cron-surveyStatusUpdate.yml
vendored
@@ -4,9 +4,13 @@ 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 * * *"
|
||||
schedule:
|
||||
# Runs "At 00:00." (see https://crontab.guru)
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
cron-weeklySummary:
|
||||
env:
|
||||
|
||||
2
.github/workflows/cron-weeklySummary.yml
vendored
@@ -9,6 +9,8 @@ on:
|
||||
- cron: "0 8 * * 1"
|
||||
jobs:
|
||||
cron-weeklySummary:
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
APP_URL: ${{ secrets.APP_URL }}
|
||||
CRON_SECRET: ${{ secrets.CRON_SECRET }}
|
||||
|
||||
50
.github/workflows/e2e.yml
vendored
@@ -1,9 +1,28 @@
|
||||
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
|
||||
@@ -60,7 +79,8 @@ jobs:
|
||||
|
||||
- name: Apply Prisma Migrations
|
||||
run: |
|
||||
pnpm prisma migrate deploy
|
||||
# pnpm prisma migrate deploy
|
||||
pnpm db:migrate:dev
|
||||
|
||||
- name: Run App
|
||||
run: |
|
||||
@@ -82,11 +102,35 @@ jobs:
|
||||
- name: Install Playwright
|
||||
run: pnpm exec playwright install --with-deps
|
||||
|
||||
- name: Run E2E Tests
|
||||
- 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'
|
||||
run: |
|
||||
pnpm test:e2e
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
|
||||
10
.github/workflows/lint.yml
vendored
@@ -1,6 +1,10 @@
|
||||
name: Lint
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Linters
|
||||
@@ -8,16 +12,16 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
|
||||
- uses: ./.github/actions/dangerous-git-checkout
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
39
.github/workflows/pr.yml
vendored
@@ -1,5 +1,13 @@
|
||||
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:
|
||||
@@ -12,55 +20,28 @@ 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
|
||||
|
||||
@@ -69,6 +50,10 @@ 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')
|
||||
|
||||
62
.github/workflows/prepare-release.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
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 }}
|
||||
5
.github/workflows/release-changesets.yml
vendored
@@ -6,6 +6,11 @@ on:
|
||||
# branches:
|
||||
# - main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
packages: write
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
env:
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
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
|
||||
2
.github/workflows/release-docker.yml
vendored
@@ -8,6 +8,8 @@ on:
|
||||
jobs:
|
||||
release-image-on-dockerhub:
|
||||
name: Release on Dockerhub
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
52
.github/workflows/sonarqube.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
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 }}
|
||||
2
.github/workflows/test.yml
vendored
@@ -6,6 +6,8 @@ jobs:
|
||||
name: Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
39
.github/workflows/tolgee-missing-key-check.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
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
|
||||
42
.github/workflows/tolgee.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
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
|
||||
@@ -3,7 +3,7 @@ name: "Welcome new contributors"
|
||||
on:
|
||||
issues:
|
||||
types: opened
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
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 at [Discord](https://formbricks.com/discord)
|
||||
Feel free to join the conversation on [Github Discussions](https://github.com/formbricks/formbricks/discussions) if you need any help or have any questions. 😊
|
||||
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
@@ -35,9 +35,6 @@ yarn-error.log*
|
||||
!packages/database/.env
|
||||
!apps/web/.env
|
||||
|
||||
# Prisma generated files
|
||||
packages/database/zod
|
||||
|
||||
# turbo
|
||||
.turbo
|
||||
|
||||
@@ -60,3 +57,6 @@ packages/lib/uploads
|
||||
|
||||
# js compiled assets
|
||||
apps/web/public/js
|
||||
|
||||
|
||||
packages/database/migrations
|
||||
6
.gitpod.Dockerfile
vendored
@@ -1,6 +0,0 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
# Install custom tools, runtime, etc.
|
||||
RUN brew install yq
|
||||
|
||||
RUN pnpm install turbo --global
|
||||
74
.gitpod.yml
@@ -1,74 +0,0 @@
|
||||
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"
|
||||
1
.husky/post-checkout
Normal file
@@ -0,0 +1 @@
|
||||
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json
|
||||
1
.husky/post-commit
Normal file
@@ -0,0 +1 @@
|
||||
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json
|
||||
@@ -1 +1,5 @@
|
||||
pnpm lint-staged
|
||||
pnpm lint-staged
|
||||
pnpm tolgee-pull || true
|
||||
echo "{\"branchName\": \"main\"}" > ../branch.json
|
||||
git add branch.json packages/lib/messages/*.json
|
||||
|
||||
|
||||
31
.tolgeerc.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$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,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://formbricks.com/docs/developer-docs/contributing/gitpod) or use [Codespaces](https://formbricks.com/docs/developer-docs/contributing/codespaces)
|
||||
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. **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? 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!
|
||||
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!
|
||||
|
||||
## 🚀 Aspiring Features
|
||||
|
||||
|
||||
2
LICENSE
@@ -2,7 +2,7 @@ Copyright (c) 2024 Formbricks GmbH
|
||||
|
||||
Portions of this software are licensed as follows:
|
||||
|
||||
- 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 "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/" 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.
|
||||
|
||||
10
README.md
@@ -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/discord">Join Discord community</a>
|
||||
<a href="https://formbricks.com/">Website</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://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://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://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/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.
|
||||
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.
|
||||
|
||||
### White-Labeling Formbricks and Other Licensing Needs
|
||||
|
||||
We currently do not offer Formbricks white-labeled. Any other needs? [Send us an email](mailto:hola@formbricks.com).
|
||||
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).
|
||||
|
||||
### Why charge for Enterprise Features?
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
<p align="right"><a href="#top">🔼 Back to top</a></p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Security Policy of Formbricks
|
||||
|
||||
This is Formbrick's security policy. Please reach out to us
|
||||
on our Discord or, if privately, via <security@formbricks.com>
|
||||
on Github 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 our Discord for this.
|
||||
- Assistance is needed to optimize Formbricks for security – please engage on Github Discussions for this.
|
||||
- Help is required for applying security-related updates.
|
||||
- The concern is not related to security.
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000
|
||||
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq
|
||||
EXPO_PUBLIC_APP_URL=http://192.168.0.197:3000
|
||||
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=cm5p0cs7r000819182b32j0a1
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"jsEngine": "hermes",
|
||||
"name": "react-native-demo",
|
||||
"newArchEnabled": true,
|
||||
"orientation": "portrait",
|
||||
"slug": "react-native-demo",
|
||||
"splash": {
|
||||
|
||||
@@ -13,16 +13,18 @@
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@formbricks/react-native": "workspace:*",
|
||||
"expo": "51.0.26",
|
||||
"expo-status-bar": "1.12.1",
|
||||
"@react-native-async-storage/async-storage": "2.1.0",
|
||||
"expo": "52.0.28",
|
||||
"expo-status-bar": "2.0.1",
|
||||
"react": "18.3.1",
|
||||
"react-native": "0.74.4",
|
||||
"react-native-webview": "13.8.6"
|
||||
"react-dom": "18.3.1",
|
||||
"react-native": "0.76.6",
|
||||
"react-native-webview": "13.12.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.25.2",
|
||||
"@types/react": "18.3.11",
|
||||
"typescript": "5.3.3"
|
||||
"@babel/core": "7.26.0",
|
||||
"@types/react": "18.3.18",
|
||||
"typescript": "5.7.2"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import React, { type JSX } from "react";
|
||||
import { Button, LogBox, StyleSheet, Text, View } from "react-native";
|
||||
import Formbricks, { track } from "@formbricks/react-native";
|
||||
import Formbricks, {
|
||||
logout,
|
||||
setAttribute,
|
||||
setAttributes,
|
||||
setLanguage,
|
||||
setUserId,
|
||||
track,
|
||||
} from "@formbricks/react-native";
|
||||
|
||||
LogBox.ignoreAllLogs();
|
||||
|
||||
@@ -9,35 +17,92 @@ export default function App(): JSX.Element {
|
||||
throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required");
|
||||
}
|
||||
|
||||
if (!process.env.EXPO_PUBLIC_API_HOST) {
|
||||
throw new Error("EXPO_PUBLIC_API_HOST is required");
|
||||
if (!process.env.EXPO_PUBLIC_APP_URL) {
|
||||
throw new Error("EXPO_PUBLIC_APP_URL 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>
|
||||
|
||||
<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);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<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 initConfig={config} />
|
||||
|
||||
<Formbricks
|
||||
appUrl={process.env.EXPO_PUBLIC_APP_URL as string}
|
||||
environmentId={process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID as string}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
extends: ["@formbricks/eslint-config/next.js"],
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Sidebar } from "./Sidebar";
|
||||
import { Sidebar } from "./sidebar";
|
||||
|
||||
export const LayoutApp = ({ children }: { children: React.ReactNode }) => {
|
||||
export function LayoutApp({ children }: { children: React.ReactNode }): React.JSX.Element {
|
||||
return (
|
||||
<div className="min-h-full">
|
||||
{/* Static sidebar for desktop */}
|
||||
@@ -10,4 +10,4 @@ export const LayoutApp = ({ children }: { children: React.ReactNode }) => {
|
||||
<div className="flex flex-1 flex-col lg:pl-64">{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -25,7 +25,7 @@ const secondaryNavigation = [
|
||||
{ name: "Privacy", href: "#", icon: ShieldCheckIcon },
|
||||
];
|
||||
|
||||
export const Sidebar = () => {
|
||||
export function Sidebar(): React.JSX.Element {
|
||||
return (
|
||||
<div className="flex flex-grow flex-col overflow-y-auto bg-cyan-700 pb-4 pt-5">
|
||||
<nav
|
||||
@@ -62,4 +62,4 @@ export const Sidebar = () => {
|
||||
</nav>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
3
apps/demo/globals.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,3 +1,3 @@
|
||||
export const classNames = (...classes: any) => {
|
||||
export function classNames(...classes: string[]): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
};
|
||||
}
|
||||
|
||||
2
apps/demo/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
|
||||
|
||||
@@ -4,22 +4,21 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"clean": "rimraf .turbo node_modules .next",
|
||||
"dev": "next dev -p 3002 --turbo",
|
||||
"go": "next dev -p 3002 --turbo",
|
||||
"dev": "next dev -p 3002 --turbopack",
|
||||
"go": "next dev -p 3002 --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"lucide-react": "0.452.0",
|
||||
"next": "14.2.15",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1"
|
||||
"lucide-react": "0.468.0",
|
||||
"next": "15.1.2",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*"
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
import "@formbricks/ui/globals.css";
|
||||
import "../globals.css";
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps) => {
|
||||
export default function App({ Component, pageProps }: AppProps): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@@ -17,6 +17,4 @@ const App = ({ Component, pageProps }: AppProps) => {
|
||||
<Component {...pageProps} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
const Document = () => {
|
||||
export default function Document(): React.JSX.Element {
|
||||
return (
|
||||
<Html lang="en" className="h-full bg-slate-50">
|
||||
<Head />
|
||||
@@ -10,6 +10,4 @@ const Document = () => {
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export default Document;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import { useEffect, useState } from "react";
|
||||
import formbricks from "@formbricks/js";
|
||||
import fbsetup from "../public/fb-setup.png";
|
||||
|
||||
declare const window: any;
|
||||
declare const window: Window;
|
||||
|
||||
const AppPage = ({}) => {
|
||||
export default function AppPage(): React.JSX.Element {
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
@@ -19,44 +19,53 @@ const AppPage = ({}) => {
|
||||
}, [darkMode]);
|
||||
|
||||
useEffect(() => {
|
||||
// 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);
|
||||
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();
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
initFormbricks();
|
||||
}, [router.events]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
||||
@@ -74,8 +83,11 @@ const AppPage = ({}) => {
|
||||
</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>
|
||||
@@ -96,8 +108,8 @@ const AppPage = ({}) => {
|
||||
{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>
|
||||
<span className="relative inline-flex h-3 w-3 rounded-full bg-green-500"></span>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,9 +120,6 @@ const AppPage = ({}) => {
|
||||
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>
|
||||
|
||||
@@ -125,8 +134,9 @@ const AppPage = ({}) => {
|
||||
</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={() => {
|
||||
formbricks.reset();
|
||||
void formbricks.reset();
|
||||
}}>
|
||||
Reset
|
||||
</button>
|
||||
@@ -138,7 +148,9 @@ const AppPage = ({}) => {
|
||||
|
||||
<div className="p-6">
|
||||
<div>
|
||||
<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
|
||||
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">
|
||||
No-Code Action
|
||||
</button>
|
||||
</div>
|
||||
@@ -147,6 +159,7 @@ const AppPage = ({}) => {
|
||||
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
|
||||
@@ -154,6 +167,7 @@ const AppPage = ({}) => {
|
||||
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.
|
||||
@@ -164,8 +178,9 @@ const AppPage = ({}) => {
|
||||
<div className="p-6">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
formbricks.setAttribute("Plan", "Free");
|
||||
void 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 'Free'
|
||||
@@ -177,6 +192,7 @@ const AppPage = ({}) => {
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/custom-attributes"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline dark:text-blue-500">
|
||||
attribute
|
||||
</a>{" "}
|
||||
@@ -187,8 +203,9 @@ const AppPage = ({}) => {
|
||||
<div className="p-6">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
formbricks.setAttribute("Plan", "Paid");
|
||||
void 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 'Paid'
|
||||
@@ -200,6 +217,7 @@ const AppPage = ({}) => {
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/custom-attributes"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline dark:text-blue-500">
|
||||
attribute
|
||||
</a>{" "}
|
||||
@@ -210,8 +228,9 @@ const AppPage = ({}) => {
|
||||
<div className="p-6">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
formbricks.setEmail("test@web.com");
|
||||
void 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
|
||||
@@ -223,6 +242,7 @@ const AppPage = ({}) => {
|
||||
<a
|
||||
href="https://formbricks.com/docs/attributes/identify-users"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="underline dark:text-blue-500">
|
||||
user email
|
||||
</a>{" "}
|
||||
@@ -234,6 +254,4 @@ const AppPage = ({}) => {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppPage;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
173
apps/docs/app/[survey-type]/global/access-roles/page.mdx
Normal file
@@ -0,0 +1,173 @@
|
||||
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.
|
||||
|
||||
---
|
||||
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -1,10 +1,10 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import SurveyEmbed from "@/components/SurveyEmbed";
|
||||
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
|
||||
# Add Image or Video to a Question
|
||||
|
||||
Enhance your questions by adding images or videos. This makes instructions clearer and the survey more engaging.
|
||||
|
||||
@@ -29,6 +29,7 @@ Upload an image by clicking the upload icon or dragging the file:
|
||||
/>
|
||||
|
||||
## How to Add Videos
|
||||
|
||||
Toggle to add a video via link:
|
||||
|
||||
<MdxImage
|
||||
@@ -42,4 +43,6 @@ Toggle to add a video via link:
|
||||
|
||||
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>
|
||||
<Note>
|
||||
**YouTube Privacy Mode**: This option reduces tracking by converting YouTube URLs to no-cookie URLs. It only works with YouTube.
|
||||
</Note>
|
||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
@@ -1,4 +1,4 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
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";
|
||||
@@ -21,7 +21,7 @@ export const metadata = {
|
||||
"Create complex survey logic with the Logic Editor. Use conditions, actions, and variables to create a personalized survey experience.",
|
||||
};
|
||||
|
||||
# Logic Editor
|
||||
# Conditional Logic
|
||||
|
||||
Create complex survey logic with the Logic Editor. Use conditions, actions, and variables to create a personalized survey experience.
|
||||
|
||||
@@ -38,8 +38,7 @@ Create complex survey logic with the Logic Editor. Use conditions, actions, and
|
||||
|
||||
<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.
|
||||
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.
|
||||
@@ -87,8 +86,7 @@ Conditons can be based on:
|
||||
/>
|
||||
|
||||
<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.
|
||||
- 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
|
||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -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.
|
||||
|
After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
@@ -1,4 +1,4 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import { MdxImage } from "@/components/mdx-image";
|
||||
|
||||
import FilledHiddenFields from "./filled-hidden-fields.webp";
|
||||
import HiddenFieldResponses from "./hidden-field-responses.webp";
|
||||
@@ -10,15 +10,13 @@ export const metadata = {
|
||||
description: "Add hidden fields to your surveys to capture additional data without requiring user inputs!",
|
||||
};
|
||||
|
||||
#### Link Surveys
|
||||
|
||||
# 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 them in the Survey Builder
|
||||
### 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.
|
||||
|
||||
@@ -29,6 +27,8 @@ Hidden fields are a powerful feature in Formbricks that allows you to add data t
|
||||
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
|
||||
@@ -45,7 +45,11 @@ Hidden fields are a powerful feature in Formbricks that allows you to add data t
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
### Set Single Hidden Field
|
||||
## Set Hidden Field in Responses
|
||||
|
||||
### Link Surveys
|
||||
|
||||
Single Hidden Field:
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Example Screen from which the User filled it">
|
||||
@@ -57,7 +61,7 @@ https://formbricks.com/clin3dxja02k8l80hpwmx4bjy?screen=pricing
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
|
||||
### Set Multiple Hidden Fields
|
||||
Multiple Hidden Fields:
|
||||
|
||||
<Col>
|
||||
<CodeGroup title="Example Screen from which the User filled it">
|
||||
@@ -69,6 +73,25 @@ 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.
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,13 +1,12 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import { MdxImage } from "@/components/mdx-image";
|
||||
|
||||
import StepOne from "./images/StepOne.webp";
|
||||
import StepThree from "./images/StepThree.webp";
|
||||
import StepTwo from "./images/StepTwo.webp";
|
||||
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.",
|
||||
description: "Limit the number of responses your survey can receive.",
|
||||
};
|
||||
|
||||
# Limit by Number of Submissions
|
||||
@@ -20,7 +19,7 @@ Automatically close your survey after a specific number of responses with Formbr
|
||||
src={StepTwo}
|
||||
alt="Choose a link survey template"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl "
|
||||
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.
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -1,5 +1,5 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import { TellaVideo } from "@/components/TellaVideo";
|
||||
import { MdxImage } from "@/components/mdx-image";
|
||||
import { TellaVideo } from "@/components/tella-video";
|
||||
|
||||
import Filters from "./filters.webp";
|
||||
import MetadataCard from "./metadata-card.webp";
|
||||
@@ -62,4 +62,4 @@ You can export the metadata of your responses along with the response data. When
|
||||
|
||||
---
|
||||
|
||||
Can’t figure it out? Join our [Discord](https://discord.com/invite/3YFcABF2Ts)!
|
||||
**Can’t figure it out?**: **[Get help in Github Discussions](https://github.com/formbricks/formbricks/discussions)**
|
||||
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -1,6 +1,6 @@
|
||||
import { MdxImage } from "@/components/MdxImage";
|
||||
import { MdxImage } from "@/components/mdx-image";
|
||||
|
||||
import { ResponsiveVideo } from "@/components/ResponsiveVideo";
|
||||
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";
|
||||
@@ -24,17 +24,13 @@ Multi-Language Surveys allow you to create surveys that support multiple languag
|
||||
|
||||
How to deliver a specific language depends on the survey type (app or link survey):
|
||||
|
||||
- app survey: Set a `language` attribute for the user. [See the guide below for App Surveys](#app-surveys-configuration)
|
||||
- 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)
|
||||
|
||||
<Note>
|
||||
Multi-Language is feature of the Enterprise Edition. You can use it for **free** by adding your credit card
|
||||
details on the Formbricks Cloud.
|
||||
</Note>
|
||||
|
||||
|
||||
<ResponsiveVideo title="Formbricks Multi-language Surveys"
|
||||
src="https://www.youtube-nocookie.com/embed/Vol5zjYIoos?si=bdP2y3uk8-xR0uSD&controls=0" />
|
||||
<ResponsiveVideo
|
||||
title="Formbricks Multi-language Surveys"
|
||||
src="https://www.youtube-nocookie.com/embed/Vol5zjYIoos?si=bdP2y3uk8-xR0uSD&controls=0"
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
@@ -58,11 +54,11 @@ How to deliver a specific language depends on the survey type (app or link surve
|
||||
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 product.
|
||||
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 Product"
|
||||
alt="Add Multiple Languages to your Project"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
@@ -72,7 +68,7 @@ You can come back to this page anytime to add more languages or remove existing
|
||||
|
||||
<MdxImage
|
||||
src={SurveysHome}
|
||||
alt="Add Multiple Languages to your Product"
|
||||
alt="Add Multiple Languages to your Project"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
@@ -141,8 +137,7 @@ Formbricks.init({
|
||||
</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.
|
||||
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.
|
||||
@@ -168,4 +163,4 @@ Without the `lang` parameter, Formbricks will show the survey in the default lan
|
||||
|
||||
You can now start collecting responses in multiple languages!
|
||||
|
||||
Can’t figure it out? Join our [Discord](https://discord.com/invite/3YFcABF2Ts)!
|
||||
**Can’t figure it out?**: **[Get help in Github Discussions](https://github.com/formbricks/formbricks/discussions)**
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |