Compare commits
22 Commits
v3.1.3
...
tolgee-pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36378e9c23 | ||
|
|
9c33e77755 | ||
|
|
88cb4c742f | ||
|
|
475cce8253 | ||
|
|
a86c1738d1 | ||
|
|
96a4d02c80 | ||
|
|
bb6df783ab | ||
|
|
26cca5c2f8 | ||
|
|
7e3dd7d624 | ||
|
|
db9a53f923 | ||
|
|
92ae4786f0 | ||
|
|
b35cf14d32 | ||
|
|
14374b55d2 | ||
|
|
5a919018c5 | ||
|
|
6ac73d3f25 | ||
|
|
510fe3902e | ||
|
|
2bc23594ad | ||
|
|
06e00f3066 | ||
|
|
9b3d409695 | ||
|
|
f7f5737abf | ||
|
|
458f135ee1 | ||
|
|
8e116bf62d |
@@ -167,9 +167,9 @@ ENTERPRISE_LICENSE_KEY=
|
||||
# DEFAULT_ORGANIZATION_ID=
|
||||
# DEFAULT_ORGANIZATION_ROLE=owner
|
||||
|
||||
# Send new users to customer.io
|
||||
# CUSTOMER_IO_API_KEY=
|
||||
# CUSTOMER_IO_SITE_ID=
|
||||
# Send new users to Brevo
|
||||
# BREVO_API_KEY=
|
||||
# BREVO_LIST_ID=
|
||||
|
||||
# Ignore Rate Limiting across the Formbricks app
|
||||
# RATE_LIMITING_DISABLED=1
|
||||
|
||||
39
.github/workflows/sonarqube.yml
vendored
@@ -1,11 +1,11 @@
|
||||
name: SonarQube
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# push:
|
||||
# branches:
|
||||
# - main
|
||||
# pull_request:
|
||||
# types: [opened, synchronize, reopened]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
@@ -16,6 +16,35 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
|
||||
- name: Setup Node.js 20.x
|
||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||
|
||||
- name: create .env
|
||||
run: cp .env.example .env
|
||||
|
||||
- name: Generate Random ENCRYPTION_KEY, CRON_SECRET & NEXTAUTH_SECRET and fill in .env
|
||||
run: |
|
||||
RANDOM_KEY=$(openssl rand -hex 32)
|
||||
sed -i "s/ENCRYPTION_KEY=.*/ENCRYPTION_KEY=${RANDOM_KEY}/" .env
|
||||
sed -i "s/CRON_SECRET=.*/CRON_SECRET=${RANDOM_KEY}/" .env
|
||||
sed -i "s/NEXTAUTH_SECRET=.*/NEXTAUTH_SECRET=${RANDOM_KEY}/" .env
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: |
|
||||
cd apps/web
|
||||
pnpm test:coverage
|
||||
cd ../../
|
||||
# The Vitest coverage config is in your vite.config.mts
|
||||
|
||||
- name: SonarQube Scan
|
||||
uses: SonarSource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203
|
||||
env:
|
||||
|
||||
39
.github/workflows/tolgee-missing-key-check.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Check Missing Translations
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
check-missing-translations:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install Tolgee CLI
|
||||
run: npm install -g @tolgee/cli
|
||||
|
||||
- name: Compare Tolgee Keys
|
||||
id: compare
|
||||
run: |
|
||||
tolgee compare --api-key ${{ secrets.TOLGEE_API_KEY }} > compare_output.txt
|
||||
cat compare_output.txt
|
||||
|
||||
- name: Check for Missing Translations
|
||||
run: |
|
||||
if grep -q "new key found" compare_output.txt; then
|
||||
echo "New keys found that may require translations:"
|
||||
exit 1
|
||||
else
|
||||
echo "No new keys found."
|
||||
fi
|
||||
42
.github/workflows/tolgee.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Tolgee Tagging on PR Merge
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
tag-production-keys:
|
||||
name: Tag Production Keys
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18 # Ensure compatibility with your project
|
||||
|
||||
- name: Install Tolgee CLI
|
||||
run: npm install -g @tolgee/cli
|
||||
|
||||
- name: Tag Production Keys
|
||||
run: |
|
||||
BRANCH_NAME=${GITHUB_REF##*/}
|
||||
npx tolgee tag \
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-extracted \
|
||||
--filter-tag "draft: ${BRANCH_NAME}" \
|
||||
--tag production \
|
||||
--untag "draft: ${BRANCH_NAME}"
|
||||
|
||||
- name: Tag Deprecated Keys
|
||||
run: |
|
||||
npx tolgee tag \
|
||||
--api-key ${{ secrets.TOLGEE_API_KEY }} \
|
||||
--filter-not-extracted --filter-tag production \
|
||||
--tag deprecated --untag production
|
||||
1
.gitignore
vendored
@@ -58,4 +58,5 @@ packages/lib/uploads
|
||||
# js compiled assets
|
||||
apps/web/public/js
|
||||
|
||||
|
||||
packages/database/migrations
|
||||
1
.husky/post-checkout
Normal file
@@ -0,0 +1 @@
|
||||
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json
|
||||
1
.husky/post-commit
Normal file
@@ -0,0 +1 @@
|
||||
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json
|
||||
@@ -1 +1,3 @@
|
||||
pnpm lint-staged
|
||||
pnpm lint-staged
|
||||
pnpm tolgee-pull || true
|
||||
git add packages/lib/messages/*.json
|
||||
31
.tolgeerc.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "https://docs.tolgee.io/cli-schema.json",
|
||||
"format": "JSON_TOLGEE",
|
||||
"patterns": ["./apps/web/**/*.ts?(x)"],
|
||||
"projectId": 10304,
|
||||
"pull": {
|
||||
"path": "./packages/lib/messages"
|
||||
},
|
||||
"push": {
|
||||
"files": [
|
||||
{
|
||||
"language": "en-US",
|
||||
"path": "./packages/lib/messages/en-US.json"
|
||||
},
|
||||
{
|
||||
"language": "de-DE",
|
||||
"path": "./packages/lib/messages/de-DE.json"
|
||||
},
|
||||
{
|
||||
"language": "fr-FR",
|
||||
"path": "./packages/lib/messages/fr-FR.json"
|
||||
},
|
||||
{
|
||||
"language": "pt-BR",
|
||||
"path": "./packages/lib/messages/pt-BR.json"
|
||||
}
|
||||
],
|
||||
"forceMode": "OVERRIDE"
|
||||
},
|
||||
"strictNamespace": false
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
EXPO_PUBLIC_API_HOST=http://192.168.178.20:3000
|
||||
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clzr04nkd000bcdl110j0ijyq
|
||||
EXPO_PUBLIC_APP_URL=http://192.168.0.197:3000
|
||||
EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=cm5p0cs7r000819182b32j0a1
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"jsEngine": "hermes",
|
||||
"name": "react-native-demo",
|
||||
"newArchEnabled": true,
|
||||
"orientation": "portrait",
|
||||
"slug": "react-native-demo",
|
||||
"splash": {
|
||||
|
||||
@@ -13,16 +13,17 @@
|
||||
"dependencies": {
|
||||
"@formbricks/js": "workspace:*",
|
||||
"@formbricks/react-native": "workspace:*",
|
||||
"expo": "52.0.18",
|
||||
"expo-status-bar": "2.0.0",
|
||||
"@react-native-async-storage/async-storage": "2.1.0",
|
||||
"expo": "52.0.28",
|
||||
"expo-status-bar": "2.0.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-native": "0.76.5",
|
||||
"react-native": "0.76.6",
|
||||
"react-native-webview": "13.12.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.26.0",
|
||||
"@types/react": "19.0.1",
|
||||
"@types/react": "18.3.18",
|
||||
"typescript": "5.7.2"
|
||||
},
|
||||
"private": true
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import React, { type JSX } from "react";
|
||||
import { Button, LogBox, StyleSheet, Text, View } from "react-native";
|
||||
import Formbricks, { track } from "@formbricks/react-native";
|
||||
import Formbricks, {
|
||||
logout,
|
||||
setAttribute,
|
||||
setAttributes,
|
||||
setLanguage,
|
||||
setUserId,
|
||||
track,
|
||||
} from "@formbricks/react-native";
|
||||
|
||||
LogBox.ignoreAllLogs();
|
||||
|
||||
@@ -10,35 +17,92 @@ export default function App(): JSX.Element {
|
||||
throw new Error("EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID is required");
|
||||
}
|
||||
|
||||
if (!process.env.EXPO_PUBLIC_API_HOST) {
|
||||
throw new Error("EXPO_PUBLIC_API_HOST is required");
|
||||
if (!process.env.EXPO_PUBLIC_APP_URL) {
|
||||
throw new Error("EXPO_PUBLIC_APP_URL is required");
|
||||
}
|
||||
|
||||
const config = {
|
||||
environmentId: process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID as string,
|
||||
apiHost: process.env.EXPO_PUBLIC_API_HOST as string,
|
||||
userId: "random-user-id",
|
||||
attributes: {
|
||||
language: "en",
|
||||
testAttr: "attr-test",
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Formbricks React Native SDK Demo</Text>
|
||||
|
||||
<Button
|
||||
title="Trigger Code Action"
|
||||
onPress={() => {
|
||||
track("code").catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error tracking event:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<View
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 10,
|
||||
}}>
|
||||
<Button
|
||||
title="Trigger Code Action"
|
||||
onPress={() => {
|
||||
track("code").catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error tracking event:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Set User Id"
|
||||
onPress={() => {
|
||||
setUserId("random-user-id").catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error setting user id:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Set User Attributess (multiple)"
|
||||
onPress={() => {
|
||||
setAttributes({
|
||||
testAttr: "attr-test",
|
||||
testAttr2: "attr-test-2",
|
||||
testAttr3: "attr-test-3",
|
||||
testAttr4: "attr-test-4",
|
||||
}).catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error setting user attributes:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Set User Attributes (single)"
|
||||
onPress={() => {
|
||||
setAttribute("testSingleAttr", "testSingleAttr").catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error setting user attributes:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Logout"
|
||||
onPress={() => {
|
||||
logout().catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error logging out:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
title="Set Language (de)"
|
||||
onPress={() => {
|
||||
setLanguage("de").catch((error: unknown) => {
|
||||
// eslint-disable-next-line no-console -- logging is allowed in demo apps
|
||||
console.error("Error setting language:", error);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<StatusBar style="auto" />
|
||||
<Formbricks initConfig={config} />
|
||||
|
||||
<Formbricks
|
||||
appUrl={process.env.EXPO_PUBLIC_APP_URL as string}
|
||||
environmentId={process.env.EXPO_PUBLIC_FORMBRICKS_ENVIRONMENT_ID as string}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -357,17 +357,11 @@ Now, update your App.js/App.tsx file to initialize Formbricks:
|
||||
// other imports
|
||||
import Formbricks from "@formbricks/react-native";
|
||||
|
||||
const config = {
|
||||
environmentId: "<environment-id>",
|
||||
apiHost: "<api-host>",
|
||||
userId: "<user-id>", // optional
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
{/* Your app content */}
|
||||
<Formbricks initConfig={config} />
|
||||
<Formbricks appUrl="https://app.formbricks.com" environmentId="your-environment-id" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -381,7 +375,7 @@ export default function App() {
|
||||
<Property name="environment-id" type="string">
|
||||
Formbricks Environment ID.
|
||||
</Property>
|
||||
<Property name="api-host" type="string">
|
||||
<Property name="app-url" type="string">
|
||||
URL of the hosted Formbricks instance.
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 154 KiB |
155
apps/docs/app/developer-docs/integrations/activepieces/page.mdx
Normal file
@@ -0,0 +1,155 @@
|
||||
import { MdxImage } from "@/components/mdx-image";
|
||||
|
||||
import CreateConnection from "./create-connection.webp";
|
||||
import CreateNewFlow from "./create-new-flow.webp";
|
||||
import DuplicateSurvey from "./duplicate-survey.webp";
|
||||
import ConfigureConnection from "./configure-connection.webp";
|
||||
import SearchFormbricks from "./search-formbricks.webp";
|
||||
import SelectSurvey from "./select-survey.webp";
|
||||
import TestTrigger from "./test-trigger.webp";
|
||||
import UpdateQuestionId from "./update-question-id.webp";
|
||||
import SuccessResponse from "./success-response.webp";
|
||||
import SelectGSSheet from "./select-gs-sheet.webp";
|
||||
import SelectGoogleSheet from "./select-google-sheet.webp";
|
||||
import MatchData from "./match-data.webp";
|
||||
import Result from "./result.webp";
|
||||
|
||||
export const metadata = {
|
||||
title: "Formbricks Integration with Activepieces: A Step-by-Step Guide",
|
||||
description:
|
||||
"Learn how to integrate Formbricks with Activepieces. Follow our detailed guide to set up workflows, connect with various apps, and send your survey data to multiple platforms.",
|
||||
};
|
||||
|
||||
#### Integrations
|
||||
|
||||
# Activepieces Setup
|
||||
|
||||
Activepieces is a versatile tool to automate workflows between Formbricks and numerous applications. Here's how to set it up.
|
||||
|
||||
<Note>
|
||||
Ensure your survey is finalized before setting up Activepieces. Any changes in the survey will require additional adjustments in the workflow.
|
||||
</Note>
|
||||
|
||||
## Step 1: Setup your survey incl. `questionId` for every question
|
||||
|
||||
Set up the `questionId`s of your survey questions before publishing.
|
||||
|
||||
<MdxImage
|
||||
src={UpdateQuestionId}
|
||||
alt="Update Question ID"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
_Update the Question ID field in every question card under Advanced Settings._
|
||||
|
||||
<Note>
|
||||
Already published? Duplicate survey You can only update the questionId before publishing the survey. If already published, simply duplicate it.
|
||||
<MdxImage
|
||||
src={DuplicateSurvey}
|
||||
alt="Duplicate Survey"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
</Note>
|
||||
|
||||
## Step 2: Setup Activepieces
|
||||
|
||||
Visit [Activepieces](https://activepieces.com) to start a new Flow.
|
||||
|
||||
<MdxImage
|
||||
src={CreateNewFlow}
|
||||
alt="Create New Flow"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Search for `Formbricks` and choose the event to trigger the flow, we will choose `Response Finished` here
|
||||
|
||||
<MdxImage
|
||||
src={SearchFormbricks}
|
||||
alt="Search Formbricks"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Step 3: Connect Formbricks with Activepieces
|
||||
|
||||
Click on `Create connection`:
|
||||
|
||||
<MdxImage
|
||||
src={CreateConnection}
|
||||
alt="Create Connection"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Enter the Formbricks API Host and API Key. API Host is by default set to https://app.formbricks.com but can be modified for self-hosting instances. Learn how to get an API Key from the [API Key tutorial](/additional-features/api#how-to-generate-an-api-key).
|
||||
|
||||
<MdxImage
|
||||
src={ConfigureConnection}
|
||||
alt="Configure Connection"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Step 4: Select Survey
|
||||
|
||||
Choose from your created surveys:
|
||||
|
||||
<MdxImage
|
||||
src={SelectSurvey}
|
||||
alt="Select Survey"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Step 5: Send a test response
|
||||
|
||||
You need a test response for Activepieces setup. Click on Test trigger and submit a test response in the connected Formbricks survey to see the data in Activepieces.
|
||||
|
||||
<MdxImage
|
||||
src={TestTrigger}
|
||||
alt="Test Trigger"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
If the test response is successful, you will see the data in Activepieces.
|
||||
|
||||
<MdxImage
|
||||
src={SuccessResponse}
|
||||
alt="Success Response"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
## Step 6: Set up Google Sheet
|
||||
|
||||
Decide on the desired action for the data. Here, we'll send submissions to a Google Sheet. Add Google sheet step to your flow and configure it as follows:
|
||||
Choose "Add a Row" for the action. Authenticate with Google and select the spreadsheet you want to add the data to.
|
||||
|
||||
<MdxImage
|
||||
src={SelectGSSheet}
|
||||
alt="Select Google sheet"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
Specify the fields you want to add to the spreadsheet.
|
||||
|
||||
<MdxImage
|
||||
src={MatchData}
|
||||
alt="Match Data"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
||||
A new row gets added to the spreadsheet for every response:
|
||||
|
||||
<MdxImage
|
||||
src={Result}
|
||||
alt="Result"
|
||||
quality="100"
|
||||
className="max-w-full rounded-lg sm:max-w-3xl"
|
||||
/>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 21 KiB |
@@ -120,6 +120,7 @@ export const navigation: NavGroup[] = [
|
||||
{ title: "Airtable", href: "/developer-docs/integrations/airtable" },
|
||||
{ title: "Google Sheets", href: "/developer-docs/integrations/google-sheets" },
|
||||
{ title: "Make", href: "/developer-docs/integrations/make" },
|
||||
{ title: "Activepieces", href: "/developer-docs/integrations/activepieces" },
|
||||
{ title: "n8n", href: "/developer-docs/integrations/n8n" },
|
||||
{ title: "Notion", href: "/developer-docs/integrations/notion" },
|
||||
{ title: "Slack", href: "/developer-docs/integrations/slack" },
|
||||
|
||||
@@ -35,33 +35,41 @@ tags:
|
||||
sessions, and responses within the Formbricks platform. This API is ideal
|
||||
for client-side interactions, as it doesn't expose sensitive information.
|
||||
|
||||
|
||||
- [Actions
|
||||
API](https://go.postman.co/workspace/90ba379a-0de2-47d2-a94c-8eb541e47082/documentation/11026000-927c954f-85a9-4f8f-b0ec-14191b903737?entity=folder-b8f3a10e-1642-4d82-a629-fef0a8c6c86c)
|
||||
- Create actions for a person
|
||||
|
||||
- [Displays API](https://formbricks.com/docs/api/client/displays) - Mark
|
||||
- Displays API - Mark
|
||||
Survey as Displayed or Responded for a Person
|
||||
|
||||
- [People API](https://formbricks.com/docs/api/client/people) - Create &
|
||||
update people (e.g. attributes)
|
||||
- Responses API - Create & update responses for a survey
|
||||
|
||||
- Environment API - Get the environment state to be used in Formbricks SDKs
|
||||
|
||||
- Contacts API - Identify & update contacts (e.g. attributes)
|
||||
|
||||
- User API - Identify & track users based on their attributes, device type, etc.
|
||||
|
||||
- [Responses API](https://formbricks.com/docs/api/client/responses) -
|
||||
Create & update responses for a survey
|
||||
- name: Client API > Display
|
||||
description: >-
|
||||
Displays are metrics used to measure the number of times a survey was
|
||||
viewed both by unidentified or identified users.
|
||||
- name: Client API > People
|
||||
description: >-
|
||||
Persons are the identified users on Formbricks app that get initated when
|
||||
you pass a userId and have user activation enabled. This now allows you to
|
||||
track & show them targeted surveys based on their actions, attributes,
|
||||
etc.
|
||||
- name: Client API > Response
|
||||
description: >-
|
||||
Responses are captured whenever a user fills in your survey either
|
||||
partially or completely.
|
||||
- name: Client API > Environment
|
||||
description: >-
|
||||
Get the environment state to be used in Formbricks SDKs
|
||||
- name: Client API > Contacts
|
||||
description: >-
|
||||
Contacts are the identified users on Formbricks app that get initated when
|
||||
you pass a userId and have user activation enabled. This now allows you to
|
||||
track & show them targeted surveys based on their attributes, device type,
|
||||
etc.
|
||||
- name: Client API > User
|
||||
description: >-
|
||||
Users are the identified users on Formbricks app that get initated when
|
||||
you pass a userId and have user activation enabled. This now allows you to
|
||||
track & show them targeted surveys based on their attributes, device type,
|
||||
etc. Currently, this api is only being used in the react-native sdk.
|
||||
|
||||
- name: Management API
|
||||
description: "The Management API provides access to all data and settings that are visible in the Formbricks App. This API requires a personal API Key for authentication, which can be generated in the Settings section of the Formbricks App. With the Management API, you can manage your Formbricks account programmatically, accessing and modifying data and settings as needed.\n\n> **For Auth:**\_we use the `x-api-key` header \n \n\nAPI requests made to the Management API are authorized using a personal API key. This key grants the same rights and access as if you were logged in at formbricks.com. It's essential to keep your API key secure and not share it with others.\n\nTo generate, store, or delete an API key, follow the instructions provided on the following page\_[API Key](https://formbricks.com/docs/api/management/api-key-setup)."
|
||||
- name: Management API > Action Class
|
||||
@@ -436,139 +444,424 @@ paths:
|
||||
code: internal_server_error
|
||||
message: Person with ID 2 not found
|
||||
details: {}
|
||||
/api/v1/client/{environmentId}/people:
|
||||
post:
|
||||
/api/v1/client/{environmentId}/environment:
|
||||
get:
|
||||
tags:
|
||||
- Client API > People
|
||||
summary: Create Person
|
||||
description: Creates a person in Formbricks with the given userId, it must be unique.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
userId: "{{userId}}"
|
||||
- Client API > Environment
|
||||
summary: Get Environment State
|
||||
description: >-
|
||||
Retrieves the environment state to be used in Formbricks SDKs
|
||||
parameters:
|
||||
- name: environmentId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The ID of the environment
|
||||
responses:
|
||||
"200":
|
||||
description: "HTTP Status 200"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
surveys:
|
||||
type: array
|
||||
description: List of surveys in the environment
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "cm6orr901000g19wjwwa690eo"
|
||||
welcomeCard:
|
||||
type: object
|
||||
properties:
|
||||
html:
|
||||
type: string
|
||||
example: "Thanks for providing your feedback - let's go!"
|
||||
enabled:
|
||||
type: boolean
|
||||
example: false
|
||||
headline:
|
||||
type: string
|
||||
example: "Welcome!"
|
||||
buttonLabel:
|
||||
type: string
|
||||
example: "Next"
|
||||
timeToFinish:
|
||||
type: boolean
|
||||
example: false
|
||||
showResponseCount:
|
||||
type: boolean
|
||||
example: false
|
||||
name:
|
||||
type: string
|
||||
example: "Start from scratch"
|
||||
questions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "dd5c8w2a4ttkbnjb9nwhtb17"
|
||||
type:
|
||||
type: string
|
||||
example: "openText"
|
||||
headline:
|
||||
type: string
|
||||
example: "What would you like to know?"
|
||||
required:
|
||||
type: boolean
|
||||
example: true
|
||||
charLimit:
|
||||
type: boolean
|
||||
example: false
|
||||
inputType:
|
||||
type: string
|
||||
example: "text"
|
||||
buttonLabel:
|
||||
type: string
|
||||
example: "Next"
|
||||
placeholder:
|
||||
type: string
|
||||
example: "Type your answer here..."
|
||||
variables:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
example: "app"
|
||||
showLanguageSwitch:
|
||||
type: boolean
|
||||
example: null
|
||||
languages:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
endings:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "o729tod5klhix62njmk262dk"
|
||||
type:
|
||||
type: string
|
||||
example: "endScreen"
|
||||
headline:
|
||||
type: string
|
||||
example: "Thank you!"
|
||||
subheader:
|
||||
type: string
|
||||
example: "We appreciate your feedback."
|
||||
buttonLink:
|
||||
type: string
|
||||
example: "https://formbricks.com"
|
||||
buttonLabel:
|
||||
type: string
|
||||
example: "Create your own Survey"
|
||||
autoClose:
|
||||
type: boolean
|
||||
example: null
|
||||
styling:
|
||||
type: object
|
||||
example: null
|
||||
status:
|
||||
type: string
|
||||
example: "inProgress"
|
||||
segment:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "cm6orr90h000h19wj1lnwoxwg"
|
||||
createdAt:
|
||||
type: string
|
||||
example: "2025-02-03T08:08:33.377Z"
|
||||
updatedAt:
|
||||
type: string
|
||||
example: "2025-02-03T08:08:33.377Z"
|
||||
title:
|
||||
type: string
|
||||
example: "cm6orr901000g19wjwwa690eo"
|
||||
description:
|
||||
type: string
|
||||
example: null
|
||||
isPrivate:
|
||||
type: boolean
|
||||
example: true
|
||||
filters:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
recontactDays:
|
||||
type: integer
|
||||
example: 0
|
||||
displayLimit:
|
||||
type: integer
|
||||
example: 5
|
||||
displayOption:
|
||||
type: string
|
||||
example: "respondMultiple"
|
||||
hiddenFields:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
example: true
|
||||
fieldIds:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
triggers:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
actionClass:
|
||||
type: string
|
||||
example: "code action"
|
||||
displayPercentage:
|
||||
type: integer
|
||||
example: null
|
||||
delay:
|
||||
type: integer
|
||||
example: 0
|
||||
actionClasses:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "cm6orqtdd000b19wjec82bpp2"
|
||||
type:
|
||||
type: string
|
||||
example: "automatic"
|
||||
name:
|
||||
type: string
|
||||
example: "New Session"
|
||||
key:
|
||||
type: string
|
||||
nullable: true
|
||||
example: null
|
||||
noCodeConfig:
|
||||
type: object
|
||||
nullable: true
|
||||
example: null
|
||||
example:
|
||||
- id: "cm6orqtdd000b19wjec82bpp2"
|
||||
type: "automatic"
|
||||
name: "New Session"
|
||||
key: null
|
||||
noCodeConfig: null
|
||||
- id: "cm6oryki3000i19wj860utcnn"
|
||||
type: "code"
|
||||
name: "code action"
|
||||
key: "code"
|
||||
noCodeConfig: null
|
||||
project:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
example: "cm6orqtcl000319wj9wb7dltl"
|
||||
recontactDays:
|
||||
type: integer
|
||||
example: 7
|
||||
clickOutsideClose:
|
||||
type: boolean
|
||||
example: true
|
||||
darkOverlay:
|
||||
type: boolean
|
||||
example: false
|
||||
placement:
|
||||
type: string
|
||||
example: "bottomRight"
|
||||
inAppSurveyBranding:
|
||||
type: boolean
|
||||
example: true
|
||||
styling:
|
||||
type: object
|
||||
properties:
|
||||
brandColor:
|
||||
type: object
|
||||
properties:
|
||||
light:
|
||||
type: string
|
||||
example: "#64748b"
|
||||
allowStyleOverwrite:
|
||||
type: boolean
|
||||
example: true
|
||||
"404":
|
||||
description: Not Found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: "not_found"
|
||||
message:
|
||||
type: string
|
||||
example: "Environment not found"
|
||||
details:
|
||||
type: object
|
||||
properties:
|
||||
resource_id:
|
||||
type: string
|
||||
example: "tpywklouw2p7tebdu4zv01an"
|
||||
resource_type:
|
||||
type: string
|
||||
example: "environment"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: "internal_server_error"
|
||||
message:
|
||||
type: string
|
||||
example: "Unable to complete request: Expected property name or '}' in JSON at position 29"
|
||||
details:
|
||||
type: object
|
||||
/api/v1/client/{environmentId}/identify/contacts/{userId}:
|
||||
get:
|
||||
tags:
|
||||
- Client API > Contacts
|
||||
summary: Get Contact State
|
||||
description: >-
|
||||
Retrieves a contact's state including their segments, displays, responses
|
||||
and other tracking information. If the contact doesn't exist, it will be created.
|
||||
parameters:
|
||||
- name: environmentId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The ID of the environment
|
||||
- name: userId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: The user ID to identify the contact
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
headers:
|
||||
Access-Control-Allow-Credentials:
|
||||
schema:
|
||||
type: boolean
|
||||
example: "true"
|
||||
Access-Control-Allow-Origin:
|
||||
schema:
|
||||
type: string
|
||||
example: "*"
|
||||
access-control-allow-methods:
|
||||
schema:
|
||||
type: string
|
||||
example: GET, POST, PUT, DELETE, OPTIONS
|
||||
access-control-allow-headers:
|
||||
schema:
|
||||
type: string
|
||||
example: Content-Type, Authorization
|
||||
vary:
|
||||
schema:
|
||||
type: string
|
||||
example: RSC, Next-Router-State-Tree, Next-Router-Prefetch
|
||||
cache-control:
|
||||
schema:
|
||||
type: string
|
||||
example: private, no-store
|
||||
content-type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Tue, 23 Apr 2024 07:35:47 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
Transfer-Encoding:
|
||||
schema:
|
||||
type: string
|
||||
example: chunked
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
data:
|
||||
userId: Shubham
|
||||
"400":
|
||||
description: Bad Request
|
||||
headers:
|
||||
Access-Control-Allow-Credentials:
|
||||
schema:
|
||||
type: boolean
|
||||
example: "true"
|
||||
Access-Control-Allow-Origin:
|
||||
schema:
|
||||
type: string
|
||||
example: "*"
|
||||
access-control-allow-methods:
|
||||
schema:
|
||||
type: string
|
||||
example: GET, POST, PUT, DELETE, OPTIONS
|
||||
access-control-allow-headers:
|
||||
schema:
|
||||
type: string
|
||||
example: Content-Type, Authorization
|
||||
vary:
|
||||
schema:
|
||||
type: string
|
||||
example: RSC, Next-Router-State-Tree, Next-Router-Prefetch
|
||||
content-type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Tue, 23 Apr 2024 07:36:43 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
Transfer-Encoding:
|
||||
schema:
|
||||
type: string
|
||||
example: chunked
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
description: The user ID of the contact
|
||||
segments:
|
||||
type: array
|
||||
description: List of segment IDs the contact belongs to
|
||||
items:
|
||||
type: string
|
||||
displays:
|
||||
type: array
|
||||
description: List of survey displays for this contact
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
surveyId:
|
||||
type: string
|
||||
createdAt:
|
||||
type: string
|
||||
format: date-time
|
||||
responses:
|
||||
type: array
|
||||
description: List of survey IDs the contact has responded to
|
||||
items:
|
||||
type: string
|
||||
lastDisplayAt:
|
||||
type: string
|
||||
format: date-time
|
||||
nullable: true
|
||||
description: Timestamp of the last survey display
|
||||
example:
|
||||
userId: "user-123"
|
||||
segments: ["fi8f9oekza95wwszrptidivq", "zgwrv8eg7vfavdhzv1s0po1w"]
|
||||
displays: [{ surveyId: "pjogp5a1wyxon6umplmf49b8", createdAt: "2024-04-23T08:59:37.550Z" }]
|
||||
responses: ["pjogp5a1wyxon6umplmf49b8"]
|
||||
lastDisplayAt: "2024-04-23T08:59:37.550Z"
|
||||
"403":
|
||||
description: Forbidden
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
code: bad_request
|
||||
message: userId is required
|
||||
details:
|
||||
environmentId: clurwouax000azffxt7n5unn3
|
||||
/api/v1/client/{environmentId}/people/{userId}/attributes:
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: "forbidden"
|
||||
message:
|
||||
type: string
|
||||
example: "User identification is only available for enterprise users."
|
||||
"404":
|
||||
description: Not Found
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: "not_found"
|
||||
message:
|
||||
type: string
|
||||
example: "Environment not found"
|
||||
details:
|
||||
type: object
|
||||
properties:
|
||||
resource_id:
|
||||
type: string
|
||||
example: "tpywklouw2p7tebdu4zv01an"
|
||||
resource_type:
|
||||
type: string
|
||||
example: "environment"
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: string
|
||||
example: "internal_server_error"
|
||||
message:
|
||||
type: string
|
||||
example: "Unable to complete request: Expected property name or '}' in JSON at position 29"
|
||||
details:
|
||||
type: object
|
||||
/api/v1/client/{environmentId}/contacts/{userId}/attributes:
|
||||
put:
|
||||
tags:
|
||||
- Client API > People
|
||||
summary: Update Person
|
||||
- Client API > Contacts
|
||||
summary: Update Contact (Attributes)
|
||||
description: >-
|
||||
Update a person's attributes in Formbricks to keep them in sync with
|
||||
Update a contact's attributes in Formbricks to keep them in sync with
|
||||
your app or when you want to set a custom attribute in Formbricks.
|
||||
requestBody:
|
||||
content:
|
||||
@@ -713,6 +1006,280 @@ paths:
|
||||
Unable to complete request: Expected property name or '}' in
|
||||
JSON at position 29
|
||||
details: {}
|
||||
/api/v1/client/{environmentId}/user:
|
||||
post:
|
||||
tags:
|
||||
- Client API > User
|
||||
summary: Create or Identify User
|
||||
description: >
|
||||
Endpoint for creating or identifying a user within the specified environment.
|
||||
If the user already exists, this will identify them and potentially
|
||||
update user attributes. If they don't exist, it will create a new user.
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
userId: "hello-user"
|
||||
attributes:
|
||||
plan: "free"
|
||||
parameters:
|
||||
- name: environmentId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
headers:
|
||||
Access-Control-Allow-Credentials:
|
||||
schema:
|
||||
type: boolean
|
||||
example: "true"
|
||||
Access-Control-Allow-Origin:
|
||||
schema:
|
||||
type: string
|
||||
example: "*"
|
||||
access-control-allow-methods:
|
||||
schema:
|
||||
type: string
|
||||
example: GET, POST, PUT, DELETE, OPTIONS
|
||||
access-control-allow-headers:
|
||||
schema:
|
||||
type: string
|
||||
example: Content-Type, Authorization
|
||||
vary:
|
||||
schema:
|
||||
type: string
|
||||
example: RSC, Next-Router-State-Tree, Next-Router-Prefetch
|
||||
content-type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Tue, 23 Apr 2024 07:36:43 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
Transfer-Encoding:
|
||||
schema:
|
||||
type: string
|
||||
example: chunked
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
state:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
type: object
|
||||
properties:
|
||||
userId:
|
||||
type: string
|
||||
example: "hello-user"
|
||||
segments:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "cm6onrezn000hw2ahcokiz41v"
|
||||
displays:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
surveyId:
|
||||
type: string
|
||||
example: "cm6orqtdd000a19wjhnbces5s"
|
||||
createdAt:
|
||||
type: string
|
||||
example: "2025-02-03T11:23:13.050Z"
|
||||
responses:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: "cm6orqtdd000a19wjhnbces5s"
|
||||
lastDisplayAt:
|
||||
type: string
|
||||
example: "2025-02-03T11:23:13.050Z"
|
||||
expiresAt:
|
||||
type: string
|
||||
example: "2025-02-03T11:23:13.050Z"
|
||||
"400":
|
||||
description: Bad Request
|
||||
headers:
|
||||
Access-Control-Allow-Credentials:
|
||||
schema:
|
||||
type: boolean
|
||||
example: "true"
|
||||
Access-Control-Allow-Origin:
|
||||
schema:
|
||||
type: string
|
||||
example: "*"
|
||||
access-control-allow-methods:
|
||||
schema:
|
||||
type: string
|
||||
example: GET, POST, PUT, DELETE, OPTIONS
|
||||
access-control-allow-headers:
|
||||
schema:
|
||||
type: string
|
||||
example: Content-Type, Authorization
|
||||
vary:
|
||||
schema:
|
||||
type: string
|
||||
example: RSC, Next-Router-State-Tree, Next-Router-Prefetch
|
||||
content-type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Tue, 23 Apr 2024 07:36:43 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
Transfer-Encoding:
|
||||
schema:
|
||||
type: string
|
||||
example: chunked
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
code: bad_request
|
||||
message: userId is required
|
||||
"404":
|
||||
description: Not Found
|
||||
headers:
|
||||
Access-Control-Allow-Credentials:
|
||||
schema:
|
||||
type: boolean
|
||||
example: "true"
|
||||
Access-Control-Allow-Origin:
|
||||
schema:
|
||||
type: string
|
||||
example: "*"
|
||||
access-control-allow-methods:
|
||||
schema:
|
||||
type: string
|
||||
example: GET, POST, PUT, DELETE, OPTIONS
|
||||
access-control-allow-headers:
|
||||
schema:
|
||||
type: string
|
||||
example: Content-Type, Authorization
|
||||
vary:
|
||||
schema:
|
||||
type: string
|
||||
example: RSC, Next-Router-State-Tree, Next-Router-Prefetch
|
||||
cache-control:
|
||||
schema:
|
||||
type: string
|
||||
example: private, no-store
|
||||
content-type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Tue, 23 Apr 2024 07:56:29 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
Transfer-Encoding:
|
||||
schema:
|
||||
type: string
|
||||
example: chunked
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
code: not_found
|
||||
message: Environment not found
|
||||
details:
|
||||
resource_id: f16ttdvtkx85k5m4s561ruqj
|
||||
resource_type: Environment
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
headers:
|
||||
Access-Control-Allow-Credentials:
|
||||
schema:
|
||||
type: boolean
|
||||
example: "true"
|
||||
Access-Control-Allow-Origin:
|
||||
schema:
|
||||
type: string
|
||||
example: "*"
|
||||
access-control-allow-methods:
|
||||
schema:
|
||||
type: string
|
||||
example: GET, POST, PUT, DELETE, OPTIONS
|
||||
access-control-allow-headers:
|
||||
schema:
|
||||
type: string
|
||||
example: Content-Type, Authorization
|
||||
vary:
|
||||
schema:
|
||||
type: string
|
||||
example: RSC, Next-Router-State-Tree, Next-Router-Prefetch
|
||||
cache-control:
|
||||
schema:
|
||||
type: string
|
||||
example: private, no-store
|
||||
content-type:
|
||||
schema:
|
||||
type: string
|
||||
example: application/json
|
||||
Date:
|
||||
schema:
|
||||
type: string
|
||||
example: Tue, 23 Apr 2024 07:56:29 GMT
|
||||
Connection:
|
||||
schema:
|
||||
type: string
|
||||
example: keep-alive
|
||||
Keep-Alive:
|
||||
schema:
|
||||
type: string
|
||||
example: timeout=5
|
||||
Transfer-Encoding:
|
||||
schema:
|
||||
type: string
|
||||
example: chunked
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
example:
|
||||
code: internal_server_error
|
||||
message: An unexpected error occurred
|
||||
details: {}
|
||||
/api/v1/client/{environmentId}/responses:
|
||||
post:
|
||||
tags:
|
||||
|
||||
@@ -18,6 +18,7 @@ FROM node:22-alpine3.20 AS base
|
||||
FROM base AS installer
|
||||
|
||||
# Enable corepack and prepare pnpm
|
||||
RUN npm install -g corepack@latest
|
||||
RUN corepack enable
|
||||
|
||||
# Install necessary build tools and compilers
|
||||
@@ -61,6 +62,8 @@ RUN jq -r '.devDependencies.prisma' packages/database/package.json > /prisma_ver
|
||||
## step 3: setup production runner
|
||||
#
|
||||
FROM base AS runner
|
||||
|
||||
RUN npm install -g corepack@latest
|
||||
RUN corepack enable
|
||||
|
||||
RUN apk add --no-cache curl \
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
@@ -23,7 +23,7 @@ export const ConnectWithFormbricks = ({
|
||||
widgetSetupCompleted,
|
||||
channel,
|
||||
}: ConnectWithFormbricksProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const router = useRouter();
|
||||
const handleFinishOnboarding = async () => {
|
||||
router.push(`/environments/${environment.id}/surveys`);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Button } from "@/modules/ui/components/button";
|
||||
import { CodeBlock } from "@/modules/ui/components/code-block";
|
||||
import { Html5Icon, NpmIcon } from "@/modules/ui/components/icons";
|
||||
import { TabBar } from "@/modules/ui/components/tab-bar";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Link from "next/link";
|
||||
import "prismjs/themes/prism.css";
|
||||
import { useState } from "react";
|
||||
@@ -29,7 +29,7 @@ export const OnboardingSetupInstructions = ({
|
||||
channel,
|
||||
widgetSetupCompleted,
|
||||
}: OnboardingSetupInstructionsProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [activeTab, setActiveTab] = useState(tabs[0].id);
|
||||
const htmlSnippetForAppSurveys = `<!-- START Formbricks Surveys -->
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ConnectWithFormbricks } from "@/app/(app)/(onboarding)/environments/[environmentId]/connect/components/ConnectWithFormbricks";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { XIcon } from "lucide-react";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import Link from "next/link";
|
||||
import { WEBAPP_URL } from "@formbricks/lib/constants";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
@@ -16,7 +16,7 @@ interface ConnectPageProps {
|
||||
|
||||
const Page = async (props: ConnectPageProps) => {
|
||||
const params = await props.params;
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const environment = await getEnvironment(params.environmentId);
|
||||
|
||||
if (!environment) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import { getXMTemplates } from "@/app/(app)/(onboarding)/environments/[environme
|
||||
import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizations/components/OnboardingOptionsContainer";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { createSurveyAction } from "@/modules/surveys/components/TemplateList/actions";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ActivityIcon, ShoppingCartIcon, SmileIcon, StarIcon, ThumbsUpIcon, UsersIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -23,7 +23,7 @@ interface XMTemplateListProps {
|
||||
|
||||
export const XMTemplateList = ({ project, user, environmentId }: XMTemplateListProps) => {
|
||||
const [activeTemplateId, setActiveTemplateId] = useState<number | null>(null);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const router = useRouter();
|
||||
|
||||
const createSurvey = async (activeTemplate: TXMTemplate) => {
|
||||
@@ -47,7 +47,7 @@ export const XMTemplateList = ({ project, user, environmentId }: XMTemplateListP
|
||||
|
||||
const handleTemplateClick = (templateIdx: number) => {
|
||||
setActiveTemplateId(templateIdx);
|
||||
const template = getXMTemplates(user.locale)[templateIdx];
|
||||
const template = getXMTemplates(t)[templateIdx];
|
||||
const newTemplate = replacePresetPlaceholders(template, project);
|
||||
createSurvey(newTemplate);
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { TXMTemplate } from "@formbricks/types/templates";
|
||||
// replace all occurences of projectName with the actual project name in the current template
|
||||
export const replacePresetPlaceholders = (template: TXMTemplate, project: TProject) => {
|
||||
const survey = structuredClone(template);
|
||||
survey.name = survey.name.replace("{{projectName}}", project.name);
|
||||
survey.name = survey.name.replace("$[projectName]", project.name);
|
||||
survey.questions = survey.questions.map((question) => {
|
||||
return replaceQuestionPresetPlaceholders(question, project);
|
||||
});
|
||||
|
||||
@@ -1,25 +1,18 @@
|
||||
import { getDefaultEndingCard } from "@/app/lib/templates";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { getDefaultEndingCard, translate } from "@formbricks/lib/templates";
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
import { TXMTemplate } from "@formbricks/types/templates";
|
||||
|
||||
function validateLocale(locale: string): boolean {
|
||||
// Add logic to validate the locale, e.g., check against a list of supported locales
|
||||
return typeof locale === "string" && locale.length > 0;
|
||||
}
|
||||
|
||||
function logError(error: Error, context: string) {
|
||||
console.error(`Error in ${context}:`, error);
|
||||
}
|
||||
|
||||
export const getXMSurveyDefault = (locale: string): TXMTemplate => {
|
||||
export const getXMSurveyDefault = (t: TFnType): TXMTemplate => {
|
||||
try {
|
||||
if (!validateLocale(locale)) {
|
||||
throw new Error("Invalid locale");
|
||||
}
|
||||
return {
|
||||
name: "",
|
||||
endings: [getDefaultEndingCard([], locale)],
|
||||
endings: [getDefaultEndingCard([], t)],
|
||||
questions: [],
|
||||
styling: {
|
||||
overwriteThemeStyling: true,
|
||||
@@ -31,24 +24,24 @@ export const getXMSurveyDefault = (locale: string): TXMTemplate => {
|
||||
}
|
||||
};
|
||||
|
||||
const NPSSurvey = (locale: string): TXMTemplate => {
|
||||
const npsSurvey = (t: TFnType): TXMTemplate => {
|
||||
return {
|
||||
...getXMSurveyDefault(locale),
|
||||
name: translate("nps_survey_name", locale),
|
||||
...getXMSurveyDefault(t),
|
||||
name: t("templates.nps_survey_name"),
|
||||
questions: [
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.NPS,
|
||||
headline: { default: translate("nps_survey_question_1_headline", locale) },
|
||||
headline: { default: t("templates.nps_survey_question_1_headline") },
|
||||
required: true,
|
||||
lowerLabel: { default: translate("nps_survey_question_1_lower_label", locale) },
|
||||
upperLabel: { default: translate("nps_survey_question_1_upper_label", locale) },
|
||||
lowerLabel: { default: t("templates.nps_survey_question_1_lower_label") },
|
||||
upperLabel: { default: t("templates.nps_survey_question_1_upper_label") },
|
||||
isColorCodingEnabled: true,
|
||||
},
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("nps_survey_question_2_headline", locale) },
|
||||
headline: { default: t("templates.nps_survey_question_2_headline") },
|
||||
required: false,
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
@@ -58,7 +51,7 @@ const NPSSurvey = (locale: string): TXMTemplate => {
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("nps_survey_question_3_headline", locale) },
|
||||
headline: { default: t("templates.nps_survey_question_3_headline") },
|
||||
required: false,
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
@@ -69,13 +62,13 @@ const NPSSurvey = (locale: string): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const StarRatingSurvey = (locale: string): TXMTemplate => {
|
||||
const starRatingSurvey = (t: TFnType): TXMTemplate => {
|
||||
const reusableQuestionIds = [createId(), createId(), createId()];
|
||||
const defaultSurvey = getXMSurveyDefault(locale);
|
||||
const defaultSurvey = getXMSurveyDefault(t);
|
||||
|
||||
return {
|
||||
...defaultSurvey,
|
||||
name: translate("star_rating_survey_name", locale),
|
||||
name: t("templates.star_rating_survey_name"),
|
||||
questions: [
|
||||
{
|
||||
id: reusableQuestionIds[0],
|
||||
@@ -112,15 +105,15 @@ const StarRatingSurvey = (locale: string): TXMTemplate => {
|
||||
],
|
||||
range: 5,
|
||||
scale: "number",
|
||||
headline: { default: translate("star_rating_survey_question_1_headline", locale) },
|
||||
headline: { default: t("templates.star_rating_survey_question_1_headline") },
|
||||
required: true,
|
||||
lowerLabel: { default: translate("star_rating_survey_question_1_lower_label", locale) },
|
||||
upperLabel: { default: translate("star_rating_survey_question_1_upper_label", locale) },
|
||||
lowerLabel: { default: t("templates.star_rating_survey_question_1_lower_label") },
|
||||
upperLabel: { default: t("templates.star_rating_survey_question_1_upper_label") },
|
||||
isColorCodingEnabled: false,
|
||||
},
|
||||
{
|
||||
id: reusableQuestionIds[1],
|
||||
html: { default: translate("star_rating_survey_question_2_html", locale) },
|
||||
html: { default: t("templates.star_rating_survey_question_2_html") },
|
||||
type: TSurveyQuestionTypeEnum.CTA,
|
||||
logic: [
|
||||
{
|
||||
@@ -148,20 +141,20 @@ const StarRatingSurvey = (locale: string): TXMTemplate => {
|
||||
],
|
||||
},
|
||||
],
|
||||
headline: { default: translate("star_rating_survey_question_2_headline", locale) },
|
||||
headline: { default: t("templates.star_rating_survey_question_2_headline") },
|
||||
required: true,
|
||||
buttonUrl: "https://formbricks.com/github",
|
||||
buttonLabel: { default: translate("star_rating_survey_question_2_button_label", locale) },
|
||||
buttonLabel: { default: t("templates.star_rating_survey_question_2_button_label") },
|
||||
buttonExternal: true,
|
||||
},
|
||||
{
|
||||
id: reusableQuestionIds[2],
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: "Sorry to hear! What is ONE thing we can do better?" },
|
||||
headline: { default: t("templates.star_rating_survey_question_3_headline") },
|
||||
required: true,
|
||||
subheader: { default: "Help us improve your experience." },
|
||||
buttonLabel: { default: "Send" },
|
||||
placeholder: { default: "Type your answer here..." },
|
||||
subheader: { default: t("templates.star_rating_survey_question_3_subheader") },
|
||||
buttonLabel: { default: t("templates.star_rating_survey_question_3_button_label") },
|
||||
placeholder: { default: t("templates.star_rating_survey_question_3_placeholder") },
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
enabled: false,
|
||||
@@ -171,13 +164,13 @@ const StarRatingSurvey = (locale: string): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const CSATSurvey = (locale: string): TXMTemplate => {
|
||||
const csatSurvey = (t: TFnType): TXMTemplate => {
|
||||
const reusableQuestionIds = [createId(), createId(), createId()];
|
||||
const defaultSurvey = getXMSurveyDefault(locale);
|
||||
const defaultSurvey = getXMSurveyDefault(t);
|
||||
|
||||
return {
|
||||
...defaultSurvey,
|
||||
name: translate("csat_survey_name", locale),
|
||||
name: t("templates.csat_survey_name"),
|
||||
questions: [
|
||||
{
|
||||
id: reusableQuestionIds[0],
|
||||
@@ -214,10 +207,10 @@ const CSATSurvey = (locale: string): TXMTemplate => {
|
||||
],
|
||||
range: 5,
|
||||
scale: "smiley",
|
||||
headline: { default: translate("csat_survey_question_1_headline", locale) },
|
||||
headline: { default: t("templates.csat_survey_question_1_headline") },
|
||||
required: true,
|
||||
lowerLabel: { default: translate("csat_survey_question_1_lower_label", locale) },
|
||||
upperLabel: { default: translate("csat_survey_question_1_upper_label", locale) },
|
||||
lowerLabel: { default: t("templates.csat_survey_question_1_lower_label") },
|
||||
upperLabel: { default: t("templates.csat_survey_question_1_upper_label") },
|
||||
isColorCodingEnabled: false,
|
||||
},
|
||||
{
|
||||
@@ -249,9 +242,9 @@ const CSATSurvey = (locale: string): TXMTemplate => {
|
||||
],
|
||||
},
|
||||
],
|
||||
headline: { default: translate("csat_survey_question_2_headline", locale) },
|
||||
headline: { default: t("templates.csat_survey_question_2_headline") },
|
||||
required: false,
|
||||
placeholder: { default: translate("csat_survey_question_2_placeholder", locale) },
|
||||
placeholder: { default: t("templates.csat_survey_question_2_placeholder") },
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
enabled: false,
|
||||
@@ -260,9 +253,9 @@ const CSATSurvey = (locale: string): TXMTemplate => {
|
||||
{
|
||||
id: reusableQuestionIds[2],
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("csat_survey_question_3_headline", locale) },
|
||||
headline: { default: t("templates.csat_survey_question_3_headline") },
|
||||
required: false,
|
||||
placeholder: { default: translate("csat_survey_question_3_placeholder", locale) },
|
||||
placeholder: { default: t("templates.csat_survey_question_3_placeholder") },
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
enabled: false,
|
||||
@@ -272,28 +265,28 @@ const CSATSurvey = (locale: string): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const CESSurvey = (locale: string): TXMTemplate => {
|
||||
const cessSurvey = (t: TFnType): TXMTemplate => {
|
||||
return {
|
||||
...getXMSurveyDefault(locale),
|
||||
name: translate("cess_survey_name", locale),
|
||||
...getXMSurveyDefault(t),
|
||||
name: t("templates.cess_survey_name"),
|
||||
questions: [
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.Rating,
|
||||
range: 5,
|
||||
scale: "number",
|
||||
headline: { default: translate("cess_survey_question_1_headline", locale) },
|
||||
headline: { default: t("templates.cess_survey_question_1_headline") },
|
||||
required: true,
|
||||
lowerLabel: { default: translate("cess_survey_question_1_lower_label", locale) },
|
||||
upperLabel: { default: translate("cess_survey_question_1_upper_label", locale) },
|
||||
lowerLabel: { default: t("templates.cess_survey_question_1_lower_label") },
|
||||
upperLabel: { default: t("templates.cess_survey_question_1_upper_label") },
|
||||
isColorCodingEnabled: false,
|
||||
},
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("cess_survey_question_2_headline", locale) },
|
||||
headline: { default: t("templates.cess_survey_question_2_headline") },
|
||||
required: true,
|
||||
placeholder: { default: translate("cess_survey_question_2_placeholder", locale) },
|
||||
placeholder: { default: t("templates.cess_survey_question_2_placeholder") },
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
enabled: false,
|
||||
@@ -303,13 +296,13 @@ const CESSurvey = (locale: string): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const SmileysRatingSurvey = (locale: string): TXMTemplate => {
|
||||
const smileysRatingSurvey = (t: TFnType): TXMTemplate => {
|
||||
const reusableQuestionIds = [createId(), createId(), createId()];
|
||||
const defaultSurvey = getXMSurveyDefault(locale);
|
||||
const defaultSurvey = getXMSurveyDefault(t);
|
||||
|
||||
return {
|
||||
...defaultSurvey,
|
||||
name: translate("smileys_survey_name", locale),
|
||||
name: t("templates.smileys_survey_name"),
|
||||
questions: [
|
||||
{
|
||||
id: reusableQuestionIds[0],
|
||||
@@ -346,15 +339,15 @@ const SmileysRatingSurvey = (locale: string): TXMTemplate => {
|
||||
],
|
||||
range: 5,
|
||||
scale: "smiley",
|
||||
headline: { default: translate("smileys_survey_question_1_headline", locale) },
|
||||
headline: { default: t("templates.smileys_survey_question_1_headline") },
|
||||
required: true,
|
||||
lowerLabel: { default: translate("smileys_survey_question_1_lower_label", locale) },
|
||||
upperLabel: { default: translate("smileys_survey_question_1_upper_label", locale) },
|
||||
lowerLabel: { default: t("templates.smileys_survey_question_1_lower_label") },
|
||||
upperLabel: { default: t("templates.smileys_survey_question_1_upper_label") },
|
||||
isColorCodingEnabled: false,
|
||||
},
|
||||
{
|
||||
id: reusableQuestionIds[1],
|
||||
html: { default: translate("smileys_survey_question_2_html", locale) },
|
||||
html: { default: t("templates.smileys_survey_question_2_html") },
|
||||
type: TSurveyQuestionTypeEnum.CTA,
|
||||
logic: [
|
||||
{
|
||||
@@ -382,20 +375,20 @@ const SmileysRatingSurvey = (locale: string): TXMTemplate => {
|
||||
],
|
||||
},
|
||||
],
|
||||
headline: { default: translate("smileys_survey_question_2_headline", locale) },
|
||||
headline: { default: t("templates.smileys_survey_question_2_headline") },
|
||||
required: true,
|
||||
buttonUrl: "https://formbricks.com/github",
|
||||
buttonLabel: { default: translate("smileys_survey_question_2_button_label", locale) },
|
||||
buttonLabel: { default: t("templates.smileys_survey_question_2_button_label") },
|
||||
buttonExternal: true,
|
||||
},
|
||||
{
|
||||
id: reusableQuestionIds[2],
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("smileys_survey_question_3_headline", locale) },
|
||||
headline: { default: t("templates.smileys_survey_question_3_headline") },
|
||||
required: true,
|
||||
subheader: { default: translate("smileys_survey_question_3_subheader", locale) },
|
||||
buttonLabel: { default: translate("smileys_survey_question_3_button_label", locale) },
|
||||
placeholder: { default: translate("smileys_survey_question_3_placeholder", locale) },
|
||||
subheader: { default: t("templates.smileys_survey_question_3_subheader") },
|
||||
buttonLabel: { default: t("templates.smileys_survey_question_3_button_label") },
|
||||
placeholder: { default: t("templates.smileys_survey_question_3_placeholder") },
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
enabled: false,
|
||||
@@ -405,26 +398,26 @@ const SmileysRatingSurvey = (locale: string): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
const eNPSSurvey = (locale: string): TXMTemplate => {
|
||||
const enpsSurvey = (t: TFnType): TXMTemplate => {
|
||||
return {
|
||||
...getXMSurveyDefault(locale),
|
||||
name: translate("enps_survey_name", locale),
|
||||
...getXMSurveyDefault(t),
|
||||
name: t("templates.enps_survey_name"),
|
||||
questions: [
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.NPS,
|
||||
headline: {
|
||||
default: translate("enps_survey_question_1_headline", locale),
|
||||
default: t("templates.enps_survey_question_1_headline"),
|
||||
},
|
||||
required: false,
|
||||
lowerLabel: { default: translate("enps_survey_question_1_lower_label", locale) },
|
||||
upperLabel: { default: translate("enps_survey_question_1_upper_label", locale) },
|
||||
lowerLabel: { default: t("templates.enps_survey_question_1_lower_label") },
|
||||
upperLabel: { default: t("templates.enps_survey_question_1_upper_label") },
|
||||
isColorCodingEnabled: true,
|
||||
},
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("enps_survey_question_2_headline", locale) },
|
||||
headline: { default: t("templates.enps_survey_question_2_headline") },
|
||||
required: false,
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
@@ -434,7 +427,7 @@ const eNPSSurvey = (locale: string): TXMTemplate => {
|
||||
{
|
||||
id: createId(),
|
||||
type: TSurveyQuestionTypeEnum.OpenText,
|
||||
headline: { default: translate("enps_survey_question_3_headline", locale) },
|
||||
headline: { default: t("templates.enps_survey_question_3_headline") },
|
||||
required: false,
|
||||
inputType: "text",
|
||||
charLimit: {
|
||||
@@ -445,18 +438,15 @@ const eNPSSurvey = (locale: string): TXMTemplate => {
|
||||
};
|
||||
};
|
||||
|
||||
export const getXMTemplates = (locale: string): TXMTemplate[] => {
|
||||
export const getXMTemplates = (t: TFnType): TXMTemplate[] => {
|
||||
try {
|
||||
if (!validateLocale(locale)) {
|
||||
throw new Error("Invalid locale");
|
||||
}
|
||||
return [
|
||||
NPSSurvey(locale),
|
||||
StarRatingSurvey(locale),
|
||||
CSATSurvey(locale),
|
||||
CESSurvey(locale),
|
||||
SmileysRatingSurvey(locale),
|
||||
eNPSSurvey(locale),
|
||||
npsSurvey(t),
|
||||
starRatingSurvey(t),
|
||||
csatSurvey(t),
|
||||
cessSurvey(t),
|
||||
smileysRatingSurvey(t),
|
||||
enpsSurvey(t),
|
||||
];
|
||||
} catch (error) {
|
||||
logError(error, "getXMTemplates");
|
||||
|
||||
@@ -3,9 +3,9 @@ import { getOrganizationIdFromEnvironmentId } from "@/lib/utils/helper";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { XIcon } from "lucide-react";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import Link from "next/link";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getProjectByEnvironmentId, getUserProjects } from "@formbricks/lib/project/service";
|
||||
@@ -21,7 +21,7 @@ const Page = async (props: XMTemplatePageProps) => {
|
||||
const params = await props.params;
|
||||
const session = await getServerSession(authOptions);
|
||||
const environment = await getEnvironment(params.environmentId);
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
if (!session) {
|
||||
throw new Error(t("common.session_not_found"));
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ import {
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowUpRightIcon, ChevronRightIcon, LogOutIcon, PlusIcon } from "lucide-react";
|
||||
import { signOut } from "next-auth/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
@@ -44,7 +44,7 @@ export const LandingSidebar = ({
|
||||
}: LandingSidebarProps) => {
|
||||
const [openCreateOrganizationModal, setOpenCreateOrganizationModal] = useState<boolean>(false);
|
||||
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@@ -127,7 +127,7 @@ export const LandingSidebar = ({
|
||||
await signOut({ callbackUrl: "/auth/login" });
|
||||
await formbricksLogout();
|
||||
}}
|
||||
icon={<LogOutIcon className="h-4 w-4" strokeWidth={1.5} />}>
|
||||
icon={<LogOutIcon className="mr-2 h-4 w-4" strokeWidth={1.5} />}>
|
||||
{t("common.logout")}
|
||||
</DropdownMenuItem>
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@ import { LandingSidebar } from "@/app/(app)/(onboarding)/organizations/[organiza
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { getEnterpriseLicense } from "@/modules/ee/license-check/lib/utils";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { getOrganization, getOrganizationsByUserId } from "@formbricks/lib/organization/service";
|
||||
import { getUser } from "@formbricks/lib/user/service";
|
||||
|
||||
const Page = async (props) => {
|
||||
const params = await props.params;
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session || !session.user) {
|
||||
return redirect(`/auth/login`);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { PosthogIdentify } from "@/app/(app)/environments/[environmentId]/components/PosthogIdentify";
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { ToasterClient } from "@/modules/ui/components/toaster-client";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { redirect } from "next/navigation";
|
||||
import { canUserAccessOrganization } from "@formbricks/lib/organization/auth";
|
||||
import { getOrganization } from "@formbricks/lib/organization/service";
|
||||
@@ -14,7 +14,7 @@ const ProjectOnboardingLayout = async (props) => {
|
||||
|
||||
const { children } = props;
|
||||
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session || !session.user) {
|
||||
return redirect(`/auth/login`);
|
||||
|
||||
@@ -2,9 +2,9 @@ import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizatio
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { PictureInPicture2Icon, SendIcon, XIcon } from "lucide-react";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getUserProjects } from "@formbricks/lib/project/service";
|
||||
@@ -22,7 +22,7 @@ const Page = async (props: ChannelPageProps) => {
|
||||
return redirect(`/auth/login`);
|
||||
}
|
||||
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const channelOptions = [
|
||||
{
|
||||
title: t("organizations.projects.new.channel.link_and_email_surveys"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { getOrganizationProjectsLimit } from "@/modules/ee/license-check/lib/utils";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
@@ -12,7 +12,7 @@ const OnboardingLayout = async (props) => {
|
||||
const params = await props.params;
|
||||
|
||||
const { children } = props;
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session || !session.user) {
|
||||
|
||||
@@ -2,9 +2,9 @@ import { OnboardingOptionsContainer } from "@/app/(app)/(onboarding)/organizatio
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { HeartIcon, ListTodoIcon, XIcon } from "lucide-react";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { getUserProjects } from "@formbricks/lib/project/service";
|
||||
@@ -22,7 +22,7 @@ const Page = async (props: ModePageProps) => {
|
||||
return redirect(`/auth/login`);
|
||||
}
|
||||
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const channelOptions = [
|
||||
{
|
||||
title: t("organizations.projects.new.mode.formbricks_surveys"),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { createProjectAction } from "@/app/(app)/environments/[environmentId]/actions";
|
||||
import { previewSurvey } from "@/app/lib/templates";
|
||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||
import { TOrganizationTeam } from "@/modules/ee/teams/project-teams/types/team";
|
||||
import { CreateTeamModal } from "@/modules/ee/teams/team-list/components/create-team-modal";
|
||||
@@ -19,14 +20,13 @@ import { Input } from "@/modules/ui/components/input";
|
||||
import { MultiSelect } from "@/modules/ui/components/multi-select";
|
||||
import { SurveyInline } from "@/modules/ui/components/survey";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { FORMBRICKS_SURVEYS_FILTERS_KEY_LS } from "@formbricks/lib/localStorage";
|
||||
import { getPreviewSurvey } from "@formbricks/lib/styling/constants";
|
||||
import {
|
||||
TProjectConfigChannel,
|
||||
TProjectConfigIndustry,
|
||||
@@ -43,7 +43,6 @@ interface ProjectSettingsProps {
|
||||
defaultBrandColor: string;
|
||||
organizationTeams: TOrganizationTeam[];
|
||||
canDoRoleManagement: boolean;
|
||||
locale: string;
|
||||
userProjectsCount: number;
|
||||
}
|
||||
|
||||
@@ -55,13 +54,12 @@ export const ProjectSettings = ({
|
||||
defaultBrandColor,
|
||||
organizationTeams,
|
||||
canDoRoleManagement = false,
|
||||
locale,
|
||||
userProjectsCount,
|
||||
}: ProjectSettingsProps) => {
|
||||
const [createTeamModalOpen, setCreateTeamModalOpen] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const addProject = async (data: TProjectUpdateInput) => {
|
||||
try {
|
||||
const createProjectResponse = await createProjectAction({
|
||||
@@ -233,7 +231,7 @@ export const ProjectSettings = ({
|
||||
<p className="text-sm text-slate-400">{t("common.preview")}</p>
|
||||
<div className="z-0 h-3/4 w-3/4">
|
||||
<SurveyInline
|
||||
survey={getPreviewSurvey(locale, projectName || "my Product")}
|
||||
survey={previewSurvey(projectName || "my Product", t)}
|
||||
styling={{ brandColor: { light: brandColor } }}
|
||||
isBrandingEnabled={false}
|
||||
languageCode="default"
|
||||
|
||||
@@ -4,15 +4,14 @@ import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { getRoleManagementPermission } from "@/modules/ee/license-check/lib/utils";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Header } from "@/modules/ui/components/header";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { XIcon } from "lucide-react";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { DEFAULT_BRAND_COLOR, DEFAULT_LOCALE } from "@formbricks/lib/constants";
|
||||
import { DEFAULT_BRAND_COLOR } from "@formbricks/lib/constants";
|
||||
import { getOrganization } from "@formbricks/lib/organization/service";
|
||||
import { getUserProjects } from "@formbricks/lib/project/service";
|
||||
import { getUserLocale } from "@formbricks/lib/user/service";
|
||||
import { TProjectConfigChannel, TProjectConfigIndustry, TProjectMode } from "@formbricks/types/project";
|
||||
|
||||
interface ProjectSettingsPageProps {
|
||||
@@ -29,7 +28,7 @@ interface ProjectSettingsPageProps {
|
||||
const Page = async (props: ProjectSettingsPageProps) => {
|
||||
const searchParams = await props.searchParams;
|
||||
const params = await props.params;
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const session = await getServerSession(authOptions);
|
||||
|
||||
if (!session || !session.user) {
|
||||
@@ -39,7 +38,6 @@ const Page = async (props: ProjectSettingsPageProps) => {
|
||||
const channel = searchParams.channel || null;
|
||||
const industry = searchParams.industry || null;
|
||||
const mode = searchParams.mode || "surveys";
|
||||
const locale = session?.user.id ? await getUserLocale(session.user.id) : undefined;
|
||||
const projects = await getUserProjects(session.user.id, params.organizationId);
|
||||
|
||||
const organizationTeams = await getTeamsByOrganizationId(params.organizationId);
|
||||
@@ -70,7 +68,6 @@ const Page = async (props: ProjectSettingsPageProps) => {
|
||||
defaultBrandColor={DEFAULT_BRAND_COLOR}
|
||||
organizationTeams={organizationTeams}
|
||||
canDoRoleManagement={canDoRoleManagement}
|
||||
locale={locale ?? DEFAULT_LOCALE}
|
||||
userProjectsCount={projects.length}
|
||||
/>
|
||||
{projects.length >= 1 && (
|
||||
|
||||
@@ -4,8 +4,8 @@ import { ResponseFilterProvider } from "@/app/(app)/environments/[environmentId]
|
||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||
import { DevEnvironmentBanner } from "@/modules/ui/components/dev-environment-banner";
|
||||
import { ToasterClient } from "@/modules/ui/components/toaster-client";
|
||||
import { getTranslate } from "@/tolgee/server";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import { redirect } from "next/navigation";
|
||||
import { hasUserEnvironmentAccess } from "@formbricks/lib/environment/auth";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
@@ -18,7 +18,7 @@ const SurveyEditorEnvironmentLayout = async (props) => {
|
||||
|
||||
const { children } = props;
|
||||
|
||||
const t = await getTranslations();
|
||||
const t = await getTranslate();
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session || !session.user) {
|
||||
return redirect(`/auth/login`);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { ModalWithTabs } from "@/modules/ui/components/modal-with-tabs";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { TActionClass } from "@formbricks/types/action-classes";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
import { CreateNewActionTab } from "./CreateNewActionTab";
|
||||
@@ -28,7 +28,7 @@ export const AddActionModal = ({
|
||||
isReadOnly,
|
||||
environmentId,
|
||||
}: AddActionModalProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const tabs = [
|
||||
{
|
||||
title: t("environments.surveys.edit.select_saved_action"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface AddEndingCardButtonProps {
|
||||
@@ -11,7 +11,7 @@ interface AddEndingCardButtonProps {
|
||||
}
|
||||
|
||||
export const AddEndingCardButton = ({ localSurvey, addEndingCard }: AddEndingCardButtonProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<div
|
||||
className="group inline-flex rounded-lg border border-slate-300 bg-slate-50 hover:cursor-pointer hover:bg-white"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import {
|
||||
@@ -19,14 +19,13 @@ interface AddQuestionButtonProps {
|
||||
addQuestion: (question: any) => void;
|
||||
project: TProject;
|
||||
isCxMode: boolean;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export const AddQuestionButton = ({ addQuestion, project, isCxMode, locale }: AddQuestionButtonProps) => {
|
||||
const t = useTranslations();
|
||||
export const AddQuestionButton = ({ addQuestion, project, isCxMode }: AddQuestionButtonProps) => {
|
||||
const { t } = useTranslate();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [hoveredQuestionId, setHoveredQuestionId] = useState<string | null>(null);
|
||||
const availableQuestionTypes = isCxMode ? getCXQuestionTypes(locale) : getQuestionTypes(locale);
|
||||
const availableQuestionTypes = isCxMode ? getCXQuestionTypes(t) : getQuestionTypes(t);
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
return (
|
||||
@@ -60,7 +59,7 @@ export const AddQuestionButton = ({ addQuestion, project, isCxMode, locale }: Ad
|
||||
onClick={() => {
|
||||
addQuestion({
|
||||
...universalQuestionPresets,
|
||||
...getQuestionDefaults(questionType.id, project, locale),
|
||||
...getQuestionDefaults(questionType.id, project, t),
|
||||
id: createId(),
|
||||
type: questionType.id,
|
||||
});
|
||||
|
||||
@@ -4,8 +4,8 @@ import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInpu
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { QuestionToggleTable } from "@/modules/ui/components/question-toggle-table";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { type JSX, useEffect } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurvey, TSurveyAddressQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -34,7 +34,7 @@ export const AddressQuestionForm = ({
|
||||
locale,
|
||||
}: AddressQuestionFormProps): JSX.Element => {
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages ?? []);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const fields = [
|
||||
{
|
||||
id: "addressLine1",
|
||||
|
||||
@@ -5,8 +5,8 @@ import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@/
|
||||
import { Slider } from "@/modules/ui/components/slider";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TProjectStyling } from "@formbricks/types/project";
|
||||
@@ -34,7 +34,7 @@ export const BackgroundStylingCard = ({
|
||||
isUnsplashConfigured,
|
||||
form,
|
||||
}: BackgroundStylingCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { OptionsSwitch } from "@/modules/ui/components/options-switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { type JSX, useState } from "react";
|
||||
import { TSurvey, TSurveyCTAQuestion } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -34,7 +34,7 @@ export const CTAQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: CTAQuestionFormProps): JSX.Element => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const options = [
|
||||
{
|
||||
value: "internal",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
|
||||
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { type JSX, useEffect, useState } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurvey, TSurveyCalQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -34,7 +36,7 @@ export const CalQuestionForm = ({
|
||||
}: CalQuestionFormProps): JSX.Element => {
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages);
|
||||
const [isCalHostEnabled, setIsCalHostEnabled] = useState(!!question.calHost);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
useEffect(() => {
|
||||
if (!isCalHostEnabled) {
|
||||
updateQuestion(questionIdx, { calHost: undefined });
|
||||
|
||||
@@ -8,8 +8,8 @@ import { Slider } from "@/modules/ui/components/slider";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import React from "react";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
@@ -36,7 +36,7 @@ export const CardStylingSettings = ({
|
||||
setOpen,
|
||||
form,
|
||||
}: CardStylingSettingsProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const isAppSurvey = surveyType === "app";
|
||||
const surveyTypeDerived = isAppSurvey ? "App" : "Link";
|
||||
const isLogoVisible = !!project.logo?.url;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { LogicEditor } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/LogicEditor";
|
||||
import {
|
||||
getDefaultOperatorForQuestion,
|
||||
@@ -13,6 +15,7 @@ import {
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import {
|
||||
ArrowDownIcon,
|
||||
ArrowUpIcon,
|
||||
@@ -22,7 +25,6 @@ import {
|
||||
SplitIcon,
|
||||
TrashIcon,
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useMemo } from "react";
|
||||
import { duplicateLogicItem } from "@formbricks/lib/surveyLogic/utils";
|
||||
import { replaceHeadlineRecall } from "@formbricks/lib/utils/recall";
|
||||
@@ -41,7 +43,7 @@ export function ConditionalLogic({
|
||||
questionIdx,
|
||||
updateQuestion,
|
||||
}: ConditionalLogicProps) {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const transformedSurvey = useMemo(() => {
|
||||
let modifiedSurvey = replaceHeadlineRecall(localSurvey, "default");
|
||||
modifiedSurvey = replaceEndingCardHeadlineRecall(modifiedSurvey, "default");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { LocalizedEditor } from "@/modules/ee/multi-language-surveys/components/localized-editor";
|
||||
import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { type JSX, useState } from "react";
|
||||
import { TSurvey, TSurveyConsentQuestion } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -30,7 +30,7 @@ export const ConsentQuestionForm = ({
|
||||
locale,
|
||||
}: ConsentQuestionFormProps): JSX.Element => {
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
return (
|
||||
<form>
|
||||
<QuestionFormInput
|
||||
|
||||
@@ -4,8 +4,8 @@ import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInpu
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { QuestionToggleTable } from "@/modules/ui/components/question-toggle-table";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { type JSX, useEffect } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurvey, TSurveyContactInfoQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -33,7 +33,7 @@ export const ContactInfoQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: ContactInfoQuestionFormProps): JSX.Element => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages ?? []);
|
||||
|
||||
const fields = [
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { isValidCssSelector } from "@/app/lib/actionClass/actionClass";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { CodeActionForm } from "@/modules/ui/components/code-action-form";
|
||||
@@ -7,7 +9,7 @@ import { Label } from "@/modules/ui/components/label";
|
||||
import { NoCodeActionForm } from "@/modules/ui/components/no-code-action-form";
|
||||
import { TabToggle } from "@/modules/ui/components/tab-toggle";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useMemo } from "react";
|
||||
import { FormProvider, useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -38,7 +40,7 @@ export const CreateNewActionTab = ({
|
||||
setLocalSurvey,
|
||||
environmentId,
|
||||
}: CreateNewActionTabProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const actionClassNames = useMemo(
|
||||
() => actionClasses.map((actionClass) => actionClass.name),
|
||||
[actionClasses]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { OptionsSwitch } from "@/modules/ui/components/options-switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { JSX } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurvey, TSurveyDateQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -48,7 +50,7 @@ export const DateQuestionForm = ({
|
||||
locale,
|
||||
}: IDateQuestionFormProps): JSX.Element => {
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [parent] = useAutoAnimate();
|
||||
return (
|
||||
<form>
|
||||
|
||||
@@ -14,8 +14,8 @@ import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { GripIcon, Handshake, Undo2 } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
@@ -59,7 +59,7 @@ export const EditEndingCard = ({
|
||||
locale,
|
||||
}: EditEndingCardProps) => {
|
||||
const endingCard = localSurvey.endings[endingCardIndex];
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const isRedirectToUrlDisabled = isFormbricksCloud
|
||||
? plan === "free" && endingCard.type !== "redirectToUrl"
|
||||
: false;
|
||||
@@ -231,7 +231,6 @@ export const EditEndingCard = ({
|
||||
updateCard={() => {}}
|
||||
addCard={addEndingCard}
|
||||
cardType="ending"
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,8 +6,8 @@ import { FileInput } from "@/modules/ui/components/file-input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Hand } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
@@ -35,7 +35,7 @@ export const EditWelcomeCard = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: EditWelcomeCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
const path = usePathname();
|
||||
const environmentId = path?.split("/environments/")[1]?.split("/")[0];
|
||||
|
||||
@@ -13,13 +13,13 @@ import {
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import { TooltipRenderer } from "@/modules/ui/components/tooltip";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowDownIcon, ArrowUpIcon, CopyIcon, EllipsisIcon, TrashIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
QUESTIONS_ICON_MAP,
|
||||
getCXQuestionNameMap,
|
||||
getQuestionDefaults,
|
||||
getQuestionIconMap,
|
||||
getQuestionNameMap,
|
||||
} from "@formbricks/lib/utils/questions";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
@@ -44,7 +44,6 @@ interface EditorCardMenuProps {
|
||||
cardType: "question" | "ending";
|
||||
project?: TProject;
|
||||
isCxMode?: boolean;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export const EditorCardMenu = ({
|
||||
@@ -60,9 +59,9 @@ export const EditorCardMenu = ({
|
||||
addCard,
|
||||
cardType,
|
||||
isCxMode = false,
|
||||
locale,
|
||||
}: EditorCardMenuProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
||||
const [logicWarningModal, setLogicWarningModal] = useState(false);
|
||||
const [changeToType, setChangeToType] = useState(() => {
|
||||
if (card.type !== "endScreen" && card.type !== "redirectToUrl") {
|
||||
@@ -76,7 +75,7 @@ export const EditorCardMenu = ({
|
||||
? survey.questions.length === 1
|
||||
: survey.type === "link" && survey.endings.length === 1;
|
||||
|
||||
const availableQuestionTypes = isCxMode ? getCXQuestionNameMap(locale) : getQuestionNameMap(locale);
|
||||
const availableQuestionTypes = isCxMode ? getCXQuestionNameMap(t) : getQuestionNameMap(t);
|
||||
|
||||
const changeQuestionType = (type?: TSurveyQuestionTypeEnum) => {
|
||||
if (!type) return;
|
||||
@@ -84,7 +83,7 @@ export const EditorCardMenu = ({
|
||||
const { headline, required, subheader, imageUrl, videoUrl, buttonLabel, backButtonLabel } =
|
||||
card as TSurveyQuestion;
|
||||
|
||||
const questionDefaults = getQuestionDefaults(type, project, locale);
|
||||
const questionDefaults = getQuestionDefaults(type, project, t);
|
||||
|
||||
if (
|
||||
(type === TSurveyQuestionTypeEnum.MultipleChoiceSingle &&
|
||||
@@ -123,7 +122,7 @@ export const EditorCardMenu = ({
|
||||
};
|
||||
|
||||
const addQuestionCardBelow = (type: TSurveyQuestionTypeEnum) => {
|
||||
const questionDefaults = getQuestionDefaults(type, project, locale);
|
||||
const questionDefaults = getQuestionDefaults(type, project, t);
|
||||
|
||||
addCard(
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ import { RecallWrapper } from "@/modules/surveys/components/QuestionFormInput/co
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useState } from "react";
|
||||
import { useRef } from "react";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
@@ -34,7 +34,7 @@ export const EndScreenForm = ({
|
||||
endingCard,
|
||||
locale,
|
||||
}: EndScreenFormProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [showEndingCardCTA, setshowEndingCardCTA] = useState<boolean>(
|
||||
endingCard.type === "endScreen" &&
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { useGetBillingInfo } from "@/modules/utils/hooks/useGetBillingInfo";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon, XCircleIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { type JSX, useMemo, useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
@@ -45,7 +45,7 @@ export const FileUploadQuestionForm = ({
|
||||
locale,
|
||||
}: FileUploadFormProps): JSX.Element => {
|
||||
const [extension, setExtension] = useState("");
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [isMaxSizeError, setMaxSizeError] = useState(false);
|
||||
const {
|
||||
billingInfo,
|
||||
@@ -227,7 +227,7 @@ export const FileUploadQuestionForm = ({
|
||||
className="underline"
|
||||
target="_blank"
|
||||
href={`/environments/${localSurvey.environmentId}/settings/billing`}>
|
||||
{t("environments.surveys.edit.upgrade_your_plan")}
|
||||
{t("common.please_upgrade_your_plan")}
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { ColorPicker } from "@/modules/ui/components/color-picker";
|
||||
import { FormControl, FormDescription, FormField, FormItem, FormLabel } from "@/modules/ui/components/form";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CheckIcon, SparklesIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import React from "react";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
@@ -30,7 +30,7 @@ export const FormStylingSettings = ({
|
||||
setOpen,
|
||||
form,
|
||||
}: FormStylingSettingsProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const brandColor = form.watch("brandColor.light") || COLOR_DEFAULTS.brandColor;
|
||||
const background = form.watch("background");
|
||||
const highlightBorderColor = form.watch("highlightBorderColor");
|
||||
|
||||
@@ -8,8 +8,8 @@ import { Switch } from "@/modules/ui/components/switch";
|
||||
import { Tag } from "@/modules/ui/components/tag";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { EyeOff } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
@@ -32,7 +32,7 @@ export const HiddenFieldsCard = ({
|
||||
}: HiddenFieldsCardProps) => {
|
||||
const open = activeQuestionId == "hidden";
|
||||
const [hiddenField, setHiddenField] = useState<string>("");
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const setOpen = (open: boolean) => {
|
||||
if (open) {
|
||||
setActiveQuestionId("hidden");
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import { getDefaultEndingCard } from "@/app/lib/templates";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { RadioGroup, RadioGroupItem } from "@/modules/ui/components/radio-group";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { AlertCircleIcon, CheckIcon, LinkIcon, MonitorIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { getDefaultEndingCard } from "@formbricks/lib/templates";
|
||||
import { TEnvironment } from "@formbricks/types/environment";
|
||||
import { TSegment } from "@formbricks/types/segment";
|
||||
import { TSurvey, TSurveyType } from "@formbricks/types/surveys/types";
|
||||
@@ -19,13 +19,12 @@ interface HowToSendCardProps {
|
||||
localSurvey: TSurvey;
|
||||
setLocalSurvey: (survey: TSurvey | ((TSurvey: TSurvey) => TSurvey)) => void;
|
||||
environment: TEnvironment;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export const HowToSendCard = ({ localSurvey, setLocalSurvey, environment, locale }: HowToSendCardProps) => {
|
||||
export const HowToSendCard = ({ localSurvey, setLocalSurvey, environment }: HowToSendCardProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [appSetupCompleted, setAppSetupCompleted] = useState(false);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
useEffect(() => {
|
||||
if (environment) {
|
||||
setAppSetupCompleted(environment.appSetupCompleted);
|
||||
@@ -35,7 +34,7 @@ export const HowToSendCard = ({ localSurvey, setLocalSurvey, environment, locale
|
||||
const setSurveyType = (type: TSurveyType) => {
|
||||
const endingsTemp = localSurvey.endings;
|
||||
if (type === "link" && localSurvey.endings.length === 0) {
|
||||
endingsTemp.push(getDefaultEndingCard(localSurvey.languages, locale));
|
||||
endingsTemp.push(getDefaultEndingCard(localSurvey.languages, t));
|
||||
}
|
||||
setLocalSurvey((prevSurvey) => ({
|
||||
...prevSurvey,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { LogicEditorActions } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/LogicEditorActions";
|
||||
import { LogicEditorConditions } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/LogicEditorConditions";
|
||||
import {
|
||||
@@ -7,11 +9,11 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/modules/ui/components/select";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowRightIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { ReactElement, useMemo } from "react";
|
||||
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
|
||||
import { QUESTIONS_ICON_MAP } from "@formbricks/lib/utils/questions";
|
||||
import { getQuestionIconMap } from "@formbricks/lib/utils/questions";
|
||||
import { TSurvey, TSurveyLogic, TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface LogicEditorProps {
|
||||
@@ -33,8 +35,8 @@ export function LogicEditor({
|
||||
logicIdx,
|
||||
isLast,
|
||||
}: LogicEditorProps) {
|
||||
const t = useTranslations();
|
||||
|
||||
const { t } = useTranslate();
|
||||
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
||||
const fallbackOptions = useMemo(() => {
|
||||
let options: {
|
||||
icon?: ReactElement;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getActionObjectiveOptions,
|
||||
getActionOperatorOptions,
|
||||
@@ -14,8 +16,8 @@ import {
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import { InputCombobox } from "@/modules/ui/components/input-combo-box";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CopyIcon, CornerDownRightIcon, EllipsisVerticalIcon, PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { getUpdatedActionBody } from "@formbricks/lib/surveyLogic/utils";
|
||||
import {
|
||||
TActionNumberVariableCalculateOperator,
|
||||
@@ -46,7 +48,7 @@ export function LogicEditorActions({
|
||||
questionIdx,
|
||||
}: LogicEditorActions) {
|
||||
const actions = logicItem.actions;
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const handleActionsChange = (
|
||||
operation: "remove" | "addBelow" | "duplicate" | "update",
|
||||
actionIdx: number,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
getConditionOperatorOptions,
|
||||
getConditionValueOptions,
|
||||
@@ -13,8 +15,8 @@ import {
|
||||
import { InputCombobox, TComboboxOption } from "@/modules/ui/components/input-combo-box";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CopyIcon, EllipsisVerticalIcon, PlusIcon, TrashIcon, WorkflowIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import {
|
||||
addConditionBelow,
|
||||
@@ -54,7 +56,7 @@ export function LogicEditorConditions({
|
||||
updateQuestion,
|
||||
depth = 0,
|
||||
}: LogicEditorConditionsProps) {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
const handleAddConditionBelow = (resourceId: string) => {
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Label } from "@/modules/ui/components/label";
|
||||
import { ShuffleOptionSelect } from "@/modules/ui/components/shuffle-option-select";
|
||||
import { TooltipRenderer } from "@/modules/ui/components/tooltip";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { JSX } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TI18nString, TSurvey, TSurveyMatrixQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -37,7 +37,7 @@ export const MatrixQuestionForm = ({
|
||||
locale,
|
||||
}: MatrixQuestionFormProps): JSX.Element => {
|
||||
const languageCodes = extractLanguageCodes(localSurvey.languages);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
// Function to add a new Label input field
|
||||
const handleAddLabel = (type: "row" | "column") => {
|
||||
if (type === "row") {
|
||||
|
||||
@@ -9,8 +9,8 @@ import { DndContext } from "@dnd-kit/core";
|
||||
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { type JSX, useEffect, useRef, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
@@ -46,7 +46,7 @@ export const MultipleChoiceQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: MultipleChoiceQuestionFormProps): JSX.Element => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const lastChoiceRef = useRef<HTMLInputElement>(null);
|
||||
const [isNew, setIsNew] = useState(true);
|
||||
const [isInvalidValue, setisInvalidValue] = useState<string | null>(null);
|
||||
|
||||
@@ -4,8 +4,8 @@ import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInpu
|
||||
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { JSX } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurvey, TSurveyNPSQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -34,7 +34,7 @@ export const NPSQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: NPSQuestionFormProps): JSX.Element => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages);
|
||||
// Auto animate
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
@@ -7,8 +7,8 @@ import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { OptionsSwitch } from "@/modules/ui/components/options-switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { HashIcon, LinkIcon, MailIcon, MessageSquareTextIcon, PhoneIcon, PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { JSX, useEffect, useState } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import {
|
||||
@@ -40,7 +40,7 @@ export const OpenQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: OpenQuestionFormProps): JSX.Element => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const questionTypes = [
|
||||
{ value: "text", label: t("common.text"), icon: <MessageSquareTextIcon className="h-4 w-4" /> },
|
||||
{ value: "email", label: t("common.email"), icon: <MailIcon className="h-4 w-4" /> },
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { FileInput } from "@/modules/ui/components/file-input";
|
||||
@@ -5,8 +7,8 @@ import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { JSX } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
@@ -37,7 +39,7 @@ export const PictureSelectionForm = ({
|
||||
}: PictureSelectionFormProps): JSX.Element => {
|
||||
const environmentId = localSurvey.environmentId;
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages);
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const handleChoiceDeletion = (choiceValue: string) => {
|
||||
// Filter out the deleted choice from the choices array
|
||||
const newChoices = question.choices?.filter((choice) => choice.id !== choiceValue) || [];
|
||||
|
||||
@@ -3,18 +3,10 @@
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { getPlacementStyle } from "@/modules/ui/components/preview-survey/lib/utils";
|
||||
import { RadioGroup, RadioGroupItem } from "@/modules/ui/components/radio-group";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TPlacement } from "@formbricks/types/common";
|
||||
|
||||
const placements = [
|
||||
{ name: "common.bottom_right", value: "bottomRight", disabled: false },
|
||||
{ name: "common.top_right", value: "topRight", disabled: false },
|
||||
{ name: "common.top_left", value: "topLeft", disabled: false },
|
||||
{ name: "common.bottom_left", value: "bottomLeft", disabled: false },
|
||||
{ name: "common.centered_modal", value: "center", disabled: false },
|
||||
];
|
||||
|
||||
interface TPlacementProps {
|
||||
currentPlacement: TPlacement;
|
||||
setCurrentPlacement: (placement: TPlacement) => void;
|
||||
@@ -32,7 +24,14 @@ export const Placement = ({
|
||||
setClickOutsideClose,
|
||||
clickOutsideClose,
|
||||
}: TPlacementProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const placements = [
|
||||
{ name: t("common.bottom_right"), value: "bottomRight", disabled: false },
|
||||
{ name: t("common.top_right"), value: "topRight", disabled: false },
|
||||
{ name: t("common.top_left"), value: "topLeft", disabled: false },
|
||||
{ name: t("common.bottom_left"), value: "bottomLeft", disabled: false },
|
||||
{ name: t("common.centered_modal"), value: "center", disabled: false },
|
||||
];
|
||||
const overlayStyle =
|
||||
currentPlacement === "center" && overlay === "dark" ? "bg-slate-700/80" : "bg-slate-200";
|
||||
return (
|
||||
|
||||
@@ -10,11 +10,11 @@ import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ChevronDownIcon, ChevronRightIcon, GripIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { QUESTIONS_ICON_MAP, getTSurveyQuestionTypeEnumName } from "@formbricks/lib/utils/questions";
|
||||
import { getQuestionIconMap, getTSurveyQuestionTypeEnumName } from "@formbricks/lib/utils/questions";
|
||||
import { recallToHeadline } from "@formbricks/lib/utils/recall";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
import {
|
||||
@@ -84,7 +84,8 @@ export const QuestionCard = ({
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: question.id,
|
||||
});
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
||||
const open = activeQuestionId === question.id;
|
||||
const [openAdvanced, setOpenAdvanced] = useState(question.logic && question.logic.length > 0);
|
||||
const [parent] = useAutoAnimate();
|
||||
@@ -225,7 +226,7 @@ export const QuestionCard = ({
|
||||
selectedLanguageCode
|
||||
] ?? ""
|
||||
)
|
||||
: getTSurveyQuestionTypeEnumName(question.type, locale)}
|
||||
: getTSurveyQuestionTypeEnumName(question.type, t)}
|
||||
</p>
|
||||
{!open && (
|
||||
<p className="mt-1 truncate text-xs text-slate-500">
|
||||
@@ -251,7 +252,6 @@ export const QuestionCard = ({
|
||||
addCard={addQuestion}
|
||||
cardType="question"
|
||||
isCxMode={isCxMode}
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { TooltipRenderer } from "@/modules/ui/components/tooltip";
|
||||
import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { GripVerticalIcon, PlusIcon, TrashIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { createI18nString } from "@formbricks/lib/i18n/utils";
|
||||
import {
|
||||
@@ -56,7 +58,7 @@ export const QuestionOptionChoice = ({
|
||||
updateQuestion,
|
||||
locale,
|
||||
}: ChoiceProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const isDragDisabled = choice.id === "other";
|
||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({
|
||||
id: choice.id,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { AddEndingCardButton } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/AddEndingCardButton";
|
||||
import { SurveyVariablesCard } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/SurveyVariablesCard";
|
||||
import { findQuestionUsedInLogic } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/lib/utils";
|
||||
import { getDefaultEndingCard } from "@/app/lib/templates";
|
||||
import { MultiLanguageCard } from "@/modules/ee/multi-language-surveys/components/multi-language-card";
|
||||
import {
|
||||
DndContext,
|
||||
@@ -15,13 +16,12 @@ import {
|
||||
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import React, { SetStateAction, useEffect, useMemo } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { addMultiLanguageLabels, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { structuredClone } from "@formbricks/lib/pollyfills/structuredClone";
|
||||
import { isConditionGroup } from "@formbricks/lib/surveyLogic/utils";
|
||||
import { getDefaultEndingCard } from "@formbricks/lib/templates";
|
||||
import { checkForEmptyFallBackValue, extractRecallInfo } from "@formbricks/lib/utils/recall";
|
||||
import { TOrganizationBillingPlan } from "@formbricks/types/organizations";
|
||||
import { TProject } from "@formbricks/types/project";
|
||||
@@ -80,7 +80,7 @@ export const QuestionsView = ({
|
||||
isCxMode,
|
||||
locale,
|
||||
}: QuestionsViewProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const internalQuestionIdMap = useMemo(() => {
|
||||
return localSurvey.questions.reduce((acc, question) => {
|
||||
acc[question.id] = createId();
|
||||
@@ -338,7 +338,7 @@ export const QuestionsView = ({
|
||||
|
||||
const addEndingCard = (index: number) => {
|
||||
const updatedSurvey = structuredClone(localSurvey);
|
||||
const newEndingCard = getDefaultEndingCard(localSurvey.languages, locale);
|
||||
const newEndingCard = getDefaultEndingCard(localSurvey.languages, t);
|
||||
|
||||
updatedSurvey.endings.splice(index, 0, newEndingCard);
|
||||
setActiveQuestionId(newEndingCard.id);
|
||||
@@ -461,7 +461,7 @@ export const QuestionsView = ({
|
||||
/>
|
||||
</DndContext>
|
||||
|
||||
<AddQuestionButton addQuestion={addQuestion} project={project} isCxMode={isCxMode} locale={locale} />
|
||||
<AddQuestionButton addQuestion={addQuestion} project={project} isCxMode={isCxMode} />
|
||||
<div className="mt-5 flex flex-col gap-5" ref={parent}>
|
||||
<hr className="border-t border-dashed" />
|
||||
<DndContext
|
||||
|
||||
@@ -8,8 +8,8 @@ import { DndContext } from "@dnd-kit/core";
|
||||
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { type JSX, useEffect, useRef, useState } from "react";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TI18nString, TSurvey, TSurveyRankingQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -38,7 +38,7 @@ export const RankingQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: RankingQuestionFormProps): JSX.Element => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const lastChoiceRef = useRef<HTMLInputElement>(null);
|
||||
const [isInvalidValue, setIsInvalidValue] = useState<string | null>(null);
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { QuestionFormInput } from "@/modules/surveys/components/QuestionFormInput";
|
||||
import { AdvancedOptionToggle } from "@/modules/ui/components/advanced-option-toggle";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { HashIcon, PlusIcon, SmileIcon, StarIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { createI18nString, extractLanguageCodes } from "@formbricks/lib/i18n/utils";
|
||||
import { TSurvey, TSurveyRatingQuestion } from "@formbricks/types/surveys/types";
|
||||
import { TUserLocale } from "@formbricks/types/user";
|
||||
@@ -32,7 +34,7 @@ export const RatingQuestionForm = ({
|
||||
setSelectedLanguageCode,
|
||||
locale,
|
||||
}: RatingQuestionFormProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages);
|
||||
const [parent] = useAutoAnimate();
|
||||
return (
|
||||
|
||||
@@ -6,10 +6,10 @@ import { Label } from "@/modules/ui/components/label";
|
||||
import { RadioGroup, RadioGroupItem } from "@/modules/ui/components/radio-group";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface DisplayOption {
|
||||
@@ -18,29 +18,6 @@ interface DisplayOption {
|
||||
description: string;
|
||||
}
|
||||
|
||||
const displayOptions: DisplayOption[] = [
|
||||
{
|
||||
id: "displayOnce",
|
||||
name: "environments.surveys.edit.show_only_once",
|
||||
description: "environments.surveys.edit.the_survey_will_be_shown_once_even_if_person_doesnt_respond",
|
||||
},
|
||||
{
|
||||
id: "displaySome",
|
||||
name: "environments.surveys.edit.show_multiple_times",
|
||||
description: "environments.surveys.edit.the_survey_will_be_shown_multiple_times_until_they_respond",
|
||||
},
|
||||
{
|
||||
id: "displayMultiple",
|
||||
name: "environments.surveys.edit.until_they_submit_a_response",
|
||||
description: "environments.surveys.edit.if_you_really_want_that_answer_ask_until_you_get_it",
|
||||
},
|
||||
{
|
||||
id: "respondMultiple",
|
||||
name: "environments.surveys.edit.keep_showing_while_conditions_match",
|
||||
description: "environments.surveys.edit.even_after_they_submitted_a_response_e_g_feedback_box",
|
||||
},
|
||||
];
|
||||
|
||||
interface RecontactOptionsCardProps {
|
||||
localSurvey: TSurvey;
|
||||
setLocalSurvey: (survey: TSurvey) => void;
|
||||
@@ -52,7 +29,38 @@ export const RecontactOptionsCard = ({
|
||||
setLocalSurvey,
|
||||
environmentId,
|
||||
}: RecontactOptionsCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
|
||||
const displayOptions: DisplayOption[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "displayOnce",
|
||||
name: t("environments.surveys.edit.show_only_once"),
|
||||
description: t(
|
||||
"environments.surveys.edit.the_survey_will_be_shown_once_even_if_person_doesnt_respond"
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "displaySome",
|
||||
name: t("environments.surveys.edit.show_multiple_times"),
|
||||
description: t(
|
||||
"environments.surveys.edit.the_survey_will_be_shown_multiple_times_until_they_respond"
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "displayMultiple",
|
||||
name: t("environments.surveys.edit.until_they_submit_a_response"),
|
||||
description: t("environments.surveys.edit.if_you_really_want_that_answer_ask_until_you_get_it"),
|
||||
},
|
||||
{
|
||||
id: "respondMultiple",
|
||||
name: t("environments.surveys.edit.keep_showing_while_conditions_match"),
|
||||
description: t("environments.surveys.edit.even_after_they_submitted_a_response_e_g_feedback_box"),
|
||||
},
|
||||
],
|
||||
[t]
|
||||
);
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const ignoreWaiting = localSurvey.recontactDays !== null;
|
||||
const [inputDays, setInputDays] = useState(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { RecallWrapper } from "@/modules/surveys/components/QuestionFormInput/components/RecallWrapper";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useRef } from "react";
|
||||
import { headlineToRecall, recallToHeadline } from "@formbricks/lib/utils/recall";
|
||||
import { TSurvey, TSurveyRedirectUrlCard } from "@formbricks/types/surveys/types";
|
||||
@@ -14,7 +16,7 @@ interface RedirectUrlFormProps {
|
||||
|
||||
export const RedirectUrlForm = ({ localSurvey, endingCard, updateSurvey }: RedirectUrlFormProps) => {
|
||||
const selectedLanguageCode = "default";
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,8 +7,8 @@ import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { ArrowUpRight, CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { KeyboardEventHandler, useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -26,7 +26,7 @@ export const ResponseOptionsCard = ({
|
||||
setLocalSurvey,
|
||||
responseCount,
|
||||
}: ResponseOptionsCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [open, setOpen] = useState(localSurvey.type === "link" ? true : false);
|
||||
const autoComplete = localSurvey.autoComplete !== null;
|
||||
const [runOnDateToggle, setRunOnDateToggle] = useState(false);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { Code2Icon, MousePointerClickIcon, SparklesIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
import { TActionClass } from "@formbricks/types/action-classes";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
@@ -18,7 +20,7 @@ export const SavedActionsTab = ({
|
||||
setLocalSurvey,
|
||||
setOpen,
|
||||
}: SavedActionsTabProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const availableActions = actionClasses.filter(
|
||||
(actionClass) => !localSurvey.triggers.some((trigger) => trigger.actionClass.id === actionClass.id)
|
||||
);
|
||||
|
||||
@@ -23,7 +23,6 @@ interface SettingsViewProps {
|
||||
responseCount: number;
|
||||
membershipRole?: TOrganizationRole;
|
||||
isUserTargetingAllowed?: boolean;
|
||||
locale: string;
|
||||
projectPermission: TTeamPermission | null;
|
||||
isFormbricksCloud: boolean;
|
||||
}
|
||||
@@ -38,7 +37,6 @@ export const SettingsView = ({
|
||||
responseCount,
|
||||
membershipRole,
|
||||
isUserTargetingAllowed = false,
|
||||
locale,
|
||||
projectPermission,
|
||||
isFormbricksCloud,
|
||||
}: SettingsViewProps) => {
|
||||
@@ -46,12 +44,7 @@ export const SettingsView = ({
|
||||
|
||||
return (
|
||||
<div className="mt-12 space-y-3 p-5">
|
||||
<HowToSendCard
|
||||
localSurvey={localSurvey}
|
||||
setLocalSurvey={setLocalSurvey}
|
||||
environment={environment}
|
||||
locale={locale}
|
||||
/>
|
||||
<HowToSendCard localSurvey={localSurvey} setLocalSurvey={setLocalSurvey} environment={environment} />
|
||||
|
||||
{localSurvey.type === "app" ? (
|
||||
<div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { AlertDialog } from "@/modules/ui/components/alert-dialog";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import {
|
||||
@@ -9,8 +11,8 @@ import {
|
||||
FormProvider,
|
||||
} from "@/modules/ui/components/form";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { RotateCcwIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { UseFormReturn, useForm } from "react-hook-form";
|
||||
@@ -50,7 +52,7 @@ export const StylingView = ({
|
||||
isUnsplashConfigured,
|
||||
isCxMode,
|
||||
}: StylingViewProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
|
||||
const form = useForm<TSurveyStyling>({
|
||||
defaultValues: { ...defaultStyling, ...project.styling, ...localSurvey.styling },
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { TabBar } from "@/modules/ui/components/tab-bar";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AnimatedSurveyBg } from "./AnimatedSurveyBg";
|
||||
import { ColorSurveyBg } from "./ColorSurveyBg";
|
||||
@@ -25,7 +27,7 @@ export const SurveyBgSelectorTab = ({
|
||||
isUnsplashConfigured,
|
||||
}: SurveyBgSelectorTabProps) => {
|
||||
const [activeTab, setActiveTab] = useState(bgType || "color");
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [parent] = useAutoAnimate();
|
||||
const [colorBackground, setColorBackground] = useState(bg);
|
||||
const [animationBackground, setAnimationBackground] = useState(bg);
|
||||
|
||||
@@ -217,7 +217,6 @@ export const SurveyEditor = ({
|
||||
responseCount={responseCount}
|
||||
membershipRole={membershipRole}
|
||||
isUserTargetingAllowed={isUserTargetingAllowed}
|
||||
locale={locale}
|
||||
projectPermission={projectPermission}
|
||||
isFormbricksCloud={isFormbricksCloud}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { ProBadge } from "@/modules/ui/components/pro-badge";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { MailIcon, PaintbrushIcon, Rows3Icon, SettingsIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { type JSX, useMemo } from "react";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TSurveyEditorTabs } from "@formbricks/types/surveys/types";
|
||||
@@ -27,27 +29,27 @@ export const SurveyEditorTabs = ({
|
||||
isCxMode,
|
||||
isSurveyFollowUpsAllowed = false,
|
||||
}: SurveyEditorTabsProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const tabsComputed = useMemo(() => {
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
id: "questions",
|
||||
label: "common.questions",
|
||||
label: t("common.questions"),
|
||||
icon: <Rows3Icon className="h-5 w-5" />,
|
||||
},
|
||||
{
|
||||
id: "styling",
|
||||
label: "common.styling",
|
||||
label: t("common.styling"),
|
||||
icon: <PaintbrushIcon className="h-5 w-5" />,
|
||||
},
|
||||
{
|
||||
id: "settings",
|
||||
label: "common.settings",
|
||||
label: t("common.settings"),
|
||||
icon: <SettingsIcon className="h-5 w-5" />,
|
||||
},
|
||||
{
|
||||
id: "followUps",
|
||||
label: "environments.surveys.edit.follow_ups",
|
||||
label: t("environments.surveys.edit.follow_ups"),
|
||||
icon: <MailIcon className="h-5 w-5" />,
|
||||
isPro: !isSurveyFollowUpsAllowed,
|
||||
},
|
||||
@@ -78,7 +80,7 @@ export const SurveyEditorTabs = ({
|
||||
)}
|
||||
aria-current={tab.id === activeId ? "page" : undefined}>
|
||||
{tab.icon && <div className="mr-2 h-5 w-5">{tab.icon}</div>}
|
||||
{t(tab.label)}
|
||||
{tab.label}
|
||||
{tab.isPro && <ProBadge />}
|
||||
</button>
|
||||
))}
|
||||
|
||||
@@ -6,9 +6,9 @@ import { AlertDialog } from "@/modules/ui/components/alert-dialog";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { isEqual } from "lodash";
|
||||
import { AlertTriangleIcon, ArrowLeftIcon, SettingsIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -57,7 +57,7 @@ export const SurveyMenuBar = ({
|
||||
isCxMode,
|
||||
locale,
|
||||
}: SurveyMenuBarProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const router = useRouter();
|
||||
const [audiencePrompt, setAudiencePrompt] = useState(true);
|
||||
const [isLinkSurvey, setIsLinkSurvey] = useState(true);
|
||||
|
||||
@@ -4,8 +4,8 @@ import { Label } from "@/modules/ui/components/label";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { CheckIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { TPlacement } from "@formbricks/types/common";
|
||||
@@ -23,7 +23,7 @@ export const SurveyPlacementCard = ({
|
||||
setLocalSurvey,
|
||||
environmentId,
|
||||
}: SurveyPlacementCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { projectOverwrites } = localSurvey ?? {};
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { FileDigitIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TSurvey, TSurveyQuestionId } from "@formbricks/types/surveys/types";
|
||||
import { SurveyVariablesCardItem } from "./SurveyVariablesCardItem";
|
||||
@@ -24,7 +24,7 @@ export const SurveyVariablesCard = ({
|
||||
setActiveQuestionId,
|
||||
}: SurveyVariablesCardProps) => {
|
||||
const open = activeQuestionId === variablesCardId;
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [parent] = useAutoAnimate();
|
||||
|
||||
const setOpenState = (state: boolean) => {
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
SelectValue,
|
||||
} from "@/modules/ui/components/select";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { TrashIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import React, { useCallback, useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -34,7 +34,7 @@ export const SurveyVariablesCardItem = ({
|
||||
setLocalSurvey,
|
||||
mode,
|
||||
}: SurveyVariablesCardItemProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const form = useForm<TSurveyVariable>({
|
||||
defaultValues: variable ?? {
|
||||
id: createId(),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { UpgradePrompt } from "@/modules/ui/components/upgrade-prompt";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { LockIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useState } from "react";
|
||||
|
||||
interface TargetingLockedCardProps {
|
||||
@@ -10,7 +12,7 @@ interface TargetingLockedCardProps {
|
||||
}
|
||||
|
||||
export const TargetingLockedCard = ({ isFormbricksCloud, environmentId }: TargetingLockedCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { LoadingSpinner } from "@/modules/ui/components/loading-spinner";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { debounce } from "lodash";
|
||||
import { SearchIcon } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import UnsplashImage from "next/image";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -121,7 +121,7 @@ const defaultImages = [
|
||||
];
|
||||
|
||||
export const ImageFromUnsplashSurveyBg = ({ handleBgChange }: ImageFromUnsplashSurveyBgProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const inputFocus = useRef<HTMLInputElement>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { Label } from "@/modules/ui/components/label";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
@@ -22,7 +22,7 @@ export const UpdateQuestionId = ({
|
||||
questionIdx,
|
||||
updateQuestion,
|
||||
}: UpdateQuestionIdProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [currentValue, setCurrentValue] = useState(question.id);
|
||||
const [prevValue, setPrevValue] = useState(question.id);
|
||||
const [isInputInvalid, setIsInputInvalid] = useState(
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Button } from "@/modules/ui/components/button";
|
||||
import { Input } from "@/modules/ui/components/input";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import {
|
||||
CheckIcon,
|
||||
Code2Icon,
|
||||
@@ -15,7 +16,6 @@ import {
|
||||
SparklesIcon,
|
||||
Trash2Icon,
|
||||
} from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { TActionClass } from "@formbricks/types/action-classes";
|
||||
@@ -40,7 +40,7 @@ export const WhenToSendCard = ({
|
||||
membershipRole,
|
||||
projectPermission,
|
||||
}: WhenToSendCardProps) => {
|
||||
const t = useTranslations();
|
||||
const { t } = useTranslate();
|
||||
const [open, setOpen] = useState(localSurvey.type === "app" ? true : false);
|
||||
const [isAddActionModalOpen, setAddActionModalOpen] = useState(false);
|
||||
const [actionClasses, setActionClasses] = useState<TActionClass[]>(propActionClasses);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { TSurveyQuestionTypeEnum, ZSurveyLogicConditionsOperator } from "@formbricks/types/surveys/types";
|
||||
|
||||
export const getLogicRules = (t: (key: string) => string) => {
|
||||
export const getLogicRules = (t: TFnType) => {
|
||||
return {
|
||||
question: {
|
||||
[`${TSurveyQuestionTypeEnum.OpenText}.text`]: {
|
||||
|
||||