/teams improvements (#6831)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
co-authored by
Alex van Andel
parent
7ee5967075
commit
ef78db7856
@@ -1557,6 +1557,11 @@
|
||||
"email_no_user_step_four":"Join {{teamName}}",
|
||||
"email_no_user_signoff":"Happy Scheduling from the {{appName}} team",
|
||||
"impersonation_user_tip": "You are about to impersonate a user, which means you can make changes on their behalf. Please be careful.",
|
||||
"scheduler": "{Scheduler}",
|
||||
"available_variables": "Available variables",
|
||||
"scheduler": "{Scheduler}"
|
||||
"recommended_next_steps": "Recommended next steps",
|
||||
"create_a_managed_event": "Create a managed event type",
|
||||
"meetings_are_better_with_the_right": "Meetings are better with the right team members there. Invite them now.",
|
||||
"collective_or_roundrobin": "Collective or round-robin",
|
||||
"book_your_team_members": "Book your team members together with collective events or cycle through to get the right person with round-robin."
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||
import { showToast } from "@calcom/ui";
|
||||
import { Card, showToast } from "@calcom/ui";
|
||||
import { FiUserPlus, FiUsers, FiUnlock, FiEdit } from "@calcom/ui/components/icon";
|
||||
|
||||
import TeamListItem from "./TeamListItem";
|
||||
|
||||
@@ -12,6 +14,8 @@ interface Props {
|
||||
export default function TeamList(props: Props) {
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const { t } = useLocale();
|
||||
|
||||
const [hideDropdown, setHideDropdown] = useState(false);
|
||||
|
||||
function selectAction(action: string, teamId: number) {
|
||||
@@ -47,6 +51,72 @@ export default function TeamList(props: Props) {
|
||||
setHideDropdown={setHideDropdown}
|
||||
/>
|
||||
))}
|
||||
|
||||
{/* only show recommended steps when there is only one team */}
|
||||
{props.teams.length === 1 && (
|
||||
<>
|
||||
{props.teams.map(
|
||||
(team, i) =>
|
||||
i === 0 && (
|
||||
<div className="bg-gray-100 p-6">
|
||||
<h3 className="mb-4 text-sm font-semibold text-gray-900">{t("recommended_next_steps")}</h3>
|
||||
<div className="grid-col-1 grid gap-2 md:grid-cols-3">
|
||||
<Card
|
||||
icon={<FiUserPlus className="h-5 w-5 text-green-700" />}
|
||||
variant="basic"
|
||||
title={t("invite_team_member")}
|
||||
description="Meetings are better with the right team members there. Invite them now."
|
||||
actionButton={{
|
||||
href: "/settings/teams/" + team.id + "/members",
|
||||
child: t("invite"),
|
||||
}}
|
||||
/>
|
||||
{/* @TODO: uncomment once managed event types is live
|
||||
<Card
|
||||
icon={<FiUnlock className="h-5 w-5 text-blue-700" />}
|
||||
variant="basic"
|
||||
title={t("create_a_managed_event")}
|
||||
description={t("meetings_are_better_with_the_right")}
|
||||
actionButton={{
|
||||
href:
|
||||
"/event-types?dialog=new-eventtype&eventPage=team%2F" +
|
||||
team.slug +
|
||||
"&teamId=" +
|
||||
team.id +
|
||||
"&managed=true",
|
||||
child: t("create"),
|
||||
}}
|
||||
/> */}
|
||||
<Card
|
||||
icon={<FiUsers className="h-5 w-5 text-orange-700" />}
|
||||
variant="basic"
|
||||
title={t("collective_or_roundrobin")}
|
||||
description={t("book_your_team_members")}
|
||||
actionButton={{
|
||||
href:
|
||||
"/event-types?dialog=new-eventtype&eventPage=team%2F" +
|
||||
team.slug +
|
||||
"&teamId=" +
|
||||
team.id,
|
||||
child: t("create"),
|
||||
}}
|
||||
/>
|
||||
<Card
|
||||
icon={<FiEdit className="h-5 w-5 text-purple-700" />}
|
||||
variant="basic"
|
||||
title={t("appearance")}
|
||||
description={t("appearance_subtitle")}
|
||||
actionButton={{
|
||||
href: "/settings/teams/" + team.id + "/appearance",
|
||||
child: t("edit"),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ export function TeamsListing() {
|
||||
<UpgradeTip
|
||||
title="calcom_is_better_with_team"
|
||||
description="add_your_team_members"
|
||||
emptyTitle="no_teams"
|
||||
emptyDescription="no_teams_description"
|
||||
features={features}
|
||||
background="/team-banner-background.jpg"
|
||||
buttons={
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useMemo } from "react";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
@@ -11,6 +10,8 @@ import { FiUsers } from "@calcom/ui/components/icon";
|
||||
export function UpgradeTip({
|
||||
dark,
|
||||
title,
|
||||
emptyTitle,
|
||||
emptyDescription,
|
||||
description,
|
||||
background,
|
||||
features,
|
||||
@@ -21,6 +22,9 @@ export function UpgradeTip({
|
||||
dark?: boolean;
|
||||
title: string;
|
||||
description: string;
|
||||
/* overwrite EmptyScreen text */
|
||||
emptyTitle?: string;
|
||||
emptyDescription?: string;
|
||||
background: string;
|
||||
features: Array<{ icon: JSX.Element; title: string; description: string }>;
|
||||
buttons?: JSX.Element;
|
||||
@@ -36,7 +40,14 @@ export function UpgradeTip({
|
||||
if (isParentLoading || isLoading) return <>{isParentLoading}</>;
|
||||
|
||||
if (!isCalcom)
|
||||
return <EmptyScreen Icon={FiUsers} headline={title} description={description} buttonRaw={buttons} />;
|
||||
return (
|
||||
<EmptyScreen
|
||||
Icon={FiUsers}
|
||||
headline={emptyTitle ? t(emptyTitle) : t(title)}
|
||||
description={emptyDescription ? t(emptyDescription) : t(description)}
|
||||
buttonRaw={buttons}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
// @TODO: turn this into a more generic component that has the same Props API as MUI https://mui.com/material-ui/react-card/
|
||||
import Link from "next/link";
|
||||
import { ReactNode } from "react";
|
||||
import React from "react";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { FiArrowRight } from "@calcom/ui/components/icon";
|
||||
|
||||
import { Button } from "../button";
|
||||
|
||||
export type BaseCardProps = {
|
||||
image?: string;
|
||||
icon?: ReactNode;
|
||||
variant: keyof typeof cardTypeByVariant;
|
||||
imageProps?: JSX.IntrinsicElements["img"];
|
||||
title: string;
|
||||
@@ -26,10 +29,12 @@ export type BaseCardProps = {
|
||||
thumbnailUrl?: string;
|
||||
};
|
||||
|
||||
// @TODO: use CVA
|
||||
|
||||
const cardTypeByVariant = {
|
||||
AppStore: {
|
||||
basic: {
|
||||
image: "w-10 h-auto",
|
||||
card: "p-5 w-64",
|
||||
card: "p-5",
|
||||
title: "text-base",
|
||||
description: "text-sm leading-[18px] text-gray-500 font-normal",
|
||||
},
|
||||
@@ -50,6 +55,7 @@ const cardTypeByVariant = {
|
||||
export function Card({
|
||||
image,
|
||||
title,
|
||||
icon,
|
||||
description,
|
||||
variant,
|
||||
actionButton,
|
||||
@@ -64,33 +70,36 @@ export function Card({
|
||||
className={classNames(
|
||||
containerProps?.className,
|
||||
cardTypeByVariant[variant].card,
|
||||
"rounded-md border border-gray-200 bg-white"
|
||||
"flex flex-col justify-between rounded-md border border-gray-200 bg-white"
|
||||
)}
|
||||
{...containerProps}>
|
||||
{image && (
|
||||
<img
|
||||
src={image}
|
||||
// Stops eslint complaining - not smart enough to realise it comes from ...imageProps
|
||||
alt={imageProps?.alt}
|
||||
className={classNames(imageProps?.className, cardTypeByVariant[variant].image, "mb-4")}
|
||||
{...imageProps}
|
||||
/>
|
||||
)}
|
||||
<h5
|
||||
title={title}
|
||||
className={classNames(
|
||||
cardTypeByVariant[variant].title,
|
||||
"line-clamp-1 font-bold leading-5 text-gray-900"
|
||||
)}>
|
||||
{title}
|
||||
</h5>
|
||||
{description && (
|
||||
<p
|
||||
title={description.toString()}
|
||||
className={classNames(cardTypeByVariant[variant].description, "pt-1")}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
<div>
|
||||
{icon && icon}
|
||||
{image && (
|
||||
<img
|
||||
src={image}
|
||||
// Stops eslint complaining - not smart enough to realise it comes from ...imageProps
|
||||
alt={imageProps?.alt}
|
||||
className={classNames(imageProps?.className, cardTypeByVariant[variant].image)}
|
||||
{...imageProps}
|
||||
/>
|
||||
)}
|
||||
<h5
|
||||
title={title}
|
||||
className={classNames(
|
||||
cardTypeByVariant[variant].title,
|
||||
"line-clamp-1 mt-4 font-bold leading-5 text-gray-900"
|
||||
)}>
|
||||
{title}
|
||||
</h5>
|
||||
{description && (
|
||||
<p
|
||||
title={description.toString()}
|
||||
className={classNames(cardTypeByVariant[variant].description, "pt-1")}>
|
||||
{description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{variant === "SidebarCard" && (
|
||||
<a
|
||||
onClick={actionButton?.onClick}
|
||||
@@ -120,12 +129,16 @@ export function Card({
|
||||
<img alt="play feature video" src={thumbnailUrl} />
|
||||
</a>
|
||||
)}
|
||||
{variant === "AppStore" && (
|
||||
<Button color="secondary" href={actionButton?.href} size="lg" className="mt-10 w-full">
|
||||
{/* Force it to be centered as this usecase of a button is off - doesnt meet normal sizes */}
|
||||
<div className="mx-auto">{actionButton?.child}</div>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* TODO: this should be CardActions https://mui.com/material-ui/api/card-actions/ */}
|
||||
<div>
|
||||
{variant === "basic" && (
|
||||
<Button color="secondary" href={actionButton?.href} className="mt-10" EndIcon={FiArrowRight}>
|
||||
{actionButton?.child}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{variant === "SidebarCard" && (
|
||||
<div className="mt-2 flex items-center justify-between">
|
||||
{learnMore && (
|
||||
|
||||
Reference in New Issue
Block a user