1
1
import type { ActionArgs , LoaderArgs } from "@remix-run/node" ;
2
2
import { redirect } from "@remix-run/node" ;
3
- import { Form , useTransition } from "@remix-run/react" ;
3
+ import { Form , useNavigation , useTransition } from "@remix-run/react" ;
4
4
import { TypedMetaFunction , typedjson , useTypedLoaderData } from "remix-typedjson" ;
5
5
import { z } from "zod" ;
6
6
import { LogoIcon } from "~/components/LogoIcon" ;
@@ -20,6 +20,7 @@ import magicLinkIcon from "./login.magic.svg";
20
20
import type { LoaderType as RootLoader } from "~/root" ;
21
21
import { appEnvTitleTag } from "~/utils" ;
22
22
import { TextLink } from "~/components/primitives/TextLink" ;
23
+ import { FormError } from "~/components/primitives/FormError" ;
23
24
24
25
export const meta : TypedMetaFunction < typeof loader , { root : RootLoader } > = ( { parentsData } ) => ( {
25
26
title : `Login to Trigger.dev${ appEnvTitleTag ( parentsData ?. root . appEnv ) } ` ,
@@ -31,10 +32,26 @@ export async function loader({ request }: LoaderArgs) {
31
32
} ) ;
32
33
33
34
const session = await getUserSession ( request ) ;
35
+ const error = session . get ( "auth:error" ) ;
34
36
35
- return typedjson ( {
36
- magicLinkSent : session . has ( "triggerdotdev:magiclink" ) ,
37
- } ) ;
37
+ let magicLinkError : string | undefined ;
38
+ if ( error ) {
39
+ if ( "message" in error ) {
40
+ magicLinkError = error . message ;
41
+ } else {
42
+ magicLinkError = JSON . stringify ( error , null , 2 ) ;
43
+ }
44
+ }
45
+
46
+ return typedjson (
47
+ {
48
+ magicLinkSent : session . has ( "triggerdotdev:magiclink" ) ,
49
+ magicLinkError,
50
+ } ,
51
+ {
52
+ headers : { "Set-Cookie" : await commitSession ( session ) } ,
53
+ }
54
+ ) ;
38
55
}
39
56
40
57
export async function action ( { request } : ActionArgs ) {
@@ -49,7 +66,7 @@ export async function action({ request }: ActionArgs) {
49
66
. parse ( payload ) ;
50
67
51
68
if ( action === "send" ) {
52
- await authenticator . authenticate ( "email-link" , request , {
69
+ return authenticator . authenticate ( "email-link" , request , {
53
70
successRedirect : "/login/magic" ,
54
71
failureRedirect : "/login/magic" ,
55
72
} ) ;
@@ -66,13 +83,13 @@ export async function action({ request }: ActionArgs) {
66
83
}
67
84
68
85
export default function LoginMagicLinkPage ( ) {
69
- const { magicLinkSent } = useTypedLoaderData < typeof loader > ( ) ;
70
- const transition = useTransition ( ) ;
86
+ const { magicLinkSent, magicLinkError } = useTypedLoaderData < typeof loader > ( ) ;
87
+ const navigate = useNavigation ( ) ;
71
88
72
89
const isLoading =
73
- ( transition . state === "loading" || transition . state === "submitting" ) &&
74
- transition . type === "actionSubmission" &&
75
- transition . submission . formData . get ( "action" ) === "send" ;
90
+ ( navigate . state === "loading" || navigate . state === "submitting" ) &&
91
+ navigate . formAction !== undefined &&
92
+ navigate . formData ? .get ( "action" ) === "send" ;
76
93
77
94
return (
78
95
< AppContainer showBackgroundGradient = { true } >
@@ -152,6 +169,7 @@ export default function LoginMagicLinkPage() {
152
169
/>
153
170
{ isLoading ? "Sending…" : "Send a magic link" }
154
171
</ Button >
172
+ { magicLinkError && < FormError > { magicLinkError } </ FormError > }
155
173
</ Fieldset >
156
174
< Paragraph variant = "extra-small" className = "my-4 text-center" >
157
175
By logging in with your email you agree to our{ " " }
0 commit comments