@shipfox/client-auth 5.0.0 → 6.0.2
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 +57 -0
- package/dist/components/auth-guard.d.ts.map +1 -1
- package/dist/components/auth-guard.js +4 -4
- package/dist/components/auth-guard.js.map +1 -1
- package/dist/components/redirect-target.d.ts +7 -0
- package/dist/components/redirect-target.d.ts.map +1 -1
- package/dist/components/redirect-target.js +35 -4
- package/dist/components/redirect-target.js.map +1 -1
- package/dist/components/workspace-switcher.d.ts.map +1 -1
- package/dist/components/workspace-switcher.js +3 -2
- package/dist/components/workspace-switcher.js.map +1 -1
- package/dist/core/auth.d.ts +57 -0
- package/dist/core/auth.d.ts.map +1 -0
- package/dist/core/auth.js +3 -0
- package/dist/core/auth.js.map +1 -0
- package/dist/hooks/api/auth-mapper.d.ts +2 -17
- package/dist/hooks/api/auth-mapper.d.ts.map +1 -1
- package/dist/hooks/api/auth-mapper.js.map +1 -1
- package/dist/hooks/api/login-auth.d.ts +3 -6
- package/dist/hooks/api/login-auth.d.ts.map +1 -1
- package/dist/hooks/api/login-auth.js +3 -3
- package/dist/hooks/api/login-auth.js.map +1 -1
- package/dist/hooks/api/logout-auth.js +3 -3
- package/dist/hooks/api/logout-auth.js.map +1 -1
- package/dist/hooks/api/password-reset-auth.d.ts +5 -10
- package/dist/hooks/api/password-reset-auth.d.ts.map +1 -1
- package/dist/hooks/api/password-reset-auth.js +10 -7
- package/dist/hooks/api/password-reset-auth.js.map +1 -1
- package/dist/hooks/api/refresh-auth.d.ts +1 -1
- package/dist/hooks/api/refresh-auth.d.ts.map +1 -1
- package/dist/hooks/api/refresh-auth.js +1 -1
- package/dist/hooks/api/refresh-auth.js.map +1 -1
- package/dist/hooks/api/signup-auth.d.ts +2 -6
- package/dist/hooks/api/signup-auth.d.ts.map +1 -1
- package/dist/hooks/api/signup-auth.js +9 -2
- package/dist/hooks/api/signup-auth.js.map +1 -1
- package/dist/hooks/api/verify-email-auth.d.ts +4 -10
- package/dist/hooks/api/verify-email-auth.d.ts.map +1 -1
- package/dist/hooks/api/verify-email-auth.js +16 -6
- package/dist/hooks/api/verify-email-auth.js.map +1 -1
- package/dist/hooks/api/workspace-auth.d.ts +5 -7
- package/dist/hooks/api/workspace-auth.d.ts.map +1 -1
- package/dist/hooks/api/workspace-auth.js +6 -4
- package/dist/hooks/api/workspace-auth.js.map +1 -1
- package/dist/hooks/api/workspace-mapper.d.ts +1 -5
- package/dist/hooks/api/workspace-mapper.d.ts.map +1 -1
- package/dist/hooks/api/workspace-mapper.js.map +1 -1
- package/dist/pages/login-page.d.ts.map +1 -1
- package/dist/pages/login-page.js +4 -5
- package/dist/pages/login-page.js.map +1 -1
- package/dist/pages/logout-page.d.ts.map +1 -1
- package/dist/pages/logout-page.js +8 -18
- package/dist/pages/logout-page.js.map +1 -1
- package/dist/pages/password-reset-page.d.ts.map +1 -1
- package/dist/pages/password-reset-page.js +8 -7
- package/dist/pages/password-reset-page.js.map +1 -1
- package/dist/pages/signup-page.d.ts.map +1 -1
- package/dist/pages/signup-page.js +16 -10
- package/dist/pages/signup-page.js.map +1 -1
- package/dist/pages/workspace-onboarding-page.d.ts.map +1 -1
- package/dist/pages/workspace-onboarding-page.js +6 -3
- package/dist/pages/workspace-onboarding-page.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +4 -3
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/inputs.d.ts +9 -0
- package/dist/routes/inputs.d.ts.map +1 -0
- package/dist/routes/inputs.js +17 -0
- package/dist/routes/inputs.js.map +1 -0
- package/dist/routes/login.d.ts +2 -0
- package/dist/routes/login.d.ts.map +1 -1
- package/dist/routes/login.js +2 -0
- package/dist/routes/login.js.map +1 -1
- package/dist/routes/logout.d.ts +2 -0
- package/dist/routes/logout.d.ts.map +1 -1
- package/dist/routes/logout.js +2 -0
- package/dist/routes/logout.js.map +1 -1
- package/dist/routes/reset.d.ts +2 -0
- package/dist/routes/reset.d.ts.map +1 -1
- package/dist/routes/reset.js +2 -0
- package/dist/routes/reset.js.map +1 -1
- package/dist/routes/signup.d.ts +2 -0
- package/dist/routes/signup.d.ts.map +1 -1
- package/dist/routes/signup.js +2 -0
- package/dist/routes/signup.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +11 -12
- package/src/components/auth-guard.tsx +4 -2
- package/src/components/auth-provider.test.tsx +132 -19
- package/src/components/redirect-target.test.ts +32 -1
- package/src/components/redirect-target.ts +41 -4
- package/src/components/workspace-switcher.tsx +3 -2
- package/src/core/auth.ts +55 -0
- package/src/hooks/api/auth-mapper.ts +2 -12
- package/src/hooks/api/login-auth.ts +5 -4
- package/src/hooks/api/logout-auth.ts +3 -3
- package/src/hooks/api/password-reset-auth.test.ts +2 -2
- package/src/hooks/api/password-reset-auth.ts +11 -11
- package/src/hooks/api/refresh-auth.ts +1 -0
- package/src/hooks/api/signup-auth.ts +9 -3
- package/src/hooks/api/verify-email-auth.ts +20 -10
- package/src/hooks/api/workspace-auth.ts +10 -5
- package/src/hooks/api/workspace-mapper.ts +1 -6
- package/src/pages/login-page.tsx +4 -3
- package/src/pages/logout-page.test.tsx +75 -0
- package/src/pages/logout-page.tsx +9 -13
- package/src/pages/password-reset-page.tsx +5 -5
- package/src/pages/signup-page.tsx +14 -8
- package/src/pages/workspace-onboarding-page.tsx +6 -3
- package/src/routes/index.tsx +5 -3
- package/src/routes/inputs.test.ts +17 -0
- package/src/routes/inputs.ts +21 -0
- package/src/routes/login.tsx +2 -0
- package/src/routes/logout.tsx +2 -1
- package/src/routes/reset.tsx +2 -0
- package/src/routes/signup.tsx +2 -0
- package/src/state/last-workspace.test.ts +21 -28
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vercel.json +5 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/api/signup-auth.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/signup-auth.ts"],"sourcesContent":["import {signupResponseSchema} from '@shipfox/api-auth-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {useMutation} from '@tanstack/react-query';\nimport type {SignupCommand} from '#core/auth.js';\nimport {toSignupResult} from './auth-mapper.js';\n\nasync function signupAuth(command: SignupCommand) {\n const response = await checkedApiRequest(signupResponseSchema, '/auth/signup', {\n method: 'POST',\n body: {\n email: command.email,\n password: command.password,\n name: command.name,\n ...(command.invitationToken ? {invitation_token: command.invitationToken} : {}),\n },\n });\n return toSignupResult(response);\n}\n\nexport function useSignupAuth() {\n return useMutation({mutationFn: signupAuth});\n}\n"],"names":["signupResponseSchema","checkedApiRequest","useMutation","toSignupResult","signupAuth","command","response","method","body","email","password","name","invitationToken","invitation_token","useSignupAuth","mutationFn"],"mappings":"AAAA,SAAQA,oBAAoB,QAAO,wBAAwB;AAC3D,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SAAQC,WAAW,QAAO,wBAAwB;AAElD,SAAQC,cAAc,QAAO,mBAAmB;AAEhD,eAAeC,WAAWC,OAAsB;IAC9C,MAAMC,WAAW,MAAML,kBAAkBD,sBAAsB,gBAAgB;QAC7EO,QAAQ;QACRC,MAAM;YACJC,OAAOJ,QAAQI,KAAK;YACpBC,UAAUL,QAAQK,QAAQ;YAC1BC,MAAMN,QAAQM,IAAI;YAClB,GAAIN,QAAQO,eAAe,GAAG;gBAACC,kBAAkBR,QAAQO,eAAe;YAAA,IAAI,CAAC,CAAC;QAChF;IACF;IACA,OAAOT,eAAeG;AACxB;AAEA,OAAO,SAASQ;IACd,OAAOZ,YAAY;QAACa,YAAYX;IAAU;AAC5C"}
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
+
import type { ResendEmailVerificationCommand, VerifyEmailCommand } from '#core/auth.js';
|
|
1
2
|
export declare function useResendEmailVerificationAuth(): import("@tanstack/react-query").UseMutationResult<{
|
|
2
|
-
|
|
3
|
-
}, Error,
|
|
4
|
-
|
|
5
|
-
challenge_id: string;
|
|
6
|
-
}, unknown>;
|
|
7
|
-
export declare function useVerifyEmailAuth(): import("@tanstack/react-query").UseMutationResult<import("@shipfox/client-shell/runtime").AuthenticatedSession, Error, {
|
|
8
|
-
email: string;
|
|
9
|
-
challenge_id: string;
|
|
10
|
-
code: string;
|
|
11
|
-
}, unknown>;
|
|
3
|
+
nextResendAvailableAt: string;
|
|
4
|
+
}, Error, ResendEmailVerificationCommand, unknown>;
|
|
5
|
+
export declare function useVerifyEmailAuth(): import("@tanstack/react-query").UseMutationResult<import("@shipfox/client-shell/runtime").AuthenticatedSession, Error, VerifyEmailCommand, unknown>;
|
|
12
6
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"verify-email-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/verify-email-auth.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,8BAA8B,EAC9B,kBAAkB,EACnB,MAAM,eAAe,CAAC;AA8BvB,wBAAgB,8BAA8B;;mDAE7C;AAED,wBAAgB,kBAAkB,wJAOjC"}
|
|
@@ -3,18 +3,28 @@ import { checkedApiRequest } from '@shipfox/client-api';
|
|
|
3
3
|
import { useMutation } from '@tanstack/react-query';
|
|
4
4
|
import { useAuthTransition } from '#state/auth.js';
|
|
5
5
|
import { toAuthenticatedSession } from './auth-mapper.js';
|
|
6
|
-
async function verifyEmailAuth(
|
|
6
|
+
async function verifyEmailAuth(command) {
|
|
7
7
|
const response = await checkedApiRequest(verifyEmailConfirmResponseSchema, '/auth/verify-email/confirm', {
|
|
8
8
|
method: 'POST',
|
|
9
|
-
body
|
|
9
|
+
body: {
|
|
10
|
+
email: command.email,
|
|
11
|
+
challenge_id: command.challengeId,
|
|
12
|
+
code: command.code
|
|
13
|
+
}
|
|
10
14
|
});
|
|
11
15
|
return toAuthenticatedSession(response);
|
|
12
16
|
}
|
|
13
|
-
async function resendEmailVerificationAuth(
|
|
14
|
-
|
|
17
|
+
async function resendEmailVerificationAuth(command) {
|
|
18
|
+
const response = await checkedApiRequest(verifyEmailResendResponseSchema, '/auth/verify-email/resend', {
|
|
15
19
|
method: 'POST',
|
|
16
|
-
body
|
|
20
|
+
body: {
|
|
21
|
+
email: command.email,
|
|
22
|
+
challenge_id: command.challengeId
|
|
23
|
+
}
|
|
17
24
|
});
|
|
25
|
+
return {
|
|
26
|
+
nextResendAvailableAt: response.next_resend_available_at
|
|
27
|
+
};
|
|
18
28
|
}
|
|
19
29
|
export function useResendEmailVerificationAuth() {
|
|
20
30
|
return useMutation({
|
|
@@ -25,7 +35,7 @@ export function useVerifyEmailAuth() {
|
|
|
25
35
|
const { enterAuthenticated } = useAuthTransition();
|
|
26
36
|
return useMutation({
|
|
27
37
|
mutationFn: verifyEmailAuth,
|
|
28
|
-
onSuccess: enterAuthenticated
|
|
38
|
+
onSuccess: (session)=>enterAuthenticated(session)
|
|
29
39
|
});
|
|
30
40
|
}
|
|
31
41
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/api/verify-email-auth.ts"],"sourcesContent":["import {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/verify-email-auth.ts"],"sourcesContent":["import {\n verifyEmailConfirmResponseSchema,\n verifyEmailResendResponseSchema,\n} from '@shipfox/api-auth-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {useMutation} from '@tanstack/react-query';\nimport type {\n EmailVerificationResendResult,\n ResendEmailVerificationCommand,\n VerifyEmailCommand,\n} from '#core/auth.js';\nimport {useAuthTransition} from '#state/auth.js';\nimport {toAuthenticatedSession} from './auth-mapper.js';\n\nasync function verifyEmailAuth(command: VerifyEmailCommand) {\n const response = await checkedApiRequest(\n verifyEmailConfirmResponseSchema,\n '/auth/verify-email/confirm',\n {\n method: 'POST',\n body: {email: command.email, challenge_id: command.challengeId, code: command.code},\n },\n );\n return toAuthenticatedSession(response);\n}\n\nasync function resendEmailVerificationAuth(command: ResendEmailVerificationCommand) {\n const response = await checkedApiRequest(\n verifyEmailResendResponseSchema,\n '/auth/verify-email/resend',\n {\n method: 'POST',\n body: {email: command.email, challenge_id: command.challengeId},\n },\n );\n return {\n nextResendAvailableAt: response.next_resend_available_at,\n } satisfies EmailVerificationResendResult;\n}\n\nexport function useResendEmailVerificationAuth() {\n return useMutation({mutationFn: resendEmailVerificationAuth});\n}\n\nexport function useVerifyEmailAuth() {\n const {enterAuthenticated} = useAuthTransition();\n\n return useMutation({\n mutationFn: verifyEmailAuth,\n onSuccess: (session) => enterAuthenticated(session),\n });\n}\n"],"names":["verifyEmailConfirmResponseSchema","verifyEmailResendResponseSchema","checkedApiRequest","useMutation","useAuthTransition","toAuthenticatedSession","verifyEmailAuth","command","response","method","body","email","challenge_id","challengeId","code","resendEmailVerificationAuth","nextResendAvailableAt","next_resend_available_at","useResendEmailVerificationAuth","mutationFn","useVerifyEmailAuth","enterAuthenticated","onSuccess","session"],"mappings":"AAAA,SACEA,gCAAgC,EAChCC,+BAA+B,QAC1B,wBAAwB;AAC/B,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SAAQC,WAAW,QAAO,wBAAwB;AAMlD,SAAQC,iBAAiB,QAAO,iBAAiB;AACjD,SAAQC,sBAAsB,QAAO,mBAAmB;AAExD,eAAeC,gBAAgBC,OAA2B;IACxD,MAAMC,WAAW,MAAMN,kBACrBF,kCACA,8BACA;QACES,QAAQ;QACRC,MAAM;YAACC,OAAOJ,QAAQI,KAAK;YAAEC,cAAcL,QAAQM,WAAW;YAAEC,MAAMP,QAAQO,IAAI;QAAA;IACpF;IAEF,OAAOT,uBAAuBG;AAChC;AAEA,eAAeO,4BAA4BR,OAAuC;IAChF,MAAMC,WAAW,MAAMN,kBACrBD,iCACA,6BACA;QACEQ,QAAQ;QACRC,MAAM;YAACC,OAAOJ,QAAQI,KAAK;YAAEC,cAAcL,QAAQM,WAAW;QAAA;IAChE;IAEF,OAAO;QACLG,uBAAuBR,SAASS,wBAAwB;IAC1D;AACF;AAEA,OAAO,SAASC;IACd,OAAOf,YAAY;QAACgB,YAAYJ;IAA2B;AAC7D;AAEA,OAAO,SAASK;IACd,MAAM,EAACC,kBAAkB,EAAC,GAAGjB;IAE7B,OAAOD,YAAY;QACjBgB,YAAYb;QACZgB,WAAW,CAACC,UAAYF,mBAAmBE;IAC7C;AACF"}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export declare function createWorkspace(
|
|
4
|
-
export { listUserWorkspaces, userWorkspacesQueryKey };
|
|
5
|
-
export declare function useCreateWorkspaceAuth(): import("@tanstack/react-query").UseMutationResult<import("
|
|
6
|
-
name: string;
|
|
7
|
-
}, unknown>;
|
|
1
|
+
import { listUserWorkspaces, userWorkspacesQueryKey, userWorkspacesQueryOptions } from '@shipfox/client-shell/runtime';
|
|
2
|
+
import type { WorkspaceCreateCommand } from '#core/auth.js';
|
|
3
|
+
export declare function createWorkspace(command: WorkspaceCreateCommand): Promise<import("#core/auth.js").Workspace>;
|
|
4
|
+
export { listUserWorkspaces, userWorkspacesQueryKey, userWorkspacesQueryOptions };
|
|
5
|
+
export declare function useCreateWorkspaceAuth(): import("@tanstack/react-query").UseMutationResult<import("#core/auth.js").Workspace, Error, WorkspaceCreateCommand, unknown>;
|
|
8
6
|
//# sourceMappingURL=workspace-auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/workspace-auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workspace-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/workspace-auth.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,0BAA0B,EAC3B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,eAAe,CAAC;AAI1D,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,8CAMpE;AAED,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAE,0BAA0B,EAAC,CAAC;AAEhF,wBAAgB,sBAAsB,iIAYrC"}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { workspaceResponseSchema } from '@shipfox/api-workspaces-dto';
|
|
2
2
|
import { checkedApiRequest } from '@shipfox/client-api';
|
|
3
|
-
import { listUserWorkspaces, userWorkspacesQueryKey } from '@shipfox/client-shell/runtime';
|
|
3
|
+
import { listUserWorkspaces, userWorkspacesQueryKey, userWorkspacesQueryOptions } from '@shipfox/client-shell/runtime';
|
|
4
4
|
import { useMutation } from '@tanstack/react-query';
|
|
5
5
|
import { useRefreshAuth } from './refresh-auth.js';
|
|
6
6
|
import { toWorkspace } from './workspace-mapper.js';
|
|
7
|
-
export async function createWorkspace(
|
|
7
|
+
export async function createWorkspace(command) {
|
|
8
8
|
const response = await checkedApiRequest(workspaceResponseSchema, '/workspaces', {
|
|
9
9
|
method: 'POST',
|
|
10
|
-
body
|
|
10
|
+
body: {
|
|
11
|
+
name: command.name
|
|
12
|
+
}
|
|
11
13
|
});
|
|
12
14
|
return toWorkspace(response);
|
|
13
15
|
}
|
|
14
|
-
export { listUserWorkspaces, userWorkspacesQueryKey };
|
|
16
|
+
export { listUserWorkspaces, userWorkspacesQueryKey, userWorkspacesQueryOptions };
|
|
15
17
|
export function useCreateWorkspaceAuth() {
|
|
16
18
|
const refreshAuth = useRefreshAuth();
|
|
17
19
|
return useMutation({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/api/workspace-auth.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/workspace-auth.ts"],"sourcesContent":["import {workspaceResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {\n listUserWorkspaces,\n userWorkspacesQueryKey,\n userWorkspacesQueryOptions,\n} from '@shipfox/client-shell/runtime';\nimport {useMutation} from '@tanstack/react-query';\nimport type {WorkspaceCreateCommand} from '#core/auth.js';\nimport {useRefreshAuth} from './refresh-auth.js';\nimport {toWorkspace} from './workspace-mapper.js';\n\nexport async function createWorkspace(command: WorkspaceCreateCommand) {\n const response = await checkedApiRequest(workspaceResponseSchema, '/workspaces', {\n method: 'POST',\n body: {name: command.name},\n });\n return toWorkspace(response);\n}\n\nexport {listUserWorkspaces, userWorkspacesQueryKey, userWorkspacesQueryOptions};\n\nexport function useCreateWorkspaceAuth() {\n const refreshAuth = useRefreshAuth();\n\n return useMutation({\n mutationFn: createWorkspace,\n onSuccess: async () => {\n // The new workspace introduces a membership the existing access token\n // doesn't carry. Refresh so the next request includes it in the JWT\n // claim and passes the in-memory canAccess() check on the server.\n await refreshAuth();\n },\n });\n}\n"],"names":["workspaceResponseSchema","checkedApiRequest","listUserWorkspaces","userWorkspacesQueryKey","userWorkspacesQueryOptions","useMutation","useRefreshAuth","toWorkspace","createWorkspace","command","response","method","body","name","useCreateWorkspaceAuth","refreshAuth","mutationFn","onSuccess"],"mappings":"AAAA,SAAQA,uBAAuB,QAAO,8BAA8B;AACpE,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SACEC,kBAAkB,EAClBC,sBAAsB,EACtBC,0BAA0B,QACrB,gCAAgC;AACvC,SAAQC,WAAW,QAAO,wBAAwB;AAElD,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAAQC,WAAW,QAAO,wBAAwB;AAElD,OAAO,eAAeC,gBAAgBC,OAA+B;IACnE,MAAMC,WAAW,MAAMT,kBAAkBD,yBAAyB,eAAe;QAC/EW,QAAQ;QACRC,MAAM;YAACC,MAAMJ,QAAQI,IAAI;QAAA;IAC3B;IACA,OAAON,YAAYG;AACrB;AAEA,SAAQR,kBAAkB,EAAEC,sBAAsB,EAAEC,0BAA0B,GAAE;AAEhF,OAAO,SAASU;IACd,MAAMC,cAAcT;IAEpB,OAAOD,YAAY;QACjBW,YAAYR;QACZS,WAAW;YACT,sEAAsE;YACtE,oEAAoE;YACpE,kEAAkE;YAClE,MAAMF;QACR;IACF;AACF"}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import type { WorkspaceResponseDto } from '@shipfox/api-workspaces-dto';
|
|
2
|
-
|
|
3
|
-
id: string;
|
|
4
|
-
name: string;
|
|
5
|
-
status: 'active' | 'suspended' | 'deleted';
|
|
6
|
-
}
|
|
2
|
+
import type { Workspace } from '#core/auth.js';
|
|
7
3
|
export declare function toWorkspace(dto: WorkspaceResponseDto): Workspace;
|
|
8
4
|
//# sourceMappingURL=workspace-mapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-mapper.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/workspace-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace-mapper.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/workspace-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oBAAoB,EAAC,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,eAAe,CAAC;AAE7C,wBAAgB,WAAW,CAAC,GAAG,EAAE,oBAAoB,GAAG,SAAS,CAEhE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/hooks/api/workspace-mapper.ts"],"sourcesContent":["import type {WorkspaceResponseDto} from '@shipfox/api-workspaces-dto';\
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/workspace-mapper.ts"],"sourcesContent":["import type {WorkspaceResponseDto} from '@shipfox/api-workspaces-dto';\nimport type {Workspace} from '#core/auth.js';\n\nexport function toWorkspace(dto: WorkspaceResponseDto): Workspace {\n return {id: dto.id, name: dto.name, status: dto.status};\n}\n"],"names":["toWorkspace","dto","id","name","status"],"mappings":"AAGA,OAAO,SAASA,YAAYC,GAAyB;IACnD,OAAO;QAACC,IAAID,IAAIC,EAAE;QAAEC,MAAMF,IAAIE,IAAI;QAAEC,QAAQH,IAAIG,MAAM;IAAA;AACxD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-page.d.ts","sourceRoot":"","sources":["../../src/pages/login-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"login-page.d.ts","sourceRoot":"","sources":["../../src/pages/login-page.tsx"],"names":[],"mappings":"AAqBA,wBAAgB,SAAS,gCAmKxB"}
|
package/dist/pages/login-page.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { loginBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
-
import { AuthShell } from '@shipfox/client-shell/runtime';
|
|
3
|
+
import { AuthShell, useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
4
4
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
5
5
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
6
6
|
import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
|
|
7
7
|
import { Icon } from '@shipfox/react-ui/icon';
|
|
8
8
|
import { Text } from '@shipfox/react-ui/typography';
|
|
9
9
|
import { useForm } from '@tanstack/react-form';
|
|
10
|
-
import { Link
|
|
10
|
+
import { Link } from '@tanstack/react-router';
|
|
11
11
|
import { useAtom } from 'jotai';
|
|
12
12
|
import { useEffect, useRef, useState } from 'react';
|
|
13
13
|
import { useLoginAuth } from '#hooks/api/login-auth.js';
|
|
14
14
|
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
15
|
+
import { validateRedirectSearch } from '../routes/inputs.js';
|
|
15
16
|
import { loginErrorToFormError } from './form-errors.js';
|
|
16
17
|
import { extractInvitationToken, pendingInvitation, useInvitationContext } from './invitation-context.js';
|
|
17
18
|
export function LoginPage() {
|
|
18
19
|
const login = useLoginAuth();
|
|
19
|
-
const search =
|
|
20
|
-
strict: false
|
|
21
|
-
});
|
|
20
|
+
const search = useRouteSearch(validateRedirectSearch);
|
|
22
21
|
const invitationToken = extractInvitationToken(search.redirect);
|
|
23
22
|
const invitationPreview = useInvitationContext(invitationToken);
|
|
24
23
|
const invitationPending = pendingInvitation(invitationPreview.data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/login-page.tsx"],"sourcesContent":["import {loginBodySchema} from '@shipfox/api-auth-dto';\nimport {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {useLoginAuth} from '#hooks/api/login-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {loginErrorToFormError} from './form-errors.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function LoginPage() {\n const login = useLoginAuth();\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 [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 // Lock the email field when arriving from an invitation link so the user\n // can't log in as a different account than the one the invitation targets.\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 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 const isInvitationEmailLocked = Boolean(invitationPending);\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 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=\"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={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: 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 <ButtonLink asChild variant=\"subtle\" className=\"-mt-8 self-end\">\n <Link to=\"/auth/reset\">Forgot password?</Link>\n </ButtonLink>\n <Button className=\"w-full\" isLoading={login.isPending} type=\"submit\">\n {login.isPending ? 'Logging in...' : 'Log in'}\n </Button>\n </form>\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":["loginBodySchema","AuthShell","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","Text","useForm","Link","useSearch","useAtom","useEffect","useRef","useState","useLoginAuth","authFormDraftAtom","initialAuthFormDraft","loginErrorToFormError","extractInvitationToken","pendingInvitation","useInvitationContext","LoginPage","login","search","strict","invitationToken","redirect","invitationPreview","invitationPending","data","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","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","headerTitle","workspaceName","headerDescription","persistDraft","title","description","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","role","type","Field","name","validators","onBlur","shape","label","id","autoComplete","onChange","handleChange","target","handleBlur","readOnly","iconRight","aria-hidden","asChild","variant","to","isLoading","isPending","size","underline"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/pages/login-page.tsx"],"sourcesContent":["import {loginBodySchema} 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 {Icon} from '@shipfox/react-ui/icon';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {useLoginAuth} from '#hooks/api/login-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {validateRedirectSearch} from '../routes/inputs.js';\nimport {loginErrorToFormError} from './form-errors.js';\nimport {\n extractInvitationToken,\n pendingInvitation,\n useInvitationContext,\n} from './invitation-context.js';\n\nexport function LoginPage() {\n const login = useLoginAuth();\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 [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 // Lock the email field when arriving from an invitation link so the user\n // can't log in as a different account than the one the invitation targets.\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 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 const isInvitationEmailLocked = Boolean(invitationPending);\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 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=\"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={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: 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 <ButtonLink asChild variant=\"subtle\" className=\"-mt-8 self-end\">\n <Link to=\"/auth/reset\">Forgot password?</Link>\n </ButtonLink>\n <Button className=\"w-full\" isLoading={login.isPending} type=\"submit\">\n {login.isPending ? 'Logging in...' : 'Log in'}\n </Button>\n </form>\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":["loginBodySchema","AuthShell","useRouteSearch","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","Text","useForm","Link","useAtom","useEffect","useRef","useState","useLoginAuth","authFormDraftAtom","initialAuthFormDraft","validateRedirectSearch","loginErrorToFormError","extractInvitationToken","pendingInvitation","useInvitationContext","LoginPage","login","search","invitationToken","redirect","invitationPreview","invitationPending","data","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","isInvitationEmailLocked","Boolean","invitationRedirect","encodeURIComponent","headerTitle","workspaceName","headerDescription","persistDraft","title","description","className","noValidate","event","preventDefault","stopPropagation","handleSubmit","role","type","Field","name","validators","onBlur","shape","label","id","autoComplete","onChange","handleChange","target","handleBlur","readOnly","iconRight","aria-hidden","asChild","variant","to","isLoading","isPending","size","underline"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout-page.d.ts","sourceRoot":"","sources":["../../src/pages/logout-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logout-page.d.ts","sourceRoot":"","sources":["../../src/pages/logout-page.tsx"],"names":[],"mappings":"AAQA,wBAAgB,UAAU,gCAoBzB"}
|
|
@@ -1,33 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { AuthShell } from '@shipfox/client-shell/runtime';
|
|
2
|
+
import { AuthShell, useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
3
3
|
import { Text } from '@shipfox/react-ui/typography';
|
|
4
|
-
import {
|
|
4
|
+
import { useRouter } from '@tanstack/react-router';
|
|
5
5
|
import { useEffect } from 'react';
|
|
6
|
-
import {
|
|
6
|
+
import { sanitizeLogoutRedirectPath } from '#/components/redirect-target.js';
|
|
7
7
|
import { useLogoutAuth } from '#hooks/api/logout-auth.js';
|
|
8
|
+
import { validateRedirectSearch } from '../routes/inputs.js';
|
|
8
9
|
export function LogoutPage() {
|
|
9
10
|
const logout = useLogoutAuth();
|
|
10
|
-
const navigate = useNavigate();
|
|
11
11
|
const router = useRouter();
|
|
12
|
-
const search =
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
const target = sanitizeRedirectPath(search.redirect);
|
|
12
|
+
const search = useRouteSearch(validateRedirectSearch);
|
|
13
|
+
const target = sanitizeLogoutRedirectPath(search.redirect);
|
|
16
14
|
useEffect(()=>{
|
|
17
15
|
logout.mutateAsync().finally(()=>{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
router.history.replace(target);
|
|
21
|
-
} else {
|
|
22
|
-
navigate({
|
|
23
|
-
to: '/auth/login',
|
|
24
|
-
replace: true
|
|
25
|
-
});
|
|
26
|
-
}
|
|
16
|
+
// Same-origin runtime path; let history drive route resolution.
|
|
17
|
+
router.history.replace(target);
|
|
27
18
|
});
|
|
28
19
|
}, [
|
|
29
20
|
logout.mutateAsync,
|
|
30
|
-
navigate,
|
|
31
21
|
router,
|
|
32
22
|
target
|
|
33
23
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/logout-page.tsx"],"sourcesContent":["import {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/pages/logout-page.tsx"],"sourcesContent":["import {AuthShell, useRouteSearch} from '@shipfox/client-shell/runtime';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useRouter} from '@tanstack/react-router';\nimport {useEffect} from 'react';\nimport {sanitizeLogoutRedirectPath} from '#/components/redirect-target.js';\nimport {useLogoutAuth} from '#hooks/api/logout-auth.js';\nimport {validateRedirectSearch} from '../routes/inputs.js';\n\nexport function LogoutPage() {\n const logout = useLogoutAuth();\n const router = useRouter();\n const search = useRouteSearch(validateRedirectSearch);\n const target = sanitizeLogoutRedirectPath(search.redirect);\n\n useEffect(() => {\n logout.mutateAsync().finally(() => {\n // Same-origin runtime path; let history drive route resolution.\n router.history.replace(target);\n });\n }, [logout.mutateAsync, router, target]);\n\n return (\n <AuthShell title=\"Logging out\" description=\"Ending your Shipfox session.\">\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n You will be sent back to login in a moment.\n </Text>\n </AuthShell>\n );\n}\n"],"names":["AuthShell","useRouteSearch","Text","useRouter","useEffect","sanitizeLogoutRedirectPath","useLogoutAuth","validateRedirectSearch","LogoutPage","logout","router","search","target","redirect","mutateAsync","finally","history","replace","title","description","size","className"],"mappings":";AAAA,SAAQA,SAAS,EAAEC,cAAc,QAAO,gCAAgC;AACxE,SAAQC,IAAI,QAAO,+BAA+B;AAClD,SAAQC,SAAS,QAAO,yBAAyB;AACjD,SAAQC,SAAS,QAAO,QAAQ;AAChC,SAAQC,0BAA0B,QAAO,kCAAkC;AAC3E,SAAQC,aAAa,QAAO,4BAA4B;AACxD,SAAQC,sBAAsB,QAAO,sBAAsB;AAE3D,OAAO,SAASC;IACd,MAAMC,SAASH;IACf,MAAMI,SAASP;IACf,MAAMQ,SAASV,eAAeM;IAC9B,MAAMK,SAASP,2BAA2BM,OAAOE,QAAQ;IAEzDT,UAAU;QACRK,OAAOK,WAAW,GAAGC,OAAO,CAAC;YAC3B,gEAAgE;YAChEL,OAAOM,OAAO,CAACC,OAAO,CAACL;QACzB;IACF,GAAG;QAACH,OAAOK,WAAW;QAAEJ;QAAQE;KAAO;IAEvC,qBACE,KAACZ;QAAUkB,OAAM;QAAcC,aAAY;kBACzC,cAAA,KAACjB;YAAKkB,MAAK;YAAKC,WAAU;sBAA6C;;;AAK7E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-reset-page.d.ts","sourceRoot":"","sources":["../../src/pages/password-reset-page.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"password-reset-page.d.ts","sourceRoot":"","sources":["../../src/pages/password-reset-page.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,iBAAiB,gCAQhC"}
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { passwordResetConfirmBodySchema, passwordResetRequestBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
-
import { AuthShell } from '@shipfox/client-shell/runtime';
|
|
3
|
+
import { AuthShell, useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
4
4
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
5
5
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
6
6
|
import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
|
|
7
7
|
import { toast } from '@shipfox/react-ui/toast';
|
|
8
8
|
import { Text } from '@shipfox/react-ui/typography';
|
|
9
9
|
import { useForm } from '@tanstack/react-form';
|
|
10
|
-
import { Link, useNavigate
|
|
10
|
+
import { Link, useNavigate } from '@tanstack/react-router';
|
|
11
11
|
import { useAtom } from 'jotai';
|
|
12
12
|
import { useEffect, useRef, useState } from 'react';
|
|
13
13
|
import { useConfirmPasswordResetAuth, useRequestPasswordResetAuth } from '#hooks/api/password-reset-auth.js';
|
|
14
14
|
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
15
|
+
import { validatePasswordResetSearch } from '../routes/inputs.js';
|
|
15
16
|
import { passwordResetConfirmErrorToFormError, passwordResetRequestErrorToFormError } from './form-errors.js';
|
|
16
17
|
export function PasswordResetPage() {
|
|
17
|
-
const
|
|
18
|
-
strict: false
|
|
19
|
-
});
|
|
20
|
-
const token = typeof search.token === 'string' ? search.token : undefined;
|
|
18
|
+
const { token } = useRouteSearch(validatePasswordResetSearch);
|
|
21
19
|
if (token) {
|
|
22
20
|
return /*#__PURE__*/ _jsx(PasswordResetConfirm, {
|
|
23
21
|
token: token
|
|
@@ -187,7 +185,10 @@ function PasswordResetConfirm({ token }) {
|
|
|
187
185
|
token,
|
|
188
186
|
new_password: value.new_password
|
|
189
187
|
});
|
|
190
|
-
await confirmPasswordReset.mutateAsync(
|
|
188
|
+
await confirmPasswordReset.mutateAsync({
|
|
189
|
+
token: body.token,
|
|
190
|
+
newPassword: body.new_password
|
|
191
|
+
});
|
|
191
192
|
setAuthFormDraft(initialAuthFormDraft);
|
|
192
193
|
toast.success('Your password has been changed. You are now logged in.');
|
|
193
194
|
await navigate({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pages/password-reset-page.tsx"],"sourcesContent":["import {\n passwordResetConfirmBodySchema,\n passwordResetRequestBodySchema,\n} from '@shipfox/api-auth-dto';\nimport {AuthShell} from '@shipfox/client-shell/runtime';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {\n useConfirmPasswordResetAuth,\n useRequestPasswordResetAuth,\n} from '#hooks/api/password-reset-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n passwordResetConfirmErrorToFormError,\n passwordResetRequestErrorToFormError,\n} from './form-errors.js';\n\nexport function PasswordResetPage() {\n const search = useSearch({strict: false});\n const token = typeof search.token === 'string' ? search.token : undefined;\n\n if (token) {\n return <PasswordResetConfirm token={token} />;\n }\n\n return <PasswordResetRequest />;\n}\n\nfunction PasswordResetRequest() {\n const requestPasswordReset = useRequestPasswordResetAuth();\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [submittedEmail, setSubmittedEmail] = useState<string | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const parsed = passwordResetRequestBodySchema.parse(value);\n await requestPasswordReset.mutateAsync(parsed);\n setSubmittedEmail(parsed.email);\n } catch (error) {\n const mapped = passwordResetRequestErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n useEffect(() => {\n return () => {\n const {email} = form.state.values;\n if (email !== draftRef.current.email) {\n setAuthFormDraft((current) => ({...current, email}));\n }\n };\n }, [form, setAuthFormDraft]);\n\n if (submittedEmail) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent reset instructions to ${submittedEmail}.`}\n >\n <Callout role=\"alert\" type=\"success\">\n If a Shipfox account exists for that email, the reset link will arrive shortly.\n </Callout>\n <Button\n className=\"w-full\"\n variant=\"secondary\"\n type=\"button\"\n onClick={() => setSubmittedEmail(undefined)}\n >\n Use another email\n </Button>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n return (\n <AuthShell title=\"Reset your password\" description=\"Enter your email to get a reset link.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"email\"\n validators={{\n onBlur: passwordResetRequestBodySchema.shape.email,\n onSubmit: passwordResetRequestBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n setAuthFormDraft((current) => ({...current, email: field.state.value}));\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={requestPasswordReset.isPending} type=\"submit\">\n {requestPasswordReset.isPending ? 'Sending link...' : 'Send reset link'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n\nfunction PasswordResetConfirm({token}: {token: string}) {\n const confirmPasswordReset = useConfirmPasswordResetAuth();\n const navigate = useNavigate();\n const [, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {new_password: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = passwordResetConfirmBodySchema.parse({\n token,\n new_password: value.new_password,\n });\n await confirmPasswordReset.mutateAsync(body);\n setAuthFormDraft(initialAuthFormDraft);\n toast.success('Your password has been changed. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n const mapped = passwordResetConfirmErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n return (\n <AuthShell title=\"Set a new password\" description=\"Choose a password for your Shipfox account.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"new_password\"\n validators={{\n onBlur: passwordResetConfirmBodySchema.shape.new_password,\n onSubmit: passwordResetConfirmBodySchema.shape.new_password,\n }}\n >\n {(field) => (\n <FormField label=\"New password\" id=\"new-password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"new_password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={confirmPasswordReset.isPending} type=\"submit\">\n {confirmPasswordReset.isPending ? 'Updating password...' : 'Update password'}\n </Button>\n </form>\n <ButtonLink asChild variant=\"subtle\" className=\"self-center\">\n <Link to=\"/auth/login\">Back to login</Link>\n </ButtonLink>\n </AuthShell>\n );\n}\n"],"names":["passwordResetConfirmBodySchema","passwordResetRequestBodySchema","AuthShell","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","useConfirmPasswordResetAuth","useRequestPasswordResetAuth","authFormDraftAtom","initialAuthFormDraft","passwordResetConfirmErrorToFormError","passwordResetRequestErrorToFormError","PasswordResetPage","search","strict","token","undefined","PasswordResetConfirm","PasswordResetRequest","requestPasswordReset","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","formError","setFormError","draftRef","current","form","defaultValues","email","onSubmit","value","parsed","parse","mutateAsync","error","mapped","message","state","values","title","description","role","type","className","variant","onClick","size","asChild","underline","to","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","shape","field","label","id","autoComplete","onChange","handleChange","target","handleBlur","isLoading","isPending","confirmPasswordReset","navigate","new_password","body","success","replace"],"mappings":";AAAA,SACEA,8BAA8B,EAC9BC,8BAA8B,QACzB,wBAAwB;AAC/B,SAAQC,SAAS,QAAO,gCAAgC;AACxD,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,EAAEC,SAAS,QAAO,yBAAyB;AACpE,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,SACEC,oCAAoC,EACpCC,oCAAoC,QAC/B,mBAAmB;AAE1B,OAAO,SAASC;IACd,MAAMC,SAASZ,UAAU;QAACa,QAAQ;IAAK;IACvC,MAAMC,QAAQ,OAAOF,OAAOE,KAAK,KAAK,WAAWF,OAAOE,KAAK,GAAGC;IAEhE,IAAID,OAAO;QACT,qBAAO,KAACE;YAAqBF,OAAOA;;IACtC;IAEA,qBAAO,KAACG;AACV;AAEA,SAASA;IACP,MAAMC,uBAAuBZ;IAC7B,MAAM,CAACa,eAAeC,iBAAiB,GAAGnB,QAAQM;IAClD,MAAM,CAACc,gBAAgBC,kBAAkB,GAAGlB;IAC5C,MAAM,CAACmB,WAAWC,aAAa,GAAGpB;IAClC,MAAMqB,WAAWtB,OAAOgB;IACxBM,SAASC,OAAO,GAAGP;IAEnB,MAAMQ,OAAO9B,QAAQ;QACnB+B,eAAe;YAACC,OAAOV,cAAcU,KAAK;QAAA;QAC1CC,UAAU,OAAO,EAACC,KAAK,EAAC;YACtBP,aAAaT;YACb,IAAI;gBACF,MAAMiB,SAAS7C,+BAA+B8C,KAAK,CAACF;gBACpD,MAAMb,qBAAqBgB,WAAW,CAACF;gBACvCV,kBAAkBU,OAAOH,KAAK;YAChC,EAAE,OAAOM,OAAO;gBACd,MAAMC,SAAS1B,qCAAqCyB;gBACpDX,aAAaY,OAAOC,OAAO;YAC7B;QACF;IACF;IAEAnC,UAAU;QACR,OAAO;YACL,MAAM,EAAC2B,KAAK,EAAC,GAAGF,KAAKW,KAAK,CAACC,MAAM;YACjC,IAAIV,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,MAACjC;YACCoD,OAAM;YACNC,aAAa,CAAC,8BAA8B,EAAEpB,eAAe,CAAC,CAAC;;8BAE/D,KAAC9B;oBAAQmD,MAAK;oBAAQC,MAAK;8BAAU;;8BAGrC,KAACtD;oBACCuD,WAAU;oBACVC,SAAQ;oBACRF,MAAK;oBACLG,SAAS,IAAMxB,kBAAkBP;8BAClC;;8BAGD,MAACnB;oBAAKmD,MAAK;oBAAKH,WAAU;;wBAA6C;wBACtD;sCACf,KAACtD;4BAAW0D,OAAO;4BAACH,SAAQ;4BAAcI,SAAS;sCACjD,cAAA,KAACnD;gCAAKoD,IAAG;0CAAc;;;;;;;IAKjC;IAEA,qBACE,MAAC9D;QAAUoD,OAAM;QAAsBC,aAAY;;0BACjD,MAACd;gBACCiB,WAAU;gBACVO,UAAU;gBACVrB,UAAU,CAACsB;oBACTA,MAAMC,cAAc;oBACpBD,MAAME,eAAe;oBACrB,KAAK3B,KAAK4B,YAAY;gBACxB;;oBAEChC,0BACC,KAAChC;wBAAQmD,MAAK;wBAAQC,MAAK;kCACxBpB;yBAED;kCACJ,KAACI,KAAK6B,KAAK;wBACTC,MAAK;wBACLC,YAAY;4BACVC,QAAQxE,+BAA+ByE,KAAK,CAAC/B,KAAK;4BAClDC,UAAU3C,+BAA+ByE,KAAK,CAAC/B,KAAK;wBACtD;kCAEC,CAACgC,sBACA,KAACrE;gCAAUsE,OAAM;gCAAQC,IAAG;gCAAQ5B,OAAOzC,WAAWmE;0CACpD,cAAA,KAACpE;oCACCuE,cAAa;oCACbP,MAAK;oCACLd,MAAK;oCACLZ,OAAO8B,MAAMvB,KAAK,CAACP,KAAK;oCACxBkC,UAAU,CAACb,QAAUS,MAAMK,YAAY,CAACd,MAAMe,MAAM,CAACpC,KAAK;oCAC1D4B,QAAQ;wCACNE,MAAMO,UAAU;wCAChBhD,iBAAiB,CAACM,UAAa,CAAA;gDAAC,GAAGA,OAAO;gDAAEG,OAAOgC,MAAMvB,KAAK,CAACP,KAAK;4CAAA,CAAA;oCACtE;;;;kCAKR,KAAC1C;wBAAOuD,WAAU;wBAASyB,WAAWnD,qBAAqBoD,SAAS;wBAAE3B,MAAK;kCACxEzB,qBAAqBoD,SAAS,GAAG,oBAAoB;;;;0BAG1D,MAAC1E;gBAAKmD,MAAK;gBAAKH,WAAU;;oBAA6C;oBACtD;kCACf,KAACtD;wBAAW0D,OAAO;wBAACH,SAAQ;wBAAcI,SAAS;kCACjD,cAAA,KAACnD;4BAAKoD,IAAG;sCAAc;;;;;;;AAKjC;AAEA,SAASlC,qBAAqB,EAACF,KAAK,EAAkB;IACpD,MAAMyD,uBAAuBlE;IAC7B,MAAMmE,WAAWzE;IACjB,MAAM,GAAGqB,iBAAiB,GAAGnB,QAAQM;IACrC,MAAM,CAACgB,WAAWC,aAAa,GAAGpB;IAElC,MAAMuB,OAAO9B,QAAQ;QACnB+B,eAAe;YAAC6C,cAAc;QAAE;QAChC3C,UAAU,OAAO,EAACC,KAAK,EAAC;YACtBP,aAAaT;YACb,IAAI;gBACF,MAAM2D,OAAOxF,+BAA+B+C,KAAK,CAAC;oBAChDnB;oBACA2D,cAAc1C,MAAM0C,YAAY;gBAClC;gBACA,MAAMF,qBAAqBrC,WAAW,CAACwC;gBACvCtD,iBAAiBZ;gBACjBb,MAAMgF,OAAO,CAAC;gBACd,MAAMH,SAAS;oBAACtB,IAAI;oBAAK0B,SAAS;gBAAI;YACxC,EAAE,OAAOzC,OAAO;gBACd,MAAMC,SAAS3B,qCAAqC0B;gBACpDX,aAAaY,OAAOC,OAAO;YAC7B;QACF;IACF;IAEA,qBACE,MAACjD;QAAUoD,OAAM;QAAqBC,aAAY;;0BAChD,MAACd;gBACCiB,WAAU;gBACVO,UAAU;gBACVrB,UAAU,CAACsB;oBACTA,MAAMC,cAAc;oBACpBD,MAAME,eAAe;oBACrB,KAAK3B,KAAK4B,YAAY;gBACxB;;oBAEChC,0BACC,KAAChC;wBAAQmD,MAAK;wBAAQC,MAAK;kCACxBpB;yBAED;kCACJ,KAACI,KAAK6B,KAAK;wBACTC,MAAK;wBACLC,YAAY;4BACVC,QAAQzE,+BAA+B0E,KAAK,CAACa,YAAY;4BACzD3C,UAAU5C,+BAA+B0E,KAAK,CAACa,YAAY;wBAC7D;kCAEC,CAACZ,sBACA,KAACrE;gCAAUsE,OAAM;gCAAeC,IAAG;gCAAe5B,OAAOzC,WAAWmE;0CAClE,cAAA,KAACpE;oCACCuE,cAAa;oCACbP,MAAK;oCACLd,MAAK;oCACLZ,OAAO8B,MAAMvB,KAAK,CAACP,KAAK;oCACxBkC,UAAU,CAACb,QAAUS,MAAMK,YAAY,CAACd,MAAMe,MAAM,CAACpC,KAAK;oCAC1D4B,QAAQE,MAAMO,UAAU;;;;kCAKhC,KAAC/E;wBAAOuD,WAAU;wBAASyB,WAAWE,qBAAqBD,SAAS;wBAAE3B,MAAK;kCACxE4B,qBAAqBD,SAAS,GAAG,yBAAyB;;;;0BAG/D,KAAChF;gBAAW0D,OAAO;gBAACH,SAAQ;gBAASD,WAAU;0BAC7C,cAAA,KAAC9C;oBAAKoD,IAAG;8BAAc;;;;;AAI/B"}
|
|
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 +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":"AA2BA,wBAAgB,UAAU,gCA+RzB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { signupBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
-
import { AuthShell } from '@shipfox/client-shell/runtime';
|
|
3
|
+
import { AuthShell, useRouteSearch } from '@shipfox/client-shell/runtime';
|
|
4
4
|
import { displayNameFieldError } from '@shipfox/client-ui';
|
|
5
5
|
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
6
6
|
import { Callout } from '@shipfox/react-ui/callout';
|
|
@@ -9,7 +9,7 @@ import { Icon } from '@shipfox/react-ui/icon';
|
|
|
9
9
|
import { toast } from '@shipfox/react-ui/toast';
|
|
10
10
|
import { Text } from '@shipfox/react-ui/typography';
|
|
11
11
|
import { useForm } from '@tanstack/react-form';
|
|
12
|
-
import { Link, useNavigate
|
|
12
|
+
import { Link, useNavigate } from '@tanstack/react-router';
|
|
13
13
|
import { useAtom } from 'jotai';
|
|
14
14
|
import { useEffect, useRef, useState } from 'react';
|
|
15
15
|
import { EmailCodeVerification } from '#/components/email-code-verification.js';
|
|
@@ -17,6 +17,7 @@ import { useRefreshAuth } from '#hooks/api/refresh-auth.js';
|
|
|
17
17
|
import { useSignupAuth } from '#hooks/api/signup-auth.js';
|
|
18
18
|
import { useResendEmailVerificationAuth, useVerifyEmailAuth } from '#hooks/api/verify-email-auth.js';
|
|
19
19
|
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
20
|
+
import { validateRedirectSearch } from '../routes/inputs.js';
|
|
20
21
|
import { signupErrorToFormError } from './form-errors.js';
|
|
21
22
|
import { authErrorMessage } from './form-utils.js';
|
|
22
23
|
import { extractInvitationToken, pendingInvitation, useInvitationContext } from './invitation-context.js';
|
|
@@ -26,9 +27,7 @@ export function SignupPage() {
|
|
|
26
27
|
const resendEmailVerification = useResendEmailVerificationAuth();
|
|
27
28
|
const refreshAuth = useRefreshAuth();
|
|
28
29
|
const navigate = useNavigate();
|
|
29
|
-
const search =
|
|
30
|
-
strict: false
|
|
31
|
-
});
|
|
30
|
+
const search = useRouteSearch(validateRedirectSearch);
|
|
32
31
|
const invitationToken = extractInvitationToken(search.redirect);
|
|
33
32
|
const invitationPreview = useInvitationContext(invitationToken);
|
|
34
33
|
const invitationPending = pendingInvitation(invitationPreview.data);
|
|
@@ -51,7 +50,7 @@ export function SignupPage() {
|
|
|
51
50
|
onSubmit: async ({ value })=>{
|
|
52
51
|
setFormError(undefined);
|
|
53
52
|
try {
|
|
54
|
-
const
|
|
53
|
+
const parsed = signupBodySchema.parse({
|
|
55
54
|
email: value.email,
|
|
56
55
|
password: value.password,
|
|
57
56
|
name: value.name,
|
|
@@ -59,7 +58,14 @@ export function SignupPage() {
|
|
|
59
58
|
invitation_token: invitationToken
|
|
60
59
|
} : {}
|
|
61
60
|
});
|
|
62
|
-
const result = await signup.mutateAsync(
|
|
61
|
+
const result = await signup.mutateAsync({
|
|
62
|
+
email: parsed.email,
|
|
63
|
+
password: parsed.password,
|
|
64
|
+
name: parsed.name,
|
|
65
|
+
...parsed.invitation_token ? {
|
|
66
|
+
invitationToken: parsed.invitation_token
|
|
67
|
+
} : {}
|
|
68
|
+
});
|
|
63
69
|
skipDraftPersistRef.current = true;
|
|
64
70
|
setAuthFormDraft(initialAuthFormDraft);
|
|
65
71
|
if (invitationToken && result.membership && invitationPending) {
|
|
@@ -151,9 +157,9 @@ export function SignupPage() {
|
|
|
151
157
|
try {
|
|
152
158
|
const result = await resendEmailVerification.mutateAsync({
|
|
153
159
|
email: emailChallenge.email,
|
|
154
|
-
|
|
160
|
+
challengeId: emailChallenge.id
|
|
155
161
|
});
|
|
156
|
-
setNextResendAvailableAt(result.
|
|
162
|
+
setNextResendAvailableAt(result.nextResendAvailableAt);
|
|
157
163
|
toast.success('If another verification email can be sent, it will arrive shortly.');
|
|
158
164
|
} catch (error) {
|
|
159
165
|
setResendError(authErrorMessage(error));
|
|
@@ -171,7 +177,7 @@ export function SignupPage() {
|
|
|
171
177
|
try {
|
|
172
178
|
await verifyEmail.mutateAsync({
|
|
173
179
|
email: emailChallenge.email,
|
|
174
|
-
|
|
180
|
+
challengeId: emailChallenge.id,
|
|
175
181
|
code
|
|
176
182
|
});
|
|
177
183
|
await refreshAuth();
|