Merge branch 'formbricks:main' into feature/docs-in-page-section-nav

This commit is contained in:
Pranoy Roy
2024-06-07 13:19:53 +05:30
committed by GitHub
108 changed files with 1254 additions and 551 deletions

View File

@@ -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
View File

@@ -0,0 +1,2 @@
node_modules/
packages/config-eslint/

View File

@@ -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/*/"],
},
},
};

View File

@@ -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

View File

@@ -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...

View File

@@ -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"

View File

@@ -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

View 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}

View File

@@ -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

View File

@@ -1,4 +1,4 @@
const baseConfig = require("./packages/prettier-config/prettier-preset");
const baseConfig = require("./packages/config-prettier/prettier-preset");
module.exports = {
...baseConfig,

View File

@@ -1,4 +1,3 @@
module.exports = {
root: true,
extends: ["formbricks"],
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -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:*"
}
}

View File

@@ -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">

View File

@@ -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"]
}

View File

@@ -1,4 +1,3 @@
module.exports = {
root: true,
extends: ["formbricks"],
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -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

View File

@@ -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.
{" "}

View File

@@ -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:*"
}
}

View File

@@ -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": {

View File

@@ -1,6 +1,6 @@
import type { Preview } from "@storybook/react";
import "../src/index.css";
import "../../web/app/globals.css";
const preview: Preview = {
parameters: {

View File

@@ -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",

View File

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

View File

@@ -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,

View File

@@ -1,4 +1,3 @@
module.exports = {
root: true,
extends: ["formbricks"],
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -14,6 +14,7 @@ export const minimalSurvey: TSurvey = {
triggers: [],
redirectUrl: null,
recontactDays: null,
displayLimit: null,
welcomeCard: {
enabled: false,
headline: { default: "Welcome!" },

View File

@@ -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 &quot;Admin&quot; role regardless of the role specified in the CSV
file.

View 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>

View File

@@ -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);

View File

@@ -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:*"
}
}

View File

@@ -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 = {

View File

@@ -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": {

View File

@@ -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,

View File

@@ -1,3 +1,4 @@
module.exports = {
extends: [ "turbo", "prettier"],
};
extends: ["@formbricks/eslint-config/legacy-library.js"],
parser: "@typescript-eslint/parser",
};

View File

@@ -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"

View File

@@ -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(), {

View File

@@ -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,

View File

@@ -0,0 +1,3 @@
# `@turbo/eslint-config`
Collection of internal eslint configurations.

View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ["turbo", "prettier"],
};

View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ["turbo", "prettier", "plugin:react-hooks/recommended"],
};

View 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/"],
};

View 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",
},
};

View 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
View 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,
},
},
],
};

View File

@@ -1,5 +1,5 @@
{
"name": "@formbricks/prettier-config",
"name": "@formbricks/config-prettier",
"version": "1.0.0",
"private": true,
"license": "MIT",

View File

@@ -1,5 +1,5 @@
{
"name": "@formbricks/tailwind-config",
"name": "@formbricks/config-tailwind",
"version": "0.0.0",
"private": true,
"main": "index.js",

View File

@@ -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: {

View File

@@ -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,

View File

@@ -1,5 +1,5 @@
{
"name": "@formbricks/tsconfig",
"name": "@formbricks/config-typescript",
"version": "1.0.0",
"private": true,
"sideEffects": false,

View File

@@ -1,4 +1,3 @@
module.exports = {
root: true,
extends: ["formbricks"],
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Survey" ALTER COLUMN "displayPercentage" SET DATA TYPE DECIMAL(65,30);

View File

@@ -0,0 +1,5 @@
-- AlterEnum
ALTER TYPE "displayOptions" ADD VALUE 'displaySome';
-- AlterTable
ALTER TABLE "Survey" ADD COLUMN "displayLimit" INTEGER;

View File

@@ -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",

View File

@@ -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])

View File

@@ -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
View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -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";
}

View File

@@ -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"
}
}

View File

@@ -1,5 +1,5 @@
{
"extends": "@formbricks/tsconfig/react-library.json",
"extends": "@formbricks/config-typescript/react-library.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {

View File

@@ -0,0 +1,3 @@
module.exports = {
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -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;

View File

@@ -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"
}
}

View File

@@ -1,3 +1,4 @@
module.exports = {
extends: ["turbo", "prettier"],
extends: ["@formbricks/eslint-config/legacy-library.js"],
parser: "@typescript-eslint/parser",
};

View File

@@ -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"

View File

@@ -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;
};

View File

@@ -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");
}
});

View File

@@ -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;
};

View File

@@ -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

View File

@@ -1,3 +1,4 @@
module.exports = {
extends: ["turbo", "prettier"],
extends: ["@formbricks/eslint-config/legacy-library.js"],
parser: "@typescript-eslint/parser",
};

View File

@@ -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"

View File

@@ -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,

View File

@@ -1,4 +1,3 @@
module.exports = {
root: true,
extends: ["formbricks"],
extends: ["@formbricks/eslint-config/legacy-next.js"],
};

View File

@@ -287,6 +287,7 @@ export const mockSurvey: TSurvey = {
},
displayOption: "displayOnce",
recontactDays: null,
displayLimit: null,
autoClose: null,
runOnDate: null,
closeOnDate: null,

View File

@@ -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"

View File

@@ -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) => {

View File

@@ -113,6 +113,7 @@ export const PREVIEW_SURVEY = {
},
displayOption: "displayOnce",
recontactDays: null,
displayLimit: null,
autoClose: null,
runOnDate: null,
closeOnDate: null,

View File

@@ -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");
}
});

View File

@@ -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 },

View File

@@ -18,6 +18,7 @@ export const transformPrismaSurvey = (surveyPrisma: any): TSurvey => {
const transformedSurvey: TSurvey = {
...surveyPrisma,
displayPercentage: Number(surveyPrisma.displayPercentage) || null,
segment,
};

View File

@@ -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": {

View File

@@ -1,3 +1,4 @@
module.exports = {
extends: [ "turbo", "prettier"],
};
extends: ["@formbricks/eslint-config/legacy-react.js"],
parser: "@typescript-eslint/parser",
};

View File

@@ -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",

View File

@@ -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);
};

View File

@@ -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">

View File

@@ -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" />
)}

View File

@@ -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}

View File

@@ -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,

View File

@@ -8,7 +8,7 @@
"clean": "rimraf node_modules .turbo"
},
"devDependencies": {
"@formbricks/tsconfig": "workspace:*"
"@formbricks/config-typescript": "workspace:*"
},
"dependencies": {
"zod": "^3.23.8"

View File

@@ -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(),
});

View File

@@ -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