mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-07 14:20:31 -06:00
fix: tweak sharing results UX (#1928)
This commit is contained in:
@@ -19,6 +19,8 @@ import { TTag } from "@formbricks/types/tags";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import ContentWrapper from "@formbricks/ui/ContentWrapper";
|
||||
|
||||
import ResultsShareButton from "../../../components/ResultsShareButton";
|
||||
|
||||
interface ResponsePageProps {
|
||||
environment: TEnvironment;
|
||||
survey: TSurvey;
|
||||
@@ -70,12 +72,15 @@ const ResponsePage = ({
|
||||
user={user}
|
||||
membershipRole={membershipRole}
|
||||
/>
|
||||
<CustomFilter
|
||||
environmentTags={environmentTags}
|
||||
responses={filterResponses}
|
||||
survey={survey}
|
||||
totalResponses={responses}
|
||||
/>
|
||||
<div className="flex gap-1.5">
|
||||
<CustomFilter
|
||||
environmentTags={environmentTags}
|
||||
responses={filterResponses}
|
||||
survey={survey}
|
||||
totalResponses={responses}
|
||||
/>
|
||||
<ResultsShareButton survey={survey} webAppUrl={webAppUrl} product={product} user={user} />
|
||||
</div>
|
||||
<SurveyResultsTabs activeId="responses" environmentId={environment.id} surveyId={surveyId} />
|
||||
<ResponseTimeline
|
||||
environment={environment}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { CheckCircleIcon, GlobeEuropeAfricaIcon } from "@heroicons/react/24/solid";
|
||||
import { CheckCircleIcon, ExclamationCircleIcon } from "@heroicons/react/24/solid";
|
||||
import { Clipboard } from "lucide-react";
|
||||
import { toast } from "react-hot-toast";
|
||||
|
||||
@@ -69,7 +69,7 @@ export default function ShareSurveyResults({
|
||||
</Button>
|
||||
|
||||
<Button variant="darkCTA" className=" text-center" href={surveyUrl} target="_blank">
|
||||
View Site
|
||||
View site
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -77,21 +77,22 @@ export default function ShareSurveyResults({
|
||||
) : (
|
||||
<DialogContent className="flex flex-col rounded-2xl bg-white p-8">
|
||||
<div className="flex flex-col items-center gap-y-6 text-center">
|
||||
<GlobeEuropeAfricaIcon className="h-20 w-20 text-slate-300" />
|
||||
<ExclamationCircleIcon className="h-20 w-20 text-slate-300" />
|
||||
<div>
|
||||
<p className="text-lg font-medium text-slate-600">Publish Results to web</p>
|
||||
<p className="text-lg font-medium text-slate-600">
|
||||
You are about to release these survey results to the public.
|
||||
</p>
|
||||
<p className="text-balanced mt-2 text-sm text-slate-500">
|
||||
Your survey results are shared with anyone who has the link. The results will not be indexed
|
||||
by search engines.
|
||||
Your survey results will be public. Anyone outside your team can access them if they have the
|
||||
link.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="darkCTA"
|
||||
className="h-full text-center"
|
||||
onClick={() => handlePublish()}>
|
||||
Publish to web
|
||||
Publish to public web
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
@@ -21,6 +21,8 @@ import { TTag } from "@formbricks/types/tags";
|
||||
import { TUser } from "@formbricks/types/user";
|
||||
import ContentWrapper from "@formbricks/ui/ContentWrapper";
|
||||
|
||||
import ResultsShareButton from "../../../components/ResultsShareButton";
|
||||
|
||||
interface SummaryPageProps {
|
||||
environment: TEnvironment;
|
||||
survey: TSurvey;
|
||||
@@ -76,12 +78,15 @@ const SummaryPage = ({
|
||||
user={user}
|
||||
membershipRole={membershipRole}
|
||||
/>
|
||||
<CustomFilter
|
||||
environmentTags={environmentTags}
|
||||
responses={filterResponses}
|
||||
survey={survey}
|
||||
totalResponses={responses}
|
||||
/>
|
||||
<div className="flex gap-1.5">
|
||||
<CustomFilter
|
||||
environmentTags={environmentTags}
|
||||
responses={filterResponses}
|
||||
survey={survey}
|
||||
totalResponses={responses}
|
||||
/>
|
||||
<ResultsShareButton survey={survey} webAppUrl={webAppUrl} product={product} user={user} />
|
||||
</div>
|
||||
<SurveyResultsTabs activeId="summary" environmentId={environment.id} surveyId={surveyId} />
|
||||
<SummaryMetadata
|
||||
responses={filterResponses}
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
generateResultShareUrlAction,
|
||||
getResultShareUrlAction,
|
||||
} from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/actions";
|
||||
import { ArrowUpRightIcon, GlobeAltIcon, LinkIcon } from "@heroicons/react/24/outline";
|
||||
import { DocumentDuplicateIcon, GlobeAltIcon, LinkIcon } from "@heroicons/react/24/outline";
|
||||
import { DownloadIcon } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -20,10 +20,10 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from "@formbricks/ui/DropdownMenu";
|
||||
|
||||
import ShareEmbedSurvey from "./ShareEmbedSurvey";
|
||||
import ShareSurveyResults from "./ShareSurveyResults";
|
||||
import ShareEmbedSurvey from "../(analysis)/summary/components/ShareEmbedSurvey";
|
||||
import ShareSurveyResults from "../(analysis)/summary/components/ShareSurveyResults";
|
||||
|
||||
interface SurveyShareButtonProps {
|
||||
interface ResultsShareButtonProps {
|
||||
survey: TSurvey;
|
||||
className?: string;
|
||||
webAppUrl: string;
|
||||
@@ -31,7 +31,7 @@ interface SurveyShareButtonProps {
|
||||
user: TUser;
|
||||
}
|
||||
|
||||
export default function SurveyShareButton({ survey, webAppUrl, product, user }: SurveyShareButtonProps) {
|
||||
export default function ResultsShareButton({ survey, webAppUrl, product, user }: ResultsShareButtonProps) {
|
||||
const [showLinkModal, setShowLinkModal] = useState(false);
|
||||
const [showResultsLinkModal, setShowResultsLinkModal] = useState(false);
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function SurveyShareButton({ survey, webAppUrl, product, user }:
|
||||
const handleUnpublish = () => {
|
||||
deleteResultShareUrlAction(survey.id)
|
||||
.then(() => {
|
||||
toast.success("Survey Unpublished successfully");
|
||||
toast.success("Results unpublished successfully.");
|
||||
setShowPublishModal(false);
|
||||
setShowLinkModal(false);
|
||||
})
|
||||
@@ -74,39 +74,55 @@ export default function SurveyShareButton({ survey, webAppUrl, product, user }:
|
||||
}
|
||||
}, [showResultsLinkModal]);
|
||||
|
||||
const copyUrlToClipboard = () => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Check if window is defined (i.e., if the code is running in the browser)
|
||||
const currentUrl = window.location.href; // Get the current URL
|
||||
navigator.clipboard
|
||||
.writeText(currentUrl) // Copy it to the clipboard
|
||||
.then(() => {
|
||||
toast.success("Link to results copied to clipboard."); // Show success message
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to copy: ", err); // Handle any errors
|
||||
toast.error("Failed to copy link to results to clipboard."); // Show error message
|
||||
});
|
||||
} else {
|
||||
console.error("Cannot copy URL: not running in a browser environment.");
|
||||
toast.error("Failed to copy URL: not in a browser environment.");
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className="mb-12">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
asChild
|
||||
className="focus:bg-muted cursor-pointer border border-slate-300 outline-none hover:border-slate-400">
|
||||
<div className="min-w-auto h-auto rounded-md border bg-white p-3 sm:flex sm:min-w-[7rem] sm:px-6 sm:py-3">
|
||||
<div className="hidden w-full items-center justify-between sm:flex">
|
||||
<span className="text-sm text-slate-700"> Share</span>
|
||||
<LinkIcon className="h-4 w-4" />
|
||||
<span className="text-sm text-slate-700">Share Results</span>
|
||||
<LinkIcon className="ml-2 h-4 w-4" />
|
||||
</div>
|
||||
<DownloadIcon className="block h-4 sm:hidden" />
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
{survey.type === "link" && (
|
||||
<DropdownMenuItem
|
||||
className="hover:ring-0"
|
||||
onClick={() => {
|
||||
setShowLinkModal(true);
|
||||
}}>
|
||||
<p className="text-slate-700">
|
||||
Share Survey <ArrowUpRightIcon className="ml-2 inline h-4 w-4" />
|
||||
</p>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="hover:ring-0"
|
||||
onClick={() => {
|
||||
copyUrlToClipboard();
|
||||
}}>
|
||||
<p className="text-slate-700">
|
||||
Copy link <DocumentDuplicateIcon className="ml-1.5 inline h-4 w-4" />
|
||||
</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="hover:ring-0"
|
||||
onClick={() => {
|
||||
setShowResultsLinkModal(true);
|
||||
}}>
|
||||
<p className="text-slate-700">
|
||||
Publish Results <GlobeAltIcon className="ml-2 inline h-4 w-4" />
|
||||
Publish to web <GlobeAltIcon className="ml-1.5 inline h-4 w-4" />
|
||||
</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
@@ -132,6 +148,6 @@ export default function SurveyShareButton({ survey, webAppUrl, product, user }:
|
||||
showPublishModal={showPublishModal}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import SuccessMessage from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SuccessMessage";
|
||||
import SurveyShareButton from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SurveyShareButton";
|
||||
import ResultsShareButton from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/ResultsShareButton";
|
||||
import SurveyStatusDropdown from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SurveyStatusDropdown";
|
||||
import { updateSurveyAction } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/edit/actions";
|
||||
import { EllipsisHorizontalIcon, PencilSquareIcon } from "@heroicons/react/24/solid";
|
||||
import { EllipsisHorizontalIcon, PencilSquareIcon, ShareIcon } from "@heroicons/react/24/solid";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
@@ -30,6 +31,8 @@ import {
|
||||
} from "@formbricks/ui/DropdownMenu";
|
||||
import { SurveyStatusIndicator } from "@formbricks/ui/SurveyStatusIndicator";
|
||||
|
||||
import ShareEmbedSurvey from "../(analysis)/summary/components/ShareEmbedSurvey";
|
||||
|
||||
interface SummaryHeaderProps {
|
||||
surveyId: string;
|
||||
environment: TEnvironment;
|
||||
@@ -54,23 +57,33 @@ const SummaryHeader = ({
|
||||
const closeOnDate = survey.closeOnDate ? new Date(survey.closeOnDate) : null;
|
||||
const isStatusChangeDisabled = (isCloseOnDateEnabled && closeOnDate && closeOnDate < new Date()) ?? false;
|
||||
const { isViewer } = getAccessFlags(membershipRole);
|
||||
const [showShareSurveyModal, setShowShareSurveyModal] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="mb-11 mt-6 flex flex-wrap items-center justify-between">
|
||||
<div>
|
||||
<div className="flex gap-4">
|
||||
<p className="text-3xl font-bold text-slate-800">{survey.name}</p>
|
||||
{survey.resultShareKey && <Badge text="Public Results" type="warning" size="normal"></Badge>}
|
||||
{survey.resultShareKey && <Badge text="Results are public" type="warning" size="normal"></Badge>}
|
||||
</div>
|
||||
<span className="text-base font-extralight text-slate-600">{product.name}</span>
|
||||
</div>
|
||||
<div className="hidden justify-end gap-x-1.5 sm:flex">
|
||||
<SurveyShareButton survey={survey} webAppUrl={webAppUrl} product={product} user={user} />
|
||||
{/* <ResultsShareButton survey={survey} webAppUrl={webAppUrl} product={product} user={user} /> */}
|
||||
{!isViewer &&
|
||||
(environment?.widgetSetupCompleted || survey.type === "link") &&
|
||||
survey?.status !== "draft" ? (
|
||||
<SurveyStatusDropdown environment={environment} survey={survey} />
|
||||
) : null}
|
||||
{survey.type === "link" && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setShowShareSurveyModal(true);
|
||||
}}>
|
||||
<ShareIcon className="h-5 w-5" />
|
||||
</Button>
|
||||
)}
|
||||
{!isViewer && (
|
||||
<Button
|
||||
variant="darkCTA"
|
||||
@@ -91,7 +104,7 @@ const SummaryHeader = ({
|
||||
<DropdownMenuContent align="end" className="p-2">
|
||||
{survey.type === "link" && (
|
||||
<>
|
||||
<SurveyShareButton
|
||||
<ResultsShareButton
|
||||
className="flex w-full justify-center p-1"
|
||||
survey={survey}
|
||||
webAppUrl={webAppUrl}
|
||||
@@ -183,6 +196,16 @@ const SummaryHeader = ({
|
||||
product={product}
|
||||
user={user}
|
||||
/>
|
||||
{showShareSurveyModal && (
|
||||
<ShareEmbedSurvey
|
||||
survey={survey}
|
||||
open={showShareSurveyModal}
|
||||
setOpen={setShowShareSurveyModal}
|
||||
product={product}
|
||||
webAppUrl={webAppUrl}
|
||||
user={user}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ export const metadata: Metadata = {
|
||||
|
||||
export default async function EnvironmentLayout({ children }) {
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="flex-1 bg-slate-50">
|
||||
<ResponseFilterProvider>{children}</ResponseFilterProvider>
|
||||
</div>
|
||||
);
|
||||
|
||||
1
oss.gg
Submodule
1
oss.gg
Submodule
Submodule oss.gg added at 3e15d79895
Reference in New Issue
Block a user