diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/LinkTab.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/LinkTab.tsx index 14725110de..eee5232091 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/LinkTab.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/shareEmbedModal/LinkTab.tsx @@ -16,6 +16,7 @@ interface LinkTabProps { export const LinkTab = ({ survey, webAppUrl, surveyUrl, setSurveyUrl, locale }: LinkTabProps) => { const { t } = useTranslate(); + const docsLinks = [ { title: t("environments.surveys.summary.data_prefilling"), @@ -48,6 +49,7 @@ export const LinkTab = ({ survey, webAppUrl, surveyUrl, setSurveyUrl, locale }: locale={locale} /> +

{t("environments.surveys.summary.you_can_do_a_lot_more_with_links_surveys")} 💡 diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/get-qr-code-options.ts b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/get-qr-code-options.ts new file mode 100644 index 0000000000..300c58c271 --- /dev/null +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/get-qr-code-options.ts @@ -0,0 +1,36 @@ +import { Options } from "qr-code-styling"; + +export const getQRCodeOptions = (width: number, height: number): Options => ({ + width, + height, + type: "svg", + data: "", + margin: 0, + qrOptions: { + typeNumber: 0, + mode: "Byte", + errorCorrectionLevel: "L", + }, + imageOptions: { + saveAsBlob: true, + hideBackgroundDots: false, + imageSize: 0, + margin: 0, + }, + dotsOptions: { + type: "extra-rounded", + color: "#000000", + roundSize: true, + }, + backgroundOptions: { + color: "#ffffff", + }, + cornersSquareOptions: { + type: "dot", + color: "#000000", + }, + cornersDotOptions: { + type: "dot", + color: "#000000", + }, +}); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/survey-qr-code.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/survey-qr-code.tsx new file mode 100644 index 0000000000..2a74b21961 --- /dev/null +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/survey-qr-code.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { getQRCodeOptions } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/get-qr-code-options"; +import { useTranslate } from "@tolgee/react"; +import QRCodeStyling from "qr-code-styling"; +import { useEffect, useRef } from "react"; +import { toast } from "react-hot-toast"; + +export const useSurveyQRCode = (surveyUrl: string) => { + const qrCodeRef = useRef(null); + const qrInstance = useRef(null); + const { t } = useTranslate(); + + useEffect(() => { + try { + if (!qrInstance.current) { + qrInstance.current = new QRCodeStyling(getQRCodeOptions(70, 70)); + } + + if (surveyUrl && qrInstance.current) { + qrInstance.current.update({ data: surveyUrl }); + + if (qrCodeRef.current) { + qrCodeRef.current.innerHTML = ""; + qrInstance.current.append(qrCodeRef.current); + } + } + } catch (error) { + toast.error(t("environments.surveys.summary.failed_to_generate_qr_code")); + } + }, [surveyUrl]); + + const downloadQRCode = () => { + try { + const downloadInstance = new QRCodeStyling(getQRCodeOptions(500, 500)); + downloadInstance.update({ data: surveyUrl }); + downloadInstance.download({ name: "survey-qr", extension: "png" }); + } catch (error) { + toast.error(t("environments.surveys.summary.failed_to_generate_qr_code")); + } + }; + + return { qrCodeRef, downloadQRCode }; +}; diff --git a/apps/web/modules/analysis/components/ShareSurveyLink/index.tsx b/apps/web/modules/analysis/components/ShareSurveyLink/index.tsx index 33528d9f51..70b983eb1b 100644 --- a/apps/web/modules/analysis/components/ShareSurveyLink/index.tsx +++ b/apps/web/modules/analysis/components/ShareSurveyLink/index.tsx @@ -1,10 +1,11 @@ "use client"; +import { useSurveyQRCode } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/lib/survey-qr-code"; import { getFormattedErrorMessage } from "@/lib/utils/helper"; import { generateSingleUseIdAction } from "@/modules/survey/list/actions"; import { Button } from "@/modules/ui/components/button"; import { useTranslate } from "@tolgee/react"; -import { Copy, RefreshCcw, SquareArrowOutUpRight } from "lucide-react"; +import { Copy, QrCode, RefreshCcw, SquareArrowOutUpRight } from "lucide-react"; import { useCallback, useEffect, useState } from "react"; import { toast } from "react-hot-toast"; import { TSurvey } from "@formbricks/types/surveys/types"; @@ -68,6 +69,8 @@ export const ShareSurveyLink = ({ getUrl(); }, [survey, getUrl, language]); + const { downloadQRCode } = useSurveyQRCode(surveyUrl); + return (

@@ -100,6 +103,14 @@ export const ShareSurveyLink = ({ {t("common.copy")} + {survey.singleUse?.enabled && (