@shipfox/client-auth 3.0.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +32 -0
  3. package/dist/components/email-code-verification.d.ts +20 -0
  4. package/dist/components/email-code-verification.d.ts.map +1 -0
  5. package/dist/components/email-code-verification.js +136 -0
  6. package/dist/components/email-code-verification.js.map +1 -0
  7. package/dist/components/redirect-context.d.ts +11 -0
  8. package/dist/components/redirect-context.d.ts.map +1 -0
  9. package/dist/components/redirect-context.js +22 -0
  10. package/dist/components/redirect-context.js.map +1 -0
  11. package/dist/components/redirect-target.d.ts.map +1 -1
  12. package/dist/components/redirect-target.js +13 -12
  13. package/dist/components/redirect-target.js.map +1 -1
  14. package/dist/components/workspace-switcher.stories.d.ts +1 -1
  15. package/dist/continuation.d.ts +4 -0
  16. package/dist/continuation.d.ts.map +1 -0
  17. package/dist/continuation.js +5 -0
  18. package/dist/continuation.js.map +1 -0
  19. package/dist/hooks/api/auth-mapper.d.ts +21 -0
  20. package/dist/hooks/api/auth-mapper.d.ts.map +1 -0
  21. package/dist/hooks/api/auth-mapper.js +25 -0
  22. package/dist/hooks/api/auth-mapper.js.map +1 -0
  23. package/dist/hooks/api/login-auth.d.ts +3 -25
  24. package/dist/hooks/api/login-auth.d.ts.map +1 -1
  25. package/dist/hooks/api/login-auth.js +9 -30
  26. package/dist/hooks/api/login-auth.js.map +1 -1
  27. package/dist/hooks/api/logout-auth.d.ts.map +1 -1
  28. package/dist/hooks/api/logout-auth.js +4 -14
  29. package/dist/hooks/api/logout-auth.js.map +1 -1
  30. package/dist/hooks/api/password-reset-auth.d.ts +3 -25
  31. package/dist/hooks/api/password-reset-auth.d.ts.map +1 -1
  32. package/dist/hooks/api/password-reset-auth.js +9 -25
  33. package/dist/hooks/api/password-reset-auth.js.map +1 -1
  34. package/dist/hooks/api/signup-auth.d.ts +1 -25
  35. package/dist/hooks/api/signup-auth.d.ts.map +1 -1
  36. package/dist/hooks/api/signup-auth.js +5 -2
  37. package/dist/hooks/api/signup-auth.js.map +1 -1
  38. package/dist/hooks/api/verify-email-auth.d.ts +1 -12
  39. package/dist/hooks/api/verify-email-auth.d.ts.map +1 -1
  40. package/dist/hooks/api/verify-email-auth.js +10 -26
  41. package/dist/hooks/api/verify-email-auth.js.map +1 -1
  42. package/dist/hooks/api/workspace-auth.d.ts +3 -17
  43. package/dist/hooks/api/workspace-auth.d.ts.map +1 -1
  44. package/dist/hooks/api/workspace-auth.js +5 -2
  45. package/dist/hooks/api/workspace-auth.js.map +1 -1
  46. package/dist/hooks/api/workspace-mapper.d.ts +8 -0
  47. package/dist/hooks/api/workspace-mapper.d.ts.map +1 -0
  48. package/dist/hooks/api/workspace-mapper.js +9 -0
  49. package/dist/hooks/api/workspace-mapper.js.map +1 -0
  50. package/dist/index.d.ts +0 -1
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +0 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/pages/invitation-context.d.ts +9 -19
  55. package/dist/pages/invitation-context.d.ts.map +1 -1
  56. package/dist/pages/invitation-context.js +7 -46
  57. package/dist/pages/invitation-context.js.map +1 -1
  58. package/dist/pages/login-page.js +2 -2
  59. package/dist/pages/login-page.js.map +1 -1
  60. package/dist/pages/logout-page.js +1 -1
  61. package/dist/pages/logout-page.js.map +1 -1
  62. package/dist/pages/password-reset-page.d.ts.map +1 -1
  63. package/dist/pages/password-reset-page.js +1 -4
  64. package/dist/pages/password-reset-page.js.map +1 -1
  65. package/dist/pages/signup-page.d.ts.map +1 -1
  66. package/dist/pages/signup-page.js +58 -152
  67. package/dist/pages/signup-page.js.map +1 -1
  68. package/dist/state/auth.d.ts +1 -1
  69. package/dist/state/auth.d.ts.map +1 -1
  70. package/dist/state/auth.js +1 -1
  71. package/dist/state/auth.js.map +1 -1
  72. package/dist/tsconfig.test.tsbuildinfo +1 -1
  73. package/package.json +10 -39
  74. package/src/components/auth-provider.test.tsx +173 -3
  75. package/src/components/email-code-verification.test.tsx +159 -0
  76. package/src/components/email-code-verification.tsx +177 -0
  77. package/src/components/redirect-context.test.ts +51 -0
  78. package/src/components/redirect-context.ts +26 -0
  79. package/src/components/redirect-target.test.ts +8 -0
  80. package/src/components/redirect-target.ts +13 -12
  81. package/src/continuation.test.ts +15 -0
  82. package/src/continuation.ts +6 -0
  83. package/src/hooks/api/auth-mapper.test.ts +77 -0
  84. package/src/hooks/api/auth-mapper.ts +39 -0
  85. package/src/hooks/api/login-auth.test.ts +1 -1
  86. package/src/hooks/api/login-auth.ts +12 -31
  87. package/src/hooks/api/logout-auth.ts +4 -10
  88. package/src/hooks/api/password-reset-auth.test.ts +1 -1
  89. package/src/hooks/api/password-reset-auth.ts +19 -32
  90. package/src/hooks/api/signup-auth.ts +8 -3
  91. package/src/hooks/api/verify-email-auth.ts +21 -34
  92. package/src/hooks/api/workspace-auth.ts +8 -3
  93. package/src/hooks/api/workspace-mapper.test.ts +21 -0
  94. package/src/hooks/api/workspace-mapper.ts +11 -0
  95. package/src/index.ts +0 -1
  96. package/src/pages/invitation-context.ts +10 -51
  97. package/src/pages/login-page.test.tsx +1 -1
  98. package/src/pages/login-page.tsx +2 -2
  99. package/src/pages/logout-page.tsx +1 -1
  100. package/src/pages/password-reset-page.tsx +1 -4
  101. package/src/pages/signup-page.test.tsx +1 -1
  102. package/src/pages/signup-page.tsx +41 -137
  103. package/src/state/auth.ts +1 -0
  104. package/src/state/last-workspace.test.ts +16 -0
  105. package/tsconfig.build.tsbuildinfo +1 -1
  106. package/dist/components/auth-shell.d.ts +0 -11
  107. package/dist/components/auth-shell.d.ts.map +0 -1
  108. package/dist/components/auth-shell.js +0 -63
  109. package/dist/components/auth-shell.js.map +0 -1
  110. package/dist/pages/email-verification-resend-model.d.ts +0 -6
  111. package/dist/pages/email-verification-resend-model.d.ts.map +0 -1
  112. package/dist/pages/email-verification-resend-model.js +0 -11
  113. package/dist/pages/email-verification-resend-model.js.map +0 -1
  114. package/src/components/auth-shell.tsx +0 -53
  115. package/src/pages/email-verification-resend-model.test.ts +0 -42
  116. package/src/pages/email-verification-resend-model.ts +0 -15
@@ -1 +1 @@
1
- {"version":3,"file":"password-reset-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/password-reset-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAE3B,2BAA2B,EAC5B,MAAM,uBAAuB,CAAC;AAQ/B,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,iBAE3E;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B;;;;;;;;;;;GAK3E;AAED,wBAAgB,2BAA2B;;YAE1C;AAED,wBAAgB,2BAA2B;;;;;;;;;;;;;;YAuB1C"}
1
+ {"version":3,"file":"password-reset-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/password-reset-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAEjC,MAAM,uBAAuB,CAAC;AAM/B,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,iBAE3E;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,yEAU3E;AAED,wBAAgB,2BAA2B;;YAE1C;AAED,wBAAgB,2BAA2B;;;YAO1C"}
@@ -1,9 +1,8 @@
1
- import { apiRequest } from '@shipfox/client-api';
2
- import { useMutation, useQueryClient } from '@tanstack/react-query';
3
- import { useSetAtom } from 'jotai';
4
- import { authStateAtom, toAuthenticatedState } from '#state/auth.js';
5
- import { authRefreshQueryKey } from './refresh-auth.js';
6
- import { listUserWorkspaces, userWorkspacesQueryKey } from './workspace-auth.js';
1
+ import { passwordResetConfirmResponseSchema } from '@shipfox/api-auth-dto';
2
+ import { apiRequest, checkedApiRequest } from '@shipfox/client-api';
3
+ import { useMutation } from '@tanstack/react-query';
4
+ import { useAuthTransition } from '#state/auth.js';
5
+ import { toAuthenticatedSession } from './auth-mapper.js';
7
6
  export async function requestPasswordReset(body) {
8
7
  await apiRequest('/auth/password-reset', {
9
8
  method: 'POST',
@@ -11,10 +10,11 @@ export async function requestPasswordReset(body) {
11
10
  });
12
11
  }
13
12
  export async function confirmPasswordReset(body) {
14
- return await apiRequest('/auth/password-reset/confirm', {
13
+ const response = await checkedApiRequest(passwordResetConfirmResponseSchema, '/auth/password-reset/confirm', {
15
14
  method: 'POST',
16
15
  body
17
16
  });
17
+ return toAuthenticatedSession(response);
18
18
  }
19
19
  export function useRequestPasswordResetAuth() {
20
20
  return useMutation({
@@ -22,26 +22,10 @@ export function useRequestPasswordResetAuth() {
22
22
  });
23
23
  }
24
24
  export function useConfirmPasswordResetAuth() {
25
- const queryClient = useQueryClient();
26
- const setState = useSetAtom(authStateAtom);
25
+ const { enterAuthenticated } = useAuthTransition();
27
26
  return useMutation({
28
27
  mutationFn: confirmPasswordReset,
29
- onSuccess: async (result)=>{
30
- setState(toAuthenticatedState(result));
31
- queryClient.setQueryData(authRefreshQueryKey, result);
32
- try {
33
- const workspaces = await queryClient.fetchQuery({
34
- queryKey: userWorkspacesQueryKey,
35
- queryFn: ()=>listUserWorkspaces(result.token),
36
- retry: false,
37
- staleTime: 0
38
- });
39
- setState(toAuthenticatedState(result, workspaces.memberships));
40
- queryClient.setQueryData(userWorkspacesQueryKey, workspaces);
41
- } catch {
42
- // The user is authenticated even if workspace hydration fails.
43
- }
44
- }
28
+ onSuccess: enterAuthenticated
45
29
  });
46
30
  }
47
31
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/hooks/api/password-reset-auth.ts"],"sourcesContent":["import type {\n PasswordResetConfirmBodyDto,\n PasswordResetConfirmResponseDto,\n PasswordResetRequestBodyDto,\n} from '@shipfox/api-auth-dto';\nimport {apiRequest} from '@shipfox/client-api';\nimport {useMutation, useQueryClient} from '@tanstack/react-query';\nimport {useSetAtom} from 'jotai';\nimport {authStateAtom, toAuthenticatedState} from '#state/auth.js';\nimport {authRefreshQueryKey} from './refresh-auth.js';\nimport {listUserWorkspaces, userWorkspacesQueryKey} from './workspace-auth.js';\n\nexport async function requestPasswordReset(body: PasswordResetRequestBodyDto) {\n await apiRequest<void>('/auth/password-reset', {method: 'POST', body});\n}\n\nexport async function confirmPasswordReset(body: PasswordResetConfirmBodyDto) {\n return await apiRequest<PasswordResetConfirmResponseDto>('/auth/password-reset/confirm', {\n method: 'POST',\n body,\n });\n}\n\nexport function useRequestPasswordResetAuth() {\n return useMutation({mutationFn: requestPasswordReset});\n}\n\nexport function useConfirmPasswordResetAuth() {\n const queryClient = useQueryClient();\n const setState = useSetAtom(authStateAtom);\n\n return useMutation({\n mutationFn: confirmPasswordReset,\n onSuccess: async (result) => {\n setState(toAuthenticatedState(result));\n queryClient.setQueryData(authRefreshQueryKey, result);\n try {\n const workspaces = await queryClient.fetchQuery({\n queryKey: userWorkspacesQueryKey,\n queryFn: () => listUserWorkspaces(result.token),\n retry: false,\n staleTime: 0,\n });\n setState(toAuthenticatedState(result, workspaces.memberships));\n queryClient.setQueryData(userWorkspacesQueryKey, workspaces);\n } catch {\n // The user is authenticated even if workspace hydration fails.\n }\n },\n });\n}\n"],"names":["apiRequest","useMutation","useQueryClient","useSetAtom","authStateAtom","toAuthenticatedState","authRefreshQueryKey","listUserWorkspaces","userWorkspacesQueryKey","requestPasswordReset","body","method","confirmPasswordReset","useRequestPasswordResetAuth","mutationFn","useConfirmPasswordResetAuth","queryClient","setState","onSuccess","result","setQueryData","workspaces","fetchQuery","queryKey","queryFn","token","retry","staleTime","memberships"],"mappings":"AAKA,SAAQA,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,WAAW,EAAEC,cAAc,QAAO,wBAAwB;AAClE,SAAQC,UAAU,QAAO,QAAQ;AACjC,SAAQC,aAAa,EAAEC,oBAAoB,QAAO,iBAAiB;AACnE,SAAQC,mBAAmB,QAAO,oBAAoB;AACtD,SAAQC,kBAAkB,EAAEC,sBAAsB,QAAO,sBAAsB;AAE/E,OAAO,eAAeC,qBAAqBC,IAAiC;IAC1E,MAAMV,WAAiB,wBAAwB;QAACW,QAAQ;QAAQD;IAAI;AACtE;AAEA,OAAO,eAAeE,qBAAqBF,IAAiC;IAC1E,OAAO,MAAMV,WAA4C,gCAAgC;QACvFW,QAAQ;QACRD;IACF;AACF;AAEA,OAAO,SAASG;IACd,OAAOZ,YAAY;QAACa,YAAYL;IAAoB;AACtD;AAEA,OAAO,SAASM;IACd,MAAMC,cAAcd;IACpB,MAAMe,WAAWd,WAAWC;IAE5B,OAAOH,YAAY;QACjBa,YAAYF;QACZM,WAAW,OAAOC;YAChBF,SAASZ,qBAAqBc;YAC9BH,YAAYI,YAAY,CAACd,qBAAqBa;YAC9C,IAAI;gBACF,MAAME,aAAa,MAAML,YAAYM,UAAU,CAAC;oBAC9CC,UAAUf;oBACVgB,SAAS,IAAMjB,mBAAmBY,OAAOM,KAAK;oBAC9CC,OAAO;oBACPC,WAAW;gBACb;gBACAV,SAASZ,qBAAqBc,QAAQE,WAAWO,WAAW;gBAC5DZ,YAAYI,YAAY,CAACZ,wBAAwBa;YACnD,EAAE,OAAM;YACN,+DAA+D;YACjE;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/hooks/api/password-reset-auth.ts"],"sourcesContent":["import {\n type PasswordResetConfirmBodyDto,\n type PasswordResetRequestBodyDto,\n passwordResetConfirmResponseSchema,\n} from '@shipfox/api-auth-dto';\nimport {apiRequest, checkedApiRequest} from '@shipfox/client-api';\nimport {useMutation} from '@tanstack/react-query';\nimport {useAuthTransition} from '#state/auth.js';\nimport {toAuthenticatedSession} from './auth-mapper.js';\n\nexport async function requestPasswordReset(body: PasswordResetRequestBodyDto) {\n await apiRequest<void>('/auth/password-reset', {method: 'POST', body});\n}\n\nexport async function confirmPasswordReset(body: PasswordResetConfirmBodyDto) {\n const response = await checkedApiRequest(\n passwordResetConfirmResponseSchema,\n '/auth/password-reset/confirm',\n {\n method: 'POST',\n body,\n },\n );\n return toAuthenticatedSession(response);\n}\n\nexport function useRequestPasswordResetAuth() {\n return useMutation({mutationFn: requestPasswordReset});\n}\n\nexport function useConfirmPasswordResetAuth() {\n const {enterAuthenticated} = useAuthTransition();\n\n return useMutation({\n mutationFn: confirmPasswordReset,\n onSuccess: enterAuthenticated,\n });\n}\n"],"names":["passwordResetConfirmResponseSchema","apiRequest","checkedApiRequest","useMutation","useAuthTransition","toAuthenticatedSession","requestPasswordReset","body","method","confirmPasswordReset","response","useRequestPasswordResetAuth","mutationFn","useConfirmPasswordResetAuth","enterAuthenticated","onSuccess"],"mappings":"AAAA,SAGEA,kCAAkC,QAC7B,wBAAwB;AAC/B,SAAQC,UAAU,EAAEC,iBAAiB,QAAO,sBAAsB;AAClE,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,iBAAiB,QAAO,iBAAiB;AACjD,SAAQC,sBAAsB,QAAO,mBAAmB;AAExD,OAAO,eAAeC,qBAAqBC,IAAiC;IAC1E,MAAMN,WAAiB,wBAAwB;QAACO,QAAQ;QAAQD;IAAI;AACtE;AAEA,OAAO,eAAeE,qBAAqBF,IAAiC;IAC1E,MAAMG,WAAW,MAAMR,kBACrBF,oCACA,gCACA;QACEQ,QAAQ;QACRD;IACF;IAEF,OAAOF,uBAAuBK;AAChC;AAEA,OAAO,SAASC;IACd,OAAOR,YAAY;QAACS,YAAYN;IAAoB;AACtD;AAEA,OAAO,SAASO;IACd,MAAM,EAACC,kBAAkB,EAAC,GAAGV;IAE7B,OAAOD,YAAY;QACjBS,YAAYH;QACZM,WAAWD;IACb;AACF"}
@@ -1,28 +1,4 @@
1
- export declare function useSignupAuth(): import("@tanstack/react-query").UseMutationResult<{
2
- user: {
3
- id: string;
4
- email: string;
5
- name: string | null;
6
- email_verified_at: string | null;
7
- status: "active" | "suspended" | "deleted";
8
- created_at: string;
9
- updated_at: string;
10
- };
11
- email_challenge?: {
12
- id: string;
13
- next_resend_available_at: string;
14
- } | undefined;
15
- token?: string | undefined;
16
- membership?: {
17
- id: string;
18
- user_id: string;
19
- workspace_id: string;
20
- } | null | undefined;
21
- accept_error?: {
22
- code: string;
23
- message: string;
24
- } | undefined;
25
- }, Error, {
1
+ export declare function useSignupAuth(): import("@tanstack/react-query").UseMutationResult<import("./auth-mapper.js").SignupResult, Error, {
26
2
  email: string;
27
3
  password: string;
28
4
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"signup-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/signup-auth.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAE5B"}
1
+ {"version":3,"file":"signup-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/signup-auth.ts"],"names":[],"mappings":"AAaA,wBAAgB,aAAa;;;;;YAE5B"}
@@ -1,10 +1,13 @@
1
- import { apiRequest } from '@shipfox/client-api';
1
+ import { signupResponseSchema } from '@shipfox/api-auth-dto';
2
+ import { checkedApiRequest } from '@shipfox/client-api';
2
3
  import { useMutation } from '@tanstack/react-query';
4
+ import { toSignupResult } from './auth-mapper.js';
3
5
  async function signupAuth(body) {
4
- return await apiRequest('/auth/signup', {
6
+ const response = await checkedApiRequest(signupResponseSchema, '/auth/signup', {
5
7
  method: 'POST',
6
8
  body
7
9
  });
10
+ return toSignupResult(response);
8
11
  }
9
12
  export function useSignupAuth() {
10
13
  return useMutation({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/hooks/api/signup-auth.ts"],"sourcesContent":["import type {SignupBodyDto, SignupResponseDto} from '@shipfox/api-auth-dto';\nimport {apiRequest} from '@shipfox/client-api';\nimport {useMutation} from '@tanstack/react-query';\n\nasync function signupAuth(body: SignupBodyDto) {\n return await apiRequest<SignupResponseDto>('/auth/signup', {method: 'POST', body});\n}\n\nexport function useSignupAuth() {\n return useMutation({mutationFn: signupAuth});\n}\n"],"names":["apiRequest","useMutation","signupAuth","body","method","useSignupAuth","mutationFn"],"mappings":"AACA,SAAQA,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,WAAW,QAAO,wBAAwB;AAElD,eAAeC,WAAWC,IAAmB;IAC3C,OAAO,MAAMH,WAA8B,gBAAgB;QAACI,QAAQ;QAAQD;IAAI;AAClF;AAEA,OAAO,SAASE;IACd,OAAOJ,YAAY;QAACK,YAAYJ;IAAU;AAC5C"}
1
+ {"version":3,"sources":["../../../src/hooks/api/signup-auth.ts"],"sourcesContent":["import {type SignupBodyDto, signupResponseSchema} from '@shipfox/api-auth-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {useMutation} from '@tanstack/react-query';\nimport {toSignupResult} from './auth-mapper.js';\n\nasync function signupAuth(body: SignupBodyDto) {\n const response = await checkedApiRequest(signupResponseSchema, '/auth/signup', {\n method: 'POST',\n body,\n });\n return toSignupResult(response);\n}\n\nexport function useSignupAuth() {\n return useMutation({mutationFn: signupAuth});\n}\n"],"names":["signupResponseSchema","checkedApiRequest","useMutation","toSignupResult","signupAuth","body","response","method","useSignupAuth","mutationFn"],"mappings":"AAAA,SAA4BA,oBAAoB,QAAO,wBAAwB;AAC/E,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,cAAc,QAAO,mBAAmB;AAEhD,eAAeC,WAAWC,IAAmB;IAC3C,MAAMC,WAAW,MAAML,kBAAkBD,sBAAsB,gBAAgB;QAC7EO,QAAQ;QACRF;IACF;IACA,OAAOF,eAAeG;AACxB;AAEA,OAAO,SAASE;IACd,OAAON,YAAY;QAACO,YAAYL;IAAU;AAC5C"}
@@ -4,18 +4,7 @@ export declare function useResendEmailVerificationAuth(): import("@tanstack/reac
4
4
  email: string;
5
5
  challenge_id: string;
6
6
  }, unknown>;
7
- export declare function useVerifyEmailAuth(): import("@tanstack/react-query").UseMutationResult<{
8
- token: string;
9
- user: {
10
- id: string;
11
- email: string;
12
- name: string | null;
13
- email_verified_at: string | null;
14
- status: "active" | "suspended" | "deleted";
15
- created_at: string;
16
- updated_at: string;
17
- };
18
- }, Error, {
7
+ export declare function useVerifyEmailAuth(): import("@tanstack/react-query").UseMutationResult<import("@shipfox/client-shell/runtime").AuthenticatedSession, Error, {
19
8
  email: string;
20
9
  challenge_id: string;
21
10
  code: string;
@@ -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;;;;;YAE7C;AAED,wBAAgB,kBAAkB;;;;;;;;;;;;;;;YAuBjC"}
1
+ {"version":3,"file":"verify-email-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/verify-email-auth.ts"],"names":[],"mappings":"AA8BA,wBAAgB,8BAA8B;;;;;YAE7C;AAED,wBAAgB,kBAAkB;;;;YAOjC"}
@@ -1,17 +1,17 @@
1
- import { apiRequest } from '@shipfox/client-api';
2
- import { useMutation, useQueryClient } from '@tanstack/react-query';
3
- import { useSetAtom } from 'jotai';
4
- import { authStateAtom, toAuthenticatedState } from '#state/auth.js';
5
- import { authRefreshQueryKey } from './refresh-auth.js';
6
- import { listUserWorkspaces, userWorkspacesQueryKey } from './workspace-auth.js';
1
+ import { verifyEmailConfirmResponseSchema, verifyEmailResendResponseSchema } from '@shipfox/api-auth-dto';
2
+ import { checkedApiRequest } from '@shipfox/client-api';
3
+ import { useMutation } from '@tanstack/react-query';
4
+ import { useAuthTransition } from '#state/auth.js';
5
+ import { toAuthenticatedSession } from './auth-mapper.js';
7
6
  async function verifyEmailAuth(body) {
8
- return await apiRequest('/auth/verify-email/confirm', {
7
+ const response = await checkedApiRequest(verifyEmailConfirmResponseSchema, '/auth/verify-email/confirm', {
9
8
  method: 'POST',
10
9
  body
11
10
  });
11
+ return toAuthenticatedSession(response);
12
12
  }
13
13
  async function resendEmailVerificationAuth(body) {
14
- return await apiRequest('/auth/verify-email/resend', {
14
+ return await checkedApiRequest(verifyEmailResendResponseSchema, '/auth/verify-email/resend', {
15
15
  method: 'POST',
16
16
  body
17
17
  });
@@ -22,26 +22,10 @@ export function useResendEmailVerificationAuth() {
22
22
  });
23
23
  }
24
24
  export function useVerifyEmailAuth() {
25
- const queryClient = useQueryClient();
26
- const setState = useSetAtom(authStateAtom);
25
+ const { enterAuthenticated } = useAuthTransition();
27
26
  return useMutation({
28
27
  mutationFn: verifyEmailAuth,
29
- onSuccess: async (result)=>{
30
- setState(toAuthenticatedState(result));
31
- queryClient.setQueryData(authRefreshQueryKey, result);
32
- try {
33
- const workspaces = await queryClient.fetchQuery({
34
- queryKey: userWorkspacesQueryKey,
35
- queryFn: ()=>listUserWorkspaces(result.token),
36
- retry: false,
37
- staleTime: 0
38
- });
39
- setState(toAuthenticatedState(result, workspaces.memberships));
40
- queryClient.setQueryData(userWorkspacesQueryKey, workspaces);
41
- } catch {
42
- // The user is authenticated even if workspace hydration fails.
43
- }
44
- }
28
+ onSuccess: enterAuthenticated
45
29
  });
46
30
  }
47
31
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/hooks/api/verify-email-auth.ts"],"sourcesContent":["import type {\n VerifyEmailConfirmBodyDto,\n VerifyEmailConfirmResponseDto,\n VerifyEmailResendBodyDto,\n VerifyEmailResendResponseDto,\n} from '@shipfox/api-auth-dto';\nimport {apiRequest} from '@shipfox/client-api';\nimport {useMutation, useQueryClient} from '@tanstack/react-query';\nimport {useSetAtom} from 'jotai';\nimport {authStateAtom, toAuthenticatedState} from '#state/auth.js';\nimport {authRefreshQueryKey} from './refresh-auth.js';\nimport {listUserWorkspaces, userWorkspacesQueryKey} from './workspace-auth.js';\n\nasync function verifyEmailAuth(body: VerifyEmailConfirmBodyDto) {\n return await apiRequest<VerifyEmailConfirmResponseDto>('/auth/verify-email/confirm', {\n method: 'POST',\n body,\n });\n}\n\nasync function resendEmailVerificationAuth(body: VerifyEmailResendBodyDto) {\n return await apiRequest<VerifyEmailResendResponseDto>('/auth/verify-email/resend', {\n method: 'POST',\n body,\n });\n}\n\nexport function useResendEmailVerificationAuth() {\n return useMutation({mutationFn: resendEmailVerificationAuth});\n}\n\nexport function useVerifyEmailAuth() {\n const queryClient = useQueryClient();\n const setState = useSetAtom(authStateAtom);\n\n return useMutation({\n mutationFn: verifyEmailAuth,\n onSuccess: async (result) => {\n setState(toAuthenticatedState(result));\n queryClient.setQueryData(authRefreshQueryKey, result);\n try {\n const workspaces = await queryClient.fetchQuery({\n queryKey: userWorkspacesQueryKey,\n queryFn: () => listUserWorkspaces(result.token),\n retry: false,\n staleTime: 0,\n });\n setState(toAuthenticatedState(result, workspaces.memberships));\n queryClient.setQueryData(userWorkspacesQueryKey, workspaces);\n } catch {\n // The user is authenticated even if workspace hydration fails.\n }\n },\n });\n}\n"],"names":["apiRequest","useMutation","useQueryClient","useSetAtom","authStateAtom","toAuthenticatedState","authRefreshQueryKey","listUserWorkspaces","userWorkspacesQueryKey","verifyEmailAuth","body","method","resendEmailVerificationAuth","useResendEmailVerificationAuth","mutationFn","useVerifyEmailAuth","queryClient","setState","onSuccess","result","setQueryData","workspaces","fetchQuery","queryKey","queryFn","token","retry","staleTime","memberships"],"mappings":"AAMA,SAAQA,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,WAAW,EAAEC,cAAc,QAAO,wBAAwB;AAClE,SAAQC,UAAU,QAAO,QAAQ;AACjC,SAAQC,aAAa,EAAEC,oBAAoB,QAAO,iBAAiB;AACnE,SAAQC,mBAAmB,QAAO,oBAAoB;AACtD,SAAQC,kBAAkB,EAAEC,sBAAsB,QAAO,sBAAsB;AAE/E,eAAeC,gBAAgBC,IAA+B;IAC5D,OAAO,MAAMV,WAA0C,8BAA8B;QACnFW,QAAQ;QACRD;IACF;AACF;AAEA,eAAeE,4BAA4BF,IAA8B;IACvE,OAAO,MAAMV,WAAyC,6BAA6B;QACjFW,QAAQ;QACRD;IACF;AACF;AAEA,OAAO,SAASG;IACd,OAAOZ,YAAY;QAACa,YAAYF;IAA2B;AAC7D;AAEA,OAAO,SAASG;IACd,MAAMC,cAAcd;IACpB,MAAMe,WAAWd,WAAWC;IAE5B,OAAOH,YAAY;QACjBa,YAAYL;QACZS,WAAW,OAAOC;YAChBF,SAASZ,qBAAqBc;YAC9BH,YAAYI,YAAY,CAACd,qBAAqBa;YAC9C,IAAI;gBACF,MAAME,aAAa,MAAML,YAAYM,UAAU,CAAC;oBAC9CC,UAAUf;oBACVgB,SAAS,IAAMjB,mBAAmBY,OAAOM,KAAK;oBAC9CC,OAAO;oBACPC,WAAW;gBACb;gBACAV,SAASZ,qBAAqBc,QAAQE,WAAWO,WAAW;gBAC5DZ,YAAYI,YAAY,CAACZ,wBAAwBa;YACnD,EAAE,OAAM;YACN,+DAA+D;YACjE;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/hooks/api/verify-email-auth.ts"],"sourcesContent":["import {\n type VerifyEmailConfirmBodyDto,\n type VerifyEmailResendBodyDto,\n verifyEmailConfirmResponseSchema,\n verifyEmailResendResponseSchema,\n} from '@shipfox/api-auth-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {useMutation} from '@tanstack/react-query';\nimport {useAuthTransition} from '#state/auth.js';\nimport {toAuthenticatedSession} from './auth-mapper.js';\n\nasync function verifyEmailAuth(body: VerifyEmailConfirmBodyDto) {\n const response = await checkedApiRequest(\n verifyEmailConfirmResponseSchema,\n '/auth/verify-email/confirm',\n {\n method: 'POST',\n body,\n },\n );\n return toAuthenticatedSession(response);\n}\n\nasync function resendEmailVerificationAuth(body: VerifyEmailResendBodyDto) {\n return await checkedApiRequest(verifyEmailResendResponseSchema, '/auth/verify-email/resend', {\n method: 'POST',\n body,\n });\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: enterAuthenticated,\n });\n}\n"],"names":["verifyEmailConfirmResponseSchema","verifyEmailResendResponseSchema","checkedApiRequest","useMutation","useAuthTransition","toAuthenticatedSession","verifyEmailAuth","body","response","method","resendEmailVerificationAuth","useResendEmailVerificationAuth","mutationFn","useVerifyEmailAuth","enterAuthenticated","onSuccess"],"mappings":"AAAA,SAGEA,gCAAgC,EAChCC,+BAA+B,QAC1B,wBAAwB;AAC/B,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,iBAAiB,QAAO,iBAAiB;AACjD,SAAQC,sBAAsB,QAAO,mBAAmB;AAExD,eAAeC,gBAAgBC,IAA+B;IAC5D,MAAMC,WAAW,MAAMN,kBACrBF,kCACA,8BACA;QACES,QAAQ;QACRF;IACF;IAEF,OAAOF,uBAAuBG;AAChC;AAEA,eAAeE,4BAA4BH,IAA8B;IACvE,OAAO,MAAML,kBAAkBD,iCAAiC,6BAA6B;QAC3FQ,QAAQ;QACRF;IACF;AACF;AAEA,OAAO,SAASI;IACd,OAAOR,YAAY;QAACS,YAAYF;IAA2B;AAC7D;AAEA,OAAO,SAASG;IACd,MAAM,EAACC,kBAAkB,EAAC,GAAGV;IAE7B,OAAOD,YAAY;QACjBS,YAAYN;QACZS,WAAWD;IACb;AACF"}
@@ -1,22 +1,8 @@
1
- import type { CreateWorkspaceBodyDto } from '@shipfox/api-workspaces-dto';
1
+ import { type CreateWorkspaceBodyDto } from '@shipfox/api-workspaces-dto';
2
2
  import { listUserWorkspaces, userWorkspacesQueryKey } from '@shipfox/client-shell/runtime';
3
- export declare function createWorkspace(body: CreateWorkspaceBodyDto): Promise<{
4
- id: string;
5
- name: string;
6
- status: "active" | "suspended" | "deleted";
7
- settings: Record<string, unknown>;
8
- created_at: string;
9
- updated_at: string;
10
- }>;
3
+ export declare function createWorkspace(body: CreateWorkspaceBodyDto): Promise<import("./workspace-mapper.js").Workspace>;
11
4
  export { listUserWorkspaces, userWorkspacesQueryKey };
12
- export declare function useCreateWorkspaceAuth(): import("@tanstack/react-query").UseMutationResult<{
13
- id: string;
14
- name: string;
15
- status: "active" | "suspended" | "deleted";
16
- settings: Record<string, unknown>;
17
- created_at: string;
18
- updated_at: string;
19
- }, Error, {
5
+ export declare function useCreateWorkspaceAuth(): import("@tanstack/react-query").UseMutationResult<import("./workspace-mapper.js").Workspace, Error, {
20
6
  name: string;
21
7
  }, unknown>;
22
8
  //# 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":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAuB,MAAM,6BAA6B,CAAC;AAE9F,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAC,MAAM,+BAA+B,CAAC;AAIzF,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB;;;;;;;GAEjE;AAED,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAC,CAAC;AAEpD,wBAAgB,sBAAsB;;;;;;;;;YAYrC"}
1
+ {"version":3,"file":"workspace-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/workspace-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,sBAAsB,EAA0B,MAAM,6BAA6B,CAAC;AAEjG,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAC,MAAM,+BAA+B,CAAC;AAKzF,wBAAsB,eAAe,CAAC,IAAI,EAAE,sBAAsB,sDAMjE;AAED,OAAO,EAAC,kBAAkB,EAAE,sBAAsB,EAAC,CAAC;AAEpD,wBAAgB,sBAAsB;;YAYrC"}
@@ -1,12 +1,15 @@
1
- import { apiRequest } from '@shipfox/client-api';
1
+ import { workspaceResponseSchema } from '@shipfox/api-workspaces-dto';
2
+ import { checkedApiRequest } from '@shipfox/client-api';
2
3
  import { listUserWorkspaces, userWorkspacesQueryKey } from '@shipfox/client-shell/runtime';
3
4
  import { useMutation } from '@tanstack/react-query';
4
5
  import { useRefreshAuth } from './refresh-auth.js';
6
+ import { toWorkspace } from './workspace-mapper.js';
5
7
  export async function createWorkspace(body) {
6
- return await apiRequest('/workspaces', {
8
+ const response = await checkedApiRequest(workspaceResponseSchema, '/workspaces', {
7
9
  method: 'POST',
8
10
  body
9
11
  });
12
+ return toWorkspace(response);
10
13
  }
11
14
  export { listUserWorkspaces, userWorkspacesQueryKey };
12
15
  export function useCreateWorkspaceAuth() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/hooks/api/workspace-auth.ts"],"sourcesContent":["import type {CreateWorkspaceBodyDto, WorkspaceResponseDto} from '@shipfox/api-workspaces-dto';\nimport {apiRequest} from '@shipfox/client-api';\nimport {listUserWorkspaces, userWorkspacesQueryKey} from '@shipfox/client-shell/runtime';\nimport {useMutation} from '@tanstack/react-query';\nimport {useRefreshAuth} from './refresh-auth.js';\n\nexport async function createWorkspace(body: CreateWorkspaceBodyDto) {\n return await apiRequest<WorkspaceResponseDto>('/workspaces', {method: 'POST', body});\n}\n\nexport {listUserWorkspaces, userWorkspacesQueryKey};\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":["apiRequest","listUserWorkspaces","userWorkspacesQueryKey","useMutation","useRefreshAuth","createWorkspace","body","method","useCreateWorkspaceAuth","refreshAuth","mutationFn","onSuccess"],"mappings":"AACA,SAAQA,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,kBAAkB,EAAEC,sBAAsB,QAAO,gCAAgC;AACzF,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,cAAc,QAAO,oBAAoB;AAEjD,OAAO,eAAeC,gBAAgBC,IAA4B;IAChE,OAAO,MAAMN,WAAiC,eAAe;QAACO,QAAQ;QAAQD;IAAI;AACpF;AAEA,SAAQL,kBAAkB,EAAEC,sBAAsB,GAAE;AAEpD,OAAO,SAASM;IACd,MAAMC,cAAcL;IAEpB,OAAOD,YAAY;QACjBO,YAAYL;QACZM,WAAW;YACT,sEAAsE;YACtE,oEAAoE;YACpE,kEAAkE;YAClE,MAAMF;QACR;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/hooks/api/workspace-auth.ts"],"sourcesContent":["import {type CreateWorkspaceBodyDto, workspaceResponseSchema} from '@shipfox/api-workspaces-dto';\nimport {checkedApiRequest} from '@shipfox/client-api';\nimport {listUserWorkspaces, userWorkspacesQueryKey} from '@shipfox/client-shell/runtime';\nimport {useMutation} from '@tanstack/react-query';\nimport {useRefreshAuth} from './refresh-auth.js';\nimport {toWorkspace} from './workspace-mapper.js';\n\nexport async function createWorkspace(body: CreateWorkspaceBodyDto) {\n const response = await checkedApiRequest(workspaceResponseSchema, '/workspaces', {\n method: 'POST',\n body,\n });\n return toWorkspace(response);\n}\n\nexport {listUserWorkspaces, userWorkspacesQueryKey};\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","useMutation","useRefreshAuth","toWorkspace","createWorkspace","body","response","method","useCreateWorkspaceAuth","refreshAuth","mutationFn","onSuccess"],"mappings":"AAAA,SAAqCA,uBAAuB,QAAO,8BAA8B;AACjG,SAAQC,iBAAiB,QAAO,sBAAsB;AACtD,SAAQC,kBAAkB,EAAEC,sBAAsB,QAAO,gCAAgC;AACzF,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAAQC,WAAW,QAAO,wBAAwB;AAElD,OAAO,eAAeC,gBAAgBC,IAA4B;IAChE,MAAMC,WAAW,MAAMR,kBAAkBD,yBAAyB,eAAe;QAC/EU,QAAQ;QACRF;IACF;IACA,OAAOF,YAAYG;AACrB;AAEA,SAAQP,kBAAkB,EAAEC,sBAAsB,GAAE;AAEpD,OAAO,SAASQ;IACd,MAAMC,cAAcP;IAEpB,OAAOD,YAAY;QACjBS,YAAYN;QACZO,WAAW;YACT,sEAAsE;YACtE,oEAAoE;YACpE,kEAAkE;YAClE,MAAMF;QACR;IACF;AACF"}
@@ -0,0 +1,8 @@
1
+ import type { WorkspaceResponseDto } from '@shipfox/api-workspaces-dto';
2
+ export interface Workspace {
3
+ id: string;
4
+ name: string;
5
+ status: 'active' | 'suspended' | 'deleted';
6
+ }
7
+ export declare function toWorkspace(dto: WorkspaceResponseDto): Workspace;
8
+ //# sourceMappingURL=workspace-mapper.d.ts.map
@@ -0,0 +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;AAEtE,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;CAC5C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,oBAAoB,GAAG,SAAS,CAEhE"}
@@ -0,0 +1,9 @@
1
+ export function toWorkspace(dto) {
2
+ return {
3
+ id: dto.id,
4
+ name: dto.name,
5
+ status: dto.status
6
+ };
7
+ }
8
+
9
+ //# sourceMappingURL=workspace-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/api/workspace-mapper.ts"],"sourcesContent":["import type {WorkspaceResponseDto} from '@shipfox/api-workspaces-dto';\n\nexport interface Workspace {\n id: string;\n name: string;\n status: 'active' | 'suspended' | 'deleted';\n}\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":"AAQA,OAAO,SAASA,YAAYC,GAAyB;IACnD,OAAO;QAACC,IAAID,IAAIC,EAAE;QAAEC,MAAMF,IAAIE,IAAI;QAAEC,QAAQH,IAAIG,MAAM;IAAA;AACxD"}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './components/auth-guard.js';
2
2
  export * from './components/auth-provider.js';
3
- export * from './components/auth-shell.js';
4
3
  export { WorkspaceCrumb, type WorkspaceCrumbProps } from './components/workspace-crumb.js';
5
4
  export { WorkspaceSwitcher, type WorkspaceSwitcherProps } from './components/workspace-switcher.js';
6
5
  export * from './hooks/index.js';
@@ -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,sCAAsC,CAAC;AACrD,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,2BAA2B,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,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
@@ -1,6 +1,5 @@
1
1
  export * from './components/auth-guard.js';
2
2
  export * from './components/auth-provider.js';
3
- export * from './components/auth-shell.js';
4
3
  export { WorkspaceCrumb } from './components/workspace-crumb.js';
5
4
  export { WorkspaceSwitcher } from './components/workspace-switcher.js';
6
5
  export * from './hooks/index.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/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
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './components/auth-guard.js';\nexport * from './components/auth-provider.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,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,31 +1,21 @@
1
- import type { PreviewInvitationPendingDto, PreviewInvitationResponseDto } from '@shipfox/api-workspaces-dto';
2
- /**
3
- * Extract an invitation token from a `redirect=` URL if it points at the
4
- * canonical pre-auth invitation page. Returns undefined when the redirect is
5
- * absent, malformed, or unrelated to invitations.
6
- */
1
+ import { type InvitationPreview, pendingInvitation } from '@shipfox/client-invitations';
7
2
  export declare function extractInvitationToken(redirect: unknown): string | undefined;
8
- export declare function pendingInvitation(data: PreviewInvitationResponseDto | undefined): PreviewInvitationPendingDto | undefined;
9
- /**
10
- * Mirrors the hook in `@shipfox/client-invitations` but lives in `client-auth`
11
- * to avoid a circular dependency. Signup/login pages call this to lock the
12
- * email field when arriving from an invitation link.
13
- */
14
3
  export declare function useInvitationContext(token: string | undefined): import("@tanstack/react-query").UseQueryResult<NoInfer<{
15
4
  status: "pending";
16
- workspace_id: string;
17
- workspace_name: string;
5
+ workspaceId: string;
6
+ workspaceName: string;
18
7
  email: string;
19
- invited_by_display: string | null;
20
- expires_at: string;
8
+ invitedByDisplay?: string;
9
+ expiresAt: string;
21
10
  } | {
22
11
  status: "expired";
23
- workspace_name: string;
24
- expires_at: string;
12
+ workspaceName: string;
13
+ expiresAt: string;
25
14
  } | {
26
15
  status: "already_used";
27
- workspace_name: string;
16
+ workspaceName: string;
28
17
  } | {
29
18
  status: "invalid";
30
19
  }>, Error>;
20
+ export { type InvitationPreview, pendingInvitation };
31
21
  //# sourceMappingURL=invitation-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"invitation-context.d.ts","sourceRoot":"","sources":["../../src/pages/invitation-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,4BAA4B,EAC7B,MAAM,6BAA6B,CAAC;AAMrC;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAc5E;AASD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,4BAA4B,GAAG,SAAS,GAC7C,2BAA2B,GAAG,SAAS,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;WAQ7D"}
1
+ {"version":3,"file":"invitation-context.d.ts","sourceRoot":"","sources":["../../src/pages/invitation-context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,iBAAiB,EACtB,iBAAiB,EAElB,MAAM,6BAA6B,CAAC;AAGrC,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE5E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS;;;;;;;;;;;;;;;;WAE7D;AAED,OAAO,EAAC,KAAK,iBAAiB,EAAE,iBAAiB,EAAC,CAAC"}
@@ -1,50 +1,11 @@
1
- import { apiRequest } from '@shipfox/client-api';
2
- import { useQuery } from '@tanstack/react-query';
3
- const INVITATION_ACCEPT_PATH = '/invitations/accept';
4
- /**
5
- * Extract an invitation token from a `redirect=` URL if it points at the
6
- * canonical pre-auth invitation page. Returns undefined when the redirect is
7
- * absent, malformed, or unrelated to invitations.
8
- */ export function extractInvitationToken(redirect) {
9
- if (typeof redirect !== 'string') return undefined;
10
- let decoded;
11
- try {
12
- decoded = decodeURIComponent(redirect);
13
- } catch {
14
- return undefined;
15
- }
16
- if (!decoded.startsWith('/')) return undefined;
17
- const [path, queryString = ''] = decoded.split('?', 2);
18
- if (path !== INVITATION_ACCEPT_PATH) return undefined;
19
- const params = new URLSearchParams(queryString);
20
- const token = params.get('token');
21
- return token && token.length > 0 ? token : undefined;
1
+ import { pendingInvitation, usePreviewInvitation } from '@shipfox/client-invitations';
2
+ import { parseRedirectContext } from '#/components/redirect-context.js';
3
+ export function extractInvitationToken(redirect) {
4
+ return parseRedirectContext(redirect).invitationToken;
22
5
  }
23
- async function fetchPreview(token) {
24
- const params = new URLSearchParams({
25
- token
26
- });
27
- return await apiRequest(`/invitations/preview?${params.toString()}`);
28
- }
29
- export function pendingInvitation(data) {
30
- return data?.status === 'pending' ? data : undefined;
31
- }
32
- /**
33
- * Mirrors the hook in `@shipfox/client-invitations` but lives in `client-auth`
34
- * to avoid a circular dependency. Signup/login pages call this to lock the
35
- * email field when arriving from an invitation link.
36
- */ export function useInvitationContext(token) {
37
- return useQuery({
38
- queryKey: [
39
- 'invitations',
40
- 'preview',
41
- token ?? ''
42
- ],
43
- queryFn: ()=>fetchPreview(token),
44
- enabled: Boolean(token),
45
- retry: false,
46
- staleTime: 30_000
47
- });
6
+ export function useInvitationContext(token) {
7
+ return usePreviewInvitation(token);
48
8
  }
9
+ export { pendingInvitation };
49
10
 
50
11
  //# sourceMappingURL=invitation-context.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/invitation-context.ts"],"sourcesContent":["import type {\n PreviewInvitationPendingDto,\n PreviewInvitationResponseDto,\n} from '@shipfox/api-workspaces-dto';\nimport {apiRequest} from '@shipfox/client-api';\nimport {useQuery} from '@tanstack/react-query';\n\nconst INVITATION_ACCEPT_PATH = '/invitations/accept';\n\n/**\n * Extract an invitation token from a `redirect=` URL if it points at the\n * canonical pre-auth invitation page. Returns undefined when the redirect is\n * absent, malformed, or unrelated to invitations.\n */\nexport function extractInvitationToken(redirect: unknown): string | undefined {\n if (typeof redirect !== 'string') return undefined;\n let decoded: string;\n try {\n decoded = decodeURIComponent(redirect);\n } catch {\n return undefined;\n }\n if (!decoded.startsWith('/')) return undefined;\n const [path, queryString = ''] = decoded.split('?', 2);\n if (path !== INVITATION_ACCEPT_PATH) return undefined;\n const params = new URLSearchParams(queryString);\n const token = params.get('token');\n return token && token.length > 0 ? token : undefined;\n}\n\nasync function fetchPreview(token: string): Promise<PreviewInvitationResponseDto> {\n const params = new URLSearchParams({token});\n return await apiRequest<PreviewInvitationResponseDto>(\n `/invitations/preview?${params.toString()}`,\n );\n}\n\nexport function pendingInvitation(\n data: PreviewInvitationResponseDto | undefined,\n): PreviewInvitationPendingDto | undefined {\n return data?.status === 'pending' ? data : undefined;\n}\n\n/**\n * Mirrors the hook in `@shipfox/client-invitations` but lives in `client-auth`\n * to avoid a circular dependency. Signup/login pages call this to lock the\n * email field when arriving from an invitation link.\n */\nexport function useInvitationContext(token: string | undefined) {\n return useQuery({\n queryKey: ['invitations', 'preview', token ?? ''] as const,\n queryFn: () => fetchPreview(token as string),\n enabled: Boolean(token),\n retry: false,\n staleTime: 30_000,\n });\n}\n"],"names":["apiRequest","useQuery","INVITATION_ACCEPT_PATH","extractInvitationToken","redirect","undefined","decoded","decodeURIComponent","startsWith","path","queryString","split","params","URLSearchParams","token","get","length","fetchPreview","toString","pendingInvitation","data","status","useInvitationContext","queryKey","queryFn","enabled","Boolean","retry","staleTime"],"mappings":"AAIA,SAAQA,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,QAAQ,QAAO,wBAAwB;AAE/C,MAAMC,yBAAyB;AAE/B;;;;CAIC,GACD,OAAO,SAASC,uBAAuBC,QAAiB;IACtD,IAAI,OAAOA,aAAa,UAAU,OAAOC;IACzC,IAAIC;IACJ,IAAI;QACFA,UAAUC,mBAAmBH;IAC/B,EAAE,OAAM;QACN,OAAOC;IACT;IACA,IAAI,CAACC,QAAQE,UAAU,CAAC,MAAM,OAAOH;IACrC,MAAM,CAACI,MAAMC,cAAc,EAAE,CAAC,GAAGJ,QAAQK,KAAK,CAAC,KAAK;IACpD,IAAIF,SAASP,wBAAwB,OAAOG;IAC5C,MAAMO,SAAS,IAAIC,gBAAgBH;IACnC,MAAMI,QAAQF,OAAOG,GAAG,CAAC;IACzB,OAAOD,SAASA,MAAME,MAAM,GAAG,IAAIF,QAAQT;AAC7C;AAEA,eAAeY,aAAaH,KAAa;IACvC,MAAMF,SAAS,IAAIC,gBAAgB;QAACC;IAAK;IACzC,OAAO,MAAMd,WACX,CAAC,qBAAqB,EAAEY,OAAOM,QAAQ,IAAI;AAE/C;AAEA,OAAO,SAASC,kBACdC,IAA8C;IAE9C,OAAOA,MAAMC,WAAW,YAAYD,OAAOf;AAC7C;AAEA;;;;CAIC,GACD,OAAO,SAASiB,qBAAqBR,KAAyB;IAC5D,OAAOb,SAAS;QACdsB,UAAU;YAAC;YAAe;YAAWT,SAAS;SAAG;QACjDU,SAAS,IAAMP,aAAaH;QAC5BW,SAASC,QAAQZ;QACjBa,OAAO;QACPC,WAAW;IACb;AACF"}
1
+ {"version":3,"sources":["../../src/pages/invitation-context.ts"],"sourcesContent":["import {\n type InvitationPreview,\n pendingInvitation,\n usePreviewInvitation,\n} from '@shipfox/client-invitations';\nimport {parseRedirectContext} from '#/components/redirect-context.js';\n\nexport function extractInvitationToken(redirect: unknown): string | undefined {\n return parseRedirectContext(redirect).invitationToken;\n}\n\nexport function useInvitationContext(token: string | undefined) {\n return usePreviewInvitation(token);\n}\n\nexport {type InvitationPreview, pendingInvitation};\n"],"names":["pendingInvitation","usePreviewInvitation","parseRedirectContext","extractInvitationToken","redirect","invitationToken","useInvitationContext","token"],"mappings":"AAAA,SAEEA,iBAAiB,EACjBC,oBAAoB,QACf,8BAA8B;AACrC,SAAQC,oBAAoB,QAAO,mCAAmC;AAEtE,OAAO,SAASC,uBAAuBC,QAAiB;IACtD,OAAOF,qBAAqBE,UAAUC,eAAe;AACvD;AAEA,OAAO,SAASC,qBAAqBC,KAAyB;IAC5D,OAAON,qBAAqBM;AAC9B;AAEA,SAAgCP,iBAAiB,GAAE"}
@@ -1,5 +1,6 @@
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
4
  import { Button, ButtonLink } from '@shipfox/react-ui/button';
4
5
  import { Callout } from '@shipfox/react-ui/callout';
5
6
  import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
@@ -9,7 +10,6 @@ import { useForm } from '@tanstack/react-form';
9
10
  import { Link, useSearch } from '@tanstack/react-router';
10
11
  import { useAtom } from 'jotai';
11
12
  import { useEffect, useRef, useState } from 'react';
12
- import { AuthShell } from '#/components/auth-shell.js';
13
13
  import { useLoginAuth } from '#hooks/api/login-auth.js';
14
14
  import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
15
15
  import { loginErrorToFormError } from './form-errors.js';
@@ -91,7 +91,7 @@ export function LoginPage() {
91
91
  ]);
92
92
  const isInvitationEmailLocked = Boolean(invitationPending);
93
93
  const invitationRedirect = invitationToken ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}` : undefined;
94
- const headerTitle = invitationPending ? `Join ${invitationPending.workspace_name}` : 'Connect to Shipfox';
94
+ const headerTitle = invitationPending ? `Join ${invitationPending.workspaceName}` : 'Connect to Shipfox';
95
95
  const headerDescription = invitationPending ? 'Log in to accept your invitation.' : 'Log in to access Shipfox.';
96
96
  function persistDraft() {
97
97
  const { email, password } = form.state.values;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/pages/login-page.tsx"],"sourcesContent":["import {loginBodySchema} from '@shipfox/api-auth-dto';\nimport {Button, ButtonLink} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {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 {AuthShell} from '#/components/auth-shell.js';\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.workspace_name}`\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","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","Icon","Text","useForm","Link","useSearch","useAtom","useEffect","useRef","useState","AuthShell","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","workspace_name","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} 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,8 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { AuthShell } from '@shipfox/client-shell/runtime';
2
3
  import { Text } from '@shipfox/react-ui/typography';
3
4
  import { useNavigate, useRouter, useSearch } from '@tanstack/react-router';
4
5
  import { useEffect } from 'react';
5
- import { AuthShell } from '#/components/auth-shell.js';
6
6
  import { sanitizeRedirectPath } from '#/components/redirect-target.js';
7
7
  import { useLogoutAuth } from '#hooks/api/logout-auth.js';
8
8
  export function LogoutPage() {