diff --git a/apps/trust/src/pages/auth/ConnectPage.tsx b/apps/trust/src/pages/auth/ConnectPage.tsx index 5a0fdcf6d..a22075abf 100644 --- a/apps/trust/src/pages/auth/ConnectPage.tsx +++ b/apps/trust/src/pages/auth/ConnectPage.tsx @@ -18,145 +18,40 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import type { GraphQLError } from "@probo/helpers"; import { usePageTitle } from "@probo/hooks"; import { useTranslate } from "@probo/i18n"; -import { Button, Field, useToast } from "@probo/ui"; -import { useEffect, useRef, useState } from "react"; -import { type PreloadedQuery, useMutation, usePreloadedQuery } from "react-relay"; +import { Button } from "@probo/ui"; +import { type PreloadedQuery, usePreloadedQuery } from "react-relay"; import { graphql } from "relay-runtime"; -import { z } from "zod"; -import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useSafeContinueUrl } from "#/hooks/useSafeContinueUrl"; -import type { ConnectPageMutation, SendMagicLinkInput } from "./__generated__/ConnectPageMutation.graphql"; import type { ConnectPageQuery } from "./__generated__/ConnectPageQuery.graphql"; -import { Divider } from "./_components/Divider"; -import { OIDCButton } from "./_components/OIDCButton"; export const connectPageQuery = graphql` query ConnectPageQuery { currentTrustCenter @required(action: THROW) { title } - oidcProviders { - ...OIDCButtonFragment - } } `; -const sendMagicLinkMutation = graphql` - mutation ConnectPageMutation($input: SendMagicLinkInput!) { - sendMagicLink(input: $input) { - success - } - } -`; - -const schema = z.object({ - email: z.string().email(), -}); - -type FormData = z.infer; - -const timerDurationSeconds = 60; - export function ConnectPage(props: { queryRef: PreloadedQuery; }) { const { queryRef } = props; const { __ } = useTranslate(); - const { toast } = useToast(); - const [magicLinkSent, setMagicLinkSent] = useState(false); - const interval = useRef>(undefined); - const [timer, setTimer] = useState(timerDurationSeconds); const safeContinueUrl = useSafeContinueUrl(); const { currentTrustCenter: { title }, - oidcProviders, } = usePreloadedQuery(connectPageQuery, queryRef); - useEffect(() => { - if (!magicLinkSent && interval.current) { - clearInterval(interval.current); - interval.current = undefined; - } - if (magicLinkSent) { - clearInterval(interval.current); - interval.current = setInterval(() => { - setTimer(timer => Math.max(timer - 1, 0)); - }, 1000); - } - - return () => { - clearInterval(interval.current); - }; - }, [magicLinkSent]); - usePageTitle(__(`Connect to ${title}'s Compliance Page`)); - const { - handleSubmit: handleSubmitWrapper, - register, - formState, - } = useFormWithSchema(schema, { - defaultValues: { - email: "", - }, - }); - - const [sendMagicLink] = useMutation( - sendMagicLinkMutation, - ); - - const handleSubmit = handleSubmitWrapper(({ email }: FormData) => { - const input: SendMagicLinkInput = { email }; - if (safeContinueUrl) { - input.continue = safeContinueUrl.toString(); - } - sendMagicLink({ - variables: { - input: { - email, - continue: safeContinueUrl.toString(), - }, - }, - onCompleted: (_, errors: GraphQLError[] | null) => { - if (errors) { - for (const err of errors) { - if (err.extensions?.code === "ALREADY_AUTHENTICATED") { - window.location.href = "/"; - return; - } - } - toast({ - title: __("Error"), - description: __("Cannot send magic link"), - variant: "error", - }); - return; - } - - toast({ - title: __("Success"), - description: __("Magic link sent!"), - variant: "success", - }); - setTimer(timerDurationSeconds); - setMagicLinkSent(true); - }, - onError: (error) => { - toast({ - title: __("Error"), - description: error.message, - variant: "error", - }); - }, - }); - }); + const initiateURL = new URL("/initiate", window.location.origin); + initiateURL.searchParams.set("continue", safeContinueUrl.toString()); return (
@@ -171,45 +66,14 @@ export function ConnectPage(props: {

- {oidcProviders.length > 0 && ( -
- {oidcProviders.map((providerRef, index) => ( - - ))} - {__("Or")} -
- )} - -
void handleSubmit(e)} className="space-y-6"> - - - {magicLinkSent && ( -

- {__( - "Magic Link Sent! Check your emails and use the link to connect.", - )} -

- )} - - - + ); } diff --git a/apps/trust/src/pages/auth/VerifyMagicLinkPage.tsx b/apps/trust/src/pages/auth/VerifyMagicLinkPage.tsx deleted file mode 100644 index b44e7113c..000000000 --- a/apps/trust/src/pages/auth/VerifyMagicLinkPage.tsx +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -import { formatError, type GraphQLError } from "@probo/helpers"; -import { usePageTitle } from "@probo/hooks"; -import { useTranslate } from "@probo/i18n"; -import { useToast } from "@probo/ui"; -import { useCallback, useEffect, useRef } from "react"; -import { useMutation } from "react-relay"; -import { Link, useNavigate, useSearchParams } from "react-router"; -import { graphql } from "relay-runtime"; - -import type { VerifyMagicLinkPageMutation } from "./__generated__/VerifyMagicLinkPageMutation.graphql"; - -const verifyMagicLinkMutation = graphql` - mutation VerifyMagicLinkPageMutation($input: VerifyMagicLinkInput!) { - verifyMagicLink(input: $input) { - continue - } - } -`; - -export default function VerifyMagicLinkPagePageMutation() { - const { __ } = useTranslate(); - const { toast } = useToast(); - const navigate = useNavigate(); - const [searchParams] = useSearchParams(); - const submittedRef = useRef(false); - - usePageTitle(__("Verify Magic Link")); - - const [verifyMagicLink] = useMutation( - verifyMagicLinkMutation, - ); - - const handleVerifyMagicToken = useCallback((token: string) => { - if (submittedRef.current) return; - - verifyMagicLink({ - variables: { - input: { token }, - }, - onCompleted: (response, errors: GraphQLError[] | null) => { - if (errors) { - for (const err of errors) { - if (err.extensions?.code === "ALREADY_AUTHENTICATED") { - window.location.href = window.location.origin; - return; - } - - if (err.extensions?.code === "TOKEN_EXPIRED") { - void navigate("/magic-link-expired"); - return; - } - - if (err.extensions?.code === "TOKEN_ALREADY_USED") { - void navigate("/magic-link-already-used"); - return; - } - } - - toast({ - title: __("Error"), - description: formatError(__("Failed to connect"), errors), - variant: "error", - }); - return; - } - - const { verifyMagicLink } = response; - - toast({ - title: __("Success"), - description: __("Your have successfully signed in"), - variant: "success", - }); - - if (verifyMagicLink?.continue) { - try { - const continueUrl = new URL(verifyMagicLink.continue, window.location.origin); - window.location.href = window.location.origin + continueUrl.pathname + continueUrl.search; - } catch { - window.location.href = window.location.origin; - } - } else { - window.location.href = window.location.origin; - } - }, - onError: (err) => { - toast({ - title: __("Error"), - description: err.message, - variant: "error", - }); - }, - }); - }, [__, navigate, toast, verifyMagicLink]); - - useEffect(() => { - const token = searchParams.get("token"); - if (!submittedRef.current && token) { - void handleVerifyMagicToken(token.trim()); - submittedRef.current = true; - } - }, [handleVerifyMagicToken, searchParams]); - - return ( -
-
-

{__("Email Confirmation")}

-

- {__("Confirming your email address to complete registration…")} -

-
-
- - {__("Go back")} - -
-
- ); -} diff --git a/apps/trust/src/providers/RelayProviders.tsx b/apps/trust/src/providers/RelayProviders.tsx index d3f281a96..a1789a5e8 100644 --- a/apps/trust/src/providers/RelayProviders.tsx +++ b/apps/trust/src/providers/RelayProviders.tsx @@ -43,7 +43,7 @@ export function buildEndpoint(): string { const url = new URL(formattedHost); // Compliance pages are always served at the root of a dedicated host. - url.pathname = `/api/trust/v1/graphql`; + url.pathname = "/graphql"; return url.toString(); } diff --git a/apps/trust/src/routes.tsx b/apps/trust/src/routes.tsx index dc6549e82..cdc48414d 100644 --- a/apps/trust/src/routes.tsx +++ b/apps/trust/src/routes.tsx @@ -55,18 +55,6 @@ const routes = [ path: "/connect", Component: lazy(() => import("#/pages/auth/ConnectPageLoader")), }, - { - path: "/verify-magic-link", - Component: lazy(() => import("#/pages/auth/VerifyMagicLinkPage")), - }, - { - path: "/magic-link-expired", - Component: lazy(() => import("#/pages/auth/MagicLinkExpiredPage")), - }, - { - path: "/magic-link-already-used", - Component: lazy(() => import("#/pages/auth/MagicLinkAlreadyUsedPage")), - }, { path: "/full-name", Component: lazy(() => import("#/pages/auth/FullNamePage")),