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/3796 new UI for signin #4369

Merged
merged 19 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
32fd797
3796: Added new v2 design for sign in page.
JeroenReumkens Sep 9, 2022
a3ee7ef
feat: 3796 Final UI tweaks for v2 design login page, and added passwo…
JeroenReumkens Sep 11, 2022
e180eac
feat: 3976 added story for passwordfield.
JeroenReumkens Sep 11, 2022
7aaf093
Merge branch 'main' into feat/3796-new-ui-for-signin
JeroenReumkens Sep 11, 2022
aadf60e
Merge branch 'main' into feat/3796-new-ui-for-signin
sean-brydon Sep 12, 2022
9116019
Merge branch 'main' into feat/3796-new-ui-for-signin
JeroenReumkens Sep 12, 2022
7172819
Merge branch 'main' into feat/3796-new-ui-for-signin
sean-brydon Sep 12, 2022
9652b95
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
b329f91
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
e26caa6
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
03a656d
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
3adb1ce
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
51da0d7
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
e0b6528
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
b86ba44
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 12, 2022
3a8eab6
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 13, 2022
089275e
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 13, 2022
99c21e1
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 13, 2022
3070213
Merge branch 'main' into feat/3796-new-ui-for-signin
kodiakhq[bot] Sep 13, 2022
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
9 changes: 8 additions & 1 deletion apps/storybook/stories/Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { TooltipProvider } from "@radix-ui/react-tooltip";
import { ComponentMeta, ComponentStory } from "@storybook/react";
import { Copy } from "react-feather";

import { TextAreaField, TextField } from "@calcom/ui/v2/core/form/fields";
import { TextAreaField, TextField, PasswordField } from "@calcom/ui/v2/core/form/fields";
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";

export default {
Expand Down Expand Up @@ -66,3 +67,9 @@ export const TextAreaInput: ComponentStory<typeof TextAreaField> = () => (
);

export const DatePickerInput: ComponentStory<typeof DatePicker> = () => <DatePicker date={new Date()} />;

export const PasswordInput: ComponentStory<typeof PasswordField> = () => (
<TooltipProvider>
<PasswordField />
</TooltipProvider>
);
4 changes: 2 additions & 2 deletions apps/web/components/v2/ui/AuthContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ export default function AuthContainer(props: React.PropsWithChildren<Props>) {
</div>
)}
<div className="mb-auto mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-8 sm:px-10">
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-10 sm:px-10">
{props.children}
</div>
<div className="mt-4 text-center text-sm text-neutral-600">{props.footerText}</div>
<div className="mt-8 text-center text-sm text-neutral-600">{props.footerText}</div>
</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions apps/web/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const V2_WHITELIST = [
"/workflows",
"/apps",
"/success",
"/auth/login",
];
const V2_BLACKLIST = [
//
Expand Down
100 changes: 49 additions & 51 deletions apps/web/pages/v2/auth/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { FaGoogle } from "react-icons/fa";

import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import prisma from "@calcom/prisma";
import { Icon } from "@calcom/ui";
import { Alert } from "@calcom/ui/Alert";
import { Icon } from "@calcom/ui/Icon";
import { Button, EmailField, Form, PasswordField } from "@calcom/ui/v2";
import SAMLLogin from "@calcom/ui/v2/modules/auth/SAMLLogin";

Expand All @@ -22,7 +23,7 @@ import { inferSSRProps } from "@lib/types/inferSSRProps";

import AddToHomescreen from "@components/AddToHomescreen";
import TwoFactor from "@components/auth/TwoFactor";
import AuthContainer from "@components/ui/AuthContainer";
import AuthContainer from "@components/v2/ui/AuthContainer";

import { IS_GOOGLE_LOGIN_ENABLED } from "@server/lib/constants";
import { ssrInit } from "@server/lib/ssr";
Expand Down Expand Up @@ -76,12 +77,9 @@ export default function Login({
callbackUrl = safeCallbackUrl || "";

const LoginFooter = (
<span className="text-gray-600">
{t("dont_have_an_account")}{" "}
<a href={`${WEBSITE_URL}/signup`} className="text-brand-500 font-medium">
{t("create_an_account")}
</a>
</span>
<a href={`${WEBSITE_URL}/signup`} className="text-brand-500 font-medium">
{t("i_dont_have_an_account")}
</a>
);

const TwoFactorFooter = (
Expand All @@ -106,7 +104,6 @@ export default function Login({
footerText={twoFactorRequired ? TwoFactorFooter : LoginFooter}>
<Form
form={form}
className="space-y-6"
handleSubmit={async (values) => {
setErrorMessage(null);
telemetry.event(telemetryEventTypes.login, collectPageParameters());
Expand All @@ -132,51 +129,52 @@ export default function Login({
{...form.register("csrfToken")}
/>
</div>
<div className={classNames("space-y-6", { hidden: twoFactorRequired })}>
<EmailField
id="email"
label={t("email_address")}
defaultValue={router.query.email as string}
placeholder="john.doe@example.com"
required
{...form.register("email")}
/>
<div className="relative">
<div className="absolute right-0 -top-[2px]">
<Link href="/auth/forgot-password">
<a tabIndex={-1} className="text-sm font-medium text-gray-600">
{t("forgot_password")}
</a>
</Link>
</div>
<PasswordField
id="password"
type="password"
autoComplete="current-password"
<div className="space-y-6">
<div className={classNames("space-y-6", { hidden: twoFactorRequired })}>
<EmailField
id="email"
label={t("email_address")}
defaultValue={router.query.email as string}
placeholder="john.doe@example.com"
required
{...form.register("password")}
{...form.register("email")}
/>
<div className="relative">
<div className="absolute right-0 -top-[6px]">
<Link href="/auth/forgot-password">
<a tabIndex={-1} className="text-sm font-medium text-gray-600">
{t("forgot")}
</a>
</Link>
</div>
<PasswordField
id="password"
autoComplete="current-password"
required
className="mb-0"
{...form.register("password")}
/>
</div>
</div>
</div>

{twoFactorRequired && <TwoFactor center />}
{twoFactorRequired && <TwoFactor center />}

{errorMessage && <Alert severity="error" title={errorMessage} />}
<div className="pb-8">
<Button type="submit" color="primary" disabled={isSubmitting} className="w-full">
{errorMessage && <Alert severity="error" title={errorMessage} />}
<Button type="submit" color="primary" disabled={isSubmitting} className="w-full justify-center">
{twoFactorRequired ? t("submit") : t("sign_in")}
</Button>
</div>
</Form>
<hr />
{!twoFactorRequired && (
<>
{isGoogleLoginEnabled && (
<div className="mt-8">
{(isGoogleLoginEnabled || isSAMLLoginEnabled) && <hr className="my-8" />}
<div className="space-y-3">
{isGoogleLoginEnabled && (
<Button
color="secondary"
className="w-full"
className="w-full justify-center"
data-testid="google"
StartIcon={FaGoogle}
onClick={async (e) => {
e.preventDefault();
// track Google logins. Without personal data/payload
Expand All @@ -185,17 +183,17 @@ export default function Login({
}}>
{t("signin_with_google")}
</Button>
</div>
)}
{isSAMLLoginEnabled && (
<SAMLLogin
email={form.getValues("email")}
samlTenantID={samlTenantID}
samlProductID={samlProductID}
hostedCal={hostedCal}
setErrorMessage={setErrorMessage}
/>
)}
)}
{isSAMLLoginEnabled && (
<SAMLLogin
email={form.getValues("email")}
samlTenantID={samlTenantID}
samlProductID={samlProductID}
hostedCal={hostedCal}
setErrorMessage={setErrorMessage}
/>
)}
</div>
</>
)}
</AuthContainer>
Expand Down
3 changes: 3 additions & 0 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
"2fa_enter_six_digit_code": "Enter the six-digit code from your authenticator app below.",
"create_an_account": "Create an account",
"dont_have_an_account": "Don't have an account?",
"i_dont_have_an_account": "I don't have an account",
"2fa_code": "Two-Factor Code",
"sign_in_account": "Sign in to your account",
"sign_in": "Sign in",
Expand Down Expand Up @@ -266,6 +267,8 @@
"enter_new_password": "Enter the new password you'd like for your account.",
"reset_password": "Reset Password",
"change_your_password": "Change your password",
"show_password": "Show password",
"hide_password": "Hide password",
"try_again": "Try Again",
"request_is_expired": "That Request is Expired.",
"reset_instructions": "Enter the email address associated with your account and we will send you a link to reset your password.",
Expand Down
39 changes: 36 additions & 3 deletions packages/ui/v2/core/form/fields.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useId } from "@radix-ui/react-id";
import React, { forwardRef, ReactElement, ReactNode, Ref } from "react";
import { Check, Circle, Info, X } from "react-feather";
import React, { forwardRef, ReactElement, ReactNode, Ref, useCallback, useMemo, useState } from "react";
import { Check, Circle, Info, X, Eye, EyeOff } from "react-feather";
import {
FieldErrors,
FieldValues,
Expand All @@ -13,6 +13,7 @@ import {
import classNames from "@calcom/lib/classNames";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Tooltip } from "@calcom/ui/v2";
import showToast from "@calcom/ui/v2/core/notifications";

import { Alert } from "../../../Alert";
Expand Down Expand Up @@ -248,7 +249,39 @@ export const PasswordField = forwardRef<HTMLInputElement, InputFieldProps>(funct
props,
ref
) {
return <InputField type="password" placeholder="•••••••••••••" ref={ref} {...props} />;
const { t } = useLocale();
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const toggleIsPasswordVisible = useCallback(
() => setIsPasswordVisible(!isPasswordVisible),
[isPasswordVisible, setIsPasswordVisible]
);
const textLabel = isPasswordVisible ? t("hide_password") : t("show_password");

return (
<div className="relative">
<InputField
type={isPasswordVisible ? "text" : "password"}
placeholder="•••••••••••••"
ref={ref}
{...props}
className={classNames("pr-10", props.className)}
/>

<Tooltip content={textLabel}>
<button
className="absolute bottom-0 right-3 h-9 text-gray-900"
type="button"
onClick={() => toggleIsPasswordVisible()}>
{isPasswordVisible ? (
<EyeOff className="h-4 stroke-[2.5px]" />
) : (
<Eye className="h-4 stroke-[2.5px]" />
)}
<span className="sr-only">{textLabel}</span>
</button>
</Tooltip>
JeroenReumkens marked this conversation as resolved.
Show resolved Hide resolved
</div>
);
});

export const EmailInput = forwardRef<HTMLInputElement, InputFieldProps>(function EmailInput(props, ref) {
Expand Down
48 changes: 24 additions & 24 deletions packages/ui/v2/modules/auth/SAMLLogin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useFormContext } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui";
import Button from "@calcom/ui/v2/core/Button";

interface Props {
Expand All @@ -30,32 +31,31 @@ export default function SAMLLogin(props: Props) {
});

return (
<div className="mt-5">
<Button
color="secondary"
data-testid="saml"
className="flex w-full justify-center"
onClick={async (event) => {
event.preventDefault();
<Button
StartIcon={Icon.FiLock}
color="secondary"
data-testid="saml"
className="flex w-full justify-center"
onClick={async (event) => {
event.preventDefault();

// track Google logins. Without personal data/payload
telemetry.event(telemetryEventTypes.googleLogin, collectPageParameters());
// track Google logins. Without personal data/payload
telemetry.event(telemetryEventTypes.googleLogin, collectPageParameters());

if (!props.hostedCal) {
await signIn("saml", {}, { tenant: props.samlTenantID, product: props.samlProductID });
} else {
if (props.email.length === 0) {
props.setErrorMessage(t("saml_email_required"));
return;
}
// hosted solution, fetch tenant and product from the backend
mutation.mutate({
email: methods.getValues("email"),
});
if (!props.hostedCal) {
await signIn("saml", {}, { tenant: props.samlTenantID, product: props.samlProductID });
} else {
if (props.email.length === 0) {
props.setErrorMessage(t("saml_email_required"));
return;
}
}}>
{t("signin_with_saml")}
</Button>
</div>
// hosted solution, fetch tenant and product from the backend
mutation.mutate({
email: methods.getValues("email"),
});
}
}}>
{t("signin_with_saml")}
</Button>
);
}