@shipfox/client-auth 3.0.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +32 -0
- package/dist/components/email-code-verification.d.ts +20 -0
- package/dist/components/email-code-verification.d.ts.map +1 -0
- package/dist/components/email-code-verification.js +136 -0
- package/dist/components/email-code-verification.js.map +1 -0
- package/dist/components/redirect-context.d.ts +11 -0
- package/dist/components/redirect-context.d.ts.map +1 -0
- package/dist/components/redirect-context.js +22 -0
- package/dist/components/redirect-context.js.map +1 -0
- package/dist/components/redirect-target.d.ts.map +1 -1
- package/dist/components/redirect-target.js +13 -12
- package/dist/components/redirect-target.js.map +1 -1
- package/dist/components/workspace-switcher.stories.d.ts +1 -1
- package/dist/continuation.d.ts +4 -0
- package/dist/continuation.d.ts.map +1 -0
- package/dist/continuation.js +5 -0
- package/dist/continuation.js.map +1 -0
- package/dist/hooks/api/auth-mapper.d.ts +21 -0
- package/dist/hooks/api/auth-mapper.d.ts.map +1 -0
- package/dist/hooks/api/auth-mapper.js +25 -0
- package/dist/hooks/api/auth-mapper.js.map +1 -0
- package/dist/hooks/api/login-auth.d.ts +3 -25
- package/dist/hooks/api/login-auth.d.ts.map +1 -1
- package/dist/hooks/api/login-auth.js +9 -30
- package/dist/hooks/api/login-auth.js.map +1 -1
- package/dist/hooks/api/logout-auth.d.ts.map +1 -1
- package/dist/hooks/api/logout-auth.js +4 -14
- package/dist/hooks/api/logout-auth.js.map +1 -1
- package/dist/hooks/api/password-reset-auth.d.ts +3 -25
- package/dist/hooks/api/password-reset-auth.d.ts.map +1 -1
- package/dist/hooks/api/password-reset-auth.js +9 -25
- package/dist/hooks/api/password-reset-auth.js.map +1 -1
- package/dist/hooks/api/signup-auth.d.ts +1 -25
- package/dist/hooks/api/signup-auth.d.ts.map +1 -1
- package/dist/hooks/api/signup-auth.js +5 -2
- package/dist/hooks/api/signup-auth.js.map +1 -1
- package/dist/hooks/api/verify-email-auth.d.ts +1 -12
- package/dist/hooks/api/verify-email-auth.d.ts.map +1 -1
- package/dist/hooks/api/verify-email-auth.js +10 -26
- package/dist/hooks/api/verify-email-auth.js.map +1 -1
- package/dist/hooks/api/workspace-auth.d.ts +3 -17
- package/dist/hooks/api/workspace-auth.d.ts.map +1 -1
- package/dist/hooks/api/workspace-auth.js +5 -2
- package/dist/hooks/api/workspace-auth.js.map +1 -1
- package/dist/hooks/api/workspace-mapper.d.ts +8 -0
- package/dist/hooks/api/workspace-mapper.d.ts.map +1 -0
- package/dist/hooks/api/workspace-mapper.js +9 -0
- package/dist/hooks/api/workspace-mapper.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/pages/invitation-context.d.ts +9 -19
- package/dist/pages/invitation-context.d.ts.map +1 -1
- package/dist/pages/invitation-context.js +7 -46
- package/dist/pages/invitation-context.js.map +1 -1
- package/dist/pages/login-page.js +2 -2
- package/dist/pages/login-page.js.map +1 -1
- package/dist/pages/logout-page.js +1 -1
- package/dist/pages/logout-page.js.map +1 -1
- package/dist/pages/password-reset-page.d.ts.map +1 -1
- package/dist/pages/password-reset-page.js +1 -4
- package/dist/pages/password-reset-page.js.map +1 -1
- package/dist/pages/signup-page.d.ts.map +1 -1
- package/dist/pages/signup-page.js +58 -152
- package/dist/pages/signup-page.js.map +1 -1
- package/dist/state/auth.d.ts +1 -1
- package/dist/state/auth.d.ts.map +1 -1
- package/dist/state/auth.js +1 -1
- package/dist/state/auth.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +10 -39
- package/src/components/auth-provider.test.tsx +173 -3
- package/src/components/email-code-verification.test.tsx +159 -0
- package/src/components/email-code-verification.tsx +177 -0
- package/src/components/redirect-context.test.ts +51 -0
- package/src/components/redirect-context.ts +26 -0
- package/src/components/redirect-target.test.ts +8 -0
- package/src/components/redirect-target.ts +13 -12
- package/src/continuation.test.ts +15 -0
- package/src/continuation.ts +6 -0
- package/src/hooks/api/auth-mapper.test.ts +77 -0
- package/src/hooks/api/auth-mapper.ts +39 -0
- package/src/hooks/api/login-auth.test.ts +1 -1
- package/src/hooks/api/login-auth.ts +12 -31
- package/src/hooks/api/logout-auth.ts +4 -10
- package/src/hooks/api/password-reset-auth.test.ts +1 -1
- package/src/hooks/api/password-reset-auth.ts +19 -32
- package/src/hooks/api/signup-auth.ts +8 -3
- package/src/hooks/api/verify-email-auth.ts +21 -34
- package/src/hooks/api/workspace-auth.ts +8 -3
- package/src/hooks/api/workspace-mapper.test.ts +21 -0
- package/src/hooks/api/workspace-mapper.ts +11 -0
- package/src/index.ts +0 -1
- package/src/pages/invitation-context.ts +10 -51
- package/src/pages/login-page.test.tsx +1 -1
- package/src/pages/login-page.tsx +2 -2
- package/src/pages/logout-page.tsx +1 -1
- package/src/pages/password-reset-page.tsx +1 -4
- package/src/pages/signup-page.test.tsx +1 -1
- package/src/pages/signup-page.tsx +41 -137
- package/src/state/auth.ts +1 -0
- package/src/state/last-workspace.test.ts +16 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/components/auth-shell.d.ts +0 -11
- package/dist/components/auth-shell.d.ts.map +0 -1
- package/dist/components/auth-shell.js +0 -63
- package/dist/components/auth-shell.js.map +0 -1
- package/dist/pages/email-verification-resend-model.d.ts +0 -6
- package/dist/pages/email-verification-resend-model.d.ts.map +0 -1
- package/dist/pages/email-verification-resend-model.js +0 -11
- package/dist/pages/email-verification-resend-model.js.map +0 -1
- package/src/components/auth-shell.tsx +0 -53
- package/src/pages/email-verification-resend-model.test.ts +0 -42
- package/src/pages/email-verification-resend-model.ts +0 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/logout-page.tsx"],"sourcesContent":["import {Text} from '@shipfox/react-ui/typography';\nimport {useNavigate, useRouter, useSearch} from '@tanstack/react-router';\nimport {useEffect} from 'react';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/pages/logout-page.tsx"],"sourcesContent":["import {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useNavigate, useRouter, useSearch} from '@tanstack/react-router';\nimport {useEffect} from 'react';\nimport {sanitizeRedirectPath} from '#/components/redirect-target.js';\nimport {useLogoutAuth} from '#hooks/api/logout-auth.js';\n\nexport function LogoutPage() {\n const logout = useLogoutAuth();\n const navigate = useNavigate();\n const router = useRouter();\n const search = useSearch({strict: false}) as {redirect?: unknown};\n const target = sanitizeRedirectPath(search.redirect);\n\n useEffect(() => {\n logout.mutateAsync().finally(() => {\n if (target !== undefined) {\n // Same-origin runtime path; let history drive route resolution.\n router.history.replace(target);\n } else {\n navigate({to: '/auth/login', replace: true});\n }\n });\n }, [logout.mutateAsync, navigate, router, target]);\n\n return (\n <AuthShell title=\"Logging out\" description=\"Ending your Shipfox session.\">\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n You will be sent back to login in a moment.\n </Text>\n </AuthShell>\n );\n}\n"],"names":["AuthShell","Text","useNavigate","useRouter","useSearch","useEffect","sanitizeRedirectPath","useLogoutAuth","LogoutPage","logout","navigate","router","search","strict","target","redirect","mutateAsync","finally","undefined","history","replace","to","title","description","size","className"],"mappings":";AAAA,SAAQA,SAAS,QAAO,gCAAgC;AACxD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,WAAW,EAAEC,SAAS,EAAEC,SAAS,QAAO,yBAAyB;AACzE,SAAQC,SAAS,QAAO,QAAQ;AAChC,SAAQC,oBAAoB,QAAO,kCAAkC;AACrE,SAAQC,aAAa,QAAO,4BAA4B;AAExD,OAAO,SAASC;IACd,MAAMC,SAASF;IACf,MAAMG,WAAWR;IACjB,MAAMS,SAASR;IACf,MAAMS,SAASR,UAAU;QAACS,QAAQ;IAAK;IACvC,MAAMC,SAASR,qBAAqBM,OAAOG,QAAQ;IAEnDV,UAAU;QACRI,OAAOO,WAAW,GAAGC,OAAO,CAAC;YAC3B,IAAIH,WAAWI,WAAW;gBACxB,gEAAgE;gBAChEP,OAAOQ,OAAO,CAACC,OAAO,CAACN;YACzB,OAAO;gBACLJ,SAAS;oBAACW,IAAI;oBAAeD,SAAS;gBAAI;YAC5C;QACF;IACF,GAAG;QAACX,OAAOO,WAAW;QAAEN;QAAUC;QAAQG;KAAO;IAEjD,qBACE,KAACd;QAAUsB,OAAM;QAAcC,aAAY;kBACzC,cAAA,KAACtB;YAAKuB,MAAK;YAAKC,WAAU;sBAA6C;;;AAK7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-reset-page.d.ts","sourceRoot":"","sources":["../../src/pages/password-reset-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"password-reset-page.d.ts","sourceRoot":"","sources":["../../src/pages/password-reset-page.tsx"],"names":[],"mappings":"AAwBA,wBAAgB,iBAAiB,gCAShC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { passwordResetConfirmBodySchema, passwordResetRequestBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
+
import { AuthShell } from '@shipfox/client-shell/runtime';
|
|
3
4
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
4
5
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
5
6
|
import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
|
|
@@ -9,9 +10,7 @@ import { useForm } from '@tanstack/react-form';
|
|
|
9
10
|
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
|
|
10
11
|
import { useAtom } from 'jotai';
|
|
11
12
|
import { useEffect, useRef, useState } from 'react';
|
|
12
|
-
import { AuthShell } from '#/components/auth-shell.js';
|
|
13
13
|
import { useConfirmPasswordResetAuth, useRequestPasswordResetAuth } from '#hooks/api/password-reset-auth.js';
|
|
14
|
-
import { useRefreshAuth } from '#hooks/api/refresh-auth.js';
|
|
15
14
|
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
16
15
|
import { passwordResetConfirmErrorToFormError, passwordResetRequestErrorToFormError } from './form-errors.js';
|
|
17
16
|
export function PasswordResetPage() {
|
|
@@ -174,7 +173,6 @@ function PasswordResetRequest() {
|
|
|
174
173
|
}
|
|
175
174
|
function PasswordResetConfirm({ token }) {
|
|
176
175
|
const confirmPasswordReset = useConfirmPasswordResetAuth();
|
|
177
|
-
const refreshAuth = useRefreshAuth();
|
|
178
176
|
const navigate = useNavigate();
|
|
179
177
|
const [, setAuthFormDraft] = useAtom(authFormDraftAtom);
|
|
180
178
|
const [formError, setFormError] = useState();
|
|
@@ -190,7 +188,6 @@ function PasswordResetConfirm({ token }) {
|
|
|
190
188
|
new_password: value.new_password
|
|
191
189
|
});
|
|
192
190
|
await confirmPasswordReset.mutateAsync(body);
|
|
193
|
-
await refreshAuth();
|
|
194
191
|
setAuthFormDraft(initialAuthFormDraft);
|
|
195
192
|
toast.success('Your password has been changed. You are now logged in.');
|
|
196
193
|
await navigate({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/password-reset-page.tsx"],"sourcesContent":["import {\n passwordResetConfirmBodySchema,\n passwordResetRequestBodySchema,\n} from '@shipfox/api-auth-dto';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {AuthShell} from '#/components/auth-shell.js';\nimport {\n useConfirmPasswordResetAuth,\n useRequestPasswordResetAuth,\n} from '#hooks/api/password-reset-auth.js';\nimport {useRefreshAuth} from '#hooks/api/refresh-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n passwordResetConfirmErrorToFormError,\n passwordResetRequestErrorToFormError,\n} from './form-errors.js';\n\nexport function PasswordResetPage() {\n const search = useSearch({strict: false});\n const token = typeof search.token === 'string' ? search.token : undefined;\n\n if (token) {\n return <PasswordResetConfirm token={token} />;\n }\n\n return <PasswordResetRequest />;\n}\n\nfunction PasswordResetRequest() {\n const requestPasswordReset = useRequestPasswordResetAuth();\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [submittedEmail, setSubmittedEmail] = useState<string | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const parsed = passwordResetRequestBodySchema.parse(value);\n await requestPasswordReset.mutateAsync(parsed);\n setSubmittedEmail(parsed.email);\n } catch (error) {\n const mapped = passwordResetRequestErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n useEffect(() => {\n return () => {\n const {email} = form.state.values;\n if (email !== draftRef.current.email) {\n setAuthFormDraft((current) => ({...current, email}));\n }\n };\n }, [form, setAuthFormDraft]);\n\n if (submittedEmail) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent reset instructions to ${submittedEmail}.`}\n >\n <Callout role=\"alert\" type=\"success\">\n If a Shipfox account exists for that email, the reset link will arrive shortly.\n </Callout>\n <Button\n className=\"w-full\"\n variant=\"secondary\"\n type=\"button\"\n onClick={() => setSubmittedEmail(undefined)}\n >\n Use another email\n </Button>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n return (\n <AuthShell title=\"Reset your password\" description=\"Enter your email to get a reset link.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"email\"\n validators={{\n onBlur: passwordResetRequestBodySchema.shape.email,\n onSubmit: passwordResetRequestBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n setAuthFormDraft((current) => ({...current, email: field.state.value}));\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={requestPasswordReset.isPending} type=\"submit\">\n {requestPasswordReset.isPending ? 'Sending link...' : 'Send reset link'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n\nfunction PasswordResetConfirm({token}: {token: string}) {\n const confirmPasswordReset = useConfirmPasswordResetAuth();\n const refreshAuth = useRefreshAuth();\n const navigate = useNavigate();\n const [, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {new_password: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = passwordResetConfirmBodySchema.parse({\n token,\n new_password: value.new_password,\n });\n await confirmPasswordReset.mutateAsync(body);\n await refreshAuth();\n setAuthFormDraft(initialAuthFormDraft);\n toast.success('Your password has been changed. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n const mapped = passwordResetConfirmErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n return (\n <AuthShell title=\"Set a new password\" description=\"Choose a password for your Shipfox account.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"new_password\"\n validators={{\n onBlur: passwordResetConfirmBodySchema.shape.new_password,\n onSubmit: passwordResetConfirmBodySchema.shape.new_password,\n }}\n >\n {(field) => (\n <FormField label=\"New password\" id=\"new-password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"new_password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={confirmPasswordReset.isPending} type=\"submit\">\n {confirmPasswordReset.isPending ? 'Updating password...' : 'Update password'}\n </Button>\n </form>\n <ButtonLink asChild variant=\"subtle\" className=\"self-center\">\n <Link to=\"/auth/login\">Back to login</Link>\n </ButtonLink>\n </AuthShell>\n );\n}\n"],"names":["passwordResetConfirmBodySchema","passwordResetRequestBodySchema","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","AuthShell","useConfirmPasswordResetAuth","useRequestPasswordResetAuth","useRefreshAuth","authFormDraftAtom","initialAuthFormDraft","passwordResetConfirmErrorToFormError","passwordResetRequestErrorToFormError","PasswordResetPage","search","strict","token","undefined","PasswordResetConfirm","PasswordResetRequest","requestPasswordReset","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","formError","setFormError","draftRef","current","form","defaultValues","email","onSubmit","value","parsed","parse","mutateAsync","error","mapped","message","state","values","title","description","role","type","className","variant","onClick","size","asChild","underline","to","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","shape","field","label","id","autoComplete","onChange","handleChange","target","handleBlur","isLoading","isPending","confirmPasswordReset","refreshAuth","navigate","new_password","body","success","replace"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/pages/password-reset-page.tsx"],"sourcesContent":["import {\n passwordResetConfirmBodySchema,\n passwordResetRequestBodySchema,\n} from '@shipfox/api-auth-dto';\nimport {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {\n useConfirmPasswordResetAuth,\n useRequestPasswordResetAuth,\n} from '#hooks/api/password-reset-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n passwordResetConfirmErrorToFormError,\n passwordResetRequestErrorToFormError,\n} from './form-errors.js';\n\nexport function PasswordResetPage() {\n const search = useSearch({strict: false});\n const token = typeof search.token === 'string' ? search.token : undefined;\n\n if (token) {\n return <PasswordResetConfirm token={token} />;\n }\n\n return <PasswordResetRequest />;\n}\n\nfunction PasswordResetRequest() {\n const requestPasswordReset = useRequestPasswordResetAuth();\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [submittedEmail, setSubmittedEmail] = useState<string | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const parsed = passwordResetRequestBodySchema.parse(value);\n await requestPasswordReset.mutateAsync(parsed);\n setSubmittedEmail(parsed.email);\n } catch (error) {\n const mapped = passwordResetRequestErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n useEffect(() => {\n return () => {\n const {email} = form.state.values;\n if (email !== draftRef.current.email) {\n setAuthFormDraft((current) => ({...current, email}));\n }\n };\n }, [form, setAuthFormDraft]);\n\n if (submittedEmail) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent reset instructions to ${submittedEmail}.`}\n >\n <Callout role=\"alert\" type=\"success\">\n If a Shipfox account exists for that email, the reset link will arrive shortly.\n </Callout>\n <Button\n className=\"w-full\"\n variant=\"secondary\"\n type=\"button\"\n onClick={() => setSubmittedEmail(undefined)}\n >\n Use another email\n </Button>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n return (\n <AuthShell title=\"Reset your password\" description=\"Enter your email to get a reset link.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"email\"\n validators={{\n onBlur: passwordResetRequestBodySchema.shape.email,\n onSubmit: passwordResetRequestBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n setAuthFormDraft((current) => ({...current, email: field.state.value}));\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={requestPasswordReset.isPending} type=\"submit\">\n {requestPasswordReset.isPending ? 'Sending link...' : 'Send reset link'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n\nfunction PasswordResetConfirm({token}: {token: string}) {\n const confirmPasswordReset = useConfirmPasswordResetAuth();\n const navigate = useNavigate();\n const [, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {new_password: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = passwordResetConfirmBodySchema.parse({\n token,\n new_password: value.new_password,\n });\n await confirmPasswordReset.mutateAsync(body);\n setAuthFormDraft(initialAuthFormDraft);\n toast.success('Your password has been changed. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n const mapped = passwordResetConfirmErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n return (\n <AuthShell title=\"Set a new password\" description=\"Choose a password for your Shipfox account.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"new_password\"\n validators={{\n onBlur: passwordResetConfirmBodySchema.shape.new_password,\n onSubmit: passwordResetConfirmBodySchema.shape.new_password,\n }}\n >\n {(field) => (\n <FormField label=\"New password\" id=\"new-password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"new_password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={confirmPasswordReset.isPending} type=\"submit\">\n {confirmPasswordReset.isPending ? 'Updating password...' : 'Update password'}\n </Button>\n </form>\n <ButtonLink asChild variant=\"subtle\" className=\"self-center\">\n <Link to=\"/auth/login\">Back to login</Link>\n </ButtonLink>\n </AuthShell>\n );\n}\n"],"names":["passwordResetConfirmBodySchema","passwordResetRequestBodySchema","AuthShell","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","useConfirmPasswordResetAuth","useRequestPasswordResetAuth","authFormDraftAtom","initialAuthFormDraft","passwordResetConfirmErrorToFormError","passwordResetRequestErrorToFormError","PasswordResetPage","search","strict","token","undefined","PasswordResetConfirm","PasswordResetRequest","requestPasswordReset","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","formError","setFormError","draftRef","current","form","defaultValues","email","onSubmit","value","parsed","parse","mutateAsync","error","mapped","message","state","values","title","description","role","type","className","variant","onClick","size","asChild","underline","to","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","shape","field","label","id","autoComplete","onChange","handleChange","target","handleBlur","isLoading","isPending","confirmPasswordReset","navigate","new_password","body","success","replace"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup-page.d.ts","sourceRoot":"","sources":["../../src/pages/signup-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signup-page.d.ts","sourceRoot":"","sources":["../../src/pages/signup-page.tsx"],"names":[],"mappings":"AA0BA,wBAAgB,UAAU,gCA0RzB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { signupBodySchema
|
|
2
|
+
import { signupBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
+
import { AuthShell } from '@shipfox/client-shell/runtime';
|
|
3
4
|
import { displayNameFieldError } from '@shipfox/client-ui';
|
|
4
5
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
5
6
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
@@ -11,12 +12,11 @@ import { useForm } from '@tanstack/react-form';
|
|
|
11
12
|
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
|
|
12
13
|
import { useAtom } from 'jotai';
|
|
13
14
|
import { useEffect, useRef, useState } from 'react';
|
|
14
|
-
import {
|
|
15
|
+
import { EmailCodeVerification } from '#/components/email-code-verification.js';
|
|
15
16
|
import { useRefreshAuth } from '#hooks/api/refresh-auth.js';
|
|
16
17
|
import { useSignupAuth } from '#hooks/api/signup-auth.js';
|
|
17
18
|
import { useResendEmailVerificationAuth, useVerifyEmailAuth } from '#hooks/api/verify-email-auth.js';
|
|
18
19
|
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
19
|
-
import { getResendRemainingSeconds, parseNextResendAvailableAt } from './email-verification-resend-model.js';
|
|
20
20
|
import { signupErrorToFormError } from './form-errors.js';
|
|
21
21
|
import { authErrorMessage } from './form-utils.js';
|
|
22
22
|
import { extractInvitationToken, pendingInvitation, useInvitationContext } from './invitation-context.js';
|
|
@@ -34,7 +34,6 @@ export function SignupPage() {
|
|
|
34
34
|
const invitationPending = pendingInvitation(invitationPreview.data);
|
|
35
35
|
const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);
|
|
36
36
|
const [emailChallenge, setEmailChallenge] = useState();
|
|
37
|
-
const [now, setNow] = useState(()=>Date.now());
|
|
38
37
|
const [nextResendAvailableAt, setNextResendAvailableAt] = useState();
|
|
39
38
|
const [formError, setFormError] = useState();
|
|
40
39
|
const [resendError, setResendError] = useState();
|
|
@@ -43,11 +42,6 @@ export function SignupPage() {
|
|
|
43
42
|
// Set just before clearing the draft on success so the unmount cleanup
|
|
44
43
|
// below does not repersist the just-submitted credentials.
|
|
45
44
|
const skipDraftPersistRef = useRef(false);
|
|
46
|
-
const resendRemainingSeconds = getResendRemainingSeconds({
|
|
47
|
-
nextResendAvailableAt,
|
|
48
|
-
now
|
|
49
|
-
});
|
|
50
|
-
const isResendCoolingDown = resendRemainingSeconds > 0;
|
|
51
45
|
const form = useForm({
|
|
52
46
|
defaultValues: {
|
|
53
47
|
email: authFormDraft.email,
|
|
@@ -75,17 +69,17 @@ export function SignupPage() {
|
|
|
75
69
|
// Refresh failures don't block the success message — the next API
|
|
76
70
|
// call's 401 handling will re-route the user.
|
|
77
71
|
}
|
|
78
|
-
toast.success(`You joined ${invitationPending.
|
|
72
|
+
toast.success(`You joined ${invitationPending.workspaceName}.`);
|
|
79
73
|
await navigate({
|
|
80
74
|
to: '/workspaces/$wid',
|
|
81
75
|
params: {
|
|
82
|
-
wid: result.membership.
|
|
76
|
+
wid: result.membership.workspaceId
|
|
83
77
|
}
|
|
84
78
|
});
|
|
85
79
|
return;
|
|
86
80
|
}
|
|
87
|
-
if (invitationToken && result.
|
|
88
|
-
toast.error(result.
|
|
81
|
+
if (invitationToken && result.acceptError) {
|
|
82
|
+
toast.error(result.acceptError.message);
|
|
89
83
|
await navigate({
|
|
90
84
|
to: '/invitations/accept',
|
|
91
85
|
search: {
|
|
@@ -94,17 +88,15 @@ export function SignupPage() {
|
|
|
94
88
|
});
|
|
95
89
|
return;
|
|
96
90
|
}
|
|
97
|
-
if (!result.
|
|
91
|
+
if (!result.emailChallenge) {
|
|
98
92
|
throw new Error('Signup did not return an email verification challenge');
|
|
99
93
|
}
|
|
100
94
|
setEmailChallenge({
|
|
101
95
|
email: result.user.email,
|
|
102
|
-
id: result.
|
|
96
|
+
id: result.emailChallenge.id
|
|
103
97
|
});
|
|
104
|
-
verificationForm.reset();
|
|
105
98
|
setResendError(undefined);
|
|
106
|
-
|
|
107
|
-
setNextResendAvailableAt(parseNextResendAvailableAt(result.email_challenge.next_resend_available_at));
|
|
99
|
+
setNextResendAvailableAt(result.emailChallenge.nextResendAvailableAt);
|
|
108
100
|
} catch (error) {
|
|
109
101
|
const mapped = signupErrorToFormError(error);
|
|
110
102
|
if (mapped.kind === 'field') {
|
|
@@ -121,51 +113,6 @@ export function SignupPage() {
|
|
|
121
113
|
}
|
|
122
114
|
}
|
|
123
115
|
});
|
|
124
|
-
const verificationForm = useForm({
|
|
125
|
-
defaultValues: {
|
|
126
|
-
code: ''
|
|
127
|
-
},
|
|
128
|
-
onSubmit: async ({ value })=>{
|
|
129
|
-
if (!emailChallenge || verifyEmail.isPending) return;
|
|
130
|
-
setResendError(undefined);
|
|
131
|
-
try {
|
|
132
|
-
await verifyEmail.mutateAsync({
|
|
133
|
-
email: emailChallenge.email,
|
|
134
|
-
challenge_id: emailChallenge.id,
|
|
135
|
-
code: value.code
|
|
136
|
-
});
|
|
137
|
-
await refreshAuth();
|
|
138
|
-
toast.success('Your email is verified. You are now logged in.');
|
|
139
|
-
await navigate({
|
|
140
|
-
to: '/',
|
|
141
|
-
replace: true
|
|
142
|
-
});
|
|
143
|
-
} catch (error) {
|
|
144
|
-
setResendError(authErrorMessage(error));
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
useEffect(()=>{
|
|
149
|
-
if (!emailChallenge || !nextResendAvailableAt) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const current = Date.now();
|
|
153
|
-
setNow(current);
|
|
154
|
-
if (nextResendAvailableAt <= current) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const handle = window.setInterval(()=>{
|
|
158
|
-
const tickNow = Date.now();
|
|
159
|
-
setNow(tickNow);
|
|
160
|
-
if (nextResendAvailableAt <= tickNow) {
|
|
161
|
-
window.clearInterval(handle);
|
|
162
|
-
}
|
|
163
|
-
}, 1000);
|
|
164
|
-
return ()=>window.clearInterval(handle);
|
|
165
|
-
}, [
|
|
166
|
-
emailChallenge,
|
|
167
|
-
nextResendAvailableAt
|
|
168
|
-
]);
|
|
169
116
|
// When arriving from an invitation link, prefill the email and lock it.
|
|
170
117
|
useEffect(()=>{
|
|
171
118
|
if (invitationPending && form.state.values.email !== invitationPending.email) {
|
|
@@ -199,110 +146,69 @@ export function SignupPage() {
|
|
|
199
146
|
setAuthFormDraft
|
|
200
147
|
]);
|
|
201
148
|
async function onResendVerificationEmail() {
|
|
202
|
-
if (!emailChallenge ||
|
|
149
|
+
if (!emailChallenge || resendEmailVerification.isPending) return;
|
|
203
150
|
setResendError(undefined);
|
|
204
151
|
try {
|
|
205
152
|
const result = await resendEmailVerification.mutateAsync({
|
|
206
153
|
email: emailChallenge.email,
|
|
207
154
|
challenge_id: emailChallenge.id
|
|
208
155
|
});
|
|
209
|
-
|
|
210
|
-
setNow(Date.now());
|
|
211
|
-
if (nextAvailableAt !== undefined) {
|
|
212
|
-
setNextResendAvailableAt(nextAvailableAt);
|
|
213
|
-
}
|
|
156
|
+
setNextResendAvailableAt(result.next_resend_available_at);
|
|
214
157
|
toast.success('If another verification email can be sent, it will arrive shortly.');
|
|
215
158
|
} catch (error) {
|
|
216
159
|
setResendError(authErrorMessage(error));
|
|
217
160
|
}
|
|
218
161
|
}
|
|
219
162
|
if (emailChallenge) {
|
|
220
|
-
return /*#__PURE__*/
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
children: "Verify email"
|
|
263
|
-
}),
|
|
264
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
265
|
-
"aria-disabled": isResendCoolingDown ? true : undefined,
|
|
266
|
-
className: "w-full aria-disabled:cursor-not-allowed aria-disabled:opacity-70",
|
|
267
|
-
variant: "secondary",
|
|
268
|
-
type: "button",
|
|
269
|
-
isLoading: resendEmailVerification.isPending,
|
|
270
|
-
onClick: onResendVerificationEmail,
|
|
271
|
-
children: resendEmailVerification.isPending ? 'Sending email...' : isResendCoolingDown ? `Resend in ${resendRemainingSeconds}s` : 'Resend verification email'
|
|
272
|
-
})
|
|
273
|
-
]
|
|
274
|
-
}),
|
|
275
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
276
|
-
className: "w-full",
|
|
277
|
-
variant: "transparent",
|
|
278
|
-
type: "button",
|
|
279
|
-
onClick: ()=>{
|
|
280
|
-
setEmailChallenge(undefined);
|
|
281
|
-
setResendError(undefined);
|
|
282
|
-
},
|
|
283
|
-
children: "Use another email"
|
|
284
|
-
}),
|
|
285
|
-
/*#__PURE__*/ _jsxs(Text, {
|
|
286
|
-
size: "sm",
|
|
287
|
-
className: "text-center text-foreground-neutral-subtle",
|
|
288
|
-
children: [
|
|
289
|
-
"Already verified?",
|
|
290
|
-
' ',
|
|
291
|
-
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
292
|
-
asChild: true,
|
|
293
|
-
variant: "interactive",
|
|
294
|
-
underline: true,
|
|
295
|
-
children: /*#__PURE__*/ _jsx(Link, {
|
|
296
|
-
to: "/auth/login",
|
|
297
|
-
children: "Log in"
|
|
298
|
-
})
|
|
163
|
+
return /*#__PURE__*/ _jsx(EmailCodeVerification, {
|
|
164
|
+
destination: emailChallenge.email,
|
|
165
|
+
nextResendAvailableAt: nextResendAvailableAt,
|
|
166
|
+
isResending: resendEmailVerification.isPending,
|
|
167
|
+
isVerifying: verifyEmail.isPending,
|
|
168
|
+
error: resendError,
|
|
169
|
+
onVerify: async (code)=>{
|
|
170
|
+
setResendError(undefined);
|
|
171
|
+
try {
|
|
172
|
+
await verifyEmail.mutateAsync({
|
|
173
|
+
email: emailChallenge.email,
|
|
174
|
+
challenge_id: emailChallenge.id,
|
|
175
|
+
code
|
|
176
|
+
});
|
|
177
|
+
await refreshAuth();
|
|
178
|
+
toast.success('Your email is verified. You are now logged in.');
|
|
179
|
+
await navigate({
|
|
180
|
+
to: '/',
|
|
181
|
+
replace: true
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
setResendError(authErrorMessage(error));
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
onResend: onResendVerificationEmail,
|
|
188
|
+
onUseAnotherEmail: ()=>{
|
|
189
|
+
setEmailChallenge(undefined);
|
|
190
|
+
setResendError(undefined);
|
|
191
|
+
},
|
|
192
|
+
children: /*#__PURE__*/ _jsxs(Text, {
|
|
193
|
+
size: "sm",
|
|
194
|
+
className: "text-center text-foreground-neutral-subtle",
|
|
195
|
+
children: [
|
|
196
|
+
"Already verified?",
|
|
197
|
+
' ',
|
|
198
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
199
|
+
asChild: true,
|
|
200
|
+
variant: "interactive",
|
|
201
|
+
underline: true,
|
|
202
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
203
|
+
to: "/auth/login",
|
|
204
|
+
children: "Log in"
|
|
299
205
|
})
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
})
|
|
303
209
|
});
|
|
304
210
|
}
|
|
305
|
-
const headerTitle = invitationPending ? `Join ${invitationPending.
|
|
211
|
+
const headerTitle = invitationPending ? `Join ${invitationPending.workspaceName}` : 'Create your Shipfox account';
|
|
306
212
|
const headerDescription = invitationPending ? `Create an account to accept your invitation.` : 'Start with your email and a password.';
|
|
307
213
|
const isInvitationEmailLocked = Boolean(invitationPending);
|
|
308
214
|
const invitationRedirect = invitationToken ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}` : undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/signup-page.tsx"],"sourcesContent":["import {signupBodySchema, verifyEmailConfirmBodySchema} from '@shipfox/api-auth-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {AuthShell} from '#/components/auth-shell.js';\nimport {useRefreshAuth} from '#hooks/api/refresh-auth.js';\nimport {useSignupAuth} from '#hooks/api/signup-auth.js';\nimport {useResendEmailVerificationAuth, useVerifyEmailAuth} from '#hooks/api/verify-email-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n getResendRemainingSeconds,\n parseNextResendAvailableAt,\n} from './email-verification-resend-model.js';\nimport {signupErrorToFormError} from './form-errors.js';\nimport {authErrorMessage} from './form-utils.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function SignupPage() {\n const signup = useSignupAuth();\n const verifyEmail = useVerifyEmailAuth();\n const resendEmailVerification = useResendEmailVerificationAuth();\n const refreshAuth = useRefreshAuth();\n const navigate = useNavigate();\n const search = useSearch({strict: false}) as {redirect?: unknown};\n const invitationToken = extractInvitationToken(search.redirect);\n const invitationPreview = useInvitationContext(invitationToken);\n const invitationPending = pendingInvitation(invitationPreview.data);\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [emailChallenge, setEmailChallenge] = useState<{email: string; id: string} | undefined>();\n const [now, setNow] = useState(() => Date.now());\n const [nextResendAvailableAt, setNextResendAvailableAt] = useState<number | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const [resendError, setResendError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n // Set just before clearing the draft on success so the unmount cleanup\n // below does not repersist the just-submitted credentials.\n const skipDraftPersistRef = useRef(false);\n const resendRemainingSeconds = getResendRemainingSeconds({nextResendAvailableAt, now});\n const isResendCoolingDown = resendRemainingSeconds > 0;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email, password: authFormDraft.password, name: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = signupBodySchema.parse({\n email: value.email,\n password: value.password,\n name: value.name,\n ...(invitationToken ? {invitation_token: invitationToken} : {}),\n });\n const result = await signup.mutateAsync(body);\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n\n if (invitationToken && result.membership && invitationPending) {\n try {\n await refreshAuth();\n } catch {\n // Refresh failures don't block the success message — the next API\n // call's 401 handling will re-route the user.\n }\n toast.success(`You joined ${invitationPending.workspace_name}.`);\n await navigate({\n to: '/workspaces/$wid',\n params: {wid: result.membership.workspace_id},\n });\n return;\n }\n\n if (invitationToken && result.accept_error) {\n toast.error(result.accept_error.message);\n await navigate({\n to: '/invitations/accept',\n search: {token: invitationToken},\n });\n return;\n }\n\n if (!result.email_challenge) {\n throw new Error('Signup did not return an email verification challenge');\n }\n setEmailChallenge({email: result.user.email, id: result.email_challenge.id});\n verificationForm.reset();\n setResendError(undefined);\n setNow(Date.now());\n setNextResendAvailableAt(\n parseNextResendAvailableAt(result.email_challenge.next_resend_available_at),\n );\n } catch (error) {\n const mapped = signupErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n const verificationForm = useForm({\n defaultValues: {code: ''},\n onSubmit: async ({value}) => {\n if (!emailChallenge || verifyEmail.isPending) return;\n\n setResendError(undefined);\n try {\n await verifyEmail.mutateAsync({\n email: emailChallenge.email,\n challenge_id: emailChallenge.id,\n code: value.code,\n });\n await refreshAuth();\n toast.success('Your email is verified. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n },\n });\n\n useEffect(() => {\n if (!emailChallenge || !nextResendAvailableAt) {\n return;\n }\n\n const current = Date.now();\n setNow(current);\n if (nextResendAvailableAt <= current) {\n return;\n }\n\n const handle = window.setInterval(() => {\n const tickNow = Date.now();\n setNow(tickNow);\n if (nextResendAvailableAt <= tickNow) {\n window.clearInterval(handle);\n }\n }, 1000);\n return () => window.clearInterval(handle);\n }, [emailChallenge, nextResendAvailableAt]);\n\n // When arriving from an invitation link, prefill the email and lock it.\n useEffect(() => {\n if (invitationPending && form.state.values.email !== invitationPending.email) {\n form.setFieldValue('email', invitationPending.email);\n setAuthFormDraft((current) => ({...current, email: invitationPending.email}));\n }\n }, [invitationPending, form, setAuthFormDraft]);\n\n // Sync form values back to the Jotai draft on unmount (only email + password\n // — name is intentionally not persisted across navigation). Skipped after a\n // successful signup because we just intentionally cleared the draft.\n useEffect(() => {\n return () => {\n if (skipDraftPersistRef.current) return;\n const {email, password} = form.state.values;\n if (email !== draftRef.current.email || password !== draftRef.current.password) {\n setAuthFormDraft({email, password});\n }\n };\n }, [form, setAuthFormDraft]);\n\n async function onResendVerificationEmail() {\n if (!emailChallenge || isResendCoolingDown || resendEmailVerification.isPending) return;\n\n setResendError(undefined);\n try {\n const result = await resendEmailVerification.mutateAsync({\n email: emailChallenge.email,\n challenge_id: emailChallenge.id,\n });\n const nextAvailableAt = parseNextResendAvailableAt(result.next_resend_available_at);\n setNow(Date.now());\n if (nextAvailableAt !== undefined) {\n setNextResendAvailableAt(nextAvailableAt);\n }\n toast.success('If another verification email can be sent, it will arrive shortly.');\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n }\n\n if (emailChallenge) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent an eight-digit verification code to ${emailChallenge.email}.`}\n >\n {resendError ? (\n <Callout role=\"alert\" type=\"error\">\n {resendError}\n </Callout>\n ) : null}\n <form\n className=\"flex flex-col gap-8\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n void verificationForm.handleSubmit();\n }}\n >\n <verificationForm.Field\n name=\"code\"\n validators={{\n onBlur: verifyEmailConfirmBodySchema.shape.code,\n onSubmit: verifyEmailConfirmBodySchema.shape.code,\n }}\n >\n {(field) => (\n <FormField label=\"Verification code\" id=\"verification-code\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"one-time-code\"\n inputMode=\"numeric\"\n maxLength={8}\n pattern=\"[0-9]{8}\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value.replace(/\\D/gu, ''))}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </verificationForm.Field>\n <Button className=\"w-full\" type=\"submit\" isLoading={verifyEmail.isPending}>\n Verify email\n </Button>\n <Button\n aria-disabled={isResendCoolingDown ? true : undefined}\n className=\"w-full aria-disabled:cursor-not-allowed aria-disabled:opacity-70\"\n variant=\"secondary\"\n type=\"button\"\n isLoading={resendEmailVerification.isPending}\n onClick={onResendVerificationEmail}\n >\n {resendEmailVerification.isPending\n ? 'Sending email...'\n : isResendCoolingDown\n ? `Resend in ${resendRemainingSeconds}s`\n : 'Resend verification email'}\n </Button>\n </form>\n <Button\n className=\"w-full\"\n variant=\"transparent\"\n type=\"button\"\n onClick={() => {\n setEmailChallenge(undefined);\n setResendError(undefined);\n }}\n >\n Use another email\n </Button>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already verified?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n const headerTitle = invitationPending\n ? `Join ${invitationPending.workspace_name}`\n : 'Create your Shipfox account';\n const headerDescription = invitationPending\n ? `Create an account to accept your invitation.`\n : 'Start with your email and a password.';\n const isInvitationEmailLocked = Boolean(invitationPending);\n const invitationRedirect = invitationToken\n ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}`\n : undefined;\n\n function persistDraft() {\n const {email, password} = form.state.values;\n setAuthFormDraft({email, password});\n }\n\n return (\n <AuthShell title={headerTitle} description={headerDescription}>\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n onSubmit: ({value}) =>\n displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n }}\n >\n {(field) => (\n <FormField label=\"Name\" id=\"name\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"name\"\n name=\"name\"\n type=\"text\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"email\"\n validators={{\n onBlur: signupBodySchema.shape.email,\n onSubmit: signupBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n readOnly={isInvitationEmailLocked}\n iconRight={\n isInvitationEmailLocked ? (\n <Icon\n aria-hidden=\"true\"\n className=\"size-16 text-foreground-neutral-disabled\"\n name=\"lockLine\"\n />\n ) : undefined\n }\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"password\"\n validators={{\n onBlur: signupBodySchema.shape.password,\n onSubmit: signupBodySchema.shape.password,\n }}\n >\n {(field) => (\n <FormField label=\"Password\" id=\"password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={signup.isPending} type=\"submit\">\n {signup.isPending ? 'Creating account...' : 'Create account'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already have an account?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link\n to=\"/auth/login\"\n search={invitationRedirect ? {redirect: invitationRedirect} : undefined}\n >\n Log in\n </Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n"],"names":["signupBodySchema","verifyEmailConfirmBodySchema","displayNameFieldError","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","AuthShell","useRefreshAuth","useSignupAuth","useResendEmailVerificationAuth","useVerifyEmailAuth","authFormDraftAtom","initialAuthFormDraft","getResendRemainingSeconds","parseNextResendAvailableAt","signupErrorToFormError","authErrorMessage","extractInvitationToken","pendingInvitation","useInvitationContext","SignupPage","signup","verifyEmail","resendEmailVerification","refreshAuth","navigate","search","strict","invitationToken","redirect","invitationPreview","invitationPending","data","authFormDraft","setAuthFormDraft","emailChallenge","setEmailChallenge","now","setNow","Date","nextResendAvailableAt","setNextResendAvailableAt","formError","setFormError","resendError","setResendError","draftRef","current","skipDraftPersistRef","resendRemainingSeconds","isResendCoolingDown","form","defaultValues","email","password","name","onSubmit","value","undefined","body","parse","invitation_token","result","mutateAsync","membership","success","workspace_name","to","params","wid","workspace_id","accept_error","error","message","token","email_challenge","Error","user","id","verificationForm","reset","next_resend_available_at","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","code","isPending","challenge_id","replace","handle","window","setInterval","tickNow","clearInterval","state","values","setFieldValue","onResendVerificationEmail","nextAvailableAt","title","description","role","type","className","noValidate","event","preventDefault","handleSubmit","Field","validators","onBlur","shape","label","autoComplete","inputMode","maxLength","pattern","onChange","handleChange","target","handleBlur","isLoading","aria-disabled","variant","onClick","size","asChild","underline","headerTitle","headerDescription","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","persistDraft","stopPropagation","readOnly","iconRight","aria-hidden"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/pages/signup-page.tsx"],"sourcesContent":["import {signupBodySchema} from '@shipfox/api-auth-dto';\nimport {AuthShell} from '@shipfox/client-shell/runtime';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {EmailCodeVerification} from '#/components/email-code-verification.js';\nimport {useRefreshAuth} from '#hooks/api/refresh-auth.js';\nimport {useSignupAuth} from '#hooks/api/signup-auth.js';\nimport {useResendEmailVerificationAuth, useVerifyEmailAuth} from '#hooks/api/verify-email-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {signupErrorToFormError} from './form-errors.js';\nimport {authErrorMessage} from './form-utils.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function SignupPage() {\n const signup = useSignupAuth();\n const verifyEmail = useVerifyEmailAuth();\n const resendEmailVerification = useResendEmailVerificationAuth();\n const refreshAuth = useRefreshAuth();\n const navigate = useNavigate();\n const search = useSearch({strict: false}) as {redirect?: unknown};\n const invitationToken = extractInvitationToken(search.redirect);\n const invitationPreview = useInvitationContext(invitationToken);\n const invitationPending = pendingInvitation(invitationPreview.data);\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [emailChallenge, setEmailChallenge] = useState<{email: string; id: string} | undefined>();\n const [nextResendAvailableAt, setNextResendAvailableAt] = useState<string | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const [resendError, setResendError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n // Set just before clearing the draft on success so the unmount cleanup\n // below does not repersist the just-submitted credentials.\n const skipDraftPersistRef = useRef(false);\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email, password: authFormDraft.password, name: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = signupBodySchema.parse({\n email: value.email,\n password: value.password,\n name: value.name,\n ...(invitationToken ? {invitation_token: invitationToken} : {}),\n });\n const result = await signup.mutateAsync(body);\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n\n if (invitationToken && result.membership && invitationPending) {\n try {\n await refreshAuth();\n } catch {\n // Refresh failures don't block the success message — the next API\n // call's 401 handling will re-route the user.\n }\n toast.success(`You joined ${invitationPending.workspaceName}.`);\n await navigate({\n to: '/workspaces/$wid',\n params: {wid: result.membership.workspaceId},\n });\n return;\n }\n\n if (invitationToken && result.acceptError) {\n toast.error(result.acceptError.message);\n await navigate({\n to: '/invitations/accept',\n search: {token: invitationToken},\n });\n return;\n }\n\n if (!result.emailChallenge) {\n throw new Error('Signup did not return an email verification challenge');\n }\n setEmailChallenge({email: result.user.email, id: result.emailChallenge.id});\n setResendError(undefined);\n setNextResendAvailableAt(result.emailChallenge.nextResendAvailableAt);\n } catch (error) {\n const mapped = signupErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n // When arriving from an invitation link, prefill the email and lock it.\n useEffect(() => {\n if (invitationPending && form.state.values.email !== invitationPending.email) {\n form.setFieldValue('email', invitationPending.email);\n setAuthFormDraft((current) => ({...current, email: invitationPending.email}));\n }\n }, [invitationPending, form, setAuthFormDraft]);\n\n // Sync form values back to the Jotai draft on unmount (only email + password\n // — name is intentionally not persisted across navigation). Skipped after a\n // successful signup because we just intentionally cleared the draft.\n useEffect(() => {\n return () => {\n if (skipDraftPersistRef.current) return;\n const {email, password} = form.state.values;\n if (email !== draftRef.current.email || password !== draftRef.current.password) {\n setAuthFormDraft({email, password});\n }\n };\n }, [form, setAuthFormDraft]);\n\n async function onResendVerificationEmail() {\n if (!emailChallenge || resendEmailVerification.isPending) return;\n\n setResendError(undefined);\n try {\n const result = await resendEmailVerification.mutateAsync({\n email: emailChallenge.email,\n challenge_id: emailChallenge.id,\n });\n setNextResendAvailableAt(result.next_resend_available_at);\n toast.success('If another verification email can be sent, it will arrive shortly.');\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n }\n\n if (emailChallenge) {\n return (\n <EmailCodeVerification\n destination={emailChallenge.email}\n nextResendAvailableAt={nextResendAvailableAt}\n isResending={resendEmailVerification.isPending}\n isVerifying={verifyEmail.isPending}\n error={resendError}\n onVerify={async (code) => {\n setResendError(undefined);\n try {\n await verifyEmail.mutateAsync({\n email: emailChallenge.email,\n challenge_id: emailChallenge.id,\n code,\n });\n await refreshAuth();\n toast.success('Your email is verified. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n }}\n onResend={onResendVerificationEmail}\n onUseAnotherEmail={() => {\n setEmailChallenge(undefined);\n setResendError(undefined);\n }}\n >\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already verified?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </EmailCodeVerification>\n );\n }\n\n const headerTitle = invitationPending\n ? `Join ${invitationPending.workspaceName}`\n : 'Create your Shipfox account';\n const headerDescription = invitationPending\n ? `Create an account to accept your invitation.`\n : 'Start with your email and a password.';\n const isInvitationEmailLocked = Boolean(invitationPending);\n const invitationRedirect = invitationToken\n ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}`\n : undefined;\n\n function persistDraft() {\n const {email, password} = form.state.values;\n setAuthFormDraft({email, password});\n }\n\n return (\n <AuthShell title={headerTitle} description={headerDescription}>\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n onSubmit: ({value}) =>\n displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n }}\n >\n {(field) => (\n <FormField label=\"Name\" id=\"name\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"name\"\n name=\"name\"\n type=\"text\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"email\"\n validators={{\n onBlur: signupBodySchema.shape.email,\n onSubmit: signupBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n readOnly={isInvitationEmailLocked}\n iconRight={\n isInvitationEmailLocked ? (\n <Icon\n aria-hidden=\"true\"\n className=\"size-16 text-foreground-neutral-disabled\"\n name=\"lockLine\"\n />\n ) : undefined\n }\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"password\"\n validators={{\n onBlur: signupBodySchema.shape.password,\n onSubmit: signupBodySchema.shape.password,\n }}\n >\n {(field) => (\n <FormField label=\"Password\" id=\"password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={signup.isPending} type=\"submit\">\n {signup.isPending ? 'Creating account...' : 'Create account'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already have an account?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link\n to=\"/auth/login\"\n search={invitationRedirect ? {redirect: invitationRedirect} : undefined}\n >\n Log in\n </Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n"],"names":["signupBodySchema","AuthShell","displayNameFieldError","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","EmailCodeVerification","useRefreshAuth","useSignupAuth","useResendEmailVerificationAuth","useVerifyEmailAuth","authFormDraftAtom","initialAuthFormDraft","signupErrorToFormError","authErrorMessage","extractInvitationToken","pendingInvitation","useInvitationContext","SignupPage","signup","verifyEmail","resendEmailVerification","refreshAuth","navigate","search","strict","invitationToken","redirect","invitationPreview","invitationPending","data","authFormDraft","setAuthFormDraft","emailChallenge","setEmailChallenge","nextResendAvailableAt","setNextResendAvailableAt","formError","setFormError","resendError","setResendError","draftRef","current","skipDraftPersistRef","form","defaultValues","email","password","name","onSubmit","value","undefined","body","parse","invitation_token","result","mutateAsync","membership","success","workspaceName","to","params","wid","workspaceId","acceptError","error","message","token","Error","user","id","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","state","values","setFieldValue","onResendVerificationEmail","isPending","challenge_id","next_resend_available_at","destination","isResending","isVerifying","onVerify","code","replace","onResend","onUseAnotherEmail","size","className","asChild","variant","underline","headerTitle","headerDescription","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","persistDraft","title","description","noValidate","event","preventDefault","stopPropagation","handleSubmit","role","type","Field","validators","onBlur","shape","label","autoComplete","onChange","handleChange","target","handleBlur","readOnly","iconRight","aria-hidden","isLoading"],"mappings":"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"}
|
package/dist/state/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type AuthState, type AuthStatus, authStateAtom, initialAuthState, toAuthenticatedState, type Workspace, } from '@shipfox/client-shell/runtime';
|
|
1
|
+
export { type AuthState, type AuthStatus, authStateAtom, initialAuthState, toAuthenticatedState, useAuthTransition, type Workspace, } from '@shipfox/client-shell/runtime';
|
|
2
2
|
export interface AuthFormDraft {
|
|
3
3
|
email: string;
|
|
4
4
|
password: string;
|
package/dist/state/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/state/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,UAAU,EACf,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,SAAS,GACf,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,oBAAoB,EAAE,aAAyC,CAAC;AAC7E,eAAO,MAAM,iBAAiB;;CAA4C,CAAC"}
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/state/auth.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,SAAS,EACd,KAAK,UAAU,EACf,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,SAAS,GACf,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,oBAAoB,EAAE,aAAyC,CAAC;AAC7E,eAAO,MAAM,iBAAiB;;CAA4C,CAAC"}
|
package/dist/state/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { atom } from 'jotai';
|
|
2
|
-
export { authStateAtom, initialAuthState, toAuthenticatedState } from '@shipfox/client-shell/runtime';
|
|
2
|
+
export { authStateAtom, initialAuthState, toAuthenticatedState, useAuthTransition } from '@shipfox/client-shell/runtime';
|
|
3
3
|
export const initialAuthFormDraft = {
|
|
4
4
|
email: '',
|
|
5
5
|
password: ''
|
package/dist/state/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/state/auth.ts"],"sourcesContent":["import {atom} from 'jotai';\n\nexport {\n type AuthState,\n type AuthStatus,\n authStateAtom,\n initialAuthState,\n toAuthenticatedState,\n type Workspace,\n} from '@shipfox/client-shell/runtime';\n\nexport interface AuthFormDraft {\n email: string;\n password: string;\n}\n\nexport const initialAuthFormDraft: AuthFormDraft = {email: '', password: ''};\nexport const authFormDraftAtom = atom<AuthFormDraft>(initialAuthFormDraft);\n"],"names":["atom","authStateAtom","initialAuthState","toAuthenticatedState","initialAuthFormDraft","email","password","authFormDraftAtom"],"mappings":"AAAA,SAAQA,IAAI,QAAO,QAAQ;AAE3B,SAGEC,aAAa,EACbC,gBAAgB,EAChBC,oBAAoB,
|
|
1
|
+
{"version":3,"sources":["../../src/state/auth.ts"],"sourcesContent":["import {atom} from 'jotai';\n\nexport {\n type AuthState,\n type AuthStatus,\n authStateAtom,\n initialAuthState,\n toAuthenticatedState,\n useAuthTransition,\n type Workspace,\n} from '@shipfox/client-shell/runtime';\n\nexport interface AuthFormDraft {\n email: string;\n password: string;\n}\n\nexport const initialAuthFormDraft: AuthFormDraft = {email: '', password: ''};\nexport const authFormDraftAtom = atom<AuthFormDraft>(initialAuthFormDraft);\n"],"names":["atom","authStateAtom","initialAuthState","toAuthenticatedState","useAuthTransition","initialAuthFormDraft","email","password","authFormDraftAtom"],"mappings":"AAAA,SAAQA,IAAI,QAAO,QAAQ;AAE3B,SAGEC,aAAa,EACbC,gBAAgB,EAChBC,oBAAoB,EACpBC,iBAAiB,QAEZ,gCAAgC;AAOvC,OAAO,MAAMC,uBAAsC;IAACC,OAAO;IAAIC,UAAU;AAAE,EAAE;AAC7E,OAAO,MAAMC,oBAAoBR,KAAoBK,sBAAsB"}
|