feat: tracking and displaying last used login method (#16356)
This commit is contained in:
@@ -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<typeof getServerSideProps> & WithNonceProps<{}>) {
|
||||
const [twoFactorRequired, setTwoFactorRequired] = useState(!!totpEmail || false);
|
||||
const [twoFactorLostAccess, setTwoFactorLostAccess] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [lastUsed, setLastUsed] = useLastUsed();
|
||||
|
||||
const errorMessages: { [key: string]: string } = {
|
||||
// [ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"),
|
||||
@@ -151,8 +153,10 @@ inferSSRProps<typeof getServerSideProps> & 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<typeof getServerSideProps> & WithNonceProps<{}>) {
|
||||
color="primary"
|
||||
disabled={formState.isSubmitting}
|
||||
className="w-full justify-center">
|
||||
{twoFactorRequired ? t("submit") : t("sign_in")}
|
||||
<span>{twoFactorRequired ? t("submit") : t("sign_in")}</span>
|
||||
{lastUsed === "credentials" && (
|
||||
<span className="absolute right-3 text-xs text-gray-600">{t("last_used")}</span>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -249,11 +256,15 @@ inferSSRProps<typeof getServerSideProps> & WithNonceProps<{}>) {
|
||||
CustomStartIcon={<GoogleIcon />}
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
await signIn("google", {
|
||||
const res = await signIn("google", {
|
||||
callbackUrl,
|
||||
});
|
||||
if (res && !res.error) {
|
||||
setLastUsed("google");
|
||||
}
|
||||
}}>
|
||||
{t("signin_with_google")}
|
||||
<span>{t("signin_with_google")}</span>
|
||||
{lastUsed === "google" && <LastUsed />}
|
||||
</Button>
|
||||
)}
|
||||
{displaySSOLogin && (
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")}
|
||||
<span>{t("signin_with_saml_oidc")}</span>
|
||||
{lastUsed === "saml" && <LastUsed />}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<LoginType>(() => {
|
||||
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 <span className="text-subtle absolute right-3 text-xs">{t("last_used")}</span>;
|
||||
};
|
||||
Reference in New Issue
Block a user