Added single use link modal to shareEmbedSurvey modal

This commit is contained in:
Dhruwang
2023-10-07 10:28:23 +05:30
parent 334aacc00c
commit 2575b7483b
3 changed files with 105 additions and 100 deletions
@@ -7,7 +7,6 @@ import { useState } from "react";
import clsx from "clsx";
import { TProduct } from "@formbricks/types/v1/product";
import ShareEmbedSurvey from "./ShareEmbedSurvey";
import LinkSingleUseSurveyModal from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/LinkSingleUseSurveyModal";
import { TProfile } from "@formbricks/types/v1/profile";
interface LinkSurveyShareButtonProps {
@@ -16,7 +15,6 @@ interface LinkSurveyShareButtonProps {
surveyBaseUrl: string;
product: TProduct;
profile: TProfile;
singleUseIds?: string[];
}
export default function LinkSurveyShareButton({
@@ -43,7 +41,14 @@ export default function LinkSurveyShareButton({
<ShareIcon className="h-5 w-5" />
</Button>
{showLinkModal && isSingleUse ? (
<LinkSingleUseSurveyModal survey={survey} open={true} setOpen={setShowLinkModal} />
<ShareEmbedSurvey
survey={survey}
open={showLinkModal}
setOpen={setShowLinkModal}
product={product}
surveyBaseUrl={surveyBaseUrl}
profile={profile}
/>
) : (
<ShareEmbedSurvey
survey={survey}
@@ -1,9 +1,9 @@
"use client";
import { Button, Dialog, DialogContent } from "@formbricks/ui";
import { Button } from "@formbricks/ui";
import { TSurvey } from "@formbricks/types/v1/surveys";
import { ArrowPathIcon, CheckIcon } from "@heroicons/react/24/outline";
import { CheckCircleIcon, DocumentDuplicateIcon, EyeIcon } from "@heroicons/react/24/solid";
import { ArrowPathIcon } from "@heroicons/react/24/outline";
import { DocumentDuplicateIcon, EyeIcon } from "@heroicons/react/24/solid";
import { useRef, useState, useEffect } from "react";
import toast from "react-hot-toast";
import { truncateMiddle } from "@/lib/utils";
@@ -12,11 +12,10 @@ import { generateSingleUseIdAction } from "@/app/(app)/environments/[environment
interface LinkSingleUseSurveyModalProps {
survey: TSurvey;
open: boolean;
setOpen: (open: boolean) => void;
surveyBaseUrl: string;
}
export default function LinkSingleUseSurveyModal({ survey, open, setOpen }: LinkSingleUseSurveyModalProps) {
export default function LinkSingleUseSurveyModal({ survey, surveyBaseUrl }: LinkSingleUseSurveyModalProps) {
const [singleUseIds, setSingleUseIds] = useState<string[] | null>(null);
useEffect(() => {
@@ -36,7 +35,7 @@ export default function LinkSingleUseSurveyModal({ survey, open, setOpen }: Link
return ids;
};
const defaultSurveyUrl = `${window.location.protocol}//${window.location.host}/s/${survey.id}`;
const defaultSurveyUrl = `${surveyBaseUrl}/${survey.id}`;
const [selectedSingleUseIds, setSelectedSingleIds] = useState<number[]>([]);
const linkTextRef = useRef<HTMLDivElement>(null);
@@ -52,88 +51,85 @@ export default function LinkSingleUseSurveyModal({ survey, open, setOpen }: Link
return (
<>
{singleUseIds && (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="bottom-0 max-w-sm bg-white p-4 sm:bottom-auto sm:max-w-xl sm:p-6">
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-teal-100">
<CheckIcon className="h-6 w-6 text-teal-600" aria-hidden="true" />
<div className="bg-gremin-w-full w-full">
<div className="flex justify-end">
<Button
variant="darkCTA"
title="Preview survey"
aria-label="Preview survey"
className="flex w-fit justify-center"
href={`${defaultSurveyUrl}?suId=${singleUseIds[0]}&preview=true`}
target="_blank"
EndIcon={EyeIcon}>
Preview Survey
</Button>
</div>
<div className="mt-4">
<div ref={linkTextRef} className="min flex flex-col space-y-4">
{singleUseIds &&
singleUseIds.map((singleUseId, index) => {
const isSelected = selectedSingleUseIds.includes(index);
return (
<div className="flex h-fit justify-center p-0">
<div
key={singleUseId}
className={cn(
"row relative flex w-full cursor-pointer items-center justify-between overflow-hidden rounded-lg border border-slate-300 bg-white px-6 py-4 text-left text-slate-800 transition-all duration-200 ease-in-out hover:border-slate-500",
isSelected && "border-slate-200 text-slate-400 hover:border-slate-200"
)}
onClick={() => {
if (!isSelected) {
handleLinkOnClick(index);
}
}}>
<span>{truncateMiddle(`${defaultSurveyUrl}?suId=${singleUseId}`, 48)}</span>
</div>
<div className="ml-2 min-h-full">
<Button
variant="secondary"
className="m-0 my-0 h-full w-full overflow-hidden whitespace-pre px-4 text-center"
onClick={() => handleLinkOnClick(index)}>
{isSelected ? "Copied" : " Copy "}
</Button>
</div>
</div>
);
})}
</div>
<div className="mt-3 text-center sm:mt-5">
<h3 className="text-lg font-semibold leading-6 text-gray-900">Your survey is ready!</h3>
<div className="mt-4">
<p className="text-sm text-gray-500">
Here are 5 single use links to let people answer your survey:
</p>
<div ref={linkTextRef}>
{singleUseIds &&
singleUseIds.map((singleUseId, index) => {
const isSelected = selectedSingleUseIds.includes(index);
return (
<div
key={singleUseId}
className={cn(
"row relative mt-3 flex max-w-full cursor-pointer items-center justify-between overflow-auto rounded-lg border border-slate-300 bg-slate-50 px-8 py-4 text-left text-slate-800 transition-all duration-200 ease-in-out hover:border-slate-500",
isSelected && "border-slate-200 text-slate-400 hover:border-slate-200"
)}
onClick={() => {
if (!isSelected) {
handleLinkOnClick(index);
}
}}>
<span>{truncateMiddle(`${defaultSurveyUrl}?suId=${singleUseId}`, 48)}</span>
{isSelected ? (
<CheckCircleIcon className="ml-4 h-4 w-4" />
) : (
<DocumentDuplicateIcon className="ml-4 h-4 w-4" />
)}
</div>
);
})}
</div>
</div>
<div className="mt-4 flex flex-col justify-center gap-2 sm:flex-row sm:justify-end">
<Button
variant="secondary"
title="Generate new single-use survey link"
aria-label="Generate new single-use survey link"
className="flex justify-center"
onClick={() => {
fetchSingleUseIds();
setSelectedSingleIds([]);
toast.success("New survey links generated!");
}}
EndIcon={ArrowPathIcon}>
Regenerate
</Button>
<Button
variant="secondary"
onClick={() => {
setSelectedSingleIds(Array.from(singleUseIds.keys()));
const allSurveyUrls = singleUseIds
.map((singleUseId) => `${defaultSurveyUrl}?suId=${singleUseId}`)
.join("\n");
navigator.clipboard.writeText(allSurveyUrls);
toast.success("All URLs copied to clipboard!");
}}
title="Copy all survey links to clipboard"
aria-label="Copy all survey links to clipboard"
className="flex justify-center"
EndIcon={DocumentDuplicateIcon}>
Copy 5 URLs
</Button>
<Button
variant="darkCTA"
title="Preview survey"
aria-label="Preview survey"
className="flex justify-center"
href={`${defaultSurveyUrl}?suId=${singleUseIds[0]}&preview=true`}
target="_blank"
EndIcon={EyeIcon}>
Preview
</Button>
</div>
</div>
</DialogContent>
</Dialog>
</div>
<div className="mt-4 flex flex-col justify-center gap-2 sm:flex-row sm:justify-end">
<Button
variant="secondary"
title="Generate new single-use survey link"
aria-label="Generate new single-use survey link"
className="flex justify-center"
onClick={() => {
fetchSingleUseIds();
setSelectedSingleIds([]);
toast.success("New survey links generated!");
}}
EndIcon={ArrowPathIcon}>
Regenerate
</Button>
<Button
variant="secondary"
onClick={() => {
setSelectedSingleIds(Array.from(singleUseIds.keys()));
const allSurveyUrls = singleUseIds
.map((singleUseId) => `${defaultSurveyUrl}?suId=${singleUseId}`)
.join("\n");
navigator.clipboard.writeText(allSurveyUrls);
toast.success("All URLs copied to clipboard!");
}}
title="Copy all survey links to clipboard"
aria-label="Copy all survey links to clipboard"
className="flex justify-center"
EndIcon={DocumentDuplicateIcon}>
Copy all URLs
</Button>
</div>
</div>
)}
</>
);
@@ -3,6 +3,7 @@
import LinkTab from "./shareEmbedTabs/LinkTab";
import EmailTab from "./shareEmbedTabs/EmailTab";
import WebpageTab from "./shareEmbedTabs/WebpageTab";
import LinkSingleUseSurveyModal from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/LinkSingleUseSurveyModal";
import { useMemo, useState } from "react";
import { TProduct } from "@formbricks/types/v1/product";
import { TSurvey } from "@formbricks/types/v1/surveys";
@@ -19,7 +20,6 @@ interface ShareEmbedSurveyProps {
product: TProduct;
profile: TProfile;
}
export default function ShareEmbedSurvey({
survey,
open,
@@ -29,14 +29,24 @@ export default function ShareEmbedSurvey({
profile,
}: ShareEmbedSurveyProps) {
const surveyUrl = useMemo(() => surveyBaseUrl + survey.id, [survey]);
const isSingleUseLinkSurvey = survey.singleUse?.enabled;
const { email } = profile;
const { brandColor } = product;
const tabs = [
{ id: "link", label: `${isSingleUseLinkSurvey ? "Single Use Links" : "Share the Link"}`, icon: LinkIcon },
{ id: "email", label: "Embed in an Email", icon: EnvelopeIcon },
{ id: "webpage", label: "Embed in a Web Page", icon: CodeBracketIcon },
];
const [activeId, setActiveId] = useState(tabs[0].id);
const componentMap = {
link: <LinkTab surveyUrl={surveyUrl} survey={survey} brandColor={brandColor} />,
link: isSingleUseLinkSurvey ? (
<LinkSingleUseSurveyModal survey={survey} surveyBaseUrl={surveyBaseUrl} />
) : (
<LinkTab surveyUrl={surveyUrl} survey={survey} brandColor={brandColor} />
),
email: <EmailTab survey={survey} surveyUrl={surveyUrl} email={email} brandColor={brandColor} />,
webpage: <WebpageTab surveyUrl={surveyUrl} />,
};
@@ -99,9 +109,3 @@ export default function ShareEmbedSurvey({
</Dialog>
);
}
const tabs = [
{ id: "link", label: "Share the Link", icon: LinkIcon },
{ id: "email", label: "Embed in an Email", icon: EnvelopeIcon },
{ id: "webpage", label: "Embed in a Web Page", icon: CodeBracketIcon },
];