mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-06 22:02:04 -05:00
feat: adds metadata columns in response table (#6368)
This commit is contained in:
+12
@@ -240,6 +240,12 @@ describe("ResponseDataView", () => {
|
||||
language: "en",
|
||||
person: null,
|
||||
contactAttributes: null,
|
||||
meta: {
|
||||
url: "http://localhost",
|
||||
userAgent: {
|
||||
browser: "test-agent",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
responseData: {
|
||||
@@ -255,6 +261,12 @@ describe("ResponseDataView", () => {
|
||||
language: "de",
|
||||
person: null,
|
||||
contactAttributes: null,
|
||||
meta: {
|
||||
url: "http://localhost",
|
||||
userAgent: {
|
||||
browser: "test-agent-2",
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
+1
@@ -101,6 +101,7 @@ export const mapResponsesToTableData = (
|
||||
language: response.language,
|
||||
person: response.contact,
|
||||
contactAttributes: response.contactAttributes,
|
||||
meta: response.meta,
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
+6
@@ -102,6 +102,12 @@ vi.mock("lucide-react", () => ({
|
||||
EyeOffIcon: () => <span>EyeOff</span>,
|
||||
MailIcon: () => <span>Mail</span>,
|
||||
TagIcon: () => <span>Tag</span>,
|
||||
MousePointerClickIcon: () => <span>MousePointerClick</span>,
|
||||
AirplayIcon: () => <span>Airplay</span>,
|
||||
ArrowUpFromDotIcon: () => <span>ArrowUpFromDot</span>,
|
||||
FlagIcon: () => <span>Flag</span>,
|
||||
GlobeIcon: () => <span>Globe</span>,
|
||||
SmartphoneIcon: () => <span>Smartphone</span>,
|
||||
}));
|
||||
|
||||
// Mock new dependencies
|
||||
|
||||
+38
-37
@@ -19,43 +19,14 @@ import { CircleHelpIcon, EyeOffIcon, MailIcon, TagIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { TResponseTableData } from "@formbricks/types/responses";
|
||||
import { TSurvey, TSurveyQuestion } from "@formbricks/types/surveys/types";
|
||||
|
||||
const getAddressFieldLabel = (field: string, t: TFnType) => {
|
||||
switch (field) {
|
||||
case "addressLine1":
|
||||
return t("environments.surveys.responses.address_line_1");
|
||||
case "addressLine2":
|
||||
return t("environments.surveys.responses.address_line_2");
|
||||
case "city":
|
||||
return t("environments.surveys.responses.city");
|
||||
case "state":
|
||||
return t("environments.surveys.responses.state_region");
|
||||
case "zip":
|
||||
return t("environments.surveys.responses.zip_post_code");
|
||||
case "country":
|
||||
return t("environments.surveys.responses.country");
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const getContactInfoFieldLabel = (field: string, t: TFnType) => {
|
||||
switch (field) {
|
||||
case "firstName":
|
||||
return t("environments.surveys.responses.first_name");
|
||||
case "lastName":
|
||||
return t("environments.surveys.responses.last_name");
|
||||
case "email":
|
||||
return t("environments.surveys.responses.email");
|
||||
case "phone":
|
||||
return t("environments.surveys.responses.phone");
|
||||
case "company":
|
||||
return t("environments.surveys.responses.company");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
import {
|
||||
COLUMNS_ICON_MAP,
|
||||
METADATA_FIELDS,
|
||||
getAddressFieldLabel,
|
||||
getContactInfoFieldLabel,
|
||||
getMetadataFieldLabel,
|
||||
getMetadataValue,
|
||||
} from "../lib/utils";
|
||||
|
||||
const getQuestionColumnsData = (
|
||||
question: TSurveyQuestion,
|
||||
@@ -256,6 +227,33 @@ const getQuestionColumnsData = (
|
||||
}
|
||||
};
|
||||
|
||||
const getMetadataColumnsData = (t: TFnType): ColumnDef<TResponseTableData>[] => {
|
||||
const metadataColumns: ColumnDef<TResponseTableData>[] = [];
|
||||
|
||||
METADATA_FIELDS.forEach((label) => {
|
||||
const IconComponent = COLUMNS_ICON_MAP[label];
|
||||
|
||||
metadataColumns.push({
|
||||
accessorKey: label,
|
||||
header: () => (
|
||||
<div className="flex items-center space-x-2 overflow-hidden">
|
||||
<span className="h-4 w-4">{IconComponent && <IconComponent className="h-4 w-4" />}</span>
|
||||
<span className="truncate">{getMetadataFieldLabel(label, t)}</span>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
const value = getMetadataValue(row.original.meta, label);
|
||||
if (value) {
|
||||
return <div className="truncate text-slate-900">{value}</div>;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return metadataColumns;
|
||||
};
|
||||
|
||||
export const generateResponseTableColumns = (
|
||||
survey: TSurvey,
|
||||
isExpanded: boolean,
|
||||
@@ -389,6 +387,8 @@ export const generateResponseTableColumns = (
|
||||
})
|
||||
: [];
|
||||
|
||||
const metadataColumns = getMetadataColumnsData(t);
|
||||
|
||||
const verifiedEmailColumn: ColumnDef<TResponseTableData> = {
|
||||
accessorKey: "verifiedEmail",
|
||||
header: () => (
|
||||
@@ -410,6 +410,7 @@ export const generateResponseTableColumns = (
|
||||
...questionColumns,
|
||||
...variableColumns,
|
||||
...hiddenFieldColumns,
|
||||
...metadataColumns,
|
||||
tagsColumn,
|
||||
notesColumn,
|
||||
];
|
||||
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import {
|
||||
AirplayIcon,
|
||||
ArrowUpFromDotIcon,
|
||||
FlagIcon,
|
||||
GlobeIcon,
|
||||
MousePointerClickIcon,
|
||||
SmartphoneIcon,
|
||||
} from "lucide-react";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import {
|
||||
COLUMNS_ICON_MAP,
|
||||
getAddressFieldLabel,
|
||||
getContactInfoFieldLabel,
|
||||
getMetadataFieldLabel,
|
||||
getMetadataValue,
|
||||
} from "./utils";
|
||||
|
||||
describe("utils", () => {
|
||||
const mockT = vi.fn((key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
"environments.surveys.responses.address_line_1": "Address Line 1",
|
||||
"environments.surveys.responses.address_line_2": "Address Line 2",
|
||||
"environments.surveys.responses.city": "City",
|
||||
"environments.surveys.responses.state_region": "State/Region",
|
||||
"environments.surveys.responses.zip_post_code": "ZIP/Post Code",
|
||||
"environments.surveys.responses.country": "Country",
|
||||
"environments.surveys.responses.first_name": "First Name",
|
||||
"environments.surveys.responses.last_name": "Last Name",
|
||||
"environments.surveys.responses.email": "Email",
|
||||
"environments.surveys.responses.phone": "Phone",
|
||||
"environments.surveys.responses.company": "Company",
|
||||
"common.action": "Action",
|
||||
"environments.surveys.responses.os": "OS",
|
||||
"environments.surveys.responses.device": "Device",
|
||||
"environments.surveys.responses.browser": "Browser",
|
||||
"common.url": "URL",
|
||||
"environments.surveys.responses.source": "Source",
|
||||
};
|
||||
return translations[key] || key;
|
||||
});
|
||||
|
||||
describe("getAddressFieldLabel", () => {
|
||||
test("returns correct label for addressLine1", () => {
|
||||
const result = getAddressFieldLabel("addressLine1", mockT);
|
||||
expect(result).toBe("Address Line 1");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.address_line_1");
|
||||
});
|
||||
|
||||
test("returns correct label for addressLine2", () => {
|
||||
const result = getAddressFieldLabel("addressLine2", mockT);
|
||||
expect(result).toBe("Address Line 2");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.address_line_2");
|
||||
});
|
||||
|
||||
test("returns correct label for city", () => {
|
||||
const result = getAddressFieldLabel("city", mockT);
|
||||
expect(result).toBe("City");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.city");
|
||||
});
|
||||
|
||||
test("returns correct label for state", () => {
|
||||
const result = getAddressFieldLabel("state", mockT);
|
||||
expect(result).toBe("State/Region");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.state_region");
|
||||
});
|
||||
|
||||
test("returns correct label for zip", () => {
|
||||
const result = getAddressFieldLabel("zip", mockT);
|
||||
expect(result).toBe("ZIP/Post Code");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.zip_post_code");
|
||||
});
|
||||
|
||||
test("returns correct label for country", () => {
|
||||
const result = getAddressFieldLabel("country", mockT);
|
||||
expect(result).toBe("Country");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.country");
|
||||
});
|
||||
|
||||
test("returns undefined for unknown field", () => {
|
||||
const result = getAddressFieldLabel("unknown", mockT);
|
||||
expect(result).toBeUndefined();
|
||||
expect(mockT).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getContactInfoFieldLabel", () => {
|
||||
test("returns correct label for firstName", () => {
|
||||
const result = getContactInfoFieldLabel("firstName", mockT);
|
||||
expect(result).toBe("First Name");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.first_name");
|
||||
});
|
||||
|
||||
test("returns correct label for lastName", () => {
|
||||
const result = getContactInfoFieldLabel("lastName", mockT);
|
||||
expect(result).toBe("Last Name");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.last_name");
|
||||
});
|
||||
|
||||
test("returns correct label for email", () => {
|
||||
const result = getContactInfoFieldLabel("email", mockT);
|
||||
expect(result).toBe("Email");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.email");
|
||||
});
|
||||
|
||||
test("returns correct label for phone", () => {
|
||||
const result = getContactInfoFieldLabel("phone", mockT);
|
||||
expect(result).toBe("Phone");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.phone");
|
||||
});
|
||||
|
||||
test("returns correct label for company", () => {
|
||||
const result = getContactInfoFieldLabel("company", mockT);
|
||||
expect(result).toBe("Company");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.company");
|
||||
});
|
||||
|
||||
test("returns undefined for unknown field", () => {
|
||||
const result = getContactInfoFieldLabel("unknown", mockT);
|
||||
expect(result).toBeUndefined();
|
||||
expect(mockT).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMetadataFieldLabel", () => {
|
||||
test("returns correct label for action", () => {
|
||||
const result = getMetadataFieldLabel("action", mockT);
|
||||
expect(result).toBe("Action");
|
||||
expect(mockT).toHaveBeenCalledWith("common.action");
|
||||
});
|
||||
|
||||
test("returns correct label for country", () => {
|
||||
const result = getMetadataFieldLabel("country", mockT);
|
||||
expect(result).toBe("Country");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.country");
|
||||
});
|
||||
|
||||
test("returns correct label for os", () => {
|
||||
const result = getMetadataFieldLabel("os", mockT);
|
||||
expect(result).toBe("OS");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.os");
|
||||
});
|
||||
|
||||
test("returns correct label for device", () => {
|
||||
const result = getMetadataFieldLabel("device", mockT);
|
||||
expect(result).toBe("Device");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.device");
|
||||
});
|
||||
|
||||
test("returns correct label for browser", () => {
|
||||
const result = getMetadataFieldLabel("browser", mockT);
|
||||
expect(result).toBe("Browser");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.browser");
|
||||
});
|
||||
|
||||
test("returns correct label for url", () => {
|
||||
const result = getMetadataFieldLabel("url", mockT);
|
||||
expect(result).toBe("URL");
|
||||
expect(mockT).toHaveBeenCalledWith("common.url");
|
||||
});
|
||||
|
||||
test("returns correct label for source", () => {
|
||||
const result = getMetadataFieldLabel("source", mockT);
|
||||
expect(result).toBe("Source");
|
||||
expect(mockT).toHaveBeenCalledWith("environments.surveys.responses.source");
|
||||
});
|
||||
|
||||
test("returns capitalized label for unknown field", () => {
|
||||
const result = getMetadataFieldLabel("customField", mockT);
|
||||
expect(result).toBe("Customfield");
|
||||
expect(mockT).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("returns capitalized label for field with underscores", () => {
|
||||
const result = getMetadataFieldLabel("custom_field", mockT);
|
||||
expect(result).toBe("Custom_field");
|
||||
expect(mockT).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("COLUMNS_ICON_MAP", () => {
|
||||
test("contains correct icon mappings", () => {
|
||||
expect(COLUMNS_ICON_MAP.action).toBe(MousePointerClickIcon);
|
||||
expect(COLUMNS_ICON_MAP.country).toBe(FlagIcon);
|
||||
expect(COLUMNS_ICON_MAP.browser).toBe(GlobeIcon);
|
||||
expect(COLUMNS_ICON_MAP.os).toBe(AirplayIcon);
|
||||
expect(COLUMNS_ICON_MAP.device).toBe(SmartphoneIcon);
|
||||
expect(COLUMNS_ICON_MAP.source).toBe(ArrowUpFromDotIcon);
|
||||
expect(COLUMNS_ICON_MAP.url).toBe(GlobeIcon);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMetadataValue", () => {
|
||||
test("returns correct value for action", () => {
|
||||
const result = getMetadataValue({ action: "action_column" }, "action");
|
||||
expect(result).toBe("action_column");
|
||||
});
|
||||
|
||||
test("returns correct value for userAgent", () => {
|
||||
const result = getMetadataValue({ userAgent: { browser: "browser_column" } }, "browser");
|
||||
expect(result).toBe("browser_column");
|
||||
});
|
||||
});
|
||||
});
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { TFnType } from "@tolgee/react";
|
||||
import { capitalize } from "lodash";
|
||||
import {
|
||||
AirplayIcon,
|
||||
ArrowUpFromDotIcon,
|
||||
FlagIcon,
|
||||
GlobeIcon,
|
||||
MousePointerClickIcon,
|
||||
SmartphoneIcon,
|
||||
} from "lucide-react";
|
||||
import { TResponseMeta } from "@formbricks/types/responses";
|
||||
|
||||
export const getAddressFieldLabel = (field: string, t: TFnType) => {
|
||||
switch (field) {
|
||||
case "addressLine1":
|
||||
return t("environments.surveys.responses.address_line_1");
|
||||
case "addressLine2":
|
||||
return t("environments.surveys.responses.address_line_2");
|
||||
case "city":
|
||||
return t("environments.surveys.responses.city");
|
||||
case "state":
|
||||
return t("environments.surveys.responses.state_region");
|
||||
case "zip":
|
||||
return t("environments.surveys.responses.zip_post_code");
|
||||
case "country":
|
||||
return t("environments.surveys.responses.country");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const getContactInfoFieldLabel = (field: string, t: TFnType) => {
|
||||
switch (field) {
|
||||
case "firstName":
|
||||
return t("environments.surveys.responses.first_name");
|
||||
case "lastName":
|
||||
return t("environments.surveys.responses.last_name");
|
||||
case "email":
|
||||
return t("environments.surveys.responses.email");
|
||||
case "phone":
|
||||
return t("environments.surveys.responses.phone");
|
||||
case "company":
|
||||
return t("environments.surveys.responses.company");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const getMetadataFieldLabel = (label: string, t: TFnType) => {
|
||||
switch (label) {
|
||||
case "action":
|
||||
return t("common.action");
|
||||
case "country":
|
||||
return t("environments.surveys.responses.country");
|
||||
case "os":
|
||||
return t("environments.surveys.responses.os");
|
||||
case "device":
|
||||
return t("environments.surveys.responses.device");
|
||||
case "browser":
|
||||
return t("environments.surveys.responses.browser");
|
||||
case "url":
|
||||
return t("common.url");
|
||||
case "source":
|
||||
return t("environments.surveys.responses.source");
|
||||
default:
|
||||
return capitalize(label);
|
||||
}
|
||||
};
|
||||
|
||||
export const COLUMNS_ICON_MAP = {
|
||||
action: MousePointerClickIcon,
|
||||
country: FlagIcon,
|
||||
browser: GlobeIcon,
|
||||
os: AirplayIcon,
|
||||
device: SmartphoneIcon,
|
||||
source: ArrowUpFromDotIcon,
|
||||
url: GlobeIcon,
|
||||
};
|
||||
|
||||
const userAgentFields = ["browser", "os", "device"];
|
||||
export const METADATA_FIELDS = ["action", "country", ...userAgentFields, "source", "url"];
|
||||
|
||||
export const getMetadataValue = (meta: TResponseMeta, label: string) => {
|
||||
if (userAgentFields.includes(label)) {
|
||||
return meta.userAgent?.[label];
|
||||
}
|
||||
return meta[label];
|
||||
};
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "Firma",
|
||||
"completed": "Erledigt ✅",
|
||||
"country": "Land",
|
||||
"delete_response_confirmation": "Dies wird die Umfrageantwort einschließlich aller Antworten, Notizen, Tags, angehängter Dokumente und Antwortmetadaten löschen.",
|
||||
"delete_response_confirmation": "Dies wird die Umfrageantwort einschließlich aller Antworten, Tags, angehängter Dokumente und Antwort-Metadaten löschen.",
|
||||
"device": "Gerät",
|
||||
"device_info": "Geräteinfo",
|
||||
"email": "E-Mail",
|
||||
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "Company",
|
||||
"completed": "Completed ✅",
|
||||
"country": "Country",
|
||||
"delete_response_confirmation": "This will delete the survey response, including all answers, notes, tags, attached documents, and response metadata.",
|
||||
"delete_response_confirmation": "This will delete the survey response, including all answers, tags, attached documents, and response metadata.",
|
||||
"device": "Device",
|
||||
"device_info": "Device info",
|
||||
"email": "Email",
|
||||
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "Société",
|
||||
"completed": "Terminé ✅",
|
||||
"country": "Pays",
|
||||
"delete_response_confirmation": "\"Cela supprimera la réponse au sondage, y compris toutes les réponses, notes, étiquettes, documents joints et métadonnées de réponse.\"",
|
||||
"delete_response_confirmation": "Cela supprimera la réponse au sondage, y compris toutes les réponses, les étiquettes, les documents joints et les métadonnées de réponse.",
|
||||
"device": "Dispositif",
|
||||
"device_info": "Informations sur l'appareil",
|
||||
"email": "Email",
|
||||
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "empresa",
|
||||
"completed": "Concluído ✅",
|
||||
"country": "País",
|
||||
"delete_response_confirmation": "Isso excluirá a resposta da pesquisa, incluindo todas as respostas, notas, etiquetas, documentos anexados e metadados da resposta.",
|
||||
"delete_response_confirmation": "Isso irá excluir a resposta da pesquisa, incluindo todas as respostas, etiquetas, documentos anexados e metadados da resposta.",
|
||||
"device": "dispositivo",
|
||||
"device_info": "Informações do dispositivo",
|
||||
"email": "Email",
|
||||
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "Empresa",
|
||||
"completed": "Concluído ✅",
|
||||
"country": "País",
|
||||
"delete_response_confirmation": "Isto irá eliminar a resposta ao questionário, incluindo todas as respostas, notas, etiquetas, documentos anexados e metadados da resposta.",
|
||||
"delete_response_confirmation": "Isto irá apagar a resposta do inquérito, incluindo todas as respostas, etiquetas, documentos anexos e metadados da resposta.",
|
||||
"device": "Dispositivo",
|
||||
"device_info": "Informações do dispositivo",
|
||||
"email": "Email",
|
||||
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "Companie",
|
||||
"completed": "Finalizat ✅",
|
||||
"country": "Țară",
|
||||
"delete_response_confirmation": "Aceasta va șterge răspunsul la sondaj, inclusiv toate răspunsurile, notele, etichetele, documentele atașate și metadatele răspunsului.",
|
||||
"delete_response_confirmation": "Aceasta va șterge răspunsul la sondaj, inclusiv toate răspunsurile, etichetele, documentele atașate și metadatele răspunsului.",
|
||||
"device": "Dispozitiv",
|
||||
"device_info": "Informații despre dispozitiv",
|
||||
"email": "Email",
|
||||
|
||||
@@ -1646,7 +1646,7 @@
|
||||
"company": "公司",
|
||||
"completed": "已完成 ✅",
|
||||
"country": "國家/地區",
|
||||
"delete_response_confirmation": "這將刪除調查回覆,包括所有答案、註解、標籤、附加文件和回覆元數據。",
|
||||
"delete_response_confirmation": "這將刪除調查響應,包括所有回答、標籤、附件文件以及響應元數據。",
|
||||
"device": "裝置",
|
||||
"device_info": "裝置資訊",
|
||||
"email": "電子郵件",
|
||||
|
||||
+52
-13
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { COLUMNS_ICON_MAP } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/lib/utils";
|
||||
import { getLocalizedValue } from "@/lib/i18n/utils";
|
||||
import { getQuestionIconMap } from "@/modules/survey/lib/questions";
|
||||
import { Switch } from "@/modules/ui/components/switch";
|
||||
@@ -9,6 +10,7 @@ import { Column } from "@tanstack/react-table";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import { capitalize } from "lodash";
|
||||
import { GripVertical } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { TSurvey } from "@formbricks/types/surveys/types";
|
||||
|
||||
interface DataTableSettingsModalItemProps<T> {
|
||||
@@ -16,6 +18,18 @@ interface DataTableSettingsModalItemProps<T> {
|
||||
survey?: TSurvey;
|
||||
}
|
||||
|
||||
const getColumnIcon = (columnId: string) => {
|
||||
const Icon = COLUMNS_ICON_MAP[columnId];
|
||||
if (Icon) {
|
||||
return (
|
||||
<span className="h-4 w-4" aria-hidden="true">
|
||||
<Icon className="h-4 w-4" aria-hidden="true" focusable="false" />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const DataTableSettingsModalItem = <T,>({ column, survey }: DataTableSettingsModalItemProps<T>) => {
|
||||
const { t } = useTranslate();
|
||||
const QUESTIONS_ICON_MAP = getQuestionIconMap(t);
|
||||
@@ -57,6 +71,20 @@ export const DataTableSettingsModalItem = <T,>({ column, survey }: DataTableSett
|
||||
return t("environments.contacts.first_name");
|
||||
case "lastName":
|
||||
return t("environments.contacts.last_name");
|
||||
case "action":
|
||||
return t("common.action");
|
||||
case "country":
|
||||
return t("environments.surveys.responses.country");
|
||||
case "os":
|
||||
return t("environments.surveys.responses.os");
|
||||
case "device":
|
||||
return t("environments.surveys.responses.device");
|
||||
case "browser":
|
||||
return t("environments.surveys.responses.browser");
|
||||
case "url":
|
||||
return t("common.url");
|
||||
case "source":
|
||||
return t("environments.surveys.responses.source");
|
||||
|
||||
default:
|
||||
return capitalize(column.id);
|
||||
@@ -71,26 +99,37 @@ export const DataTableSettingsModalItem = <T,>({ column, survey }: DataTableSett
|
||||
zIndex: isDragging ? 10 : 1,
|
||||
};
|
||||
|
||||
const iconMemo = useMemo(() => {
|
||||
return getColumnIcon(column.id);
|
||||
}, [column.id]);
|
||||
|
||||
return (
|
||||
<div ref={setNodeRef} style={style} id={column.id}>
|
||||
<div {...listeners} {...attributes}>
|
||||
<div
|
||||
key={column.id}
|
||||
className="flex w-full items-center justify-between rounded-md p-1.5 hover:cursor-move hover:bg-slate-100">
|
||||
<div className="flex items-center space-x-2">
|
||||
<button onClick={(e) => e.preventDefault()}>
|
||||
className="flex w-full items-center justify-between gap-4 rounded-md p-1.5 hover:cursor-move hover:bg-slate-100">
|
||||
<div className="flex items-center space-x-2 overflow-hidden">
|
||||
<button type="button" aria-label="Reorder column" onClick={(e) => e.preventDefault()}>
|
||||
<GripVertical className="h-4 w-4" />
|
||||
</button>
|
||||
{question ? (
|
||||
<div className="flex items-center space-x-2">
|
||||
<span className="h-4 w-4">{QUESTIONS_ICON_MAP[question.type]}</span>
|
||||
<span className="max-w-xs truncate">{getLocalizedValue(question.headline, "default")}</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="max-w-xs truncate">
|
||||
{isOptionIdColumn ? getOptionIdColumnLabel() : getLabelFromColumnId()}
|
||||
</span>
|
||||
)}
|
||||
<div className="flex items-center space-x-2 overflow-hidden">
|
||||
{question ? (
|
||||
<>
|
||||
<span className="h-4 w-4" aria-hidden="true">
|
||||
{QUESTIONS_ICON_MAP[question.type]}
|
||||
</span>
|
||||
<span className="truncate">{getLocalizedValue(question.headline, "default")}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{iconMemo}
|
||||
<span className="truncate">
|
||||
{isOptionIdColumn ? getOptionIdColumnLabel() : getLabelFromColumnId()}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
id={column.id}
|
||||
|
||||
@@ -376,6 +376,7 @@ export const ZResponseTableData = z.object({
|
||||
variables: z.record(z.union([z.string(), z.number()])),
|
||||
person: ZResponseContact.nullable(),
|
||||
contactAttributes: ZResponseContactAttributes,
|
||||
meta: ZResponseMeta,
|
||||
});
|
||||
|
||||
export type TResponseTableData = z.infer<typeof ZResponseTableData>;
|
||||
|
||||
Reference in New Issue
Block a user