split dialog, dropdown and tooltip into separate components
parent
c517d4dacd
commit
1bdb75a363
|
@ -0,0 +1,30 @@
|
||||||
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
export function EventTypeDialog({
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
content,
|
||||||
|
}: {
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: () => void;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
content?: ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{title}</DialogTitle>
|
||||||
|
<DialogDescription>{description}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
{!!content && content}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { MoreHorizontal, Copy, Code, Trash } from "lucide-react";
|
||||||
|
|
||||||
|
import { EventTypeEmbedButton } from "@calcom/features/embed/EventTypeEmbed";
|
||||||
|
|
||||||
|
export function EventTypeDropdown({
|
||||||
|
group,
|
||||||
|
isReadOnly,
|
||||||
|
id,
|
||||||
|
isManagedEventType,
|
||||||
|
isChildrenManagedEventType,
|
||||||
|
embedLink,
|
||||||
|
onEdit,
|
||||||
|
onDuplicate,
|
||||||
|
}: {
|
||||||
|
group: any;
|
||||||
|
isReadOnly: boolean;
|
||||||
|
isManagedEventType: boolean;
|
||||||
|
isChildrenManagedEventType: boolean;
|
||||||
|
id: any;
|
||||||
|
embedLink: string;
|
||||||
|
onEdit: (id: string) => void;
|
||||||
|
onDuplicate: (id: string) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
className="bg-secondary text-secondary ltr:radix-state-open:rounded-r-md rtl:radix-state-open:rounded-l-md">
|
||||||
|
<MoreHorizontal />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>
|
||||||
|
{!isReadOnly && (
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
data-testid={`event-type-edit-${id}`}
|
||||||
|
onClick={() => {
|
||||||
|
onEdit(id);
|
||||||
|
}}>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
{!isManagedEventType && !isChildrenManagedEventType && (
|
||||||
|
<DropdownMenuItem className="outline-none">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
data-testid={`event-type-duplicate-${id}`}
|
||||||
|
onClick={() => {
|
||||||
|
onDuplicate(id);
|
||||||
|
}}>
|
||||||
|
<Copy />
|
||||||
|
Duplicate
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
{!isManagedEventType && (
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<EventTypeEmbedButton
|
||||||
|
className="w-full rounded-none"
|
||||||
|
eventId={id}
|
||||||
|
type="button"
|
||||||
|
StartIcon={Code}
|
||||||
|
embedUrl={encodeURIComponent(embedLink)}>
|
||||||
|
Embed
|
||||||
|
</EventTypeEmbedButton>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
{(group.metadata?.readOnly === false || group.metadata.readOnly === null) &&
|
||||||
|
!isChildrenManagedEventType && (
|
||||||
|
<>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => {
|
||||||
|
// Add appropriate handler for destruction
|
||||||
|
}}
|
||||||
|
className="w-full rounded-none">
|
||||||
|
<Trash /> Delete
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
export function EventTypeTooltip({
|
||||||
|
trigger,
|
||||||
|
content,
|
||||||
|
}: {
|
||||||
|
trigger: ReactNode | string;
|
||||||
|
content: ReactNode | string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
|
||||||
|
<TooltipContent>{content}</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { EmptyScreen } from "EventTypeList/components/empty-screen";
|
||||||
|
import { LinkIcon } from "lucide-react";
|
||||||
|
|
||||||
|
export function CreateFirstEventTypeView({ slug }: { slug: string }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EmptyScreen
|
||||||
|
Icon={LinkIcon}
|
||||||
|
className="mb-16"
|
||||||
|
headline="Create your first event type"
|
||||||
|
description="Event types enable you to share links that show available times on your calendar and allow people to make bookings with you."
|
||||||
|
buttonRaw={
|
||||||
|
<Button>
|
||||||
|
<a href={`?dialog=new&eventPage=${slug}`}>Create</a>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { EmptyScreen } from "EventTypeList/components/empty-screen";
|
||||||
|
|
||||||
|
export function EmptyEventTypeList({ group }: { group: any }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EmptyScreen
|
||||||
|
headline="This team has no event types"
|
||||||
|
buttonRaw={
|
||||||
|
<Button type="button" className="mt-5">
|
||||||
|
<a href={`?dialog=new&eventPage=${group.profile.slug}&teamId=${group.teamId}`}>Create</a>
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import type { LucideIcon as IconType } from "lucide-react";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import type { SVGComponent } from "@calcom/types/SVGComponent";
|
||||||
|
|
||||||
|
type EmptyScreenProps = {
|
||||||
|
Icon?: SVGComponent | IconType;
|
||||||
|
avatar?: React.ReactElement;
|
||||||
|
headline: string | React.ReactElement;
|
||||||
|
description?: string | React.ReactElement;
|
||||||
|
buttonText?: string;
|
||||||
|
buttonOnClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||||
|
buttonRaw?: ReactNode; // Used incase you want to provide your own button.
|
||||||
|
border?: boolean;
|
||||||
|
dashedBorder?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EmptyScreen({
|
||||||
|
Icon,
|
||||||
|
avatar,
|
||||||
|
headline,
|
||||||
|
description,
|
||||||
|
buttonText,
|
||||||
|
buttonOnClick,
|
||||||
|
buttonRaw,
|
||||||
|
border = true,
|
||||||
|
dashedBorder = true,
|
||||||
|
className,
|
||||||
|
}: EmptyScreenProps & React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
data-testid="empty-screen"
|
||||||
|
className={cn(
|
||||||
|
"flex w-full select-none flex-col items-center justify-center rounded-lg p-7 lg:p-20",
|
||||||
|
border && "border-subtle border",
|
||||||
|
dashedBorder && "border-dashed",
|
||||||
|
className
|
||||||
|
)}>
|
||||||
|
{!avatar ? null : (
|
||||||
|
<div className="flex h-[72px] w-[72px] items-center justify-center rounded-full">{avatar}</div>
|
||||||
|
)}
|
||||||
|
{!Icon ? null : (
|
||||||
|
<div className="bg-emphasis flex h-[72px] w-[72px] items-center justify-center rounded-full ">
|
||||||
|
<Icon className="text-default inline-block h-10 w-10 stroke-[1.3px]" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex max-w-[420px] flex-col items-center">
|
||||||
|
<h2 className={cn("text-semibold font-cal text-emphasis text-center text-xl", !!Icon && "mt-6")}>
|
||||||
|
{headline}
|
||||||
|
</h2>
|
||||||
|
{!!description && (
|
||||||
|
<div className="text-default mb-8 mt-3 text-center text-sm font-normal leading-6">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{!!buttonOnClick && !!buttonText && <Button onClick={(e) => buttonOnClick(e)}>{buttonText}</Button>}
|
||||||
|
{buttonRaw}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue