mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 02:10:12 -06:00
Merge branch 'formbricks:main' into feature/docs-in-page-section-nav
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
|
||||
// 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 && pnpm install && pnpm db:migrate:dev",
|
||||
"postAttachCommand": "pnpm dev --filter=web... --filter=demo...",
|
||||
"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"
|
||||
|
||||
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
packages/config-eslint/
|
||||
10
.eslintrc.js
10
.eslintrc.js
@@ -1,10 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
// This tells ESLint to load the config from the package `eslint-config-formbricks`
|
||||
extends: ["formbricks"],
|
||||
settings: {
|
||||
next: {
|
||||
rootDir: ["apps/*/"],
|
||||
},
|
||||
},
|
||||
};
|
||||
16
.github/actions/cache-build-web/action.yml
vendored
16
.github/actions/cache-build-web/action.yml
vendored
@@ -49,20 +49,16 @@ runs:
|
||||
run: cp .env.example .env
|
||||
shell: bash
|
||||
|
||||
- name: Add E2E Testing Mode
|
||||
- name: Fill ENCRYPTION_KE, ENTERPRISE_LICENSE_KEY and E2E_TESTING in .env
|
||||
run: |
|
||||
echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Generate Random ENCRYPTION_KEY
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
echo "ENTERPRISE_LICENSE_KEY=$SECRET" >> $GITHUB_ENV
|
||||
RANDOM_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
||||
sed -i "s/ENTERPRISE_LICENSE_KEY=.*/ENTERPRISE_LICENSE_KEY=${RANDOM_KEY}/" .env
|
||||
echo "E2E_TESTING=${{ inputs.e2e_testing_mode }}" >> .env
|
||||
shell: bash
|
||||
|
||||
- run: |
|
||||
pnpm build --filter=web...
|
||||
pnpm build --filter=@formbricks/web...
|
||||
|
||||
if: steps.cache-build.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/build-web.yml
vendored
2
.github/workflows/build-web.yml
vendored
@@ -31,4 +31,4 @@ jobs:
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
|
||||
- name: Build Formbricks-web
|
||||
run: pnpm build --filter=web...
|
||||
run: pnpm build --filter=@formbricks/web...
|
||||
|
||||
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Run App
|
||||
run: |
|
||||
NODE_ENV=test pnpm start --filter=web &
|
||||
NODE_ENV=test pnpm start --filter=@formbricks/web &
|
||||
for attempt in {1..20}; do
|
||||
if [ $(curl -o /dev/null -s -w "%{http_code}" http://localhost:3000/health) -eq 200 ]; then
|
||||
echo "Ready"
|
||||
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
- name: create .env
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Generate Random ENCRYPTION_KEY
|
||||
- name: Generate Random ENCRYPTION_KEY and fill in .env
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_KEY}/" .env
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
91
.github/workflows/release-docker-github-experimental.yml
vendored
Normal file
91
.github/workflows/release-docker-github-experimental.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: Docker Release to Github Experimental
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}-experimental
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/formbricks?schema=public"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Depot CLI
|
||||
uses: depot/setup-action@v1
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@v3.5.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3 # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: depot/build-push-action@v1
|
||||
with:
|
||||
project: tw0fqmsx3c
|
||||
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||
context: .
|
||||
file: ./apps/web/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
# Sign the resulting Docker image digest except on PRs.
|
||||
# This will only write to the public Rekor transparency log when the Docker
|
||||
# repository is public to avoid leaking data. If you would like to publish
|
||||
# transparency data even for private images, pass --force to cosign below.
|
||||
# https://github.com/sigstore/cosign
|
||||
- name: Sign the published Docker image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
env:
|
||||
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
# This step uses the identity token to provision an ephemeral certificate
|
||||
# against the sigstore community Fulcio instance.
|
||||
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
- name: create .env
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Generate Random ENCRYPTION_KEY
|
||||
- name: Generate Random ENCRYPTION_KEY and fill in .env
|
||||
run: |
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
echo "ENCRYPTION_KEY=$SECRET" >> $GITHUB_ENV
|
||||
ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${ENCRYPTION_KEY}/" .env
|
||||
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const baseConfig = require("./packages/prettier-config/prettier-preset");
|
||||
const baseConfig = require("./packages/config-prettier/prettier-preset");
|
||||
|
||||
module.exports = {
|
||||
...baseConfig,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["formbricks"],
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"react-dom": "18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/tsconfig": "workspace:*"
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ const AppPage = ({}) => {
|
||||
router.events.off("routeChangeComplete", handleRouteChange);
|
||||
};
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-white px-12 py-6 dark:bg-slate-800">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/nextjs.json",
|
||||
"extends": "@formbricks/config-typescript/nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["formbricks"],
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
|
||||
@@ -43,7 +43,6 @@ To be able to keep working on Formbricks over the coming years, we need to colle
|
||||
|
||||
Once you open a PR, you will get a message from the CLA bot to fill out the form. Please note that we can only get your contribution merged when we have a CLA signed by you.
|
||||
|
||||
|
||||
## Setup Dev Environment
|
||||
|
||||
We currently officially support the below methods to set up your development environment for Formbricks.
|
||||
@@ -425,7 +424,7 @@ This usually happens when the Formbricks Widget wasn't correctly or completely b
|
||||
<CodeGroup title="Build js library first and then run again">
|
||||
|
||||
```bash
|
||||
pnpm build --filter=js
|
||||
pnpm build --filter=@formbricks/js
|
||||
|
||||
// Run the app again
|
||||
pnpm dev
|
||||
@@ -441,15 +440,15 @@ Since we're working with a monorepo structure, the repository can get quite big.
|
||||
<CodeGroup title="Only run the required project">
|
||||
|
||||
```bash {{ title: 'Formbricks Web-App' }}
|
||||
pnpm dev --filter=web...
|
||||
pnpm dev --filter=@formbricks/web...
|
||||
```
|
||||
|
||||
```bash {{ title: 'Formbricks Landing Page' }}
|
||||
pnpm dev --filter=formbricks-com...
|
||||
```bash {{ title: 'Formbricks Docs' }}
|
||||
pnpm dev --filter=@formbricks/docs...
|
||||
```
|
||||
|
||||
```bash {{ title: 'Formbricks Demo App' }}
|
||||
pnpm dev --filter=demo...
|
||||
pnpm dev --filter=@formbricks/demo...
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -468,4 +467,3 @@ However, in our experience it's better to run `pnpm dev` than having two termina
|
||||
This happens when you're using the Demo App and delete the Person within the Formbricks app which the widget is currently connected with. We're fixing it, but you can also just logout your test person and reload the page to get rid of it.
|
||||
|
||||
<MdxImage src={Logout} alt="Logout Person" quality="100" className="max-w-full rounded-lg sm:max-w-3xl" />
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -43,7 +43,7 @@ Set up this feature to control how many users see your survey, using a simple sl
|
||||
4. **Adjust User Visibility Percentage**:
|
||||
|
||||
- Find the **`Show Survey to % of Users`** toggle. Enable it.
|
||||
- Use the slider to select the desired percentage (from 1% to 100%) of users to whom the survey will be shown.
|
||||
- Enter the desired percentage (from 0.01% to 100%) of users to whom the survey will be shown.
|
||||
|
||||
{" "}
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@
|
||||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/react-highlight-words": "^0.16.7",
|
||||
"eslint-config-formbricks": "workspace:*"
|
||||
"@formbricks/eslint-config": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/nextjs.json",
|
||||
"extends": "@formbricks/config-typescript/nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "../../packages/types/*.d.ts"],
|
||||
"exclude": ["../../.env", "node_modules"],
|
||||
"compilerOptions": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Preview } from "@storybook/react";
|
||||
|
||||
import "../src/index.css";
|
||||
import "../../web/app/globals.css";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
|
||||
@@ -12,12 +12,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^1.5.0",
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@storybook/addon-essentials": "^8.1.5",
|
||||
"@storybook/addon-interactions": "^8.1.5",
|
||||
"@storybook/addon-links": "^8.1.5",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -1,5 +1,5 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
import base from "../../packages/tailwind-config/tailwind.config";
|
||||
import base from "../../packages/config-tailwind/tailwind.config";
|
||||
|
||||
export default {
|
||||
...base,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["formbricks"],
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
|
||||
@@ -48,8 +48,8 @@ COPY --from=builder /app/out/full/ .
|
||||
RUN touch /app/apps/web/.env
|
||||
|
||||
# Build the project
|
||||
RUN pnpm post-install --filter=web...
|
||||
RUN pnpm turbo run build --filter=web...
|
||||
RUN pnpm post-install --filter=@formbricks/web...
|
||||
RUN pnpm turbo run build --filter=@formbricks/web...
|
||||
|
||||
# Extract Prisma version
|
||||
RUN jq -r '.devDependencies.prisma' packages/database/package.json > /prisma_version.txt
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Label } from "@formbricks/ui/Label";
|
||||
import { RadioGroup, RadioGroupItem } from "@formbricks/ui/RadioGroup";
|
||||
|
||||
interface DisplayOption {
|
||||
id: "displayOnce" | "displayMultiple" | "respondMultiple";
|
||||
id: "displayOnce" | "displayMultiple" | "respondMultiple" | "displaySome";
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
@@ -23,6 +23,11 @@ const displayOptions: DisplayOption[] = [
|
||||
name: "Show only once",
|
||||
description: "The survey will be shown once, even if person doesn't respond.",
|
||||
},
|
||||
{
|
||||
id: "displaySome",
|
||||
name: "Show multiple times",
|
||||
description: "The survey will be shown multiple times until they respond",
|
||||
},
|
||||
{
|
||||
id: "displayMultiple",
|
||||
name: "Until they submit a response",
|
||||
@@ -51,6 +56,7 @@ export const RecontactOptionsCard = ({
|
||||
const [inputDays, setInputDays] = useState(
|
||||
localSurvey.recontactDays !== null ? localSurvey.recontactDays : 1
|
||||
);
|
||||
const [displayLimit, setDisplayLimit] = useState(localSurvey.displayLimit ?? 1);
|
||||
|
||||
const handleCheckMark = () => {
|
||||
if (ignoreWaiting) {
|
||||
@@ -70,6 +76,14 @@ export const RecontactOptionsCard = ({
|
||||
setLocalSurvey(updatedSurvey);
|
||||
};
|
||||
|
||||
const handleRecontactSessionDaysChange = (event) => {
|
||||
const value = Number(event.target.value);
|
||||
setDisplayLimit(value);
|
||||
|
||||
const updatedSurvey = { ...localSurvey, displayLimit: value } satisfies TSurvey;
|
||||
setLocalSurvey(updatedSurvey);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (localSurvey.type === "link") {
|
||||
setOpen(false);
|
||||
@@ -115,24 +129,49 @@ export const RecontactOptionsCard = ({
|
||||
if (v === "displayOnce" || v === "displayMultiple" || v === "respondMultiple") {
|
||||
const updatedSurvey: TSurvey = { ...localSurvey, displayOption: v };
|
||||
setLocalSurvey(updatedSurvey);
|
||||
} else if (v === "displaySome") {
|
||||
const updatedSurvey: TSurvey = {
|
||||
...localSurvey,
|
||||
displayOption: v,
|
||||
displayLimit,
|
||||
};
|
||||
setLocalSurvey(updatedSurvey);
|
||||
}
|
||||
}}>
|
||||
{displayOptions.map((option) => (
|
||||
<Label
|
||||
key={option.name}
|
||||
htmlFor={option.name}
|
||||
className="flex w-full cursor-pointer items-center rounded-lg border bg-slate-50 p-4">
|
||||
<RadioGroupItem
|
||||
value={option.id}
|
||||
id={option.name}
|
||||
className="aria-checked:border-brand-dark mx-5 disabled:border-slate-400 aria-checked:border-2"
|
||||
/>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-700">{option.name}</p>
|
||||
<>
|
||||
<Label
|
||||
key={option.name}
|
||||
htmlFor={option.name}
|
||||
className="flex w-full cursor-pointer items-center rounded-lg border bg-slate-50 p-4">
|
||||
<RadioGroupItem
|
||||
value={option.id}
|
||||
id={option.name}
|
||||
className="aria-checked:border-brand-dark mx-5 disabled:border-slate-400 aria-checked:border-2"
|
||||
/>
|
||||
<div>
|
||||
<p className="font-semibold text-slate-700">{option.name}</p>
|
||||
|
||||
<p className="mt-2 text-xs font-normal text-slate-600">{option.description}</p>
|
||||
</div>
|
||||
</Label>
|
||||
<p className="mt-2 text-xs font-normal text-slate-600">{option.description}</p>
|
||||
</div>
|
||||
</Label>
|
||||
{option.id === "displaySome" && localSurvey.displayOption === "displaySome" && (
|
||||
<label htmlFor="displayLimit" className="cursor-pointer p-4">
|
||||
<p className="text-sm font-semibold text-slate-700">
|
||||
Show survey maximum of
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
id="displayLimit"
|
||||
value={displayLimit.toString()}
|
||||
onChange={(e) => handleRecontactSessionDaysChange(e)}
|
||||
className="mx-2 inline w-16 bg-white text-center text-sm"
|
||||
/>
|
||||
times.
|
||||
</p>
|
||||
</label>
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +101,23 @@ export const WhenToSendCard = ({
|
||||
};
|
||||
|
||||
const handleRandomizerInput = (e) => {
|
||||
const updatedSurvey = { ...localSurvey, displayPercentage: parseInt(e.target.value) };
|
||||
let value: number | null = null;
|
||||
|
||||
if (e.target.value !== "") {
|
||||
value = parseFloat(e.target.value);
|
||||
|
||||
if (Number.isNaN(value)) {
|
||||
value = 1;
|
||||
}
|
||||
|
||||
if (value < 0.01) value = 0.01;
|
||||
if (value > 100) value = 100;
|
||||
|
||||
// Round value to two decimal places. eg: 10.555(and higher like 10.556) -> 10.56 and 10.554(and lower like 10.553) ->10.55
|
||||
value = Math.round(value * 100) / 100;
|
||||
}
|
||||
|
||||
const updatedSurvey = { ...localSurvey, displayPercentage: value };
|
||||
setLocalSurvey(updatedSurvey);
|
||||
};
|
||||
|
||||
@@ -294,22 +310,21 @@ export const WhenToSendCard = ({
|
||||
title="Show survey to % of users"
|
||||
description="Only display the survey to a subset of the users"
|
||||
childBorder={true}>
|
||||
<div className="w-full">
|
||||
<div className="flex flex-col justify-center rounded-lg border bg-slate-50 p-6">
|
||||
<h3 className="mb-4 text-sm font-semibold text-slate-700">
|
||||
Show to {localSurvey.displayPercentage}% of targeted users
|
||||
</h3>
|
||||
<input
|
||||
<label htmlFor="small-range" className="cursor-pointer p-4">
|
||||
<p className="text-sm font-semibold text-slate-700">
|
||||
Show to {localSurvey.displayPercentage}% of targeted users
|
||||
<Input
|
||||
id="small-range"
|
||||
type="range"
|
||||
min="1"
|
||||
type="number"
|
||||
step="0.01"
|
||||
min="0.01"
|
||||
max="100"
|
||||
value={localSurvey.displayPercentage ?? 50}
|
||||
value={localSurvey.displayPercentage ?? ""}
|
||||
onChange={handleRandomizerInput}
|
||||
className="range-sm mb-6 h-1 w-full cursor-pointer appearance-none rounded-lg bg-slate-200 dark:bg-slate-700"
|
||||
className="mx-2 inline w-20 bg-white text-center text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</label>
|
||||
</AdvancedOptionToggle>
|
||||
</div>
|
||||
</Collapsible.CollapsibleContent>
|
||||
|
||||
@@ -14,6 +14,7 @@ export const minimalSurvey: TSurvey = {
|
||||
triggers: [],
|
||||
redirectUrl: null,
|
||||
recontactDays: null,
|
||||
displayLimit: null,
|
||||
welcomeCard: {
|
||||
enabled: false,
|
||||
headline: { default: "Welcome!" },
|
||||
|
||||
@@ -86,9 +86,9 @@ export const BulkInviteTab = ({ setOpen, onSubmit, canDoRoleManagement }: BulkIn
|
||||
</div>
|
||||
<div>
|
||||
{!canDoRoleManagement && (
|
||||
<Alert variant="destructive" className="mt-1.5 flex items-start bg-slate-50">
|
||||
<Alert variant="error" className="mt-1.5 flex items-start bg-slate-50">
|
||||
<AlertDescription className="ml-2">
|
||||
<p className="text-sm text-slate-700 ">
|
||||
<p className="text-sm">
|
||||
<strong>Warning: </strong> Please note that on the Free Plan, all organization members are
|
||||
automatically assigned the "Admin" role regardless of the role specified in the CSV
|
||||
file.
|
||||
|
||||
@@ -40,7 +40,7 @@ export const Testimonial = () => {
|
||||
<Image
|
||||
src={Peer}
|
||||
alt="Cal.com Co-Founder Peer Richelsen"
|
||||
className="-mb-12 h-28 w-28 rounded-full border border-slate-200 shadow-sm"
|
||||
className="h-28 w-28 rounded-full border border-slate-200 shadow-sm"
|
||||
/>
|
||||
<div>
|
||||
<p className="mb-1.5 text-sm text-slate-500">Peer Richelsen, Co-Founder Cal.com</p>
|
||||
|
||||
@@ -199,11 +199,7 @@ export const GET = async (
|
||||
};
|
||||
}
|
||||
|
||||
return responses.successResponse(
|
||||
{ ...state },
|
||||
true,
|
||||
"public, s-maxage=100, max-age=110, stale-while-revalidate=100, stale-if-error=100"
|
||||
);
|
||||
return responses.successResponse({ ...state }, true);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return responses.internalServerErrorResponse("Unable to handle the request: " + error.message, true);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@formbricks/js-core": "workspace:*",
|
||||
"@formbricks/lib": "workspace:*",
|
||||
"@formbricks/surveys": "workspace:*",
|
||||
"@formbricks/tailwind-config": "workspace:*",
|
||||
"@formbricks/config-tailwind": "workspace:*",
|
||||
"@formbricks/types": "workspace:*",
|
||||
"@formbricks/ui": "workspace:*",
|
||||
"@hookform/resolvers": "^3.4.2",
|
||||
@@ -69,13 +69,13 @@
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@neshca/cache-handler": "^1.3.2",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/lodash": "^4.17.4",
|
||||
"@types/markdown-it": "^14.1.1",
|
||||
"@types/papaparse": "^5.3.14",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"eslint-config-formbricks": "workspace:*"
|
||||
"@formbricks/eslint-config": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const base = require("../../packages/tailwind-config/tailwind.config");
|
||||
const base = require("../../packages/config-tailwind/tailwind.config");
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/nextjs.json",
|
||||
"extends": "@formbricks/config-typescript/nextjs.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "../../packages/types/*.d.ts"],
|
||||
"exclude": ["../../.env", "node_modules"],
|
||||
"compilerOptions": {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
|
||||
"generate": "turbo run generate",
|
||||
"lint": "turbo run lint",
|
||||
"release": "turbo run build --filter=js... && turbo run build --filter=n8n-node... && changeset publish",
|
||||
"release": "turbo run build --filter=@formbricks/js... && changeset publish",
|
||||
"test": "turbo run test --no-cache",
|
||||
"test:e2e": "playwright test",
|
||||
"prepare": "husky install",
|
||||
@@ -34,12 +34,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.44.1",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"eslint": "^8.57.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.5",
|
||||
"rimraf": "^5.0.7",
|
||||
"tsx": "^4.11.0",
|
||||
"turbo": "^1.13.3"
|
||||
"turbo": "^2.0.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"(apps|packages)/**/*.{js,ts,jsx,tsx}": [
|
||||
@@ -56,7 +57,7 @@
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"packageManager": "pnpm@9.1.3",
|
||||
"packageManager": "pnpm@8.15.8",
|
||||
"nextBundleAnalysis": {
|
||||
"budget": 358400,
|
||||
"budgetPercentIncreaseRed": 20,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
extends: [ "turbo", "prettier"],
|
||||
};
|
||||
extends: ["@formbricks/eslint-config/legacy-library.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
};
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
"build": "tsc && vite build",
|
||||
"build:dev": "pnpm build",
|
||||
"go": "vite build --watch",
|
||||
"lint": "eslint ./src --fix",
|
||||
"lint": "eslint . --ext .ts,.js,.tsx,.jsx",
|
||||
"clean": "rimraf .turbo node_modules dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/types": "workspace:*",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"terser": "^5.31.0",
|
||||
"vite": "^5.2.11",
|
||||
"vite-plugin-dts": "^3.9.1"
|
||||
|
||||
@@ -7,7 +7,7 @@ export const makeRequest = async <T>(
|
||||
method: "GET" | "POST" | "PUT" | "DELETE",
|
||||
data?: any
|
||||
): Promise<Result<T, NetworkError | Error>> => {
|
||||
const url = new URL(endpoint, apiHost);
|
||||
const url = new URL(apiHost + endpoint);
|
||||
const body = JSON.stringify(data);
|
||||
|
||||
const res = wrapThrows(fetch)(url.toString(), {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/js-library.json",
|
||||
"include": ["src", "package.json"],
|
||||
"extends": "@formbricks/config-typescript/js-library.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["dist", "build", "node_modules"],
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
3
packages/config-eslint/README.md
Normal file
3
packages/config-eslint/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `@turbo/eslint-config`
|
||||
|
||||
Collection of internal eslint configurations.
|
||||
3
packages/config-eslint/legacy-library.js
Normal file
3
packages/config-eslint/legacy-library.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ["turbo", "prettier"],
|
||||
};
|
||||
3
packages/config-eslint/legacy-react.js
Normal file
3
packages/config-eslint/legacy-react.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ["turbo", "prettier", "plugin:react-hooks/recommended"],
|
||||
};
|
||||
34
packages/config-eslint/library.js
Normal file
34
packages/config-eslint/library.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const { resolve } = require("node:path");
|
||||
|
||||
const project = resolve(process.cwd(), "tsconfig.json");
|
||||
|
||||
/*
|
||||
* This is a custom ESLint configuration for use with
|
||||
* typescript packages.
|
||||
*
|
||||
* This config extends the Vercel Engineering Style Guide.
|
||||
* For more information, see https://github.com/vercel/style-guide
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
extends: ["@vercel/style-guide/eslint/node", "@vercel/style-guide/eslint/typescript"].map(require.resolve),
|
||||
parserOptions: {
|
||||
project,
|
||||
},
|
||||
globals: {
|
||||
React: true,
|
||||
JSX: true,
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: {
|
||||
project,
|
||||
},
|
||||
node: {
|
||||
extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx"],
|
||||
},
|
||||
},
|
||||
},
|
||||
ignorePatterns: ["node_modules/", "dist/"],
|
||||
};
|
||||
45
packages/config-eslint/next.js
Normal file
45
packages/config-eslint/next.js
Normal file
@@ -0,0 +1,45 @@
|
||||
const { resolve } = require("node:path");
|
||||
|
||||
const project = resolve(process.cwd(), "tsconfig.json");
|
||||
|
||||
/*
|
||||
* This is a custom ESLint configuration for use with
|
||||
* Next.js apps.
|
||||
*
|
||||
* This config extends the Vercel Engineering Style Guide.
|
||||
* For more information, see https://github.com/vercel/style-guide
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
"@vercel/style-guide/eslint/node",
|
||||
"@vercel/style-guide/eslint/typescript",
|
||||
"@vercel/style-guide/eslint/browser",
|
||||
"@vercel/style-guide/eslint/react",
|
||||
"@vercel/style-guide/eslint/next",
|
||||
"eslint-config-turbo",
|
||||
].map(require.resolve),
|
||||
parserOptions: {
|
||||
project,
|
||||
},
|
||||
globals: {
|
||||
React: true,
|
||||
JSX: true,
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: {
|
||||
project,
|
||||
},
|
||||
node: {
|
||||
extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx"],
|
||||
},
|
||||
},
|
||||
},
|
||||
ignorePatterns: ["node_modules/", "dist/"],
|
||||
// add rules configurations here
|
||||
rules: {
|
||||
"import/no-default-export": "off",
|
||||
},
|
||||
};
|
||||
17
packages/config-eslint/package.json
Normal file
17
packages/config-eslint/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@formbricks/eslint-config",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@next/eslint-plugin-next": "^14.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"@vercel/style-guide": "^5.2.0",
|
||||
"eslint-config-next": "^14.2.3",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-turbo": "^2.0.0",
|
||||
"eslint-plugin-react": "7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7"
|
||||
}
|
||||
}
|
||||
49
packages/config-eslint/react.js
vendored
Normal file
49
packages/config-eslint/react.js
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
const { resolve } = require("node:path");
|
||||
|
||||
const project = resolve(process.cwd(), "tsconfig.json");
|
||||
|
||||
/*
|
||||
* This is a custom ESLint configuration for use a library
|
||||
* that utilizes React.
|
||||
*
|
||||
* This config extends the Vercel Engineering Style Guide.
|
||||
* For more information, see https://github.com/vercel/style-guide
|
||||
*
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
"@vercel/style-guide/eslint/browser",
|
||||
"@vercel/style-guide/eslint/typescript",
|
||||
"@vercel/style-guide/eslint/react",
|
||||
].map(require.resolve),
|
||||
parserOptions: {
|
||||
project,
|
||||
},
|
||||
globals: {
|
||||
JSX: true,
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
typescript: {
|
||||
project,
|
||||
},
|
||||
node: {
|
||||
extensions: [".mjs", ".js", ".jsx", ".ts", ".tsx"],
|
||||
},
|
||||
},
|
||||
},
|
||||
ignorePatterns: ["node_modules/", "dist/", ".eslintrc.js", "**/*.css"],
|
||||
// add rules configurations here
|
||||
rules: {
|
||||
"import/no-default-export": "off",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.config.js"],
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@formbricks/prettier-config",
|
||||
"name": "@formbricks/config-prettier",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@formbricks/tailwind-config",
|
||||
"name": "@formbricks/config-tailwind",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
@@ -35,25 +35,22 @@ module.exports = {
|
||||
dark: "#00C4B8",
|
||||
},
|
||||
focus: "var(--formbricks-focus, #1982fc)",
|
||||
error: "var(--formbricks-error, #d13a3a)",
|
||||
error: "rgb(from var(--formbricks-error) r g b / <alpha-value>)",
|
||||
brandnew: "var(--formbricks-brand, #038178)",
|
||||
borderColor: {
|
||||
primary: "var(--formbricks-border-primary, #e0e0e0)",
|
||||
secondary: "var(--formbricks-border-secondary, #0f172a)",
|
||||
disabled: "var(--formbricks-border-disabled, #ececec)",
|
||||
error: "var(--formbricks-error, #d13a3a)",
|
||||
},
|
||||
labelColor: {
|
||||
primary: "var(--formbricks-label-primary, #0f172a)",
|
||||
secondary: "var(--formbricks-label-secondary, #384258)",
|
||||
disabled: "var(--formbricks-label-disabled, #bdbdbd)",
|
||||
error: "var(--formbricks-error, #d13a3a)",
|
||||
},
|
||||
fill: {
|
||||
primary: "var(--formbricks-fill-primary, #fefefe)",
|
||||
secondary: "var(--formbricks-fill-secondary, #0f172a)",
|
||||
disabled: "var(--formbricks-fill-disabled, #e0e0e0)",
|
||||
error: "var(--formbricks-error, #d13a3a)",
|
||||
},
|
||||
},
|
||||
keyframes: {
|
||||
@@ -5,10 +5,10 @@
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"declaration": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"module": "esnext",
|
||||
"target": "ES2021",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["es2022", "dom", "dom.iterable"],
|
||||
"module": "ESNext",
|
||||
"target": "ES2022",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@formbricks/tsconfig",
|
||||
"name": "@formbricks/config-typescript",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["formbricks"],
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Survey" ALTER COLUMN "displayPercentage" SET DATA TYPE DECIMAL(65,30);
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "displayOptions" ADD VALUE 'displaySome';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Survey" ADD COLUMN "displayLimit" INTEGER;
|
||||
@@ -41,10 +41,10 @@
|
||||
"dotenv-cli": "^7.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/types": "workspace:*",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"prisma": "^5.14.0",
|
||||
"prisma-dbml-generator": "^0.12.0",
|
||||
"prisma-json-types-generator": "^3.0.4",
|
||||
|
||||
@@ -248,6 +248,7 @@ enum SurveyType {
|
||||
enum displayOptions {
|
||||
displayOnce
|
||||
displayMultiple
|
||||
displaySome
|
||||
respondMultiple
|
||||
}
|
||||
|
||||
@@ -278,6 +279,7 @@ model Survey {
|
||||
responses Response[]
|
||||
displayOption displayOptions @default(displayOnce)
|
||||
recontactDays Int?
|
||||
displayLimit Int?
|
||||
triggers SurveyTrigger[]
|
||||
/// @zod.custom(imports.ZSurveyInlineTriggers)
|
||||
/// [SurveyInlineTriggers]
|
||||
@@ -312,7 +314,7 @@ model Survey {
|
||||
verifyEmail Json?
|
||||
pin String?
|
||||
resultShareKey String? @unique
|
||||
displayPercentage Int?
|
||||
displayPercentage Decimal?
|
||||
languages SurveyLanguage[]
|
||||
|
||||
@@index([environmentId, updatedAt])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/node16.json",
|
||||
"extends": "@formbricks/config-typescript/node16.json",
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "tsup.config.ts"],
|
||||
"exclude": ["node_modules", "dist", "zod"]
|
||||
}
|
||||
|
||||
3
packages/ee/.eslintrc.js
Normal file
3
packages/ee/.eslintrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
import "server-only";
|
||||
|
||||
import axios from "axios";
|
||||
|
||||
import { cache, revalidateTag } from "@formbricks/lib/cache";
|
||||
import { E2E_TESTING, ENTERPRISE_LICENSE_KEY, IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import { hashString } from "@formbricks/lib/hashString";
|
||||
@@ -82,17 +84,13 @@ export const getIsEnterpriseEdition = async (): Promise<boolean> => {
|
||||
},
|
||||
});
|
||||
|
||||
const res = await fetch("https://ee.formbricks.com/api/licenses/check", {
|
||||
body: JSON.stringify({
|
||||
licenseKey: ENTERPRISE_LICENSE_KEY,
|
||||
usage: { responseCount: responseCount },
|
||||
}),
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "POST",
|
||||
const response = await axios.post("https://ee.formbricks.com/api/licenses/check", {
|
||||
licenseKey: process.env.ENTERPRISE_LICENSE_KEY,
|
||||
usage: { responseCount: responseCount },
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const responseJson = await res.json();
|
||||
if (response.status === 200) {
|
||||
const responseJson = response.data;
|
||||
return responseJson.data.status === "active";
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/lib": "*",
|
||||
"@formbricks/tsconfig": "*",
|
||||
"@formbricks/config-typescript": "*",
|
||||
"@formbricks/types": "*",
|
||||
"@formbricks/ui": "*",
|
||||
"eslint-config-formbricks": "workspace:*"
|
||||
"@formbricks/eslint-config": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@formbricks/lib": "workspace:*",
|
||||
"axios": "^1.7.2",
|
||||
"stripe": "^15.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/react-library.json",
|
||||
"extends": "@formbricks/config-typescript/react-library.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
||||
3
packages/email/.eslintrc.js
Normal file
3
packages/email/.eslintrc.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
@@ -30,8 +30,7 @@ import { ResponseFinishedEmail } from "./components/survey/ResponseFinishedEmail
|
||||
import { NoLiveSurveyNotificationEmail } from "./components/weekly-summary/NoLiveSurveyNotificationEmail";
|
||||
import { WeeklySummaryNotificationEmail } from "./components/weekly-summary/WeeklySummaryNotificationEmail";
|
||||
|
||||
export const IS_SMTP_CONFIGURED: boolean =
|
||||
SMTP_HOST && SMTP_PORT && SMTP_USER && SMTP_PASSWORD ? true : false;
|
||||
export const IS_SMTP_CONFIGURED: boolean = SMTP_HOST && SMTP_PORT ? true : false;
|
||||
|
||||
interface sendEmailData {
|
||||
to: string;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "eslint-config-formbricks",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"clean": "rimraf node_modules .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^7.10.0",
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "^14.2.3",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-turbo": "1.10.12",
|
||||
"eslint-plugin-react": "7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-react-refresh": "^0.4.7",
|
||||
"eslint-plugin-storybook": "^0.8.0"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
extends: ["turbo", "prettier"],
|
||||
extends: ["@formbricks/eslint-config/legacy-library.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
};
|
||||
|
||||
@@ -29,6 +29,17 @@
|
||||
"import": "./dist/website.js",
|
||||
"require": "./dist/website.umd.cjs",
|
||||
"types": "./dist/website.d.ts"
|
||||
},
|
||||
"./*": "./dist/*"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"app": [
|
||||
"./dist/app.d.ts"
|
||||
],
|
||||
"website": [
|
||||
"./dist/website.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
@@ -38,16 +49,16 @@
|
||||
"build": "pnpm build:app && pnpm build:website",
|
||||
"build:dev": "tsc && vite build --mode dev",
|
||||
"go": "vite build --watch --mode dev",
|
||||
"lint": "eslint ./src --fix",
|
||||
"lint": "eslint . --ext .ts,.js,.tsx,.jsx",
|
||||
"clean": "rimraf .turbo node_modules dist coverage"
|
||||
},
|
||||
"author": "Formbricks <hola@formbricks.com>",
|
||||
"devDependencies": {
|
||||
"@formbricks/api": "workspace:*",
|
||||
"@formbricks/lib": "workspace:*",
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/types": "workspace:*",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"terser": "^5.31.0",
|
||||
"vite": "^5.2.11",
|
||||
"vite-plugin-dts": "^3.9.1"
|
||||
|
||||
@@ -27,7 +27,7 @@ export const setIsSurveyRunning = (value: boolean) => {
|
||||
};
|
||||
|
||||
const shouldDisplayBasedOnPercentage = (displayPercentage: number) => {
|
||||
const randomNum = Math.floor(Math.random() * 100) + 1;
|
||||
const randomNum = Math.floor(Math.random() * 10000) / 100;
|
||||
return randomNum <= displayPercentage;
|
||||
};
|
||||
|
||||
|
||||
@@ -98,16 +98,36 @@ export const filterPublicSurveys = (state: TJsWebsiteState): TJsWebsiteState =>
|
||||
return state;
|
||||
}
|
||||
|
||||
// filter surveys that meet the displayOption criteria
|
||||
let filteredSurveys = surveys.filter((survey) => {
|
||||
if (survey.displayOption === "respondMultiple") {
|
||||
return true;
|
||||
} else if (survey.displayOption === "displayOnce") {
|
||||
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||
} else if (survey.displayOption === "displayMultiple") {
|
||||
return displays.filter((display) => display.surveyId === survey.id && display.responded).length === 0;
|
||||
} else {
|
||||
throw Error("Invalid displayOption");
|
||||
// Function to filter surveys based on displayOption criteria
|
||||
let filteredSurveys = surveys.filter((survey: TSurvey) => {
|
||||
switch (survey.displayOption) {
|
||||
case "respondMultiple":
|
||||
return true;
|
||||
case "displayOnce":
|
||||
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||
case "displayMultiple":
|
||||
return (
|
||||
displays.filter((display) => display.surveyId === survey.id).filter((display) => display.responded)
|
||||
.length === 0
|
||||
);
|
||||
|
||||
case "displaySome":
|
||||
if (survey.displayLimit === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if any display has responded, if so, stop here
|
||||
if (
|
||||
displays.filter((display) => display.surveyId === survey.id).some((display) => display.responded)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, check if displays length is less than displayLimit
|
||||
return displays.filter((display) => display.surveyId === survey.id).length < survey.displayLimit;
|
||||
|
||||
default:
|
||||
throw Error("Invalid displayOption");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export const setIsSurveyRunning = (value: boolean) => {
|
||||
};
|
||||
|
||||
const shouldDisplayBasedOnPercentage = (displayPercentage: number) => {
|
||||
const randomNum = Math.floor(Math.random() * 100) + 1;
|
||||
const randomNum = Math.floor(Math.random() * 10000) / 100;
|
||||
return randomNum <= displayPercentage;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/js-library.json",
|
||||
"extends": "@formbricks/config-typescript/js-library.json",
|
||||
"include": ["src", "package.json", "../types/surveys.d.ts"],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
extends: ["turbo", "prettier"],
|
||||
extends: ["@formbricks/eslint-config/legacy-library.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@formbricks/js",
|
||||
"license": "MIT",
|
||||
"version": "2.0.1",
|
||||
"version": "2.1.0",
|
||||
"description": "Formbricks-js allows you to connect your app to Formbricks, display surveys and trigger events.",
|
||||
"homepage": "https://formbricks.com",
|
||||
"repository": {
|
||||
@@ -28,6 +28,17 @@
|
||||
"import": "./dist/website.js",
|
||||
"require": "./dist/website.cjs",
|
||||
"types": "./dist/website.d.ts"
|
||||
},
|
||||
"./*": "./dist/*"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"app": [
|
||||
"./dist/app.d.ts"
|
||||
],
|
||||
"website": [
|
||||
"./dist/website.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
@@ -35,14 +46,14 @@
|
||||
"build": "tsc && vite build",
|
||||
"build:dev": "tsc && vite build --mode dev",
|
||||
"go": "vite build --watch --mode dev",
|
||||
"lint": "eslint ./src --fix",
|
||||
"lint": "eslint . --ext .ts,.js,.tsx,.jsx",
|
||||
"clean": "rimraf .turbo node_modules dist coverage"
|
||||
},
|
||||
"author": "Formbricks <hola@formbricks.com>",
|
||||
"devDependencies": {
|
||||
"@formbricks/js-core": "workspace:*",
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"terser": "^5.31.0",
|
||||
"vite": "^5.2.11",
|
||||
"vite-plugin-dts": "^3.9.1"
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/js-library.json",
|
||||
"extends": "@formbricks/config-typescript/js-library.json",
|
||||
"include": ["src", "package.json"],
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"declaration": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["formbricks"],
|
||||
extends: ["@formbricks/eslint-config/legacy-next.js"],
|
||||
};
|
||||
|
||||
@@ -287,6 +287,7 @@ export const mockSurvey: TSurvey = {
|
||||
},
|
||||
displayOption: "displayOnce",
|
||||
recontactDays: null,
|
||||
displayLimit: null,
|
||||
autoClose: null,
|
||||
runOnDate: null,
|
||||
closeOnDate: null,
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
"tailwind-merge": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "*",
|
||||
"@formbricks/config-typescript": "*",
|
||||
"@types/jsonwebtoken": "^9.0.6",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/ungap__structured-clone": "^1.2.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"ts-node": "^10.9.2",
|
||||
"vitest": "^1.6.0",
|
||||
"vitest-mock-extended": "^1.3.1"
|
||||
|
||||
@@ -101,7 +101,7 @@ export const responseSelection = {
|
||||
},
|
||||
};
|
||||
|
||||
export const getResponsesByPersonId = (personId: string, page?: number): Promise<Array<TResponse> | null> =>
|
||||
export const getResponsesByPersonId = (personId: string, page?: number): Promise<TResponse[] | null> =>
|
||||
cache(
|
||||
async () => {
|
||||
validateInputs([personId, ZId], [page, ZOptionalNumber]);
|
||||
@@ -123,7 +123,7 @@ export const getResponsesByPersonId = (personId: string, page?: number): Promise
|
||||
throw new ResourceNotFoundError("Response from PersonId", personId);
|
||||
}
|
||||
|
||||
let responses: Array<TResponse> = [];
|
||||
let responses: TResponse[] = [];
|
||||
|
||||
await Promise.all(
|
||||
responsePrisma.map(async (response) => {
|
||||
|
||||
@@ -113,6 +113,7 @@ export const PREVIEW_SURVEY = {
|
||||
},
|
||||
displayOption: "displayOnce",
|
||||
recontactDays: null,
|
||||
displayLimit: null,
|
||||
autoClose: null,
|
||||
runOnDate: null,
|
||||
closeOnDate: null,
|
||||
|
||||
@@ -60,6 +60,7 @@ export const selectSurvey = {
|
||||
hiddenFields: true,
|
||||
displayOption: true,
|
||||
recontactDays: true,
|
||||
displayLimit: true,
|
||||
autoClose: true,
|
||||
runOnDate: true,
|
||||
closeOnDate: true,
|
||||
@@ -499,6 +500,7 @@ export const updateSurvey = async (updatedSurvey: TSurvey): Promise<TSurvey> =>
|
||||
// @ts-expect-error
|
||||
const modifiedSurvey: TSurvey = {
|
||||
...prismaSurvey, // Properties from prismaSurvey
|
||||
displayPercentage: Number(prismaSurvey.displayPercentage) || null,
|
||||
segment: surveySegment,
|
||||
};
|
||||
|
||||
@@ -851,17 +853,35 @@ export const getSyncSurveys = (
|
||||
|
||||
// filter surveys that meet the displayOption criteria
|
||||
surveys = surveys.filter((survey) => {
|
||||
if (survey.displayOption === "respondMultiple") {
|
||||
return true;
|
||||
} else if (survey.displayOption === "displayOnce") {
|
||||
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||
} else if (survey.displayOption === "displayMultiple") {
|
||||
return (
|
||||
displays.filter((display) => display.surveyId === survey.id && display.responseId !== null)
|
||||
.length === 0
|
||||
);
|
||||
} else {
|
||||
throw Error("Invalid displayOption");
|
||||
switch (survey.displayOption) {
|
||||
case "respondMultiple":
|
||||
return true;
|
||||
case "displayOnce":
|
||||
return displays.filter((display) => display.surveyId === survey.id).length === 0;
|
||||
case "displayMultiple":
|
||||
return (
|
||||
displays
|
||||
.filter((display) => display.surveyId === survey.id)
|
||||
.filter((display) => display.responseId).length === 0
|
||||
);
|
||||
case "displaySome":
|
||||
if (survey.displayLimit === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
displays
|
||||
.filter((display) => display.surveyId === survey.id)
|
||||
.some((display) => display.responseId)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
displays.filter((display) => display.surveyId === survey.id).length < survey.displayLimit
|
||||
);
|
||||
default:
|
||||
throw Error("Invalid displayOption");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ export const mockProduct: TProduct = {
|
||||
brandColor: "#000000",
|
||||
highlightBorderColor: "#000000",
|
||||
recontactDays: 0,
|
||||
displayLimit: 0,
|
||||
linkSurveyBranding: false,
|
||||
inAppSurveyBranding: false,
|
||||
placement: "bottomRight",
|
||||
@@ -169,6 +170,7 @@ const baseSurveyProperties = {
|
||||
closeOnDate: currentDate,
|
||||
redirectUrl: "http://github.com/formbricks/formbricks",
|
||||
recontactDays: 3,
|
||||
displayLimit: 3,
|
||||
welcomeCard: mockWelcomeCard,
|
||||
questions: [mockQuestion],
|
||||
thankYouCard: { enabled: false },
|
||||
|
||||
@@ -18,6 +18,7 @@ export const transformPrismaSurvey = (surveyPrisma: any): TSurvey => {
|
||||
|
||||
const transformedSurvey: TSurvey = {
|
||||
...surveyPrisma,
|
||||
displayPercentage: Number(surveyPrisma.displayPercentage) || null,
|
||||
segment,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/nextjs.json",
|
||||
"extends": "@formbricks/config-typescript/nextjs.json",
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules", "../../packages/types/surveys.d.ts"],
|
||||
"compilerOptions": {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
extends: [ "turbo", "prettier"],
|
||||
};
|
||||
extends: ["@formbricks/eslint-config/legacy-react.js"],
|
||||
parser: "@typescript-eslint/parser",
|
||||
};
|
||||
|
||||
@@ -38,12 +38,12 @@
|
||||
"devDependencies": {
|
||||
"@calcom/embed-snippet": "1.3.0",
|
||||
"@formbricks/lib": "workspace:*",
|
||||
"@formbricks/tsconfig": "workspace:*",
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
"@formbricks/types": "workspace:*",
|
||||
"@preact/preset-vite": "^2.8.2",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"concurrently": "8.2.2",
|
||||
"eslint-config-formbricks": "workspace:*",
|
||||
"@formbricks/eslint-config": "workspace:*",
|
||||
"isomorphic-dompurify": "^2.11.0",
|
||||
"postcss": "^8.4.38",
|
||||
"preact": "^10.22.0",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useMemo, useState } from "preact/hooks";
|
||||
// @ts-expect-error
|
||||
import { JSXInternal } from "preact/src/jsx";
|
||||
|
||||
import { getOriginalFileNameFromUrl } from "@formbricks/lib/storage/utils";
|
||||
@@ -101,6 +100,7 @@ export const FileInput = ({
|
||||
const handleDragOver = (e: JSXInternal.TargetedDragEvent<HTMLLabelElement>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// @ts-expect-error
|
||||
e.dataTransfer.dropEffect = "copy";
|
||||
};
|
||||
|
||||
@@ -108,6 +108,7 @@ export const FileInput = ({
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// @ts-expect-error
|
||||
handleFileSelection(e.dataTransfer.files);
|
||||
};
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ export const QuestionMedia = ({ imgUrl, videoUrl, altText = "Image" }: QuestionM
|
||||
|
||||
return (
|
||||
<div className="group/image relative mb-4 block rounded-md">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
{imgUrl && <img src={imgUrl} alt={altText} className="rounded-custom" />}
|
||||
{videoUrlWithParams && (
|
||||
<div className="relative">
|
||||
|
||||
@@ -107,7 +107,6 @@ export const WelcomeCard = ({
|
||||
<ScrollableContainer>
|
||||
<div>
|
||||
{fileUrl && (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img src={fileUrl} className="mb-8 max-h-96 w-1/3 rounded-lg object-contain" alt="Company Logo" />
|
||||
)}
|
||||
|
||||
|
||||
@@ -132,7 +132,6 @@ export const PictureSelectionQuestion = ({
|
||||
: "",
|
||||
"focus:border-brand group/image rounded-custom relative inline-block h-28 w-full cursor-pointer overflow-hidden border focus:border-4 focus:outline-none"
|
||||
)}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={choice.imageUrl}
|
||||
id={choice.id}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/js-library.json",
|
||||
"extends": "@formbricks/config-typescript/js-library.json",
|
||||
"include": ["src", "../types/surveys.d.ts"],
|
||||
"compilerOptions": {
|
||||
"allowImportingTsExtensions": true,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"clean": "rimraf node_modules .turbo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/tsconfig": "workspace:*"
|
||||
"@formbricks/config-typescript": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.23.8"
|
||||
|
||||
@@ -445,7 +445,12 @@ export const ZSurveyQuestionsObject = z.object({
|
||||
|
||||
export type TSurveyQuestionsObject = z.infer<typeof ZSurveyQuestionsObject>;
|
||||
|
||||
export const ZSurveyDisplayOption = z.enum(["displayOnce", "displayMultiple", "respondMultiple"]);
|
||||
export const ZSurveyDisplayOption = z.enum([
|
||||
"displayOnce",
|
||||
"displayMultiple",
|
||||
"respondMultiple",
|
||||
"displaySome",
|
||||
]);
|
||||
|
||||
export type TSurveyDisplayOption = z.infer<typeof ZSurveyDisplayOption>;
|
||||
|
||||
@@ -478,6 +483,7 @@ export const ZSurvey = z.object({
|
||||
triggers: z.array(z.object({ actionClass: ZActionClass })),
|
||||
redirectUrl: z.string().url().nullable(),
|
||||
recontactDays: z.number().nullable(),
|
||||
displayLimit: z.number().nullable(),
|
||||
welcomeCard: ZSurveyWelcomeCard,
|
||||
questions: ZSurveyQuestions,
|
||||
thankYouCard: ZSurveyThankYouCard,
|
||||
@@ -494,7 +500,7 @@ export const ZSurvey = z.object({
|
||||
verifyEmail: ZSurveyVerifyEmail.nullable(),
|
||||
pin: z.string().nullish(),
|
||||
resultShareKey: z.string().nullable(),
|
||||
displayPercentage: z.number().min(1).max(100).nullable(),
|
||||
displayPercentage: z.number().min(0.01).max(100).nullable(),
|
||||
languages: z.array(ZSurveyLanguage),
|
||||
});
|
||||
|
||||
@@ -521,7 +527,7 @@ export const ZSurveyInput = z.object({
|
||||
verifyEmail: ZSurveyVerifyEmail.optional(),
|
||||
pin: z.string().nullish(),
|
||||
resultShareKey: z.string().nullish(),
|
||||
displayPercentage: z.number().min(1).max(100).nullish(),
|
||||
displayPercentage: z.number().min(0.01).max(100).nullish(),
|
||||
triggers: z.array(z.object({ actionClass: ZActionClass })).optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@formbricks/tsconfig/base.json",
|
||||
"extends": "@formbricks/config-typescript/base.json",
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"],
|
||||
"compilerOptions": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user