@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,168 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { argosScreenshot } from '@argos-ci/storybook/vitest';
|
|
3
|
+
import { createMemoryHistory, createRootRoute, createRoute, createRouter, Outlet, RouterProvider } from '@tanstack/react-router';
|
|
4
|
+
import { createStore, Provider as JotaiProvider } from 'jotai';
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
|
7
|
+
import { authStateAtom } from '#state/auth.js';
|
|
8
|
+
import { WorkspaceSwitcher } from './workspace-switcher.js';
|
|
9
|
+
const ACTIVE_WORKSPACE_ID = '00000000-0000-4000-8000-000000000001';
|
|
10
|
+
const WORKSPACES = [
|
|
11
|
+
workspace(ACTIVE_WORKSPACE_ID, 'Alpha Workspace', 1),
|
|
12
|
+
workspace('00000000-0000-4000-8000-000000000002', 'Beta Workspace', 2),
|
|
13
|
+
workspace('00000000-0000-4000-8000-000000000003', 'Gamma Workspace', 3),
|
|
14
|
+
workspace('00000000-0000-4000-8000-000000000004', 'Delta Workspace', 4)
|
|
15
|
+
];
|
|
16
|
+
const SINGLE_WORKSPACE = [
|
|
17
|
+
workspace(ACTIVE_WORKSPACE_ID, 'Alpha Workspace', 1)
|
|
18
|
+
];
|
|
19
|
+
const MANY_WORKSPACES = Array.from({
|
|
20
|
+
length: 20
|
|
21
|
+
}, (_, index)=>workspace(`00000000-0000-4000-8000-${String(index + 1).padStart(12, '0')}`, `Workspace ${String(index + 1).padStart(2, '0')}`, index + 1));
|
|
22
|
+
function workspace(id, name, index) {
|
|
23
|
+
return {
|
|
24
|
+
id,
|
|
25
|
+
name,
|
|
26
|
+
membershipId: `10000000-0000-4000-8000-${String(index).padStart(12, '0')}`
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function WorkspaceSwitcherStory({ activeWorkspaceId, workspaces }) {
|
|
30
|
+
const store = useMemo(()=>{
|
|
31
|
+
const nextStore = createStore();
|
|
32
|
+
nextStore.set(authStateAtom, {
|
|
33
|
+
status: 'authenticated',
|
|
34
|
+
workspaces
|
|
35
|
+
});
|
|
36
|
+
return nextStore;
|
|
37
|
+
}, [
|
|
38
|
+
workspaces
|
|
39
|
+
]);
|
|
40
|
+
const router = useMemo(()=>{
|
|
41
|
+
const rootRoute = createRootRoute({
|
|
42
|
+
component: Outlet
|
|
43
|
+
});
|
|
44
|
+
const workspaceRoute = createRoute({
|
|
45
|
+
getParentRoute: ()=>rootRoute,
|
|
46
|
+
path: '/workspaces/$wid',
|
|
47
|
+
component: ()=>/*#__PURE__*/ _jsx(JotaiProvider, {
|
|
48
|
+
store: store,
|
|
49
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
50
|
+
className: "min-h-screen bg-background-neutral-base p-24",
|
|
51
|
+
children: /*#__PURE__*/ _jsx("div", {
|
|
52
|
+
className: "w-[280px] rounded-10 bg-background-neutral-overlay shadow-tooltip",
|
|
53
|
+
children: /*#__PURE__*/ _jsx(WorkspaceSwitcher, {
|
|
54
|
+
activeWorkspaceId: activeWorkspaceId
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
});
|
|
60
|
+
const setupRoute = createRoute({
|
|
61
|
+
getParentRoute: ()=>rootRoute,
|
|
62
|
+
path: '/setup/workspaces/new',
|
|
63
|
+
component: ()=>/*#__PURE__*/ _jsx("div", {})
|
|
64
|
+
});
|
|
65
|
+
return createRouter({
|
|
66
|
+
routeTree: rootRoute.addChildren([
|
|
67
|
+
workspaceRoute,
|
|
68
|
+
setupRoute
|
|
69
|
+
]),
|
|
70
|
+
history: createMemoryHistory({
|
|
71
|
+
initialEntries: [
|
|
72
|
+
`/workspaces/${activeWorkspaceId}`
|
|
73
|
+
]
|
|
74
|
+
})
|
|
75
|
+
});
|
|
76
|
+
}, [
|
|
77
|
+
activeWorkspaceId,
|
|
78
|
+
store
|
|
79
|
+
]);
|
|
80
|
+
return /*#__PURE__*/ _jsx(RouterProvider, {
|
|
81
|
+
router: router
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const meta = {
|
|
85
|
+
title: 'Auth/WorkspaceSwitcher',
|
|
86
|
+
component: WorkspaceSwitcherStory,
|
|
87
|
+
parameters: {
|
|
88
|
+
layout: 'fullscreen'
|
|
89
|
+
},
|
|
90
|
+
args: {
|
|
91
|
+
activeWorkspaceId: ACTIVE_WORKSPACE_ID,
|
|
92
|
+
workspaces: WORKSPACES
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
export default meta;
|
|
96
|
+
async function captureSwitcher(ctx, screenshotName) {
|
|
97
|
+
const canvas = within(ctx.canvasElement);
|
|
98
|
+
await canvas.findByRole('option', {
|
|
99
|
+
name: 'Create workspace'
|
|
100
|
+
});
|
|
101
|
+
await argosScreenshot(ctx, screenshotName);
|
|
102
|
+
}
|
|
103
|
+
export const Playground = {
|
|
104
|
+
play: async (ctx)=>{
|
|
105
|
+
await captureSwitcher(ctx, 'Workspace Switcher Open');
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
export const SingleWithCreate = {
|
|
109
|
+
args: {
|
|
110
|
+
workspaces: SINGLE_WORKSPACE
|
|
111
|
+
},
|
|
112
|
+
play: async (ctx)=>{
|
|
113
|
+
await captureSwitcher(ctx, 'Workspace Switcher Single With Create');
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
export const EmptySearch = {
|
|
117
|
+
play: async (ctx)=>{
|
|
118
|
+
const canvas = within(ctx.canvasElement);
|
|
119
|
+
await userEvent.type(await canvas.findByPlaceholderText('Search workspaces...'), 'zzz-no-match');
|
|
120
|
+
await canvas.findByText('No workspaces found.');
|
|
121
|
+
await canvas.findByRole('option', {
|
|
122
|
+
name: 'Create workspace'
|
|
123
|
+
});
|
|
124
|
+
await argosScreenshot(ctx, 'Workspace Switcher Empty Search');
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
export const ManyOverflow = {
|
|
128
|
+
args: {
|
|
129
|
+
activeWorkspaceId: MANY_WORKSPACES[0]?.id ?? ACTIVE_WORKSPACE_ID,
|
|
130
|
+
workspaces: MANY_WORKSPACES
|
|
131
|
+
},
|
|
132
|
+
play: async (ctx)=>{
|
|
133
|
+
await captureSwitcher(ctx, 'Workspace Switcher Many Overflow');
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
export const ManyOverflowScrolled = {
|
|
137
|
+
args: {
|
|
138
|
+
activeWorkspaceId: MANY_WORKSPACES[0]?.id ?? ACTIVE_WORKSPACE_ID,
|
|
139
|
+
workspaces: MANY_WORKSPACES
|
|
140
|
+
},
|
|
141
|
+
play: async (ctx)=>{
|
|
142
|
+
const canvas = within(ctx.canvasElement);
|
|
143
|
+
await canvas.findByRole('option', {
|
|
144
|
+
name: 'Workspace 01'
|
|
145
|
+
});
|
|
146
|
+
await canvas.findByRole('option', {
|
|
147
|
+
name: 'Create workspace'
|
|
148
|
+
});
|
|
149
|
+
await canvas.findByRole('option', {
|
|
150
|
+
name: 'Workspace 20'
|
|
151
|
+
});
|
|
152
|
+
const firstOption = canvas.getAllByRole('option')[0];
|
|
153
|
+
let scrollContainer = firstOption?.parentElement ?? null;
|
|
154
|
+
while(scrollContainer && scrollContainer.scrollHeight <= scrollContainer.clientHeight){
|
|
155
|
+
scrollContainer = scrollContainer.parentElement;
|
|
156
|
+
}
|
|
157
|
+
if (!scrollContainer) {
|
|
158
|
+
throw new Error('Workspace switcher scroll container was not found');
|
|
159
|
+
}
|
|
160
|
+
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
161
|
+
await waitFor(()=>expect(canvas.getByRole('option', {
|
|
162
|
+
name: 'Workspace 20'
|
|
163
|
+
})).toBeVisible());
|
|
164
|
+
await argosScreenshot(ctx, 'Workspace Switcher Many Overflow Scrolled');
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
//# sourceMappingURL=workspace-switcher.stories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-switcher.stories.tsx"],"sourcesContent":["import {argosScreenshot} from '@argos-ci/storybook/vitest';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {\n createMemoryHistory,\n createRootRoute,\n createRoute,\n createRouter,\n Outlet,\n RouterProvider,\n} from '@tanstack/react-router';\nimport {createStore, Provider as JotaiProvider} from 'jotai';\nimport {useMemo} from 'react';\nimport {expect, userEvent, waitFor, within} from 'storybook/test';\nimport {authStateAtom, type Workspace} from '#state/auth.js';\nimport {WorkspaceSwitcher} from './workspace-switcher.js';\n\nconst ACTIVE_WORKSPACE_ID = '00000000-0000-4000-8000-000000000001';\nconst WORKSPACES = [\n workspace(ACTIVE_WORKSPACE_ID, 'Alpha Workspace', 1),\n workspace('00000000-0000-4000-8000-000000000002', 'Beta Workspace', 2),\n workspace('00000000-0000-4000-8000-000000000003', 'Gamma Workspace', 3),\n workspace('00000000-0000-4000-8000-000000000004', 'Delta Workspace', 4),\n];\nconst SINGLE_WORKSPACE = [workspace(ACTIVE_WORKSPACE_ID, 'Alpha Workspace', 1)];\nconst MANY_WORKSPACES = Array.from({length: 20}, (_, index) =>\n workspace(\n `00000000-0000-4000-8000-${String(index + 1).padStart(12, '0')}`,\n `Workspace ${String(index + 1).padStart(2, '0')}`,\n index + 1,\n ),\n);\n\ninterface WorkspaceSwitcherStoryProps {\n activeWorkspaceId: string;\n workspaces: Workspace[];\n}\n\nfunction workspace(id: string, name: string, index: number): Workspace {\n return {\n id,\n name,\n membershipId: `10000000-0000-4000-8000-${String(index).padStart(12, '0')}`,\n };\n}\n\nfunction WorkspaceSwitcherStory({activeWorkspaceId, workspaces}: WorkspaceSwitcherStoryProps) {\n const store = useMemo(() => {\n const nextStore = createStore();\n nextStore.set(authStateAtom, {status: 'authenticated', workspaces});\n return nextStore;\n }, [workspaces]);\n const router = useMemo(() => {\n const rootRoute = createRootRoute({component: Outlet});\n const workspaceRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/workspaces/$wid',\n component: () => (\n <JotaiProvider store={store}>\n <div className=\"min-h-screen bg-background-neutral-base p-24\">\n <div className=\"w-[280px] rounded-10 bg-background-neutral-overlay shadow-tooltip\">\n <WorkspaceSwitcher activeWorkspaceId={activeWorkspaceId} />\n </div>\n </div>\n </JotaiProvider>\n ),\n });\n const setupRoute = createRoute({\n getParentRoute: () => rootRoute,\n path: '/setup/workspaces/new',\n component: () => <div />,\n });\n\n return createRouter({\n routeTree: rootRoute.addChildren([workspaceRoute, setupRoute]),\n history: createMemoryHistory({initialEntries: [`/workspaces/${activeWorkspaceId}`]}),\n });\n }, [activeWorkspaceId, store]);\n\n return <RouterProvider router={router} />;\n}\n\nconst meta = {\n title: 'Auth/WorkspaceSwitcher',\n component: WorkspaceSwitcherStory,\n parameters: {\n layout: 'fullscreen',\n },\n args: {\n activeWorkspaceId: ACTIVE_WORKSPACE_ID,\n workspaces: WORKSPACES,\n },\n} satisfies Meta<typeof WorkspaceSwitcherStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\ntype WorkspaceSwitcherStoryContext = Parameters<NonNullable<Story['play']>>[0];\n\nasync function captureSwitcher(ctx: WorkspaceSwitcherStoryContext, screenshotName: string) {\n const canvas = within(ctx.canvasElement);\n await canvas.findByRole('option', {name: 'Create workspace'});\n await argosScreenshot(ctx, screenshotName);\n}\n\nexport const Playground: Story = {\n play: async (ctx) => {\n await captureSwitcher(ctx, 'Workspace Switcher Open');\n },\n};\n\nexport const SingleWithCreate: Story = {\n args: {\n workspaces: SINGLE_WORKSPACE,\n },\n play: async (ctx) => {\n await captureSwitcher(ctx, 'Workspace Switcher Single With Create');\n },\n};\n\nexport const EmptySearch: Story = {\n play: async (ctx) => {\n const canvas = within(ctx.canvasElement);\n\n await userEvent.type(\n await canvas.findByPlaceholderText('Search workspaces...'),\n 'zzz-no-match',\n );\n\n await canvas.findByText('No workspaces found.');\n await canvas.findByRole('option', {name: 'Create workspace'});\n await argosScreenshot(ctx, 'Workspace Switcher Empty Search');\n },\n};\n\nexport const ManyOverflow: Story = {\n args: {\n activeWorkspaceId: MANY_WORKSPACES[0]?.id ?? ACTIVE_WORKSPACE_ID,\n workspaces: MANY_WORKSPACES,\n },\n play: async (ctx) => {\n await captureSwitcher(ctx, 'Workspace Switcher Many Overflow');\n },\n};\n\nexport const ManyOverflowScrolled: Story = {\n args: {\n activeWorkspaceId: MANY_WORKSPACES[0]?.id ?? ACTIVE_WORKSPACE_ID,\n workspaces: MANY_WORKSPACES,\n },\n play: async (ctx) => {\n const canvas = within(ctx.canvasElement);\n\n await canvas.findByRole('option', {name: 'Workspace 01'});\n await canvas.findByRole('option', {name: 'Create workspace'});\n\n await canvas.findByRole('option', {name: 'Workspace 20'});\n const firstOption = canvas.getAllByRole('option')[0];\n let scrollContainer = firstOption?.parentElement ?? null;\n while (scrollContainer && scrollContainer.scrollHeight <= scrollContainer.clientHeight) {\n scrollContainer = scrollContainer.parentElement;\n }\n if (!scrollContainer) {\n throw new Error('Workspace switcher scroll container was not found');\n }\n scrollContainer.scrollTop = scrollContainer.scrollHeight;\n\n await waitFor(() => expect(canvas.getByRole('option', {name: 'Workspace 20'})).toBeVisible());\n await argosScreenshot(ctx, 'Workspace Switcher Many Overflow Scrolled');\n },\n};\n"],"names":["argosScreenshot","createMemoryHistory","createRootRoute","createRoute","createRouter","Outlet","RouterProvider","createStore","Provider","JotaiProvider","useMemo","expect","userEvent","waitFor","within","authStateAtom","WorkspaceSwitcher","ACTIVE_WORKSPACE_ID","WORKSPACES","workspace","SINGLE_WORKSPACE","MANY_WORKSPACES","Array","from","length","_","index","String","padStart","id","name","membershipId","WorkspaceSwitcherStory","activeWorkspaceId","workspaces","store","nextStore","set","status","router","rootRoute","component","workspaceRoute","getParentRoute","path","div","className","setupRoute","routeTree","addChildren","history","initialEntries","meta","title","parameters","layout","args","captureSwitcher","ctx","screenshotName","canvas","canvasElement","findByRole","Playground","play","SingleWithCreate","EmptySearch","type","findByPlaceholderText","findByText","ManyOverflow","ManyOverflowScrolled","firstOption","getAllByRole","scrollContainer","parentElement","scrollHeight","clientHeight","Error","scrollTop","getByRole","toBeVisible"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const authFeature: {
|
|
2
|
+
readonly id: "shipfox.auth";
|
|
3
|
+
readonly routes: readonly [{
|
|
4
|
+
readonly path: "/";
|
|
5
|
+
readonly parent: "root";
|
|
6
|
+
readonly impl: "@shipfox/client-auth/routes/index";
|
|
7
|
+
}, {
|
|
8
|
+
readonly path: "/auth/login";
|
|
9
|
+
readonly parent: "root";
|
|
10
|
+
readonly impl: "@shipfox/client-auth/routes/login";
|
|
11
|
+
}, {
|
|
12
|
+
readonly path: "/auth/logout";
|
|
13
|
+
readonly parent: "root";
|
|
14
|
+
readonly impl: "@shipfox/client-auth/routes/logout";
|
|
15
|
+
}, {
|
|
16
|
+
readonly path: "/auth/reset";
|
|
17
|
+
readonly parent: "root";
|
|
18
|
+
readonly impl: "@shipfox/client-auth/routes/reset";
|
|
19
|
+
}, {
|
|
20
|
+
readonly path: "/auth/signup";
|
|
21
|
+
readonly parent: "root";
|
|
22
|
+
readonly impl: "@shipfox/client-auth/routes/signup";
|
|
23
|
+
}, {
|
|
24
|
+
readonly path: "/auth/verify-email";
|
|
25
|
+
readonly parent: "root";
|
|
26
|
+
readonly impl: "@shipfox/client-auth/routes/verify-email";
|
|
27
|
+
}, {
|
|
28
|
+
readonly path: "/setup/workspaces/new";
|
|
29
|
+
readonly parent: "root";
|
|
30
|
+
readonly impl: "@shipfox/client-auth/routes/workspace-onboarding";
|
|
31
|
+
}];
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=feature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAetB,CAAC"}
|
package/dist/feature.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineClientFeature } from '@shipfox/client-shell';
|
|
2
|
+
export const authFeature = defineClientFeature({
|
|
3
|
+
id: 'shipfox.auth',
|
|
4
|
+
routes: [
|
|
5
|
+
{
|
|
6
|
+
path: '/',
|
|
7
|
+
parent: 'root',
|
|
8
|
+
impl: '@shipfox/client-auth/routes/index'
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
path: '/auth/login',
|
|
12
|
+
parent: 'root',
|
|
13
|
+
impl: '@shipfox/client-auth/routes/login'
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
path: '/auth/logout',
|
|
17
|
+
parent: 'root',
|
|
18
|
+
impl: '@shipfox/client-auth/routes/logout'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
path: '/auth/reset',
|
|
22
|
+
parent: 'root',
|
|
23
|
+
impl: '@shipfox/client-auth/routes/reset'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
path: '/auth/signup',
|
|
27
|
+
parent: 'root',
|
|
28
|
+
impl: '@shipfox/client-auth/routes/signup'
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
path: '/auth/verify-email',
|
|
32
|
+
parent: 'root',
|
|
33
|
+
impl: '@shipfox/client-auth/routes/verify-email'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
path: '/setup/workspaces/new',
|
|
37
|
+
parent: 'root',
|
|
38
|
+
impl: '@shipfox/client-auth/routes/workspace-onboarding'
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/feature.ts"],"sourcesContent":["import {defineClientFeature} from '@shipfox/client-shell';\n\nexport const authFeature = defineClientFeature({\n id: 'shipfox.auth',\n routes: [\n {path: '/', parent: 'root', impl: '@shipfox/client-auth/routes/index'},\n {path: '/auth/login', parent: 'root', impl: '@shipfox/client-auth/routes/login'},\n {path: '/auth/logout', parent: 'root', impl: '@shipfox/client-auth/routes/logout'},\n {path: '/auth/reset', parent: 'root', impl: '@shipfox/client-auth/routes/reset'},\n {path: '/auth/signup', parent: 'root', impl: '@shipfox/client-auth/routes/signup'},\n {path: '/auth/verify-email', parent: 'root', impl: '@shipfox/client-auth/routes/verify-email'},\n {\n path: '/setup/workspaces/new',\n parent: 'root',\n impl: '@shipfox/client-auth/routes/workspace-onboarding',\n },\n ],\n});\n"],"names":["defineClientFeature","authFeature","id","routes","path","parent","impl"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,wBAAwB;AAE1D,OAAO,MAAMC,cAAcD,oBAAoB;IAC7CE,IAAI;IACJC,QAAQ;QACN;YAACC,MAAM;YAAKC,QAAQ;YAAQC,MAAM;QAAmC;QACrE;YAACF,MAAM;YAAeC,QAAQ;YAAQC,MAAM;QAAmC;QAC/E;YAACF,MAAM;YAAgBC,QAAQ;YAAQC,MAAM;QAAoC;QACjF;YAACF,MAAM;YAAeC,QAAQ;YAAQC,MAAM;QAAmC;QAC/E;YAACF,MAAM;YAAgBC,QAAQ;YAAQC,MAAM;QAAoC;QACjF;YAACF,MAAM;YAAsBC,QAAQ;YAAQC,MAAM;QAA0C;QAC7F;YACEF,MAAM;YACNC,QAAQ;YACRC,MAAM;QACR;KACD;AACH,GAAG"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { LoginBodyDto } from '@shipfox/api-auth-dto';
|
|
2
|
+
export declare function loginAuth(body: LoginBodyDto): Promise<{
|
|
3
|
+
token: string;
|
|
4
|
+
user: {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
name: string | null;
|
|
8
|
+
email_verified_at: string | null;
|
|
9
|
+
status: "active" | "suspended" | "deleted";
|
|
10
|
+
created_at: string;
|
|
11
|
+
updated_at: string;
|
|
12
|
+
};
|
|
13
|
+
}>;
|
|
14
|
+
export declare function useLoginAuth(): import("@tanstack/react-query").UseMutationResult<{
|
|
15
|
+
token: string;
|
|
16
|
+
user: {
|
|
17
|
+
id: string;
|
|
18
|
+
email: string;
|
|
19
|
+
name: string | null;
|
|
20
|
+
email_verified_at: string | null;
|
|
21
|
+
status: "active" | "suspended" | "deleted";
|
|
22
|
+
created_at: string;
|
|
23
|
+
updated_at: string;
|
|
24
|
+
};
|
|
25
|
+
}, Error, {
|
|
26
|
+
email: string;
|
|
27
|
+
password: string;
|
|
28
|
+
}, unknown>;
|
|
29
|
+
//# sourceMappingURL=login-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/login-auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAmB,MAAM,uBAAuB,CAAC;AAQ1E,wBAAsB,SAAS,CAAC,IAAI,EAAE,YAAY;;;;;;;;;;;GAEjD;AAED,wBAAgB,YAAY;;;;;;;;;;;;;;YA4B3B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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';
|
|
7
|
+
export async function loginAuth(body) {
|
|
8
|
+
return await apiRequest('/auth/login', {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
body
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export function useLoginAuth() {
|
|
14
|
+
const queryClient = useQueryClient();
|
|
15
|
+
const setState = useSetAtom(authStateAtom);
|
|
16
|
+
return useMutation({
|
|
17
|
+
mutationFn: loginAuth,
|
|
18
|
+
onSuccess: async (result)=>{
|
|
19
|
+
queryClient.setQueryData(authRefreshQueryKey, result);
|
|
20
|
+
// Resolve workspaces before flipping auth state to authenticated. A
|
|
21
|
+
// single atomic setState avoids the intermediate window where the user
|
|
22
|
+
// appears authenticated with zero workspaces, which sent users with
|
|
23
|
+
// workspaces straight to `/setup/workspaces/new` after form login.
|
|
24
|
+
let memberships = [];
|
|
25
|
+
try {
|
|
26
|
+
const workspaces = await queryClient.fetchQuery({
|
|
27
|
+
queryKey: userWorkspacesQueryKey,
|
|
28
|
+
queryFn: ()=>listUserWorkspaces(result.token),
|
|
29
|
+
retry: false,
|
|
30
|
+
staleTime: 0
|
|
31
|
+
});
|
|
32
|
+
memberships = workspaces.memberships;
|
|
33
|
+
queryClient.setQueryData(userWorkspacesQueryKey, workspaces);
|
|
34
|
+
} catch {
|
|
35
|
+
// The user is authenticated even if workspace hydration fails.
|
|
36
|
+
}
|
|
37
|
+
setState(toAuthenticatedState(result, memberships));
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=login-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/login-auth.ts"],"sourcesContent":["import type {LoginBodyDto, LoginResponseDto} 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 loginAuth(body: LoginBodyDto) {\n return await apiRequest<LoginResponseDto>('/auth/login', {method: 'POST', body});\n}\n\nexport function useLoginAuth() {\n const queryClient = useQueryClient();\n const setState = useSetAtom(authStateAtom);\n\n return useMutation({\n mutationFn: loginAuth,\n onSuccess: async (result) => {\n queryClient.setQueryData(authRefreshQueryKey, result);\n // Resolve workspaces before flipping auth state to authenticated. A\n // single atomic setState avoids the intermediate window where the user\n // appears authenticated with zero workspaces, which sent users with\n // workspaces straight to `/setup/workspaces/new` after form login.\n let memberships: Awaited<ReturnType<typeof listUserWorkspaces>>['memberships'] = [];\n try {\n const workspaces = await queryClient.fetchQuery({\n queryKey: userWorkspacesQueryKey,\n queryFn: () => listUserWorkspaces(result.token),\n retry: false,\n staleTime: 0,\n });\n memberships = workspaces.memberships;\n queryClient.setQueryData(userWorkspacesQueryKey, workspaces);\n } catch {\n // The user is authenticated even if workspace hydration fails.\n }\n setState(toAuthenticatedState(result, memberships));\n },\n });\n}\n"],"names":["apiRequest","useMutation","useQueryClient","useSetAtom","authStateAtom","toAuthenticatedState","authRefreshQueryKey","listUserWorkspaces","userWorkspacesQueryKey","loginAuth","body","method","useLoginAuth","queryClient","setState","mutationFn","onSuccess","result","setQueryData","memberships","workspaces","fetchQuery","queryKey","queryFn","token","retry","staleTime"],"mappings":"AACA,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,UAAUC,IAAkB;IAChD,OAAO,MAAMV,WAA6B,eAAe;QAACW,QAAQ;QAAQD;IAAI;AAChF;AAEA,OAAO,SAASE;IACd,MAAMC,cAAcX;IACpB,MAAMY,WAAWX,WAAWC;IAE5B,OAAOH,YAAY;QACjBc,YAAYN;QACZO,WAAW,OAAOC;YAChBJ,YAAYK,YAAY,CAACZ,qBAAqBW;YAC9C,oEAAoE;YACpE,uEAAuE;YACvE,oEAAoE;YACpE,mEAAmE;YACnE,IAAIE,cAA6E,EAAE;YACnF,IAAI;gBACF,MAAMC,aAAa,MAAMP,YAAYQ,UAAU,CAAC;oBAC9CC,UAAUd;oBACVe,SAAS,IAAMhB,mBAAmBU,OAAOO,KAAK;oBAC9CC,OAAO;oBACPC,WAAW;gBACb;gBACAP,cAAcC,WAAWD,WAAW;gBACpCN,YAAYK,YAAY,CAACV,wBAAwBY;YACnD,EAAE,OAAM;YACN,+DAA+D;YACjE;YACAN,SAAST,qBAAqBY,QAAQE;QACxC;IACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/logout-auth.ts"],"names":[],"mappings":"AAcA,wBAAgB,aAAa,kFAW5B"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { apiRequest } from '@shipfox/client-api';
|
|
2
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { useSetAtom } from 'jotai';
|
|
4
|
+
import { authStateAtom } from '#state/auth.js';
|
|
5
|
+
import { authRefreshQueryKey } from './refresh-auth.js';
|
|
6
|
+
async function logoutAuth() {
|
|
7
|
+
try {
|
|
8
|
+
await apiRequest('/auth/logout', {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
body: {}
|
|
11
|
+
});
|
|
12
|
+
} catch {
|
|
13
|
+
// Logout is best-effort: local session state must clear even if the API is offline.
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function useLogoutAuth() {
|
|
17
|
+
const queryClient = useQueryClient();
|
|
18
|
+
const setState = useSetAtom(authStateAtom);
|
|
19
|
+
return useMutation({
|
|
20
|
+
mutationFn: logoutAuth,
|
|
21
|
+
onSettled: ()=>{
|
|
22
|
+
setState({
|
|
23
|
+
status: 'guest'
|
|
24
|
+
});
|
|
25
|
+
queryClient.removeQueries({
|
|
26
|
+
queryKey: authRefreshQueryKey
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=logout-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/logout-auth.ts"],"sourcesContent":["import {apiRequest} from '@shipfox/client-api';\nimport {useMutation, useQueryClient} from '@tanstack/react-query';\nimport {useSetAtom} from 'jotai';\nimport {authStateAtom} from '#state/auth.js';\nimport {authRefreshQueryKey} from './refresh-auth.js';\n\nasync function logoutAuth() {\n try {\n await apiRequest<void>('/auth/logout', {method: 'POST', body: {}});\n } catch {\n // Logout is best-effort: local session state must clear even if the API is offline.\n }\n}\n\nexport function useLogoutAuth() {\n const queryClient = useQueryClient();\n const setState = useSetAtom(authStateAtom);\n\n return useMutation({\n mutationFn: logoutAuth,\n onSettled: () => {\n setState({status: 'guest'});\n queryClient.removeQueries({queryKey: authRefreshQueryKey});\n },\n });\n}\n"],"names":["apiRequest","useMutation","useQueryClient","useSetAtom","authStateAtom","authRefreshQueryKey","logoutAuth","method","body","useLogoutAuth","queryClient","setState","mutationFn","onSettled","status","removeQueries","queryKey"],"mappings":"AAAA,SAAQA,UAAU,QAAO,sBAAsB;AAC/C,SAAQC,WAAW,EAAEC,cAAc,QAAO,wBAAwB;AAClE,SAAQC,UAAU,QAAO,QAAQ;AACjC,SAAQC,aAAa,QAAO,iBAAiB;AAC7C,SAAQC,mBAAmB,QAAO,oBAAoB;AAEtD,eAAeC;IACb,IAAI;QACF,MAAMN,WAAiB,gBAAgB;YAACO,QAAQ;YAAQC,MAAM,CAAC;QAAC;IAClE,EAAE,OAAM;IACN,oFAAoF;IACtF;AACF;AAEA,OAAO,SAASC;IACd,MAAMC,cAAcR;IACpB,MAAMS,WAAWR,WAAWC;IAE5B,OAAOH,YAAY;QACjBW,YAAYN;QACZO,WAAW;YACTF,SAAS;gBAACG,QAAQ;YAAO;YACzBJ,YAAYK,aAAa,CAAC;gBAACC,UAAUX;YAAmB;QAC1D;IACF;AACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PasswordResetConfirmBodyDto, PasswordResetRequestBodyDto } from '@shipfox/api-auth-dto';
|
|
2
|
+
export declare function requestPasswordReset(body: PasswordResetRequestBodyDto): Promise<void>;
|
|
3
|
+
export declare function confirmPasswordReset(body: PasswordResetConfirmBodyDto): Promise<{
|
|
4
|
+
token: string;
|
|
5
|
+
user: {
|
|
6
|
+
id: string;
|
|
7
|
+
email: string;
|
|
8
|
+
name: string | null;
|
|
9
|
+
email_verified_at: string | null;
|
|
10
|
+
status: "active" | "suspended" | "deleted";
|
|
11
|
+
created_at: string;
|
|
12
|
+
updated_at: string;
|
|
13
|
+
};
|
|
14
|
+
}>;
|
|
15
|
+
export declare function useRequestPasswordResetAuth(): import("@tanstack/react-query").UseMutationResult<void, Error, {
|
|
16
|
+
email: string;
|
|
17
|
+
}, unknown>;
|
|
18
|
+
export declare function useConfirmPasswordResetAuth(): import("@tanstack/react-query").UseMutationResult<{
|
|
19
|
+
token: string;
|
|
20
|
+
user: {
|
|
21
|
+
id: string;
|
|
22
|
+
email: string;
|
|
23
|
+
name: string | null;
|
|
24
|
+
email_verified_at: string | null;
|
|
25
|
+
status: "active" | "suspended" | "deleted";
|
|
26
|
+
created_at: string;
|
|
27
|
+
updated_at: string;
|
|
28
|
+
};
|
|
29
|
+
}, Error, {
|
|
30
|
+
token: string;
|
|
31
|
+
new_password: string;
|
|
32
|
+
}, unknown>;
|
|
33
|
+
//# sourceMappingURL=password-reset-auth.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
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';
|
|
7
|
+
export async function requestPasswordReset(body) {
|
|
8
|
+
await apiRequest('/auth/password-reset', {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
body
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export async function confirmPasswordReset(body) {
|
|
14
|
+
return await apiRequest('/auth/password-reset/confirm', {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
body
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function useRequestPasswordResetAuth() {
|
|
20
|
+
return useMutation({
|
|
21
|
+
mutationFn: requestPasswordReset
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function useConfirmPasswordResetAuth() {
|
|
25
|
+
const queryClient = useQueryClient();
|
|
26
|
+
const setState = useSetAtom(authStateAtom);
|
|
27
|
+
return useMutation({
|
|
28
|
+
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
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=password-reset-auth.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/refresh-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GACf,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/hooks/api/refresh-auth.ts"],"sourcesContent":["export {\n authRefreshQueryKey,\n getAuthRefreshDelayMs,\n useRefreshAuth,\n} from '@shipfox/client-shell/runtime';\n"],"names":["authRefreshQueryKey","getAuthRefreshDelayMs","useRefreshAuth"],"mappings":"AAAA,SACEA,mBAAmB,EACnBC,qBAAqB,EACrBC,cAAc,QACT,gCAAgC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
token?: string | undefined;
|
|
12
|
+
membership?: {
|
|
13
|
+
id: string;
|
|
14
|
+
user_id: string;
|
|
15
|
+
workspace_id: string;
|
|
16
|
+
} | null | undefined;
|
|
17
|
+
accept_error?: {
|
|
18
|
+
code: string;
|
|
19
|
+
message: string;
|
|
20
|
+
} | undefined;
|
|
21
|
+
}, Error, {
|
|
22
|
+
email: string;
|
|
23
|
+
password: string;
|
|
24
|
+
name: string;
|
|
25
|
+
invitation_token?: string | undefined;
|
|
26
|
+
}, unknown>;
|
|
27
|
+
//# sourceMappingURL=signup-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signup-auth.d.ts","sourceRoot":"","sources":["../../../src/hooks/api/signup-auth.ts"],"names":[],"mappings":"AAQA,wBAAgB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;YAE5B"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { apiRequest } from '@shipfox/client-api';
|
|
2
|
+
import { useMutation } from '@tanstack/react-query';
|
|
3
|
+
async function signupAuth(body) {
|
|
4
|
+
return await apiRequest('/auth/signup', {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
body
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export function useSignupAuth() {
|
|
10
|
+
return useMutation({
|
|
11
|
+
mutationFn: signupAuth
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=signup-auth.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function useResendEmailVerificationAuth(): import("@tanstack/react-query").UseMutationResult<{
|
|
2
|
+
next_resend_available_at: string;
|
|
3
|
+
}, Error, {
|
|
4
|
+
email: string;
|
|
5
|
+
}, unknown>;
|
|
6
|
+
export declare function useVerifyEmailAuth(): import("@tanstack/react-query").UseMutationResult<{
|
|
7
|
+
token: string;
|
|
8
|
+
user: {
|
|
9
|
+
id: string;
|
|
10
|
+
email: string;
|
|
11
|
+
name: string | null;
|
|
12
|
+
email_verified_at: string | null;
|
|
13
|
+
status: "active" | "suspended" | "deleted";
|
|
14
|
+
created_at: string;
|
|
15
|
+
updated_at: string;
|
|
16
|
+
};
|
|
17
|
+
}, Error, {
|
|
18
|
+
token: string;
|
|
19
|
+
}, unknown>;
|
|
20
|
+
//# sourceMappingURL=verify-email-auth.d.ts.map
|
|
@@ -0,0 +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"}
|