diff --git a/apps/web/playwright/oidc.e2e.ts b/apps/web/playwright/oidc.e2e.ts
index 0917ec22ccb80e..6cff54faed1a6b 100644
--- a/apps/web/playwright/oidc.e2e.ts
+++ b/apps/web/playwright/oidc.e2e.ts
@@ -49,7 +49,7 @@ test.describe("OIDC", () => {
// Login a user using the OIDC provider.
// The credentials are handled by the provider, so we don't need to create a user in the db.
await page.goto("/auth/login");
- await page.click('[data-testid="saml"]');
+ await page.click('[data-testid="samlAndOidc"]');
// Redirected outide of the app, the user would be redirected to the OIDC provider.
await page.waitForURL(/https:\/\/[^/]+\/oauth2\/v1\/authorize\?.*/);
await page.getByRole("textbox", { name: "Username" }).fill(OIDC_USER_EMAIL);
diff --git a/apps/web/playwright/organization/organization-invitation.e2e.ts b/apps/web/playwright/organization/organization-invitation.e2e.ts
index 70ab68dd458ad0..021ccc16e93303 100644
--- a/apps/web/playwright/organization/organization-invitation.e2e.ts
+++ b/apps/web/playwright/organization/organization-invitation.e2e.ts
@@ -297,6 +297,9 @@ test.describe("Organization", () => {
await test.step("Signing up with the previous username of the migrated user - shouldn't be allowed", async () => {
await page.goto("/signup");
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await page.locator('input[name="username"]').fill(existingUser.username!);
diff --git a/apps/web/playwright/signup.e2e.ts b/apps/web/playwright/signup.e2e.ts
index 348994717c6dc7..227c9483b51424 100644
--- a/apps/web/playwright/signup.e2e.ts
+++ b/apps/web/playwright/signup.e2e.ts
@@ -10,7 +10,26 @@ import { expectInvitationEmailToBeReceived } from "./team/expects";
test.describe.configure({ mode: "parallel" });
-test.describe("Signup Flow Test", async () => {
+test.describe("Signup Main Page Test", async () => {
+ test.beforeEach(async ({ features }) => {
+ features.reset();
+ });
+
+ test("Continue with email button must exist / work", async ({ page }) => {
+ await page.goto("/signup");
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ });
+
+ test("Continue with google button must exist / work", async ({ page }) => {
+ await page.goto("/signup");
+ await expect(page.locator('[data-testid="continue-with-google-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-google-button"]').click();
+ await page.waitForURL("/auth/sso/google");
+ });
+});
+
+test.describe("Email Signup Flow Test", async () => {
test.beforeEach(async ({ features }) => {
features.reset(); // This resets to the inital state not an empt yarray
});
@@ -26,6 +45,9 @@ test.describe("Signup Flow Test", async () => {
await page.goto("/signup");
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
const alertMessage = "Username or email is already taken";
@@ -52,8 +74,10 @@ test.describe("Signup Flow Test", async () => {
});
await page.goto("/signup");
-
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
const alertMessage = "Username or email is already taken";
@@ -85,6 +109,9 @@ test.describe("Signup Flow Test", async () => {
// Signup with premium username name
await page.goto("/signup");
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
// Fill form
await page.locator('input[name="username"]').fill("rock");
@@ -113,6 +140,9 @@ test.describe("Signup Flow Test", async () => {
await page.goto("/signup");
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
// Fill form
await page.locator('input[name="username"]').fill(userToCreate.username);
@@ -132,6 +162,9 @@ test.describe("Signup Flow Test", async () => {
const signupUrlWithParams = "/signup?username=rick-jones&email=rick-jones%40example.com";
await page.goto(signupUrlWithParams);
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
// Fill form
const usernameInput = page.locator('input[name="username"]');
@@ -182,6 +215,7 @@ test.describe("Signup Flow Test", async () => {
const signupUrlWithToken = `/signup?token=${token}`;
await page.goto(signupUrlWithToken);
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
const usernameField = page.locator('input[name="username"]');
const emailField = page.locator('input[name="email"]');
@@ -213,6 +247,9 @@ test.describe("Signup Flow Test", async () => {
await page.goto("/signup");
await expect(page.locator("text=Create your account")).toBeVisible();
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
// Fill form
await page.locator('input[name="username"]').fill(userToCreate.username);
@@ -283,7 +320,9 @@ test.describe("Signup Flow Test", async () => {
const url = new URL(newPage.url());
expect(url.pathname).toBe("/signup");
-
+ await expect(page.locator('[data-testid="continue-with-email-button"]')).toBeVisible();
+ await page.locator('[data-testid="continue-with-email-button"]').click();
+ await expect(page.locator('[data-testid="signup-submit-button"]')).toBeVisible();
// Check required fields
await newPage.locator("input[name=password]").fill(`P4ssw0rd!`);
await newPage.locator("button[type=submit]").click();
diff --git a/apps/web/public/google-icon-colored.svg b/apps/web/public/google-icon-colored.svg
new file mode 100644
index 00000000000000..3f8813d5cafd37
--- /dev/null
+++ b/apps/web/public/google-icon-colored.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index b2f7b3864d09c5..25f060ccf8dfca 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -972,6 +972,8 @@
"signin_with_google": "Sign in with Google",
"signin_with_saml": "Sign in with SAML",
"signin_with_saml_oidc": "Sign in with SAML/OIDC",
+ "continue_with_email": "Continue with email",
+ "continue_with_google": "Continue with Google",
"last_used": "Last used",
"you_will_need_to_generate": "You will need to generate an access token from your old scheduling tool.",
"import": "Import",
@@ -2484,6 +2486,7 @@
"lock_org_users_eventtypes_description": "Prevent members from creating their own event types.",
"add_to_event_type": "Add to event type",
"create_account_password": "Create account password",
+ "create_account_with_saml": "Create Account with SAML",
"error_creating_account_password": "Failed to create account password",
"cannot_create_account_password_cal_provider": "Cannot create account password for cal accounts",
"cannot_create_account_password_already_existing": "Cannot create account password for already created ones",
diff --git a/packages/app-store/stripepayment/lib/utils.ts b/packages/app-store/stripepayment/lib/utils.ts
index 67e1dbda08e99f..3fc19527ce8260 100644
--- a/packages/app-store/stripepayment/lib/utils.ts
+++ b/packages/app-store/stripepayment/lib/utils.ts
@@ -18,5 +18,5 @@ export function getPerSeatPlanPrice(): string {
}
export function getPremiumPlanPriceValue() {
- return "$29/mo";
+ return "$29/month";
}
diff --git a/packages/features/auth/SAMLLogin.tsx b/packages/features/auth/SAMLLogin.tsx
index b5c196bf04d716..75420c5503e32d 100644
--- a/packages/features/auth/SAMLLogin.tsx
+++ b/packages/features/auth/SAMLLogin.tsx
@@ -7,6 +7,7 @@ import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants";
import { LastUsed, useLastUsed } from "@calcom/lib/hooks/useLastUsed";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
+import type { ButtonProps } from "@calcom/ui";
import { Button } from "@calcom/ui";
interface Props {
@@ -19,7 +20,12 @@ const schema = z.object({
email: z.string().email({ message: "Please enter a valid email" }),
});
-export function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Props) {
+export function SAMLLogin({
+ samlTenantID,
+ samlProductID,
+ setErrorMessage,
+ ...buttonProps
+}: Props & ButtonProps) {
const { t } = useLocale();
const methods = useFormContext();
const [lastUsed, setLastUsed] = useLastUsed();
@@ -38,7 +44,7 @@ export function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Prop