Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
DexterStorey authored Dec 20, 2023
2 parents 89daa34 + b384e4a commit c2d53e0
Show file tree
Hide file tree
Showing 29 changed files with 400 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shard: [1, 2, 3, 4, 5]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/dangerous-git-checkout
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Here is what you need to be able to run Cal.com.

- Duplicate `.env.example` to `.env`
- Use `openssl rand -base64 32` to generate a key and add it under `NEXTAUTH_SECRET` in the `.env` file.
- Use `openssl rand -base64 24` to generate a key and add it under `CALENDSO_ENCRYPTION_KEY` in the `.env` file.
- Use `openssl rand -base64 32` to generate a key and add it under `CALENDSO_ENCRYPTION_KEY` in the `.env` file.

5. Setup Node
If your Node version does not meet the project's requirements as instructed by the docs, "nvm" (Node Version Manager) allows using Node at the version required by the project:
Expand Down
30 changes: 25 additions & 5 deletions apps/web/components/eventtype/EventAppsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,25 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
};
};

const getAppDataSetter = (appId: EventTypeAppsList, credentialId?: number): SetAppData => {
const eventTypeFormMetadata = methods.getValues("metadata");

const getAppDataSetter = (
appId: EventTypeAppsList,
appCategories: string[],
credentialId?: number
): SetAppData => {
return function (key, value) {
// Always get latest data available in Form because consequent calls to setData would update the Form but not allAppsData(it would update during next render)
const allAppsDataFromForm = methods.getValues("metadata")?.apps || {};

const appData = allAppsDataFromForm[appId];
setAllAppsData({
...allAppsDataFromForm,
[appId]: {
...appData,
[key]: value,
credentialId,
appCategories,
},
});
};
Expand All @@ -77,10 +85,15 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
appCards.push(
<EventTypeAppCard
getAppData={getAppDataGetter(app.slug as EventTypeAppsList)}
setAppData={getAppDataSetter(app.slug as EventTypeAppsList, app.userCredentialIds[0])}
setAppData={getAppDataSetter(
app.slug as EventTypeAppsList,
app.categories,
app.userCredentialIds[0]
)}
key={app.slug}
app={app}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
{...shouldLockDisableProps("apps")}
/>
);
Expand All @@ -91,7 +104,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
appCards.push(
<EventTypeAppCard
getAppData={getAppDataGetter(app.slug as EventTypeAppsList)}
setAppData={getAppDataSetter(app.slug as EventTypeAppsList, team.credentialId)}
setAppData={getAppDataSetter(app.slug as EventTypeAppsList, app.categories, team.credentialId)}
key={app.slug + team?.credentialId}
app={{
...app,
Expand All @@ -104,6 +117,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
},
}}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
{...shouldLockDisableProps("apps")}
/>
);
Expand Down Expand Up @@ -148,10 +162,15 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
return (
<EventTypeAppCard
getAppData={getAppDataGetter(app.slug as EventTypeAppsList)}
setAppData={getAppDataSetter(app.slug as EventTypeAppsList, app.userCredentialIds[0])}
setAppData={getAppDataSetter(
app.slug as EventTypeAppsList,
app.categories,
app.userCredentialIds[0]
)}
key={app.slug}
app={app}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
{...shouldLockDisableProps("apps")}
/>
);
Expand Down Expand Up @@ -179,10 +198,11 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
{notInstalledApps?.map((app) => (
<EventTypeAppCard
getAppData={getAppDataGetter(app.slug as EventTypeAppsList)}
setAppData={getAppDataSetter(app.slug as EventTypeAppsList)}
setAppData={getAppDataSetter(app.slug as EventTypeAppsList, app.categories)}
key={app.slug}
app={app}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
/>
))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/eventtype/EventTypeSingleLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ function EventTypeSingleLayout({
icon: Users,
info: `${t(eventType.schedulingType?.toLowerCase() ?? "")}${
isManagedEventType
? ` - ${t("count_members", { count: formMethods.watch("children").length || 0 })}`
? ` - ${t("number_member", { count: formMethods.watch("children").length || 0 })}`
: ""
}`,
});
Expand Down
13 changes: 5 additions & 8 deletions apps/web/components/ui/avatar/UserAvatarGroupWithOrg.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,32 @@
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
import type { Team, User } from "@calcom/prisma/client";
import { AvatarGroup } from "@calcom/ui";

type UserAvatarProps = Omit<React.ComponentProps<typeof AvatarGroup>, "items"> & {
users: Pick<User, "organizationId" | "name" | "username">[];
users: (Pick<User, "organizationId" | "name" | "username"> & { bookerUrl: string })[];
organization: Pick<Team, "slug" | "name">;
};

export function UserAvatarGroupWithOrg(props: UserAvatarProps) {
const { users, organization, ...rest } = props;
const orgBranding = useOrgBranding();
const baseUrl = `${orgBranding?.fullDomain ?? CAL_URL}`;
const items = [
{
href: baseUrl,
href: getBookerBaseUrlSync(organization.slug),
image: `${WEBAPP_URL}/team/${organization.slug}/avatar.png`,
alt: organization.name || undefined,
title: organization.name,
},
].concat(
users.map((user) => {
return {
href: `${baseUrl}/${user.username}/?redirect=false`,
href: `${user.bookerUrl}/${user.username}?redirect=false`,
image: getUserAvatarUrl(user),
alt: user.name || undefined,
title: user.name || user.username || "",
};
})
);
users.unshift();
return <AvatarGroup {...rest} items={items} />;
}
12 changes: 12 additions & 0 deletions apps/web/pages/event-types/[type]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";

import checkForMultiplePaymentApps from "@calcom/app-store/_utils/payments/checkForMultiplePaymentApps";
import { getEventLocationType } from "@calcom/app-store/locations";
import { validateCustomEventName } from "@calcom/core/event";
import type { EventLocationType } from "@calcom/core/location";
Expand Down Expand Up @@ -484,6 +485,11 @@ const EventTypePage = (props: EventTypeSetupProps) => {
}
}

// 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 as z.infer<typeof EventTypeMetaDataSchema>))
throw new Error(t("event_setup_multiple_payment_apps_error"));

if (metadata?.apps?.stripe?.paymentOption === "HOLD" && seatsPerTimeSlot) {
throw new Error(t("seats_and_no_show_fee_error"));
}
Expand Down Expand Up @@ -584,6 +590,12 @@ const EventTypePage = (props: EventTypeSetupProps) => {
}
}
}

// 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 as z.infer<typeof EventTypeMetaDataSchema>))
throw new Error(t("event_setup_multiple_payment_apps_error"));

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { availability, ...rest } = input;
updateMutation.mutate({
Expand Down
8 changes: 4 additions & 4 deletions apps/web/playwright/fixtures/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,8 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
});
},
getPaymentCredential: async () => getPaymentCredential(store.page),
setupEventWithPrice: async (eventType: Pick<Prisma.EventType, "id">) =>
setupEventWithPrice(eventType, store.page),
setupEventWithPrice: async (eventType: Pick<Prisma.EventType, "id">, slug: string) =>
setupEventWithPrice(eventType, slug, store.page),
bookAndPayEvent: async (eventType: Pick<Prisma.EventType, "slug">) =>
bookAndPayEvent(user, eventType, store.page),
makePaymentUsingStripe: async () => makePaymentUsingStripe(store.page),
Expand Down Expand Up @@ -693,9 +693,9 @@ export async function apiLogin(
});
}

export async function setupEventWithPrice(eventType: Pick<Prisma.EventType, "id">, page: Page) {
export async function setupEventWithPrice(eventType: Pick<Prisma.EventType, "id">, slug: string, page: Page) {
await page.goto(`/event-types/${eventType?.id}?tabName=apps`);
await page.locator("[data-testid='app-switch']").first().click();
await page.locator(`[data-testid='${slug}-app-switch']`).first().click();
await page.getByPlaceholder("Price").fill("100");
await page.getByTestId("update-eventtype").click();
}
Expand Down
16 changes: 8 additions & 8 deletions apps/web/playwright/integrations-stripe.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test.describe("Stripe integration", () => {
await user.getPaymentCredential();

const eventType = user.eventTypes.find((e) => e.slug === "paid") as Prisma.EventType;
await user.setupEventWithPrice(eventType);
await user.setupEventWithPrice(eventType, "stripe");

// Need to wait for the DB to be updated with the metadata
await page.waitForResponse((res) => res.url().includes("update") && res.status() === 200);
Expand Down Expand Up @@ -104,7 +104,7 @@ test.describe("Stripe integration", () => {
page.click('[id="skip-account-app"]'),
]);

await owner.setupEventWithPrice(teamEvent);
await owner.setupEventWithPrice(teamEvent, "stripe");

// Need to wait for the DB to be updated with the metadata
await page.waitForResponse((res) => res.url().includes("update") && res.status() === 200);
Expand Down Expand Up @@ -134,7 +134,7 @@ test.describe("Stripe integration", () => {
await page.goto("/apps/installed");

await user.getPaymentCredential();
await user.setupEventWithPrice(eventType);
await user.setupEventWithPrice(eventType, "stripe");
await user.bookAndPayEvent(eventType);
// success
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
Expand All @@ -147,7 +147,7 @@ test.describe("Stripe integration", () => {
await page.goto("/apps/installed");

await user.getPaymentCredential();
await user.setupEventWithPrice(eventType);
await user.setupEventWithPrice(eventType, "stripe");

// booking process without payment
await page.goto(`${user.username}/${eventType?.slug}`);
Expand All @@ -171,7 +171,7 @@ test.describe("Stripe integration", () => {
await page.goto("/apps/installed");

await user.getPaymentCredential();
await user.setupEventWithPrice(eventType);
await user.setupEventWithPrice(eventType, "stripe");
await user.bookAndPayEvent(eventType);

// Rescheduling the event
Expand All @@ -194,7 +194,7 @@ test.describe("Stripe integration", () => {
await page.goto("/apps/installed");

await user.getPaymentCredential();
await user.setupEventWithPrice(eventType);
await user.setupEventWithPrice(eventType, "stripe");
await user.bookAndPayEvent(eventType);

await page.click('[data-testid="cancel"]');
Expand All @@ -214,7 +214,7 @@ test.describe("Stripe integration", () => {
await page.goto("/apps/installed");

await user.getPaymentCredential();
await user.setupEventWithPrice(eventType);
await user.setupEventWithPrice(eventType, "stripe");
await user.bookAndPayEvent(eventType);
await user.confirmPendingPayment();
});
Expand Down Expand Up @@ -264,7 +264,7 @@ test.describe("Stripe integration", () => {
await page.locator("#event-type-form").getByRole("switch").click();

// Set price
await page.getByTestId("price-input-stripe").fill("200");
await page.getByTestId("stripe-price-input").fill("200");

// Select currency in dropdown
await page.getByTestId("stripe-currency-select").click();
Expand Down
Loading

0 comments on commit c2d53e0

Please sign in to comment.