@shipfox/client-auth 17.0.0 → 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/dist/pages/workspace-onboarding-page.d.ts.map +1 -1
  4. package/dist/pages/workspace-onboarding-page.js +82 -75
  5. package/dist/pages/workspace-onboarding-page.js.map +1 -1
  6. package/dist/routes/index.d.ts +3 -0
  7. package/dist/routes/index.d.ts.map +1 -1
  8. package/dist/routes/index.js +3 -0
  9. package/dist/routes/index.js.map +1 -1
  10. package/dist/routes/login.d.ts +3 -0
  11. package/dist/routes/login.js +3 -0
  12. package/dist/routes/login.js.map +1 -1
  13. package/dist/routes/logout.d.ts +3 -0
  14. package/dist/routes/logout.js +3 -0
  15. package/dist/routes/logout.js.map +1 -1
  16. package/dist/routes/reset.d.ts +3 -0
  17. package/dist/routes/reset.js +3 -0
  18. package/dist/routes/reset.js.map +1 -1
  19. package/dist/routes/signup.d.ts +3 -0
  20. package/dist/routes/signup.js +3 -0
  21. package/dist/routes/signup.js.map +1 -1
  22. package/dist/routes/workspace-onboarding.d.ts +3 -0
  23. package/dist/routes/workspace-onboarding.d.ts.map +1 -1
  24. package/dist/routes/workspace-onboarding.js +3 -0
  25. package/dist/routes/workspace-onboarding.js.map +1 -1
  26. package/dist/tsconfig.test.tsbuildinfo +1 -1
  27. package/package.json +5 -5
  28. package/src/pages/workspace-onboarding-page.test.tsx +16 -6
  29. package/src/pages/workspace-onboarding-page.tsx +100 -92
  30. package/src/routes/index.tsx +1 -0
  31. package/src/routes/login.tsx +1 -0
  32. package/src/routes/logout.tsx +5 -1
  33. package/src/routes/reset.tsx +1 -0
  34. package/src/routes/signup.tsx +1 -0
  35. package/src/routes/workspace-onboarding.tsx +1 -0
  36. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/client-auth",
3
3
  "license": "MIT",
4
- "version": "17.0.0",
4
+ "version": "21.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -39,10 +39,10 @@
39
39
  "@shipfox/api-auth-dto": "12.0.0",
40
40
  "@shipfox/api-workspaces-dto": "12.0.0",
41
41
  "@shipfox/client-api": "6.0.1",
42
- "@shipfox/client-invitations": "17.0.0",
43
- "@shipfox/client-ui": "17.0.0",
44
- "@shipfox/react-ui": "1.2.0",
45
- "@shipfox/client-shell": "17.0.0"
42
+ "@shipfox/client-invitations": "21.0.0",
43
+ "@shipfox/client-shell": "21.0.0",
44
+ "@shipfox/client-ui": "21.0.0",
45
+ "@shipfox/react-ui": "2.0.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@tanstack/react-query": "^5.101.0",
@@ -66,7 +66,7 @@ describe('WorkspaceOnboardingPage', () => {
66
66
  });
67
67
  configureApiClient({fetchImpl});
68
68
 
69
- renderAuthPage(
69
+ const {container} = renderAuthPage(
70
70
  '/',
71
71
  <AuthGuard>
72
72
  <WorkspaceGuard>
@@ -74,7 +74,13 @@ describe('WorkspaceOnboardingPage', () => {
74
74
  </WorkspaceGuard>
75
75
  </AuthGuard>,
76
76
  );
77
- fireEvent.change(await screen.findByLabelText('Workspace name'), {
77
+ const workspaceName = await screen.findByLabelText('Workspace name');
78
+ expect(container.querySelector('[data-slot="panel-header"]')).toHaveAttribute(
79
+ 'data-variant',
80
+ 'plain',
81
+ );
82
+ expect(container.querySelector('[data-slot="panel-body"]')).toBeInTheDocument();
83
+ fireEvent.change(workspaceName, {
78
84
  target: {value: ' Acme '},
79
85
  });
80
86
  fireEvent.click(screen.getByRole('button', {name: 'Create workspace'}));
@@ -111,13 +117,17 @@ describe('WorkspaceOnboardingPage', () => {
111
117
  const slug = screen.getByLabelText('Workspace slug');
112
118
  expect(screen.getByText(`${window.location.origin}/w/acme`)).toBeInTheDocument();
113
119
  fireEvent.change(name, {target: {value: 'Acme Labs'}});
114
- expect(slug).toHaveValue('acme-labs');
115
- expect(screen.getByText(`${window.location.origin}/w/acme-labs`)).toBeInTheDocument();
120
+ await waitFor(() => {
121
+ expect(slug).toHaveValue('acme-labs');
122
+ expect(screen.getByText(`${window.location.origin}/w/acme-labs`)).toBeInTheDocument();
123
+ });
116
124
 
117
125
  fireEvent.change(slug, {target: {value: 'custom-workspace'}});
118
126
  fireEvent.change(name, {target: {value: 'Renamed Labs'}});
119
- expect(slug).toHaveValue('custom-workspace');
120
- expect(screen.getByText(`${window.location.origin}/w/custom-workspace`)).toBeInTheDocument();
127
+ await waitFor(() => {
128
+ expect(slug).toHaveValue('custom-workspace');
129
+ expect(screen.getByText(`${window.location.origin}/w/custom-workspace`)).toBeInTheDocument();
130
+ });
121
131
  });
122
132
 
123
133
  test('checks a manually edited workspace slug for availability', async () => {
@@ -3,9 +3,9 @@ import {createWorkspaceBodySchema} from '@shipfox/api-workspaces-dto';
3
3
  import {displayNameFieldError, SlugField} from '@shipfox/client-ui';
4
4
  import {Button} from '@shipfox/react-ui/button';
5
5
  import {Callout} from '@shipfox/react-ui/callout';
6
- import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@shipfox/react-ui/card';
7
6
  import {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';
8
7
  import {Icon} from '@shipfox/react-ui/icon';
8
+ import {Panel, PanelBody, PanelHeader, PanelTitle} from '@shipfox/react-ui/panel';
9
9
  import {toast} from '@shipfox/react-ui/toast';
10
10
  import {Text} from '@shipfox/react-ui/typography';
11
11
  import {useForm} from '@tanstack/react-form';
@@ -78,8 +78,8 @@ export function WorkspaceOnboardingPage() {
78
78
  });
79
79
 
80
80
  return (
81
- <main className="min-h-screen bg-background-subtle-base px-frame py-frame max-[520px]:px-row">
82
- <div className="mx-auto flex min-h-[calc(100vh-64px)] w-full max-w-[1120px] flex-col gap-section">
81
+ <main className="min-h-screen px-frame py-frame max-[520px]:px-row">
82
+ <div className="mx-auto flex min-h-[calc(100vh-64px)] w-full flex-col gap-section">
83
83
  <header className="flex items-center justify-between">
84
84
  <div className="flex items-center gap-inline">
85
85
  <div className="flex size-36 items-center justify-center rounded-8 border border-border-neutral-base bg-background-neutral-base shadow-button-neutral">
@@ -102,103 +102,111 @@ export function WorkspaceOnboardingPage() {
102
102
  void form.handleSubmit();
103
103
  }}
104
104
  >
105
- <Card className="gap-section p-panel shadow-button-neutral">
106
- <CardHeader className="gap-inline">
107
- <CardTitle id="workspace-onboarding-title" variant="h1">
105
+ <Panel>
106
+ <PanelHeader variant="plain" className="flex-col items-start gap-inline">
107
+ <PanelTitle id="workspace-onboarding-title" variant="h1">
108
108
  Create your workspace
109
- </CardTitle>
110
- <CardDescription>Give your team a place to collaborate.</CardDescription>
111
- </CardHeader>
109
+ </PanelTitle>
110
+ <Text size="sm" className="text-foreground-neutral-muted">
111
+ Give your team a place to collaborate.
112
+ </Text>
113
+ </PanelHeader>
112
114
 
113
- {formError ? (
114
- <Callout role="alert" type="error">
115
- {formError}
116
- </Callout>
117
- ) : null}
115
+ <PanelBody className="flex flex-col gap-section p-panel">
116
+ {formError ? (
117
+ <Callout role="alert" type="error">
118
+ {formError}
119
+ </Callout>
120
+ ) : null}
118
121
 
119
- <CardContent className="flex flex-col gap-inline">
120
- <form.Field
121
- name="name"
122
- validators={{
123
- onBlur: ({value}) =>
124
- displayNameFieldError(
125
- value,
126
- 'Workspace name',
127
- createWorkspaceBodySchema.shape.name,
128
- ),
129
- onSubmit: ({value}) =>
130
- displayNameFieldError(
131
- value,
132
- 'Workspace name',
133
- createWorkspaceBodySchema.shape.name,
134
- ),
135
- }}
136
- >
137
- {(field) => (
138
- <FormField label="Workspace name" id="workspace-name" error={fieldError(field)}>
139
- <FormFieldInput
140
- autoComplete="organization"
141
- name="name"
142
- placeholder="Acme"
143
- type="text"
122
+ <div className="flex flex-col gap-inline">
123
+ <form.Field
124
+ name="name"
125
+ validators={{
126
+ onBlur: ({value}) =>
127
+ displayNameFieldError(
128
+ value,
129
+ 'Workspace name',
130
+ createWorkspaceBodySchema.shape.name,
131
+ ),
132
+ onSubmit: ({value}) =>
133
+ displayNameFieldError(
134
+ value,
135
+ 'Workspace name',
136
+ createWorkspaceBodySchema.shape.name,
137
+ ),
138
+ }}
139
+ >
140
+ {(field) => (
141
+ <FormField
142
+ label="Workspace name"
143
+ id="workspace-name"
144
+ error={fieldError(field)}
145
+ >
146
+ <FormFieldInput
147
+ autoComplete="organization"
148
+ name="name"
149
+ placeholder="Acme"
150
+ type="text"
151
+ value={field.state.value}
152
+ onChange={(event) => {
153
+ const name = event.target.value;
154
+ field.handleChange(name);
155
+ if (!slugTouched) {
156
+ form.setFieldValue(
157
+ 'slug',
158
+ name ? slugifyName(name, {fallback: 'workspace'}) : '',
159
+ );
160
+ }
161
+ }}
162
+ onBlur={field.handleBlur}
163
+ />
164
+ </FormField>
165
+ )}
166
+ </form.Field>
167
+ <form.Field
168
+ name="slug"
169
+ validators={{
170
+ onBlur: createWorkspaceBodySchema.shape.slug,
171
+ onSubmit: createWorkspaceBodySchema.shape.slug,
172
+ }}
173
+ >
174
+ {(field) => (
175
+ <SlugField
176
+ id="workspace-slug"
177
+ label="Workspace slug"
178
+ name="slug"
144
179
  value={field.state.value}
145
- onChange={(event) => {
146
- const name = event.target.value;
147
- field.handleChange(name);
148
- if (!slugTouched) {
149
- form.setFieldValue(
150
- 'slug',
151
- name ? slugifyName(name, {fallback: 'workspace'}) : '',
152
- );
153
- }
180
+ onChange={(value) => {
181
+ setSlugTouched(true);
182
+ field.handleChange(value);
154
183
  }}
155
184
  onBlur={field.handleBlur}
185
+ error={fieldError(field)}
186
+ description={
187
+ <span className="break-all font-code">
188
+ {`${window.location.origin}/w/${field.state.value || 'acme'}`}
189
+ </span>
190
+ }
191
+ placeholder="acme"
192
+ checkEnabled={slugTouched}
193
+ isValid={isSlugValid}
194
+ checkAvailability={checkWorkspaceSlugAvailability}
156
195
  />
157
- </FormField>
158
- )}
159
- </form.Field>
160
- <form.Field
161
- name="slug"
162
- validators={{
163
- onBlur: createWorkspaceBodySchema.shape.slug,
164
- onSubmit: createWorkspaceBodySchema.shape.slug,
165
- }}
166
- >
167
- {(field) => (
168
- <SlugField
169
- id="workspace-slug"
170
- label="Workspace slug"
171
- name="slug"
172
- value={field.state.value}
173
- onChange={(value) => {
174
- setSlugTouched(true);
175
- field.handleChange(value);
176
- }}
177
- onBlur={field.handleBlur}
178
- error={fieldError(field)}
179
- description={
180
- <span className="break-all font-code">
181
- {`${window.location.origin}/w/${field.state.value || 'acme'}`}
182
- </span>
183
- }
184
- placeholder="acme"
185
- checkEnabled={slugTouched}
186
- isValid={isSlugValid}
187
- checkAvailability={checkWorkspaceSlugAvailability}
188
- />
189
- )}
190
- </form.Field>
191
- </CardContent>
196
+ )}
197
+ </form.Field>
198
+ </div>
192
199
 
193
- <Button
194
- className="w-full"
195
- iconRight="chevronRight"
196
- isLoading={createWorkspace.isPending}
197
- type="submit"
198
- >
199
- {createWorkspace.isPending ? 'Creating workspace...' : 'Create workspace'}
200
- </Button>
201
- </Card>
200
+ <Button
201
+ className="w-full"
202
+ iconRight="chevronRight"
203
+ isLoading={createWorkspace.isPending}
204
+ type="submit"
205
+ >
206
+ {createWorkspace.isPending ? 'Creating workspace...' : 'Create workspace'}
207
+ </Button>
208
+ </PanelBody>
209
+ </Panel>
202
210
  </form>
203
211
 
204
212
  <div className="hidden flex-col gap-group lg:flex" aria-hidden="true">
@@ -4,6 +4,7 @@ import {FullPageLoader} from '@shipfox/react-ui/loader';
4
4
  import {redirect} from '@tanstack/react-router';
5
5
 
6
6
  export default defineRoute({
7
+ staticData: {frame: 'focused'},
7
8
  beforeLoad: ({context}: {context: RouterContext}) => {
8
9
  const auth = context.auth;
9
10
  if (!auth || auth.isLoading) return;
@@ -4,6 +4,7 @@ import {LoginPage} from '#pages/login-page.js';
4
4
  import {validateRedirectSearch} from './inputs.js';
5
5
 
6
6
  export default defineRoute({
7
+ staticData: {frame: 'focused'},
7
8
  validateSearch: validateRedirectSearch,
8
9
  component: () => (
9
10
  <GuestGuard>
@@ -2,4 +2,8 @@ import {defineRoute} from '@shipfox/client-shell/runtime';
2
2
  import {LogoutPage} from '#pages/logout-page.js';
3
3
  import {validateRedirectSearch} from './inputs.js';
4
4
 
5
- export default defineRoute({validateSearch: validateRedirectSearch, component: LogoutPage});
5
+ export default defineRoute({
6
+ staticData: {frame: 'focused'},
7
+ validateSearch: validateRedirectSearch,
8
+ component: LogoutPage,
9
+ });
@@ -4,6 +4,7 @@ import {PasswordResetPage} from '#pages/password-reset-page.js';
4
4
  import {validatePasswordResetSearch} from './inputs.js';
5
5
 
6
6
  export default defineRoute({
7
+ staticData: {frame: 'focused'},
7
8
  validateSearch: validatePasswordResetSearch,
8
9
  component: () => (
9
10
  <GuestGuard>
@@ -4,6 +4,7 @@ import {SignupPage} from '#pages/signup-page.js';
4
4
  import {validateRedirectSearch} from './inputs.js';
5
5
 
6
6
  export default defineRoute({
7
+ staticData: {frame: 'focused'},
7
8
  validateSearch: validateRedirectSearch,
8
9
  component: () => (
9
10
  <GuestGuard>
@@ -4,6 +4,7 @@ import {redirect} from '@tanstack/react-router';
4
4
  import {WorkspaceOnboardingPage} from '#pages/workspace-onboarding-page.js';
5
5
 
6
6
  export default defineRoute({
7
+ staticData: {frame: 'focused'},
7
8
  beforeLoad: ({context, location}: {context: RouterContext; location: {href: string}}) => {
8
9
  const auth = context.auth;
9
10
  if (!auth || auth.isLoading) return;