@shipfox/client-auth 1.0.0 → 3.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +44 -0
- package/dist/feature.d.ts +0 -4
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +0 -5
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/signup-auth.d.ts +4 -0
- package/dist/hooks/api/signup-auth.d.ts.map +1 -1
- package/dist/hooks/api/verify-email-auth.d.ts +4 -1
- package/dist/hooks/api/verify-email-auth.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/pages/email-verification-resend-model.d.ts +0 -1
- package/dist/pages/email-verification-resend-model.d.ts.map +1 -1
- package/dist/pages/email-verification-resend-model.js +0 -4
- package/dist/pages/email-verification-resend-model.js.map +1 -1
- package/dist/pages/form-utils.js +1 -1
- package/dist/pages/form-utils.js.map +1 -1
- package/dist/pages/signup-page.d.ts.map +1 -1
- package/dist/pages/signup-page.js +90 -36
- package/dist/pages/signup-page.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/feature.ts +0 -1
- package/src/index.ts +0 -1
- package/src/pages/email-verification-resend-model.test.ts +0 -10
- package/src/pages/email-verification-resend-model.ts +0 -6
- package/src/pages/form-errors.test.ts +1 -1
- package/src/pages/form-utils.test.ts +1 -4
- package/src/pages/form-utils.ts +1 -1
- package/src/pages/signup-page.test.tsx +60 -6
- package/src/pages/signup-page.tsx +87 -36
- package/test/pages.tsx +0 -7
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/pages/verify-email-page.d.ts +0 -2
- package/dist/pages/verify-email-page.d.ts.map +0 -1
- package/dist/pages/verify-email-page.js +0 -57
- package/dist/pages/verify-email-page.js.map +0 -1
- package/dist/routes/verify-email.d.ts +0 -5
- package/dist/routes/verify-email.d.ts.map +0 -1
- package/dist/routes/verify-email.js +0 -11
- package/dist/routes/verify-email.js.map +0 -1
- package/src/pages/verify-email-page.test.tsx +0 -82
- package/src/pages/verify-email-page.tsx +0 -48
- package/src/routes/verify-email.tsx +0 -11
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 36 files with swc (473.63ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# @shipfox/client-auth
|
|
2
2
|
|
|
3
|
+
## 3.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ffc7fc9: Republishes the affected release set after recovering package publication.
|
|
8
|
+
- Updated dependencies [ffc7fc9]
|
|
9
|
+
- @shipfox/client-shell@3.0.1
|
|
10
|
+
- @shipfox/client-ui@3.0.1
|
|
11
|
+
- @shipfox/react-ui@0.3.5
|
|
12
|
+
|
|
13
|
+
## 3.0.0
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [cb58afe]
|
|
18
|
+
- Updated dependencies [d735fe3]
|
|
19
|
+
- Updated dependencies [5b06cd5]
|
|
20
|
+
- @shipfox/react-ui@0.3.4
|
|
21
|
+
- @shipfox/client-shell@3.0.0
|
|
22
|
+
- @shipfox/client-ui@3.0.0
|
|
23
|
+
|
|
24
|
+
## 2.0.0
|
|
25
|
+
|
|
26
|
+
### Major Changes
|
|
27
|
+
|
|
28
|
+
- ba2e3dc: Migrates password email verification from magic links to shared eight-digit email challenges.
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- Updated dependencies [e6eba5b]
|
|
33
|
+
- Updated dependencies [ba2e3dc]
|
|
34
|
+
- Updated dependencies [1820feb]
|
|
35
|
+
- Updated dependencies [7ac43a4]
|
|
36
|
+
- Updated dependencies [1b79cda]
|
|
37
|
+
- Updated dependencies [c2db8c3]
|
|
38
|
+
- Updated dependencies [112c0fa]
|
|
39
|
+
- Updated dependencies [326f4c0]
|
|
40
|
+
- Updated dependencies [4a91956]
|
|
41
|
+
- @shipfox/api-auth-dto@6.0.0
|
|
42
|
+
- @shipfox/react-ui@0.3.3
|
|
43
|
+
- @shipfox/client-shell@2.0.0
|
|
44
|
+
- @shipfox/api-workspaces-dto@6.0.0
|
|
45
|
+
- @shipfox/client-ui@2.0.0
|
|
46
|
+
|
|
3
47
|
## 1.0.0
|
|
4
48
|
|
|
5
49
|
### Patch Changes
|
package/dist/feature.d.ts
CHANGED
|
@@ -20,10 +20,6 @@ export declare const authFeature: {
|
|
|
20
20
|
readonly path: "/auth/signup";
|
|
21
21
|
readonly parent: "root";
|
|
22
22
|
readonly impl: "@shipfox/client-auth/routes/signup";
|
|
23
|
-
}, {
|
|
24
|
-
readonly path: "/auth/verify-email";
|
|
25
|
-
readonly parent: "root";
|
|
26
|
-
readonly impl: "@shipfox/client-auth/routes/verify-email";
|
|
27
23
|
}, {
|
|
28
24
|
readonly path: "/setup/workspaces/new";
|
|
29
25
|
readonly parent: "root";
|
package/dist/feature.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;CActB,CAAC"}
|
package/dist/feature.js
CHANGED
|
@@ -27,11 +27,6 @@ export const authFeature = defineClientFeature({
|
|
|
27
27
|
parent: 'root',
|
|
28
28
|
impl: '@shipfox/client-auth/routes/signup'
|
|
29
29
|
},
|
|
30
|
-
{
|
|
31
|
-
path: '/auth/verify-email',
|
|
32
|
-
parent: 'root',
|
|
33
|
-
impl: '@shipfox/client-auth/routes/verify-email'
|
|
34
|
-
},
|
|
35
30
|
{
|
|
36
31
|
path: '/setup/workspaces/new',
|
|
37
32
|
parent: 'root',
|
package/dist/feature.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/feature.ts"],"sourcesContent":["import {defineClientFeature} from '@shipfox/client-shell';\n\nexport const authFeature = defineClientFeature({\n id: 'shipfox.auth',\n routes: [\n {path: '/', parent: 'root', impl: '@shipfox/client-auth/routes/index'},\n {path: '/auth/login', parent: 'root', impl: '@shipfox/client-auth/routes/login'},\n {path: '/auth/logout', parent: 'root', impl: '@shipfox/client-auth/routes/logout'},\n {path: '/auth/reset', parent: 'root', impl: '@shipfox/client-auth/routes/reset'},\n {path: '/auth/signup', parent: 'root', impl: '@shipfox/client-auth/routes/signup'},\n {
|
|
1
|
+
{"version":3,"sources":["../src/feature.ts"],"sourcesContent":["import {defineClientFeature} from '@shipfox/client-shell';\n\nexport const authFeature = defineClientFeature({\n id: 'shipfox.auth',\n routes: [\n {path: '/', parent: 'root', impl: '@shipfox/client-auth/routes/index'},\n {path: '/auth/login', parent: 'root', impl: '@shipfox/client-auth/routes/login'},\n {path: '/auth/logout', parent: 'root', impl: '@shipfox/client-auth/routes/logout'},\n {path: '/auth/reset', parent: 'root', impl: '@shipfox/client-auth/routes/reset'},\n {path: '/auth/signup', parent: 'root', impl: '@shipfox/client-auth/routes/signup'},\n {\n path: '/setup/workspaces/new',\n parent: 'root',\n impl: '@shipfox/client-auth/routes/workspace-onboarding',\n },\n ],\n});\n"],"names":["defineClientFeature","authFeature","id","routes","path","parent","impl"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,wBAAwB;AAE1D,OAAO,MAAMC,cAAcD,oBAAoB;IAC7CE,IAAI;IACJC,QAAQ;QACN;YAACC,MAAM;YAAKC,QAAQ;YAAQC,MAAM;QAAmC;QACrE;YAACF,MAAM;YAAeC,QAAQ;YAAQC,MAAM;QAAmC;QAC/E;YAACF,MAAM;YAAgBC,QAAQ;YAAQC,MAAM;QAAoC;QACjF;YAACF,MAAM;YAAeC,QAAQ;YAAQC,MAAM;QAAmC;QAC/E;YAACF,MAAM;YAAgBC,QAAQ;YAAQC,MAAM;QAAoC;QACjF;YACEF,MAAM;YACNC,QAAQ;YACRC,MAAM;QACR;KACD;AACH,GAAG"}
|
|
@@ -8,6 +8,10 @@ export declare function useSignupAuth(): import("@tanstack/react-query").UseMuta
|
|
|
8
8
|
created_at: string;
|
|
9
9
|
updated_at: string;
|
|
10
10
|
};
|
|
11
|
+
email_challenge?: {
|
|
12
|
+
id: string;
|
|
13
|
+
next_resend_available_at: string;
|
|
14
|
+
} | undefined;
|
|
11
15
|
token?: string | undefined;
|
|
12
16
|
membership?: {
|
|
13
17
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/signup-auth.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa
|
|
1
|
+
{"version":3,"file":"signup-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/signup-auth.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAE5B"}
|
|
@@ -2,6 +2,7 @@ export declare function useResendEmailVerificationAuth(): import("@tanstack/reac
|
|
|
2
2
|
next_resend_available_at: string;
|
|
3
3
|
}, Error, {
|
|
4
4
|
email: string;
|
|
5
|
+
challenge_id: string;
|
|
5
6
|
}, unknown>;
|
|
6
7
|
export declare function useVerifyEmailAuth(): import("@tanstack/react-query").UseMutationResult<{
|
|
7
8
|
token: string;
|
|
@@ -15,6 +16,8 @@ export declare function useVerifyEmailAuth(): import("@tanstack/react-query").Us
|
|
|
15
16
|
updated_at: string;
|
|
16
17
|
};
|
|
17
18
|
}, Error, {
|
|
18
|
-
|
|
19
|
+
email: string;
|
|
20
|
+
challenge_id: string;
|
|
21
|
+
code: string;
|
|
19
22
|
}, unknown>;
|
|
20
23
|
//# sourceMappingURL=verify-email-auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-email-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/verify-email-auth.ts"],"names":[],"mappings":"AA2BA,wBAAgB,8BAA8B
|
|
1
|
+
{"version":3,"file":"verify-email-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/verify-email-auth.ts"],"names":[],"mappings":"AA2BA,wBAAgB,8BAA8B;;;;;YAE7C;AAED,wBAAgB,kBAAkB;;;;;;;;;;;;;;;YAuBjC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export * from './pages/login-page.js';
|
|
|
8
8
|
export * from './pages/logout-page.js';
|
|
9
9
|
export * from './pages/password-reset-page.js';
|
|
10
10
|
export * from './pages/signup-page.js';
|
|
11
|
-
export * from './pages/verify-email-page.js';
|
|
12
11
|
export * from './pages/workspace-onboarding-page.js';
|
|
13
12
|
export * from './state/auth.js';
|
|
14
13
|
export { getLastWorkspaceId, lastWorkspaceIdAtom, rememberLastWorkspaceId, } from './state/last-workspace.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAC,cAAc,EAAE,KAAK,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzF,OAAO,EAAC,iBAAiB,EAAE,KAAK,sBAAsB,EAAC,MAAM,oCAAoC,CAAC;AAClG,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AACvC,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,4BAA4B,CAAC;AAC3C,OAAO,EAAC,cAAc,EAAE,KAAK,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzF,OAAO,EAAC,iBAAiB,EAAE,KAAK,sBAAsB,EAAC,MAAM,oCAAoC,CAAC;AAClG,cAAc,kBAAkB,CAAC;AACjC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,wBAAwB,CAAC;AACvC,cAAc,sCAAsC,CAAC;AACrD,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,2BAA2B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,6 @@ export * from './pages/login-page.js';
|
|
|
8
8
|
export * from './pages/logout-page.js';
|
|
9
9
|
export * from './pages/password-reset-page.js';
|
|
10
10
|
export * from './pages/signup-page.js';
|
|
11
|
-
export * from './pages/verify-email-page.js';
|
|
12
11
|
export * from './pages/workspace-onboarding-page.js';
|
|
13
12
|
export * from './state/auth.js';
|
|
14
13
|
export { getLastWorkspaceId, lastWorkspaceIdAtom, rememberLastWorkspaceId } from './state/last-workspace.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './components/auth-guard.js';\nexport * from './components/auth-provider.js';\nexport * from './components/auth-shell.js';\nexport {WorkspaceCrumb, type WorkspaceCrumbProps} from './components/workspace-crumb.js';\nexport {WorkspaceSwitcher, type WorkspaceSwitcherProps} from './components/workspace-switcher.js';\nexport * from './hooks/index.js';\nexport * from './pages/login-page.js';\nexport * from './pages/logout-page.js';\nexport * from './pages/password-reset-page.js';\nexport * from './pages/signup-page.js';\nexport * from './pages/
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './components/auth-guard.js';\nexport * from './components/auth-provider.js';\nexport * from './components/auth-shell.js';\nexport {WorkspaceCrumb, type WorkspaceCrumbProps} from './components/workspace-crumb.js';\nexport {WorkspaceSwitcher, type WorkspaceSwitcherProps} from './components/workspace-switcher.js';\nexport * from './hooks/index.js';\nexport * from './pages/login-page.js';\nexport * from './pages/logout-page.js';\nexport * from './pages/password-reset-page.js';\nexport * from './pages/signup-page.js';\nexport * from './pages/workspace-onboarding-page.js';\nexport * from './state/auth.js';\nexport {\n getLastWorkspaceId,\n lastWorkspaceIdAtom,\n rememberLastWorkspaceId,\n} from './state/last-workspace.js';\n"],"names":["WorkspaceCrumb","WorkspaceSwitcher","getLastWorkspaceId","lastWorkspaceIdAtom","rememberLastWorkspaceId"],"mappings":"AAAA,cAAc,6BAA6B;AAC3C,cAAc,gCAAgC;AAC9C,cAAc,6BAA6B;AAC3C,SAAQA,cAAc,QAAiC,kCAAkC;AACzF,SAAQC,iBAAiB,QAAoC,qCAAqC;AAClG,cAAc,mBAAmB;AACjC,cAAc,wBAAwB;AACtC,cAAc,yBAAyB;AACvC,cAAc,iCAAiC;AAC/C,cAAc,yBAAyB;AACvC,cAAc,uCAAuC;AACrD,cAAc,kBAAkB;AAChC,SACEC,kBAAkB,EAClBC,mBAAmB,EACnBC,uBAAuB,QAClB,4BAA4B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email-verification-resend-model.d.ts","sourceRoot":"","sources":["../../src/pages/email-verification-resend-model.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"email-verification-resend-model.d.ts","sourceRoot":"","sources":["../../src/pages/email-verification-resend-model.ts"],"names":[],"mappings":"AAAA,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,qBAAqB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,MAAM,CAIT;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK5E"}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS } from '@shipfox/api-auth-dto';
|
|
2
|
-
export function getLocalResendAvailableAt(now) {
|
|
3
|
-
return now + EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS * 1000;
|
|
4
|
-
}
|
|
5
1
|
export function getResendRemainingSeconds(params) {
|
|
6
2
|
if (!params.nextResendAvailableAt) return 0;
|
|
7
3
|
return Math.max(0, Math.ceil((params.nextResendAvailableAt - params.now) / 1000));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/email-verification-resend-model.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/pages/email-verification-resend-model.ts"],"sourcesContent":["export function getResendRemainingSeconds(params: {\n nextResendAvailableAt: number | undefined;\n now: number;\n}): number {\n if (!params.nextResendAvailableAt) return 0;\n\n return Math.max(0, Math.ceil((params.nextResendAvailableAt - params.now) / 1000));\n}\n\nexport function parseNextResendAvailableAt(value: string): number | undefined {\n const nextAvailableAt = Date.parse(value);\n if (!Number.isFinite(nextAvailableAt)) return undefined;\n\n return nextAvailableAt;\n}\n"],"names":["getResendRemainingSeconds","params","nextResendAvailableAt","Math","max","ceil","now","parseNextResendAvailableAt","value","nextAvailableAt","Date","parse","Number","isFinite","undefined"],"mappings":"AAAA,OAAO,SAASA,0BAA0BC,MAGzC;IACC,IAAI,CAACA,OAAOC,qBAAqB,EAAE,OAAO;IAE1C,OAAOC,KAAKC,GAAG,CAAC,GAAGD,KAAKE,IAAI,CAAC,AAACJ,CAAAA,OAAOC,qBAAqB,GAAGD,OAAOK,GAAG,AAAD,IAAK;AAC7E;AAEA,OAAO,SAASC,2BAA2BC,KAAa;IACtD,MAAMC,kBAAkBC,KAAKC,KAAK,CAACH;IACnC,IAAI,CAACI,OAAOC,QAAQ,CAACJ,kBAAkB,OAAOK;IAE9C,OAAOL;AACT"}
|
package/dist/pages/form-utils.js
CHANGED
|
@@ -17,7 +17,7 @@ export function authErrorMessage(error) {
|
|
|
17
17
|
return 'Too many attempts. Wait a bit and try again.';
|
|
18
18
|
}
|
|
19
19
|
if (error.code === 'auth-rate-limit-unavailable') {
|
|
20
|
-
return 'Sign-in
|
|
20
|
+
return 'Sign-in is temporarily unavailable. Try again soon.';
|
|
21
21
|
}
|
|
22
22
|
if (error.code === 'network-error') {
|
|
23
23
|
return 'We could not reach the API. Check your connection and try again.';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/form-utils.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\n\nexport function authErrorMessage(error: unknown): string {\n if (!(error instanceof ApiError)) return 'Something went wrong. Try again.';\n\n if (error.code === 'invalid-credentials') {\n return 'Email or password is incorrect.';\n }\n if (error.code === 'email-not-verified') {\n return 'Verify your email before signing in.';\n }\n if (error.code === 'email-taken') {\n return 'An account already exists for this email.';\n }\n if (error.code === 'token-invalid') {\n return 'This link is invalid or expired.';\n }\n if (error.code === 'rate-limited') {\n return 'Too many attempts. Wait a bit and try again.';\n }\n if (error.code === 'auth-rate-limit-unavailable') {\n return 'Sign-in
|
|
1
|
+
{"version":3,"sources":["../../src/pages/form-utils.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\n\nexport function authErrorMessage(error: unknown): string {\n if (!(error instanceof ApiError)) return 'Something went wrong. Try again.';\n\n if (error.code === 'invalid-credentials') {\n return 'Email or password is incorrect.';\n }\n if (error.code === 'email-not-verified') {\n return 'Verify your email before signing in.';\n }\n if (error.code === 'email-taken') {\n return 'An account already exists for this email.';\n }\n if (error.code === 'token-invalid') {\n return 'This link is invalid or expired.';\n }\n if (error.code === 'rate-limited') {\n return 'Too many attempts. Wait a bit and try again.';\n }\n if (error.code === 'auth-rate-limit-unavailable') {\n return 'Sign-in is temporarily unavailable. Try again soon.';\n }\n if (error.code === 'network-error') {\n return 'We could not reach the API. Check your connection and try again.';\n }\n\n return error.message || 'Something went wrong. Try again.';\n}\n"],"names":["ApiError","authErrorMessage","error","code","message"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAE7C,OAAO,SAASC,iBAAiBC,KAAc;IAC7C,IAAI,CAAEA,CAAAA,iBAAiBF,QAAO,GAAI,OAAO;IAEzC,IAAIE,MAAMC,IAAI,KAAK,uBAAuB;QACxC,OAAO;IACT;IACA,IAAID,MAAMC,IAAI,KAAK,sBAAsB;QACvC,OAAO;IACT;IACA,IAAID,MAAMC,IAAI,KAAK,eAAe;QAChC,OAAO;IACT;IACA,IAAID,MAAMC,IAAI,KAAK,iBAAiB;QAClC,OAAO;IACT;IACA,IAAID,MAAMC,IAAI,KAAK,gBAAgB;QACjC,OAAO;IACT;IACA,IAAID,MAAMC,IAAI,KAAK,+BAA+B;QAChD,OAAO;IACT;IACA,IAAID,MAAMC,IAAI,KAAK,iBAAiB;QAClC,OAAO;IACT;IAEA,OAAOD,MAAME,OAAO,IAAI;AAC1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signup-page.d.ts","sourceRoot":"","sources":["../../src/pages/signup-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signup-page.d.ts","sourceRoot":"","sources":["../../src/pages/signup-page.tsx"],"names":[],"mappings":"AA6BA,wBAAgB,UAAU,gCAuXzB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { signupBodySchema } from '@shipfox/api-auth-dto';
|
|
2
|
+
import { signupBodySchema, verifyEmailConfirmBodySchema } from '@shipfox/api-auth-dto';
|
|
3
3
|
import { displayNameFieldError } from '@shipfox/client-ui';
|
|
4
4
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
5
5
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
@@ -14,14 +14,15 @@ import { useEffect, useRef, useState } from 'react';
|
|
|
14
14
|
import { AuthShell } from '#/components/auth-shell.js';
|
|
15
15
|
import { useRefreshAuth } from '#hooks/api/refresh-auth.js';
|
|
16
16
|
import { useSignupAuth } from '#hooks/api/signup-auth.js';
|
|
17
|
-
import { useResendEmailVerificationAuth } from '#hooks/api/verify-email-auth.js';
|
|
17
|
+
import { useResendEmailVerificationAuth, useVerifyEmailAuth } from '#hooks/api/verify-email-auth.js';
|
|
18
18
|
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
19
|
-
import {
|
|
19
|
+
import { getResendRemainingSeconds, parseNextResendAvailableAt } from './email-verification-resend-model.js';
|
|
20
20
|
import { signupErrorToFormError } from './form-errors.js';
|
|
21
21
|
import { authErrorMessage } from './form-utils.js';
|
|
22
22
|
import { extractInvitationToken, pendingInvitation, useInvitationContext } from './invitation-context.js';
|
|
23
23
|
export function SignupPage() {
|
|
24
24
|
const signup = useSignupAuth();
|
|
25
|
+
const verifyEmail = useVerifyEmailAuth();
|
|
25
26
|
const resendEmailVerification = useResendEmailVerificationAuth();
|
|
26
27
|
const refreshAuth = useRefreshAuth();
|
|
27
28
|
const navigate = useNavigate();
|
|
@@ -32,7 +33,7 @@ export function SignupPage() {
|
|
|
32
33
|
const invitationPreview = useInvitationContext(invitationToken);
|
|
33
34
|
const invitationPending = pendingInvitation(invitationPreview.data);
|
|
34
35
|
const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);
|
|
35
|
-
const [
|
|
36
|
+
const [emailChallenge, setEmailChallenge] = useState();
|
|
36
37
|
const [now, setNow] = useState(()=>Date.now());
|
|
37
38
|
const [nextResendAvailableAt, setNextResendAvailableAt] = useState();
|
|
38
39
|
const [formError, setFormError] = useState();
|
|
@@ -93,9 +94,17 @@ export function SignupPage() {
|
|
|
93
94
|
});
|
|
94
95
|
return;
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
+
if (!result.email_challenge) {
|
|
98
|
+
throw new Error('Signup did not return an email verification challenge');
|
|
99
|
+
}
|
|
100
|
+
setEmailChallenge({
|
|
101
|
+
email: result.user.email,
|
|
102
|
+
id: result.email_challenge.id
|
|
103
|
+
});
|
|
104
|
+
verificationForm.reset();
|
|
97
105
|
setResendError(undefined);
|
|
98
|
-
|
|
106
|
+
setNow(Date.now());
|
|
107
|
+
setNextResendAvailableAt(parseNextResendAvailableAt(result.email_challenge.next_resend_available_at));
|
|
99
108
|
} catch (error) {
|
|
100
109
|
const mapped = signupErrorToFormError(error);
|
|
101
110
|
if (mapped.kind === 'field') {
|
|
@@ -112,8 +121,32 @@ export function SignupPage() {
|
|
|
112
121
|
}
|
|
113
122
|
}
|
|
114
123
|
});
|
|
124
|
+
const verificationForm = useForm({
|
|
125
|
+
defaultValues: {
|
|
126
|
+
code: ''
|
|
127
|
+
},
|
|
128
|
+
onSubmit: async ({ value })=>{
|
|
129
|
+
if (!emailChallenge || verifyEmail.isPending) return;
|
|
130
|
+
setResendError(undefined);
|
|
131
|
+
try {
|
|
132
|
+
await verifyEmail.mutateAsync({
|
|
133
|
+
email: emailChallenge.email,
|
|
134
|
+
challenge_id: emailChallenge.id,
|
|
135
|
+
code: value.code
|
|
136
|
+
});
|
|
137
|
+
await refreshAuth();
|
|
138
|
+
toast.success('Your email is verified. You are now logged in.');
|
|
139
|
+
await navigate({
|
|
140
|
+
to: '/',
|
|
141
|
+
replace: true
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
setResendError(authErrorMessage(error));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
115
148
|
useEffect(()=>{
|
|
116
|
-
if (!
|
|
149
|
+
if (!emailChallenge || !nextResendAvailableAt) {
|
|
117
150
|
return;
|
|
118
151
|
}
|
|
119
152
|
const current = Date.now();
|
|
@@ -130,14 +163,9 @@ export function SignupPage() {
|
|
|
130
163
|
}, 1000);
|
|
131
164
|
return ()=>window.clearInterval(handle);
|
|
132
165
|
}, [
|
|
133
|
-
|
|
134
|
-
|
|
166
|
+
emailChallenge,
|
|
167
|
+
nextResendAvailableAt
|
|
135
168
|
]);
|
|
136
|
-
function restartLocalCooldown() {
|
|
137
|
-
const current = Date.now();
|
|
138
|
-
setNow(current);
|
|
139
|
-
setNextResendAvailableAt(getLocalResendAvailableAt(current));
|
|
140
|
-
}
|
|
141
169
|
// When arriving from an invitation link, prefill the email and lock it.
|
|
142
170
|
useEffect(()=>{
|
|
143
171
|
if (invitationPending && form.state.values.email !== invitationPending.email) {
|
|
@@ -171,42 +199,68 @@ export function SignupPage() {
|
|
|
171
199
|
setAuthFormDraft
|
|
172
200
|
]);
|
|
173
201
|
async function onResendVerificationEmail() {
|
|
174
|
-
if (!
|
|
202
|
+
if (!emailChallenge || isResendCoolingDown || resendEmailVerification.isPending) return;
|
|
175
203
|
setResendError(undefined);
|
|
176
204
|
try {
|
|
177
205
|
const result = await resendEmailVerification.mutateAsync({
|
|
178
|
-
email:
|
|
206
|
+
email: emailChallenge.email,
|
|
207
|
+
challenge_id: emailChallenge.id
|
|
179
208
|
});
|
|
180
209
|
const nextAvailableAt = parseNextResendAvailableAt(result.next_resend_available_at);
|
|
181
210
|
setNow(Date.now());
|
|
182
211
|
if (nextAvailableAt !== undefined) {
|
|
183
212
|
setNextResendAvailableAt(nextAvailableAt);
|
|
184
|
-
} else {
|
|
185
|
-
restartLocalCooldown();
|
|
186
213
|
}
|
|
187
214
|
toast.success('If another verification email can be sent, it will arrive shortly.');
|
|
188
215
|
} catch (error) {
|
|
189
216
|
setResendError(authErrorMessage(error));
|
|
190
217
|
}
|
|
191
218
|
}
|
|
192
|
-
if (
|
|
219
|
+
if (emailChallenge) {
|
|
193
220
|
return /*#__PURE__*/ _jsxs(AuthShell, {
|
|
194
221
|
title: "Check your email",
|
|
195
|
-
description: `We sent
|
|
222
|
+
description: `We sent an eight-digit verification code to ${emailChallenge.email}.`,
|
|
196
223
|
children: [
|
|
197
224
|
resendError ? /*#__PURE__*/ _jsx(Callout, {
|
|
198
225
|
role: "alert",
|
|
199
226
|
type: "error",
|
|
200
227
|
children: resendError
|
|
201
228
|
}) : null,
|
|
202
|
-
/*#__PURE__*/
|
|
203
|
-
role: "alert",
|
|
204
|
-
type: "success",
|
|
205
|
-
children: "Click the verification link to activate your account, then come back to log in."
|
|
206
|
-
}),
|
|
207
|
-
/*#__PURE__*/ _jsxs("div", {
|
|
229
|
+
/*#__PURE__*/ _jsxs("form", {
|
|
208
230
|
className: "flex flex-col gap-8",
|
|
231
|
+
noValidate: true,
|
|
232
|
+
onSubmit: (event)=>{
|
|
233
|
+
event.preventDefault();
|
|
234
|
+
void verificationForm.handleSubmit();
|
|
235
|
+
},
|
|
209
236
|
children: [
|
|
237
|
+
/*#__PURE__*/ _jsx(verificationForm.Field, {
|
|
238
|
+
name: "code",
|
|
239
|
+
validators: {
|
|
240
|
+
onBlur: verifyEmailConfirmBodySchema.shape.code,
|
|
241
|
+
onSubmit: verifyEmailConfirmBodySchema.shape.code
|
|
242
|
+
},
|
|
243
|
+
children: (field)=>/*#__PURE__*/ _jsx(FormField, {
|
|
244
|
+
label: "Verification code",
|
|
245
|
+
id: "verification-code",
|
|
246
|
+
error: fieldError(field),
|
|
247
|
+
children: /*#__PURE__*/ _jsx(FormFieldInput, {
|
|
248
|
+
autoComplete: "one-time-code",
|
|
249
|
+
inputMode: "numeric",
|
|
250
|
+
maxLength: 8,
|
|
251
|
+
pattern: "[0-9]{8}",
|
|
252
|
+
value: field.state.value,
|
|
253
|
+
onChange: (event)=>field.handleChange(event.target.value.replace(/\D/gu, '')),
|
|
254
|
+
onBlur: field.handleBlur
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
}),
|
|
258
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
259
|
+
className: "w-full",
|
|
260
|
+
type: "submit",
|
|
261
|
+
isLoading: verifyEmail.isPending,
|
|
262
|
+
children: "Verify email"
|
|
263
|
+
}),
|
|
210
264
|
/*#__PURE__*/ _jsx(Button, {
|
|
211
265
|
"aria-disabled": isResendCoolingDown ? true : undefined,
|
|
212
266
|
className: "w-full aria-disabled:cursor-not-allowed aria-disabled:opacity-70",
|
|
@@ -215,19 +269,19 @@ export function SignupPage() {
|
|
|
215
269
|
isLoading: resendEmailVerification.isPending,
|
|
216
270
|
onClick: onResendVerificationEmail,
|
|
217
271
|
children: resendEmailVerification.isPending ? 'Sending email...' : isResendCoolingDown ? `Resend in ${resendRemainingSeconds}s` : 'Resend verification email'
|
|
218
|
-
}),
|
|
219
|
-
/*#__PURE__*/ _jsx(Button, {
|
|
220
|
-
className: "w-full",
|
|
221
|
-
variant: "transparent",
|
|
222
|
-
type: "button",
|
|
223
|
-
onClick: ()=>{
|
|
224
|
-
setSubmittedEmail(undefined);
|
|
225
|
-
setResendError(undefined);
|
|
226
|
-
},
|
|
227
|
-
children: "Use another email"
|
|
228
272
|
})
|
|
229
273
|
]
|
|
230
274
|
}),
|
|
275
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
276
|
+
className: "w-full",
|
|
277
|
+
variant: "transparent",
|
|
278
|
+
type: "button",
|
|
279
|
+
onClick: ()=>{
|
|
280
|
+
setEmailChallenge(undefined);
|
|
281
|
+
setResendError(undefined);
|
|
282
|
+
},
|
|
283
|
+
children: "Use another email"
|
|
284
|
+
}),
|
|
231
285
|
/*#__PURE__*/ _jsxs(Text, {
|
|
232
286
|
size: "sm",
|
|
233
287
|
className: "text-center text-foreground-neutral-subtle",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/signup-page.tsx"],"sourcesContent":["import {signupBodySchema} from '@shipfox/api-auth-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {AuthShell} from '#/components/auth-shell.js';\nimport {useRefreshAuth} from '#hooks/api/refresh-auth.js';\nimport {useSignupAuth} from '#hooks/api/signup-auth.js';\nimport {useResendEmailVerificationAuth} from '#hooks/api/verify-email-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n getLocalResendAvailableAt,\n getResendRemainingSeconds,\n parseNextResendAvailableAt,\n} from './email-verification-resend-model.js';\nimport {signupErrorToFormError} from './form-errors.js';\nimport {authErrorMessage} from './form-utils.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function SignupPage() {\n const signup = useSignupAuth();\n const resendEmailVerification = useResendEmailVerificationAuth();\n const refreshAuth = useRefreshAuth();\n const navigate = useNavigate();\n const search = useSearch({strict: false}) as {redirect?: unknown};\n const invitationToken = extractInvitationToken(search.redirect);\n const invitationPreview = useInvitationContext(invitationToken);\n const invitationPending = pendingInvitation(invitationPreview.data);\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [submittedEmail, setSubmittedEmail] = useState<string | undefined>();\n const [now, setNow] = useState(() => Date.now());\n const [nextResendAvailableAt, setNextResendAvailableAt] = useState<number | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const [resendError, setResendError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n // Set just before clearing the draft on success so the unmount cleanup\n // below does not repersist the just-submitted credentials.\n const skipDraftPersistRef = useRef(false);\n const resendRemainingSeconds = getResendRemainingSeconds({nextResendAvailableAt, now});\n const isResendCoolingDown = resendRemainingSeconds > 0;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email, password: authFormDraft.password, name: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = signupBodySchema.parse({\n email: value.email,\n password: value.password,\n name: value.name,\n ...(invitationToken ? {invitation_token: invitationToken} : {}),\n });\n const result = await signup.mutateAsync(body);\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n\n if (invitationToken && result.membership && invitationPending) {\n try {\n await refreshAuth();\n } catch {\n // Refresh failures don't block the success message — the next API\n // call's 401 handling will re-route the user.\n }\n toast.success(`You joined ${invitationPending.workspace_name}.`);\n await navigate({\n to: '/workspaces/$wid',\n params: {wid: result.membership.workspace_id},\n });\n return;\n }\n\n if (invitationToken && result.accept_error) {\n toast.error(result.accept_error.message);\n await navigate({\n to: '/invitations/accept',\n search: {token: invitationToken},\n });\n return;\n }\n\n setSubmittedEmail(result.user.email);\n setResendError(undefined);\n restartLocalCooldown();\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 useEffect(() => {\n if (!submittedEmail || !nextResendAvailableAt) {\n return;\n }\n\n const current = Date.now();\n setNow(current);\n if (nextResendAvailableAt <= current) {\n return;\n }\n\n const handle = window.setInterval(() => {\n const tickNow = Date.now();\n setNow(tickNow);\n if (nextResendAvailableAt <= tickNow) {\n window.clearInterval(handle);\n }\n }, 1000);\n return () => window.clearInterval(handle);\n }, [nextResendAvailableAt, submittedEmail]);\n\n function restartLocalCooldown() {\n const current = Date.now();\n setNow(current);\n setNextResendAvailableAt(getLocalResendAvailableAt(current));\n }\n\n // When arriving from an invitation link, prefill the email and lock it.\n useEffect(() => {\n if (invitationPending && form.state.values.email !== invitationPending.email) {\n form.setFieldValue('email', invitationPending.email);\n setAuthFormDraft((current) => ({...current, email: invitationPending.email}));\n }\n }, [invitationPending, form, setAuthFormDraft]);\n\n // Sync form values back to the Jotai draft on unmount (only email + password\n // — name is intentionally not persisted across navigation). Skipped after a\n // successful signup because we just intentionally cleared the draft.\n useEffect(() => {\n return () => {\n if (skipDraftPersistRef.current) return;\n const {email, password} = form.state.values;\n if (email !== draftRef.current.email || password !== draftRef.current.password) {\n setAuthFormDraft({email, password});\n }\n };\n }, [form, setAuthFormDraft]);\n\n async function onResendVerificationEmail() {\n if (!submittedEmail || isResendCoolingDown || resendEmailVerification.isPending) return;\n\n setResendError(undefined);\n try {\n const result = await resendEmailVerification.mutateAsync({email: submittedEmail});\n const nextAvailableAt = parseNextResendAvailableAt(result.next_resend_available_at);\n setNow(Date.now());\n if (nextAvailableAt !== undefined) {\n setNextResendAvailableAt(nextAvailableAt);\n } else {\n restartLocalCooldown();\n }\n toast.success('If another verification email can be sent, it will arrive shortly.');\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n }\n\n if (submittedEmail) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent a verification link to ${submittedEmail}.`}\n >\n {resendError ? (\n <Callout role=\"alert\" type=\"error\">\n {resendError}\n </Callout>\n ) : null}\n <Callout role=\"alert\" type=\"success\">\n Click the verification link to activate your account, then come back to log in.\n </Callout>\n <div className=\"flex flex-col gap-8\">\n <Button\n aria-disabled={isResendCoolingDown ? true : undefined}\n className=\"w-full aria-disabled:cursor-not-allowed aria-disabled:opacity-70\"\n variant=\"secondary\"\n type=\"button\"\n isLoading={resendEmailVerification.isPending}\n onClick={onResendVerificationEmail}\n >\n {resendEmailVerification.isPending\n ? 'Sending email...'\n : isResendCoolingDown\n ? `Resend in ${resendRemainingSeconds}s`\n : 'Resend verification email'}\n </Button>\n <Button\n className=\"w-full\"\n variant=\"transparent\"\n type=\"button\"\n onClick={() => {\n setSubmittedEmail(undefined);\n setResendError(undefined);\n }}\n >\n Use another email\n </Button>\n </div>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already verified?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n const headerTitle = invitationPending\n ? `Join ${invitationPending.workspace_name}`\n : 'Create your Shipfox account';\n const headerDescription = invitationPending\n ? `Create an account to accept your invitation.`\n : 'Start with your email and a password.';\n const isInvitationEmailLocked = Boolean(invitationPending);\n const invitationRedirect = invitationToken\n ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}`\n : undefined;\n\n function persistDraft() {\n const {email, password} = form.state.values;\n setAuthFormDraft({email, password});\n }\n\n return (\n <AuthShell title={headerTitle} description={headerDescription}>\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n onSubmit: ({value}) =>\n displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n }}\n >\n {(field) => (\n <FormField label=\"Name\" id=\"name\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"name\"\n name=\"name\"\n type=\"text\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"email\"\n validators={{\n onBlur: signupBodySchema.shape.email,\n onSubmit: signupBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n readOnly={isInvitationEmailLocked}\n iconRight={\n isInvitationEmailLocked ? (\n <Icon\n aria-hidden=\"true\"\n className=\"size-16 text-foreground-neutral-disabled\"\n name=\"lockLine\"\n />\n ) : undefined\n }\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"password\"\n validators={{\n onBlur: signupBodySchema.shape.password,\n onSubmit: signupBodySchema.shape.password,\n }}\n >\n {(field) => (\n <FormField label=\"Password\" id=\"password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={signup.isPending} type=\"submit\">\n {signup.isPending ? 'Creating account...' : 'Create account'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already have an account?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link\n to=\"/auth/login\"\n search={invitationRedirect ? {redirect: invitationRedirect} : undefined}\n >\n Log in\n </Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n"],"names":["signupBodySchema","displayNameFieldError","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","AuthShell","useRefreshAuth","useSignupAuth","useResendEmailVerificationAuth","authFormDraftAtom","initialAuthFormDraft","getLocalResendAvailableAt","getResendRemainingSeconds","parseNextResendAvailableAt","signupErrorToFormError","authErrorMessage","extractInvitationToken","pendingInvitation","useInvitationContext","SignupPage","signup","resendEmailVerification","refreshAuth","navigate","search","strict","invitationToken","redirect","invitationPreview","invitationPending","data","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","now","setNow","Date","nextResendAvailableAt","setNextResendAvailableAt","formError","setFormError","resendError","setResendError","draftRef","current","skipDraftPersistRef","resendRemainingSeconds","isResendCoolingDown","form","defaultValues","email","password","name","onSubmit","value","undefined","body","parse","invitation_token","result","mutateAsync","membership","success","workspace_name","to","params","wid","workspace_id","accept_error","error","message","token","user","restartLocalCooldown","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","handle","window","setInterval","tickNow","clearInterval","state","values","setFieldValue","onResendVerificationEmail","isPending","nextAvailableAt","next_resend_available_at","title","description","role","type","div","className","aria-disabled","variant","isLoading","onClick","size","asChild","underline","headerTitle","headerDescription","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","persistDraft","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","validators","onBlur","shape","label","id","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, verifyEmailConfirmBodySchema} from '@shipfox/api-auth-dto';\nimport {displayNameFieldError} from '@shipfox/client-ui';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {AuthShell} from '#/components/auth-shell.js';\nimport {useRefreshAuth} from '#hooks/api/refresh-auth.js';\nimport {useSignupAuth} from '#hooks/api/signup-auth.js';\nimport {useResendEmailVerificationAuth, useVerifyEmailAuth} from '#hooks/api/verify-email-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n getResendRemainingSeconds,\n parseNextResendAvailableAt,\n} from './email-verification-resend-model.js';\nimport {signupErrorToFormError} from './form-errors.js';\nimport {authErrorMessage} from './form-utils.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function SignupPage() {\n const signup = useSignupAuth();\n const verifyEmail = useVerifyEmailAuth();\n const resendEmailVerification = useResendEmailVerificationAuth();\n const refreshAuth = useRefreshAuth();\n const navigate = useNavigate();\n const search = useSearch({strict: false}) as {redirect?: unknown};\n const invitationToken = extractInvitationToken(search.redirect);\n const invitationPreview = useInvitationContext(invitationToken);\n const invitationPending = pendingInvitation(invitationPreview.data);\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [emailChallenge, setEmailChallenge] = useState<{email: string; id: string} | undefined>();\n const [now, setNow] = useState(() => Date.now());\n const [nextResendAvailableAt, setNextResendAvailableAt] = useState<number | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const [resendError, setResendError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n // Set just before clearing the draft on success so the unmount cleanup\n // below does not repersist the just-submitted credentials.\n const skipDraftPersistRef = useRef(false);\n const resendRemainingSeconds = getResendRemainingSeconds({nextResendAvailableAt, now});\n const isResendCoolingDown = resendRemainingSeconds > 0;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email, password: authFormDraft.password, name: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = signupBodySchema.parse({\n email: value.email,\n password: value.password,\n name: value.name,\n ...(invitationToken ? {invitation_token: invitationToken} : {}),\n });\n const result = await signup.mutateAsync(body);\n skipDraftPersistRef.current = true;\n setAuthFormDraft(initialAuthFormDraft);\n\n if (invitationToken && result.membership && invitationPending) {\n try {\n await refreshAuth();\n } catch {\n // Refresh failures don't block the success message — the next API\n // call's 401 handling will re-route the user.\n }\n toast.success(`You joined ${invitationPending.workspace_name}.`);\n await navigate({\n to: '/workspaces/$wid',\n params: {wid: result.membership.workspace_id},\n });\n return;\n }\n\n if (invitationToken && result.accept_error) {\n toast.error(result.accept_error.message);\n await navigate({\n to: '/invitations/accept',\n search: {token: invitationToken},\n });\n return;\n }\n\n if (!result.email_challenge) {\n throw new Error('Signup did not return an email verification challenge');\n }\n setEmailChallenge({email: result.user.email, id: result.email_challenge.id});\n verificationForm.reset();\n setResendError(undefined);\n setNow(Date.now());\n setNextResendAvailableAt(\n parseNextResendAvailableAt(result.email_challenge.next_resend_available_at),\n );\n } catch (error) {\n const mapped = signupErrorToFormError(error);\n if (mapped.kind === 'field') {\n form.setFieldMeta(mapped.field, (prev) => ({\n ...prev,\n errorMap: {...prev.errorMap, onServer: mapped.message},\n }));\n } else {\n setFormError(mapped.message);\n }\n }\n },\n });\n\n const verificationForm = useForm({\n defaultValues: {code: ''},\n onSubmit: async ({value}) => {\n if (!emailChallenge || verifyEmail.isPending) return;\n\n setResendError(undefined);\n try {\n await verifyEmail.mutateAsync({\n email: emailChallenge.email,\n challenge_id: emailChallenge.id,\n code: value.code,\n });\n await refreshAuth();\n toast.success('Your email is verified. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n },\n });\n\n useEffect(() => {\n if (!emailChallenge || !nextResendAvailableAt) {\n return;\n }\n\n const current = Date.now();\n setNow(current);\n if (nextResendAvailableAt <= current) {\n return;\n }\n\n const handle = window.setInterval(() => {\n const tickNow = Date.now();\n setNow(tickNow);\n if (nextResendAvailableAt <= tickNow) {\n window.clearInterval(handle);\n }\n }, 1000);\n return () => window.clearInterval(handle);\n }, [emailChallenge, nextResendAvailableAt]);\n\n // When arriving from an invitation link, prefill the email and lock it.\n useEffect(() => {\n if (invitationPending && form.state.values.email !== invitationPending.email) {\n form.setFieldValue('email', invitationPending.email);\n setAuthFormDraft((current) => ({...current, email: invitationPending.email}));\n }\n }, [invitationPending, form, setAuthFormDraft]);\n\n // Sync form values back to the Jotai draft on unmount (only email + password\n // — name is intentionally not persisted across navigation). Skipped after a\n // successful signup because we just intentionally cleared the draft.\n useEffect(() => {\n return () => {\n if (skipDraftPersistRef.current) return;\n const {email, password} = form.state.values;\n if (email !== draftRef.current.email || password !== draftRef.current.password) {\n setAuthFormDraft({email, password});\n }\n };\n }, [form, setAuthFormDraft]);\n\n async function onResendVerificationEmail() {\n if (!emailChallenge || isResendCoolingDown || resendEmailVerification.isPending) return;\n\n setResendError(undefined);\n try {\n const result = await resendEmailVerification.mutateAsync({\n email: emailChallenge.email,\n challenge_id: emailChallenge.id,\n });\n const nextAvailableAt = parseNextResendAvailableAt(result.next_resend_available_at);\n setNow(Date.now());\n if (nextAvailableAt !== undefined) {\n setNextResendAvailableAt(nextAvailableAt);\n }\n toast.success('If another verification email can be sent, it will arrive shortly.');\n } catch (error) {\n setResendError(authErrorMessage(error));\n }\n }\n\n if (emailChallenge) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent an eight-digit verification code to ${emailChallenge.email}.`}\n >\n {resendError ? (\n <Callout role=\"alert\" type=\"error\">\n {resendError}\n </Callout>\n ) : null}\n <form\n className=\"flex flex-col gap-8\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n void verificationForm.handleSubmit();\n }}\n >\n <verificationForm.Field\n name=\"code\"\n validators={{\n onBlur: verifyEmailConfirmBodySchema.shape.code,\n onSubmit: verifyEmailConfirmBodySchema.shape.code,\n }}\n >\n {(field) => (\n <FormField label=\"Verification code\" id=\"verification-code\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"one-time-code\"\n inputMode=\"numeric\"\n maxLength={8}\n pattern=\"[0-9]{8}\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value.replace(/\\D/gu, ''))}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </verificationForm.Field>\n <Button className=\"w-full\" type=\"submit\" isLoading={verifyEmail.isPending}>\n Verify email\n </Button>\n <Button\n aria-disabled={isResendCoolingDown ? true : undefined}\n className=\"w-full aria-disabled:cursor-not-allowed aria-disabled:opacity-70\"\n variant=\"secondary\"\n type=\"button\"\n isLoading={resendEmailVerification.isPending}\n onClick={onResendVerificationEmail}\n >\n {resendEmailVerification.isPending\n ? 'Sending email...'\n : isResendCoolingDown\n ? `Resend in ${resendRemainingSeconds}s`\n : 'Resend verification email'}\n </Button>\n </form>\n <Button\n className=\"w-full\"\n variant=\"transparent\"\n type=\"button\"\n onClick={() => {\n setEmailChallenge(undefined);\n setResendError(undefined);\n }}\n >\n Use another email\n </Button>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already verified?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n const headerTitle = invitationPending\n ? `Join ${invitationPending.workspace_name}`\n : 'Create your Shipfox account';\n const headerDescription = invitationPending\n ? `Create an account to accept your invitation.`\n : 'Start with your email and a password.';\n const isInvitationEmailLocked = Boolean(invitationPending);\n const invitationRedirect = invitationToken\n ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}`\n : undefined;\n\n function persistDraft() {\n const {email, password} = form.state.values;\n setAuthFormDraft({email, password});\n }\n\n return (\n <AuthShell title={headerTitle} description={headerDescription}>\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"name\"\n validators={{\n onBlur: ({value}) => displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n onSubmit: ({value}) =>\n displayNameFieldError(value, 'Name', signupBodySchema.shape.name),\n }}\n >\n {(field) => (\n <FormField label=\"Name\" id=\"name\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"name\"\n name=\"name\"\n type=\"text\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"email\"\n validators={{\n onBlur: signupBodySchema.shape.email,\n onSubmit: signupBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n readOnly={isInvitationEmailLocked}\n iconRight={\n isInvitationEmailLocked ? (\n <Icon\n aria-hidden=\"true\"\n className=\"size-16 text-foreground-neutral-disabled\"\n name=\"lockLine\"\n />\n ) : undefined\n }\n />\n </FormField>\n )}\n </form.Field>\n <form.Field\n name=\"password\"\n validators={{\n onBlur: signupBodySchema.shape.password,\n onSubmit: signupBodySchema.shape.password,\n }}\n >\n {(field) => (\n <FormField label=\"Password\" id=\"password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n persistDraft();\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={signup.isPending} type=\"submit\">\n {signup.isPending ? 'Creating account...' : 'Create account'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Already have an account?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link\n to=\"/auth/login\"\n search={invitationRedirect ? {redirect: invitationRedirect} : undefined}\n >\n Log in\n </Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n"],"names":["signupBodySchema","verifyEmailConfirmBodySchema","displayNameFieldError","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","AuthShell","useRefreshAuth","useSignupAuth","useResendEmailVerificationAuth","useVerifyEmailAuth","authFormDraftAtom","initialAuthFormDraft","getResendRemainingSeconds","parseNextResendAvailableAt","signupErrorToFormError","authErrorMessage","extractInvitationToken","pendingInvitation","useInvitationContext","SignupPage","signup","verifyEmail","resendEmailVerification","refreshAuth","navigate","search","strict","invitationToken","redirect","invitationPreview","invitationPending","data","authFormDraft","setAuthFormDraft","emailChallenge","setEmailChallenge","now","setNow","Date","nextResendAvailableAt","setNextResendAvailableAt","formError","setFormError","resendError","setResendError","draftRef","current","skipDraftPersistRef","resendRemainingSeconds","isResendCoolingDown","form","defaultValues","email","password","name","onSubmit","value","undefined","body","parse","invitation_token","result","mutateAsync","membership","success","workspace_name","to","params","wid","workspace_id","accept_error","error","message","token","email_challenge","Error","user","id","verificationForm","reset","next_resend_available_at","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","code","isPending","challenge_id","replace","handle","window","setInterval","tickNow","clearInterval","state","values","setFieldValue","onResendVerificationEmail","nextAvailableAt","title","description","role","type","className","noValidate","event","preventDefault","handleSubmit","Field","validators","onBlur","shape","label","autoComplete","inputMode","maxLength","pattern","onChange","handleChange","target","handleBlur","isLoading","aria-disabled","variant","onClick","size","asChild","underline","headerTitle","headerDescription","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","persistDraft","stopPropagation","readOnly","iconRight","aria-hidden"],"mappings":"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"}
|