fix: tweak sharing results UX (#1928)

This commit is contained in:
Johannes
2024-01-19 13:09:55 -06:00
committed by GitHub
parent 8745fd8b92
commit c4ace07765
7 changed files with 99 additions and 48 deletions

View File

@@ -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}

View File

@@ -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>

View File

@@ -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}

View File

@@ -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>
);
}

View File

@@ -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>
);
};

View File

@@ -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

Submodule oss.gg added at 3e15d79895