@burdenoff/microfe-auth 2026.1102.1 → 2026.1107.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/hooks/useAuthOperations.d.ts +1 -4
- package/dist/auth/hooks/useDelegationOperations.d.ts +2 -8
- package/dist/auth/hooks/useMyWorkspaces.d.ts +1 -4
- package/dist/auth/hooks/useSessionOperations.d.ts +7 -14
- package/dist/auth/hooks/useSsoOperations.d.ts +1 -4
- package/dist/auth/pages/EmailVerifyPage.js +91 -80
- package/dist/auth/pages/EmailVerifyPage.js.map +1 -1
- package/dist/auth/pages/LoginPage.js +236 -235
- package/dist/auth/pages/LoginPage.js.map +1 -1
- package/dist/auth/pages/MagicLinkPage.js +1 -1
- package/dist/auth/pages/MagicLinkPage.js.map +1 -1
- package/dist/auth/pages/MfaVerifyPage.js +1 -1
- package/dist/auth/pages/MfaVerifyPage.js.map +1 -1
- package/dist/auth/pages/RegisterPage.js +213 -208
- package/dist/auth/pages/RegisterPage.js.map +1 -1
- package/dist/auth/pages/SSORelayPage.js.map +1 -1
- package/dist/auth/utils/pendingReturnTo.d.ts +42 -0
- package/dist/auth/utils/pendingReturnTo.js +74 -0
- package/dist/auth/utils/pendingReturnTo.js.map +1 -0
- package/dist/auth/utils/validation.js.map +1 -1
- package/dist/generated/global-operations.d.ts +602 -553
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.d.ts +2 -21
- package/dist/generated/wspace-operations.d.ts +89 -14
- package/dist/generated/wspace-operations.js.map +1 -1
- package/dist/generated/wspace-types.d.ts +4 -23
- package/dist/microfe-auth.css +2 -2
- package/dist/test/happydom.d.ts +1 -0
- package/package.json +73 -57
|
@@ -32,10 +32,7 @@ export declare function useAuthOperations(options?: UseAuthOperationsOptions): {
|
|
|
32
32
|
success: boolean;
|
|
33
33
|
message: string;
|
|
34
34
|
}>;
|
|
35
|
-
refetchCurrentUser: (
|
|
36
|
-
data: CurrentUserResponse | undefined;
|
|
37
|
-
error?: import('@apollo/client').ErrorLike;
|
|
38
|
-
}>;
|
|
35
|
+
refetchCurrentUser: import('@apollo/client/react/internal').RefetchFunction<CurrentUserResponse, import('@apollo/client').OperationVariables, undefined>;
|
|
39
36
|
login: (input: SignInInput) => Promise<AuthResult>;
|
|
40
37
|
register: (input: SignUpInput) => Promise<AuthPayload | {
|
|
41
38
|
requiresEmailVerification: boolean;
|
|
@@ -21,14 +21,8 @@ export declare function useDelegationOperations(): {
|
|
|
21
21
|
revokeDelegation: (id: string, reason?: string) => Promise<boolean>;
|
|
22
22
|
getDelegationsForApp: (appId: string) => AppUserDelegation[];
|
|
23
23
|
hasWorkspaceDelegation: (appId: string, workspaceId: string) => boolean;
|
|
24
|
-
refetchDelegations: (
|
|
25
|
-
|
|
26
|
-
error?: import('@apollo/client').ErrorLike;
|
|
27
|
-
}>;
|
|
28
|
-
refetchGrantedDelegations: (variables?: Partial<import('@apollo/client').OperationVariables> | undefined) => Promise<{
|
|
29
|
-
data: CurrentUserGrantedDelegationsResponse | undefined;
|
|
30
|
-
error?: import('@apollo/client').ErrorLike;
|
|
31
|
-
}>;
|
|
24
|
+
refetchDelegations: import('@apollo/client/react/internal').RefetchFunction<CurrentUserDelegationsResponse, import('@apollo/client').OperationVariables, undefined>;
|
|
25
|
+
refetchGrantedDelegations: import('@apollo/client/react/internal').RefetchFunction<CurrentUserGrantedDelegationsResponse, import('@apollo/client').OperationVariables, undefined>;
|
|
32
26
|
delegations: AppUserDelegation[];
|
|
33
27
|
grantedDelegations: AppUserDelegation[];
|
|
34
28
|
isLoading: boolean;
|
|
@@ -38,9 +38,6 @@ export declare function useMyWorkspaces(): {
|
|
|
38
38
|
workspace?: Workspace;
|
|
39
39
|
error?: string;
|
|
40
40
|
}>;
|
|
41
|
-
refetch: (
|
|
42
|
-
data: MyWorkspacesResponse | undefined;
|
|
43
|
-
error?: import('@apollo/client').ErrorLike;
|
|
44
|
-
}>;
|
|
41
|
+
refetch: import('@apollo/client/react/internal').RefetchFunction<MyWorkspacesResponse, import('@apollo/client').OperationVariables, undefined>;
|
|
45
42
|
};
|
|
46
43
|
export {};
|
|
@@ -22,24 +22,20 @@ export declare function useSessionOperations(options?: UseSessionOperationsOptio
|
|
|
22
22
|
revokedCount: number;
|
|
23
23
|
}>;
|
|
24
24
|
activeSessions: {
|
|
25
|
-
__typename?: "Session";
|
|
26
25
|
id: string;
|
|
27
26
|
status: import('../../generated/global-types').SessionStatus;
|
|
28
|
-
ipAddress
|
|
29
|
-
userAgent
|
|
30
|
-
location
|
|
27
|
+
ipAddress: string | null;
|
|
28
|
+
userAgent: string | null;
|
|
29
|
+
location: string | null;
|
|
31
30
|
lastActivityAt: string;
|
|
32
31
|
expiresAt: string;
|
|
33
32
|
createdAt: string;
|
|
34
33
|
isCurrent: boolean;
|
|
35
34
|
}[];
|
|
36
35
|
sessionsLoading: boolean;
|
|
37
|
-
refetchSessions:
|
|
38
|
-
pagination?: import('../../generated/global-types').
|
|
39
|
-
}
|
|
40
|
-
data: ActiveSessionsQuery | undefined;
|
|
41
|
-
error?: import('@apollo/client').ErrorLike;
|
|
42
|
-
}>;
|
|
36
|
+
refetchSessions: import('@apollo/client/react/internal').RefetchFunction<ActiveSessionsQuery, {
|
|
37
|
+
pagination?: import('../../generated/global-types').PaginationInput | null | undefined;
|
|
38
|
+
}, undefined>;
|
|
43
39
|
page: number;
|
|
44
40
|
pageSize: number;
|
|
45
41
|
totalCount: number;
|
|
@@ -48,9 +44,6 @@ export declare function useSessionOperations(options?: UseSessionOperationsOptio
|
|
|
48
44
|
goToNextPage: () => void;
|
|
49
45
|
goToPrevPage: () => void;
|
|
50
46
|
isLoading: boolean;
|
|
51
|
-
refetch: (
|
|
52
|
-
data: TokenInfoQueryResponse | undefined;
|
|
53
|
-
error?: import('@apollo/client').ErrorLike;
|
|
54
|
-
}>;
|
|
47
|
+
refetch: import('@apollo/client/react/internal').RefetchFunction<TokenInfoQueryResponse, import('@apollo/client').OperationVariables, undefined>;
|
|
55
48
|
};
|
|
56
49
|
export {};
|
|
@@ -58,9 +58,6 @@ export declare function useSsoOperations(tenantId?: string): {
|
|
|
58
58
|
isSaving: boolean;
|
|
59
59
|
isDeleting: boolean;
|
|
60
60
|
isGeneratingOidcUrl: boolean;
|
|
61
|
-
refetchConfig: (
|
|
62
|
-
data: GetSSOConfigResponse | undefined;
|
|
63
|
-
error?: import('@apollo/client').ErrorLike;
|
|
64
|
-
}>;
|
|
61
|
+
refetchConfig: import('@apollo/client/react/internal').RefetchFunction<GetSSOConfigResponse, GetSSOConfigVariables, undefined>;
|
|
65
62
|
};
|
|
66
63
|
export {};
|
|
@@ -3,90 +3,101 @@ import { useAuthOperations as t } from "../hooks/useAuthOperations.js";
|
|
|
3
3
|
import { useAuthStore as n } from "../store/authStore.js";
|
|
4
4
|
import { AuthLayout as r } from "../components/AuthLayout.js";
|
|
5
5
|
import { SpinnerIcon as i } from "../components/AuthIcons.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { authPathWithReturnTo as a, consumeReturnTo as o } from "../utils/pendingReturnTo.js";
|
|
7
|
+
import { useCallback as s, useEffect as c, useRef as l, useState as u } from "react";
|
|
8
|
+
import { useSearchParams as d } from "react-router";
|
|
9
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
10
|
+
import { Button as h } from "@burdenoff/fe-libs/button";
|
|
10
11
|
// src/auth/pages/EmailVerifyPage.tsx
|
|
11
|
-
var
|
|
12
|
-
function
|
|
13
|
-
|
|
14
|
-
window.location.href = n;
|
|
12
|
+
var g = 5, _ = /* @__PURE__ */ new Map();
|
|
13
|
+
function v(e, t) {
|
|
14
|
+
window.location.href = a("/auth/login", t, e);
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
let
|
|
18
|
-
|
|
19
|
-
if (!
|
|
16
|
+
function y() {
|
|
17
|
+
let a = e(), [y] = d(), b = y.get("token"), x = y.get("email"), { verifyEmail: S, resendVerification: C } = t(), w = n((e) => e.user), [T, E] = u("verifying"), D = l(x || void 0), [O, k] = u(!1), [A, j] = u(!1), [M, N] = u(g), P = l(null);
|
|
18
|
+
c(() => {
|
|
19
|
+
if (!b) return;
|
|
20
20
|
try {
|
|
21
|
-
window.sessionStorage.removeItem(`auth:verifyEmail:inflight:${
|
|
21
|
+
window.sessionStorage.removeItem(`auth:verifyEmail:inflight:${b}`);
|
|
22
22
|
} catch {}
|
|
23
|
-
if (
|
|
24
|
-
let e =
|
|
25
|
-
e || (e = Promise.resolve().then(() =>
|
|
26
|
-
token:
|
|
23
|
+
if (P.current?.token !== b) {
|
|
24
|
+
let e = _.get(b);
|
|
25
|
+
e || (e = Promise.resolve().then(() => S(b)).finally(() => _.delete(b)), _.set(b, e)), P.current = {
|
|
26
|
+
token: b,
|
|
27
27
|
request: e
|
|
28
|
-
},
|
|
28
|
+
}, E("verifying");
|
|
29
29
|
}
|
|
30
30
|
let e = !0;
|
|
31
|
-
return
|
|
32
|
-
e && (t.success ? (
|
|
31
|
+
return P.current.request.then((t) => {
|
|
32
|
+
e && (t.success ? (E("success"), t.email && (D.current = t.email)) : E("error"));
|
|
33
33
|
}).catch(() => {
|
|
34
|
-
e &&
|
|
34
|
+
e && E("error");
|
|
35
35
|
}), () => {
|
|
36
36
|
e = !1;
|
|
37
37
|
};
|
|
38
|
-
}, [
|
|
39
|
-
|
|
38
|
+
}, [b, S]);
|
|
39
|
+
let F = l(void 0), I = s(() => (F.current === void 0 && (F.current = o()), F.current), []), L = l(!1), R = s((e) => {
|
|
40
|
+
L.current || (L.current = !0, v(e, I()));
|
|
41
|
+
}, [I]);
|
|
42
|
+
c(() => {
|
|
43
|
+
if (T !== "success") return;
|
|
44
|
+
I();
|
|
40
45
|
let e = setInterval(() => {
|
|
41
|
-
|
|
46
|
+
N((e) => e <= 1 ? 0 : e - 1);
|
|
42
47
|
}, 1e3);
|
|
43
48
|
return () => clearInterval(e);
|
|
44
|
-
}, [
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
}, [T, I]), c(() => {
|
|
50
|
+
T === "success" && M === 0 && R(D.current);
|
|
51
|
+
}, [
|
|
52
|
+
T,
|
|
53
|
+
M,
|
|
54
|
+
R
|
|
55
|
+
]);
|
|
56
|
+
let z = async () => {
|
|
57
|
+
let e = x || w?.email;
|
|
47
58
|
if (!e) console.error("No email available for resend");
|
|
48
59
|
else {
|
|
49
|
-
|
|
60
|
+
k(!0);
|
|
50
61
|
try {
|
|
51
|
-
await
|
|
62
|
+
await C(e), j(!0);
|
|
52
63
|
} catch (e) {
|
|
53
64
|
console.error("Failed to resend verification:", e);
|
|
54
65
|
} finally {
|
|
55
|
-
|
|
66
|
+
k(!1);
|
|
56
67
|
}
|
|
57
68
|
}
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
}, []),
|
|
61
|
-
|
|
62
|
-
}, []);
|
|
63
|
-
return
|
|
64
|
-
title:
|
|
65
|
-
subtitle:
|
|
66
|
-
children: /* @__PURE__ */
|
|
69
|
+
}, B = s(() => {
|
|
70
|
+
R(D.current);
|
|
71
|
+
}, [R]), V = s(() => {
|
|
72
|
+
R();
|
|
73
|
+
}, [R]);
|
|
74
|
+
return T === "verifying" ? /* @__PURE__ */ p(r, {
|
|
75
|
+
title: a("auth.emailVerify.verifyingTitle", "Verifying your email"),
|
|
76
|
+
subtitle: a("auth.emailVerify.verifyingSubtitle", "Please wait while we confirm your email address"),
|
|
77
|
+
children: /* @__PURE__ */ m("div", {
|
|
67
78
|
className: "flex flex-col items-center justify-center py-12",
|
|
68
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ p("div", {
|
|
69
80
|
className: "size-16 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center mb-6",
|
|
70
|
-
children: /* @__PURE__ */
|
|
71
|
-
}), /* @__PURE__ */
|
|
81
|
+
children: /* @__PURE__ */ p(i, { className: "size-8 text-[var(--color-text-link)] animate-spin" })
|
|
82
|
+
}), /* @__PURE__ */ p("p", {
|
|
72
83
|
className: "text-[var(--color-text-secondary)]",
|
|
73
|
-
children:
|
|
84
|
+
children: a("auth.emailVerify.moment", "This will only take a moment...")
|
|
74
85
|
})]
|
|
75
86
|
})
|
|
76
|
-
}) :
|
|
77
|
-
title:
|
|
78
|
-
subtitle:
|
|
79
|
-
children: /* @__PURE__ */
|
|
87
|
+
}) : T === "success" ? /* @__PURE__ */ p(r, {
|
|
88
|
+
title: a("auth.emailVerify.verifiedTitle", "Email verified!"),
|
|
89
|
+
subtitle: a("auth.emailVerify.verifiedSubtitle", "Your email address has been confirmed"),
|
|
90
|
+
children: /* @__PURE__ */ m("div", {
|
|
80
91
|
className: "flex flex-col items-center justify-center py-6",
|
|
81
92
|
children: [
|
|
82
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ p("div", {
|
|
83
94
|
className: "size-20 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center mb-6",
|
|
84
|
-
children: /* @__PURE__ */
|
|
95
|
+
children: /* @__PURE__ */ p("svg", {
|
|
85
96
|
className: "size-10 text-[var(--color-status-success-text)]",
|
|
86
97
|
fill: "none",
|
|
87
98
|
viewBox: "0 0 24 24",
|
|
88
99
|
stroke: "currentColor",
|
|
89
|
-
children: /* @__PURE__ */
|
|
100
|
+
children: /* @__PURE__ */ p("path", {
|
|
90
101
|
strokeLinecap: "round",
|
|
91
102
|
strokeLinejoin: "round",
|
|
92
103
|
strokeWidth: 2,
|
|
@@ -94,47 +105,47 @@ function _() {
|
|
|
94
105
|
})
|
|
95
106
|
})
|
|
96
107
|
}),
|
|
97
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ p("p", {
|
|
98
109
|
className: "text-center text-[var(--color-text-secondary)] mb-6",
|
|
99
|
-
children:
|
|
110
|
+
children: a("auth.emailVerify.successMessage", "Your email has been successfully verified. You can now sign in to access all features.")
|
|
100
111
|
}),
|
|
101
|
-
/* @__PURE__ */
|
|
112
|
+
/* @__PURE__ */ m("p", {
|
|
102
113
|
className: "text-center text-sm text-[var(--color-text-tertiary)] mb-6",
|
|
103
114
|
children: [
|
|
104
|
-
|
|
115
|
+
a("auth.emailVerify.redirecting", "Redirecting to sign in in"),
|
|
105
116
|
" ",
|
|
106
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ p("span", {
|
|
107
118
|
className: "font-semibold text-[var(--color-text-primary)]",
|
|
108
|
-
children:
|
|
119
|
+
children: M
|
|
109
120
|
}),
|
|
110
121
|
" ",
|
|
111
|
-
|
|
122
|
+
a("auth.emailVerify.seconds", "seconds...")
|
|
112
123
|
]
|
|
113
124
|
}),
|
|
114
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ p(h, {
|
|
115
126
|
type: "button",
|
|
116
|
-
onClick:
|
|
127
|
+
onClick: B,
|
|
117
128
|
variant: "default",
|
|
118
129
|
size: "lg",
|
|
119
130
|
className: "w-full font-semibold",
|
|
120
|
-
children:
|
|
131
|
+
children: a("auth.emailVerify.continueSignIn", "Continue to sign in now")
|
|
121
132
|
})
|
|
122
133
|
]
|
|
123
134
|
})
|
|
124
|
-
}) : /* @__PURE__ */
|
|
125
|
-
title:
|
|
126
|
-
subtitle:
|
|
127
|
-
children: /* @__PURE__ */
|
|
135
|
+
}) : /* @__PURE__ */ p(r, {
|
|
136
|
+
title: a("auth.emailVerify.failedTitle", "Verification failed"),
|
|
137
|
+
subtitle: a("auth.emailVerify.failedSubtitle", "We couldn't verify your email address"),
|
|
138
|
+
children: /* @__PURE__ */ m("div", {
|
|
128
139
|
className: "flex flex-col items-center justify-center py-6",
|
|
129
140
|
children: [
|
|
130
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ p("div", {
|
|
131
142
|
className: "size-20 rounded-full bg-[var(--color-status-error-bg-subtle)] flex items-center justify-center mb-6",
|
|
132
|
-
children: /* @__PURE__ */
|
|
143
|
+
children: /* @__PURE__ */ p("svg", {
|
|
133
144
|
className: "size-10 text-[var(--color-status-error-text)]",
|
|
134
145
|
fill: "none",
|
|
135
146
|
viewBox: "0 0 24 24",
|
|
136
147
|
stroke: "currentColor",
|
|
137
|
-
children: /* @__PURE__ */
|
|
148
|
+
children: /* @__PURE__ */ p("path", {
|
|
138
149
|
strokeLinecap: "round",
|
|
139
150
|
strokeLinejoin: "round",
|
|
140
151
|
strokeWidth: 2,
|
|
@@ -142,35 +153,35 @@ function _() {
|
|
|
142
153
|
})
|
|
143
154
|
})
|
|
144
155
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
156
|
+
/* @__PURE__ */ p("p", {
|
|
146
157
|
className: "text-center text-[var(--color-text-secondary)] mb-8",
|
|
147
|
-
children:
|
|
158
|
+
children: a("auth.emailVerify.invalidLink", "The verification link is invalid or has expired. Please request a new verification email.")
|
|
148
159
|
}),
|
|
149
|
-
|
|
160
|
+
A ? /* @__PURE__ */ p("div", {
|
|
150
161
|
className: "w-full p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)] text-center mb-4",
|
|
151
|
-
children: /* @__PURE__ */
|
|
162
|
+
children: /* @__PURE__ */ p("p", {
|
|
152
163
|
className: "text-sm text-[var(--color-status-success-text)]",
|
|
153
|
-
children:
|
|
164
|
+
children: a("auth.emailVerify.emailSent", "Verification email sent! Please check your inbox.")
|
|
154
165
|
})
|
|
155
|
-
}) : /* @__PURE__ */
|
|
166
|
+
}) : /* @__PURE__ */ p(h, {
|
|
156
167
|
type: "button",
|
|
157
|
-
onClick:
|
|
158
|
-
disabled:
|
|
168
|
+
onClick: z,
|
|
169
|
+
disabled: O,
|
|
159
170
|
variant: "default",
|
|
160
171
|
size: "lg",
|
|
161
172
|
className: "mb-4 w-full font-semibold",
|
|
162
|
-
children:
|
|
173
|
+
children: O ? /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(i, { className: "size-5 animate-spin" }), /* @__PURE__ */ p("span", { children: a("auth.emailVerify.sending", "Sending…") })] }) : a("auth.emailVerify.resend", "Resend verification email")
|
|
163
174
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ p("button", {
|
|
165
176
|
type: "button",
|
|
166
|
-
onClick:
|
|
177
|
+
onClick: V,
|
|
167
178
|
className: "w-full py-3 px-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] font-medium hover:bg-[var(--color-bg-sunken)] transition-all",
|
|
168
|
-
children:
|
|
179
|
+
children: a("auth.emailVerify.backToSignIn", "Back to sign in")
|
|
169
180
|
})
|
|
170
181
|
]
|
|
171
182
|
})
|
|
172
183
|
});
|
|
173
184
|
}
|
|
174
|
-
export {
|
|
185
|
+
export { y as EmailVerifyPage };
|
|
175
186
|
|
|
176
187
|
//# sourceMappingURL=EmailVerifyPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmailVerifyPage.js","names":["_Fragment","REDIRECT_DELAY","pendingVerifications","navigateToLogin","baseUrl","url","email","EmailVerifyPage","tr","useTr","searchParams","useSearchParams","token","urlEmail","useAuthOperations","user","useAuthStore","state","status","setStatus","useState","verifiedEmailRef","useRef","isResending","setIsResending","resendSuccess","setResendSuccess","countdown","setCountdown","verificationRef","useEffect","request","verifyEmail","active","result","timer","prev","handleResend","resendEmail","resendVerification","err","handleContinueToLogin","useCallback","handleBackToLogin","AuthLayout","SpinnerIcon","Button","Fragment"],"sources":["../../../src/auth/pages/EmailVerifyPage.tsx"],"sourcesContent":["import { useEffect, useState, useCallback, useRef } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useSearchParams } from \"react-router\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { SpinnerIcon } from \"../components/AuthIcons\";\n\nconst REDIRECT_DELAY = 5; // seconds\ntype VerificationRequest = ReturnType<\n ReturnType<typeof useAuthOperations>[\"verifyEmail\"]\n>;\n// Share only pending work across remounts; never persist bearer tokens.\nconst pendingVerifications = new Map<string, VerificationRequest>();\n\n/**\n * Navigate to login page with optional email pre-fill\n * Uses window.location.href for reliable navigation (works in new tabs)\n */\nfunction navigateToLogin(email?: string) {\n const baseUrl = \"/auth/login\";\n const url = email ? `${baseUrl}?email=${encodeURIComponent(email)}` : baseUrl;\n window.location.href = url;\n}\n\n/**\n * Email verification page with professional design\n */\nexport function EmailVerifyPage() {\n const tr = useTr();\n\n const [searchParams] = useSearchParams();\n const token = searchParams.get(\"token\");\n const urlEmail = searchParams.get(\"email\");\n const { verifyEmail, resendVerification } = useAuthOperations();\n const user = useAuthStore((state) => state.user);\n const [status, setStatus] = useState<\"verifying\" | \"success\" | \"error\">(\n \"verifying\",\n );\n const verifiedEmailRef = useRef<string | undefined>(urlEmail || undefined);\n const [isResending, setIsResending] = useState(false);\n const [resendSuccess, setResendSuccess] = useState(false);\n const [countdown, setCountdown] = useState(REDIRECT_DELAY);\n\n const verificationRef = useRef<{\n token: string;\n request: VerificationRequest;\n } | null>(null);\n\n useEffect(() => {\n if (!token) return;\n try {\n // Remove the marker left by older versions, without touching other data.\n window.sessionStorage.removeItem(`auth:verifyEmail:inflight:${token}`);\n } catch {\n // Verification must also work when browser storage is unavailable.\n }\n\n if (verificationRef.current?.token !== token) {\n let request = pendingVerifications.get(token);\n if (!request) {\n request = Promise.resolve()\n .then(() => verifyEmail(token))\n .finally(() => pendingVerifications.delete(token));\n pendingVerifications.set(token, request);\n }\n verificationRef.current = { token, request };\n setStatus(\"verifying\");\n }\n\n let active = true;\n verificationRef.current.request\n .then((result) => {\n if (!active) return;\n if (result.success) {\n setStatus(\"success\");\n if (result.email) verifiedEmailRef.current = result.email;\n } else {\n setStatus(\"error\");\n }\n })\n .catch(() => {\n if (active) setStatus(\"error\");\n });\n return () => {\n active = false;\n };\n }, [token, verifyEmail]);\n\n // Auto-redirect countdown after successful verification\n useEffect(() => {\n if (status !== \"success\") return;\n\n const timer = setInterval(() => {\n setCountdown((prev) => {\n if (prev <= 1) {\n clearInterval(timer);\n navigateToLogin(verifiedEmailRef.current);\n return 0;\n }\n return prev - 1;\n });\n }, 1000);\n\n return () => clearInterval(timer);\n }, [status]);\n\n const handleResend = async () => {\n const resendEmail = urlEmail || user?.email;\n if (!resendEmail) {\n console.error(\"No email available for resend\");\n return;\n }\n setIsResending(true);\n try {\n await resendVerification(resendEmail);\n setResendSuccess(true);\n } catch (err) {\n console.error(\"Failed to resend verification:\", err);\n } finally {\n setIsResending(false);\n }\n };\n\n const handleContinueToLogin = useCallback(() => {\n navigateToLogin(verifiedEmailRef.current);\n }, []);\n\n const handleBackToLogin = useCallback(() => {\n navigateToLogin();\n }, []);\n\n if (status === \"verifying\") {\n return (\n <AuthLayout\n title={tr(\"auth.emailVerify.verifyingTitle\", \"Verifying your email\")}\n subtitle={tr(\n \"auth.emailVerify.verifyingSubtitle\",\n \"Please wait while we confirm your email address\",\n )}\n >\n {/* Loading Spinner */}\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"size-16 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center mb-6\">\n <SpinnerIcon className=\"size-8 text-[var(--color-text-link)] animate-spin\" />\n </div>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\"auth.emailVerify.moment\", \"This will only take a moment...\")}\n </p>\n </div>\n </AuthLayout>\n );\n }\n\n if (status === \"success\") {\n return (\n <AuthLayout\n title={tr(\"auth.emailVerify.verifiedTitle\", \"Email verified!\")}\n subtitle={tr(\n \"auth.emailVerify.verifiedSubtitle\",\n \"Your email address has been confirmed\",\n )}\n >\n {/* Success Icon */}\n <div className=\"flex flex-col items-center justify-center py-6\">\n <div className=\"size-20 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-6\">\n {tr(\n \"auth.emailVerify.successMessage\",\n \"Your email has been successfully verified. You can now sign in to access all features.\",\n )}\n </p>\n\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)] mb-6\">\n {tr(\"auth.emailVerify.redirecting\", \"Redirecting to sign in in\")}{\" \"}\n <span className=\"font-semibold text-[var(--color-text-primary)]\">\n {countdown}\n </span>{\" \"}\n {tr(\"auth.emailVerify.seconds\", \"seconds...\")}\n </p>\n\n <Button\n type=\"button\"\n onClick={handleContinueToLogin}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {tr(\"auth.emailVerify.continueSignIn\", \"Continue to sign in now\")}\n </Button>\n </div>\n </AuthLayout>\n );\n }\n\n // Error state\n return (\n <AuthLayout\n title={tr(\"auth.emailVerify.failedTitle\", \"Verification failed\")}\n subtitle={tr(\n \"auth.emailVerify.failedSubtitle\",\n \"We couldn't verify your email address\",\n )}\n >\n <div className=\"flex flex-col items-center justify-center py-6\">\n {/* Error Icon */}\n <div className=\"size-20 rounded-full bg-[var(--color-status-error-bg-subtle)] flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-[var(--color-status-error-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8\">\n {tr(\n \"auth.emailVerify.invalidLink\",\n \"The verification link is invalid or has expired. Please request a new verification email.\",\n )}\n </p>\n\n {resendSuccess ? (\n <div className=\"w-full p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)] text-center mb-4\">\n <p className=\"text-sm text-[var(--color-status-success-text)]\">\n {tr(\n \"auth.emailVerify.emailSent\",\n \"Verification email sent! Please check your inbox.\",\n )}\n </p>\n </div>\n ) : (\n <Button\n type=\"button\"\n onClick={handleResend}\n disabled={isResending}\n variant=\"default\"\n size=\"lg\"\n className=\"mb-4 w-full font-semibold\"\n >\n {isResending ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.emailVerify.sending\", \"Sending…\")}</span>\n </>\n ) : (\n tr(\"auth.emailVerify.resend\", \"Resend verification email\")\n )}\n </Button>\n )}\n\n <button\n type=\"button\"\n onClick={handleBackToLogin}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] font-medium hover:bg-[var(--color-bg-sunken)] transition-all\"\n >\n {tr(\"auth.emailVerify.backToSignIn\", \"Back to sign in\")}\n </button>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EmailVerifyPage.js","names":["_Fragment","REDIRECT_DELAY","pendingVerifications","navigateToLogin","authPathWithReturnTo","returnTo","email","EmailVerifyPage","tr","useTr","searchParams","useSearchParams","token","urlEmail","useAuthOperations","user","useAuthStore","state","status","setStatus","useState","verifiedEmailRef","useRef","isResending","setIsResending","resendSuccess","setResendSuccess","countdown","setCountdown","verificationRef","request","verifyEmail","active","result","returnToRef","pendingReturnTo","useCallback","consumeReturnTo","redirectedRef","goToLogin","useEffect","timer","prev","handleResend","resendEmail","resendVerification","err","handleContinueToLogin","handleBackToLogin","AuthLayout","SpinnerIcon","Button","Fragment"],"sources":["../../../src/auth/pages/EmailVerifyPage.tsx"],"sourcesContent":["import { useEffect, useState, useCallback, useRef } from \"react\";\nimport { useTr } from \"../../shared/hooks/useTr\";\nimport { useSearchParams } from \"react-router\";\nimport { useAuthOperations } from \"../hooks/useAuthOperations\";\nimport { useAuthStore } from \"../store/authStore\";\nimport { AuthLayout } from \"../components/AuthLayout\";\nimport { Button } from \"@burdenoff/fe-libs/button\";\nimport { SpinnerIcon } from \"../components/AuthIcons\";\nimport {\n authPathWithReturnTo,\n consumeReturnTo,\n} from \"../utils/pendingReturnTo\";\n\nconst REDIRECT_DELAY = 5; // seconds\ntype VerificationRequest = ReturnType<\n ReturnType<typeof useAuthOperations>[\"verifyEmail\"]\n>;\n// Share only pending work across remounts; never persist bearer tokens.\nconst pendingVerifications = new Map<string, VerificationRequest>();\n\n/**\n * Navigate to login page with optional email pre-fill.\n * Uses window.location.href for reliable navigation (works in new tabs).\n * Carries the destination the visitor chose before signing up (for example\n * the developer portal from \"Build on Workspaces\") as `returnTo`, which the\n * login page validates and honours (BOFF-10636).\n */\nfunction navigateToLogin(email: string | undefined, returnTo: string | null) {\n window.location.href = authPathWithReturnTo(\"/auth/login\", returnTo, email);\n}\n\n/**\n * Email verification page with professional design\n */\nexport function EmailVerifyPage() {\n const tr = useTr();\n\n const [searchParams] = useSearchParams();\n const token = searchParams.get(\"token\");\n const urlEmail = searchParams.get(\"email\");\n const { verifyEmail, resendVerification } = useAuthOperations();\n const user = useAuthStore((state) => state.user);\n const [status, setStatus] = useState<\"verifying\" | \"success\" | \"error\">(\n \"verifying\",\n );\n const verifiedEmailRef = useRef<string | undefined>(urlEmail || undefined);\n const [isResending, setIsResending] = useState(false);\n const [resendSuccess, setResendSuccess] = useState(false);\n const [countdown, setCountdown] = useState(REDIRECT_DELAY);\n\n const verificationRef = useRef<{\n token: string;\n request: VerificationRequest;\n } | null>(null);\n\n useEffect(() => {\n if (!token) return;\n try {\n // Remove the marker left by older versions, without touching other data.\n window.sessionStorage.removeItem(`auth:verifyEmail:inflight:${token}`);\n } catch {\n // Verification must also work when browser storage is unavailable.\n }\n\n if (verificationRef.current?.token !== token) {\n let request = pendingVerifications.get(token);\n if (!request) {\n request = Promise.resolve()\n .then(() => verifyEmail(token))\n .finally(() => pendingVerifications.delete(token));\n pendingVerifications.set(token, request);\n }\n verificationRef.current = { token, request };\n setStatus(\"verifying\");\n }\n\n let active = true;\n verificationRef.current.request\n .then((result) => {\n if (!active) return;\n if (result.success) {\n setStatus(\"success\");\n if (result.email) verifiedEmailRef.current = result.email;\n } else {\n setStatus(\"error\");\n }\n })\n .catch(() => {\n if (active) setStatus(\"error\");\n });\n return () => {\n active = false;\n };\n }, [token, verifyEmail]);\n\n // The destination remembered at sign-up is read once and kept in a ref, so\n // every way out of this page (auto-redirect, either button) carries the\n // same value, and StrictMode's repeated effects cannot consume it twice.\n const returnToRef = useRef<string | null | undefined>(undefined);\n const pendingReturnTo = useCallback((): string | null => {\n if (returnToRef.current === undefined) {\n returnToRef.current = consumeReturnTo();\n }\n return returnToRef.current;\n }, []);\n const redirectedRef = useRef(false);\n const goToLogin = useCallback(\n (email?: string) => {\n if (redirectedRef.current) return;\n redirectedRef.current = true;\n navigateToLogin(email, pendingReturnTo());\n },\n [pendingReturnTo],\n );\n\n // Auto-redirect countdown after successful verification. The interval only\n // counts down (state updaters stay pure); the redirect happens in the\n // effect below once the count reaches zero.\n useEffect(() => {\n if (status !== \"success\") return;\n pendingReturnTo();\n\n const timer = setInterval(() => {\n setCountdown((prev) => (prev <= 1 ? 0 : prev - 1));\n }, 1000);\n\n return () => clearInterval(timer);\n }, [status, pendingReturnTo]);\n\n useEffect(() => {\n if (status === \"success\" && countdown === 0) {\n goToLogin(verifiedEmailRef.current);\n }\n }, [status, countdown, goToLogin]);\n\n const handleResend = async () => {\n const resendEmail = urlEmail || user?.email;\n if (!resendEmail) {\n console.error(\"No email available for resend\");\n return;\n }\n setIsResending(true);\n try {\n await resendVerification(resendEmail);\n setResendSuccess(true);\n } catch (err) {\n console.error(\"Failed to resend verification:\", err);\n } finally {\n setIsResending(false);\n }\n };\n\n const handleContinueToLogin = useCallback(() => {\n goToLogin(verifiedEmailRef.current);\n }, [goToLogin]);\n\n const handleBackToLogin = useCallback(() => {\n goToLogin();\n }, [goToLogin]);\n\n if (status === \"verifying\") {\n return (\n <AuthLayout\n title={tr(\"auth.emailVerify.verifyingTitle\", \"Verifying your email\")}\n subtitle={tr(\n \"auth.emailVerify.verifyingSubtitle\",\n \"Please wait while we confirm your email address\",\n )}\n >\n {/* Loading Spinner */}\n <div className=\"flex flex-col items-center justify-center py-12\">\n <div className=\"size-16 rounded-full bg-[var(--color-bg-accent)] flex items-center justify-center mb-6\">\n <SpinnerIcon className=\"size-8 text-[var(--color-text-link)] animate-spin\" />\n </div>\n <p className=\"text-[var(--color-text-secondary)]\">\n {tr(\"auth.emailVerify.moment\", \"This will only take a moment...\")}\n </p>\n </div>\n </AuthLayout>\n );\n }\n\n if (status === \"success\") {\n return (\n <AuthLayout\n title={tr(\"auth.emailVerify.verifiedTitle\", \"Email verified!\")}\n subtitle={tr(\n \"auth.emailVerify.verifiedSubtitle\",\n \"Your email address has been confirmed\",\n )}\n >\n {/* Success Icon */}\n <div className=\"flex flex-col items-center justify-center py-6\">\n <div className=\"size-20 rounded-full bg-[var(--color-status-success-bg-subtle)] flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-[var(--color-status-success-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-6\">\n {tr(\n \"auth.emailVerify.successMessage\",\n \"Your email has been successfully verified. You can now sign in to access all features.\",\n )}\n </p>\n\n <p className=\"text-center text-sm text-[var(--color-text-tertiary)] mb-6\">\n {tr(\"auth.emailVerify.redirecting\", \"Redirecting to sign in in\")}{\" \"}\n <span className=\"font-semibold text-[var(--color-text-primary)]\">\n {countdown}\n </span>{\" \"}\n {tr(\"auth.emailVerify.seconds\", \"seconds...\")}\n </p>\n\n <Button\n type=\"button\"\n onClick={handleContinueToLogin}\n variant=\"default\"\n size=\"lg\"\n className=\"w-full font-semibold\"\n >\n {tr(\"auth.emailVerify.continueSignIn\", \"Continue to sign in now\")}\n </Button>\n </div>\n </AuthLayout>\n );\n }\n\n // Error state\n return (\n <AuthLayout\n title={tr(\"auth.emailVerify.failedTitle\", \"Verification failed\")}\n subtitle={tr(\n \"auth.emailVerify.failedSubtitle\",\n \"We couldn't verify your email address\",\n )}\n >\n <div className=\"flex flex-col items-center justify-center py-6\">\n {/* Error Icon */}\n <div className=\"size-20 rounded-full bg-[var(--color-status-error-bg-subtle)] flex items-center justify-center mb-6\">\n <svg\n className=\"size-10 text-[var(--color-status-error-text)]\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n\n <p className=\"text-center text-[var(--color-text-secondary)] mb-8\">\n {tr(\n \"auth.emailVerify.invalidLink\",\n \"The verification link is invalid or has expired. Please request a new verification email.\",\n )}\n </p>\n\n {resendSuccess ? (\n <div className=\"w-full p-4 rounded-xl bg-[var(--color-status-success-bg-subtle)] border border-[var(--color-status-success-border)] text-center mb-4\">\n <p className=\"text-sm text-[var(--color-status-success-text)]\">\n {tr(\n \"auth.emailVerify.emailSent\",\n \"Verification email sent! Please check your inbox.\",\n )}\n </p>\n </div>\n ) : (\n <Button\n type=\"button\"\n onClick={handleResend}\n disabled={isResending}\n variant=\"default\"\n size=\"lg\"\n className=\"mb-4 w-full font-semibold\"\n >\n {isResending ? (\n <>\n <SpinnerIcon className=\"size-5 animate-spin\" />\n <span>{tr(\"auth.emailVerify.sending\", \"Sending…\")}</span>\n </>\n ) : (\n tr(\"auth.emailVerify.resend\", \"Resend verification email\")\n )}\n </Button>\n )}\n\n <button\n type=\"button\"\n onClick={handleBackToLogin}\n className=\"w-full py-3 px-4 rounded-xl border border-[var(--color-border-default)] bg-[var(--color-bg-surface)] text-[var(--color-text-primary)] font-medium hover:bg-[var(--color-bg-sunken)] transition-all\"\n >\n {tr(\"auth.emailVerify.backToSignIn\", \"Back to sign in\")}\n </button>\n </div>\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|