@shipfox/client-auth 14.0.0 → 14.0.1

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.
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 42 files with swc (467.06ms)
2
+ Successfully compiled: 42 files with swc (392.8ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @shipfox/client-auth
2
2
 
3
+ ## 14.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 88bf8e8: Migrates the shell, auth, secrets, logs, workspace-settings, config, and invitations
8
+ surfaces to semantic spacing roles and brings them under the `no-raw-spacing` Biome
9
+ plugin. Adds shared menu-surface and edge-specific panel roles for existing spacing
10
+ contracts.
11
+ - Updated dependencies [88bf8e8]
12
+ - Updated dependencies [6aa6c7a]
13
+ - Updated dependencies [5c56ba6]
14
+ - @shipfox/react-ui@1.0.0
15
+ - @shipfox/client-invitations@14.0.1
16
+ - @shipfox/client-shell@14.0.1
17
+ - @shipfox/client-ui@14.0.1
18
+
3
19
  ## 14.0.0
4
20
 
5
21
  ### Patch Changes
@@ -79,7 +79,7 @@ export function EmailCodeVerification({ destination, expiresAt, nextResendAvaila
79
79
  children: "This verification code has expired. Use another email to try again."
80
80
  }) : null,
81
81
  /*#__PURE__*/ _jsxs("form", {
82
- className: "flex flex-col gap-8",
82
+ className: "flex flex-col gap-inline",
83
83
  noValidate: true,
84
84
  onSubmit: submit,
85
85
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/email-code-verification.tsx"],"sourcesContent":["import {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput} from '@shipfox/react-ui/form-field';\nimport type {HeaderProps} from '@shipfox/react-ui/typography';\nimport type {FormEvent, ReactNode, Ref} from 'react';\nimport {useCallback, useEffect, useRef, useState} from 'react';\n\nconst EIGHT_DIGIT_CODE_RE = /^\\d{8}$/u;\n\nexport interface EmailCodeVerificationProps {\n destination: string;\n expiresAt?: string | undefined;\n nextResendAvailableAt?: string | undefined;\n isResending?: boolean;\n isVerifying?: boolean;\n error?: string | undefined;\n title?: string;\n description?: string;\n children?: ReactNode;\n headingProps?: Omit<HeaderProps, 'children' | 'id'> | undefined;\n headingRef?: Ref<HTMLHeadingElement> | undefined;\n onResend: () => void | Promise<void>;\n onUseAnotherEmail: () => void;\n onVerify: (code: string) => void | Promise<void>;\n}\n\nfunction timestamp(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nexport function EmailCodeVerification({\n destination,\n expiresAt,\n nextResendAvailableAt,\n isResending = false,\n isVerifying = false,\n error,\n title = 'Check your email',\n description = `We sent an eight-digit verification code to ${destination}.`,\n children,\n headingProps,\n headingRef,\n onResend,\n onUseAnotherEmail,\n onVerify,\n}: EmailCodeVerificationProps) {\n const [code, setCode] = useState('');\n const [now, setNow] = useState(() => Date.now());\n const [codeError, setCodeError] = useState<string>();\n const localHeadingRef = useRef<HTMLHeadingElement>(null);\n const resendAvailableAt = timestamp(nextResendAvailableAt);\n const expiration = timestamp(expiresAt);\n const resendRemainingSeconds = resendAvailableAt\n ? Math.max(0, Math.ceil((resendAvailableAt - now) / 1_000))\n : 0;\n const isResendCoolingDown = resendRemainingSeconds > 0;\n const isExpired = expiration !== undefined && expiration <= now;\n const isPending = isResending || isVerifying;\n\n useEffect(() => {\n const isPending = (tickNow: number) =>\n (resendAvailableAt !== undefined && resendAvailableAt > tickNow) ||\n (expiration !== undefined && expiration > tickNow);\n\n const tickNow = Date.now();\n setNow(tickNow);\n if (!isPending(tickNow)) return;\n\n const interval = window.setInterval(() => {\n const nextTick = Date.now();\n setNow(nextTick);\n if (!isPending(nextTick)) {\n window.clearInterval(interval);\n }\n }, 1_000);\n return () => window.clearInterval(interval);\n }, [expiration, resendAvailableAt]);\n\n useEffect(() => {\n localHeadingRef.current?.focus();\n }, []);\n\n const setHeadingRef = useCallback(\n (heading: HTMLHeadingElement | null) => {\n localHeadingRef.current = heading;\n if (typeof headingRef === 'function') {\n headingRef(heading);\n } else if (headingRef) {\n headingRef.current = heading;\n }\n },\n [headingRef],\n );\n\n function submit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n if (isExpired || isVerifying) return;\n if (!validateCode()) return;\n void onVerify(code);\n }\n\n function validateCode() {\n const error = EIGHT_DIGIT_CODE_RE.test(code)\n ? undefined\n : 'Enter the eight-digit verification code.';\n setCodeError(error);\n return error === undefined;\n }\n\n return (\n <AuthShell\n title={title}\n description={description}\n headingProps={headingProps}\n headingRef={setHeadingRef}\n >\n {error ? (\n <Callout role=\"alert\" type=\"error\">\n {error}\n </Callout>\n ) : null}\n {isExpired ? (\n <Callout role=\"alert\" type=\"error\">\n This verification code has expired. Use another email to try again.\n </Callout>\n ) : null}\n <form className=\"flex flex-col gap-8\" noValidate onSubmit={submit}>\n <FormField label=\"Verification code\" id=\"verification-code\" error={codeError}>\n <FormFieldInput\n autoComplete=\"one-time-code\"\n inputMode=\"numeric\"\n maxLength={8}\n pattern=\"[0-9]{8}\"\n value={code}\n onChange={(event) => {\n setCode(event.target.value.replace(/\\D/gu, '').slice(0, 8));\n setCodeError(undefined);\n }}\n onBlur={validateCode}\n />\n </FormField>\n <Button className=\"w-full\" type=\"submit\" disabled={isExpired} isLoading={isVerifying}>\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={isResending}\n onClick={() => {\n if (!isResendCoolingDown && !isResending) void onResend();\n }}\n >\n {isResending\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 disabled={isPending}\n onClick={onUseAnotherEmail}\n >\n Use another email\n </Button>\n {children}\n </AuthShell>\n );\n}\n"],"names":["AuthShell","Button","Callout","FormField","FormFieldInput","useCallback","useEffect","useRef","useState","EIGHT_DIGIT_CODE_RE","timestamp","value","undefined","parsed","Date","parse","Number","isNaN","EmailCodeVerification","destination","expiresAt","nextResendAvailableAt","isResending","isVerifying","error","title","description","children","headingProps","headingRef","onResend","onUseAnotherEmail","onVerify","code","setCode","now","setNow","codeError","setCodeError","localHeadingRef","resendAvailableAt","expiration","resendRemainingSeconds","Math","max","ceil","isResendCoolingDown","isExpired","isPending","tickNow","interval","window","setInterval","nextTick","clearInterval","current","focus","setHeadingRef","heading","submit","event","preventDefault","validateCode","test","role","type","form","className","noValidate","onSubmit","label","id","autoComplete","inputMode","maxLength","pattern","onChange","target","replace","slice","onBlur","disabled","isLoading","aria-disabled","variant","onClick"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/email-code-verification.tsx"],"sourcesContent":["import {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput} from '@shipfox/react-ui/form-field';\nimport type {HeaderProps} from '@shipfox/react-ui/typography';\nimport type {FormEvent, ReactNode, Ref} from 'react';\nimport {useCallback, useEffect, useRef, useState} from 'react';\n\nconst EIGHT_DIGIT_CODE_RE = /^\\d{8}$/u;\n\nexport interface EmailCodeVerificationProps {\n destination: string;\n expiresAt?: string | undefined;\n nextResendAvailableAt?: string | undefined;\n isResending?: boolean;\n isVerifying?: boolean;\n error?: string | undefined;\n title?: string;\n description?: string;\n children?: ReactNode;\n headingProps?: Omit<HeaderProps, 'children' | 'id'> | undefined;\n headingRef?: Ref<HTMLHeadingElement> | undefined;\n onResend: () => void | Promise<void>;\n onUseAnotherEmail: () => void;\n onVerify: (code: string) => void | Promise<void>;\n}\n\nfunction timestamp(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const parsed = Date.parse(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n\nexport function EmailCodeVerification({\n destination,\n expiresAt,\n nextResendAvailableAt,\n isResending = false,\n isVerifying = false,\n error,\n title = 'Check your email',\n description = `We sent an eight-digit verification code to ${destination}.`,\n children,\n headingProps,\n headingRef,\n onResend,\n onUseAnotherEmail,\n onVerify,\n}: EmailCodeVerificationProps) {\n const [code, setCode] = useState('');\n const [now, setNow] = useState(() => Date.now());\n const [codeError, setCodeError] = useState<string>();\n const localHeadingRef = useRef<HTMLHeadingElement>(null);\n const resendAvailableAt = timestamp(nextResendAvailableAt);\n const expiration = timestamp(expiresAt);\n const resendRemainingSeconds = resendAvailableAt\n ? Math.max(0, Math.ceil((resendAvailableAt - now) / 1_000))\n : 0;\n const isResendCoolingDown = resendRemainingSeconds > 0;\n const isExpired = expiration !== undefined && expiration <= now;\n const isPending = isResending || isVerifying;\n\n useEffect(() => {\n const isPending = (tickNow: number) =>\n (resendAvailableAt !== undefined && resendAvailableAt > tickNow) ||\n (expiration !== undefined && expiration > tickNow);\n\n const tickNow = Date.now();\n setNow(tickNow);\n if (!isPending(tickNow)) return;\n\n const interval = window.setInterval(() => {\n const nextTick = Date.now();\n setNow(nextTick);\n if (!isPending(nextTick)) {\n window.clearInterval(interval);\n }\n }, 1_000);\n return () => window.clearInterval(interval);\n }, [expiration, resendAvailableAt]);\n\n useEffect(() => {\n localHeadingRef.current?.focus();\n }, []);\n\n const setHeadingRef = useCallback(\n (heading: HTMLHeadingElement | null) => {\n localHeadingRef.current = heading;\n if (typeof headingRef === 'function') {\n headingRef(heading);\n } else if (headingRef) {\n headingRef.current = heading;\n }\n },\n [headingRef],\n );\n\n function submit(event: FormEvent<HTMLFormElement>) {\n event.preventDefault();\n if (isExpired || isVerifying) return;\n if (!validateCode()) return;\n void onVerify(code);\n }\n\n function validateCode() {\n const error = EIGHT_DIGIT_CODE_RE.test(code)\n ? undefined\n : 'Enter the eight-digit verification code.';\n setCodeError(error);\n return error === undefined;\n }\n\n return (\n <AuthShell\n title={title}\n description={description}\n headingProps={headingProps}\n headingRef={setHeadingRef}\n >\n {error ? (\n <Callout role=\"alert\" type=\"error\">\n {error}\n </Callout>\n ) : null}\n {isExpired ? (\n <Callout role=\"alert\" type=\"error\">\n This verification code has expired. Use another email to try again.\n </Callout>\n ) : null}\n <form className=\"flex flex-col gap-inline\" noValidate onSubmit={submit}>\n <FormField label=\"Verification code\" id=\"verification-code\" error={codeError}>\n <FormFieldInput\n autoComplete=\"one-time-code\"\n inputMode=\"numeric\"\n maxLength={8}\n pattern=\"[0-9]{8}\"\n value={code}\n onChange={(event) => {\n setCode(event.target.value.replace(/\\D/gu, '').slice(0, 8));\n setCodeError(undefined);\n }}\n onBlur={validateCode}\n />\n </FormField>\n <Button className=\"w-full\" type=\"submit\" disabled={isExpired} isLoading={isVerifying}>\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={isResending}\n onClick={() => {\n if (!isResendCoolingDown && !isResending) void onResend();\n }}\n >\n {isResending\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 disabled={isPending}\n onClick={onUseAnotherEmail}\n >\n Use another email\n </Button>\n {children}\n </AuthShell>\n );\n}\n"],"names":["AuthShell","Button","Callout","FormField","FormFieldInput","useCallback","useEffect","useRef","useState","EIGHT_DIGIT_CODE_RE","timestamp","value","undefined","parsed","Date","parse","Number","isNaN","EmailCodeVerification","destination","expiresAt","nextResendAvailableAt","isResending","isVerifying","error","title","description","children","headingProps","headingRef","onResend","onUseAnotherEmail","onVerify","code","setCode","now","setNow","codeError","setCodeError","localHeadingRef","resendAvailableAt","expiration","resendRemainingSeconds","Math","max","ceil","isResendCoolingDown","isExpired","isPending","tickNow","interval","window","setInterval","nextTick","clearInterval","current","focus","setHeadingRef","heading","submit","event","preventDefault","validateCode","test","role","type","form","className","noValidate","onSubmit","label","id","autoComplete","inputMode","maxLength","pattern","onChange","target","replace","slice","onBlur","disabled","isLoading","aria-disabled","variant","onClick"],"mappings":"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"}
@@ -85,7 +85,7 @@ export function PasswordLoginForm({ children, invitationEmail } = {}) {
85
85
  });
86
86
  }
87
87
  return /*#__PURE__*/ _jsxs("form", {
88
- className: "flex flex-col gap-18",
88
+ className: "flex flex-col gap-group",
89
89
  noValidate: true,
90
90
  onSubmit: (event)=>{
91
91
  event.preventDefault();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/password-login-form.tsx"],"sourcesContent":["import {loginBodySchema} from '@shipfox/api-auth-dto';\nimport {Button} 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 {useForm} from '@tanstack/react-form';\nimport {useAtom} from 'jotai';\nimport {type ReactNode, useEffect, useRef, useState} from 'react';\nimport {useLoginAuth} from '#hooks/api/login-auth.js';\nimport {loginErrorToFormError} from '#pages/form-errors.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\n\nexport interface PasswordLoginFormProps {\n children?: ReactNode;\n invitationEmail?: string | undefined;\n}\n\nexport function PasswordLoginForm({children, invitationEmail}: PasswordLoginFormProps = {}) {\n const login = useLoginAuth();\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [formError, setFormError] = 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},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n await login.mutateAsync(value);\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n } catch (error) {\n const mapped = loginErrorToFormError(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 useEffect(() => {\n if (invitationEmail && form.state.values.email !== invitationEmail) {\n form.setFieldValue('email', invitationEmail);\n setAuthFormDraft((current) => ({...current, email: invitationEmail}));\n }\n }, [form, invitationEmail, setAuthFormDraft]);\n\n // Sync TanStack Form values back into the Jotai draft on unmount so a\n // navigation to /signup or /reset preserves what the user typed. Skipped\n // after a successful login 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 function persistDraft() {\n const {email, password} = form.state.values;\n setAuthFormDraft({email, password});\n }\n\n return (\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={{onBlur: loginBodySchema.shape.email, onSubmit: loginBodySchema.shape.email}}\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={Boolean(invitationEmail)}\n iconRight={\n invitationEmail ? (\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: loginBodySchema.shape.password,\n onSubmit: loginBodySchema.shape.password,\n }}\n >\n {(field) => (\n <FormField label=\"Password\" id=\"password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"current-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 {children}\n <Button className=\"w-full\" isLoading={login.isPending} type=\"submit\">\n {login.isPending ? 'Logging in...' : 'Log in'}\n </Button>\n </form>\n );\n}\n"],"names":["loginBodySchema","Button","Callout","FormField","FormFieldInput","fieldError","Icon","useForm","useAtom","useEffect","useRef","useState","useLoginAuth","loginErrorToFormError","authFormDraftAtom","initialAuthFormDraft","PasswordLoginForm","children","invitationEmail","login","authFormDraft","setAuthFormDraft","formError","setFormError","draftRef","current","skipDraftPersistRef","form","defaultValues","email","password","onSubmit","value","undefined","mutateAsync","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","state","values","setFieldValue","persistDraft","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","role","type","Field","name","validators","onBlur","shape","label","id","autoComplete","onChange","handleChange","target","handleBlur","readOnly","Boolean","iconRight","aria-hidden","isLoading","isPending"],"mappings":";AAAA,SAAQA,eAAe,QAAO,wBAAwB;AACtD,SAAQC,MAAM,QAAO,2BAA2B;AAChD,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,SAAS,EAAEC,cAAc,EAAEC,UAAU,QAAO,+BAA+B;AACnF,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,OAAO,QAAO,uBAAuB;AAC7C,SAAQC,OAAO,QAAO,QAAQ;AAC9B,SAAwBC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAClE,SAAQC,YAAY,QAAO,2BAA2B;AACtD,SAAQC,qBAAqB,QAAO,wBAAwB;AAC5D,SAAQC,iBAAiB,EAAEC,oBAAoB,QAAO,iBAAiB;AAOvE,OAAO,SAASC,kBAAkB,EAACC,QAAQ,EAAEC,eAAe,EAAyB,GAAG,CAAC,CAAC;IACxF,MAAMC,QAAQP;IACd,MAAM,CAACQ,eAAeC,iBAAiB,GAAGb,QAAQM;IAClD,MAAM,CAACQ,WAAWC,aAAa,GAAGZ;IAClC,MAAMa,WAAWd,OAAOU;IACxBI,SAASC,OAAO,GAAGL;IACnB,uEAAuE;IACvE,2DAA2D;IAC3D,MAAMM,sBAAsBhB,OAAO;IAEnC,MAAMiB,OAAOpB,QAAQ;QACnBqB,eAAe;YAACC,OAAOT,cAAcS,KAAK;YAAEC,UAAUV,cAAcU,QAAQ;QAAA;QAC5EC,UAAU,OAAO,EAACC,KAAK,EAAC;YACtBT,aAAaU;YACb,IAAI;gBACF,MAAMd,MAAMe,WAAW,CAACF;gBACxBN,oBAAoBD,OAAO,GAAG;gBAC9BJ,iBAAiBN;YACnB,EAAE,OAAOoB,OAAO;gBACd,MAAMC,SAASvB,sBAAsBsB;gBACrC,IAAIC,OAAOC,IAAI,KAAK,SAAS;oBAC3BV,KAAKW,YAAY,CAACF,OAAOG,KAAK,EAAE,CAACC,OAAU,CAAA;4BACzC,GAAGA,IAAI;4BACPC,UAAU;gCAAC,GAAGD,KAAKC,QAAQ;gCAAEC,UAAUN,OAAOO,OAAO;4BAAA;wBACvD,CAAA;gBACF,OAAO;oBACLpB,aAAaa,OAAOO,OAAO;gBAC7B;YACF;QACF;IACF;IAEAlC,UAAU;QACR,IAAIS,mBAAmBS,KAAKiB,KAAK,CAACC,MAAM,CAAChB,KAAK,KAAKX,iBAAiB;YAClES,KAAKmB,aAAa,CAAC,SAAS5B;YAC5BG,iBAAiB,CAACI,UAAa,CAAA;oBAAC,GAAGA,OAAO;oBAAEI,OAAOX;gBAAe,CAAA;QACpE;IACF,GAAG;QAACS;QAAMT;QAAiBG;KAAiB;IAE5C,sEAAsE;IACtE,yEAAyE;IACzE,4EAA4E;IAC5EZ,UAAU;QACR,OAAO;YACL,IAAIiB,oBAAoBD,OAAO,EAAE;YACjC,MAAM,EAACI,KAAK,EAAEC,QAAQ,EAAC,GAAGH,KAAKiB,KAAK,CAACC,MAAM;YAC3C,IAAIhB,UAAUL,SAASC,OAAO,CAACI,KAAK,IAAIC,aAAaN,SAASC,OAAO,CAACK,QAAQ,EAAE;gBAC9ET,iBAAiB;oBAACQ;oBAAOC;gBAAQ;YACnC;QACF;IACF,GAAG;QAACH;QAAMN;KAAiB;IAE3B,SAAS0B;QACP,MAAM,EAAClB,KAAK,EAAEC,QAAQ,EAAC,GAAGH,KAAKiB,KAAK,CAACC,MAAM;QAC3CxB,iBAAiB;YAACQ;YAAOC;QAAQ;IACnC;IAEA,qBACE,MAACH;QACCqB,WAAU;QACVC,UAAU;QACVlB,UAAU,CAACmB;YACTA,MAAMC,cAAc;YACpBD,MAAME,eAAe;YACrB,KAAKzB,KAAK0B,YAAY;QACxB;;YAEC/B,0BACC,KAACpB;gBAAQoD,MAAK;gBAAQC,MAAK;0BACxBjC;iBAED;0BACJ,KAACK,KAAK6B,KAAK;gBACTC,MAAK;gBACLC,YAAY;oBAACC,QAAQ3D,gBAAgB4D,KAAK,CAAC/B,KAAK;oBAAEE,UAAU/B,gBAAgB4D,KAAK,CAAC/B,KAAK;gBAAA;0BAEtF,CAACU,sBACA,KAACpC;wBAAU0D,OAAM;wBAAQC,IAAG;wBAAQ3B,OAAO9B,WAAWkC;kCACpD,cAAA,KAACnC;4BACC2D,cAAa;4BACbN,MAAK;4BACLF,MAAK;4BACLvB,OAAOO,MAAMK,KAAK,CAACZ,KAAK;4BACxBgC,UAAU,CAACd,QAAUX,MAAM0B,YAAY,CAACf,MAAMgB,MAAM,CAAClC,KAAK;4BAC1D2B,QAAQ;gCACNpB,MAAM4B,UAAU;gCAChBpB;4BACF;4BACAqB,UAAUC,QAAQnD;4BAClBoD,WACEpD,gCACE,KAACZ;gCACCiE,eAAY;gCACZvB,WAAU;gCACVS,MAAK;iCAELxB;;;;0BAMd,KAACN,KAAK6B,KAAK;gBACTC,MAAK;gBACLC,YAAY;oBACVC,QAAQ3D,gBAAgB4D,KAAK,CAAC9B,QAAQ;oBACtCC,UAAU/B,gBAAgB4D,KAAK,CAAC9B,QAAQ;gBAC1C;0BAEC,CAACS,sBACA,KAACpC;wBAAU0D,OAAM;wBAAWC,IAAG;wBAAW3B,OAAO9B,WAAWkC;kCAC1D,cAAA,KAACnC;4BACC2D,cAAa;4BACbN,MAAK;4BACLF,MAAK;4BACLvB,OAAOO,MAAMK,KAAK,CAACZ,KAAK;4BACxBgC,UAAU,CAACd,QAAUX,MAAM0B,YAAY,CAACf,MAAMgB,MAAM,CAAClC,KAAK;4BAC1D2B,QAAQ;gCACNpB,MAAM4B,UAAU;gCAChBpB;4BACF;;;;YAKP9B;0BACD,KAAChB;gBAAO+C,WAAU;gBAASwB,WAAWrD,MAAMsD,SAAS;gBAAElB,MAAK;0BACzDpC,MAAMsD,SAAS,GAAG,kBAAkB;;;;AAI7C"}
1
+ {"version":3,"sources":["../../src/components/password-login-form.tsx"],"sourcesContent":["import {loginBodySchema} from '@shipfox/api-auth-dto';\nimport {Button} 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 {useForm} from '@tanstack/react-form';\nimport {useAtom} from 'jotai';\nimport {type ReactNode, useEffect, useRef, useState} from 'react';\nimport {useLoginAuth} from '#hooks/api/login-auth.js';\nimport {loginErrorToFormError} from '#pages/form-errors.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\n\nexport interface PasswordLoginFormProps {\n children?: ReactNode;\n invitationEmail?: string | undefined;\n}\n\nexport function PasswordLoginForm({children, invitationEmail}: PasswordLoginFormProps = {}) {\n const login = useLoginAuth();\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [formError, setFormError] = 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},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n await login.mutateAsync(value);\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n } catch (error) {\n const mapped = loginErrorToFormError(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 useEffect(() => {\n if (invitationEmail && form.state.values.email !== invitationEmail) {\n form.setFieldValue('email', invitationEmail);\n setAuthFormDraft((current) => ({...current, email: invitationEmail}));\n }\n }, [form, invitationEmail, setAuthFormDraft]);\n\n // Sync TanStack Form values back into the Jotai draft on unmount so a\n // navigation to /signup or /reset preserves what the user typed. Skipped\n // after a successful login 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 function persistDraft() {\n const {email, password} = form.state.values;\n setAuthFormDraft({email, password});\n }\n\n return (\n <form\n className=\"flex flex-col gap-group\"\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={{onBlur: loginBodySchema.shape.email, onSubmit: loginBodySchema.shape.email}}\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={Boolean(invitationEmail)}\n iconRight={\n invitationEmail ? (\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: loginBodySchema.shape.password,\n onSubmit: loginBodySchema.shape.password,\n }}\n >\n {(field) => (\n <FormField label=\"Password\" id=\"password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"current-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 {children}\n <Button className=\"w-full\" isLoading={login.isPending} type=\"submit\">\n {login.isPending ? 'Logging in...' : 'Log in'}\n </Button>\n </form>\n );\n}\n"],"names":["loginBodySchema","Button","Callout","FormField","FormFieldInput","fieldError","Icon","useForm","useAtom","useEffect","useRef","useState","useLoginAuth","loginErrorToFormError","authFormDraftAtom","initialAuthFormDraft","PasswordLoginForm","children","invitationEmail","login","authFormDraft","setAuthFormDraft","formError","setFormError","draftRef","current","skipDraftPersistRef","form","defaultValues","email","password","onSubmit","value","undefined","mutateAsync","error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","state","values","setFieldValue","persistDraft","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","role","type","Field","name","validators","onBlur","shape","label","id","autoComplete","onChange","handleChange","target","handleBlur","readOnly","Boolean","iconRight","aria-hidden","isLoading","isPending"],"mappings":"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"}
@@ -41,7 +41,7 @@ export function WorkspaceSwitcher({ activeWorkspaceId, onSelect }) {
41
41
  className: "max-h-none overflow-visible overflow-y-visible p-0",
42
42
  children: [
43
43
  /*#__PURE__*/ _jsxs("div", {
44
- className: "max-h-300 overflow-y-auto overflow-x-hidden p-4 scrollbar",
44
+ className: "max-h-300 overflow-y-auto overflow-x-hidden p-menu-surface scrollbar",
45
45
  children: [
46
46
  /*#__PURE__*/ _jsx(CommandEmpty, {
47
47
  children: "No workspaces found."
@@ -57,7 +57,7 @@ export function WorkspaceSwitcher({ activeWorkspaceId, onSelect }) {
57
57
  children: [
58
58
  /*#__PURE__*/ _jsx(Icon, {
59
59
  name: "check",
60
- className: `size-16 mr-8 ${activeWorkspaceId === workspace.id ? 'opacity-100' : 'opacity-0'}`
60
+ className: `size-16 ${activeWorkspaceId === workspace.id ? 'opacity-100' : 'opacity-0'}`
61
61
  }),
62
62
  workspace.name
63
63
  ]
@@ -71,7 +71,7 @@ export function WorkspaceSwitcher({ activeWorkspaceId, onSelect }) {
71
71
  }),
72
72
  /*#__PURE__*/ _jsx(CommandGroup, {
73
73
  forceMount: true,
74
- className: "p-4",
74
+ className: "p-menu-surface",
75
75
  children: /*#__PURE__*/ _jsxs(CommandItem, {
76
76
  value: "__create",
77
77
  onSelect: handleCreate,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/workspace-switcher.tsx"],"sourcesContent":["import {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from '@shipfox/react-ui/command';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useSetAtom} from 'jotai';\nimport {useAuthState} from '#hooks/use-auth-state.js';\nimport {lastWorkspaceIdAtom, rememberLastWorkspaceId} from '#state/last-workspace.js';\n\nexport interface WorkspaceSwitcherProps {\n activeWorkspaceId: string | undefined;\n onSelect?: () => void;\n}\n\nexport function WorkspaceSwitcher({activeWorkspaceId, onSelect}: WorkspaceSwitcherProps) {\n const {user, workspaces} = useAuthState();\n const navigate = useNavigate();\n const setLastWorkspaceId = useSetAtom(lastWorkspaceIdAtom);\n\n const handleSelect = (workspaceId: string) => {\n const workspace = workspaces.find((candidate) => candidate.id === workspaceId);\n if (!workspace) return;\n try {\n setLastWorkspaceId(workspaceId);\n if (user?.id) rememberLastWorkspaceId(user.id, workspaceId);\n } catch {\n // localStorage may throw in private browsing or quota-exceeded; persistence is best-effort.\n }\n navigate({to: '/w/$workspaceSlug', params: {workspaceSlug: workspace.slug}});\n onSelect?.();\n };\n\n const handleCreate = () => {\n navigate({to: '/setup/workspaces/new'});\n onSelect?.();\n };\n\n return (\n <Command>\n <CommandInput placeholder=\"Search workspaces...\" />\n <CommandList className=\"max-h-none overflow-visible overflow-y-visible p-0\">\n <div className=\"max-h-300 overflow-y-auto overflow-x-hidden p-4 scrollbar\">\n <CommandEmpty>No workspaces found.</CommandEmpty>\n <CommandGroup heading=\"Workspaces\">\n {workspaces.map((workspace) => (\n <CommandItem\n key={workspace.id}\n value={workspace.id}\n keywords={[workspace.name]}\n onSelect={() => handleSelect(workspace.id)}\n >\n <Icon\n name=\"check\"\n className={`size-16 mr-8 ${\n activeWorkspaceId === workspace.id ? 'opacity-100' : 'opacity-0'\n }`}\n />\n {workspace.name}\n </CommandItem>\n ))}\n </CommandGroup>\n </div>\n <CommandSeparator alwaysRender className=\"mx-0\" />\n <CommandGroup forceMount className=\"p-4\">\n <CommandItem value=\"__create\" onSelect={handleCreate} forceMount>\n <Icon name=\"addLine\" className=\"size-16\" />\n Create workspace\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n );\n}\n"],"names":["Command","CommandEmpty","CommandGroup","CommandInput","CommandItem","CommandList","CommandSeparator","Icon","useNavigate","useSetAtom","useAuthState","lastWorkspaceIdAtom","rememberLastWorkspaceId","WorkspaceSwitcher","activeWorkspaceId","onSelect","user","workspaces","navigate","setLastWorkspaceId","handleSelect","workspaceId","workspace","find","candidate","id","to","params","workspaceSlug","slug","handleCreate","placeholder","className","div","heading","map","value","keywords","name","alwaysRender","forceMount"],"mappings":";AAAA,SACEA,OAAO,EACPC,YAAY,EACZC,YAAY,EACZC,YAAY,EACZC,WAAW,EACXC,WAAW,EACXC,gBAAgB,QACX,4BAA4B;AACnC,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,yBAAyB;AACnD,SAAQC,UAAU,QAAO,QAAQ;AACjC,SAAQC,YAAY,QAAO,2BAA2B;AACtD,SAAQC,mBAAmB,EAAEC,uBAAuB,QAAO,2BAA2B;AAOtF,OAAO,SAASC,kBAAkB,EAACC,iBAAiB,EAAEC,QAAQ,EAAyB;IACrF,MAAM,EAACC,IAAI,EAAEC,UAAU,EAAC,GAAGP;IAC3B,MAAMQ,WAAWV;IACjB,MAAMW,qBAAqBV,WAAWE;IAEtC,MAAMS,eAAe,CAACC;QACpB,MAAMC,YAAYL,WAAWM,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKJ;QAClE,IAAI,CAACC,WAAW;QAChB,IAAI;YACFH,mBAAmBE;YACnB,IAAIL,MAAMS,IAAIb,wBAAwBI,KAAKS,EAAE,EAAEJ;QACjD,EAAE,OAAM;QACN,4FAA4F;QAC9F;QACAH,SAAS;YAACQ,IAAI;YAAqBC,QAAQ;gBAACC,eAAeN,UAAUO,IAAI;YAAA;QAAC;QAC1Ed;IACF;IAEA,MAAMe,eAAe;QACnBZ,SAAS;YAACQ,IAAI;QAAuB;QACrCX;IACF;IAEA,qBACE,MAACf;;0BACC,KAACG;gBAAa4B,aAAY;;0BAC1B,MAAC1B;gBAAY2B,WAAU;;kCACrB,MAACC;wBAAID,WAAU;;0CACb,KAAC/B;0CAAa;;0CACd,KAACC;gCAAagC,SAAQ;0CACnBjB,WAAWkB,GAAG,CAAC,CAACb,0BACf,MAAClB;wCAECgC,OAAOd,UAAUG,EAAE;wCACnBY,UAAU;4CAACf,UAAUgB,IAAI;yCAAC;wCAC1BvB,UAAU,IAAMK,aAAaE,UAAUG,EAAE;;0DAEzC,KAAClB;gDACC+B,MAAK;gDACLN,WAAW,CAAC,aAAa,EACvBlB,sBAAsBQ,UAAUG,EAAE,GAAG,gBAAgB,aACrD;;4CAEHH,UAAUgB,IAAI;;uCAXVhB,UAAUG,EAAE;;;;kCAgBzB,KAACnB;wBAAiBiC,YAAY;wBAACP,WAAU;;kCACzC,KAAC9B;wBAAasC,UAAU;wBAACR,WAAU;kCACjC,cAAA,MAAC5B;4BAAYgC,OAAM;4BAAWrB,UAAUe;4BAAcU,UAAU;;8CAC9D,KAACjC;oCAAK+B,MAAK;oCAAUN,WAAU;;gCAAY;;;;;;;;AAOvD"}
1
+ {"version":3,"sources":["../../src/components/workspace-switcher.tsx"],"sourcesContent":["import {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from '@shipfox/react-ui/command';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {useNavigate} from '@tanstack/react-router';\nimport {useSetAtom} from 'jotai';\nimport {useAuthState} from '#hooks/use-auth-state.js';\nimport {lastWorkspaceIdAtom, rememberLastWorkspaceId} from '#state/last-workspace.js';\n\nexport interface WorkspaceSwitcherProps {\n activeWorkspaceId: string | undefined;\n onSelect?: () => void;\n}\n\nexport function WorkspaceSwitcher({activeWorkspaceId, onSelect}: WorkspaceSwitcherProps) {\n const {user, workspaces} = useAuthState();\n const navigate = useNavigate();\n const setLastWorkspaceId = useSetAtom(lastWorkspaceIdAtom);\n\n const handleSelect = (workspaceId: string) => {\n const workspace = workspaces.find((candidate) => candidate.id === workspaceId);\n if (!workspace) return;\n try {\n setLastWorkspaceId(workspaceId);\n if (user?.id) rememberLastWorkspaceId(user.id, workspaceId);\n } catch {\n // localStorage may throw in private browsing or quota-exceeded; persistence is best-effort.\n }\n navigate({to: '/w/$workspaceSlug', params: {workspaceSlug: workspace.slug}});\n onSelect?.();\n };\n\n const handleCreate = () => {\n navigate({to: '/setup/workspaces/new'});\n onSelect?.();\n };\n\n return (\n <Command>\n <CommandInput placeholder=\"Search workspaces...\" />\n <CommandList className=\"max-h-none overflow-visible overflow-y-visible p-0\">\n <div className=\"max-h-300 overflow-y-auto overflow-x-hidden p-menu-surface scrollbar\">\n <CommandEmpty>No workspaces found.</CommandEmpty>\n <CommandGroup heading=\"Workspaces\">\n {workspaces.map((workspace) => (\n <CommandItem\n key={workspace.id}\n value={workspace.id}\n keywords={[workspace.name]}\n onSelect={() => handleSelect(workspace.id)}\n >\n <Icon\n name=\"check\"\n className={`size-16 ${\n activeWorkspaceId === workspace.id ? 'opacity-100' : 'opacity-0'\n }`}\n />\n {workspace.name}\n </CommandItem>\n ))}\n </CommandGroup>\n </div>\n <CommandSeparator alwaysRender className=\"mx-0\" />\n <CommandGroup forceMount className=\"p-menu-surface\">\n <CommandItem value=\"__create\" onSelect={handleCreate} forceMount>\n <Icon name=\"addLine\" className=\"size-16\" />\n Create workspace\n </CommandItem>\n </CommandGroup>\n </CommandList>\n </Command>\n );\n}\n"],"names":["Command","CommandEmpty","CommandGroup","CommandInput","CommandItem","CommandList","CommandSeparator","Icon","useNavigate","useSetAtom","useAuthState","lastWorkspaceIdAtom","rememberLastWorkspaceId","WorkspaceSwitcher","activeWorkspaceId","onSelect","user","workspaces","navigate","setLastWorkspaceId","handleSelect","workspaceId","workspace","find","candidate","id","to","params","workspaceSlug","slug","handleCreate","placeholder","className","div","heading","map","value","keywords","name","alwaysRender","forceMount"],"mappings":";AAAA,SACEA,OAAO,EACPC,YAAY,EACZC,YAAY,EACZC,YAAY,EACZC,WAAW,EACXC,WAAW,EACXC,gBAAgB,QACX,4BAA4B;AACnC,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,WAAW,QAAO,yBAAyB;AACnD,SAAQC,UAAU,QAAO,QAAQ;AACjC,SAAQC,YAAY,QAAO,2BAA2B;AACtD,SAAQC,mBAAmB,EAAEC,uBAAuB,QAAO,2BAA2B;AAOtF,OAAO,SAASC,kBAAkB,EAACC,iBAAiB,EAAEC,QAAQ,EAAyB;IACrF,MAAM,EAACC,IAAI,EAAEC,UAAU,EAAC,GAAGP;IAC3B,MAAMQ,WAAWV;IACjB,MAAMW,qBAAqBV,WAAWE;IAEtC,MAAMS,eAAe,CAACC;QACpB,MAAMC,YAAYL,WAAWM,IAAI,CAAC,CAACC,YAAcA,UAAUC,EAAE,KAAKJ;QAClE,IAAI,CAACC,WAAW;QAChB,IAAI;YACFH,mBAAmBE;YACnB,IAAIL,MAAMS,IAAIb,wBAAwBI,KAAKS,EAAE,EAAEJ;QACjD,EAAE,OAAM;QACN,4FAA4F;QAC9F;QACAH,SAAS;YAACQ,IAAI;YAAqBC,QAAQ;gBAACC,eAAeN,UAAUO,IAAI;YAAA;QAAC;QAC1Ed;IACF;IAEA,MAAMe,eAAe;QACnBZ,SAAS;YAACQ,IAAI;QAAuB;QACrCX;IACF;IAEA,qBACE,MAACf;;0BACC,KAACG;gBAAa4B,aAAY;;0BAC1B,MAAC1B;gBAAY2B,WAAU;;kCACrB,MAACC;wBAAID,WAAU;;0CACb,KAAC/B;0CAAa;;0CACd,KAACC;gCAAagC,SAAQ;0CACnBjB,WAAWkB,GAAG,CAAC,CAACb,0BACf,MAAClB;wCAECgC,OAAOd,UAAUG,EAAE;wCACnBY,UAAU;4CAACf,UAAUgB,IAAI;yCAAC;wCAC1BvB,UAAU,IAAMK,aAAaE,UAAUG,EAAE;;0DAEzC,KAAClB;gDACC+B,MAAK;gDACLN,WAAW,CAAC,QAAQ,EAClBlB,sBAAsBQ,UAAUG,EAAE,GAAG,gBAAgB,aACrD;;4CAEHH,UAAUgB,IAAI;;uCAXVhB,UAAUG,EAAE;;;;kCAgBzB,KAACnB;wBAAiBiC,YAAY;wBAACP,WAAU;;kCACzC,KAAC9B;wBAAasC,UAAU;wBAACR,WAAU;kCACjC,cAAA,MAAC5B;4BAAYgC,OAAM;4BAAWrB,UAAUe;4BAAcU,UAAU;;8CAC9D,KAACjC;oCAAK+B,MAAK;oCAAUN,WAAU;;gCAAY;;;;;;;;AAOvD"}
@@ -23,7 +23,7 @@ export function LoginPage() {
23
23
  children: /*#__PURE__*/ _jsx(ButtonLink, {
24
24
  asChild: true,
25
25
  variant: "subtle",
26
- className: "-mt-8 self-end",
26
+ className: "-mt-inline self-end",
27
27
  children: /*#__PURE__*/ _jsx(Link, {
28
28
  to: "/auth/reset",
29
29
  children: "Forgot password?"
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/login-page.tsx"],"sourcesContent":["import {AuthShell, useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {PasswordLoginForm} from '#components/password-login-form.js';\nimport {validateRedirectSearch} from '../routes/inputs.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function LoginPage() {\n const search = useRouteSearch(validateRedirectSearch);\n const invitationToken = extractInvitationToken(search.redirect);\n const invitationPreview = useInvitationContext(invitationToken);\n const invitationPending = pendingInvitation(invitationPreview.data);\n const invitationRedirect = invitationToken\n ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}`\n : undefined;\n const headerTitle = invitationPending\n ? `Join ${invitationPending.workspaceName}`\n : 'Connect to Shipfox';\n const headerDescription = invitationPending\n ? 'Log in to accept your invitation.'\n : 'Log in to access Shipfox.';\n\n return (\n <AuthShell title={headerTitle} description={headerDescription}>\n <PasswordLoginForm invitationEmail={invitationPending?.email}>\n <ButtonLink asChild variant=\"subtle\" className=\"-mt-8 self-end\">\n <Link to=\"/auth/reset\">Forgot password?</Link>\n </ButtonLink>\n </PasswordLoginForm>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n New to Shipfox?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link\n to=\"/auth/signup\"\n search={invitationRedirect ? {redirect: invitationRedirect} : undefined}\n >\n Create an account\n </Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n"],"names":["AuthShell","useRouteSearch","ButtonLink","Text","Link","PasswordLoginForm","validateRedirectSearch","extractInvitationToken","pendingInvitation","useInvitationContext","LoginPage","search","invitationToken","redirect","invitationPreview","invitationPending","data","invitationRedirect","encodeURIComponent","undefined","headerTitle","workspaceName","headerDescription","title","description","invitationEmail","email","asChild","variant","className","to","size","underline"],"mappings":";AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,gCAAgC;AACxE,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,iBAAiB,QAAO,qCAAqC;AACrE,SAAQC,sBAAsB,QAAO,sBAAsB;AAC3D,SACEC,sBAAsB,EACtBC,iBAAiB,EACjBC,oBAAoB,QACf,0BAA0B;AAEjC,OAAO,SAASC;IACd,MAAMC,SAASV,eAAeK;IAC9B,MAAMM,kBAAkBL,uBAAuBI,OAAOE,QAAQ;IAC9D,MAAMC,oBAAoBL,qBAAqBG;IAC/C,MAAMG,oBAAoBP,kBAAkBM,kBAAkBE,IAAI;IAClE,MAAMC,qBAAqBL,kBACvB,CAAC,0BAA0B,EAAEM,mBAAmBN,kBAAkB,GAClEO;IACJ,MAAMC,cAAcL,oBAChB,CAAC,KAAK,EAAEA,kBAAkBM,aAAa,EAAE,GACzC;IACJ,MAAMC,oBAAoBP,oBACtB,sCACA;IAEJ,qBACE,MAACf;QAAUuB,OAAOH;QAAaI,aAAaF;;0BAC1C,KAACjB;gBAAkBoB,iBAAiBV,mBAAmBW;0BACrD,cAAA,KAACxB;oBAAWyB,OAAO;oBAACC,SAAQ;oBAASC,WAAU;8BAC7C,cAAA,KAACzB;wBAAK0B,IAAG;kCAAc;;;;0BAG3B,MAAC3B;gBAAK4B,MAAK;gBAAKF,WAAU;;oBAA6C;oBACrD;kCAChB,KAAC3B;wBAAWyB,OAAO;wBAACC,SAAQ;wBAAcI,SAAS;kCACjD,cAAA,KAAC5B;4BACC0B,IAAG;4BACHnB,QAAQM,qBAAqB;gCAACJ,UAAUI;4BAAkB,IAAIE;sCAC/D;;;;;;;AAOX"}
1
+ {"version":3,"sources":["../../src/pages/login-page.tsx"],"sourcesContent":["import {AuthShell, useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {ButtonLink} from '@shipfox/react-ui/button';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {Link} from '@tanstack/react-router';\nimport {PasswordLoginForm} from '#components/password-login-form.js';\nimport {validateRedirectSearch} from '../routes/inputs.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function LoginPage() {\n const search = useRouteSearch(validateRedirectSearch);\n const invitationToken = extractInvitationToken(search.redirect);\n const invitationPreview = useInvitationContext(invitationToken);\n const invitationPending = pendingInvitation(invitationPreview.data);\n const invitationRedirect = invitationToken\n ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}`\n : undefined;\n const headerTitle = invitationPending\n ? `Join ${invitationPending.workspaceName}`\n : 'Connect to Shipfox';\n const headerDescription = invitationPending\n ? 'Log in to accept your invitation.'\n : 'Log in to access Shipfox.';\n\n return (\n <AuthShell title={headerTitle} description={headerDescription}>\n <PasswordLoginForm invitationEmail={invitationPending?.email}>\n <ButtonLink asChild variant=\"subtle\" className=\"-mt-inline self-end\">\n <Link to=\"/auth/reset\">Forgot password?</Link>\n </ButtonLink>\n </PasswordLoginForm>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n New to Shipfox?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link\n to=\"/auth/signup\"\n search={invitationRedirect ? {redirect: invitationRedirect} : undefined}\n >\n Create an account\n </Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n"],"names":["AuthShell","useRouteSearch","ButtonLink","Text","Link","PasswordLoginForm","validateRedirectSearch","extractInvitationToken","pendingInvitation","useInvitationContext","LoginPage","search","invitationToken","redirect","invitationPreview","invitationPending","data","invitationRedirect","encodeURIComponent","undefined","headerTitle","workspaceName","headerDescription","title","description","invitationEmail","email","asChild","variant","className","to","size","underline"],"mappings":";AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,gCAAgC;AACxE,SAAQC,UAAU,QAAO,2BAA2B;AACpD,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,IAAI,QAAO,yBAAyB;AAC5C,SAAQC,iBAAiB,QAAO,qCAAqC;AACrE,SAAQC,sBAAsB,QAAO,sBAAsB;AAC3D,SACEC,sBAAsB,EACtBC,iBAAiB,EACjBC,oBAAoB,QACf,0BAA0B;AAEjC,OAAO,SAASC;IACd,MAAMC,SAASV,eAAeK;IAC9B,MAAMM,kBAAkBL,uBAAuBI,OAAOE,QAAQ;IAC9D,MAAMC,oBAAoBL,qBAAqBG;IAC/C,MAAMG,oBAAoBP,kBAAkBM,kBAAkBE,IAAI;IAClE,MAAMC,qBAAqBL,kBACvB,CAAC,0BAA0B,EAAEM,mBAAmBN,kBAAkB,GAClEO;IACJ,MAAMC,cAAcL,oBAChB,CAAC,KAAK,EAAEA,kBAAkBM,aAAa,EAAE,GACzC;IACJ,MAAMC,oBAAoBP,oBACtB,sCACA;IAEJ,qBACE,MAACf;QAAUuB,OAAOH;QAAaI,aAAaF;;0BAC1C,KAACjB;gBAAkBoB,iBAAiBV,mBAAmBW;0BACrD,cAAA,KAACxB;oBAAWyB,OAAO;oBAACC,SAAQ;oBAASC,WAAU;8BAC7C,cAAA,KAACzB;wBAAK0B,IAAG;kCAAc;;;;0BAG3B,MAAC3B;gBAAK4B,MAAK;gBAAKF,WAAU;;oBAA6C;oBACrD;kCAChB,KAAC3B;wBAAWyB,OAAO;wBAACC,SAAQ;wBAAcI,SAAS;kCACjD,cAAA,KAAC5B;4BACC0B,IAAG;4BACHnB,QAAQM,qBAAqB;gCAACJ,UAAUI;4BAAkB,IAAIE;sCAC/D;;;;;;;AAOX"}
@@ -102,7 +102,7 @@ function PasswordResetRequest() {
102
102
  description: "Enter your email to get a reset link.",
103
103
  children: [
104
104
  /*#__PURE__*/ _jsxs("form", {
105
- className: "flex flex-col gap-18",
105
+ className: "flex flex-col gap-group",
106
106
  noValidate: true,
107
107
  onSubmit: (event)=>{
108
108
  event.preventDefault();
@@ -206,7 +206,7 @@ function PasswordResetConfirm({ token }) {
206
206
  description: "Choose a password for your Shipfox account.",
207
207
  children: [
208
208
  /*#__PURE__*/ _jsxs("form", {
209
- className: "flex flex-col gap-18",
209
+ className: "flex flex-col gap-group",
210
210
  noValidate: true,
211
211
  onSubmit: (event)=>{
212
212
  event.preventDefault();
@@ -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 {AuthShell, useRouteSearch} 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} 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 {validatePasswordResetSearch} from '../routes/inputs.js';\nimport {\n passwordResetConfirmErrorToFormError,\n passwordResetRequestErrorToFormError,\n} from './form-errors.js';\n\nexport function PasswordResetPage() {\n const {token} = useRouteSearch(validatePasswordResetSearch);\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({token: body.token, newPassword: body.new_password});\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","useRouteSearch","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Text","useForm","Link","useNavigate","useAtom","useEffect","useRef","useState","useConfirmPasswordResetAuth","useRequestPasswordResetAuth","authFormDraftAtom","initialAuthFormDraft","validatePasswordResetSearch","passwordResetConfirmErrorToFormError","passwordResetRequestErrorToFormError","PasswordResetPage","token","PasswordResetConfirm","PasswordResetRequest","requestPasswordReset","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","formError","setFormError","draftRef","current","form","defaultValues","email","onSubmit","value","undefined","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","newPassword","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, useRouteSearch} 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} 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 {validatePasswordResetSearch} from '../routes/inputs.js';\nimport {\n passwordResetConfirmErrorToFormError,\n passwordResetRequestErrorToFormError,\n} from './form-errors.js';\n\nexport function PasswordResetPage() {\n const {token} = useRouteSearch(validatePasswordResetSearch);\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-group\"\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({token: body.token, newPassword: body.new_password});\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-group\"\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","useRouteSearch","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Text","useForm","Link","useNavigate","useAtom","useEffect","useRef","useState","useConfirmPasswordResetAuth","useRequestPasswordResetAuth","authFormDraftAtom","initialAuthFormDraft","validatePasswordResetSearch","passwordResetConfirmErrorToFormError","passwordResetRequestErrorToFormError","PasswordResetPage","token","PasswordResetConfirm","PasswordResetRequest","requestPasswordReset","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","formError","setFormError","draftRef","current","form","defaultValues","email","onSubmit","value","undefined","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","newPassword","success","replace"],"mappings":";AAAA,SACEA,8BAA8B,EAC9BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,SAAS,EAAEC,cAAc,QAAO,gCAAgC;AACxE,SAAQC,MAAM,EAAEC,UAAU,QAAO,2BAA2B;AAC5D,SAAQC,OAAO,QAAO,4BAA4B;AAClD,SAAQC,SAAS,EAAEC,cAAc,EAAEC,UAAU,QAAO,+BAA+B;AACnF,SAAQC,KAAK,QAAO,0BAA0B;AAC9C,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,OAAO,QAAO,uBAAuB;AAC7C,SAAQC,IAAI,EAAEC,WAAW,QAAO,yBAAyB;AACzD,SAAQC,OAAO,QAAO,QAAQ;AAC9B,SAAQC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAO,QAAQ;AAClD,SACEC,2BAA2B,EAC3BC,2BAA2B,QACtB,oCAAoC;AAC3C,SAAQC,iBAAiB,EAAEC,oBAAoB,QAAO,iBAAiB;AACvE,SAAQC,2BAA2B,QAAO,sBAAsB;AAChE,SACEC,oCAAoC,EACpCC,oCAAoC,QAC/B,mBAAmB;AAE1B,OAAO,SAASC;IACd,MAAM,EAACC,KAAK,EAAC,GAAGxB,eAAeoB;IAE/B,IAAII,OAAO;QACT,qBAAO,KAACC;YAAqBD,OAAOA;;IACtC;IAEA,qBAAO,KAACE;AACV;AAEA,SAASA;IACP,MAAMC,uBAAuBV;IAC7B,MAAM,CAACW,eAAeC,iBAAiB,GAAGjB,QAAQM;IAClD,MAAM,CAACY,gBAAgBC,kBAAkB,GAAGhB;IAC5C,MAAM,CAACiB,WAAWC,aAAa,GAAGlB;IAClC,MAAMmB,WAAWpB,OAAOc;IACxBM,SAASC,OAAO,GAAGP;IAEnB,MAAMQ,OAAO3B,QAAQ;QACnB4B,eAAe;YAACC,OAAOV,cAAcU,KAAK;QAAA;QAC1CC,UAAU,OAAO,EAACC,KAAK,EAAC;YACtBP,aAAaQ;YACb,IAAI;gBACF,MAAMC,SAAS5C,+BAA+B6C,KAAK,CAACH;gBACpD,MAAMb,qBAAqBiB,WAAW,CAACF;gBACvCX,kBAAkBW,OAAOJ,KAAK;YAChC,EAAE,OAAOO,OAAO;gBACd,MAAMC,SAASxB,qCAAqCuB;gBACpDZ,aAAaa,OAAOC,OAAO;YAC7B;QACF;IACF;IAEAlC,UAAU;QACR,OAAO;YACL,MAAM,EAACyB,KAAK,EAAC,GAAGF,KAAKY,KAAK,CAACC,MAAM;YACjC,IAAIX,UAAUJ,SAASC,OAAO,CAACG,KAAK,EAAE;gBACpCT,iBAAiB,CAACM,UAAa,CAAA;wBAAC,GAAGA,OAAO;wBAAEG;oBAAK,CAAA;YACnD;QACF;IACF,GAAG;QAACF;QAAMP;KAAiB;IAE3B,IAAIC,gBAAgB;QAClB,qBACE,MAAC/B;YACCmD,OAAM;YACNC,aAAa,CAAC,8BAA8B,EAAErB,eAAe,CAAC,CAAC;;8BAE/D,KAAC3B;oBAAQiD,MAAK;oBAAQC,MAAK;8BAAU;;8BAGrC,KAACpD;oBACCqD,WAAU;oBACVC,SAAQ;oBACRF,MAAK;oBACLG,SAAS,IAAMzB,kBAAkBU;8BAClC;;8BAGD,MAACjC;oBAAKiD,MAAK;oBAAKH,WAAU;;wBAA6C;wBACtD;sCACf,KAACpD;4BAAWwD,OAAO;4BAACH,SAAQ;4BAAcI,SAAS;sCACjD,cAAA,KAACjD;gCAAKkD,IAAG;0CAAc;;;;;;;IAKjC;IAEA,qBACE,MAAC7D;QAAUmD,OAAM;QAAsBC,aAAY;;0BACjD,MAACf;gBACCkB,WAAU;gBACVO,UAAU;gBACVtB,UAAU,CAACuB;oBACTA,MAAMC,cAAc;oBACpBD,MAAME,eAAe;oBACrB,KAAK5B,KAAK6B,YAAY;gBACxB;;oBAECjC,0BACC,KAAC7B;wBAAQiD,MAAK;wBAAQC,MAAK;kCACxBrB;yBAED;kCACJ,KAACI,KAAK8B,KAAK;wBACTC,MAAK;wBACLC,YAAY;4BACVC,QAAQvE,+BAA+BwE,KAAK,CAAChC,KAAK;4BAClDC,UAAUzC,+BAA+BwE,KAAK,CAAChC,KAAK;wBACtD;kCAEC,CAACiC,sBACA,KAACnE;gCAAUoE,OAAM;gCAAQC,IAAG;gCAAQ5B,OAAOvC,WAAWiE;0CACpD,cAAA,KAAClE;oCACCqE,cAAa;oCACbP,MAAK;oCACLd,MAAK;oCACLb,OAAO+B,MAAMvB,KAAK,CAACR,KAAK;oCACxBmC,UAAU,CAACb,QAAUS,MAAMK,YAAY,CAACd,MAAMe,MAAM,CAACrC,KAAK;oCAC1D6B,QAAQ;wCACNE,MAAMO,UAAU;wCAChBjD,iBAAiB,CAACM,UAAa,CAAA;gDAAC,GAAGA,OAAO;gDAAEG,OAAOiC,MAAMvB,KAAK,CAACR,KAAK;4CAAA,CAAA;oCACtE;;;;kCAKR,KAACvC;wBAAOqD,WAAU;wBAASyB,WAAWpD,qBAAqBqD,SAAS;wBAAE3B,MAAK;kCACxE1B,qBAAqBqD,SAAS,GAAG,oBAAoB;;;;0BAG1D,MAACxE;gBAAKiD,MAAK;gBAAKH,WAAU;;oBAA6C;oBACtD;kCACf,KAACpD;wBAAWwD,OAAO;wBAACH,SAAQ;wBAAcI,SAAS;kCACjD,cAAA,KAACjD;4BAAKkD,IAAG;sCAAc;;;;;;;AAKjC;AAEA,SAASnC,qBAAqB,EAACD,KAAK,EAAkB;IACpD,MAAMyD,uBAAuBjE;IAC7B,MAAMkE,WAAWvE;IACjB,MAAM,GAAGkB,iBAAiB,GAAGjB,QAAQM;IACrC,MAAM,CAACc,WAAWC,aAAa,GAAGlB;IAElC,MAAMqB,OAAO3B,QAAQ;QACnB4B,eAAe;YAAC8C,cAAc;QAAE;QAChC5C,UAAU,OAAO,EAACC,KAAK,EAAC;YACtBP,aAAaQ;YACb,IAAI;gBACF,MAAM2C,OAAOvF,+BAA+B8C,KAAK,CAAC;oBAChDnB;oBACA2D,cAAc3C,MAAM2C,YAAY;gBAClC;gBACA,MAAMF,qBAAqBrC,WAAW,CAAC;oBAACpB,OAAO4D,KAAK5D,KAAK;oBAAE6D,aAAaD,KAAKD,YAAY;gBAAA;gBACzFtD,iBAAiBV;gBACjBZ,MAAM+E,OAAO,CAAC;gBACd,MAAMJ,SAAS;oBAACtB,IAAI;oBAAK2B,SAAS;gBAAI;YACxC,EAAE,OAAO1C,OAAO;gBACd,MAAMC,SAASzB,qCAAqCwB;gBACpDZ,aAAaa,OAAOC,OAAO;YAC7B;QACF;IACF;IAEA,qBACE,MAAChD;QAAUmD,OAAM;QAAqBC,aAAY;;0BAChD,MAACf;gBACCkB,WAAU;gBACVO,UAAU;gBACVtB,UAAU,CAACuB;oBACTA,MAAMC,cAAc;oBACpBD,MAAME,eAAe;oBACrB,KAAK5B,KAAK6B,YAAY;gBACxB;;oBAECjC,0BACC,KAAC7B;wBAAQiD,MAAK;wBAAQC,MAAK;kCACxBrB;yBAED;kCACJ,KAACI,KAAK8B,KAAK;wBACTC,MAAK;wBACLC,YAAY;4BACVC,QAAQxE,+BAA+ByE,KAAK,CAACa,YAAY;4BACzD5C,UAAU1C,+BAA+ByE,KAAK,CAACa,YAAY;wBAC7D;kCAEC,CAACZ,sBACA,KAACnE;gCAAUoE,OAAM;gCAAeC,IAAG;gCAAe5B,OAAOvC,WAAWiE;0CAClE,cAAA,KAAClE;oCACCqE,cAAa;oCACbP,MAAK;oCACLd,MAAK;oCACLb,OAAO+B,MAAMvB,KAAK,CAACR,KAAK;oCACxBmC,UAAU,CAACb,QAAUS,MAAMK,YAAY,CAACd,MAAMe,MAAM,CAACrC,KAAK;oCAC1D6B,QAAQE,MAAMO,UAAU;;;;kCAKhC,KAAC7E;wBAAOqD,WAAU;wBAASyB,WAAWE,qBAAqBD,SAAS;wBAAE3B,MAAK;kCACxE4B,qBAAqBD,SAAS,GAAG,yBAAyB;;;;0BAG/D,KAAC9E;gBAAWwD,OAAO;gBAACH,SAAQ;gBAASD,WAAU;0BAC7C,cAAA,KAAC5C;oBAAKkD,IAAG;8BAAc;;;;;AAI/B"}
@@ -211,7 +211,7 @@ export function SignupPage() {
211
211
  role: "alert",
212
212
  type: "error",
213
213
  children: /*#__PURE__*/ _jsxs("div", {
214
- className: "flex flex-col gap-8",
214
+ className: "flex flex-col gap-inline",
215
215
  children: [
216
216
  /*#__PURE__*/ _jsx(Text, {
217
217
  size: "sm",
@@ -297,7 +297,7 @@ export function SignupPage() {
297
297
  description: headerDescription,
298
298
  children: [
299
299
  /*#__PURE__*/ _jsxs("form", {
300
- className: "flex flex-col gap-18",
300
+ className: "flex flex-col gap-group",
301
301
  noValidate: true,
302
302
  onSubmit: (event)=>{
303
303
  event.preventDefault();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/signup-page.tsx"],"sourcesContent":["import {signupBodySchema} from '@shipfox/api-auth-dto';\nimport {\n AuthShell,\n rememberLastWorkspaceId,\n useRouteSearch,\n userWorkspacesQueryKey,\n} 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 {useQueryClient} from '@tanstack/react-query';\nimport {Link, useNavigate} 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 {validateRedirectSearch} from '../routes/inputs.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 queryClient = useQueryClient();\n const navigate = useNavigate();\n const search = useRouteSearch(validateRedirectSearch);\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 [invitationRefreshFailure, setInvitationRefreshFailure] = useState<{\n workspaceId: string;\n workspaceName: string;\n userId?: string | undefined;\n }>();\n const [isRetryingInvitationRefresh, setIsRetryingInvitationRefresh] = useState(false);\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 async function refreshInvitationWorkspace(workspaceId: string) {\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n await refreshAuth();\n const workspaces = queryClient.getQueryData<{\n memberships: Array<{id: string}>;\n }>(userWorkspacesQueryKey);\n const memberships = workspaces?.memberships ?? [];\n if (memberships.some(({id}) => id === workspaceId)) return true;\n } catch {\n // A mount-time refresh can race the post-signup refresh. Retry once so\n // an in-flight request that predates membership creation does not turn\n // a successful invitation into a manual recovery flow.\n }\n }\n return false;\n }\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 parsed = 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({\n email: parsed.email,\n password: parsed.password,\n name: parsed.name,\n ...(parsed.invitation_token ? {invitationToken: parsed.invitation_token} : {}),\n });\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n\n if (invitationToken && result.membership && invitationPending) {\n const joinedWorkspace = await refreshInvitationWorkspace(result.membership.workspaceId);\n if (!joinedWorkspace) {\n setInvitationRefreshFailure({\n workspaceId: result.membership.workspaceId,\n workspaceName: invitationPending.workspaceName,\n userId: result.user?.id,\n });\n return;\n }\n toast.success(`You joined ${invitationPending.workspaceName}.`);\n if (result.user?.id) {\n rememberLastWorkspaceId(result.user.id, result.membership.workspaceId);\n }\n await navigate({to: '/'});\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 async function retryInvitationAuthRefresh() {\n if (!invitationRefreshFailure || isRetryingInvitationRefresh) return;\n setIsRetryingInvitationRefresh(true);\n const joinedWorkspace = await refreshInvitationWorkspace(invitationRefreshFailure.workspaceId);\n setIsRetryingInvitationRefresh(false);\n if (!joinedWorkspace) return;\n if (invitationRefreshFailure.userId) {\n rememberLastWorkspaceId(\n invitationRefreshFailure.userId,\n invitationRefreshFailure.workspaceId,\n );\n }\n toast.success(`You joined ${invitationRefreshFailure.workspaceName}.`);\n setInvitationRefreshFailure(undefined);\n await navigate({to: '/'});\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. The draft stores only\n // email and password; name is intentionally not persisted across navigation.\n // 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 challengeId: emailChallenge.id,\n });\n setNextResendAvailableAt(result.nextResendAvailableAt);\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 (invitationRefreshFailure) {\n return (\n <AuthShell\n title={`Join ${invitationRefreshFailure.workspaceName}`}\n description=\"Your account was created, but we could not finish signing you in.\"\n >\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-8\">\n <Text size=\"sm\">\n Try again to finish joining your workspace. You can safely retry this step.\n </Text>\n <Button\n type=\"button\"\n variant=\"secondary\"\n className=\"w-fit\"\n isLoading={isRetryingInvitationRefresh}\n onClick={() => {\n void retryInvitationAuthRefresh();\n }}\n >\n Retry sign-in\n </Button>\n </div>\n </Callout>\n </AuthShell>\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 challengeId: 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","rememberLastWorkspaceId","useRouteSearch","userWorkspacesQueryKey","displayNameFieldError","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","toast","Text","useForm","useQueryClient","Link","useNavigate","useAtom","useEffect","useRef","useState","EmailCodeVerification","useRefreshAuth","useSignupAuth","useResendEmailVerificationAuth","useVerifyEmailAuth","authFormDraftAtom","initialAuthFormDraft","validateRedirectSearch","signupErrorToFormError","authErrorMessage","extractInvitationToken","pendingInvitation","useInvitationContext","SignupPage","signup","verifyEmail","resendEmailVerification","refreshAuth","queryClient","navigate","search","invitationToken","redirect","invitationPreview","invitationPending","data","authFormDraft","setAuthFormDraft","emailChallenge","setEmailChallenge","nextResendAvailableAt","setNextResendAvailableAt","formError","setFormError","resendError","setResendError","invitationRefreshFailure","setInvitationRefreshFailure","isRetryingInvitationRefresh","setIsRetryingInvitationRefresh","draftRef","current","skipDraftPersistRef","refreshInvitationWorkspace","workspaceId","attempt","workspaces","getQueryData","memberships","some","id","form","defaultValues","email","password","name","onSubmit","value","undefined","parsed","parse","invitation_token","result","mutateAsync","membership","joinedWorkspace","workspaceName","userId","user","success","to","acceptError","error","message","token","Error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","retryInvitationAuthRefresh","state","values","setFieldValue","onResendVerificationEmail","isPending","challengeId","title","description","role","type","div","className","size","variant","isLoading","onClick","destination","isResending","isVerifying","onVerify","code","replace","onResend","onUseAnotherEmail","asChild","underline","headerTitle","headerDescription","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","persistDraft","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","validators","onBlur","shape","label","autoComplete","onChange","handleChange","target","handleBlur","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 {\n AuthShell,\n rememberLastWorkspaceId,\n useRouteSearch,\n userWorkspacesQueryKey,\n} 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 {useQueryClient} from '@tanstack/react-query';\nimport {Link, useNavigate} 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 {validateRedirectSearch} from '../routes/inputs.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 queryClient = useQueryClient();\n const navigate = useNavigate();\n const search = useRouteSearch(validateRedirectSearch);\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 [invitationRefreshFailure, setInvitationRefreshFailure] = useState<{\n workspaceId: string;\n workspaceName: string;\n userId?: string | undefined;\n }>();\n const [isRetryingInvitationRefresh, setIsRetryingInvitationRefresh] = useState(false);\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 async function refreshInvitationWorkspace(workspaceId: string) {\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n await refreshAuth();\n const workspaces = queryClient.getQueryData<{\n memberships: Array<{id: string}>;\n }>(userWorkspacesQueryKey);\n const memberships = workspaces?.memberships ?? [];\n if (memberships.some(({id}) => id === workspaceId)) return true;\n } catch {\n // A mount-time refresh can race the post-signup refresh. Retry once so\n // an in-flight request that predates membership creation does not turn\n // a successful invitation into a manual recovery flow.\n }\n }\n return false;\n }\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 parsed = 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({\n email: parsed.email,\n password: parsed.password,\n name: parsed.name,\n ...(parsed.invitation_token ? {invitationToken: parsed.invitation_token} : {}),\n });\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n\n if (invitationToken && result.membership && invitationPending) {\n const joinedWorkspace = await refreshInvitationWorkspace(result.membership.workspaceId);\n if (!joinedWorkspace) {\n setInvitationRefreshFailure({\n workspaceId: result.membership.workspaceId,\n workspaceName: invitationPending.workspaceName,\n userId: result.user?.id,\n });\n return;\n }\n toast.success(`You joined ${invitationPending.workspaceName}.`);\n if (result.user?.id) {\n rememberLastWorkspaceId(result.user.id, result.membership.workspaceId);\n }\n await navigate({to: '/'});\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 async function retryInvitationAuthRefresh() {\n if (!invitationRefreshFailure || isRetryingInvitationRefresh) return;\n setIsRetryingInvitationRefresh(true);\n const joinedWorkspace = await refreshInvitationWorkspace(invitationRefreshFailure.workspaceId);\n setIsRetryingInvitationRefresh(false);\n if (!joinedWorkspace) return;\n if (invitationRefreshFailure.userId) {\n rememberLastWorkspaceId(\n invitationRefreshFailure.userId,\n invitationRefreshFailure.workspaceId,\n );\n }\n toast.success(`You joined ${invitationRefreshFailure.workspaceName}.`);\n setInvitationRefreshFailure(undefined);\n await navigate({to: '/'});\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. The draft stores only\n // email and password; name is intentionally not persisted across navigation.\n // 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 challengeId: emailChallenge.id,\n });\n setNextResendAvailableAt(result.nextResendAvailableAt);\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 (invitationRefreshFailure) {\n return (\n <AuthShell\n title={`Join ${invitationRefreshFailure.workspaceName}`}\n description=\"Your account was created, but we could not finish signing you in.\"\n >\n <Callout role=\"alert\" type=\"error\">\n <div className=\"flex flex-col gap-inline\">\n <Text size=\"sm\">\n Try again to finish joining your workspace. You can safely retry this step.\n </Text>\n <Button\n type=\"button\"\n variant=\"secondary\"\n className=\"w-fit\"\n isLoading={isRetryingInvitationRefresh}\n onClick={() => {\n void retryInvitationAuthRefresh();\n }}\n >\n Retry sign-in\n </Button>\n </div>\n </Callout>\n </AuthShell>\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 challengeId: 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-group\"\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","rememberLastWorkspaceId","useRouteSearch","userWorkspacesQueryKey","displayNameFieldError","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","toast","Text","useForm","useQueryClient","Link","useNavigate","useAtom","useEffect","useRef","useState","EmailCodeVerification","useRefreshAuth","useSignupAuth","useResendEmailVerificationAuth","useVerifyEmailAuth","authFormDraftAtom","initialAuthFormDraft","validateRedirectSearch","signupErrorToFormError","authErrorMessage","extractInvitationToken","pendingInvitation","useInvitationContext","SignupPage","signup","verifyEmail","resendEmailVerification","refreshAuth","queryClient","navigate","search","invitationToken","redirect","invitationPreview","invitationPending","data","authFormDraft","setAuthFormDraft","emailChallenge","setEmailChallenge","nextResendAvailableAt","setNextResendAvailableAt","formError","setFormError","resendError","setResendError","invitationRefreshFailure","setInvitationRefreshFailure","isRetryingInvitationRefresh","setIsRetryingInvitationRefresh","draftRef","current","skipDraftPersistRef","refreshInvitationWorkspace","workspaceId","attempt","workspaces","getQueryData","memberships","some","id","form","defaultValues","email","password","name","onSubmit","value","undefined","parsed","parse","invitation_token","result","mutateAsync","membership","joinedWorkspace","workspaceName","userId","user","success","to","acceptError","error","message","token","Error","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","retryInvitationAuthRefresh","state","values","setFieldValue","onResendVerificationEmail","isPending","challengeId","title","description","role","type","div","className","size","variant","isLoading","onClick","destination","isResending","isVerifying","onVerify","code","replace","onResend","onUseAnotherEmail","asChild","underline","headerTitle","headerDescription","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","persistDraft","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","validators","onBlur","shape","label","autoComplete","onChange","handleChange","target","handleBlur","readOnly","iconRight","aria-hidden"],"mappings":"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"}