feat: tolgee (#4692)

Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
This commit is contained in:
Dhruwang Jariwala
2025-02-07 11:19:35 +05:30
committed by GitHub
parent 9c33e77755
commit 36378e9c23
497 changed files with 4314 additions and 4618 deletions

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

@@ -58,4 +58,5 @@ packages/lib/uploads
# js compiled assets
apps/web/public/js
packages/database/migrations

1
.husky/post-checkout Normal file
View File

@@ -0,0 +1 @@
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json

1
.husky/post-commit Normal file
View File

@@ -0,0 +1 @@
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ../branch.json

View File

@@ -1 +1,3 @@
pnpm lint-staged
pnpm lint-staged
pnpm tolgee-pull || true
git add packages/lib/messages/*.json

31
.tolgeerc.json Normal file
View 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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -217,7 +217,6 @@ export const SurveyEditor = ({
responseCount={responseCount}
membershipRole={membershipRole}
isUserTargetingAllowed={isUserTargetingAllowed}
locale={locale}
projectPermission={projectPermission}
isFormbricksCloud={isFormbricksCloud}
/>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
import { TComboboxGroupedOption, TComboboxOption } from "@/modules/ui/components/input-combo-box";
import { TFnType } from "@tolgee/react";
import { EyeOffIcon, FileDigitIcon, FileType2Icon } from "lucide-react";
import { HTMLInputTypeAttribute } from "react";
import { getLocalizedValue } from "@formbricks/lib/i18n/utils";
import { isConditionGroup } from "@formbricks/lib/surveyLogic/utils";
import { translate } from "@formbricks/lib/templates";
import { getQuestionTypes } from "@formbricks/lib/utils/questions";
import { recallToHeadline } from "@formbricks/lib/utils/recall";
import {
@@ -21,7 +21,6 @@ import {
TSurveyQuestionTypeEnum,
TSurveyVariable,
} from "@formbricks/types/surveys/types";
import { TUserLocale } from "@formbricks/types/user";
import { TLogicRuleOption, getLogicRules } from "./logicRuleEngine";
// formats the text to highlight specific parts of the text with slashes
@@ -43,18 +42,19 @@ export const formatTextWithSlashes = (text: string) => {
});
};
const questionIconMapping = getQuestionTypes("en-US").reduce(
(prev, curr) => ({
...prev,
[curr.id]: curr.icon,
}),
{}
);
const getQuestionIconMapping = (t: TFnType) =>
getQuestionTypes(t).reduce(
(prev, curr) => ({
...prev,
[curr.id]: curr.icon,
}),
{}
);
export const getConditionValueOptions = (
localSurvey: TSurvey,
currQuestionIdx: number,
t: (key: string) => string
t: TFnType
): TComboboxGroupedOption[] => {
const hiddenFields = localSurvey.hiddenFields?.fieldIds ?? [];
const variables = localSurvey.variables ?? [];
@@ -65,7 +65,7 @@ export const getConditionValueOptions = (
.filter((_, idx) => idx <= currQuestionIdx)
.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -133,7 +133,7 @@ export const replaceEndingCardHeadlineRecall = (survey: TSurvey, language: strin
return modifiedSurvey;
};
export const getActionObjectiveOptions = (t: (key: string) => string): TComboboxOption[] => [
export const getActionObjectiveOptions = (t: TFnType): TComboboxOption[] => [
{ label: t("environments.surveys.edit.calculate"), value: "calculate" },
{ label: t("environments.surveys.edit.require_answer"), value: "requireAnswer" },
{ label: t("environments.surveys.edit.jump_to_question"), value: "jumpToQuestion" },
@@ -143,10 +143,7 @@ export const hasJumpToQuestionAction = (actions: TSurveyLogicActions): boolean =
return actions.some((action) => action.objective === "jumpToQuestion");
};
const getQuestionOperatorOptions = (
question: TSurveyQuestion,
t: (key: string) => string
): TComboboxOption[] => {
const getQuestionOperatorOptions = (question: TSurveyQuestion, t: TFnType): TComboboxOption[] => {
let options: TLogicRuleOption;
if (question.type === "openText") {
@@ -165,7 +162,7 @@ const getQuestionOperatorOptions = (
export const getDefaultOperatorForQuestion = (
question: TSurveyQuestion,
t: (key: string) => string
t: TFnType
): TSurveyLogicConditionsOperator => {
const options = getQuestionOperatorOptions(question, t);
@@ -175,7 +172,7 @@ export const getDefaultOperatorForQuestion = (
export const getConditionOperatorOptions = (
condition: TSingleCondition,
localSurvey: TSurvey,
t: (key: string) => string
t: TFnType
): TComboboxOption[] => {
if (condition.leftOperand.type === "variable") {
const variables = localSurvey.variables ?? [];
@@ -199,7 +196,7 @@ export const getMatchValueProps = (
condition: TSingleCondition,
localSurvey: TSurvey,
questionIdx: number,
t: (key: string) => string
t: TFnType
): {
show?: boolean;
showInput?: boolean;
@@ -257,7 +254,7 @@ export const getMatchValueProps = (
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -460,7 +457,7 @@ export const getMatchValueProps = (
const questionOptions = openTextQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -541,7 +538,7 @@ export const getMatchValueProps = (
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -615,7 +612,7 @@ export const getMatchValueProps = (
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -695,7 +692,7 @@ export const getMatchValueProps = (
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -769,7 +766,7 @@ export const getActionTargetOptions = (
action: TSurveyLogicAction,
localSurvey: TSurvey,
currQuestionIdx: number,
t: (key: string) => string
t: TFnType
): TComboboxOption[] => {
let questions = localSurvey.questions.filter((_, idx) => idx > currQuestionIdx);
@@ -779,7 +776,7 @@ export const getActionTargetOptions = (
const questionOptions = questions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
};
@@ -816,7 +813,7 @@ export const getActionVariableOptions = (localSurvey: TSurvey): TComboboxOption[
};
export const getActionOperatorOptions = (
t: (key: string) => string,
t: TFnType,
variableType?: TSurveyVariable["type"]
): TComboboxOption[] => {
if (variableType === "number") {
@@ -861,7 +858,7 @@ export const getActionValueOptions = (
variableId: string,
localSurvey: TSurvey,
questionIdx: number,
t: (key: string) => string
t: TFnType
): TComboboxGroupedOption[] => {
const hiddenFields = localSurvey.hiddenFields?.fieldIds ?? [];
let variables = localSurvey.variables ?? [];
@@ -897,7 +894,7 @@ export const getActionValueOptions = (
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -955,7 +952,7 @@ export const getActionValueOptions = (
const questionOptions = allowedQuestions.map((question) => {
return {
icon: questionIconMapping[question.type],
icon: getQuestionIconMapping(t)[question.type],
label: getLocalizedValue(question.headline, "default"),
value: question.id,
meta: {
@@ -1157,8 +1154,8 @@ export const findHiddenFieldUsedInLogic = (survey: TSurvey, hiddenFieldId: strin
return survey.questions.findIndex((question) => question.logic?.some(isUsedInLogicRule));
};
export const getSurveyFollowUpActionDefaultBody = (locale: TUserLocale) => {
return translate("follow_ups_modal_action_body", locale) as string;
export const getSurveyFollowUpActionDefaultBody = (t: TFnType) => {
return t("templates.follow_ups_modal_action_body") as string;
};
export const findEndingCardUsedInLogic = (survey: TSurvey, endingCardId: string): number => {

View File

@@ -1,4 +1,5 @@
// extend this object in order to add more validation rules
import { TFnType } from "@tolgee/react";
import { toast } from "react-hot-toast";
import { z } from "zod";
import { extractLanguageCodes, getLocalizedValue } from "@formbricks/lib/i18n/utils";
@@ -227,7 +228,7 @@ export const isEndingCardValid = (
}
};
export const isSurveyValid = (survey: TSurvey, selectedLanguageCode: string, t: (key: string) => string) => {
export const isSurveyValid = (survey: TSurvey, selectedLanguageCode: string, t: TFnType) => {
const questionWithEmptyFallback = checkForEmptyFallBackValue(survey, selectedLanguageCode);
if (questionWithEmptyFallback) {
toast.error(t("environments.surveys.edit.fallback_missing"));

View File

@@ -7,8 +7,8 @@ import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getSurveyFollowUpsPermission } from "@/modules/survey-follow-ups/lib/utils";
import { ErrorComponent } from "@/modules/ui/components/error-component";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
import {
DEFAULT_LOCALE,
@@ -38,7 +38,7 @@ export const generateMetadata = async (props) => {
const Page = async (props) => {
const searchParams = await props.searchParams;
const params = await props.params;
const t = await getTranslations();
const t = await getTranslate();
const [
survey,
project,

View File

@@ -1,7 +1,8 @@
import { getDefaultEndingCard, getDefaultWelcomeCard } from "@formbricks/lib/templates";
import { getDefaultEndingCard, getDefaultWelcomeCard } from "@/app/lib/templates";
import { TFnType } from "@tolgee/react";
import { TSurvey } from "@formbricks/types/surveys/types";
export const getMinimalSurvey = (locale: string): TSurvey => ({
export const getMinimalSurvey = (t: TFnType): TSurvey => ({
id: "someUniqueId1",
createdAt: new Date(),
updatedAt: new Date(),
@@ -15,9 +16,9 @@ export const getMinimalSurvey = (locale: string): TSurvey => ({
triggers: [],
recontactDays: null,
displayLimit: null,
welcomeCard: getDefaultWelcomeCard(locale),
welcomeCard: getDefaultWelcomeCard(t),
questions: [],
endings: [getDefaultEndingCard([], locale)],
endings: [getDefaultEndingCard([], t)],
hiddenFields: {
enabled: false,
},

View File

@@ -1,13 +1,13 @@
"use client";
import { Button } from "@/modules/ui/components/button";
import { useTranslate } from "@tolgee/react";
import { ArrowLeftIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
export const BackButton = () => {
const router = useRouter();
const t = useTranslations();
const { t } = useTranslate();
return (
<Button
variant="secondary"

View File

@@ -12,8 +12,8 @@ import {
CardTitle,
} from "@/modules/ui/components/card";
import { Textarea } from "@/modules/ui/components/textarea";
import { useTranslate } from "@tolgee/react";
import { Sparkles } 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 FormbricksAICardProps {
}
export const FormbricksAICard = ({ environmentId }: FormbricksAICardProps) => {
const t = useTranslations();
const { t } = useTranslate();
const router = useRouter();
const [aiPrompt, setAiPrompt] = useState("");
const [isLoading, setIsLoading] = useState(false);
@@ -52,14 +52,14 @@ export const FormbricksAICard = ({ environmentId }: FormbricksAICardProps) => {
<Card className="mx-auto w-full bg-gradient-to-tr from-slate-100 to-slate-200">
<CardHeader>
<CardTitle className="text-2xl font-bold">Formbricks AI</CardTitle>
<CardDescription>{t("environments.surveys.templates.formbricks_ai_description")}</CardDescription>
<CardDescription>{t("environments.surveys.edit.formbricks_ai_description")}</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-4">
<Textarea
className="bg-slate-50"
id="ai-prompt"
placeholder={t("environments.surveys.templates.formbricks_ai_prompt_placeholder")}
placeholder={t("environments.surveys.edit.formbricks_ai_prompt_placeholder")}
value={aiPrompt}
onChange={(e) => setAiPrompt(e.target.value)}
required
@@ -75,7 +75,7 @@ export const FormbricksAICard = ({ environmentId }: FormbricksAICardProps) => {
variant="secondary"
loading={isLoading}>
<Sparkles className="mr-2 h-4 w-4" />
{t("environments.surveys.templates.formbricks_ai_generate")}
{t("environments.surveys.edit.formbricks_ai_generate")}
</Button>
</CardFooter>
</Card>

View File

@@ -2,13 +2,13 @@
import { FormbricksAICard } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/FormbricksAICard";
import { MenuBar } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/templates/components/MenuBar";
import { customSurveyTemplate } from "@/app/lib/templates";
import { TemplateList } from "@/modules/surveys/components/TemplateList";
import { PreviewSurvey } from "@/modules/ui/components/preview-survey";
import { SearchBar } from "@/modules/ui/components/search-bar";
import { Separator } from "@/modules/ui/components/separator";
import { useTranslations } from "next-intl";
import { useTranslate } from "@tolgee/react";
import { useState } from "react";
import { getCustomSurveyTemplate } from "@formbricks/lib/templates";
import type { TEnvironment } from "@formbricks/types/environment";
import type { TProject, TProjectConfigChannel, TProjectConfigIndustry } from "@formbricks/types/project";
import type { TTemplate, TTemplateRole } from "@formbricks/types/templates";
@@ -31,8 +31,8 @@ export const TemplateContainerWithPreview = ({
prefilledFilters,
isAIEnabled,
}: TemplateContainerWithPreviewProps) => {
const t = useTranslations();
const initialTemplate = getCustomSurveyTemplate(user.locale);
const { t } = useTranslate();
const initialTemplate = customSurveyTemplate(t);
const [activeTemplate, setActiveTemplate] = useState<TTemplate>(initialTemplate);
const [activeQuestionId, setActiveQuestionId] = useState<string>(initialTemplate.preset.questions[0].id);
const [templateSearch, setTemplateSearch] = useState<string | null>(null);
@@ -80,7 +80,7 @@ export const TemplateContainerWithPreview = ({
<aside className="group hidden flex-1 flex-shrink-0 items-center justify-center overflow-hidden border-l border-slate-100 bg-slate-50 md:flex md:flex-col">
{activeTemplate && (
<PreviewSurvey
survey={{ ...getMinimalSurvey(user.locale), ...activeTemplate.preset }}
survey={{ ...getMinimalSurvey(t), ...activeTemplate.preset }}
questionId={activeQuestionId}
project={project}
environment={environment}

View File

@@ -1,8 +1,8 @@
import { authOptions } from "@/modules/auth/lib/authOptions";
import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { getTranslate } from "@/tolgee/server";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { redirect } from "next/navigation";
import { getEnvironment } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
@@ -27,7 +27,7 @@ interface SurveyTemplateProps {
const Page = async (props: SurveyTemplateProps) => {
const searchParams = await props.searchParams;
const params = await props.params;
const t = await getTranslations();
const t = await getTranslate();
const session = await getServerSession(authOptions);
const environmentId = params.environmentId;

View File

@@ -2,7 +2,7 @@
import { Button } from "@/modules/ui/components/button";
import { Confetti } from "@/modules/ui/components/confetti";
import { useTranslations } from "next-intl";
import { useTranslate } from "@tolgee/react";
import Link from "next/link";
import { useEffect, useState } from "react";
@@ -11,7 +11,7 @@ interface ConfirmationPageProps {
}
export const ConfirmationPage = ({ environmentId }: ConfirmationPageProps) => {
const t = useTranslations();
const { t } = useTranslate();
const [showConfetti, setShowConfetti] = useState(false);
useEffect(() => {
setShowConfetti(true);

View File

@@ -6,8 +6,8 @@ import { Button } from "@/modules/ui/components/button";
import { ErrorComponent } from "@/modules/ui/components/error-component";
import { Label } from "@/modules/ui/components/label";
import { LoadingSpinner } from "@/modules/ui/components/loading-spinner";
import { useTranslate } from "@tolgee/react";
import { Code2Icon, MousePointerClickIcon, SparklesIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useEffect, useMemo, useState } from "react";
import toast from "react-hot-toast";
import { convertDateTimeStringShort } from "@formbricks/lib/time";
@@ -33,7 +33,7 @@ export const ActionActivityTab = ({
environment,
isReadOnly,
}: ActivityTabProps) => {
const t = useTranslations();
const { t } = useTranslate();
const [activeSurveys, setActiveSurveys] = useState<string[] | undefined>();
const [inactiveSurveys, setInactiveSurveys] = useState<string[] | undefined>();
const [loading, setLoading] = useState(true);

View File

@@ -1,6 +1,8 @@
"use client";
import { ModalWithTabs } from "@/modules/ui/components/modal-with-tabs";
import { useTranslate } from "@tolgee/react";
import { Code2Icon, MousePointerClickIcon, SparklesIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { TActionClass } from "@formbricks/types/action-classes";
import { TEnvironment } from "@formbricks/types/environment";
import { ActionActivityTab } from "./ActionActivityTab";
@@ -29,7 +31,7 @@ export const ActionDetailModal = ({
otherEnvActionClasses,
otherEnvironment,
}: ActionDetailModalProps) => {
const t = useTranslations();
const { t } = useTranslate();
const tabs = [
{
title: t("common.activity"),

View File

@@ -12,8 +12,8 @@ import { FormControl, FormError, FormField, FormItem, FormLabel } from "@/module
import { Input } from "@/modules/ui/components/input";
import { NoCodeActionForm } from "@/modules/ui/components/no-code-action-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTranslate } from "@tolgee/react";
import { TrashIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useMemo, useState } from "react";
@@ -38,7 +38,7 @@ export const ActionSettingsTab = ({
const { createdAt, updatedAt, id, ...restActionClass } = actionClass;
const router = useRouter();
const [openDeleteDialog, setOpenDeleteDialog] = useState(false);
const t = useTranslations();
const { t } = useTranslate();
const [isUpdatingAction, setIsUpdatingAction] = useState(false);
const [isDeletingAction, setIsDeletingAction] = useState(false);

View File

@@ -1,7 +1,7 @@
import { getTranslations } from "next-intl/server";
import { getTranslate } from "@/tolgee/server";
export const ActionTableHeading = async () => {
const t = await getTranslations();
const t = await getTranslate();
return (
<>
<div className="grid h-12 grid-cols-6 content-center border-b border-slate-200 text-left text-sm font-semibold text-slate-900">

View File

@@ -3,8 +3,8 @@
import { CreateNewActionTab } from "@/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/CreateNewActionTab";
import { Button } from "@/modules/ui/components/button";
import { Modal } from "@/modules/ui/components/modal";
import { useTranslate } from "@tolgee/react";
import { MousePointerClickIcon, PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { TActionClass } from "@formbricks/types/action-classes";
@@ -15,7 +15,7 @@ interface AddActionModalProps {
}
export const AddActionModal = ({ environmentId, actionClasses, isReadOnly }: AddActionModalProps) => {
const t = useTranslations();
const { t } = useTranslate();
const [open, setOpen] = useState(false);
const [newActionClasses, setNewActionClasses] = useState<TActionClass[]>(actionClasses);

View File

@@ -1,9 +1,11 @@
"use client";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { useTranslations } from "next-intl";
import { useTranslate } from "@tolgee/react";
const Loading = () => {
const t = useTranslations();
const { t } = useTranslate();
return (
<>
<PageContentWrapper>

View File

@@ -7,9 +7,9 @@ import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { getTranslate } from "@/tolgee/server";
import { Metadata } from "next";
import { getServerSession } from "next-auth";
import { getTranslations } from "next-intl/server";
import { redirect } from "next/navigation";
import { getActionClasses } from "@formbricks/lib/actionClass/service";
import { getEnvironments } from "@formbricks/lib/environment/service";
@@ -26,7 +26,7 @@ export const metadata: Metadata = {
const Page = async (props) => {
const params = await props.params;
const session = await getServerSession(authOptions);
const t = await getTranslations();
const t = await getTranslate();
const [actionClasses, organization, project] = await Promise.all([
getActionClasses(params.environmentId),
getOrganizationByEnvironmentId(params.environmentId),

View File

@@ -5,8 +5,8 @@ import { getProjectPermissionByUserId } from "@/modules/ee/teams/lib/roles";
import { DevEnvironmentBanner } from "@/modules/ui/components/dev-environment-banner";
import { LimitsReachedBanner } from "@/modules/ui/components/limits-reached-banner";
import { PendingDowngradeBanner } from "@/modules/ui/components/pending-downgrade-banner";
import { getTranslate } from "@/tolgee/server";
import type { Session } from "next-auth";
import { getTranslations } from "next-intl/server";
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
import { getEnvironment, getEnvironments } from "@formbricks/lib/environment/service";
import { getMembershipByUserIdOrganizationId } from "@formbricks/lib/membership/service";
@@ -27,7 +27,7 @@ interface EnvironmentLayoutProps {
}
export const EnvironmentLayout = async ({ environmentId, session, children }: EnvironmentLayoutProps) => {
const t = await getTranslations();
const t = await getTranslate();
const [user, environment, organizations, organization] = await Promise.all([
getUser(session.user.id),
getEnvironment(environmentId),

View File

@@ -2,7 +2,7 @@
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 { useRouter } from "next/navigation";
import { useState } from "react";
import { cn } from "@formbricks/lib/cn";
@@ -14,7 +14,7 @@ interface EnvironmentSwitchProps {
}
export const EnvironmentSwitch = ({ environment, environments }: EnvironmentSwitchProps) => {
const t = useTranslations();
const { t } = useTranslate();
const router = useRouter();
const [isEnvSwitchChecked, setIsEnvSwitchChecked] = useState(environment?.type === "development");
const [isLoading, setIsLoading] = useState(false);

View File

@@ -21,6 +21,7 @@ import {
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@/modules/ui/components/dropdown-menu";
import { useTranslate } from "@tolgee/react";
import {
ArrowUpRightIcon,
BlocksIcon,
@@ -40,7 +41,6 @@ import {
UsersIcon,
} 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 { usePathname, useRouter } from "next/navigation";
@@ -82,7 +82,7 @@ export const MainNavigation = ({
}: NavigationProps) => {
const router = useRouter();
const pathname = usePathname();
const t = useTranslations();
const { t } = useTranslate();
const [currentOrganizationName, setCurrentOrganizationName] = useState("");
const [currentOrganizationId, setCurrentOrganizationId] = useState("");
const [showCreateOrganizationModal, setShowCreateOrganizationModal] = useState(false);

View File

@@ -5,8 +5,8 @@ import { TTeamPermission } from "@/modules/ee/teams/project-teams/types/team";
import { getTeamPermissionFlags } from "@/modules/ee/teams/utils/teams";
import { Button } from "@/modules/ui/components/button";
import { TooltipRenderer } from "@/modules/ui/components/tooltip";
import { useTranslate } from "@tolgee/react";
import { CircleUserIcon, MessageCircleQuestionIcon, PlusIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation";
import formbricks from "@formbricks/js";
import { getAccessFlags } from "@formbricks/lib/membership/utils";
@@ -28,7 +28,7 @@ export const TopControlButtons = ({
membershipRole,
projectPermission,
}: TopControlButtonsProps) => {
const t = useTranslations();
const { t } = useTranslate();
const router = useRouter();
const { isMember, isBilling } = getAccessFlags(membershipRole);

View File

@@ -1,5 +1,7 @@
"use client";
import { useTranslate } from "@tolgee/react";
import { AlertTriangleIcon, CheckIcon } from "lucide-react";
import { useTranslations } from "next-intl";
import { cn } from "@formbricks/lib/cn";
import { TEnvironment } from "@formbricks/types/environment";
@@ -8,7 +10,7 @@ interface WidgetStatusIndicatorProps {
}
export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProps) => {
const t = useTranslations();
const { t } = useTranslate();
const stati = {
notImplemented: {
icon: AlertTriangleIcon,

View File

@@ -17,7 +17,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/modules/ui/components/select";
import { useTranslations } from "next-intl";
import { useTranslate } from "@tolgee/react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
@@ -59,7 +59,7 @@ export type IntegrationModalInputs = {
};
const NoBaseFoundError = () => {
const t = useTranslations();
const { t } = useTranslate();
return (
<Alert>
<AlertTitle>{t("environments.integrations.airtable.no_bases_found")}</AlertTitle>
@@ -78,7 +78,7 @@ export const AddIntegrationModal = ({
isEditMode,
defaultData,
}: AddIntegrationModalProps) => {
const t = useTranslations();
const { t } = useTranslate();
const router = useRouter();
const [tables, setTables] = useState<TIntegrationAirtableTables["tables"]>([]);
const [isLoading, setIsLoading] = useState(false);

View File

@@ -1,3 +1,5 @@
"use client";
import { Label } from "@/modules/ui/components/label";
import {
Select,
@@ -6,7 +8,7 @@ import {
SelectTrigger,
SelectValue,
} from "@/modules/ui/components/select";
import { useTranslations } from "next-intl";
import { useTranslate } from "@tolgee/react";
import { Control, Controller, UseFormSetValue } from "react-hook-form";
import { TIntegrationItem } from "@formbricks/types/integration";
import { IntegrationModalInputs } from "./AddIntegrationModal";
@@ -28,7 +30,7 @@ export const BaseSelectDropdown = ({
setValue,
defaultValue,
}: BaseSelectProps) => {
const t = useTranslations();
const { t } = useTranslate();
return (
<div className="flex w-full flex-col">
<Label htmlFor="base">{t("environments.integrations.airtable.airtable_base")}</Label>

View File

@@ -9,8 +9,8 @@ import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { Button } from "@/modules/ui/components/button";
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
import { EmptySpaceFiller } from "@/modules/ui/components/empty-space-filler";
import { useTranslate } from "@tolgee/react";
import { Trash2Icon } from "lucide-react";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { toast } from "react-hot-toast";
import { timeSince } from "@formbricks/lib/time";
@@ -39,7 +39,7 @@ const tableHeaders = [
export const ManageIntegration = (props: ManageIntegrationProps) => {
const { airtableIntegration, environment, environmentId, setIsConnected, surveys, airtableArray } = props;
const t = useTranslations();
const { t } = useTranslate();
const [isDeleting, setisDeleting] = useState(false);
const [isDeleteIntegrationModalOpen, setIsDeleteIntegrationModalOpen] = useState(false);
const [defaultValues, setDefaultValues] = useState<(IntegrationModalInputs & { index: number }) | null>(

Some files were not shown because too many files have changed in this diff Show More