diff --git a/apps/web/pages/auth/login.tsx b/apps/web/pages/auth/login.tsx index 9df9c88c70..9781dc8212 100644 --- a/apps/web/pages/auth/login.tsx +++ b/apps/web/pages/auth/login.tsx @@ -14,6 +14,7 @@ import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode"; import { HOSTED_CAL_FEATURES, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants"; import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; +import { useLastUsed, LastUsed } from "@calcom/lib/hooks/useLastUsed"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; import { trpc } from "@calcom/trpc/react"; @@ -68,6 +69,7 @@ inferSSRProps & WithNonceProps<{}>) { const [twoFactorRequired, setTwoFactorRequired] = useState(!!totpEmail || false); const [twoFactorLostAccess, setTwoFactorLostAccess] = useState(false); const [errorMessage, setErrorMessage] = useState(null); + const [lastUsed, setLastUsed] = useLastUsed(); const errorMessages: { [key: string]: string } = { // [ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"), @@ -151,8 +153,10 @@ inferSSRProps & WithNonceProps<{}>) { }); if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]); // we're logged in! let's do a hard refresh to the desired url - else if (!res.error) router.push(callbackUrl); - else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true); + else if (!res.error) { + setLastUsed("credentials"); + router.push(callbackUrl); + } else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true); else if (res.error === ErrorCode.IncorrectBackupCode) setErrorMessage(t("incorrect_backup_code")); else if (res.error === ErrorCode.MissingBackupCodes) setErrorMessage(t("missing_backup_codes")); // fallback if error not found @@ -232,7 +236,10 @@ inferSSRProps & WithNonceProps<{}>) { color="primary" disabled={formState.isSubmitting} className="w-full justify-center"> - {twoFactorRequired ? t("submit") : t("sign_in")} + {twoFactorRequired ? t("submit") : t("sign_in")} + {lastUsed === "credentials" && ( + {t("last_used")} + )} @@ -249,11 +256,15 @@ inferSSRProps & WithNonceProps<{}>) { CustomStartIcon={} onClick={async (e) => { e.preventDefault(); - await signIn("google", { + const res = await signIn("google", { callbackUrl, }); + if (res && !res.error) { + setLastUsed("google"); + } }}> - {t("signin_with_google")} + {t("signin_with_google")} + {lastUsed === "google" && } )} {displaySSOLogin && ( diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index d62903929b..0ac06f9542 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -957,6 +957,7 @@ "signin_with_google": "Sign in with Google", "signin_with_saml": "Sign in with SAML", "signin_with_saml_oidc": "Sign in with SAML/OIDC", + "last_used": "Last used", "you_will_need_to_generate": "You will need to generate an access token from your old scheduling tool.", "import": "Import", "import_from": "Import from", diff --git a/packages/features/auth/SAMLLogin.tsx b/packages/features/auth/SAMLLogin.tsx index 2c74571f4f..b6d9ae5153 100644 --- a/packages/features/auth/SAMLLogin.tsx +++ b/packages/features/auth/SAMLLogin.tsx @@ -4,6 +4,7 @@ import { useFormContext } from "react-hook-form"; import z from "zod"; import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants"; +import { useLastUsed, LastUsed } from "@calcom/lib/hooks/useLastUsed"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { Button } from "@calcom/ui"; @@ -21,10 +22,14 @@ const schema = z.object({ export function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Props) { const { t } = useLocale(); const methods = useFormContext(); + const [lastUsed, setLastUsed] = useLastUsed(); const mutation = trpc.viewer.public.samlTenantProduct.useMutation({ onSuccess: async (data) => { - await signIn("saml", {}, { tenant: data.tenant, product: data.product }); + const res = await signIn("saml", {}, { tenant: data.tenant, product: data.product }); + if (res && !res.error) { + setLastUsed("saml"); + } }, onError: (err) => { setErrorMessage(t(err.message)); @@ -62,7 +67,8 @@ export function SAMLLogin({ samlTenantID, samlProductID, setErrorMessage }: Prop email, }); }}> - {t("signin_with_saml_oidc")} + {t("signin_with_saml_oidc")} + {lastUsed === "saml" && } ); } diff --git a/packages/lib/hooks/useLastUsed.tsx b/packages/lib/hooks/useLastUsed.tsx new file mode 100644 index 0000000000..1c3333eb4c --- /dev/null +++ b/packages/lib/hooks/useLastUsed.tsx @@ -0,0 +1,28 @@ +import { useState, useEffect } from "react"; + +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { localStorage } from "@calcom/lib/webstorage"; + +type LoginType = "saml" | "google" | "credentials" | undefined; + +export function useLastUsed() { + const [lastUsed, setLastUsed] = useState(() => { + const storedValue = localStorage.getItem("last_cal_login"); + return storedValue ? (storedValue as LoginType) : undefined; + }); + + useEffect(() => { + if (lastUsed) { + localStorage.setItem("last_cal_login", lastUsed); + } else { + localStorage.removeItem("last_cal_login"); + } + }, [lastUsed]); + + return [lastUsed, setLastUsed] as const; +} + +export const LastUsed = () => { + const { t } = useLocale(); + return {t("last_used")}; +};