feat: feedback records table (#7422)

Co-authored-by: Harsh Bhat <harshbhat@Harshs-MacBook-Air.local>
Co-authored-by: Harsh Bhat <harsh121102@gmail.com>
Co-authored-by: Johannes <johannes@formbricks.com>
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
Co-authored-by: TheodorTomas <theodortomas@gmail.com>
This commit is contained in:
Anshuman Pandey
2026-03-09 13:41:18 +05:30
committed by GitHub
parent 077a9934ad
commit cffeb0513e
30 changed files with 843 additions and 36 deletions

View File

@@ -19,7 +19,14 @@ export const UnifyConfigNavigation = ({
const activeId = activeIdProp ?? "sources";
const navigation = [{ id: "sources", label: t("environments.unify.sources"), href: `${baseHref}/sources` }];
const navigation = [
{ id: "sources", label: t("environments.unify.sources"), href: `${baseHref}/sources` },
{
id: "feedback-records",
label: t("environments.unify.feedback_records"),
href: `${baseHref}/feedback-records`,
},
];
return <SecondaryNavigation navigation={navigation} activeId={activeId} loading={loading} />;
};

View File

@@ -0,0 +1,36 @@
"use client";
import { useTranslation } from "react-i18next";
import type { FeedbackRecordData } from "@/modules/hub/types";
import { PageContentWrapper } from "@/modules/ui/components/page-content-wrapper";
import { PageHeader } from "@/modules/ui/components/page-header";
import { UnifyConfigNavigation } from "../components/UnifyConfigNavigation";
import { FeedbackRecordsTable } from "./feedback-records-table";
interface FeedbackRecordsPageClientProps {
environmentId: string;
initialRecords: FeedbackRecordData[];
initialTotal: number;
}
export function FeedbackRecordsPageClient({
environmentId,
initialRecords,
initialTotal,
}: FeedbackRecordsPageClientProps) {
const { t } = useTranslation();
return (
<PageContentWrapper>
<PageHeader pageTitle={t("environments.unify.unify_feedback")}>
<UnifyConfigNavigation environmentId={environmentId} activeId="feedback-records" />
</PageHeader>
<FeedbackRecordsTable
environmentId={environmentId}
initialRecords={initialRecords}
initialTotal={initialTotal}
/>
</PageContentWrapper>
);
}

View File

@@ -0,0 +1,270 @@
"use client";
import { TFunction } from "i18next";
import {
CalendarIcon,
HashIcon,
MessageSquareTextIcon,
RefreshCwIcon,
ToggleLeftIcon,
TypeIcon,
} from "lucide-react";
import { useCallback, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { listFeedbackRecordsAction } from "@/lib/connector/actions";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import type { FeedbackRecordData } from "@/modules/hub/types";
import { Badge } from "@/modules/ui/components/badge";
import { Button } from "@/modules/ui/components/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/modules/ui/components/tooltip";
const RECORDS_PER_PAGE = 50;
const FIELD_TYPE_ICONS: Record<string, React.ReactNode> = {
text: <TypeIcon className="h-3.5 w-3.5" />,
categorical: <HashIcon className="h-3.5 w-3.5" />,
nps: <HashIcon className="h-3.5 w-3.5" />,
csat: <HashIcon className="h-3.5 w-3.5" />,
ces: <HashIcon className="h-3.5 w-3.5" />,
rating: <HashIcon className="h-3.5 w-3.5" />,
number: <HashIcon className="h-3.5 w-3.5" />,
boolean: <ToggleLeftIcon className="h-3.5 w-3.5" />,
date: <CalendarIcon className="h-3.5 w-3.5" />,
};
const formatValue = (record: FeedbackRecordData, t: TFunction, locale?: string): string => {
if (record.value_text != null) return record.value_text;
if (record.value_number != null) return String(record.value_number);
if (record.value_boolean != null) return record.value_boolean ? t("common.yes") : t("common.no");
if (record.value_date != null) return new Date(record.value_date).toLocaleDateString(locale);
return "—";
};
function formatDate(isoString: string, locale: string): string {
return new Date(isoString).toLocaleDateString(locale, {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}
function truncate(str: string, maxLen: number): string {
if (str.length <= maxLen) return str;
return str.slice(0, maxLen) + "…";
}
interface FeedbackRecordsTableProps {
environmentId: string;
initialRecords: FeedbackRecordData[];
initialTotal: number;
}
export const FeedbackRecordsTable = ({
environmentId,
initialRecords,
initialTotal,
}: FeedbackRecordsTableProps) => {
const { t, i18n } = useTranslation();
const [records, setRecords] = useState<FeedbackRecordData[]>(initialRecords);
const [total, setTotal] = useState(initialTotal);
const [isRefreshing, setIsRefreshing] = useState(false);
const [isLoadingMore, setIsLoadingMore] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchRecords = useCallback(
async (offset: number, append: boolean) => {
const setLoading = offset === 0 ? setIsRefreshing : setIsLoadingMore;
setLoading(true);
setError(null);
const result = await listFeedbackRecordsAction({
environmentId,
limit: RECORDS_PER_PAGE,
offset,
});
if (!result?.data) {
setError(getFormattedErrorMessage(result) ?? t("environments.unify.failed_to_load_feedback_records"));
setLoading(false);
return;
}
const response = result.data;
setRecords((prev) => (append ? [...prev, ...response.data] : response.data));
setTotal(response.total);
setLoading(false);
},
[environmentId, t]
);
const handleLoadMore = () => {
fetchRecords(records.length, true);
};
const handleRefresh = async () => {
if (isRefreshing) return;
setIsRefreshing(true);
setError(null);
const toastId = toast.loading(t("environments.unify.refreshing_feedback_records"));
const result = await listFeedbackRecordsAction({
environmentId,
limit: RECORDS_PER_PAGE,
offset: 0,
});
if (!result?.data) {
toast.error(
getFormattedErrorMessage(result) ?? t("environments.unify.failed_to_load_feedback_records"),
{ id: toastId }
);
setIsRefreshing(false);
return;
}
setRecords(result.data.data);
setTotal(result.data.total);
setIsRefreshing(false);
toast.success(t("environments.unify.feedback_records_refreshed"), { id: toastId });
};
const hasMore = records.length < total;
if (error) {
return (
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="flex h-48 flex-col items-center justify-center gap-3 px-4 text-center">
<MessageSquareTextIcon className="h-8 w-8 text-slate-400" />
<p className="text-sm text-slate-500">{error}</p>
<Button variant="secondary" size="sm" onClick={handleRefresh}>
{t("common.retry")}
</Button>
</div>
</div>
);
}
const isEmpty = records.length === 0 && !isRefreshing;
return (
<div className="space-y-3">
{!isEmpty && (
<div className="flex items-center justify-between">
<p className="text-sm text-slate-500">
{t("environments.unify.showing_count", { count: records.length, total })}
</p>
<Button
variant="secondary"
size="sm"
onClick={handleRefresh}
disabled={isRefreshing}
aria-label={t("environments.unify.refresh_feedback_records")}>
<RefreshCwIcon className="h-3.5 w-3.5" aria-hidden="true" />
</Button>
</div>
)}
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="overflow-x-auto">
<table className="w-full min-w-[900px]">
<thead>
<tr className="border-b border-slate-200 text-left text-sm text-slate-900 [&>th]:font-semibold">
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.collected_at")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.source_type")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.source_name")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.field_label")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.field_type")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.value")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.user_identifier")}</th>
</tr>
</thead>
{isEmpty ? (
<tbody>
<tr>
<td colSpan={7}>
<div className="flex h-32 items-center justify-center">
<p className="text-sm text-slate-500">{t("environments.unify.no_feedback_records")}</p>
</div>
</td>
</tr>
</tbody>
) : (
<tbody className="divide-y divide-slate-100">
{records.map((record) => (
<FeedbackRecordRow key={record.id} record={record} locale={i18n.language} t={t} />
))}
</tbody>
)}
</table>
</div>
</div>
{hasMore && (
<div className="flex justify-center">
<Button variant="secondary" size="sm" onClick={handleLoadMore} loading={isLoadingMore}>
{t("environments.unify.load_more")}
</Button>
</div>
)}
</div>
);
};
const FeedbackRecordRow = ({
record,
locale,
t,
}: {
record: FeedbackRecordData;
locale: string;
t: TFunction;
}) => {
const value = formatValue(record, t, locale);
const isLongValue = value.length > 60;
return (
<tr className="text-sm text-slate-700 transition-colors hover:bg-slate-50">
<td className="whitespace-nowrap px-4 py-3 text-slate-500">
{formatDate(record.collected_at, locale)}
</td>
<td className="whitespace-nowrap px-4 py-3">
<Badge text={record.source_type} type="gray" size="tiny" />
</td>
<td className="max-w-[150px] truncate px-4 py-3" title={record.source_name ?? undefined}>
{record.source_name ?? "—"}
</td>
<td className="max-w-[200px] truncate px-4 py-3" title={record.field_label ?? undefined}>
{record.field_label ?? record.field_id}
</td>
<td className="whitespace-nowrap px-4 py-3">
<span className="inline-flex items-center gap-1 text-slate-600">
{FIELD_TYPE_ICONS[record.field_type] ?? <HashIcon className="h-3.5 w-3.5" />}
{record.field_type}
</span>
</td>
<td className="max-w-[250px] px-4 py-3">
{isLongValue ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span className="cursor-default truncate">{truncate(value, 60)}</span>
</TooltipTrigger>
<TooltipContent side="top" className="max-w-sm whitespace-pre-wrap">
{value}
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<span>{value}</span>
)}
</td>
<td className="max-w-[120px] truncate px-4 py-3 text-slate-500" title={record.user_identifier}>
{record.user_identifier ?? "—"}
</td>
</tr>
);
};

View File

@@ -0,0 +1,46 @@
import { notFound } from "next/navigation";
import { getTranslate } from "@/lingodotdev/server";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { listFeedbackRecords } from "@/modules/hub/service";
import { FeedbackRecordsPageClient } from "./feedback-records-page-client";
const INITIAL_PAGE_SIZE = 50;
export default async function UnifyFeedbackRecordsPage(props: {
params: Promise<{ environmentId: string }>;
}) {
const t = await getTranslate();
const params = await props.params;
const { isOwner, isManager, hasReadAccess, hasReadWriteAccess, hasManageAccess, session } =
await getEnvironmentAuth(params.environmentId);
if (!session) {
throw new Error(t("common.session_not_found"));
}
const hasAccess = isOwner || isManager || hasReadAccess || hasReadWriteAccess || hasManageAccess;
if (!hasAccess) {
return notFound();
}
const result = await listFeedbackRecords({
tenant_id: params.environmentId,
limit: INITIAL_PAGE_SIZE,
offset: 0,
});
if (result.error) {
throw new Error(t("environments.unify.failed_to_load_feedback_records"));
}
const initialData = result.data ?? { data: [], total: 0, limit: INITIAL_PAGE_SIZE, offset: 0 };
return (
<FeedbackRecordsPageClient
environmentId={params.environmentId}
initialRecords={initialData.data}
initialTotal={initialData.total}
/>
);
}

View File

@@ -161,7 +161,7 @@ export function ConnectorsSection({
environmentId={environmentId}
/>
}>
<UnifyConfigNavigation environmentId={environmentId} />
<UnifyConfigNavigation environmentId={environmentId} activeId="sources" />
</PageHeader>
<div className="space-y-6">

View File

@@ -1,13 +1,26 @@
import { notFound } from "next/navigation";
import { getConnectorsWithMappings } from "@/lib/connector/service";
import { getSurveys } from "@/lib/survey/service";
import { getTranslate } from "@/lingodotdev/server";
import { getEnvironmentAuth } from "@/modules/environments/lib/utils";
import { ConnectorsSection } from "./components/connectors-page-client";
import { transformToUnifySurvey } from "./lib";
export default async function UnifySourcesPage(props: { params: Promise<{ environmentId: string }> }) {
const t = await getTranslate();
const params = await props.params;
await getEnvironmentAuth(params.environmentId);
const { isOwner, isManager, hasReadAccess, hasReadWriteAccess, hasManageAccess, session } =
await getEnvironmentAuth(params.environmentId);
if (!session) {
throw new Error(t("common.session_not_found"));
}
const hasAccess = isOwner || isManager || hasReadAccess || hasReadWriteAccess || hasManageAccess;
if (!hasAccess) {
return notFound();
}
const [connectors, surveys] = await Promise.all([
getConnectorsWithMappings(params.environmentId),

View File

@@ -261,6 +261,7 @@ checksums:
common/new: 126d036fae5fb6b629728ecb97e6195b
common/new_version_available: 399ddfc4232712e18ddab2587356b3dc
common/next: 89ddbcf710eba274963494f312bdc8a9
common/no: 8c708225830b06df2d1141c536f2a0d6
common/no_background_image_found: 4108a781a9022c65671a826d4e299d5b
common/no_code: f602144ab7d28a5b19a446bf74b4dcc4
common/no_files_uploaded: c97be829e195a41b2f6b6717b87a232b
@@ -334,6 +335,7 @@ checksums:
common/response_id: 73375099cc976dc7203b8e27f5f709e0
common/responses: 14bb6c69f906d7bbd1359f7ef1bb3c28
common/restart: bab6232e89f24e3129f8e48268739d5b
common/retry: 6e44d18639560596569a1278f9c83676
common/role: 53743bbb6ca938f5b893552e839d067f
common/saas: f01686245bcfb35a3590ab56db677bdb
common/sales: 38758eb50094cd8190a71fe67be4d647
@@ -441,6 +443,7 @@ checksums:
common/workspace_permission_not_found: e94bdff8af51175c5767714f82bb4833
common/workspaces: 8ba082a84aa35cf851af1cf874b853e2
common/years: eb4f5fdd2b320bf13e200fd6a6c1abff
common/yes: ec580fd11a45779b039466f1e35eed2a
common/you: db2a4a796b70cc1430d1b21f6ffb6dcb
common/you_are_downgraded_to_the_community_edition: e3ae56502ff787109cae0997519f628e
common/you_are_not_authorized_to_perform_this_action: 1b3255ab740582ddff016a399f8bf302
@@ -1939,6 +1942,7 @@ checksums:
environments/unify/change_file: c5163ac18bf443370228a8ecbb0b07da
environments/unify/click_load_sample_csv: 0ee0bf93f10f02863fc658b359706316
environments/unify/click_to_upload: 74a7e7d79a88b6bbfd9f22084bffdb9b
environments/unify/collected_at: b41902ddb4586ba4a4611d726b5014aa
environments/unify/configure_import: 71d550661f7e9fe322b60e7e870aa2fd
environments/unify/configure_mapping: c794411c50bc511f8fc332def0e4e2f9
environments/unify/connection: 421e709602c92ffbe04a266f6a092089
@@ -1969,8 +1973,13 @@ checksums:
environments/unify/enter_name_for_source: de6d02a0a8ccc99204ad831ca6dcdbd3
environments/unify/enter_value: 4f068bb59617975c1e546218373122cd
environments/unify/enum: 96fc644f35edd6b1c09d1d503f078acc
environments/unify/failed_to_load_feedback_records: 57f6c8c5fa524d7c2d8777315e5036c8
environments/unify/feedback_date: ddba5d3270d4a6394d29721025a04400
environments/unify/feedback_record_fields: 88c0f13afeb88fe751f85e79b0f73064
environments/unify/feedback_records: e24cf48bb6985910f4ffe5e00512d388
environments/unify/feedback_records_refreshed: 4b27a8e2a8dbe8afa945d9f874aa7ef1
environments/unify/field_label: 6384505ca0e40010c666b712511132a6
environments/unify/field_type: 2581066dc304c853a4a817c20996fa08
environments/unify/formbricks_surveys: eba2fce04ee68f02626e5509adf7d66a
environments/unify/historical_import_complete: f46f98bf4db63bf2993bfb234dc95f62
environments/unify/import_csv_data: f05e1d1ed88d528256efe5702df46646
@@ -1980,8 +1989,10 @@ checksums:
environments/unify/importing_historical_data: f5be578704ec26dc4ec573309e9fff20
environments/unify/invalid_enum_values: e6ca8740dab72f64e8dc5780b5cffcc6
environments/unify/invalid_values_found: 5011dc9c0294a222033f9910ea919b8a
environments/unify/load_more: 365c2d8dfc53ac7e9188acd5274e2837
environments/unify/load_sample_csv: ad21fa63f4a3df96a5939c753be21f4e
environments/unify/n_supported_questions: d75413d386441b5eb137a1ea191e4bd9
environments/unify/no_feedback_records: 16a905c40f6d47a5e8f93b3d8c6f6693
environments/unify/no_source_fields_loaded: a597b1d16262cbe897001046eb3ff640
environments/unify/no_sources_connected: 0e8a5612530bfc82091091f40f95012f
environments/unify/no_surveys_found: 649a2f29b4c34525778d9177605fb326
@@ -1990,6 +2001,8 @@ checksums:
environments/unify/question_selected: b9ff13b6212874258da911867932dc7d
environments/unify/question_type_not_supported: 8d9f7554e3b509dfd5307d8d1fef08d7
environments/unify/questions_selected: 1f13d6fecafa2ce5ea9e6d07078a1d38
environments/unify/refresh_feedback_records: c111751e02a7dee57390ed7fb79cfcc6
environments/unify/refreshing_feedback_records: 2a03b44510ebe19eea6473639e9a7222
environments/unify/required: 04d7fb6f37ffe0a6ca97d49e2a8b6eb5
environments/unify/save_changes: 53dd9f4f0a4accc822fa5c1f2f6d118a
environments/unify/select_a_survey_to_see_questions: 792eba3d2f6d210231a2266401111a20
@@ -2003,12 +2016,14 @@ checksums:
environments/unify/select_survey_questions_description: 3386ed56085eabebefa3cc453269fc5b
environments/unify/set_value: b8a86f8da957ebd599ece4b1b1936a78
environments/unify/setup_connection: cce7d9c488d737d04e70bed929a46f8a
environments/unify/showing_count: 20675071b78443b250ab13b11138f30d
environments/unify/showing_rows: 83d3440314d1e6f2721e034369a3a131
environments/unify/source: 45309626f464f4bda161ee783a4c8c80
environments/unify/source_connect_csv_description: 2f9d1dd31668ac52578f16323157b746
environments/unify/source_connect_formbricks_description: 77bda4e1d485d76770ba2221f1faf9ff
environments/unify/source_fields: 1bae074990e64cbfd820a0b6462397be
environments/unify/source_name: 157675beca12efcd8ec512c5256b1a61
environments/unify/source_type: d1ff69af76c687eb189db72030717570
environments/unify/source_type_cannot_be_changed: bb5232c6e92df7f88731310fabbb1eb1
environments/unify/sources: ecbbe6e49baa335c5afd7b04b609d006
environments/unify/status_active: 3de9afebcb9d4ce8ac42e14995f79ffd
@@ -2024,6 +2039,8 @@ checksums:
environments/unify/updated_at: 8fdb85248e591254973403755dcc3724
environments/unify/upload_csv_data_description: 7fab46222ab05a4424db90a7cc96cdf5
environments/unify/upload_csv_file: b77797b68cb46a614b3adaa4db24d4c2
environments/unify/user_identifier: 61073457a5c3901084b557d065f876be
environments/unify/value: 34b0eaa85808b15cbc4be94c64d0146b
environments/workspace/api_keys/add_api_key: 3c7633bae18a6e19af7a5af12f9bc3da
environments/workspace/api_keys/api_key: ce825fec5b3e1f8e27c45b1a63619985
environments/workspace/api_keys/api_key_copied_to_clipboard: daeeac786ba09ffa650e206609b88f9c

View File

@@ -25,6 +25,9 @@ import {
getProjectIdFromConnectorId,
getProjectIdFromEnvironmentId,
} from "@/lib/utils/helper";
import { getTranslate } from "@/lingodotdev/server";
import { listFeedbackRecords } from "@/modules/hub/service";
import type { FeedbackRecordListParams, FeedbackRecordListResponse } from "@/modules/hub/types";
import { importCsvData } from "./csv-import";
import { importHistoricalResponses } from "./import";
import {
@@ -462,3 +465,61 @@ export const importCsvDataAction = authenticatedActionClient
return result;
}
);
const ZListFeedbackRecordsAction = z.object({
environmentId: ZId,
limit: z.number().min(1).max(1000).optional(),
offset: z.number().min(0).optional(),
sourceType: z.string().optional(),
fieldType: z.string().optional(),
since: z.string().optional(),
until: z.string().optional(),
});
export const listFeedbackRecordsAction = authenticatedActionClient
.schema(ZListFeedbackRecordsAction)
.action(
async ({
ctx,
parsedInput,
}: {
ctx: AuthenticatedActionClientCtx;
parsedInput: z.infer<typeof ZListFeedbackRecordsAction>;
}): Promise<FeedbackRecordListResponse> => {
const organizationId = await getOrganizationIdFromEnvironmentId(parsedInput.environmentId);
await checkAuthorizationUpdated({
userId: ctx.user.id,
organizationId,
access: [
{
type: "organization",
roles: ["owner", "manager"],
},
{
type: "projectTeam",
minPermission: "read",
projectId: await getProjectIdFromEnvironmentId(parsedInput.environmentId),
},
],
});
const params: FeedbackRecordListParams = {
tenant_id: parsedInput.environmentId,
limit: parsedInput.limit ?? 50,
offset: parsedInput.offset ?? 0,
};
if (parsedInput.sourceType) params.source_type = parsedInput.sourceType;
if (parsedInput.fieldType) params.field_type = parsedInput.fieldType;
if (parsedInput.since) params.since = parsedInput.since;
if (parsedInput.until) params.until = parsedInput.until;
const result = await listFeedbackRecords(params);
if (result.error || !result.data) {
logger.warn({ error: result.error }, "Failed to list feedback records");
const t = await getTranslate();
throw new Error(result.error?.message ?? t("environments.unify.failed_to_load_feedback_records"));
}
return result.data;
}
);

View File

@@ -19,7 +19,11 @@ export const importCsvData = async (
throw new InvalidInputError("Connector has no field mappings configured");
}
const { records, skipped } = transformCsvRowsToFeedbackRecords(csvRows, connector.fieldMappings);
const { records, skipped } = transformCsvRowsToFeedbackRecords(
csvRows,
connector.fieldMappings,
connector.environmentId
);
let successes = 0;
let failures = 0;

View File

@@ -55,7 +55,8 @@ const resolveValue = (
*/
export const transformCsvRowToFeedbackRecord = (
row: Record<string, string>,
mappings: TConnectorFieldMapping[]
mappings: TConnectorFieldMapping[],
tenantId?: string
): FeedbackRecordCreateParams | null => {
const record: Record<string, string | number | boolean | Record<string, unknown> | undefined> = {};
@@ -78,6 +79,10 @@ export const transformCsvRowToFeedbackRecord = (
return null;
}
if (tenantId && !record.tenant_id) {
record.tenant_id = tenantId;
}
return record as unknown as FeedbackRecordCreateParams;
};
@@ -87,13 +92,14 @@ export const transformCsvRowToFeedbackRecord = (
*/
export const transformCsvRowsToFeedbackRecords = (
rows: Record<string, string>[],
mappings: TConnectorFieldMapping[]
mappings: TConnectorFieldMapping[],
tenantId?: string
): { records: FeedbackRecordCreateParams[]; skipped: number } => {
const records: FeedbackRecordCreateParams[] = [];
let skipped = 0;
for (const row of rows) {
const record = transformCsvRowToFeedbackRecord(row, mappings);
const record = transformCsvRowToFeedbackRecord(row, mappings, tenantId);
if (record) {
records.push(record);
} else {

View File

@@ -13,14 +13,15 @@ export type TImportResult = { successes: number; failures: number; skipped: numb
const processBatch = async (
responses: Awaited<ReturnType<typeof getResponses>>,
survey: TSurvey,
mappings: TConnectorFormbricksMapping[]
mappings: TConnectorFormbricksMapping[],
tenantId: string
): Promise<TImportResult> => {
let successes = 0;
let failures = 0;
const expectedRecords = responses.length * mappings.length;
const allRecords = responses.flatMap((response) =>
transformResponseToFeedbackRecords(response, survey, mappings)
transformResponseToFeedbackRecords(response, survey, mappings, tenantId)
);
if (allRecords.length > 0) {
@@ -49,7 +50,12 @@ export const importHistoricalResponses = async (
const responses = await getResponses(survey.id, IMPORT_BATCH_SIZE, offset);
if (responses.length === 0) break;
const batch = await processBatch(responses, survey, connector.formbricksMappings);
const batch = await processBatch(
responses,
survey,
connector.formbricksMappings,
connector.environmentId
);
successes += batch.successes;
failures += batch.failures;
skipped += batch.skipped;

View File

@@ -288,6 +288,7 @@
"new": "Neu",
"new_version_available": "Formbricks {version} ist da. Jetzt aktualisieren!",
"next": "Weiter",
"no": "Nein",
"no_background_image_found": "Kein Hintergrundbild gefunden.",
"no_code": "No Code",
"no_files_uploaded": "Keine Dateien hochgeladen",
@@ -361,6 +362,7 @@
"response_id": "Antwort-ID",
"responses": "Antworten",
"restart": "Neustart",
"retry": "Erneut versuchen",
"role": "Rolle",
"saas": "SaaS",
"sales": "Vertrieb",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Projektberechtigung nicht gefunden",
"workspaces": "Projekte",
"years": "Jahre",
"yes": "Ja",
"you": "Du",
"you_are_downgraded_to_the_community_edition": "Du wurdest auf die Community Edition herabgestuft.",
"you_are_not_authorized_to_perform_this_action": "Du bist nicht berechtigt, diese Aktion durchzuführen.",
@@ -2047,6 +2050,7 @@
"change_file": "Datei ändern",
"click_load_sample_csv": "Klicke auf 'Beispiel-CSV laden', um Spalten zu sehen",
"click_to_upload": "Klicke zum Hochladen",
"collected_at": "Erfasst am",
"configure_import": "Import konfigurieren",
"configure_mapping": "Zuordnung konfigurieren",
"connection": "Verbindung",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Gib einen Namen für diese Quelle ein",
"enter_value": "Wert eingeben...",
"enum": "Enum",
"failed_to_load_feedback_records": "Feedback-Einträge konnten nicht geladen werden",
"feedback_date": "Aktuelles Datum",
"feedback_record_fields": "Feedback-Datensatzfelder",
"feedback_records": "Feedback-Einträge",
"feedback_records_refreshed": "Feedback-Einträge aktualisiert",
"field_label": "Feldbezeichnung",
"field_type": "Feldtyp",
"formbricks_surveys": "Formbricks Umfragen",
"historical_import_complete": "Import abgeschlossen: {successes} erfolgreich, {failures} fehlgeschlagen, {skipped} übersprungen (keine Daten)",
"import_csv_data": "Feedback importieren",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Historische Daten werden importiert...",
"invalid_enum_values": "Ungültige Werte in Spalte, die auf {field} gemappt ist",
"invalid_values_found": "Gefunden: {values} (Zeilen: {rows}) {extra}",
"load_more": "Mehr laden",
"load_sample_csv": "Beispiel-CSV laden",
"n_supported_questions": "{count} unterstützte Fragen",
"no_feedback_records": "Noch keine Feedback-Einträge vorhanden. Einträge werden hier angezeigt, sobald deine Konnektoren Daten senden.",
"no_source_fields_loaded": "Noch keine Quellfelder geladen",
"no_sources_connected": "Noch keine Quellen verbunden. Füge eine Quelle hinzu, um loszulegen.",
"no_surveys_found": "Keine Umfragen in dieser Umgebung gefunden",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> Frage ausgewählt. Jede Antwort auf diese Frage erstellt einen neuen Feedback-Datensatz.",
"question_type_not_supported": "Dieser Fragetyp wird nicht unterstützt",
"questions_selected": "<strong>{count}</strong> Fragen ausgewählt. Jede Antwort auf diese Fragen erstellt einen neuen Feedback-Datensatz.",
"refresh_feedback_records": "Feedback-Datensätze aktualisieren",
"refreshing_feedback_records": "Feedback-Einträge werden aktualisiert...",
"required": "Erforderlich",
"save_changes": "Änderungen speichern",
"select_a_survey_to_see_questions": "Wähle eine Umfrage aus, um ihre Fragen zu sehen",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Wähle aus, welche Umfragefragen FeedbackRecords erstellen sollen.",
"set_value": "Wert festlegen",
"setup_connection": "Verbindung einrichten",
"showing_count": "Zeige {count} von {total} Einträgen",
"showing_rows": "Zeige 3 von {count} Zeilen",
"source": "Quelle",
"source_connect_csv_description": "Feedback aus CSV-Dateien importieren",
"source_connect_formbricks_description": "Feedback aus deinen Formbricks-Umfragen verbinden",
"source_fields": "Quellenfelder",
"source_name": "Quellenname",
"source_type": "Quellentyp",
"source_type_cannot_be_changed": "Quellentyp kann nicht geändert werden",
"sources": "Quellen",
"status_active": "Im Gange",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Aktualisiere die Mapping-Konfiguration für diese Quelle.",
"updated_at": "Aktualisiert am",
"upload_csv_data_description": "Lade eine CSV-Datei hoch, um Feedback-Daten zu importieren.",
"upload_csv_file": "CSV-Datei hochladen"
"upload_csv_file": "CSV-Datei hochladen",
"user_identifier": "Benutzer",
"value": "Wert"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "New",
"new_version_available": "Formbricks {version} is here. Upgrade now!",
"next": "Next",
"no": "No",
"no_background_image_found": "No background image found.",
"no_code": "No code",
"no_files_uploaded": "No files were uploaded",
@@ -361,6 +362,7 @@
"response_id": "Response ID",
"responses": "Responses",
"restart": "Restart",
"retry": "Retry",
"role": "Role",
"saas": "SaaS",
"sales": "Sales",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Workspace permission not found",
"workspaces": "Workspaces",
"years": "years",
"yes": "Yes",
"you": "You",
"you_are_downgraded_to_the_community_edition": "You are downgraded to the Community Edition.",
"you_are_not_authorized_to_perform_this_action": "You are not authorized to perform this action.",
@@ -2047,6 +2050,7 @@
"change_file": "Change file",
"click_load_sample_csv": "Click 'Load sample CSV' to see columns",
"click_to_upload": "Click to upload",
"collected_at": "Collected At",
"configure_import": "Configure import",
"configure_mapping": "Configure Mapping",
"connection": "Connection",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Enter a name for this source",
"enter_value": "Enter value...",
"enum": "enum",
"failed_to_load_feedback_records": "Failed to load feedback records",
"feedback_date": "Current date",
"feedback_record_fields": "Feedback Record Fields",
"feedback_records": "Feedback Records",
"feedback_records_refreshed": "Feedback records refreshed",
"field_label": "Field Label",
"field_type": "Field Type",
"formbricks_surveys": "Formbricks Surveys",
"historical_import_complete": "Import complete: {successes} succeeded, {failures} failed, {skipped} skipped (no data)",
"import_csv_data": "Import feedback",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Importing historical data...",
"invalid_enum_values": "Invalid values in column mapped to {field}",
"invalid_values_found": "Found: {values} (rows: {rows}) {extra}",
"load_more": "Load more",
"load_sample_csv": "Load sample CSV",
"n_supported_questions": "{count} supported questions",
"no_feedback_records": "No feedback records yet. Records will appear here once your connectors start sending data.",
"no_source_fields_loaded": "No source fields loaded yet",
"no_sources_connected": "No sources connected yet. Add a source to get started.",
"no_surveys_found": "No surveys found in this environment",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> question selected. Each response to these questions will create a new Feedback Record.",
"question_type_not_supported": "This question type is not supported",
"questions_selected": "<strong>{count}</strong> questions selected. Each response to these questions will create a new Feedback Record.",
"refresh_feedback_records": "Refresh feedback records",
"refreshing_feedback_records": "Refreshing feedback records...",
"required": "Required",
"save_changes": "Save changes",
"select_a_survey_to_see_questions": "Select a survey to see its questions",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Choose which survey questions should create FeedbackRecords.",
"set_value": "set value",
"setup_connection": "Setup connection",
"showing_count": "Showing {count} of {total} records",
"showing_rows": "Showing 3 of {count} rows",
"source": "source",
"source_connect_csv_description": "Import feedback from CSV files",
"source_connect_formbricks_description": "Connect feedback from your Formbricks surveys",
"source_fields": "Source Fields",
"source_name": "Source Name",
"source_type": "Source Type",
"source_type_cannot_be_changed": "Source type cannot be changed",
"sources": "Sources",
"status_active": "In Progress",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Update the mapping configuration for this source.",
"updated_at": "Updated at",
"upload_csv_data_description": "Upload a CSV file to import feedback data.",
"upload_csv_file": "Upload CSV File"
"upload_csv_file": "Upload CSV File",
"user_identifier": "User",
"value": "Value"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Nuevo",
"new_version_available": "Formbricks {version} está aquí. ¡Actualiza ahora!",
"next": "Siguiente",
"no": "No",
"no_background_image_found": "No se encontró imagen de fondo.",
"no_code": "Sin código",
"no_files_uploaded": "No se subieron archivos",
@@ -361,6 +362,7 @@
"response_id": "ID de respuesta",
"responses": "Respuestas",
"restart": "Reiniciar",
"retry": "Reintentar",
"role": "Rol",
"saas": "SaaS",
"sales": "Ventas",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Permiso del proyecto no encontrado",
"workspaces": "Proyectos",
"years": "años",
"yes": "Sí",
"you": "Tú",
"you_are_downgraded_to_the_community_edition": "Has sido degradado a la edición Community.",
"you_are_not_authorized_to_perform_this_action": "No tienes autorización para realizar esta acción.",
@@ -2047,6 +2050,7 @@
"change_file": "Cambiar archivo",
"click_load_sample_csv": "Haz clic en 'Cargar CSV de muestra' para ver las columnas",
"click_to_upload": "Haz clic para subir",
"collected_at": "Recopilado el",
"configure_import": "Configurar importación",
"configure_mapping": "Configurar asignación",
"connection": "Conexión",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Introduce un nombre para este origen",
"enter_value": "Introduce un valor...",
"enum": "enum",
"failed_to_load_feedback_records": "Error al cargar los registros de comentarios",
"feedback_date": "Fecha actual",
"feedback_record_fields": "Campos de registro de comentarios",
"feedback_records": "Registros de comentarios",
"feedback_records_refreshed": "Registros de comentarios actualizados",
"field_label": "Etiqueta de campo",
"field_type": "Tipo de campo",
"formbricks_surveys": "Formbricks Surveys",
"historical_import_complete": "Importación completada: {successes} correctas, {failures} fallidas, {skipped} omitidas (sin datos)",
"import_csv_data": "Importar comentarios",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Importando datos históricos...",
"invalid_enum_values": "Valores no válidos en la columna asignada a {field}",
"invalid_values_found": "Encontrados: {values} (filas: {rows}) {extra}",
"load_more": "Cargar más",
"load_sample_csv": "Cargar CSV de muestra",
"n_supported_questions": "{count} preguntas compatibles",
"no_feedback_records": "Aún no hay registros de comentarios. Los registros aparecerán aquí una vez que tus conectores empiecen a enviar datos.",
"no_source_fields_loaded": "Aún no se han cargado campos de origen",
"no_sources_connected": "Aún no hay fuentes conectadas. Añade una fuente para empezar.",
"no_surveys_found": "No se encontraron encuestas en este entorno",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> pregunta seleccionada. Cada respuesta a esta pregunta creará un registro de feedback nuevo.",
"question_type_not_supported": "Este tipo de pregunta no es compatible",
"questions_selected": "<strong>{count}</strong> preguntas seleccionadas. Cada respuesta a estas preguntas creará un registro de feedback nuevo.",
"refresh_feedback_records": "Actualizar los registros de comentarios",
"refreshing_feedback_records": "Actualizando registros de comentarios...",
"required": "Obligatorio",
"save_changes": "Guardar cambios",
"select_a_survey_to_see_questions": "Selecciona una encuesta para ver sus preguntas",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Elige qué preguntas de la encuesta deben crear FeedbackRecords.",
"set_value": "establecer valor",
"setup_connection": "Configurar conexión",
"showing_count": "Mostrando {count} de {total} registros",
"showing_rows": "Mostrando 3 de {count} filas",
"source": "origen",
"source_connect_csv_description": "Importar feedback desde archivos CSV",
"source_connect_formbricks_description": "Conectar feedback de tus encuestas de Formbricks",
"source_fields": "Campos de origen",
"source_name": "Nombre de origen",
"source_type": "Tipo de fuente",
"source_type_cannot_be_changed": "El tipo de origen no se puede cambiar",
"sources": "Orígenes",
"status_active": "En progreso",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Actualiza la configuración de mapeo para esta fuente.",
"updated_at": "Actualizado el",
"upload_csv_data_description": "Sube un archivo CSV para importar datos de comentarios.",
"upload_csv_file": "Subir archivo CSV"
"upload_csv_file": "Subir archivo CSV",
"user_identifier": "Usuario",
"value": "Valor"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Nouveau",
"new_version_available": "Formbricks {version} est là. Mettez à jour maintenant !",
"next": "Suivant",
"no": "Non",
"no_background_image_found": "Aucune image de fond trouvée.",
"no_code": "Sans code",
"no_files_uploaded": "Aucun fichier n'a été téléchargé.",
@@ -361,6 +362,7 @@
"response_id": "ID de réponse",
"responses": "Réponses",
"restart": "Recommencer",
"retry": "Réessayer",
"role": "Rôle",
"saas": "SaaS",
"sales": "Ventes",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Permission du projet introuvable",
"workspaces": "Projets",
"years": "années",
"yes": "Oui",
"you": "Vous",
"you_are_downgraded_to_the_community_edition": "Vous êtes rétrogradé à l'édition communautaire.",
"you_are_not_authorized_to_perform_this_action": "Vous n'êtes pas autorisé à effectuer cette action.",
@@ -2047,6 +2050,7 @@
"change_file": "Changer de fichier",
"click_load_sample_csv": "Clique sur «Charger un exemple CSV» pour voir les colonnes",
"click_to_upload": "Clique pour charger",
"collected_at": "Collecté le",
"configure_import": "Configurer l'importation",
"configure_mapping": "Configurer le mappage",
"connection": "Connexion",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Entrez un nom pour cette source",
"enter_value": "Saisir une valeur...",
"enum": "enum",
"failed_to_load_feedback_records": "Échec du chargement des enregistrements de feedback",
"feedback_date": "Date actuelle",
"feedback_record_fields": "Champs d'enregistrement de feedback",
"feedback_records": "Enregistrements de feedback",
"feedback_records_refreshed": "Enregistrements de feedback actualisés",
"field_label": "Libellé du champ",
"field_type": "Type de champ",
"formbricks_surveys": "Sondages Formbricks",
"historical_import_complete": "Importation terminée: {successes} réussies, {failures} échouées, {skipped} ignorées (aucune donnée)",
"import_csv_data": "Importer les retours",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Importation des données historiques...",
"invalid_enum_values": "Valeurs non valides dans la colonne mappée à {field}",
"invalid_values_found": "Trouvées: {values} (lignes: {rows}) {extra}",
"load_more": "Charger plus",
"load_sample_csv": "Charger un exemple de CSV",
"n_supported_questions": "{count} questions prises en charge",
"no_feedback_records": "Aucun enregistrement de feedback pour le moment. Les enregistrements apparaîtront ici une fois que vos connecteurs commenceront à envoyer des données.",
"no_source_fields_loaded": "Aucun champ source chargé pour le moment",
"no_sources_connected": "Aucune source connectée pour le moment. Ajoutez une source pour commencer.",
"no_surveys_found": "Aucune enquête trouvée dans cet environnement",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> question sélectionnée. Chaque réponse à cette question créera un nouvel enregistrement de feedback.",
"question_type_not_supported": "Ce type de question n'est pas pris en charge",
"questions_selected": "<strong>{count}</strong> questions sélectionnées. Chaque réponse à ces questions créera un nouvel enregistrement de feedback.",
"refresh_feedback_records": "Actualiser les enregistrements de retours",
"refreshing_feedback_records": "Actualisation des enregistrements de feedback...",
"required": "Requis",
"save_changes": "Enregistrer les modifications",
"select_a_survey_to_see_questions": "Sélectionnez une enquête pour voir ses questions",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Choisissez quelles questions d'enquête doivent créer des FeedbackRecords.",
"set_value": "définir la valeur",
"setup_connection": "Configurer la connexion",
"showing_count": "Affichage de {count} sur {total} enregistrements",
"showing_rows": "Affichage de 3 sur {count} lignes",
"source": "source",
"source_connect_csv_description": "Importer des feedbacks depuis des fichiers CSV",
"source_connect_formbricks_description": "Connecter les feedbacks de vos enquêtes Formbricks",
"source_fields": "Champs source",
"source_name": "Nom de la source",
"source_type": "Type de source",
"source_type_cannot_be_changed": "Le type de source ne peut pas être modifié",
"sources": "Sources",
"status_active": "En cours",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Mettre à jour la configuration de mappage pour cette source.",
"updated_at": "Mis à jour à",
"upload_csv_data_description": "Téléchargez un fichier CSV pour importer des données de feedback.",
"upload_csv_file": "Télécharger un fichier CSV"
"upload_csv_file": "Télécharger un fichier CSV",
"user_identifier": "Utilisateur",
"value": "Valeur"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Új",
"new_version_available": "A Formbricks {version} megérkezett. Frissítsen most!",
"next": "Következő",
"no": "Nem",
"no_background_image_found": "Nem található háttérkép.",
"no_code": "Kód nélkül",
"no_files_uploaded": "Nem lettek fájlok feltöltve",
@@ -361,6 +362,7 @@
"response_id": "Válaszazonosító",
"responses": "Válaszok",
"restart": "Újraindítás",
"retry": "Újra",
"role": "Szerep",
"saas": "SaaS",
"sales": "Értékesítés",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "A munkaterület-jogosultság nem található",
"workspaces": "Munkaterületek",
"years": "évek",
"yes": "Igen",
"you": "Ön",
"you_are_downgraded_to_the_community_edition": "Visszaváltott a közösségi kiadásra.",
"you_are_not_authorized_to_perform_this_action": "Nincs felhatalmazva ennek a műveletnek a végrehajtásához.",
@@ -2047,6 +2050,7 @@
"change_file": "Fájl módosítása",
"click_load_sample_csv": "Kattintson a 'Minta CSV betöltése' gombra az oszlopok megtekintéséhez",
"click_to_upload": "Kattintson a feltöltéshez",
"collected_at": "Gyűjtve",
"configure_import": "Importálás konfigurálása",
"configure_mapping": "Leképezés konfigurálása",
"connection": "Kapcsolat",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Adj nevet ennek a forrásnak",
"enter_value": "Érték megadása...",
"enum": "felsorolás",
"failed_to_load_feedback_records": "Nem sikerült betölteni a visszajelzési rekordokat",
"feedback_date": "Aktuális dátum",
"feedback_record_fields": "Visszajelzési rekord mezői",
"feedback_record_fields": "Visszajelzési rekord mezők",
"feedback_records": "Visszajelzési rekordok",
"feedback_records_refreshed": "Visszajelzési rekordok frissítve",
"field_label": "Mező címke",
"field_type": "Mező típus",
"formbricks_surveys": "Formbricks kérdőívek",
"historical_import_complete": "Importálás befejezve: {successes} sikeres, {failures} sikertelen, {skipped} kihagyva (nincs adat)",
"import_csv_data": "Visszajelzés importálása",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Történeti adatok importálása...",
"invalid_enum_values": "Érvénytelen értékek a(z) {field} mezőhöz rendelt oszlopban",
"invalid_values_found": "Talált értékek: {values} (sorok: {rows}) {extra}",
"load_more": "Továbbiak betöltése",
"load_sample_csv": "Minta CSV betöltése",
"n_supported_questions": "{count} támogatott kérdés",
"no_feedback_records": "Még nincsenek visszajelzési rekordok. A rekordok itt fognak megjelenni, amint a csatlakozók elkezdik küldeni az adatokat.",
"no_source_fields_loaded": "Még nincsenek forrás mezők betöltve",
"no_sources_connected": "Még nincsenek források csatlakoztatva. Adj hozzá egy forrást a kezdéshez.",
"no_surveys_found": "Nem találhatók kérdőívek ebben a környezetben",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> kérdés kiválasztva. Minden válasz ezekre a kérdésekre új visszajelzési rekordot hoz létre.",
"question_type_not_supported": "Ez a kérdéstípus nem támogatott",
"questions_selected": "<strong>{count}</strong> kérdés kiválasztva. Minden válasz ezekre a kérdésekre új visszajelzési rekordot hoz létre.",
"refresh_feedback_records": "Visszajelzési rekordok frissítése",
"refreshing_feedback_records": "Visszajelzési rekordok frissítése...",
"required": "Kötelező",
"save_changes": "Változtatások mentése",
"select_a_survey_to_see_questions": "Válassz egy kérdőívet a kérdések megtekintéséhez",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Válassza ki, mely kérdőívkérdések hozzanak létre visszajelzési rekordokat.",
"set_value": "érték beállítása",
"setup_connection": "Kapcsolat beállítása",
"showing_count": "{count} / {total} rekord megjelenítése",
"showing_rows": "3 megjelenítve {count} sorból",
"source": "forrás",
"source_connect_csv_description": "Visszajelzések importálása CSV fájlokból",
"source_connect_formbricks_description": "Visszajelzések csatlakoztatása a Formbricks kérdőívekből",
"source_fields": "Forrásmezők",
"source_name": "Forrásnév",
"source_type": "Forrás típus",
"source_type_cannot_be_changed": "A forrástípus nem módosítható",
"sources": "Források",
"status_active": "Folyamatban",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Frissítse a leképezési konfigurációt ehhez a forráshoz.",
"updated_at": "Frissítve",
"upload_csv_data_description": "Tölts fel egy CSV fájlt a visszajelzési adatok importálásához.",
"upload_csv_file": "CSV fájl feltöltése"
"upload_csv_file": "CSV fájl feltöltése",
"user_identifier": "Felhasználó",
"value": "Érték"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "新規",
"new_version_available": "Formbricks {version} が利用可能です。今すぐアップグレード!",
"next": "次へ",
"no": "いいえ",
"no_background_image_found": "背景画像が見つかりません。",
"no_code": "ノーコード",
"no_files_uploaded": "ファイルがアップロードされていません",
@@ -361,6 +362,7 @@
"response_id": "回答ID",
"responses": "回答",
"restart": "再開",
"retry": "再試行",
"role": "役割",
"saas": "SaaS",
"sales": "セールス",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "ワークスペースの権限が見つかりません",
"workspaces": "ワークスペース",
"years": "年",
"yes": "はい",
"you": "あなた",
"you_are_downgraded_to_the_community_edition": "コミュニティ版にダウングレードされました。",
"you_are_not_authorized_to_perform_this_action": "このアクションを実行する権限がありません。",
@@ -2047,6 +2050,7 @@
"change_file": "ファイルを変更",
"click_load_sample_csv": "「サンプルCSVを読み込む」をクリックして列を表示",
"click_to_upload": "クリックしてアップロード",
"collected_at": "収集日時",
"configure_import": "インポートを設定",
"configure_mapping": "マッピングを設定",
"connection": "接続",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "このソースの名前を入力",
"enter_value": "値を入力...",
"enum": "列挙型",
"failed_to_load_feedback_records": "フィードバックレコードの読み込みに失敗しました",
"feedback_date": "現在の日付",
"feedback_record_fields": "フィードバックレコードフィールド",
"feedback_records": "フィードバックレコード",
"feedback_records_refreshed": "フィードバックレコードを更新しました",
"field_label": "フィールドラベル",
"field_type": "フィールドタイプ",
"formbricks_surveys": "Formbricks フォーム",
"historical_import_complete": "インポート完了: {successes}件成功、{failures}件失敗、{skipped}件スキップ(データなし)",
"import_csv_data": "フィードバックをインポート",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "過去のデータをインポート中...",
"invalid_enum_values": "{field}にマッピングされた列に無効な値があります",
"invalid_values_found": "検出された値: {values}(行: {rows}{extra}",
"load_more": "さらに読み込む",
"load_sample_csv": "サンプルCSVを読み込む",
"n_supported_questions": "{count} 件のサポートされている質問",
"no_feedback_records": "フィードバックレコードはまだありません。コネクタがデータの送信を開始すると、ここにレコードが表示されます。",
"no_source_fields_loaded": "ソースフィールドがまだ読み込まれていません",
"no_sources_connected": "ソースがまだ接続されていません。開始するにはソースを追加してください。",
"no_surveys_found": "この環境にフォームが見つかりません",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong>件の質問が選択されています。これらの質問への各回答は、新しいフィードバックレコードを作成します。",
"question_type_not_supported": "この質問タイプはサポートされていません",
"questions_selected": "<strong>{count}</strong>件の質問が選択されています。これらの質問への各回答は、新しいフィードバックレコードを作成します。",
"refresh_feedback_records": "フィードバック記録を更新",
"refreshing_feedback_records": "フィードバックレコードを更新中...",
"required": "必須",
"save_changes": "変更を保存",
"select_a_survey_to_see_questions": "フォームを選択して質問を表示",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "フィードバックレコードを作成するフォームの質問を選択してください。",
"set_value": "値を設定",
"setup_connection": "接続を設定",
"showing_count": "{total}件中{count}件を表示",
"showing_rows": "{count}行中3行を表示",
"source": "ソース",
"source_connect_csv_description": "CSVファイルからフィードバックをインポート",
"source_connect_formbricks_description": "Formbricksフォームからフィードバックを接続",
"source_fields": "ソースフィールド",
"source_name": "ソース名",
"source_type": "ソースタイプ",
"source_type_cannot_be_changed": "ソースタイプは変更できません",
"sources": "ソース",
"status_active": "進行中",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "このソースのマッピング設定を更新します。",
"updated_at": "更新日時",
"upload_csv_data_description": "CSVファイルをアップロードして、フィードバックデータをインポートします。",
"upload_csv_file": "CSVファイルをアップロード"
"upload_csv_file": "CSVファイルをアップロード",
"user_identifier": "ユーザー",
"value": "値"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Nieuw",
"new_version_available": "Formbricks {version} is hier. Upgrade nu!",
"next": "Volgende",
"no": "Nee",
"no_background_image_found": "Geen achtergrondafbeelding gevonden.",
"no_code": "Geen code",
"no_files_uploaded": "Er zijn geen bestanden geüpload",
@@ -361,6 +362,7 @@
"response_id": "Antwoord-ID",
"responses": "Reacties",
"restart": "Opnieuw opstarten",
"retry": "Opnieuw proberen",
"role": "Rol",
"saas": "SaaS",
"sales": "Verkoop",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Werkruimte-machtiging niet gevonden",
"workspaces": "Werkruimtes",
"years": "jaren",
"yes": "Ja",
"you": "Jij",
"you_are_downgraded_to_the_community_edition": "Je bent gedowngraded naar de Community-editie.",
"you_are_not_authorized_to_perform_this_action": "U bent niet geautoriseerd om deze actie uit te voeren.",
@@ -2047,6 +2050,7 @@
"change_file": "Bestand wijzigen",
"click_load_sample_csv": "Klik op 'Voorbeeld CSV laden' om kolommen te zien",
"click_to_upload": "Klik om te uploaden",
"collected_at": "Verzameld op",
"configure_import": "Import configureren",
"configure_mapping": "Koppeling configureren",
"connection": "Verbinding",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Voer een naam in voor deze bron",
"enter_value": "Voer waarde in...",
"enum": "enum",
"failed_to_load_feedback_records": "Kan feedbackrecords niet laden",
"feedback_date": "Huidige datum",
"feedback_record_fields": "Feedbackrecordvelden",
"feedback_records": "Feedbackrecords",
"feedback_records_refreshed": "Feedbackrecords vernieuwd",
"field_label": "Veldlabel",
"field_type": "Veldtype",
"formbricks_surveys": "Formbricks Surveys",
"historical_import_complete": "Import voltooid: {successes} geslaagd, {failures} mislukt, {skipped} overgeslagen (geen data)",
"import_csv_data": "Feedback importeren",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Historische gegevens importeren...",
"invalid_enum_values": "Ongeldige waarden in kolom gekoppeld aan {field}",
"invalid_values_found": "Gevonden: {values} (rijen: {rows}) {extra}",
"load_more": "Laad meer",
"load_sample_csv": "Voorbeeld-CSV laden",
"n_supported_questions": "{count} ondersteunde vragen",
"no_feedback_records": "Nog geen feedbackrecords. Records verschijnen hier zodra je connectoren gegevens beginnen te verzenden.",
"no_source_fields_loaded": "Nog geen bronvelden geladen",
"no_sources_connected": "Nog geen bronnen verbonden. Voeg een bron toe om te beginnen.",
"no_surveys_found": "Geen enquêtes gevonden in deze omgeving",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> vraag geselecteerd. Elk antwoord op deze vraag zal een nieuw feedbackrecord aanmaken.",
"question_type_not_supported": "Dit vraagtype wordt niet ondersteund",
"questions_selected": "<strong>{count}</strong> vragen geselecteerd. Elk antwoord op deze vragen zal een nieuw feedbackrecord aanmaken.",
"refresh_feedback_records": "Feedbackrecords verversen",
"refreshing_feedback_records": "Feedbackrecords vernieuwen...",
"required": "Vereist",
"save_changes": "Wijzigingen opslaan",
"select_a_survey_to_see_questions": "Selecteer een enquête om de vragen te zien",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Kies welke enquêtevragen FeedbackRecords moeten aanmaken.",
"set_value": "waarde instellen",
"setup_connection": "Verbinding instellen",
"showing_count": "{count} van {total} records weergegeven",
"showing_rows": "3 van {count} rijen weergegeven",
"source": "bron",
"source_connect_csv_description": "Importeer feedback uit CSV-bestanden",
"source_connect_formbricks_description": "Verbind feedback van je Formbricks-enquêtes",
"source_fields": "Bronvelden",
"source_name": "Bronnaam",
"source_type": "Brontype",
"source_type_cannot_be_changed": "Brontype kan niet worden gewijzigd",
"sources": "Bronnen",
"status_active": "In uitvoering",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Werk de mappingconfiguratie voor deze bron bij.",
"updated_at": "Bijgewerkt op",
"upload_csv_data_description": "Upload een CSV-bestand om feedbackgegevens te importeren.",
"upload_csv_file": "CSV-bestand uploaden"
"upload_csv_file": "CSV-bestand uploaden",
"user_identifier": "Gebruiker",
"value": "Waarde"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Novo",
"new_version_available": "Formbricks {version} chegou. Atualize agora!",
"next": "Próximo",
"no": "Não",
"no_background_image_found": "Imagem de fundo não encontrada.",
"no_code": "Sem código",
"no_files_uploaded": "Nenhum arquivo foi enviado",
@@ -361,6 +362,7 @@
"response_id": "ID da resposta",
"responses": "Respostas",
"restart": "Reiniciar",
"retry": "Tentar novamente",
"role": "Rolê",
"saas": "SaaS",
"sales": "vendas",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Permissão do projeto não encontrada",
"workspaces": "Projetos",
"years": "anos",
"yes": "Sim",
"you": "Você",
"you_are_downgraded_to_the_community_edition": "Você foi rebaixado para a Edição Comunitária.",
"you_are_not_authorized_to_perform_this_action": "Você não tem autorização para realizar essa ação.",
@@ -2047,6 +2050,7 @@
"change_file": "Alterar arquivo",
"click_load_sample_csv": "Clique em 'Carregar CSV de exemplo' para ver as colunas",
"click_to_upload": "Clique para fazer upload",
"collected_at": "Coletado em",
"configure_import": "Configurar importação",
"configure_mapping": "Configurar mapeamento",
"connection": "Conexão",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Digite um nome para esta origem",
"enter_value": "Digite o valor...",
"enum": "enum",
"failed_to_load_feedback_records": "Falha ao carregar registros de feedback",
"feedback_date": "Data atual",
"feedback_record_fields": "Campos de registro de feedback",
"feedback_record_fields": "Campos do registro de feedback",
"feedback_records": "Registros de feedback",
"feedback_records_refreshed": "Registros de feedback atualizados",
"field_label": "Rótulo do campo",
"field_type": "Tipo de campo",
"formbricks_surveys": "Pesquisas Formbricks",
"historical_import_complete": "Importação concluída: {successes} bem-sucedidas, {failures} falharam, {skipped} ignoradas (sem dados)",
"import_csv_data": "Importar feedback",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Importando dados históricos...",
"invalid_enum_values": "Valores inválidos na coluna mapeada para {field}",
"invalid_values_found": "Encontrados: {values} (linhas: {rows}) {extra}",
"load_more": "Carregar mais",
"load_sample_csv": "Carregar CSV de exemplo",
"n_supported_questions": "{count} perguntas suportadas",
"no_feedback_records": "Nenhum registro de feedback ainda. Os registros aparecerão aqui assim que seus conectores começarem a enviar dados.",
"no_source_fields_loaded": "Nenhum campo de origem carregado ainda",
"no_sources_connected": "Nenhuma origem conectada ainda. Adicione uma origem para começar.",
"no_surveys_found": "Nenhuma pesquisa encontrada neste ambiente",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> pergunta selecionada. Cada resposta a esta pergunta criará um novo registro de feedback.",
"question_type_not_supported": "Este tipo de pergunta não é suportado",
"questions_selected": "<strong>{count}</strong> perguntas selecionadas. Cada resposta a estas perguntas criará um novo registro de feedback.",
"refresh_feedback_records": "Atualizar registros de feedback",
"refreshing_feedback_records": "Atualizando registros de feedback...",
"required": "Obrigatório",
"save_changes": "Salvar alterações",
"select_a_survey_to_see_questions": "Selecione uma pesquisa para ver suas perguntas",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Escolha quais perguntas da pesquisa devem criar FeedbackRecords.",
"set_value": "definir valor",
"setup_connection": "Configurar conexão",
"showing_count": "Mostrando {count} de {total} registros",
"showing_rows": "Mostrando 3 de {count} linhas",
"source": "fonte",
"source_connect_csv_description": "Importar feedback de arquivos CSV",
"source_connect_formbricks_description": "Conectar feedback das suas pesquisas Formbricks",
"source_fields": "Campos de origem",
"source_name": "Nome da origem",
"source_type": "Tipo de fonte",
"source_type_cannot_be_changed": "O tipo de origem não pode ser alterado",
"sources": "Origens",
"status_active": "Em andamento",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Atualize a configuração de mapeamento para esta fonte.",
"updated_at": "Atualizado em",
"upload_csv_data_description": "Faça upload de um arquivo CSV para importar dados de feedback.",
"upload_csv_file": "Fazer upload de arquivo CSV"
"upload_csv_file": "Fazer upload de arquivo CSV",
"user_identifier": "Usuário",
"value": "Valor"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Novo",
"new_version_available": "Formbricks {version} está aqui. Atualize agora!",
"next": "Seguinte",
"no": "Não",
"no_background_image_found": "Nenhuma imagem de fundo encontrada.",
"no_code": "Sem código",
"no_files_uploaded": "Nenhum ficheiro foi carregado",
@@ -361,6 +362,7 @@
"response_id": "ID de resposta",
"responses": "Respostas",
"restart": "Reiniciar",
"retry": "Tentar novamente",
"role": "Função",
"saas": "SaaS",
"sales": "Vendas",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Permissão do projeto não encontrada",
"workspaces": "Projetos",
"years": "anos",
"yes": "Sim",
"you": "Você",
"you_are_downgraded_to_the_community_edition": "Foi rebaixado para a Edição Comunitária.",
"you_are_not_authorized_to_perform_this_action": "Não está autorizado a realizar esta ação.",
@@ -2047,6 +2050,7 @@
"change_file": "Alterar ficheiro",
"click_load_sample_csv": "Clique em 'Carregar CSV de exemplo' para ver as colunas",
"click_to_upload": "Clique para carregar",
"collected_at": "Recolhido em",
"configure_import": "Configurar importação",
"configure_mapping": "Configurar mapeamento",
"connection": "Conexão",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Introduz um nome para esta origem",
"enter_value": "Introduzir valor...",
"enum": "enum",
"failed_to_load_feedback_records": "Falha ao carregar registos de feedback",
"feedback_date": "Data atual",
"feedback_record_fields": "Campos de registo de feedback",
"feedback_records": "Registos de feedback",
"feedback_records_refreshed": "Registos de feedback atualizados",
"field_label": "Etiqueta do campo",
"field_type": "Tipo de campo",
"formbricks_surveys": "Pesquisas Formbricks",
"historical_import_complete": "Importação concluída: {successes} com sucesso, {failures} falharam, {skipped} ignorados (sem dados)",
"import_csv_data": "Importar feedback",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "A importar dados históricos...",
"invalid_enum_values": "Valores inválidos na coluna mapeada para {field}",
"invalid_values_found": "Encontrados: {values} (linhas: {rows}) {extra}",
"load_more": "Carregar mais",
"load_sample_csv": "Carregar CSV de exemplo",
"n_supported_questions": "{count} perguntas suportadas",
"no_feedback_records": "Ainda não há registos de feedback. Os registos aparecerão aqui assim que os teus conectores começarem a enviar dados.",
"no_source_fields_loaded": "Ainda não foram carregados campos de origem",
"no_sources_connected": "Ainda não há origens ligadas. Adicione uma origem para começar.",
"no_surveys_found": "Nenhum inquérito encontrado neste ambiente",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> pergunta selecionada. Cada resposta a esta pergunta criará um novo registo de feedback.",
"question_type_not_supported": "Este tipo de pergunta não é suportado",
"questions_selected": "<strong>{count}</strong> perguntas selecionadas. Cada resposta a estas perguntas criará um novo registo de feedback.",
"refresh_feedback_records": "Atualizar registos de feedback",
"refreshing_feedback_records": "A atualizar registos de feedback...",
"required": "Obrigatório",
"save_changes": "Guardar alterações",
"select_a_survey_to_see_questions": "Selecione um inquérito para ver as suas perguntas",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Escolha quais perguntas do inquérito devem criar FeedbackRecords.",
"set_value": "definir valor",
"setup_connection": "Configurar ligação",
"showing_count": "A mostrar {count} de {total} registos",
"showing_rows": "A mostrar 3 de {count} linhas",
"source": "fonte",
"source_connect_csv_description": "Importar feedback de ficheiros CSV",
"source_connect_formbricks_description": "Conectar feedback dos seus inquéritos Formbricks",
"source_fields": "Campos da fonte",
"source_name": "Nome da fonte",
"source_type": "Tipo de fonte",
"source_type_cannot_be_changed": "O tipo de fonte não pode ser alterado",
"sources": "Fontes",
"status_active": "Em progresso",
@@ -2130,8 +2145,10 @@
"unify_feedback": "Unificar feedback",
"update_mapping_description": "Atualiza a configuração de mapeamento para esta origem.",
"updated_at": "Atualizado em",
"upload_csv_data_description": "Carregue um ficheiro CSV para importar dados de feedback.",
"upload_csv_file": "Carregar ficheiro CSV"
"upload_csv_data_description": "Carrega um ficheiro CSV para importar dados de feedback.",
"upload_csv_file": "Carregar ficheiro CSV",
"user_identifier": "Utilizador",
"value": "Valor"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Nou",
"new_version_available": "Formbricks {version} este disponibil. Actualizați acum!",
"next": "Următorul",
"no": "Nu",
"no_background_image_found": "Nu a fost găsită nicio imagine de fundal.",
"no_code": "Fără Cod",
"no_files_uploaded": "Nu au fost încărcate fișiere",
@@ -361,6 +362,7 @@
"response_id": "ID răspuns",
"responses": "Răspunsuri",
"restart": "Repornește",
"retry": "Reîncearcă",
"role": "Rolul",
"saas": "SaaS",
"sales": "Vânzări",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Permisiunea pentru workspace nu a fost găsită",
"workspaces": "Workspaces",
"years": "ani",
"yes": "Da",
"you": "Tu",
"you_are_downgraded_to_the_community_edition": "Ai fost retrogradat la ediția Community.",
"you_are_not_authorized_to_perform_this_action": "Nu sunteți autorizat să efectuați această acțiune.",
@@ -2047,6 +2050,7 @@
"change_file": "Schimbă fișierul",
"click_load_sample_csv": "Apasă pe „Încarcă CSV de exemplu” pentru a vedea coloanele",
"click_to_upload": "Apasă pentru a încărca",
"collected_at": "Colectat la",
"configure_import": "Configurează importul",
"configure_mapping": "Configurează maparea",
"connection": "Conexiune",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Introdu un nume pentru această sursă",
"enter_value": "Introdu valoarea...",
"enum": "enum",
"failed_to_load_feedback_records": "Nu s-au putut încărca înregistrările de feedback",
"feedback_date": "Data curentă",
"feedback_record_fields": "Câmpuri ale înregistrării de feedback",
"feedback_record_fields": "Câmpuri înregistrare feedback",
"feedback_records": "Înregistrări de feedback",
"feedback_records_refreshed": "Înregistrările de feedback au fost actualizate",
"field_label": "Etichetă câmp",
"field_type": "Tip câmp",
"formbricks_surveys": "Chestionare Formbricks",
"historical_import_complete": "Import finalizat: {successes} reușite, {failures} eșuate, {skipped} omise (fără date)",
"import_csv_data": "Importă feedback",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Se importă datele istorice...",
"invalid_enum_values": "Valori invalide în coloana mapată la {field}",
"invalid_values_found": "Găsite: {values} (rânduri: {rows}) {extra}",
"load_more": "Încarcă mai multe",
"load_sample_csv": "Încarcă un CSV de exemplu",
"n_supported_questions": "{count} întrebări acceptate",
"no_feedback_records": "Nu există încă înregistrări de feedback. Înregistrările vor apărea aici după ce conectorii tăi vor începe să trimită date.",
"no_source_fields_loaded": "Nu au fost încă încărcate câmpuri sursă",
"no_sources_connected": "Nicio sursă conectată încă. Adaugă o sursă pentru a începe.",
"no_surveys_found": "Nu s-au găsit sondaje în acest mediu",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> întrebare selectată. Fiecare răspuns la aceste întrebări va crea un nou Feedback Record.",
"question_type_not_supported": "Acest tip de întrebare nu este suportat",
"questions_selected": "<strong>{count}</strong> întrebări selectate. Fiecare răspuns la aceste întrebări va crea un nou Feedback Record.",
"refresh_feedback_records": "Reîmprospătează înregistrările de feedback",
"refreshing_feedback_records": "Se actualizează înregistrările de feedback...",
"required": "Obligatoriu",
"save_changes": "Salvează modificările",
"select_a_survey_to_see_questions": "Selectează un chestionar pentru a vedea întrebările",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Alege ce întrebări din chestionar vor crea FeedbackRecords.",
"set_value": "setează valoare",
"setup_connection": "Configurează conexiunea",
"showing_count": "Se afișează {count} din {total} înregistrări",
"showing_rows": "Se afișează 3 din {count} rânduri",
"source": "sursă",
"source_connect_csv_description": "Importă feedback din fișiere CSV",
"source_connect_formbricks_description": "Conectează feedback din sondajele Formbricks",
"source_fields": "Câmpuri sursă",
"source_name": "Nume sursă",
"source_type": "Tip sursă",
"source_type_cannot_be_changed": "Tipul sursei nu poate fi schimbat",
"sources": "Surse",
"status_active": "În progres",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Actualizează configurația de mapare pentru această sursă.",
"updated_at": "Actualizat la",
"upload_csv_data_description": "Încarcă un fișier CSV pentru a importa date de feedback.",
"upload_csv_file": "Încarcă fișier CSV"
"upload_csv_file": "Încarcă fișier CSV",
"user_identifier": "Utilizator",
"value": "Valoare"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Новый",
"new_version_available": "Formbricks {version} уже здесь. Обновитесь сейчас!",
"next": "Далее",
"no": "Нет",
"no_background_image_found": "Фоновое изображение не найдено.",
"no_code": "Нет кода",
"no_files_uploaded": "Файлы не были загружены",
@@ -361,6 +362,7 @@
"response_id": "ID ответа",
"responses": "Ответы",
"restart": "Перезапустить",
"retry": "Повторить",
"role": "Роль",
"saas": "SaaS",
"sales": "Продажи",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Разрешение на рабочее пространство не найдено",
"workspaces": "Рабочие пространства",
"years": "годы",
"yes": "Да",
"you": "Вы",
"you_are_downgraded_to_the_community_edition": "Ваша версия понижена до Community Edition.",
"you_are_not_authorized_to_perform_this_action": "У вас нет прав для выполнения этого действия.",
@@ -2047,6 +2050,7 @@
"change_file": "Изменить файл",
"click_load_sample_csv": "Нажмите «Загрузить пример CSV», чтобы увидеть столбцы",
"click_to_upload": "Кликните для загрузки",
"collected_at": "Собрано",
"configure_import": "Настроить импорт",
"configure_mapping": "Настроить сопоставление",
"connection": "Подключение",
@@ -2064,7 +2068,7 @@
"csv_files_only": "Только файлы CSV",
"csv_import": "Импорт CSV",
"csv_import_complete": "Импорт CSV завершён: {successes} успешно, {failures} с ошибками, {skipped} пропущено",
"csv_import_duplicate_warning": "Если импортировать данные дважды, будут созданы дубликаты записей.",
"csv_import_duplicate_warning": "Импорт уже загруженных данных может создать дубликаты записей.",
"csv_inconsistent_columns": "В строке {row} несоответствие столбцов. Во всех строках должны быть одинаковые заголовки.",
"csv_max_records": "Допустимо не более {max} записей.",
"default_connector_name_csv": "Импорт CSV",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Введи имя для этого источника",
"enter_value": "Введите значение...",
"enum": "enum",
"failed_to_load_feedback_records": "Не удалось загрузить отзывы",
"feedback_date": "Текущая дата",
"feedback_record_fields": "Поля записи обратной связи",
"feedback_record_fields": "Поля записи отзыва",
"feedback_records": "Записи отзывов",
"feedback_records_refreshed": "Записи отзывов обновлены",
"field_label": "Метка поля",
"field_type": "Тип поля",
"formbricks_surveys": "Formbricks Surveys",
"historical_import_complete": "Импорт завершён: {successes} успешно, {failures} с ошибками, {skipped} пропущено (нет данных)",
"import_csv_data": "Импортировать отзывы",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Импорт исторических данных...",
"invalid_enum_values": "Недопустимые значения в столбце, сопоставленном с {field}",
"invalid_values_found": "Найдено: {values} (строки: {rows}) {extra}",
"load_more": "Загрузить ещё",
"load_sample_csv": "Загрузить пример CSV",
"n_supported_questions": "Поддерживается {count} вопрос(ов)",
"no_feedback_records": "Пока нет записей отзывов. Они появятся здесь, когда коннекторы начнут отправлять данные.",
"no_source_fields_loaded": "Поля источника ещё не загружены",
"no_sources_connected": "Нет подключённых источников. Добавьте источник, чтобы начать.",
"no_surveys_found": "В этой среде не найдено опросов",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> выбранный вопрос. Каждый ответ на эти вопросы создаст новую запись обратной связи.",
"question_type_not_supported": "Этот тип вопроса не поддерживается",
"questions_selected": "<strong>{count}</strong> выбранных вопроса. Каждый ответ на эти вопросы создаст новую запись обратной связи.",
"refresh_feedback_records": "Обновить записи отзывов",
"refreshing_feedback_records": "Обновляем записи отзывов...",
"required": "Обязательно",
"save_changes": "Сохранить изменения",
"select_a_survey_to_see_questions": "Выберите опрос, чтобы увидеть его вопросы",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Выберите, какие вопросы опроса должны создавать FeedbackRecords.",
"set_value": "установить значение",
"setup_connection": "Настроить подключение",
"showing_count": "Показано {count} из {total} записей",
"showing_rows": "Показано 3 из {count} строк",
"source": "источник",
"source_connect_csv_description": "Импортировать отзывы из CSV-файлов",
"source_connect_formbricks_description": "Подключить отзывы из ваших опросов Formbricks",
"source_fields": "Поля источника",
"source_name": "Имя источника",
"source_type": "Тип источника",
"source_type_cannot_be_changed": "Тип источника нельзя изменить",
"sources": "Источники",
"status_active": "В процессе",
@@ -2130,8 +2145,10 @@
"unify_feedback": "Обратная связь Unify",
"update_mapping_description": "Обнови настройки сопоставления для этого источника.",
"updated_at": "Обновлено",
"upload_csv_data_description": "Загрузите CSV-файл, чтобы импортировать данные обратной связи.",
"upload_csv_file": "Загрузить CSV-файл"
"upload_csv_data_description": "Загрузи CSV-файл, чтобы импортировать данные отзывов.",
"upload_csv_file": "Загрузить CSV-файл",
"user_identifier": "Пользователь",
"value": "Значение"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "Ny",
"new_version_available": "Formbricks {version} är här. Uppgradera nu!",
"next": "Nästa",
"no": "Nej",
"no_background_image_found": "Ingen bakgrundsbild hittades.",
"no_code": "Ingen kod",
"no_files_uploaded": "Inga filer laddades upp",
@@ -361,6 +362,7 @@
"response_id": "Svar-ID",
"responses": "Svar",
"restart": "Starta om",
"retry": "Försök igen",
"role": "Roll",
"saas": "SaaS",
"sales": "Försäljning",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "Arbetsytebehörighet hittades inte",
"workspaces": "Arbetsytor",
"years": "år",
"yes": "Ja",
"you": "Du",
"you_are_downgraded_to_the_community_edition": "Du har nedgraderats till Community Edition.",
"you_are_not_authorized_to_perform_this_action": "Du har inte behörighet att utföra denna åtgärd.",
@@ -2047,6 +2050,7 @@
"change_file": "Byt fil",
"click_load_sample_csv": "Klicka på 'Ladda exempel-CSV' för att se kolumner",
"click_to_upload": "Klicka för att ladda upp",
"collected_at": "Insamlad",
"configure_import": "Konfigurera import",
"configure_mapping": "Konfigurera mappning",
"connection": "Anslutning",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "Ange ett namn för denna källa",
"enter_value": "Ange värde...",
"enum": "enum",
"failed_to_load_feedback_records": "Det gick inte att ladda feedbackposter",
"feedback_date": "Aktuellt datum",
"feedback_record_fields": "Fält för feedbackpost",
"feedback_records": "Feedbackposter",
"feedback_records_refreshed": "Feedbackposter har uppdaterats",
"field_label": "Fältetikett",
"field_type": "Fälttyp",
"formbricks_surveys": "Formbricks Surveys",
"historical_import_complete": "Importen klar: {successes} lyckades, {failures} misslyckades, {skipped} hoppades över (ingen data)",
"import_csv_data": "Importera feedback",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "Importerar historisk data...",
"invalid_enum_values": "Ogiltiga värden i kolumnen som är kopplad till {field}",
"invalid_values_found": "Hittade: {values} (rader: {rows}) {extra}",
"load_more": "Ladda mer",
"load_sample_csv": "Ladda exempel-CSV",
"n_supported_questions": "{count} stödda frågor",
"no_feedback_records": "Inga feedbackposter ännu. Poster visas här när dina connectors börjar skicka data.",
"no_source_fields_loaded": "Inga källfält har laddats än",
"no_sources_connected": "Inga källor är anslutna än. Lägg till en källa för att komma igång.",
"no_surveys_found": "Inga enkäter hittades i denna miljö",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> fråga vald. Varje svar på dessa frågor skapar en ny feedbackpost.",
"question_type_not_supported": "Den här frågetypen stöds inte",
"questions_selected": "<strong>{count}</strong> frågor valda. Varje svar på dessa frågor skapar en ny feedbackpost.",
"refresh_feedback_records": "Uppdatera feedbackposter",
"refreshing_feedback_records": "Uppdaterar feedbackposter...",
"required": "Obligatoriskt",
"save_changes": "Spara ändringar",
"select_a_survey_to_see_questions": "Välj en enkät för att se dess frågor",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "Välj vilka enkätfrågor som ska skapa FeedbackRecords.",
"set_value": "ange värde",
"setup_connection": "Ställ in anslutning",
"showing_count": "Visar {count} av {total} poster",
"showing_rows": "Visar 3 av {count} rader",
"source": "källa",
"source_connect_csv_description": "Importera feedback från CSV-filer",
"source_connect_formbricks_description": "Anslut feedback från dina Formbricks-enkäter",
"source_fields": "Källfält",
"source_name": "Källnamn",
"source_type": "Källtyp",
"source_type_cannot_be_changed": "Källtyp kan inte ändras",
"sources": "Källor",
"status_active": "Pågående",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "Uppdatera mappningskonfigurationen för den här källan.",
"updated_at": "Uppdaterad",
"upload_csv_data_description": "Ladda upp en CSV-fil för att importera feedbackdata.",
"upload_csv_file": "Ladda upp CSV-fil"
"upload_csv_file": "Ladda upp CSV-fil",
"user_identifier": "Användare",
"value": "Värde"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "新建",
"new_version_available": "Formbricks {version} 在 这里。立即 升级!",
"next": "下一步",
"no": "否",
"no_background_image_found": "未找到 背景 图片。",
"no_code": "无代码",
"no_files_uploaded": "没有 文件 被 上传",
@@ -361,6 +362,7 @@
"response_id": "响应 ID",
"responses": "反馈",
"restart": "重新启动",
"retry": "重试",
"role": "角色",
"saas": "SaaS",
"sales": "销售",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "未找到工作区权限",
"workspaces": "工作区",
"years": "年",
"yes": "是",
"you": "你 ",
"you_are_downgraded_to_the_community_edition": "您已降级到社区版。",
"you_are_not_authorized_to_perform_this_action": "您无权执行此操作。",
@@ -2047,6 +2050,7 @@
"change_file": "更换文件",
"click_load_sample_csv": "点击“加载示例 CSV”查看列",
"click_to_upload": "点击上传",
"collected_at": "收集时间",
"configure_import": "配置导入",
"configure_mapping": "配置映射",
"connection": "连接",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "为此来源输入名称",
"enter_value": "请输入值...",
"enum": "枚举",
"failed_to_load_feedback_records": "加载反馈记录失败",
"feedback_date": "当前日期",
"feedback_record_fields": "反馈记录字段",
"feedback_records": "反馈记录",
"feedback_records_refreshed": "反馈记录已刷新",
"field_label": "字段标签",
"field_type": "字段类型",
"formbricks_surveys": "Formbricks Surveys",
"historical_import_complete": "导入完成:{successes} 个成功,{failures} 个失败,{skipped} 个跳过(无数据)",
"import_csv_data": "导入反馈",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "正在导入历史数据…",
"invalid_enum_values": "映射到 {field} 的列中存在无效值",
"invalid_values_found": "发现:{values}(行:{rows}{extra}",
"load_more": "加载更多",
"load_sample_csv": "加载示例 CSV",
"n_supported_questions": "{count} 个支持的问题",
"no_feedback_records": "暂无反馈记录。当你的连接器开始发送数据后,记录会显示在这里。",
"no_source_fields_loaded": "尚未加载源字段",
"no_sources_connected": "还没有连接数据源。添加一个数据源开始吧。",
"no_surveys_found": "此环境下未找到调查",
@@ -2098,6 +2109,8 @@
"question_selected": "<strong>{count}</strong> 个问题已选。每个问题的回答都会创建一条新的反馈记录。",
"question_type_not_supported": "不支持此问题类型",
"questions_selected": "<strong>{count}</strong> 个问题已选。每个问题的回答都会创建一条新的反馈记录。",
"refresh_feedback_records": "刷新反馈记录",
"refreshing_feedback_records": "正在刷新反馈记录…",
"required": "必填",
"save_changes": "保存更改",
"select_a_survey_to_see_questions": "请选择一个调查以查看其问题",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "选择哪些调查问题会创建反馈记录。",
"set_value": "设置值",
"setup_connection": "设置连接",
"showing_count": "正在显示 {count} / {total} 条记录",
"showing_rows": "显示 {count} 行中的 3 行",
"source": "source",
"source_connect_csv_description": "从 CSV 文件导入反馈",
"source_connect_formbricks_description": "连接来自你 Formbricks 调查的反馈",
"source_fields": "来源字段",
"source_name": "来源名称",
"source_type": "来源类型",
"source_type_cannot_be_changed": "来源类型无法更改",
"sources": "来源",
"status_active": "进行中",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "更新此来源的映射配置。",
"updated_at": "更新于",
"upload_csv_data_description": "上传 CSV 文件以导入反馈数据。",
"upload_csv_file": "上传 CSV 文件"
"upload_csv_file": "上传 CSV 文件",
"user_identifier": "用户",
"value": "值"
},
"workspace": {
"api_keys": {

View File

@@ -288,6 +288,7 @@
"new": "新增",
"new_version_available": "Formbricks '{'version'}' 已推出。立即升級!",
"next": "下一步",
"no": "否",
"no_background_image_found": "找不到背景圖片。",
"no_code": "無程式碼",
"no_files_uploaded": "沒有上傳任何檔案",
@@ -361,6 +362,7 @@
"response_id": "回應 ID",
"responses": "回應",
"restart": "重新開始",
"retry": "重試",
"role": "角色",
"saas": "SaaS",
"sales": "銷售",
@@ -468,6 +470,7 @@
"workspace_permission_not_found": "找不到工作區權限",
"workspaces": "工作區",
"years": "年",
"yes": "是",
"you": "您",
"you_are_downgraded_to_the_community_edition": "您已降級至社群版。",
"you_are_not_authorized_to_perform_this_action": "您沒有執行此操作的權限。",
@@ -2047,6 +2050,7 @@
"change_file": "更換檔案",
"click_load_sample_csv": "點擊「載入範例 CSV」以查看欄位",
"click_to_upload": "點擊以上傳",
"collected_at": "收集時間",
"configure_import": "設定匯入",
"configure_mapping": "設定對應關係",
"connection": "連線",
@@ -2077,8 +2081,13 @@
"enter_name_for_source": "請輸入此來源的名稱",
"enter_value": "請輸入值……",
"enum": "enum",
"failed_to_load_feedback_records": "載入回饋紀錄失敗",
"feedback_date": "目前日期",
"feedback_record_fields": "回饋紀錄欄位",
"feedback_records": "回饋紀錄",
"feedback_records_refreshed": "回饋紀錄已更新",
"field_label": "欄位標籤",
"field_type": "欄位類型",
"formbricks_surveys": "Formbricks 問卷",
"historical_import_complete": "匯入完成:{successes} 筆成功,{failures} 筆失敗,{skipped} 筆略過(無資料)",
"import_csv_data": "匯入 CSV 資料",
@@ -2088,8 +2097,10 @@
"importing_historical_data": "正在匯入歷史資料…",
"invalid_enum_values": "對應到 {field} 欄位的值無效",
"invalid_values_found": "發現:{values}(列:{rows}{extra}",
"load_more": "載入更多",
"load_sample_csv": "載入範例 CSV",
"n_supported_questions": "{count} 個支援的問題",
"no_feedback_records": "目前尚無回饋紀錄。當你的連接器開始傳送資料時,紀錄會顯示在這裡。",
"no_source_fields_loaded": "尚未載入來源欄位",
"no_sources_connected": "尚未連接任何來源。請新增來源以開始使用。",
"no_surveys_found": "此環境中找不到問卷",
@@ -2098,6 +2109,8 @@
"question_selected": "已選擇 <strong>{count}</strong> 題。每份這些題目的回應都會建立一筆新的意見紀錄。",
"question_type_not_supported": "不支援此題型",
"questions_selected": "已選擇 <strong>{count}</strong> 題。每份這些題目的回應都會建立一筆新的意見紀錄。",
"refresh_feedback_records": "重新整理回饋紀錄",
"refreshing_feedback_records": "正在更新回饋紀錄…",
"required": "必填",
"save_changes": "儲存變更",
"select_a_survey_to_see_questions": "請選擇問卷以查看其問題",
@@ -2111,12 +2124,14 @@
"select_survey_questions_description": "請選擇哪些問卷問題要建立 FeedbackRecords。",
"set_value": "設定值",
"setup_connection": "設定連線",
"showing_count": "顯示 {count} 筆,共 {total} 筆紀錄",
"showing_rows": "顯示 {count} 筆資料中的 3 筆",
"source": "來源",
"source_connect_csv_description": "從 CSV 檔案匯入回饋",
"source_connect_formbricks_description": "連接來自你 Formbricks 問卷的回饋",
"source_fields": "來源欄位",
"source_name": "來源名稱",
"source_type": "來源類型",
"source_type_cannot_be_changed": "來源類型無法變更",
"sources": "來源",
"status_active": "進行中",
@@ -2131,7 +2146,9 @@
"update_mapping_description": "更新此來源的對應設定。",
"updated_at": "更新時間",
"upload_csv_data_description": "上傳 CSV 檔案以匯入回饋資料。",
"upload_csv_file": "上傳 CSV 檔案"
"upload_csv_file": "上傳 CSV 檔案",
"user_identifier": "使用者",
"value": "值"
},
"workspace": {
"api_keys": {

View File

@@ -1,3 +1,14 @@
export { getHubClient } from "./hub-client";
export { createFeedbackRecord, createFeedbackRecordsBatch, type CreateFeedbackRecordResult } from "./service";
export type { FeedbackRecordCreateParams, FeedbackRecordData } from "./types";
export {
createFeedbackRecord,
createFeedbackRecordsBatch,
listFeedbackRecords,
type CreateFeedbackRecordResult,
type ListFeedbackRecordsResult,
} from "./service";
export type {
FeedbackRecordCreateParams,
FeedbackRecordData,
FeedbackRecordListParams,
FeedbackRecordListResponse,
} from "./types";

View File

@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, test, vi } from "vitest";
import { createFeedbackRecord, createFeedbackRecordsBatch } from "./service";
import { createFeedbackRecord, createFeedbackRecordsBatch, listFeedbackRecords } from "./service";
import type { FeedbackRecordCreateParams } from "./types";
vi.mock("@formbricks/logger", () => ({
@@ -65,6 +65,62 @@ describe("hub service", () => {
});
});
describe("listFeedbackRecords", () => {
test("returns error result when getHubClient returns null", async () => {
vi.mocked(getHubClient).mockReturnValue(null);
const result = await listFeedbackRecords({ tenant_id: "env-1" });
expect(result.data).toBeNull();
expect(result.error).toMatchObject({
status: 0,
message: "HUB_API_KEY is not set; Hub integration is disabled.",
});
});
test("returns data when client.list succeeds", async () => {
const listResponse = {
data: [{ id: "rec-1", field_id: "el-1", field_type: "rating" }],
total: 1,
limit: 50,
offset: 0,
};
vi.mocked(getHubClient).mockReturnValue({
feedbackRecords: { list: vi.fn().mockResolvedValue(listResponse) },
} as any);
const result = await listFeedbackRecords({ tenant_id: "env-1", limit: 50, offset: 0 });
expect(result.error).toBeNull();
expect(result.data).toEqual(listResponse);
});
test("returns error result when client.list throws", async () => {
vi.mocked(getHubClient).mockReturnValue({
feedbackRecords: { list: vi.fn().mockRejectedValue(new Error("Network error")) },
} as any);
const result = await listFeedbackRecords({ tenant_id: "env-1" });
expect(result.data).toBeNull();
expect(result.error).toMatchObject({ status: 0, message: "Network error" });
});
test("returns data when called without params", async () => {
const listResponse = { data: [], total: 0, limit: 50, offset: 0 };
const listFn = vi.fn().mockResolvedValue(listResponse);
vi.mocked(getHubClient).mockReturnValue({
feedbackRecords: { list: listFn },
} as any);
const result = await listFeedbackRecords();
expect(result.error).toBeNull();
expect(result.data).toEqual(listResponse);
expect(listFn).toHaveBeenCalledWith(undefined);
});
});
describe("createFeedbackRecordsBatch", () => {
test("returns all errors when getHubClient returns null", async () => {
vi.mocked(getHubClient).mockReturnValue(null);

View File

@@ -2,7 +2,12 @@ import "server-only";
import FormbricksHub from "@formbricks/hub";
import { logger } from "@formbricks/logger";
import { getHubClient } from "./hub-client";
import type { FeedbackRecordCreateParams, FeedbackRecordData } from "./types";
import type {
FeedbackRecordCreateParams,
FeedbackRecordData,
FeedbackRecordListParams,
FeedbackRecordListResponse,
} from "./types";
export type CreateFeedbackRecordResult = {
data: FeedbackRecordData | null;
@@ -41,6 +46,32 @@ export const createFeedbackRecord = async (
}
};
export type ListFeedbackRecordsResult = {
data: FeedbackRecordListResponse | null;
error: { status: number; message: string; detail: string } | null;
};
/**
* List feedback records from the Hub with optional filters and pagination.
*/
export const listFeedbackRecords = async (
params?: FeedbackRecordListParams
): Promise<ListFeedbackRecordsResult> => {
const client = getHubClient();
if (!client) {
return { data: null, error: { ...NO_CONFIG_ERROR } };
}
try {
const data = await client.feedbackRecords.list(params);
return { data, error: null };
} catch (err) {
logger.warn({ err }, "Hub: listFeedbackRecords failed");
const status = err instanceof FormbricksHub.APIError ? err.status : 0;
const message = err instanceof Error ? err.message : String(err);
return { data: null, error: { status, message, detail: message } };
}
};
/**
* Create multiple feedback records in the Hub in parallel.
* Returns an array of results (data or error) per input; logs failures.

View File

@@ -2,3 +2,5 @@ import type FormbricksHub from "@formbricks/hub";
export type FeedbackRecordCreateParams = FormbricksHub.FeedbackRecordCreateParams;
export type FeedbackRecordData = FormbricksHub.FeedbackRecordData;
export type FeedbackRecordListParams = FormbricksHub.FeedbackRecordListParams;
export type FeedbackRecordListResponse = FormbricksHub.FeedbackRecordListResponse;

View File

@@ -51,7 +51,10 @@ services:
image: ghcr.io/formbricks/hub:latest
restart: "no"
entrypoint: ["sh", "-c"]
command: ["if [ -x /usr/local/bin/goose ] && [ -x /usr/local/bin/river ]; then /usr/local/bin/goose -dir /app/migrations postgres \"$$DATABASE_URL\" up && /usr/local/bin/river migrate-up --database-url \"$$DATABASE_URL\"; else echo 'Migration tools (goose/river) not in image, skipping migrations.'; fi"]
command:
[
'if [ -x /usr/local/bin/goose ] && [ -x /usr/local/bin/river ]; then /usr/local/bin/goose -dir /app/migrations postgres "$$DATABASE_URL" up && /usr/local/bin/river migrate-up --database-url "$$DATABASE_URL"; else echo ''Migration tools (goose/river) not in image, skipping migrations.''; fi',
]
environment:
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres?sslmode=disable
depends_on: