Skip to content

Commit

Permalink
feat: created icon bar component in survey overview (formbricks#4240)
Browse files Browse the repository at this point in the history
Co-authored-by: Johannes <[email protected]>
Co-authored-by: Piyush Gupta <[email protected]>
  • Loading branch information
3 people authored Nov 26, 2024
1 parent b83b54e commit 7598a16
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 && (
Expand All @@ -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 && (
<>
Expand Down
14 changes: 8 additions & 6 deletions apps/web/modules/ui/components/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
);
});
Expand Down
41 changes: 41 additions & 0 deletions apps/web/modules/ui/components/iconbar/index.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
};

0 comments on commit 7598a16

Please sign in to comment.