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 && (