This commit is contained in:
sean-brydon
2023-01-24 15:27:05 +00:00
committed by GitHub
parent 6ccc19985f
commit 905a110377
9 changed files with 85 additions and 51 deletions
@@ -24,6 +24,7 @@
"rejection_confirmation": "Reject the booking",
"manage_this_event": "Manage this event",
"invite_team_member": "Invite team member",
"invite_team_notifcation_badge":"Inv.",
"your_event_has_been_scheduled": "Your event has been scheduled",
"your_event_has_been_scheduled_recurring": "Your recurring event has been scheduled",
"accept_our_license": "Accept our license by changing the .env variable <1>NEXT_PUBLIC_LICENSE_CONSENT</1> to '{{agree}}'.",
@@ -1313,6 +1314,7 @@
"password_reset_leading": "If you don't receive an email soon, check that the email address you entered is correct, check your spam folder or reach out to support if the issue persists.",
"password_updated": "Password updated!",
"pending_payment": "Pending payment",
"pending_invites":"Pending Invites",
"confirmation_page_rainbow": "Token gate your event with tokens or NFTs on Ethereum, Polygon, and more.",
"not_on_cal": "Not on {{appName}}",
"no_calendar_installed": "No calendar installed",
@@ -36,19 +36,17 @@ export default function TeamList(props: Props) {
}
return (
<div>
<ul className="mb-2 divide-y divide-neutral-200 rounded border bg-white">
{props.teams.map((team) => (
<TeamListItem
key={team?.id as number}
team={team}
onActionSelect={(action: string) => selectAction(action, team?.id as number)}
isLoading={deleteTeamMutation.isLoading}
hideDropdown={hideDropdown}
setHideDropdown={setHideDropdown}
/>
))}
</ul>
</div>
<ul className="mb-2 divide-y divide-neutral-200 rounded border bg-white">
{props.teams.map((team) => (
<TeamListItem
key={team?.id as number}
team={team}
onActionSelect={(action: string) => selectAction(action, team?.id as number)}
isLoading={deleteTeamMutation.isLoading}
hideDropdown={hideDropdown}
setHideDropdown={setHideDropdown}
/>
))}
</ul>
);
}
@@ -67,6 +67,7 @@ export default function TeamListItem(props: Props) {
const acceptOrLeaveMutation = trpc.viewer.teams.acceptOrLeave.useMutation({
onSuccess: () => {
utils.viewer.teams.list.invalidate();
utils.viewer.teams.listInvites.invalidate();
},
});
@@ -88,12 +89,12 @@ export default function TeamListItem(props: Props) {
if (!team) return <></>;
const teamInfo = (
<div className="flex px-5 py-5">
<div className="item-center flex px-5 py-5">
<Avatar
size="sm"
size="md"
imageSrc={getPlaceholderAvatar(team?.logo, team?.name as string)}
alt="Team Logo"
className="min-h-9 min-w-9 h-9 w-9 rounded-full"
className="inline-flex justify-center"
/>
<div className="inline-block ltr:ml-3 rtl:mr-3">
<span className="text-sm font-bold text-gray-700">{team.name}</span>
@@ -105,7 +106,7 @@ export default function TeamListItem(props: Props) {
);
return (
<li className="divide-y">
<li className="">
<MemberInvitationModal
isOpen={openMemberInvitationModal}
onExit={() => {
@@ -140,13 +141,14 @@ export default function TeamListItem(props: Props) {
<div className="px-5 py-5">
{isInvitee ? (
<>
<div className="hidden sm:block">
<div className="hidden justify-center sm:flex">
<Button type="button" color="secondary" onClick={declineInvite}>
{t("reject")}
</Button>
<Button
type="button"
color="primary"
color="secondary"
StartIcon={FiCheck}
className="ltr:ml-2 ltr:mr-2 rtl:ml-2"
onClick={acceptInvite}>
{t("accept")}
@@ -1,10 +1,10 @@
import { useState } from "react";
import { useState, useMemo } from "react";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Alert, Button, ButtonGroup } from "@calcom/ui";
import { Alert, Button, ButtonGroup, Label } from "@calcom/ui";
import { FiUsers, FiRefreshCcw, FiUserPlus, FiMail, FiVideo, FiEyeOff } from "@calcom/ui/components/icon";
import { UpgradeTip } from "../../../tips";
@@ -21,7 +21,8 @@ export function TeamsListing() {
},
});
const teams = data?.filter((m) => m.accepted) || [];
const teams = useMemo(() => data?.filter((m) => m.accepted) || [], [data]);
const invites = useMemo(() => data?.filter((m) => !m.accepted) || [], [data]);
const features = [
{
@@ -56,15 +57,19 @@ export function TeamsListing() {
},
];
if (isLoading) {
return <SkeletonLoaderTeamList />;
}
return (
<>
{!!errorMessage && <Alert severity="error" title={errorMessage} />}
<UpgradeTip
title="calcom_is_better_with_team"
description="add_your_team_members"
features={features}
background="/team-banner-background.jpg"
isParentLoading={isLoading && <SkeletonLoaderTeamList />}
buttons={
<div className="space-y-2 rtl:space-x-reverse sm:space-x-2">
<ButtonGroup>
@@ -77,7 +82,17 @@ export function TeamsListing() {
</ButtonGroup>
</div>
}>
<TeamList teams={teams} />
<>
{invites.length > 0 && (
<div className="mb-6 rounded-md bg-gray-100 p-5">
<Label className="dark:text-darkgray-900 pb-2 font-semibold text-gray-900">
{t("pending_invites")}
</Label>
<TeamList teams={invites} />
</div>
)}
{teams.length > 0 && <TeamList teams={teams} />}
</>
</UpgradeTip>
</>
);
+3
View File
@@ -61,6 +61,8 @@ import {
FiArrowLeft,
} from "@calcom/ui/components/icon";
import { TeamInviteBadge } from "./TeamInviteBadge";
/* TODO: Migate this */
export const ONBOARDING_INTRODUCED_AT = dayjs("September 1 2021").toISOString();
@@ -474,6 +476,7 @@ const navigation: NavigationItemType[] = [
href: "/teams",
icon: FiUsers,
onlyDesktop: true,
badge: <TeamInviteBadge />,
},
{
name: "apps",
@@ -0,0 +1,12 @@
import { useTeamInvites } from "@calcom/lib/hooks/useHasTeamPlan";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Badge } from "@calcom/ui";
export function TeamInviteBadge() {
const { isLoading, listInvites } = useTeamInvites();
const { t } = useLocale();
if (isLoading || !listInvites || listInvites.length === 0) return null;
return <Badge variant="default">{t("invite_team_notifcation_badge")}</Badge>;
}
+11 -26
View File
@@ -4,14 +4,10 @@ import type { ReactNode } from "react";
import { classNames } from "@calcom/lib";
import { useHasTeamPlan } from "@calcom/lib/hooks/useHasTeamPlan";
import { useLocale } from "@calcom/lib/hooks/useLocale";
// import isCalcom from "@calcom/lib/isCalcom";
import { trpc } from "@calcom/trpc/react";
import isCalcom from "@calcom/lib/isCalcom";
import { EmptyScreen } from "@calcom/ui";
import { FiUsers } from "@calcom/ui/components/icon";
import TeamList from "../ee/teams/components/TeamList";
const isCalcom = true;
export function UpgradeTip({
dark,
title,
@@ -32,19 +28,16 @@ export function UpgradeTip({
children: JSX.Element;
isParentLoading?: ReactNode;
}) {
const { data } = trpc.viewer.teams.list.useQuery();
const invites = useMemo(() => data?.filter((m) => !m.accepted) || [], [data]);
const { t } = useLocale();
const { isLoading, hasTeamPlan } = useHasTeamPlan();
if (hasTeamPlan) return children;
if (isParentLoading || isLoading) return <>{isParentLoading}</>;
if (!isCalcom)
return <EmptyScreen Icon={FiUsers} headline={title} description={description} buttonRaw={buttons} />;
if (isParentLoading || isLoading) return <>{isParentLoading}</>;
return (
<>
<div className="-mt-10 rtl:ml-4 sm:mt-0 md:rtl:ml-0 lg:-mt-6">
@@ -63,23 +56,15 @@ export function UpgradeTip({
{buttons}
</div>
</div>
{invites.length > 0 && (
<div className="my-4">
<h3 className="font-cal mb-4 text-xl">{t("open_invitations")}</h3>
<TeamList teams={invites} />
</div>
)}
<div className="mt-4 grid-cols-3 md:grid md:gap-4">
{invites.length === 0 &&
features.map((feature) => (
<div
key={feature.title}
className="mb-4 min-h-[180px] w-full rounded-md bg-gray-50 p-8 md:mb-0">
{feature.icon}
<h2 className="font-cal mt-4 text-lg">{feature.title}</h2>
<p className="text-gray-700">{feature.description}</p>
</div>
))}
{features.map((feature) => (
<div key={feature.title} className="mb-4 min-h-[180px] w-full rounded-md bg-gray-50 p-8 md:mb-0">
{feature.icon}
<h2 className="font-cal mt-4 text-lg">{feature.title}</h2>
<p className="text-gray-700">{feature.description}</p>
</div>
))}
</div>
</div>
</>
+6
View File
@@ -6,4 +6,10 @@ export function useHasTeamPlan() {
return { isLoading: hasTeam.isLoading, hasTeamPlan: hasTeam.data?.hasTeamPlan || false };
}
export function useTeamInvites() {
const listInvites = trpc.viewer.teams.listInvites.useQuery();
return { isLoading: listInvites.isLoading, listInvites: listInvites.data };
}
export default useHasTeamPlan;
@@ -700,4 +700,15 @@ export const viewerTeamsRouter = router({
});
return { hasTeamPlan: !!hasTeamPlan };
}),
listInvites: authedProcedure.query(async ({ ctx }) => {
const userId = ctx.user.id;
return await ctx.prisma.membership.findMany({
where: {
user: {
id: userId,
},
accepted: false,
},
});
}),
});