Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: virtual queues tab in insights #18260

Merged
merged 26 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
97eb034
creat virtual-queues page in insights
Dec 17, 2024
cbcf01b
refactor SingleForm
Dec 17, 2024
2e00ee9
fix dialog issue
Dec 18, 2024
34a5745
first virtual queues logic
Dec 18, 2024
bf7e389
finish showing ordered hosts in insights/virtual-queues
Dec 18, 2024
468a5f1
add missing translation
Dec 19, 2024
3fec04e
remove comment
Dec 19, 2024
722d4c7
use raw sql to prevent nested loops
sean-brydon Dec 19, 2024
ba43ac8
Merge branch 'main' into feat/temp-virtual-queues
sean-brydon Dec 19, 2024
c398873
Apply suggestions from code review
PeerRich Dec 19, 2024
5c2e8fd
Merge branch 'main' into feat/temp-virtual-queues
zomars Dec 19, 2024
075d4db
UI fix
Dec 20, 2024
7fb43a8
Merge branch 'main' into feat/temp-virtual-queues
CarinaWolli Dec 20, 2024
383c155
remove type
Dec 22, 2024
fce9bce
define type
Dec 22, 2024
4dc9a7f
type changes
Dec 22, 2024
9eaa3f6
use appschema only when necessary
hariombalhara Dec 23, 2024
0ff5d1a
Merge remote-tracking branch 'origin/fix-ts-crash-api-v2' into feat/t…
hariombalhara Dec 23, 2024
cd127e8
Variable casing change and type fixes
hariombalhara Dec 23, 2024
60913c9
Merge remote-tracking branch 'origin/fix-ts-crash-api-v2' into feat/t…
hariombalhara Dec 23, 2024
564cec0
Another fix
hariombalhara Dec 23, 2024
4819bad
Another missing ts fix
hariombalhara Dec 23, 2024
7954f7e
Merge remote-tracking branch 'origin/fix-ts-crash-api-v2' into feat/t…
hariombalhara Dec 23, 2024
34c438e
Merge branch 'main' into feat/temp-virtual-queues
Dec 23, 2024
3779792
add missing import
Dec 23, 2024
cdd762d
check for undefined metadata
Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions apps/web/app/insights/virtual-queues/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";

import { getServerSideProps } from "@lib/insights/getServerSideProps";

import InsightsVirtualQueuesPage from "~/insights/insights-virtual-queues-view";

export const generateMetadata = async () =>
await _generateMetadata(
(t) => t("insights"),
(t) => t("insights_subtitle")
);

const getData = withAppDirSsr(getServerSideProps);

export default WithLayout({ getLayout: null, getData, Page: InsightsVirtualQueuesPage });
22 changes: 14 additions & 8 deletions apps/web/modules/apps/installation/[[...step]]/step-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { Team } from "@calcom/prisma/client";
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import type { AppMeta } from "@calcom/types/App";
Expand Down Expand Up @@ -243,15 +244,20 @@ const OnboardingPage = ({
const promises = group.eventTypes
.filter((eventType) => eventType.selected)
.map((value: TEventType) => {
// Prevent two payment apps to be enabled
// Ok to cast type here because this metadata will be updated as the event type metadata
if (
checkForMultiplePaymentApps(value.metadata as z.infer<typeof EventTypeMetaDataSchema>)
)
throw new Error(t("event_setup_multiple_payment_apps_error"));
if (value.metadata?.apps?.stripe?.paymentOption === "HOLD" && value.seatsPerTimeSlot) {
throw new Error(t("seats_and_no_show_fee_error"));
if (value.metadata) {
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(value.metadata);
// Prevent two payment apps to be enabled
// Ok to cast type here because this metadata will be updated as the event type metadata
if (checkForMultiplePaymentApps(metadata))
throw new Error(t("event_setup_multiple_payment_apps_error"));
if (
value.metadata?.apps?.stripe?.paymentOption === "HOLD" &&
value.seatsPerTimeSlot
) {
throw new Error(t("seats_and_no_show_fee_error"));
}
}

let updateObject: TUpdateObject = { id: value.id };
if (isConferencing) {
updateObject = {
Expand Down
16 changes: 13 additions & 3 deletions apps/web/modules/bookings/views/bookings-single-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat";
import { localStorage } from "@calcom/lib/webstorage";
import { BookingStatus, SchedulingType } from "@calcom/prisma/enums";
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { bookingMetadataSchema, eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";
import { trpc } from "@calcom/trpc/react";
import {
Alert,
Expand All @@ -61,6 +61,7 @@ import {
showToast,
EmptyScreen,
Icon,
HeadSeo,
} from "@calcom/ui";
import CancelBooking from "@calcom/web/components/booking/CancelBooking";
import EventReservationSchema from "@calcom/web/components/schemas/EventReservationSchema";
Expand Down Expand Up @@ -229,7 +230,13 @@ export default function Success(props: PageProps) {
t,
};

const giphyAppData = getEventTypeAppData(eventType, "giphy");
const giphyAppData = getEventTypeAppData(
{
...eventType,
metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata),
},
"giphy"
);
const giphyImage = giphyAppData?.thankYouPage;
const isRoundRobin = eventType.schedulingType === SchedulingType.ROUND_ROBIN;

Expand Down Expand Up @@ -433,7 +440,10 @@ export default function Success(props: PageProps) {
</Link>
</div>
)}
<BookingPageTagManager eventType={eventType} />
<HeadSeo origin={getOrgFullOrigin(orgSlug)} title={title} description={title} />
<BookingPageTagManager
eventType={{ ...eventType, metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata) }}
/>
<main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
<div className={classNames("overflow-y-auto", isEmbed ? "" : "z-50 ")}>
<div
Expand Down
49 changes: 49 additions & 0 deletions apps/web/modules/insights/insights-virtual-queues-view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use client";

import { useState } from "react";

import { TestForm } from "@calcom/app-store/routing-forms/components/SingleForm";
import type { RoutingForm } from "@calcom/app-store/routing-forms/types/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Label, Select } from "@calcom/ui";

import InsightsLayout from "./layout";

export default function InsightsVirtualQueuesPage() {
const { t } = useLocale();
const { data: routingForms, isLoading: isRoutingFormsLoading } =
trpc.viewer.insights.getUserRelevantTeamRoutingForms.useQuery();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only returns routing forms of teams the user is a member of + that have at least one route that redirects to a weighted round robin event type where the user is a host of


const [selectedForm, setSelectedForm] = useState<RoutingForm | undefined>(
routingForms && routingForms.length > 0 ? routingForms[0] : undefined
);

if (routingForms && !selectedForm && routingForms.length > 0) {
setSelectedForm(routingForms[0]);
}

return (
<InsightsLayout>
<Label>{t("routing_form")}</Label>
<Select
placeholder="Select project"
options={routingForms?.map((form) => ({ label: form.name, value: form.id })) ?? []}
isLoading={isRoutingFormsLoading}
className="w-60"
onChange={(e) => {
if (e && routingForms) {
const form = routingForms.find((form) => form.id === e.value);
setSelectedForm(form);
}
}}
value={selectedForm ? { label: selectedForm.name, value: selectedForm.id } : undefined}
/>
<div className="mt-10">
{selectedForm ? <TestForm form={selectedForm} showAllData={false} /> : <></>}
</div>
</InsightsLayout>
);

return <></>;
}
3 changes: 3 additions & 0 deletions apps/web/modules/test-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ vi.mock("@calcom/prisma/zod-utils", () => ({
EventTypeMetaDataSchema: {
parse: vi.fn(),
},
eventTypeMetaDataSchemaWithTypedApps: {
parse: vi.fn(),
},
bookingMetadataSchema: {
parse: vi.fn(),
},
Expand Down
10 changes: 5 additions & 5 deletions apps/web/playwright/integrations-stripe.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type Prisma from "@prisma/client";

import prisma from "@calcom/prisma";
import { SchedulingType } from "@calcom/prisma/enums";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";

import { test, todo } from "./lib/fixtures";
import type { Fixtures } from "./lib/fixtures";
Expand Down Expand Up @@ -51,7 +51,7 @@ test.describe("Stripe integration skip true", () => {
},
});

const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);

const stripeAppMetadata = metadata?.apps?.stripe;

Expand Down Expand Up @@ -93,7 +93,7 @@ test.describe("Stripe integration skip true", () => {
},
});

const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);

const stripeAppMetadata = metadata?.apps?.stripe;

Expand Down Expand Up @@ -291,7 +291,7 @@ test.describe("Stripe integration with the new app install flow skip false", ()
});

for (const eventTypeMetadata of eventTypeMetadatas) {
const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);
const stripeAppMetadata = metadata?.apps?.stripe;
expect(stripeAppMetadata).toHaveProperty("credentialId");
expect(typeof stripeAppMetadata?.credentialId).toBe("number");
Expand Down Expand Up @@ -328,7 +328,7 @@ test.describe("Stripe integration with the new app install flow skip false", ()
},
});

const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);

const stripeAppMetadata = metadata?.apps?.stripe;

Expand Down
7 changes: 7 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2895,5 +2895,12 @@
"reassigned": "Reassigned",
"rerouted": "Rerouted",
"salesforce_assigned": "Salesforce assignment",
"router_position": "Router Position",
"show_matching_hosts": "Show matching hosts",
"no_active_queues": "No active queues for this input",
"weight": "Weight",
"calibration": "Calibration",
"shortfall": "Shortfall",
"routing_form": "Routing Form",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
6 changes: 4 additions & 2 deletions packages/app-store/_utils/getEventTypeAppData.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { z } from "zod";

import type { BookerEvent } from "@calcom/features/bookings/types";
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";

export type EventTypeApps = NonNullable<NonNullable<z.infer<typeof EventTypeMetaDataSchema>>["apps"]>;
export type EventTypeApps = NonNullable<
NonNullable<z.infer<typeof eventTypeMetaDataSchemaWithTypedApps>>["apps"]
>;
export type EventTypeAppsList = keyof EventTypeApps;

export const getEventTypeAppData = <T extends EventTypeAppsList>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type z from "zod";

import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils";

import type { appDataSchemas } from "../../apps.schemas.generated";

Expand All @@ -11,7 +11,12 @@ import type { appDataSchemas } from "../../apps.schemas.generated";
* @returns boolean
*/
const checkForMultiplePaymentApps = (
metadata: z.infer<typeof EventTypeMetaDataSchema>,
metadata:
| {
apps?: z.infer<typeof eventTypeAppMetadataOptionalSchema>;
}
| null
| undefined,
inclusive = false
) => {
let enabledPaymentApps = 0;
Expand Down
Loading
Loading