diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 890138ac50..84f15b803e 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -313,6 +313,7 @@ "past_bookings": "Your past bookings will show up here.", "cancelled_bookings": "Your cancelled bookings will show up here.", "unconfirmed_bookings": "Your unconfirmed bookings will show up here.", + "unconfirmed_bookings_tooltip": "Unconfirmed bookings", "on": "on", "and": "and", "calendar_shows_busy_between": "Your calendar shows you as busy between", diff --git a/packages/features/bookings/UnconfirmedBookingBadge.tsx b/packages/features/bookings/UnconfirmedBookingBadge.tsx new file mode 100644 index 0000000000..538c8f6d2a --- /dev/null +++ b/packages/features/bookings/UnconfirmedBookingBadge.tsx @@ -0,0 +1,21 @@ +import Link from "next/link"; + +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { trpc } from "@calcom/trpc/react"; +import Badge from "@calcom/ui/v2/core/Badge"; + +export default function UnconfirmedBookingBadge() { + const { t } = useLocale(); + const { data: unconfirmedBookingCount } = trpc.useQuery(["viewer.bookingUnconfirmedCount"]); + if (!unconfirmedBookingCount) return null; + else + return ( + + + + {unconfirmedBookingCount} + + + + ); +} diff --git a/packages/trpc/server/routers/viewer.tsx b/packages/trpc/server/routers/viewer.tsx index 00ff75643c..f407e5ab84 100644 --- a/packages/trpc/server/routers/viewer.tsx +++ b/packages/trpc/server/routers/viewer.tsx @@ -587,6 +587,7 @@ const loggedInViewerRouter = createProtectedRouter() status: { notIn: [BookingStatus.CANCELLED], }, + userId: user.id, }, }); @@ -1437,6 +1438,34 @@ const loggedInViewerRouter = createProtectedRouter() }, }); }, + }) + .query("bookingUnconfirmedCount", { + async resolve({ ctx }) { + const { prisma, user } = ctx; + const count = await prisma.booking.count({ + where: { + status: BookingStatus.PENDING, + userId: user.id, + endTime: { gt: new Date() }, + }, + }); + const recurringGrouping = await prisma.booking.groupBy({ + by: ["recurringEventId"], + _count: { + recurringEventId: true, + }, + where: { + recurringEventId: { not: { equals: null } }, + status: { equals: "PENDING" }, + userId: user.id, + }, + }); + return recurringGrouping.reduce((prev, current) => { + // recurringEventId is the total number of recurring instances for a booking + // we need to substract all but one, to represent a single recurring booking + return prev - (current._count?.recurringEventId - 1); + }, count); + }, }); export const viewerRouter = createRouter() diff --git a/packages/trpc/server/routers/viewer/bookings.tsx b/packages/trpc/server/routers/viewer/bookings.tsx index 1742443f64..b1581b7f9b 100644 --- a/packages/trpc/server/routers/viewer/bookings.tsx +++ b/packages/trpc/server/routers/viewer/bookings.tsx @@ -20,6 +20,7 @@ import getWebhooks from "@calcom/features/webhooks/utils/getWebhooks"; import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib"; import logger from "@calcom/lib/logger"; import { getTranslation } from "@calcom/lib/server"; +import prisma from "@calcom/prisma"; import { bookingConfirmPatchBodySchema } from "@calcom/prisma/zod-utils"; import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar"; diff --git a/packages/ui/v2/core/Badge.tsx b/packages/ui/v2/core/Badge.tsx index ce27865052..ae3703e592 100644 --- a/packages/ui/v2/core/Badge.tsx +++ b/packages/ui/v2/core/Badge.tsx @@ -24,17 +24,19 @@ export type BadgeProps = { size?: keyof typeof classNameBySize; StartIcon?: Icon; bold?: boolean; + rounded?: boolean; } & JSX.IntrinsicElements["div"]; export const Badge = function Badge(props: BadgeProps) { - const { variant, className, size = "default", StartIcon, bold, ...passThroughProps } = props; + const { variant, className, size = "default", rounded, StartIcon, bold, ...passThroughProps } = props; return (
, }, { name: "availability", @@ -563,7 +567,10 @@ const NavigationItem: React.FC<{ /> )} {isLocaleReady ? ( - {t(item.name)} + +
{t(item.name)}
+ {item.badge && item.badge} +
) : ( )} @@ -619,6 +626,7 @@ const MobileNavigationItem: React.FC<{ + {item.badge &&
{item.badge}
} {item.icon && (