+
{IMPRINT_URL && (
-
+
Imprint
)}
{IMPRINT_URL && PRIVACY_URL && | }
{PRIVACY_URL && (
-
+
Privacy Policy
)}
@@ -34,7 +34,8 @@ export const LegalFooter = ({
+ className="hover:underline"
+ tabIndex={-1}>
Report Survey
)}
diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts
index c3ed0f4893..cdb7fd0011 100644
--- a/apps/web/middleware.ts
+++ b/apps/web/middleware.ts
@@ -20,8 +20,8 @@ import { RATE_LIMITING_DISABLED, WEBAPP_URL } from "@formbricks/lib/constants";
import { isValidCallbackUrl } from "@formbricks/lib/utils/url";
export const middleware = async (request: NextRequest) => {
- // issue with next auth types & Next 15; let's review when new fixes are available
- const token = await getToken({ req: request });
+ // issue with next auth types; let's review when new fixes are available
+ const token = await getToken({ req: request as any });
if (isAuthProtectedRoute(request.nextUrl.pathname) && !token) {
const loginUrl = `${WEBAPP_URL}/auth/login?callbackUrl=${encodeURIComponent(WEBAPP_URL + request.nextUrl.pathname + request.nextUrl.search)}`;
diff --git a/apps/web/next-env.d.ts b/apps/web/next-env.d.ts
index 4f11a03dc6..40c3d68096 100644
--- a/apps/web/next-env.d.ts
+++ b/apps/web/next-env.d.ts
@@ -2,4 +2,4 @@
///
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/basic-features/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index 5545d63325..d472d502e7 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -163,6 +163,74 @@ const nextConfig = {
},
],
},
+ {
+ source: "/js/(.*)",
+ headers: [
+ {
+ key: "Cache-Control",
+ value: "public, max-age=3600, s-maxage=604800, stale-while-revalidate=3600, stale-if-error=3600",
+ },
+ {
+ key: "Content-Type",
+ value: "application/javascript; charset=UTF-8",
+ },
+ {
+ key: "Access-Control-Allow-Origin",
+ value: "*",
+ },
+ ],
+ },
+
+ // headers for /api/packages/(.*) -- the api route does not exist, but we still need the headers for the rewrites to work correctly!
+ {
+ source: "/api/packages/(.*)",
+ headers: [
+ {
+ key: "Cache-Control",
+ value: "public, max-age=3600, s-maxage=604800, stale-while-revalidate=3600, stale-if-error=3600",
+ },
+ {
+ key: "Content-Type",
+ value: "application/javascript; charset=UTF-8",
+ },
+ {
+ key: "Access-Control-Allow-Origin",
+ value: "*",
+ },
+ ],
+ },
+ ];
+ },
+ async rewrites() {
+ return [
+ {
+ source: "/api/packages/website",
+ destination: "/js/formbricks.umd.cjs",
+ },
+ {
+ source: "/api/packages/app",
+ destination: "/js/formbricks.umd.cjs",
+ },
+ {
+ source: "/api/packages/js",
+ destination: "/js/formbricks.umd.cjs",
+ },
+ {
+ source: "/api/packages/surveys",
+ destination: "/js/surveys.umd.cjs",
+ },
+ {
+ source: "/api/v1/client/:environmentId/website/environment",
+ destination: "/api/v1/client/:environmentId/environment",
+ },
+ {
+ source: "/api/v1/client/:environmentId/app/environment",
+ destination: "/api/v1/client/:environmentId/environment",
+ },
+ {
+ source: "/api/v1/client/:environmentId/app/people/:userId",
+ destination: "/api/v1/client/:environmentId/identify/people/:userId",
+ },
];
},
env: {
diff --git a/apps/web/package.json b/apps/web/package.json
index 81e60a8d07..8ee64517af 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,21 +1,21 @@
{
"name": "@formbricks/web",
- "version": "2.5.3",
+ "version": "2.6.0",
"private": true,
"scripts": {
"clean": "rimraf .turbo node_modules .next",
- "dev": "next dev -p 3000",
- "go": "next dev -p 3000",
+ "dev": "next dev -p 3000 --turbo",
+ "go": "next dev -p 3000 --turbo",
"build": "next build",
"build:dev": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
- "@dnd-kit/core": "^6.1.0",
- "@dnd-kit/modifiers": "^7.0.0",
- "@dnd-kit/sortable": "^8.0.0",
- "@dnd-kit/utilities": "^3.2.2",
+ "@dnd-kit/core": "6.1.0",
+ "@dnd-kit/modifiers": "7.0.0",
+ "@dnd-kit/sortable": "8.0.0",
+ "@dnd-kit/utilities": "3.2.2",
"@formbricks/api": "workspace:*",
"@formbricks/database": "workspace:*",
"@formbricks/ee": "workspace:*",
@@ -26,52 +26,53 @@
"@formbricks/surveys": "workspace:*",
"@formbricks/types": "workspace:*",
"@formbricks/ui": "workspace:*",
- "@hookform/resolvers": "^3.9.0",
- "@json2csv/node": "^7.0.6",
- "@paralleldrive/cuid2": "^2.2.2",
- "@radix-ui/react-collapsible": "^1.1.0",
- "@react-email/components": "^0.0.22",
- "@sentry/nextjs": "^8.26.0",
- "@tanstack/react-table": "^8.20.1",
- "@vercel/og": "^0.6.2",
- "@vercel/speed-insights": "^1.0.12",
- "bcryptjs": "^2.4.3",
- "dotenv": "^16.4.5",
- "encoding": "^0.1.13",
- "file-loader": "^6.2.0",
- "framer-motion": "11.3.28",
- "googleapis": "^140.0.1",
- "jiti": "^1.21.6",
- "jsonwebtoken": "^9.0.2",
- "lodash": "^4.17.21",
- "lru-cache": "^11.0.0",
- "lucide-react": "^0.427.0",
- "mime": "^4.0.4",
- "next": "14.2.5",
- "next-safe-action": "^7.6.2",
- "optional": "^0.1.4",
- "otplib": "^12.0.1",
- "papaparse": "^5.4.1",
- "posthog-js": "^1.155.4",
- "prismjs": "^1.29.0",
+ "@hookform/resolvers": "3.9.0",
+ "@json2csv/node": "7.0.6",
+ "@paralleldrive/cuid2": "2.2.2",
+ "@radix-ui/react-collapsible": "1.1.1",
+ "@react-email/components": "0.0.25",
+ "@sentry/nextjs": "8.34.0",
+ "@tanstack/react-table": "8.20.5",
+ "@vercel/og": "0.6.3",
+ "@vercel/speed-insights": "1.0.12",
+ "bcryptjs": "2.4.3",
+ "dotenv": "16.4.5",
+ "encoding": "0.1.13",
+ "file-loader": "6.2.0",
+ "framer-motion": "11.11.8",
+ "googleapis": "144.0.0",
+ "jiti": "2.3.3",
+ "jsonwebtoken": "9.0.2",
+ "lodash": "4.17.21",
+ "lru-cache": "11.0.1",
+ "lucide-react": "0.452.0",
+ "mime": "4.0.4",
+ "next": "14.2.15",
+ "next-safe-action": "7.9.3",
+ "optional": "0.1.4",
+ "otplib": "12.0.1",
+ "papaparse": "5.4.1",
+ "posthog-js": "1.167.0",
+ "prismjs": "1.29.0",
"react": "18.3.1",
"react-dom": "18.3.1",
- "react-hook-form": "^7.52.2",
- "react-hot-toast": "^2.4.1",
- "redis": "^4.7.0",
- "sharp": "^0.33.4",
- "ua-parser-js": "^1.0.38",
- "webpack": "^5.93.0",
- "xlsx": "^0.18.5"
+ "react-hook-form": "7.53.0",
+ "react-hot-toast": "2.4.1",
+ "react-icons": "5.3.0",
+ "redis": "4.7.0",
+ "sharp": "0.33.5",
+ "ua-parser-js": "1.0.39",
+ "webpack": "5.95.0",
+ "xlsx": "0.18.5"
},
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*",
- "@neshca/cache-handler": "^1.5.1",
- "@types/bcryptjs": "^2.4.6",
- "@types/lodash": "^4.17.7",
- "@types/markdown-it": "^14.1.2",
- "@types/papaparse": "^5.3.14",
- "@types/qrcode": "^1.5.5"
+ "@neshca/cache-handler": "1.7.4",
+ "@types/bcryptjs": "2.4.6",
+ "@types/lodash": "4.17.10",
+ "@types/markdown-it": "14.1.2",
+ "@types/papaparse": "5.3.14",
+ "@types/qrcode": "1.5.5"
}
}
diff --git a/apps/web/playwright/js.spec.ts b/apps/web/playwright/js.spec.ts
index a23a19135c..4f83234cdd 100644
--- a/apps/web/playwright/js.spec.ts
+++ b/apps/web/playwright/js.spec.ts
@@ -59,7 +59,7 @@ test.describe("JS Package Test", async () => {
// Formbricks In App Sync has happened
const syncApi = await page.waitForResponse(
(response) => {
- return response.url().includes("/app/environment");
+ return response.url().includes("/environment");
},
{
timeout: 120000,
diff --git a/apps/web/playwright/organization.spec.ts b/apps/web/playwright/organization.spec.ts
index d402190942..6d65223a33 100644
--- a/apps/web/playwright/organization.spec.ts
+++ b/apps/web/playwright/organization.spec.ts
@@ -29,8 +29,8 @@ test.describe("Invite, accept and remove organization member", async () => {
await page.locator('[data-testid="members-loading-card"]:first-child').waitFor({ state: "hidden" });
// Add member button
- await expect(page.getByRole("button", { name: "Add Member" })).toBeVisible();
- await page.getByRole("button", { name: "Add Member" }).click();
+ await expect(page.getByRole("button", { name: "Add member" })).toBeVisible();
+ await page.getByRole("button", { name: "Add member" }).click();
// Fill the member name and email form
await expect(page.getByLabel("Email")).toBeVisible();
diff --git a/apps/web/playwright/survey.spec.ts b/apps/web/playwright/survey.spec.ts
index c9010e0ee1..990c03faa6 100644
--- a/apps/web/playwright/survey.spec.ts
+++ b/apps/web/playwright/survey.spec.ts
@@ -247,47 +247,47 @@ test.describe("Multi Language Survey Create", async () => {
await page.getByText("Welcome CardShownOn").click();
// Add questions in default language
- await page.getByText("Add Question").click();
+ await page.getByText("Add question").click();
await page.getByRole("button", { name: "Single-Select" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Multi-Select" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Picture Selection" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Rating" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Net Promoter Score (NPS)" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Date" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "File Upload" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
@@ -295,13 +295,13 @@ test.describe("Multi Language Survey Create", async () => {
await page.getByRole("button", { name: "Matrix" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Address" }).click();
await page
.locator("div")
- .filter({ hasText: /^Add QuestionAdd a new question to your survey$/ })
+ .filter({ hasText: /^Add questionAdd a new question to your survey$/ })
.nth(1)
.click();
await page.getByRole("button", { name: "Ranking" }).click();
diff --git a/apps/web/playwright/utils/helper.ts b/apps/web/playwright/utils/helper.ts
index 07b31115ee..5f30eb5315 100644
--- a/apps/web/playwright/utils/helper.ts
+++ b/apps/web/playwright/utils/helper.ts
@@ -136,7 +136,7 @@ export const signupUsingInviteToken = async (page: Page, name: string, email: st
};
export const createSurvey = async (page: Page, params: CreateSurveyParams) => {
- const addQuestion = "Add QuestionAdd a new question to your survey";
+ const addQuestion = "Add questionAdd a new question to your survey";
await page.getByRole("button", { name: "Start from scratch Create a" }).click();
await page.getByRole("button", { name: "Create survey", exact: true }).click();
@@ -324,7 +324,7 @@ export const createSurvey = async (page: Page, params: CreateSurveyParams) => {
};
export const createSurveyWithLogic = async (page: Page, params: CreateSurveyWithLogicParams) => {
- const addQuestion = "Add QuestionAdd a new question to your survey";
+ const addQuestion = "Add questionAdd a new question to your survey";
await page.getByRole("button", { name: "Start from scratch Create a" }).click();
await page.getByRole("button", { name: "Create survey", exact: true }).click();
diff --git a/apps/web/images/tooltips/change-survey-type-app.mp4 b/apps/web/public/video/tooltips/change-survey-type-app.mp4
similarity index 100%
rename from apps/web/images/tooltips/change-survey-type-app.mp4
rename to apps/web/public/video/tooltips/change-survey-type-app.mp4
diff --git a/apps/web/images/tooltips/change-survey-type.mp4 b/apps/web/public/video/tooltips/change-survey-type.mp4
similarity index 100%
rename from apps/web/images/tooltips/change-survey-type.mp4
rename to apps/web/public/video/tooltips/change-survey-type.mp4
diff --git a/oss-gg/formbricks-quests/1-meme-magic.md b/oss-gg/formbricks-quests/1-meme-magic.md
deleted file mode 100644
index a390a04d94..0000000000
--- a/oss-gg/formbricks-quests/1-meme-magic.md
+++ /dev/null
@@ -1,23 +0,0 @@
-**Side Quest**: Meme Magic - Craft a meme where a brick plays a role. Tweet it, and tag us @formbricks to submit.
-**Points**: 150 Points
-**Proof**: Add a screenshot of meme to the PR description. Add a link to your tweet in the list below.
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR NAME
-Âť Link to Tweet: https://x.com/...
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by YOUR NAME
-Âť Link to Tweet: https://x.com/...
-
----
diff --git a/oss-gg/formbricks-quests/2-gif-magic.md b/oss-gg/formbricks-quests/2-gif-magic.md
deleted file mode 100644
index 3ff30f1c83..0000000000
--- a/oss-gg/formbricks-quests/2-gif-magic.md
+++ /dev/null
@@ -1,23 +0,0 @@
-**Side Quest**: GIF Magic - Craft a GIF where a brick plays a role. Upload it to GIPHY with tags 'open source', 'foss', 'formbricks'.
-**Points**: 150 Points
-**Proof**: Add a screenshot of GIF on Giphy to the PR description. Add a link to your GIPHY in the list below.
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR NAME
-Âť Link to Tweet: https://giphy.com/...
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by YOUR NAME
-Âť Link to Tweet: https://x.com/...
-
----
diff --git a/oss-gg/formbricks-quests/3-follow-linkedin.md b/oss-gg/formbricks-quests/3-follow-linkedin.md
deleted file mode 100644
index 9f167f21e1..0000000000
--- a/oss-gg/formbricks-quests/3-follow-linkedin.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Follow us on LinkedIn
-**Points**: 50 Points
-**Proof**: Add a screenshot of you following our LinkedIn profile to the PR description. Add your name to the list below.
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR NAME
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by YOUR NAME
-
----
diff --git a/oss-gg/formbricks-quests/4-starry-eyed-supporter.md b/oss-gg/formbricks-quests/4-starry-eyed-supporter.md
deleted file mode 100644
index 2362694413..0000000000
--- a/oss-gg/formbricks-quests/4-starry-eyed-supporter.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Starry-eyed Supporter - Get five friends to star our repository.
-**Points**: 150 Points
-**Proof**: Screenshots to prove that you asked them and they confirmed + their GitHub names
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR GITHUB HANDLE
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by X
-
----
diff --git a/oss-gg/formbricks-quests/5-brickfy-someone-famous.md b/oss-gg/formbricks-quests/5-brickfy-someone-famous.md
deleted file mode 100644
index cc318a0f9d..0000000000
--- a/oss-gg/formbricks-quests/5-brickfy-someone-famous.md
+++ /dev/null
@@ -1,23 +0,0 @@
-**Side Quest**: Brickify someone famous with AI
-**Points**: 150 Points
-**Proof**: Add link to your tweet with the brickified image to the list below.
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR GITHUB HANDLE
-Âť Link to Tweet: https://x.com/...
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by X
-Âť Link to Tweet: https://x.com/...
-
----
diff --git a/oss-gg/formbricks-quests/6-secret-side-quests.md b/oss-gg/formbricks-quests/6-secret-side-quests.md
deleted file mode 100644
index e954afea89..0000000000
--- a/oss-gg/formbricks-quests/6-secret-side-quests.md
+++ /dev/null
@@ -1,19 +0,0 @@
-**Side Quest**: Completed secret side quests
-**Points**: 150-300 Points
-**Proof**: Add your name to the list
-
-Please follow the following schema:
-
----
-
-## Âť 05-April-2024 by YOUR NAME
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by X
-
----
diff --git a/oss-gg/formbricks-quests/7-quest-wizard.md b/oss-gg/formbricks-quests/7-quest-wizard.md
deleted file mode 100644
index 0709409033..0000000000
--- a/oss-gg/formbricks-quests/7-quest-wizard.md
+++ /dev/null
@@ -1,19 +0,0 @@
-**Side Quest**: Complete all Formbricks side quests
-**Points**: 300 Points
-**Proof**: Add screenshots for each side quest to the PR description. Add your name to the list below.
-
-Please follow the following schema:
-
----
-
-## Âť 05-April-2024 by YOUR NAME
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by X
-
----
diff --git a/oss-gg/oss-gg-quests/1-retweet-launch-tweet.md b/oss-gg/oss-gg-quests/1-retweet-launch-tweet.md
deleted file mode 100644
index ac6365d243..0000000000
--- a/oss-gg/oss-gg-quests/1-retweet-launch-tweet.md
+++ /dev/null
@@ -1,31 +0,0 @@
-**Side Quest**: Like & Re-Tweet oss.gg Launch Tweet
-**Points**: 50 Points
-**Proof**: Add a screenshot of your retweet and liking the tweet to the PR description. Add your handle to the list below.
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR TWITTER HANDLE
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
----
-
-Âť 02-October-2024 by [@Jemeni11\_](https://x.com/Jemeni11_)
-
----
-
-Âť 03-October-2024 by [@adityadeshlahre](https://x.com/adityadeshlahre/)
-Âť 03-October-2024 by [@HarshBhatX](https://x.com/HarshBhatX/status/HarshBhatX)
----
-
-Âť 03-October-2024 by [@Ionfinisher](https://x.com/ion_finisher/)
-
----
diff --git a/oss-gg/oss-gg-quests/2-engage-on-reddit.md b/oss-gg/oss-gg-quests/2-engage-on-reddit.md
deleted file mode 100644
index 463878791b..0000000000
--- a/oss-gg/oss-gg-quests/2-engage-on-reddit.md
+++ /dev/null
@@ -1,21 +0,0 @@
-**Side Quest**: Upvote and comment on Reddit launch thread
-**Points**: 50 Points
-**Proof**: Add a screenshot of your upvote and comment in the PR description. Add your handle to the list below.
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR REDDIT HANDLE
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 01-October-2024 by X
-Âť 03-October-2024 by [@Recent-Highlight-449](https://www.reddit.com/user/Recent-Highlight-449/)
----
diff --git a/oss-gg/oss-gg-quests/3-spread-the-word.md b/oss-gg/oss-gg-quests/3-spread-the-word.md
deleted file mode 100644
index ee984ab9b6..0000000000
--- a/oss-gg/oss-gg-quests/3-spread-the-word.md
+++ /dev/null
@@ -1,28 +0,0 @@
-**Side Quest**: Spread the word
-**Points**: 50-150 Points
-**Proof**: Add a screenshots of your comments in the PR description. Add links to the content in the list below
-
-Please follow the following schema:
-
----
-
-Âť 05-April-2024 by YOUR NAME
-Âť Link 1: https://link.to/content
-Âť Link 2: https://link.to/content
-Âť Link 3: https://link.to/content
-
----
-
-////////////////////////////
-
-Your turn đ
-
-////////////////////////////
-
-Âť 03-Octobet-2024 by Harsh Bhat
-Âť Link 1: https://youtu.be/pcC4Dr6Wj2Q?si=j1Rftb57M0V6vfLS
-Âť Link 2: https://youtu.be/NQi1CdGo6dU?si=Dhe_sVaT27MOrYVj
-Âť Link 3: https://youtu.be/hI4RksyFE2k?si=tgREw0wQ_tirD41O
-
-
----
diff --git a/package.json b/package.json
index 2f3b690b01..2d5b25531d 100644
--- a/package.json
+++ b/package.json
@@ -32,14 +32,14 @@
"storybook": "turbo run storybook"
},
"devDependencies": {
- "@playwright/test": "^1.45.3",
+ "@playwright/test": "1.45.3",
"@formbricks/eslint-config": "workspace:*",
- "eslint": "^8.57.0",
- "husky": "^9.1.4",
- "lint-staged": "^15.2.7",
- "rimraf": "^6.0.1",
- "tsx": "^4.16.5",
- "turbo": "^2.0.11"
+ "eslint": "8.57.0",
+ "husky": "9.1.4",
+ "lint-staged": "15.2.7",
+ "rimraf": "6.0.1",
+ "tsx": "4.16.5",
+ "turbo": "2.0.11"
},
"lint-staged": {
"(apps|packages)/**/*.{js,ts,jsx,tsx}": [
@@ -64,7 +64,7 @@
"showDetails": true
},
"dependencies": {
- "@changesets/cli": "^2.27.7",
- "playwright": "^1.45.3"
+ "@changesets/cli": "2.27.7",
+ "playwright": "1.45.3"
}
}
diff --git a/packages/api/package.json b/packages/api/package.json
index e88fe0b366..eb470634ed 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -37,11 +37,11 @@
"@formbricks/config-typescript": "workspace:*",
"@formbricks/eslint-config": "workspace:*",
"@formbricks/types": "workspace:*",
- "@rollup/plugin-inject": "^5.0.5",
- "buffer": "^6.0.3",
- "terser": "^5.31.6",
- "vite": "^5.4.1",
- "vite-plugin-dts": "^3.9.1",
- "vite-plugin-node-polyfills": "^0.22.0"
+ "@rollup/plugin-inject": "5.0.5",
+ "buffer": "6.0.3",
+ "terser": "5.31.6",
+ "vite": "5.4.8",
+ "vite-plugin-dts": "3.9.1",
+ "vite-plugin-node-polyfills": "0.22.0"
}
}
diff --git a/packages/api/src/api/client/storage.ts b/packages/api/src/api/client/storage.ts
index 2bc8f7f39c..c660123e38 100644
--- a/packages/api/src/api/client/storage.ts
+++ b/packages/api/src/api/client/storage.ts
@@ -1,5 +1,4 @@
/* eslint-disable no-console -- used for error logging */
-import { Buffer } from "node:buffer";
import type { TUploadFileConfig, TUploadFileResponse } from "@formbricks/types/storage";
export class StorageAPI {
@@ -67,18 +66,18 @@ export class StorageAPI {
const formDataForS3 = new FormData();
if (presignedFields) {
- Object.keys(presignedFields).forEach((key) => {
- formDataForS3.append(key, presignedFields[key]);
+ Object.entries(presignedFields).forEach(([key, value]) => {
+ formDataForS3.append(key, value);
});
try {
- const buffer = Buffer.from(file.base64.split(",")[1], "base64");
- const blob = new Blob([buffer], { type: file.type });
+ const binaryString = atob(file.base64.split(",")[1]);
+ const uint8Array = Uint8Array.from([...binaryString].map((char) => char.charCodeAt(0)));
+ const blob = new Blob([uint8Array], { type: file.type });
formDataForS3.append("file", blob);
- } catch (buffErr) {
- console.error({ buffErr });
-
+ } catch (err) {
+ console.error(err);
throw new Error("Error uploading file");
}
}
diff --git a/packages/api/vite.config.js b/packages/api/vite.config.js
index cb325b77ef..6ca4f9a1bb 100644
--- a/packages/api/vite.config.js
+++ b/packages/api/vite.config.js
@@ -1,7 +1,6 @@
import { resolve } from "path";
import { defineConfig } from "vite";
import dts from "vite-plugin-dts";
-import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
build: {
@@ -17,13 +16,5 @@ export default defineConfig({
fileName: "index",
},
},
- plugins: [
- dts({ rollupTypes: true }),
- nodePolyfills({
- include: ["buffer"],
- globals: {
- Buffer: true,
- },
- }),
- ],
+ plugins: [dts({ rollupTypes: true })],
});
diff --git a/packages/config-eslint/package.json b/packages/config-eslint/package.json
index 1c77f5e994..07ab1c4b81 100644
--- a/packages/config-eslint/package.json
+++ b/packages/config-eslint/package.json
@@ -3,15 +3,15 @@
"version": "0.0.0",
"private": true,
"devDependencies": {
- "@next/eslint-plugin-next": "^14.2.5",
- "@typescript-eslint/eslint-plugin": "^8.0.0",
- "@typescript-eslint/parser": "^8.0.0",
- "@vercel/style-guide": "^6.0.0",
- "eslint-config-next": "^14.2.5",
- "eslint-config-prettier": "^9.1.0",
- "eslint-config-turbo": "^2.0.14",
+ "@next/eslint-plugin-next": "14.2.5",
+ "@typescript-eslint/eslint-plugin": "8.0.0",
+ "@typescript-eslint/parser": "8.0.0",
+ "@vercel/style-guide": "6.0.0",
+ "eslint-config-next": "14.2.5",
+ "eslint-config-prettier": "9.1.0",
+ "eslint-config-turbo": "2.0.14",
"eslint-plugin-react": "7.35.0",
- "eslint-plugin-react-hooks": "^4.6.2",
- "eslint-plugin-react-refresh": "^0.4.9"
+ "eslint-plugin-react-hooks": "4.6.2",
+ "eslint-plugin-react-refresh": "0.4.9"
}
}
diff --git a/packages/config-prettier/package.json b/packages/config-prettier/package.json
index f915b37639..ba673dc4aa 100644
--- a/packages/config-prettier/package.json
+++ b/packages/config-prettier/package.json
@@ -7,8 +7,8 @@
"clean": "rimraf node_modules .turbo"
},
"devDependencies": {
- "@trivago/prettier-plugin-sort-imports": "^4.3.0",
- "prettier": "^3.3.3",
- "prettier-plugin-tailwindcss": "^0.6.6"
+ "@trivago/prettier-plugin-sort-imports": "4.3.0",
+ "prettier": "3.3.3",
+ "prettier-plugin-tailwindcss": "0.6.6"
}
}
diff --git a/packages/config-typescript/package.json b/packages/config-typescript/package.json
index a7f4029b35..da9e34afb3 100644
--- a/packages/config-typescript/package.json
+++ b/packages/config-typescript/package.json
@@ -8,7 +8,7 @@
},
"devDependencies": {
"@types/node": "22.3.0",
- "@types/react": "18.3.3",
+ "@types/react": "18.3.11",
"@types/react-dom": "18.3.0",
"typescript": "5.4.5"
}
diff --git a/packages/database/data-migrations/20241002123456_migrate_survey_types/data-migration.ts b/packages/database/data-migrations/20241002123456_migrate_survey_types/data-migration.ts
new file mode 100644
index 0000000000..8d6800e790
--- /dev/null
+++ b/packages/database/data-migrations/20241002123456_migrate_survey_types/data-migration.ts
@@ -0,0 +1,75 @@
+/* eslint-disable no-console -- logging is allowed in migration scripts */
+import { PrismaClient } from "@prisma/client";
+
+const prisma = new PrismaClient();
+const TRANSACTION_TIMEOUT = 30 * 60 * 1000; // 30 minutes in milliseconds
+
+async function runMigration(): Promise {
+ const startTime = Date.now();
+ console.log("Starting data migration...");
+
+ await prisma.$transaction(
+ async (transactionPrisma) => {
+ const websiteSurveys = await transactionPrisma.survey.findMany({
+ where: { type: "website" },
+ });
+
+ const updationPromises = [];
+
+ for (const websiteSurvey of websiteSurveys) {
+ updationPromises.push(
+ transactionPrisma.survey.update({
+ where: { id: websiteSurvey.id },
+ data: {
+ type: "app",
+ segment: {
+ connectOrCreate: {
+ where: {
+ environmentId_title: {
+ environmentId: websiteSurvey.environmentId,
+ title: websiteSurvey.id,
+ },
+ },
+ create: {
+ title: websiteSurvey.id,
+ isPrivate: true,
+ environmentId: websiteSurvey.environmentId,
+ },
+ },
+ },
+ },
+ })
+ );
+ }
+
+ await Promise.all(updationPromises);
+ console.log(`Updated ${websiteSurveys.length.toString()} website surveys to app surveys`);
+ },
+ {
+ timeout: TRANSACTION_TIMEOUT,
+ }
+ );
+
+ const endTime = Date.now();
+ console.log(`Data migration completed. Total time: ${((endTime - startTime) / 1000).toFixed(2)}s`);
+}
+
+function handleError(error: unknown): void {
+ console.error("An error occurred during migration:", error);
+ process.exit(1);
+}
+
+function handleDisconnectError(): void {
+ console.error("Failed to disconnect Prisma client");
+ process.exit(1);
+}
+
+function main(): void {
+ runMigration()
+ .catch(handleError)
+ .finally(() => {
+ prisma.$disconnect().catch(handleDisconnectError);
+ });
+}
+
+main();
diff --git a/packages/database/migrations/20241004070040_removed_website_setup_completed/migration.sql b/packages/database/migrations/20241004070040_removed_website_setup_completed/migration.sql
new file mode 100644
index 0000000000..45812adf63
--- /dev/null
+++ b/packages/database/migrations/20241004070040_removed_website_setup_completed/migration.sql
@@ -0,0 +1,8 @@
+/*
+ Warnings:
+
+ - You are about to drop the column `websiteSetupCompleted` on the `Environment` table. All the data in the column will be lost.
+
+*/
+-- AlterTable
+ALTER TABLE "Environment" DROP COLUMN "websiteSetupCompleted";
diff --git a/packages/database/package.json b/packages/database/package.json
index 75e628477c..ab164a8d35 100644
--- a/packages/database/package.json
+++ b/packages/database/package.json
@@ -51,23 +51,25 @@
"data-migration:add-display-id-to-response": "ts-node ./data-migrations/20240905120500_refactor_display_response_relationship/data-migration.ts",
"data-migration:address-question": "ts-node ./data-migrations/20240924123456_migrate_address_question/data-migration.ts",
"data-migration:advanced-logic": "ts-node ./data-migrations/20240828122408_advanced_logic_editor/data-migration.ts",
- "data-migration:segments-actions-cleanup": "ts-node ./data-migrations/20240904091113_removed_actions_table/data-migration.ts"
+ "data-migration:segments-actions-cleanup": "ts-node ./data-migrations/20240904091113_removed_actions_table/data-migration.ts",
+ "data-migration:migrate-survey-types": "ts-node ./data-migrations/20241002123456_migrate_survey_types/data-migration.ts",
+ "data-migration:v2.6": "pnpm data-migration:add-display-id-to-response && pnpm data-migration:address-question && pnpm data-migration:advanced-logic && pnpm data-migration:segments-actions-cleanup && pnpm data-migration:migrate-survey-types"
},
"dependencies": {
- "@prisma/client": "^5.18.0",
- "@prisma/extension-accelerate": "^1.1.0",
- "dotenv-cli": "^7.4.2"
+ "@prisma/client": "5.20.0",
+ "@prisma/extension-accelerate": "1.2.1",
+ "dotenv-cli": "7.4.2"
},
"devDependencies": {
"@formbricks/config-typescript": "workspace:*",
"@formbricks/types": "workspace:*",
- "@paralleldrive/cuid2": "^2.2.2",
+ "@paralleldrive/cuid2": "2.2.2",
"@formbricks/eslint-config": "workspace:*",
- "prisma": "^5.18.0",
- "prisma-dbml-generator": "^0.12.0",
- "prisma-json-types-generator": "^3.0.4",
- "ts-node": "^10.9.2",
- "zod": "^3.23.8",
- "zod-prisma": "^0.5.4"
+ "prisma": "5.20.0",
+ "prisma-dbml-generator": "0.12.0",
+ "prisma-json-types-generator": "3.1.1",
+ "ts-node": "10.9.2",
+ "zod": "3.23.8",
+ "zod-prisma": "0.5.4"
}
}
diff --git a/packages/database/schema.prisma b/packages/database/schema.prisma
index d7ed320930..3342dca9ed 100644
--- a/packages/database/schema.prisma
+++ b/packages/database/schema.prisma
@@ -386,24 +386,23 @@ model Integration {
}
model Environment {
- id String @id @default(cuid())
- createdAt DateTime @default(now()) @map(name: "created_at")
- updatedAt DateTime @updatedAt @map(name: "updated_at")
- type EnvironmentType
- product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
- productId String
- widgetSetupCompleted Boolean @default(false)
- appSetupCompleted Boolean @default(false)
- websiteSetupCompleted Boolean @default(false)
- surveys Survey[]
- people Person[]
- actionClasses ActionClass[]
- attributeClasses AttributeClass[]
- apiKeys ApiKey[]
- webhooks Webhook[]
- tags Tag[]
- segments Segment[]
- integration Integration[]
+ id String @id @default(cuid())
+ createdAt DateTime @default(now()) @map(name: "created_at")
+ updatedAt DateTime @updatedAt @map(name: "updated_at")
+ type EnvironmentType
+ product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
+ productId String
+ widgetSetupCompleted Boolean @default(false)
+ appSetupCompleted Boolean @default(false)
+ surveys Survey[]
+ people Person[]
+ actionClasses ActionClass[]
+ attributeClasses AttributeClass[]
+ apiKeys ApiKey[]
+ webhooks Webhook[]
+ tags Tag[]
+ segments Segment[]
+ integration Integration[]
@@index([productId])
}
diff --git a/packages/ee/advanced-targeting/components/advanced-targeting-card.tsx b/packages/ee/advanced-targeting/components/advanced-targeting-card.tsx
index 839b8d6fff..c09cf5a1d1 100644
--- a/packages/ee/advanced-targeting/components/advanced-targeting-card.tsx
+++ b/packages/ee/advanced-targeting/components/advanced-targeting-card.tsx
@@ -16,6 +16,7 @@ import type {
TSegmentUpdateInput,
} from "@formbricks/types/segment";
import type { TSurvey } from "@formbricks/types/surveys/types";
+import { Alert, AlertDescription } from "@formbricks/ui/components/Alert";
import { AlertDialog } from "@formbricks/ui/components/AlertDialog";
import { Button } from "@formbricks/ui/components/Button";
import { LoadSegmentModal } from "@formbricks/ui/components/LoadSegmentModal";
@@ -161,7 +162,7 @@ export function AdvancedTargetingCard({
return (
);
diff --git a/packages/ee/advanced-targeting/components/segment-editor.tsx b/packages/ee/advanced-targeting/components/segment-editor.tsx
index 435fbc1a36..4ee733f091 100644
--- a/packages/ee/advanced-targeting/components/segment-editor.tsx
+++ b/packages/ee/advanced-targeting/components/segment-editor.tsx
@@ -1,4 +1,4 @@
-import { MoreVertical, Trash2 } from "lucide-react";
+import { ArrowDownIcon, ArrowUpIcon, MoreVertical, Trash2 } from "lucide-react";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
@@ -224,7 +224,8 @@ export function SegmentEditor({
@@ -232,7 +233,8 @@ export function SegmentEditor({
onClick={() => {
if (viewOnly) return;
handleMoveResource(groupId, "down");
- }}>
+ }}
+ icon={
}>
Move down
diff --git a/packages/ee/advanced-targeting/components/segment-filter.tsx b/packages/ee/advanced-targeting/components/segment-filter.tsx
index f80e9596e6..3c04143428 100644
--- a/packages/ee/advanced-targeting/components/segment-filter.tsx
+++ b/packages/ee/advanced-targeting/components/segment-filter.tsx
@@ -1,4 +1,6 @@
import {
+ ArrowDownIcon,
+ ArrowUpIcon,
FingerprintIcon,
MonitorSmartphoneIcon,
MoreVertical,
@@ -161,13 +163,15 @@ function SegmentFilterItemContextMenu({
diff --git a/packages/ee/lib/service.ts b/packages/ee/lib/service.ts
index d9c5572661..b2ca13e85d 100644
--- a/packages/ee/lib/service.ts
+++ b/packages/ee/lib/service.ts
@@ -1,6 +1,7 @@
import "server-only";
import { HttpsProxyAgent } from "https-proxy-agent";
import fetch from "node-fetch";
+import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
import { cache, revalidateTag } from "@formbricks/lib/cache";
import {
@@ -184,53 +185,55 @@ export const getLicenseFeatures = async (): Promise