mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-13 11:29:31 -05:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b0facf1cd0 | |||
| c6ebaea989 | |||
| 68c1422733 | |||
| 6942502baf | |||
| a4bd217761 | |||
| fee770358c | |||
| 44f8f80cac | |||
| 858a7f7aa9 | |||
| ac40b90e81 | |||
| aa21b4e442 | |||
| fa72296de5 |
@@ -101,6 +101,9 @@ RUN chown -R nextjs:nextjs ./apps/web/public && chmod -R 755 ./apps/web/public
|
||||
# Create packages/database directory structure with proper ownership for runtime migrations
|
||||
RUN mkdir -p ./packages/database/migrations && chown -R nextjs:nextjs ./packages/database
|
||||
|
||||
COPY --from=installer /app/packages/database/package.json ./packages/database/package.json
|
||||
RUN chown nextjs:nextjs ./packages/database/package.json && chmod 644 ./packages/database/package.json
|
||||
|
||||
COPY --from=installer /app/packages/database/schema.prisma ./packages/database/schema.prisma
|
||||
RUN chown nextjs:nextjs ./packages/database/schema.prisma && chmod 644 ./packages/database/schema.prisma
|
||||
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ export const OrganizationBreadcrumb = ({
|
||||
getOrganizationsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
|
||||
if (result?.data) {
|
||||
// Sort organizations by name
|
||||
const sorted = result.data.toSorted((a, b) => a.name.localeCompare(b.name));
|
||||
const sorted = [...result.data].sort((a, b) => a.name.localeCompare(b.name));
|
||||
setOrganizations(sorted);
|
||||
} else {
|
||||
// Handle server errors or validation errors
|
||||
|
||||
@@ -82,7 +82,7 @@ export const ProjectBreadcrumb = ({
|
||||
getProjectsForSwitcherAction({ organizationId: currentOrganizationId }).then((result) => {
|
||||
if (result?.data) {
|
||||
// Sort projects by name
|
||||
const sorted = result.data.toSorted((a, b) => a.name.localeCompare(b.name));
|
||||
const sorted = [...result.data].sort((a, b) => a.name.localeCompare(b.name));
|
||||
setProjects(sorted);
|
||||
} else {
|
||||
// Handle server errors or validation errors
|
||||
|
||||
+1
-1
@@ -352,7 +352,7 @@ export const AnonymousLinksTab = ({
|
||||
},
|
||||
{
|
||||
title: t("environments.surveys.share.anonymous_links.custom_start_point"),
|
||||
href: "https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/start-at-question",
|
||||
href: "https://formbricks.com/docs/xm-and-surveys/surveys/link-surveys/start-at-block",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
+2
-2
@@ -241,7 +241,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
<Popover open={isOpen} onOpenChange={handleOpenChange}>
|
||||
<PopoverTrigger asChild>
|
||||
<PopoverTriggerButton isOpen={isOpen}>
|
||||
Filter <b>{activeFilterCount > 0 && `(${activeFilterCount})`}</b>
|
||||
{t("common.filter")} <b>{activeFilterCount > 0 && `(${activeFilterCount})`}</b>
|
||||
</PopoverTriggerButton>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
@@ -329,7 +329,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
</div>
|
||||
{i !== filterValue.filter.length - 1 && (
|
||||
<div className="my-4 flex items-center">
|
||||
<p className="mr-4 font-semibold text-slate-800">and</p>
|
||||
<p className="mr-4 font-semibold text-slate-800">{t("common.and")}</p>
|
||||
<hr className="w-full text-slate-600" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from "@formbricks/types/integration/slack";
|
||||
import { responses } from "@/app/lib/api/response";
|
||||
import { TSessionAuthentication, withV1ApiWrapper } from "@/app/lib/api/with-api-logging";
|
||||
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, WEBAPP_URL } from "@/lib/constants";
|
||||
import { SLACK_CLIENT_ID, SLACK_CLIENT_SECRET, SLACK_REDIRECT_URI, WEBAPP_URL } from "@/lib/constants";
|
||||
import { hasUserEnvironmentAccess } from "@/lib/environment/auth";
|
||||
import { createOrUpdateIntegration, getIntegrationByType } from "@/lib/integration/service";
|
||||
|
||||
@@ -56,6 +56,7 @@ export const GET = withV1ApiWrapper({
|
||||
code,
|
||||
client_id: SLACK_CLIENT_ID,
|
||||
client_secret: SLACK_CLIENT_SECRET,
|
||||
redirect_uri: SLACK_REDIRECT_URI,
|
||||
};
|
||||
const formBody: string[] = [];
|
||||
for (const property in formData) {
|
||||
|
||||
@@ -199,6 +199,7 @@ checksums:
|
||||
common/failed_to_load_organizations: 512808a2b674c7c28bca73f8f91fd87e
|
||||
common/failed_to_load_workspaces: 6ee3448097394517dc605074cd4e6ea4
|
||||
common/finish: ffa7a10f71182b48fefed7135bee24fa
|
||||
common/first_name: cf040a5d6a9fd696be400380cc99f54b
|
||||
common/follow_these: 3a730b242bb17a3f95e01bf0dae86885
|
||||
common/formbricks_version: d9967c797f3e49ca0cae78bc0ebd19cb
|
||||
common/full_name: f45991923345e8322c9ff8cd6b7e2b16
|
||||
@@ -211,6 +212,7 @@ checksums:
|
||||
common/hidden_field: 3ed5c58d0ed359e558cdf7bd33606d2d
|
||||
common/hidden_fields: 3de6cfd308293a826cb8679fd1d49972
|
||||
common/hide_column: 23ce94db148f2d8e4a0923defead6cf1
|
||||
common/id: c8886d38aeea2ed5f785aba4fc96784b
|
||||
common/image: 048ba7a239de0fbd883ade8558415830
|
||||
common/images: 9305827c28694866f49db42b4c51831f
|
||||
common/import: 348b8ab981de5b7f1fca6d7302263bbd
|
||||
@@ -228,6 +230,7 @@ checksums:
|
||||
common/key: 3d1065ab98a1c2f1210507fd5c7bf515
|
||||
common/label: a5c71bf158481233f8215dbd38cc196b
|
||||
common/language: 277fd1a41cc237a437cd1d5e4a80463b
|
||||
common/last_name: 2c9a7de7738ca007ba9023c385149c26
|
||||
common/learn_more: e598091d132f890c37a6d4ed94f6d794
|
||||
common/license_expired: 7af13535e320e4197989472c01387d2c
|
||||
common/light_overlay: 0499907ea7b8405f4267b117998b5a78
|
||||
|
||||
@@ -63,7 +63,8 @@ export const INVITE_DISABLED = env.INVITE_DISABLED === "1";
|
||||
|
||||
export const SLACK_CLIENT_SECRET = env.SLACK_CLIENT_SECRET;
|
||||
export const SLACK_CLIENT_ID = env.SLACK_CLIENT_ID;
|
||||
export const SLACK_AUTH_URL = `https://slack.com/oauth/v2/authorize?client_id=${env.SLACK_CLIENT_ID}&scope=channels:read,chat:write,chat:write.public,chat:write.customize,groups:read`;
|
||||
export const SLACK_REDIRECT_URI = `${WEBAPP_URL}/api/v1/integrations/slack/callback`;
|
||||
export const SLACK_AUTH_URL = `https://slack.com/oauth/v2/authorize?client_id=${env.SLACK_CLIENT_ID}&scope=channels:read,chat:write,chat:write.public,chat:write.customize,groups:read&redirect_uri=${SLACK_REDIRECT_URI}`;
|
||||
|
||||
export const GOOGLE_SHEETS_CLIENT_ID = env.GOOGLE_SHEETS_CLIENT_ID;
|
||||
export const GOOGLE_SHEETS_CLIENT_SECRET = env.GOOGLE_SHEETS_CLIENT_SECRET;
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Fehler beim Kopieren in die Zwischenablage",
|
||||
"failed_to_load_organizations": "Fehler beim Laden der Organisationen",
|
||||
"failed_to_load_workspaces": "Projekte konnten nicht geladen werden",
|
||||
"filter": "Filter",
|
||||
"finish": "Fertigstellen",
|
||||
"first_name": "Vorname",
|
||||
"follow_these": "Folge diesen",
|
||||
"formbricks_version": "Formbricks Version",
|
||||
"full_name": "Name",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Verstecktes Feld",
|
||||
"hidden_fields": "Versteckte Felder",
|
||||
"hide_column": "Spalte ausblenden",
|
||||
"id": "ID",
|
||||
"image": "Bild",
|
||||
"images": "Bilder",
|
||||
"import": "Importieren",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Schlüssel",
|
||||
"label": "Bezeichnung",
|
||||
"language": "Sprache",
|
||||
"last_name": "Nachname",
|
||||
"learn_more": "Mehr erfahren",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Helle Überlagerung",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Failed to copy to clipboard",
|
||||
"failed_to_load_organizations": "Failed to load organizations",
|
||||
"failed_to_load_workspaces": "Failed to load workspaces",
|
||||
"filter": "Filter",
|
||||
"finish": "Finish",
|
||||
"first_name": "First Name",
|
||||
"follow_these": "Follow these",
|
||||
"formbricks_version": "Formbricks Version",
|
||||
"full_name": "Full name",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Hidden field",
|
||||
"hidden_fields": "Hidden fields",
|
||||
"hide_column": "Hide column",
|
||||
"id": "ID",
|
||||
"image": "Image",
|
||||
"images": "Images",
|
||||
"import": "Import",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Key",
|
||||
"label": "Label",
|
||||
"language": "Language",
|
||||
"last_name": "Last Name",
|
||||
"learn_more": "Learn more",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Light overlay",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Error al copiar al portapapeles",
|
||||
"failed_to_load_organizations": "Error al cargar organizaciones",
|
||||
"failed_to_load_workspaces": "Error al cargar los proyectos",
|
||||
"filter": "Filtro",
|
||||
"finish": "Finalizar",
|
||||
"first_name": "Nombre",
|
||||
"follow_these": "Sigue estos",
|
||||
"formbricks_version": "Versión de Formbricks",
|
||||
"full_name": "Nombre completo",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Campo oculto",
|
||||
"hidden_fields": "Campos ocultos",
|
||||
"hide_column": "Ocultar columna",
|
||||
"id": "ID",
|
||||
"image": "Imagen",
|
||||
"images": "Imágenes",
|
||||
"import": "Importar",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Clave",
|
||||
"label": "Etiqueta",
|
||||
"language": "Idioma",
|
||||
"last_name": "Apellido",
|
||||
"learn_more": "Saber más",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Superposición clara",
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
"link_expired_description": "Le lien que vous avez utilisé n'est plus valide."
|
||||
},
|
||||
"common": {
|
||||
"Filter": "Filtrer",
|
||||
"accepted": "Accepté",
|
||||
"account": "Compte",
|
||||
"account_settings": "Paramètres du compte",
|
||||
@@ -225,7 +226,9 @@
|
||||
"failed_to_copy_to_clipboard": "Échec de la copie dans le presse-papiers",
|
||||
"failed_to_load_organizations": "Échec du chargement des organisations",
|
||||
"failed_to_load_workspaces": "Échec du chargement des projets",
|
||||
"filter": "Filtre",
|
||||
"finish": "Terminer",
|
||||
"first_name": "Prénom",
|
||||
"follow_these": "Suivez ceci",
|
||||
"formbricks_version": "Version de Formbricks",
|
||||
"full_name": "Nom complet",
|
||||
@@ -238,6 +241,7 @@
|
||||
"hidden_field": "Champ caché",
|
||||
"hidden_fields": "Champs cachés",
|
||||
"hide_column": "Cacher la colonne",
|
||||
"id": "ID",
|
||||
"image": "Image",
|
||||
"images": "Images",
|
||||
"import": "Importer",
|
||||
@@ -255,6 +259,7 @@
|
||||
"key": "Clé",
|
||||
"label": "Étiquette",
|
||||
"language": "Langue",
|
||||
"last_name": "Nom de famille",
|
||||
"learn_more": "En savoir plus",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Claire",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Nem sikerült másolni a vágólapra",
|
||||
"failed_to_load_organizations": "Nem sikerült betölteni a szervezeteket",
|
||||
"failed_to_load_workspaces": "Nem sikerült a munkaterületek betöltése",
|
||||
"filter": "Szűrő",
|
||||
"finish": "Befejezés",
|
||||
"first_name": "Keresztnév",
|
||||
"follow_these": "Ezek követése",
|
||||
"formbricks_version": "Formbricks verziója",
|
||||
"full_name": "Teljes név",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Rejtett mező",
|
||||
"hidden_fields": "Rejtett mezők",
|
||||
"hide_column": "Oszlop elrejtése",
|
||||
"id": "ID",
|
||||
"image": "Kép",
|
||||
"images": "Képek",
|
||||
"import": "Importálás",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Kulcs",
|
||||
"label": "Címke",
|
||||
"language": "Nyelv",
|
||||
"last_name": "Vezetéknév",
|
||||
"learn_more": "Tudjon meg többet",
|
||||
"license_expired": "A licenc lejárt",
|
||||
"light_overlay": "Világos rávetítés",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "クリップボードへのコピーに失敗しました",
|
||||
"failed_to_load_organizations": "組織の読み込みに失敗しました",
|
||||
"failed_to_load_workspaces": "ワークスペースの読み込みに失敗しました",
|
||||
"filter": "フィルター",
|
||||
"finish": "完了",
|
||||
"first_name": "名",
|
||||
"follow_these": "こちらの手順に従って",
|
||||
"formbricks_version": "Formbricksバージョン",
|
||||
"full_name": "氏名",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "非表示フィールド",
|
||||
"hidden_fields": "非表示フィールド",
|
||||
"hide_column": "列を非表示",
|
||||
"id": "ID",
|
||||
"image": "画像",
|
||||
"images": "画像",
|
||||
"import": "インポート",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "キー",
|
||||
"label": "ラベル",
|
||||
"language": "言語",
|
||||
"last_name": "姓",
|
||||
"learn_more": "詳細を見る",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "明るいオーバーレイ",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Kopiëren naar klembord mislukt",
|
||||
"failed_to_load_organizations": "Laden van organisaties mislukt",
|
||||
"failed_to_load_workspaces": "Laden van werkruimtes mislukt",
|
||||
"filter": "Filter",
|
||||
"finish": "Finish",
|
||||
"first_name": "Voornaam",
|
||||
"follow_these": "Volg deze",
|
||||
"formbricks_version": "Formbricks-versie",
|
||||
"full_name": "Volledige naam",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Verborgen veld",
|
||||
"hidden_fields": "Verborgen velden",
|
||||
"hide_column": "Kolom verbergen",
|
||||
"id": "ID",
|
||||
"image": "Afbeelding",
|
||||
"images": "Afbeeldingen",
|
||||
"import": "Importeren",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Sleutel",
|
||||
"label": "Label",
|
||||
"language": "Taal",
|
||||
"last_name": "Achternaam",
|
||||
"learn_more": "Meer informatie",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Lichte overlay",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
|
||||
"failed_to_load_organizations": "Falha ao carregar organizações",
|
||||
"failed_to_load_workspaces": "Falha ao carregar projetos",
|
||||
"filter": "Filtro",
|
||||
"finish": "Terminar",
|
||||
"first_name": "Primeiro nome",
|
||||
"follow_these": "Siga esses",
|
||||
"formbricks_version": "Versão do Formbricks",
|
||||
"full_name": "Nome completo",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Campo oculto",
|
||||
"hidden_fields": "Campos ocultos",
|
||||
"hide_column": "Ocultar coluna",
|
||||
"id": "ID",
|
||||
"image": "imagem",
|
||||
"images": "Imagens",
|
||||
"import": "importar",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Chave",
|
||||
"label": "Etiqueta",
|
||||
"language": "Língua",
|
||||
"last_name": "Sobrenome",
|
||||
"learn_more": "Saiba mais",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "sobreposição leve",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Falha ao copiar para a área de transferência",
|
||||
"failed_to_load_organizations": "Falha ao carregar organizações",
|
||||
"failed_to_load_workspaces": "Falha ao carregar projetos",
|
||||
"filter": "Filtro",
|
||||
"finish": "Concluir",
|
||||
"first_name": "Primeiro nome",
|
||||
"follow_these": "Siga estes",
|
||||
"formbricks_version": "Versão do Formbricks",
|
||||
"full_name": "Nome completo",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Campo oculto",
|
||||
"hidden_fields": "Campos ocultos",
|
||||
"hide_column": "Ocultar coluna",
|
||||
"id": "ID",
|
||||
"image": "Imagem",
|
||||
"images": "Imagens",
|
||||
"import": "Importar",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Chave",
|
||||
"label": "Etiqueta",
|
||||
"language": "Idioma",
|
||||
"last_name": "Apelido",
|
||||
"learn_more": "Saiba mais",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Sobreposição leve",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Nu s-a reușit copierea în clipboard",
|
||||
"failed_to_load_organizations": "Nu s-a reușit încărcarea organizațiilor",
|
||||
"failed_to_load_workspaces": "Nu s-au putut încărca workspaces",
|
||||
"filter": "Filtru",
|
||||
"finish": "Finalizează",
|
||||
"first_name": "Prenume",
|
||||
"follow_these": "Urmați acestea",
|
||||
"formbricks_version": "Versiunea Formbricks",
|
||||
"full_name": "Nume complet",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Câmp ascuns",
|
||||
"hidden_fields": "Câmpuri ascunse",
|
||||
"hide_column": "Ascunde coloana",
|
||||
"id": "ID",
|
||||
"image": "Imagine",
|
||||
"images": "Imagini",
|
||||
"import": "Import",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Cheie",
|
||||
"label": "Etichetă",
|
||||
"language": "Limba",
|
||||
"last_name": "Nume de familie",
|
||||
"learn_more": "Află mai multe",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Suprapunere ușoară",
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
"link_expired_description": "Ссылка, которой вы воспользовались, больше не действительна."
|
||||
},
|
||||
"common": {
|
||||
"Filter": "Фильтр",
|
||||
"accepted": "Принято",
|
||||
"account": "Аккаунт",
|
||||
"account_settings": "Настройки аккаунта",
|
||||
@@ -225,7 +226,9 @@
|
||||
"failed_to_copy_to_clipboard": "Не удалось скопировать в буфер обмена",
|
||||
"failed_to_load_organizations": "Не удалось загрузить организации",
|
||||
"failed_to_load_workspaces": "Не удалось загрузить рабочие пространства",
|
||||
"filter": "Фильтр",
|
||||
"finish": "Завершить",
|
||||
"first_name": "Имя",
|
||||
"follow_these": "Выполните следующие действия",
|
||||
"formbricks_version": "Версия Formbricks",
|
||||
"full_name": "Полное имя",
|
||||
@@ -238,6 +241,7 @@
|
||||
"hidden_field": "Скрытое поле",
|
||||
"hidden_fields": "Скрытые поля",
|
||||
"hide_column": "Скрыть столбец",
|
||||
"id": "ID",
|
||||
"image": "Изображение",
|
||||
"images": "Изображения",
|
||||
"import": "Импорт",
|
||||
@@ -255,6 +259,7 @@
|
||||
"key": "Ключ",
|
||||
"label": "Метка",
|
||||
"language": "Язык",
|
||||
"last_name": "Фамилия",
|
||||
"learn_more": "Подробнее",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Светлый оверлей",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "Misslyckades att kopiera till urklipp",
|
||||
"failed_to_load_organizations": "Misslyckades att ladda organisationer",
|
||||
"failed_to_load_workspaces": "Det gick inte att ladda arbetsytor",
|
||||
"filter": "Filter",
|
||||
"finish": "Slutför",
|
||||
"first_name": "Förnamn",
|
||||
"follow_these": "Följ dessa",
|
||||
"formbricks_version": "Formbricks-version",
|
||||
"full_name": "Fullständigt namn",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "Dolt fält",
|
||||
"hidden_fields": "Dolda fält",
|
||||
"hide_column": "Dölj kolumn",
|
||||
"id": "ID",
|
||||
"image": "Bild",
|
||||
"images": "Bilder",
|
||||
"import": "Importera",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "Nyckel",
|
||||
"label": "Etikett",
|
||||
"language": "Språk",
|
||||
"last_name": "Efternamn",
|
||||
"learn_more": "Läs mer",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "Ljust överlägg",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "复制到剪贴板失败",
|
||||
"failed_to_load_organizations": "加载组织失败",
|
||||
"failed_to_load_workspaces": "加载工作区失败",
|
||||
"filter": "筛选",
|
||||
"finish": "完成",
|
||||
"first_name": "名字",
|
||||
"follow_these": "遵循 这些",
|
||||
"formbricks_version": "Formbricks 版本",
|
||||
"full_name": "全名",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "隐藏 字段",
|
||||
"hidden_fields": "隐藏 字段",
|
||||
"hide_column": "隐藏 列",
|
||||
"id": "ID",
|
||||
"image": "图片",
|
||||
"images": "图片",
|
||||
"import": "导入",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "键",
|
||||
"label": "标签",
|
||||
"language": "语言",
|
||||
"last_name": "姓",
|
||||
"learn_more": "了解 更多",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "浅色遮罩层",
|
||||
|
||||
@@ -225,7 +225,9 @@
|
||||
"failed_to_copy_to_clipboard": "無法複製到剪貼簿",
|
||||
"failed_to_load_organizations": "無法載入組織",
|
||||
"failed_to_load_workspaces": "載入工作區失敗",
|
||||
"filter": "篩選",
|
||||
"finish": "完成",
|
||||
"first_name": "名字",
|
||||
"follow_these": "按照這些步驟",
|
||||
"formbricks_version": "Formbricks 版本",
|
||||
"full_name": "全名",
|
||||
@@ -238,6 +240,7 @@
|
||||
"hidden_field": "隱藏欄位",
|
||||
"hidden_fields": "隱藏欄位",
|
||||
"hide_column": "隱藏欄位",
|
||||
"id": "ID",
|
||||
"image": "圖片",
|
||||
"images": "圖片",
|
||||
"import": "匯入",
|
||||
@@ -255,6 +258,7 @@
|
||||
"key": "金鑰",
|
||||
"label": "標籤",
|
||||
"language": "語言",
|
||||
"last_name": "姓氏",
|
||||
"learn_more": "瞭解更多",
|
||||
"license_expired": "License Expired",
|
||||
"light_overlay": "淺色覆蓋",
|
||||
|
||||
@@ -52,54 +52,41 @@ export const getPersonSegmentIds = async (
|
||||
return [];
|
||||
}
|
||||
|
||||
// Phase 1: Build all Prisma where clauses concurrently.
|
||||
// This converts segment filters into where clauses without per-contact DB queries.
|
||||
const segmentWithClauses = await Promise.all(
|
||||
segments.map(async (segment) => {
|
||||
const filters = segment.filters as TBaseFilters | null;
|
||||
|
||||
if (!filters || filters.length === 0) {
|
||||
return { segmentId: segment.id, whereClause: {} as Prisma.ContactWhereInput };
|
||||
}
|
||||
|
||||
const queryResult = await segmentFilterToPrismaQuery(segment.id, filters, environmentId, deviceType);
|
||||
|
||||
if (!queryResult.ok) {
|
||||
logger.warn(
|
||||
{ segmentId: segment.id, environmentId, error: queryResult.error },
|
||||
"Failed to build Prisma query for segment"
|
||||
);
|
||||
return { segmentId: segment.id, whereClause: null };
|
||||
}
|
||||
|
||||
return { segmentId: segment.id, whereClause: queryResult.data.whereClause };
|
||||
})
|
||||
);
|
||||
|
||||
// Separate segments into: always-match (no filters), needs-DB-check, and failed-to-build
|
||||
// Phase 1: Build WHERE clauses sequentially to avoid connection pool contention.
|
||||
// segmentFilterToPrismaQuery can itself hit the DB (e.g. unmigrated-row checks),
|
||||
// so running all builds concurrently would saturate the pool.
|
||||
const alwaysMatchIds: string[] = [];
|
||||
const toCheck: { segmentId: string; whereClause: Prisma.ContactWhereInput }[] = [];
|
||||
const dbChecks: { segmentId: string; whereClause: Prisma.ContactWhereInput }[] = [];
|
||||
|
||||
for (const item of segmentWithClauses) {
|
||||
if (item.whereClause === null) {
|
||||
for (const segment of segments) {
|
||||
const filters = segment.filters as TBaseFilters;
|
||||
|
||||
if (!filters?.length) {
|
||||
alwaysMatchIds.push(segment.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Object.keys(item.whereClause).length === 0) {
|
||||
alwaysMatchIds.push(item.segmentId);
|
||||
} else {
|
||||
toCheck.push({ segmentId: item.segmentId, whereClause: item.whereClause });
|
||||
const queryResult = await segmentFilterToPrismaQuery(segment.id, filters, environmentId, deviceType);
|
||||
|
||||
if (!queryResult.ok) {
|
||||
logger.warn(
|
||||
{ segmentId: segment.id, environmentId, error: queryResult.error },
|
||||
"Failed to build Prisma query for segment, skipping"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
dbChecks.push({ segmentId: segment.id, whereClause: queryResult.data.whereClause });
|
||||
}
|
||||
|
||||
if (toCheck.length === 0) {
|
||||
if (dbChecks.length === 0) {
|
||||
return alwaysMatchIds;
|
||||
}
|
||||
|
||||
// Phase 2: Batch all contact-match checks into a single DB transaction.
|
||||
// Replaces N individual findFirst queries with one batched round-trip.
|
||||
const batchResults = await prisma.$transaction(
|
||||
toCheck.map(({ whereClause }) =>
|
||||
// Phase 2: Execute all membership checks in a single transaction.
|
||||
// Uses one connection instead of N concurrent ones, eliminating pool contention.
|
||||
const txResults = await prisma.$transaction(
|
||||
dbChecks.map(({ whereClause }) =>
|
||||
prisma.contact.findFirst({
|
||||
where: { id: contactId, ...whereClause },
|
||||
select: { id: true },
|
||||
@@ -107,17 +94,12 @@ export const getPersonSegmentIds = async (
|
||||
)
|
||||
);
|
||||
|
||||
// Phase 3: Collect matching segment IDs
|
||||
const dbMatchIds = toCheck.filter((_, i) => batchResults[i] !== null).map(({ segmentId }) => segmentId);
|
||||
const matchedIds = dbChecks.filter((_, i) => txResults[i] !== null).map(({ segmentId }) => segmentId);
|
||||
|
||||
return [...alwaysMatchIds, ...dbMatchIds];
|
||||
return [...alwaysMatchIds, ...matchedIds];
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
{
|
||||
environmentId,
|
||||
contactId,
|
||||
error,
|
||||
},
|
||||
{ environmentId, contactId, error },
|
||||
"Failed to get person segment IDs, returning empty array"
|
||||
);
|
||||
return [];
|
||||
|
||||
-139
@@ -1,139 +0,0 @@
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TJsPersonState } from "@formbricks/types/js";
|
||||
import { getPersonSegmentIds } from "./segments";
|
||||
import { getUserState } from "./user-state";
|
||||
|
||||
vi.mock("@formbricks/database", () => ({
|
||||
prisma: {
|
||||
contact: {
|
||||
findUniqueOrThrow: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./segments", () => ({
|
||||
getPersonSegmentIds: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockEnvironmentId = "test-environment-id";
|
||||
const mockUserId = "test-user-id";
|
||||
const mockContactId = "test-contact-id";
|
||||
const mockDevice = "desktop";
|
||||
|
||||
describe("getUserState", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test("should return user state with empty responses and displays", async () => {
|
||||
const mockContactData = {
|
||||
id: mockContactId,
|
||||
responses: [],
|
||||
displays: [],
|
||||
};
|
||||
vi.mocked(prisma.contact.findUniqueOrThrow).mockResolvedValue(mockContactData as any);
|
||||
vi.mocked(getPersonSegmentIds).mockResolvedValue(["segment1"]);
|
||||
|
||||
const result = await getUserState({
|
||||
environmentId: mockEnvironmentId,
|
||||
userId: mockUserId,
|
||||
contactId: mockContactId,
|
||||
device: mockDevice,
|
||||
});
|
||||
|
||||
expect(prisma.contact.findUniqueOrThrow).toHaveBeenCalledWith({
|
||||
where: { id: mockContactId },
|
||||
select: {
|
||||
id: true,
|
||||
responses: {
|
||||
select: { surveyId: true },
|
||||
},
|
||||
displays: {
|
||||
select: { surveyId: true, createdAt: true },
|
||||
orderBy: { createdAt: "desc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(getPersonSegmentIds).toHaveBeenCalledWith(
|
||||
mockEnvironmentId,
|
||||
mockContactId,
|
||||
mockUserId,
|
||||
mockDevice
|
||||
);
|
||||
expect(result).toEqual<TJsPersonState["data"]>({
|
||||
contactId: mockContactId,
|
||||
userId: mockUserId,
|
||||
segments: ["segment1"],
|
||||
displays: [],
|
||||
responses: [],
|
||||
lastDisplayAt: null,
|
||||
});
|
||||
});
|
||||
|
||||
test("should return user state with responses and displays, and sort displays by createdAt", async () => {
|
||||
const mockDate1 = new Date("2023-01-01T00:00:00.000Z");
|
||||
const mockDate2 = new Date("2023-01-02T00:00:00.000Z");
|
||||
|
||||
const mockContactData = {
|
||||
id: mockContactId,
|
||||
responses: [{ surveyId: "survey1" }, { surveyId: "survey2" }],
|
||||
displays: [
|
||||
{ surveyId: "survey4", createdAt: mockDate2 }, // most recent (already sorted by desc)
|
||||
{ surveyId: "survey3", createdAt: mockDate1 },
|
||||
],
|
||||
};
|
||||
vi.mocked(prisma.contact.findUniqueOrThrow).mockResolvedValue(mockContactData as any);
|
||||
vi.mocked(getPersonSegmentIds).mockResolvedValue(["segment2", "segment3"]);
|
||||
|
||||
const result = await getUserState({
|
||||
environmentId: mockEnvironmentId,
|
||||
userId: mockUserId,
|
||||
contactId: mockContactId,
|
||||
device: mockDevice,
|
||||
});
|
||||
|
||||
expect(result).toEqual<TJsPersonState["data"]>({
|
||||
contactId: mockContactId,
|
||||
userId: mockUserId,
|
||||
segments: ["segment2", "segment3"],
|
||||
displays: [
|
||||
{ surveyId: "survey4", createdAt: mockDate2 },
|
||||
{ surveyId: "survey3", createdAt: mockDate1 },
|
||||
],
|
||||
responses: ["survey1", "survey2"],
|
||||
lastDisplayAt: mockDate2,
|
||||
});
|
||||
});
|
||||
|
||||
test("should handle empty arrays from prisma", async () => {
|
||||
// This case tests with proper empty arrays instead of null
|
||||
const mockContactData = {
|
||||
id: mockContactId,
|
||||
responses: [],
|
||||
displays: [],
|
||||
};
|
||||
vi.mocked(prisma.contact.findUniqueOrThrow).mockResolvedValue(mockContactData as any);
|
||||
vi.mocked(getPersonSegmentIds).mockResolvedValue([]);
|
||||
|
||||
const result = await getUserState({
|
||||
environmentId: mockEnvironmentId,
|
||||
userId: mockUserId,
|
||||
contactId: mockContactId,
|
||||
device: mockDevice,
|
||||
});
|
||||
|
||||
expect(result).toEqual<TJsPersonState["data"]>({
|
||||
contactId: mockContactId,
|
||||
userId: mockUserId,
|
||||
segments: [],
|
||||
displays: [],
|
||||
responses: [],
|
||||
lastDisplayAt: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,80 +0,0 @@
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { TJsPersonState } from "@formbricks/types/js";
|
||||
import { getPersonSegmentIds } from "./segments";
|
||||
|
||||
/**
|
||||
* Optimized single query to get all user state data
|
||||
* Replaces multiple separate queries with one efficient query
|
||||
*/
|
||||
const getUserStateDataOptimized = async (contactId: string) => {
|
||||
return prisma.contact.findUniqueOrThrow({
|
||||
where: { id: contactId },
|
||||
select: {
|
||||
id: true,
|
||||
responses: {
|
||||
select: { surveyId: true },
|
||||
},
|
||||
displays: {
|
||||
select: {
|
||||
surveyId: true,
|
||||
createdAt: true,
|
||||
},
|
||||
orderBy: { createdAt: "desc" },
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Optimized user state fetcher without caching
|
||||
* Uses single database query and efficient data processing
|
||||
* NO CACHING - user state changes frequently with contact updates
|
||||
*
|
||||
* @param environmentId - The environment id
|
||||
* @param userId - The user id
|
||||
* @param device - The device type
|
||||
* @returns The person state
|
||||
* @throws {ValidationError} - If the input is invalid
|
||||
* @throws {ResourceNotFoundError} - If the environment or organization is not found
|
||||
*/
|
||||
export const getUserState = async ({
|
||||
environmentId,
|
||||
userId,
|
||||
contactId,
|
||||
device,
|
||||
}: {
|
||||
environmentId: string;
|
||||
userId: string;
|
||||
contactId: string;
|
||||
device: "phone" | "desktop";
|
||||
}): Promise<TJsPersonState["data"]> => {
|
||||
// Single optimized query for all contact data
|
||||
const contactData = await getUserStateDataOptimized(contactId);
|
||||
|
||||
// Get segments using Prisma-based evaluation (no attributes needed - fetched from DB)
|
||||
const segments = await getPersonSegmentIds(environmentId, contactId, userId, device);
|
||||
|
||||
// Process displays efficiently
|
||||
const displays = (contactData.displays ?? []).map((display) => ({
|
||||
surveyId: display.surveyId,
|
||||
createdAt: display.createdAt,
|
||||
}));
|
||||
|
||||
// Get latest display date
|
||||
const lastDisplayAt =
|
||||
contactData.displays && contactData.displays.length > 0 ? contactData.displays[0].createdAt : null;
|
||||
|
||||
// Process responses efficiently
|
||||
const responses = (contactData.responses ?? []).map((response) => response.surveyId);
|
||||
|
||||
const userState: TJsPersonState["data"] = {
|
||||
contactId,
|
||||
userId,
|
||||
segments,
|
||||
displays,
|
||||
responses,
|
||||
lastDisplayAt,
|
||||
};
|
||||
|
||||
return userState;
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getLocale } from "@/lingodotdev/language";
|
||||
import { getTranslate } from "@/lingodotdev/server";
|
||||
import { ContactsPageLayout } from "@/modules/ee/contacts/components/contacts-page-layout";
|
||||
import { getContactAttributeKeys } from "@/modules/ee/contacts/lib/contact-attribute-keys";
|
||||
import { getIsContactsEnabled } from "@/modules/ee/license-check/lib/utils";
|
||||
@@ -13,7 +14,7 @@ export const AttributesPage = async ({
|
||||
}) => {
|
||||
const params = await paramsProps;
|
||||
const locale = await getLocale();
|
||||
|
||||
const t = await getTranslate();
|
||||
const [{ isReadOnly }, contactAttributeKeys] = await Promise.all([
|
||||
getEnvironmentAuth(params.environmentId),
|
||||
getContactAttributeKeys(params.environmentId),
|
||||
@@ -23,7 +24,7 @@ export const AttributesPage = async ({
|
||||
|
||||
return (
|
||||
<ContactsPageLayout
|
||||
pageTitle="Contacts"
|
||||
pageTitle={t("common.contacts")}
|
||||
activeId="attributes"
|
||||
environmentId={params.environmentId}
|
||||
isContactsEnabled={isContactsEnabled}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { TFunction } from "i18next";
|
||||
import { formatAttributeValue } from "@/modules/ee/contacts/lib/format-attribute-value";
|
||||
import { getSelectionColumn } from "@/modules/ui/components/data-table";
|
||||
import { HighlightedText } from "@/modules/ui/components/highlighted-text";
|
||||
@@ -10,12 +11,13 @@ import { TContactTableData } from "../types/contact";
|
||||
export const generateContactTableColumns = (
|
||||
searchValue: string,
|
||||
data: TContactTableData[],
|
||||
isReadOnly: boolean
|
||||
isReadOnly: boolean,
|
||||
t: TFunction
|
||||
): ColumnDef<TContactTableData>[] => {
|
||||
const userColumn: ColumnDef<TContactTableData> = {
|
||||
id: "contactsTableUser",
|
||||
accessorKey: "contactsTableUser",
|
||||
header: "ID",
|
||||
header: t("common.id"),
|
||||
cell: ({ row }) => {
|
||||
const contactId = row.original.id;
|
||||
return <HighlightedText value={contactId} searchValue={searchValue} />;
|
||||
@@ -25,7 +27,7 @@ export const generateContactTableColumns = (
|
||||
const userIdColumn: ColumnDef<TContactTableData> = {
|
||||
id: "userId",
|
||||
accessorKey: "userId",
|
||||
header: "User ID",
|
||||
header: t("common.user_id"),
|
||||
cell: ({ row }) => {
|
||||
const userId = row.original.userId;
|
||||
return <IdBadge id={userId} />;
|
||||
@@ -35,7 +37,7 @@ export const generateContactTableColumns = (
|
||||
const emailColumn: ColumnDef<TContactTableData> = {
|
||||
id: "email",
|
||||
accessorKey: "email",
|
||||
header: "Email",
|
||||
header: t("common.email"),
|
||||
cell: ({ row }) => {
|
||||
const email = row.original.email;
|
||||
if (email) {
|
||||
@@ -47,7 +49,7 @@ export const generateContactTableColumns = (
|
||||
const firstNameColumn: ColumnDef<TContactTableData> = {
|
||||
id: "firstName",
|
||||
accessorKey: "firstName",
|
||||
header: "First Name",
|
||||
header: t("common.first_name"),
|
||||
cell: ({ row }) => {
|
||||
const firstName = row.original.firstName;
|
||||
return <HighlightedText value={firstName} searchValue={searchValue} />;
|
||||
@@ -57,7 +59,7 @@ export const generateContactTableColumns = (
|
||||
const lastNameColumn: ColumnDef<TContactTableData> = {
|
||||
id: "lastName",
|
||||
accessorKey: "lastName",
|
||||
header: "Last Name",
|
||||
header: t("common.last_name"),
|
||||
cell: ({ row }) => {
|
||||
const lastName = row.original.lastName;
|
||||
return <HighlightedText value={lastName} searchValue={searchValue} />;
|
||||
|
||||
@@ -71,7 +71,7 @@ export const ContactsTable = ({
|
||||
|
||||
// Generate columns
|
||||
const columns = useMemo(() => {
|
||||
return generateContactTableColumns(searchValue, data, isReadOnly);
|
||||
return generateContactTableColumns(searchValue, data, isReadOnly, t);
|
||||
}, [searchValue, data, isReadOnly]);
|
||||
|
||||
// Load saved settings from localStorage
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { format, formatDistanceToNow } from "date-fns";
|
||||
import { TFunction } from "i18next";
|
||||
import { UsersIcon } from "lucide-react";
|
||||
import { TSegmentWithSurveyNames } from "@formbricks/types/segment";
|
||||
|
||||
export const generateSegmentTableColumns = (): ColumnDef<TSegmentWithSurveyNames>[] => {
|
||||
export const generateSegmentTableColumns = (t: TFunction): ColumnDef<TSegmentWithSurveyNames>[] => {
|
||||
const titleColumn: ColumnDef<TSegmentWithSurveyNames> = {
|
||||
id: "title",
|
||||
accessorKey: "title",
|
||||
header: "Title",
|
||||
header: t("common.title"),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex items-center gap-4">
|
||||
@@ -30,7 +31,7 @@ export const generateSegmentTableColumns = (): ColumnDef<TSegmentWithSurveyNames
|
||||
const updatedAtColumn: ColumnDef<TSegmentWithSurveyNames> = {
|
||||
id: "updatedAt",
|
||||
accessorKey: "updatedAt",
|
||||
header: "Updated",
|
||||
header: t("common.updated_at"),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<span className="text-sm text-slate-900">
|
||||
@@ -43,7 +44,7 @@ export const generateSegmentTableColumns = (): ColumnDef<TSegmentWithSurveyNames
|
||||
const createdAtColumn: ColumnDef<TSegmentWithSurveyNames> = {
|
||||
id: "createdAt",
|
||||
accessorKey: "createdAt",
|
||||
header: "Created",
|
||||
header: t("common.created_at"),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<span className="text-sm text-slate-900">{format(row.original.createdAt, "do 'of' MMMM, yyyy")}</span>
|
||||
|
||||
@@ -26,7 +26,7 @@ export function SegmentTable({
|
||||
const [editingSegment, setEditingSegment] = useState<TSegmentWithSurveyNames | null>(null);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return generateSegmentTableColumns();
|
||||
return generateSegmentTableColumns(t);
|
||||
}, []);
|
||||
|
||||
const table = useReactTable({
|
||||
|
||||
@@ -32,7 +32,7 @@ export const SegmentsPage = async ({
|
||||
|
||||
return (
|
||||
<ContactsPageLayout
|
||||
pageTitle="Contacts"
|
||||
pageTitle={t("common.contacts")}
|
||||
activeId="segments"
|
||||
environmentId={params.environmentId}
|
||||
isContactsEnabled={isContactsEnabled}
|
||||
|
||||
@@ -274,9 +274,7 @@ export const ThemeStyling = ({
|
||||
survey={previewSurvey(project.name, t)}
|
||||
project={{
|
||||
...project,
|
||||
styling: form.watch("allowStyleOverwrite")
|
||||
? { ...form.watch(), brandColor: { light: previewBrandColor } }
|
||||
: STYLE_DEFAULTS,
|
||||
styling: { ...form.watch(), brandColor: { light: previewBrandColor } },
|
||||
}}
|
||||
previewType={previewSurveyType}
|
||||
setPreviewType={setPreviewSurveyType}
|
||||
|
||||
@@ -54,9 +54,9 @@
|
||||
"@opentelemetry/sdk-node": "0.211.0",
|
||||
"@opentelemetry/sdk-trace-base": "2.5.0",
|
||||
"@opentelemetry/semantic-conventions": "1.38.0",
|
||||
"@prisma/instrumentation": "6.14.0",
|
||||
"@paralleldrive/cuid2": "2.2.2",
|
||||
"@prisma/client": "6.14.0",
|
||||
"@prisma/instrumentation": "6.14.0",
|
||||
"@radix-ui/react-accordion": "1.2.10",
|
||||
"@radix-ui/react-checkbox": "1.3.1",
|
||||
"@radix-ui/react-collapsible": "1.1.10",
|
||||
@@ -114,10 +114,12 @@
|
||||
"prismjs": "1.30.0",
|
||||
"qr-code-styling": "1.9.2",
|
||||
"qrcode": "1.5.4",
|
||||
"react": "19.2.3",
|
||||
"react-calendar": "5.1.0",
|
||||
"react-colorful": "5.6.1",
|
||||
"react-confetti": "6.4.0",
|
||||
"react-day-picker": "9.6.7",
|
||||
"react-dom": "19.2.3",
|
||||
"react-hook-form": "7.56.2",
|
||||
"react-hot-toast": "2.5.2",
|
||||
"react-i18next": "15.7.3",
|
||||
@@ -135,10 +137,8 @@
|
||||
"uuid": "11.1.0",
|
||||
"webpack": "5.99.8",
|
||||
"xlsx": "file:vendor/xlsx-0.20.3.tgz",
|
||||
"zod": "3.24.4",
|
||||
"zod-openapi": "4.2.4",
|
||||
"react": "19.2.3",
|
||||
"react-dom": "19.2.3"
|
||||
"zod": "3.25.76",
|
||||
"zod-openapi": "4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
|
||||
+6
-2
@@ -99,7 +99,7 @@
|
||||
"xm-and-surveys/surveys/link-surveys/personal-links",
|
||||
"xm-and-surveys/surveys/link-surveys/single-use-links",
|
||||
"xm-and-surveys/surveys/link-surveys/source-tracking",
|
||||
"xm-and-surveys/surveys/link-surveys/start-at-question",
|
||||
"xm-and-surveys/surveys/link-surveys/start-at-block",
|
||||
"xm-and-surveys/surveys/link-surveys/verify-email-before-survey",
|
||||
"xm-and-surveys/surveys/link-surveys/market-research-panel",
|
||||
"xm-and-surveys/surveys/link-surveys/pin-protected-surveys",
|
||||
@@ -516,9 +516,13 @@
|
||||
"source": "/docs/link-surveys/global/schedule-start-end-dates"
|
||||
},
|
||||
{
|
||||
"destination": "/docs/xm-and-surveys/surveys/link-surveys/start-at-question",
|
||||
"destination": "/docs/xm-and-surveys/surveys/link-surveys/start-at-block",
|
||||
"source": "/docs/link-surveys/start-at-question"
|
||||
},
|
||||
{
|
||||
"destination": "/docs/xm-and-surveys/surveys/link-surveys/start-at-block",
|
||||
"source": "/docs/xm-and-surveys/surveys/link-surveys/start-at-question"
|
||||
},
|
||||
{
|
||||
"destination": "/docs/xm-and-surveys/surveys/general-features/metadata",
|
||||
"source": "/docs/link-surveys/global/metadata"
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: "Start At Specific Block"
|
||||
description:
|
||||
"Start a survey at a specific block using the URL to skip earlier blocks."
|
||||
icon: "arrow-right"
|
||||
---
|
||||
|
||||
The `startAt` URL parameter lets you open a Link Survey at a specific **block** instead of from the beginning. This is useful when you want to link to a specific part of the survey from external sources or reuse the same survey at different points in the user journey.
|
||||
|
||||
## How it works
|
||||
|
||||
The survey navigation is block-based: each block can contain one or more questions. When you pass `startAt` with a Question ID, the survey finds the **block** that contains that question and opens at that block. All questions in that block are shown together.
|
||||
|
||||
<Note>
|
||||
**Multi-question blocks:** When a block has multiple questions, `startAt` opens at the block—you will see all questions in that block, not only the question whose ID you used. For precise "start at this exact question" behavior, use one question per block.
|
||||
</Note>
|
||||
|
||||
## How to use it
|
||||
|
||||
1. In the Survey Editor, open the Questions Tab and ensure the survey is set as a **Link Survey**.
|
||||
|
||||
2. Find the question (or block) you want to start at, click on **Show Advanced Settings**, and copy the **Question ID** of any question in that block.
|
||||
|
||||
<Note>
|
||||
Each question has a unique Question ID. Since `startAt` resolves to the block containing the question, you can use any question ID from the target block—typically the first question in that block.
|
||||
</Note>
|
||||
|
||||
3. Append `?startAt=question_id` to your survey's URL, replacing `question_id` with the copied Question ID.
|
||||
|
||||
4. Share this modified URL with your users to start the survey at the specified block.
|
||||
|
||||
### Sample Link Survey URL with `startAt`
|
||||
|
||||
```sh Example Link Survey URL with startAt configured
|
||||
https://formbricks.com/clny997dj087ho30fdzyf4nkl?startAt=bqd29m94l9k0hnc3azbrexl8
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
- **Link to a specific block from an external source:** Direct users to a specific block in your survey from emails, chatbots, or web pages.
|
||||
- **Use the same survey in different parts of the user journey:** Reuse the survey at different stages, starting at different blocks to gather insights.
|
||||
- **Create a personalized survey experience:** Tailor the survey by starting at a particular block based on the user's past interactions or preferences.
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
title: "Start At Specific Question"
|
||||
description:
|
||||
"Start a survey at a specific question using the URL to skip the initial questions."
|
||||
icon: "arrow-right"
|
||||
---
|
||||
|
||||
You can start a survey at a specific question from the survey using the URL to skip the initial questions. This is useful when you want to link to a specific question from an external source or want to use the same survey in different parts of the user journey.
|
||||
|
||||
## How to Use it?
|
||||
|
||||
1. In the Survey Editor, open the Questions Tab and ensure the survey is set as a **Link Survey**.
|
||||
|
||||
2. Find the question you want to start at, click on **Show Advanced Settings**, and copy the **Question ID**.
|
||||
|
||||
<Note>
|
||||
Each question has a unique Question ID, which is used to identify it in the
|
||||
survey. You can use different Question IDs for multiple **startAt** points in
|
||||
the URL.
|
||||
</Note>
|
||||
|
||||
3. Append `?startAt=question_id` to your survey's URL, replacing `question_id` with the copied Question ID.
|
||||
|
||||
4. Share this modified URL with your users to start the survey at the specified question.
|
||||
|
||||
### Sample Link Survey URL with `startAt`
|
||||
|
||||
```sh Example Link Survey URL with startAt configured
|
||||
https://formbricks.com/clny997dj087ho30fdzyf4nkl?startAt=bqd29m94l9k0hnc3azbrexl8
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Link to a specific question from an external source:** Use this feature to direct users to a specific question in your survey from emails, chatbots, or web pages, providing a seamless experience.
|
||||
- **Use the same survey in different parts of the user journey:** Employ the same survey at various stages of the user journey, starting at different questions to gather comprehensive insights.
|
||||
- **Create a personalized survey experience:** Tailor the survey experience by starting at a particular question based on the user's past interactions or preferences, enhancing engagement.
|
||||
Vendored
+1
-1
@@ -39,7 +39,7 @@
|
||||
"dependencies": {
|
||||
"@formbricks/logger": "workspace:*",
|
||||
"redis": "5.8.1",
|
||||
"zod": "3.24.4"
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formbricks/config-typescript": "workspace:*",
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"@prisma/client": "6.14.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"uuid": "11.1.0",
|
||||
"zod": "3.24.4",
|
||||
"zod": "3.25.76",
|
||||
"zod-openapi": "4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
},
|
||||
"author": "Formbricks <hola@formbricks.com>",
|
||||
"dependencies": {
|
||||
"zod": "3.24.4",
|
||||
"zod": "3.25.76",
|
||||
"pino": "10.0.0",
|
||||
"pino-opentelemetry-transport": "2.0.0",
|
||||
"pino-pretty": "13.1.1"
|
||||
|
||||
@@ -80,7 +80,7 @@ describe("service.ts", () => {
|
||||
Key: "uploads/images/test-file.jpg",
|
||||
Fields: {
|
||||
"Content-Type": "image/jpeg",
|
||||
// "Content-Encoding": "base64",
|
||||
success_action_status: "201",
|
||||
},
|
||||
Conditions: [["content-length-range", 0, mockMaxSize]],
|
||||
});
|
||||
@@ -175,7 +175,7 @@ describe("service.ts", () => {
|
||||
Key: "uploads/images/test-file.jpg",
|
||||
Fields: {
|
||||
"Content-Type": "image/jpeg",
|
||||
// "Content-Encoding": "base64",
|
||||
success_action_status: "201",
|
||||
},
|
||||
Conditions: [["content-length-range", 0, mockMaxSize]],
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ export const getSignedUploadUrl = async (
|
||||
Key: `${filePath}/${fileName}`,
|
||||
Fields: {
|
||||
"Content-Type": contentType,
|
||||
// "Content-Encoding": "base64",
|
||||
success_action_status: "201",
|
||||
},
|
||||
Conditions: postConditions,
|
||||
});
|
||||
|
||||
@@ -141,18 +141,18 @@ function DropdownVariant({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<ElementError errorMessage={errorMessage} dir={dir} />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={disabled}
|
||||
className="rounded-input bg-option-bg rounded-option border-option-border h-input my-0 w-full justify-between border"
|
||||
className="rounded-input min-h-input bg-input-bg border-input-border text-input-text py-input-y px-input-x w-full justify-between"
|
||||
aria-invalid={Boolean(errorMessage)}
|
||||
aria-label={headline}>
|
||||
<span className="font-input font-input-weight text-input-text truncate">{displayText}</span>
|
||||
<ChevronDown className="label-headline ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
@@ -235,7 +235,7 @@ function DropdownVariant({
|
||||
disabled={disabled}
|
||||
aria-required={required}
|
||||
dir={dir}
|
||||
className="w-full"
|
||||
className="mt-2 w-full"
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -151,7 +151,7 @@ function SingleSelect({
|
||||
/>
|
||||
|
||||
{/* Options */}
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
{variant === "dropdown" ? (
|
||||
<>
|
||||
<ElementError errorMessage={errorMessage} dir={dir} />
|
||||
@@ -160,11 +160,11 @@ function SingleSelect({
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={disabled}
|
||||
className="rounded-input bg-option-bg rounded-option border-option-border h-input my-0 w-full justify-between border"
|
||||
className="rounded-input min-h-input bg-input-bg border-input-border text-input-text py-input-y px-input-x w-full justify-between"
|
||||
aria-invalid={Boolean(errorMessage)}
|
||||
aria-label={headline}>
|
||||
<span className="font-input font-input-weight text-input-text truncate">{displayText}</span>
|
||||
<ChevronDown className="label-headline ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
@@ -226,7 +226,7 @@ function SingleSelect({
|
||||
placeholder={otherOptionPlaceholder}
|
||||
disabled={disabled}
|
||||
dir={dir}
|
||||
className="w-full"
|
||||
className="mt-2 w-full"
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "6.14.0",
|
||||
"zod": "3.24.4",
|
||||
"zod": "3.25.76",
|
||||
"zod-openapi": "4.2.4",
|
||||
"node-html-parser": "7.0.1"
|
||||
},
|
||||
|
||||
Generated
+40
-42
@@ -28,7 +28,7 @@ importers:
|
||||
dependencies:
|
||||
next:
|
||||
specifier: 16.1.6
|
||||
version: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
react:
|
||||
specifier: 19.2.3
|
||||
version: 19.2.3
|
||||
@@ -294,10 +294,10 @@ importers:
|
||||
version: 1.2.6(@types/react-dom@19.2.1(@types/react@19.2.1))(@types/react@19.2.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
'@sentry/nextjs':
|
||||
specifier: 10.5.0
|
||||
version: 10.5.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.99.8(esbuild@0.25.12))
|
||||
version: 10.5.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.99.8(esbuild@0.25.12))
|
||||
'@t3-oss/env-nextjs':
|
||||
specifier: 0.13.4
|
||||
version: 0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.24.4)
|
||||
version: 0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.25.76)
|
||||
'@tailwindcss/forms':
|
||||
specifier: 0.5.10
|
||||
version: 0.5.10(tailwindcss@3.4.17(ts-node@10.9.2(@types/node@22.15.18)(typescript@5.8.3)))
|
||||
@@ -384,13 +384,13 @@ importers:
|
||||
version: 3.0.1
|
||||
next:
|
||||
specifier: 16.1.6
|
||||
version: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
version: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
next-auth:
|
||||
specifier: 4.24.12
|
||||
version: 4.24.12(patch_hash=7ac5717a8d7d2049442182b5d83ab492d33fe774ff51ff5ea3884628b77df87b)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(nodemailer@7.0.11)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
version: 4.24.12(patch_hash=7ac5717a8d7d2049442182b5d83ab492d33fe774ff51ff5ea3884628b77df87b)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(nodemailer@7.0.11)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
next-safe-action:
|
||||
specifier: 7.10.8
|
||||
version: 7.10.8(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.24.4)
|
||||
version: 7.10.8(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76)
|
||||
node-fetch:
|
||||
specifier: 3.3.2
|
||||
version: 3.3.2
|
||||
@@ -482,11 +482,11 @@ importers:
|
||||
specifier: file:vendor/xlsx-0.20.3.tgz
|
||||
version: file:apps/web/vendor/xlsx-0.20.3.tgz
|
||||
zod:
|
||||
specifier: 3.24.4
|
||||
version: 3.24.4
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
zod-openapi:
|
||||
specifier: 4.2.4
|
||||
version: 4.2.4(zod@3.24.4)
|
||||
version: 4.2.4(zod@3.25.76)
|
||||
devDependencies:
|
||||
'@formbricks/config-typescript':
|
||||
specifier: workspace:*
|
||||
@@ -582,8 +582,8 @@ importers:
|
||||
specifier: 5.8.1
|
||||
version: 5.8.1
|
||||
zod:
|
||||
specifier: 3.24.4
|
||||
version: 3.24.4
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@formbricks/config-typescript':
|
||||
specifier: workspace:*
|
||||
@@ -685,11 +685,11 @@ importers:
|
||||
specifier: 11.1.0
|
||||
version: 11.1.0
|
||||
zod:
|
||||
specifier: 3.24.4
|
||||
version: 3.24.4
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
zod-openapi:
|
||||
specifier: 4.2.4
|
||||
version: 4.2.4(zod@3.24.4)
|
||||
version: 4.2.4(zod@3.25.76)
|
||||
devDependencies:
|
||||
'@formbricks/config-typescript':
|
||||
specifier: workspace:*
|
||||
@@ -829,8 +829,8 @@ importers:
|
||||
specifier: 13.1.1
|
||||
version: 13.1.1
|
||||
zod:
|
||||
specifier: 3.24.4
|
||||
version: 3.24.4
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@formbricks/config-typescript':
|
||||
specifier: workspace:*
|
||||
@@ -1067,11 +1067,11 @@ importers:
|
||||
specifier: 7.0.1
|
||||
version: 7.0.1
|
||||
zod:
|
||||
specifier: 3.24.4
|
||||
version: 3.24.4
|
||||
specifier: 3.25.76
|
||||
version: 3.25.76
|
||||
zod-openapi:
|
||||
specifier: 4.2.4
|
||||
version: 4.2.4(zod@3.24.4)
|
||||
version: 4.2.4(zod@3.25.76)
|
||||
devDependencies:
|
||||
'@formbricks/config-typescript':
|
||||
specifier: workspace:*
|
||||
@@ -11758,8 +11758,8 @@ packages:
|
||||
peerDependencies:
|
||||
zod: ^3.21.4
|
||||
|
||||
zod@3.24.4:
|
||||
resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==}
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
snapshots:
|
||||
|
||||
@@ -17049,7 +17049,7 @@ snapshots:
|
||||
|
||||
'@sentry/core@10.5.0': {}
|
||||
|
||||
'@sentry/nextjs@10.5.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.99.8(esbuild@0.25.12))':
|
||||
'@sentry/nextjs@10.5.0(@opentelemetry/context-async-hooks@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.0(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(webpack@5.99.8(esbuild@0.25.12))':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/semantic-conventions': 1.38.0
|
||||
@@ -17062,7 +17062,7 @@ snapshots:
|
||||
'@sentry/vercel-edge': 10.5.0
|
||||
'@sentry/webpack-plugin': 4.6.1(encoding@0.1.13)(webpack@5.99.8(esbuild@0.25.12))
|
||||
chalk: 3.0.0
|
||||
next: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
next: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
resolve: 1.22.8
|
||||
rollup: 4.54.0
|
||||
stacktrace-parser: 0.1.11
|
||||
@@ -17928,19 +17928,19 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@t3-oss/env-core@0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.24.4)':
|
||||
'@t3-oss/env-core@0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.25.76)':
|
||||
dependencies:
|
||||
arktype: 2.1.29
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
zod: 3.24.4
|
||||
zod: 3.25.76
|
||||
|
||||
'@t3-oss/env-nextjs@0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.24.4)':
|
||||
'@t3-oss/env-nextjs@0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.25.76)':
|
||||
dependencies:
|
||||
'@t3-oss/env-core': 0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.24.4)
|
||||
'@t3-oss/env-core': 0.13.4(arktype@2.1.29)(typescript@5.8.3)(zod@3.25.76)
|
||||
optionalDependencies:
|
||||
typescript: 5.8.3
|
||||
zod: 3.24.4
|
||||
zod: 3.25.76
|
||||
transitivePeerDependencies:
|
||||
- arktype
|
||||
|
||||
@@ -22186,13 +22186,13 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
next-auth@4.24.12(patch_hash=7ac5717a8d7d2049442182b5d83ab492d33fe774ff51ff5ea3884628b77df87b)(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(nodemailer@7.0.11)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
next-auth@4.24.12(patch_hash=7ac5717a8d7d2049442182b5d83ab492d33fe774ff51ff5ea3884628b77df87b)(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(nodemailer@7.0.11)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
'@panva/hkdf': 1.2.1
|
||||
cookie: 0.7.2
|
||||
jose: 4.15.9
|
||||
next: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
next: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
oauth: 0.9.15
|
||||
openid-client: 5.7.1
|
||||
preact: 10.28.2
|
||||
@@ -22203,13 +22203,13 @@ snapshots:
|
||||
optionalDependencies:
|
||||
nodemailer: 7.0.11
|
||||
|
||||
next-safe-action@7.10.8(next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.24.4):
|
||||
next-safe-action@7.10.8(next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(zod@3.25.76):
|
||||
dependencies:
|
||||
next: 16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
next: 16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
optionalDependencies:
|
||||
zod: 3.24.4
|
||||
zod: 3.25.76
|
||||
|
||||
next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
@@ -22219,7 +22219,7 @@ snapshots:
|
||||
postcss: 8.4.31
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.3)
|
||||
styled-jsx: 5.1.6(react@19.2.3)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 16.0.10
|
||||
'@next/swc-darwin-x64': 16.0.10
|
||||
@@ -22236,7 +22236,7 @@ snapshots:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
||||
next@16.1.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
next@16.1.6(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
|
||||
dependencies:
|
||||
'@next/env': 16.1.6
|
||||
'@swc/helpers': 0.5.15
|
||||
@@ -22245,7 +22245,7 @@ snapshots:
|
||||
postcss: 8.4.31
|
||||
react: 19.2.3
|
||||
react-dom: 19.2.3(react@19.2.3)
|
||||
styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.3)
|
||||
styled-jsx: 5.1.6(react@19.2.3)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 16.1.6
|
||||
'@next/swc-darwin-x64': 16.1.6
|
||||
@@ -23991,12 +23991,10 @@ snapshots:
|
||||
|
||||
stubborn-utils@1.0.2: {}
|
||||
|
||||
styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.3):
|
||||
styled-jsx@5.1.6(react@19.2.3):
|
||||
dependencies:
|
||||
client-only: 0.0.1
|
||||
react: 19.2.3
|
||||
optionalDependencies:
|
||||
'@babel/core': 7.28.5
|
||||
|
||||
stylis@4.3.6: {}
|
||||
|
||||
@@ -25089,8 +25087,8 @@ snapshots:
|
||||
|
||||
yoga-layout@3.2.1: {}
|
||||
|
||||
zod-openapi@4.2.4(zod@3.24.4):
|
||||
zod-openapi@4.2.4(zod@3.25.76):
|
||||
dependencies:
|
||||
zod: 3.24.4
|
||||
zod: 3.25.76
|
||||
|
||||
zod@3.24.4: {}
|
||||
zod@3.25.76: {}
|
||||
|
||||
Reference in New Issue
Block a user