diff --git a/messages/en-US.json b/messages/en-US.json index ca5557bc..e4b2999c 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1725,5 +1725,6 @@ "healthCheckNotAvailable": "Local", "rewritePath": "Rewrite Path", "rewritePathDescription": "Optionally rewrite the path before forwarding to the target.", - "continueToApplication": "Continue to application" + "continueToApplication": "Continue to application", + "checkingInvite": "Checking Invite" } diff --git a/src/app/invite/page.tsx b/src/app/invite/page.tsx index 49c5a2c5..2e027f77 100644 --- a/src/app/invite/page.tsx +++ b/src/app/invite/page.tsx @@ -1,11 +1,6 @@ -import { internal } from "@app/lib/api"; -import { authCookieHeader } from "@app/lib/api/cookies"; import { verifySession } from "@app/lib/auth/verifySession"; -import { AcceptInviteResponse } from "@server/routers/user"; -import { AxiosResponse } from "axios"; import { redirect } from "next/navigation"; import InviteStatusCard from "../../components/InviteStatusCard"; -import { formatAxiosError } from "@app/lib/api"; import { getTranslations } from "next-intl/server"; export default async function InvitePage(props: { @@ -27,8 +22,8 @@ export default async function InvitePage(props: { if (parts.length !== 2) { return ( <> -

{t('inviteInvalid')}

-

{t('inviteInvalidDescription')}

+

{t("inviteInvalid")}

+

{t("inviteInvalidDescription")}

); } @@ -36,58 +31,15 @@ export default async function InvitePage(props: { const inviteId = parts[0]; const token = parts[1]; - let error = ""; - const res = await internal - .post>( - `/invite/accept`, - { - inviteId, - token, - }, - await authCookieHeader() - ) - .catch((e) => { - error = formatAxiosError(e); - console.error(error); - }); - - if (res && res.status === 200) { - redirect(`/${res.data.data.orgId}`); - } - - function cardType() { - if (error.includes("Invite is not for this user")) { - return "wrong_user"; - } else if ( - error.includes("User does not exist. Please create an account first.") - ) { - return "user_does_not_exist"; - } else if (error.includes("You must be logged in to accept an invite")) { - return "not_logged_in"; - } else { - return "rejected"; - } - } - - const type = cardType(); - - if (!user && type === "user_does_not_exist") { - const redirectUrl = emailParam - ? `/auth/signup?redirect=/invite?token=${params.token}&email=${encodeURIComponent(emailParam)}` - : `/auth/signup?redirect=/invite?token=${params.token}`; - redirect(redirectUrl); - } - - if (!user && type === "not_logged_in") { - const redirectUrl = emailParam - ? `/auth/login?redirect=/invite?token=${params.token}&email=${encodeURIComponent(emailParam)}` - : `/auth/login?redirect=/invite?token=${params.token}`; - redirect(redirectUrl); - } - return ( <> - + ); } diff --git a/src/components/InviteStatusCard.tsx b/src/components/InviteStatusCard.tsx index 6d7db4dc..d394bd57 100644 --- a/src/components/InviteStatusCard.tsx +++ b/src/components/InviteStatusCard.tsx @@ -1,47 +1,119 @@ "use client"; -import { createApiClient } from "@app/lib/api"; +import { createApiClient, formatAxiosError } from "@app/lib/api"; import { Button } from "@app/components/ui/button"; import { Card, CardContent, CardFooter, CardHeader, - CardTitle, + CardTitle } from "@app/components/ui/card"; import { useEnvContext } from "@app/hooks/useEnvContext"; -import { XCircle } from "lucide-react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; +import { useEffect, useState } from "react"; +import { AxiosResponse } from "axios"; +import { AcceptInviteResponse, GetUserResponse } from "@server/routers/user"; +import { Loader2 } from "lucide-react"; type InviteStatusCardProps = { - type: "rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in"; - token: string; + user: GetUserResponse | null; + tokenParam: string; + inviteId: string; + inviteToken: string; email?: string; }; export default function InviteStatusCard({ - type, - token, + inviteId, email, + user, + tokenParam, + inviteToken }: InviteStatusCardProps) { const router = useRouter(); const api = createApiClient(useEnvContext()); const t = useTranslations(); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + const [type, setType] = useState< + "rejected" | "wrong_user" | "user_does_not_exist" | "not_logged_in" + >("rejected"); + + useEffect(() => { + async function init() { + let error = ""; + const res = await api + .post>(`/invite/accept`, { + inviteId, + token: inviteToken + }) + .catch((e) => { + error = formatAxiosError(e); + console.log("Error accepting invite:", error); + setError(error); + // console.error(e); + }); + + if (res && res.status === 200) { + router.push(`/${res.data.data.orgId}`); + return; + } + + function cardType() { + if (error.includes("Invite is not for this user")) { + return "wrong_user"; + } else if ( + error.includes( + "User does not exist. Please create an account first." + ) + ) { + return "user_does_not_exist"; + } else if ( + error.includes("You must be logged in to accept an invite") + ) { + return "not_logged_in"; + } else { + return "rejected"; + } + } + + const type = cardType(); + setType(type); + + if (!user && type === "user_does_not_exist") { + const redirectUrl = email + ? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/signup?redirect=/invite?token=${tokenParam}`; + router.push(redirectUrl); + } else if (!user && type === "not_logged_in") { + const redirectUrl = email + ? `/auth/login?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/login?redirect=/invite?token=${tokenParam}`; + router.push(redirectUrl); + } else { + setLoading(false); + } + } + + init(); + }, []); + async function goToLogin() { await api.post("/auth/logout", {}); - const redirectUrl = email - ? `/auth/login?redirect=/invite?token=${token}&email=${encodeURIComponent(email)}` - : `/auth/login?redirect=/invite?token=${token}`; + const redirectUrl = email + ? `/auth/login?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/login?redirect=/invite?token=${tokenParam}`; router.push(redirectUrl); } async function goToSignup() { await api.post("/auth/logout", {}); - const redirectUrl = email - ? `/auth/signup?redirect=/invite?token=${token}&email=${encodeURIComponent(email)}` - : `/auth/signup?redirect=/invite?token=${token}`; + const redirectUrl = email + ? `/auth/signup?redirect=/invite?token=${tokenParam}&email=${encodeURIComponent(email)}` + : `/auth/signup?redirect=/invite?token=${tokenParam}`; router.push(redirectUrl); } @@ -50,35 +122,27 @@ export default function InviteStatusCard({ return (

- {t('inviteErrorNotValid')} + {t("inviteErrorNotValid")}

    -
  • {t('inviteErrorExpired')}
  • -
  • {t('inviteErrorRevoked')}
  • -
  • {t('inviteErrorTypo')}
  • +
  • {t("inviteErrorExpired")}
  • +
  • {t("inviteErrorRevoked")}
  • +
  • {t("inviteErrorTypo")}
); } else if (type === "wrong_user") { return (
-

- {t('inviteErrorUser')} -

-

- {t('inviteLoginUser')} -

+

{t("inviteErrorUser")}

+

{t("inviteLoginUser")}

); } else if (type === "user_does_not_exist") { return (
-

- {t('inviteErrorNoUser')} -

-

- {t('inviteCreateUser')} -

+

{t("inviteErrorNoUser")}

+

{t("inviteCreateUser")}

); } @@ -92,37 +156,43 @@ export default function InviteStatusCard({ router.push("/"); }} > - {t('goHome')} + {t("goHome")} ); } else if (type === "wrong_user") { return ( - + ); } else if (type === "user_does_not_exist") { - return ; + return ; } } return ( -
+
- {/*
-
*/} - {t('inviteNotAccepted')} + {loading ? t("checkingInvite") : t("inviteNotAccepted")}
- {renderBody()} + + {loading && ( +
+
+ + {t("loading")} +
+
+ )} + {!loading && renderBody()} +
- - {renderFooter()} - + {!loading && ( + + {renderFooter()} + + )}
);