mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-12 11:28:58 -05:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08a4607e31 | |||
| 2a89766500 | |||
| 96d87cff36 | |||
| b03eaf03b1 | |||
| d50c8fb401 | |||
| 9a40c0628f | |||
| 72eb7532aa | |||
| 146ac28649 | |||
| 9804851ccc | |||
| 0875c25d47 | |||
| e1bf65871e | |||
| 36955ddbb8 | |||
| ef56e97c95 | |||
| ea3b4b9413 | |||
| 131a04b77c | |||
| ca7e2c64de | |||
| e4bd9a839a |
@@ -229,5 +229,16 @@ REDIS_URL=redis://localhost:6379
|
|||||||
# AUDIT_LOG_GET_USER_IP=0
|
# AUDIT_LOG_GET_USER_IP=0
|
||||||
|
|
||||||
|
|
||||||
|
# Cube.js Analytics (required for the analytics/dashboard feature)
|
||||||
|
# URL where the Cube.js instance is running (used by the Next.js app)
|
||||||
|
# CUBEJS_API_URL=http://localhost:4000
|
||||||
|
# Must match CUBEJS_API_SECRET set on the Cube.js server (docker-compose.dev.yml)
|
||||||
|
# CUBEJS_API_SECRET=changeme
|
||||||
|
# API token sent with each Cube.js request (empty is accepted when CUBEJS_DEV_MODE=true)
|
||||||
|
# CUBEJS_API_TOKEN=
|
||||||
|
# Postgres connection used by the Cube.js container (see docker-compose.dev.yml)
|
||||||
|
# The Cube.js container connects as user "formbricks" to database "hub" on this port
|
||||||
|
# POSTGRES_PORT=5432
|
||||||
|
|
||||||
# Lingo.dev API key for translation generation
|
# Lingo.dev API key for translation generation
|
||||||
LINGODOTDEV_API_KEY=your_api_key_here
|
LINGODOTDEV_API_KEY=your_api_key_here
|
||||||
@@ -6,9 +6,19 @@ permissions:
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
paths:
|
||||||
|
- "apps/web/**/*.ts"
|
||||||
|
- "apps/web/**/*.tsx"
|
||||||
|
- "apps/web/locales/**/*.json"
|
||||||
|
- "scan-translations.ts"
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
paths:
|
||||||
|
- "apps/web/**/*.ts"
|
||||||
|
- "apps/web/**/*.tsx"
|
||||||
|
- "apps/web/locales/**/*.json"
|
||||||
|
- "scan-translations.ts"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-translations:
|
validate-translations:
|
||||||
@@ -23,38 +33,30 @@ jobs:
|
|||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
|
||||||
- name: Check for relevant changes
|
|
||||||
id: changes
|
|
||||||
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
|
|
||||||
with:
|
with:
|
||||||
filters: |
|
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||||
translations:
|
|
||||||
- 'apps/web/**/*.ts'
|
|
||||||
- 'apps/web/**/*.tsx'
|
|
||||||
- 'apps/web/locales/**/*.json'
|
|
||||||
- 'packages/surveys/src/**/*.{ts,tsx}'
|
|
||||||
- 'packages/surveys/locales/**/*.json'
|
|
||||||
- 'packages/email/**/*.{ts,tsx}'
|
|
||||||
|
|
||||||
- name: Setup Node.js 22.x
|
- name: Setup Node.js 22.x
|
||||||
if: steps.changes.outputs.translations == 'true'
|
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
|
||||||
with:
|
with:
|
||||||
node-version: 22.x
|
node-version: 22.x
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
if: steps.changes.outputs.translations == 'true'
|
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: steps.changes.outputs.translations == 'true'
|
|
||||||
run: pnpm install --config.platform=linux --config.architecture=x64
|
run: pnpm install --config.platform=linux --config.architecture=x64
|
||||||
|
|
||||||
- name: Validate translation keys
|
- name: Validate translation keys
|
||||||
if: steps.changes.outputs.translations == 'true'
|
run: |
|
||||||
run: pnpm run scan-translations
|
echo ""
|
||||||
|
echo "🔍 Validating translation keys..."
|
||||||
|
echo ""
|
||||||
|
pnpm run scan-translations
|
||||||
|
|
||||||
- name: Skip (no translation-related changes)
|
- name: Summary
|
||||||
if: steps.changes.outputs.translations != 'true'
|
if: success()
|
||||||
run: echo "No translation-related files changed — skipping validation."
|
run: |
|
||||||
|
echo ""
|
||||||
|
echo "✅ Translation validation completed successfully!"
|
||||||
|
echo ""
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
echo "{\"branchName\": \"$(git rev-parse --abbrev-ref HEAD)\"}" > ./branch.json
|
||||||
|
prettier --write ./branch.json
|
||||||
+40
-1
@@ -1 +1,40 @@
|
|||||||
pnpm lint-staged
|
# Load environment variables from .env files
|
||||||
|
if [ -f .env ]; then
|
||||||
|
set -a
|
||||||
|
. .env
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
pnpm lint-staged
|
||||||
|
|
||||||
|
# Run Lingo.dev i18n workflow if LINGODOTDEV_API_KEY is set
|
||||||
|
if [ -n "$LINGODOTDEV_API_KEY" ]; then
|
||||||
|
echo ""
|
||||||
|
echo "🌍 Running Lingo.dev translation workflow..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Run translation generation and validation
|
||||||
|
if pnpm run i18n; then
|
||||||
|
echo ""
|
||||||
|
echo "✅ Translation validation passed"
|
||||||
|
echo ""
|
||||||
|
# Add updated locale files to git
|
||||||
|
git add apps/web/locales/*.json
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "❌ Translation validation failed!"
|
||||||
|
echo ""
|
||||||
|
echo "Please fix the translation issues above before committing:"
|
||||||
|
echo " • Add missing translation keys to your locale files"
|
||||||
|
echo " • Remove unused translation keys"
|
||||||
|
echo ""
|
||||||
|
echo "Or run 'pnpm i18n' to see the detailed report"
|
||||||
|
echo ""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ Skipping translation validation: LINGODOTDEV_API_KEY is not set"
|
||||||
|
echo " (This is expected for community contributors)"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
+17
-12
@@ -10,20 +10,25 @@
|
|||||||
"build-storybook": "storybook build",
|
"build-storybook": "storybook build",
|
||||||
"clean": "rimraf .turbo node_modules dist storybook-static"
|
"clean": "rimraf .turbo node_modules dist storybook-static"
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@formbricks/survey-ui": "workspace:*"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chromatic-com/storybook": "^5.0.1",
|
"@chromatic-com/storybook": "^5.0.0",
|
||||||
"@storybook/addon-a11y": "10.2.14",
|
"@storybook/addon-a11y": "10.1.11",
|
||||||
"@storybook/addon-links": "10.2.14",
|
"@storybook/addon-links": "10.1.11",
|
||||||
"@storybook/addon-onboarding": "10.2.14",
|
"@storybook/addon-onboarding": "10.1.11",
|
||||||
"@storybook/react-vite": "10.2.14",
|
"@storybook/react-vite": "10.1.11",
|
||||||
"@typescript-eslint/eslint-plugin": "8.56.1",
|
"@typescript-eslint/eslint-plugin": "8.53.0",
|
||||||
"@tailwindcss/vite": "4.2.1",
|
"@tailwindcss/vite": "4.1.18",
|
||||||
"@typescript-eslint/parser": "8.56.1",
|
"@typescript-eslint/parser": "8.53.0",
|
||||||
"@vitejs/plugin-react": "5.1.4",
|
"@vitejs/plugin-react": "5.1.2",
|
||||||
|
"esbuild": "0.25.12",
|
||||||
"eslint-plugin-react-refresh": "0.4.26",
|
"eslint-plugin-react-refresh": "0.4.26",
|
||||||
"eslint-plugin-storybook": "10.2.14",
|
"eslint-plugin-storybook": "10.1.11",
|
||||||
"storybook": "10.2.14",
|
"prop-types": "15.8.1",
|
||||||
|
"storybook": "10.1.11",
|
||||||
"vite": "7.3.1",
|
"vite": "7.3.1",
|
||||||
"@storybook/addon-docs": "10.2.14"
|
"@storybook/addon-docs": "10.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
const baseConfig = require("../../.prettierrc.js");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
...baseConfig,
|
|
||||||
tailwindConfig: "./tailwind.config.js",
|
|
||||||
};
|
|
||||||
@@ -101,9 +101,6 @@ RUN chown -R nextjs:nextjs ./apps/web/public && chmod -R 755 ./apps/web/public
|
|||||||
# Create packages/database directory structure with proper ownership for runtime migrations
|
# Create packages/database directory structure with proper ownership for runtime migrations
|
||||||
RUN mkdir -p ./packages/database/migrations && chown -R nextjs:nextjs ./packages/database
|
RUN mkdir -p ./packages/database/migrations && chown -R nextjs:nextjs ./packages/database
|
||||||
|
|
||||||
COPY --from=installer /app/packages/database/package.json ./packages/database/package.json
|
|
||||||
RUN chown nextjs:nextjs ./packages/database/package.json && chmod 644 ./packages/database/package.json
|
|
||||||
|
|
||||||
COPY --from=installer /app/packages/database/schema.prisma ./packages/database/schema.prisma
|
COPY --from=installer /app/packages/database/schema.prisma ./packages/database/schema.prisma
|
||||||
RUN chown nextjs:nextjs ./packages/database/schema.prisma && chmod 644 ./packages/database/schema.prisma
|
RUN chown nextjs:nextjs ./packages/database/schema.prisma && chmod 644 ./packages/database/schema.prisma
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ export const ConnectWithFormbricks = ({
|
|||||||
) : (
|
) : (
|
||||||
<div className="flex animate-pulse flex-col items-center space-y-4">
|
<div className="flex animate-pulse flex-col items-center space-y-4">
|
||||||
<span className="relative flex h-10 w-10">
|
<span className="relative flex h-10 w-10">
|
||||||
<span className="absolute inline-flex h-full w-full animate-ping-slow rounded-full bg-slate-400 opacity-75"></span>
|
<span className="animate-ping-slow absolute inline-flex h-full w-full rounded-full bg-slate-400 opacity-75"></span>
|
||||||
<span className="relative inline-flex h-10 w-10 rounded-full bg-slate-500"></span>
|
<span className="relative inline-flex h-10 w-10 rounded-full bg-slate-500"></span>
|
||||||
</span>
|
</span>
|
||||||
<p className="pt-4 text-sm font-medium text-slate-600">
|
<p className="pt-4 text-sm font-medium text-slate-600">
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const Page = async (props: ConnectPageProps) => {
|
|||||||
channel={channel}
|
channel={channel}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700"
|
className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
asChild>
|
asChild>
|
||||||
<Link href={`/environments/${environment.id}`}>
|
<Link href={`/environments/${environment.id}`}>
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const Page = async (props: XMTemplatePageProps) => {
|
|||||||
<XMTemplateList project={project} user={user} environmentId={environment.id} />
|
<XMTemplateList project={project} user={user} environmentId={environment.id} />
|
||||||
{projects.length >= 2 && (
|
{projects.length >= 2 && (
|
||||||
<Button
|
<Button
|
||||||
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700"
|
className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
asChild>
|
asChild>
|
||||||
<Link href={`/environments/${environment.id}/surveys`}>
|
<Link href={`/environments/${environment.id}/surveys`}>
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ export const LandingSidebar = ({ user, organization }: LandingSidebarProps) => {
|
|||||||
return (
|
return (
|
||||||
<aside
|
<aside
|
||||||
className={cn(
|
className={cn(
|
||||||
"z-40 flex w-sidebar-collapsed flex-col justify-between rounded-r-xl border-r border-slate-200 bg-white pt-3 shadow-md transition-all duration-100"
|
"w-sidebar-collapsed z-40 flex flex-col justify-between rounded-r-xl border-r border-slate-200 bg-white pt-3 shadow-md transition-all duration-100"
|
||||||
)}>
|
)}>
|
||||||
<Image src={FBLogo} width={160} height={30} alt={t("environments.formbricks_logo")} />
|
<Image src={FBLogo} width={160} height={30} alt={t("environments.formbricks_logo")} />
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -50,7 +50,7 @@ const Page = async (props: ChannelPageProps) => {
|
|||||||
<OnboardingOptionsContainer options={channelOptions} />
|
<OnboardingOptionsContainer options={channelOptions} />
|
||||||
{projects.length >= 1 && (
|
{projects.length >= 1 && (
|
||||||
<Button
|
<Button
|
||||||
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700"
|
className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
asChild>
|
asChild>
|
||||||
<Link href={"/"}>
|
<Link href={"/"}>
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ const Page = async (props: ModePageProps) => {
|
|||||||
<OnboardingOptionsContainer options={channelOptions} />
|
<OnboardingOptionsContainer options={channelOptions} />
|
||||||
{projects.length >= 1 && (
|
{projects.length >= 1 && (
|
||||||
<Button
|
<Button
|
||||||
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700"
|
className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
asChild>
|
asChild>
|
||||||
<Link href={"/"}>
|
<Link href={"/"}>
|
||||||
|
|||||||
+13
-11
@@ -228,7 +228,7 @@ export const ProjectSettings = ({
|
|||||||
</FormProvider>
|
</FormProvider>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative flex w-1/2 flex-col items-center justify-center space-y-2 rounded-lg border bg-slate-200 p-6 shadow">
|
<div className="relative flex h-[30rem] w-1/2 flex-col items-center justify-center space-y-2 rounded-lg border bg-slate-200 shadow">
|
||||||
{logoUrl && (
|
{logoUrl && (
|
||||||
<Image
|
<Image
|
||||||
src={logoUrl}
|
src={logoUrl}
|
||||||
@@ -239,16 +239,18 @@ export const ProjectSettings = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<p className="text-sm text-slate-400">{t("common.preview")}</p>
|
<p className="text-sm text-slate-400">{t("common.preview")}</p>
|
||||||
<SurveyInline
|
<div className="z-0 h-3/4 w-3/4">
|
||||||
appUrl={publicDomain}
|
<SurveyInline
|
||||||
isPreviewMode={true}
|
appUrl={publicDomain}
|
||||||
survey={previewSurvey(projectName || t("common.my_product"), t)}
|
isPreviewMode={true}
|
||||||
styling={previewStyling}
|
survey={previewSurvey(projectName || "my Product", t)}
|
||||||
isBrandingEnabled={false}
|
styling={previewStyling}
|
||||||
languageCode="default"
|
isBrandingEnabled={false}
|
||||||
onFileUpload={async (file) => file.name}
|
languageCode="default"
|
||||||
autoFocus={false}
|
onFileUpload={async (file) => file.name}
|
||||||
/>
|
autoFocus={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CreateTeamModal
|
<CreateTeamModal
|
||||||
open={createTeamModalOpen}
|
open={createTeamModalOpen}
|
||||||
|
|||||||
+1
-1
@@ -69,7 +69,7 @@ const Page = async (props: ProjectSettingsPageProps) => {
|
|||||||
/>
|
/>
|
||||||
{projects.length >= 1 && (
|
{projects.length >= 1 && (
|
||||||
<Button
|
<Button
|
||||||
className="absolute right-5 top-5 !mt-0 text-slate-500 hover:text-slate-700"
|
className="absolute top-5 right-5 !mt-0 text-slate-500 hover:text-slate-700"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
asChild>
|
asChild>
|
||||||
<Link href={"/"}>
|
<Link href={"/"}>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { ChartsListSkeleton } from "@/modules/ee/analysis/charts/components/charts-list-skeleton";
|
||||||
|
|
||||||
|
export default function ChartsListLoading() {
|
||||||
|
return <ChartsListSkeleton />;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { ChartsListPage as default } from "@/modules/ee/analysis/charts/pages/charts-list-page";
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import { DashboardsListPage } from "@/modules/ee/analysis/dashboards/pages/dashboards-list-page";
|
||||||
|
|
||||||
|
export default DashboardsListPage;
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { AnalysisLayoutClient } from "@/modules/ee/analysis/components/analysis-layout-client";
|
||||||
|
|
||||||
|
export default function AnalysisLayout({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
params: Promise<{ environmentId: string }>;
|
||||||
|
}) {
|
||||||
|
return <AnalysisLayoutClient params={params}>{children}</AnalysisLayoutClient>;
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
import { DashboardDetailPage } from "@/modules/ee/analysis/dashboards/pages/dashboard-detail-page";
|
||||||
|
|
||||||
|
export default DashboardDetailPage;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export default async function AnalysisPage({ params }: { params: Promise<{ environmentId: string }> }) {
|
||||||
|
const { environmentId } = await params;
|
||||||
|
if (!environmentId || environmentId === "undefined") {
|
||||||
|
redirect("/");
|
||||||
|
}
|
||||||
|
redirect(`/environments/${environmentId}/analysis/dashboards`);
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
MessageCircle,
|
MessageCircle,
|
||||||
PanelLeftCloseIcon,
|
PanelLeftCloseIcon,
|
||||||
PanelLeftOpenIcon,
|
PanelLeftOpenIcon,
|
||||||
|
PieChart,
|
||||||
RocketIcon,
|
RocketIcon,
|
||||||
UserCircleIcon,
|
UserCircleIcon,
|
||||||
UserIcon,
|
UserIcon,
|
||||||
@@ -105,6 +106,13 @@ export const MainNavigation = ({
|
|||||||
isActive: pathname?.includes("/surveys"),
|
isActive: pathname?.includes("/surveys"),
|
||||||
isHidden: false,
|
isHidden: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: t("common.analysis"),
|
||||||
|
href: `/environments/${environment.id}/analysis`,
|
||||||
|
icon: PieChart,
|
||||||
|
isActive: pathname?.includes("/analysis"),
|
||||||
|
isHidden: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: `/environments/${environment.id}/contacts`,
|
href: `/environments/${environment.id}/contacts`,
|
||||||
name: t("common.contacts"),
|
name: t("common.contacts"),
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ export const WidgetStatusIndicator = ({ environment }: WidgetStatusIndicatorProp
|
|||||||
<currentStatus.icon />
|
<currentStatus.icon />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-md font-bold text-slate-800 md:text-xl">{currentStatus.title}</p>
|
<p className="text-md font-bold text-slate-800 md:text-xl">{currentStatus.title}</p>
|
||||||
<p className="w-2/3 text-balance text-sm text-slate-600">{currentStatus.subtitle}</p>
|
<p className="w-2/3 text-sm text-balance text-slate-600">{currentStatus.subtitle}</p>
|
||||||
{status === "notImplemented" && (
|
{status === "notImplemented" && (
|
||||||
<Button variant="outline" size="sm" className="bg-white" onClick={() => router.refresh()}>
|
<Button variant="outline" size="sm" className="bg-white" onClick={() => router.refresh()}>
|
||||||
<RotateCcwIcon />
|
<RotateCcwIcon />
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ export const OrganizationBreadcrumb = ({
|
|||||||
getOrganizationsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
|
getOrganizationsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
|
||||||
if (result?.data) {
|
if (result?.data) {
|
||||||
// Sort organizations by name
|
// Sort organizations by name
|
||||||
const sorted = [...result.data].sort((a, b) => a.name.localeCompare(b.name));
|
const sorted = result.data.toSorted((a, b) => a.name.localeCompare(b.name));
|
||||||
setOrganizations(sorted);
|
setOrganizations(sorted);
|
||||||
} else {
|
} else {
|
||||||
// Handle server errors or validation errors
|
// Handle server errors or validation errors
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export const ProjectBreadcrumb = ({
|
|||||||
getProjectsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
|
getProjectsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
|
||||||
if (result?.data) {
|
if (result?.data) {
|
||||||
// Sort projects by name
|
// Sort projects by name
|
||||||
const sorted = [...result.data].sort((a, b) => a.name.localeCompare(b.name));
|
const sorted = result.data.toSorted((a, b) => a.name.localeCompare(b.name));
|
||||||
setProjects(sorted);
|
setProjects(sorted);
|
||||||
} else {
|
} else {
|
||||||
// Handle server errors or validation errors
|
// Handle server errors or validation errors
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ const EnvLayout = async (props: {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) => {
|
}) => {
|
||||||
const params = await props.params;
|
const params = await props.params;
|
||||||
|
const { environmentId } = params;
|
||||||
|
|
||||||
|
if (!environmentId || environmentId === "undefined") {
|
||||||
|
return redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
// Check session first (required for userId)
|
// Check session first (required for userId)
|
||||||
|
|||||||
+4
-7
@@ -30,7 +30,7 @@ export const NotificationSwitch = ({
|
|||||||
const isChecked =
|
const isChecked =
|
||||||
notificationType === "unsubscribedOrganizationIds"
|
notificationType === "unsubscribedOrganizationIds"
|
||||||
? !notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProjectOrOrganizationId)
|
? !notificationSettings.unsubscribedOrganizationIds?.includes(surveyOrProjectOrOrganizationId)
|
||||||
: notificationSettings[notificationType]?.[surveyOrProjectOrOrganizationId] === true;
|
: notificationSettings[notificationType][surveyOrProjectOrOrganizationId] === true;
|
||||||
|
|
||||||
const handleSwitchChange = async () => {
|
const handleSwitchChange = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@@ -49,11 +49,8 @@ export const NotificationSwitch = ({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedNotificationSettings[notificationType] = {
|
updatedNotificationSettings[notificationType][surveyOrProjectOrOrganizationId] =
|
||||||
...updatedNotificationSettings[notificationType],
|
!updatedNotificationSettings[notificationType][surveyOrProjectOrOrganizationId];
|
||||||
[surveyOrProjectOrOrganizationId]:
|
|
||||||
!updatedNotificationSettings[notificationType]?.[surveyOrProjectOrOrganizationId],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedNotificationSettingsActionResponse = await updateNotificationSettingsAction({
|
const updatedNotificationSettingsActionResponse = await updateNotificationSettingsAction({
|
||||||
@@ -81,7 +78,7 @@ export const NotificationSwitch = ({
|
|||||||
) {
|
) {
|
||||||
switch (notificationType) {
|
switch (notificationType) {
|
||||||
case "alert":
|
case "alert":
|
||||||
if (notificationSettings[notificationType]?.[surveyOrProjectOrOrganizationId] === true) {
|
if (notificationSettings[notificationType][surveyOrProjectOrOrganizationId] === true) {
|
||||||
handleSwitchChange();
|
handleSwitchChange();
|
||||||
toast.success(
|
toast.success(
|
||||||
t(
|
t(
|
||||||
|
|||||||
+1
-1
@@ -98,7 +98,7 @@ export const PasswordConfirmationModal = ({
|
|||||||
aria-label="password"
|
aria-label="password"
|
||||||
aria-required="true"
|
aria-required="true"
|
||||||
required
|
required
|
||||||
className="block w-full rounded-md border-slate-300 shadow-sm focus:border-brand-dark focus:ring-brand-dark sm:text-sm"
|
className="focus:border-brand-dark focus:ring-brand-dark block w-full rounded-md border-slate-300 shadow-sm sm:text-sm"
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onChange={(password) => field.onChange(password)}
|
onChange={(password) => field.onChange(password)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+1
-5
@@ -9,7 +9,6 @@ import { Alert, AlertDescription } from "@/modules/ui/components/alert";
|
|||||||
import { IdBadge } from "@/modules/ui/components/id-badge";
|
import { IdBadge } from "@/modules/ui/components/id-badge";
|
||||||
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
|
||||||
import { PageHeader } from "@/modules/ui/components/page-header";
|
import { PageHeader } from "@/modules/ui/components/page-header";
|
||||||
import packageJson from "@/package.json";
|
|
||||||
import { SettingsCard } from "../../components/SettingsCard";
|
import { SettingsCard } from "../../components/SettingsCard";
|
||||||
import { DeleteOrganization } from "./components/DeleteOrganization";
|
import { DeleteOrganization } from "./components/DeleteOrganization";
|
||||||
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
|
import { EditOrganizationNameForm } from "./components/EditOrganizationNameForm";
|
||||||
@@ -82,10 +81,7 @@ const Page = async (props: { params: Promise<{ environmentId: string }> }) => {
|
|||||||
</SettingsCard>
|
</SettingsCard>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<IdBadge id={organization.id} label={t("common.organization_id")} variant="column" />
|
||||||
<IdBadge id={organization.id} label={t("common.organization_id")} variant="column" />
|
|
||||||
<IdBadge id={packageJson.version} label={t("common.formbricks_version")} variant="column" />
|
|
||||||
</div>
|
|
||||||
</PageContentWrapper>
|
</PageContentWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
-29
@@ -4,7 +4,6 @@ import { revalidatePath } from "next/cache";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ZId } from "@formbricks/types/common";
|
import { ZId } from "@formbricks/types/common";
|
||||||
import { ZResponseFilterCriteria } from "@formbricks/types/responses";
|
import { ZResponseFilterCriteria } from "@formbricks/types/responses";
|
||||||
import { getDisplaysBySurveyIdWithContact } from "@/lib/display/service";
|
|
||||||
import { getResponseCountBySurveyId, getResponses } from "@/lib/response/service";
|
import { getResponseCountBySurveyId, getResponses } from "@/lib/response/service";
|
||||||
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
||||||
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
||||||
@@ -107,31 +106,3 @@ export const getResponseCountAction = authenticatedActionClient
|
|||||||
|
|
||||||
return getResponseCountBySurveyId(parsedInput.surveyId, parsedInput.filterCriteria);
|
return getResponseCountBySurveyId(parsedInput.surveyId, parsedInput.filterCriteria);
|
||||||
});
|
});
|
||||||
|
|
||||||
const ZGetDisplaysWithContactAction = z.object({
|
|
||||||
surveyId: ZId,
|
|
||||||
limit: z.number().int().min(1).max(100),
|
|
||||||
offset: z.number().int().nonnegative(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getDisplaysWithContactAction = authenticatedActionClient
|
|
||||||
.schema(ZGetDisplaysWithContactAction)
|
|
||||||
.action(async ({ ctx, parsedInput }) => {
|
|
||||||
await checkAuthorizationUpdated({
|
|
||||||
userId: ctx.user.id,
|
|
||||||
organizationId: await getOrganizationIdFromSurveyId(parsedInput.surveyId),
|
|
||||||
access: [
|
|
||||||
{
|
|
||||||
type: "organization",
|
|
||||||
roles: ["owner", "manager"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "projectTeam",
|
|
||||||
minPermission: "read",
|
|
||||||
projectId: await getProjectIdFromSurveyId(parsedInput.surveyId),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
return getDisplaysBySurveyIdWithContact(parsedInput.surveyId, parsedInput.limit, parsedInput.offset);
|
|
||||||
});
|
|
||||||
|
|||||||
+1
-3
@@ -4,7 +4,6 @@ import { ResponseFilterProvider } from "@/app/(app)/environments/[environmentId]
|
|||||||
import { getResponseCountBySurveyId } from "@/lib/response/service";
|
import { getResponseCountBySurveyId } from "@/lib/response/service";
|
||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
import { getTranslate } from "@/lingodotdev/server";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
params: Promise<{ surveyId: string; environmentId: string }>;
|
params: Promise<{ surveyId: string; environmentId: string }>;
|
||||||
@@ -15,11 +14,10 @@ export const generateMetadata = async (props: Props): Promise<Metadata> => {
|
|||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
const survey = await getSurvey(params.surveyId);
|
const survey = await getSurvey(params.surveyId);
|
||||||
const responseCount = await getResponseCountBySurveyId(params.surveyId);
|
const responseCount = await getResponseCountBySurveyId(params.surveyId);
|
||||||
const t = await getTranslate();
|
|
||||||
|
|
||||||
if (session) {
|
if (session) {
|
||||||
return {
|
return {
|
||||||
title: `${t("common.count_responses", { count: responseCount })} | ${t("environments.surveys.summary.survey_results", { surveyName: survey?.name })}`,
|
title: `${responseCount} Responses | ${survey?.name} Results`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
+4
-2
@@ -30,7 +30,8 @@ export const CalSummary = ({ elementSummary, survey }: CalSummaryProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="flex w-32 items-end justify-end text-slate-600">
|
<p className="flex w-32 items-end justify-end text-slate-600">
|
||||||
{t("common.count_responses", { count: elementSummary.booked.count })}
|
{elementSummary.booked.count}{" "}
|
||||||
|
{elementSummary.booked.count === 1 ? t("common.response") : t("common.responses")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ProgressBar barColor="bg-brand-dark" progress={elementSummary.booked.percentage / 100} />
|
<ProgressBar barColor="bg-brand-dark" progress={elementSummary.booked.percentage / 100} />
|
||||||
@@ -46,7 +47,8 @@ export const CalSummary = ({ elementSummary, survey }: CalSummaryProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="flex w-32 items-end justify-end text-slate-600">
|
<p className="flex w-32 items-end justify-end text-slate-600">
|
||||||
{t("common.count_responses", { count: elementSummary.skipped.count })}
|
{elementSummary.skipped.count}{" "}
|
||||||
|
{elementSummary.skipped.count === 1 ? t("common.response") : t("common.responses")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ProgressBar barColor="bg-brand-dark" progress={elementSummary.skipped.percentage / 100} />
|
<ProgressBar barColor="bg-brand-dark" progress={elementSummary.skipped.percentage / 100} />
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ export const ConsentSummary = ({ elementSummary, survey, setFilter }: ConsentSum
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="flex w-32 items-end justify-end text-slate-600">
|
<p className="flex w-32 items-end justify-end text-slate-600">
|
||||||
{t("common.count_responses", { count: summaryItem.count })}
|
{summaryItem.count} {summaryItem.count === 1 ? t("common.response") : t("common.responses")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="group-hover:opacity-80">
|
<div className="group-hover:opacity-80">
|
||||||
|
|||||||
+1
-1
@@ -48,7 +48,7 @@ export const ElementSummaryHeader = ({
|
|||||||
{showResponses && (
|
{showResponses && (
|
||||||
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
||||||
<InboxIcon className="mr-2 h-4 w-4" />
|
<InboxIcon className="mr-2 h-4 w-4" />
|
||||||
{t("common.count_responses", { count: elementSummary.responseCount })}
|
{`${elementSummary.responseCount} ${t("common.responses")}`}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{additionalInfo}
|
{additionalInfo}
|
||||||
|
|||||||
+2
-1
@@ -41,7 +41,8 @@ export const HiddenFieldsSummary = ({ environment, elementSummary, locale }: Hid
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
||||||
<InboxIcon className="mr-2 h-4 w-4" />
|
<InboxIcon className="mr-2 h-4 w-4" />
|
||||||
{t("common.count_responses", { count: elementSummary.responseCount })}
|
{elementSummary.responseCount}{" "}
|
||||||
|
{elementSummary.responseCount === 1 ? t("common.response") : t("common.responses")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+2
-2
@@ -31,7 +31,7 @@ export const MatrixElementSummary = ({ elementSummary, survey, setFilter }: Matr
|
|||||||
if (label) {
|
if (label) {
|
||||||
return label;
|
return label;
|
||||||
} else if (percentage !== undefined && totalResponsesForRow !== undefined) {
|
} else if (percentage !== undefined && totalResponsesForRow !== undefined) {
|
||||||
return t("common.count_responses", { count: Math.round((percentage / 100) * totalResponsesForRow) });
|
return `${Math.round((percentage / 100) * totalResponsesForRow)} ${t("common.responses")}`;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
@@ -77,7 +77,7 @@ export const MatrixElementSummary = ({ elementSummary, survey, setFilter }: Matr
|
|||||||
)}>
|
)}>
|
||||||
<button
|
<button
|
||||||
style={{ backgroundColor: `rgba(0,196,184,${getOpacityLevel(percentage)})` }}
|
style={{ backgroundColor: `rgba(0,196,184,${getOpacityLevel(percentage)})` }}
|
||||||
className="m-1 flex h-full w-40 cursor-pointer items-center justify-center rounded p-4 text-sm text-slate-950 hover:outline hover:outline-brand-dark"
|
className="hover:outline-brand-dark m-1 flex h-full w-40 cursor-pointer items-center justify-center rounded p-4 text-sm text-slate-950 hover:outline"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setFilter(
|
setFilter(
|
||||||
elementSummary.element.id,
|
elementSummary.element.id,
|
||||||
|
|||||||
+2
-2
@@ -75,7 +75,7 @@ export const MultipleChoiceSummary = ({
|
|||||||
elementSummary.type === "multipleChoiceMulti" ? (
|
elementSummary.type === "multipleChoiceMulti" ? (
|
||||||
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
||||||
<InboxIcon className="mr-2 h-4 w-4" />
|
<InboxIcon className="mr-2 h-4 w-4" />
|
||||||
{t("common.count_selections", { count: elementSummary.selectionCount })}
|
{`${elementSummary.selectionCount} ${t("common.selections")}`}
|
||||||
</div>
|
</div>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
@@ -110,7 +110,7 @@ export const MultipleChoiceSummary = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex w-full space-x-2">
|
<div className="flex w-full space-x-2">
|
||||||
<p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0">
|
<p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0">
|
||||||
{t("common.count_selections", { count: result.count })}
|
{result.count} {result.count === 1 ? t("common.selection") : t("common.selections")}
|
||||||
</p>
|
</p>
|
||||||
<p className="rounded-lg bg-slate-100 px-2 text-slate-700">
|
<p className="rounded-lg bg-slate-100 px-2 text-slate-700">
|
||||||
{convertFloatToNDecimal(result.percentage, 2)}%
|
{convertFloatToNDecimal(result.percentage, 2)}%
|
||||||
|
|||||||
+3
-2
@@ -123,7 +123,8 @@ export const NPSSummary = ({ elementSummary, survey, setFilter }: NPSSummaryProp
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="flex w-32 items-end justify-end text-slate-600">
|
<p className="flex w-32 items-end justify-end text-slate-600">
|
||||||
{t("common.count_responses", { count: elementSummary[group]?.count })}
|
{elementSummary[group]?.count}{" "}
|
||||||
|
{elementSummary[group]?.count === 1 ? t("common.response") : t("common.responses")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
@@ -157,7 +158,7 @@ export const NPSSummary = ({ elementSummary, survey, setFilter }: NPSSummaryProp
|
|||||||
}>
|
}>
|
||||||
<div className="flex h-32 w-full flex-col items-center justify-end">
|
<div className="flex h-32 w-full flex-col items-center justify-end">
|
||||||
<div
|
<div
|
||||||
className="w-full rounded-t-lg border border-slate-200 bg-brand-dark transition-all group-hover:brightness-110"
|
className="bg-brand-dark w-full rounded-t-lg border border-slate-200 transition-all group-hover:brightness-110"
|
||||||
style={{
|
style={{
|
||||||
height: `${Math.max(choice.percentage, 2)}%`,
|
height: `${Math.max(choice.percentage, 2)}%`,
|
||||||
opacity,
|
opacity,
|
||||||
|
|||||||
+2
-2
@@ -37,7 +37,7 @@ export const PictureChoiceSummary = ({ elementSummary, survey, setFilter }: Pict
|
|||||||
elementSummary.element.allowMulti ? (
|
elementSummary.element.allowMulti ? (
|
||||||
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
<div className="flex items-center rounded-lg bg-slate-100 p-2">
|
||||||
<InboxIcon className="mr-2 h-4 w-4" />
|
<InboxIcon className="mr-2 h-4 w-4" />
|
||||||
{t("common.count_selections", { count: elementSummary.selectionCount })}
|
{`${elementSummary.selectionCount} ${t("common.selections")}`}
|
||||||
</div>
|
</div>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ export const PictureChoiceSummary = ({ elementSummary, survey, setFilter }: Pict
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex w-full space-x-2">
|
<div className="flex w-full space-x-2">
|
||||||
<p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0">
|
<p className="flex w-full pt-1 text-slate-600 sm:items-end sm:justify-end sm:pt-0">
|
||||||
{t("common.count_selections", { count: result.count })}
|
{result.count} {result.count === 1 ? t("common.selection") : t("common.selections")}
|
||||||
</p>
|
</p>
|
||||||
<p className="self-end rounded-lg bg-slate-100 px-2 text-slate-700">
|
<p className="self-end rounded-lg bg-slate-100 px-2 text-slate-700">
|
||||||
{convertFloatToNDecimal(result.percentage, 2)}%
|
{convertFloatToNDecimal(result.percentage, 2)}%
|
||||||
|
|||||||
+4
-3
@@ -116,7 +116,7 @@ export const RatingSummary = ({ elementSummary, survey, setFilter }: RatingSumma
|
|||||||
)
|
)
|
||||||
}>
|
}>
|
||||||
<div
|
<div
|
||||||
className={`h-full bg-brand-dark ${isFirst ? "rounded-tl-lg" : ""} ${isLast ? "rounded-tr-lg" : ""}`}
|
className={`bg-brand-dark h-full ${isFirst ? "rounded-tl-lg" : ""} ${isLast ? "rounded-tr-lg" : ""}`}
|
||||||
style={{ opacity }}
|
style={{ opacity }}
|
||||||
/>
|
/>
|
||||||
</ClickableBarSegment>
|
</ClickableBarSegment>
|
||||||
@@ -198,7 +198,7 @@ export const RatingSummary = ({ elementSummary, survey, setFilter }: RatingSumma
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="flex w-32 items-end justify-end text-slate-600">
|
<p className="flex w-32 items-end justify-end text-slate-600">
|
||||||
{t("common.count_responses", { count: result.count })}
|
{result.count} {result.count === 1 ? t("common.response") : t("common.responses")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ProgressBar barColor="bg-brand-dark" progress={result.percentage / 100} />
|
<ProgressBar barColor="bg-brand-dark" progress={result.percentage / 100} />
|
||||||
@@ -215,7 +215,8 @@ export const RatingSummary = ({ elementSummary, survey, setFilter }: RatingSumma
|
|||||||
<div className="text flex justify-between px-2">
|
<div className="text flex justify-between px-2">
|
||||||
<p className="font-semibold text-slate-700">{t("common.dismissed")}</p>
|
<p className="font-semibold text-slate-700">{t("common.dismissed")}</p>
|
||||||
<p className="flex w-32 items-end justify-end text-slate-600">
|
<p className="flex w-32 items-end justify-end text-slate-600">
|
||||||
{t("common.count_responses", { count: elementSummary.dismissed.count })}
|
{elementSummary.dismissed.count}{" "}
|
||||||
|
{elementSummary.dismissed.count === 1 ? t("common.response") : t("common.responses")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
-125
@@ -1,125 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import { AlertCircleIcon, InfoIcon } from "lucide-react";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { TDisplayWithContact } from "@formbricks/types/displays";
|
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
|
||||||
import { timeSince } from "@/lib/time";
|
|
||||||
import { Button } from "@/modules/ui/components/button";
|
|
||||||
|
|
||||||
interface SummaryImpressionsProps {
|
|
||||||
displays: TDisplayWithContact[];
|
|
||||||
isLoading: boolean;
|
|
||||||
hasMore: boolean;
|
|
||||||
displaysError: string | null;
|
|
||||||
environmentId: string;
|
|
||||||
locale: TUserLocale;
|
|
||||||
onLoadMore: () => void;
|
|
||||||
onRetry: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getDisplayContactIdentifier = (display: TDisplayWithContact): string => {
|
|
||||||
if (!display.contact) return "";
|
|
||||||
return display.contact.attributes?.email || display.contact.attributes?.userId || display.contact.id;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SummaryImpressions = ({
|
|
||||||
displays,
|
|
||||||
isLoading,
|
|
||||||
hasMore,
|
|
||||||
displaysError,
|
|
||||||
environmentId,
|
|
||||||
locale,
|
|
||||||
onLoadMore,
|
|
||||||
onRetry,
|
|
||||||
}: SummaryImpressionsProps) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const renderContent = () => {
|
|
||||||
if (displaysError) {
|
|
||||||
return (
|
|
||||||
<div className="p-8">
|
|
||||||
<div className="flex flex-col items-center gap-4 text-center">
|
|
||||||
<div className="flex items-center gap-2 text-red-600">
|
|
||||||
<AlertCircleIcon className="h-5 w-5" />
|
|
||||||
<span className="text-sm font-medium">{t("common.error_loading_data")}</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-slate-500">{displaysError}</p>
|
|
||||||
<Button onClick={onRetry} variant="secondary" size="sm">
|
|
||||||
{t("common.try_again")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displays.length === 0) {
|
|
||||||
return (
|
|
||||||
<div className="p-8 text-center text-sm text-slate-500">
|
|
||||||
{t("environments.surveys.summary.no_identified_impressions")}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="grid min-h-10 grid-cols-4 items-center border-b border-slate-200 bg-slate-100 text-sm font-semibold text-slate-600">
|
|
||||||
<div className="col-span-2 px-4 md:px-6">{t("common.user")}</div>
|
|
||||||
<div className="col-span-2 px-4 md:px-6">{t("environments.contacts.survey_viewed_at")}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-h-[62vh] overflow-y-auto">
|
|
||||||
{displays.map((display) => (
|
|
||||||
<div
|
|
||||||
key={display.id}
|
|
||||||
className="grid grid-cols-4 items-center border-b border-slate-100 py-2 text-xs text-slate-800 last:border-transparent md:text-sm">
|
|
||||||
<div className="col-span-2 pl-4 md:pl-6">
|
|
||||||
{display.contact ? (
|
|
||||||
<Link
|
|
||||||
className="ph-no-capture break-all text-slate-600 hover:underline"
|
|
||||||
href={`/environments/${environmentId}/contacts/${display.contact.id}`}>
|
|
||||||
{getDisplayContactIdentifier(display)}
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<span className="break-all text-slate-600">{t("common.anonymous")}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="col-span-2 px-4 text-slate-500 md:px-6">
|
|
||||||
{timeSince(display.createdAt.toString(), locale)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{hasMore && (
|
|
||||||
<div className="flex justify-center border-t border-slate-100 py-4">
|
|
||||||
<Button onClick={onLoadMore} variant="secondary" size="sm">
|
|
||||||
{t("common.load_more")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return (
|
|
||||||
<div className="rounded-xl border border-slate-200 bg-white p-8 shadow-sm">
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<div className="h-6 w-32 animate-pulse rounded-full bg-slate-200"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
|
|
||||||
<div className="flex items-center gap-2 rounded-t-xl border-b border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600">
|
|
||||||
<InfoIcon className="h-4 w-4 shrink-0" />
|
|
||||||
<span>{t("environments.surveys.summary.impressions_identified_only")}</span>
|
|
||||||
</div>
|
|
||||||
{renderContent()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+4
-8
@@ -10,8 +10,8 @@ interface SummaryMetadataProps {
|
|||||||
surveySummary: TSurveySummary["meta"];
|
surveySummary: TSurveySummary["meta"];
|
||||||
quotasCount: number;
|
quotasCount: number;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
tab: "dropOffs" | "quotas" | "impressions" | undefined;
|
tab: "dropOffs" | "quotas" | undefined;
|
||||||
setTab: React.Dispatch<React.SetStateAction<"dropOffs" | "quotas" | "impressions" | undefined>>;
|
setTab: React.Dispatch<React.SetStateAction<"dropOffs" | "quotas" | undefined>>;
|
||||||
isQuotasAllowed: boolean;
|
isQuotasAllowed: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ export const SummaryMetadata = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount;
|
const dropoffCountValue = dropOffCount === 0 ? <span>-</span> : dropOffCount;
|
||||||
|
|
||||||
const handleTabChange = (val: "dropOffs" | "quotas" | "impressions") => {
|
const handleTabChange = (val: "dropOffs" | "quotas") => {
|
||||||
const change = tab === val ? undefined : val;
|
const change = tab === val ? undefined : val;
|
||||||
setTab(change);
|
setTab(change);
|
||||||
};
|
};
|
||||||
@@ -65,16 +65,12 @@ export const SummaryMetadata = ({
|
|||||||
`grid gap-4 sm:grid-cols-2 md:grid-cols-3 md:gap-x-2 lg:grid-cols-3 2xl:grid-cols-5`,
|
`grid gap-4 sm:grid-cols-2 md:grid-cols-3 md:gap-x-2 lg:grid-cols-3 2xl:grid-cols-5`,
|
||||||
isQuotasAllowed && quotasCount > 0 && "2xl:grid-cols-6"
|
isQuotasAllowed && quotasCount > 0 && "2xl:grid-cols-6"
|
||||||
)}>
|
)}>
|
||||||
<InteractiveCard
|
<StatCard
|
||||||
key="impressions"
|
|
||||||
tab="impressions"
|
|
||||||
label={t("environments.surveys.summary.impressions")}
|
label={t("environments.surveys.summary.impressions")}
|
||||||
percentage={null}
|
percentage={null}
|
||||||
value={displayCount === 0 ? <span>-</span> : displayCount}
|
value={displayCount === 0 ? <span>-</span> : displayCount}
|
||||||
tooltipText={t("environments.surveys.summary.impressions_tooltip")}
|
tooltipText={t("environments.surveys.summary.impressions_tooltip")}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onClick={() => handleTabChange("impressions")}
|
|
||||||
isActive={tab === "impressions"}
|
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
label={t("environments.surveys.summary.starts")}
|
label={t("environments.surveys.summary.starts")}
|
||||||
|
|||||||
+3
-84
@@ -1,31 +1,21 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useSearchParams } from "next/navigation";
|
import { useSearchParams } from "next/navigation";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { TDisplayWithContact } from "@formbricks/types/displays";
|
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import { TSurvey, TSurveySummary } from "@formbricks/types/surveys/types";
|
import { TSurvey, TSurveySummary } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import {
|
import { getSurveySummaryAction } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
|
||||||
getDisplaysWithContactAction,
|
|
||||||
getSurveySummaryAction,
|
|
||||||
} from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/actions";
|
|
||||||
import { useResponseFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context";
|
import { useResponseFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/components/response-filter-context";
|
||||||
import ScrollToTop from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ScrollToTop";
|
import ScrollToTop from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ScrollToTop";
|
||||||
import { SummaryDropOffs } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs";
|
import { SummaryDropOffs } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryDropOffs";
|
||||||
import { SummaryImpressions } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SummaryImpressions";
|
|
||||||
import { CustomFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/CustomFilter";
|
import { CustomFilter } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/CustomFilter";
|
||||||
import { getFormattedFilters } from "@/app/lib/surveys/surveys";
|
import { getFormattedFilters } from "@/app/lib/surveys/surveys";
|
||||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
|
||||||
import { replaceHeadlineRecall } from "@/lib/utils/recall";
|
import { replaceHeadlineRecall } from "@/lib/utils/recall";
|
||||||
import { QuotasSummary } from "@/modules/ee/quotas/components/quotas-summary";
|
import { QuotasSummary } from "@/modules/ee/quotas/components/quotas-summary";
|
||||||
import { SummaryList } from "./SummaryList";
|
import { SummaryList } from "./SummaryList";
|
||||||
import { SummaryMetadata } from "./SummaryMetadata";
|
import { SummaryMetadata } from "./SummaryMetadata";
|
||||||
|
|
||||||
const DISPLAYS_PER_PAGE = 15;
|
|
||||||
|
|
||||||
const defaultSurveySummary: TSurveySummary = {
|
const defaultSurveySummary: TSurveySummary = {
|
||||||
meta: {
|
meta: {
|
||||||
completedPercentage: 0,
|
completedPercentage: 0,
|
||||||
@@ -61,76 +51,17 @@ export const SummaryPage = ({
|
|||||||
initialSurveySummary,
|
initialSurveySummary,
|
||||||
isQuotasAllowed,
|
isQuotasAllowed,
|
||||||
}: SummaryPageProps) => {
|
}: SummaryPageProps) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
const [surveySummary, setSurveySummary] = useState<TSurveySummary>(
|
const [surveySummary, setSurveySummary] = useState<TSurveySummary>(
|
||||||
initialSurveySummary || defaultSurveySummary
|
initialSurveySummary || defaultSurveySummary
|
||||||
);
|
);
|
||||||
|
|
||||||
const [tab, setTab] = useState<"dropOffs" | "quotas" | "impressions" | undefined>(undefined);
|
const [tab, setTab] = useState<"dropOffs" | "quotas" | undefined>(undefined);
|
||||||
const [isLoading, setIsLoading] = useState(!initialSurveySummary);
|
const [isLoading, setIsLoading] = useState(!initialSurveySummary);
|
||||||
|
|
||||||
const { selectedFilter, dateRange, resetState } = useResponseFilter();
|
const { selectedFilter, dateRange, resetState } = useResponseFilter();
|
||||||
|
|
||||||
const [displays, setDisplays] = useState<TDisplayWithContact[]>([]);
|
|
||||||
const [isDisplaysLoading, setIsDisplaysLoading] = useState(false);
|
|
||||||
const [hasMoreDisplays, setHasMoreDisplays] = useState(true);
|
|
||||||
const [displaysError, setDisplaysError] = useState<string | null>(null);
|
|
||||||
const displaysFetchedRef = useRef(false);
|
|
||||||
|
|
||||||
const fetchDisplays = useCallback(
|
|
||||||
async (offset: number) => {
|
|
||||||
const response = await getDisplaysWithContactAction({
|
|
||||||
surveyId,
|
|
||||||
limit: DISPLAYS_PER_PAGE,
|
|
||||||
offset,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response?.data) {
|
|
||||||
const errorMessage = getFormattedErrorMessage(response);
|
|
||||||
throw new Error(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response?.data ?? [];
|
|
||||||
},
|
|
||||||
[surveyId]
|
|
||||||
);
|
|
||||||
|
|
||||||
const loadInitialDisplays = useCallback(async () => {
|
|
||||||
setIsDisplaysLoading(true);
|
|
||||||
setDisplaysError(null);
|
|
||||||
try {
|
|
||||||
const data = await fetchDisplays(0);
|
|
||||||
setDisplays(data);
|
|
||||||
setHasMoreDisplays(data.length === DISPLAYS_PER_PAGE);
|
|
||||||
} catch (error) {
|
|
||||||
toast.error(error);
|
|
||||||
setDisplays([]);
|
|
||||||
setHasMoreDisplays(false);
|
|
||||||
} finally {
|
|
||||||
setIsDisplaysLoading(false);
|
|
||||||
}
|
|
||||||
}, [fetchDisplays, t]);
|
|
||||||
|
|
||||||
const handleLoadMoreDisplays = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
const data = await fetchDisplays(displays.length);
|
|
||||||
setDisplays((prev) => [...prev, ...data]);
|
|
||||||
setHasMoreDisplays(data.length === DISPLAYS_PER_PAGE);
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : t("common.something_went_wrong");
|
|
||||||
toast.error(errorMessage);
|
|
||||||
}
|
|
||||||
}, [fetchDisplays, displays.length, t]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (tab === "impressions" && !displaysFetchedRef.current) {
|
|
||||||
displaysFetchedRef.current = true;
|
|
||||||
loadInitialDisplays();
|
|
||||||
}
|
|
||||||
}, [tab, loadInitialDisplays]);
|
|
||||||
|
|
||||||
// Only fetch data when filters change or when there's no initial data
|
// Only fetch data when filters change or when there's no initial data
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// If we have initial data and no filters are applied, don't fetch
|
// If we have initial data and no filters are applied, don't fetch
|
||||||
@@ -190,18 +121,6 @@ export const SummaryPage = ({
|
|||||||
setTab={setTab}
|
setTab={setTab}
|
||||||
isQuotasAllowed={isQuotasAllowed}
|
isQuotasAllowed={isQuotasAllowed}
|
||||||
/>
|
/>
|
||||||
{tab === "impressions" && (
|
|
||||||
<SummaryImpressions
|
|
||||||
displays={displays}
|
|
||||||
isLoading={isDisplaysLoading}
|
|
||||||
hasMore={hasMoreDisplays}
|
|
||||||
displaysError={displaysError}
|
|
||||||
environmentId={environment.id}
|
|
||||||
locale={locale}
|
|
||||||
onLoadMore={handleLoadMoreDisplays}
|
|
||||||
onRetry={loadInitialDisplays}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{tab === "dropOffs" && <SummaryDropOffs dropOff={surveySummary.dropOff} survey={surveyMemoized} />}
|
{tab === "dropOffs" && <SummaryDropOffs dropOff={surveySummary.dropOff} survey={surveyMemoized} />}
|
||||||
{isQuotasAllowed && tab === "quotas" && <QuotasSummary quotas={surveySummary.quotas} />}
|
{isQuotasAllowed && tab === "quotas" && <QuotasSummary quotas={surveySummary.quotas} />}
|
||||||
<div className="flex gap-1.5">
|
<div className="flex gap-1.5">
|
||||||
|
|||||||
+2
-2
@@ -4,9 +4,9 @@ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react";
|
|||||||
import { BaseCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/base-card";
|
import { BaseCard } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/base-card";
|
||||||
|
|
||||||
interface InteractiveCardProps {
|
interface InteractiveCardProps {
|
||||||
tab: "dropOffs" | "quotas" | "impressions";
|
tab: "dropOffs" | "quotas";
|
||||||
label: string;
|
label: string;
|
||||||
percentage: number | null;
|
percentage: number;
|
||||||
value: React.ReactNode;
|
value: React.ReactNode;
|
||||||
tooltipText: string;
|
tooltipText: string;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
|
|||||||
+1
-1
@@ -352,7 +352,7 @@ export const AnonymousLinksTab = ({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("environments.surveys.share.anonymous_links.custom_start_point"),
|
title: t("environments.surveys.share.anonymous_links.custom_start_point"),
|
||||||
href: "https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/start-at-block",
|
href: "https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/start-at-question",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+2
-2
@@ -105,7 +105,7 @@ export const CustomHtmlTab = ({ projectCustomScripts, isReadOnly }: CustomHtmlTa
|
|||||||
<div className={scriptsMode === "replace" ? "opacity-50" : ""}>
|
<div className={scriptsMode === "replace" ? "opacity-50" : ""}>
|
||||||
<FormLabel>{t("environments.surveys.share.custom_html.workspace_scripts_label")}</FormLabel>
|
<FormLabel>{t("environments.surveys.share.custom_html.workspace_scripts_label")}</FormLabel>
|
||||||
<div className="mt-2 max-h-32 overflow-auto rounded-md border border-slate-200 bg-slate-50 p-3">
|
<div className="mt-2 max-h-32 overflow-auto rounded-md border border-slate-200 bg-slate-50 p-3">
|
||||||
<pre className="whitespace-pre-wrap font-mono text-xs text-slate-600">
|
<pre className="font-mono text-xs whitespace-pre-wrap text-slate-600">
|
||||||
{projectCustomScripts}
|
{projectCustomScripts}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,7 +135,7 @@ export const CustomHtmlTab = ({ projectCustomScripts, isReadOnly }: CustomHtmlTa
|
|||||||
rows={8}
|
rows={8}
|
||||||
placeholder={t("environments.surveys.share.custom_html.placeholder")}
|
placeholder={t("environments.surveys.share.custom_html.placeholder")}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full rounded-md border border-slate-300 bg-white px-3 py-2 font-mono text-xs text-slate-800 placeholder:text-slate-400 focus:border-brand-dark focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
"focus:border-brand-dark flex w-full rounded-md border border-slate-300 bg-white px-3 py-2 font-mono text-xs text-slate-800 placeholder:text-slate-400 focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
)}
|
)}
|
||||||
{...field}
|
{...field}
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
|
|||||||
+1
-1
@@ -66,7 +66,7 @@ export const SuccessView: React.FC<SuccessViewProps> = ({
|
|||||||
className="relative flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-center text-sm text-slate-900 hover:border-slate-200 md:p-8">
|
className="relative flex flex-col items-center gap-3 rounded-lg border border-slate-100 bg-white p-4 text-center text-sm text-slate-900 hover:border-slate-200 md:p-8">
|
||||||
<UserIcon className="h-8 w-8 stroke-1 text-slate-900" />
|
<UserIcon className="h-8 w-8 stroke-1 text-slate-900" />
|
||||||
{t("environments.surveys.summary.use_personal_links")}
|
{t("environments.surveys.summary.use_personal_links")}
|
||||||
<Badge size="normal" type="success" className="absolute right-3 top-3" text={t("common.new")} />
|
<Badge size="normal" type="success" className="absolute top-3 right-3" text={t("common.new")} />
|
||||||
</button>
|
</button>
|
||||||
<Link
|
<Link
|
||||||
href={`/environments/${environmentId}/settings/notifications`}
|
href={`/environments/${environmentId}/settings/notifications`}
|
||||||
|
|||||||
+1
-1
@@ -192,7 +192,7 @@ export const ElementsComboBox = ({ options, selected, onChangeValue }: ElementCo
|
|||||||
value={inputValue}
|
value={inputValue}
|
||||||
onValueChange={setInputValue}
|
onValueChange={setInputValue}
|
||||||
placeholder={open ? `${t("common.search")}...` : t("common.select_filter")}
|
placeholder={open ? `${t("common.search")}...` : t("common.select_filter")}
|
||||||
className="max-w-full grow border-none p-0 pl-2 text-sm shadow-none outline-none ring-offset-transparent focus:border-none focus:shadow-none focus:outline-none focus:ring-offset-0"
|
className="max-w-full grow border-none p-0 pl-2 text-sm shadow-none ring-offset-transparent outline-none focus:border-none focus:shadow-none focus:ring-offset-0 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
+2
-2
@@ -241,7 +241,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
|||||||
<Popover open={isOpen} onOpenChange={handleOpenChange}>
|
<Popover open={isOpen} onOpenChange={handleOpenChange}>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<PopoverTriggerButton isOpen={isOpen}>
|
<PopoverTriggerButton isOpen={isOpen}>
|
||||||
{t("common.filter")} <b>{activeFilterCount > 0 && `(${activeFilterCount})`}</b>
|
Filter <b>{activeFilterCount > 0 && `(${activeFilterCount})`}</b>
|
||||||
</PopoverTriggerButton>
|
</PopoverTriggerButton>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
@@ -329,7 +329,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
|||||||
</div>
|
</div>
|
||||||
{i !== filterValue.filter.length - 1 && (
|
{i !== filterValue.filter.length - 1 && (
|
||||||
<div className="my-4 flex items-center">
|
<div className="my-4 flex items-center">
|
||||||
<p className="mr-4 font-semibold text-slate-800">{t("common.and")}</p>
|
<p className="mr-4 font-semibold text-slate-800">and</p>
|
||||||
<hr className="w-full text-slate-600" />
|
<hr className="w-full text-slate-600" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+2
-39
@@ -1,49 +1,12 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ZId } from "@formbricks/types/common";
|
import { ZIntegrationGoogleSheets } from "@formbricks/types/integration/google-sheet";
|
||||||
import {
|
import { getSpreadsheetNameById } from "@/lib/googleSheet/service";
|
||||||
TIntegrationGoogleSheets,
|
|
||||||
ZIntegrationGoogleSheets,
|
|
||||||
} from "@formbricks/types/integration/google-sheet";
|
|
||||||
import { getSpreadsheetNameById, validateGoogleSheetsConnection } from "@/lib/googleSheet/service";
|
|
||||||
import { getIntegrationByType } from "@/lib/integration/service";
|
|
||||||
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
import { authenticatedActionClient } from "@/lib/utils/action-client";
|
||||||
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
import { checkAuthorizationUpdated } from "@/lib/utils/action-client/action-client-middleware";
|
||||||
import { getOrganizationIdFromEnvironmentId, getProjectIdFromEnvironmentId } from "@/lib/utils/helper";
|
import { getOrganizationIdFromEnvironmentId, getProjectIdFromEnvironmentId } from "@/lib/utils/helper";
|
||||||
|
|
||||||
const ZValidateGoogleSheetsConnectionAction = z.object({
|
|
||||||
environmentId: ZId,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const validateGoogleSheetsConnectionAction = authenticatedActionClient
|
|
||||||
.schema(ZValidateGoogleSheetsConnectionAction)
|
|
||||||
.action(async ({ ctx, parsedInput }) => {
|
|
||||||
await checkAuthorizationUpdated({
|
|
||||||
userId: ctx.user.id,
|
|
||||||
organizationId: await getOrganizationIdFromEnvironmentId(parsedInput.environmentId),
|
|
||||||
access: [
|
|
||||||
{
|
|
||||||
type: "organization",
|
|
||||||
roles: ["owner", "manager"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "projectTeam",
|
|
||||||
projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
|
|
||||||
minPermission: "readWrite",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
const integration = await getIntegrationByType(parsedInput.environmentId, "googleSheets");
|
|
||||||
if (!integration) {
|
|
||||||
return { data: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
await validateGoogleSheetsConnection(integration as TIntegrationGoogleSheets);
|
|
||||||
return { data: true };
|
|
||||||
});
|
|
||||||
|
|
||||||
const ZGetSpreadsheetNameByIdAction = z.object({
|
const ZGetSpreadsheetNameByIdAction = z.object({
|
||||||
googleSheetIntegration: ZIntegrationGoogleSheets,
|
googleSheetIntegration: ZIntegrationGoogleSheets,
|
||||||
environmentId: z.string(),
|
environmentId: z.string(),
|
||||||
|
|||||||
+4
-18
@@ -20,10 +20,6 @@ import {
|
|||||||
isValidGoogleSheetsUrl,
|
isValidGoogleSheetsUrl,
|
||||||
} from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/util";
|
} from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/util";
|
||||||
import GoogleSheetLogo from "@/images/googleSheetsLogo.png";
|
import GoogleSheetLogo from "@/images/googleSheetsLogo.png";
|
||||||
import {
|
|
||||||
GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION,
|
|
||||||
GOOGLE_SHEET_INTEGRATION_INVALID_GRANT,
|
|
||||||
} from "@/lib/googleSheet/constants";
|
|
||||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||||
import { recallToHeadline } from "@/lib/utils/recall";
|
import { recallToHeadline } from "@/lib/utils/recall";
|
||||||
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
|
import { getElementsFromBlocks } from "@/modules/survey/lib/client-utils";
|
||||||
@@ -122,17 +118,6 @@ export const AddIntegrationModal = ({
|
|||||||
resetForm();
|
resetForm();
|
||||||
}, [selectedIntegration, surveys]);
|
}, [selectedIntegration, surveys]);
|
||||||
|
|
||||||
const showErrorMessageToast = (response: Awaited<ReturnType<typeof getSpreadsheetNameByIdAction>>) => {
|
|
||||||
const errorMessage = getFormattedErrorMessage(response);
|
|
||||||
if (errorMessage === GOOGLE_SHEET_INTEGRATION_INVALID_GRANT) {
|
|
||||||
toast.error(t("environments.integrations.google_sheets.token_expired_error"));
|
|
||||||
} else if (errorMessage === GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION) {
|
|
||||||
toast.error(t("environments.integrations.google_sheets.spreadsheet_permission_error"));
|
|
||||||
} else {
|
|
||||||
toast.error(errorMessage);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const linkSheet = async () => {
|
const linkSheet = async () => {
|
||||||
try {
|
try {
|
||||||
if (!isValidGoogleSheetsUrl(spreadsheetUrl)) {
|
if (!isValidGoogleSheetsUrl(spreadsheetUrl)) {
|
||||||
@@ -144,7 +129,6 @@ export const AddIntegrationModal = ({
|
|||||||
if (selectedElements.length === 0) {
|
if (selectedElements.length === 0) {
|
||||||
throw new Error(t("environments.integrations.select_at_least_one_question_error"));
|
throw new Error(t("environments.integrations.select_at_least_one_question_error"));
|
||||||
}
|
}
|
||||||
setIsLinkingSheet(true);
|
|
||||||
const spreadsheetId = extractSpreadsheetIdFromUrl(spreadsheetUrl);
|
const spreadsheetId = extractSpreadsheetIdFromUrl(spreadsheetUrl);
|
||||||
const spreadsheetNameResponse = await getSpreadsheetNameByIdAction({
|
const spreadsheetNameResponse = await getSpreadsheetNameByIdAction({
|
||||||
googleSheetIntegration,
|
googleSheetIntegration,
|
||||||
@@ -153,11 +137,13 @@ export const AddIntegrationModal = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!spreadsheetNameResponse?.data) {
|
if (!spreadsheetNameResponse?.data) {
|
||||||
showErrorMessageToast(spreadsheetNameResponse);
|
const errorMessage = getFormattedErrorMessage(spreadsheetNameResponse);
|
||||||
return;
|
throw new Error(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
const spreadsheetName = spreadsheetNameResponse.data;
|
const spreadsheetName = spreadsheetNameResponse.data;
|
||||||
|
|
||||||
|
setIsLinkingSheet(true);
|
||||||
integrationData.spreadsheetId = spreadsheetId;
|
integrationData.spreadsheetId = spreadsheetId;
|
||||||
integrationData.spreadsheetName = spreadsheetName;
|
integrationData.spreadsheetName = spreadsheetName;
|
||||||
integrationData.surveyId = selectedSurvey.id;
|
integrationData.surveyId = selectedSurvey.id;
|
||||||
|
|||||||
+1
-18
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { TEnvironment } from "@formbricks/types/environment";
|
import { TEnvironment } from "@formbricks/types/environment";
|
||||||
import {
|
import {
|
||||||
TIntegrationGoogleSheets,
|
TIntegrationGoogleSheets,
|
||||||
@@ -8,11 +8,9 @@ import {
|
|||||||
} from "@formbricks/types/integration/google-sheet";
|
} from "@formbricks/types/integration/google-sheet";
|
||||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||||
import { TUserLocale } from "@formbricks/types/user";
|
import { TUserLocale } from "@formbricks/types/user";
|
||||||
import { validateGoogleSheetsConnectionAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/actions";
|
|
||||||
import { ManageIntegration } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/ManageIntegration";
|
import { ManageIntegration } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/components/ManageIntegration";
|
||||||
import { authorize } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/google";
|
import { authorize } from "@/app/(app)/environments/[environmentId]/workspace/integrations/google-sheets/lib/google";
|
||||||
import googleSheetLogo from "@/images/googleSheetsLogo.png";
|
import googleSheetLogo from "@/images/googleSheetsLogo.png";
|
||||||
import { GOOGLE_SHEET_INTEGRATION_INVALID_GRANT } from "@/lib/googleSheet/constants";
|
|
||||||
import { ConnectIntegration } from "@/modules/ui/components/connect-integration";
|
import { ConnectIntegration } from "@/modules/ui/components/connect-integration";
|
||||||
import { AddIntegrationModal } from "./AddIntegrationModal";
|
import { AddIntegrationModal } from "./AddIntegrationModal";
|
||||||
|
|
||||||
@@ -37,23 +35,10 @@ export const GoogleSheetWrapper = ({
|
|||||||
googleSheetIntegration ? googleSheetIntegration.config?.key : false
|
googleSheetIntegration ? googleSheetIntegration.config?.key : false
|
||||||
);
|
);
|
||||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||||
const [showReconnectButton, setShowReconnectButton] = useState<boolean>(false);
|
|
||||||
const [selectedIntegration, setSelectedIntegration] = useState<
|
const [selectedIntegration, setSelectedIntegration] = useState<
|
||||||
(TIntegrationGoogleSheetsConfigData & { index: number }) | null
|
(TIntegrationGoogleSheetsConfigData & { index: number }) | null
|
||||||
>(null);
|
>(null);
|
||||||
|
|
||||||
const validateConnection = useCallback(async () => {
|
|
||||||
if (!isConnected || !googleSheetIntegration) return;
|
|
||||||
const response = await validateGoogleSheetsConnectionAction({ environmentId: environment.id });
|
|
||||||
if (response?.serverError === GOOGLE_SHEET_INTEGRATION_INVALID_GRANT) {
|
|
||||||
setShowReconnectButton(true);
|
|
||||||
}
|
|
||||||
}, [environment.id, isConnected, googleSheetIntegration]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
validateConnection();
|
|
||||||
}, [validateConnection]);
|
|
||||||
|
|
||||||
const handleGoogleAuthorization = async () => {
|
const handleGoogleAuthorization = async () => {
|
||||||
authorize(environment.id, webAppUrl).then((url: string) => {
|
authorize(environment.id, webAppUrl).then((url: string) => {
|
||||||
if (url) {
|
if (url) {
|
||||||
@@ -79,8 +64,6 @@ export const GoogleSheetWrapper = ({
|
|||||||
setOpenAddIntegrationModal={setIsModalOpen}
|
setOpenAddIntegrationModal={setIsModalOpen}
|
||||||
setIsConnected={setIsConnected}
|
setIsConnected={setIsConnected}
|
||||||
setSelectedIntegration={setSelectedIntegration}
|
setSelectedIntegration={setSelectedIntegration}
|
||||||
showReconnectButton={showReconnectButton}
|
|
||||||
handleGoogleAuthorization={handleGoogleAuthorization}
|
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
+2
-31
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { RefreshCcwIcon, Trash2Icon } from "lucide-react";
|
import { Trash2Icon } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
@@ -12,19 +12,15 @@ import { TUserLocale } from "@formbricks/types/user";
|
|||||||
import { deleteIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions";
|
import { deleteIntegrationAction } from "@/app/(app)/environments/[environmentId]/workspace/integrations/actions";
|
||||||
import { timeSince } from "@/lib/time";
|
import { timeSince } from "@/lib/time";
|
||||||
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
import { getFormattedErrorMessage } from "@/lib/utils/helper";
|
||||||
import { Alert, AlertButton, AlertDescription } from "@/modules/ui/components/alert";
|
|
||||||
import { Button } from "@/modules/ui/components/button";
|
import { Button } from "@/modules/ui/components/button";
|
||||||
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
|
import { DeleteDialog } from "@/modules/ui/components/delete-dialog";
|
||||||
import { EmptyState } from "@/modules/ui/components/empty-state";
|
import { EmptyState } from "@/modules/ui/components/empty-state";
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
|
|
||||||
|
|
||||||
interface ManageIntegrationProps {
|
interface ManageIntegrationProps {
|
||||||
googleSheetIntegration: TIntegrationGoogleSheets;
|
googleSheetIntegration: TIntegrationGoogleSheets;
|
||||||
setOpenAddIntegrationModal: (v: boolean) => void;
|
setOpenAddIntegrationModal: (v: boolean) => void;
|
||||||
setIsConnected: (v: boolean) => void;
|
setIsConnected: (v: boolean) => void;
|
||||||
setSelectedIntegration: (v: (TIntegrationGoogleSheetsConfigData & { index: number }) | null) => void;
|
setSelectedIntegration: (v: (TIntegrationGoogleSheetsConfigData & { index: number }) | null) => void;
|
||||||
showReconnectButton: boolean;
|
|
||||||
handleGoogleAuthorization: () => void;
|
|
||||||
locale: TUserLocale;
|
locale: TUserLocale;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,8 +29,6 @@ export const ManageIntegration = ({
|
|||||||
setOpenAddIntegrationModal,
|
setOpenAddIntegrationModal,
|
||||||
setIsConnected,
|
setIsConnected,
|
||||||
setSelectedIntegration,
|
setSelectedIntegration,
|
||||||
showReconnectButton,
|
|
||||||
handleGoogleAuthorization,
|
|
||||||
locale,
|
locale,
|
||||||
}: ManageIntegrationProps) => {
|
}: ManageIntegrationProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -74,17 +68,7 @@ export const ManageIntegration = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-6 flex w-full flex-col items-center justify-center p-6">
|
<div className="mt-6 flex w-full flex-col items-center justify-center p-6">
|
||||||
{showReconnectButton && (
|
<div className="flex w-full justify-end">
|
||||||
<Alert variant="warning" size="small" className="mb-4 w-full">
|
|
||||||
<AlertDescription>
|
|
||||||
{t("environments.integrations.google_sheets.reconnect_button_description")}
|
|
||||||
</AlertDescription>
|
|
||||||
<AlertButton onClick={handleGoogleAuthorization}>
|
|
||||||
{t("environments.integrations.google_sheets.reconnect_button")}
|
|
||||||
</AlertButton>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
<div className="flex w-full justify-end space-x-2">
|
|
||||||
<div className="mr-6 flex items-center">
|
<div className="mr-6 flex items-center">
|
||||||
<span className="mr-4 h-4 w-4 rounded-full bg-green-600"></span>
|
<span className="mr-4 h-4 w-4 rounded-full bg-green-600"></span>
|
||||||
<span className="text-slate-500">
|
<span className="text-slate-500">
|
||||||
@@ -93,19 +77,6 @@ export const ManageIntegration = ({
|
|||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<TooltipProvider>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<Button variant="outline" onClick={handleGoogleAuthorization}>
|
|
||||||
<RefreshCcwIcon className="mr-2 h-4 w-4" />
|
|
||||||
{t("environments.integrations.google_sheets.reconnect_button")}
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
{t("environments.integrations.google_sheets.reconnect_button_tooltip")}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedIntegration(null);
|
setSelectedIntegration(null);
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ const Loading = () => {
|
|||||||
<div className="mt-6 p-6">
|
<div className="mt-6 p-6">
|
||||||
<GoBackButton />
|
<GoBackButton />
|
||||||
<div className="mb-6 text-right">
|
<div className="mb-6 text-right">
|
||||||
<Button className="pointer-events-none animate-pulse cursor-not-allowed select-none bg-slate-200">
|
<Button className="pointer-events-none animate-pulse cursor-not-allowed bg-slate-200 select-none">
|
||||||
{t("environments.integrations.google_sheets.link_new_sheet")}
|
{t("environments.integrations.google_sheets.link_new_sheet")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,7 +51,7 @@ const Loading = () => {
|
|||||||
<div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div>
|
<div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 my-auto flex items-center justify-center whitespace-nowrap text-center text-sm text-slate-500">
|
<div className="col-span-2 my-auto flex items-center justify-center text-center text-sm whitespace-nowrap text-slate-500">
|
||||||
<div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div>
|
<div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center"></div>
|
<div className="text-center"></div>
|
||||||
|
|||||||
+2
-2
@@ -10,7 +10,7 @@ const Loading = () => {
|
|||||||
<div className="mt-6 p-6">
|
<div className="mt-6 p-6">
|
||||||
<GoBackButton />
|
<GoBackButton />
|
||||||
<div className="mb-6 text-right">
|
<div className="mb-6 text-right">
|
||||||
<Button className="pointer-events-none animate-pulse cursor-not-allowed select-none bg-slate-200">
|
<Button className="pointer-events-none animate-pulse cursor-not-allowed bg-slate-200 select-none">
|
||||||
{t("environments.integrations.notion.link_database")}
|
{t("environments.integrations.notion.link_database")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@ const Loading = () => {
|
|||||||
<div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div>
|
<div className="mt-0 h-4 w-24 animate-pulse rounded-full bg-slate-200"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2 my-auto flex items-center justify-center whitespace-nowrap text-center text-sm text-slate-500">
|
<div className="col-span-2 my-auto flex items-center justify-center text-center text-sm whitespace-nowrap text-slate-500">
|
||||||
<div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div>
|
<div className="h-4 w-16 animate-pulse rounded-full bg-slate-200"></div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center"></div>
|
<div className="text-center"></div>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import { getElementsFromBlocks } from "@/lib/survey/utils";
|
|||||||
import { getFormattedDateTimeString } from "@/lib/utils/datetime";
|
import { getFormattedDateTimeString } from "@/lib/utils/datetime";
|
||||||
import { parseRecallInfo } from "@/lib/utils/recall";
|
import { parseRecallInfo } from "@/lib/utils/recall";
|
||||||
import { truncateText } from "@/lib/utils/strings";
|
import { truncateText } from "@/lib/utils/strings";
|
||||||
import { resolveStorageUrlAuto } from "@/modules/storage/utils";
|
|
||||||
|
|
||||||
const convertMetaObjectToString = (metadata: TResponseMeta): string => {
|
const convertMetaObjectToString = (metadata: TResponseMeta): string => {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
@@ -257,16 +256,10 @@ const processElementResponse = (
|
|||||||
const selectedChoiceIds = responseValue as string[];
|
const selectedChoiceIds = responseValue as string[];
|
||||||
return element.choices
|
return element.choices
|
||||||
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
||||||
.map((choice) => resolveStorageUrlAuto(choice.imageUrl))
|
.map((choice) => choice.imageUrl)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.type === TSurveyElementTypeEnum.FileUpload && Array.isArray(responseValue)) {
|
|
||||||
return responseValue
|
|
||||||
.map((url) => (typeof url === "string" ? resolveStorageUrlAuto(url) : url))
|
|
||||||
.join("; ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return processResponseData(responseValue);
|
return processResponseData(responseValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -375,7 +368,7 @@ const buildNotionPayloadProperties = (
|
|||||||
|
|
||||||
responses[resp] = (pictureElement as any)?.choices
|
responses[resp] = (pictureElement as any)?.choices
|
||||||
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
.filter((choice) => selectedChoiceIds.includes(choice.id))
|
||||||
.map((choice) => resolveStorageUrlAuto(choice.imageUrl));
|
.map((choice) => choice.imageUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { convertDatesInObject } from "@/lib/time";
|
|||||||
import { queueAuditEvent } from "@/modules/ee/audit-logs/lib/handler";
|
import { queueAuditEvent } from "@/modules/ee/audit-logs/lib/handler";
|
||||||
import { TAuditStatus, UNKNOWN_DATA } from "@/modules/ee/audit-logs/types/audit-log";
|
import { TAuditStatus, UNKNOWN_DATA } from "@/modules/ee/audit-logs/types/audit-log";
|
||||||
import { sendResponseFinishedEmail } from "@/modules/email";
|
import { sendResponseFinishedEmail } from "@/modules/email";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { sendFollowUpsForResponse } from "@/modules/survey/follow-ups/lib/follow-ups";
|
import { sendFollowUpsForResponse } from "@/modules/survey/follow-ups/lib/follow-ups";
|
||||||
import { FollowUpSendError } from "@/modules/survey/follow-ups/types/follow-up";
|
import { FollowUpSendError } from "@/modules/survey/follow-ups/types/follow-up";
|
||||||
import { handleIntegrations } from "./lib/handleIntegrations";
|
import { handleIntegrations } from "./lib/handleIntegrations";
|
||||||
@@ -96,15 +95,12 @@ export const POST = async (request: Request) => {
|
|||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolvedResponseData = resolveStorageUrlsInObject(response.data);
|
|
||||||
|
|
||||||
const webhookPromises = webhooks.map((webhook) => {
|
const webhookPromises = webhooks.map((webhook) => {
|
||||||
const body = JSON.stringify({
|
const body = JSON.stringify({
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
event,
|
event,
|
||||||
data: {
|
data: {
|
||||||
...response,
|
...response,
|
||||||
data: resolvedResponseData,
|
|
||||||
survey: {
|
survey: {
|
||||||
title: survey.name,
|
title: survey.name,
|
||||||
type: survey.type,
|
type: survey.type,
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import cubejs, { Query } from "@cubejs-client/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cube.js client for executing analytics queries.
|
||||||
|
*
|
||||||
|
* Authentication is handled via the CUBEJS_API_SECRET env var which must match
|
||||||
|
* the secret configured on the Cube.js server. In development an empty token is
|
||||||
|
* accepted when the Cube.js instance runs in dev mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const getApiUrl = () => {
|
||||||
|
const baseUrl = process.env.CUBEJS_API_URL || "http://localhost:4000";
|
||||||
|
if (baseUrl.includes("/cubejs-api/v1")) {
|
||||||
|
return baseUrl;
|
||||||
|
}
|
||||||
|
return `${baseUrl.replace(/\/$/, "")}/cubejs-api/v1`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const API_URL = getApiUrl();
|
||||||
|
|
||||||
|
export function createCubeClient() {
|
||||||
|
const token = process.env.CUBEJS_API_TOKEN ?? "";
|
||||||
|
console.log(`[CubeClient] Connecting to ${API_URL} (token ${token ? "set" : "empty"})`);
|
||||||
|
return cubejs(token, {
|
||||||
|
apiUrl: API_URL,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a Cube.js query and return the table pivot data.
|
||||||
|
*/
|
||||||
|
export async function executeQuery(query: Query) {
|
||||||
|
console.log("[CubeClient] executeQuery called with:", JSON.stringify(query, null, 2));
|
||||||
|
const client = createCubeClient();
|
||||||
|
try {
|
||||||
|
const resultSet = await client.load(query);
|
||||||
|
const rows = resultSet.tablePivot();
|
||||||
|
console.log(`[CubeClient] Query succeeded — ${rows.length} row(s) returned`);
|
||||||
|
return rows;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[CubeClient] Query failed:", {
|
||||||
|
message: error instanceof Error ? error.message : String(error),
|
||||||
|
stack: error instanceof Error ? error.stack : undefined,
|
||||||
|
apiUrl: API_URL,
|
||||||
|
query,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the Cube.js schema file to extract measures and dimensions
|
||||||
|
* This keeps the schema as the single source of truth for AI query generation
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface MeasureInfo {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DimensionInfo {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
type: "string" | "number" | "time";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path to schema file - self-contained within the analytics folder
|
||||||
|
const SCHEMA_FILE_PATH = path.join(process.cwd(), "app", "api", "analytics", "_schema", "FeedbackRecords.js");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract description from a schema property object
|
||||||
|
*/
|
||||||
|
function extractDescription(objStr: string): string {
|
||||||
|
const descMatch = objStr.match(/description:\s*`([^`]+)`/);
|
||||||
|
return descMatch ? descMatch[1] : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract type from a dimension object
|
||||||
|
*/
|
||||||
|
function extractType(objStr: string): "string" | "number" | "time" {
|
||||||
|
const typeMatch = objStr.match(/type:\s*`(string|number|time)`/);
|
||||||
|
return (typeMatch?.[1] as "string" | "number" | "time") || "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to extract content inside the first matching brace block
|
||||||
|
*/
|
||||||
|
function extractInnerBlockContent(content: string, startRegex: RegExp): string | null {
|
||||||
|
const match = content.match(startRegex);
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
// Backtrack to find the opening brace in the match
|
||||||
|
const braceIndex = match[0].lastIndexOf("{");
|
||||||
|
if (braceIndex === -1) return null; // Should not happen given regex usage
|
||||||
|
|
||||||
|
// Actually we can just start scanning from the end of the match if the regex ends with {
|
||||||
|
// But let's be safer: start counting from the opening brace.
|
||||||
|
const absoluteStartIndex = match.index! + braceIndex;
|
||||||
|
|
||||||
|
let braceCount = 1;
|
||||||
|
let i = absoluteStartIndex + 1;
|
||||||
|
|
||||||
|
while (braceCount > 0 && i < content.length) {
|
||||||
|
if (content[i] === "{") braceCount++;
|
||||||
|
else if (content[i] === "}") braceCount--;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (braceCount === 0) {
|
||||||
|
return content.substring(absoluteStartIndex + 1, i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse measures from the schema file
|
||||||
|
*/
|
||||||
|
function parseMeasures(schemaContent: string): MeasureInfo[] {
|
||||||
|
const measures: MeasureInfo[] = [];
|
||||||
|
|
||||||
|
const measuresBlock = extractInnerBlockContent(schemaContent, /measures:\s*\{/);
|
||||||
|
if (!measuresBlock) return measures;
|
||||||
|
|
||||||
|
// Match each measure: measureName: { ... }
|
||||||
|
const measureRegex = /(\w+):\s*\{/g;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = measureRegex.exec(measuresBlock)) !== null) {
|
||||||
|
const name = match[1];
|
||||||
|
const startIndex = match.index + match[0].length;
|
||||||
|
|
||||||
|
// Find the matching closing brace
|
||||||
|
let braceCount = 1;
|
||||||
|
let endIndex = startIndex;
|
||||||
|
|
||||||
|
while (braceCount > 0 && endIndex < measuresBlock.length) {
|
||||||
|
if (measuresBlock[endIndex] === "{") braceCount++;
|
||||||
|
if (measuresBlock[endIndex] === "}") braceCount--;
|
||||||
|
endIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = measuresBlock.substring(startIndex, endIndex - 1);
|
||||||
|
const description = extractDescription(body);
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
measures.push({ name, description });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return measures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse dimensions from a specific cube
|
||||||
|
*/
|
||||||
|
function parseDimensionsFromCube(cubeContent: string, cubeName: string): DimensionInfo[] {
|
||||||
|
const dimensions: DimensionInfo[] = [];
|
||||||
|
|
||||||
|
const dimensionsBlock = extractInnerBlockContent(cubeContent, /dimensions:\s*\{/);
|
||||||
|
if (!dimensionsBlock) return dimensions;
|
||||||
|
|
||||||
|
// Match each dimension: dimensionName: { ... }
|
||||||
|
const dimensionRegex = /(\w+):\s*\{/g;
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = dimensionRegex.exec(dimensionsBlock)) !== null) {
|
||||||
|
const name = match[1];
|
||||||
|
const startIndex = match.index + match[0].length;
|
||||||
|
|
||||||
|
// Find the matching closing brace
|
||||||
|
let braceCount = 1;
|
||||||
|
let endIndex = startIndex;
|
||||||
|
|
||||||
|
while (braceCount > 0 && endIndex < dimensionsBlock.length) {
|
||||||
|
if (dimensionsBlock[endIndex] === "{") braceCount++;
|
||||||
|
if (dimensionsBlock[endIndex] === "}") braceCount--;
|
||||||
|
endIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = dimensionsBlock.substring(startIndex, endIndex - 1);
|
||||||
|
const description = extractDescription(body);
|
||||||
|
const type = extractType(body);
|
||||||
|
|
||||||
|
// Skip primaryKey dimensions (like 'id') and internal dimensions
|
||||||
|
if (body.includes("primaryKey: true") || name === "feedbackRecordId") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description) {
|
||||||
|
dimensions.push({
|
||||||
|
name: cubeName === "FeedbackRecords" ? name : `${cubeName}.${name}`,
|
||||||
|
description,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse dimensions from the schema file
|
||||||
|
*/
|
||||||
|
function parseDimensions(schemaContent: string): DimensionInfo[] {
|
||||||
|
const dimensions: DimensionInfo[] = [];
|
||||||
|
|
||||||
|
// Extract dimensions from FeedbackRecords cube
|
||||||
|
const feedbackRecordsMatch = schemaContent.match(/cube\(`FeedbackRecords`,\s*\{([\s\S]*?)\n\}\);/);
|
||||||
|
if (feedbackRecordsMatch) {
|
||||||
|
const feedbackRecordsDimensions = parseDimensionsFromCube(feedbackRecordsMatch[1], "FeedbackRecords");
|
||||||
|
dimensions.push(...feedbackRecordsDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract dimensions from TopicsUnnested cube
|
||||||
|
const topicsUnnestedMatch = schemaContent.match(/cube\(`TopicsUnnested`,\s*\{([\s\S]*?)\n\}\);/);
|
||||||
|
if (topicsUnnestedMatch) {
|
||||||
|
const topicsDimensions = parseDimensionsFromCube(topicsUnnestedMatch[1], "TopicsUnnested");
|
||||||
|
dimensions.push(...topicsDimensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and parse the schema file
|
||||||
|
*/
|
||||||
|
export function parseSchemaFile(): {
|
||||||
|
measures: MeasureInfo[];
|
||||||
|
dimensions: DimensionInfo[];
|
||||||
|
} {
|
||||||
|
try {
|
||||||
|
const schemaContent = fs.readFileSync(SCHEMA_FILE_PATH, "utf-8");
|
||||||
|
const measures = parseMeasures(schemaContent);
|
||||||
|
const dimensions = parseDimensions(schemaContent);
|
||||||
|
|
||||||
|
return { measures, dimensions };
|
||||||
|
} catch {
|
||||||
|
return { measures: [], dimensions: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the schema context string for AI query generation
|
||||||
|
*/
|
||||||
|
export function generateSchemaContext(): string {
|
||||||
|
const { measures, dimensions } = parseSchemaFile();
|
||||||
|
const CUBE_NAME = "FeedbackRecords";
|
||||||
|
|
||||||
|
const measuresList = measures.map((m) => `- ${CUBE_NAME}.${m.name} - ${m.description}`).join("\n");
|
||||||
|
|
||||||
|
const dimensionsList = dimensions
|
||||||
|
.map((d) => {
|
||||||
|
const typeLabel = d.type === "time" ? " (time dimension)" : ` (${d.type})`;
|
||||||
|
// Dimensions from TopicsUnnested already have the cube prefix
|
||||||
|
const fullName = d.name.includes(".") ? d.name : `${CUBE_NAME}.${d.name}`;
|
||||||
|
return `- ${fullName} - ${d.description}${typeLabel}`;
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const categoricalDimensions = dimensions
|
||||||
|
.filter(
|
||||||
|
(d) =>
|
||||||
|
d.type === "string" &&
|
||||||
|
!d.name.includes("responseId") &&
|
||||||
|
!d.name.includes("userIdentifier") &&
|
||||||
|
!d.name.includes("feedbackRecordId")
|
||||||
|
)
|
||||||
|
.map((d) => (d.name.includes(".") ? d.name : `${CUBE_NAME}.${d.name}`))
|
||||||
|
.join(", ");
|
||||||
|
|
||||||
|
return `
|
||||||
|
You are a CubeJS query generator. Your task is to convert natural language requests into valid CubeJS query JSON objects.
|
||||||
|
|
||||||
|
Available Cubes: ${CUBE_NAME}, TopicsUnnested
|
||||||
|
|
||||||
|
MEASURES (use these in the "measures" array):
|
||||||
|
${measuresList}
|
||||||
|
|
||||||
|
DIMENSIONS (use these in the "dimensions" array):
|
||||||
|
${dimensionsList}
|
||||||
|
|
||||||
|
TIME DIMENSIONS:
|
||||||
|
- ${CUBE_NAME}.collectedAt can be used with granularity: 'day', 'week', 'month', 'year'
|
||||||
|
- Use "timeDimensions" array for time-based queries with dateRange like "last 7 days", "last 30 days", "this month", etc.
|
||||||
|
|
||||||
|
CHART TYPE SUGGESTIONS:
|
||||||
|
- If query has timeDimensions → suggest "bar" or "line"
|
||||||
|
- If query has categorical dimensions (${categoricalDimensions}) → suggest "donut" or "bar"
|
||||||
|
- If query has only measures → suggest "kpi"
|
||||||
|
- If query compares multiple measures → suggest "bar"
|
||||||
|
|
||||||
|
FILTERS:
|
||||||
|
- Use "filters" array to include/exclude records based on dimension values
|
||||||
|
- Filter format: { "member": "CubeName.dimensionName", "operator": "operator" } OR { "member": "CubeName.dimensionName", "operator": "operator", "values": [...] }
|
||||||
|
- Common operators:
|
||||||
|
* "set" - dimension is not null/empty (Set "values" to null)
|
||||||
|
Example: { "member": "${CUBE_NAME}.emotion", "operator": "set", "values": null }
|
||||||
|
* "notSet" - dimension is null/empty (Set "values" to null)
|
||||||
|
Example: { "member": "${CUBE_NAME}.emotion", "operator": "notSet", "values": null }
|
||||||
|
* "equals" - exact match (REQUIRES "values" field)
|
||||||
|
Example: { "member": "${CUBE_NAME}.emotion", "operator": "equals", "values": ["happy"] }
|
||||||
|
* "notEquals" - not equal (REQUIRES "values" field)
|
||||||
|
Example: { "member": "${CUBE_NAME}.emotion", "operator": "notEquals", "values": ["sad"] }
|
||||||
|
* "contains" - contains text (REQUIRES "values" field)
|
||||||
|
Example: { "member": "${CUBE_NAME}.emotion", "operator": "contains", "values": ["happy"] }
|
||||||
|
- Examples for common user requests:
|
||||||
|
* "only records with emotion" or "for records that have emotion" → { "member": "${CUBE_NAME}.emotion", "operator": "set", "values": null }
|
||||||
|
* "exclude records without emotion" or "do not include records without emotion" → { "member": "${CUBE_NAME}.emotion", "operator": "set", "values": null }
|
||||||
|
* "exclude records with emotion" or "do not include records with emotion" → { "member": "${CUBE_NAME}.emotion", "operator": "notSet", "values": null }
|
||||||
|
* "only happy emotions" → { "member": "${CUBE_NAME}.emotion", "operator": "equals", "values": ["happy"] }
|
||||||
|
|
||||||
|
IMPORTANT RULES:
|
||||||
|
1. Always return valid JSON only, no markdown or code blocks
|
||||||
|
2. Use exact measure/dimension names as listed above
|
||||||
|
3. Include "chartType" field: "bar", "line", "donut", "kpi", or "area"
|
||||||
|
4. For time queries, use timeDimensions array with granularity and dateRange
|
||||||
|
5. Return format: { "measures": [...], "dimensions": [...], "timeDimensions": [...], "filters": [...], "chartType": "..." }
|
||||||
|
6. If user asks about trends over time, use timeDimensions
|
||||||
|
7. If user asks "by X", add X as a dimension
|
||||||
|
8. If user asks for counts or totals, use ${CUBE_NAME}.count
|
||||||
|
9. If user asks for NPS, use ${CUBE_NAME}.npsScore
|
||||||
|
10. If user asks about topics, use TopicsUnnested.topic (NOT ${CUBE_NAME}.topic)
|
||||||
|
11. CRITICAL: If user says "only records with X", "exclude records without X", or "for records that have X", add a filter with operator "set" for that dimension
|
||||||
|
12. CRITICAL: If user says "exclude records with X", "do not include records with X", or "without X", add a filter with operator "notSet" for that dimension
|
||||||
|
13. Always include filters when user explicitly mentions including/excluding records based on dimension values
|
||||||
|
`.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CUBE_NAME = "FeedbackRecords";
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* TypeScript types for the Analytics API
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface TimeDimension {
|
||||||
|
dimension: string;
|
||||||
|
granularity?: "hour" | "day" | "week" | "month" | "quarter" | "year";
|
||||||
|
dateRange?: string | string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Filter {
|
||||||
|
member: string;
|
||||||
|
operator:
|
||||||
|
| "equals"
|
||||||
|
| "notEquals"
|
||||||
|
| "contains"
|
||||||
|
| "notContains"
|
||||||
|
| "set"
|
||||||
|
| "notSet"
|
||||||
|
| "gt"
|
||||||
|
| "gte"
|
||||||
|
| "lt"
|
||||||
|
| "lte";
|
||||||
|
values?: string[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CubeQuery {
|
||||||
|
measures: string[];
|
||||||
|
dimensions?: string[];
|
||||||
|
timeDimensions?: TimeDimension[];
|
||||||
|
filters?: Filter[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AnalyticsResponse {
|
||||||
|
query: CubeQuery;
|
||||||
|
chartType: "bar" | "line" | "donut" | "kpi" | "area" | "pie";
|
||||||
|
data?: Record<string, any>[];
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
cube(`FeedbackRecords`, {
|
||||||
|
sql: `SELECT * FROM feedback_records`,
|
||||||
|
|
||||||
|
measures: {
|
||||||
|
count: {
|
||||||
|
type: `count`,
|
||||||
|
description: `Total number of feedback responses`,
|
||||||
|
},
|
||||||
|
|
||||||
|
promoterCount: {
|
||||||
|
type: `count`,
|
||||||
|
filters: [{ sql: `${CUBE}.value_number >= 9` }],
|
||||||
|
description: `Number of promoters (NPS score 9-10)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
detractorCount: {
|
||||||
|
type: `count`,
|
||||||
|
filters: [{ sql: `${CUBE}.value_number <= 6` }],
|
||||||
|
description: `Number of detractors (NPS score 0-6)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
passiveCount: {
|
||||||
|
type: `count`,
|
||||||
|
filters: [{ sql: `${CUBE}.value_number >= 7 AND ${CUBE}.value_number <= 8` }],
|
||||||
|
description: `Number of passives (NPS score 7-8)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
npsScore: {
|
||||||
|
type: `number`,
|
||||||
|
sql: `
|
||||||
|
CASE
|
||||||
|
WHEN COUNT(*) = 0 THEN 0
|
||||||
|
ELSE ROUND(
|
||||||
|
(
|
||||||
|
(COUNT(CASE WHEN ${CUBE}.value_number >= 9 THEN 1 END)::numeric -
|
||||||
|
COUNT(CASE WHEN ${CUBE}.value_number <= 6 THEN 1 END)::numeric)
|
||||||
|
/ COUNT(*)::numeric
|
||||||
|
) * 100,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
END
|
||||||
|
`,
|
||||||
|
description: `Net Promoter Score: ((Promoters - Detractors) / Total) * 100`,
|
||||||
|
},
|
||||||
|
|
||||||
|
averageScore: {
|
||||||
|
type: `avg`,
|
||||||
|
sql: `${CUBE}.value_number`,
|
||||||
|
description: `Average NPS score`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
dimensions: {
|
||||||
|
id: {
|
||||||
|
sql: `id`,
|
||||||
|
type: `string`,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
sentiment: {
|
||||||
|
sql: `sentiment`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Sentiment extracted from metadata JSONB field`,
|
||||||
|
},
|
||||||
|
|
||||||
|
sourceType: {
|
||||||
|
sql: `source_type`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Source type of the feedback (e.g., nps_campaign, survey)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
sourceName: {
|
||||||
|
sql: `source_name`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Human-readable name of the source`,
|
||||||
|
},
|
||||||
|
|
||||||
|
fieldType: {
|
||||||
|
sql: `field_type`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Type of feedback field (e.g., nps, text, rating)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
collectedAt: {
|
||||||
|
sql: `collected_at`,
|
||||||
|
type: `time`,
|
||||||
|
description: `Timestamp when the feedback was collected`,
|
||||||
|
},
|
||||||
|
|
||||||
|
npsValue: {
|
||||||
|
sql: `value_number`,
|
||||||
|
type: `number`,
|
||||||
|
description: `Raw NPS score value (0-10)`,
|
||||||
|
},
|
||||||
|
|
||||||
|
responseId: {
|
||||||
|
sql: `response_id`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Unique identifier linking related feedback records`,
|
||||||
|
},
|
||||||
|
|
||||||
|
userIdentifier: {
|
||||||
|
sql: `user_identifier`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Identifier of the user who provided feedback`,
|
||||||
|
},
|
||||||
|
|
||||||
|
emotion: {
|
||||||
|
sql: `emotion`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Emotion extracted from metadata JSONB field`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
joins: {
|
||||||
|
TopicsUnnested: {
|
||||||
|
sql: `${CUBE}.id = ${TopicsUnnested}.feedback_record_id`,
|
||||||
|
relationship: `hasMany`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
cube(`TopicsUnnested`, {
|
||||||
|
sql: `
|
||||||
|
SELECT
|
||||||
|
fr.id as feedback_record_id,
|
||||||
|
topic_elem.topic
|
||||||
|
FROM feedback_records fr
|
||||||
|
CROSS JOIN LATERAL jsonb_array_elements_text(COALESCE(fr.metadata->'topics', '[]'::jsonb)) AS topic_elem(topic)
|
||||||
|
`,
|
||||||
|
|
||||||
|
measures: {
|
||||||
|
count: {
|
||||||
|
type: `count`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
dimensions: {
|
||||||
|
id: {
|
||||||
|
sql: `feedback_record_id || '-' || topic`,
|
||||||
|
type: `string`,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
feedbackRecordId: {
|
||||||
|
sql: `feedback_record_id`,
|
||||||
|
type: `string`,
|
||||||
|
},
|
||||||
|
|
||||||
|
topic: {
|
||||||
|
sql: `topic`,
|
||||||
|
type: `string`,
|
||||||
|
description: `Individual topic from the topics array`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import OpenAI from "openai";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { executeQuery } from "../_lib/cube-client";
|
||||||
|
import { CUBE_NAME, generateSchemaContext } from "../_lib/schema-parser";
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
measures: z.array(z.string()).describe("List of measures to query"),
|
||||||
|
dimensions: z.array(z.string()).nullable().describe("List of dimensions to query"),
|
||||||
|
timeDimensions: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
dimension: z.string(),
|
||||||
|
granularity: z.enum(["day", "week", "month", "year"]).nullable(),
|
||||||
|
dateRange: z.string().nullable(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.nullable()
|
||||||
|
.describe("Time dimensions with granularity and date range"),
|
||||||
|
chartType: z
|
||||||
|
.enum(["bar", "line", "donut", "kpi", "area", "pie"])
|
||||||
|
.describe("Suggested chart type for visualization"),
|
||||||
|
filters: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
member: z.string(),
|
||||||
|
operator: z.enum([
|
||||||
|
"equals",
|
||||||
|
"notEquals",
|
||||||
|
"contains",
|
||||||
|
"notContains",
|
||||||
|
"set",
|
||||||
|
"notSet",
|
||||||
|
"gt",
|
||||||
|
"gte",
|
||||||
|
"lt",
|
||||||
|
"lte",
|
||||||
|
]),
|
||||||
|
values: z.array(z.string()).nullable(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.nullable()
|
||||||
|
.describe("Filters to apply to the query"),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Generate schema context dynamically from the schema file
|
||||||
|
const SCHEMA_CONTEXT = generateSchemaContext();
|
||||||
|
|
||||||
|
// JSON Schema for OpenAI structured outputs (manually created to avoid zod-to-json-schema dependency)
|
||||||
|
const jsonSchema = {
|
||||||
|
type: "object",
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
measures: {
|
||||||
|
type: "array",
|
||||||
|
items: { type: "string" },
|
||||||
|
description: "List of measures to query",
|
||||||
|
},
|
||||||
|
dimensions: {
|
||||||
|
anyOf: [{ type: "array", items: { type: "string" } }, { type: "null" }],
|
||||||
|
description: "List of dimensions to query",
|
||||||
|
},
|
||||||
|
timeDimensions: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "object",
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
dimension: { type: "string" },
|
||||||
|
granularity: {
|
||||||
|
anyOf: [{ type: "string", enum: ["day", "week", "month", "year"] }, { type: "null" }],
|
||||||
|
},
|
||||||
|
dateRange: {
|
||||||
|
anyOf: [{ type: "string" }, { type: "null" }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["dimension", "granularity", "dateRange"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ type: "null" },
|
||||||
|
],
|
||||||
|
description: "Time dimensions with granularity and date range",
|
||||||
|
},
|
||||||
|
chartType: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["bar", "line", "donut", "kpi", "area", "pie"],
|
||||||
|
description: "Suggested chart type for visualization",
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: "array",
|
||||||
|
items: {
|
||||||
|
type: "object",
|
||||||
|
additionalProperties: false,
|
||||||
|
properties: {
|
||||||
|
member: { type: "string" },
|
||||||
|
operator: {
|
||||||
|
type: "string",
|
||||||
|
enum: [
|
||||||
|
"equals",
|
||||||
|
"notEquals",
|
||||||
|
"contains",
|
||||||
|
"notContains",
|
||||||
|
"set",
|
||||||
|
"notSet",
|
||||||
|
"gt",
|
||||||
|
"gte",
|
||||||
|
"lt",
|
||||||
|
"lte",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
values: {
|
||||||
|
anyOf: [{ type: "array", items: { type: "string" } }, { type: "null" }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["member", "operator", "values"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ type: "null" },
|
||||||
|
],
|
||||||
|
description: "Filters to apply to the query",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["measures", "dimensions", "timeDimensions", "chartType", "filters"],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// Initialize OpenAI client
|
||||||
|
const getOpenAIClient = () => {
|
||||||
|
if (!process.env.OPENAI_API_KEY) {
|
||||||
|
throw new Error("OPENAI_API_KEY is not configured");
|
||||||
|
}
|
||||||
|
return new OpenAI({
|
||||||
|
apiKey: process.env.OPENAI_API_KEY,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const { prompt, executeQuery: shouldExecuteQuery = true } = await request.json();
|
||||||
|
|
||||||
|
if (!prompt || typeof prompt !== "string") {
|
||||||
|
return NextResponse.json({ error: "Prompt is required and must be a string" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const openai = getOpenAIClient();
|
||||||
|
|
||||||
|
// Generate Cube.js query using OpenAI structured outputs
|
||||||
|
const completion = await openai.chat.completions.create({
|
||||||
|
model: "gpt-4o-mini",
|
||||||
|
messages: [
|
||||||
|
{ role: "system", content: SCHEMA_CONTEXT },
|
||||||
|
{ role: "user", content: `User request: "${prompt}"` },
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "generate_cube_query",
|
||||||
|
description: "Generate a Cube.js query based on the user request",
|
||||||
|
parameters: jsonSchema,
|
||||||
|
strict: true, // Enable structured outputs
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tool_choice: { type: "function", function: { name: "generate_cube_query" } },
|
||||||
|
});
|
||||||
|
|
||||||
|
const toolCall = completion.choices[0]?.message?.tool_calls?.[0];
|
||||||
|
if (toolCall?.function.name !== "generate_cube_query") {
|
||||||
|
throw new Error("Failed to generate structured output from OpenAI");
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = JSON.parse(toolCall.function.arguments);
|
||||||
|
|
||||||
|
// Validate with zod schema (for type safety)
|
||||||
|
const validatedQuery = schema.parse(query);
|
||||||
|
|
||||||
|
// Validate required fields (measures should minimally be present if not specified, default to count)
|
||||||
|
if (!validatedQuery.measures || validatedQuery.measures.length === 0) {
|
||||||
|
validatedQuery.measures = [`${CUBE_NAME}.count`];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract chartType (for UI purposes only, not part of CubeJS query)
|
||||||
|
const { chartType, ...cubeQuery } = validatedQuery;
|
||||||
|
|
||||||
|
// Build a clean query object, stripping null / empty arrays so Cube.js is happy
|
||||||
|
const cleanQuery: Record<string, unknown> = {
|
||||||
|
measures: cubeQuery.measures,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Array.isArray(cubeQuery.dimensions) && cubeQuery.dimensions.length > 0) {
|
||||||
|
cleanQuery.dimensions = cubeQuery.dimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(cubeQuery.filters) && cubeQuery.filters.length > 0) {
|
||||||
|
cleanQuery.filters = cubeQuery.filters.map(
|
||||||
|
(f: { member: string; operator: string; values?: string[] | null }) => {
|
||||||
|
const cleaned: Record<string, unknown> = { member: f.member, operator: f.operator };
|
||||||
|
if (f.values !== null && f.values !== undefined) cleaned.values = f.values;
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(cubeQuery.timeDimensions) && cubeQuery.timeDimensions.length > 0) {
|
||||||
|
cleanQuery.timeDimensions = cubeQuery.timeDimensions.map(
|
||||||
|
(td: { dimension: string; granularity?: string | null; dateRange?: string | null }) => {
|
||||||
|
const cleaned: Record<string, unknown> = { dimension: td.dimension };
|
||||||
|
if (td.granularity !== null && td.granularity !== undefined) cleaned.granularity = td.granularity;
|
||||||
|
if (td.dateRange !== null && td.dateRange !== undefined) cleaned.dateRange = td.dateRange;
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute query if requested (default: true)
|
||||||
|
let data: Record<string, unknown>[] | undefined;
|
||||||
|
if (shouldExecuteQuery) {
|
||||||
|
try {
|
||||||
|
console.log("[analytics/query] Executing Cube.js query:", JSON.stringify(cleanQuery, null, 2));
|
||||||
|
data = await executeQuery(cleanQuery);
|
||||||
|
console.log(`[analytics/query] Query returned ${data?.length ?? 0} row(s)`);
|
||||||
|
} catch (queryError: unknown) {
|
||||||
|
const message = queryError instanceof Error ? queryError.message : "Unknown error";
|
||||||
|
console.error("[analytics/query] Query execution failed:", {
|
||||||
|
error: message,
|
||||||
|
stack: queryError instanceof Error ? queryError.stack : undefined,
|
||||||
|
query: cleanQuery,
|
||||||
|
cubeUrl: process.env.CUBEJS_API_URL || "http://localhost:4000 (default)",
|
||||||
|
});
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
query: cleanQuery,
|
||||||
|
chartType,
|
||||||
|
error: `Failed to execute query: ${message}`,
|
||||||
|
},
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
query: cleanQuery,
|
||||||
|
chartType,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const message = error instanceof Error ? error.message : "Failed to generate query";
|
||||||
|
return NextResponse.json({ error: message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { google } from "googleapis";
|
import { google } from "googleapis";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { TIntegrationGoogleSheetsConfig } from "@formbricks/types/integration/google-sheet";
|
|
||||||
import { responses } from "@/app/lib/api/response";
|
import { responses } from "@/app/lib/api/response";
|
||||||
import {
|
import {
|
||||||
GOOGLE_SHEETS_CLIENT_ID,
|
GOOGLE_SHEETS_CLIENT_ID,
|
||||||
@@ -9,7 +8,7 @@ import {
|
|||||||
WEBAPP_URL,
|
WEBAPP_URL,
|
||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
||||||
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
import { createOrUpdateIntegration } from "@/lib/integration/service";
|
||||||
import { authOptions } from "@/modules/auth/lib/authOptions";
|
import { authOptions } from "@/modules/auth/lib/authOptions";
|
||||||
|
|
||||||
export const GET = async (req: Request) => {
|
export const GET = async (req: Request) => {
|
||||||
@@ -43,39 +42,33 @@ export const GET = async (req: Request) => {
|
|||||||
if (!redirect_uri) return responses.internalServerErrorResponse("Google redirect url is missing");
|
if (!redirect_uri) return responses.internalServerErrorResponse("Google redirect url is missing");
|
||||||
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
|
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
|
||||||
|
|
||||||
if (!code) {
|
let key;
|
||||||
return Response.redirect(
|
let userEmail;
|
||||||
`${WEBAPP_URL}/environments/${environmentId}/workspace/integrations/google-sheets`
|
|
||||||
);
|
if (code) {
|
||||||
|
const token = await oAuth2Client.getToken(code);
|
||||||
|
key = token.res?.data;
|
||||||
|
|
||||||
|
// Set credentials using the provided token
|
||||||
|
oAuth2Client.setCredentials({
|
||||||
|
access_token: key.access_token,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch user's email
|
||||||
|
const oauth2 = google.oauth2({
|
||||||
|
auth: oAuth2Client,
|
||||||
|
version: "v2",
|
||||||
|
});
|
||||||
|
const userInfo = await oauth2.userinfo.get();
|
||||||
|
userEmail = userInfo.data.email;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await oAuth2Client.getToken(code);
|
|
||||||
const key = token.res?.data;
|
|
||||||
if (!key) {
|
|
||||||
return Response.redirect(
|
|
||||||
`${WEBAPP_URL}/environments/${environmentId}/workspace/integrations/google-sheets`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuth2Client.setCredentials({ access_token: key.access_token });
|
|
||||||
const oauth2 = google.oauth2({ auth: oAuth2Client, version: "v2" });
|
|
||||||
const userInfo = await oauth2.userinfo.get();
|
|
||||||
const userEmail = userInfo.data.email;
|
|
||||||
|
|
||||||
if (!userEmail) {
|
|
||||||
return responses.internalServerErrorResponse("Failed to get user email");
|
|
||||||
}
|
|
||||||
|
|
||||||
const integrationType = "googleSheets" as const;
|
|
||||||
const existingIntegration = await getIntegrationByType(environmentId, integrationType);
|
|
||||||
const existingConfig = existingIntegration?.config as TIntegrationGoogleSheetsConfig;
|
|
||||||
|
|
||||||
const googleSheetIntegration = {
|
const googleSheetIntegration = {
|
||||||
type: integrationType,
|
type: "googleSheets" as "googleSheets",
|
||||||
environment: environmentId,
|
environment: environmentId,
|
||||||
config: {
|
config: {
|
||||||
key,
|
key,
|
||||||
data: existingConfig?.data ?? [],
|
data: [],
|
||||||
email: userEmail,
|
email: userEmail,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
TJsEnvironmentStateSurvey,
|
TJsEnvironmentStateSurvey,
|
||||||
} from "@formbricks/types/js";
|
} from "@formbricks/types/js";
|
||||||
import { validateInputs } from "@/lib/utils/validate";
|
import { validateInputs } from "@/lib/utils/validate";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { transformPrismaSurvey } from "@/modules/survey/lib/utils";
|
import { transformPrismaSurvey } from "@/modules/survey/lib/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,14 +177,14 @@ export const getEnvironmentStateData = async (environmentId: string): Promise<En
|
|||||||
overlay: environmentData.project.overlay,
|
overlay: environmentData.project.overlay,
|
||||||
placement: environmentData.project.placement,
|
placement: environmentData.project.placement,
|
||||||
inAppSurveyBranding: environmentData.project.inAppSurveyBranding,
|
inAppSurveyBranding: environmentData.project.inAppSurveyBranding,
|
||||||
styling: resolveStorageUrlsInObject(environmentData.project.styling),
|
styling: environmentData.project.styling,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
organization: {
|
organization: {
|
||||||
id: environmentData.project.organization.id,
|
id: environmentData.project.organization.id,
|
||||||
billing: environmentData.project.organization.billing,
|
billing: environmentData.project.organization.billing,
|
||||||
},
|
},
|
||||||
surveys: resolveStorageUrlsInObject(transformedSurveys),
|
surveys: transformedSurveys,
|
||||||
actionClasses: environmentData.actionClasses as TJsEnvironmentStateActionClass[],
|
actionClasses: environmentData.actionClasses as TJsEnvironmentStateActionClass[],
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -44,10 +44,13 @@ const validateResponse = (
|
|||||||
...responseUpdateInput.data,
|
...responseUpdateInput.data,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isFinished = responseUpdateInput.finished ?? false;
|
||||||
|
|
||||||
const validationErrors = validateResponseData(
|
const validationErrors = validateResponseData(
|
||||||
survey.blocks,
|
survey.blocks,
|
||||||
mergedData,
|
mergedData,
|
||||||
responseUpdateInput.language ?? response.language ?? "en",
|
responseUpdateInput.language ?? response.language ?? "en",
|
||||||
|
isFinished,
|
||||||
survey.questions
|
survey.questions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ const validateResponse = (responseInputData: TResponseInput, survey: TSurvey) =>
|
|||||||
survey.blocks,
|
survey.blocks,
|
||||||
responseInputData.data,
|
responseInputData.data,
|
||||||
responseInputData.language ?? "en",
|
responseInputData.language ?? "en",
|
||||||
|
responseInputData.finished,
|
||||||
survey.questions
|
survey.questions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -6,138 +6,140 @@ export const GET = async (req: NextRequest) => {
|
|||||||
let brandColor = req.nextUrl.searchParams.get("brandColor");
|
let brandColor = req.nextUrl.searchParams.get("brandColor");
|
||||||
|
|
||||||
return new ImageResponse(
|
return new ImageResponse(
|
||||||
<div
|
(
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
backgroundColor: brandColor ? brandColor + "BF" : "#0000BFBF", // /75 opacity is approximately BF in hex
|
|
||||||
borderRadius: "0.75rem",
|
|
||||||
}}>
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
width: "80%",
|
width: "100%",
|
||||||
height: "60%",
|
height: "100%",
|
||||||
backgroundColor: "white",
|
|
||||||
borderRadius: "0.75rem",
|
|
||||||
marginTop: "3.25rem",
|
|
||||||
position: "absolute",
|
|
||||||
left: "3rem",
|
|
||||||
top: "0.75rem",
|
|
||||||
opacity: 0.2,
|
|
||||||
transform: "rotate(356deg)",
|
|
||||||
}}></div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "84%",
|
|
||||||
height: "60%",
|
|
||||||
backgroundColor: "white",
|
|
||||||
borderRadius: "0.75rem",
|
|
||||||
marginTop: "3rem",
|
|
||||||
position: "absolute",
|
|
||||||
top: "1.25rem",
|
|
||||||
left: "3.25rem",
|
|
||||||
borderWidth: "2px",
|
|
||||||
opacity: 0.6,
|
|
||||||
transform: "rotate(357deg)",
|
|
||||||
}}></div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "85%",
|
|
||||||
height: "67%",
|
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
backgroundColor: "white",
|
backgroundColor: brandColor ? brandColor + "BF" : "#0000BFBF", // /75 opacity is approximately BF in hex
|
||||||
borderRadius: "0.75rem",
|
borderRadius: "0.75rem",
|
||||||
marginTop: "2rem",
|
|
||||||
position: "absolute",
|
|
||||||
top: "2.3rem",
|
|
||||||
left: "3.5rem",
|
|
||||||
transform: "rotate(360deg)",
|
|
||||||
}}>
|
}}>
|
||||||
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
<div
|
||||||
<div
|
style={{
|
||||||
style={{
|
display: "flex",
|
||||||
display: "flex",
|
flexDirection: "column",
|
||||||
flexDirection: "column",
|
width: "80%",
|
||||||
width: "100%",
|
height: "60%",
|
||||||
justifyContent: "space-between",
|
backgroundColor: "white",
|
||||||
}}>
|
borderRadius: "0.75rem",
|
||||||
|
marginTop: "3.25rem",
|
||||||
|
position: "absolute",
|
||||||
|
left: "3rem",
|
||||||
|
top: "0.75rem",
|
||||||
|
opacity: 0.2,
|
||||||
|
transform: "rotate(356deg)",
|
||||||
|
}}></div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "84%",
|
||||||
|
height: "60%",
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: "0.75rem",
|
||||||
|
marginTop: "3rem",
|
||||||
|
position: "absolute",
|
||||||
|
top: "1.25rem",
|
||||||
|
left: "3.25rem",
|
||||||
|
borderWidth: "2px",
|
||||||
|
opacity: 0.6,
|
||||||
|
transform: "rotate(357deg)",
|
||||||
|
}}></div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
width: "85%",
|
||||||
|
height: "67%",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: "0.75rem",
|
||||||
|
marginTop: "2rem",
|
||||||
|
position: "absolute",
|
||||||
|
top: "2.3rem",
|
||||||
|
left: "3.5rem",
|
||||||
|
transform: "rotate(360deg)",
|
||||||
|
}}>
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
paddingLeft: "2rem",
|
width: "100%",
|
||||||
paddingRight: "2rem",
|
justifyContent: "space-between",
|
||||||
}}>
|
}}>
|
||||||
<h2
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
fontSize: "2rem",
|
paddingLeft: "2rem",
|
||||||
fontWeight: "700",
|
paddingRight: "2rem",
|
||||||
letterSpacing: "-0.025em",
|
|
||||||
color: "#0f172a",
|
|
||||||
textAlign: "left",
|
|
||||||
marginTop: "3.75rem",
|
|
||||||
}}>
|
}}>
|
||||||
{name}
|
<h2
|
||||||
</h2>
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
fontSize: "2rem",
|
||||||
|
fontWeight: "700",
|
||||||
|
letterSpacing: "-0.025em",
|
||||||
|
color: "#0f172a",
|
||||||
|
textAlign: "left",
|
||||||
|
marginTop: "3.75rem",
|
||||||
|
}}>
|
||||||
|
{name}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div style={{ display: "flex", justifyContent: "flex-end", marginRight: "2.5rem" }}>
|
||||||
<div style={{ display: "flex", justifyContent: "flex-end", marginRight: "2.5rem" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
borderRadius: "1rem",
|
|
||||||
position: "absolute",
|
|
||||||
right: "-0.5rem",
|
|
||||||
marginTop: "0.5rem",
|
|
||||||
}}>
|
|
||||||
<div
|
|
||||||
content=""
|
|
||||||
style={{
|
|
||||||
borderRadius: "0.75rem",
|
|
||||||
border: "1px solid transparent",
|
|
||||||
backgroundColor: brandColor ?? "#000",
|
|
||||||
height: "4.5rem",
|
|
||||||
width: "9.5rem",
|
|
||||||
opacity: 0.5,
|
|
||||||
}}></div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
borderRadius: "1rem",
|
|
||||||
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
|
|
||||||
}}>
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
borderRadius: "1rem",
|
||||||
justifyContent: "center",
|
position: "absolute",
|
||||||
borderRadius: "0.75rem",
|
right: "-0.5rem",
|
||||||
border: "1px solid transparent",
|
marginTop: "0.5rem",
|
||||||
backgroundColor: brandColor ?? "#000",
|
|
||||||
fontSize: "1.5rem",
|
|
||||||
color: "white",
|
|
||||||
height: "4.5rem",
|
|
||||||
width: "9.5rem",
|
|
||||||
}}>
|
}}>
|
||||||
Begin!
|
<div
|
||||||
|
content=""
|
||||||
|
style={{
|
||||||
|
borderRadius: "0.75rem",
|
||||||
|
border: "1px solid transparent",
|
||||||
|
backgroundColor: brandColor ?? "#000",
|
||||||
|
height: "4.5rem",
|
||||||
|
width: "9.5rem",
|
||||||
|
opacity: 0.5,
|
||||||
|
}}></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
borderRadius: "1rem",
|
||||||
|
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
|
||||||
|
}}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
borderRadius: "0.75rem",
|
||||||
|
border: "1px solid transparent",
|
||||||
|
backgroundColor: brandColor ?? "#000",
|
||||||
|
fontSize: "1.5rem",
|
||||||
|
color: "white",
|
||||||
|
height: "4.5rem",
|
||||||
|
width: "9.5rem",
|
||||||
|
}}>
|
||||||
|
Begin!
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>,
|
),
|
||||||
{
|
{
|
||||||
width: 800,
|
width: 800,
|
||||||
height: 400,
|
height: 400,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from "@formbricks/types/integration/slack";
|
} from "@formbricks/types/integration/slack";
|
||||||
import { responses } from "@/app/lib/api/response";
|
import { responses } from "@/app/lib/api/response";
|
||||||
import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||||
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_REDIRECT_URI, WEBAPP_URL } from "@/lib/constants";
|
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
|
||||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
||||||
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
||||||
|
|
||||||
@@ -56,7 +56,6 @@ export const GET = withV1ApiWrapper({
|
|||||||
code,
|
code,
|
||||||
client_id: SLACK_CLIENT_ID,
|
client_id: SLACK_CLIENT_ID,
|
||||||
client_secret: SLACK_CLIENT_SECRET,
|
client_secret: SLACK_CLIENT_SECRET,
|
||||||
redirect_uri: SLACK_REDIRECT_URI,
|
|
||||||
};
|
};
|
||||||
const formBody: string[] = [];
|
const formBody: string[] = [];
|
||||||
for (const property in formData) {
|
for (const property in formData) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { deleteResponse, getResponse } from "@/lib/response/service";
|
|||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject, validateFileUploads } from "@/modules/storage/utils";
|
import { validateFileUploads } from "@/modules/storage/utils";
|
||||||
import { updateResponseWithQuotaEvaluation } from "./lib/response";
|
import { updateResponseWithQuotaEvaluation } from "./lib/response";
|
||||||
|
|
||||||
async function fetchAndAuthorizeResponse(
|
async function fetchAndAuthorizeResponse(
|
||||||
@@ -57,10 +57,7 @@ export const GET = withV1ApiWrapper({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse({
|
response: responses.successResponse(result.response),
|
||||||
...result.response,
|
|
||||||
data: resolveStorageUrlsInObject(result.response.data),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
@@ -149,6 +146,7 @@ export const PUT = withV1ApiWrapper({
|
|||||||
result.survey.blocks,
|
result.survey.blocks,
|
||||||
responseUpdate.data,
|
responseUpdate.data,
|
||||||
responseUpdate.language ?? "en",
|
responseUpdate.language ?? "en",
|
||||||
|
responseUpdate.finished,
|
||||||
result.survey.questions
|
result.survey.questions
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -192,7 +190,7 @@ export const PUT = withV1ApiWrapper({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse({ ...updated, data: resolveStorageUrlsInObject(updated.data) }),
|
response: responses.successResponse(updated),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { sendToPipeline } from "@/app/lib/pipelines";
|
|||||||
import { getSurvey } from "@/lib/survey/service";
|
import { getSurvey } from "@/lib/survey/service";
|
||||||
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
import { formatValidationErrorsForV1Api, validateResponseData } from "@/modules/api/lib/validation";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject, validateFileUploads } from "@/modules/storage/utils";
|
import { validateFileUploads } from "@/modules/storage/utils";
|
||||||
import {
|
import {
|
||||||
createResponseWithQuotaEvaluation,
|
createResponseWithQuotaEvaluation,
|
||||||
getResponses,
|
getResponses,
|
||||||
@@ -54,9 +54,7 @@ export const GET = withV1ApiWrapper({
|
|||||||
allResponses.push(...environmentResponses);
|
allResponses.push(...environmentResponses);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(
|
response: responses.successResponse(allResponses),
|
||||||
allResponses.map((r) => ({ ...r, data: resolveStorageUrlsInObject(r.data) }))
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof DatabaseError) {
|
if (error instanceof DatabaseError) {
|
||||||
@@ -157,6 +155,7 @@ export const POST = withV1ApiWrapper({
|
|||||||
surveyResult.survey.blocks,
|
surveyResult.survey.blocks,
|
||||||
responseInput.data,
|
responseInput.data,
|
||||||
responseInput.language ?? "en",
|
responseInput.language ?? "en",
|
||||||
|
responseInput.finished,
|
||||||
surveyResult.survey.questions
|
surveyResult.survey.questions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib
|
|||||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||||
import { getSurvey, updateSurvey } from "@/lib/survey/service";
|
import { getSurvey, updateSurvey } from "@/lib/survey/service";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
|
|
||||||
const fetchAndAuthorizeSurvey = async (
|
const fetchAndAuthorizeSurvey = async (
|
||||||
surveyId: string,
|
surveyId: string,
|
||||||
@@ -59,18 +58,16 @@ export const GET = withV1ApiWrapper({
|
|||||||
|
|
||||||
if (shouldTransformToQuestions) {
|
if (shouldTransformToQuestions) {
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(
|
response: responses.successResponse({
|
||||||
resolveStorageUrlsInObject({
|
...result.survey,
|
||||||
...result.survey,
|
questions: transformBlocksToQuestions(result.survey.blocks, result.survey.endings),
|
||||||
questions: transformBlocksToQuestions(result.survey.blocks, result.survey.endings),
|
blocks: [],
|
||||||
blocks: [],
|
}),
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(result.survey)),
|
response: responses.successResponse(result.survey),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
@@ -205,12 +202,12 @@ export const PUT = withV1ApiWrapper({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(surveyWithQuestions)),
|
response: responses.successResponse(surveyWithQuestions),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(updatedSurvey)),
|
response: responses.successResponse(updatedSurvey),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { TApiAuditLog, TApiKeyAuthentication, withV1ApiWrapper } from "@/app/lib
|
|||||||
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
import { getOrganizationByEnvironmentId } from "@/lib/organization/service";
|
||||||
import { createSurvey } from "@/lib/survey/service";
|
import { createSurvey } from "@/lib/survey/service";
|
||||||
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
import { hasPermission } from "@/modules/organization/settings/api-keys/lib/utils";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { getSurveys } from "./lib/surveys";
|
import { getSurveys } from "./lib/surveys";
|
||||||
|
|
||||||
export const GET = withV1ApiWrapper({
|
export const GET = withV1ApiWrapper({
|
||||||
@@ -56,7 +55,7 @@ export const GET = withV1ApiWrapper({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
response: responses.successResponse(resolveStorageUrlsInObject(surveysWithQuestions)),
|
response: responses.successResponse(surveysWithQuestions),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof DatabaseError) {
|
if (error instanceof DatabaseError) {
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ export const POST = async (request: Request, context: Context): Promise<Response
|
|||||||
survey.blocks,
|
survey.blocks,
|
||||||
responseInputData.data,
|
responseInputData.data,
|
||||||
responseInputData.language ?? "en",
|
responseInputData.language ?? "en",
|
||||||
|
responseInputData.finished,
|
||||||
survey.questions
|
survey.questions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -131,11 +131,13 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("logs and audits on error response with API key authentication", async () => {
|
test("logs and audits on error response with API key authentication", async () => {
|
||||||
const { queueAuditEvent: mockedQueueAuditEvent } =
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock };
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
)) as unknown as { queueAuditEvent: Mock };
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
||||||
@@ -183,11 +185,13 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("does not log Sentry if not 500", async () => {
|
test("does not log Sentry if not 500", async () => {
|
||||||
const { queueAuditEvent: mockedQueueAuditEvent } =
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock };
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
)) as unknown as { queueAuditEvent: Mock };
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
||||||
@@ -229,11 +233,13 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("logs and audits on thrown error", async () => {
|
test("logs and audits on thrown error", async () => {
|
||||||
const { queueAuditEvent: mockedQueueAuditEvent } =
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock };
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
)) as unknown as { queueAuditEvent: Mock };
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
||||||
@@ -285,11 +291,13 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("does not log on success response but still audits", async () => {
|
test("does not log on success response but still audits", async () => {
|
||||||
const { queueAuditEvent: mockedQueueAuditEvent } =
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock };
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
)) as unknown as { queueAuditEvent: Mock };
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
||||||
@@ -339,11 +347,13 @@ describe("withV1ApiWrapper", () => {
|
|||||||
REDIS_URL: "redis://localhost:6379",
|
REDIS_URL: "redis://localhost:6379",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { queueAuditEvent: mockedQueueAuditEvent } =
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock };
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
)) as unknown as { queueAuditEvent: Mock };
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
const { withV1ApiWrapper } = await import("./with-api-logging");
|
const { withV1ApiWrapper } = await import("./with-api-logging");
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
@@ -366,8 +376,9 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("handles client-side API routes without authentication", async () => {
|
test("handles client-side API routes without authentication", async () => {
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { applyIPRateLimit } = await import("@/modules/core/rate-limit/helpers");
|
const { applyIPRateLimit } = await import("@/modules/core/rate-limit/helpers");
|
||||||
|
|
||||||
@@ -399,8 +410,9 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("returns authentication error for non-client routes without auth", async () => {
|
test("returns authentication error for non-client routes without auth", async () => {
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
|
|
||||||
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
||||||
@@ -423,8 +435,9 @@ describe("withV1ApiWrapper", () => {
|
|||||||
|
|
||||||
test("handles rate limiting errors", async () => {
|
test("handles rate limiting errors", async () => {
|
||||||
const { applyRateLimit } = await import("@/modules/core/rate-limit/helpers");
|
const { applyRateLimit } = await import("@/modules/core/rate-limit/helpers");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
@@ -449,11 +462,13 @@ describe("withV1ApiWrapper", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("skips audit log creation when no action/targetType provided", async () => {
|
test("skips audit log creation when no action/targetType provided", async () => {
|
||||||
const { queueAuditEvent: mockedQueueAuditEvent } =
|
const { queueAuditEvent: mockedQueueAuditEvent } = (await import(
|
||||||
(await import("@/modules/ee/audit-logs/lib/handler")) as unknown as { queueAuditEvent: Mock };
|
"@/modules/ee/audit-logs/lib/handler"
|
||||||
|
)) as unknown as { queueAuditEvent: Mock };
|
||||||
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
const { authenticateRequest } = await import("@/app/api/v1/auth");
|
||||||
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } =
|
const { isClientSideApiRoute, isManagementApiRoute, isIntegrationRoute } = await import(
|
||||||
await import("@/app/middleware/endpoint-validator");
|
"@/app/middleware/endpoint-validator"
|
||||||
|
);
|
||||||
|
|
||||||
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
vi.mocked(authenticateRequest).mockResolvedValue(mockApiAuthentication);
|
||||||
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
vi.mocked(isClientSideApiRoute).mockReturnValue({ isClientSideApi: false, isRateLimited: true });
|
||||||
|
|||||||
@@ -4854,17 +4854,6 @@ export const previewSurvey = (projectName: string, t: TFunction): TSurvey => {
|
|||||||
}),
|
}),
|
||||||
isDraft: true,
|
isDraft: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
...buildOpenTextElement({
|
|
||||||
id: "preview-open-text-01",
|
|
||||||
headline: t("templates.preview_survey_question_open_text_headline"),
|
|
||||||
subheader: t("templates.preview_survey_question_open_text_subheader"),
|
|
||||||
placeholder: t("templates.preview_survey_question_open_text_placeholder"),
|
|
||||||
inputType: "text",
|
|
||||||
required: false,
|
|
||||||
}),
|
|
||||||
isDraft: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
buttonLabel: createI18nString(t("templates.next"), []),
|
buttonLabel: createI18nString(t("templates.next"), []),
|
||||||
backButtonLabel: createI18nString(t("templates.preview_survey_question_2_back_button_label"), []),
|
backButtonLabel: createI18nString(t("templates.preview_survey_question_2_back_button_label"), []),
|
||||||
|
|||||||
@@ -257,7 +257,6 @@ describe("endpoint-validator", () => {
|
|||||||
expect(isAuthProtectedRoute("/api/v1/client/test")).toBe(false);
|
expect(isAuthProtectedRoute("/api/v1/client/test")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("/")).toBe(false);
|
expect(isAuthProtectedRoute("/")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("/s/survey123")).toBe(false);
|
expect(isAuthProtectedRoute("/s/survey123")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("/p/pretty-url")).toBe(false);
|
|
||||||
expect(isAuthProtectedRoute("/c/jwt-token")).toBe(false);
|
expect(isAuthProtectedRoute("/c/jwt-token")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("/health")).toBe(false);
|
expect(isAuthProtectedRoute("/health")).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -314,19 +313,6 @@ describe("endpoint-validator", () => {
|
|||||||
expect(isPublicDomainRoute("/contact/token")).toBe(false);
|
expect(isPublicDomainRoute("/contact/token")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should return true for pretty URL survey routes", () => {
|
|
||||||
expect(isPublicDomainRoute("/p/pretty123")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/p/pretty-name-with-dashes")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/p/survey_id_with_underscores")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/p/abc123def456")).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should return false for malformed pretty URL survey routes", () => {
|
|
||||||
expect(isPublicDomainRoute("/p/")).toBe(false);
|
|
||||||
expect(isPublicDomainRoute("/p")).toBe(false);
|
|
||||||
expect(isPublicDomainRoute("/pretty/123")).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("should return true for client API routes", () => {
|
test("should return true for client API routes", () => {
|
||||||
expect(isPublicDomainRoute("/api/v1/client/something")).toBe(true);
|
expect(isPublicDomainRoute("/api/v1/client/something")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/api/v2/client/other")).toBe(true);
|
expect(isPublicDomainRoute("/api/v2/client/other")).toBe(true);
|
||||||
@@ -389,8 +375,6 @@ describe("endpoint-validator", () => {
|
|||||||
expect(isAdminDomainRoute("/s/survey-id-with-dashes")).toBe(false);
|
expect(isAdminDomainRoute("/s/survey-id-with-dashes")).toBe(false);
|
||||||
expect(isAdminDomainRoute("/c/jwt-token")).toBe(false);
|
expect(isAdminDomainRoute("/c/jwt-token")).toBe(false);
|
||||||
expect(isAdminDomainRoute("/c/very-long-jwt-token-123")).toBe(false);
|
expect(isAdminDomainRoute("/c/very-long-jwt-token-123")).toBe(false);
|
||||||
expect(isAdminDomainRoute("/p/pretty123")).toBe(false);
|
|
||||||
expect(isAdminDomainRoute("/p/pretty-name-with-dashes")).toBe(false);
|
|
||||||
expect(isAdminDomainRoute("/api/v1/client/test")).toBe(false);
|
expect(isAdminDomainRoute("/api/v1/client/test")).toBe(false);
|
||||||
expect(isAdminDomainRoute("/api/v2/client/other")).toBe(false);
|
expect(isAdminDomainRoute("/api/v2/client/other")).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -406,7 +390,6 @@ describe("endpoint-validator", () => {
|
|||||||
test("should allow public routes on public domain", () => {
|
test("should allow public routes on public domain", () => {
|
||||||
expect(isRouteAllowedForDomain("/s/survey123", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/s/survey123", true)).toBe(true);
|
||||||
expect(isRouteAllowedForDomain("/c/jwt-token", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/c/jwt-token", true)).toBe(true);
|
||||||
expect(isRouteAllowedForDomain("/p/pretty123", true)).toBe(true);
|
|
||||||
expect(isRouteAllowedForDomain("/api/v1/client/test", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/api/v1/client/test", true)).toBe(true);
|
||||||
expect(isRouteAllowedForDomain("/api/v2/client/other", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/api/v2/client/other", true)).toBe(true);
|
||||||
expect(isRouteAllowedForDomain("/health", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/health", true)).toBe(true);
|
||||||
@@ -443,8 +426,6 @@ describe("endpoint-validator", () => {
|
|||||||
expect(isRouteAllowedForDomain("/s/survey-id-with-dashes", false)).toBe(false);
|
expect(isRouteAllowedForDomain("/s/survey-id-with-dashes", false)).toBe(false);
|
||||||
expect(isRouteAllowedForDomain("/c/jwt-token", false)).toBe(false);
|
expect(isRouteAllowedForDomain("/c/jwt-token", false)).toBe(false);
|
||||||
expect(isRouteAllowedForDomain("/c/very-long-jwt-token-123", false)).toBe(false);
|
expect(isRouteAllowedForDomain("/c/very-long-jwt-token-123", false)).toBe(false);
|
||||||
expect(isRouteAllowedForDomain("/p/pretty123", false)).toBe(false);
|
|
||||||
expect(isRouteAllowedForDomain("/p/pretty-name-with-dashes", false)).toBe(false);
|
|
||||||
expect(isRouteAllowedForDomain("/api/v1/client/test", false)).toBe(false);
|
expect(isRouteAllowedForDomain("/api/v1/client/test", false)).toBe(false);
|
||||||
expect(isRouteAllowedForDomain("/api/v2/client/other", false)).toBe(false);
|
expect(isRouteAllowedForDomain("/api/v2/client/other", false)).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -459,8 +440,6 @@ describe("endpoint-validator", () => {
|
|||||||
test("should handle paths with query parameters and fragments", () => {
|
test("should handle paths with query parameters and fragments", () => {
|
||||||
expect(isRouteAllowedForDomain("/s/survey123?param=value", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/s/survey123?param=value", true)).toBe(true);
|
||||||
expect(isRouteAllowedForDomain("/s/survey123#section", true)).toBe(true);
|
expect(isRouteAllowedForDomain("/s/survey123#section", true)).toBe(true);
|
||||||
expect(isRouteAllowedForDomain("/p/pretty123?param=value", true)).toBe(true);
|
|
||||||
expect(isRouteAllowedForDomain("/p/pretty123#section", true)).toBe(true);
|
|
||||||
expect(isRouteAllowedForDomain("/environments/123?tab=settings", true)).toBe(false);
|
expect(isRouteAllowedForDomain("/environments/123?tab=settings", true)).toBe(false);
|
||||||
expect(isRouteAllowedForDomain("/environments/123?tab=settings", false)).toBe(true);
|
expect(isRouteAllowedForDomain("/environments/123?tab=settings", false)).toBe(true);
|
||||||
});
|
});
|
||||||
@@ -471,7 +450,6 @@ describe("endpoint-validator", () => {
|
|||||||
describe("URL parsing edge cases", () => {
|
describe("URL parsing edge cases", () => {
|
||||||
test("should handle paths with query parameters", () => {
|
test("should handle paths with query parameters", () => {
|
||||||
expect(isPublicDomainRoute("/s/survey123?param=value&other=test")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey123?param=value&other=test")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/pretty123?param=value&other=test")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/api/v1/client/test?query=data")).toBe(true);
|
expect(isPublicDomainRoute("/api/v1/client/test?query=data")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/environments/123?tab=settings")).toBe(false);
|
expect(isPublicDomainRoute("/environments/123?tab=settings")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("/environments/123?tab=overview")).toBe(true);
|
expect(isAuthProtectedRoute("/environments/123?tab=overview")).toBe(true);
|
||||||
@@ -480,14 +458,12 @@ describe("endpoint-validator", () => {
|
|||||||
test("should handle paths with fragments", () => {
|
test("should handle paths with fragments", () => {
|
||||||
expect(isPublicDomainRoute("/s/survey123#section")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey123#section")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/c/jwt-token#top")).toBe(true);
|
expect(isPublicDomainRoute("/c/jwt-token#top")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/pretty123#section")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/environments/123#overview")).toBe(false);
|
expect(isPublicDomainRoute("/environments/123#overview")).toBe(false);
|
||||||
expect(isAuthProtectedRoute("/organizations/456#settings")).toBe(true);
|
expect(isAuthProtectedRoute("/organizations/456#settings")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should handle trailing slashes", () => {
|
test("should handle trailing slashes", () => {
|
||||||
expect(isPublicDomainRoute("/s/survey123/")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey123/")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/pretty123/")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/api/v1/client/test/")).toBe(true);
|
expect(isPublicDomainRoute("/api/v1/client/test/")).toBe(true);
|
||||||
expect(isManagementApiRoute("/api/v1/management/test/")).toEqual({
|
expect(isManagementApiRoute("/api/v1/management/test/")).toEqual({
|
||||||
isManagementApi: true,
|
isManagementApi: true,
|
||||||
@@ -502,9 +478,6 @@ describe("endpoint-validator", () => {
|
|||||||
expect(isPublicDomainRoute("/s/survey123/preview")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey123/preview")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/s/survey123/embed")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey123/embed")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/s/survey123/thank-you")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey123/thank-you")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/pretty123/preview")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/p/pretty123/embed")).toBe(true);
|
|
||||||
expect(isPublicDomainRoute("/p/pretty123/thank-you")).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("should handle nested client API routes", () => {
|
test("should handle nested client API routes", () => {
|
||||||
@@ -556,7 +529,6 @@ describe("endpoint-validator", () => {
|
|||||||
test("should handle special characters in survey IDs", () => {
|
test("should handle special characters in survey IDs", () => {
|
||||||
expect(isPublicDomainRoute("/s/survey-123_test.v2")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey-123_test.v2")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/c/jwt.token.with.dots")).toBe(true);
|
expect(isPublicDomainRoute("/c/jwt.token.with.dots")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/pretty-123_test.v2")).toBe(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -564,7 +536,6 @@ describe("endpoint-validator", () => {
|
|||||||
test("should properly validate malicious or injection-like URLs", () => {
|
test("should properly validate malicious or injection-like URLs", () => {
|
||||||
// SQL injection-like attempts
|
// SQL injection-like attempts
|
||||||
expect(isPublicDomainRoute("/s/'; DROP TABLE users; --")).toBe(true); // Still valid survey ID format
|
expect(isPublicDomainRoute("/s/'; DROP TABLE users; --")).toBe(true); // Still valid survey ID format
|
||||||
expect(isPublicDomainRoute("/p/'; DROP TABLE users; --")).toBe(true);
|
|
||||||
expect(isManagementApiRoute("/api/v1/management/'; DROP TABLE users; --")).toEqual({
|
expect(isManagementApiRoute("/api/v1/management/'; DROP TABLE users; --")).toEqual({
|
||||||
isManagementApi: true,
|
isManagementApi: true,
|
||||||
authenticationMethod: AuthenticationMethod.ApiKey,
|
authenticationMethod: AuthenticationMethod.ApiKey,
|
||||||
@@ -572,12 +543,10 @@ describe("endpoint-validator", () => {
|
|||||||
|
|
||||||
// Path traversal attempts
|
// Path traversal attempts
|
||||||
expect(isPublicDomainRoute("/s/../../../etc/passwd")).toBe(true); // Still matches pattern
|
expect(isPublicDomainRoute("/s/../../../etc/passwd")).toBe(true); // Still matches pattern
|
||||||
expect(isPublicDomainRoute("/p/../../../etc/passwd")).toBe(true);
|
|
||||||
expect(isAuthProtectedRoute("/environments/../../../etc/passwd")).toBe(true);
|
expect(isAuthProtectedRoute("/environments/../../../etc/passwd")).toBe(true);
|
||||||
|
|
||||||
// XSS-like attempts
|
// XSS-like attempts
|
||||||
expect(isPublicDomainRoute("/s/<script>alert('xss')</script>")).toBe(true);
|
expect(isPublicDomainRoute("/s/<script>alert('xss')</script>")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/<script>alert('xss')</script>")).toBe(true);
|
|
||||||
expect(isClientSideApiRoute("/api/v1/client/<script>alert('xss')</script>")).toEqual({
|
expect(isClientSideApiRoute("/api/v1/client/<script>alert('xss')</script>")).toEqual({
|
||||||
isClientSideApi: true,
|
isClientSideApi: true,
|
||||||
isRateLimited: true,
|
isRateLimited: true,
|
||||||
@@ -587,7 +556,6 @@ describe("endpoint-validator", () => {
|
|||||||
test("should handle URL encoding", () => {
|
test("should handle URL encoding", () => {
|
||||||
expect(isPublicDomainRoute("/s/survey%20123")).toBe(true);
|
expect(isPublicDomainRoute("/s/survey%20123")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/c/jwt%2Etoken")).toBe(true);
|
expect(isPublicDomainRoute("/c/jwt%2Etoken")).toBe(true);
|
||||||
expect(isPublicDomainRoute("/p/pretty%20123")).toBe(true);
|
|
||||||
expect(isAuthProtectedRoute("/environments%2F123")).toBe(true);
|
expect(isAuthProtectedRoute("/environments%2F123")).toBe(true);
|
||||||
expect(isManagementApiRoute("/api/v1/management/test%20route")).toEqual({
|
expect(isManagementApiRoute("/api/v1/management/test%20route")).toEqual({
|
||||||
isManagementApi: true,
|
isManagementApi: true,
|
||||||
@@ -623,7 +591,6 @@ describe("endpoint-validator", () => {
|
|||||||
// These should not match due to case sensitivity
|
// These should not match due to case sensitivity
|
||||||
expect(isPublicDomainRoute("/S/survey123")).toBe(false);
|
expect(isPublicDomainRoute("/S/survey123")).toBe(false);
|
||||||
expect(isPublicDomainRoute("/C/jwt-token")).toBe(false);
|
expect(isPublicDomainRoute("/C/jwt-token")).toBe(false);
|
||||||
expect(isPublicDomainRoute("/P/pretty123")).toBe(false);
|
|
||||||
expect(isClientSideApiRoute("/API/V1/CLIENT/test")).toEqual({
|
expect(isClientSideApiRoute("/API/V1/CLIENT/test")).toEqual({
|
||||||
isClientSideApi: false,
|
isClientSideApi: false,
|
||||||
isRateLimited: true,
|
isRateLimited: true,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const PUBLIC_ROUTES = {
|
|||||||
SURVEY_ROUTES: [
|
SURVEY_ROUTES: [
|
||||||
/^\/s\/[^/]+/, // /s/[surveyId] - survey pages
|
/^\/s\/[^/]+/, // /s/[surveyId] - survey pages
|
||||||
/^\/c\/[^/]+/, // /c/[jwt] - contact survey pages
|
/^\/c\/[^/]+/, // /c/[jwt] - contact survey pages
|
||||||
/^\/p\/[^/]+/, // /p/[prettyUrl] - pretty URL pages
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// API routes accessible from public domain
|
// API routes accessible from public domain
|
||||||
|
|||||||
+8
-33
@@ -106,6 +106,7 @@ checksums:
|
|||||||
common/allow: 3e39cc5940255e6bff0fea95c817dd43
|
common/allow: 3e39cc5940255e6bff0fea95c817dd43
|
||||||
common/allow_users_to_exit_by_clicking_outside_the_survey: 1c09db6e85214f1b1c3d4774c4c5cd56
|
common/allow_users_to_exit_by_clicking_outside_the_survey: 1c09db6e85214f1b1c3d4774c4c5cd56
|
||||||
common/an_unknown_error_occurred_while_deleting_table_items: 06be3fd128aeb51eed4fba9a079ecee2
|
common/an_unknown_error_occurred_while_deleting_table_items: 06be3fd128aeb51eed4fba9a079ecee2
|
||||||
|
common/analysis: 409bac6215382c47e59f5039cc4cdcdd
|
||||||
common/and: dc75b95c804b16dc617a5f16f7393bca
|
common/and: dc75b95c804b16dc617a5f16f7393bca
|
||||||
common/and_response_limit_of: 05be41a1d7e8dafa4aa012dcba77f5d4
|
common/and_response_limit_of: 05be41a1d7e8dafa4aa012dcba77f5d4
|
||||||
common/anonymous: 77b5222e710cc1dae073dae32309f8ed
|
common/anonymous: 77b5222e710cc1dae073dae32309f8ed
|
||||||
@@ -150,9 +151,7 @@ checksums:
|
|||||||
common/copy_link: 57a37acfe6d7ed71d00fbbc8079fbb35
|
common/copy_link: 57a37acfe6d7ed71d00fbbc8079fbb35
|
||||||
common/count_attributes: 042fba9baffef5afe2c24f13d4f50697
|
common/count_attributes: 042fba9baffef5afe2c24f13d4f50697
|
||||||
common/count_contacts: b1c413a4b06961b71b6aeee95d6775d7
|
common/count_contacts: b1c413a4b06961b71b6aeee95d6775d7
|
||||||
common/count_members: 8cabb9805075f20e3977b919b3b2fdc5
|
|
||||||
common/count_responses: 690118a456c01c5b4d437ae82b50b131
|
common/count_responses: 690118a456c01c5b4d437ae82b50b131
|
||||||
common/count_selections: c0f581d21468af2f46dad171921f71ba
|
|
||||||
common/create_new_organization: 51dae7b33143686ee218abf5bea764a5
|
common/create_new_organization: 51dae7b33143686ee218abf5bea764a5
|
||||||
common/create_segment: 9d8291cd4d778b53b73bbc84fd91c181
|
common/create_segment: 9d8291cd4d778b53b73bbc84fd91c181
|
||||||
common/create_survey: 1cfbba08d34876566d84b2960054a987
|
common/create_survey: 1cfbba08d34876566d84b2960054a987
|
||||||
@@ -166,7 +165,6 @@ checksums:
|
|||||||
common/days: c95fe8aedde21a0b5653dbd0b3c58b48
|
common/days: c95fe8aedde21a0b5653dbd0b3c58b48
|
||||||
common/default: d9c6dc5c412fe94143dfd1d332ec81d4
|
common/default: d9c6dc5c412fe94143dfd1d332ec81d4
|
||||||
common/delete: 8bcf303dd10a645b5baacb02b47d72c9
|
common/delete: 8bcf303dd10a645b5baacb02b47d72c9
|
||||||
common/delete_what: 718ddfcc1dec7f3e8b67856fba838267
|
|
||||||
common/description: e17686a22ffad04cc7bb70524ed4478b
|
common/description: e17686a22ffad04cc7bb70524ed4478b
|
||||||
common/dev_env: e650911d5e19ba256358e0cda154c005
|
common/dev_env: e650911d5e19ba256358e0cda154c005
|
||||||
common/development: 85211dbb918bda7a6e87649dcfc1b17a
|
common/development: 85211dbb918bda7a6e87649dcfc1b17a
|
||||||
@@ -194,16 +192,13 @@ checksums:
|
|||||||
common/error: 3c95bcb32c2104b99a46f5b3dd015248
|
common/error: 3c95bcb32c2104b99a46f5b3dd015248
|
||||||
common/error_component_description: fa9eee04f864c3fe6e6681f716caa015
|
common/error_component_description: fa9eee04f864c3fe6e6681f716caa015
|
||||||
common/error_component_title: ae68fa341a143aaa13a5ea30dd57a63e
|
common/error_component_title: ae68fa341a143aaa13a5ea30dd57a63e
|
||||||
common/error_loading_data: aaeffbfe4a2c2145442a57de524494be
|
|
||||||
common/error_rate_limit_description: 37791a33a947204662ee9c6544e90f51
|
common/error_rate_limit_description: 37791a33a947204662ee9c6544e90f51
|
||||||
common/error_rate_limit_title: 23ac9419e267e610e1bfd38e1dc35dc0
|
common/error_rate_limit_title: 23ac9419e267e610e1bfd38e1dc35dc0
|
||||||
common/expand_rows: b6e06327cb8718dfd6651720843e4dad
|
common/expand_rows: b6e06327cb8718dfd6651720843e4dad
|
||||||
common/failed_to_copy_to_clipboard: de836a7d628d36c832809252f188f784
|
common/failed_to_copy_to_clipboard: de836a7d628d36c832809252f188f784
|
||||||
common/failed_to_load_organizations: 512808a2b674c7c28bca73f8f91fd87e
|
common/failed_to_load_organizations: 512808a2b674c7c28bca73f8f91fd87e
|
||||||
common/failed_to_load_workspaces: 6ee3448097394517dc605074cd4e6ea4
|
common/failed_to_load_workspaces: 6ee3448097394517dc605074cd4e6ea4
|
||||||
common/filter: 626325a05e4c8800f7ede7012b0cadaf
|
|
||||||
common/finish: ffa7a10f71182b48fefed7135bee24fa
|
common/finish: ffa7a10f71182b48fefed7135bee24fa
|
||||||
common/first_name: cf040a5d6a9fd696be400380cc99f54b
|
|
||||||
common/follow_these: 3a730b242bb17a3f95e01bf0dae86885
|
common/follow_these: 3a730b242bb17a3f95e01bf0dae86885
|
||||||
common/formbricks_version: d9967c797f3e49ca0cae78bc0ebd19cb
|
common/formbricks_version: d9967c797f3e49ca0cae78bc0ebd19cb
|
||||||
common/full_name: f45991923345e8322c9ff8cd6b7e2b16
|
common/full_name: f45991923345e8322c9ff8cd6b7e2b16
|
||||||
@@ -216,7 +211,6 @@ checksums:
|
|||||||
common/hidden_field: 3ed5c58d0ed359e558cdf7bd33606d2d
|
common/hidden_field: 3ed5c58d0ed359e558cdf7bd33606d2d
|
||||||
common/hidden_fields: 3de6cfd308293a826cb8679fd1d49972
|
common/hidden_fields: 3de6cfd308293a826cb8679fd1d49972
|
||||||
common/hide_column: 23ce94db148f2d8e4a0923defead6cf1
|
common/hide_column: 23ce94db148f2d8e4a0923defead6cf1
|
||||||
common/id: c8886d38aeea2ed5f785aba4fc96784b
|
|
||||||
common/image: 048ba7a239de0fbd883ade8558415830
|
common/image: 048ba7a239de0fbd883ade8558415830
|
||||||
common/images: 9305827c28694866f49db42b4c51831f
|
common/images: 9305827c28694866f49db42b4c51831f
|
||||||
common/import: 348b8ab981de5b7f1fca6d7302263bbd
|
common/import: 348b8ab981de5b7f1fca6d7302263bbd
|
||||||
@@ -234,7 +228,6 @@ checksums:
|
|||||||
common/key: 3d1065ab98a1c2f1210507fd5c7bf515
|
common/key: 3d1065ab98a1c2f1210507fd5c7bf515
|
||||||
common/label: a5c71bf158481233f8215dbd38cc196b
|
common/label: a5c71bf158481233f8215dbd38cc196b
|
||||||
common/language: 277fd1a41cc237a437cd1d5e4a80463b
|
common/language: 277fd1a41cc237a437cd1d5e4a80463b
|
||||||
common/last_name: 2c9a7de7738ca007ba9023c385149c26
|
|
||||||
common/learn_more: e598091d132f890c37a6d4ed94f6d794
|
common/learn_more: e598091d132f890c37a6d4ed94f6d794
|
||||||
common/license_expired: 7af13535e320e4197989472c01387d2c
|
common/license_expired: 7af13535e320e4197989472c01387d2c
|
||||||
common/light_overlay: 0499907ea7b8405f4267b117998b5a78
|
common/light_overlay: 0499907ea7b8405f4267b117998b5a78
|
||||||
@@ -261,7 +254,6 @@ checksums:
|
|||||||
common/move_down: 4f4de55743043355ad4a839aff2c48ff
|
common/move_down: 4f4de55743043355ad4a839aff2c48ff
|
||||||
common/move_up: 69f25b205c677abdb26cbb69d97cd10b
|
common/move_up: 69f25b205c677abdb26cbb69d97cd10b
|
||||||
common/multiple_languages: 7d8ddd4b40d32fcd7bd6f7bac6485b1f
|
common/multiple_languages: 7d8ddd4b40d32fcd7bd6f7bac6485b1f
|
||||||
common/my_product: ad022177062f9ef6e9acf33b13e889aa
|
|
||||||
common/name: 9368b5a047572b6051f334af5aa76819
|
common/name: 9368b5a047572b6051f334af5aa76819
|
||||||
common/new: 126d036fae5fb6b629728ecb97e6195b
|
common/new: 126d036fae5fb6b629728ecb97e6195b
|
||||||
common/new_version_available: 399ddfc4232712e18ddab2587356b3dc
|
common/new_version_available: 399ddfc4232712e18ddab2587356b3dc
|
||||||
@@ -410,7 +402,6 @@ checksums:
|
|||||||
common/top_right: 241f95c923846911aaf13af6109333e5
|
common/top_right: 241f95c923846911aaf13af6109333e5
|
||||||
common/try_again: 33dd8820e743e35a66e6977f69e9d3b5
|
common/try_again: 33dd8820e743e35a66e6977f69e9d3b5
|
||||||
common/type: f04471a7ddac844b9ad145eb9911ef75
|
common/type: f04471a7ddac844b9ad145eb9911ef75
|
||||||
common/unknown_survey: dd8f6985e17ccf19fac1776e18b2c498
|
|
||||||
common/unlock_more_workspaces_with_a_higher_plan: fe1590075b855bb4306c9388b65143b0
|
common/unlock_more_workspaces_with_a_higher_plan: fe1590075b855bb4306c9388b65143b0
|
||||||
common/update: 079fc039262fd31b10532929685c2d1b
|
common/update: 079fc039262fd31b10532929685c2d1b
|
||||||
common/updated: 8aa8ff2dc2977ca4b269e80a513100b4
|
common/updated: 8aa8ff2dc2977ca4b269e80a513100b4
|
||||||
@@ -619,6 +610,7 @@ checksums:
|
|||||||
environments/contacts/contacts_table_refresh: 6a959475991dd4ab28ad881bae569a09
|
environments/contacts/contacts_table_refresh: 6a959475991dd4ab28ad881bae569a09
|
||||||
environments/contacts/contacts_table_refresh_success: 40951396e88e5c8fdafa0b3bb4fadca8
|
environments/contacts/contacts_table_refresh_success: 40951396e88e5c8fdafa0b3bb4fadca8
|
||||||
environments/contacts/create_attribute: 87320615901f95b4f35ee83c290a3a6c
|
environments/contacts/create_attribute: 87320615901f95b4f35ee83c290a3a6c
|
||||||
|
environments/contacts/create_key: 0d385c354af8963acbe35cd646710f86
|
||||||
environments/contacts/create_new_attribute: c17d407dacd0b90f360f9f5e899d662f
|
environments/contacts/create_new_attribute: c17d407dacd0b90f360f9f5e899d662f
|
||||||
environments/contacts/create_new_attribute_description: cc19d76bb6940537bbe3461191f25d26
|
environments/contacts/create_new_attribute_description: cc19d76bb6940537bbe3461191f25d26
|
||||||
environments/contacts/custom_attributes: fffc7722742d1291b102dc737cf2fc9e
|
environments/contacts/custom_attributes: fffc7722742d1291b102dc737cf2fc9e
|
||||||
@@ -629,7 +621,6 @@ checksums:
|
|||||||
environments/contacts/delete_attribute_confirmation: 01d99b89eb3d27ff468d0db1b4aeb394
|
environments/contacts/delete_attribute_confirmation: 01d99b89eb3d27ff468d0db1b4aeb394
|
||||||
environments/contacts/delete_contact_confirmation: 2d45579e0bb4bc40fb1ee75b43c0e7a4
|
environments/contacts/delete_contact_confirmation: 2d45579e0bb4bc40fb1ee75b43c0e7a4
|
||||||
environments/contacts/delete_contact_confirmation_with_quotas: d3d17f13ae46ce04c126c82bf01299ac
|
environments/contacts/delete_contact_confirmation_with_quotas: d3d17f13ae46ce04c126c82bf01299ac
|
||||||
environments/contacts/displays: fcc4527002bd045021882be463b8ac72
|
|
||||||
environments/contacts/edit_attribute: 92a83c96a5d850e7d39002e8fd5898f4
|
environments/contacts/edit_attribute: 92a83c96a5d850e7d39002e8fd5898f4
|
||||||
environments/contacts/edit_attribute_description: 073a3084bb2f3b34ed1320ed1cd6db3c
|
environments/contacts/edit_attribute_description: 073a3084bb2f3b34ed1320ed1cd6db3c
|
||||||
environments/contacts/edit_attribute_values: 44e4e7a661cc1b59200bb07c710072a7
|
environments/contacts/edit_attribute_values: 44e4e7a661cc1b59200bb07c710072a7
|
||||||
@@ -641,7 +632,6 @@ checksums:
|
|||||||
environments/contacts/invalid_csv_column_names: dcb8534e7d4c00b9ea7bdaf389f72328
|
environments/contacts/invalid_csv_column_names: dcb8534e7d4c00b9ea7bdaf389f72328
|
||||||
environments/contacts/invalid_date_format: 5bad9730ac5a5bacd0792098f712b1c4
|
environments/contacts/invalid_date_format: 5bad9730ac5a5bacd0792098f712b1c4
|
||||||
environments/contacts/invalid_number_format: bd0422507385f671c3046730a6febc64
|
environments/contacts/invalid_number_format: bd0422507385f671c3046730a6febc64
|
||||||
environments/contacts/no_activity_yet: f88897ac05afd6bf8af0d4834ad24ffc
|
|
||||||
environments/contacts/no_published_link_surveys_available: 9c1abc5b21aba827443cdf87dd6c8bfe
|
environments/contacts/no_published_link_surveys_available: 9c1abc5b21aba827443cdf87dd6c8bfe
|
||||||
environments/contacts/no_published_surveys: bd945b0e2e2328c17615c94143bdd62b
|
environments/contacts/no_published_surveys: bd945b0e2e2328c17615c94143bdd62b
|
||||||
environments/contacts/no_responses_found: f10190cffdda4ca1bed479acbb89b13f
|
environments/contacts/no_responses_found: f10190cffdda4ca1bed479acbb89b13f
|
||||||
@@ -656,8 +646,6 @@ checksums:
|
|||||||
environments/contacts/select_a_survey: 1f49086dfb874307aae1136e88c3d514
|
environments/contacts/select_a_survey: 1f49086dfb874307aae1136e88c3d514
|
||||||
environments/contacts/select_attribute: d93fb60eb4fbb42bf13a22f6216fbd79
|
environments/contacts/select_attribute: d93fb60eb4fbb42bf13a22f6216fbd79
|
||||||
environments/contacts/select_attribute_key: 673a6683fab41b387d921841cded7e38
|
environments/contacts/select_attribute_key: 673a6683fab41b387d921841cded7e38
|
||||||
environments/contacts/survey_viewed: 646d413218626787b0373ffd71cb7451
|
|
||||||
environments/contacts/survey_viewed_at: 2ab535237af5c3c3f33acc792a7e70a4
|
|
||||||
environments/contacts/system_attributes: eadb6a8888c7b32c0e68881f945ae9b6
|
environments/contacts/system_attributes: eadb6a8888c7b32c0e68881f945ae9b6
|
||||||
environments/contacts/unlock_contacts_description: c5572047f02b4c39e5109f9de715499d
|
environments/contacts/unlock_contacts_description: c5572047f02b4c39e5109f9de715499d
|
||||||
environments/contacts/unlock_contacts_title: a8b3d7db03eb404d9267fd5cdd6d5ddb
|
environments/contacts/unlock_contacts_title: a8b3d7db03eb404d9267fd5cdd6d5ddb
|
||||||
@@ -724,12 +712,7 @@ checksums:
|
|||||||
environments/integrations/google_sheets/link_google_sheet: fa78146ae26ce5b1d2aaf2678f628943
|
environments/integrations/google_sheets/link_google_sheet: fa78146ae26ce5b1d2aaf2678f628943
|
||||||
environments/integrations/google_sheets/link_new_sheet: 8ad2ea8708f50ed184c00b84577b325e
|
environments/integrations/google_sheets/link_new_sheet: 8ad2ea8708f50ed184c00b84577b325e
|
||||||
environments/integrations/google_sheets/no_integrations_yet: ea46f7747937baf48a47a4c1b1776aee
|
environments/integrations/google_sheets/no_integrations_yet: ea46f7747937baf48a47a4c1b1776aee
|
||||||
environments/integrations/google_sheets/reconnect_button: 8992a0f250278c116cb26be448b68ba2
|
|
||||||
environments/integrations/google_sheets/reconnect_button_description: 851fd2fda57211293090f371d5b2c734
|
|
||||||
environments/integrations/google_sheets/reconnect_button_tooltip: 210dd97470fde8264d2c076db3c98fde
|
|
||||||
environments/integrations/google_sheets/spreadsheet_permission_error: 94f0007a187d3b9a7ab8200fe26aad20
|
|
||||||
environments/integrations/google_sheets/spreadsheet_url: b1665f96e6ecce23ea2d9196f4a3e5dd
|
environments/integrations/google_sheets/spreadsheet_url: b1665f96e6ecce23ea2d9196f4a3e5dd
|
||||||
environments/integrations/google_sheets/token_expired_error: 555d34c18c554ec8ac66614f21bd44fc
|
|
||||||
environments/integrations/include_created_at: 8011355b13e28e638d74e6f3d68a2bbf
|
environments/integrations/include_created_at: 8011355b13e28e638d74e6f3d68a2bbf
|
||||||
environments/integrations/include_hidden_fields: 25f0ea5ca1c6ead2cd121f8754cb8d72
|
environments/integrations/include_hidden_fields: 25f0ea5ca1c6ead2cd121f8754cb8d72
|
||||||
environments/integrations/include_metadata: 750091d965d7cc8d02468b5239816dc5
|
environments/integrations/include_metadata: 750091d965d7cc8d02468b5239816dc5
|
||||||
@@ -1024,7 +1007,7 @@ checksums:
|
|||||||
environments/settings/general/email_customization_preview_email_heading: 8b798cb8438b3dd356c02dab33b4c897
|
environments/settings/general/email_customization_preview_email_heading: 8b798cb8438b3dd356c02dab33b4c897
|
||||||
environments/settings/general/email_customization_preview_email_text: fa6ae92403cc8f3c35c03e6c94cbde51
|
environments/settings/general/email_customization_preview_email_text: fa6ae92403cc8f3c35c03e6c94cbde51
|
||||||
environments/settings/general/error_deleting_organization_please_try_again: 7f0fe257d4a0b40bff025408a7766706
|
environments/settings/general/error_deleting_organization_please_try_again: 7f0fe257d4a0b40bff025408a7766706
|
||||||
environments/settings/general/from_your_organization: 9ebd6dcd79f7bfad3fea46ed2e3133d2
|
environments/settings/general/from_your_organization: 4b7970431edb3d0f13c394dbd755a055
|
||||||
environments/settings/general/invitation_sent_once_more: e6e5ea066810f9dcb65788aa4f05d6e2
|
environments/settings/general/invitation_sent_once_more: e6e5ea066810f9dcb65788aa4f05d6e2
|
||||||
environments/settings/general/invite_deleted_successfully: 1c7dca6d0f6870d945288e38cfd2f943
|
environments/settings/general/invite_deleted_successfully: 1c7dca6d0f6870d945288e38cfd2f943
|
||||||
environments/settings/general/invite_expires_on: 6fd2356ad91a5f189070c43855904bb4
|
environments/settings/general/invite_expires_on: 6fd2356ad91a5f189070c43855904bb4
|
||||||
@@ -1179,7 +1162,6 @@ checksums:
|
|||||||
environments/surveys/edit/add_fallback_placeholder: 0e77ea487ddd7bc7fc2f1574b018dc08
|
environments/surveys/edit/add_fallback_placeholder: 0e77ea487ddd7bc7fc2f1574b018dc08
|
||||||
environments/surveys/edit/add_hidden_field_id: a8f55b51b790cf5f4d898af7770ad1ed
|
environments/surveys/edit/add_hidden_field_id: a8f55b51b790cf5f4d898af7770ad1ed
|
||||||
environments/surveys/edit/add_highlight_border: 66f52b21fbb9aa6561c98a090abaaf8f
|
environments/surveys/edit/add_highlight_border: 66f52b21fbb9aa6561c98a090abaaf8f
|
||||||
environments/surveys/edit/add_highlight_border_description: fe548fe03ea10ef5cd9e553d6812b3c2
|
|
||||||
environments/surveys/edit/add_logic: f234c9f1393a9ed4792dfbd15838c951
|
environments/surveys/edit/add_logic: f234c9f1393a9ed4792dfbd15838c951
|
||||||
environments/surveys/edit/add_none_of_the_above: dbe1ada4512d6c3f80c54c8fac107ec6
|
environments/surveys/edit/add_none_of_the_above: dbe1ada4512d6c3f80c54c8fac107ec6
|
||||||
environments/surveys/edit/add_option: 143c54f0b201067fe5159284d6daeca2
|
environments/surveys/edit/add_option: 143c54f0b201067fe5159284d6daeca2
|
||||||
@@ -1378,6 +1360,7 @@ checksums:
|
|||||||
environments/surveys/edit/follow_ups_modal_updated_successfull_toast: 61204fada3231f4f1fe3866e87e1130a
|
environments/surveys/edit/follow_ups_modal_updated_successfull_toast: 61204fada3231f4f1fe3866e87e1130a
|
||||||
environments/surveys/edit/follow_ups_new: 224c779d252b3e75086e4ed456ba2548
|
environments/surveys/edit/follow_ups_new: 224c779d252b3e75086e4ed456ba2548
|
||||||
environments/surveys/edit/follow_ups_upgrade_button_text: 4cd167527fc6cdb5b0bfc9b486b142a8
|
environments/surveys/edit/follow_ups_upgrade_button_text: 4cd167527fc6cdb5b0bfc9b486b142a8
|
||||||
|
environments/surveys/edit/form_styling: 1278a2db4257b5500474161133acc857
|
||||||
environments/surveys/edit/formbricks_sdk_is_not_connected: 35165b0cac182a98408007a378cc677e
|
environments/surveys/edit/formbricks_sdk_is_not_connected: 35165b0cac182a98408007a378cc677e
|
||||||
environments/surveys/edit/four_points: b289628a6b8a6cd0f7d17a14ca6cd7bf
|
environments/surveys/edit/four_points: b289628a6b8a6cd0f7d17a14ca6cd7bf
|
||||||
environments/surveys/edit/heading: 79e9dfa461f38a239d34b9833ca103f1
|
environments/surveys/edit/heading: 79e9dfa461f38a239d34b9833ca103f1
|
||||||
@@ -1548,7 +1531,7 @@ checksums:
|
|||||||
environments/surveys/edit/response_limits_redirections_and_more: e4f1cf94e56ad0e1b08701158d688802
|
environments/surveys/edit/response_limits_redirections_and_more: e4f1cf94e56ad0e1b08701158d688802
|
||||||
environments/surveys/edit/response_options: 2988136d5248d7726583108992dcbaee
|
environments/surveys/edit/response_options: 2988136d5248d7726583108992dcbaee
|
||||||
environments/surveys/edit/roundness: 5a161c8f5f258defb57ed1d551737cc4
|
environments/surveys/edit/roundness: 5a161c8f5f258defb57ed1d551737cc4
|
||||||
environments/surveys/edit/roundness_description: 03940a6871ae43efa4810cba7cadb74b
|
environments/surveys/edit/roundness_description: bde131aa5674836416dcdf2ff517d899
|
||||||
environments/surveys/edit/row_used_in_logic_error: f89453ff1b6db77ad84af840fedd9813
|
environments/surveys/edit/row_used_in_logic_error: f89453ff1b6db77ad84af840fedd9813
|
||||||
environments/surveys/edit/rows: 8f41f34e6ca28221cf1ebd948af4c151
|
environments/surveys/edit/rows: 8f41f34e6ca28221cf1ebd948af4c151
|
||||||
environments/surveys/edit/save_and_close: 6ede705b3f82f30269ff3054a5049e34
|
environments/surveys/edit/save_and_close: 6ede705b3f82f30269ff3054a5049e34
|
||||||
@@ -1594,7 +1577,6 @@ checksums:
|
|||||||
environments/surveys/edit/survey_completed_subheading: db537c356c3ab6564d24de0d11a0fee2
|
environments/surveys/edit/survey_completed_subheading: db537c356c3ab6564d24de0d11a0fee2
|
||||||
environments/surveys/edit/survey_display_settings: 8ed19e6a8e1376f7a1ba037d82c4ae11
|
environments/surveys/edit/survey_display_settings: 8ed19e6a8e1376f7a1ba037d82c4ae11
|
||||||
environments/surveys/edit/survey_placement: 083c10f257337f9648bf9d435b18ec2c
|
environments/surveys/edit/survey_placement: 083c10f257337f9648bf9d435b18ec2c
|
||||||
environments/surveys/edit/survey_styling: 7f96d6563e934e65687b74374a33b1dc
|
|
||||||
environments/surveys/edit/survey_trigger: f0c7014a684ca566698b87074fad5579
|
environments/surveys/edit/survey_trigger: f0c7014a684ca566698b87074fad5579
|
||||||
environments/surveys/edit/switch_multi_language_on_to_get_started: cca0ef91ee49095da30cd1e3f26c406f
|
environments/surveys/edit/switch_multi_language_on_to_get_started: cca0ef91ee49095da30cd1e3f26c406f
|
||||||
environments/surveys/edit/target_block_not_found: 0a0c401017ab32364fec2fcbf815d832
|
environments/surveys/edit/target_block_not_found: 0a0c401017ab32364fec2fcbf815d832
|
||||||
@@ -1683,6 +1665,7 @@ checksums:
|
|||||||
environments/surveys/edit/welcome_message: 986a434e3895c8ee0b267df95cc40051
|
environments/surveys/edit/welcome_message: 986a434e3895c8ee0b267df95cc40051
|
||||||
environments/surveys/edit/without_a_filter_all_of_your_users_can_be_surveyed: 451990569c61f25d01044cc45b1ce122
|
environments/surveys/edit/without_a_filter_all_of_your_users_can_be_surveyed: 451990569c61f25d01044cc45b1ce122
|
||||||
environments/surveys/edit/you_have_not_created_a_segment_yet: c6658bd1cee9c5c957c675db044708dd
|
environments/surveys/edit/you_have_not_created_a_segment_yet: c6658bd1cee9c5c957c675db044708dd
|
||||||
|
environments/surveys/edit/you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations: 04241177ba989ef4c1d8c01e1a7b8541
|
||||||
environments/surveys/edit/your_description_here_recall_information_with: 60f73a3cc9bdb9afea2166a7db8fd618
|
environments/surveys/edit/your_description_here_recall_information_with: 60f73a3cc9bdb9afea2166a7db8fd618
|
||||||
environments/surveys/edit/your_question_here_recall_information_with: 6395bd54f5167830c9d662ba403da167
|
environments/surveys/edit/your_question_here_recall_information_with: 6395bd54f5167830c9d662ba403da167
|
||||||
environments/surveys/edit/your_web_app: 07234bed03a33330dc50ae9fcf0174f3
|
environments/surveys/edit/your_web_app: 07234bed03a33330dc50ae9fcf0174f3
|
||||||
@@ -1864,7 +1847,6 @@ checksums:
|
|||||||
environments/surveys/summary/filtered_responses_excel: 06e57bae9e41979fd7fc4b8bfe3466f9
|
environments/surveys/summary/filtered_responses_excel: 06e57bae9e41979fd7fc4b8bfe3466f9
|
||||||
environments/surveys/summary/generating_qr_code: 5026d4a76f995db458195e5215d9bbd9
|
environments/surveys/summary/generating_qr_code: 5026d4a76f995db458195e5215d9bbd9
|
||||||
environments/surveys/summary/impressions: 7fe38d42d68a64d3fd8436a063751584
|
environments/surveys/summary/impressions: 7fe38d42d68a64d3fd8436a063751584
|
||||||
environments/surveys/summary/impressions_identified_only: 10f8c491463c73b8e6534314ee00d165
|
|
||||||
environments/surveys/summary/impressions_tooltip: 4d0823cbf360304770c7c5913e33fdc8
|
environments/surveys/summary/impressions_tooltip: 4d0823cbf360304770c7c5913e33fdc8
|
||||||
environments/surveys/summary/in_app/connection_description: 9710bbf8048a8a5c3b2b56db9d946b73
|
environments/surveys/summary/in_app/connection_description: 9710bbf8048a8a5c3b2b56db9d946b73
|
||||||
environments/surveys/summary/in_app/connection_title: 29e8a40ad6a7fdb5af5ee9451a70a9aa
|
environments/surveys/summary/in_app/connection_title: 29e8a40ad6a7fdb5af5ee9451a70a9aa
|
||||||
@@ -1905,7 +1887,6 @@ checksums:
|
|||||||
environments/surveys/summary/last_quarter: 2e565a81de9b3d7b1ee709ebb6f6eda1
|
environments/surveys/summary/last_quarter: 2e565a81de9b3d7b1ee709ebb6f6eda1
|
||||||
environments/surveys/summary/last_year: fe7c268a48bf85bc40da000e6e437637
|
environments/surveys/summary/last_year: fe7c268a48bf85bc40da000e6e437637
|
||||||
environments/surveys/summary/limit: 347051f1a068e01e8c4e4f6744d8e727
|
environments/surveys/summary/limit: 347051f1a068e01e8c4e4f6744d8e727
|
||||||
environments/surveys/summary/no_identified_impressions: c3bc42e6feb9010ced905ded51c5afc4
|
|
||||||
environments/surveys/summary/no_responses_found: f10190cffdda4ca1bed479acbb89b13f
|
environments/surveys/summary/no_responses_found: f10190cffdda4ca1bed479acbb89b13f
|
||||||
environments/surveys/summary/other_values_found: 48a74ee68c05f7fb162072b50c683b6a
|
environments/surveys/summary/other_values_found: 48a74ee68c05f7fb162072b50c683b6a
|
||||||
environments/surveys/summary/overall: 6c6d6533013d4739766af84b2871bca6
|
environments/surveys/summary/overall: 6c6d6533013d4739766af84b2871bca6
|
||||||
@@ -1928,7 +1909,6 @@ checksums:
|
|||||||
environments/surveys/summary/starts: 3153990a4ade414f501a7e63ab771362
|
environments/surveys/summary/starts: 3153990a4ade414f501a7e63ab771362
|
||||||
environments/surveys/summary/starts_tooltip: 0a7dd01320490dbbea923053fa1ccad6
|
environments/surveys/summary/starts_tooltip: 0a7dd01320490dbbea923053fa1ccad6
|
||||||
environments/surveys/summary/survey_reset_successfully: f53db36a28980ef4766215cf13f01e51
|
environments/surveys/summary/survey_reset_successfully: f53db36a28980ef4766215cf13f01e51
|
||||||
environments/surveys/summary/survey_results: b7d86f636beaee2b4d5746bdda058d07
|
|
||||||
environments/surveys/summary/this_month: 50845a38865204a97773c44dcd2ebb90
|
environments/surveys/summary/this_month: 50845a38865204a97773c44dcd2ebb90
|
||||||
environments/surveys/summary/this_quarter: 9c77d94783dff2269c069389122cd7bd
|
environments/surveys/summary/this_quarter: 9c77d94783dff2269c069389122cd7bd
|
||||||
environments/surveys/summary/this_year: 1e69651c2ac722f8ce138f43cf2e02f9
|
environments/surveys/summary/this_year: 1e69651c2ac722f8ce138f43cf2e02f9
|
||||||
@@ -2048,7 +2028,7 @@ checksums:
|
|||||||
environments/workspace/look/advanced_styling_field_description_size: a0d51c3ab7dc56320ecedc2b27917842
|
environments/workspace/look/advanced_styling_field_description_size: a0d51c3ab7dc56320ecedc2b27917842
|
||||||
environments/workspace/look/advanced_styling_field_description_size_description: ff880ea1beddd1b1ec7416d0b8a69cf3
|
environments/workspace/look/advanced_styling_field_description_size_description: ff880ea1beddd1b1ec7416d0b8a69cf3
|
||||||
environments/workspace/look/advanced_styling_field_description_weight: 514680cc7202ad29835c1cbcde3def1c
|
environments/workspace/look/advanced_styling_field_description_weight: 514680cc7202ad29835c1cbcde3def1c
|
||||||
environments/workspace/look/advanced_styling_field_description_weight_description: aa95bc81b5336a548e256bce49350683
|
environments/workspace/look/advanced_styling_field_description_weight_description: 441ac8db1a32557813eb68fbfd759061
|
||||||
environments/workspace/look/advanced_styling_field_font_size: ca44d14429b2175a1b194793b4ab8f6b
|
environments/workspace/look/advanced_styling_field_font_size: ca44d14429b2175a1b194793b4ab8f6b
|
||||||
environments/workspace/look/advanced_styling_field_font_weight: bfef83778146cf40550df9650d8a07da
|
environments/workspace/look/advanced_styling_field_font_weight: bfef83778146cf40550df9650d8a07da
|
||||||
environments/workspace/look/advanced_styling_field_headline_color: 4ccf3935ad90c88ad4add24f498673ce
|
environments/workspace/look/advanced_styling_field_headline_color: 4ccf3935ad90c88ad4add24f498673ce
|
||||||
@@ -2062,7 +2042,7 @@ checksums:
|
|||||||
environments/workspace/look/advanced_styling_field_indicator_bg_description: 7eb3b54a8b331354ec95c0dc1545c620
|
environments/workspace/look/advanced_styling_field_indicator_bg_description: 7eb3b54a8b331354ec95c0dc1545c620
|
||||||
environments/workspace/look/advanced_styling_field_input_border_radius_description: 0007f1bb572b35d9a3720daeb7a55617
|
environments/workspace/look/advanced_styling_field_input_border_radius_description: 0007f1bb572b35d9a3720daeb7a55617
|
||||||
environments/workspace/look/advanced_styling_field_input_font_size_description: 5311f95dcbd083623e35c98ea5374c3b
|
environments/workspace/look/advanced_styling_field_input_font_size_description: 5311f95dcbd083623e35c98ea5374c3b
|
||||||
environments/workspace/look/advanced_styling_field_input_height_description: bb7439d42ec3848a8fa9edb8b001b69a
|
environments/workspace/look/advanced_styling_field_input_height_description: e19ec0dc432478def0fd1199ad765e38
|
||||||
environments/workspace/look/advanced_styling_field_input_padding_x_description: 10e14296468321c13fda77fd1ba58dfd
|
environments/workspace/look/advanced_styling_field_input_padding_x_description: 10e14296468321c13fda77fd1ba58dfd
|
||||||
environments/workspace/look/advanced_styling_field_input_padding_y_description: 98b4aeff2940516d05ea61bdc1211d0d
|
environments/workspace/look/advanced_styling_field_input_padding_y_description: 98b4aeff2940516d05ea61bdc1211d0d
|
||||||
environments/workspace/look/advanced_styling_field_input_placeholder_opacity_description: f55a6700884d24014404e58876121ddf
|
environments/workspace/look/advanced_styling_field_input_placeholder_opacity_description: f55a6700884d24014404e58876121ddf
|
||||||
@@ -2071,8 +2051,6 @@ checksums:
|
|||||||
environments/workspace/look/advanced_styling_field_input_text_description: 460450df24ea0cc902710118a5000feb
|
environments/workspace/look/advanced_styling_field_input_text_description: 460450df24ea0cc902710118a5000feb
|
||||||
environments/workspace/look/advanced_styling_field_option_bg: 0ceaed10d99ed4ad83cb0934ab970174
|
environments/workspace/look/advanced_styling_field_option_bg: 0ceaed10d99ed4ad83cb0934ab970174
|
||||||
environments/workspace/look/advanced_styling_field_option_bg_description: 6cd6ccecbbb9f2f19439d7c682eb67c1
|
environments/workspace/look/advanced_styling_field_option_bg_description: 6cd6ccecbbb9f2f19439d7c682eb67c1
|
||||||
environments/workspace/look/advanced_styling_field_option_border: aa478eb148515b6a2637fb144ff72028
|
|
||||||
environments/workspace/look/advanced_styling_field_option_border_description: 8f75b740e8dcb7f6cfeff2e5d5ca7c92
|
|
||||||
environments/workspace/look/advanced_styling_field_option_border_radius_description: 23f81c25b2681a7c9e2c4f2e7d2e0656
|
environments/workspace/look/advanced_styling_field_option_border_radius_description: 23f81c25b2681a7c9e2c4f2e7d2e0656
|
||||||
environments/workspace/look/advanced_styling_field_option_font_size_description: 5430fd9b08819972f0a613bf3fa659da
|
environments/workspace/look/advanced_styling_field_option_font_size_description: 5430fd9b08819972f0a613bf3fa659da
|
||||||
environments/workspace/look/advanced_styling_field_option_label: 2767a5db32742073a01aac16488e93dc
|
environments/workspace/look/advanced_styling_field_option_label: 2767a5db32742073a01aac16488e93dc
|
||||||
@@ -2854,9 +2832,6 @@ checksums:
|
|||||||
templates/preview_survey_question_2_choice_2_label: 1af148222f327f28cf0db6513de5989e
|
templates/preview_survey_question_2_choice_2_label: 1af148222f327f28cf0db6513de5989e
|
||||||
templates/preview_survey_question_2_headline: 5cfb173d156555227fbc2c97ad921e72
|
templates/preview_survey_question_2_headline: 5cfb173d156555227fbc2c97ad921e72
|
||||||
templates/preview_survey_question_2_subheader: 2e652d8acd68d072e5a0ae686c4011c0
|
templates/preview_survey_question_2_subheader: 2e652d8acd68d072e5a0ae686c4011c0
|
||||||
templates/preview_survey_question_open_text_headline: a9509a47e0456ae98ec3ddac3d6fad2c
|
|
||||||
templates/preview_survey_question_open_text_placeholder: 37ee9c84f3777b9220d4faec1e1c78ee
|
|
||||||
templates/preview_survey_question_open_text_subheader: 3c7bf09f3f17b02bc2fbbbdb347a5830
|
|
||||||
templates/preview_survey_welcome_card_headline: 8778dc41547a2778d0f9482da989fc00
|
templates/preview_survey_welcome_card_headline: 8778dc41547a2778d0f9482da989fc00
|
||||||
templates/prioritize_features_description: 1eae41fad0e3947f803d8539081e59ec
|
templates/prioritize_features_description: 1eae41fad0e3947f803d8539081e59ec
|
||||||
templates/prioritize_features_name: 4ca59ff1f9c319aaa68c3106d820fd6a
|
templates/prioritize_features_name: 4ca59ff1f9c319aaa68c3106d820fd6a
|
||||||
|
|||||||
@@ -63,8 +63,7 @@ export const INVITE_DISABLED = env.INVITE_DISABLED === "1";
|
|||||||
|
|
||||||
export const SLACK_CLIENT_SECRET = env.SLACK_CLIENT_SECRET;
|
export const SLACK_CLIENT_SECRET = env.SLACK_CLIENT_SECRET;
|
||||||
export const SLACK_CLIENT_ID = env.SLACK_CLIENT_ID;
|
export const SLACK_CLIENT_ID = env.SLACK_CLIENT_ID;
|
||||||
export const SLACK_REDIRECT_URI = `${WEBAPP_URL}/api/v1/integrations/slack/callback`;
|
export const SLACK_AUTH_URL = `https://slack.com/oauth/v2/authorize?client_id=${env.SLACK_CLIENT_ID}&scope=channels:read,chat:write,chat:write.public,chat:write.customize,groups:read`;
|
||||||
export const SLACK_AUTH_URL = `https://slack.com/oauth/v2/authorize?client_id=${env.SLACK_CLIENT_ID}&scope=channels:read,chat:write,chat:write.public,chat:write.customize,groups:read&redirect_uri=${SLACK_REDIRECT_URI}`;
|
|
||||||
|
|
||||||
export const GOOGLE_SHEETS_CLIENT_ID = env.GOOGLE_SHEETS_CLIENT_ID;
|
export const GOOGLE_SHEETS_CLIENT_ID = env.GOOGLE_SHEETS_CLIENT_ID;
|
||||||
export const GOOGLE_SHEETS_CLIENT_SECRET = env.GOOGLE_SHEETS_CLIENT_SECRET;
|
export const GOOGLE_SHEETS_CLIENT_SECRET = env.GOOGLE_SHEETS_CLIENT_SECRET;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import "server-only";
|
import "server-only";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { cache as reactCache } from "react";
|
import { cache as reactCache } from "react";
|
||||||
import { z } from "zod";
|
|
||||||
import { prisma } from "@formbricks/database";
|
import { prisma } from "@formbricks/database";
|
||||||
import { ZId } from "@formbricks/types/common";
|
import { ZId } from "@formbricks/types/common";
|
||||||
import { TDisplay, TDisplayFilters, TDisplayWithContact } from "@formbricks/types/displays";
|
import { TDisplay, TDisplayFilters } from "@formbricks/types/displays";
|
||||||
import { DatabaseError } from "@formbricks/types/errors";
|
import { DatabaseError } from "@formbricks/types/errors";
|
||||||
import { validateInputs } from "../utils/validate";
|
import { validateInputs } from "../utils/validate";
|
||||||
|
|
||||||
@@ -24,12 +23,13 @@ export const getDisplayCountBySurveyId = reactCache(
|
|||||||
const displayCount = await prisma.display.count({
|
const displayCount = await prisma.display.count({
|
||||||
where: {
|
where: {
|
||||||
surveyId: surveyId,
|
surveyId: surveyId,
|
||||||
...(filters?.createdAt && {
|
...(filters &&
|
||||||
createdAt: {
|
filters.createdAt && {
|
||||||
gte: filters.createdAt.min,
|
createdAt: {
|
||||||
lte: filters.createdAt.max,
|
gte: filters.createdAt.min,
|
||||||
},
|
lte: filters.createdAt.max,
|
||||||
}),
|
},
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return displayCount;
|
return displayCount;
|
||||||
@@ -42,97 +42,6 @@ export const getDisplayCountBySurveyId = reactCache(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getDisplaysByContactId = reactCache(
|
|
||||||
async (contactId: string): Promise<Pick<TDisplay, "id" | "createdAt" | "surveyId">[]> => {
|
|
||||||
validateInputs([contactId, ZId]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const displays = await prisma.display.findMany({
|
|
||||||
where: { contactId },
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
createdAt: true,
|
|
||||||
surveyId: true,
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: "desc" },
|
|
||||||
});
|
|
||||||
|
|
||||||
return displays;
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
||||||
throw new DatabaseError(error.message);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getDisplaysBySurveyIdWithContact = reactCache(
|
|
||||||
async (surveyId: string, limit?: number, offset?: number): Promise<TDisplayWithContact[]> => {
|
|
||||||
validateInputs(
|
|
||||||
[surveyId, ZId],
|
|
||||||
[limit, z.number().int().min(1).optional()],
|
|
||||||
[offset, z.number().int().nonnegative().optional()]
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const displays = await prisma.display.findMany({
|
|
||||||
where: {
|
|
||||||
surveyId,
|
|
||||||
contactId: { not: null },
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
createdAt: true,
|
|
||||||
surveyId: true,
|
|
||||||
contact: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
attributes: {
|
|
||||||
where: {
|
|
||||||
attributeKey: {
|
|
||||||
key: { in: ["email", "userId"] },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
attributeKey: { select: { key: true } },
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: "desc" },
|
|
||||||
take: limit,
|
|
||||||
skip: offset,
|
|
||||||
});
|
|
||||||
|
|
||||||
return displays.map((display) => ({
|
|
||||||
id: display.id,
|
|
||||||
createdAt: display.createdAt,
|
|
||||||
surveyId: display.surveyId,
|
|
||||||
contact: display.contact
|
|
||||||
? {
|
|
||||||
id: display.contact.id,
|
|
||||||
attributes: display.contact.attributes.reduce(
|
|
||||||
(acc, attr) => {
|
|
||||||
acc[attr.attributeKey.key] = attr.value;
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{} as Record<string, string>
|
|
||||||
),
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
||||||
throw new DatabaseError(error.message);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const deleteDisplay = async (displayId: string, tx?: Prisma.TransactionClient): Promise<TDisplay> => {
|
export const deleteDisplay = async (displayId: string, tx?: Prisma.TransactionClient): Promise<TDisplay> => {
|
||||||
validateInputs([displayId, ZId]);
|
validateInputs([displayId, ZId]);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,219 +0,0 @@
|
|||||||
import { mockDisplayId, mockSurveyId } from "./__mocks__/data.mock";
|
|
||||||
import { prisma } from "@/lib/__mocks__/database";
|
|
||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import { describe, expect, test, vi } from "vitest";
|
|
||||||
import { PrismaErrorType } from "@formbricks/database/types/error";
|
|
||||||
import { DatabaseError, ValidationError } from "@formbricks/types/errors";
|
|
||||||
import { getDisplaysByContactId, getDisplaysBySurveyIdWithContact } from "../service";
|
|
||||||
|
|
||||||
const mockContactId = "clqnj99r9000008lebgf8734j";
|
|
||||||
|
|
||||||
const mockDisplaysForContact = [
|
|
||||||
{
|
|
||||||
id: mockDisplayId,
|
|
||||||
createdAt: new Date("2024-01-15T10:00:00Z"),
|
|
||||||
surveyId: mockSurveyId,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "clqkr5smu000208jy50v6g5k5",
|
|
||||||
createdAt: new Date("2024-01-14T10:00:00Z"),
|
|
||||||
surveyId: "clqkr8dlv000308jybb08evgs",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const mockDisplaysWithContact = [
|
|
||||||
{
|
|
||||||
id: mockDisplayId,
|
|
||||||
createdAt: new Date("2024-01-15T10:00:00Z"),
|
|
||||||
surveyId: mockSurveyId,
|
|
||||||
contact: {
|
|
||||||
id: mockContactId,
|
|
||||||
attributes: [
|
|
||||||
{ attributeKey: { key: "email" }, value: "test@example.com" },
|
|
||||||
{ attributeKey: { key: "userId" }, value: "user-123" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "clqkr5smu000208jy50v6g5k5",
|
|
||||||
createdAt: new Date("2024-01-14T10:00:00Z"),
|
|
||||||
surveyId: "clqkr8dlv000308jybb08evgs",
|
|
||||||
contact: {
|
|
||||||
id: "clqnj99r9000008lebgf8734k",
|
|
||||||
attributes: [{ attributeKey: { key: "userId" }, value: "user-456" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
describe("getDisplaysByContactId", () => {
|
|
||||||
describe("Happy Path", () => {
|
|
||||||
test("returns displays for a contact ordered by createdAt desc", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockResolvedValue(mockDisplaysForContact as any);
|
|
||||||
|
|
||||||
const result = await getDisplaysByContactId(mockContactId);
|
|
||||||
|
|
||||||
expect(result).toEqual(mockDisplaysForContact);
|
|
||||||
expect(prisma.display.findMany).toHaveBeenCalledWith({
|
|
||||||
where: { contactId: mockContactId },
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
createdAt: true,
|
|
||||||
surveyId: true,
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: "desc" },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("returns empty array when contact has no displays", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
|
|
||||||
|
|
||||||
const result = await getDisplaysByContactId(mockContactId);
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Sad Path", () => {
|
|
||||||
test("throws a ValidationError if the contactId is invalid", async () => {
|
|
||||||
await expect(getDisplaysByContactId("not-a-cuid")).rejects.toThrow(ValidationError);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws DatabaseError on PrismaClientKnownRequestError", async () => {
|
|
||||||
const errToThrow = new Prisma.PrismaClientKnownRequestError("Mock error", {
|
|
||||||
code: PrismaErrorType.UniqueConstraintViolation,
|
|
||||||
clientVersion: "0.0.1",
|
|
||||||
});
|
|
||||||
|
|
||||||
vi.mocked(prisma.display.findMany).mockRejectedValue(errToThrow);
|
|
||||||
|
|
||||||
await expect(getDisplaysByContactId(mockContactId)).rejects.toThrow(DatabaseError);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws generic Error for other exceptions", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockRejectedValue(new Error("Mock error"));
|
|
||||||
|
|
||||||
await expect(getDisplaysByContactId(mockContactId)).rejects.toThrow(Error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getDisplaysBySurveyIdWithContact", () => {
|
|
||||||
describe("Happy Path", () => {
|
|
||||||
test("returns displays with contact attributes transformed", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockResolvedValue(mockDisplaysWithContact as any);
|
|
||||||
|
|
||||||
const result = await getDisplaysBySurveyIdWithContact(mockSurveyId, 15, 0);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
id: mockDisplayId,
|
|
||||||
createdAt: new Date("2024-01-15T10:00:00Z"),
|
|
||||||
surveyId: mockSurveyId,
|
|
||||||
contact: {
|
|
||||||
id: mockContactId,
|
|
||||||
attributes: { email: "test@example.com", userId: "user-123" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "clqkr5smu000208jy50v6g5k5",
|
|
||||||
createdAt: new Date("2024-01-14T10:00:00Z"),
|
|
||||||
surveyId: "clqkr8dlv000308jybb08evgs",
|
|
||||||
contact: {
|
|
||||||
id: "clqnj99r9000008lebgf8734k",
|
|
||||||
attributes: { userId: "user-456" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("calls prisma with correct where clause and pagination", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
|
|
||||||
|
|
||||||
await getDisplaysBySurveyIdWithContact(mockSurveyId, 15, 0);
|
|
||||||
|
|
||||||
expect(prisma.display.findMany).toHaveBeenCalledWith({
|
|
||||||
where: {
|
|
||||||
surveyId: mockSurveyId,
|
|
||||||
contactId: { not: null },
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
createdAt: true,
|
|
||||||
surveyId: true,
|
|
||||||
contact: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
attributes: {
|
|
||||||
where: {
|
|
||||||
attributeKey: {
|
|
||||||
key: { in: ["email", "userId"] },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
attributeKey: { select: { key: true } },
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: "desc" },
|
|
||||||
take: 15,
|
|
||||||
skip: 0,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test("returns empty array when no displays found", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockResolvedValue([]);
|
|
||||||
|
|
||||||
const result = await getDisplaysBySurveyIdWithContact(mockSurveyId);
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("handles display with null contact", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockResolvedValue([
|
|
||||||
{
|
|
||||||
id: mockDisplayId,
|
|
||||||
createdAt: new Date("2024-01-15T10:00:00Z"),
|
|
||||||
surveyId: mockSurveyId,
|
|
||||||
contact: null,
|
|
||||||
},
|
|
||||||
] as any);
|
|
||||||
|
|
||||||
const result = await getDisplaysBySurveyIdWithContact(mockSurveyId);
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{
|
|
||||||
id: mockDisplayId,
|
|
||||||
createdAt: new Date("2024-01-15T10:00:00Z"),
|
|
||||||
surveyId: mockSurveyId,
|
|
||||||
contact: null,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Sad Path", () => {
|
|
||||||
test("throws a ValidationError if the surveyId is invalid", async () => {
|
|
||||||
await expect(getDisplaysBySurveyIdWithContact("not-a-cuid")).rejects.toThrow(ValidationError);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws DatabaseError on PrismaClientKnownRequestError", async () => {
|
|
||||||
const errToThrow = new Prisma.PrismaClientKnownRequestError("Mock error", {
|
|
||||||
code: PrismaErrorType.UniqueConstraintViolation,
|
|
||||||
clientVersion: "0.0.1",
|
|
||||||
});
|
|
||||||
|
|
||||||
vi.mocked(prisma.display.findMany).mockRejectedValue(errToThrow);
|
|
||||||
|
|
||||||
await expect(getDisplaysBySurveyIdWithContact(mockSurveyId)).rejects.toThrow(DatabaseError);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("throws generic Error for other exceptions", async () => {
|
|
||||||
vi.mocked(prisma.display.findMany).mockRejectedValue(new Error("Mock error"));
|
|
||||||
|
|
||||||
await expect(getDisplaysBySurveyIdWithContact(mockSurveyId)).rejects.toThrow(Error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,66 +1,44 @@
|
|||||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
// Mock constants module
|
||||||
const envMock = {
|
const envMock = {
|
||||||
WEBAPP_URL: undefined as string | undefined,
|
env: {
|
||||||
VERCEL_URL: undefined as string | undefined,
|
WEBAPP_URL: "http://localhost:3000",
|
||||||
PUBLIC_URL: undefined as string | undefined,
|
PUBLIC_URL: undefined as string | undefined,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
vi.mock("./env", () => ({
|
vi.mock("@/lib/env", () => envMock);
|
||||||
env: envMock,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const loadGetPublicDomain = async () => {
|
|
||||||
vi.resetModules();
|
|
||||||
const { getPublicDomain } = await import("./getPublicUrl");
|
|
||||||
return getPublicDomain;
|
|
||||||
};
|
|
||||||
|
|
||||||
describe("getPublicDomain", () => {
|
describe("getPublicDomain", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
envMock.WEBAPP_URL = undefined;
|
vi.resetModules();
|
||||||
envMock.VERCEL_URL = undefined;
|
|
||||||
envMock.PUBLIC_URL = undefined;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns trimmed WEBAPP_URL when configured", async () => {
|
test("should return WEBAPP_URL when PUBLIC_URL is not set", async () => {
|
||||||
envMock.WEBAPP_URL = " https://app.formbricks.com ";
|
const { getPublicDomain } = await import("./getPublicUrl");
|
||||||
|
const domain = getPublicDomain();
|
||||||
const getPublicDomain = await loadGetPublicDomain();
|
expect(domain).toBe("http://localhost:3000");
|
||||||
|
|
||||||
expect(getPublicDomain()).toBe("https://app.formbricks.com");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("falls back to VERCEL_URL when WEBAPP_URL is empty", async () => {
|
test("should return PUBLIC_URL when it is set", async () => {
|
||||||
envMock.WEBAPP_URL = " ";
|
envMock.env.PUBLIC_URL = "https://surveys.example.com";
|
||||||
envMock.VERCEL_URL = "preview.formbricks.com";
|
const { getPublicDomain } = await import("./getPublicUrl");
|
||||||
|
const domain = getPublicDomain();
|
||||||
const getPublicDomain = await loadGetPublicDomain();
|
expect(domain).toBe("https://surveys.example.com");
|
||||||
|
|
||||||
expect(getPublicDomain()).toBe("https://preview.formbricks.com");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("falls back to localhost when WEBAPP_URL and VERCEL_URL are not set", async () => {
|
test("should handle empty string PUBLIC_URL by returning WEBAPP_URL", async () => {
|
||||||
const getPublicDomain = await loadGetPublicDomain();
|
envMock.env.PUBLIC_URL = "";
|
||||||
|
const { getPublicDomain } = await import("./getPublicUrl");
|
||||||
expect(getPublicDomain()).toBe("http://localhost:3000");
|
const domain = getPublicDomain();
|
||||||
|
expect(domain).toBe("http://localhost:3000");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("returns PUBLIC_URL when set", async () => {
|
test("should handle undefined PUBLIC_URL by returning WEBAPP_URL", async () => {
|
||||||
envMock.WEBAPP_URL = "https://app.formbricks.com";
|
envMock.env.PUBLIC_URL = undefined;
|
||||||
envMock.PUBLIC_URL = "https://surveys.formbricks.com";
|
const { getPublicDomain } = await import("./getPublicUrl");
|
||||||
|
const domain = getPublicDomain();
|
||||||
const getPublicDomain = await loadGetPublicDomain();
|
expect(domain).toBe("http://localhost:3000");
|
||||||
|
|
||||||
expect(getPublicDomain()).toBe("https://surveys.formbricks.com");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("falls back to WEBAPP_URL when PUBLIC_URL is empty", async () => {
|
|
||||||
envMock.WEBAPP_URL = "https://app.formbricks.com";
|
|
||||||
envMock.PUBLIC_URL = " ";
|
|
||||||
|
|
||||||
const getPublicDomain = await loadGetPublicDomain();
|
|
||||||
|
|
||||||
expect(getPublicDomain()).toBe("https://app.formbricks.com");
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,8 @@
|
|||||||
import "server-only";
|
import "server-only";
|
||||||
import { env } from "./env";
|
import { env } from "./env";
|
||||||
|
|
||||||
const configuredWebappUrl = env.WEBAPP_URL?.trim() ?? "";
|
const WEBAPP_URL =
|
||||||
const WEBAPP_URL = (() => {
|
env.WEBAPP_URL ?? (env.VERCEL_URL ? `https://${env.VERCEL_URL}` : "") ?? "http://localhost:3000";
|
||||||
if (configuredWebappUrl !== "") {
|
|
||||||
return configuredWebappUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.VERCEL_URL) {
|
|
||||||
return `https://${env.VERCEL_URL}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "http://localhost:3000";
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the public domain URL
|
* Returns the public domain URL
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Error codes returned by Google Sheets integration.
|
|
||||||
* Use these constants when comparing error responses to avoid typos and enable reuse.
|
|
||||||
*/
|
|
||||||
export const GOOGLE_SHEET_INTEGRATION_INVALID_GRANT = "invalid_grant";
|
|
||||||
export const GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION = "insufficient_permission";
|
|
||||||
@@ -2,12 +2,7 @@ import "server-only";
|
|||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { ZString } from "@formbricks/types/common";
|
import { ZString } from "@formbricks/types/common";
|
||||||
import {
|
import { DatabaseError, UnknownError } from "@formbricks/types/errors";
|
||||||
AuthenticationError,
|
|
||||||
DatabaseError,
|
|
||||||
OperationNotAllowedError,
|
|
||||||
UnknownError,
|
|
||||||
} from "@formbricks/types/errors";
|
|
||||||
import {
|
import {
|
||||||
TIntegrationGoogleSheets,
|
TIntegrationGoogleSheets,
|
||||||
ZIntegrationGoogleSheets,
|
ZIntegrationGoogleSheets,
|
||||||
@@ -16,12 +11,8 @@ import {
|
|||||||
GOOGLE_SHEETS_CLIENT_ID,
|
GOOGLE_SHEETS_CLIENT_ID,
|
||||||
GOOGLE_SHEETS_CLIENT_SECRET,
|
GOOGLE_SHEETS_CLIENT_SECRET,
|
||||||
GOOGLE_SHEETS_REDIRECT_URL,
|
GOOGLE_SHEETS_REDIRECT_URL,
|
||||||
GOOGLE_SHEET_MESSAGE_LIMIT,
|
|
||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
import {
|
import { GOOGLE_SHEET_MESSAGE_LIMIT } from "@/lib/constants";
|
||||||
GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION,
|
|
||||||
GOOGLE_SHEET_INTEGRATION_INVALID_GRANT,
|
|
||||||
} from "@/lib/googleSheet/constants";
|
|
||||||
import { createOrUpdateIntegration } from "@/lib/integration/service";
|
import { createOrUpdateIntegration } from "@/lib/integration/service";
|
||||||
import { truncateText } from "../utils/strings";
|
import { truncateText } from "../utils/strings";
|
||||||
import { validateInputs } from "../utils/validate";
|
import { validateInputs } from "../utils/validate";
|
||||||
@@ -90,17 +81,6 @@ export const writeData = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validateGoogleSheetsConnection = async (
|
|
||||||
googleSheetIntegrationData: TIntegrationGoogleSheets
|
|
||||||
): Promise<void> => {
|
|
||||||
validateInputs([googleSheetIntegrationData, ZIntegrationGoogleSheets]);
|
|
||||||
const integrationData = structuredClone(googleSheetIntegrationData);
|
|
||||||
integrationData.config.data.forEach((data) => {
|
|
||||||
data.createdAt = new Date(data.createdAt);
|
|
||||||
});
|
|
||||||
await authorize(integrationData);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSpreadsheetNameById = async (
|
export const getSpreadsheetNameById = async (
|
||||||
googleSheetIntegrationData: TIntegrationGoogleSheets,
|
googleSheetIntegrationData: TIntegrationGoogleSheets,
|
||||||
spreadsheetId: string
|
spreadsheetId: string
|
||||||
@@ -114,17 +94,7 @@ export const getSpreadsheetNameById = async (
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
sheets.spreadsheets.get({ spreadsheetId }, (err, response) => {
|
sheets.spreadsheets.get({ spreadsheetId }, (err, response) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
const msg = err.message?.toLowerCase() ?? "";
|
reject(new UnknownError(`Error while fetching spreadsheet data: ${err.message}`));
|
||||||
const isPermissionError =
|
|
||||||
msg.includes("permission") ||
|
|
||||||
msg.includes("caller does not have") ||
|
|
||||||
msg.includes("insufficient permission") ||
|
|
||||||
msg.includes("access denied");
|
|
||||||
if (isPermissionError) {
|
|
||||||
reject(new OperationNotAllowedError(GOOGLE_SHEET_INTEGRATION_INSUFFICIENT_PERMISSION));
|
|
||||||
} else {
|
|
||||||
reject(new UnknownError(`Error while fetching spreadsheet data: ${err.message}`));
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const spreadsheetTitle = response.data.properties.title;
|
const spreadsheetTitle = response.data.properties.title;
|
||||||
@@ -139,70 +109,26 @@ export const getSpreadsheetNameById = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isInvalidGrantError = (error: unknown): boolean => {
|
|
||||||
const err = error as { message?: string; response?: { data?: { error?: string } } };
|
|
||||||
return (
|
|
||||||
typeof err?.message === "string" &&
|
|
||||||
err.message.toLowerCase().includes(GOOGLE_SHEET_INTEGRATION_INVALID_GRANT)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Buffer in ms before expiry_date to consider token near-expired (5 minutes). */
|
|
||||||
const TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
|
|
||||||
|
|
||||||
const GOOGLE_TOKENINFO_URL = "https://www.googleapis.com/oauth2/v1/tokeninfo";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies that the access token is still valid and not revoked (e.g. user removed app access).
|
|
||||||
* Returns true if token is valid, false if invalid/revoked.
|
|
||||||
*/
|
|
||||||
const isAccessTokenValid = async (accessToken: string): Promise<boolean> => {
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${GOOGLE_TOKENINFO_URL}?access_token=${encodeURIComponent(accessToken)}`);
|
|
||||||
return res.ok;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const authorize = async (googleSheetIntegrationData: TIntegrationGoogleSheets) => {
|
const authorize = async (googleSheetIntegrationData: TIntegrationGoogleSheets) => {
|
||||||
const client_id = GOOGLE_SHEETS_CLIENT_ID;
|
const client_id = GOOGLE_SHEETS_CLIENT_ID;
|
||||||
const client_secret = GOOGLE_SHEETS_CLIENT_SECRET;
|
const client_secret = GOOGLE_SHEETS_CLIENT_SECRET;
|
||||||
const redirect_uri = GOOGLE_SHEETS_REDIRECT_URL;
|
const redirect_uri = GOOGLE_SHEETS_REDIRECT_URL;
|
||||||
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
|
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
|
||||||
const key = googleSheetIntegrationData.config.key;
|
const refresh_token = googleSheetIntegrationData.config.key.refresh_token;
|
||||||
|
oAuth2Client.setCredentials({
|
||||||
|
refresh_token,
|
||||||
|
});
|
||||||
|
const { credentials } = await oAuth2Client.refreshAccessToken();
|
||||||
|
await createOrUpdateIntegration(googleSheetIntegrationData.environmentId, {
|
||||||
|
type: "googleSheets",
|
||||||
|
config: {
|
||||||
|
data: googleSheetIntegrationData.config?.data ?? [],
|
||||||
|
email: googleSheetIntegrationData.config?.email ?? "",
|
||||||
|
key: credentials,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const hasStoredCredentials =
|
oAuth2Client.setCredentials(credentials);
|
||||||
key.access_token && key.expiry_date && key.expiry_date > Date.now() + TOKEN_EXPIRY_BUFFER_MS;
|
|
||||||
|
|
||||||
if (hasStoredCredentials && (await isAccessTokenValid(key.access_token))) {
|
return oAuth2Client;
|
||||||
oAuth2Client.setCredentials(key);
|
|
||||||
return oAuth2Client;
|
|
||||||
}
|
|
||||||
|
|
||||||
oAuth2Client.setCredentials({ refresh_token: key.refresh_token });
|
|
||||||
|
|
||||||
try {
|
|
||||||
const { credentials } = await oAuth2Client.refreshAccessToken();
|
|
||||||
const mergedCredentials = {
|
|
||||||
...credentials,
|
|
||||||
refresh_token: credentials.refresh_token ?? key.refresh_token,
|
|
||||||
};
|
|
||||||
await createOrUpdateIntegration(googleSheetIntegrationData.environmentId, {
|
|
||||||
type: "googleSheets",
|
|
||||||
config: {
|
|
||||||
data: googleSheetIntegrationData.config?.data ?? [],
|
|
||||||
email: googleSheetIntegrationData.config?.email ?? "",
|
|
||||||
key: mergedCredentials,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
oAuth2Client.setCredentials(mergedCredentials);
|
|
||||||
return oAuth2Client;
|
|
||||||
} catch (error) {
|
|
||||||
if (isInvalidGrantError(error)) {
|
|
||||||
throw new AuthenticationError(GOOGLE_SHEET_INTEGRATION_INVALID_GRANT);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import { getElementsFromBlocks } from "@/lib/survey/utils";
|
|||||||
import { getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
import { getIsQuotasEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||||
import { reduceQuotaLimits } from "@/modules/ee/quotas/lib/quotas";
|
import { reduceQuotaLimits } from "@/modules/ee/quotas/lib/quotas";
|
||||||
import { deleteFile } from "@/modules/storage/service";
|
import { deleteFile } from "@/modules/storage/service";
|
||||||
import { resolveStorageUrlsInObject } from "@/modules/storage/utils";
|
|
||||||
import { getOrganizationIdFromEnvironmentId } from "@/modules/survey/lib/organization";
|
import { getOrganizationIdFromEnvironmentId } from "@/modules/survey/lib/organization";
|
||||||
import { getOrganizationBilling } from "@/modules/survey/lib/survey";
|
import { getOrganizationBilling } from "@/modules/survey/lib/survey";
|
||||||
import { ITEMS_PER_PAGE } from "../constants";
|
import { ITEMS_PER_PAGE } from "../constants";
|
||||||
@@ -397,6 +396,7 @@ export const getResponseDownloadFile = async (
|
|||||||
"Survey ID",
|
"Survey ID",
|
||||||
"Formbricks ID (internal)",
|
"Formbricks ID (internal)",
|
||||||
"User ID",
|
"User ID",
|
||||||
|
"Notes",
|
||||||
"Tags",
|
"Tags",
|
||||||
...metaDataFields,
|
...metaDataFields,
|
||||||
...elements.flat(),
|
...elements.flat(),
|
||||||
@@ -408,10 +408,9 @@ export const getResponseDownloadFile = async (
|
|||||||
if (survey.isVerifyEmailEnabled) {
|
if (survey.isVerifyEmailEnabled) {
|
||||||
headers.push("Verified Email");
|
headers.push("Verified Email");
|
||||||
}
|
}
|
||||||
const resolvedResponses = responses.map((r) => ({ ...r, data: resolveStorageUrlsInObject(r.data) }));
|
|
||||||
const jsonData = getResponsesJson(
|
const jsonData = getResponsesJson(
|
||||||
survey,
|
survey,
|
||||||
resolvedResponses,
|
responses,
|
||||||
elements,
|
elements,
|
||||||
userAttributes,
|
userAttributes,
|
||||||
hiddenFields,
|
hiddenFields,
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ export const getSuggestedColors = (brandColor: string = DEFAULT_BRAND_COLOR) =>
|
|||||||
// Options (Radio / Checkbox)
|
// Options (Radio / Checkbox)
|
||||||
"optionBgColor.light": inputBg,
|
"optionBgColor.light": inputBg,
|
||||||
"optionLabelColor.light": questionColor,
|
"optionLabelColor.light": questionColor,
|
||||||
"optionBorderColor.light": inputBorder,
|
|
||||||
|
|
||||||
// Card
|
// Card
|
||||||
"cardBackgroundColor.light": cardBg,
|
"cardBackgroundColor.light": cardBg,
|
||||||
@@ -139,7 +138,6 @@ export const STYLE_DEFAULTS: TProjectStyling = {
|
|||||||
// Options
|
// Options
|
||||||
optionBgColor: { light: _colors["optionBgColor.light"] },
|
optionBgColor: { light: _colors["optionBgColor.light"] },
|
||||||
optionLabelColor: { light: _colors["optionLabelColor.light"] },
|
optionLabelColor: { light: _colors["optionLabelColor.light"] },
|
||||||
optionBorderColor: { light: _colors["optionBorderColor.light"] },
|
|
||||||
optionBorderRadius: 8,
|
optionBorderRadius: 8,
|
||||||
optionPaddingX: 16,
|
optionPaddingX: 16,
|
||||||
optionPaddingY: 16,
|
optionPaddingY: 16,
|
||||||
@@ -151,43 +149,6 @@ export const STYLE_DEFAULTS: TProjectStyling = {
|
|||||||
progressIndicatorBgColor: { light: _colors["progressIndicatorBgColor.light"] },
|
progressIndicatorBgColor: { light: _colors["progressIndicatorBgColor.light"] },
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Fills in new v4.7 color fields from legacy v4.6 fields when they are missing.
|
|
||||||
*
|
|
||||||
* v4.6 stored: brandColor, questionColor, inputColor, inputBorderColor.
|
|
||||||
* v4.7 adds: elementHeadlineColor, buttonBgColor, optionBgColor, etc.
|
|
||||||
*
|
|
||||||
* When loading v4.6 data the new fields are absent. Without this helper the
|
|
||||||
* form would fall back to STYLE_DEFAULTS (derived from the *default* brand
|
|
||||||
* colour), causing a visible mismatch. This function derives the new fields
|
|
||||||
* from the actually-saved legacy fields so the preview and form stay coherent.
|
|
||||||
*
|
|
||||||
* Only sets a field when the legacy source exists AND the new field is absent.
|
|
||||||
*/
|
|
||||||
export const deriveNewFieldsFromLegacy = (saved: Record<string, unknown>): Record<string, unknown> => {
|
|
||||||
const light = (key: string): string | undefined =>
|
|
||||||
(saved[key] as { light?: string } | null | undefined)?.light;
|
|
||||||
|
|
||||||
const q = light("questionColor");
|
|
||||||
const b = light("brandColor");
|
|
||||||
const i = light("inputColor");
|
|
||||||
const inputBorder = light("inputBorderColor");
|
|
||||||
|
|
||||||
return {
|
|
||||||
...(q && !saved.elementHeadlineColor && { elementHeadlineColor: { light: q } }),
|
|
||||||
...(q && !saved.elementDescriptionColor && { elementDescriptionColor: { light: q } }),
|
|
||||||
...(q && !saved.elementUpperLabelColor && { elementUpperLabelColor: { light: q } }),
|
|
||||||
...(q && !saved.inputTextColor && { inputTextColor: { light: q } }),
|
|
||||||
...(q && !saved.optionLabelColor && { optionLabelColor: { light: q } }),
|
|
||||||
...(b && !saved.buttonBgColor && { buttonBgColor: { light: b } }),
|
|
||||||
...(b && !saved.buttonTextColor && { buttonTextColor: { light: isLight(b) ? "#0f172a" : "#ffffff" } }),
|
|
||||||
...(i && !saved.optionBgColor && { optionBgColor: { light: i } }),
|
|
||||||
...(inputBorder && !saved.optionBorderColor && { optionBorderColor: { light: inputBorder } }),
|
|
||||||
...(b && !saved.progressIndicatorBgColor && { progressIndicatorBgColor: { light: b } }),
|
|
||||||
...(b && !saved.progressTrackBgColor && { progressTrackBgColor: { light: mixColor(b, "#ffffff", 0.8) } }),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a complete TProjectStyling object from a single brand color.
|
* Builds a complete TProjectStyling object from a single brand color.
|
||||||
*
|
*
|
||||||
@@ -214,7 +175,6 @@ export const buildStylingFromBrandColor = (brandColor: string = DEFAULT_BRAND_CO
|
|||||||
inputTextColor: { light: colors["inputTextColor.light"] },
|
inputTextColor: { light: colors["inputTextColor.light"] },
|
||||||
optionBgColor: { light: colors["optionBgColor.light"] },
|
optionBgColor: { light: colors["optionBgColor.light"] },
|
||||||
optionLabelColor: { light: colors["optionLabelColor.light"] },
|
optionLabelColor: { light: colors["optionLabelColor.light"] },
|
||||||
optionBorderColor: { light: colors["optionBorderColor.light"] },
|
|
||||||
cardBackgroundColor: { light: colors["cardBackgroundColor.light"] },
|
cardBackgroundColor: { light: colors["cardBackgroundColor.light"] },
|
||||||
cardBorderColor: { light: colors["cardBorderColor.light"] },
|
cardBorderColor: { light: colors["cardBorderColor.light"] },
|
||||||
highlightBorderColor: { light: colors["highlightBorderColor.light"] },
|
highlightBorderColor: { light: colors["highlightBorderColor.light"] },
|
||||||
|
|||||||
@@ -508,7 +508,6 @@ export const updateSurveyInternal = async (
|
|||||||
newFollowUps.length > 0
|
newFollowUps.length > 0
|
||||||
? {
|
? {
|
||||||
data: newFollowUps.map((followUp) => ({
|
data: newFollowUps.map((followUp) => ({
|
||||||
id: followUp.id,
|
|
||||||
name: followUp.name,
|
name: followUp.name,
|
||||||
trigger: followUp.trigger,
|
trigger: followUp.trigger,
|
||||||
action: followUp.action,
|
action: followUp.action,
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ export type AuditLoggingCtx = {
|
|||||||
quotaId?: string;
|
quotaId?: string;
|
||||||
teamId?: string;
|
teamId?: string;
|
||||||
integrationId?: string;
|
integrationId?: string;
|
||||||
|
chartId?: string;
|
||||||
|
dashboardId?: string;
|
||||||
|
dashboardWidgetId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActionClientCtx = {
|
export type ActionClientCtx = {
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "erlauben",
|
"allow": "erlauben",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Erlaube Nutzern, die Umfrage zu verlassen, indem sie außerhalb klicken",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Erlaube Nutzern, die Umfrage zu verlassen, indem sie außerhalb klicken",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Beim Löschen von {type}s ist ein unbekannter Fehler aufgetreten",
|
"an_unknown_error_occurred_while_deleting_table_items": "Beim Löschen von {type}s ist ein unbekannter Fehler aufgetreten",
|
||||||
|
"analysis": "Analyse",
|
||||||
"and": "und",
|
"and": "und",
|
||||||
"and_response_limit_of": "und Antwortlimit von",
|
"and_response_limit_of": "und Antwortlimit von",
|
||||||
"anonymous": "Anonym",
|
"anonymous": "Anonym",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Kopieren",
|
"copy": "Kopieren",
|
||||||
"copy_code": "Code kopieren",
|
"copy_code": "Code kopieren",
|
||||||
"copy_link": "Link kopieren",
|
"copy_link": "Link kopieren",
|
||||||
"count_attributes": "{count, plural, one {{count} Attribut} other {{count} Attribute}}",
|
"count_attributes": "{value, plural, one {{value} Attribut} other {{value} Attribute}}",
|
||||||
"count_contacts": "{count, plural, one {{count} Kontakt} other {{count} Kontakte}}",
|
"count_contacts": "{value, plural, one {{value} Kontakt} other {{value} Kontakte}}",
|
||||||
"count_members": "{count, plural, one {{count} Mitglied} other {{count} Mitglieder}}",
|
"count_responses": "{value, plural, one {{value} Antwort} other {{value} Antworten}}",
|
||||||
"count_responses": "{count, plural, one {{count} Antwort} other {{count} Antworten}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} Auswahl} other {{count} Auswahlen}}",
|
|
||||||
"create_new_organization": "Neue Organisation erstellen",
|
"create_new_organization": "Neue Organisation erstellen",
|
||||||
"create_segment": "Segment erstellen",
|
"create_segment": "Segment erstellen",
|
||||||
"create_survey": "Umfrage erstellen",
|
"create_survey": "Umfrage erstellen",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "Tage",
|
"days": "Tage",
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
"delete_what": "{deleteWhat} löschen",
|
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"dev_env": "Entwicklungsumgebung",
|
"dev_env": "Entwicklungsumgebung",
|
||||||
"development": "Entwicklung",
|
"development": "Entwicklung",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"error_component_description": "Diese Ressource existiert nicht oder Du hast nicht die notwendigen Rechte, um darauf zuzugreifen.",
|
"error_component_description": "Diese Ressource existiert nicht oder Du hast nicht die notwendigen Rechte, um darauf zuzugreifen.",
|
||||||
"error_component_title": "Fehler beim Laden der Ressourcen",
|
"error_component_title": "Fehler beim Laden der Ressourcen",
|
||||||
"error_loading_data": "Fehler beim Laden der Daten",
|
|
||||||
"error_rate_limit_description": "Maximale Anzahl an Anfragen erreicht. Bitte später erneut versuchen.",
|
"error_rate_limit_description": "Maximale Anzahl an Anfragen erreicht. Bitte später erneut versuchen.",
|
||||||
"error_rate_limit_title": "Rate Limit Überschritten",
|
"error_rate_limit_title": "Rate Limit Überschritten",
|
||||||
"expand_rows": "Zeilen erweitern",
|
"expand_rows": "Zeilen erweitern",
|
||||||
"failed_to_copy_to_clipboard": "Fehler beim Kopieren in die Zwischenablage",
|
"failed_to_copy_to_clipboard": "Fehler beim Kopieren in die Zwischenablage",
|
||||||
"failed_to_load_organizations": "Fehler beim Laden der Organisationen",
|
"failed_to_load_organizations": "Fehler beim Laden der Organisationen",
|
||||||
"failed_to_load_workspaces": "Projekte konnten nicht geladen werden",
|
"failed_to_load_workspaces": "Projekte konnten nicht geladen werden",
|
||||||
"filter": "Filter",
|
|
||||||
"finish": "Fertigstellen",
|
"finish": "Fertigstellen",
|
||||||
"first_name": "Vorname",
|
|
||||||
"follow_these": "Folge diesen",
|
"follow_these": "Folge diesen",
|
||||||
"formbricks_version": "Formbricks Version",
|
"formbricks_version": "Formbricks Version",
|
||||||
"full_name": "Name",
|
"full_name": "Name",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Verstecktes Feld",
|
"hidden_field": "Verstecktes Feld",
|
||||||
"hidden_fields": "Versteckte Felder",
|
"hidden_fields": "Versteckte Felder",
|
||||||
"hide_column": "Spalte ausblenden",
|
"hide_column": "Spalte ausblenden",
|
||||||
"id": "ID",
|
|
||||||
"image": "Bild",
|
"image": "Bild",
|
||||||
"images": "Bilder",
|
"images": "Bilder",
|
||||||
"import": "Importieren",
|
"import": "Importieren",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Schlüssel",
|
"key": "Schlüssel",
|
||||||
"label": "Bezeichnung",
|
"label": "Bezeichnung",
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
"last_name": "Nachname",
|
|
||||||
"learn_more": "Mehr erfahren",
|
"learn_more": "Mehr erfahren",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Helle Überlagerung",
|
"light_overlay": "Helle Überlagerung",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Darstellung",
|
"look_and_feel": "Darstellung",
|
||||||
"manage": "Verwalten",
|
"manage": "Verwalten",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Mitglied",
|
||||||
"members": "Mitglieder",
|
"members": "Mitglieder",
|
||||||
"members_and_teams": "Mitglieder & Teams",
|
"members_and_teams": "Mitglieder & Teams",
|
||||||
"membership_not_found": "Mitgliedschaft nicht gefunden",
|
"membership_not_found": "Mitgliedschaft nicht gefunden",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Nach unten bewegen",
|
"move_down": "Nach unten bewegen",
|
||||||
"move_up": "Nach oben bewegen",
|
"move_up": "Nach oben bewegen",
|
||||||
"multiple_languages": "Mehrsprachigkeit",
|
"multiple_languages": "Mehrsprachigkeit",
|
||||||
"my_product": "mein Produkt",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"new": "Neu",
|
"new": "Neu",
|
||||||
"new_version_available": "Formbricks {version} ist da. Jetzt aktualisieren!",
|
"new_version_available": "Formbricks {version} ist da. Jetzt aktualisieren!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Teams auswählen",
|
"select_teams": "Teams auswählen",
|
||||||
"selected": "Ausgewählt",
|
"selected": "Ausgewählt",
|
||||||
"selected_questions": "Ausgewählte Fragen",
|
"selected_questions": "Ausgewählte Fragen",
|
||||||
|
"selection": "Auswahl",
|
||||||
|
"selections": "Auswahlen",
|
||||||
"send_test_email": "Test-E-Mail senden",
|
"send_test_email": "Test-E-Mail senden",
|
||||||
"session_not_found": "Sitzung nicht gefunden",
|
"session_not_found": "Sitzung nicht gefunden",
|
||||||
"settings": "Einstellungen",
|
"settings": "Einstellungen",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Oben rechts",
|
"top_right": "Oben rechts",
|
||||||
"try_again": "Versuch's nochmal",
|
"try_again": "Versuch's nochmal",
|
||||||
"type": "Typ",
|
"type": "Typ",
|
||||||
"unknown_survey": "Unbekannte Umfrage",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Schalten Sie mehr Projekte mit einem höheren Tarif frei.",
|
"unlock_more_workspaces_with_a_higher_plan": "Schalten Sie mehr Projekte mit einem höheren Tarif frei.",
|
||||||
"update": "Aktualisierung",
|
"update": "Aktualisierung",
|
||||||
"updated": "Aktualisiert",
|
"updated": "Aktualisiert",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Kontakte aktualisieren",
|
"contacts_table_refresh": "Kontakte aktualisieren",
|
||||||
"contacts_table_refresh_success": "Kontakte erfolgreich aktualisiert",
|
"contacts_table_refresh_success": "Kontakte erfolgreich aktualisiert",
|
||||||
"create_attribute": "Attribut erstellen",
|
"create_attribute": "Attribut erstellen",
|
||||||
|
"create_key": "Schlüssel erstellen",
|
||||||
"create_new_attribute": "Neues Attribut erstellen",
|
"create_new_attribute": "Neues Attribut erstellen",
|
||||||
"create_new_attribute_description": "Erstellen Sie ein neues Attribut für Segmentierungszwecke.",
|
"create_new_attribute_description": "Erstellen Sie ein neues Attribut für Segmentierungszwecke.",
|
||||||
"custom_attributes": "Benutzerdefinierte Attribute",
|
"custom_attributes": "Benutzerdefinierte Attribute",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Dadurch wird das ausgewählte Attribut gelöscht. Alle mit diesem Attribut verknüpften Kontaktdaten gehen verloren.} other {Dadurch werden die ausgewählten Attribute gelöscht. Alle mit diesen Attributen verknüpften Kontaktdaten gehen verloren.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Dadurch wird das ausgewählte Attribut gelöscht. Alle mit diesem Attribut verknüpften Kontaktdaten gehen verloren.} other {Dadurch werden die ausgewählten Attribute gelöscht. Alle mit diesen Attributen verknüpften Kontaktdaten gehen verloren.}}",
|
||||||
"delete_contact_confirmation": "Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren.",
|
"delete_contact_confirmation": "Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn dieser Kontakt Antworten hat, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.} other {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesen Kontakten verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn diesen Kontakten Antworten haben, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesem Kontakt verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn dieser Kontakt Antworten hat, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.} other {Dies wird alle Umfrageantworten und Kontaktattribute löschen, die mit diesen Kontakten verbunden sind. Jegliche zielgerichtete Kommunikation und Personalisierung basierend auf den Daten dieses Kontakts gehen verloren. Wenn diesen Kontakten Antworten haben, die zu den Umfragequoten zählen, werden die Quotenstände reduziert, aber die Quotenlimits bleiben unverändert.}}",
|
||||||
"displays": "Anzeigen",
|
|
||||||
"edit_attribute": "Attribut bearbeiten",
|
"edit_attribute": "Attribut bearbeiten",
|
||||||
"edit_attribute_description": "Aktualisieren Sie die Bezeichnung und Beschreibung für dieses Attribut.",
|
"edit_attribute_description": "Aktualisieren Sie die Bezeichnung und Beschreibung für dieses Attribut.",
|
||||||
"edit_attribute_values": "Attribute bearbeiten",
|
"edit_attribute_values": "Attribute bearbeiten",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Ungültige CSV-Spaltennamen: {columns}. Spaltennamen, die zu neuen Attributen werden, dürfen nur Kleinbuchstaben, Zahlen und Unterstriche enthalten und müssen mit einem Buchstaben beginnen.",
|
"invalid_csv_column_names": "Ungültige CSV-Spaltennamen: {columns}. Spaltennamen, die zu neuen Attributen werden, dürfen nur Kleinbuchstaben, Zahlen und Unterstriche enthalten und müssen mit einem Buchstaben beginnen.",
|
||||||
"invalid_date_format": "Ungültiges Datumsformat. Bitte verwende ein gültiges Datum.",
|
"invalid_date_format": "Ungültiges Datumsformat. Bitte verwende ein gültiges Datum.",
|
||||||
"invalid_number_format": "Ungültiges Zahlenformat. Bitte gib eine gültige Zahl ein.",
|
"invalid_number_format": "Ungültiges Zahlenformat. Bitte gib eine gültige Zahl ein.",
|
||||||
"no_activity_yet": "Noch keine Aktivität",
|
|
||||||
"no_published_link_surveys_available": "Keine veröffentlichten Link-Umfragen verfügbar. Bitte veröffentliche zuerst eine Link-Umfrage.",
|
"no_published_link_surveys_available": "Keine veröffentlichten Link-Umfragen verfügbar. Bitte veröffentliche zuerst eine Link-Umfrage.",
|
||||||
"no_published_surveys": "Keine veröffentlichten Umfragen",
|
"no_published_surveys": "Keine veröffentlichten Umfragen",
|
||||||
"no_responses_found": "Keine Antworten gefunden",
|
"no_responses_found": "Keine Antworten gefunden",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Wähle eine Umfrage aus",
|
"select_a_survey": "Wähle eine Umfrage aus",
|
||||||
"select_attribute": "Attribut auswählen",
|
"select_attribute": "Attribut auswählen",
|
||||||
"select_attribute_key": "Attributschlüssel auswählen",
|
"select_attribute_key": "Attributschlüssel auswählen",
|
||||||
"survey_viewed": "Umfrage angesehen",
|
|
||||||
"survey_viewed_at": "Angesehen am",
|
|
||||||
"system_attributes": "Systemattribute",
|
"system_attributes": "Systemattribute",
|
||||||
"unlock_contacts_description": "Verwalte Kontakte und sende gezielte Umfragen",
|
"unlock_contacts_description": "Verwalte Kontakte und sende gezielte Umfragen",
|
||||||
"unlock_contacts_title": "Kontakte mit einem höheren Plan freischalten",
|
"unlock_contacts_title": "Kontakte mit einem höheren Plan freischalten",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Tabelle verlinken",
|
"link_google_sheet": "Tabelle verlinken",
|
||||||
"link_new_sheet": "Neues Blatt verknüpfen",
|
"link_new_sheet": "Neues Blatt verknüpfen",
|
||||||
"no_integrations_yet": "Deine verknüpften Tabellen werden hier angezeigt, sobald Du sie hinzufügst ⏲️",
|
"no_integrations_yet": "Deine verknüpften Tabellen werden hier angezeigt, sobald Du sie hinzufügst ⏲️",
|
||||||
"reconnect_button": "Erneut verbinden",
|
"spreadsheet_url": "Tabellen-URL"
|
||||||
"reconnect_button_description": "Deine Google Sheets-Verbindung ist abgelaufen. Bitte verbinde dich erneut, um weiterhin Antworten zu synchronisieren. Deine bestehenden Tabellen-Links und Daten bleiben erhalten.",
|
|
||||||
"reconnect_button_tooltip": "Verbinde die Integration erneut, um deinen Zugriff zu aktualisieren. Deine bestehenden Tabellen-Links und Daten bleiben erhalten.",
|
|
||||||
"spreadsheet_permission_error": "Du hast keine Berechtigung, auf diese Tabelle zuzugreifen. Bitte stelle sicher, dass die Tabelle mit deinem Google-Konto geteilt ist und du Schreibzugriff auf die Tabelle hast.",
|
|
||||||
"spreadsheet_url": "Tabellen-URL",
|
|
||||||
"token_expired_error": "Das Google Sheets-Aktualisierungstoken ist abgelaufen oder wurde widerrufen. Bitte verbinde die Integration erneut."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Erstellungsdatum einbeziehen",
|
"include_created_at": "Erstellungsdatum einbeziehen",
|
||||||
"include_hidden_fields": "Versteckte Felder (hidden fields) einbeziehen",
|
"include_hidden_fields": "Versteckte Felder (hidden fields) einbeziehen",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Hey {userName}",
|
"email_customization_preview_email_heading": "Hey {userName}",
|
||||||
"email_customization_preview_email_text": "Dies ist eine E-Mail-Vorschau, um dir zu zeigen, welches Logo in den E-Mails gerendert wird.",
|
"email_customization_preview_email_text": "Dies ist eine E-Mail-Vorschau, um dir zu zeigen, welches Logo in den E-Mails gerendert wird.",
|
||||||
"error_deleting_organization_please_try_again": "Fehler beim Löschen der Organisation. Bitte versuche es erneut.",
|
"error_deleting_organization_please_try_again": "Fehler beim Löschen der Organisation. Bitte versuche es erneut.",
|
||||||
"from_your_organization": "{memberName} aus Ihrer Organisation",
|
"from_your_organization": "von deiner Organisation",
|
||||||
"invitation_sent_once_more": "Einladung nochmal gesendet.",
|
"invitation_sent_once_more": "Einladung nochmal gesendet.",
|
||||||
"invite_deleted_successfully": "Einladung erfolgreich gelöscht",
|
"invite_deleted_successfully": "Einladung erfolgreich gelöscht",
|
||||||
"invite_expires_on": "Einladung läuft ab am {date}",
|
"invite_expires_on": "Einladung läuft ab am {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Platzhalter hinzufügen, falls kein Wert zur Verfügung steht.",
|
"add_fallback_placeholder": "Platzhalter hinzufügen, falls kein Wert zur Verfügung steht.",
|
||||||
"add_hidden_field_id": "Verstecktes Feld ID hinzufügen",
|
"add_hidden_field_id": "Verstecktes Feld ID hinzufügen",
|
||||||
"add_highlight_border": "Rahmen hinzufügen",
|
"add_highlight_border": "Rahmen hinzufügen",
|
||||||
"add_highlight_border_description": "Gilt nur für In-Product-Umfragen.",
|
|
||||||
"add_logic": "Logik hinzufügen",
|
"add_logic": "Logik hinzufügen",
|
||||||
"add_none_of_the_above": "Füge \"Keine der oben genannten Optionen\" hinzu",
|
"add_none_of_the_above": "Füge \"Keine der oben genannten Optionen\" hinzu",
|
||||||
"add_option": "Option hinzufügen",
|
"add_option": "Option hinzufügen",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Nachverfolgung aktualisiert und wird gespeichert, sobald du die Umfrage speicherst.",
|
"follow_ups_modal_updated_successfull_toast": "Nachverfolgung aktualisiert und wird gespeichert, sobald du die Umfrage speicherst.",
|
||||||
"follow_ups_new": "Neues Follow-up",
|
"follow_ups_new": "Neues Follow-up",
|
||||||
"follow_ups_upgrade_button_text": "Upgrade, um Follow-ups zu aktivieren",
|
"follow_ups_upgrade_button_text": "Upgrade, um Follow-ups zu aktivieren",
|
||||||
|
"form_styling": "Umfrage Styling",
|
||||||
"formbricks_sdk_is_not_connected": "Formbricks SDK ist nicht verbunden",
|
"formbricks_sdk_is_not_connected": "Formbricks SDK ist nicht verbunden",
|
||||||
"four_points": "4 Punkte",
|
"four_points": "4 Punkte",
|
||||||
"heading": "Überschrift",
|
"heading": "Überschrift",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Antwort Limits, Weiterleitungen und mehr.",
|
"response_limits_redirections_and_more": "Antwort Limits, Weiterleitungen und mehr.",
|
||||||
"response_options": "Antwortoptionen",
|
"response_options": "Antwortoptionen",
|
||||||
"roundness": "Rundheit",
|
"roundness": "Rundheit",
|
||||||
"roundness_description": "Steuert, wie abgerundet die Ecken sind.",
|
"roundness_description": "Steuert, wie abgerundet die Kartenecken sind.",
|
||||||
"row_used_in_logic_error": "Diese Zeile wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.",
|
"row_used_in_logic_error": "Diese Zeile wird in der Logik der Frage {questionIndex} verwendet. Bitte entferne sie zuerst aus der Logik.",
|
||||||
"rows": "Zeilen",
|
"rows": "Zeilen",
|
||||||
"save_and_close": "Speichern & Schließen",
|
"save_and_close": "Speichern & Schließen",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Diese kostenlose und quelloffene Umfrage wurde geschlossen",
|
"survey_completed_subheading": "Diese kostenlose und quelloffene Umfrage wurde geschlossen",
|
||||||
"survey_display_settings": "Einstellungen zur Anzeige der Umfrage",
|
"survey_display_settings": "Einstellungen zur Anzeige der Umfrage",
|
||||||
"survey_placement": "Platzierung der Umfrage",
|
"survey_placement": "Platzierung der Umfrage",
|
||||||
"survey_styling": "Umfrage Styling",
|
|
||||||
"survey_trigger": "Auslöser der Umfrage",
|
"survey_trigger": "Auslöser der Umfrage",
|
||||||
"switch_multi_language_on_to_get_started": "Aktiviere Mehrsprachigkeit, um loszulegen 👉",
|
"switch_multi_language_on_to_get_started": "Aktiviere Mehrsprachigkeit, um loszulegen 👉",
|
||||||
"target_block_not_found": "Zielblock nicht gefunden",
|
"target_block_not_found": "Zielblock nicht gefunden",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Willkommensnachricht",
|
"welcome_message": "Willkommensnachricht",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Ohne Filter können alle deine Nutzer befragt werden.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Ohne Filter können alle deine Nutzer befragt werden.",
|
||||||
"you_have_not_created_a_segment_yet": "Du hast noch keinen Segment erstellt.",
|
"you_have_not_created_a_segment_yet": "Du hast noch keinen Segment erstellt.",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Sie müssen zwei oder mehr Sprachen in Ihrem Workspace eingerichtet haben, um mit Übersetzungen zu arbeiten.",
|
||||||
"your_description_here_recall_information_with": "Deine Beschreibung hier. Informationen abrufen mit @",
|
"your_description_here_recall_information_with": "Deine Beschreibung hier. Informationen abrufen mit @",
|
||||||
"your_question_here_recall_information_with": "Deine Frage hier. Informationen abrufen mit @",
|
"your_question_here_recall_information_with": "Deine Frage hier. Informationen abrufen mit @",
|
||||||
"your_web_app": "Deine Web-App",
|
"your_web_app": "Deine Web-App",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Gefilterte Antworten (Excel)",
|
"filtered_responses_excel": "Gefilterte Antworten (Excel)",
|
||||||
"generating_qr_code": "QR-Code wird generiert",
|
"generating_qr_code": "QR-Code wird generiert",
|
||||||
"impressions": "Eindrücke",
|
"impressions": "Eindrücke",
|
||||||
"impressions_identified_only": "Zeigt nur Impressionen von identifizierten Kontakten",
|
|
||||||
"impressions_tooltip": "Anzahl der Aufrufe der Umfrage.",
|
"impressions_tooltip": "Anzahl der Aufrufe der Umfrage.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "Die Umfrage wird den Nutzern Ihrer Website angezeigt, die den unten aufgeführten Kriterien entsprechen",
|
"connection_description": "Die Umfrage wird den Nutzern Ihrer Website angezeigt, die den unten aufgeführten Kriterien entsprechen",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Letztes Quartal",
|
"last_quarter": "Letztes Quartal",
|
||||||
"last_year": "Letztes Jahr",
|
"last_year": "Letztes Jahr",
|
||||||
"limit": "Limit",
|
"limit": "Limit",
|
||||||
"no_identified_impressions": "Keine Impressionen von identifizierten Kontakten",
|
|
||||||
"no_responses_found": "Keine Antworten gefunden",
|
"no_responses_found": "Keine Antworten gefunden",
|
||||||
"other_values_found": "Andere Werte gefunden",
|
"other_values_found": "Andere Werte gefunden",
|
||||||
"overall": "Insgesamt",
|
"overall": "Insgesamt",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Startet",
|
"starts": "Startet",
|
||||||
"starts_tooltip": "So oft wurde die Umfrage gestartet.",
|
"starts_tooltip": "So oft wurde die Umfrage gestartet.",
|
||||||
"survey_reset_successfully": "Umfrage erfolgreich zurückgesetzt! {responseCount} Antworten und {displayCount} Anzeigen wurden gelöscht.",
|
"survey_reset_successfully": "Umfrage erfolgreich zurückgesetzt! {responseCount} Antworten und {displayCount} Anzeigen wurden gelöscht.",
|
||||||
"survey_results": "{surveyName}-Ergebnisse",
|
|
||||||
"this_month": "Dieser Monat",
|
"this_month": "Dieser Monat",
|
||||||
"this_quarter": "Dieses Quartal",
|
"this_quarter": "Dieses Quartal",
|
||||||
"this_year": "Dieses Jahr",
|
"this_year": "Dieses Jahr",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Färbt den gefüllten Teil des Balkens.",
|
"advanced_styling_field_indicator_bg_description": "Färbt den gefüllten Teil des Balkens.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Rundet die Eingabeecken ab.",
|
"advanced_styling_field_input_border_radius_description": "Rundet die Eingabeecken ab.",
|
||||||
"advanced_styling_field_input_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.",
|
"advanced_styling_field_input_font_size_description": "Skaliert den eingegebenen Text in Eingabefeldern.",
|
||||||
"advanced_styling_field_input_height_description": "Steuert die Mindesthöhe der Eingabe.",
|
"advanced_styling_field_input_height_description": "Legt die Mindesthöhe des Eingabefelds fest.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Fügt links und rechts Abstand hinzu.",
|
"advanced_styling_field_input_padding_x_description": "Fügt links und rechts Abstand hinzu.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Fügt oben und unten Abstand hinzu.",
|
"advanced_styling_field_input_padding_y_description": "Fügt oben und unten Abstand hinzu.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Blendet den Platzhaltertext aus.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Färbt den eingegebenen Text in Eingabefeldern.",
|
"advanced_styling_field_input_text_description": "Färbt den eingegebenen Text in Eingabefeldern.",
|
||||||
"advanced_styling_field_option_bg": "Hintergrund",
|
"advanced_styling_field_option_bg": "Hintergrund",
|
||||||
"advanced_styling_field_option_bg_description": "Füllt die Optionselemente.",
|
"advanced_styling_field_option_bg_description": "Füllt die Optionselemente.",
|
||||||
"advanced_styling_field_option_border": "Rahmenfarbe",
|
|
||||||
"advanced_styling_field_option_border_description": "Umrandet Radio- und Checkbox-Optionen.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Rundet die Ecken der Optionen ab.",
|
"advanced_styling_field_option_border_radius_description": "Rundet die Ecken der Optionen ab.",
|
||||||
"advanced_styling_field_option_font_size_description": "Skaliert den Text der Optionsbeschriftung.",
|
"advanced_styling_field_option_font_size_description": "Skaliert den Text der Optionsbeschriftung.",
|
||||||
"advanced_styling_field_option_label": "Label-Farbe",
|
"advanced_styling_field_option_label": "Label-Farbe",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Nein, danke!",
|
"preview_survey_question_2_choice_2_label": "Nein, danke!",
|
||||||
"preview_survey_question_2_headline": "Möchtest Du auf dem Laufenden bleiben?",
|
"preview_survey_question_2_headline": "Möchtest Du auf dem Laufenden bleiben?",
|
||||||
"preview_survey_question_2_subheader": "Dies ist eine Beispielbeschreibung.",
|
"preview_survey_question_2_subheader": "Dies ist eine Beispielbeschreibung.",
|
||||||
"preview_survey_question_open_text_headline": "Möchtest Du noch etwas teilen?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Tippe deine Antwort hier...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Dein Feedback hilft uns, besser zu werden.",
|
|
||||||
"preview_survey_welcome_card_headline": "Willkommen!",
|
"preview_survey_welcome_card_headline": "Willkommen!",
|
||||||
"prioritize_features_description": "Identifiziere die Funktionen, die deine Nutzer am meisten und am wenigsten brauchen.",
|
"prioritize_features_description": "Identifiziere die Funktionen, die deine Nutzer am meisten und am wenigsten brauchen.",
|
||||||
"prioritize_features_name": "Funktionen priorisieren",
|
"prioritize_features_name": "Funktionen priorisieren",
|
||||||
|
|||||||
+28
-37
@@ -122,6 +122,7 @@
|
|||||||
"activity": "Activity",
|
"activity": "Activity",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"add_action": "Add action",
|
"add_action": "Add action",
|
||||||
|
"add_chart": "Add chart",
|
||||||
"add_filter": "Add filter",
|
"add_filter": "Add filter",
|
||||||
"add_logo": "Add logo",
|
"add_logo": "Add logo",
|
||||||
"add_member": "Add member",
|
"add_member": "Add member",
|
||||||
@@ -133,6 +134,7 @@
|
|||||||
"allow": "Allow",
|
"allow": "Allow",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Allow users to exit by clicking outside the survey",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Allow users to exit by clicking outside the survey",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "An unknown error occurred while deleting {type}s",
|
"an_unknown_error_occurred_while_deleting_table_items": "An unknown error occurred while deleting {type}s",
|
||||||
|
"analysis": "Analysis",
|
||||||
"and": "And",
|
"and": "And",
|
||||||
"and_response_limit_of": "and response limit of",
|
"and_response_limit_of": "and response limit of",
|
||||||
"anonymous": "Anonymous",
|
"anonymous": "Anonymous",
|
||||||
@@ -175,11 +177,9 @@
|
|||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"copy_code": "Copy code",
|
"copy_code": "Copy code",
|
||||||
"copy_link": "Copy Link",
|
"copy_link": "Copy Link",
|
||||||
"count_attributes": "{count, plural, one {{count} attribute} other {{count} attributes}}",
|
"count_attributes": "{value, plural, one {{value} attribute} other {{value} attributes}}",
|
||||||
"count_contacts": "{count, plural, one {{count} contact} other {{count} contacts}}",
|
"count_contacts": "{value, plural, one {{value} contact} other {{value} contacts}}",
|
||||||
"count_members": "{count, plural, one {{count} member} other {{count} members}}",
|
"count_responses": "{value, plural, one {{value} response} other {{value} responses}}",
|
||||||
"count_responses": "{count, plural, one {{count} response} other {{count} responses}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} selection} other {{count} selections}}",
|
|
||||||
"create_new_organization": "Create new organization",
|
"create_new_organization": "Create new organization",
|
||||||
"create_segment": "Create segment",
|
"create_segment": "Create segment",
|
||||||
"create_survey": "Create survey",
|
"create_survey": "Create survey",
|
||||||
@@ -193,7 +193,6 @@
|
|||||||
"days": "days",
|
"days": "days",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"delete_what": "Delete {deleteWhat}",
|
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"dev_env": "Dev Environment",
|
"dev_env": "Dev Environment",
|
||||||
"development": "Development",
|
"development": "Development",
|
||||||
@@ -221,16 +220,13 @@
|
|||||||
"error": "Error",
|
"error": "Error",
|
||||||
"error_component_description": "This resource does not exist or you do not have the necessary rights to access it.",
|
"error_component_description": "This resource does not exist or you do not have the necessary rights to access it.",
|
||||||
"error_component_title": "Error loading resources",
|
"error_component_title": "Error loading resources",
|
||||||
"error_loading_data": "Error loading data",
|
|
||||||
"error_rate_limit_description": "Maximum number of requests reached. Please try again later.",
|
"error_rate_limit_description": "Maximum number of requests reached. Please try again later.",
|
||||||
"error_rate_limit_title": "Rate Limit Exceeded",
|
"error_rate_limit_title": "Rate Limit Exceeded",
|
||||||
"expand_rows": "Expand rows",
|
"expand_rows": "Expand rows",
|
||||||
"failed_to_copy_to_clipboard": "Failed to copy to clipboard",
|
"failed_to_copy_to_clipboard": "Failed to copy to clipboard",
|
||||||
"failed_to_load_organizations": "Failed to load organizations",
|
"failed_to_load_organizations": "Failed to load organizations",
|
||||||
"failed_to_load_workspaces": "Failed to load workspaces",
|
"failed_to_load_workspaces": "Failed to load workspaces",
|
||||||
"filter": "Filter",
|
|
||||||
"finish": "Finish",
|
"finish": "Finish",
|
||||||
"first_name": "First Name",
|
|
||||||
"follow_these": "Follow these",
|
"follow_these": "Follow these",
|
||||||
"formbricks_version": "Formbricks Version",
|
"formbricks_version": "Formbricks Version",
|
||||||
"full_name": "Full name",
|
"full_name": "Full name",
|
||||||
@@ -243,7 +239,6 @@
|
|||||||
"hidden_field": "Hidden field",
|
"hidden_field": "Hidden field",
|
||||||
"hidden_fields": "Hidden fields",
|
"hidden_fields": "Hidden fields",
|
||||||
"hide_column": "Hide column",
|
"hide_column": "Hide column",
|
||||||
"id": "ID",
|
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"images": "Images",
|
"images": "Images",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
@@ -261,7 +256,6 @@
|
|||||||
"key": "Key",
|
"key": "Key",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"language": "Language",
|
"language": "Language",
|
||||||
"last_name": "Last Name",
|
|
||||||
"learn_more": "Learn more",
|
"learn_more": "Learn more",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Light overlay",
|
"light_overlay": "Light overlay",
|
||||||
@@ -276,6 +270,7 @@
|
|||||||
"look_and_feel": "Look & Feel",
|
"look_and_feel": "Look & Feel",
|
||||||
"manage": "Manage",
|
"manage": "Manage",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Member",
|
||||||
"members": "Members",
|
"members": "Members",
|
||||||
"members_and_teams": "Members & Teams",
|
"members_and_teams": "Members & Teams",
|
||||||
"membership_not_found": "Membership not found",
|
"membership_not_found": "Membership not found",
|
||||||
@@ -287,7 +282,6 @@
|
|||||||
"move_down": "Move down",
|
"move_down": "Move down",
|
||||||
"move_up": "Move up",
|
"move_up": "Move up",
|
||||||
"multiple_languages": "Multiple languages",
|
"multiple_languages": "Multiple languages",
|
||||||
"my_product": "my Product",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"new": "New",
|
"new": "New",
|
||||||
"new_version_available": "Formbricks {version} is here. Upgrade now!",
|
"new_version_available": "Formbricks {version} is here. Upgrade now!",
|
||||||
@@ -383,6 +377,8 @@
|
|||||||
"select_teams": "Select teams",
|
"select_teams": "Select teams",
|
||||||
"selected": "Selected",
|
"selected": "Selected",
|
||||||
"selected_questions": "Selected questions",
|
"selected_questions": "Selected questions",
|
||||||
|
"selection": "Selection",
|
||||||
|
"selections": "Selections",
|
||||||
"send_test_email": "Send test email",
|
"send_test_email": "Send test email",
|
||||||
"session_not_found": "Session not found",
|
"session_not_found": "Session not found",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
@@ -405,6 +401,7 @@
|
|||||||
"styling": "Styling",
|
"styling": "Styling",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"summary": "Summary",
|
"summary": "Summary",
|
||||||
|
"chart": "Chart",
|
||||||
"survey": "Survey",
|
"survey": "Survey",
|
||||||
"survey_completed": "Survey completed.",
|
"survey_completed": "Survey completed.",
|
||||||
"survey_id": "Survey ID",
|
"survey_id": "Survey ID",
|
||||||
@@ -434,7 +431,6 @@
|
|||||||
"top_right": "Top Right",
|
"top_right": "Top Right",
|
||||||
"try_again": "Try again",
|
"try_again": "Try again",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unknown_survey": "Unknown survey",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Unlock more workspaces with a higher plan.",
|
"unlock_more_workspaces_with_a_higher_plan": "Unlock more workspaces with a higher plan.",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"updated": "Updated",
|
"updated": "Updated",
|
||||||
@@ -625,6 +621,17 @@
|
|||||||
"subtitle": "It takes less than 4 minutes.",
|
"subtitle": "It takes less than 4 minutes.",
|
||||||
"waiting_for_your_signal": "Waiting for your signal…"
|
"waiting_for_your_signal": "Waiting for your signal…"
|
||||||
},
|
},
|
||||||
|
"analysis": {
|
||||||
|
"charts": {
|
||||||
|
"edit_chart_description": "View and edit your chart configuration.",
|
||||||
|
"edit_chart_title": "Edit Chart",
|
||||||
|
"chart_deleted_successfully": "Chart deleted successfully",
|
||||||
|
"chart_duplicated_successfully": "Chart duplicated successfully",
|
||||||
|
"chart_duplication_error": "Failed to duplicate chart",
|
||||||
|
"delete_chart_confirmation": "Are you sure you want to delete this chart? This action cannot be undone.",
|
||||||
|
"open_options": "Open options"
|
||||||
|
}
|
||||||
|
},
|
||||||
"contacts": {
|
"contacts": {
|
||||||
"add_attribute": "Add Attribute",
|
"add_attribute": "Add Attribute",
|
||||||
"attribute_created_successfully": "Attribute created successfully",
|
"attribute_created_successfully": "Attribute created successfully",
|
||||||
@@ -652,6 +659,7 @@
|
|||||||
"contacts_table_refresh": "Refresh contacts",
|
"contacts_table_refresh": "Refresh contacts",
|
||||||
"contacts_table_refresh_success": "Contacts refreshed successfully",
|
"contacts_table_refresh_success": "Contacts refreshed successfully",
|
||||||
"create_attribute": "Create attribute",
|
"create_attribute": "Create attribute",
|
||||||
|
"create_key": "Create Key",
|
||||||
"create_new_attribute": "Create new attribute",
|
"create_new_attribute": "Create new attribute",
|
||||||
"create_new_attribute_description": "Create a new attribute for segmentation purposes.",
|
"create_new_attribute_description": "Create a new attribute for segmentation purposes.",
|
||||||
"custom_attributes": "Custom Attributes",
|
"custom_attributes": "Custom Attributes",
|
||||||
@@ -662,7 +670,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {This will delete the selected attribute. Any contact data associated with this attribute will be lost.} other {This will delete the selected attributes. Any contact data associated with these attributes will be lost.}}",
|
"delete_attribute_confirmation": "{value, plural, one {This will delete the selected attribute. Any contact data associated with this attribute will be lost.} other {This will delete the selected attributes. Any contact data associated with these attributes will be lost.}}",
|
||||||
"delete_contact_confirmation": "This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contact’s data will be lost.",
|
"delete_contact_confirmation": "This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contact’s data will be lost.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contact’s data will be lost. If this contact has responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.} other {This will delete all survey responses and contact attributes associated with these contacts. Any targeting and personalization based on these contacts’ data will be lost. If these contacts have responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {This will delete all survey responses and contact attributes associated with this contact. Any targeting and personalization based on this contact’s data will be lost. If this contact has responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.} other {This will delete all survey responses and contact attributes associated with these contacts. Any targeting and personalization based on these contacts’ data will be lost. If these contacts have responses that count towards survey quotas, the quota counts will be reduced but the quota limits will remain unchanged.}}",
|
||||||
"displays": "Displays",
|
|
||||||
"edit_attribute": "Edit attribute",
|
"edit_attribute": "Edit attribute",
|
||||||
"edit_attribute_description": "Update the label and description for this attribute.",
|
"edit_attribute_description": "Update the label and description for this attribute.",
|
||||||
"edit_attribute_values": "Edit attributes",
|
"edit_attribute_values": "Edit attributes",
|
||||||
@@ -674,7 +681,6 @@
|
|||||||
"invalid_csv_column_names": "Invalid CSV column name(s): {columns}. Column names that will become new attributes must only contain lowercase letters, numbers, and underscores, and must start with a letter.",
|
"invalid_csv_column_names": "Invalid CSV column name(s): {columns}. Column names that will become new attributes must only contain lowercase letters, numbers, and underscores, and must start with a letter.",
|
||||||
"invalid_date_format": "Invalid date format. Please use a valid date.",
|
"invalid_date_format": "Invalid date format. Please use a valid date.",
|
||||||
"invalid_number_format": "Invalid number format. Please enter a valid number.",
|
"invalid_number_format": "Invalid number format. Please enter a valid number.",
|
||||||
"no_activity_yet": "No activity yet",
|
|
||||||
"no_published_link_surveys_available": "No published link surveys available. Please publish a link survey first.",
|
"no_published_link_surveys_available": "No published link surveys available. Please publish a link survey first.",
|
||||||
"no_published_surveys": "No published surveys",
|
"no_published_surveys": "No published surveys",
|
||||||
"no_responses_found": "No responses found",
|
"no_responses_found": "No responses found",
|
||||||
@@ -689,8 +695,6 @@
|
|||||||
"select_a_survey": "Select a survey",
|
"select_a_survey": "Select a survey",
|
||||||
"select_attribute": "Select Attribute",
|
"select_attribute": "Select Attribute",
|
||||||
"select_attribute_key": "Select attribute key",
|
"select_attribute_key": "Select attribute key",
|
||||||
"survey_viewed": "Survey viewed",
|
|
||||||
"survey_viewed_at": "Viewed At",
|
|
||||||
"system_attributes": "System Attributes",
|
"system_attributes": "System Attributes",
|
||||||
"unlock_contacts_description": "Manage contacts and send out targeted surveys",
|
"unlock_contacts_description": "Manage contacts and send out targeted surveys",
|
||||||
"unlock_contacts_title": "Unlock contacts with a higher plan",
|
"unlock_contacts_title": "Unlock contacts with a higher plan",
|
||||||
@@ -762,12 +766,7 @@
|
|||||||
"link_google_sheet": "Link Google Sheet",
|
"link_google_sheet": "Link Google Sheet",
|
||||||
"link_new_sheet": "Link new Sheet",
|
"link_new_sheet": "Link new Sheet",
|
||||||
"no_integrations_yet": "Your google sheet integrations will appear here as soon as you add them. ⏲️",
|
"no_integrations_yet": "Your google sheet integrations will appear here as soon as you add them. ⏲️",
|
||||||
"reconnect_button": "Reconnect",
|
"spreadsheet_url": "Spreadsheet URL"
|
||||||
"reconnect_button_description": "Your Google Sheets connection has expired. Please reconnect to continue syncing responses. Your existing spreadsheet links and data will be preserved.",
|
|
||||||
"reconnect_button_tooltip": "Reconnect the integration to refresh your access. Your existing spreadsheet links and data will be preserved.",
|
|
||||||
"spreadsheet_permission_error": "You don't have permission to access this spreadsheet. Please ensure the spreadsheet is shared with your Google account and you have write access to the spreadsheet.",
|
|
||||||
"spreadsheet_url": "Spreadsheet URL",
|
|
||||||
"token_expired_error": "Google Sheets refresh token has expired or been revoked. Please reconnect the integration."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Include Created At",
|
"include_created_at": "Include Created At",
|
||||||
"include_hidden_fields": "Include Hidden Fields",
|
"include_hidden_fields": "Include Hidden Fields",
|
||||||
@@ -1082,7 +1081,7 @@
|
|||||||
"email_customization_preview_email_heading": "Hey {userName}",
|
"email_customization_preview_email_heading": "Hey {userName}",
|
||||||
"email_customization_preview_email_text": "This is an email preview to show you which logo will be rendered in the emails.",
|
"email_customization_preview_email_text": "This is an email preview to show you which logo will be rendered in the emails.",
|
||||||
"error_deleting_organization_please_try_again": "Error deleting organization. Please try again.",
|
"error_deleting_organization_please_try_again": "Error deleting organization. Please try again.",
|
||||||
"from_your_organization": "{memberName} from your organization",
|
"from_your_organization": "from your organization",
|
||||||
"invitation_sent_once_more": "Invitation sent once more.",
|
"invitation_sent_once_more": "Invitation sent once more.",
|
||||||
"invite_deleted_successfully": "Invite deleted successfully",
|
"invite_deleted_successfully": "Invite deleted successfully",
|
||||||
"invite_expires_on": "Invite expires on {date}",
|
"invite_expires_on": "Invite expires on {date}",
|
||||||
@@ -1247,7 +1246,6 @@
|
|||||||
"add_fallback_placeholder": "Add a placeholder to show if there is no value to recall.",
|
"add_fallback_placeholder": "Add a placeholder to show if there is no value to recall.",
|
||||||
"add_hidden_field_id": "Add hidden field ID",
|
"add_hidden_field_id": "Add hidden field ID",
|
||||||
"add_highlight_border": "Add highlight border",
|
"add_highlight_border": "Add highlight border",
|
||||||
"add_highlight_border_description": "Only applies to in-product surveys.",
|
|
||||||
"add_logic": "Add logic",
|
"add_logic": "Add logic",
|
||||||
"add_none_of_the_above": "Add “None of the Above”",
|
"add_none_of_the_above": "Add “None of the Above”",
|
||||||
"add_option": "Add option",
|
"add_option": "Add option",
|
||||||
@@ -1446,6 +1444,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Follow-up updated and will be saved once you save the survey.",
|
"follow_ups_modal_updated_successfull_toast": "Follow-up updated and will be saved once you save the survey.",
|
||||||
"follow_ups_new": "New follow-up",
|
"follow_ups_new": "New follow-up",
|
||||||
"follow_ups_upgrade_button_text": "Upgrade to enable follow-ups",
|
"follow_ups_upgrade_button_text": "Upgrade to enable follow-ups",
|
||||||
|
"form_styling": "Form styling",
|
||||||
"formbricks_sdk_is_not_connected": "Formbricks SDK is not connected",
|
"formbricks_sdk_is_not_connected": "Formbricks SDK is not connected",
|
||||||
"four_points": "4 points",
|
"four_points": "4 points",
|
||||||
"heading": "Heading",
|
"heading": "Heading",
|
||||||
@@ -1618,7 +1617,7 @@
|
|||||||
"response_limits_redirections_and_more": "Response limits, redirections and more.",
|
"response_limits_redirections_and_more": "Response limits, redirections and more.",
|
||||||
"response_options": "Response Options",
|
"response_options": "Response Options",
|
||||||
"roundness": "Roundness",
|
"roundness": "Roundness",
|
||||||
"roundness_description": "Controls how rounded corners are.",
|
"roundness_description": "Controls how rounded the card corners are.",
|
||||||
"row_used_in_logic_error": "This row is used in logic of question {questionIndex}. Please remove it from logic first.",
|
"row_used_in_logic_error": "This row is used in logic of question {questionIndex}. Please remove it from logic first.",
|
||||||
"rows": "Rows",
|
"rows": "Rows",
|
||||||
"save_and_close": "Save & Close",
|
"save_and_close": "Save & Close",
|
||||||
@@ -1664,7 +1663,6 @@
|
|||||||
"survey_completed_subheading": "This free & open-source survey has been closed",
|
"survey_completed_subheading": "This free & open-source survey has been closed",
|
||||||
"survey_display_settings": "Survey Display Settings",
|
"survey_display_settings": "Survey Display Settings",
|
||||||
"survey_placement": "Survey Placement",
|
"survey_placement": "Survey Placement",
|
||||||
"survey_styling": "Survey styling",
|
|
||||||
"survey_trigger": "Survey Trigger",
|
"survey_trigger": "Survey Trigger",
|
||||||
"switch_multi_language_on_to_get_started": "Switch multi-language on to get started 👉",
|
"switch_multi_language_on_to_get_started": "Switch multi-language on to get started 👉",
|
||||||
"target_block_not_found": "Target block not found",
|
"target_block_not_found": "Target block not found",
|
||||||
@@ -1755,6 +1753,7 @@
|
|||||||
"welcome_message": "Welcome message",
|
"welcome_message": "Welcome message",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Without a filter, all of your users can be surveyed.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Without a filter, all of your users can be surveyed.",
|
||||||
"you_have_not_created_a_segment_yet": "You have not created a segment yet",
|
"you_have_not_created_a_segment_yet": "You have not created a segment yet",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "You need to have two or more languages set up in your workspace to work with translations.",
|
||||||
"your_description_here_recall_information_with": "Your description here. Recall information with @",
|
"your_description_here_recall_information_with": "Your description here. Recall information with @",
|
||||||
"your_question_here_recall_information_with": "Your question here. Recall information with @",
|
"your_question_here_recall_information_with": "Your question here. Recall information with @",
|
||||||
"your_web_app": "Your web app",
|
"your_web_app": "Your web app",
|
||||||
@@ -1962,7 +1961,6 @@
|
|||||||
"filtered_responses_excel": "Filtered responses (Excel)",
|
"filtered_responses_excel": "Filtered responses (Excel)",
|
||||||
"generating_qr_code": "Generating QR code",
|
"generating_qr_code": "Generating QR code",
|
||||||
"impressions": "Impressions",
|
"impressions": "Impressions",
|
||||||
"impressions_identified_only": "Only showing impressions from identified contacts",
|
|
||||||
"impressions_tooltip": "Number of times the survey has been viewed.",
|
"impressions_tooltip": "Number of times the survey has been viewed.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "The survey will be shown to users of your website, that match the criteria listed below",
|
"connection_description": "The survey will be shown to users of your website, that match the criteria listed below",
|
||||||
@@ -2005,7 +2003,6 @@
|
|||||||
"last_quarter": "Last quarter",
|
"last_quarter": "Last quarter",
|
||||||
"last_year": "Last year",
|
"last_year": "Last year",
|
||||||
"limit": "Limit",
|
"limit": "Limit",
|
||||||
"no_identified_impressions": "No impressions from identified contacts",
|
|
||||||
"no_responses_found": "No responses found",
|
"no_responses_found": "No responses found",
|
||||||
"other_values_found": "Other values found",
|
"other_values_found": "Other values found",
|
||||||
"overall": "Overall",
|
"overall": "Overall",
|
||||||
@@ -2028,7 +2025,6 @@
|
|||||||
"starts": "Starts",
|
"starts": "Starts",
|
||||||
"starts_tooltip": "Number of times the survey has been started.",
|
"starts_tooltip": "Number of times the survey has been started.",
|
||||||
"survey_reset_successfully": "Survey reset successfully. {responseCount} responses and {displayCount} displays were deleted.",
|
"survey_reset_successfully": "Survey reset successfully. {responseCount} responses and {displayCount} displays were deleted.",
|
||||||
"survey_results": "{surveyName} Results",
|
|
||||||
"this_month": "This month",
|
"this_month": "This month",
|
||||||
"this_quarter": "This quarter",
|
"this_quarter": "This quarter",
|
||||||
"this_year": "This year",
|
"this_year": "This year",
|
||||||
@@ -2162,7 +2158,7 @@
|
|||||||
"advanced_styling_field_description_size": "Description Font Size",
|
"advanced_styling_field_description_size": "Description Font Size",
|
||||||
"advanced_styling_field_description_size_description": "Scales the description text.",
|
"advanced_styling_field_description_size_description": "Scales the description text.",
|
||||||
"advanced_styling_field_description_weight": "Description Font Weight",
|
"advanced_styling_field_description_weight": "Description Font Weight",
|
||||||
"advanced_styling_field_description_weight_description": "Makes descr. text lighter or bolder.",
|
"advanced_styling_field_description_weight_description": "Makes description text lighter or bolder.",
|
||||||
"advanced_styling_field_font_size": "Font Size",
|
"advanced_styling_field_font_size": "Font Size",
|
||||||
"advanced_styling_field_font_weight": "Font Weight",
|
"advanced_styling_field_font_weight": "Font Weight",
|
||||||
"advanced_styling_field_headline_color": "Headline Color",
|
"advanced_styling_field_headline_color": "Headline Color",
|
||||||
@@ -2176,7 +2172,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Colors the filled portion of the bar.",
|
"advanced_styling_field_indicator_bg_description": "Colors the filled portion of the bar.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Rounds the input corners.",
|
"advanced_styling_field_input_border_radius_description": "Rounds the input corners.",
|
||||||
"advanced_styling_field_input_font_size_description": "Scales the typed text in inputs.",
|
"advanced_styling_field_input_font_size_description": "Scales the typed text in inputs.",
|
||||||
"advanced_styling_field_input_height_description": "Controls the min. height of the input.",
|
"advanced_styling_field_input_height_description": "Controls the minimum height of the input field.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Adds space on the left and right.",
|
"advanced_styling_field_input_padding_x_description": "Adds space on the left and right.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Adds space on the top and bottom.",
|
"advanced_styling_field_input_padding_y_description": "Adds space on the top and bottom.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Fades the placeholder hint text.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Fades the placeholder hint text.",
|
||||||
@@ -2185,8 +2181,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Colors the typed text in inputs.",
|
"advanced_styling_field_input_text_description": "Colors the typed text in inputs.",
|
||||||
"advanced_styling_field_option_bg": "Background",
|
"advanced_styling_field_option_bg": "Background",
|
||||||
"advanced_styling_field_option_bg_description": "Fills the option items.",
|
"advanced_styling_field_option_bg_description": "Fills the option items.",
|
||||||
"advanced_styling_field_option_border": "Border Color",
|
|
||||||
"advanced_styling_field_option_border_description": "Outlines radio and checkbox options.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Rounds the option corners.",
|
"advanced_styling_field_option_border_radius_description": "Rounds the option corners.",
|
||||||
"advanced_styling_field_option_font_size_description": "Scales the option label text.",
|
"advanced_styling_field_option_font_size_description": "Scales the option label text.",
|
||||||
"advanced_styling_field_option_label": "Label Color",
|
"advanced_styling_field_option_label": "Label Color",
|
||||||
@@ -3006,9 +3000,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "No, thank you!",
|
"preview_survey_question_2_choice_2_label": "No, thank you!",
|
||||||
"preview_survey_question_2_headline": "Want to stay in the loop?",
|
"preview_survey_question_2_headline": "Want to stay in the loop?",
|
||||||
"preview_survey_question_2_subheader": "This is an example description.",
|
"preview_survey_question_2_subheader": "This is an example description.",
|
||||||
"preview_survey_question_open_text_headline": "Anything else you'd like to share?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Type your answer here…",
|
|
||||||
"preview_survey_question_open_text_subheader": "Your feedback helps us improve.",
|
|
||||||
"preview_survey_welcome_card_headline": "Welcome!",
|
"preview_survey_welcome_card_headline": "Welcome!",
|
||||||
"prioritize_features_description": "Identify features your users need most and least.",
|
"prioritize_features_description": "Identify features your users need most and least.",
|
||||||
"prioritize_features_name": "Prioritize Features",
|
"prioritize_features_name": "Prioritize Features",
|
||||||
|
|||||||
+15
-37
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Permitir",
|
"allow": "Permitir",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Permitir a los usuarios salir haciendo clic fuera de la encuesta",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Permitir a los usuarios salir haciendo clic fuera de la encuesta",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Se ha producido un error desconocido al eliminar {type}s",
|
"an_unknown_error_occurred_while_deleting_table_items": "Se ha producido un error desconocido al eliminar {type}s",
|
||||||
|
"analysis": "Análisis",
|
||||||
"and": "Y",
|
"and": "Y",
|
||||||
"and_response_limit_of": "y límite de respuesta de",
|
"and_response_limit_of": "y límite de respuesta de",
|
||||||
"anonymous": "Anónimo",
|
"anonymous": "Anónimo",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copy_code": "Copiar código",
|
"copy_code": "Copiar código",
|
||||||
"copy_link": "Copiar enlace",
|
"copy_link": "Copiar enlace",
|
||||||
"count_attributes": "{count, plural, one {{count} atributo} other {{count} atributos}}",
|
"count_attributes": "{value, plural, one {{value} atributo} other {{value} atributos}}",
|
||||||
"count_contacts": "{count, plural, one {{count} contacto} other {{count} contactos}}",
|
"count_contacts": "{value, plural, one {{value} contacto} other {{value} contactos}}",
|
||||||
"count_members": "{count, plural, one {{count} miembro} other {{count} miembros}}",
|
"count_responses": "{value, plural, one {{value} respuesta} other {{value} respuestas}}",
|
||||||
"count_responses": "{count, plural, one {{count} respuesta} other {{count} respuestas}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} selección} other {{count} selecciones}}",
|
|
||||||
"create_new_organization": "Crear organización nueva",
|
"create_new_organization": "Crear organización nueva",
|
||||||
"create_segment": "Crear segmento",
|
"create_segment": "Crear segmento",
|
||||||
"create_survey": "Crear encuesta",
|
"create_survey": "Crear encuesta",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "días",
|
"days": "días",
|
||||||
"default": "Predeterminado",
|
"default": "Predeterminado",
|
||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"delete_what": "Eliminar {deleteWhat}",
|
|
||||||
"description": "Descripción",
|
"description": "Descripción",
|
||||||
"dev_env": "Entorno de desarrollo",
|
"dev_env": "Entorno de desarrollo",
|
||||||
"development": "Desarrollo",
|
"development": "Desarrollo",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Error",
|
"error": "Error",
|
||||||
"error_component_description": "Este recurso no existe o no tienes los derechos necesarios para acceder a él.",
|
"error_component_description": "Este recurso no existe o no tienes los derechos necesarios para acceder a él.",
|
||||||
"error_component_title": "Error al cargar recursos",
|
"error_component_title": "Error al cargar recursos",
|
||||||
"error_loading_data": "Error al cargar los datos",
|
|
||||||
"error_rate_limit_description": "Número máximo de solicitudes alcanzado. Por favor, inténtalo de nuevo más tarde.",
|
"error_rate_limit_description": "Número máximo de solicitudes alcanzado. Por favor, inténtalo de nuevo más tarde.",
|
||||||
"error_rate_limit_title": "Límite de frecuencia excedido",
|
"error_rate_limit_title": "Límite de frecuencia excedido",
|
||||||
"expand_rows": "Expandir filas",
|
"expand_rows": "Expandir filas",
|
||||||
"failed_to_copy_to_clipboard": "Error al copiar al portapapeles",
|
"failed_to_copy_to_clipboard": "Error al copiar al portapapeles",
|
||||||
"failed_to_load_organizations": "Error al cargar organizaciones",
|
"failed_to_load_organizations": "Error al cargar organizaciones",
|
||||||
"failed_to_load_workspaces": "Error al cargar los proyectos",
|
"failed_to_load_workspaces": "Error al cargar los proyectos",
|
||||||
"filter": "Filtro",
|
|
||||||
"finish": "Finalizar",
|
"finish": "Finalizar",
|
||||||
"first_name": "Nombre",
|
|
||||||
"follow_these": "Sigue estos",
|
"follow_these": "Sigue estos",
|
||||||
"formbricks_version": "Versión de Formbricks",
|
"formbricks_version": "Versión de Formbricks",
|
||||||
"full_name": "Nombre completo",
|
"full_name": "Nombre completo",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Campo oculto",
|
"hidden_field": "Campo oculto",
|
||||||
"hidden_fields": "Campos ocultos",
|
"hidden_fields": "Campos ocultos",
|
||||||
"hide_column": "Ocultar columna",
|
"hide_column": "Ocultar columna",
|
||||||
"id": "ID",
|
|
||||||
"image": "Imagen",
|
"image": "Imagen",
|
||||||
"images": "Imágenes",
|
"images": "Imágenes",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Clave",
|
"key": "Clave",
|
||||||
"label": "Etiqueta",
|
"label": "Etiqueta",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"last_name": "Apellido",
|
|
||||||
"learn_more": "Saber más",
|
"learn_more": "Saber más",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Superposición clara",
|
"light_overlay": "Superposición clara",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Apariencia",
|
"look_and_feel": "Apariencia",
|
||||||
"manage": "Gestionar",
|
"manage": "Gestionar",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Miembro",
|
||||||
"members": "Miembros",
|
"members": "Miembros",
|
||||||
"members_and_teams": "Miembros y equipos",
|
"members_and_teams": "Miembros y equipos",
|
||||||
"membership_not_found": "Membresía no encontrada",
|
"membership_not_found": "Membresía no encontrada",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Mover hacia abajo",
|
"move_down": "Mover hacia abajo",
|
||||||
"move_up": "Mover hacia arriba",
|
"move_up": "Mover hacia arriba",
|
||||||
"multiple_languages": "Múltiples idiomas",
|
"multiple_languages": "Múltiples idiomas",
|
||||||
"my_product": "mi producto",
|
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
"new": "Nuevo",
|
"new": "Nuevo",
|
||||||
"new_version_available": "Formbricks {version} está aquí. ¡Actualiza ahora!",
|
"new_version_available": "Formbricks {version} está aquí. ¡Actualiza ahora!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Seleccionar equipos",
|
"select_teams": "Seleccionar equipos",
|
||||||
"selected": "Seleccionado",
|
"selected": "Seleccionado",
|
||||||
"selected_questions": "Preguntas seleccionadas",
|
"selected_questions": "Preguntas seleccionadas",
|
||||||
|
"selection": "Selección",
|
||||||
|
"selections": "Selecciones",
|
||||||
"send_test_email": "Enviar correo electrónico de prueba",
|
"send_test_email": "Enviar correo electrónico de prueba",
|
||||||
"session_not_found": "Sesión no encontrada",
|
"session_not_found": "Sesión no encontrada",
|
||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Superior derecha",
|
"top_right": "Superior derecha",
|
||||||
"try_again": "Intentar de nuevo",
|
"try_again": "Intentar de nuevo",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"unknown_survey": "Encuesta desconocida",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Desbloquea más proyectos con un plan superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Desbloquea más proyectos con un plan superior.",
|
||||||
"update": "Actualizar",
|
"update": "Actualizar",
|
||||||
"updated": "Actualizado",
|
"updated": "Actualizado",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Actualizar contactos",
|
"contacts_table_refresh": "Actualizar contactos",
|
||||||
"contacts_table_refresh_success": "Contactos actualizados correctamente",
|
"contacts_table_refresh_success": "Contactos actualizados correctamente",
|
||||||
"create_attribute": "Crear atributo",
|
"create_attribute": "Crear atributo",
|
||||||
|
"create_key": "Crear clave",
|
||||||
"create_new_attribute": "Crear atributo nuevo",
|
"create_new_attribute": "Crear atributo nuevo",
|
||||||
"create_new_attribute_description": "Crea un atributo nuevo para fines de segmentación.",
|
"create_new_attribute_description": "Crea un atributo nuevo para fines de segmentación.",
|
||||||
"custom_attributes": "Atributos personalizados",
|
"custom_attributes": "Atributos personalizados",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Esto eliminará el atributo seleccionado. Se perderán todos los datos de contacto asociados con este atributo.} other {Esto eliminará los atributos seleccionados. Se perderán todos los datos de contacto asociados con estos atributos.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Esto eliminará el atributo seleccionado. Se perderán todos los datos de contacto asociados con este atributo.} other {Esto eliminará los atributos seleccionados. Se perderán todos los datos de contacto asociados con estos atributos.}}",
|
||||||
"delete_contact_confirmation": "Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá.",
|
"delete_contact_confirmation": "Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá. Si este contacto tiene respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.} other {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con estos contactos. Cualquier segmentación y personalización basada en los datos de estos contactos se perderá. Si estos contactos tienen respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con este contacto. Cualquier segmentación y personalización basada en los datos de este contacto se perderá. Si este contacto tiene respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.} other {Esto eliminará todas las respuestas de encuestas y atributos de contacto asociados con estos contactos. Cualquier segmentación y personalización basada en los datos de estos contactos se perderá. Si estos contactos tienen respuestas que cuentan para las cuotas de encuesta, los recuentos de cuota se reducirán pero los límites de cuota permanecerán sin cambios.}}",
|
||||||
"displays": "Visualizaciones",
|
|
||||||
"edit_attribute": "Editar atributo",
|
"edit_attribute": "Editar atributo",
|
||||||
"edit_attribute_description": "Actualiza la etiqueta y la descripción de este atributo.",
|
"edit_attribute_description": "Actualiza la etiqueta y la descripción de este atributo.",
|
||||||
"edit_attribute_values": "Editar atributos",
|
"edit_attribute_values": "Editar atributos",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Nombre(s) de columna CSV no válido(s): {columns}. Los nombres de columna que se convertirán en nuevos atributos solo deben contener letras minúsculas, números y guiones bajos, y deben comenzar con una letra.",
|
"invalid_csv_column_names": "Nombre(s) de columna CSV no válido(s): {columns}. Los nombres de columna que se convertirán en nuevos atributos solo deben contener letras minúsculas, números y guiones bajos, y deben comenzar con una letra.",
|
||||||
"invalid_date_format": "Formato de fecha no válido. Por favor, usa una fecha válida.",
|
"invalid_date_format": "Formato de fecha no válido. Por favor, usa una fecha válida.",
|
||||||
"invalid_number_format": "Formato de número no válido. Por favor, introduce un número válido.",
|
"invalid_number_format": "Formato de número no válido. Por favor, introduce un número válido.",
|
||||||
"no_activity_yet": "Aún no hay actividad",
|
|
||||||
"no_published_link_surveys_available": "No hay encuestas de enlace publicadas disponibles. Por favor, publica primero una encuesta de enlace.",
|
"no_published_link_surveys_available": "No hay encuestas de enlace publicadas disponibles. Por favor, publica primero una encuesta de enlace.",
|
||||||
"no_published_surveys": "No hay encuestas publicadas",
|
"no_published_surveys": "No hay encuestas publicadas",
|
||||||
"no_responses_found": "No se encontraron respuestas",
|
"no_responses_found": "No se encontraron respuestas",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Selecciona una encuesta",
|
"select_a_survey": "Selecciona una encuesta",
|
||||||
"select_attribute": "Seleccionar atributo",
|
"select_attribute": "Seleccionar atributo",
|
||||||
"select_attribute_key": "Seleccionar clave de atributo",
|
"select_attribute_key": "Seleccionar clave de atributo",
|
||||||
"survey_viewed": "Encuesta vista",
|
|
||||||
"survey_viewed_at": "Vista el",
|
|
||||||
"system_attributes": "Atributos del sistema",
|
"system_attributes": "Atributos del sistema",
|
||||||
"unlock_contacts_description": "Gestiona contactos y envía encuestas dirigidas",
|
"unlock_contacts_description": "Gestiona contactos y envía encuestas dirigidas",
|
||||||
"unlock_contacts_title": "Desbloquea contactos con un plan superior",
|
"unlock_contacts_title": "Desbloquea contactos con un plan superior",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Vincular Google Sheet",
|
"link_google_sheet": "Vincular Google Sheet",
|
||||||
"link_new_sheet": "Vincular nueva hoja",
|
"link_new_sheet": "Vincular nueva hoja",
|
||||||
"no_integrations_yet": "Tus integraciones de Google Sheet aparecerán aquí tan pronto como las añadas. ⏲️",
|
"no_integrations_yet": "Tus integraciones de Google Sheet aparecerán aquí tan pronto como las añadas. ⏲️",
|
||||||
"reconnect_button": "Reconectar",
|
"spreadsheet_url": "URL de la hoja de cálculo"
|
||||||
"reconnect_button_description": "Tu conexión con Google Sheets ha caducado. Reconecta para continuar sincronizando respuestas. Tus enlaces de hojas de cálculo y datos existentes se conservarán.",
|
|
||||||
"reconnect_button_tooltip": "Reconecta la integración para actualizar tu acceso. Tus enlaces de hojas de cálculo y datos existentes se conservarán.",
|
|
||||||
"spreadsheet_permission_error": "No tienes permiso para acceder a esta hoja de cálculo. Asegúrate de que la hoja de cálculo esté compartida con tu cuenta de Google y de que tengas acceso de escritura a la hoja de cálculo.",
|
|
||||||
"spreadsheet_url": "URL de la hoja de cálculo",
|
|
||||||
"token_expired_error": "El token de actualización de Google Sheets ha caducado o ha sido revocado. Reconecta la integración."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Incluir fecha de creación",
|
"include_created_at": "Incluir fecha de creación",
|
||||||
"include_hidden_fields": "Incluir campos ocultos",
|
"include_hidden_fields": "Incluir campos ocultos",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Hola {userName}",
|
"email_customization_preview_email_heading": "Hola {userName}",
|
||||||
"email_customization_preview_email_text": "Este es un correo electrónico de vista previa para mostrarte qué logotipo se mostrará en los correos electrónicos.",
|
"email_customization_preview_email_text": "Este es un correo electrónico de vista previa para mostrarte qué logotipo se mostrará en los correos electrónicos.",
|
||||||
"error_deleting_organization_please_try_again": "Error al eliminar la organización. Por favor, inténtalo de nuevo.",
|
"error_deleting_organization_please_try_again": "Error al eliminar la organización. Por favor, inténtalo de nuevo.",
|
||||||
"from_your_organization": "{memberName} de tu organización",
|
"from_your_organization": "de tu organización",
|
||||||
"invitation_sent_once_more": "Invitación enviada una vez más.",
|
"invitation_sent_once_more": "Invitación enviada una vez más.",
|
||||||
"invite_deleted_successfully": "Invitación eliminada correctamente",
|
"invite_deleted_successfully": "Invitación eliminada correctamente",
|
||||||
"invite_expires_on": "La invitación expira el {date}",
|
"invite_expires_on": "La invitación expira el {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Añadir un marcador de posición para mostrar si no hay valor que recuperar.",
|
"add_fallback_placeholder": "Añadir un marcador de posición para mostrar si no hay valor que recuperar.",
|
||||||
"add_hidden_field_id": "Añadir ID de campo oculto",
|
"add_hidden_field_id": "Añadir ID de campo oculto",
|
||||||
"add_highlight_border": "Añadir borde destacado",
|
"add_highlight_border": "Añadir borde destacado",
|
||||||
"add_highlight_border_description": "Solo se aplica a encuestas dentro del producto.",
|
|
||||||
"add_logic": "Añadir lógica",
|
"add_logic": "Añadir lógica",
|
||||||
"add_none_of_the_above": "Añadir \"Ninguna de las anteriores\"",
|
"add_none_of_the_above": "Añadir \"Ninguna de las anteriores\"",
|
||||||
"add_option": "Añadir opción",
|
"add_option": "Añadir opción",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Seguimiento actualizado y se guardará cuando guardes la encuesta.",
|
"follow_ups_modal_updated_successfull_toast": "Seguimiento actualizado y se guardará cuando guardes la encuesta.",
|
||||||
"follow_ups_new": "Nuevo seguimiento",
|
"follow_ups_new": "Nuevo seguimiento",
|
||||||
"follow_ups_upgrade_button_text": "Actualiza para habilitar seguimientos",
|
"follow_ups_upgrade_button_text": "Actualiza para habilitar seguimientos",
|
||||||
|
"form_styling": "Estilo del formulario",
|
||||||
"formbricks_sdk_is_not_connected": "El SDK de Formbricks no está conectado",
|
"formbricks_sdk_is_not_connected": "El SDK de Formbricks no está conectado",
|
||||||
"four_points": "4 puntos",
|
"four_points": "4 puntos",
|
||||||
"heading": "Encabezado",
|
"heading": "Encabezado",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Límites de respuestas, redirecciones y más.",
|
"response_limits_redirections_and_more": "Límites de respuestas, redirecciones y más.",
|
||||||
"response_options": "Opciones de respuesta",
|
"response_options": "Opciones de respuesta",
|
||||||
"roundness": "Redondez",
|
"roundness": "Redondez",
|
||||||
"roundness_description": "Controla qué tan redondeadas están las esquinas.",
|
"roundness_description": "Controla qué tan redondeadas están las esquinas de la tarjeta.",
|
||||||
"row_used_in_logic_error": "Esta fila se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.",
|
"row_used_in_logic_error": "Esta fila se utiliza en la lógica de la pregunta {questionIndex}. Por favor, elimínala de la lógica primero.",
|
||||||
"rows": "Filas",
|
"rows": "Filas",
|
||||||
"save_and_close": "Guardar y cerrar",
|
"save_and_close": "Guardar y cerrar",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Esta encuesta gratuita y de código abierto ha sido cerrada",
|
"survey_completed_subheading": "Esta encuesta gratuita y de código abierto ha sido cerrada",
|
||||||
"survey_display_settings": "Ajustes de visualización de la encuesta",
|
"survey_display_settings": "Ajustes de visualización de la encuesta",
|
||||||
"survey_placement": "Ubicación de la encuesta",
|
"survey_placement": "Ubicación de la encuesta",
|
||||||
"survey_styling": "Estilo del formulario",
|
|
||||||
"survey_trigger": "Activador de la encuesta",
|
"survey_trigger": "Activador de la encuesta",
|
||||||
"switch_multi_language_on_to_get_started": "Activa el modo multiidioma para comenzar 👉",
|
"switch_multi_language_on_to_get_started": "Activa el modo multiidioma para comenzar 👉",
|
||||||
"target_block_not_found": "Bloque objetivo no encontrado",
|
"target_block_not_found": "Bloque objetivo no encontrado",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Mensaje de bienvenida",
|
"welcome_message": "Mensaje de bienvenida",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Sin un filtro, todos tus usuarios pueden ser encuestados.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Sin un filtro, todos tus usuarios pueden ser encuestados.",
|
||||||
"you_have_not_created_a_segment_yet": "Aún no has creado un segmento",
|
"you_have_not_created_a_segment_yet": "Aún no has creado un segmento",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Necesitas tener dos o más idiomas configurados en tu proyecto para trabajar con traducciones.",
|
||||||
"your_description_here_recall_information_with": "Tu descripción aquí. Recupera información con @",
|
"your_description_here_recall_information_with": "Tu descripción aquí. Recupera información con @",
|
||||||
"your_question_here_recall_information_with": "Tu pregunta aquí. Recupera información con @",
|
"your_question_here_recall_information_with": "Tu pregunta aquí. Recupera información con @",
|
||||||
"your_web_app": "Tu aplicación web",
|
"your_web_app": "Tu aplicación web",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Respuestas filtradas (Excel)",
|
"filtered_responses_excel": "Respuestas filtradas (Excel)",
|
||||||
"generating_qr_code": "Generando código QR",
|
"generating_qr_code": "Generando código QR",
|
||||||
"impressions": "Impresiones",
|
"impressions": "Impresiones",
|
||||||
"impressions_identified_only": "Solo se muestran impresiones de contactos identificados",
|
|
||||||
"impressions_tooltip": "Número de veces que se ha visto la encuesta.",
|
"impressions_tooltip": "Número de veces que se ha visto la encuesta.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "La encuesta se mostrará a los usuarios de tu sitio web que cumplan con los criterios enumerados a continuación",
|
"connection_description": "La encuesta se mostrará a los usuarios de tu sitio web que cumplan con los criterios enumerados a continuación",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Último trimestre",
|
"last_quarter": "Último trimestre",
|
||||||
"last_year": "Último año",
|
"last_year": "Último año",
|
||||||
"limit": "Límite",
|
"limit": "Límite",
|
||||||
"no_identified_impressions": "No hay impresiones de contactos identificados",
|
|
||||||
"no_responses_found": "No se han encontrado respuestas",
|
"no_responses_found": "No se han encontrado respuestas",
|
||||||
"other_values_found": "Otros valores encontrados",
|
"other_values_found": "Otros valores encontrados",
|
||||||
"overall": "General",
|
"overall": "General",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Inicios",
|
"starts": "Inicios",
|
||||||
"starts_tooltip": "Número de veces que se ha iniciado la encuesta.",
|
"starts_tooltip": "Número de veces que se ha iniciado la encuesta.",
|
||||||
"survey_reset_successfully": "¡Encuesta restablecida correctamente! Se eliminaron {responseCount} respuestas y {displayCount} visualizaciones.",
|
"survey_reset_successfully": "¡Encuesta restablecida correctamente! Se eliminaron {responseCount} respuestas y {displayCount} visualizaciones.",
|
||||||
"survey_results": "Resultados de {surveyName}",
|
|
||||||
"this_month": "Este mes",
|
"this_month": "Este mes",
|
||||||
"this_quarter": "Este trimestre",
|
"this_quarter": "Este trimestre",
|
||||||
"this_year": "Este año",
|
"this_year": "Este año",
|
||||||
@@ -2162,7 +2145,7 @@
|
|||||||
"advanced_styling_field_description_size": "Tamaño de fuente de la descripción",
|
"advanced_styling_field_description_size": "Tamaño de fuente de la descripción",
|
||||||
"advanced_styling_field_description_size_description": "Escala el texto de la descripción.",
|
"advanced_styling_field_description_size_description": "Escala el texto de la descripción.",
|
||||||
"advanced_styling_field_description_weight": "Grosor de fuente de la descripción",
|
"advanced_styling_field_description_weight": "Grosor de fuente de la descripción",
|
||||||
"advanced_styling_field_description_weight_description": "Hace el texto de descripción más ligero o más grueso.",
|
"advanced_styling_field_description_weight_description": "Hace el texto de la descripción más ligero o más grueso.",
|
||||||
"advanced_styling_field_font_size": "Tamaño de fuente",
|
"advanced_styling_field_font_size": "Tamaño de fuente",
|
||||||
"advanced_styling_field_font_weight": "Grosor de fuente",
|
"advanced_styling_field_font_weight": "Grosor de fuente",
|
||||||
"advanced_styling_field_headline_color": "Color del titular",
|
"advanced_styling_field_headline_color": "Color del titular",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Colorea la porción rellena de la barra.",
|
"advanced_styling_field_indicator_bg_description": "Colorea la porción rellena de la barra.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Redondea las esquinas del campo.",
|
"advanced_styling_field_input_border_radius_description": "Redondea las esquinas del campo.",
|
||||||
"advanced_styling_field_input_font_size_description": "Escala el texto escrito en los campos.",
|
"advanced_styling_field_input_font_size_description": "Escala el texto escrito en los campos.",
|
||||||
"advanced_styling_field_input_height_description": "Controla la altura mínima de la entrada.",
|
"advanced_styling_field_input_height_description": "Controla la altura mínima del campo de entrada.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Añade espacio a la izquierda y a la derecha.",
|
"advanced_styling_field_input_padding_x_description": "Añade espacio a la izquierda y a la derecha.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Añade espacio en la parte superior e inferior.",
|
"advanced_styling_field_input_padding_y_description": "Añade espacio en la parte superior e inferior.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Atenúa el texto de sugerencia del marcador de posición.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Atenúa el texto de sugerencia del marcador de posición.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Colorea el texto escrito en los campos de entrada.",
|
"advanced_styling_field_input_text_description": "Colorea el texto escrito en los campos de entrada.",
|
||||||
"advanced_styling_field_option_bg": "Fondo",
|
"advanced_styling_field_option_bg": "Fondo",
|
||||||
"advanced_styling_field_option_bg_description": "Rellena los elementos de opción.",
|
"advanced_styling_field_option_bg_description": "Rellena los elementos de opción.",
|
||||||
"advanced_styling_field_option_border": "Color del borde",
|
|
||||||
"advanced_styling_field_option_border_description": "Delimita las opciones de radio y casillas de verificación.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Redondea las esquinas de las opciones.",
|
"advanced_styling_field_option_border_radius_description": "Redondea las esquinas de las opciones.",
|
||||||
"advanced_styling_field_option_font_size_description": "Escala el texto de la etiqueta de opción.",
|
"advanced_styling_field_option_font_size_description": "Escala el texto de la etiqueta de opción.",
|
||||||
"advanced_styling_field_option_label": "Color de la etiqueta",
|
"advanced_styling_field_option_label": "Color de la etiqueta",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "¡No, gracias!",
|
"preview_survey_question_2_choice_2_label": "¡No, gracias!",
|
||||||
"preview_survey_question_2_headline": "¿Quieres estar al tanto?",
|
"preview_survey_question_2_headline": "¿Quieres estar al tanto?",
|
||||||
"preview_survey_question_2_subheader": "Esta es una descripción de ejemplo.",
|
"preview_survey_question_2_subheader": "Esta es una descripción de ejemplo.",
|
||||||
"preview_survey_question_open_text_headline": "¿Hay algo más que te gustaría compartir?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Escribe tu respuesta aquí...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Tus comentarios nos ayudan a mejorar.",
|
|
||||||
"preview_survey_welcome_card_headline": "¡Bienvenido!",
|
"preview_survey_welcome_card_headline": "¡Bienvenido!",
|
||||||
"prioritize_features_description": "Identifica las funciones que tus usuarios necesitan más y menos.",
|
"prioritize_features_description": "Identifica las funciones que tus usuarios necesitan más y menos.",
|
||||||
"prioritize_features_name": "Priorizar funciones",
|
"prioritize_features_name": "Priorizar funciones",
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Autoriser",
|
"allow": "Autoriser",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Permettre aux utilisateurs de quitter en cliquant hors de l'enquête",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Permettre aux utilisateurs de quitter en cliquant hors de l'enquête",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Une erreur inconnue est survenue lors de la suppression des {type}s",
|
"an_unknown_error_occurred_while_deleting_table_items": "Une erreur inconnue est survenue lors de la suppression des {type}s",
|
||||||
|
"analysis": "Analyse",
|
||||||
"and": "Et",
|
"and": "Et",
|
||||||
"and_response_limit_of": "et limite de réponse de",
|
"and_response_limit_of": "et limite de réponse de",
|
||||||
"anonymous": "Anonyme",
|
"anonymous": "Anonyme",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Copier",
|
"copy": "Copier",
|
||||||
"copy_code": "Copier le code",
|
"copy_code": "Copier le code",
|
||||||
"copy_link": "Copier le lien",
|
"copy_link": "Copier le lien",
|
||||||
"count_attributes": "{count, plural, one {{count} attribut} other {{count} attributs}}",
|
"count_attributes": "{value, plural, one {{value} attribut} other {{value} attributs}}",
|
||||||
"count_contacts": "{count, plural, one {# contact} other {# contacts} }",
|
"count_contacts": "{value, plural, one {# contact} other {# contacts} }",
|
||||||
"count_members": "{count, plural, one {{count} membre} other {{count} membres}}",
|
"count_responses": "{value, plural, other {# réponses}}",
|
||||||
"count_responses": "{count, plural, other {# réponses}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} sélection} other {{count} sélections}}",
|
|
||||||
"create_new_organization": "Créer une nouvelle organisation",
|
"create_new_organization": "Créer une nouvelle organisation",
|
||||||
"create_segment": "Créer un segment",
|
"create_segment": "Créer un segment",
|
||||||
"create_survey": "Créer un sondage",
|
"create_survey": "Créer un sondage",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "jours",
|
"days": "jours",
|
||||||
"default": "Par défaut",
|
"default": "Par défaut",
|
||||||
"delete": "Supprimer",
|
"delete": "Supprimer",
|
||||||
"delete_what": "Supprimer {deleteWhat}",
|
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
"dev_env": "Environnement de développement",
|
"dev_env": "Environnement de développement",
|
||||||
"development": "Développement",
|
"development": "Développement",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
"error_component_description": "Cette ressource n'existe pas ou vous n'avez pas les droits nécessaires pour y accéder.",
|
"error_component_description": "Cette ressource n'existe pas ou vous n'avez pas les droits nécessaires pour y accéder.",
|
||||||
"error_component_title": "Erreur de chargement des ressources",
|
"error_component_title": "Erreur de chargement des ressources",
|
||||||
"error_loading_data": "Erreur lors du chargement des données",
|
|
||||||
"error_rate_limit_description": "Nombre maximal de demandes atteint. Veuillez réessayer plus tard.",
|
"error_rate_limit_description": "Nombre maximal de demandes atteint. Veuillez réessayer plus tard.",
|
||||||
"error_rate_limit_title": "Limite de Taux Dépassée",
|
"error_rate_limit_title": "Limite de Taux Dépassée",
|
||||||
"expand_rows": "Développer les lignes",
|
"expand_rows": "Développer les lignes",
|
||||||
"failed_to_copy_to_clipboard": "Échec de la copie dans le presse-papiers",
|
"failed_to_copy_to_clipboard": "Échec de la copie dans le presse-papiers",
|
||||||
"failed_to_load_organizations": "Échec du chargement des organisations",
|
"failed_to_load_organizations": "Échec du chargement des organisations",
|
||||||
"failed_to_load_workspaces": "Échec du chargement des projets",
|
"failed_to_load_workspaces": "Échec du chargement des projets",
|
||||||
"filter": "Filtre",
|
|
||||||
"finish": "Terminer",
|
"finish": "Terminer",
|
||||||
"first_name": "Prénom",
|
|
||||||
"follow_these": "Suivez ceci",
|
"follow_these": "Suivez ceci",
|
||||||
"formbricks_version": "Version de Formbricks",
|
"formbricks_version": "Version de Formbricks",
|
||||||
"full_name": "Nom complet",
|
"full_name": "Nom complet",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Champ caché",
|
"hidden_field": "Champ caché",
|
||||||
"hidden_fields": "Champs cachés",
|
"hidden_fields": "Champs cachés",
|
||||||
"hide_column": "Cacher la colonne",
|
"hide_column": "Cacher la colonne",
|
||||||
"id": "ID",
|
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"images": "Images",
|
"images": "Images",
|
||||||
"import": "Importer",
|
"import": "Importer",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Clé",
|
"key": "Clé",
|
||||||
"label": "Étiquette",
|
"label": "Étiquette",
|
||||||
"language": "Langue",
|
"language": "Langue",
|
||||||
"last_name": "Nom de famille",
|
|
||||||
"learn_more": "En savoir plus",
|
"learn_more": "En savoir plus",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Claire",
|
"light_overlay": "Claire",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Apparence",
|
"look_and_feel": "Apparence",
|
||||||
"manage": "Gérer",
|
"manage": "Gérer",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Membre",
|
||||||
"members": "Membres",
|
"members": "Membres",
|
||||||
"members_and_teams": "Membres & Équipes",
|
"members_and_teams": "Membres & Équipes",
|
||||||
"membership_not_found": "Abonnement non trouvé",
|
"membership_not_found": "Abonnement non trouvé",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Déplacer vers le bas",
|
"move_down": "Déplacer vers le bas",
|
||||||
"move_up": "Déplacer vers le haut",
|
"move_up": "Déplacer vers le haut",
|
||||||
"multiple_languages": "Plusieurs langues",
|
"multiple_languages": "Plusieurs langues",
|
||||||
"my_product": "mon produit",
|
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"new": "Nouveau",
|
"new": "Nouveau",
|
||||||
"new_version_available": "Formbricks {version} est là. Mettez à jour maintenant !",
|
"new_version_available": "Formbricks {version} est là. Mettez à jour maintenant !",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Sélectionner les équipes",
|
"select_teams": "Sélectionner les équipes",
|
||||||
"selected": "Sélectionné",
|
"selected": "Sélectionné",
|
||||||
"selected_questions": "Questions sélectionnées",
|
"selected_questions": "Questions sélectionnées",
|
||||||
|
"selection": "Sélection",
|
||||||
|
"selections": "Sélections",
|
||||||
"send_test_email": "Envoyer un e-mail de test",
|
"send_test_email": "Envoyer un e-mail de test",
|
||||||
"session_not_found": "Session non trouvée",
|
"session_not_found": "Session non trouvée",
|
||||||
"settings": "Paramètres",
|
"settings": "Paramètres",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "En haut à droite",
|
"top_right": "En haut à droite",
|
||||||
"try_again": "Réessayer",
|
"try_again": "Réessayer",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unknown_survey": "Enquête inconnue",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Débloquez plus de projets avec un forfait supérieur.",
|
"unlock_more_workspaces_with_a_higher_plan": "Débloquez plus de projets avec un forfait supérieur.",
|
||||||
"update": "Mise à jour",
|
"update": "Mise à jour",
|
||||||
"updated": "Mise à jour",
|
"updated": "Mise à jour",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Actualiser les contacts",
|
"contacts_table_refresh": "Actualiser les contacts",
|
||||||
"contacts_table_refresh_success": "Contacts rafraîchis avec succès",
|
"contacts_table_refresh_success": "Contacts rafraîchis avec succès",
|
||||||
"create_attribute": "Créer un attribut",
|
"create_attribute": "Créer un attribut",
|
||||||
|
"create_key": "Créer une clé",
|
||||||
"create_new_attribute": "Créer un nouvel attribut",
|
"create_new_attribute": "Créer un nouvel attribut",
|
||||||
"create_new_attribute_description": "Créez un nouvel attribut à des fins de segmentation.",
|
"create_new_attribute_description": "Créez un nouvel attribut à des fins de segmentation.",
|
||||||
"custom_attributes": "Attributs personnalisés",
|
"custom_attributes": "Attributs personnalisés",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Cela supprimera l'attribut sélectionné. Toutes les données de contact associées à cet attribut seront perdues.} other {Cela supprimera les attributs sélectionnés. Toutes les données de contact associées à ces attributs seront perdues.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Cela supprimera l'attribut sélectionné. Toutes les données de contact associées à cet attribut seront perdues.} other {Cela supprimera les attributs sélectionnés. Toutes les données de contact associées à ces attributs seront perdues.}}",
|
||||||
"delete_contact_confirmation": "Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus.",
|
"delete_contact_confirmation": "Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, other {Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus. Si ce contact a des réponses qui comptent dans les quotas de l'enquête, les comptes de quotas seront réduits mais les limites de quota resteront inchangées.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, other {Cela supprimera toutes les réponses aux enquêtes et les attributs de contact associés à ce contact. Toute la personnalisation et le ciblage basés sur les données de ce contact seront perdus. Si ce contact a des réponses qui comptent dans les quotas de l'enquête, les comptes de quotas seront réduits mais les limites de quota resteront inchangées.}}",
|
||||||
"displays": "Affichages",
|
|
||||||
"edit_attribute": "Modifier l'attribut",
|
"edit_attribute": "Modifier l'attribut",
|
||||||
"edit_attribute_description": "Mettez à jour l'étiquette et la description de cet attribut.",
|
"edit_attribute_description": "Mettez à jour l'étiquette et la description de cet attribut.",
|
||||||
"edit_attribute_values": "Modifier les attributs",
|
"edit_attribute_values": "Modifier les attributs",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Nom(s) de colonne CSV invalide(s) : {columns}. Les noms de colonnes qui deviendront de nouveaux attributs ne doivent contenir que des lettres minuscules, des chiffres et des underscores, et doivent commencer par une lettre.",
|
"invalid_csv_column_names": "Nom(s) de colonne CSV invalide(s) : {columns}. Les noms de colonnes qui deviendront de nouveaux attributs ne doivent contenir que des lettres minuscules, des chiffres et des underscores, et doivent commencer par une lettre.",
|
||||||
"invalid_date_format": "Format de date invalide. Merci d'utiliser une date valide.",
|
"invalid_date_format": "Format de date invalide. Merci d'utiliser une date valide.",
|
||||||
"invalid_number_format": "Format de nombre invalide. Veuillez saisir un nombre valide.",
|
"invalid_number_format": "Format de nombre invalide. Veuillez saisir un nombre valide.",
|
||||||
"no_activity_yet": "Aucune activité pour le moment",
|
|
||||||
"no_published_link_surveys_available": "Aucune enquête par lien publiée n'est disponible. Veuillez d'abord publier une enquête par lien.",
|
"no_published_link_surveys_available": "Aucune enquête par lien publiée n'est disponible. Veuillez d'abord publier une enquête par lien.",
|
||||||
"no_published_surveys": "Aucune enquête publiée",
|
"no_published_surveys": "Aucune enquête publiée",
|
||||||
"no_responses_found": "Aucune réponse trouvée",
|
"no_responses_found": "Aucune réponse trouvée",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Sélectionner une enquête",
|
"select_a_survey": "Sélectionner une enquête",
|
||||||
"select_attribute": "Sélectionner un attribut",
|
"select_attribute": "Sélectionner un attribut",
|
||||||
"select_attribute_key": "Sélectionner une clé d'attribut",
|
"select_attribute_key": "Sélectionner une clé d'attribut",
|
||||||
"survey_viewed": "Enquête consultée",
|
|
||||||
"survey_viewed_at": "Consultée le",
|
|
||||||
"system_attributes": "Attributs système",
|
"system_attributes": "Attributs système",
|
||||||
"unlock_contacts_description": "Gérer les contacts et envoyer des enquêtes ciblées",
|
"unlock_contacts_description": "Gérer les contacts et envoyer des enquêtes ciblées",
|
||||||
"unlock_contacts_title": "Débloquez des contacts avec un plan supérieur.",
|
"unlock_contacts_title": "Débloquez des contacts avec un plan supérieur.",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Lien Google Sheet",
|
"link_google_sheet": "Lien Google Sheet",
|
||||||
"link_new_sheet": "Lier une nouvelle feuille",
|
"link_new_sheet": "Lier une nouvelle feuille",
|
||||||
"no_integrations_yet": "Vos intégrations Google Sheets apparaîtront ici dès que vous les ajouterez. ⏲️",
|
"no_integrations_yet": "Vos intégrations Google Sheets apparaîtront ici dès que vous les ajouterez. ⏲️",
|
||||||
"reconnect_button": "Reconnecter",
|
"spreadsheet_url": "URL de la feuille de calcul"
|
||||||
"reconnect_button_description": "Votre connexion Google Sheets a expiré. Veuillez vous reconnecter pour continuer à synchroniser les réponses. Vos liens de feuilles de calcul et données existants seront préservés.",
|
|
||||||
"reconnect_button_tooltip": "Reconnectez l'intégration pour actualiser votre accès. Vos liens de feuilles de calcul et données existants seront préservés.",
|
|
||||||
"spreadsheet_permission_error": "Vous n'avez pas la permission d'accéder à cette feuille de calcul. Veuillez vous assurer que la feuille de calcul est partagée avec votre compte Google et que vous disposez d'un accès en écriture.",
|
|
||||||
"spreadsheet_url": "URL de la feuille de calcul",
|
|
||||||
"token_expired_error": "Le jeton d'actualisation Google Sheets a expiré ou a été révoqué. Veuillez reconnecter l'intégration."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Inclure la date de création",
|
"include_created_at": "Inclure la date de création",
|
||||||
"include_hidden_fields": "Inclure les champs cachés",
|
"include_hidden_fields": "Inclure les champs cachés",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Salut {userName}",
|
"email_customization_preview_email_heading": "Salut {userName}",
|
||||||
"email_customization_preview_email_text": "Cette est une prévisualisation d'e-mail pour vous montrer quel logo sera rendu dans les e-mails.",
|
"email_customization_preview_email_text": "Cette est une prévisualisation d'e-mail pour vous montrer quel logo sera rendu dans les e-mails.",
|
||||||
"error_deleting_organization_please_try_again": "Erreur lors de la suppression de l'organisation. Veuillez réessayer.",
|
"error_deleting_organization_please_try_again": "Erreur lors de la suppression de l'organisation. Veuillez réessayer.",
|
||||||
"from_your_organization": "{memberName} de votre organisation",
|
"from_your_organization": "de votre organisation",
|
||||||
"invitation_sent_once_more": "Invitation envoyée une fois de plus.",
|
"invitation_sent_once_more": "Invitation envoyée une fois de plus.",
|
||||||
"invite_deleted_successfully": "Invitation supprimée avec succès",
|
"invite_deleted_successfully": "Invitation supprimée avec succès",
|
||||||
"invite_expires_on": "L'invitation expire le {date}",
|
"invite_expires_on": "L'invitation expire le {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Ajouter un espace réservé à afficher s'il n'y a pas de valeur à rappeler.",
|
"add_fallback_placeholder": "Ajouter un espace réservé à afficher s'il n'y a pas de valeur à rappeler.",
|
||||||
"add_hidden_field_id": "Ajouter un champ caché ID",
|
"add_hidden_field_id": "Ajouter un champ caché ID",
|
||||||
"add_highlight_border": "Ajouter une bordure de surlignage",
|
"add_highlight_border": "Ajouter une bordure de surlignage",
|
||||||
"add_highlight_border_description": "S'applique uniquement aux sondages intégrés au produit.",
|
|
||||||
"add_logic": "Ajouter de la logique",
|
"add_logic": "Ajouter de la logique",
|
||||||
"add_none_of_the_above": "Ajouter \"Aucun des éléments ci-dessus\"",
|
"add_none_of_the_above": "Ajouter \"Aucun des éléments ci-dessus\"",
|
||||||
"add_option": "Ajouter une option",
|
"add_option": "Ajouter une option",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "\"Suivi mis à jour et sera enregistré une fois que vous sauvegarderez le sondage.\"",
|
"follow_ups_modal_updated_successfull_toast": "\"Suivi mis à jour et sera enregistré une fois que vous sauvegarderez le sondage.\"",
|
||||||
"follow_ups_new": "Nouveau suivi",
|
"follow_ups_new": "Nouveau suivi",
|
||||||
"follow_ups_upgrade_button_text": "Passez à la version supérieure pour activer les relances",
|
"follow_ups_upgrade_button_text": "Passez à la version supérieure pour activer les relances",
|
||||||
|
"form_styling": "Style de formulaire",
|
||||||
"formbricks_sdk_is_not_connected": "Le SDK Formbricks n'est pas connecté",
|
"formbricks_sdk_is_not_connected": "Le SDK Formbricks n'est pas connecté",
|
||||||
"four_points": "4 points",
|
"four_points": "4 points",
|
||||||
"heading": "En-tête",
|
"heading": "En-tête",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Limites de réponse, redirections et plus.",
|
"response_limits_redirections_and_more": "Limites de réponse, redirections et plus.",
|
||||||
"response_options": "Options de réponse",
|
"response_options": "Options de réponse",
|
||||||
"roundness": "Rondeur",
|
"roundness": "Rondeur",
|
||||||
"roundness_description": "Contrôle l'arrondi des coins.",
|
"roundness_description": "Contrôle l'arrondi des coins de la carte.",
|
||||||
"row_used_in_logic_error": "Cette ligne est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.",
|
"row_used_in_logic_error": "Cette ligne est utilisée dans la logique de la question {questionIndex}. Veuillez d'abord la supprimer de la logique.",
|
||||||
"rows": "Lignes",
|
"rows": "Lignes",
|
||||||
"save_and_close": "Enregistrer et fermer",
|
"save_and_close": "Enregistrer et fermer",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Cette enquête gratuite et open-source a été fermée",
|
"survey_completed_subheading": "Cette enquête gratuite et open-source a été fermée",
|
||||||
"survey_display_settings": "Paramètres d'affichage de l'enquête",
|
"survey_display_settings": "Paramètres d'affichage de l'enquête",
|
||||||
"survey_placement": "Placement de l'enquête",
|
"survey_placement": "Placement de l'enquête",
|
||||||
"survey_styling": "Style de formulaire",
|
|
||||||
"survey_trigger": "Déclencheur d'enquête",
|
"survey_trigger": "Déclencheur d'enquête",
|
||||||
"switch_multi_language_on_to_get_started": "Activez le mode multilingue pour commencer 👉",
|
"switch_multi_language_on_to_get_started": "Activez le mode multilingue pour commencer 👉",
|
||||||
"target_block_not_found": "Bloc cible non trouvé",
|
"target_block_not_found": "Bloc cible non trouvé",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Message de bienvenue",
|
"welcome_message": "Message de bienvenue",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Sans filtre, tous vos utilisateurs peuvent être sondés.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Sans filtre, tous vos utilisateurs peuvent être sondés.",
|
||||||
"you_have_not_created_a_segment_yet": "Tu n'as pas encore créé de segment.",
|
"you_have_not_created_a_segment_yet": "Tu n'as pas encore créé de segment.",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Vous devez avoir deux langues ou plus configurées dans votre espace de travail pour travailler avec les traductions.",
|
||||||
"your_description_here_recall_information_with": "Votre description ici. Rappelez-vous des informations avec @",
|
"your_description_here_recall_information_with": "Votre description ici. Rappelez-vous des informations avec @",
|
||||||
"your_question_here_recall_information_with": "Votre question ici. Rappelez-vous des informations avec @",
|
"your_question_here_recall_information_with": "Votre question ici. Rappelez-vous des informations avec @",
|
||||||
"your_web_app": "Votre application web",
|
"your_web_app": "Votre application web",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Réponses filtrées (Excel)",
|
"filtered_responses_excel": "Réponses filtrées (Excel)",
|
||||||
"generating_qr_code": "Génération du code QR",
|
"generating_qr_code": "Génération du code QR",
|
||||||
"impressions": "Impressions",
|
"impressions": "Impressions",
|
||||||
"impressions_identified_only": "Affichage uniquement des impressions des contacts identifiés",
|
|
||||||
"impressions_tooltip": "Nombre de fois que l'enquête a été consultée.",
|
"impressions_tooltip": "Nombre de fois que l'enquête a été consultée.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "Le sondage sera affiché aux utilisateurs de votre site web, qui correspondent aux critères listés ci-dessous",
|
"connection_description": "Le sondage sera affiché aux utilisateurs de votre site web, qui correspondent aux critères listés ci-dessous",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "dernier trimestre",
|
"last_quarter": "dernier trimestre",
|
||||||
"last_year": "l'année dernière",
|
"last_year": "l'année dernière",
|
||||||
"limit": "Limite",
|
"limit": "Limite",
|
||||||
"no_identified_impressions": "Aucune impression des contacts identifiés",
|
|
||||||
"no_responses_found": "Aucune réponse trouvée",
|
"no_responses_found": "Aucune réponse trouvée",
|
||||||
"other_values_found": "D'autres valeurs trouvées",
|
"other_values_found": "D'autres valeurs trouvées",
|
||||||
"overall": "Globalement",
|
"overall": "Globalement",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Commence",
|
"starts": "Commence",
|
||||||
"starts_tooltip": "Nombre de fois que l'enquête a été commencée.",
|
"starts_tooltip": "Nombre de fois que l'enquête a été commencée.",
|
||||||
"survey_reset_successfully": "Réinitialisation du sondage réussie ! {responseCount} réponses et {displayCount} affichages ont été supprimés.",
|
"survey_reset_successfully": "Réinitialisation du sondage réussie ! {responseCount} réponses et {displayCount} affichages ont été supprimés.",
|
||||||
"survey_results": "Résultats de {surveyName}",
|
|
||||||
"this_month": "Ce mois-ci",
|
"this_month": "Ce mois-ci",
|
||||||
"this_quarter": "Ce trimestre",
|
"this_quarter": "Ce trimestre",
|
||||||
"this_year": "Cette année",
|
"this_year": "Cette année",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Colore la partie remplie de la barre.",
|
"advanced_styling_field_indicator_bg_description": "Colore la partie remplie de la barre.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Arrondit les coins du champ de saisie.",
|
"advanced_styling_field_input_border_radius_description": "Arrondit les coins du champ de saisie.",
|
||||||
"advanced_styling_field_input_font_size_description": "Ajuste la taille du texte saisi dans les champs.",
|
"advanced_styling_field_input_font_size_description": "Ajuste la taille du texte saisi dans les champs.",
|
||||||
"advanced_styling_field_input_height_description": "Contrôle la hauteur min. du champ de saisie.",
|
"advanced_styling_field_input_height_description": "Contrôle la hauteur minimale du champ de saisie.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Ajoute de l'espace à gauche et à droite.",
|
"advanced_styling_field_input_padding_x_description": "Ajoute de l'espace à gauche et à droite.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Ajoute de l'espace en haut et en bas.",
|
"advanced_styling_field_input_padding_y_description": "Ajoute de l'espace en haut et en bas.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Atténue le texte d'indication du placeholder.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Atténue le texte d'indication du placeholder.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Colore le texte saisi dans les champs.",
|
"advanced_styling_field_input_text_description": "Colore le texte saisi dans les champs.",
|
||||||
"advanced_styling_field_option_bg": "Arrière-plan",
|
"advanced_styling_field_option_bg": "Arrière-plan",
|
||||||
"advanced_styling_field_option_bg_description": "Remplit les éléments d'option.",
|
"advanced_styling_field_option_bg_description": "Remplit les éléments d'option.",
|
||||||
"advanced_styling_field_option_border": "Couleur de bordure",
|
|
||||||
"advanced_styling_field_option_border_description": "Contours des options de boutons radio et de cases à cocher.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Arrondit les coins des options.",
|
"advanced_styling_field_option_border_radius_description": "Arrondit les coins des options.",
|
||||||
"advanced_styling_field_option_font_size_description": "Ajuste la taille du texte des libellés d'option.",
|
"advanced_styling_field_option_font_size_description": "Ajuste la taille du texte des libellés d'option.",
|
||||||
"advanced_styling_field_option_label": "Couleur de l'étiquette",
|
"advanced_styling_field_option_label": "Couleur de l'étiquette",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Non, merci !",
|
"preview_survey_question_2_choice_2_label": "Non, merci !",
|
||||||
"preview_survey_question_2_headline": "Souhaitez-vous être informé ?",
|
"preview_survey_question_2_headline": "Souhaitez-vous être informé ?",
|
||||||
"preview_survey_question_2_subheader": "Ceci est un exemple de description.",
|
"preview_survey_question_2_subheader": "Ceci est un exemple de description.",
|
||||||
"preview_survey_question_open_text_headline": "Autre chose que vous aimeriez partager ?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Entrez votre réponse ici...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Vos commentaires nous aident à nous améliorer.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bienvenue !",
|
"preview_survey_welcome_card_headline": "Bienvenue !",
|
||||||
"prioritize_features_description": "Identifiez les fonctionnalités dont vos utilisateurs ont le plus et le moins besoin.",
|
"prioritize_features_description": "Identifiez les fonctionnalités dont vos utilisateurs ont le plus et le moins besoin.",
|
||||||
"prioritize_features_name": "Prioriser les fonctionnalités",
|
"prioritize_features_name": "Prioriser les fonctionnalités",
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Engedélyezés",
|
"allow": "Engedélyezés",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Lehetővé tétel a felhasználók számára, hogy a kérdőíven kívülre kattintva kilépjenek",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Lehetővé tétel a felhasználók számára, hogy a kérdőíven kívülre kattintva kilépjenek",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "{type} típusok törlésekor ismeretlen hiba történt",
|
"an_unknown_error_occurred_while_deleting_table_items": "{type} típusok törlésekor ismeretlen hiba történt",
|
||||||
|
"analysis": "Elemzés",
|
||||||
"and": "És",
|
"and": "És",
|
||||||
"and_response_limit_of": "és kérdéskorlátja ennek:",
|
"and_response_limit_of": "és kérdéskorlátja ennek:",
|
||||||
"anonymous": "Névtelen",
|
"anonymous": "Névtelen",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Másolás",
|
"copy": "Másolás",
|
||||||
"copy_code": "Kód másolása",
|
"copy_code": "Kód másolása",
|
||||||
"copy_link": "Hivatkozás másolása",
|
"copy_link": "Hivatkozás másolása",
|
||||||
"count_attributes": "{count, plural, one {{count} attribútum} other {{count} attribútum}}",
|
"count_attributes": "{value, plural, one {{value} attribútum} other {{value} attribútum}}",
|
||||||
"count_contacts": "{count, plural, one {{count} partner} other {{count} partner}}",
|
"count_contacts": "{value, plural, one {{value} partner} other {{value} partner}}",
|
||||||
"count_members": "{count, plural, one {{count} tag} other {{count} tag}}",
|
"count_responses": "{value, plural, one {{value} válasz} other {{value} válasz}}",
|
||||||
"count_responses": "{count, plural, one {{count} válasz} other {{count} válasz}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} kijelölés} other {{count} kijelölés}}",
|
|
||||||
"create_new_organization": "Új szervezet létrehozása",
|
"create_new_organization": "Új szervezet létrehozása",
|
||||||
"create_segment": "Szakasz létrehozása",
|
"create_segment": "Szakasz létrehozása",
|
||||||
"create_survey": "Kérdőív létrehozása",
|
"create_survey": "Kérdőív létrehozása",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "napok",
|
"days": "napok",
|
||||||
"default": "Alapértelmezett",
|
"default": "Alapértelmezett",
|
||||||
"delete": "Törlés",
|
"delete": "Törlés",
|
||||||
"delete_what": "{deleteWhat} törlése",
|
|
||||||
"description": "Leírás",
|
"description": "Leírás",
|
||||||
"dev_env": "Fejlesztői környezet",
|
"dev_env": "Fejlesztői környezet",
|
||||||
"development": "Fejlesztés",
|
"development": "Fejlesztés",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Hiba",
|
"error": "Hiba",
|
||||||
"error_component_description": "Ez az erőforrás nem létezik, vagy nem rendelkezik a hozzáféréshez szükséges jogosultságokkal.",
|
"error_component_description": "Ez az erőforrás nem létezik, vagy nem rendelkezik a hozzáféréshez szükséges jogosultságokkal.",
|
||||||
"error_component_title": "Hiba az erőforrások betöltésekor",
|
"error_component_title": "Hiba az erőforrások betöltésekor",
|
||||||
"error_loading_data": "Hiba az adatok betöltése során",
|
|
||||||
"error_rate_limit_description": "A kérések legnagyobb száma elérve. Próbálja meg később újra.",
|
"error_rate_limit_description": "A kérések legnagyobb száma elérve. Próbálja meg később újra.",
|
||||||
"error_rate_limit_title": "A sebességkorlát elérve",
|
"error_rate_limit_title": "A sebességkorlát elérve",
|
||||||
"expand_rows": "Sorok kinyitása",
|
"expand_rows": "Sorok kinyitása",
|
||||||
"failed_to_copy_to_clipboard": "Nem sikerült másolni a vágólapra",
|
"failed_to_copy_to_clipboard": "Nem sikerült másolni a vágólapra",
|
||||||
"failed_to_load_organizations": "Nem sikerült betölteni a szervezeteket",
|
"failed_to_load_organizations": "Nem sikerült betölteni a szervezeteket",
|
||||||
"failed_to_load_workspaces": "Nem sikerült a munkaterületek betöltése",
|
"failed_to_load_workspaces": "Nem sikerült a munkaterületek betöltése",
|
||||||
"filter": "Szűrő",
|
|
||||||
"finish": "Befejezés",
|
"finish": "Befejezés",
|
||||||
"first_name": "Keresztnév",
|
|
||||||
"follow_these": "Ezek követése",
|
"follow_these": "Ezek követése",
|
||||||
"formbricks_version": "Formbricks verziója",
|
"formbricks_version": "Formbricks verziója",
|
||||||
"full_name": "Teljes név",
|
"full_name": "Teljes név",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Rejtett mező",
|
"hidden_field": "Rejtett mező",
|
||||||
"hidden_fields": "Rejtett mezők",
|
"hidden_fields": "Rejtett mezők",
|
||||||
"hide_column": "Oszlop elrejtése",
|
"hide_column": "Oszlop elrejtése",
|
||||||
"id": "ID",
|
|
||||||
"image": "Kép",
|
"image": "Kép",
|
||||||
"images": "Képek",
|
"images": "Képek",
|
||||||
"import": "Importálás",
|
"import": "Importálás",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Kulcs",
|
"key": "Kulcs",
|
||||||
"label": "Címke",
|
"label": "Címke",
|
||||||
"language": "Nyelv",
|
"language": "Nyelv",
|
||||||
"last_name": "Vezetéknév",
|
|
||||||
"learn_more": "Tudjon meg többet",
|
"learn_more": "Tudjon meg többet",
|
||||||
"license_expired": "A licenc lejárt",
|
"license_expired": "A licenc lejárt",
|
||||||
"light_overlay": "Világos rávetítés",
|
"light_overlay": "Világos rávetítés",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Megjelenés",
|
"look_and_feel": "Megjelenés",
|
||||||
"manage": "Kezelés",
|
"manage": "Kezelés",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Tag",
|
||||||
"members": "Tagok",
|
"members": "Tagok",
|
||||||
"members_and_teams": "Tagok és csapatok",
|
"members_and_teams": "Tagok és csapatok",
|
||||||
"membership_not_found": "A tagság nem található",
|
"membership_not_found": "A tagság nem található",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Mozgatás le",
|
"move_down": "Mozgatás le",
|
||||||
"move_up": "Mozgatás fel",
|
"move_up": "Mozgatás fel",
|
||||||
"multiple_languages": "Több nyelv",
|
"multiple_languages": "Több nyelv",
|
||||||
"my_product": "saját termék",
|
|
||||||
"name": "Név",
|
"name": "Név",
|
||||||
"new": "Új",
|
"new": "Új",
|
||||||
"new_version_available": "A Formbricks {version} megérkezett. Frissítsen most!",
|
"new_version_available": "A Formbricks {version} megérkezett. Frissítsen most!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Csapatok kiválasztása",
|
"select_teams": "Csapatok kiválasztása",
|
||||||
"selected": "Kiválasztva",
|
"selected": "Kiválasztva",
|
||||||
"selected_questions": "Kiválasztott kérdések",
|
"selected_questions": "Kiválasztott kérdések",
|
||||||
|
"selection": "Kiválasztás",
|
||||||
|
"selections": "Kiválasztások",
|
||||||
"send_test_email": "Teszt e-mail küldése",
|
"send_test_email": "Teszt e-mail küldése",
|
||||||
"session_not_found": "A munkamenet nem található",
|
"session_not_found": "A munkamenet nem található",
|
||||||
"settings": "Beállítások",
|
"settings": "Beállítások",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Jobbra fent",
|
"top_right": "Jobbra fent",
|
||||||
"try_again": "Próbálja újra",
|
"try_again": "Próbálja újra",
|
||||||
"type": "Típus",
|
"type": "Típus",
|
||||||
"unknown_survey": "Ismeretlen kérdőív",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Több munkaterület feloldása egy magasabb csomaggal.",
|
"unlock_more_workspaces_with_a_higher_plan": "Több munkaterület feloldása egy magasabb csomaggal.",
|
||||||
"update": "Frissítés",
|
"update": "Frissítés",
|
||||||
"updated": "Frissítve",
|
"updated": "Frissítve",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Partnerek frissítése",
|
"contacts_table_refresh": "Partnerek frissítése",
|
||||||
"contacts_table_refresh_success": "A partnerek sikeresen frissítve",
|
"contacts_table_refresh_success": "A partnerek sikeresen frissítve",
|
||||||
"create_attribute": "Attribútum létrehozása",
|
"create_attribute": "Attribútum létrehozása",
|
||||||
|
"create_key": "Kulcs létrehozása",
|
||||||
"create_new_attribute": "Új attribútum létrehozása",
|
"create_new_attribute": "Új attribútum létrehozása",
|
||||||
"create_new_attribute_description": "Új attribútum létrehozása szakaszolási célokhoz.",
|
"create_new_attribute_description": "Új attribútum létrehozása szakaszolási célokhoz.",
|
||||||
"custom_attributes": "Egyéni attribútumok",
|
"custom_attributes": "Egyéni attribútumok",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Ez törölni fogja a kiválasztott attribútumot. Az ehhez az attribútumhoz hozzárendelt összes partneradat el fog veszni.} other {Ez törölni fogja a kiválasztott attribútumokat. Az ezekhez az attribútumokhoz hozzárendelt összes partneradat el fog veszni.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Ez törölni fogja a kiválasztott attribútumot. Az ehhez az attribútumhoz hozzárendelt összes partneradat el fog veszni.} other {Ez törölni fogja a kiválasztott attribútumokat. Az ezekhez az attribútumokhoz hozzárendelt összes partneradat el fog veszni.}}",
|
||||||
"delete_contact_confirmation": "Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni.",
|
"delete_contact_confirmation": "Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ez a partner olyan válaszokkal rendelkezik, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.} other {Ez törölni fogja az ezekhez a partnerekhez tartozó összes kérdőívválaszt és partnerattribútumot. A partnerek adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ezek a partnerek olyan válaszokkal rendelkeznek, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Ez törölni fogja az ehhez a partnerhez tartozó összes kérdőívválaszt és partnerattribútumot. A partner adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ez a partner olyan válaszokkal rendelkezik, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.} other {Ez törölni fogja az ezekhez a partnerekhez tartozó összes kérdőívválaszt és partnerattribútumot. A partnerek adatain alapuló bármilyen célzás és személyre szabás el fog veszni. Ha ezek a partnerek olyan válaszokkal rendelkeznek, amelyek a kérdőívkvótákba beletartoznak, akkor a kvóta számlálója csökkentve lesz, de a kvóta korlátai változatlanok maradnak.}}",
|
||||||
"displays": "Megjelenítések",
|
|
||||||
"edit_attribute": "Attribútum szerkesztése",
|
"edit_attribute": "Attribútum szerkesztése",
|
||||||
"edit_attribute_description": "Az attribútum címkéjének és leírásának frissítése.",
|
"edit_attribute_description": "Az attribútum címkéjének és leírásának frissítése.",
|
||||||
"edit_attribute_values": "Attribútumok szerkesztése",
|
"edit_attribute_values": "Attribútumok szerkesztése",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Érvénytelen CSV oszlopnév(nevek): {columns}. Az új attribútumokká váló oszlopnevek csak kisbetűket, számokat és aláhúzásjeleket tartalmazhatnak, és betűvel kell kezdődniük.",
|
"invalid_csv_column_names": "Érvénytelen CSV oszlopnév(nevek): {columns}. Az új attribútumokká váló oszlopnevek csak kisbetűket, számokat és aláhúzásjeleket tartalmazhatnak, és betűvel kell kezdődniük.",
|
||||||
"invalid_date_format": "Érvénytelen dátumformátum. Kérlek, adj meg egy érvényes dátumot.",
|
"invalid_date_format": "Érvénytelen dátumformátum. Kérlek, adj meg egy érvényes dátumot.",
|
||||||
"invalid_number_format": "Érvénytelen számformátum. Kérlek, adj meg egy érvényes számot.",
|
"invalid_number_format": "Érvénytelen számformátum. Kérlek, adj meg egy érvényes számot.",
|
||||||
"no_activity_yet": "Még nincs aktivitás",
|
|
||||||
"no_published_link_surveys_available": "Nem érhetők el közzétett hivatkozás-kérdőívek. Először tegyen közzé egy hivatkozás-kérdőívet.",
|
"no_published_link_surveys_available": "Nem érhetők el közzétett hivatkozás-kérdőívek. Először tegyen közzé egy hivatkozás-kérdőívet.",
|
||||||
"no_published_surveys": "Nincsenek közzétett kérdőívek",
|
"no_published_surveys": "Nincsenek közzétett kérdőívek",
|
||||||
"no_responses_found": "Nem találhatók válaszok",
|
"no_responses_found": "Nem találhatók válaszok",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Kérdőív kiválasztása",
|
"select_a_survey": "Kérdőív kiválasztása",
|
||||||
"select_attribute": "Attribútum kiválasztása",
|
"select_attribute": "Attribútum kiválasztása",
|
||||||
"select_attribute_key": "Attribútum kulcs kiválasztása",
|
"select_attribute_key": "Attribútum kulcs kiválasztása",
|
||||||
"survey_viewed": "Kérdőív megtekintve",
|
|
||||||
"survey_viewed_at": "Megtekintve",
|
|
||||||
"system_attributes": "Rendszer attribútumok",
|
"system_attributes": "Rendszer attribútumok",
|
||||||
"unlock_contacts_description": "Partnerek kezelése és célzott kérdőívek kiküldése",
|
"unlock_contacts_description": "Partnerek kezelése és célzott kérdőívek kiküldése",
|
||||||
"unlock_contacts_title": "Partnerek feloldása egy magasabb csomaggal",
|
"unlock_contacts_title": "Partnerek feloldása egy magasabb csomaggal",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Google Táblázatok összekapcsolása",
|
"link_google_sheet": "Google Táblázatok összekapcsolása",
|
||||||
"link_new_sheet": "Új táblázat összekapcsolása",
|
"link_new_sheet": "Új táblázat összekapcsolása",
|
||||||
"no_integrations_yet": "A Google Táblázatok integrációi itt fognak megjelenni, amint hozzáadja azokat. ⏲️",
|
"no_integrations_yet": "A Google Táblázatok integrációi itt fognak megjelenni, amint hozzáadja azokat. ⏲️",
|
||||||
"reconnect_button": "Újrakapcsolódás",
|
"spreadsheet_url": "Táblázat URL-e"
|
||||||
"reconnect_button_description": "A Google Táblázatok kapcsolata lejárt. Kérjük, csatlakozzon újra a válaszok szinkronizálásának folytatásához. A meglévő táblázathivatkozások és adatok megmaradnak.",
|
|
||||||
"reconnect_button_tooltip": "Csatlakoztassa újra az integrációt a hozzáférés frissítéséhez. A meglévő táblázathivatkozások és adatok megmaradnak.",
|
|
||||||
"spreadsheet_permission_error": "Nincs jogosultsága a táblázat eléréséhez. Kérjük, győződjön meg arról, hogy a táblázat meg van osztva a Google-fiókjával, és írási jogosultsággal rendelkezik a táblázathoz.",
|
|
||||||
"spreadsheet_url": "Táblázat URL-e",
|
|
||||||
"token_expired_error": "A Google Táblázatok frissítési tokenje lejárt vagy visszavonásra került. Kérjük, csatlakoztassa újra az integrációt."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Létrehozva felvétele",
|
"include_created_at": "Létrehozva felvétele",
|
||||||
"include_hidden_fields": "Rejtett mezők felvétele",
|
"include_hidden_fields": "Rejtett mezők felvétele",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Helló {userName}",
|
"email_customization_preview_email_heading": "Helló {userName}",
|
||||||
"email_customization_preview_email_text": "Ez egy e-mail előnézet, amely azt mutatja meg, hogy melyik logó fog megjelenni az e-mailekben.",
|
"email_customization_preview_email_text": "Ez egy e-mail előnézet, amely azt mutatja meg, hogy melyik logó fog megjelenni az e-mailekben.",
|
||||||
"error_deleting_organization_please_try_again": "Hiba a szervezet törlésekor. Próbálja meg újra.",
|
"error_deleting_organization_please_try_again": "Hiba a szervezet törlésekor. Próbálja meg újra.",
|
||||||
"from_your_organization": "{memberName} a szervezetből",
|
"from_your_organization": "a szervezetétől",
|
||||||
"invitation_sent_once_more": "A meghívó még egyszer elküldve.",
|
"invitation_sent_once_more": "A meghívó még egyszer elküldve.",
|
||||||
"invite_deleted_successfully": "A meghívó sikeresen törölve",
|
"invite_deleted_successfully": "A meghívó sikeresen törölve",
|
||||||
"invite_expires_on": "A meghívó lejár ekkor: {date}",
|
"invite_expires_on": "A meghívó lejár ekkor: {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Helykitöltő hozzáadása annak megjelenítéshez, hogy nincs visszahívandó érték.",
|
"add_fallback_placeholder": "Helykitöltő hozzáadása annak megjelenítéshez, hogy nincs visszahívandó érték.",
|
||||||
"add_hidden_field_id": "Rejtett mezőazonosító hozzáadása",
|
"add_hidden_field_id": "Rejtett mezőazonosító hozzáadása",
|
||||||
"add_highlight_border": "Kiemelési szegély hozzáadása",
|
"add_highlight_border": "Kiemelési szegély hozzáadása",
|
||||||
"add_highlight_border_description": "Csak a terméken belüli felmérésekre vonatkozik.",
|
|
||||||
"add_logic": "Logika hozzáadása",
|
"add_logic": "Logika hozzáadása",
|
||||||
"add_none_of_the_above": "„A fentiek közül egyik sem” hozzáadása",
|
"add_none_of_the_above": "„A fentiek közül egyik sem” hozzáadása",
|
||||||
"add_option": "Lehetőség hozzáadása",
|
"add_option": "Lehetőség hozzáadása",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "A követés frissítve, és akkor lesz elmentve, ha elmenti a kérdőívet.",
|
"follow_ups_modal_updated_successfull_toast": "A követés frissítve, és akkor lesz elmentve, ha elmenti a kérdőívet.",
|
||||||
"follow_ups_new": "Új követés",
|
"follow_ups_new": "Új követés",
|
||||||
"follow_ups_upgrade_button_text": "Magasabb csomagra váltás a követések engedélyezéséhez",
|
"follow_ups_upgrade_button_text": "Magasabb csomagra váltás a követések engedélyezéséhez",
|
||||||
|
"form_styling": "Űrlap stílusának beállítása",
|
||||||
"formbricks_sdk_is_not_connected": "A Formbricks SDK nincs csatlakoztatva",
|
"formbricks_sdk_is_not_connected": "A Formbricks SDK nincs csatlakoztatva",
|
||||||
"four_points": "4 pont",
|
"four_points": "4 pont",
|
||||||
"heading": "Címsor",
|
"heading": "Címsor",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Válaszkorlátok, átirányítások és egyebek.",
|
"response_limits_redirections_and_more": "Válaszkorlátok, átirányítások és egyebek.",
|
||||||
"response_options": "Válasz beállításai",
|
"response_options": "Válasz beállításai",
|
||||||
"roundness": "Kerekesség",
|
"roundness": "Kerekesség",
|
||||||
"roundness_description": "Szabályozza a sarkok lekerekítését.",
|
"roundness_description": "Annak vezérlése, hogy a kártya sarkai mennyire legyenek lekerekítve.",
|
||||||
"row_used_in_logic_error": "Ez a sor használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.",
|
"row_used_in_logic_error": "Ez a sor használatban van a(z) {questionIndex}. kérdés logikájában. Először távolítsa el a logikából.",
|
||||||
"rows": "Sorok",
|
"rows": "Sorok",
|
||||||
"save_and_close": "Mentés és bezárás",
|
"save_and_close": "Mentés és bezárás",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Ez a szabad és nyílt forráskódú kérdőív le lett zárva",
|
"survey_completed_subheading": "Ez a szabad és nyílt forráskódú kérdőív le lett zárva",
|
||||||
"survey_display_settings": "Kérdőív megjelenítésének beállításai",
|
"survey_display_settings": "Kérdőív megjelenítésének beállításai",
|
||||||
"survey_placement": "Kérdőív elhelyezése",
|
"survey_placement": "Kérdőív elhelyezése",
|
||||||
"survey_styling": "Űrlap stílusának beállítása",
|
|
||||||
"survey_trigger": "Kérdőív aktiválója",
|
"survey_trigger": "Kérdőív aktiválója",
|
||||||
"switch_multi_language_on_to_get_started": "Kapcsolja be a többnyelvűséget a kezdéshez 👉",
|
"switch_multi_language_on_to_get_started": "Kapcsolja be a többnyelvűséget a kezdéshez 👉",
|
||||||
"target_block_not_found": "A célblokk nem található",
|
"target_block_not_found": "A célblokk nem található",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Üdvözlő üzenet",
|
"welcome_message": "Üdvözlő üzenet",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Szűrő nélkül az összes felhasználója megkérdezhető.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Szűrő nélkül az összes felhasználója megkérdezhető.",
|
||||||
"you_have_not_created_a_segment_yet": "Még nem hozott létre szakaszt",
|
"you_have_not_created_a_segment_yet": "Még nem hozott létre szakaszt",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Be kell állítania kettő vagy több nyelvet a munkaterületen a fordításokkal való munkához.",
|
||||||
"your_description_here_recall_information_with": "Ide jön a leírás. Információk visszahívása a @ karakterrel.",
|
"your_description_here_recall_information_with": "Ide jön a leírás. Információk visszahívása a @ karakterrel.",
|
||||||
"your_question_here_recall_information_with": "Ide jön a kérdés. Információk visszahívása a @ karakterrel.",
|
"your_question_here_recall_information_with": "Ide jön a kérdés. Információk visszahívása a @ karakterrel.",
|
||||||
"your_web_app": "Saját webalkalmazás",
|
"your_web_app": "Saját webalkalmazás",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Szűrt válaszok (Excel)",
|
"filtered_responses_excel": "Szűrt válaszok (Excel)",
|
||||||
"generating_qr_code": "QR-kód előállítása",
|
"generating_qr_code": "QR-kód előállítása",
|
||||||
"impressions": "Benyomások",
|
"impressions": "Benyomások",
|
||||||
"impressions_identified_only": "Csak az azonosított kapcsolatok megjelenítései láthatók",
|
|
||||||
"impressions_tooltip": "A kérdőív megtekintési alkalmainak száma.",
|
"impressions_tooltip": "A kérdőív megtekintési alkalmainak száma.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "A kérdőív a webhelye azon felhasználóinak lesz megjelenítve, akik megfelelnek az alább felsorolt feltételeknek",
|
"connection_description": "A kérdőív a webhelye azon felhasználóinak lesz megjelenítve, akik megfelelnek az alább felsorolt feltételeknek",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Elmúlt negyedév",
|
"last_quarter": "Elmúlt negyedév",
|
||||||
"last_year": "Elmúlt év",
|
"last_year": "Elmúlt év",
|
||||||
"limit": "Korlát",
|
"limit": "Korlát",
|
||||||
"no_identified_impressions": "Nincsenek megjelenítések azonosított kapcsolatoktól",
|
|
||||||
"no_responses_found": "Nem találhatók válaszok",
|
"no_responses_found": "Nem találhatók válaszok",
|
||||||
"other_values_found": "Más értékek találhatók",
|
"other_values_found": "Más értékek találhatók",
|
||||||
"overall": "Összesen",
|
"overall": "Összesen",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Elkezdések",
|
"starts": "Elkezdések",
|
||||||
"starts_tooltip": "A kérdőív elkezdési alkalmainak száma.",
|
"starts_tooltip": "A kérdőív elkezdési alkalmainak száma.",
|
||||||
"survey_reset_successfully": "A kérdőív sikeresen visszaállítva. {responseCount} válasz és {displayCount} megjelenítés lett törölve.",
|
"survey_reset_successfully": "A kérdőív sikeresen visszaállítva. {responseCount} válasz és {displayCount} megjelenítés lett törölve.",
|
||||||
"survey_results": "{surveyName} eredményei",
|
|
||||||
"this_month": "Ez a hónap",
|
"this_month": "Ez a hónap",
|
||||||
"this_quarter": "Ez a negyedév",
|
"this_quarter": "Ez a negyedév",
|
||||||
"this_year": "Ez az év",
|
"this_year": "Ez az év",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Kiszínezi a sáv kitöltött részét.",
|
"advanced_styling_field_indicator_bg_description": "Kiszínezi a sáv kitöltött részét.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Lekerekíti a beviteli mező sarkait.",
|
"advanced_styling_field_input_border_radius_description": "Lekerekíti a beviteli mező sarkait.",
|
||||||
"advanced_styling_field_input_font_size_description": "Átméretezi a beviteli mezőkbe beírt szöveget.",
|
"advanced_styling_field_input_font_size_description": "Átméretezi a beviteli mezőkbe beírt szöveget.",
|
||||||
"advanced_styling_field_input_height_description": "Szabályozza a beviteli mező minimális magasságát.",
|
"advanced_styling_field_input_height_description": "A beviteli mező minimális magasságát szabályozza.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Térközt ad hozzá balra és jobbra.",
|
"advanced_styling_field_input_padding_x_description": "Térközt ad hozzá balra és jobbra.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Térközt ad hozzá fent és lent.",
|
"advanced_styling_field_input_padding_y_description": "Térközt ad hozzá fent és lent.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Elhalványítja a helykitöltő súgószöveget.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Elhalványítja a helykitöltő súgószöveget.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Kiszínezi a beviteli mezőkbe beírt szöveget.",
|
"advanced_styling_field_input_text_description": "Kiszínezi a beviteli mezőkbe beírt szöveget.",
|
||||||
"advanced_styling_field_option_bg": "Háttér",
|
"advanced_styling_field_option_bg": "Háttér",
|
||||||
"advanced_styling_field_option_bg_description": "Kitölti a választási lehetőség elemeit.",
|
"advanced_styling_field_option_bg_description": "Kitölti a választási lehetőség elemeit.",
|
||||||
"advanced_styling_field_option_border": "Szegély színe",
|
|
||||||
"advanced_styling_field_option_border_description": "A rádiógomb és jelölőnégyzet opciók körvonalát határozza meg.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Lekerekíti a választási lehetőség sarkait.",
|
"advanced_styling_field_option_border_radius_description": "Lekerekíti a választási lehetőség sarkait.",
|
||||||
"advanced_styling_field_option_font_size_description": "Átméretezi a választási lehetőség címkéjének szövegét.",
|
"advanced_styling_field_option_font_size_description": "Átméretezi a választási lehetőség címkéjének szövegét.",
|
||||||
"advanced_styling_field_option_label": "Címke színe",
|
"advanced_styling_field_option_label": "Címke színe",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Nem, köszönöm!",
|
"preview_survey_question_2_choice_2_label": "Nem, köszönöm!",
|
||||||
"preview_survey_question_2_headline": "Szeretne naprakész maradni?",
|
"preview_survey_question_2_headline": "Szeretne naprakész maradni?",
|
||||||
"preview_survey_question_2_subheader": "Ez egy példa a leírásra.",
|
"preview_survey_question_2_subheader": "Ez egy példa a leírásra.",
|
||||||
"preview_survey_question_open_text_headline": "Bármi egyéb, amit meg szeretne osztani?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Írja be ide a válaszát…",
|
|
||||||
"preview_survey_question_open_text_subheader": "A visszajelzése segít nekünk a fejlődésben.",
|
|
||||||
"preview_survey_welcome_card_headline": "Üdvözöljük!",
|
"preview_survey_welcome_card_headline": "Üdvözöljük!",
|
||||||
"prioritize_features_description": "A felhasználóknak leginkább és legkevésbé szükséges funkciók azonosítása.",
|
"prioritize_features_description": "A felhasználóknak leginkább és legkevésbé szükséges funkciók azonosítása.",
|
||||||
"prioritize_features_name": "Funkciók rangsorolása",
|
"prioritize_features_name": "Funkciók rangsorolása",
|
||||||
|
|||||||
+12
-34
@@ -133,6 +133,7 @@
|
|||||||
"allow": "許可",
|
"allow": "許可",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "フォームの外側をクリックしてユーザーが終了できるようにする",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "フォームの外側をクリックしてユーザーが終了できるようにする",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "{type}の削除中に不明なエラーが発生しました",
|
"an_unknown_error_occurred_while_deleting_table_items": "{type}の削除中に不明なエラーが発生しました",
|
||||||
|
"analysis": "分析",
|
||||||
"and": "および",
|
"and": "および",
|
||||||
"and_response_limit_of": "と回答数の上限",
|
"and_response_limit_of": "と回答数の上限",
|
||||||
"anonymous": "匿名",
|
"anonymous": "匿名",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "コピー",
|
"copy": "コピー",
|
||||||
"copy_code": "コードをコピー",
|
"copy_code": "コードをコピー",
|
||||||
"copy_link": "リンクをコピー",
|
"copy_link": "リンクをコピー",
|
||||||
"count_attributes": "{count, plural, other {{count}個の属性}}",
|
"count_attributes": "{value, plural, other {{value}個の属性}}",
|
||||||
"count_contacts": "{count, plural, other {# 件の連絡先}}",
|
"count_contacts": "{count, plural, other {# 件の連絡先}}",
|
||||||
"count_members": "{count, plural, other {{count}人のメンバー}}",
|
|
||||||
"count_responses": "{count, plural, other {# 件の回答}}",
|
"count_responses": "{count, plural, other {# 件の回答}}",
|
||||||
"count_selections": "{count, plural, other {{count}件選択中}}",
|
|
||||||
"create_new_organization": "新しい組織を作成",
|
"create_new_organization": "新しい組織を作成",
|
||||||
"create_segment": "セグメントを作成",
|
"create_segment": "セグメントを作成",
|
||||||
"create_survey": "フォームを作成",
|
"create_survey": "フォームを作成",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "日",
|
"days": "日",
|
||||||
"default": "デフォルト",
|
"default": "デフォルト",
|
||||||
"delete": "削除",
|
"delete": "削除",
|
||||||
"delete_what": "{deleteWhat}を削除",
|
|
||||||
"description": "説明",
|
"description": "説明",
|
||||||
"dev_env": "開発環境",
|
"dev_env": "開発環境",
|
||||||
"development": "開発",
|
"development": "開発",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "エラー",
|
"error": "エラー",
|
||||||
"error_component_description": "この リソース は 存在 しない か、アクセス する ための 必要な 権限 が ありません。",
|
"error_component_description": "この リソース は 存在 しない か、アクセス する ための 必要な 権限 が ありません。",
|
||||||
"error_component_title": "リソース の 読み込み エラー",
|
"error_component_title": "リソース の 読み込み エラー",
|
||||||
"error_loading_data": "データの読み込みエラー",
|
|
||||||
"error_rate_limit_description": "リクエストの最大数に達しました。後でもう一度試してください。",
|
"error_rate_limit_description": "リクエストの最大数に達しました。後でもう一度試してください。",
|
||||||
"error_rate_limit_title": "レート制限を超えました",
|
"error_rate_limit_title": "レート制限を超えました",
|
||||||
"expand_rows": "行を展開",
|
"expand_rows": "行を展開",
|
||||||
"failed_to_copy_to_clipboard": "クリップボードへのコピーに失敗しました",
|
"failed_to_copy_to_clipboard": "クリップボードへのコピーに失敗しました",
|
||||||
"failed_to_load_organizations": "組織の読み込みに失敗しました",
|
"failed_to_load_organizations": "組織の読み込みに失敗しました",
|
||||||
"failed_to_load_workspaces": "ワークスペースの読み込みに失敗しました",
|
"failed_to_load_workspaces": "ワークスペースの読み込みに失敗しました",
|
||||||
"filter": "フィルター",
|
|
||||||
"finish": "完了",
|
"finish": "完了",
|
||||||
"first_name": "名",
|
|
||||||
"follow_these": "こちらの手順に従って",
|
"follow_these": "こちらの手順に従って",
|
||||||
"formbricks_version": "Formbricksバージョン",
|
"formbricks_version": "Formbricksバージョン",
|
||||||
"full_name": "氏名",
|
"full_name": "氏名",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "非表示フィールド",
|
"hidden_field": "非表示フィールド",
|
||||||
"hidden_fields": "非表示フィールド",
|
"hidden_fields": "非表示フィールド",
|
||||||
"hide_column": "列を非表示",
|
"hide_column": "列を非表示",
|
||||||
"id": "ID",
|
|
||||||
"image": "画像",
|
"image": "画像",
|
||||||
"images": "画像",
|
"images": "画像",
|
||||||
"import": "インポート",
|
"import": "インポート",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "キー",
|
"key": "キー",
|
||||||
"label": "ラベル",
|
"label": "ラベル",
|
||||||
"language": "言語",
|
"language": "言語",
|
||||||
"last_name": "姓",
|
|
||||||
"learn_more": "詳細を見る",
|
"learn_more": "詳細を見る",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "明るいオーバーレイ",
|
"light_overlay": "明るいオーバーレイ",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "デザイン",
|
"look_and_feel": "デザイン",
|
||||||
"manage": "管理",
|
"manage": "管理",
|
||||||
"marketing": "マーケティング",
|
"marketing": "マーケティング",
|
||||||
|
"member": "メンバー",
|
||||||
"members": "メンバー",
|
"members": "メンバー",
|
||||||
"members_and_teams": "メンバー&チーム",
|
"members_and_teams": "メンバー&チーム",
|
||||||
"membership_not_found": "メンバーシップが見つかりません",
|
"membership_not_found": "メンバーシップが見つかりません",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "下に移動",
|
"move_down": "下に移動",
|
||||||
"move_up": "上に移動",
|
"move_up": "上に移動",
|
||||||
"multiple_languages": "多言語",
|
"multiple_languages": "多言語",
|
||||||
"my_product": "マイプロダクト",
|
|
||||||
"name": "名前",
|
"name": "名前",
|
||||||
"new": "新規",
|
"new": "新規",
|
||||||
"new_version_available": "Formbricks {version} が利用可能です。今すぐアップグレード!",
|
"new_version_available": "Formbricks {version} が利用可能です。今すぐアップグレード!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "チームを選択",
|
"select_teams": "チームを選択",
|
||||||
"selected": "選択済み",
|
"selected": "選択済み",
|
||||||
"selected_questions": "選択した質問",
|
"selected_questions": "選択した質問",
|
||||||
|
"selection": "選択",
|
||||||
|
"selections": "選択",
|
||||||
"send_test_email": "テストメールを送信",
|
"send_test_email": "テストメールを送信",
|
||||||
"session_not_found": "セッションが見つかりません",
|
"session_not_found": "セッションが見つかりません",
|
||||||
"settings": "設定",
|
"settings": "設定",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "右上",
|
"top_right": "右上",
|
||||||
"try_again": "もう一度お試しください",
|
"try_again": "もう一度お試しください",
|
||||||
"type": "種類",
|
"type": "種類",
|
||||||
"unknown_survey": "不明なフォーム",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "上位プランでより多くのワークスペースを利用できます。",
|
"unlock_more_workspaces_with_a_higher_plan": "上位プランでより多くのワークスペースを利用できます。",
|
||||||
"update": "更新",
|
"update": "更新",
|
||||||
"updated": "更新済み",
|
"updated": "更新済み",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "連絡先を更新",
|
"contacts_table_refresh": "連絡先を更新",
|
||||||
"contacts_table_refresh_success": "連絡先を正常に更新しました",
|
"contacts_table_refresh_success": "連絡先を正常に更新しました",
|
||||||
"create_attribute": "属性を作成",
|
"create_attribute": "属性を作成",
|
||||||
|
"create_key": "キーを作成",
|
||||||
"create_new_attribute": "新しい属性を作成",
|
"create_new_attribute": "新しい属性を作成",
|
||||||
"create_new_attribute_description": "セグメンテーション用の新しい属性を作成します。",
|
"create_new_attribute_description": "セグメンテーション用の新しい属性を作成します。",
|
||||||
"custom_attributes": "カスタム属性",
|
"custom_attributes": "カスタム属性",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {選択した属性を削除します。この属性に関連付けられたすべてのコンタクトデータは失われます。} other {選択した属性を削除します。これらの属性に関連付けられたすべてのコンタクトデータは失われます。}}",
|
"delete_attribute_confirmation": "{value, plural, one {選択した属性を削除します。この属性に関連付けられたすべてのコンタクトデータは失われます。} other {選択した属性を削除します。これらの属性に関連付けられたすべてのコンタクトデータは失われます。}}",
|
||||||
"delete_contact_confirmation": "これにより、この連絡先に関連付けられているすべてのフォーム回答と連絡先属性が削除されます。この連絡先のデータに基づいたターゲティングとパーソナライゼーションはすべて失われます。",
|
"delete_contact_confirmation": "これにより、この連絡先に関連付けられているすべてのフォーム回答と連絡先属性が削除されます。この連絡先のデータに基づいたターゲティングとパーソナライゼーションはすべて失われます。",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {これにより この連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。この連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。この連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。} other {これにより これらの連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。これらの連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。これらの連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {これにより この連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。この連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。この連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。} other {これにより これらの連絡先に関連するすべてのアンケート応答と連絡先属性が削除されます。これらの連絡先のデータに基づくターゲティングとパーソナライゼーションが失われます。これらの連絡先がアンケートの割当量を考慮した回答を持っている場合、割当量カウントは減少しますが、割当量の制限は変更されません。}}",
|
||||||
"displays": "表示回数",
|
|
||||||
"edit_attribute": "属性を編集",
|
"edit_attribute": "属性を編集",
|
||||||
"edit_attribute_description": "この属性のラベルと説明を更新します。",
|
"edit_attribute_description": "この属性のラベルと説明を更新します。",
|
||||||
"edit_attribute_values": "属性を編集",
|
"edit_attribute_values": "属性を編集",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "無効なCSV列名: {columns}。新しい属性となる列名は、小文字、数字、アンダースコアのみを含み、文字で始まる必要があります。",
|
"invalid_csv_column_names": "無効なCSV列名: {columns}。新しい属性となる列名は、小文字、数字、アンダースコアのみを含み、文字で始まる必要があります。",
|
||||||
"invalid_date_format": "無効な日付形式です。有効な日付を使用してください。",
|
"invalid_date_format": "無効な日付形式です。有効な日付を使用してください。",
|
||||||
"invalid_number_format": "無効な数値形式です。有効な数値を入力してください。",
|
"invalid_number_format": "無効な数値形式です。有効な数値を入力してください。",
|
||||||
"no_activity_yet": "まだアクティビティがありません",
|
|
||||||
"no_published_link_surveys_available": "公開されたリンクフォームはありません。まずリンクフォームを公開してください。",
|
"no_published_link_surveys_available": "公開されたリンクフォームはありません。まずリンクフォームを公開してください。",
|
||||||
"no_published_surveys": "公開されたフォームはありません",
|
"no_published_surveys": "公開されたフォームはありません",
|
||||||
"no_responses_found": "回答が見つかりません",
|
"no_responses_found": "回答が見つかりません",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "フォームを選択",
|
"select_a_survey": "フォームを選択",
|
||||||
"select_attribute": "属性を選択",
|
"select_attribute": "属性を選択",
|
||||||
"select_attribute_key": "属性キーを選択",
|
"select_attribute_key": "属性キーを選択",
|
||||||
"survey_viewed": "フォームを閲覧",
|
|
||||||
"survey_viewed_at": "閲覧日時",
|
|
||||||
"system_attributes": "システム属性",
|
"system_attributes": "システム属性",
|
||||||
"unlock_contacts_description": "連絡先を管理し、特定のフォームを送信します",
|
"unlock_contacts_description": "連絡先を管理し、特定のフォームを送信します",
|
||||||
"unlock_contacts_title": "上位プランで連絡先をアンロック",
|
"unlock_contacts_title": "上位プランで連絡先をアンロック",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "スプレッドシートをリンク",
|
"link_google_sheet": "スプレッドシートをリンク",
|
||||||
"link_new_sheet": "新しいシートをリンク",
|
"link_new_sheet": "新しいシートをリンク",
|
||||||
"no_integrations_yet": "Google スプレッドシート連携は、追加するとここに表示されます。⏲️",
|
"no_integrations_yet": "Google スプレッドシート連携は、追加するとここに表示されます。⏲️",
|
||||||
"reconnect_button": "再接続",
|
"spreadsheet_url": "スプレッドシートURL"
|
||||||
"reconnect_button_description": "Google Sheetsの接続が期限切れになりました。回答の同期を続けるには再接続してください。既存のスプレッドシートリンクとデータは保持されます。",
|
|
||||||
"reconnect_button_tooltip": "統合を再接続してアクセスを更新します。既存のスプレッドシートリンクとデータは保持されます。",
|
|
||||||
"spreadsheet_permission_error": "このスプレッドシートにアクセスする権限がありません。スプレッドシートがGoogleアカウントと共有されており、書き込みアクセス権があることを確認してください。",
|
|
||||||
"spreadsheet_url": "スプレッドシートURL",
|
|
||||||
"token_expired_error": "Google Sheetsのリフレッシュトークンが期限切れになったか、取り消されました。統合を再接続してください。"
|
|
||||||
},
|
},
|
||||||
"include_created_at": "作成日時を含める",
|
"include_created_at": "作成日時を含める",
|
||||||
"include_hidden_fields": "非表示フィールドを含める",
|
"include_hidden_fields": "非表示フィールドを含める",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "こんにちは、{userName}さん",
|
"email_customization_preview_email_heading": "こんにちは、{userName}さん",
|
||||||
"email_customization_preview_email_text": "これは、メールに表示されるロゴを確認するためのプレビューメールです。",
|
"email_customization_preview_email_text": "これは、メールに表示されるロゴを確認するためのプレビューメールです。",
|
||||||
"error_deleting_organization_please_try_again": "組織の削除中にエラーが発生しました。もう一度お試しください。",
|
"error_deleting_organization_please_try_again": "組織の削除中にエラーが発生しました。もう一度お試しください。",
|
||||||
"from_your_organization": "組織から{memberName}を削除",
|
"from_your_organization": "あなたの組織から",
|
||||||
"invitation_sent_once_more": "招待状を再度送信しました。",
|
"invitation_sent_once_more": "招待状を再度送信しました。",
|
||||||
"invite_deleted_successfully": "招待を正常に削除しました",
|
"invite_deleted_successfully": "招待を正常に削除しました",
|
||||||
"invite_expires_on": "招待は{date}に期限切れ",
|
"invite_expires_on": "招待は{date}に期限切れ",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "質問がスキップされた場合に表示するプレースホルダーを追加:",
|
"add_fallback_placeholder": "質問がスキップされた場合に表示するプレースホルダーを追加:",
|
||||||
"add_hidden_field_id": "非表示フィールドIDを追加",
|
"add_hidden_field_id": "非表示フィールドIDを追加",
|
||||||
"add_highlight_border": "ハイライトボーダーを追加",
|
"add_highlight_border": "ハイライトボーダーを追加",
|
||||||
"add_highlight_border_description": "プロダクト内サーベイにのみ適用されます。",
|
|
||||||
"add_logic": "ロジックを追加",
|
"add_logic": "ロジックを追加",
|
||||||
"add_none_of_the_above": "\"いずれも該当しません\" を追加",
|
"add_none_of_the_above": "\"いずれも該当しません\" を追加",
|
||||||
"add_option": "オプションを追加",
|
"add_option": "オプションを追加",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "フォローアップ が 更新され、 アンケートを 保存すると保存されます。",
|
"follow_ups_modal_updated_successfull_toast": "フォローアップ が 更新され、 アンケートを 保存すると保存されます。",
|
||||||
"follow_ups_new": "新しいフォローアップ",
|
"follow_ups_new": "新しいフォローアップ",
|
||||||
"follow_ups_upgrade_button_text": "フォローアップを有効にするためにアップグレード",
|
"follow_ups_upgrade_button_text": "フォローアップを有効にするためにアップグレード",
|
||||||
|
"form_styling": "フォームのスタイル",
|
||||||
"formbricks_sdk_is_not_connected": "Formbricks SDKが接続されていません",
|
"formbricks_sdk_is_not_connected": "Formbricks SDKが接続されていません",
|
||||||
"four_points": "4点",
|
"four_points": "4点",
|
||||||
"heading": "見出し",
|
"heading": "見出し",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。",
|
"response_limits_redirections_and_more": "回答数の上限、リダイレクトなど。",
|
||||||
"response_options": "回答オプション",
|
"response_options": "回答オプション",
|
||||||
"roundness": "丸み",
|
"roundness": "丸み",
|
||||||
"roundness_description": "角の丸みを調整します。",
|
"roundness_description": "カードの角の丸みを調整します。",
|
||||||
"row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
"row_used_in_logic_error": "この行は質問 {questionIndex} のロジックで使用されています。まず、ロジックから削除してください。",
|
||||||
"rows": "行",
|
"rows": "行",
|
||||||
"save_and_close": "保存して閉じる",
|
"save_and_close": "保存して閉じる",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました",
|
"survey_completed_subheading": "この無料のオープンソースフォームは閉鎖されました",
|
||||||
"survey_display_settings": "フォーム表示設定",
|
"survey_display_settings": "フォーム表示設定",
|
||||||
"survey_placement": "フォームの配置",
|
"survey_placement": "フォームの配置",
|
||||||
"survey_styling": "フォームのスタイル",
|
|
||||||
"survey_trigger": "フォームのトリガー",
|
"survey_trigger": "フォームのトリガー",
|
||||||
"switch_multi_language_on_to_get_started": "多言語機能をオンにして開始 👉",
|
"switch_multi_language_on_to_get_started": "多言語機能をオンにして開始 👉",
|
||||||
"target_block_not_found": "対象ブロックが見つかりません",
|
"target_block_not_found": "対象ブロックが見つかりません",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "ウェルカムメッセージ",
|
"welcome_message": "ウェルカムメッセージ",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "フィルターがなければ、すべてのユーザーがフォームに回答できます。",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "フィルターがなければ、すべてのユーザーがフォームに回答できます。",
|
||||||
"you_have_not_created_a_segment_yet": "まだセグメントを作成していません",
|
"you_have_not_created_a_segment_yet": "まだセグメントを作成していません",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "翻訳を使用するには、ワークスペースに2つ以上の言語を設定する必要があります。",
|
||||||
"your_description_here_recall_information_with": "ここにあなたの説明。@ で情報を呼び出す",
|
"your_description_here_recall_information_with": "ここにあなたの説明。@ で情報を呼び出す",
|
||||||
"your_question_here_recall_information_with": "ここにあなたの質問。@ で情報を呼び出す",
|
"your_question_here_recall_information_with": "ここにあなたの質問。@ で情報を呼び出す",
|
||||||
"your_web_app": "あなたのウェブアプリ",
|
"your_web_app": "あなたのウェブアプリ",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "フィルター済み回答 (Excel)",
|
"filtered_responses_excel": "フィルター済み回答 (Excel)",
|
||||||
"generating_qr_code": "QRコードを生成中",
|
"generating_qr_code": "QRコードを生成中",
|
||||||
"impressions": "表示回数",
|
"impressions": "表示回数",
|
||||||
"impressions_identified_only": "識別済みコンタクトからのインプレッションのみを表示しています",
|
|
||||||
"impressions_tooltip": "フォームが表示された回数。",
|
"impressions_tooltip": "フォームが表示された回数。",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "このフォームは、以下の条件に一致するあなたのウェブサイトのユーザーに表示されます",
|
"connection_description": "このフォームは、以下の条件に一致するあなたのウェブサイトのユーザーに表示されます",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "前四半期",
|
"last_quarter": "前四半期",
|
||||||
"last_year": "昨年",
|
"last_year": "昨年",
|
||||||
"limit": "制限",
|
"limit": "制限",
|
||||||
"no_identified_impressions": "識別済みコンタクトからのインプレッションはありません",
|
|
||||||
"no_responses_found": "回答が見つかりません",
|
"no_responses_found": "回答が見つかりません",
|
||||||
"other_values_found": "他の値が見つかりました",
|
"other_values_found": "他の値が見つかりました",
|
||||||
"overall": "全体",
|
"overall": "全体",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "開始",
|
"starts": "開始",
|
||||||
"starts_tooltip": "フォームが開始された回数。",
|
"starts_tooltip": "フォームが開始された回数。",
|
||||||
"survey_reset_successfully": "フォームを正常にリセットしました!{responseCount} 件の回答と {displayCount} 件の表示が削除されました。",
|
"survey_reset_successfully": "フォームを正常にリセットしました!{responseCount} 件の回答と {displayCount} 件の表示が削除されました。",
|
||||||
"survey_results": "{surveyName}の結果",
|
|
||||||
"this_month": "今月",
|
"this_month": "今月",
|
||||||
"this_quarter": "今四半期",
|
"this_quarter": "今四半期",
|
||||||
"this_year": "今年",
|
"this_year": "今年",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。",
|
"advanced_styling_field_indicator_bg_description": "バーの塗りつぶし部分に色を付けます。",
|
||||||
"advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。",
|
"advanced_styling_field_input_border_radius_description": "入力フィールドの角を丸めます。",
|
||||||
"advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。",
|
"advanced_styling_field_input_font_size_description": "入力フィールド内の入力テキストのサイズを調整します。",
|
||||||
"advanced_styling_field_input_height_description": "入力欄の最小の高さを調整します。",
|
"advanced_styling_field_input_height_description": "入力フィールドの最小の高さを制御します。",
|
||||||
"advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。",
|
"advanced_styling_field_input_padding_x_description": "左右にスペースを追加します。",
|
||||||
"advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。",
|
"advanced_styling_field_input_padding_y_description": "上下にスペースを追加します。",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。",
|
"advanced_styling_field_input_placeholder_opacity_description": "プレースホルダーのヒントテキストを薄くします。",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "入力フィールドに入力されたテキストの色を設定します。",
|
"advanced_styling_field_input_text_description": "入力フィールドに入力されたテキストの色を設定します。",
|
||||||
"advanced_styling_field_option_bg": "背景",
|
"advanced_styling_field_option_bg": "背景",
|
||||||
"advanced_styling_field_option_bg_description": "オプション項目を塗りつぶします。",
|
"advanced_styling_field_option_bg_description": "オプション項目を塗りつぶします。",
|
||||||
"advanced_styling_field_option_border": "枠線の色",
|
|
||||||
"advanced_styling_field_option_border_description": "ラジオボタンとチェックボックスの選択肢の輪郭を設定します。",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "オプションの角を丸くします。",
|
"advanced_styling_field_option_border_radius_description": "オプションの角を丸くします。",
|
||||||
"advanced_styling_field_option_font_size_description": "オプションラベルのテキストサイズを調整します。",
|
"advanced_styling_field_option_font_size_description": "オプションラベルのテキストサイズを調整します。",
|
||||||
"advanced_styling_field_option_label": "ラベルの色",
|
"advanced_styling_field_option_label": "ラベルの色",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "いいえ、結構です!",
|
"preview_survey_question_2_choice_2_label": "いいえ、結構です!",
|
||||||
"preview_survey_question_2_headline": "最新情報を知りたいですか?",
|
"preview_survey_question_2_headline": "最新情報を知りたいですか?",
|
||||||
"preview_survey_question_2_subheader": "これは説明の例です。",
|
"preview_survey_question_2_subheader": "これは説明の例です。",
|
||||||
"preview_survey_question_open_text_headline": "他に共有したいことはありますか?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "ここに回答を入力してください...",
|
|
||||||
"preview_survey_question_open_text_subheader": "あなたのフィードバックは、私たちの改善に役立ちます。",
|
|
||||||
"preview_survey_welcome_card_headline": "ようこそ!",
|
"preview_survey_welcome_card_headline": "ようこそ!",
|
||||||
"prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。",
|
"prioritize_features_description": "ユーザーが最も必要とする機能と最も必要としない機能を特定する。",
|
||||||
"prioritize_features_name": "機能の優先順位付け",
|
"prioritize_features_name": "機能の優先順位付け",
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Toestaan",
|
"allow": "Toestaan",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Laat gebruikers afsluiten door buiten de enquête te klikken",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Laat gebruikers afsluiten door buiten de enquête te klikken",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Er is een onbekende fout opgetreden bij het verwijderen van {type}s",
|
"an_unknown_error_occurred_while_deleting_table_items": "Er is een onbekende fout opgetreden bij het verwijderen van {type}s",
|
||||||
|
"analysis": "Analyse",
|
||||||
"and": "En",
|
"and": "En",
|
||||||
"and_response_limit_of": "en responslimiet van",
|
"and_response_limit_of": "en responslimiet van",
|
||||||
"anonymous": "Anoniem",
|
"anonymous": "Anoniem",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Kopiëren",
|
"copy": "Kopiëren",
|
||||||
"copy_code": "Kopieer code",
|
"copy_code": "Kopieer code",
|
||||||
"copy_link": "Kopieer link",
|
"copy_link": "Kopieer link",
|
||||||
"count_attributes": "{count, plural, one {{count} attribuut} other {{count} attributen}}",
|
"count_attributes": "{value, plural, one {{value} attribuut} other {{value} attributen}}",
|
||||||
"count_contacts": "{count, plural, one {{count} contact} other {{count} contacten}}",
|
"count_contacts": "{value, plural, one {{value} contact} other {{value} contacten}}",
|
||||||
"count_members": "{count, plural, one {{count} lid} other {{count} leden}}",
|
"count_responses": "{value, plural, one {{value} reactie} other {{value} reacties}}",
|
||||||
"count_responses": "{count, plural, one {{count} reactie} other {{count} reacties}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} selectie} other {{count} selecties}}",
|
|
||||||
"create_new_organization": "Creëer een nieuwe organisatie",
|
"create_new_organization": "Creëer een nieuwe organisatie",
|
||||||
"create_segment": "Segment maken",
|
"create_segment": "Segment maken",
|
||||||
"create_survey": "Enquête maken",
|
"create_survey": "Enquête maken",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "dagen",
|
"days": "dagen",
|
||||||
"default": "Standaard",
|
"default": "Standaard",
|
||||||
"delete": "Verwijderen",
|
"delete": "Verwijderen",
|
||||||
"delete_what": "Verwijder {deleteWhat}",
|
|
||||||
"description": "Beschrijving",
|
"description": "Beschrijving",
|
||||||
"dev_env": "Ontwikkelomgeving",
|
"dev_env": "Ontwikkelomgeving",
|
||||||
"development": "Ontwikkeling",
|
"development": "Ontwikkeling",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Fout",
|
"error": "Fout",
|
||||||
"error_component_description": "Deze bron bestaat niet of u beschikt niet over de benodigde toegangsrechten.",
|
"error_component_description": "Deze bron bestaat niet of u beschikt niet over de benodigde toegangsrechten.",
|
||||||
"error_component_title": "Fout bij het laden van bronnen",
|
"error_component_title": "Fout bij het laden van bronnen",
|
||||||
"error_loading_data": "Fout bij het laden van gegevens",
|
|
||||||
"error_rate_limit_description": "Maximaal aantal verzoeken bereikt. Probeer het later opnieuw.",
|
"error_rate_limit_description": "Maximaal aantal verzoeken bereikt. Probeer het later opnieuw.",
|
||||||
"error_rate_limit_title": "Tarieflimiet overschreden",
|
"error_rate_limit_title": "Tarieflimiet overschreden",
|
||||||
"expand_rows": "Vouw rijen uit",
|
"expand_rows": "Vouw rijen uit",
|
||||||
"failed_to_copy_to_clipboard": "Kopiëren naar klembord mislukt",
|
"failed_to_copy_to_clipboard": "Kopiëren naar klembord mislukt",
|
||||||
"failed_to_load_organizations": "Laden van organisaties mislukt",
|
"failed_to_load_organizations": "Laden van organisaties mislukt",
|
||||||
"failed_to_load_workspaces": "Laden van werkruimtes mislukt",
|
"failed_to_load_workspaces": "Laden van werkruimtes mislukt",
|
||||||
"filter": "Filter",
|
|
||||||
"finish": "Finish",
|
"finish": "Finish",
|
||||||
"first_name": "Voornaam",
|
|
||||||
"follow_these": "Volg deze",
|
"follow_these": "Volg deze",
|
||||||
"formbricks_version": "Formbricks-versie",
|
"formbricks_version": "Formbricks-versie",
|
||||||
"full_name": "Volledige naam",
|
"full_name": "Volledige naam",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Verborgen veld",
|
"hidden_field": "Verborgen veld",
|
||||||
"hidden_fields": "Verborgen velden",
|
"hidden_fields": "Verborgen velden",
|
||||||
"hide_column": "Kolom verbergen",
|
"hide_column": "Kolom verbergen",
|
||||||
"id": "ID",
|
|
||||||
"image": "Afbeelding",
|
"image": "Afbeelding",
|
||||||
"images": "Afbeeldingen",
|
"images": "Afbeeldingen",
|
||||||
"import": "Importeren",
|
"import": "Importeren",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Sleutel",
|
"key": "Sleutel",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"language": "Taal",
|
"language": "Taal",
|
||||||
"last_name": "Achternaam",
|
|
||||||
"learn_more": "Meer informatie",
|
"learn_more": "Meer informatie",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Lichte overlay",
|
"light_overlay": "Lichte overlay",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Kijk & voel",
|
"look_and_feel": "Kijk & voel",
|
||||||
"manage": "Beheren",
|
"manage": "Beheren",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Lid",
|
||||||
"members": "Leden",
|
"members": "Leden",
|
||||||
"members_and_teams": "Leden & teams",
|
"members_and_teams": "Leden & teams",
|
||||||
"membership_not_found": "Lidmaatschap niet gevonden",
|
"membership_not_found": "Lidmaatschap niet gevonden",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Ga naar beneden",
|
"move_down": "Ga naar beneden",
|
||||||
"move_up": "Ga omhoog",
|
"move_up": "Ga omhoog",
|
||||||
"multiple_languages": "Meerdere talen",
|
"multiple_languages": "Meerdere talen",
|
||||||
"my_product": "mijn product",
|
|
||||||
"name": "Naam",
|
"name": "Naam",
|
||||||
"new": "Nieuw",
|
"new": "Nieuw",
|
||||||
"new_version_available": "Formbricks {version} is hier. Upgrade nu!",
|
"new_version_available": "Formbricks {version} is hier. Upgrade nu!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Selecteer teams",
|
"select_teams": "Selecteer teams",
|
||||||
"selected": "Gekozen",
|
"selected": "Gekozen",
|
||||||
"selected_questions": "Geselecteerde vragen",
|
"selected_questions": "Geselecteerde vragen",
|
||||||
|
"selection": "Selectie",
|
||||||
|
"selections": "Selecties",
|
||||||
"send_test_email": "Test-e-mail verzenden",
|
"send_test_email": "Test-e-mail verzenden",
|
||||||
"session_not_found": "Sessie niet gevonden",
|
"session_not_found": "Sessie niet gevonden",
|
||||||
"settings": "Instellingen",
|
"settings": "Instellingen",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Rechtsboven",
|
"top_right": "Rechtsboven",
|
||||||
"try_again": "Probeer het opnieuw",
|
"try_again": "Probeer het opnieuw",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"unknown_survey": "Onbekende enquête",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Ontgrendel meer werkruimtes met een hoger abonnement.",
|
"unlock_more_workspaces_with_a_higher_plan": "Ontgrendel meer werkruimtes met een hoger abonnement.",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
"updated": "Bijgewerkt",
|
"updated": "Bijgewerkt",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Vernieuw contacten",
|
"contacts_table_refresh": "Vernieuw contacten",
|
||||||
"contacts_table_refresh_success": "Contacten zijn vernieuwd",
|
"contacts_table_refresh_success": "Contacten zijn vernieuwd",
|
||||||
"create_attribute": "Attribuut aanmaken",
|
"create_attribute": "Attribuut aanmaken",
|
||||||
|
"create_key": "Sleutel aanmaken",
|
||||||
"create_new_attribute": "Nieuw attribuut aanmaken",
|
"create_new_attribute": "Nieuw attribuut aanmaken",
|
||||||
"create_new_attribute_description": "Maak een nieuw attribuut aan voor segmentatiedoeleinden.",
|
"create_new_attribute_description": "Maak een nieuw attribuut aan voor segmentatiedoeleinden.",
|
||||||
"custom_attributes": "Aangepaste kenmerken",
|
"custom_attributes": "Aangepaste kenmerken",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Dit verwijdert het geselecteerde attribuut. Alle contactgegevens die aan dit attribuut zijn gekoppeld, gaan verloren.} other {Dit verwijdert de geselecteerde attributen. Alle contactgegevens die aan deze attributen zijn gekoppeld, gaan verloren.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Dit verwijdert het geselecteerde attribuut. Alle contactgegevens die aan dit attribuut zijn gekoppeld, gaan verloren.} other {Dit verwijdert de geselecteerde attributen. Alle contactgegevens die aan deze attributen zijn gekoppeld, gaan verloren.}}",
|
||||||
"delete_contact_confirmation": "Hierdoor worden alle enquêtereacties en contactkenmerken verwijderd die aan dit contact zijn gekoppeld. Elke targeting en personalisatie op basis van de gegevens van dit contact gaat verloren.",
|
"delete_contact_confirmation": "Hierdoor worden alle enquêtereacties en contactkenmerken verwijderd die aan dit contact zijn gekoppeld. Elke targeting en personalisatie op basis van de gegevens van dit contact gaat verloren.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Dit verwijdert alle enquêteresultaten en contactattributen die aan dit contact zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van dit contact gaan verloren. Als dit contact reacties heeft die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.} other {Dit verwijdert alle enquêteresultaten en contactattributen die aan deze contacten zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van deze contacten gaan verloren. Als deze contacten reacties hebben die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Dit verwijdert alle enquêteresultaten en contactattributen die aan dit contact zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van dit contact gaan verloren. Als dit contact reacties heeft die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.} other {Dit verwijdert alle enquêteresultaten en contactattributen die aan deze contacten zijn gekoppeld. Alle targeting en personalisatie op basis van de gegevens van deze contacten gaan verloren. Als deze contacten reacties hebben die meetellen voor enquêtekvota, worden de quotawaarden verlaagd maar blijven de limieten ongewijzigd.}}",
|
||||||
"displays": "Weergaven",
|
|
||||||
"edit_attribute": "Attribuut bewerken",
|
"edit_attribute": "Attribuut bewerken",
|
||||||
"edit_attribute_description": "Werk het label en de beschrijving voor dit attribuut bij.",
|
"edit_attribute_description": "Werk het label en de beschrijving voor dit attribuut bij.",
|
||||||
"edit_attribute_values": "Attributen bewerken",
|
"edit_attribute_values": "Attributen bewerken",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Ongeldige CSV-kolomna(a)m(en): {columns}. Kolomnamen die nieuwe kenmerken worden, mogen alleen kleine letters, cijfers en underscores bevatten en moeten beginnen met een letter.",
|
"invalid_csv_column_names": "Ongeldige CSV-kolomna(a)m(en): {columns}. Kolomnamen die nieuwe kenmerken worden, mogen alleen kleine letters, cijfers en underscores bevatten en moeten beginnen met een letter.",
|
||||||
"invalid_date_format": "Ongeldig datumformaat. Gebruik een geldige datum.",
|
"invalid_date_format": "Ongeldig datumformaat. Gebruik een geldige datum.",
|
||||||
"invalid_number_format": "Ongeldig getalformaat. Voer een geldig getal in.",
|
"invalid_number_format": "Ongeldig getalformaat. Voer een geldig getal in.",
|
||||||
"no_activity_yet": "Nog geen activiteit",
|
|
||||||
"no_published_link_surveys_available": "Geen gepubliceerde link-enquêtes beschikbaar. Publiceer eerst een link-enquête.",
|
"no_published_link_surveys_available": "Geen gepubliceerde link-enquêtes beschikbaar. Publiceer eerst een link-enquête.",
|
||||||
"no_published_surveys": "Geen gepubliceerde enquêtes",
|
"no_published_surveys": "Geen gepubliceerde enquêtes",
|
||||||
"no_responses_found": "Geen reacties gevonden",
|
"no_responses_found": "Geen reacties gevonden",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Selecteer een enquête",
|
"select_a_survey": "Selecteer een enquête",
|
||||||
"select_attribute": "Selecteer Kenmerk",
|
"select_attribute": "Selecteer Kenmerk",
|
||||||
"select_attribute_key": "Selecteer kenmerksleutel",
|
"select_attribute_key": "Selecteer kenmerksleutel",
|
||||||
"survey_viewed": "Enquête bekeken",
|
|
||||||
"survey_viewed_at": "Bekeken op",
|
|
||||||
"system_attributes": "Systeemkenmerken",
|
"system_attributes": "Systeemkenmerken",
|
||||||
"unlock_contacts_description": "Beheer contacten en verstuur gerichte enquêtes",
|
"unlock_contacts_description": "Beheer contacten en verstuur gerichte enquêtes",
|
||||||
"unlock_contacts_title": "Ontgrendel contacten met een hoger abonnement",
|
"unlock_contacts_title": "Ontgrendel contacten met een hoger abonnement",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Link Google Spreadsheet",
|
"link_google_sheet": "Link Google Spreadsheet",
|
||||||
"link_new_sheet": "Nieuw blad koppelen",
|
"link_new_sheet": "Nieuw blad koppelen",
|
||||||
"no_integrations_yet": "Uw Google Spreadsheet-integraties verschijnen hier zodra u ze toevoegt. ⏲️",
|
"no_integrations_yet": "Uw Google Spreadsheet-integraties verschijnen hier zodra u ze toevoegt. ⏲️",
|
||||||
"reconnect_button": "Maak opnieuw verbinding",
|
"spreadsheet_url": "Spreadsheet-URL"
|
||||||
"reconnect_button_description": "Je Google Sheets-verbinding is verlopen. Maak opnieuw verbinding om door te gaan met het synchroniseren van antwoorden. Je bestaande spreadsheetlinks en gegevens blijven behouden.",
|
|
||||||
"reconnect_button_tooltip": "Maak opnieuw verbinding met de integratie om je toegang te vernieuwen. Je bestaande spreadsheetlinks en gegevens blijven behouden.",
|
|
||||||
"spreadsheet_permission_error": "Je hebt geen toestemming om deze spreadsheet te openen. Zorg ervoor dat de spreadsheet is gedeeld met je Google-account en dat je schrijftoegang hebt tot de spreadsheet.",
|
|
||||||
"spreadsheet_url": "Spreadsheet-URL",
|
|
||||||
"token_expired_error": "Het vernieuwingstoken van Google Sheets is verlopen of ingetrokken. Maak opnieuw verbinding met de integratie."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Inclusief gemaakt op",
|
"include_created_at": "Inclusief gemaakt op",
|
||||||
"include_hidden_fields": "Inclusief verborgen velden",
|
"include_hidden_fields": "Inclusief verborgen velden",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Hé {userName}",
|
"email_customization_preview_email_heading": "Hé {userName}",
|
||||||
"email_customization_preview_email_text": "Dit is een e-mailvoorbeeld om u te laten zien welk logo in de e-mails wordt weergegeven.",
|
"email_customization_preview_email_text": "Dit is een e-mailvoorbeeld om u te laten zien welk logo in de e-mails wordt weergegeven.",
|
||||||
"error_deleting_organization_please_try_again": "Fout bij verwijderen van organisatie. Probeer het opnieuw.",
|
"error_deleting_organization_please_try_again": "Fout bij verwijderen van organisatie. Probeer het opnieuw.",
|
||||||
"from_your_organization": "{memberName} uit je organisatie",
|
"from_your_organization": "vanuit uw organisatie",
|
||||||
"invitation_sent_once_more": "Uitnodiging nogmaals verzonden.",
|
"invitation_sent_once_more": "Uitnodiging nogmaals verzonden.",
|
||||||
"invite_deleted_successfully": "Uitnodiging succesvol verwijderd",
|
"invite_deleted_successfully": "Uitnodiging succesvol verwijderd",
|
||||||
"invite_expires_on": "Uitnodiging verloopt op {date}",
|
"invite_expires_on": "Uitnodiging verloopt op {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Voeg een tijdelijke aanduiding toe om aan te geven of er geen waarde is om te onthouden.",
|
"add_fallback_placeholder": "Voeg een tijdelijke aanduiding toe om aan te geven of er geen waarde is om te onthouden.",
|
||||||
"add_hidden_field_id": "Voeg een verborgen veld-ID toe",
|
"add_hidden_field_id": "Voeg een verborgen veld-ID toe",
|
||||||
"add_highlight_border": "Markeerrand toevoegen",
|
"add_highlight_border": "Markeerrand toevoegen",
|
||||||
"add_highlight_border_description": "Geldt alleen voor in-product enquêtes.",
|
|
||||||
"add_logic": "Voeg logica toe",
|
"add_logic": "Voeg logica toe",
|
||||||
"add_none_of_the_above": "Voeg 'Geen van bovenstaande' toe",
|
"add_none_of_the_above": "Voeg 'Geen van bovenstaande' toe",
|
||||||
"add_option": "Optie toevoegen",
|
"add_option": "Optie toevoegen",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Follow-up bijgewerkt en wordt opgeslagen zodra u de enquête opslaat.",
|
"follow_ups_modal_updated_successfull_toast": "Follow-up bijgewerkt en wordt opgeslagen zodra u de enquête opslaat.",
|
||||||
"follow_ups_new": "Nieuw vervolg",
|
"follow_ups_new": "Nieuw vervolg",
|
||||||
"follow_ups_upgrade_button_text": "Upgrade om follow-ups mogelijk te maken",
|
"follow_ups_upgrade_button_text": "Upgrade om follow-ups mogelijk te maken",
|
||||||
|
"form_styling": "Vorm styling",
|
||||||
"formbricks_sdk_is_not_connected": "Formbricks SDK is niet verbonden",
|
"formbricks_sdk_is_not_connected": "Formbricks SDK is niet verbonden",
|
||||||
"four_points": "4 punten",
|
"four_points": "4 punten",
|
||||||
"heading": "Rubriek",
|
"heading": "Rubriek",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Reactielimieten, omleidingen en meer.",
|
"response_limits_redirections_and_more": "Reactielimieten, omleidingen en meer.",
|
||||||
"response_options": "Reactieopties",
|
"response_options": "Reactieopties",
|
||||||
"roundness": "Rondheid",
|
"roundness": "Rondheid",
|
||||||
"roundness_description": "Bepaalt hoe afgerond de hoeken zijn.",
|
"roundness_description": "Bepaalt hoe afgerond de kaarthoeken zijn.",
|
||||||
"row_used_in_logic_error": "Deze rij wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
|
"row_used_in_logic_error": "Deze rij wordt gebruikt in de logica van vraag {questionIndex}. Verwijder het eerst uit de logica.",
|
||||||
"rows": "Rijen",
|
"rows": "Rijen",
|
||||||
"save_and_close": "Opslaan en sluiten",
|
"save_and_close": "Opslaan en sluiten",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Deze gratis en open source-enquête is gesloten",
|
"survey_completed_subheading": "Deze gratis en open source-enquête is gesloten",
|
||||||
"survey_display_settings": "Enquêteweergave-instellingen",
|
"survey_display_settings": "Enquêteweergave-instellingen",
|
||||||
"survey_placement": "Enquête plaatsing",
|
"survey_placement": "Enquête plaatsing",
|
||||||
"survey_styling": "Vorm styling",
|
|
||||||
"survey_trigger": "Enquêtetrigger",
|
"survey_trigger": "Enquêtetrigger",
|
||||||
"switch_multi_language_on_to_get_started": "Schakel meertaligheid in om te beginnen 👉",
|
"switch_multi_language_on_to_get_started": "Schakel meertaligheid in om te beginnen 👉",
|
||||||
"target_block_not_found": "Doelblok niet gevonden",
|
"target_block_not_found": "Doelblok niet gevonden",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Welkomstbericht",
|
"welcome_message": "Welkomstbericht",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Zonder filter kunnen al uw gebruikers worden bevraagd.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Zonder filter kunnen al uw gebruikers worden bevraagd.",
|
||||||
"you_have_not_created_a_segment_yet": "U heeft nog geen segment aangemaakt",
|
"you_have_not_created_a_segment_yet": "U heeft nog geen segment aangemaakt",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Je moet twee of meer talen hebben ingesteld in je werkruimte om met vertalingen te kunnen werken.",
|
||||||
"your_description_here_recall_information_with": "Uw beschrijving hier. Roep informatie op met @",
|
"your_description_here_recall_information_with": "Uw beschrijving hier. Roep informatie op met @",
|
||||||
"your_question_here_recall_information_with": "Uw vraag hier. Roep informatie op met @",
|
"your_question_here_recall_information_with": "Uw vraag hier. Roep informatie op met @",
|
||||||
"your_web_app": "Uw web-app",
|
"your_web_app": "Uw web-app",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Gefilterde reacties (Excel)",
|
"filtered_responses_excel": "Gefilterde reacties (Excel)",
|
||||||
"generating_qr_code": "QR-code genereren",
|
"generating_qr_code": "QR-code genereren",
|
||||||
"impressions": "Indrukken",
|
"impressions": "Indrukken",
|
||||||
"impressions_identified_only": "Alleen weergaven van geïdentificeerde contacten worden getoond",
|
|
||||||
"impressions_tooltip": "Aantal keren dat de enquête is bekeken.",
|
"impressions_tooltip": "Aantal keren dat de enquête is bekeken.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "De enquête wordt getoond aan gebruikers van uw website die voldoen aan de onderstaande criteria",
|
"connection_description": "De enquête wordt getoond aan gebruikers van uw website die voldoen aan de onderstaande criteria",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Laatste kwartaal",
|
"last_quarter": "Laatste kwartaal",
|
||||||
"last_year": "Vorig jaar",
|
"last_year": "Vorig jaar",
|
||||||
"limit": "Beperken",
|
"limit": "Beperken",
|
||||||
"no_identified_impressions": "Geen weergaven van geïdentificeerde contacten",
|
|
||||||
"no_responses_found": "Geen reacties gevonden",
|
"no_responses_found": "Geen reacties gevonden",
|
||||||
"other_values_found": "Andere waarden gevonden",
|
"other_values_found": "Andere waarden gevonden",
|
||||||
"overall": "Algemeen",
|
"overall": "Algemeen",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Begint",
|
"starts": "Begint",
|
||||||
"starts_tooltip": "Aantal keren dat de enquête is gestart.",
|
"starts_tooltip": "Aantal keren dat de enquête is gestart.",
|
||||||
"survey_reset_successfully": "Enquête opnieuw ingesteld! {responseCount} reacties en {displayCount} displays zijn verwijderd.",
|
"survey_reset_successfully": "Enquête opnieuw ingesteld! {responseCount} reacties en {displayCount} displays zijn verwijderd.",
|
||||||
"survey_results": "Resultaten van {surveyName}",
|
|
||||||
"this_month": "Deze maand",
|
"this_month": "Deze maand",
|
||||||
"this_quarter": "Dit kwartaal",
|
"this_quarter": "Dit kwartaal",
|
||||||
"this_year": "Dit jaar",
|
"this_year": "Dit jaar",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Kleurt het gevulde deel van de balk.",
|
"advanced_styling_field_indicator_bg_description": "Kleurt het gevulde deel van de balk.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Rondt de invoerhoeken af.",
|
"advanced_styling_field_input_border_radius_description": "Rondt de invoerhoeken af.",
|
||||||
"advanced_styling_field_input_font_size_description": "Schaalt de getypte tekst in invoervelden.",
|
"advanced_styling_field_input_font_size_description": "Schaalt de getypte tekst in invoervelden.",
|
||||||
"advanced_styling_field_input_height_description": "Bepaalt de min. hoogte van het invoerveld.",
|
"advanced_styling_field_input_height_description": "Bepaalt de minimale hoogte van het invoerveld.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.",
|
"advanced_styling_field_input_padding_x_description": "Voegt ruimte toe aan de linker- en rechterkant.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.",
|
"advanced_styling_field_input_padding_y_description": "Voegt ruimte toe aan de boven- en onderkant.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Vervaagt de tijdelijke aanwijzingstekst.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Vervaagt de tijdelijke aanwijzingstekst.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Kleurt de getypte tekst in invoervelden.",
|
"advanced_styling_field_input_text_description": "Kleurt de getypte tekst in invoervelden.",
|
||||||
"advanced_styling_field_option_bg": "Achtergrond",
|
"advanced_styling_field_option_bg": "Achtergrond",
|
||||||
"advanced_styling_field_option_bg_description": "Vult de optie-items.",
|
"advanced_styling_field_option_bg_description": "Vult de optie-items.",
|
||||||
"advanced_styling_field_option_border": "Randkleur",
|
|
||||||
"advanced_styling_field_option_border_description": "Omlijnt radio- en checkboxopties.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Rondt de hoeken van opties af.",
|
"advanced_styling_field_option_border_radius_description": "Rondt de hoeken van opties af.",
|
||||||
"advanced_styling_field_option_font_size_description": "Schaalt de tekst van optielabels.",
|
"advanced_styling_field_option_font_size_description": "Schaalt de tekst van optielabels.",
|
||||||
"advanced_styling_field_option_label": "Labelkleur",
|
"advanced_styling_field_option_label": "Labelkleur",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Nee, dank je!",
|
"preview_survey_question_2_choice_2_label": "Nee, dank je!",
|
||||||
"preview_survey_question_2_headline": "Wil je op de hoogte blijven?",
|
"preview_survey_question_2_headline": "Wil je op de hoogte blijven?",
|
||||||
"preview_survey_question_2_subheader": "Dit is een voorbeeldbeschrijving.",
|
"preview_survey_question_2_subheader": "Dit is een voorbeeldbeschrijving.",
|
||||||
"preview_survey_question_open_text_headline": "Wil je nog iets delen?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Typ hier je antwoord...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Je feedback helpt ons verbeteren.",
|
|
||||||
"preview_survey_welcome_card_headline": "Welkom!",
|
"preview_survey_welcome_card_headline": "Welkom!",
|
||||||
"prioritize_features_description": "Identificeer functies die uw gebruikers het meest en het minst nodig hebben.",
|
"prioritize_features_description": "Identificeer functies die uw gebruikers het meest en het minst nodig hebben.",
|
||||||
"prioritize_features_name": "Geef prioriteit aan functies",
|
"prioritize_features_name": "Geef prioriteit aan functies",
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "permitir",
|
"allow": "permitir",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Permitir que os usuários saiam clicando fora da pesquisa",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Permitir que os usuários saiam clicando fora da pesquisa",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Ocorreu um erro desconhecido ao deletar {type}s",
|
"an_unknown_error_occurred_while_deleting_table_items": "Ocorreu um erro desconhecido ao deletar {type}s",
|
||||||
|
"analysis": "Análise",
|
||||||
"and": "E",
|
"and": "E",
|
||||||
"and_response_limit_of": "e limite de resposta de",
|
"and_response_limit_of": "e limite de resposta de",
|
||||||
"anonymous": "Anônimo",
|
"anonymous": "Anônimo",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copy_code": "Copiar código",
|
"copy_code": "Copiar código",
|
||||||
"copy_link": "Copiar Link",
|
"copy_link": "Copiar Link",
|
||||||
"count_attributes": "{count, plural, one {{count} atributo} other {{count} atributos}}",
|
"count_attributes": "{value, plural, one {{value} atributo} other {{value} atributos}}",
|
||||||
"count_contacts": "{count, plural, one {# contato} other {# contatos} }",
|
"count_contacts": "{value, plural, one {# contato} other {# contatos} }",
|
||||||
"count_members": "{count, plural, one {{count} membro} other {{count} membros}}",
|
"count_responses": "{value, plural, other {# respostas}}",
|
||||||
"count_responses": "{count, plural, other {# respostas}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} seleção} other {{count} seleções}}",
|
|
||||||
"create_new_organization": "Criar nova organização",
|
"create_new_organization": "Criar nova organização",
|
||||||
"create_segment": "Criar segmento",
|
"create_segment": "Criar segmento",
|
||||||
"create_survey": "Criar pesquisa",
|
"create_survey": "Criar pesquisa",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "dias",
|
"days": "dias",
|
||||||
"default": "Padrão",
|
"default": "Padrão",
|
||||||
"delete": "Apagar",
|
"delete": "Apagar",
|
||||||
"delete_what": "Excluir {deleteWhat}",
|
|
||||||
"description": "Descrição",
|
"description": "Descrição",
|
||||||
"dev_env": "Ambiente de Desenvolvimento",
|
"dev_env": "Ambiente de Desenvolvimento",
|
||||||
"development": "Desenvolvimento",
|
"development": "Desenvolvimento",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"error_component_description": "Esse recurso não existe ou você não tem permissão para acessá-lo.",
|
"error_component_description": "Esse recurso não existe ou você não tem permissão para acessá-lo.",
|
||||||
"error_component_title": "Erro ao carregar recursos",
|
"error_component_title": "Erro ao carregar recursos",
|
||||||
"error_loading_data": "Erro ao carregar dados",
|
|
||||||
"error_rate_limit_description": "Número máximo de requisições atingido. Por favor, tente novamente mais tarde.",
|
"error_rate_limit_description": "Número máximo de requisições atingido. Por favor, tente novamente mais tarde.",
|
||||||
"error_rate_limit_title": "Limite de Taxa Excedido",
|
"error_rate_limit_title": "Limite de Taxa Excedido",
|
||||||
"expand_rows": "Expandir linhas",
|
"expand_rows": "Expandir linhas",
|
||||||
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
|
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
|
||||||
"failed_to_load_organizations": "Falha ao carregar organizações",
|
"failed_to_load_organizations": "Falha ao carregar organizações",
|
||||||
"failed_to_load_workspaces": "Falha ao carregar projetos",
|
"failed_to_load_workspaces": "Falha ao carregar projetos",
|
||||||
"filter": "Filtro",
|
|
||||||
"finish": "Terminar",
|
"finish": "Terminar",
|
||||||
"first_name": "Primeiro nome",
|
|
||||||
"follow_these": "Siga esses",
|
"follow_these": "Siga esses",
|
||||||
"formbricks_version": "Versão do Formbricks",
|
"formbricks_version": "Versão do Formbricks",
|
||||||
"full_name": "Nome completo",
|
"full_name": "Nome completo",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Campo oculto",
|
"hidden_field": "Campo oculto",
|
||||||
"hidden_fields": "Campos ocultos",
|
"hidden_fields": "Campos ocultos",
|
||||||
"hide_column": "Ocultar coluna",
|
"hide_column": "Ocultar coluna",
|
||||||
"id": "ID",
|
|
||||||
"image": "imagem",
|
"image": "imagem",
|
||||||
"images": "Imagens",
|
"images": "Imagens",
|
||||||
"import": "importar",
|
"import": "importar",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Chave",
|
"key": "Chave",
|
||||||
"label": "Etiqueta",
|
"label": "Etiqueta",
|
||||||
"language": "Língua",
|
"language": "Língua",
|
||||||
"last_name": "Sobrenome",
|
|
||||||
"learn_more": "Saiba mais",
|
"learn_more": "Saiba mais",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "sobreposição leve",
|
"light_overlay": "sobreposição leve",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Aparência e Experiência",
|
"look_and_feel": "Aparência e Experiência",
|
||||||
"manage": "gerenciar",
|
"manage": "gerenciar",
|
||||||
"marketing": "marketing",
|
"marketing": "marketing",
|
||||||
|
"member": "Membros",
|
||||||
"members": "Membros",
|
"members": "Membros",
|
||||||
"members_and_teams": "Membros e equipes",
|
"members_and_teams": "Membros e equipes",
|
||||||
"membership_not_found": "Assinatura não encontrada",
|
"membership_not_found": "Assinatura não encontrada",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Descer",
|
"move_down": "Descer",
|
||||||
"move_up": "Subir",
|
"move_up": "Subir",
|
||||||
"multiple_languages": "Vários idiomas",
|
"multiple_languages": "Vários idiomas",
|
||||||
"my_product": "meu produto",
|
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"new": "Novo",
|
"new": "Novo",
|
||||||
"new_version_available": "Formbricks {version} chegou. Atualize agora!",
|
"new_version_available": "Formbricks {version} chegou. Atualize agora!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Selecionar times",
|
"select_teams": "Selecionar times",
|
||||||
"selected": "Selecionado",
|
"selected": "Selecionado",
|
||||||
"selected_questions": "Perguntas selecionadas",
|
"selected_questions": "Perguntas selecionadas",
|
||||||
|
"selection": "seleção",
|
||||||
|
"selections": "seleções",
|
||||||
"send_test_email": "Enviar e-mail de teste",
|
"send_test_email": "Enviar e-mail de teste",
|
||||||
"session_not_found": "Sessão não encontrada",
|
"session_not_found": "Sessão não encontrada",
|
||||||
"settings": "Configurações",
|
"settings": "Configurações",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Canto Superior Direito",
|
"top_right": "Canto Superior Direito",
|
||||||
"try_again": "Tenta de novo",
|
"try_again": "Tenta de novo",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"unknown_survey": "Pesquisa desconhecida",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
||||||
"update": "atualizar",
|
"update": "atualizar",
|
||||||
"updated": "atualizado",
|
"updated": "atualizado",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Atualizar contatos",
|
"contacts_table_refresh": "Atualizar contatos",
|
||||||
"contacts_table_refresh_success": "Contatos atualizados com sucesso",
|
"contacts_table_refresh_success": "Contatos atualizados com sucesso",
|
||||||
"create_attribute": "Criar atributo",
|
"create_attribute": "Criar atributo",
|
||||||
|
"create_key": "Criar chave",
|
||||||
"create_new_attribute": "Criar novo atributo",
|
"create_new_attribute": "Criar novo atributo",
|
||||||
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
||||||
"custom_attributes": "Atributos personalizados",
|
"custom_attributes": "Atributos personalizados",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Isso excluirá o atributo selecionado. Todos os dados de contato associados a este atributo serão perdidos.} other {Isso excluirá os atributos selecionados. Todos os dados de contato associados a estes atributos serão perdidos.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Isso excluirá o atributo selecionado. Todos os dados de contato associados a este atributo serão perdidos.} other {Isso excluirá os atributos selecionados. Todos os dados de contato associados a estes atributos serão perdidos.}}",
|
||||||
"delete_contact_confirmation": "Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
"delete_contact_confirmation": "Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos. Se este contato tiver respostas que contam para cotas da pesquisa, as contagens das cotas serão reduzidas, mas os limites das cotas permanecerão inalterados.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isso irá apagar todas as respostas da pesquisa e atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos. Se este contato tiver respostas que contam para cotas da pesquisa, as contagens das cotas serão reduzidas, mas os limites das cotas permanecerão inalterados.}}",
|
||||||
"displays": "Exibições",
|
|
||||||
"edit_attribute": "Editar atributo",
|
"edit_attribute": "Editar atributo",
|
||||||
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
||||||
"edit_attribute_values": "Editar atributos",
|
"edit_attribute_values": "Editar atributos",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Nome(s) de coluna CSV inválido(s): {columns}. Os nomes de colunas que se tornarão novos atributos devem conter apenas letras minúsculas, números e sublinhados, e devem começar com uma letra.",
|
"invalid_csv_column_names": "Nome(s) de coluna CSV inválido(s): {columns}. Os nomes de colunas que se tornarão novos atributos devem conter apenas letras minúsculas, números e sublinhados, e devem começar com uma letra.",
|
||||||
"invalid_date_format": "Formato de data inválido. Por favor, use uma data válida.",
|
"invalid_date_format": "Formato de data inválido. Por favor, use uma data válida.",
|
||||||
"invalid_number_format": "Formato de número inválido. Por favor, insira um número válido.",
|
"invalid_number_format": "Formato de número inválido. Por favor, insira um número válido.",
|
||||||
"no_activity_yet": "Nenhuma atividade ainda",
|
|
||||||
"no_published_link_surveys_available": "Não há pesquisas de link publicadas disponíveis. Por favor, publique uma pesquisa de link primeiro.",
|
"no_published_link_surveys_available": "Não há pesquisas de link publicadas disponíveis. Por favor, publique uma pesquisa de link primeiro.",
|
||||||
"no_published_surveys": "Sem pesquisas publicadas",
|
"no_published_surveys": "Sem pesquisas publicadas",
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Selecione uma pesquisa",
|
"select_a_survey": "Selecione uma pesquisa",
|
||||||
"select_attribute": "Selecionar Atributo",
|
"select_attribute": "Selecionar Atributo",
|
||||||
"select_attribute_key": "Selecionar chave de atributo",
|
"select_attribute_key": "Selecionar chave de atributo",
|
||||||
"survey_viewed": "Pesquisa visualizada",
|
|
||||||
"survey_viewed_at": "Visualizada em",
|
|
||||||
"system_attributes": "Atributos do sistema",
|
"system_attributes": "Atributos do sistema",
|
||||||
"unlock_contacts_description": "Gerencie contatos e envie pesquisas direcionadas",
|
"unlock_contacts_description": "Gerencie contatos e envie pesquisas direcionadas",
|
||||||
"unlock_contacts_title": "Desbloqueie contatos com um plano superior",
|
"unlock_contacts_title": "Desbloqueie contatos com um plano superior",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Link da Planilha do Google",
|
"link_google_sheet": "Link da Planilha do Google",
|
||||||
"link_new_sheet": "Vincular nova planilha",
|
"link_new_sheet": "Vincular nova planilha",
|
||||||
"no_integrations_yet": "Suas integrações do Google Sheets vão aparecer aqui assim que você adicioná-las. ⏲️",
|
"no_integrations_yet": "Suas integrações do Google Sheets vão aparecer aqui assim que você adicioná-las. ⏲️",
|
||||||
"reconnect_button": "Reconectar",
|
"spreadsheet_url": "URL da planilha"
|
||||||
"reconnect_button_description": "Sua conexão com o Google Sheets expirou. Reconecte para continuar sincronizando respostas. Seus links de planilhas e dados existentes serão preservados.",
|
|
||||||
"reconnect_button_tooltip": "Reconecte a integração para atualizar seu acesso. Seus links de planilhas e dados existentes serão preservados.",
|
|
||||||
"spreadsheet_permission_error": "Você não tem permissão para acessar esta planilha. Certifique-se de que a planilha está compartilhada com sua conta do Google e que você tem acesso de escrita à planilha.",
|
|
||||||
"spreadsheet_url": "URL da planilha",
|
|
||||||
"token_expired_error": "O token de atualização do Google Sheets expirou ou foi revogado. Reconecte a integração."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Incluir Data de Criação",
|
"include_created_at": "Incluir Data de Criação",
|
||||||
"include_hidden_fields": "Incluir Campos Ocultos",
|
"include_hidden_fields": "Incluir Campos Ocultos",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Oi {userName}",
|
"email_customization_preview_email_heading": "Oi {userName}",
|
||||||
"email_customization_preview_email_text": "Esta é uma pré-visualização de e-mail para mostrar qual logo será renderizado nos e-mails.",
|
"email_customization_preview_email_text": "Esta é uma pré-visualização de e-mail para mostrar qual logo será renderizado nos e-mails.",
|
||||||
"error_deleting_organization_please_try_again": "Erro ao deletar a organização. Por favor, tente novamente.",
|
"error_deleting_organization_please_try_again": "Erro ao deletar a organização. Por favor, tente novamente.",
|
||||||
"from_your_organization": "{memberName} da sua organização",
|
"from_your_organization": "da sua organização",
|
||||||
"invitation_sent_once_more": "Convite enviado de novo.",
|
"invitation_sent_once_more": "Convite enviado de novo.",
|
||||||
"invite_deleted_successfully": "Convite deletado com sucesso",
|
"invite_deleted_successfully": "Convite deletado com sucesso",
|
||||||
"invite_expires_on": "O convite expira em {date}",
|
"invite_expires_on": "O convite expira em {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Adicionar um texto padrão para mostrar se a pergunta for ignorada:",
|
"add_fallback_placeholder": "Adicionar um texto padrão para mostrar se a pergunta for ignorada:",
|
||||||
"add_hidden_field_id": "Adicionar campo oculto ID",
|
"add_hidden_field_id": "Adicionar campo oculto ID",
|
||||||
"add_highlight_border": "Adicionar borda de destaque",
|
"add_highlight_border": "Adicionar borda de destaque",
|
||||||
"add_highlight_border_description": "Aplica-se apenas a pesquisas no produto.",
|
|
||||||
"add_logic": "Adicionar lógica",
|
"add_logic": "Adicionar lógica",
|
||||||
"add_none_of_the_above": "Adicionar \"Nenhuma das opções acima\"",
|
"add_none_of_the_above": "Adicionar \"Nenhuma das opções acima\"",
|
||||||
"add_option": "Adicionar opção",
|
"add_option": "Adicionar opção",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Acompanhamento atualizado e será salvo assim que você salvar a pesquisa.",
|
"follow_ups_modal_updated_successfull_toast": "Acompanhamento atualizado e será salvo assim que você salvar a pesquisa.",
|
||||||
"follow_ups_new": "Novo acompanhamento",
|
"follow_ups_new": "Novo acompanhamento",
|
||||||
"follow_ups_upgrade_button_text": "Atualize para habilitar os Acompanhamentos",
|
"follow_ups_upgrade_button_text": "Atualize para habilitar os Acompanhamentos",
|
||||||
|
"form_styling": "Estilização de Formulários",
|
||||||
"formbricks_sdk_is_not_connected": "O SDK do Formbricks não está conectado",
|
"formbricks_sdk_is_not_connected": "O SDK do Formbricks não está conectado",
|
||||||
"four_points": "4 pontos",
|
"four_points": "4 pontos",
|
||||||
"heading": "Título",
|
"heading": "Título",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
||||||
"response_options": "Opções de Resposta",
|
"response_options": "Opções de Resposta",
|
||||||
"roundness": "Circularidade",
|
"roundness": "Circularidade",
|
||||||
"roundness_description": "Controla o arredondamento dos cantos.",
|
"roundness_description": "Controla o arredondamento dos cantos do cartão.",
|
||||||
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
||||||
"rows": "linhas",
|
"rows": "linhas",
|
||||||
"save_and_close": "Salvar e Fechar",
|
"save_and_close": "Salvar e Fechar",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Essa pesquisa gratuita e de código aberto foi encerrada",
|
"survey_completed_subheading": "Essa pesquisa gratuita e de código aberto foi encerrada",
|
||||||
"survey_display_settings": "Configurações de Exibição da Pesquisa",
|
"survey_display_settings": "Configurações de Exibição da Pesquisa",
|
||||||
"survey_placement": "Posicionamento da Pesquisa",
|
"survey_placement": "Posicionamento da Pesquisa",
|
||||||
"survey_styling": "Estilização de Formulários",
|
|
||||||
"survey_trigger": "Gatilho de Pesquisa",
|
"survey_trigger": "Gatilho de Pesquisa",
|
||||||
"switch_multi_language_on_to_get_started": "Ative o modo multilíngue para começar 👉",
|
"switch_multi_language_on_to_get_started": "Ative o modo multilíngue para começar 👉",
|
||||||
"target_block_not_found": "Bloco de destino não encontrado",
|
"target_block_not_found": "Bloco de destino não encontrado",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Mensagem de boas-vindas",
|
"welcome_message": "Mensagem de boas-vindas",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Sem um filtro, todos os seus usuários podem ser pesquisados.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Sem um filtro, todos os seus usuários podem ser pesquisados.",
|
||||||
"you_have_not_created_a_segment_yet": "Você ainda não criou um segmento.",
|
"you_have_not_created_a_segment_yet": "Você ainda não criou um segmento.",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Você precisa ter dois ou mais idiomas configurados em seu espaço de trabalho para trabalhar com traduções.",
|
||||||
"your_description_here_recall_information_with": "Sua descrição aqui. Lembre-se de informações com @",
|
"your_description_here_recall_information_with": "Sua descrição aqui. Lembre-se de informações com @",
|
||||||
"your_question_here_recall_information_with": "Sua pergunta aqui. Lembre-se de informações com @",
|
"your_question_here_recall_information_with": "Sua pergunta aqui. Lembre-se de informações com @",
|
||||||
"your_web_app": "Sua aplicação web",
|
"your_web_app": "Sua aplicação web",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
||||||
"generating_qr_code": "Gerando código QR",
|
"generating_qr_code": "Gerando código QR",
|
||||||
"impressions": "Impressões",
|
"impressions": "Impressões",
|
||||||
"impressions_identified_only": "Mostrando apenas impressões de contatos identificados",
|
|
||||||
"impressions_tooltip": "Número de vezes que a pesquisa foi visualizada.",
|
"impressions_tooltip": "Número de vezes que a pesquisa foi visualizada.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "A pesquisa será exibida para usuários do seu site, que atendam aos critérios listados abaixo",
|
"connection_description": "A pesquisa será exibida para usuários do seu site, que atendam aos critérios listados abaixo",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Último trimestre",
|
"last_quarter": "Último trimestre",
|
||||||
"last_year": "Último ano",
|
"last_year": "Último ano",
|
||||||
"limit": "Limite",
|
"limit": "Limite",
|
||||||
"no_identified_impressions": "Nenhuma impressão de contatos identificados",
|
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
"other_values_found": "Outros valores encontrados",
|
"other_values_found": "Outros valores encontrados",
|
||||||
"overall": "No geral",
|
"overall": "No geral",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "começa",
|
"starts": "começa",
|
||||||
"starts_tooltip": "Número de vezes que a pesquisa foi iniciada.",
|
"starts_tooltip": "Número de vezes que a pesquisa foi iniciada.",
|
||||||
"survey_reset_successfully": "Pesquisa redefinida com sucesso! {responseCount} respostas e {displayCount} exibições foram deletadas.",
|
"survey_reset_successfully": "Pesquisa redefinida com sucesso! {responseCount} respostas e {displayCount} exibições foram deletadas.",
|
||||||
"survey_results": "Resultados de {surveyName}",
|
|
||||||
"this_month": "Este mês",
|
"this_month": "Este mês",
|
||||||
"this_quarter": "Este trimestre",
|
"this_quarter": "Este trimestre",
|
||||||
"this_year": "Este ano",
|
"this_year": "Este ano",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.",
|
"advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.",
|
"advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.",
|
||||||
"advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.",
|
"advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.",
|
||||||
"advanced_styling_field_input_height_description": "Controla a altura mínima da entrada.",
|
"advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
"advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Adiciona espaço na parte superior e inferior.",
|
"advanced_styling_field_input_padding_y_description": "Adiciona espaço na parte superior e inferior.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Esmaece o texto de dica do placeholder.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Esmaece o texto de dica do placeholder.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.",
|
"advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.",
|
||||||
"advanced_styling_field_option_bg": "Fundo",
|
"advanced_styling_field_option_bg": "Fundo",
|
||||||
"advanced_styling_field_option_bg_description": "Preenche os itens de opção.",
|
"advanced_styling_field_option_bg_description": "Preenche os itens de opção.",
|
||||||
"advanced_styling_field_option_border": "Cor da borda",
|
|
||||||
"advanced_styling_field_option_border_description": "Contorna as opções de botões de rádio e caixas de seleção.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.",
|
"advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.",
|
||||||
"advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto do rótulo da opção.",
|
"advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto do rótulo da opção.",
|
||||||
"advanced_styling_field_option_label": "Cor do rótulo",
|
"advanced_styling_field_option_label": "Cor do rótulo",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
||||||
"preview_survey_question_2_headline": "Quer ficar por dentro?",
|
"preview_survey_question_2_headline": "Quer ficar por dentro?",
|
||||||
"preview_survey_question_2_subheader": "Este é um exemplo de descrição.",
|
"preview_survey_question_2_subheader": "Este é um exemplo de descrição.",
|
||||||
"preview_survey_question_open_text_headline": "Tem mais alguma coisa que você gostaria de compartilhar?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Digite sua resposta aqui...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Seu feedback nos ajuda a melhorar.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
||||||
"prioritize_features_description": "Identifique os recursos que seus usuários mais e menos precisam.",
|
"prioritize_features_description": "Identifique os recursos que seus usuários mais e menos precisam.",
|
||||||
"prioritize_features_name": "Priorizar Funcionalidades",
|
"prioritize_features_name": "Priorizar Funcionalidades",
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Permitir",
|
"allow": "Permitir",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Permitir que os utilizadores saiam se clicarem 'sair do questionário'",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Permitir que os utilizadores saiam se clicarem 'sair do questionário'",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Ocorreu um erro desconhecido ao eliminar {type}s",
|
"an_unknown_error_occurred_while_deleting_table_items": "Ocorreu um erro desconhecido ao eliminar {type}s",
|
||||||
|
"analysis": "Análise",
|
||||||
"and": "E",
|
"and": "E",
|
||||||
"and_response_limit_of": "e limite de resposta de",
|
"and_response_limit_of": "e limite de resposta de",
|
||||||
"anonymous": "Anónimo",
|
"anonymous": "Anónimo",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Copiar",
|
"copy": "Copiar",
|
||||||
"copy_code": "Copiar código",
|
"copy_code": "Copiar código",
|
||||||
"copy_link": "Copiar Link",
|
"copy_link": "Copiar Link",
|
||||||
"count_attributes": "{count, plural, one {{count} atributo} other {{count} atributos}}",
|
"count_attributes": "{value, plural, one {{value} atributo} other {{value} atributos}}",
|
||||||
"count_contacts": "{count, plural, one {# contacto} other {# contactos} }",
|
"count_contacts": "{value, plural, one {# contacto} other {# contactos} }",
|
||||||
"count_members": "{count, plural, one {{count} membro} other {{count} membros}}",
|
"count_responses": "{value, plural, other {# respostas}}",
|
||||||
"count_responses": "{count, plural, other {# respostas}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} seleção} other {{count} seleções}}",
|
|
||||||
"create_new_organization": "Criar nova organização",
|
"create_new_organization": "Criar nova organização",
|
||||||
"create_segment": "Criar segmento",
|
"create_segment": "Criar segmento",
|
||||||
"create_survey": "Criar inquérito",
|
"create_survey": "Criar inquérito",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "dias",
|
"days": "dias",
|
||||||
"default": "Padrão",
|
"default": "Padrão",
|
||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"delete_what": "Eliminar {deleteWhat}",
|
|
||||||
"description": "Descrição",
|
"description": "Descrição",
|
||||||
"dev_env": "Ambiente de Desenvolvimento",
|
"dev_env": "Ambiente de Desenvolvimento",
|
||||||
"development": "Desenvolvimento",
|
"development": "Desenvolvimento",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"error_component_description": "Este recurso não existe ou não tem os direitos necessários para aceder a ele.",
|
"error_component_description": "Este recurso não existe ou não tem os direitos necessários para aceder a ele.",
|
||||||
"error_component_title": "Erro ao carregar recursos",
|
"error_component_title": "Erro ao carregar recursos",
|
||||||
"error_loading_data": "Erro ao carregar dados",
|
|
||||||
"error_rate_limit_description": "Número máximo de pedidos alcançado. Por favor, tente novamente mais tarde.",
|
"error_rate_limit_description": "Número máximo de pedidos alcançado. Por favor, tente novamente mais tarde.",
|
||||||
"error_rate_limit_title": "Limite de Taxa Excedido",
|
"error_rate_limit_title": "Limite de Taxa Excedido",
|
||||||
"expand_rows": "Expandir linhas",
|
"expand_rows": "Expandir linhas",
|
||||||
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
|
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
|
||||||
"failed_to_load_organizations": "Falha ao carregar organizações",
|
"failed_to_load_organizations": "Falha ao carregar organizações",
|
||||||
"failed_to_load_workspaces": "Falha ao carregar projetos",
|
"failed_to_load_workspaces": "Falha ao carregar projetos",
|
||||||
"filter": "Filtro",
|
|
||||||
"finish": "Concluir",
|
"finish": "Concluir",
|
||||||
"first_name": "Primeiro nome",
|
|
||||||
"follow_these": "Siga estes",
|
"follow_these": "Siga estes",
|
||||||
"formbricks_version": "Versão do Formbricks",
|
"formbricks_version": "Versão do Formbricks",
|
||||||
"full_name": "Nome completo",
|
"full_name": "Nome completo",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Campo oculto",
|
"hidden_field": "Campo oculto",
|
||||||
"hidden_fields": "Campos ocultos",
|
"hidden_fields": "Campos ocultos",
|
||||||
"hide_column": "Ocultar coluna",
|
"hide_column": "Ocultar coluna",
|
||||||
"id": "ID",
|
|
||||||
"image": "Imagem",
|
"image": "Imagem",
|
||||||
"images": "Imagens",
|
"images": "Imagens",
|
||||||
"import": "Importar",
|
"import": "Importar",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Chave",
|
"key": "Chave",
|
||||||
"label": "Etiqueta",
|
"label": "Etiqueta",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"last_name": "Apelido",
|
|
||||||
"learn_more": "Saiba mais",
|
"learn_more": "Saiba mais",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Sobreposição leve",
|
"light_overlay": "Sobreposição leve",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Aparência e Sensação",
|
"look_and_feel": "Aparência e Sensação",
|
||||||
"manage": "Gerir",
|
"manage": "Gerir",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Membro",
|
||||||
"members": "Membros",
|
"members": "Membros",
|
||||||
"members_and_teams": "Membros e equipas",
|
"members_and_teams": "Membros e equipas",
|
||||||
"membership_not_found": "Associação não encontrada",
|
"membership_not_found": "Associação não encontrada",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Mover para baixo",
|
"move_down": "Mover para baixo",
|
||||||
"move_up": "Mover para cima",
|
"move_up": "Mover para cima",
|
||||||
"multiple_languages": "Várias línguas",
|
"multiple_languages": "Várias línguas",
|
||||||
"my_product": "o meu produto",
|
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"new": "Novo",
|
"new": "Novo",
|
||||||
"new_version_available": "Formbricks {version} está aqui. Atualize agora!",
|
"new_version_available": "Formbricks {version} está aqui. Atualize agora!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Selecionar equipas",
|
"select_teams": "Selecionar equipas",
|
||||||
"selected": "Selecionado",
|
"selected": "Selecionado",
|
||||||
"selected_questions": "Perguntas selecionadas",
|
"selected_questions": "Perguntas selecionadas",
|
||||||
|
"selection": "Seleção",
|
||||||
|
"selections": "Seleções",
|
||||||
"send_test_email": "Enviar email de teste",
|
"send_test_email": "Enviar email de teste",
|
||||||
"session_not_found": "Sessão não encontrada",
|
"session_not_found": "Sessão não encontrada",
|
||||||
"settings": "Configurações",
|
"settings": "Configurações",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Superior Direito",
|
"top_right": "Superior Direito",
|
||||||
"try_again": "Tente novamente",
|
"try_again": "Tente novamente",
|
||||||
"type": "Tipo",
|
"type": "Tipo",
|
||||||
"unknown_survey": "Inquérito desconhecido",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Desbloqueie mais projetos com um plano superior.",
|
||||||
"update": "Atualizar",
|
"update": "Atualizar",
|
||||||
"updated": "Atualizado",
|
"updated": "Atualizado",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Atualizar contactos",
|
"contacts_table_refresh": "Atualizar contactos",
|
||||||
"contacts_table_refresh_success": "Contactos atualizados com sucesso",
|
"contacts_table_refresh_success": "Contactos atualizados com sucesso",
|
||||||
"create_attribute": "Criar atributo",
|
"create_attribute": "Criar atributo",
|
||||||
|
"create_key": "Criar chave",
|
||||||
"create_new_attribute": "Criar novo atributo",
|
"create_new_attribute": "Criar novo atributo",
|
||||||
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
"create_new_attribute_description": "Crie um novo atributo para fins de segmentação.",
|
||||||
"custom_attributes": "Atributos personalizados",
|
"custom_attributes": "Atributos personalizados",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Isto irá eliminar o atributo selecionado. Todos os dados de contacto associados a este atributo serão perdidos.} other {Isto irá eliminar os atributos selecionados. Todos os dados de contacto associados a estes atributos serão perdidos.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Isto irá eliminar o atributo selecionado. Todos os dados de contacto associados a este atributo serão perdidos.} other {Isto irá eliminar os atributos selecionados. Todos os dados de contacto associados a estes atributos serão perdidos.}}",
|
||||||
"delete_contact_confirmation": "Isto irá eliminar todas as respostas das pesquisas e os atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
"delete_contact_confirmation": "Isto irá eliminar todas as respostas das pesquisas e os atributos de contato associados a este contato. Qualquer direcionamento e personalização baseados nos dados deste contato serão perdidos.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isto irá eliminar todas as respostas das pesquisas e os atributos de contacto associados a este contacto. Qualquer segmentação e personalização baseados nos dados deste contacto serão perdidos. Se este contacto tiver respostas que contribuam para as quotas das pesquisas, as contagens de quotas serão reduzidas, mas os limites das quotas permanecerão inalterados.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, other {Isto irá eliminar todas as respostas das pesquisas e os atributos de contacto associados a este contacto. Qualquer segmentação e personalização baseados nos dados deste contacto serão perdidos. Se este contacto tiver respostas que contribuam para as quotas das pesquisas, as contagens de quotas serão reduzidas, mas os limites das quotas permanecerão inalterados.}}",
|
||||||
"displays": "Visualizações",
|
|
||||||
"edit_attribute": "Editar atributo",
|
"edit_attribute": "Editar atributo",
|
||||||
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
"edit_attribute_description": "Atualize a etiqueta e a descrição deste atributo.",
|
||||||
"edit_attribute_values": "Editar atributos",
|
"edit_attribute_values": "Editar atributos",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Nome(s) de coluna CSV inválido(s): {columns}. Os nomes de colunas que se tornarão novos atributos devem conter apenas letras minúsculas, números e underscores, e devem começar com uma letra.",
|
"invalid_csv_column_names": "Nome(s) de coluna CSV inválido(s): {columns}. Os nomes de colunas que se tornarão novos atributos devem conter apenas letras minúsculas, números e underscores, e devem começar com uma letra.",
|
||||||
"invalid_date_format": "Formato de data inválido. Por favor, usa uma data válida.",
|
"invalid_date_format": "Formato de data inválido. Por favor, usa uma data válida.",
|
||||||
"invalid_number_format": "Formato de número inválido. Por favor, introduz um número válido.",
|
"invalid_number_format": "Formato de número inválido. Por favor, introduz um número válido.",
|
||||||
"no_activity_yet": "Ainda sem atividade",
|
|
||||||
"no_published_link_surveys_available": "Não existem inquéritos de link publicados disponíveis. Por favor, publique primeiro um inquérito de link.",
|
"no_published_link_surveys_available": "Não existem inquéritos de link publicados disponíveis. Por favor, publique primeiro um inquérito de link.",
|
||||||
"no_published_surveys": "Sem inquéritos publicados",
|
"no_published_surveys": "Sem inquéritos publicados",
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Selecione um inquérito",
|
"select_a_survey": "Selecione um inquérito",
|
||||||
"select_attribute": "Selecionar Atributo",
|
"select_attribute": "Selecionar Atributo",
|
||||||
"select_attribute_key": "Selecionar chave de atributo",
|
"select_attribute_key": "Selecionar chave de atributo",
|
||||||
"survey_viewed": "Inquérito visualizado",
|
|
||||||
"survey_viewed_at": "Visualizado em",
|
|
||||||
"system_attributes": "Atributos do sistema",
|
"system_attributes": "Atributos do sistema",
|
||||||
"unlock_contacts_description": "Gerir contactos e enviar inquéritos direcionados",
|
"unlock_contacts_description": "Gerir contactos e enviar inquéritos direcionados",
|
||||||
"unlock_contacts_title": "Desbloqueie os contactos com um plano superior",
|
"unlock_contacts_title": "Desbloqueie os contactos com um plano superior",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Ligar Folha do Google",
|
"link_google_sheet": "Ligar Folha do Google",
|
||||||
"link_new_sheet": "Ligar nova Folha",
|
"link_new_sheet": "Ligar nova Folha",
|
||||||
"no_integrations_yet": "As suas integrações com o Google Sheets aparecerão aqui assim que as adicionar. ⏲️",
|
"no_integrations_yet": "As suas integrações com o Google Sheets aparecerão aqui assim que as adicionar. ⏲️",
|
||||||
"reconnect_button": "Reconectar",
|
"spreadsheet_url": "URL da folha de cálculo"
|
||||||
"reconnect_button_description": "A tua ligação ao Google Sheets expirou. Por favor, reconecta para continuar a sincronizar respostas. As tuas ligações de folhas de cálculo e dados existentes serão preservados.",
|
|
||||||
"reconnect_button_tooltip": "Reconecta a integração para atualizar o teu acesso. As tuas ligações de folhas de cálculo e dados existentes serão preservados.",
|
|
||||||
"spreadsheet_permission_error": "Não tens permissão para aceder a esta folha de cálculo. Por favor, certifica-te de que a folha de cálculo está partilhada com a tua conta Google e que tens acesso de escrita à folha de cálculo.",
|
|
||||||
"spreadsheet_url": "URL da folha de cálculo",
|
|
||||||
"token_expired_error": "O token de atualização do Google Sheets expirou ou foi revogado. Por favor, reconecta a integração."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Incluir Criado Em",
|
"include_created_at": "Incluir Criado Em",
|
||||||
"include_hidden_fields": "Incluir Campos Ocultos",
|
"include_hidden_fields": "Incluir Campos Ocultos",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Olá {userName}",
|
"email_customization_preview_email_heading": "Olá {userName}",
|
||||||
"email_customization_preview_email_text": "Esta é uma pré-visualização de email para mostrar qual logotipo será exibido nos emails.",
|
"email_customization_preview_email_text": "Esta é uma pré-visualização de email para mostrar qual logotipo será exibido nos emails.",
|
||||||
"error_deleting_organization_please_try_again": "Erro ao eliminar a organização. Por favor, tente novamente.",
|
"error_deleting_organization_please_try_again": "Erro ao eliminar a organização. Por favor, tente novamente.",
|
||||||
"from_your_organization": "{memberName} da sua organização",
|
"from_your_organization": "da sua organização",
|
||||||
"invitation_sent_once_more": "Convite enviado mais uma vez.",
|
"invitation_sent_once_more": "Convite enviado mais uma vez.",
|
||||||
"invite_deleted_successfully": "Convite eliminado com sucesso",
|
"invite_deleted_successfully": "Convite eliminado com sucesso",
|
||||||
"invite_expires_on": "O convite expira em {date}",
|
"invite_expires_on": "O convite expira em {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Adicionar um espaço reservado para mostrar se não houver valor para recordar.",
|
"add_fallback_placeholder": "Adicionar um espaço reservado para mostrar se não houver valor para recordar.",
|
||||||
"add_hidden_field_id": "Adicionar ID do campo oculto",
|
"add_hidden_field_id": "Adicionar ID do campo oculto",
|
||||||
"add_highlight_border": "Adicionar borda de destaque",
|
"add_highlight_border": "Adicionar borda de destaque",
|
||||||
"add_highlight_border_description": "Aplica-se apenas a inquéritos no produto.",
|
|
||||||
"add_logic": "Adicionar lógica",
|
"add_logic": "Adicionar lógica",
|
||||||
"add_none_of_the_above": "Adicionar \"Nenhuma das Opções Acima\"",
|
"add_none_of_the_above": "Adicionar \"Nenhuma das Opções Acima\"",
|
||||||
"add_option": "Adicionar opção",
|
"add_option": "Adicionar opção",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Seguimento atualizado e será guardado assim que guardar o questionário.",
|
"follow_ups_modal_updated_successfull_toast": "Seguimento atualizado e será guardado assim que guardar o questionário.",
|
||||||
"follow_ups_new": "Novo acompanhamento",
|
"follow_ups_new": "Novo acompanhamento",
|
||||||
"follow_ups_upgrade_button_text": "Atualize para ativar os acompanhamentos",
|
"follow_ups_upgrade_button_text": "Atualize para ativar os acompanhamentos",
|
||||||
|
"form_styling": "Estilo do formulário",
|
||||||
"formbricks_sdk_is_not_connected": "O SDK do Formbricks não está conectado",
|
"formbricks_sdk_is_not_connected": "O SDK do Formbricks não está conectado",
|
||||||
"four_points": "4 pontos",
|
"four_points": "4 pontos",
|
||||||
"heading": "Cabeçalho",
|
"heading": "Cabeçalho",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
"response_limits_redirections_and_more": "Limites de resposta, redirecionamentos e mais.",
|
||||||
"response_options": "Opções de Resposta",
|
"response_options": "Opções de Resposta",
|
||||||
"roundness": "Arredondamento",
|
"roundness": "Arredondamento",
|
||||||
"roundness_description": "Controla o arredondamento dos cantos.",
|
"roundness_description": "Controla o arredondamento dos cantos do cartão.",
|
||||||
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
"row_used_in_logic_error": "Esta linha é usada na lógica da pergunta {questionIndex}. Por favor, remova-a da lógica primeiro.",
|
||||||
"rows": "Linhas",
|
"rows": "Linhas",
|
||||||
"save_and_close": "Guardar e Fechar",
|
"save_and_close": "Guardar e Fechar",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Este inquérito gratuito e de código aberto foi encerrado",
|
"survey_completed_subheading": "Este inquérito gratuito e de código aberto foi encerrado",
|
||||||
"survey_display_settings": "Configurações de Exibição do Inquérito",
|
"survey_display_settings": "Configurações de Exibição do Inquérito",
|
||||||
"survey_placement": "Colocação do Inquérito",
|
"survey_placement": "Colocação do Inquérito",
|
||||||
"survey_styling": "Estilo do formulário",
|
|
||||||
"survey_trigger": "Desencadeador de Inquérito",
|
"survey_trigger": "Desencadeador de Inquérito",
|
||||||
"switch_multi_language_on_to_get_started": "Ative o modo multilingue para começar 👉",
|
"switch_multi_language_on_to_get_started": "Ative o modo multilingue para começar 👉",
|
||||||
"target_block_not_found": "Bloco de destino não encontrado",
|
"target_block_not_found": "Bloco de destino não encontrado",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Mensagem de boas-vindas",
|
"welcome_message": "Mensagem de boas-vindas",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Sem um filtro, todos os seus utilizadores podem ser pesquisados.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Sem um filtro, todos os seus utilizadores podem ser pesquisados.",
|
||||||
"you_have_not_created_a_segment_yet": "Ainda não criou um segmento",
|
"you_have_not_created_a_segment_yet": "Ainda não criou um segmento",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Precisa de ter dois ou mais idiomas configurados no seu espaço de trabalho para trabalhar com traduções.",
|
||||||
"your_description_here_recall_information_with": "A sua descrição aqui. Recorde a informação com @",
|
"your_description_here_recall_information_with": "A sua descrição aqui. Recorde a informação com @",
|
||||||
"your_question_here_recall_information_with": "A sua pergunta aqui. Recorde a informação com @",
|
"your_question_here_recall_information_with": "A sua pergunta aqui. Recorde a informação com @",
|
||||||
"your_web_app": "A sua aplicação web",
|
"your_web_app": "A sua aplicação web",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
"filtered_responses_excel": "Respostas filtradas (Excel)",
|
||||||
"generating_qr_code": "A gerar código QR",
|
"generating_qr_code": "A gerar código QR",
|
||||||
"impressions": "Impressões",
|
"impressions": "Impressões",
|
||||||
"impressions_identified_only": "A mostrar apenas impressões de contactos identificados",
|
|
||||||
"impressions_tooltip": "Número de vezes que o inquérito foi visualizado.",
|
"impressions_tooltip": "Número de vezes que o inquérito foi visualizado.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "O questionário será exibido aos utilizadores do seu website que correspondam aos critérios listados abaixo",
|
"connection_description": "O questionário será exibido aos utilizadores do seu website que correspondam aos critérios listados abaixo",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Último trimestre",
|
"last_quarter": "Último trimestre",
|
||||||
"last_year": "Ano passado",
|
"last_year": "Ano passado",
|
||||||
"limit": "Limite",
|
"limit": "Limite",
|
||||||
"no_identified_impressions": "Sem impressões de contactos identificados",
|
|
||||||
"no_responses_found": "Nenhuma resposta encontrada",
|
"no_responses_found": "Nenhuma resposta encontrada",
|
||||||
"other_values_found": "Outros valores encontrados",
|
"other_values_found": "Outros valores encontrados",
|
||||||
"overall": "Geral",
|
"overall": "Geral",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Começa",
|
"starts": "Começa",
|
||||||
"starts_tooltip": "Número de vezes que o inquérito foi iniciado.",
|
"starts_tooltip": "Número de vezes que o inquérito foi iniciado.",
|
||||||
"survey_reset_successfully": "Inquérito reiniciado com sucesso! {responseCount} respostas e {displayCount} exibições foram eliminadas.",
|
"survey_reset_successfully": "Inquérito reiniciado com sucesso! {responseCount} respostas e {displayCount} exibições foram eliminadas.",
|
||||||
"survey_results": "Resultados de {surveyName}",
|
|
||||||
"this_month": "Este mês",
|
"this_month": "Este mês",
|
||||||
"this_quarter": "Este trimestre",
|
"this_quarter": "Este trimestre",
|
||||||
"this_year": "Este ano",
|
"this_year": "Este ano",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.",
|
"advanced_styling_field_indicator_bg_description": "Colore a porção preenchida da barra.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.",
|
"advanced_styling_field_input_border_radius_description": "Arredonda os cantos do campo.",
|
||||||
"advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.",
|
"advanced_styling_field_input_font_size_description": "Ajusta o tamanho do texto digitado nos campos.",
|
||||||
"advanced_styling_field_input_height_description": "Controla a altura mínima da entrada.",
|
"advanced_styling_field_input_height_description": "Controla a altura mínima do campo de entrada.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
"advanced_styling_field_input_padding_x_description": "Adiciona espaço à esquerda e à direita.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Adiciona espaço no topo e na base.",
|
"advanced_styling_field_input_padding_y_description": "Adiciona espaço no topo e na base.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Atenua o texto de sugestão do placeholder.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Atenua o texto de sugestão do placeholder.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.",
|
"advanced_styling_field_input_text_description": "Colore o texto digitado nos campos de entrada.",
|
||||||
"advanced_styling_field_option_bg": "Fundo",
|
"advanced_styling_field_option_bg": "Fundo",
|
||||||
"advanced_styling_field_option_bg_description": "Preenche os itens de opção.",
|
"advanced_styling_field_option_bg_description": "Preenche os itens de opção.",
|
||||||
"advanced_styling_field_option_border": "Cor do contorno",
|
|
||||||
"advanced_styling_field_option_border_description": "Contorna as opções de botões de rádio e caixas de seleção.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.",
|
"advanced_styling_field_option_border_radius_description": "Arredonda os cantos das opções.",
|
||||||
"advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto da etiqueta da opção.",
|
"advanced_styling_field_option_font_size_description": "Ajusta o tamanho do texto da etiqueta da opção.",
|
||||||
"advanced_styling_field_option_label": "Cor da etiqueta",
|
"advanced_styling_field_option_label": "Cor da etiqueta",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
"preview_survey_question_2_choice_2_label": "Não, obrigado!",
|
||||||
"preview_survey_question_2_headline": "Quer manter-se atualizado?",
|
"preview_survey_question_2_headline": "Quer manter-se atualizado?",
|
||||||
"preview_survey_question_2_subheader": "Este é um exemplo de descrição.",
|
"preview_survey_question_2_subheader": "Este é um exemplo de descrição.",
|
||||||
"preview_survey_question_open_text_headline": "Mais alguma coisa que gostaria de partilhar?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Escreva a sua resposta aqui...",
|
|
||||||
"preview_survey_question_open_text_subheader": "O seu feedback ajuda-nos a melhorar.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
"preview_survey_welcome_card_headline": "Bem-vindo!",
|
||||||
"prioritize_features_description": "Identifique as funcionalidades que os seus utilizadores precisam mais e menos.",
|
"prioritize_features_description": "Identifique as funcionalidades que os seus utilizadores precisam mais e menos.",
|
||||||
"prioritize_features_name": "Priorizar Funcionalidades",
|
"prioritize_features_name": "Priorizar Funcionalidades",
|
||||||
|
|||||||
+13
-35
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Permite",
|
"allow": "Permite",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Permite utilizatorilor să iasă făcând clic în afara sondajului",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Permite utilizatorilor să iasă făcând clic în afara sondajului",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "A apărut o eroare necunoscută la ștergerea elementelor de tipul {type}",
|
"an_unknown_error_occurred_while_deleting_table_items": "A apărut o eroare necunoscută la ștergerea elementelor de tipul {type}",
|
||||||
|
"analysis": "Analiză",
|
||||||
"and": "Și",
|
"and": "Și",
|
||||||
"and_response_limit_of": "și limită răspuns",
|
"and_response_limit_of": "și limită răspuns",
|
||||||
"anonymous": "Anonim",
|
"anonymous": "Anonim",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Copiază",
|
"copy": "Copiază",
|
||||||
"copy_code": "Copiază codul",
|
"copy_code": "Copiază codul",
|
||||||
"copy_link": "Copiază legătura",
|
"copy_link": "Copiază legătura",
|
||||||
"count_attributes": "{count, plural, one {{count} atribut} few {{count} atribute} other {{count} de atribute}}",
|
"count_attributes": "{value, plural, one {{value} atribut} few {{value} atribute} other {{value} de atribute}}",
|
||||||
"count_contacts": "{count, plural, one {# contact} other {# contacte} }",
|
"count_contacts": "{value, plural, one {# contact} other {# contacte} }",
|
||||||
"count_members": "{count, plural, one {{count} membru} few {{count} membri} other {{count} de membri}}",
|
"count_responses": "{value, plural, one {# răspuns} other {# răspunsuri} }",
|
||||||
"count_responses": "{count, plural, one {# răspuns} other {# răspunsuri} }",
|
|
||||||
"count_selections": "{count, plural, one {{count} selecție} few {{count} selecții} other {{count} de selecții}}",
|
|
||||||
"create_new_organization": "Creează organizație nouă",
|
"create_new_organization": "Creează organizație nouă",
|
||||||
"create_segment": "Creați segment",
|
"create_segment": "Creați segment",
|
||||||
"create_survey": "Creează sondaj",
|
"create_survey": "Creează sondaj",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "zile",
|
"days": "zile",
|
||||||
"default": "Implicit",
|
"default": "Implicit",
|
||||||
"delete": "Șterge",
|
"delete": "Șterge",
|
||||||
"delete_what": "Șterge {deleteWhat}",
|
|
||||||
"description": "Descriere",
|
"description": "Descriere",
|
||||||
"dev_env": "Mediu de dezvoltare",
|
"dev_env": "Mediu de dezvoltare",
|
||||||
"development": "Dezvoltare",
|
"development": "Dezvoltare",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Eroare",
|
"error": "Eroare",
|
||||||
"error_component_description": "Această resursă nu există sau nu aveți drepturile necesare pentru a o accesa.",
|
"error_component_description": "Această resursă nu există sau nu aveți drepturile necesare pentru a o accesa.",
|
||||||
"error_component_title": "Eroare la încărcarea resurselor",
|
"error_component_title": "Eroare la încărcarea resurselor",
|
||||||
"error_loading_data": "Eroare la încărcarea datelor",
|
|
||||||
"error_rate_limit_description": "Numărul maxim de cereri atins. Vă rugăm să încercați din nou mai târziu.",
|
"error_rate_limit_description": "Numărul maxim de cereri atins. Vă rugăm să încercați din nou mai târziu.",
|
||||||
"error_rate_limit_title": "Limită de cereri depășită",
|
"error_rate_limit_title": "Limită de cereri depășită",
|
||||||
"expand_rows": "Extinde rândurile",
|
"expand_rows": "Extinde rândurile",
|
||||||
"failed_to_copy_to_clipboard": "Nu s-a reușit copierea în clipboard",
|
"failed_to_copy_to_clipboard": "Nu s-a reușit copierea în clipboard",
|
||||||
"failed_to_load_organizations": "Nu s-a reușit încărcarea organizațiilor",
|
"failed_to_load_organizations": "Nu s-a reușit încărcarea organizațiilor",
|
||||||
"failed_to_load_workspaces": "Nu s-au putut încărca workspaces",
|
"failed_to_load_workspaces": "Nu s-au putut încărca workspaces",
|
||||||
"filter": "Filtru",
|
|
||||||
"finish": "Finalizează",
|
"finish": "Finalizează",
|
||||||
"first_name": "Prenume",
|
|
||||||
"follow_these": "Urmați acestea",
|
"follow_these": "Urmați acestea",
|
||||||
"formbricks_version": "Versiunea Formbricks",
|
"formbricks_version": "Versiunea Formbricks",
|
||||||
"full_name": "Nume complet",
|
"full_name": "Nume complet",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Câmp ascuns",
|
"hidden_field": "Câmp ascuns",
|
||||||
"hidden_fields": "Câmpuri ascunse",
|
"hidden_fields": "Câmpuri ascunse",
|
||||||
"hide_column": "Ascunde coloana",
|
"hide_column": "Ascunde coloana",
|
||||||
"id": "ID",
|
|
||||||
"image": "Imagine",
|
"image": "Imagine",
|
||||||
"images": "Imagini",
|
"images": "Imagini",
|
||||||
"import": "Import",
|
"import": "Import",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Cheie",
|
"key": "Cheie",
|
||||||
"label": "Etichetă",
|
"label": "Etichetă",
|
||||||
"language": "Limba",
|
"language": "Limba",
|
||||||
"last_name": "Nume de familie",
|
|
||||||
"learn_more": "Află mai multe",
|
"learn_more": "Află mai multe",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Suprapunere ușoară",
|
"light_overlay": "Suprapunere ușoară",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Aspect și Comportament",
|
"look_and_feel": "Aspect și Comportament",
|
||||||
"manage": "Gestionați",
|
"manage": "Gestionați",
|
||||||
"marketing": "Marketing",
|
"marketing": "Marketing",
|
||||||
|
"member": "Membru",
|
||||||
"members": "Membri",
|
"members": "Membri",
|
||||||
"members_and_teams": "Membri și echipe",
|
"members_and_teams": "Membri și echipe",
|
||||||
"membership_not_found": "Apartenența nu a fost găsită",
|
"membership_not_found": "Apartenența nu a fost găsită",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Mută în jos",
|
"move_down": "Mută în jos",
|
||||||
"move_up": "Mută sus",
|
"move_up": "Mută sus",
|
||||||
"multiple_languages": "Mai multe limbi",
|
"multiple_languages": "Mai multe limbi",
|
||||||
"my_product": "produsul meu",
|
|
||||||
"name": "Nume",
|
"name": "Nume",
|
||||||
"new": "Nou",
|
"new": "Nou",
|
||||||
"new_version_available": "Formbricks {version} este disponibil. Actualizați acum!",
|
"new_version_available": "Formbricks {version} este disponibil. Actualizați acum!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Selectați echipele",
|
"select_teams": "Selectați echipele",
|
||||||
"selected": "Selectat",
|
"selected": "Selectat",
|
||||||
"selected_questions": "Întrebări selectate",
|
"selected_questions": "Întrebări selectate",
|
||||||
|
"selection": "Selecție",
|
||||||
|
"selections": "Selecții",
|
||||||
"send_test_email": "Trimite email de test",
|
"send_test_email": "Trimite email de test",
|
||||||
"session_not_found": "Sesiune inexistentă",
|
"session_not_found": "Sesiune inexistentă",
|
||||||
"settings": "Setări",
|
"settings": "Setări",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Dreapta Sus",
|
"top_right": "Dreapta Sus",
|
||||||
"try_again": "Încearcă din nou",
|
"try_again": "Încearcă din nou",
|
||||||
"type": "Tip",
|
"type": "Tip",
|
||||||
"unknown_survey": "Chestionar necunoscut",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Deblochează mai multe workspaces cu un plan superior.",
|
"unlock_more_workspaces_with_a_higher_plan": "Deblochează mai multe workspaces cu un plan superior.",
|
||||||
"update": "Actualizare",
|
"update": "Actualizare",
|
||||||
"updated": "Actualizat",
|
"updated": "Actualizat",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Reîmprospătare contacte",
|
"contacts_table_refresh": "Reîmprospătare contacte",
|
||||||
"contacts_table_refresh_success": "Contactele au fost actualizate cu succes",
|
"contacts_table_refresh_success": "Contactele au fost actualizate cu succes",
|
||||||
"create_attribute": "Creează atribut",
|
"create_attribute": "Creează atribut",
|
||||||
|
"create_key": "Creează cheie",
|
||||||
"create_new_attribute": "Creează atribut nou",
|
"create_new_attribute": "Creează atribut nou",
|
||||||
"create_new_attribute_description": "Creează un atribut nou pentru segmentare.",
|
"create_new_attribute_description": "Creează un atribut nou pentru segmentare.",
|
||||||
"custom_attributes": "Atribute personalizate",
|
"custom_attributes": "Atribute personalizate",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Acest lucru va șterge atributul selectat. Orice date de contact asociate cu acest atribut vor fi pierdute.} few {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.} other {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Acest lucru va șterge atributul selectat. Orice date de contact asociate cu acest atribut vor fi pierdute.} few {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.} other {Acest lucru va șterge atributele selectate. Orice date de contact asociate cu aceste atribute vor fi pierdute.}}",
|
||||||
"delete_contact_confirmation": "Acest lucru va șterge toate răspunsurile la sondaj și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute.",
|
"delete_contact_confirmation": "Acest lucru va șterge toate răspunsurile la sondaj și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Această acțiune va șterge toate răspunsurile chestionarului și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute. Dacă acest contact are răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} other {Aceste acțiuni vor șterge toate răspunsurile chestionarului și atributele de contact asociate cu acești contacți. Orice țintire și personalizare bazată pe datele acestor contacți vor fi pierdute. Dacă acești contacți au răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} }",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Această acțiune va șterge toate răspunsurile chestionarului și atributele de contact asociate cu acest contact. Orice țintire și personalizare bazată pe datele acestui contact vor fi pierdute. Dacă acest contact are răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} other {Aceste acțiuni vor șterge toate răspunsurile chestionarului și atributele de contact asociate cu acești contacți. Orice țintire și personalizare bazată pe datele acestor contacți vor fi pierdute. Dacă acești contacți au răspunsuri care contează pentru cotele chestionarului, numărul cotelor va fi redus, dar limitele cotelor vor rămâne neschimbate.} }",
|
||||||
"displays": "Afișări",
|
|
||||||
"edit_attribute": "Editează atributul",
|
"edit_attribute": "Editează atributul",
|
||||||
"edit_attribute_description": "Actualizează eticheta și descrierea acestui atribut.",
|
"edit_attribute_description": "Actualizează eticheta și descrierea acestui atribut.",
|
||||||
"edit_attribute_values": "Editează atributele",
|
"edit_attribute_values": "Editează atributele",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Nume de coloană CSV nevalide: {columns}. Numele coloanelor care vor deveni atribute noi trebuie să conțină doar litere mici, cifre și caractere de subliniere și trebuie să înceapă cu o literă.",
|
"invalid_csv_column_names": "Nume de coloană CSV nevalide: {columns}. Numele coloanelor care vor deveni atribute noi trebuie să conțină doar litere mici, cifre și caractere de subliniere și trebuie să înceapă cu o literă.",
|
||||||
"invalid_date_format": "Format de dată invalid. Te rugăm să folosești o dată validă.",
|
"invalid_date_format": "Format de dată invalid. Te rugăm să folosești o dată validă.",
|
||||||
"invalid_number_format": "Format de număr invalid. Te rugăm să introduci un număr valid.",
|
"invalid_number_format": "Format de număr invalid. Te rugăm să introduci un număr valid.",
|
||||||
"no_activity_yet": "Nicio activitate încă",
|
|
||||||
"no_published_link_surveys_available": "Nu există sondaje publicate pentru linkuri disponibile. Vă rugăm să publicați mai întâi un sondaj pentru linkuri.",
|
"no_published_link_surveys_available": "Nu există sondaje publicate pentru linkuri disponibile. Vă rugăm să publicați mai întâi un sondaj pentru linkuri.",
|
||||||
"no_published_surveys": "Nu există sondaje publicate",
|
"no_published_surveys": "Nu există sondaje publicate",
|
||||||
"no_responses_found": "Nu s-au găsit răspunsuri",
|
"no_responses_found": "Nu s-au găsit răspunsuri",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Selectați un sondaj",
|
"select_a_survey": "Selectați un sondaj",
|
||||||
"select_attribute": "Selectează atributul",
|
"select_attribute": "Selectează atributul",
|
||||||
"select_attribute_key": "Selectează cheia atributului",
|
"select_attribute_key": "Selectează cheia atributului",
|
||||||
"survey_viewed": "Chestionar vizualizat",
|
|
||||||
"survey_viewed_at": "Vizualizat la",
|
|
||||||
"system_attributes": "Atribute de sistem",
|
"system_attributes": "Atribute de sistem",
|
||||||
"unlock_contacts_description": "Gestionează contactele și trimite sondaje țintite",
|
"unlock_contacts_description": "Gestionează contactele și trimite sondaje țintite",
|
||||||
"unlock_contacts_title": "Deblocați contactele cu un plan superior.",
|
"unlock_contacts_title": "Deblocați contactele cu un plan superior.",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Leagă Google Sheet",
|
"link_google_sheet": "Leagă Google Sheet",
|
||||||
"link_new_sheet": "Leagă un nou Sheet",
|
"link_new_sheet": "Leagă un nou Sheet",
|
||||||
"no_integrations_yet": "Integrațiile tale Google Sheet vor apărea aici de îndată ce le vei adăuga. ⏲️",
|
"no_integrations_yet": "Integrațiile tale Google Sheet vor apărea aici de îndată ce le vei adăuga. ⏲️",
|
||||||
"reconnect_button": "Reconectează",
|
"spreadsheet_url": "URL foaie de calcul"
|
||||||
"reconnect_button_description": "Conexiunea ta cu Google Sheets a expirat. Te rugăm să te reconectezi pentru a continua sincronizarea răspunsurilor. Linkurile și datele existente din foile de calcul vor fi păstrate.",
|
|
||||||
"reconnect_button_tooltip": "Reconectează integrarea pentru a-ți reîmprospăta accesul. Linkurile și datele existente din foile de calcul vor fi păstrate.",
|
|
||||||
"spreadsheet_permission_error": "Nu ai permisiunea de a accesa această foaie de calcul. Asigură-te că foaia de calcul este partajată cu contul tău Google și că ai acces de scriere la aceasta.",
|
|
||||||
"spreadsheet_url": "URL foaie de calcul",
|
|
||||||
"token_expired_error": "Tokenul de reîmprospătare Google Sheets a expirat sau a fost revocat. Te rugăm să reconectezi integrarea."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Include data creării",
|
"include_created_at": "Include data creării",
|
||||||
"include_hidden_fields": "Include câmpuri ascunse",
|
"include_hidden_fields": "Include câmpuri ascunse",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Salut {userName}",
|
"email_customization_preview_email_heading": "Salut {userName}",
|
||||||
"email_customization_preview_email_text": "Acesta este o previzualizare a e-mailului pentru a vă arăta ce logo va fi afișat în e-mailurile.",
|
"email_customization_preview_email_text": "Acesta este o previzualizare a e-mailului pentru a vă arăta ce logo va fi afișat în e-mailurile.",
|
||||||
"error_deleting_organization_please_try_again": "Eroare la ștergerea organizației. Vă rugăm să încercați din nou.",
|
"error_deleting_organization_please_try_again": "Eroare la ștergerea organizației. Vă rugăm să încercați din nou.",
|
||||||
"from_your_organization": "{memberName} din organizația ta",
|
"from_your_organization": "din organizația ta",
|
||||||
"invitation_sent_once_more": "Invitație trimisă din nou.",
|
"invitation_sent_once_more": "Invitație trimisă din nou.",
|
||||||
"invite_deleted_successfully": "Invitație ștearsă cu succes",
|
"invite_deleted_successfully": "Invitație ștearsă cu succes",
|
||||||
"invite_expires_on": "Invitația expiră pe {date}",
|
"invite_expires_on": "Invitația expiră pe {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Adaugă un placeholder pentru a afișa dacă nu există valoare de reamintit",
|
"add_fallback_placeholder": "Adaugă un placeholder pentru a afișa dacă nu există valoare de reamintit",
|
||||||
"add_hidden_field_id": "Adăugați ID câmp ascuns",
|
"add_hidden_field_id": "Adăugați ID câmp ascuns",
|
||||||
"add_highlight_border": "Adaugă bordură evidențiată",
|
"add_highlight_border": "Adaugă bordură evidențiată",
|
||||||
"add_highlight_border_description": "Se aplică doar sondajelor din produs.",
|
|
||||||
"add_logic": "Adaugă logică",
|
"add_logic": "Adaugă logică",
|
||||||
"add_none_of_the_above": "Adăugați \"Niciuna dintre cele de mai sus\"",
|
"add_none_of_the_above": "Adăugați \"Niciuna dintre cele de mai sus\"",
|
||||||
"add_option": "Adăugați opțiune",
|
"add_option": "Adăugați opțiune",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Urmărirea a fost actualizată și va fi salvată odată ce salvați sondajul.",
|
"follow_ups_modal_updated_successfull_toast": "Urmărirea a fost actualizată și va fi salvată odată ce salvați sondajul.",
|
||||||
"follow_ups_new": "Follow-up nou",
|
"follow_ups_new": "Follow-up nou",
|
||||||
"follow_ups_upgrade_button_text": "Actualizați pentru a activa urmărările",
|
"follow_ups_upgrade_button_text": "Actualizați pentru a activa urmărările",
|
||||||
|
"form_styling": "Stilizare formular",
|
||||||
"formbricks_sdk_is_not_connected": "SDK Formbricks nu este conectat",
|
"formbricks_sdk_is_not_connected": "SDK Formbricks nu este conectat",
|
||||||
"four_points": "4 puncte",
|
"four_points": "4 puncte",
|
||||||
"heading": "Titlu",
|
"heading": "Titlu",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Limite de răspunsuri, redirecționări și altele.",
|
"response_limits_redirections_and_more": "Limite de răspunsuri, redirecționări și altele.",
|
||||||
"response_options": "Opțiuni răspuns",
|
"response_options": "Opțiuni răspuns",
|
||||||
"roundness": "Rotunjire",
|
"roundness": "Rotunjire",
|
||||||
"roundness_description": "Controlează cât de rotunjite sunt colțurile.",
|
"roundness_description": "Controlează cât de rotunjite sunt colțurile cardului.",
|
||||||
"row_used_in_logic_error": "Această linie este folosită în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.",
|
"row_used_in_logic_error": "Această linie este folosită în logica întrebării {questionIndex}. Vă rugăm să-l eliminați din logică mai întâi.",
|
||||||
"rows": "Rânduri",
|
"rows": "Rânduri",
|
||||||
"save_and_close": "Salvează & Închide",
|
"save_and_close": "Salvează & Închide",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Acest sondaj gratuit și open-source a fost închis",
|
"survey_completed_subheading": "Acest sondaj gratuit și open-source a fost închis",
|
||||||
"survey_display_settings": "Setări de afișare a sondajului",
|
"survey_display_settings": "Setări de afișare a sondajului",
|
||||||
"survey_placement": "Amplasarea sondajului",
|
"survey_placement": "Amplasarea sondajului",
|
||||||
"survey_styling": "Stilizare formular",
|
|
||||||
"survey_trigger": "Declanșator sondaj",
|
"survey_trigger": "Declanșator sondaj",
|
||||||
"switch_multi_language_on_to_get_started": "Activați opțiunea multi-limbă pentru a începe 👉",
|
"switch_multi_language_on_to_get_started": "Activați opțiunea multi-limbă pentru a începe 👉",
|
||||||
"target_block_not_found": "Blocul țintă nu a fost găsit",
|
"target_block_not_found": "Blocul țintă nu a fost găsit",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Mesaj de bun venit",
|
"welcome_message": "Mesaj de bun venit",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Fără un filtru, toți utilizatorii pot fi chestionați.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Fără un filtru, toți utilizatorii pot fi chestionați.",
|
||||||
"you_have_not_created_a_segment_yet": "Nu ai creat încă un segment",
|
"you_have_not_created_a_segment_yet": "Nu ai creat încă un segment",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Trebuie să aveți cel puțin două limbi configurate în spațiul de lucru pentru a putea lucra cu traduceri.",
|
||||||
"your_description_here_recall_information_with": "Descrierea ta aici. Reamintiți informațiile cu @",
|
"your_description_here_recall_information_with": "Descrierea ta aici. Reamintiți informațiile cu @",
|
||||||
"your_question_here_recall_information_with": "Întrebarea ta aici. Reamintiți informațiile cu @",
|
"your_question_here_recall_information_with": "Întrebarea ta aici. Reamintiți informațiile cu @",
|
||||||
"your_web_app": "Aplicația dumneavoastră web",
|
"your_web_app": "Aplicația dumneavoastră web",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Răspunsuri filtrate (Excel)",
|
"filtered_responses_excel": "Răspunsuri filtrate (Excel)",
|
||||||
"generating_qr_code": "Se generează codul QR",
|
"generating_qr_code": "Se generează codul QR",
|
||||||
"impressions": "Impresii",
|
"impressions": "Impresii",
|
||||||
"impressions_identified_only": "Se afișează doar impresiile de la contactele identificate",
|
|
||||||
"impressions_tooltip": "Număr de ori când sondajul a fost vizualizat.",
|
"impressions_tooltip": "Număr de ori când sondajul a fost vizualizat.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "Sondajul va fi afișat utilizatorilor site-ului dvs. web, care îndeplinesc criteriile enumerate mai jos",
|
"connection_description": "Sondajul va fi afișat utilizatorilor site-ului dvs. web, care îndeplinesc criteriile enumerate mai jos",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Ultimul trimestru",
|
"last_quarter": "Ultimul trimestru",
|
||||||
"last_year": "Anul trecut",
|
"last_year": "Anul trecut",
|
||||||
"limit": "Limită",
|
"limit": "Limită",
|
||||||
"no_identified_impressions": "Nicio impresie de la contactele identificate",
|
|
||||||
"no_responses_found": "Nu s-au găsit răspunsuri",
|
"no_responses_found": "Nu s-au găsit răspunsuri",
|
||||||
"other_values_found": "Alte valori găsite",
|
"other_values_found": "Alte valori găsite",
|
||||||
"overall": "General",
|
"overall": "General",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Începuturi",
|
"starts": "Începuturi",
|
||||||
"starts_tooltip": "Număr de ori când sondajul a fost început.",
|
"starts_tooltip": "Număr de ori când sondajul a fost început.",
|
||||||
"survey_reset_successfully": "Resetarea chestionarului realizată cu succes! Au fost șterse {responseCount} răspunsuri și {displayCount} afișări.",
|
"survey_reset_successfully": "Resetarea chestionarului realizată cu succes! Au fost șterse {responseCount} răspunsuri și {displayCount} afișări.",
|
||||||
"survey_results": "Rezultatele {surveyName}",
|
|
||||||
"this_month": "Luna aceasta",
|
"this_month": "Luna aceasta",
|
||||||
"this_quarter": "Trimestrul acesta",
|
"this_quarter": "Trimestrul acesta",
|
||||||
"this_year": "Anul acesta",
|
"this_year": "Anul acesta",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Colorează textul introdus în câmpuri.",
|
"advanced_styling_field_input_text_description": "Colorează textul introdus în câmpuri.",
|
||||||
"advanced_styling_field_option_bg": "Fundal",
|
"advanced_styling_field_option_bg": "Fundal",
|
||||||
"advanced_styling_field_option_bg_description": "Umple elementele de opțiune.",
|
"advanced_styling_field_option_bg_description": "Umple elementele de opțiune.",
|
||||||
"advanced_styling_field_option_border": "Culoare contur",
|
|
||||||
"advanced_styling_field_option_border_description": "Evidențiază opțiunile radio și checkbox.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Rotunjește colțurile opțiunilor.",
|
"advanced_styling_field_option_border_radius_description": "Rotunjește colțurile opțiunilor.",
|
||||||
"advanced_styling_field_option_font_size_description": "Redimensionează textul etichetei opțiunii.",
|
"advanced_styling_field_option_font_size_description": "Redimensionează textul etichetei opțiunii.",
|
||||||
"advanced_styling_field_option_label": "Culoare etichetă",
|
"advanced_styling_field_option_label": "Culoare etichetă",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Nu, mulţumesc!",
|
"preview_survey_question_2_choice_2_label": "Nu, mulţumesc!",
|
||||||
"preview_survey_question_2_headline": "Vrei să fii în temă?",
|
"preview_survey_question_2_headline": "Vrei să fii în temă?",
|
||||||
"preview_survey_question_2_subheader": "Aceasta este o descriere exemplu.",
|
"preview_survey_question_2_subheader": "Aceasta este o descriere exemplu.",
|
||||||
"preview_survey_question_open_text_headline": "Mai vrei să împărtășești ceva?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Tastează răspunsul aici...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Feedbackul tău ne ajută să ne îmbunătățim.",
|
|
||||||
"preview_survey_welcome_card_headline": "Bun venit!",
|
"preview_survey_welcome_card_headline": "Bun venit!",
|
||||||
"prioritize_features_description": "Identificați caracteristicile de care utilizatorii dumneavoastră au cel mai mult și cel mai puțin nevoie.",
|
"prioritize_features_description": "Identificați caracteristicile de care utilizatorii dumneavoastră au cel mai mult și cel mai puțin nevoie.",
|
||||||
"prioritize_features_name": "Prioritizați caracteristicile",
|
"prioritize_features_name": "Prioritizați caracteristicile",
|
||||||
|
|||||||
+14
-36
@@ -133,6 +133,7 @@
|
|||||||
"allow": "Разрешить",
|
"allow": "Разрешить",
|
||||||
"allow_users_to_exit_by_clicking_outside_the_survey": "Разрешить пользователям выходить, кликнув вне опроса",
|
"allow_users_to_exit_by_clicking_outside_the_survey": "Разрешить пользователям выходить, кликнув вне опроса",
|
||||||
"an_unknown_error_occurred_while_deleting_table_items": "Произошла неизвестная ошибка при удалении {type}ов",
|
"an_unknown_error_occurred_while_deleting_table_items": "Произошла неизвестная ошибка при удалении {type}ов",
|
||||||
|
"analysis": "Анализ",
|
||||||
"and": "и",
|
"and": "и",
|
||||||
"and_response_limit_of": "и лимит ответов",
|
"and_response_limit_of": "и лимит ответов",
|
||||||
"anonymous": "Аноним",
|
"anonymous": "Аноним",
|
||||||
@@ -175,11 +176,9 @@
|
|||||||
"copy": "Копировать",
|
"copy": "Копировать",
|
||||||
"copy_code": "Скопировать код",
|
"copy_code": "Скопировать код",
|
||||||
"copy_link": "Скопировать ссылку",
|
"copy_link": "Скопировать ссылку",
|
||||||
"count_attributes": "{count, plural, one {{count} атрибут} few {{count} атрибута} many {{count} атрибутов} other {{count} атрибута}}",
|
"count_attributes": "{value, plural, one {{value} атрибут} few {{value} атрибута} many {{value} атрибутов} other {{value} атрибута}}",
|
||||||
"count_contacts": "{count, plural, one {{count} контакт} few {{count} контакта} many {{count} контактов} other {{count} контактов}}",
|
"count_contacts": "{value, plural, one {{value} контакт} few {{value} контакта} many {{value} контактов} other {{value} контактов}}",
|
||||||
"count_members": "{count, plural, one {{count} участник} few {{count} участника} many {{count} участников} other {{count} участника}}",
|
"count_responses": "{value, plural, one {{value} ответ} few {{value} ответа} many {{value} ответов} other {{value} ответов}}",
|
||||||
"count_responses": "{count, plural, one {{count} ответ} few {{count} ответа} many {{count} ответов} other {{count} ответов}}",
|
|
||||||
"count_selections": "{count, plural, one {{count} выбран} few {{count} выбрано} many {{count} выбрано} other {{count} выбрано}}",
|
|
||||||
"create_new_organization": "Создать новую организацию",
|
"create_new_organization": "Создать новую организацию",
|
||||||
"create_segment": "Создать сегмент",
|
"create_segment": "Создать сегмент",
|
||||||
"create_survey": "Создать опрос",
|
"create_survey": "Создать опрос",
|
||||||
@@ -193,7 +192,6 @@
|
|||||||
"days": "дни",
|
"days": "дни",
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
"delete": "Удалить",
|
"delete": "Удалить",
|
||||||
"delete_what": "Удалить {deleteWhat}",
|
|
||||||
"description": "Описание",
|
"description": "Описание",
|
||||||
"dev_env": "Dev Environment",
|
"dev_env": "Dev Environment",
|
||||||
"development": "Разработка",
|
"development": "Разработка",
|
||||||
@@ -221,16 +219,13 @@
|
|||||||
"error": "Ошибка",
|
"error": "Ошибка",
|
||||||
"error_component_description": "Этот ресурс не существует или у вас нет необходимых прав для доступа к нему.",
|
"error_component_description": "Этот ресурс не существует или у вас нет необходимых прав для доступа к нему.",
|
||||||
"error_component_title": "Ошибка загрузки ресурсов",
|
"error_component_title": "Ошибка загрузки ресурсов",
|
||||||
"error_loading_data": "Ошибка загрузки данных",
|
|
||||||
"error_rate_limit_description": "Достигнуто максимальное количество запросов. Пожалуйста, попробуйте позже.",
|
"error_rate_limit_description": "Достигнуто максимальное количество запросов. Пожалуйста, попробуйте позже.",
|
||||||
"error_rate_limit_title": "Превышен лимит запросов",
|
"error_rate_limit_title": "Превышен лимит запросов",
|
||||||
"expand_rows": "Развернуть строки",
|
"expand_rows": "Развернуть строки",
|
||||||
"failed_to_copy_to_clipboard": "Не удалось скопировать в буфер обмена",
|
"failed_to_copy_to_clipboard": "Не удалось скопировать в буфер обмена",
|
||||||
"failed_to_load_organizations": "Не удалось загрузить организации",
|
"failed_to_load_organizations": "Не удалось загрузить организации",
|
||||||
"failed_to_load_workspaces": "Не удалось загрузить рабочие пространства",
|
"failed_to_load_workspaces": "Не удалось загрузить рабочие пространства",
|
||||||
"filter": "Фильтр",
|
|
||||||
"finish": "Завершить",
|
"finish": "Завершить",
|
||||||
"first_name": "Имя",
|
|
||||||
"follow_these": "Выполните следующие действия",
|
"follow_these": "Выполните следующие действия",
|
||||||
"formbricks_version": "Версия Formbricks",
|
"formbricks_version": "Версия Formbricks",
|
||||||
"full_name": "Полное имя",
|
"full_name": "Полное имя",
|
||||||
@@ -243,7 +238,6 @@
|
|||||||
"hidden_field": "Скрытое поле",
|
"hidden_field": "Скрытое поле",
|
||||||
"hidden_fields": "Скрытые поля",
|
"hidden_fields": "Скрытые поля",
|
||||||
"hide_column": "Скрыть столбец",
|
"hide_column": "Скрыть столбец",
|
||||||
"id": "ID",
|
|
||||||
"image": "Изображение",
|
"image": "Изображение",
|
||||||
"images": "Изображения",
|
"images": "Изображения",
|
||||||
"import": "Импорт",
|
"import": "Импорт",
|
||||||
@@ -261,7 +255,6 @@
|
|||||||
"key": "Ключ",
|
"key": "Ключ",
|
||||||
"label": "Метка",
|
"label": "Метка",
|
||||||
"language": "Язык",
|
"language": "Язык",
|
||||||
"last_name": "Фамилия",
|
|
||||||
"learn_more": "Подробнее",
|
"learn_more": "Подробнее",
|
||||||
"license_expired": "License Expired",
|
"license_expired": "License Expired",
|
||||||
"light_overlay": "Светлый оверлей",
|
"light_overlay": "Светлый оверлей",
|
||||||
@@ -276,6 +269,7 @@
|
|||||||
"look_and_feel": "Внешний вид",
|
"look_and_feel": "Внешний вид",
|
||||||
"manage": "Управление",
|
"manage": "Управление",
|
||||||
"marketing": "Маркетинг",
|
"marketing": "Маркетинг",
|
||||||
|
"member": "Участник",
|
||||||
"members": "Участники",
|
"members": "Участники",
|
||||||
"members_and_teams": "Участники и команды",
|
"members_and_teams": "Участники и команды",
|
||||||
"membership_not_found": "Участие не найдено",
|
"membership_not_found": "Участие не найдено",
|
||||||
@@ -287,7 +281,6 @@
|
|||||||
"move_down": "Переместить вниз",
|
"move_down": "Переместить вниз",
|
||||||
"move_up": "Переместить вверх",
|
"move_up": "Переместить вверх",
|
||||||
"multiple_languages": "Несколько языков",
|
"multiple_languages": "Несколько языков",
|
||||||
"my_product": "мой продукт",
|
|
||||||
"name": "Имя",
|
"name": "Имя",
|
||||||
"new": "Новый",
|
"new": "Новый",
|
||||||
"new_version_available": "Formbricks {version} уже здесь. Обновитесь сейчас!",
|
"new_version_available": "Formbricks {version} уже здесь. Обновитесь сейчас!",
|
||||||
@@ -383,6 +376,8 @@
|
|||||||
"select_teams": "Выбрать команды",
|
"select_teams": "Выбрать команды",
|
||||||
"selected": "Выбрано",
|
"selected": "Выбрано",
|
||||||
"selected_questions": "Выбранные вопросы",
|
"selected_questions": "Выбранные вопросы",
|
||||||
|
"selection": "Выбор",
|
||||||
|
"selections": "Выборы",
|
||||||
"send_test_email": "Отправить тестовое письмо",
|
"send_test_email": "Отправить тестовое письмо",
|
||||||
"session_not_found": "Сессия не найдена",
|
"session_not_found": "Сессия не найдена",
|
||||||
"settings": "Настройки",
|
"settings": "Настройки",
|
||||||
@@ -434,7 +429,6 @@
|
|||||||
"top_right": "Вверху справа",
|
"top_right": "Вверху справа",
|
||||||
"try_again": "Попробуйте ещё раз",
|
"try_again": "Попробуйте ещё раз",
|
||||||
"type": "Тип",
|
"type": "Тип",
|
||||||
"unknown_survey": "Неизвестный опрос",
|
|
||||||
"unlock_more_workspaces_with_a_higher_plan": "Откройте больше рабочих пространств с более высоким тарифом.",
|
"unlock_more_workspaces_with_a_higher_plan": "Откройте больше рабочих пространств с более высоким тарифом.",
|
||||||
"update": "Обновить",
|
"update": "Обновить",
|
||||||
"updated": "Обновлено",
|
"updated": "Обновлено",
|
||||||
@@ -652,6 +646,7 @@
|
|||||||
"contacts_table_refresh": "Обновить контакты",
|
"contacts_table_refresh": "Обновить контакты",
|
||||||
"contacts_table_refresh_success": "Контакты успешно обновлены",
|
"contacts_table_refresh_success": "Контакты успешно обновлены",
|
||||||
"create_attribute": "Создать атрибут",
|
"create_attribute": "Создать атрибут",
|
||||||
|
"create_key": "Создать ключ",
|
||||||
"create_new_attribute": "Создать новый атрибут",
|
"create_new_attribute": "Создать новый атрибут",
|
||||||
"create_new_attribute_description": "Создайте новый атрибут для целей сегментации.",
|
"create_new_attribute_description": "Создайте новый атрибут для целей сегментации.",
|
||||||
"custom_attributes": "Пользовательские атрибуты",
|
"custom_attributes": "Пользовательские атрибуты",
|
||||||
@@ -662,7 +657,6 @@
|
|||||||
"delete_attribute_confirmation": "{value, plural, one {Будет удалён выбранный атрибут. Все данные контактов, связанные с этим атрибутом, будут потеряны.} few {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} many {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} other {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.}}",
|
"delete_attribute_confirmation": "{value, plural, one {Будет удалён выбранный атрибут. Все данные контактов, связанные с этим атрибутом, будут потеряны.} few {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} many {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.} other {Будут удалены выбранные атрибуты. Все данные контактов, связанные с этими атрибутами, будут потеряны.}}",
|
||||||
"delete_contact_confirmation": "Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны.",
|
"delete_contact_confirmation": "Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны.",
|
||||||
"delete_contact_confirmation_with_quotas": "{value, plural, one {Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны. Если у этого контакта есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} few {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} many {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} other {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.}}",
|
"delete_contact_confirmation_with_quotas": "{value, plural, one {Это удалит все ответы на опросы и атрибуты контакта, связанные с этим контактом. Любая таргетинг и персонализация на основе данных этого контакта будут потеряны. Если у этого контакта есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} few {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} many {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.} other {Это удалит все ответы на опросы и атрибуты контактов, связанные с этими контактами. Любая таргетинг и персонализация на основе данных этих контактов будут потеряны. Если у этих контактов есть ответы, которые учитываются в квотах опроса, количество по квотам будет уменьшено, но лимиты квот останутся без изменений.}}",
|
||||||
"displays": "Показы",
|
|
||||||
"edit_attribute": "Редактировать атрибут",
|
"edit_attribute": "Редактировать атрибут",
|
||||||
"edit_attribute_description": "Обновите метку и описание для этого атрибута.",
|
"edit_attribute_description": "Обновите метку и описание для этого атрибута.",
|
||||||
"edit_attribute_values": "Редактировать атрибуты",
|
"edit_attribute_values": "Редактировать атрибуты",
|
||||||
@@ -674,7 +668,6 @@
|
|||||||
"invalid_csv_column_names": "Недопустимые имена столбцов в CSV: {columns}. Имена столбцов, которые станут новыми атрибутами, должны содержать только строчные буквы, цифры и подчёркивания, а также начинаться с буквы.",
|
"invalid_csv_column_names": "Недопустимые имена столбцов в CSV: {columns}. Имена столбцов, которые станут новыми атрибутами, должны содержать только строчные буквы, цифры и подчёркивания, а также начинаться с буквы.",
|
||||||
"invalid_date_format": "Неверный формат даты. Пожалуйста, используйте корректную дату.",
|
"invalid_date_format": "Неверный формат даты. Пожалуйста, используйте корректную дату.",
|
||||||
"invalid_number_format": "Неверный формат числа. Пожалуйста, введите корректное число.",
|
"invalid_number_format": "Неверный формат числа. Пожалуйста, введите корректное число.",
|
||||||
"no_activity_yet": "Пока нет активности",
|
|
||||||
"no_published_link_surveys_available": "Нет доступных опубликованных опросов-ссылок. Пожалуйста, сначала опубликуйте опрос-ссылку.",
|
"no_published_link_surveys_available": "Нет доступных опубликованных опросов-ссылок. Пожалуйста, сначала опубликуйте опрос-ссылку.",
|
||||||
"no_published_surveys": "Нет опубликованных опросов",
|
"no_published_surveys": "Нет опубликованных опросов",
|
||||||
"no_responses_found": "Ответы не найдены",
|
"no_responses_found": "Ответы не найдены",
|
||||||
@@ -689,8 +682,6 @@
|
|||||||
"select_a_survey": "Выберите опрос",
|
"select_a_survey": "Выберите опрос",
|
||||||
"select_attribute": "Выберите атрибут",
|
"select_attribute": "Выберите атрибут",
|
||||||
"select_attribute_key": "Выберите ключ атрибута",
|
"select_attribute_key": "Выберите ключ атрибута",
|
||||||
"survey_viewed": "Опрос просмотрен",
|
|
||||||
"survey_viewed_at": "Просмотрено",
|
|
||||||
"system_attributes": "Системные атрибуты",
|
"system_attributes": "Системные атрибуты",
|
||||||
"unlock_contacts_description": "Управляйте контактами и отправляйте целевые опросы",
|
"unlock_contacts_description": "Управляйте контактами и отправляйте целевые опросы",
|
||||||
"unlock_contacts_title": "Откройте доступ к контактам с более высоким тарифом",
|
"unlock_contacts_title": "Откройте доступ к контактам с более высоким тарифом",
|
||||||
@@ -762,12 +753,7 @@
|
|||||||
"link_google_sheet": "Связать с Google Sheet",
|
"link_google_sheet": "Связать с Google Sheet",
|
||||||
"link_new_sheet": "Связать с новой таблицей",
|
"link_new_sheet": "Связать с новой таблицей",
|
||||||
"no_integrations_yet": "Ваши интеграции с Google Sheet появятся здесь, как только вы их добавите. ⏲️",
|
"no_integrations_yet": "Ваши интеграции с Google Sheet появятся здесь, как только вы их добавите. ⏲️",
|
||||||
"reconnect_button": "Переподключить",
|
"spreadsheet_url": "URL таблицы"
|
||||||
"reconnect_button_description": "Срок действия подключения к Google Sheets истёк. Пожалуйста, переподключись, чтобы продолжить синхронизацию ответов. Все существующие ссылки на таблицы и данные будут сохранены.",
|
|
||||||
"reconnect_button_tooltip": "Переподключи интеграцию, чтобы обновить доступ. Все существующие ссылки на таблицы и данные будут сохранены.",
|
|
||||||
"spreadsheet_permission_error": "У тебя нет доступа к этой таблице. Убедись, что таблица открыта для твоего Google-аккаунта и у тебя есть права на запись.",
|
|
||||||
"spreadsheet_url": "URL таблицы",
|
|
||||||
"token_expired_error": "Срок действия токена обновления Google Sheets истёк или он был отозван. Пожалуйста, переподключи интеграцию."
|
|
||||||
},
|
},
|
||||||
"include_created_at": "Включить дату создания",
|
"include_created_at": "Включить дату создания",
|
||||||
"include_hidden_fields": "Включить скрытые поля",
|
"include_hidden_fields": "Включить скрытые поля",
|
||||||
@@ -1082,7 +1068,7 @@
|
|||||||
"email_customization_preview_email_heading": "Привет, {userName}",
|
"email_customization_preview_email_heading": "Привет, {userName}",
|
||||||
"email_customization_preview_email_text": "Это предварительный просмотр письма, чтобы показать, какой логотип будет отображаться в письмах.",
|
"email_customization_preview_email_text": "Это предварительный просмотр письма, чтобы показать, какой логотип будет отображаться в письмах.",
|
||||||
"error_deleting_organization_please_try_again": "Ошибка при удалении организации. Пожалуйста, попробуйте ещё раз.",
|
"error_deleting_organization_please_try_again": "Ошибка при удалении организации. Пожалуйста, попробуйте ещё раз.",
|
||||||
"from_your_organization": "{memberName} из вашей организации",
|
"from_your_organization": "из вашей организации",
|
||||||
"invitation_sent_once_more": "Приглашение отправлено ещё раз.",
|
"invitation_sent_once_more": "Приглашение отправлено ещё раз.",
|
||||||
"invite_deleted_successfully": "Приглашение успешно удалено",
|
"invite_deleted_successfully": "Приглашение успешно удалено",
|
||||||
"invite_expires_on": "Приглашение истекает {date}",
|
"invite_expires_on": "Приглашение истекает {date}",
|
||||||
@@ -1247,7 +1233,6 @@
|
|||||||
"add_fallback_placeholder": "Добавить плейсхолдер, который будет показан, если нет значения для отображения.",
|
"add_fallback_placeholder": "Добавить плейсхолдер, который будет показан, если нет значения для отображения.",
|
||||||
"add_hidden_field_id": "Добавить скрытый ID поля",
|
"add_hidden_field_id": "Добавить скрытый ID поля",
|
||||||
"add_highlight_border": "Добавить выделяющую рамку",
|
"add_highlight_border": "Добавить выделяющую рамку",
|
||||||
"add_highlight_border_description": "Применяется только к опросам внутри продукта.",
|
|
||||||
"add_logic": "Добавить логику",
|
"add_logic": "Добавить логику",
|
||||||
"add_none_of_the_above": "Добавить вариант «Ничего из вышеперечисленного»",
|
"add_none_of_the_above": "Добавить вариант «Ничего из вышеперечисленного»",
|
||||||
"add_option": "Добавить вариант",
|
"add_option": "Добавить вариант",
|
||||||
@@ -1446,6 +1431,7 @@
|
|||||||
"follow_ups_modal_updated_successfull_toast": "Фоллоу-ап обновлён и будет сохранён после сохранения опроса.",
|
"follow_ups_modal_updated_successfull_toast": "Фоллоу-ап обновлён и будет сохранён после сохранения опроса.",
|
||||||
"follow_ups_new": "Новый фоллоу-ап",
|
"follow_ups_new": "Новый фоллоу-ап",
|
||||||
"follow_ups_upgrade_button_text": "Обновите тариф для активации фоллоу-апов",
|
"follow_ups_upgrade_button_text": "Обновите тариф для активации фоллоу-апов",
|
||||||
|
"form_styling": "Оформление формы",
|
||||||
"formbricks_sdk_is_not_connected": "Formbricks SDK не подключён",
|
"formbricks_sdk_is_not_connected": "Formbricks SDK не подключён",
|
||||||
"four_points": "4 балла",
|
"four_points": "4 балла",
|
||||||
"heading": "Заголовок",
|
"heading": "Заголовок",
|
||||||
@@ -1618,7 +1604,7 @@
|
|||||||
"response_limits_redirections_and_more": "Лимиты ответов, перенаправления и другое.",
|
"response_limits_redirections_and_more": "Лимиты ответов, перенаправления и другое.",
|
||||||
"response_options": "Параметры ответа",
|
"response_options": "Параметры ответа",
|
||||||
"roundness": "Скругление",
|
"roundness": "Скругление",
|
||||||
"roundness_description": "Определяет степень скругления углов.",
|
"roundness_description": "Определяет степень скругления углов карточки.",
|
||||||
"row_used_in_logic_error": "Эта строка используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите её из логики.",
|
"row_used_in_logic_error": "Эта строка используется в логике вопроса {questionIndex}. Пожалуйста, сначала удалите её из логики.",
|
||||||
"rows": "Строки",
|
"rows": "Строки",
|
||||||
"save_and_close": "Сохранить и закрыть",
|
"save_and_close": "Сохранить и закрыть",
|
||||||
@@ -1664,7 +1650,6 @@
|
|||||||
"survey_completed_subheading": "Этот бесплатный и открытый опрос был закрыт",
|
"survey_completed_subheading": "Этот бесплатный и открытый опрос был закрыт",
|
||||||
"survey_display_settings": "Настройки отображения опроса",
|
"survey_display_settings": "Настройки отображения опроса",
|
||||||
"survey_placement": "Размещение опроса",
|
"survey_placement": "Размещение опроса",
|
||||||
"survey_styling": "Оформление формы",
|
|
||||||
"survey_trigger": "Триггер опроса",
|
"survey_trigger": "Триггер опроса",
|
||||||
"switch_multi_language_on_to_get_started": "Включите многоязычный режим, чтобы начать 👉",
|
"switch_multi_language_on_to_get_started": "Включите многоязычный режим, чтобы начать 👉",
|
||||||
"target_block_not_found": "Целевой блок не найден",
|
"target_block_not_found": "Целевой блок не найден",
|
||||||
@@ -1755,6 +1740,7 @@
|
|||||||
"welcome_message": "Приветственное сообщение",
|
"welcome_message": "Приветственное сообщение",
|
||||||
"without_a_filter_all_of_your_users_can_be_surveyed": "Без фильтра все ваши пользователи могут быть опрошены.",
|
"without_a_filter_all_of_your_users_can_be_surveyed": "Без фильтра все ваши пользователи могут быть опрошены.",
|
||||||
"you_have_not_created_a_segment_yet": "Вы ещё не создали сегмент",
|
"you_have_not_created_a_segment_yet": "Вы ещё не создали сегмент",
|
||||||
|
"you_need_to_have_two_or_more_languages_set_up_in_your_workspace_to_work_with_translations": "Для работы с переводами необходимо настроить два или более языков в рабочем пространстве.",
|
||||||
"your_description_here_recall_information_with": "Ваша инструкция здесь. Вспомните информацию с помощью @",
|
"your_description_here_recall_information_with": "Ваша инструкция здесь. Вспомните информацию с помощью @",
|
||||||
"your_question_here_recall_information_with": "Ваш вопрос здесь. Вспомните информацию с помощью @",
|
"your_question_here_recall_information_with": "Ваш вопрос здесь. Вспомните информацию с помощью @",
|
||||||
"your_web_app": "Ваше веб-приложение",
|
"your_web_app": "Ваше веб-приложение",
|
||||||
@@ -1962,7 +1948,6 @@
|
|||||||
"filtered_responses_excel": "Отфильтрованные ответы (Excel)",
|
"filtered_responses_excel": "Отфильтрованные ответы (Excel)",
|
||||||
"generating_qr_code": "Генерация QR-кода",
|
"generating_qr_code": "Генерация QR-кода",
|
||||||
"impressions": "Просмотры",
|
"impressions": "Просмотры",
|
||||||
"impressions_identified_only": "Показаны только показы от идентифицированных контактов",
|
|
||||||
"impressions_tooltip": "Количество раз, когда опрос был просмотрен.",
|
"impressions_tooltip": "Количество раз, когда опрос был просмотрен.",
|
||||||
"in_app": {
|
"in_app": {
|
||||||
"connection_description": "Опрос будет показан пользователям вашего сайта, которые соответствуют указанным ниже критериям",
|
"connection_description": "Опрос будет показан пользователям вашего сайта, которые соответствуют указанным ниже критериям",
|
||||||
@@ -2005,7 +1990,6 @@
|
|||||||
"last_quarter": "Прошлый квартал",
|
"last_quarter": "Прошлый квартал",
|
||||||
"last_year": "Прошлый год",
|
"last_year": "Прошлый год",
|
||||||
"limit": "Лимит",
|
"limit": "Лимит",
|
||||||
"no_identified_impressions": "Нет показов от идентифицированных контактов",
|
|
||||||
"no_responses_found": "Ответы не найдены",
|
"no_responses_found": "Ответы не найдены",
|
||||||
"other_values_found": "Найдены другие значения",
|
"other_values_found": "Найдены другие значения",
|
||||||
"overall": "В целом",
|
"overall": "В целом",
|
||||||
@@ -2028,7 +2012,6 @@
|
|||||||
"starts": "Запуски",
|
"starts": "Запуски",
|
||||||
"starts_tooltip": "Количество запусков опроса.",
|
"starts_tooltip": "Количество запусков опроса.",
|
||||||
"survey_reset_successfully": "Опрос успешно сброшен! {responseCount} ответов и {displayCount} показов были удалены.",
|
"survey_reset_successfully": "Опрос успешно сброшен! {responseCount} ответов и {displayCount} показов были удалены.",
|
||||||
"survey_results": "Результаты {surveyName}",
|
|
||||||
"this_month": "В этом месяце",
|
"this_month": "В этом месяце",
|
||||||
"this_quarter": "В этом квартале",
|
"this_quarter": "В этом квартале",
|
||||||
"this_year": "В этом году",
|
"this_year": "В этом году",
|
||||||
@@ -2176,7 +2159,7 @@
|
|||||||
"advanced_styling_field_indicator_bg_description": "Задаёт цвет заполненной части полосы.",
|
"advanced_styling_field_indicator_bg_description": "Задаёт цвет заполненной части полосы.",
|
||||||
"advanced_styling_field_input_border_radius_description": "Скругляет углы полей ввода.",
|
"advanced_styling_field_input_border_radius_description": "Скругляет углы полей ввода.",
|
||||||
"advanced_styling_field_input_font_size_description": "Масштабирует введённый текст в полях ввода.",
|
"advanced_styling_field_input_font_size_description": "Масштабирует введённый текст в полях ввода.",
|
||||||
"advanced_styling_field_input_height_description": "Управляет минимальной высотой поля ввода.",
|
"advanced_styling_field_input_height_description": "Определяет минимальную высоту поля ввода.",
|
||||||
"advanced_styling_field_input_padding_x_description": "Добавляет отступы слева и справа.",
|
"advanced_styling_field_input_padding_x_description": "Добавляет отступы слева и справа.",
|
||||||
"advanced_styling_field_input_padding_y_description": "Добавляет пространство сверху и снизу.",
|
"advanced_styling_field_input_padding_y_description": "Добавляет пространство сверху и снизу.",
|
||||||
"advanced_styling_field_input_placeholder_opacity_description": "Делает текст подсказки менее заметным.",
|
"advanced_styling_field_input_placeholder_opacity_description": "Делает текст подсказки менее заметным.",
|
||||||
@@ -2185,8 +2168,6 @@
|
|||||||
"advanced_styling_field_input_text_description": "Задаёт цвет введённого текста в полях.",
|
"advanced_styling_field_input_text_description": "Задаёт цвет введённого текста в полях.",
|
||||||
"advanced_styling_field_option_bg": "Фон",
|
"advanced_styling_field_option_bg": "Фон",
|
||||||
"advanced_styling_field_option_bg_description": "Заливает фон элементов опций.",
|
"advanced_styling_field_option_bg_description": "Заливает фон элементов опций.",
|
||||||
"advanced_styling_field_option_border": "Цвет границы",
|
|
||||||
"advanced_styling_field_option_border_description": "Обводка для вариантов radio и checkbox.",
|
|
||||||
"advanced_styling_field_option_border_radius_description": "Скругляет углы опций.",
|
"advanced_styling_field_option_border_radius_description": "Скругляет углы опций.",
|
||||||
"advanced_styling_field_option_font_size_description": "Изменяет размер текста метки опции.",
|
"advanced_styling_field_option_font_size_description": "Изменяет размер текста метки опции.",
|
||||||
"advanced_styling_field_option_label": "Цвет метки",
|
"advanced_styling_field_option_label": "Цвет метки",
|
||||||
@@ -3006,9 +2987,6 @@
|
|||||||
"preview_survey_question_2_choice_2_label": "Нет, спасибо!",
|
"preview_survey_question_2_choice_2_label": "Нет, спасибо!",
|
||||||
"preview_survey_question_2_headline": "Хотите быть в курсе событий?",
|
"preview_survey_question_2_headline": "Хотите быть в курсе событий?",
|
||||||
"preview_survey_question_2_subheader": "Это пример описания.",
|
"preview_survey_question_2_subheader": "Это пример описания.",
|
||||||
"preview_survey_question_open_text_headline": "Есть ли ещё что-то, чем хочешь поделиться?",
|
|
||||||
"preview_survey_question_open_text_placeholder": "Введи свой ответ здесь...",
|
|
||||||
"preview_survey_question_open_text_subheader": "Твой отзыв помогает нам становиться лучше.",
|
|
||||||
"preview_survey_welcome_card_headline": "Добро пожаловать!",
|
"preview_survey_welcome_card_headline": "Добро пожаловать!",
|
||||||
"prioritize_features_description": "Определите, какие функции наиболее и наименее важны для ваших пользователей.",
|
"prioritize_features_description": "Определите, какие функции наиболее и наименее важны для ваших пользователей.",
|
||||||
"prioritize_features_name": "Приоритизация функций",
|
"prioritize_features_name": "Приоритизация функций",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user