@shipfox/client-auth 0.2.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.
- package/.storybook/main.ts +1 -0
- package/.storybook/preview.css +7 -0
- package/.storybook/preview.tsx +56 -0
- package/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +111 -0
- package/LICENSE +21 -0
- package/dist/components/auth-guard.d.ts +5 -0
- package/dist/components/auth-guard.d.ts.map +1 -0
- package/dist/components/auth-guard.js +61 -0
- package/dist/components/auth-guard.js.map +1 -0
- package/dist/components/auth-provider.d.ts +11 -0
- package/dist/components/auth-provider.d.ts.map +1 -0
- package/dist/components/auth-provider.js +21 -0
- package/dist/components/auth-provider.js.map +1 -0
- package/dist/components/auth-shell.d.ts +11 -0
- package/dist/components/auth-shell.d.ts.map +1 -0
- package/dist/components/auth-shell.js +63 -0
- package/dist/components/auth-shell.js.map +1 -0
- package/dist/components/redirect-target.d.ts +2 -0
- package/dist/components/redirect-target.d.ts.map +1 -0
- package/dist/components/redirect-target.js +22 -0
- package/dist/components/redirect-target.js.map +1 -0
- package/dist/components/workspace-crumb.d.ts +3 -0
- package/dist/components/workspace-crumb.d.ts.map +1 -0
- package/dist/components/workspace-crumb.js +3 -0
- package/dist/components/workspace-crumb.js.map +1 -0
- package/dist/components/workspace-switcher.d.ts +6 -0
- package/dist/components/workspace-switcher.d.ts.map +1 -0
- package/dist/components/workspace-switcher.js +91 -0
- package/dist/components/workspace-switcher.js.map +1 -0
- package/dist/components/workspace-switcher.stories.d.ts +26 -0
- package/dist/components/workspace-switcher.stories.d.ts.map +1 -0
- package/dist/components/workspace-switcher.stories.js +168 -0
- package/dist/components/workspace-switcher.stories.js.map +1 -0
- package/dist/feature.d.ts +33 -0
- package/dist/feature.d.ts.map +1 -0
- package/dist/feature.js +43 -0
- package/dist/feature.js.map +1 -0
- package/dist/hooks/api/login-auth.d.ts +29 -0
- package/dist/hooks/api/login-auth.d.ts.map +1 -0
- package/dist/hooks/api/login-auth.js +42 -0
- package/dist/hooks/api/login-auth.js.map +1 -0
- package/dist/hooks/api/logout-auth.d.ts +2 -0
- package/dist/hooks/api/logout-auth.d.ts.map +1 -0
- package/dist/hooks/api/logout-auth.js +32 -0
- package/dist/hooks/api/logout-auth.js.map +1 -0
- package/dist/hooks/api/password-reset-auth.d.ts +33 -0
- package/dist/hooks/api/password-reset-auth.d.ts.map +1 -0
- package/dist/hooks/api/password-reset-auth.js +48 -0
- package/dist/hooks/api/password-reset-auth.js.map +1 -0
- package/dist/hooks/api/refresh-auth.d.ts +2 -0
- package/dist/hooks/api/refresh-auth.d.ts.map +1 -0
- package/dist/hooks/api/refresh-auth.js +3 -0
- package/dist/hooks/api/refresh-auth.js.map +1 -0
- package/dist/hooks/api/signup-auth.d.ts +27 -0
- package/dist/hooks/api/signup-auth.d.ts.map +1 -0
- package/dist/hooks/api/signup-auth.js +15 -0
- package/dist/hooks/api/signup-auth.js.map +1 -0
- package/dist/hooks/api/verify-email-auth.d.ts +20 -0
- package/dist/hooks/api/verify-email-auth.d.ts.map +1 -0
- package/dist/hooks/api/verify-email-auth.js +48 -0
- package/dist/hooks/api/verify-email-auth.js.map +1 -0
- package/dist/hooks/api/workspace-auth.d.ts +22 -0
- package/dist/hooks/api/workspace-auth.d.ts.map +1 -0
- package/dist/hooks/api/workspace-auth.js +25 -0
- package/dist/hooks/api/workspace-auth.js.map +1 -0
- package/dist/hooks/index.d.ts +11 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +11 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/use-active-workspace.d.ts +2 -0
- package/dist/hooks/use-active-workspace.d.ts.map +1 -0
- package/dist/hooks/use-active-workspace.js +3 -0
- package/dist/hooks/use-active-workspace.js.map +1 -0
- package/dist/hooks/use-auth-state.d.ts +2 -0
- package/dist/hooks/use-auth-state.d.ts.map +1 -0
- package/dist/hooks/use-auth-state.js +3 -0
- package/dist/hooks/use-auth-state.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/pages/email-verification-resend-model.d.ts +7 -0
- package/dist/pages/email-verification-resend-model.d.ts.map +1 -0
- package/dist/pages/email-verification-resend-model.js +15 -0
- package/dist/pages/email-verification-resend-model.js.map +1 -0
- package/dist/pages/form-errors.d.ts +19 -0
- package/dist/pages/form-errors.d.ts.map +1 -0
- package/dist/pages/form-errors.js +51 -0
- package/dist/pages/form-errors.js.map +1 -0
- package/dist/pages/form-utils.d.ts +2 -0
- package/dist/pages/form-utils.d.ts.map +1 -0
- package/dist/pages/form-utils.js +28 -0
- package/dist/pages/form-utils.js.map +1 -0
- package/dist/pages/invitation-context.d.ts +31 -0
- package/dist/pages/invitation-context.d.ts.map +1 -0
- package/dist/pages/invitation-context.js +50 -0
- package/dist/pages/invitation-context.js.map +1 -0
- package/dist/pages/login-page.d.ts +2 -0
- package/dist/pages/login-page.d.ts.map +1 -0
- package/dist/pages/login-page.js +214 -0
- package/dist/pages/login-page.js.map +1 -0
- package/dist/pages/logout-page.d.ts +2 -0
- package/dist/pages/logout-page.d.ts.map +1 -0
- package/dist/pages/logout-page.js +45 -0
- package/dist/pages/logout-page.js.map +1 -0
- package/dist/pages/password-reset-page.d.ts +2 -0
- package/dist/pages/password-reset-page.d.ts.map +1 -0
- package/dist/pages/password-reset-page.js +265 -0
- package/dist/pages/password-reset-page.js.map +1 -0
- package/dist/pages/signup-page.d.ts +2 -0
- package/dist/pages/signup-page.d.ts.map +1 -0
- package/dist/pages/signup-page.js +384 -0
- package/dist/pages/signup-page.js.map +1 -0
- package/dist/pages/verify-email-page.d.ts +2 -0
- package/dist/pages/verify-email-page.d.ts.map +1 -0
- package/dist/pages/verify-email-page.js +57 -0
- package/dist/pages/verify-email-page.js.map +1 -0
- package/dist/pages/workspace-onboarding-page.d.ts +2 -0
- package/dist/pages/workspace-onboarding-page.d.ts.map +1 -0
- package/dist/pages/workspace-onboarding-page.js +294 -0
- package/dist/pages/workspace-onboarding-page.js.map +1 -0
- package/dist/routes/index.d.ts +10 -0
- package/dist/routes/index.d.ts.map +1 -0
- package/dist/routes/index.js +29 -0
- package/dist/routes/index.js.map +1 -0
- package/dist/routes/login.d.ts +5 -0
- package/dist/routes/login.d.ts.map +1 -0
- package/dist/routes/login.js +11 -0
- package/dist/routes/login.js.map +1 -0
- package/dist/routes/logout.d.ts +6 -0
- package/dist/routes/logout.d.ts.map +1 -0
- package/dist/routes/logout.js +7 -0
- package/dist/routes/logout.js.map +1 -0
- package/dist/routes/reset.d.ts +5 -0
- package/dist/routes/reset.d.ts.map +1 -0
- package/dist/routes/reset.js +11 -0
- package/dist/routes/reset.js.map +1 -0
- package/dist/routes/signup.d.ts +5 -0
- package/dist/routes/signup.d.ts.map +1 -0
- package/dist/routes/signup.js +11 -0
- package/dist/routes/signup.js.map +1 -0
- package/dist/routes/verify-email.d.ts +5 -0
- package/dist/routes/verify-email.d.ts.map +1 -0
- package/dist/routes/verify-email.js +11 -0
- package/dist/routes/verify-email.js.map +1 -0
- package/dist/routes/workspace-onboarding.d.ts +13 -0
- package/dist/routes/workspace-onboarding.d.ts.map +1 -0
- package/dist/routes/workspace-onboarding.js +18 -0
- package/dist/routes/workspace-onboarding.js.map +1 -0
- package/dist/state/auth.d.ts +10 -0
- package/dist/state/auth.d.ts.map +1 -0
- package/dist/state/auth.js +9 -0
- package/dist/state/auth.js.map +1 -0
- package/dist/state/last-workspace.d.ts +2 -0
- package/dist/state/last-workspace.d.ts.map +1 -0
- package/dist/state/last-workspace.js +3 -0
- package/dist/state/last-workspace.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +116 -0
- package/src/components/auth-guard.tsx +58 -0
- package/src/components/auth-provider.test.tsx +164 -0
- package/src/components/auth-provider.tsx +24 -0
- package/src/components/auth-shell.tsx +53 -0
- package/src/components/redirect-target.test.ts +60 -0
- package/src/components/redirect-target.ts +21 -0
- package/src/components/workspace-crumb.tsx +2 -0
- package/src/components/workspace-switcher.stories.tsx +169 -0
- package/src/components/workspace-switcher.tsx +76 -0
- package/src/feature.ts +18 -0
- package/src/hooks/api/login-auth.test.ts +45 -0
- package/src/hooks/api/login-auth.ts +41 -0
- package/src/hooks/api/logout-auth.ts +26 -0
- package/src/hooks/api/password-reset-auth.test.ts +68 -0
- package/src/hooks/api/password-reset-auth.ts +51 -0
- package/src/hooks/api/refresh-auth.test.ts +37 -0
- package/src/hooks/api/refresh-auth.ts +5 -0
- package/src/hooks/api/signup-auth.ts +11 -0
- package/src/hooks/api/verify-email-auth.ts +55 -0
- package/src/hooks/api/workspace-auth.test.ts +40 -0
- package/src/hooks/api/workspace-auth.ts +25 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/use-active-workspace.ts +1 -0
- package/src/hooks/use-auth-state.ts +1 -0
- package/src/index.ts +18 -0
- package/src/pages/email-verification-resend-model.test.ts +52 -0
- package/src/pages/email-verification-resend-model.ts +21 -0
- package/src/pages/form-errors.test.ts +114 -0
- package/src/pages/form-errors.ts +45 -0
- package/src/pages/form-utils.test.ts +37 -0
- package/src/pages/form-utils.ts +29 -0
- package/src/pages/invitation-context.ts +57 -0
- package/src/pages/login-page.test.tsx +128 -0
- package/src/pages/login-page.tsx +184 -0
- package/src/pages/logout-page.tsx +33 -0
- package/src/pages/password-reset-page.test.tsx +109 -0
- package/src/pages/password-reset-page.tsx +222 -0
- package/src/pages/signup-page.test.tsx +289 -0
- package/src/pages/signup-page.tsx +354 -0
- package/src/pages/verify-email-page.test.tsx +82 -0
- package/src/pages/verify-email-page.tsx +48 -0
- package/src/pages/workspace-onboarding-page.test.tsx +161 -0
- package/src/pages/workspace-onboarding-page.tsx +213 -0
- package/src/routes/index.tsx +18 -0
- package/src/routes/login.tsx +11 -0
- package/src/routes/logout.tsx +4 -0
- package/src/routes/reset.tsx +11 -0
- package/src/routes/signup.tsx +11 -0
- package/src/routes/verify-email.tsx +11 -0
- package/src/routes/workspace-onboarding.tsx +14 -0
- package/src/state/auth.ts +18 -0
- package/src/state/last-workspace.test.ts +63 -0
- package/src/state/last-workspace.ts +5 -0
- package/test/factories/user.ts +17 -0
- package/test/pages.tsx +83 -0
- package/test/setup.ts +3 -0
- package/test/utils.ts +11 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vercel.json +8 -0
- package/vitest.config.ts +71 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { loginBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
+
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
4
|
+
import { Callout } from '@shipfox/react-ui/callout';
|
|
5
|
+
import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
|
|
6
|
+
import { Icon } from '@shipfox/react-ui/icon';
|
|
7
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
|
+
import { useForm } from '@tanstack/react-form';
|
|
9
|
+
import { Link, useSearch } from '@tanstack/react-router';
|
|
10
|
+
import { useAtom } from 'jotai';
|
|
11
|
+
import { useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { AuthShell } from '#/components/auth-shell.js';
|
|
13
|
+
import { useLoginAuth } from '#hooks/api/login-auth.js';
|
|
14
|
+
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
15
|
+
import { loginErrorToFormError } from './form-errors.js';
|
|
16
|
+
import { extractInvitationToken, pendingInvitation, useInvitationContext } from './invitation-context.js';
|
|
17
|
+
export function LoginPage() {
|
|
18
|
+
const login = useLoginAuth();
|
|
19
|
+
const search = useSearch({
|
|
20
|
+
strict: false
|
|
21
|
+
});
|
|
22
|
+
const invitationToken = extractInvitationToken(search.redirect);
|
|
23
|
+
const invitationPreview = useInvitationContext(invitationToken);
|
|
24
|
+
const invitationPending = pendingInvitation(invitationPreview.data);
|
|
25
|
+
const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);
|
|
26
|
+
const [formError, setFormError] = useState();
|
|
27
|
+
const draftRef = useRef(authFormDraft);
|
|
28
|
+
draftRef.current = authFormDraft;
|
|
29
|
+
// Set just before clearing the draft on success so the unmount cleanup
|
|
30
|
+
// below does not repersist the just-submitted credentials.
|
|
31
|
+
const skipDraftPersistRef = useRef(false);
|
|
32
|
+
const form = useForm({
|
|
33
|
+
defaultValues: {
|
|
34
|
+
email: authFormDraft.email,
|
|
35
|
+
password: authFormDraft.password
|
|
36
|
+
},
|
|
37
|
+
onSubmit: async ({ value })=>{
|
|
38
|
+
setFormError(undefined);
|
|
39
|
+
try {
|
|
40
|
+
await login.mutateAsync(value);
|
|
41
|
+
skipDraftPersistRef.current = true;
|
|
42
|
+
setAuthFormDraft(initialAuthFormDraft);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const mapped = loginErrorToFormError(error);
|
|
45
|
+
if (mapped.kind === 'field') {
|
|
46
|
+
form.setFieldMeta(mapped.field, (prev)=>({
|
|
47
|
+
...prev,
|
|
48
|
+
errorMap: {
|
|
49
|
+
...prev.errorMap,
|
|
50
|
+
onServer: mapped.message
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
} else {
|
|
54
|
+
setFormError(mapped.message);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Lock the email field when arriving from an invitation link so the user
|
|
60
|
+
// can't log in as a different account than the one the invitation targets.
|
|
61
|
+
useEffect(()=>{
|
|
62
|
+
if (invitationPending && form.state.values.email !== invitationPending.email) {
|
|
63
|
+
form.setFieldValue('email', invitationPending.email);
|
|
64
|
+
setAuthFormDraft((current)=>({
|
|
65
|
+
...current,
|
|
66
|
+
email: invitationPending.email
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
}, [
|
|
70
|
+
invitationPending,
|
|
71
|
+
form,
|
|
72
|
+
setAuthFormDraft
|
|
73
|
+
]);
|
|
74
|
+
// Sync TanStack Form values back into the Jotai draft on unmount so a
|
|
75
|
+
// navigation to /signup or /reset preserves what the user typed. Skipped
|
|
76
|
+
// after a successful login because we just intentionally cleared the draft.
|
|
77
|
+
useEffect(()=>{
|
|
78
|
+
return ()=>{
|
|
79
|
+
if (skipDraftPersistRef.current) return;
|
|
80
|
+
const { email, password } = form.state.values;
|
|
81
|
+
if (email !== draftRef.current.email || password !== draftRef.current.password) {
|
|
82
|
+
setAuthFormDraft({
|
|
83
|
+
email,
|
|
84
|
+
password
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}, [
|
|
89
|
+
form,
|
|
90
|
+
setAuthFormDraft
|
|
91
|
+
]);
|
|
92
|
+
const isInvitationEmailLocked = Boolean(invitationPending);
|
|
93
|
+
const invitationRedirect = invitationToken ? `/invitations/accept?token=${encodeURIComponent(invitationToken)}` : undefined;
|
|
94
|
+
const headerTitle = invitationPending ? `Join ${invitationPending.workspace_name}` : 'Connect to Shipfox';
|
|
95
|
+
const headerDescription = invitationPending ? 'Log in to accept your invitation.' : 'Log in to access Shipfox.';
|
|
96
|
+
function persistDraft() {
|
|
97
|
+
const { email, password } = form.state.values;
|
|
98
|
+
setAuthFormDraft({
|
|
99
|
+
email,
|
|
100
|
+
password
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return /*#__PURE__*/ _jsxs(AuthShell, {
|
|
104
|
+
title: headerTitle,
|
|
105
|
+
description: headerDescription,
|
|
106
|
+
children: [
|
|
107
|
+
/*#__PURE__*/ _jsxs("form", {
|
|
108
|
+
className: "flex flex-col gap-18",
|
|
109
|
+
noValidate: true,
|
|
110
|
+
onSubmit: (event)=>{
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
event.stopPropagation();
|
|
113
|
+
void form.handleSubmit();
|
|
114
|
+
},
|
|
115
|
+
children: [
|
|
116
|
+
formError ? /*#__PURE__*/ _jsx(Callout, {
|
|
117
|
+
role: "alert",
|
|
118
|
+
type: "error",
|
|
119
|
+
children: formError
|
|
120
|
+
}) : null,
|
|
121
|
+
/*#__PURE__*/ _jsx(form.Field, {
|
|
122
|
+
name: "email",
|
|
123
|
+
validators: {
|
|
124
|
+
onBlur: loginBodySchema.shape.email,
|
|
125
|
+
onSubmit: loginBodySchema.shape.email
|
|
126
|
+
},
|
|
127
|
+
children: (field)=>/*#__PURE__*/ _jsx(FormField, {
|
|
128
|
+
label: "Email",
|
|
129
|
+
id: "email",
|
|
130
|
+
error: fieldError(field),
|
|
131
|
+
children: /*#__PURE__*/ _jsx(FormFieldInput, {
|
|
132
|
+
autoComplete: "email",
|
|
133
|
+
name: "email",
|
|
134
|
+
type: "email",
|
|
135
|
+
value: field.state.value,
|
|
136
|
+
onChange: (event)=>field.handleChange(event.target.value),
|
|
137
|
+
onBlur: ()=>{
|
|
138
|
+
field.handleBlur();
|
|
139
|
+
persistDraft();
|
|
140
|
+
},
|
|
141
|
+
readOnly: isInvitationEmailLocked,
|
|
142
|
+
iconRight: isInvitationEmailLocked ? /*#__PURE__*/ _jsx(Icon, {
|
|
143
|
+
"aria-hidden": "true",
|
|
144
|
+
className: "size-16 text-foreground-neutral-disabled",
|
|
145
|
+
name: "lockLine"
|
|
146
|
+
}) : undefined
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
}),
|
|
150
|
+
/*#__PURE__*/ _jsx(form.Field, {
|
|
151
|
+
name: "password",
|
|
152
|
+
validators: {
|
|
153
|
+
onBlur: loginBodySchema.shape.password,
|
|
154
|
+
onSubmit: loginBodySchema.shape.password
|
|
155
|
+
},
|
|
156
|
+
children: (field)=>/*#__PURE__*/ _jsx(FormField, {
|
|
157
|
+
label: "Password",
|
|
158
|
+
id: "password",
|
|
159
|
+
error: fieldError(field),
|
|
160
|
+
children: /*#__PURE__*/ _jsx(FormFieldInput, {
|
|
161
|
+
autoComplete: "current-password",
|
|
162
|
+
name: "password",
|
|
163
|
+
type: "password",
|
|
164
|
+
value: field.state.value,
|
|
165
|
+
onChange: (event)=>field.handleChange(event.target.value),
|
|
166
|
+
onBlur: ()=>{
|
|
167
|
+
field.handleBlur();
|
|
168
|
+
persistDraft();
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
}),
|
|
173
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
174
|
+
asChild: true,
|
|
175
|
+
variant: "subtle",
|
|
176
|
+
className: "-mt-8 self-end",
|
|
177
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
178
|
+
to: "/auth/reset",
|
|
179
|
+
children: "Forgot password?"
|
|
180
|
+
})
|
|
181
|
+
}),
|
|
182
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
183
|
+
className: "w-full",
|
|
184
|
+
isLoading: login.isPending,
|
|
185
|
+
type: "submit",
|
|
186
|
+
children: login.isPending ? 'Logging in...' : 'Log in'
|
|
187
|
+
})
|
|
188
|
+
]
|
|
189
|
+
}),
|
|
190
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
191
|
+
size: "sm",
|
|
192
|
+
className: "text-center text-foreground-neutral-subtle",
|
|
193
|
+
children: [
|
|
194
|
+
"New to Shipfox?",
|
|
195
|
+
' ',
|
|
196
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
197
|
+
asChild: true,
|
|
198
|
+
variant: "interactive",
|
|
199
|
+
underline: true,
|
|
200
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
201
|
+
to: "/auth/signup",
|
|
202
|
+
search: invitationRedirect ? {
|
|
203
|
+
redirect: invitationRedirect
|
|
204
|
+
} : undefined,
|
|
205
|
+
children: "Create an account"
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
]
|
|
209
|
+
})
|
|
210
|
+
]
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
//# sourceMappingURL=login-page.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout-page.d.ts","sourceRoot":"","sources":["../../src/pages/logout-page.tsx"],"names":[],"mappings":"AAOA,wBAAgB,UAAU,gCAyBzB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
3
|
+
import { useNavigate, useRouter, useSearch } from '@tanstack/react-router';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { AuthShell } from '#/components/auth-shell.js';
|
|
6
|
+
import { sanitizeRedirectPath } from '#/components/redirect-target.js';
|
|
7
|
+
import { useLogoutAuth } from '#hooks/api/logout-auth.js';
|
|
8
|
+
export function LogoutPage() {
|
|
9
|
+
const logout = useLogoutAuth();
|
|
10
|
+
const navigate = useNavigate();
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
const search = useSearch({
|
|
13
|
+
strict: false
|
|
14
|
+
});
|
|
15
|
+
const target = sanitizeRedirectPath(search.redirect);
|
|
16
|
+
useEffect(()=>{
|
|
17
|
+
logout.mutateAsync().finally(()=>{
|
|
18
|
+
if (target !== undefined) {
|
|
19
|
+
// Same-origin runtime path; let history drive route resolution.
|
|
20
|
+
router.history.replace(target);
|
|
21
|
+
} else {
|
|
22
|
+
navigate({
|
|
23
|
+
to: '/auth/login',
|
|
24
|
+
replace: true
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}, [
|
|
29
|
+
logout.mutateAsync,
|
|
30
|
+
navigate,
|
|
31
|
+
router,
|
|
32
|
+
target
|
|
33
|
+
]);
|
|
34
|
+
return /*#__PURE__*/ _jsx(AuthShell, {
|
|
35
|
+
title: "Logging out",
|
|
36
|
+
description: "Ending your Shipfox session.",
|
|
37
|
+
children: /*#__PURE__*/ _jsx(Text, {
|
|
38
|
+
size: "sm",
|
|
39
|
+
className: "text-center text-foreground-neutral-subtle",
|
|
40
|
+
children: "You will be sent back to login in a moment."
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=logout-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pages/logout-page.tsx"],"sourcesContent":["import {Text} from '@shipfox/react-ui/typography';\nimport {useNavigate, useRouter, useSearch} from '@tanstack/react-router';\nimport {useEffect} from 'react';\nimport {AuthShell} from '#/components/auth-shell.js';\nimport {sanitizeRedirectPath} from '#/components/redirect-target.js';\nimport {useLogoutAuth} from '#hooks/api/logout-auth.js';\n\nexport function LogoutPage() {\n const logout = useLogoutAuth();\n const navigate = useNavigate();\n const router = useRouter();\n const search = useSearch({strict: false}) as {redirect?: unknown};\n const target = sanitizeRedirectPath(search.redirect);\n\n useEffect(() => {\n logout.mutateAsync().finally(() => {\n if (target !== undefined) {\n // Same-origin runtime path; let history drive route resolution.\n router.history.replace(target);\n } else {\n navigate({to: '/auth/login', replace: true});\n }\n });\n }, [logout.mutateAsync, navigate, router, target]);\n\n return (\n <AuthShell title=\"Logging out\" description=\"Ending your Shipfox session.\">\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n You will be sent back to login in a moment.\n </Text>\n </AuthShell>\n );\n}\n"],"names":["Text","useNavigate","useRouter","useSearch","useEffect","AuthShell","sanitizeRedirectPath","useLogoutAuth","LogoutPage","logout","navigate","router","search","strict","target","redirect","mutateAsync","finally","undefined","history","replace","to","title","description","size","className"],"mappings":";AAAA,SAAQA,IAAI,QAAO,+BAA+B;AAClD,SAAQC,WAAW,EAAEC,SAAS,EAAEC,SAAS,QAAO,yBAAyB;AACzE,SAAQC,SAAS,QAAO,QAAQ;AAChC,SAAQC,SAAS,QAAO,6BAA6B;AACrD,SAAQC,oBAAoB,QAAO,kCAAkC;AACrE,SAAQC,aAAa,QAAO,4BAA4B;AAExD,OAAO,SAASC;IACd,MAAMC,SAASF;IACf,MAAMG,WAAWT;IACjB,MAAMU,SAAST;IACf,MAAMU,SAAST,UAAU;QAACU,QAAQ;IAAK;IACvC,MAAMC,SAASR,qBAAqBM,OAAOG,QAAQ;IAEnDX,UAAU;QACRK,OAAOO,WAAW,GAAGC,OAAO,CAAC;YAC3B,IAAIH,WAAWI,WAAW;gBACxB,gEAAgE;gBAChEP,OAAOQ,OAAO,CAACC,OAAO,CAACN;YACzB,OAAO;gBACLJ,SAAS;oBAACW,IAAI;oBAAeD,SAAS;gBAAI;YAC5C;QACF;IACF,GAAG;QAACX,OAAOO,WAAW;QAAEN;QAAUC;QAAQG;KAAO;IAEjD,qBACE,KAACT;QAAUiB,OAAM;QAAcC,aAAY;kBACzC,cAAA,KAACvB;YAAKwB,MAAK;YAAKC,WAAU;sBAA6C;;;AAK7E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"password-reset-page.d.ts","sourceRoot":"","sources":["../../src/pages/password-reset-page.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,iBAAiB,gCAShC"}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { passwordResetConfirmBodySchema, passwordResetRequestBodySchema } from '@shipfox/api-auth-dto';
|
|
3
|
+
import { Button, ButtonLink } from '@shipfox/react-ui/button';
|
|
4
|
+
import { Callout } from '@shipfox/react-ui/callout';
|
|
5
|
+
import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
|
|
6
|
+
import { toast } from '@shipfox/react-ui/toast';
|
|
7
|
+
import { Text } from '@shipfox/react-ui/typography';
|
|
8
|
+
import { useForm } from '@tanstack/react-form';
|
|
9
|
+
import { Link, useNavigate, useSearch } from '@tanstack/react-router';
|
|
10
|
+
import { useAtom } from 'jotai';
|
|
11
|
+
import { useEffect, useRef, useState } from 'react';
|
|
12
|
+
import { AuthShell } from '#/components/auth-shell.js';
|
|
13
|
+
import { useConfirmPasswordResetAuth, useRequestPasswordResetAuth } from '#hooks/api/password-reset-auth.js';
|
|
14
|
+
import { useRefreshAuth } from '#hooks/api/refresh-auth.js';
|
|
15
|
+
import { authFormDraftAtom, initialAuthFormDraft } from '#state/auth.js';
|
|
16
|
+
import { passwordResetConfirmErrorToFormError, passwordResetRequestErrorToFormError } from './form-errors.js';
|
|
17
|
+
export function PasswordResetPage() {
|
|
18
|
+
const search = useSearch({
|
|
19
|
+
strict: false
|
|
20
|
+
});
|
|
21
|
+
const token = typeof search.token === 'string' ? search.token : undefined;
|
|
22
|
+
if (token) {
|
|
23
|
+
return /*#__PURE__*/ _jsx(PasswordResetConfirm, {
|
|
24
|
+
token: token
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return /*#__PURE__*/ _jsx(PasswordResetRequest, {});
|
|
28
|
+
}
|
|
29
|
+
function PasswordResetRequest() {
|
|
30
|
+
const requestPasswordReset = useRequestPasswordResetAuth();
|
|
31
|
+
const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);
|
|
32
|
+
const [submittedEmail, setSubmittedEmail] = useState();
|
|
33
|
+
const [formError, setFormError] = useState();
|
|
34
|
+
const draftRef = useRef(authFormDraft);
|
|
35
|
+
draftRef.current = authFormDraft;
|
|
36
|
+
const form = useForm({
|
|
37
|
+
defaultValues: {
|
|
38
|
+
email: authFormDraft.email
|
|
39
|
+
},
|
|
40
|
+
onSubmit: async ({ value })=>{
|
|
41
|
+
setFormError(undefined);
|
|
42
|
+
try {
|
|
43
|
+
const parsed = passwordResetRequestBodySchema.parse(value);
|
|
44
|
+
await requestPasswordReset.mutateAsync(parsed);
|
|
45
|
+
setSubmittedEmail(parsed.email);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
const mapped = passwordResetRequestErrorToFormError(error);
|
|
48
|
+
setFormError(mapped.message);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
useEffect(()=>{
|
|
53
|
+
return ()=>{
|
|
54
|
+
const { email } = form.state.values;
|
|
55
|
+
if (email !== draftRef.current.email) {
|
|
56
|
+
setAuthFormDraft((current)=>({
|
|
57
|
+
...current,
|
|
58
|
+
email
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}, [
|
|
63
|
+
form,
|
|
64
|
+
setAuthFormDraft
|
|
65
|
+
]);
|
|
66
|
+
if (submittedEmail) {
|
|
67
|
+
return /*#__PURE__*/ _jsxs(AuthShell, {
|
|
68
|
+
title: "Check your email",
|
|
69
|
+
description: `We sent reset instructions to ${submittedEmail}.`,
|
|
70
|
+
children: [
|
|
71
|
+
/*#__PURE__*/ _jsx(Callout, {
|
|
72
|
+
role: "alert",
|
|
73
|
+
type: "success",
|
|
74
|
+
children: "If a Shipfox account exists for that email, the reset link will arrive shortly."
|
|
75
|
+
}),
|
|
76
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
77
|
+
className: "w-full",
|
|
78
|
+
variant: "secondary",
|
|
79
|
+
type: "button",
|
|
80
|
+
onClick: ()=>setSubmittedEmail(undefined),
|
|
81
|
+
children: "Use another email"
|
|
82
|
+
}),
|
|
83
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
84
|
+
size: "sm",
|
|
85
|
+
className: "text-center text-foreground-neutral-subtle",
|
|
86
|
+
children: [
|
|
87
|
+
"Remembered it?",
|
|
88
|
+
' ',
|
|
89
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
90
|
+
asChild: true,
|
|
91
|
+
variant: "interactive",
|
|
92
|
+
underline: true,
|
|
93
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
94
|
+
to: "/auth/login",
|
|
95
|
+
children: "Log in"
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
]
|
|
99
|
+
})
|
|
100
|
+
]
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return /*#__PURE__*/ _jsxs(AuthShell, {
|
|
104
|
+
title: "Reset your password",
|
|
105
|
+
description: "Enter your email to get a reset link.",
|
|
106
|
+
children: [
|
|
107
|
+
/*#__PURE__*/ _jsxs("form", {
|
|
108
|
+
className: "flex flex-col gap-18",
|
|
109
|
+
noValidate: true,
|
|
110
|
+
onSubmit: (event)=>{
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
event.stopPropagation();
|
|
113
|
+
void form.handleSubmit();
|
|
114
|
+
},
|
|
115
|
+
children: [
|
|
116
|
+
formError ? /*#__PURE__*/ _jsx(Callout, {
|
|
117
|
+
role: "alert",
|
|
118
|
+
type: "error",
|
|
119
|
+
children: formError
|
|
120
|
+
}) : null,
|
|
121
|
+
/*#__PURE__*/ _jsx(form.Field, {
|
|
122
|
+
name: "email",
|
|
123
|
+
validators: {
|
|
124
|
+
onBlur: passwordResetRequestBodySchema.shape.email,
|
|
125
|
+
onSubmit: passwordResetRequestBodySchema.shape.email
|
|
126
|
+
},
|
|
127
|
+
children: (field)=>/*#__PURE__*/ _jsx(FormField, {
|
|
128
|
+
label: "Email",
|
|
129
|
+
id: "email",
|
|
130
|
+
error: fieldError(field),
|
|
131
|
+
children: /*#__PURE__*/ _jsx(FormFieldInput, {
|
|
132
|
+
autoComplete: "email",
|
|
133
|
+
name: "email",
|
|
134
|
+
type: "email",
|
|
135
|
+
value: field.state.value,
|
|
136
|
+
onChange: (event)=>field.handleChange(event.target.value),
|
|
137
|
+
onBlur: ()=>{
|
|
138
|
+
field.handleBlur();
|
|
139
|
+
setAuthFormDraft((current)=>({
|
|
140
|
+
...current,
|
|
141
|
+
email: field.state.value
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
}),
|
|
147
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
148
|
+
className: "w-full",
|
|
149
|
+
isLoading: requestPasswordReset.isPending,
|
|
150
|
+
type: "submit",
|
|
151
|
+
children: requestPasswordReset.isPending ? 'Sending link...' : 'Send reset link'
|
|
152
|
+
})
|
|
153
|
+
]
|
|
154
|
+
}),
|
|
155
|
+
/*#__PURE__*/ _jsxs(Text, {
|
|
156
|
+
size: "sm",
|
|
157
|
+
className: "text-center text-foreground-neutral-subtle",
|
|
158
|
+
children: [
|
|
159
|
+
"Remembered it?",
|
|
160
|
+
' ',
|
|
161
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
162
|
+
asChild: true,
|
|
163
|
+
variant: "interactive",
|
|
164
|
+
underline: true,
|
|
165
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
166
|
+
to: "/auth/login",
|
|
167
|
+
children: "Log in"
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
]
|
|
171
|
+
})
|
|
172
|
+
]
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function PasswordResetConfirm({ token }) {
|
|
176
|
+
const confirmPasswordReset = useConfirmPasswordResetAuth();
|
|
177
|
+
const refreshAuth = useRefreshAuth();
|
|
178
|
+
const navigate = useNavigate();
|
|
179
|
+
const [, setAuthFormDraft] = useAtom(authFormDraftAtom);
|
|
180
|
+
const [formError, setFormError] = useState();
|
|
181
|
+
const form = useForm({
|
|
182
|
+
defaultValues: {
|
|
183
|
+
new_password: ''
|
|
184
|
+
},
|
|
185
|
+
onSubmit: async ({ value })=>{
|
|
186
|
+
setFormError(undefined);
|
|
187
|
+
try {
|
|
188
|
+
const body = passwordResetConfirmBodySchema.parse({
|
|
189
|
+
token,
|
|
190
|
+
new_password: value.new_password
|
|
191
|
+
});
|
|
192
|
+
await confirmPasswordReset.mutateAsync(body);
|
|
193
|
+
await refreshAuth();
|
|
194
|
+
setAuthFormDraft(initialAuthFormDraft);
|
|
195
|
+
toast.success('Your password has been changed. You are now logged in.');
|
|
196
|
+
await navigate({
|
|
197
|
+
to: '/',
|
|
198
|
+
replace: true
|
|
199
|
+
});
|
|
200
|
+
} catch (error) {
|
|
201
|
+
const mapped = passwordResetConfirmErrorToFormError(error);
|
|
202
|
+
setFormError(mapped.message);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
return /*#__PURE__*/ _jsxs(AuthShell, {
|
|
207
|
+
title: "Set a new password",
|
|
208
|
+
description: "Choose a password for your Shipfox account.",
|
|
209
|
+
children: [
|
|
210
|
+
/*#__PURE__*/ _jsxs("form", {
|
|
211
|
+
className: "flex flex-col gap-18",
|
|
212
|
+
noValidate: true,
|
|
213
|
+
onSubmit: (event)=>{
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
event.stopPropagation();
|
|
216
|
+
void form.handleSubmit();
|
|
217
|
+
},
|
|
218
|
+
children: [
|
|
219
|
+
formError ? /*#__PURE__*/ _jsx(Callout, {
|
|
220
|
+
role: "alert",
|
|
221
|
+
type: "error",
|
|
222
|
+
children: formError
|
|
223
|
+
}) : null,
|
|
224
|
+
/*#__PURE__*/ _jsx(form.Field, {
|
|
225
|
+
name: "new_password",
|
|
226
|
+
validators: {
|
|
227
|
+
onBlur: passwordResetConfirmBodySchema.shape.new_password,
|
|
228
|
+
onSubmit: passwordResetConfirmBodySchema.shape.new_password
|
|
229
|
+
},
|
|
230
|
+
children: (field)=>/*#__PURE__*/ _jsx(FormField, {
|
|
231
|
+
label: "New password",
|
|
232
|
+
id: "new-password",
|
|
233
|
+
error: fieldError(field),
|
|
234
|
+
children: /*#__PURE__*/ _jsx(FormFieldInput, {
|
|
235
|
+
autoComplete: "new-password",
|
|
236
|
+
name: "new_password",
|
|
237
|
+
type: "password",
|
|
238
|
+
value: field.state.value,
|
|
239
|
+
onChange: (event)=>field.handleChange(event.target.value),
|
|
240
|
+
onBlur: field.handleBlur
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
}),
|
|
244
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
245
|
+
className: "w-full",
|
|
246
|
+
isLoading: confirmPasswordReset.isPending,
|
|
247
|
+
type: "submit",
|
|
248
|
+
children: confirmPasswordReset.isPending ? 'Updating password...' : 'Update password'
|
|
249
|
+
})
|
|
250
|
+
]
|
|
251
|
+
}),
|
|
252
|
+
/*#__PURE__*/ _jsx(ButtonLink, {
|
|
253
|
+
asChild: true,
|
|
254
|
+
variant: "subtle",
|
|
255
|
+
className: "self-center",
|
|
256
|
+
children: /*#__PURE__*/ _jsx(Link, {
|
|
257
|
+
to: "/auth/login",
|
|
258
|
+
children: "Back to login"
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
]
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
//# sourceMappingURL=password-reset-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/pages/password-reset-page.tsx"],"sourcesContent":["import {\n passwordResetConfirmBodySchema,\n passwordResetRequestBodySchema,\n} 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 {toast} from '@shipfox/react-ui/toast';\nimport {Text} from '@shipfox/react-ui/typography';\nimport {useForm} from '@tanstack/react-form';\nimport {Link, useNavigate, useSearch} from '@tanstack/react-router';\nimport {useAtom} from 'jotai';\nimport {useEffect, useRef, useState} from 'react';\nimport {AuthShell} from '#/components/auth-shell.js';\nimport {\n useConfirmPasswordResetAuth,\n useRequestPasswordResetAuth,\n} from '#hooks/api/password-reset-auth.js';\nimport {useRefreshAuth} from '#hooks/api/refresh-auth.js';\nimport {authFormDraftAtom, initialAuthFormDraft} from '#state/auth.js';\nimport {\n passwordResetConfirmErrorToFormError,\n passwordResetRequestErrorToFormError,\n} from './form-errors.js';\n\nexport function PasswordResetPage() {\n const search = useSearch({strict: false});\n const token = typeof search.token === 'string' ? search.token : undefined;\n\n if (token) {\n return <PasswordResetConfirm token={token} />;\n }\n\n return <PasswordResetRequest />;\n}\n\nfunction PasswordResetRequest() {\n const requestPasswordReset = useRequestPasswordResetAuth();\n const [authFormDraft, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [submittedEmail, setSubmittedEmail] = useState<string | undefined>();\n const [formError, setFormError] = useState<string | undefined>();\n const draftRef = useRef(authFormDraft);\n draftRef.current = authFormDraft;\n\n const form = useForm({\n defaultValues: {email: authFormDraft.email},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const parsed = passwordResetRequestBodySchema.parse(value);\n await requestPasswordReset.mutateAsync(parsed);\n setSubmittedEmail(parsed.email);\n } catch (error) {\n const mapped = passwordResetRequestErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n useEffect(() => {\n return () => {\n const {email} = form.state.values;\n if (email !== draftRef.current.email) {\n setAuthFormDraft((current) => ({...current, email}));\n }\n };\n }, [form, setAuthFormDraft]);\n\n if (submittedEmail) {\n return (\n <AuthShell\n title=\"Check your email\"\n description={`We sent reset instructions to ${submittedEmail}.`}\n >\n <Callout role=\"alert\" type=\"success\">\n If a Shipfox account exists for that email, the reset link will arrive shortly.\n </Callout>\n <Button\n className=\"w-full\"\n variant=\"secondary\"\n type=\"button\"\n onClick={() => setSubmittedEmail(undefined)}\n >\n Use another email\n </Button>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n }\n\n return (\n <AuthShell title=\"Reset your password\" description=\"Enter your email to get a reset link.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"email\"\n validators={{\n onBlur: passwordResetRequestBodySchema.shape.email,\n onSubmit: passwordResetRequestBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField label=\"Email\" id=\"email\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"email\"\n name=\"email\"\n type=\"email\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={() => {\n field.handleBlur();\n setAuthFormDraft((current) => ({...current, email: field.state.value}));\n }}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={requestPasswordReset.isPending} type=\"submit\">\n {requestPasswordReset.isPending ? 'Sending link...' : 'Send reset link'}\n </Button>\n </form>\n <Text size=\"sm\" className=\"text-center text-foreground-neutral-subtle\">\n Remembered it?{' '}\n <ButtonLink asChild variant=\"interactive\" underline>\n <Link to=\"/auth/login\">Log in</Link>\n </ButtonLink>\n </Text>\n </AuthShell>\n );\n}\n\nfunction PasswordResetConfirm({token}: {token: string}) {\n const confirmPasswordReset = useConfirmPasswordResetAuth();\n const refreshAuth = useRefreshAuth();\n const navigate = useNavigate();\n const [, setAuthFormDraft] = useAtom(authFormDraftAtom);\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {new_password: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const body = passwordResetConfirmBodySchema.parse({\n token,\n new_password: value.new_password,\n });\n await confirmPasswordReset.mutateAsync(body);\n await refreshAuth();\n setAuthFormDraft(initialAuthFormDraft);\n toast.success('Your password has been changed. You are now logged in.');\n await navigate({to: '/', replace: true});\n } catch (error) {\n const mapped = passwordResetConfirmErrorToFormError(error);\n setFormError(mapped.message);\n }\n },\n });\n\n return (\n <AuthShell title=\"Set a new password\" description=\"Choose a password for your Shipfox account.\">\n <form\n className=\"flex flex-col gap-18\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form.Field\n name=\"new_password\"\n validators={{\n onBlur: passwordResetConfirmBodySchema.shape.new_password,\n onSubmit: passwordResetConfirmBodySchema.shape.new_password,\n }}\n >\n {(field) => (\n <FormField label=\"New password\" id=\"new-password\" error={fieldError(field)}>\n <FormFieldInput\n autoComplete=\"new-password\"\n name=\"new_password\"\n type=\"password\"\n value={field.state.value}\n onChange={(event) => field.handleChange(event.target.value)}\n onBlur={field.handleBlur}\n />\n </FormField>\n )}\n </form.Field>\n <Button className=\"w-full\" isLoading={confirmPasswordReset.isPending} type=\"submit\">\n {confirmPasswordReset.isPending ? 'Updating password...' : 'Update password'}\n </Button>\n </form>\n <ButtonLink asChild variant=\"subtle\" className=\"self-center\">\n <Link to=\"/auth/login\">Back to login</Link>\n </ButtonLink>\n </AuthShell>\n );\n}\n"],"names":["passwordResetConfirmBodySchema","passwordResetRequestBodySchema","Button","ButtonLink","Callout","FormField","FormFieldInput","fieldError","toast","Text","useForm","Link","useNavigate","useSearch","useAtom","useEffect","useRef","useState","AuthShell","useConfirmPasswordResetAuth","useRequestPasswordResetAuth","useRefreshAuth","authFormDraftAtom","initialAuthFormDraft","passwordResetConfirmErrorToFormError","passwordResetRequestErrorToFormError","PasswordResetPage","search","strict","token","undefined","PasswordResetConfirm","PasswordResetRequest","requestPasswordReset","authFormDraft","setAuthFormDraft","submittedEmail","setSubmittedEmail","formError","setFormError","draftRef","current","form","defaultValues","email","onSubmit","value","parsed","parse","mutateAsync","error","mapped","message","state","values","title","description","role","type","className","variant","onClick","size","asChild","underline","to","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","name","validators","onBlur","shape","field","label","id","autoComplete","onChange","handleChange","target","handleBlur","isLoading","isPending","confirmPasswordReset","refreshAuth","navigate","new_password","body","success","replace"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup-page.d.ts","sourceRoot":"","sources":["../../src/pages/signup-page.tsx"],"names":[],"mappings":"AA8BA,wBAAgB,UAAU,gCAmUzB"}
|