mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-10 18:58:44 -06:00
fix: code simplification and fixes the key validation in bulk contacts upload api
This commit is contained in:
@@ -604,6 +604,7 @@ checksums:
|
||||
environments/contacts/create_key: 0d385c354af8963acbe35cd646710f86
|
||||
environments/contacts/create_new_attribute: c17d407dacd0b90f360f9f5e899d662f
|
||||
environments/contacts/create_new_attribute_description: cc19d76bb6940537bbe3461191f25d26
|
||||
environments/contacts/custom_attributes: fffc7722742d1291b102dc737cf2fc9e
|
||||
environments/contacts/data_type: 1ea127ba2c18d0d91fb0361cc6747e2b
|
||||
environments/contacts/data_type_cannot_be_changed: 22603f6193fdac3784eeef8315df70de
|
||||
environments/contacts/data_type_description: 800bf4935df15e6cb14269e1b60c506e
|
||||
@@ -619,6 +620,7 @@ checksums:
|
||||
environments/contacts/edit_attributes_success: 39f93b1a6f1605bc5951f4da5847bb22
|
||||
environments/contacts/generate_personal_link: 9ac0865f6876d40fe858f94eae781eb8
|
||||
environments/contacts/generate_personal_link_description: b9dbaf9e2d8362505b7e3cfa40f415a6
|
||||
environments/contacts/invalid_csv_column_names: dcb8534e7d4c00b9ea7bdaf389f72328
|
||||
environments/contacts/invalid_date_format: 5bad9730ac5a5bacd0792098f712b1c4
|
||||
environments/contacts/invalid_number_format: bd0422507385f671c3046730a6febc64
|
||||
environments/contacts/no_published_link_surveys_available: 9c1abc5b21aba827443cdf87dd6c8bfe
|
||||
@@ -634,13 +636,16 @@ checksums:
|
||||
environments/contacts/search_contact: 020205a93846ab3e12c203ac4fa97c12
|
||||
environments/contacts/select_a_survey: 1f49086dfb874307aae1136e88c3d514
|
||||
environments/contacts/select_attribute: d93fb60eb4fbb42bf13a22f6216fbd79
|
||||
environments/contacts/select_attribute_key: 673a6683fab41b387d921841cded7e38
|
||||
environments/contacts/system_attributes: eadb6a8888c7b32c0e68881f945ae9b6
|
||||
environments/contacts/unlock_contacts_description: c5572047f02b4c39e5109f9de715499d
|
||||
environments/contacts/unlock_contacts_title: a8b3d7db03eb404d9267fd5cdd6d5ddb
|
||||
environments/contacts/upload_contacts_modal_attribute_header: 263246ad2a76f8e2f80f0ed175d7629a
|
||||
environments/contacts/upload_contacts_modal_attributes_description: e2cedbd4a043423002cbb2048e2145ac
|
||||
environments/contacts/upload_contacts_modal_attributes_new: 9829382598c681de74130440a37b560f
|
||||
environments/contacts/upload_contacts_modal_attributes_search_or_add: 1874839e465650d353282b43b00247a9
|
||||
environments/contacts/upload_contacts_modal_attributes_should_be_mapped_to: 693dfe5836e90b1c4c7c65b015418174
|
||||
environments/contacts/upload_contacts_modal_attributes_title: 86d0ae6fea0fbb119722ed3841f8385a
|
||||
environments/contacts/upload_contacts_modal_csv_column_header: f181add48fb8325efaa40579fe8c343e
|
||||
environments/contacts/upload_contacts_modal_description: 41566d40d25cc882aa9f82d87b4e2f03
|
||||
environments/contacts/upload_contacts_modal_download_example_csv: 7a186fc4941b264452ee6c9e785769de
|
||||
environments/contacts/upload_contacts_modal_duplicates_description: 112ce4641088520469a83a0bd740b073
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Systemattribute",
|
||||
"unlock_contacts_description": "Verwalte Kontakte und sende gezielte Umfragen",
|
||||
"unlock_contacts_title": "Kontakte mit einem höheren Plan freischalten",
|
||||
"upload_contacts_modal_attribute_header": "Formbricks-Attribut",
|
||||
"upload_contacts_modal_attributes_description": "Ordne die Spalten in deiner CSV den Attributen in Formbricks zu.",
|
||||
"upload_contacts_modal_attributes_new": "Neues Attribut",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Attribut suchen oder hinzufügen",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "sollte zugeordnet werden zu",
|
||||
"upload_contacts_modal_attributes_title": "Attribute",
|
||||
"upload_contacts_modal_csv_column_header": "CSV-Spalte",
|
||||
"upload_contacts_modal_description": "Lade eine CSV hoch, um Kontakte mit Attributen schnell zu importieren",
|
||||
"upload_contacts_modal_download_example_csv": "Beispiel-CSV herunterladen",
|
||||
"upload_contacts_modal_duplicates_description": "Wie sollen wir vorgehen, wenn ein Kontakt bereits existiert?",
|
||||
|
||||
@@ -680,7 +680,6 @@
|
||||
"upload_contacts_modal_attributes_description": "Map the columns in your CSV to the attributes in Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "New attribute",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Search or add attribute",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "should be mapped to",
|
||||
"upload_contacts_modal_attributes_title": "Attributes",
|
||||
"upload_contacts_modal_csv_column_header": "CSV Column",
|
||||
"upload_contacts_modal_description": "Upload a CSV to quickly import contacts with attributes",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Atributos del sistema",
|
||||
"unlock_contacts_description": "Gestiona contactos y envía encuestas dirigidas",
|
||||
"unlock_contacts_title": "Desbloquea contactos con un plan superior",
|
||||
"upload_contacts_modal_attribute_header": "Atributo de Formbricks",
|
||||
"upload_contacts_modal_attributes_description": "Asigna las columnas de tu CSV a los atributos en Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Nuevo atributo",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Buscar o añadir atributo",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "debe asignarse a",
|
||||
"upload_contacts_modal_attributes_title": "Atributos",
|
||||
"upload_contacts_modal_csv_column_header": "Columna CSV",
|
||||
"upload_contacts_modal_description": "Sube un CSV para importar rápidamente contactos con atributos",
|
||||
"upload_contacts_modal_download_example_csv": "Descargar CSV de ejemplo",
|
||||
"upload_contacts_modal_duplicates_description": "¿Cómo deberíamos manejar si un contacto ya existe en tus contactos?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Attributs système",
|
||||
"unlock_contacts_description": "Gérer les contacts et envoyer des enquêtes ciblées",
|
||||
"unlock_contacts_title": "Débloquez des contacts avec un plan supérieur.",
|
||||
"upload_contacts_modal_attribute_header": "Attribut Formbricks",
|
||||
"upload_contacts_modal_attributes_description": "Mappez les colonnes de votre CSV aux attributs dans Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Nouvel attribut",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Rechercher ou ajouter un attribut",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "devrait être mappé à",
|
||||
"upload_contacts_modal_attributes_title": "Attributs",
|
||||
"upload_contacts_modal_csv_column_header": "Colonne CSV",
|
||||
"upload_contacts_modal_description": "Téléchargez un fichier CSV pour importer rapidement des contacts avec des attributs.",
|
||||
"upload_contacts_modal_download_example_csv": "Télécharger un exemple de CSV",
|
||||
"upload_contacts_modal_duplicates_description": "Que faire si un contact existe déjà ?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "システム属性",
|
||||
"unlock_contacts_description": "連絡先を管理し、特定のフォームを送信します",
|
||||
"unlock_contacts_title": "上位プランで連絡先をアンロック",
|
||||
"upload_contacts_modal_attribute_header": "Formbricks属性",
|
||||
"upload_contacts_modal_attributes_description": "CSVの列をFormbricksの属性にマッピングします。",
|
||||
"upload_contacts_modal_attributes_new": "新しい属性",
|
||||
"upload_contacts_modal_attributes_search_or_add": "属性を検索または追加",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "は以下にマッピングする必要があります",
|
||||
"upload_contacts_modal_attributes_title": "属性",
|
||||
"upload_contacts_modal_csv_column_header": "CSV列",
|
||||
"upload_contacts_modal_description": "CSVをアップロードして、属性を持つ連絡先をすばやくインポート",
|
||||
"upload_contacts_modal_download_example_csv": "CSVの例をダウンロード",
|
||||
"upload_contacts_modal_duplicates_description": "連絡先がすでに存在する場合、どのように処理しますか?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Systeemkenmerken",
|
||||
"unlock_contacts_description": "Beheer contacten en verstuur gerichte enquêtes",
|
||||
"unlock_contacts_title": "Ontgrendel contacten met een hoger abonnement",
|
||||
"upload_contacts_modal_attribute_header": "Formbricks attribuut",
|
||||
"upload_contacts_modal_attributes_description": "Wijs de kolommen in uw CSV toe aan de attributen in Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Nieuw attribuut",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Kenmerk zoeken of toevoegen",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "in kaart moeten worden gebracht",
|
||||
"upload_contacts_modal_attributes_title": "Kenmerken",
|
||||
"upload_contacts_modal_csv_column_header": "CSV kolom",
|
||||
"upload_contacts_modal_description": "Upload een CSV om snel contacten met attributen te importeren",
|
||||
"upload_contacts_modal_download_example_csv": "Voorbeeld-CSV downloaden",
|
||||
"upload_contacts_modal_duplicates_description": "Hoe moeten we omgaan als er al een contact bestaat in uw contacten?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Atributos do sistema",
|
||||
"unlock_contacts_description": "Gerencie contatos e envie pesquisas direcionadas",
|
||||
"unlock_contacts_title": "Desbloqueie contatos com um plano superior",
|
||||
"upload_contacts_modal_attribute_header": "Atributo do Formbricks",
|
||||
"upload_contacts_modal_attributes_description": "Mapeie as colunas do seu CSV para os atributos no Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Novo atributo",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Buscar ou adicionar atributo",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "deve ser mapeado para",
|
||||
"upload_contacts_modal_attributes_title": "Atributos",
|
||||
"upload_contacts_modal_csv_column_header": "Coluna CSV",
|
||||
"upload_contacts_modal_description": "Faça upload de um CSV para importar contatos com atributos rapidamente",
|
||||
"upload_contacts_modal_download_example_csv": "Baixar exemplo de CSV",
|
||||
"upload_contacts_modal_duplicates_description": "O que devemos fazer se um contato já existir nos seus contatos?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Atributos do sistema",
|
||||
"unlock_contacts_description": "Gerir contactos e enviar inquéritos direcionados",
|
||||
"unlock_contacts_title": "Desbloqueie os contactos com um plano superior",
|
||||
"upload_contacts_modal_attribute_header": "Atributo Formbricks",
|
||||
"upload_contacts_modal_attributes_description": "Mapeie as colunas no seu CSV para os atributos no Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Novo atributo",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Pesquisar ou adicionar atributo",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "deve ser mapeado para",
|
||||
"upload_contacts_modal_attributes_title": "Atributos",
|
||||
"upload_contacts_modal_csv_column_header": "Coluna CSV",
|
||||
"upload_contacts_modal_description": "Carregue um ficheiro CSV para importar rapidamente contactos com atributos",
|
||||
"upload_contacts_modal_download_example_csv": "Descarregar exemplo de CSV",
|
||||
"upload_contacts_modal_duplicates_description": "Como devemos proceder se um contacto já existir nos seus contactos?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Atribute de sistem",
|
||||
"unlock_contacts_description": "Gestionează contactele și trimite sondaje țintite",
|
||||
"unlock_contacts_title": "Deblocați contactele cu un plan superior.",
|
||||
"upload_contacts_modal_attribute_header": "Atribut Formbricks",
|
||||
"upload_contacts_modal_attributes_description": "Mapează coloanele din CSV-ul tău la atributele din Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Atribut nou",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Căutați sau adăugați atribut",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "ar trebui să fie mapat către",
|
||||
"upload_contacts_modal_attributes_title": "Atribute",
|
||||
"upload_contacts_modal_csv_column_header": "Coloană CSV",
|
||||
"upload_contacts_modal_description": "Încărcați un fișier CSV pentru a importa rapid contactele cu atribute.",
|
||||
"upload_contacts_modal_download_example_csv": "Descărcați exemplul CSV",
|
||||
"upload_contacts_modal_duplicates_description": "Cum ar trebui să procedăm dacă un contact există deja în agenda dumneavoastră?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Системные атрибуты",
|
||||
"unlock_contacts_description": "Управляйте контактами и отправляйте целевые опросы",
|
||||
"unlock_contacts_title": "Откройте доступ к контактам с более высоким тарифом",
|
||||
"upload_contacts_modal_attribute_header": "Атрибут Formbricks",
|
||||
"upload_contacts_modal_attributes_description": "Сопоставьте столбцы в вашем CSV с атрибутами в Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Новый атрибут",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Найти или добавить атрибут",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "должен быть сопоставлен с",
|
||||
"upload_contacts_modal_attributes_title": "Атрибуты",
|
||||
"upload_contacts_modal_csv_column_header": "Столбец CSV",
|
||||
"upload_contacts_modal_description": "Загрузите CSV, чтобы быстро импортировать контакты с атрибутами",
|
||||
"upload_contacts_modal_download_example_csv": "Скачать пример CSV",
|
||||
"upload_contacts_modal_duplicates_description": "Как поступить, если контакт уже существует в вашей базе?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "Systemattribut",
|
||||
"unlock_contacts_description": "Hantera kontakter och skicka ut riktade enkäter",
|
||||
"unlock_contacts_title": "Lås upp kontakter med en högre plan",
|
||||
"upload_contacts_modal_attribute_header": "Formbricks-attribut",
|
||||
"upload_contacts_modal_attributes_description": "Mappa kolumnerna i din CSV till attributen i Formbricks.",
|
||||
"upload_contacts_modal_attributes_new": "Nytt attribut",
|
||||
"upload_contacts_modal_attributes_search_or_add": "Sök eller lägg till attribut",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "ska mappas till",
|
||||
"upload_contacts_modal_attributes_title": "Attribut",
|
||||
"upload_contacts_modal_csv_column_header": "CSV-kolumn",
|
||||
"upload_contacts_modal_description": "Ladda upp en CSV för att snabbt importera kontakter med attribut",
|
||||
"upload_contacts_modal_download_example_csv": "Ladda ner exempel-CSV",
|
||||
"upload_contacts_modal_duplicates_description": "Hur ska vi hantera om en kontakt redan finns i dina kontakter?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "系统属性",
|
||||
"unlock_contacts_description": "管理 联系人 并 发送 定向 调查",
|
||||
"unlock_contacts_title": "通过 更 高级 划解锁 联系人",
|
||||
"upload_contacts_modal_attribute_header": "Formbricks 属性",
|
||||
"upload_contacts_modal_attributes_description": "将您 CSV 中的列映射到 Formbricks 中的属性。",
|
||||
"upload_contacts_modal_attributes_new": "新 属性",
|
||||
"upload_contacts_modal_attributes_search_or_add": "搜索或添加属性",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "应该映射到",
|
||||
"upload_contacts_modal_attributes_title": "属性",
|
||||
"upload_contacts_modal_csv_column_header": "CSV 列",
|
||||
"upload_contacts_modal_description": "上传 CSV,快速 导入 具有 属性 的 联系人",
|
||||
"upload_contacts_modal_download_example_csv": "下载 示例 CSV",
|
||||
"upload_contacts_modal_duplicates_description": "如果联系人已经存在,应该如何处理?",
|
||||
|
||||
@@ -676,11 +676,12 @@
|
||||
"system_attributes": "系統屬性",
|
||||
"unlock_contacts_description": "管理聯絡人並發送目標問卷",
|
||||
"unlock_contacts_title": "使用更高等級的方案解鎖聯絡人",
|
||||
"upload_contacts_modal_attribute_header": "Formbricks 屬性",
|
||||
"upload_contacts_modal_attributes_description": "將 CSV 中的欄位對應到 Formbricks 中的屬性。",
|
||||
"upload_contacts_modal_attributes_new": "新增屬性",
|
||||
"upload_contacts_modal_attributes_search_or_add": "搜尋或新增屬性",
|
||||
"upload_contacts_modal_attributes_should_be_mapped_to": "應對應到",
|
||||
"upload_contacts_modal_attributes_title": "屬性",
|
||||
"upload_contacts_modal_csv_column_header": "CSV 欄位",
|
||||
"upload_contacts_modal_description": "上傳 CSV 以快速匯入具有屬性的聯絡人",
|
||||
"upload_contacts_modal_download_example_csv": "下載範例 CSV",
|
||||
"upload_contacts_modal_duplicates_description": "如果聯絡人已存在於您的聯絡人中,我們應該如何處理?",
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { z } from "zod";
|
||||
import { ZContactAttributeDataType } from "@formbricks/types/contact-attribute-key";
|
||||
import { isSafeIdentifier } from "@/lib/utils/safe-identifier";
|
||||
|
||||
export const ZContactAttributeKeyCreateInput = z.object({
|
||||
key: z.string(),
|
||||
key: z.string().refine((val) => isSafeIdentifier(val), {
|
||||
message:
|
||||
"Key must be a safe identifier: only lowercase letters, numbers, and underscores, and must start with a letter",
|
||||
}),
|
||||
description: z.string().optional(),
|
||||
type: z.enum(["custom"]),
|
||||
dataType: ZContactAttributeDataType.optional(),
|
||||
@@ -14,7 +18,13 @@ export type TContactAttributeKeyCreateInput = z.infer<typeof ZContactAttributeKe
|
||||
export const ZContactAttributeKeyUpdateInput = z.object({
|
||||
description: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
key: z.string().optional(),
|
||||
key: z
|
||||
.string()
|
||||
.refine((val) => isSafeIdentifier(val), {
|
||||
message:
|
||||
"Key must be a safe identifier: only lowercase letters, numbers, and underscores, and must start with a letter",
|
||||
})
|
||||
.optional(),
|
||||
dataType: ZContactAttributeDataType.optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { prisma } from "@formbricks/database";
|
||||
import { logger } from "@formbricks/logger";
|
||||
import { TContactAttributeDataType } from "@formbricks/types/contact-attribute-key";
|
||||
import { Result, err, ok } from "@formbricks/types/error-handlers";
|
||||
import { isSafeIdentifier } from "@/lib/utils/safe-identifier";
|
||||
import { ApiErrorResponseV2 } from "@/modules/api/v2/types/api-error";
|
||||
import { prepareAttributeColumnsForStorage } from "@/modules/ee/contacts/lib/attribute-storage";
|
||||
import { detectAttributeDataType } from "@/modules/ee/contacts/lib/detect-attribute-type";
|
||||
@@ -497,6 +498,22 @@ export const upsertBulkContacts = async (
|
||||
}),
|
||||
]);
|
||||
|
||||
// Validate new attribute keys are safe identifiers before proceeding
|
||||
const existingKeySet = new Set(existingAttributeKeys.map((ak) => ak.key));
|
||||
const invalidNewKeys = attributeKeys.filter((key) => !existingKeySet.has(key) && !isSafeIdentifier(key));
|
||||
|
||||
if (invalidNewKeys.length > 0) {
|
||||
return err({
|
||||
type: "bad_request",
|
||||
details: [
|
||||
{
|
||||
field: "attributes",
|
||||
issue: `Invalid attribute key(s): ${invalidNewKeys.join(", ")}. Keys must only contain lowercase letters, numbers, and underscores, and must start with a letter.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// Type Detection Phase
|
||||
const attributeValuesByKey = buildAttributeValuesByKey(contacts);
|
||||
const attributeTypeMap = determineAttributeTypes(attributeValuesByKey, existingAttributeKeys);
|
||||
|
||||
@@ -283,11 +283,51 @@ export const UploadContactsCSVButton = ({
|
||||
// Function to download an example CSV
|
||||
const handleDownloadExampleCSV = () => {
|
||||
const exampleData = [
|
||||
{ email: "user1@example.com", userId: "1001", first_name: "John", last_name: "Doe" },
|
||||
{ email: "user2@example.com", userId: "1002", first_name: "Jane", last_name: "Smith" },
|
||||
{ email: "user3@example.com", userId: "1003", first_name: "Mark", last_name: "Jones" },
|
||||
{ email: "user4@example.com", userId: "1004", first_name: "Emily", last_name: "Brown" },
|
||||
{ email: "user5@example.com", userId: "1005", first_name: "David", last_name: "Wilson" },
|
||||
{
|
||||
email: "user1@example.com",
|
||||
userId: "1001",
|
||||
first_name: "John",
|
||||
last_name: "Doe",
|
||||
age: "28",
|
||||
plan: "premium",
|
||||
signup_date: "2024-01-15",
|
||||
},
|
||||
{
|
||||
email: "user2@example.com",
|
||||
userId: "1002",
|
||||
first_name: "Jane",
|
||||
last_name: "Smith",
|
||||
age: "34",
|
||||
plan: "free",
|
||||
signup_date: "2024-02-20",
|
||||
},
|
||||
{
|
||||
email: "user3@example.com",
|
||||
userId: "1003",
|
||||
first_name: "Mark",
|
||||
last_name: "Jones",
|
||||
age: "45",
|
||||
plan: "enterprise",
|
||||
signup_date: "2023-11-08",
|
||||
},
|
||||
{
|
||||
email: "user4@example.com",
|
||||
userId: "1004",
|
||||
first_name: "Emily",
|
||||
last_name: "Brown",
|
||||
age: "22",
|
||||
plan: "premium",
|
||||
signup_date: "2024-03-01",
|
||||
},
|
||||
{
|
||||
email: "user5@example.com",
|
||||
userId: "1005",
|
||||
first_name: "David",
|
||||
last_name: "Wilson",
|
||||
age: "31",
|
||||
plan: "free",
|
||||
signup_date: "2024-01-28",
|
||||
},
|
||||
];
|
||||
|
||||
const headers = Object.keys(exampleData[0]);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { cache as reactCache } from "react";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { ZId, ZString } from "@formbricks/types/common";
|
||||
import { TContactAttributes } from "@formbricks/types/contact-attribute";
|
||||
import { TContactAttributeDataType } from "@formbricks/types/contact-attribute-key";
|
||||
import { DatabaseError } from "@formbricks/types/errors";
|
||||
import { ZUserEmail } from "@formbricks/types/user";
|
||||
import { validateInputs } from "@/lib/utils/validate";
|
||||
@@ -132,55 +131,24 @@ export const hasUserIdAttribute = reactCache(
|
||||
);
|
||||
|
||||
export const getDistinctAttributeValues = reactCache(
|
||||
async (attributeKeyId: string, dataType: TContactAttributeDataType, limit: number = 50) => {
|
||||
async (attributeKeyId: string, limit: number = 50): Promise<string[]> => {
|
||||
validateInputs([attributeKeyId, ZId]);
|
||||
|
||||
try {
|
||||
// Determine which column to query based on data type
|
||||
let selectField: "value" | "valueNumber" | "valueDate";
|
||||
if (dataType === "number") {
|
||||
selectField = "valueNumber";
|
||||
} else if (dataType === "date") {
|
||||
selectField = "valueDate";
|
||||
} else {
|
||||
selectField = "value";
|
||||
}
|
||||
|
||||
// Build where clause - only filter by attributeKeyId, we'll filter nulls in JS
|
||||
const results = await prisma.contactAttribute.findMany({
|
||||
where: {
|
||||
attributeKeyId,
|
||||
value: { not: "" },
|
||||
},
|
||||
select: {
|
||||
value: true,
|
||||
valueNumber: true,
|
||||
valueDate: true,
|
||||
},
|
||||
distinct: [selectField as any],
|
||||
distinct: ["value"],
|
||||
take: limit * 2, // Get more than needed to account for filtering
|
||||
orderBy: { [selectField]: "asc" },
|
||||
orderBy: { value: "asc" },
|
||||
});
|
||||
|
||||
// Extract and filter values based on data type
|
||||
let values: any[];
|
||||
if (dataType === "number") {
|
||||
values = results
|
||||
.map((r) => r.valueNumber)
|
||||
.filter((v) => v !== null && v !== undefined)
|
||||
.slice(0, limit);
|
||||
} else if (dataType === "date") {
|
||||
values = results
|
||||
.map((r) => r.valueDate)
|
||||
.filter((v) => v !== null && v !== undefined)
|
||||
.slice(0, limit);
|
||||
} else {
|
||||
values = results
|
||||
.map((r) => r.value)
|
||||
.filter((v) => v !== null && v !== undefined && v !== "")
|
||||
.slice(0, limit);
|
||||
}
|
||||
|
||||
return values;
|
||||
return results.map((r) => r.value);
|
||||
} catch (error) {
|
||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
throw new DatabaseError(error.message);
|
||||
|
||||
@@ -315,7 +315,6 @@ export const resetSegmentFiltersAction = authenticatedActionClient.schema(ZReset
|
||||
const ZGetDistinctAttributeValuesAction = z.object({
|
||||
environmentId: ZId,
|
||||
attributeKeyId: ZId,
|
||||
dataType: z.enum(["string", "number", "date"]),
|
||||
});
|
||||
|
||||
export const getDistinctAttributeValuesAction = authenticatedActionClient
|
||||
@@ -337,5 +336,5 @@ export const getDistinctAttributeValuesAction = authenticatedActionClient
|
||||
],
|
||||
});
|
||||
|
||||
return await getDistinctAttributeValues(parsedInput.attributeKeyId, parsedInput.dataType);
|
||||
return await getDistinctAttributeValues(parsedInput.attributeKeyId);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { TContactAttributeDataType } from "@formbricks/types/contact-attribute-key";
|
||||
import { cn } from "@/lib/cn";
|
||||
import { InputCombobox, TComboboxOption } from "@/modules/ui/components/input-combo-box";
|
||||
import { getDistinctAttributeValuesAction } from "../actions";
|
||||
@@ -9,9 +8,8 @@ import { getDistinctAttributeValuesAction } from "../actions";
|
||||
interface AttributeValueInputProps {
|
||||
attributeKeyId: string;
|
||||
environmentId: string;
|
||||
dataType: TContactAttributeDataType;
|
||||
value: string | number;
|
||||
onChange: (value: string | number) => void;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
valueError?: string;
|
||||
@@ -20,7 +18,6 @@ interface AttributeValueInputProps {
|
||||
export const AttributeValueInput = ({
|
||||
attributeKeyId,
|
||||
environmentId,
|
||||
dataType,
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
@@ -45,14 +42,14 @@ export const AttributeValueInput = ({
|
||||
const result = await getDistinctAttributeValuesAction({
|
||||
environmentId,
|
||||
attributeKeyId,
|
||||
dataType,
|
||||
});
|
||||
|
||||
if (!isCancelled && result?.data) {
|
||||
const comboboxOptions: TComboboxOption[] = result.data.map((val) => ({
|
||||
label: String(val),
|
||||
label: val,
|
||||
value: val,
|
||||
}));
|
||||
|
||||
setOptions(comboboxOptions);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -71,7 +68,7 @@ export const AttributeValueInput = ({
|
||||
return () => {
|
||||
isCancelled = true;
|
||||
};
|
||||
}, [environmentId, attributeKeyId, dataType]);
|
||||
}, [environmentId, attributeKeyId]);
|
||||
|
||||
const emptyDropdownText = useMemo(() => {
|
||||
if (loading) return "Loading values...";
|
||||
@@ -85,7 +82,7 @@ export const AttributeValueInput = ({
|
||||
id={`attribute-value-${attributeKeyId}`}
|
||||
options={options}
|
||||
value={value}
|
||||
onChangeValue={(newValue) => onChange(newValue as string | number)}
|
||||
onChangeValue={(newValue) => onChange(newValue as string)}
|
||||
withInput={true}
|
||||
showSearch={true}
|
||||
clearable={true}
|
||||
|
||||
@@ -315,8 +315,7 @@ function AttributeSegmentFilter({
|
||||
<AttributeValueInput
|
||||
attributeKeyId={attributeKey.id}
|
||||
environmentId={segment.environmentId}
|
||||
dataType={attributeDataType}
|
||||
value={resource.value as string | number}
|
||||
value={resource.value as string}
|
||||
onChange={(newValue) => {
|
||||
updateValueInLocalSurvey(resource.id, newValue);
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user