mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-18 11:31:12 -05:00
added shareSurveySheet
This commit is contained in:
@@ -27,7 +27,6 @@ import { cn } from "@formbricks/lib/cn";
|
||||
import { QuestionType } from "@formbricks/types/questions";
|
||||
import { SurveyInline } from "@/components/shared/Survey";
|
||||
import { TProduct } from "@formbricks/types/v1/product";
|
||||
import { useProfile } from "@/lib/profile";
|
||||
import { TProfile } from "@formbricks/types/v1/profile";
|
||||
|
||||
interface EmbedSurveyModalProps {
|
||||
@@ -36,6 +35,7 @@ interface EmbedSurveyModalProps {
|
||||
setOpen: (open: boolean) => void;
|
||||
surveyBaseUrl: string;
|
||||
product: TProduct;
|
||||
profile: TProfile;
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
@@ -50,13 +50,12 @@ export default function EmbedSurveyModal({
|
||||
setOpen,
|
||||
surveyBaseUrl,
|
||||
product,
|
||||
profile,
|
||||
}: EmbedSurveyModalProps) {
|
||||
const [activeId, setActiveId] = useState(tabs[0].id);
|
||||
|
||||
const surveyUrl = useMemo(() => surveyBaseUrl + survey.id, [survey]);
|
||||
|
||||
const { profile } = useProfile();
|
||||
|
||||
const componentMap = {
|
||||
link: <LinkTab surveyUrl={surveyUrl} survey={survey} product={product} />,
|
||||
email: <EmailTab survey={survey} surveyUrl={surveyUrl} profile={profile} />,
|
||||
@@ -98,7 +97,7 @@ export default function EmbedSurveyModal({
|
||||
}
|
||||
|
||||
// 1st tab
|
||||
const LinkTab = ({
|
||||
export const LinkTab = ({
|
||||
surveyUrl,
|
||||
survey,
|
||||
product,
|
||||
@@ -123,11 +122,11 @@ const LinkTab = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex grow flex-col gap-5">
|
||||
<div className="flex h-full grow flex-col gap-5">
|
||||
<div className="flex justify-between gap-2">
|
||||
<div
|
||||
ref={linkTextRef}
|
||||
className="relative grow overflow-auto rounded-lg border border-slate-300 bg-white px-3 py-2 text-center text-slate-800"
|
||||
className="relative grow overflow-auto rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-800"
|
||||
onClick={() => handleTextSelection()}>
|
||||
<span style={{ wordBreak: "break-all" }}>{surveyUrl}</span>
|
||||
</div>
|
||||
@@ -172,15 +171,7 @@ const LinkTab = ({
|
||||
};
|
||||
|
||||
// 2nd tab
|
||||
const EmailTab = ({
|
||||
survey,
|
||||
surveyUrl,
|
||||
profile,
|
||||
}: {
|
||||
survey: TSurvey;
|
||||
surveyUrl: string;
|
||||
profile: TProfile;
|
||||
}) => {
|
||||
export const EmailTab = ({ survey, surveyUrl, profile }: { survey: TSurvey; surveyUrl: string; profile }) => {
|
||||
const [email, setEmail] = useState(profile.email);
|
||||
const [showEmbed, setShowEmbed] = useState(false);
|
||||
|
||||
@@ -217,14 +208,16 @@ const EmailTab = ({
|
||||
// console.log(res);
|
||||
};
|
||||
return (
|
||||
<div className="flex grow flex-col gap-5">
|
||||
<div className="flex h-full grow flex-col gap-5">
|
||||
<div className="flex items-center gap-4">
|
||||
<Input
|
||||
type="email"
|
||||
placeholder="user@mail.com"
|
||||
className="h-11 grow bg-white"
|
||||
value={email}
|
||||
// onChange={(e) => setEmail(e.target.value)}
|
||||
onChange={(e) => {
|
||||
// setEmail(e.target.value)
|
||||
}}
|
||||
/>
|
||||
{showEmbed ? (
|
||||
<Button
|
||||
@@ -303,7 +296,7 @@ const WebpageTab = ({ surveyUrl }) => {
|
||||
</div>`;
|
||||
|
||||
return (
|
||||
<div className="flex grow flex-col gap-5">
|
||||
<div className="flex h-full grow flex-col gap-5">
|
||||
<div className="flex justify-between">
|
||||
<div className=""></div>
|
||||
<Button
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useState } from "react";
|
||||
import clsx from "clsx";
|
||||
import { TProduct } from "@formbricks/types/v1/product";
|
||||
import EmbedSurveyModal from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/EmbedSurveyModal";
|
||||
import ShareEmbedSurvey from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/ShareEmbedSurvey";
|
||||
|
||||
interface LinkSurveyShareButtonProps {
|
||||
survey: TSurvey;
|
||||
@@ -34,7 +35,7 @@ export default function LinkSurveyShareButton({
|
||||
onClick={() => setShowLinkModal(true)}>
|
||||
<ShareIcon className="h-5 w-5" />
|
||||
</Button>
|
||||
{showLinkModal && (
|
||||
{/* {showLinkModal && (
|
||||
<EmbedSurveyModal
|
||||
survey={survey}
|
||||
open={showLinkModal}
|
||||
@@ -42,6 +43,15 @@ export default function LinkSurveyShareButton({
|
||||
product={product}
|
||||
surveyBaseUrl={surveyBaseUrl}
|
||||
/>
|
||||
)} */}
|
||||
{showLinkModal && (
|
||||
<ShareEmbedSurvey
|
||||
survey={survey}
|
||||
open={showLinkModal}
|
||||
setOpen={setShowLinkModal}
|
||||
product={product}
|
||||
surveyBaseUrl={surveyBaseUrl}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import EmbedSurveyModal, {
|
||||
EmailTab,
|
||||
LinkTab,
|
||||
} from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/EmbedSurveyModal";
|
||||
import { useProfile } from "@/lib/profile";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TProduct } from "@formbricks/types/v1/product";
|
||||
import { TSurvey } from "@formbricks/types/v1/surveys";
|
||||
import { SheetContent, SheetHeader, Sheet, Button } from "@formbricks/ui";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
interface ShareEmbedSurveyProps {
|
||||
survey: TSurvey;
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
surveyBaseUrl: string;
|
||||
product: TProduct;
|
||||
}
|
||||
|
||||
export default function ShareEmbedSurvey({
|
||||
survey,
|
||||
open,
|
||||
setOpen,
|
||||
surveyBaseUrl,
|
||||
product,
|
||||
}: ShareEmbedSurveyProps) {
|
||||
const surveyUrl = useMemo(() => surveyBaseUrl + survey.id, [survey]);
|
||||
const { profile } = useProfile();
|
||||
|
||||
console.log({ survey, open, setOpen, surveyBaseUrl, product });
|
||||
return (
|
||||
<div className="">
|
||||
<div className="hidden lg:hidden">
|
||||
{/* <EmbedSurveyModal
|
||||
survey={survey}
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
product={product}
|
||||
surveyBaseUrl={surveyBaseUrl}
|
||||
profile = {profile}
|
||||
/> */}
|
||||
</div>
|
||||
<div className="invisible hidden lg:hidden">
|
||||
<EmbedSurveySheet
|
||||
survey={survey}
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
product={product}
|
||||
surveyUrl={surveyUrl}
|
||||
profile={profile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const EmbedSurveySheet = ({ survey, open, setOpen, surveyUrl, product, profile }) => {
|
||||
const [activeId, setActiveId] = useState("link");
|
||||
|
||||
const tabs = [
|
||||
{ id: "link", label: "Share Link" },
|
||||
{ id: "email", label: "Embed in an Email" },
|
||||
];
|
||||
const componentMap = {
|
||||
link: <LinkTab surveyUrl={surveyUrl} survey={survey} product={product} />,
|
||||
email: <EmailTab survey={survey} surveyUrl={surveyUrl} profile={profile} />,
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetContent
|
||||
className="over flex h-[90%] w-full flex-col gap-0 rounded-t-2xl bg-slate-50 p-0"
|
||||
side={"bottom"}>
|
||||
<SheetHeader>
|
||||
<div className="border-b border-gray-200 px-6 py-4">Share or embed your survey</div>
|
||||
</SheetHeader>
|
||||
<div className="flex grow flex-col gap-6 overflow-y-scroll px-4 py-6">
|
||||
<div className="tab grow overflow-y-scroll">{componentMap[activeId]}</div>
|
||||
<div className="mx-auto flex max-w-max rounded-md bg-slate-100 p-1">
|
||||
{tabs.map((tab) => (
|
||||
<Button
|
||||
variant="minimal"
|
||||
key={tab.id}
|
||||
onClick={() => setActiveId(tab.id)}
|
||||
className={cn(
|
||||
"rounded-sm px-3 py-[6px]",
|
||||
tab.id === activeId
|
||||
? "bg-white text-slate-900"
|
||||
: "border-transparent text-slate-700 hover:text-slate-900"
|
||||
)}>
|
||||
{tab.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
};
|
||||
@@ -6,7 +6,7 @@ import { Confetti } from "@formbricks/ui";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import EmbedSurveyModal from "./EmbedSurveyModal";
|
||||
import ShareEmbedSurvey from "./ShareEmbedSurvey";
|
||||
import { TProduct } from "@formbricks/types/v1/product";
|
||||
|
||||
interface SummaryMetadataProps {
|
||||
@@ -55,7 +55,7 @@ export default function SuccessMessage({
|
||||
return (
|
||||
<>
|
||||
{showLinkModal && (
|
||||
<EmbedSurveyModal
|
||||
<ShareEmbedSurvey
|
||||
survey={survey}
|
||||
open={showLinkModal}
|
||||
setOpen={setShowLinkModal}
|
||||
|
||||
120
packages/ui/components/Sheet.tsx
Normal file
120
packages/ui/components/Sheet.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog";
|
||||
import { X } from "lucide-react";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
|
||||
const Sheet = SheetPrimitive.Root;
|
||||
|
||||
const SheetTrigger = SheetPrimitive.Trigger;
|
||||
|
||||
const SheetClose = SheetPrimitive.Close;
|
||||
|
||||
const SheetPortal = ({ className, ...props }: SheetPrimitive.DialogPortalProps) => (
|
||||
<SheetPrimitive.Portal className={cn(className)} {...props} />
|
||||
);
|
||||
SheetPortal.displayName = SheetPrimitive.Portal.displayName;
|
||||
|
||||
const SheetOverlay = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"bg-background/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 backdrop-blur-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
/>
|
||||
));
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
interface SheetContentProps
|
||||
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||
VariantProps<typeof sheetVariants> {}
|
||||
|
||||
const SheetContent = React.forwardRef<React.ElementRef<typeof SheetPrimitive.Content>, SheetContentProps>(
|
||||
({ side = "right", className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
||||
{children}
|
||||
<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<X className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</SheetPrimitive.Close>
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
)
|
||||
);
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
||||
|
||||
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
|
||||
);
|
||||
SheetHeader.displayName = "SheetHeader";
|
||||
|
||||
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
SheetFooter.displayName = "SheetFooter";
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-foreground text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetTitle.displayName = SheetPrimitive.Title.displayName;
|
||||
|
||||
const SheetDescription = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Description>,
|
||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SheetDescription.displayName = SheetPrimitive.Description.displayName;
|
||||
|
||||
export {
|
||||
Sheet,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
};
|
||||
@@ -67,6 +67,16 @@ export {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "./components/Select";
|
||||
export {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
SheetClose,
|
||||
SheetFooter,
|
||||
} from "./components/Sheet";
|
||||
export { Switch } from "./components/Switch";
|
||||
export { TabBar } from "./components/TabBar";
|
||||
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./components/Tooltip";
|
||||
|
||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -18449,23 +18449,6 @@ packages:
|
||||
postcss: 8.4.27
|
||||
yaml: 2.3.1
|
||||
|
||||
/postcss-load-config@4.0.1(postcss@8.4.30):
|
||||
resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==}
|
||||
engines: {node: '>= 14'}
|
||||
peerDependencies:
|
||||
postcss: '>=8.0.9'
|
||||
ts-node: '>=9.0.0'
|
||||
peerDependenciesMeta:
|
||||
postcss:
|
||||
optional: true
|
||||
ts-node:
|
||||
optional: true
|
||||
dependencies:
|
||||
lilconfig: 2.1.0
|
||||
postcss: 8.4.30
|
||||
yaml: 2.3.1
|
||||
dev: true
|
||||
|
||||
/postcss-loader@4.3.0(postcss@8.4.27)(webpack@4.46.0):
|
||||
resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
@@ -22903,7 +22886,7 @@ packages:
|
||||
execa: 5.1.1
|
||||
globby: 11.1.0
|
||||
joycon: 3.1.1
|
||||
postcss-load-config: 4.0.1(postcss@8.4.30)
|
||||
postcss-load-config: 4.0.1(postcss@8.4.27)
|
||||
resolve-from: 5.0.0
|
||||
rollup: 3.5.1
|
||||
source-map: 0.8.0-beta.0
|
||||
|
||||
Reference in New Issue
Block a user