Compare commits

..

12 Commits

Author SHA1 Message Date
Piyush Gupta
323d39580a fix: multi-lang 2025-02-13 11:45:00 +05:30
Piyush Gupta
bae9226326 fix: discord webhook 2025-02-13 11:33:41 +05:30
Kartik Saini
1f80f8f396 fix 2025-02-12 17:48:16 +05:30
Kartik Saini
cb120c56f6 feat: prevent users from adding a discord webhook 2025-02-12 17:42:45 +05:30
Kartik Saini
d73b497f52 Merge branch 'main' into fix/discord-webhook-not-pinging 2025-02-12 16:59:39 +05:30
Piyush Gupta
22e8a137ef fix: date question accessibility (#4698)
Co-authored-by: pandeymangg <anshuman.pandey9999@gmail.com>
Co-authored-by: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com>
2025-02-12 10:47:54 +00:00
github-actions[bot]
a9fe05d64a chore: bump version to v3.1.5 (#4729)
Co-authored-by: GitHub Actions <github-actions@github.com>
2025-02-12 09:50:13 +01:00
Yannick Torrès
5219065b8e fix: fr-FR translations (#4667)
Co-authored-by: Matti Nannt <mail@matthiasnannt.com>
Co-authored-by: Dhruwang <dhruwangjariwala18@gmail.com>
2025-02-11 05:35:34 +00:00
Kartik Saini
bb4f8f1df3 Merge branch 'main' into fix/discord-webhook-not-pinging 2025-02-05 12:54:59 +05:30
Kartik Saini
5b8c548d84 Merge branch 'main' into fix/discord-webhook-not-pinging 2025-01-29 21:53:41 +05:30
Kartik Saini
c51cbda31a fix 2025-01-27 20:12:03 +05:30
Kartik Saini
c773ddd117 fix: discord webhook not pinging 2025-01-27 19:49:34 +05:30
13 changed files with 64 additions and 29 deletions

View File

@@ -1,5 +1,5 @@
pnpm lint-staged
pnpm tolgee-pull || true
pnpm tolgee-pull || true
echo "{\"branchName\": \"main\"}" > ../branch.json
git add branch.json packages/lib/messages/*.json

View File

@@ -3,7 +3,7 @@
import { getFormattedErrorMessage } from "@/lib/utils/helper";
import { SurveyCheckboxGroup } from "@/modules/integrations/webhooks/components/survey-checkbox-group";
import { TriggerCheckboxGroup } from "@/modules/integrations/webhooks/components/trigger-checkbox-group";
import { validWebHookURL } from "@/modules/integrations/webhooks/lib/utils";
import { isDiscordWebhook, validWebHookURL } from "@/modules/integrations/webhooks/lib/utils";
import { Button } from "@/modules/ui/components/button";
import { Input } from "@/modules/ui/components/input";
import { Label } from "@/modules/ui/components/label";
@@ -53,6 +53,7 @@ export const AddWebhookModal = ({ environmentId, surveys, open, setOpen }: AddWe
}
setHittingEndpoint(true);
const testEndpointActionResult = await testEndpointAction({ url: testEndpointInput });
console.log("testEndpointActionResult", testEndpointActionResult);
if (!testEndpointActionResult?.data) {
const errorMessage = getFormattedErrorMessage(testEndpointActionResult);
throw new Error(errorMessage);
@@ -113,6 +114,10 @@ export const AddWebhookModal = ({ environmentId, surveys, open, setOpen }: AddWe
throw new Error(t("common.please_select_at_least_one_survey"));
}
if (isDiscordWebhook(testEndpointInput)) {
throw new Error(t("environments.integrations.webhooks.discord_webhook_not_supported"));
}
const endpointHitSuccessfully = await handleTestEndpoint(false);
if (!endpointHitSuccessfully) return;

View File

@@ -35,3 +35,9 @@ export const validWebHookURL = (urlInput: string) => {
return { valid: false, error: "Invalid URL format. Please enter a complete URL including https://" };
}
};
export const isDiscordWebhook = (urlString: string) => {
const url = new URL(urlString);
const DISCORD_WEBHOOK_URL_PATTERN = /^https:\/\/discord\.com\/api\/webhooks\/\d+\/.+$/;
return DISCORD_WEBHOOK_URL_PATTERN.test(url.toString());
};

View File

@@ -1,4 +1,5 @@
import { webhookCache } from "@/lib/cache/webhook";
import { isDiscordWebhook } from "@/modules/integrations/webhooks/lib/utils";
import { Prisma, Webhook } from "@prisma/client";
import { prisma } from "@formbricks/database";
import { cache } from "@formbricks/lib/cache";
@@ -70,6 +71,9 @@ export const deleteWebhook = async (id: string): Promise<boolean> => {
export const createWebhook = async (environmentId: string, webhookInput: TWebhookInput): Promise<boolean> => {
try {
if (isDiscordWebhook(webhookInput.url)) {
throw new UnknownError("Discord webhooks are currently not supported.");
}
const createdWebhook = await prisma.webhook.create({
data: {
...webhookInput,
@@ -136,6 +140,10 @@ export const testEndpoint = async (url: string): Promise<boolean> => {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
if (isDiscordWebhook(url)) {
throw new UnknownError("Discord webhooks are currently not supported.");
}
const response = await fetch(url, {
method: "POST",
body: JSON.stringify({
@@ -161,6 +169,10 @@ export const testEndpoint = async (url: string): Promise<boolean> => {
if (error.name === "AbortError") {
throw new UnknownError("Request timed out after 5 seconds");
}
if (error instanceof UnknownError) {
throw error;
}
throw new UnknownError(`Error while fetching the URL: ${error.message}`);
}
};

View File

@@ -1,6 +1,6 @@
{
"name": "@formbricks/web",
"version": "3.1.4",
"version": "3.1.5",
"private": true,
"scripts": {
"clean": "rimraf .turbo node_modules .next",

View File

@@ -748,6 +748,7 @@
"add_webhook_description": "Sende Umfragedaten an einen benutzerdefinierten Endpunkt",
"all_current_and_new_surveys": "Alle aktuellen und neuen Umfragen",
"created_by_third_party": "Erstellt von einer dritten Partei",
"discord_webhook_not_supported": "Discord-Webhooks werden derzeit nicht unterstützt.",
"empty_webhook_message": "Deine Webhooks werden hier angezeigt, sobald Du sie hinzufügst ⏲️",
"endpoint_pinged": "Juhu! Wir können den Webhook anpingen!",
"endpoint_pinged_error": "Kann den Webhook nicht anpingen!",
@@ -1245,7 +1246,7 @@
"edit": {
"1_choose_the_default_language_for_this_survey": "1. Wähle die Standardsprache für diese Umfrage:",
"2_activate_translation_for_specific_languages": "2. Übersetzung für bestimmte Sprachen aktivieren:",
"add": "Hinzufügen +",
"add": "+ hinzufügen",
"add_a_delay_or_auto_close_the_survey": "Füge eine Verzögerung hinzu oder schließe die Umfrage automatisch.",
"add_a_four_digit_pin": "Füge eine vierstellige PIN hinzu",
"add_a_new_question_to_your_survey": "Neue Frage hinzufügen",

View File

@@ -748,6 +748,7 @@
"add_webhook_description": "Send survey response data to a custom endpoint",
"all_current_and_new_surveys": "All current and new surveys",
"created_by_third_party": "Created by a Third Party",
"discord_webhook_not_supported": "Discord webhooks are currently not supported.",
"empty_webhook_message": "Your webhooks will appear here as soon as you add them. ⏲️",
"endpoint_pinged": "Yay! We are able to ping the webhook!",
"endpoint_pinged_error": "Unable to ping the webhook!",

View File

@@ -242,7 +242,7 @@
"minimum": "Min",
"mobile_overlay_text": "Formbricks n'est pas disponible pour les appareils avec des résolutions plus petites.",
"move_down": "Déplacer vers le bas",
"move_up": "Monter",
"move_up": "Déplacer vers le haut",
"multiple_languages": "Plusieurs langues",
"name": "Nom",
"negative": "Négatif",
@@ -748,6 +748,7 @@
"add_webhook_description": "Envoyer les données de réponse à l'enquête à un point de terminaison personnalisé",
"all_current_and_new_surveys": "Tous les sondages actuels et nouveaux",
"created_by_third_party": "Créé par un tiers",
"discord_webhook_not_supported": "Les webhooks Discord ne sont actuellement pas pris en charge.",
"empty_webhook_message": "Vos webhooks apparaîtront ici dès que vous les ajouterez. ⏲️",
"endpoint_pinged": "Yay ! Nous pouvons pinger le webhook !",
"endpoint_pinged_error": "Impossible de pinger le webhook !",
@@ -1276,14 +1277,14 @@
"address_fields": "Champs d'adresse",
"address_line_1": "Ligne d'adresse 1",
"address_line_2": "Ligne d'adresse 2",
"adjust_survey_closed_message": "Ajuster le message \"Sondage fermé",
"adjust_survey_closed_message": "Ajuster le message \"Sondage fermé\"",
"adjust_survey_closed_message_description": "Modifiez le message que les visiteurs voient lorsque l'enquête est fermée.",
"adjust_the_theme_in_the": "Ajustez le thème dans le",
"all_other_answers_will_continue_to": "Tous les autres réponses continueront à",
"all_other_answers_will_continue_to": "Toutes les autres réponses continueront à",
"allow_file_type": "Autoriser le type de fichier",
"allow_multi_select": "Autoriser la sélection multiple",
"allow_multiple_files": "Autoriser plusieurs fichiers",
"allow_users_to_select_more_than_one_image": "Permettre aux utilisateurs de sélectionner plus d'une image",
"allow_users_to_select_more_than_one_image": "Permettre aux utilisateurs de sélectionner plusieurs images",
"always_show_survey": "Afficher toujours l'enquête",
"and_launch_surveys_in_your_website_or_app": "et lancez des enquêtes sur votre site web ou votre application.",
"animation": "Animation",
@@ -1297,13 +1298,13 @@
"automatically_closes_the_survey_at_the_beginning_of_the_day_utc": "Ferme automatiquement l'enquête au début de la journée (UTC).",
"automatically_mark_the_survey_as_complete_after": "Marquer automatiquement l'enquête comme terminée après",
"automatically_release_the_survey_at_the_beginning_of_the_day_utc": "Libérer automatiquement l'enquête au début de la journée (UTC).",
"back_button_label": "Label du bouton 'Retour''",
"back_button_label": "Label du bouton \"Retour''",
"background_styling": "Style de fond",
"blocks_survey_if_a_submission_with_the_single_use_id_suid_exists_already": "Bloque les enquêtes si une soumission avec l'Identifiant à Usage Unique (suId) existe déjà.",
"blocks_survey_if_the_survey_url_has_no_single_use_id_suid": "Bloque les enquêtes si l'URL de l'enquête n'a pas d'Identifiant d'Utilisation Unique (suId).",
"brand_color": "Couleur de marque",
"brightness": "Luminosité",
"button_label": "Étiquette du bouton",
"button_label": "Label du bouton",
"button_to_continue_in_survey": "Bouton pour continuer dans l'enquête",
"button_to_link_to_external_url": "Bouton pour lier à une URL externe",
"button_url": "URL du bouton",
@@ -1495,7 +1496,7 @@
"max_file_size_limit_is": "La taille maximale du fichier est",
"multiply": "Multiplier *",
"needed_for_self_hosted_cal_com_instance": "Nécessaire pour une instance Cal.com auto-hébergée",
"next_button_label": "Étiquette du bouton \"Suivant",
"next_button_label": "Label du bouton \"Suivant\"",
"next_question": "Question suivante",
"no_hidden_fields_yet_add_first_one_below": "Aucun champ caché pour le moment. Ajoutez le premier ci-dessous.",
"no_images_found_for": "Aucune image trouvée pour ''{query}\"",

View File

@@ -748,6 +748,7 @@
"add_webhook_description": "Enviar dados das respostas da pesquisa para um endpoint personalizado",
"all_current_and_new_surveys": "Todas as pesquisas atuais e novas",
"created_by_third_party": "Criado por um Terceiro",
"discord_webhook_not_supported": "Webhooks do Discord não são suportados no momento.",
"empty_webhook_message": "Seus webhooks vão aparecer aqui assim que você adicioná-los. ⏲️",
"endpoint_pinged": "Uhul! Conseguimos pingar o webhook!",
"endpoint_pinged_error": "Não consegui pingar o webhook!",

View File

@@ -158,7 +158,7 @@ export function DateQuestion({
subheader={question.subheader ? getLocalizedValue(question.subheader, languageCode) : ""}
questionId={question.id}
/>
<div className="fb-text-red-600">
<div id="error-message" className="fb-text-red-600" aria-live="assertive">
<span>{errorMessage}</span>
</div>
<div
@@ -166,7 +166,7 @@ export function DateQuestion({
id="date-picker-root">
<div className="fb-relative">
{!datePickerOpen && (
<div
<button
onClick={() => {
setDatePickerOpen(true);
}}
@@ -174,6 +174,8 @@ export function DateQuestion({
onKeyDown={(e) => {
if (e.key === " ") setDatePickerOpen(true);
}}
aria-label={selectedDate ? `You have selected ${formattedDate}` : "Select a date"}
aria-describedby={errorMessage ? "error-message" : undefined}
className="focus:fb-outline-brand fb-bg-input-bg hover:fb-bg-input-bg-selected fb-border-border fb-text-heading fb-rounded-custom fb-relative fb-flex fb-h-[12dvh] fb-w-full fb-cursor-pointer fb-appearance-none fb-items-center fb-justify-center fb-border fb-text-left fb-text-base fb-font-normal">
<div className="fb-flex fb-items-center fb-gap-2">
{selectedDate ? (
@@ -186,7 +188,7 @@ export function DateQuestion({
</div>
)}
</div>
</div>
</button>
)}
<DatePicker
@@ -222,14 +224,14 @@ export function DateQuestion({
"calendar-root !fb-bg-input-bg fb-border fb-border-border fb-rounded-custom fb-p-3 fb-h-[46dvh] sm:fb-h-[33dvh] fb-overflow-auto",
tileClassName: ({ date }: { date: Date }) => {
const baseClass =
"hover:fb-bg-input-bg-selected fb-rounded-custom fb-h-9 fb-p-0 fb-mt-1 fb-font-normal fb-text-heading aria-selected:fb-opacity-100 focus:fb-ring-2 focus:fb-bg-slate-200";
"hover:fb-bg-input-bg-selected fb-rounded-custom fb-h-9 fb-p-0 fb-mt-1 fb-font-normal aria-selected:fb-opacity-100 focus:fb-ring-2 focus:fb-bg-slate-200";
// today's date class
if (
date.getDate() === new Date().getDate() &&
date.getMonth() === new Date().getMonth() &&
date.getFullYear() === new Date().getFullYear()
) {
return `${baseClass} !fb-bg-brand !fb-border-border-highlight !fb-text-heading focus:fb-ring-2 focus:fb-bg-slate-200`;
return `${baseClass} !fb-bg-brand !fb-border-border-highlight !fb-text-calendar-tile focus:fb-ring-2 focus:fb-bg-slate-200`;
}
// active date class
if (
@@ -238,10 +240,10 @@ export function DateQuestion({
date.getMonth() === selectedDate.getMonth() &&
date.getFullYear() === selectedDate.getFullYear()
) {
return `${baseClass} !fb-bg-brand !fb-border-border-highlight !fb-text-heading`;
return `${baseClass} !fb-bg-brand !fb-border-border-highlight !fb-text-calendar-tile`;
}
return baseClass;
return `${baseClass} !fb-text-heading`;
},
formatShortWeekday: (_: any, date: Date) => {
return date.toLocaleDateString("en-US", { weekday: "short" }).slice(0, 2);

View File

@@ -77,16 +77,17 @@ export const addCustomThemeToDom = ({ styling }: { styling: TProjectStyling | TS
appendCssVariable("input-background-color", styling.inputColor?.light);
if (styling.questionColor?.light) {
let signatureColor = "";
let brandingColor = "";
if (isLight(styling.questionColor.light)) {
signatureColor = mixColor(styling.questionColor.light, "#000000", 0.2);
brandingColor = mixColor(styling.questionColor.light, "#000000", 0.3);
} else {
signatureColor = mixColor(styling.questionColor.light, "#ffffff", 0.2);
brandingColor = mixColor(styling.questionColor.light, "#ffffff", 0.3);
}
const isLightQuestionColor = isLight(styling.questionColor.light);
const signatureColor = mixColor(
styling.questionColor.light,
isLightQuestionColor ? "#000000" : "#ffffff",
0.2
);
const brandingColor = mixColor(
styling.questionColor.light,
isLightQuestionColor ? "#000000" : "#ffffff",
0.3
);
appendCssVariable("signature-text-color", signatureColor);
appendCssVariable("branding-text-color", brandingColor);
@@ -115,6 +116,10 @@ export const addCustomThemeToDom = ({ styling }: { styling: TProjectStyling | TS
appendCssVariable("accent-background-color", accentColor);
appendCssVariable("accent-background-color-selected", accentColorSelected);
if (isLight(brandColor)) {
appendCssVariable("calendar-tile-color", mixColor(brandColor, "#000000", 0.7));
}
}
// Close the :root block

View File

@@ -93,7 +93,7 @@ p.fb-editor-paragraph {
--fb-rating-selected: black;
--fb-close-btn-color: var(--slate-500);
--fb-close-btn-color-hover: var(--slate-700);
--fb-calendar-tile-color: var(--slate-50);
--fb-border-radius: 8px;
}

View File

@@ -37,6 +37,7 @@ module.exports = {
"submit-button-border": "var(--fb-submit-btn-border)",
"close-button": "var(--fb-close-btn-color)",
"close-button-focus": "var(--fb-close-btn-hover-color)",
"calendar-tile": "var(--fb-calendar-tile-color)",
},
borderRadius: {
custom: "var(--fb-border-radius)",