mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-24 15:10:36 -06:00
Compare commits
4 Commits
feat/singl
...
feat/icon-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65a5d5b8e7 | ||
|
|
4214e94778 | ||
|
|
7139ec5276 | ||
|
|
44bbb59c8f |
@@ -4,25 +4,9 @@ import { ShareEmbedSurvey } from "@/app/(app)/environments/[environmentId]/surve
|
||||
import { SuccessMessage } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/summary/components/SuccessMessage";
|
||||
import { SurveyStatusDropdown } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/SurveyStatusDropdown";
|
||||
import { Badge } from "@/modules/ui/components/badge";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/modules/ui/components/dropdown-menu";
|
||||
import {
|
||||
BellRing,
|
||||
Code2Icon,
|
||||
CopyIcon,
|
||||
EyeIcon,
|
||||
MoreVertical,
|
||||
SquarePenIcon,
|
||||
UsersRound,
|
||||
} from "lucide-react";
|
||||
import { IconBar } from "@/modules/ui/components/iconbar";
|
||||
import { BellRing, Code2Icon, Eye, LinkIcon, SquarePenIcon, UsersRound } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
@@ -117,6 +101,48 @@ export const SurveyAnalysisCTA = ({
|
||||
{ key: "panel", modalView: "panel" as const, setOpen: handleModalState("panel") },
|
||||
];
|
||||
|
||||
const iconActions = [
|
||||
{
|
||||
icon: Eye,
|
||||
tooltip: t("common.preview"),
|
||||
onClick: () => window.open(getPreviewUrl(), "_blank"),
|
||||
isVisible: survey.type === "link",
|
||||
},
|
||||
{
|
||||
icon: LinkIcon,
|
||||
tooltip: t("common.copy_link"),
|
||||
onClick: handleCopyLink,
|
||||
isVisible: survey.type === "link",
|
||||
},
|
||||
{
|
||||
icon: Code2Icon,
|
||||
tooltip: t("common.embed"),
|
||||
onClick: () => handleModalState("embed")(true),
|
||||
isVisible: !isReadOnly,
|
||||
},
|
||||
{
|
||||
icon: BellRing,
|
||||
tooltip: t("environments.surveys.summary.configure_alerts"),
|
||||
onClick: () => router.push(`/environments/${survey.environmentId}/settings/notifications`),
|
||||
isVisible: !isReadOnly,
|
||||
},
|
||||
{
|
||||
icon: UsersRound,
|
||||
tooltip: t("environments.surveys.summary.send_to_panel"),
|
||||
onClick: () => {
|
||||
handleModalState("panel")(true);
|
||||
setModalState((prev) => ({ ...prev, dropdown: false }));
|
||||
},
|
||||
isVisible: !isReadOnly,
|
||||
},
|
||||
{
|
||||
icon: SquarePenIcon,
|
||||
tooltip: t("common.edit"),
|
||||
onClick: () => router.push(`/environments/${environment.id}/surveys/${survey.id}/edit`),
|
||||
isVisible: !isReadOnly,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="hidden justify-end gap-x-1.5 sm:flex">
|
||||
{survey.resultShareKey && (
|
||||
@@ -132,81 +158,7 @@ export const SurveyAnalysisCTA = ({
|
||||
<SurveyStatusDropdown environment={environment} survey={survey} />
|
||||
)}
|
||||
|
||||
{!isReadOnly && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => handleModalState("embed")(true)}
|
||||
EndIcon={Code2Icon}>
|
||||
{t("common.embed")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{survey.type === "link" && (
|
||||
<Button variant="secondary" size="sm" onClick={handleCopyLink} EndIcon={CopyIcon}>
|
||||
{t("common.copy_link")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{!isReadOnly && (
|
||||
<Button
|
||||
href={`/environments/${environment.id}/surveys/${survey.id}/edit`}
|
||||
EndIcon={SquarePenIcon}
|
||||
size="base">
|
||||
{t("common.edit")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{!isReadOnly && (
|
||||
<div id={`${survey.name.toLowerCase().replace(/\s+/g, "-")}-survey-actions`}>
|
||||
<DropdownMenu
|
||||
open={modalState.dropdown}
|
||||
onOpenChange={(open) => setModalState((prev) => ({ ...prev, dropdown: open }))}>
|
||||
<DropdownMenuTrigger className="z-10 cursor-pointer" asChild>
|
||||
<Button variant="secondary" className="p-2">
|
||||
<MoreVertical className="h-7 w-4" />
|
||||
<span className="sr-only">{t("environments.surveys.summary.open_options")}</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="mr-8 w-40">
|
||||
<DropdownMenuGroup>
|
||||
{survey.type === "link" && (
|
||||
<DropdownMenuItem>
|
||||
<button
|
||||
onClick={() => window.open(getPreviewUrl(), "_blank")}
|
||||
className="flex w-full items-center">
|
||||
<EyeIcon className="mr-2 h-4 w-4" />
|
||||
{t("common.preview")}
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
<DropdownMenuItem>
|
||||
<button
|
||||
onClick={() => {
|
||||
handleModalState("panel")(true);
|
||||
setModalState((prev) => ({ ...prev, dropdown: false }));
|
||||
}}
|
||||
className="flex w-full items-center">
|
||||
<UsersRound className="mr-2 h-4 w-4" />
|
||||
{t("environments.surveys.summary.send_to_panel")}
|
||||
</button>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem>
|
||||
<Link
|
||||
href={`/environments/${survey.environmentId}/settings/notifications`}
|
||||
className="flex w-full items-center"
|
||||
onClick={() => setModalState((prev) => ({ ...prev, dropdown: false }))}>
|
||||
<BellRing className="mr-2 h-4 w-4" />
|
||||
{t("environments.surveys.summary.configure_alerts")}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)}
|
||||
<IconBar actions={iconActions} />
|
||||
|
||||
{user && (
|
||||
<>
|
||||
|
||||
@@ -141,17 +141,19 @@ export const Button: React.ForwardRefExoticComponent<
|
||||
{EndIcon && <EndIcon className={cn("-mr-1 ml-2 inline h-4 w-4 rtl:mr-2", endIconClassName || "")} />}
|
||||
</>
|
||||
);
|
||||
return props.href ? (
|
||||
<Link passHref href={props.href} shallow={shallow && shallow} target={props.target || "_self"}>
|
||||
{element}
|
||||
</Link>
|
||||
) : (
|
||||
return (
|
||||
<Wrapper
|
||||
data-testid="wrapper"
|
||||
tooltip={props.tooltip}
|
||||
tooltipSide={tooltipSide}
|
||||
tooltipOffset={tooltipOffset}>
|
||||
{element}
|
||||
{props.href ? (
|
||||
<Link passHref href={props.href} shallow={shallow && shallow} target={props.target || "_self"}>
|
||||
{element}
|
||||
</Link>
|
||||
) : (
|
||||
element
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
});
|
||||
|
||||
41
apps/web/modules/ui/components/iconbar/index.tsx
Normal file
41
apps/web/modules/ui/components/iconbar/index.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import { Button } from "../button";
|
||||
|
||||
interface IconAction {
|
||||
icon: LucideIcon;
|
||||
tooltip: string;
|
||||
onClick?: () => void;
|
||||
isVisible?: boolean;
|
||||
}
|
||||
|
||||
interface IconBarProps {
|
||||
actions: IconAction[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const IconBar = ({ actions }: IconBarProps) => {
|
||||
if (actions.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-center justify-center divide-x rounded-lg border border-slate-300 bg-white"
|
||||
role="toolbar"
|
||||
aria-label="Action buttons">
|
||||
{actions
|
||||
.filter((action) => action.isVisible)
|
||||
.map((action, index) => (
|
||||
<span key={`${action.tooltip}-${index}`}>
|
||||
<Button
|
||||
variant="minimal"
|
||||
className="border-none hover:bg-slate-50"
|
||||
size="icon"
|
||||
StartIcon={action.icon}
|
||||
tooltip={action.tooltip}
|
||||
onClick={action.onClick}
|
||||
aria-label={action.tooltip}
|
||||
/>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user