mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-06 11:20:56 -05:00
chore: adds metadata and hidden fields to csv, excel exports (#1806)
This commit is contained in:
committed by
GitHub
parent
8857c971d6
commit
f9861cf772
+45
-21
@@ -5,11 +5,7 @@ import {
|
||||
useResponseFilter,
|
||||
} from "@/app/(app)/environments/[environmentId]/components/ResponseFilterContext";
|
||||
import { fetchFile } from "@/app/lib/fetchFile";
|
||||
import {
|
||||
generateQuestionAndFilterOptions,
|
||||
generateQuestionsAndAttributes,
|
||||
getTodayDate,
|
||||
} from "@/app/lib/surveys/surveys";
|
||||
import { generateQuestionAndFilterOptions, getTodayDate } from "@/app/lib/surveys/surveys";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { differenceInDays, format, subDays } from "date-fns";
|
||||
import { ChevronDown, ChevronUp, DownloadIcon } from "lucide-react";
|
||||
@@ -91,7 +87,7 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
|
||||
|
||||
const datePickerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const getMatchQandA = (responses: any, survey: any) => {
|
||||
const getMatchQandA = (responses: TResponse[], survey: TSurvey) => {
|
||||
if (survey && responses) {
|
||||
// Create a mapping of question IDs to their headlines
|
||||
const questionIdToHeadline = {};
|
||||
@@ -145,20 +141,58 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
|
||||
return "my_survey_responses";
|
||||
}, [survey]);
|
||||
|
||||
function extracMetadataKeys(obj, parentKey = "") {
|
||||
let keys: string[] = [];
|
||||
|
||||
for (let key in obj) {
|
||||
if (typeof obj[key] === "object" && obj[key] !== null) {
|
||||
keys = keys.concat(extracMetadataKeys(obj[key], parentKey + key + " - "));
|
||||
} else {
|
||||
keys.push(parentKey + key);
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
const downloadResponses = useCallback(
|
||||
async (filter: FilterDownload, filetype: "csv" | "xlsx") => {
|
||||
const downloadResponse = filter === FilterDownload.ALL ? totalResponses : responses;
|
||||
const { attributeMap, questionNames } = generateQuestionsAndAttributes(survey, downloadResponse);
|
||||
const questionNames = survey.questions?.map((question) => question.headline);
|
||||
const hiddenFieldIds = survey.hiddenFields.fieldIds;
|
||||
const hiddenFieldResponse = {};
|
||||
let metaDataFields = extracMetadataKeys(downloadResponse[0].meta);
|
||||
const userAttributes = ["Init Attribute 1", "Init Attribute 2"];
|
||||
const matchQandA = getMatchQandA(downloadResponse, survey);
|
||||
const jsonData = matchQandA.map((response) => {
|
||||
const fileResponse = {
|
||||
const basicInfo = {
|
||||
"Response ID": response.id,
|
||||
Timestamp: response.createdAt,
|
||||
Finished: response.finished,
|
||||
"Survey ID": response.surveyId,
|
||||
"Formbricks User ID": response.person?.id ?? "",
|
||||
};
|
||||
const metaDataKeys = extracMetadataKeys(response.meta);
|
||||
let metaData = {};
|
||||
metaDataKeys.forEach((key) => {
|
||||
if (!metaDataFields.includes(key)) metaDataFields.push(key);
|
||||
if (response.meta) {
|
||||
if (key.includes("-")) {
|
||||
const nestedKeyArray = key.split("-");
|
||||
metaData[key] = response.meta[nestedKeyArray[0].trim()][nestedKeyArray[1].trim()] ?? "";
|
||||
} else {
|
||||
metaData[key] = response.meta[key] ?? "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const personAttributes = response.personAttributes;
|
||||
if (hiddenFieldIds && hiddenFieldIds.length > 0) {
|
||||
hiddenFieldIds.forEach((hiddenFieldId) => {
|
||||
hiddenFieldResponse[hiddenFieldId] = response.data[hiddenFieldId] ?? "";
|
||||
});
|
||||
}
|
||||
const fileResponse = { ...basicInfo, ...metaData, ...personAttributes, ...hiddenFieldResponse };
|
||||
// Map each question name to its corresponding answer
|
||||
questionNames.forEach((questionName: string) => {
|
||||
const matchingQuestion = response.responses.find((question) => question.question === questionName);
|
||||
@@ -177,18 +211,6 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
|
||||
return fileResponse;
|
||||
});
|
||||
|
||||
// Add attribute columns to the file
|
||||
Object.keys(attributeMap).forEach((attributeName) => {
|
||||
const attributeValues = attributeMap[attributeName];
|
||||
Object.keys(attributeValues).forEach((personId) => {
|
||||
const value = attributeValues[personId];
|
||||
const matchingResponse = jsonData.find((response) => response["Formbricks User ID"] === personId);
|
||||
if (matchingResponse) {
|
||||
matchingResponse[attributeName] = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Fields which will be used as column headers in the file
|
||||
const fields = [
|
||||
"Response ID",
|
||||
@@ -196,8 +218,10 @@ const CustomFilter = ({ environmentTags, responses, survey, totalResponses }: Cu
|
||||
"Finished",
|
||||
"Survey ID",
|
||||
"Formbricks User ID",
|
||||
...Object.keys(attributeMap),
|
||||
...metaDataFields,
|
||||
...questionNames,
|
||||
...(hiddenFieldIds ?? []),
|
||||
...(survey.type === "web" ? userAttributes : []),
|
||||
];
|
||||
|
||||
let response;
|
||||
|
||||
@@ -14,35 +14,6 @@ import { TSurveyQuestionType } from "@formbricks/types/surveys";
|
||||
import { TSurvey } from "@formbricks/types/surveys";
|
||||
import { TTag } from "@formbricks/types/tags";
|
||||
|
||||
export const generateQuestionsAndAttributes = (survey: TSurvey, responses: TResponse[]) => {
|
||||
let questionNames: string[] = [];
|
||||
|
||||
if (survey?.questions) {
|
||||
questionNames = survey.questions.map((question) => question.headline);
|
||||
}
|
||||
|
||||
const attributeMap: Record<string, Record<string, string | number>> = {};
|
||||
|
||||
if (responses) {
|
||||
responses.forEach((response) => {
|
||||
const { person } = response;
|
||||
if (person !== null) {
|
||||
const { id, attributes } = person;
|
||||
Object.keys(attributes).forEach((attributeName) => {
|
||||
if (!attributeMap.hasOwnProperty(attributeName)) {
|
||||
attributeMap[attributeName] = {};
|
||||
}
|
||||
attributeMap[attributeName][id] = attributes[attributeName];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
questionNames,
|
||||
attributeMap,
|
||||
};
|
||||
};
|
||||
|
||||
const conditionOptions = {
|
||||
openText: ["is"],
|
||||
multipleChoiceSingle: ["Includes either"],
|
||||
|
||||
Reference in New Issue
Block a user