@shipfox/client-workspace-settings 22.0.0 → 22.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/client-workspace-settings",
3
3
  "license": "MIT",
4
- "version": "22.0.0",
4
+ "version": "22.0.1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -28,9 +28,9 @@
28
28
  "@swc/helpers": "^0.5.17",
29
29
  "@tanstack/react-form": "^1.32.0",
30
30
  "@shipfox/api-workspaces-dto": "12.0.0",
31
+ "@shipfox/client-auth": "22.0.1",
31
32
  "@shipfox/client-api": "6.0.1",
32
- "@shipfox/client-auth": "22.0.0",
33
- "@shipfox/client-shell": "22.0.0",
33
+ "@shipfox/client-shell": "22.0.1",
34
34
  "@shipfox/react-ui": "2.1.0",
35
35
  "@shipfox/client-ui": "22.0.0"
36
36
  },
@@ -34,7 +34,6 @@ import {useState} from 'react';
34
34
  import {
35
35
  getInvitationExpiry,
36
36
  getMemberRemovalRestriction,
37
- memberCount,
38
37
  type PendingInvitation,
39
38
  type WorkspaceMember,
40
39
  } from '#core/membership.js';
@@ -74,12 +73,7 @@ function MembersSection({
74
73
  return (
75
74
  <section className="flex flex-col gap-group">
76
75
  <div className="flex flex-col gap-tight">
77
- <Header variant="h3">Members</Header>
78
- <Text size="sm" className="text-foreground-neutral-muted">
79
- {query.isPending
80
- ? 'Loading members…'
81
- : `${memberCount(members)} ${memberCount(members) === 1 ? 'member' : 'members'}`}
82
- </Text>
76
+ <Header variant="h1">Members</Header>
83
77
  </div>
84
78
 
85
79
  {query.isPending ? <TableSkeleton rows={3} cols={3} label="Loading members" /> : null}
@@ -215,11 +209,6 @@ function PendingInvitationsSection({
215
209
  <div className="flex items-center justify-between gap-group">
216
210
  <div className="flex flex-col gap-tight">
217
211
  <Header variant="h3">Pending invitations</Header>
218
- <Text size="sm" className="text-foreground-neutral-muted">
219
- {query.isPending
220
- ? 'Loading invitations…'
221
- : `${invitations.length} ${invitations.length === 1 ? 'invitation' : 'invitations'}`}
222
- </Text>
223
212
  </div>
224
213
  <InviteMemberModal
225
214
  open={inviteOpen}
@@ -1,5 +1,10 @@
1
- import {useMaybeActiveWorkspace} from '@shipfox/client-shell/runtime';
1
+ import {
2
+ useAuthState,
3
+ useMaybeActiveWorkspace,
4
+ WorkspaceUnavailablePage,
5
+ } from '@shipfox/client-shell/runtime';
2
6
  import {FullPageLoader} from '@shipfox/react-ui/loader';
7
+ import {Navigate} from '@tanstack/react-router';
3
8
  import type {ReactNode} from 'react';
4
9
 
5
10
  interface WorkspaceSettingsShellProps {
@@ -7,8 +12,11 @@ interface WorkspaceSettingsShellProps {
7
12
  }
8
13
 
9
14
  export function WorkspaceSettingsShell({children}: WorkspaceSettingsShellProps) {
15
+ const auth = useAuthState();
10
16
  const workspace = useMaybeActiveWorkspace();
11
- if (!workspace) return <FullPageLoader />;
17
+ if (auth.isLoading) return <FullPageLoader />;
18
+ if (!auth.isAuthenticated) return <Navigate to={'/auth/login' as never} replace />;
19
+ if (!workspace) return <WorkspaceUnavailablePage />;
12
20
 
13
21
  return children(workspace);
14
22
  }
@@ -8,8 +8,9 @@ import {displayNameFieldError, SlugChangeWarning, SlugField} from '@shipfox/clie
8
8
  import {Button} from '@shipfox/react-ui/button';
9
9
  import {Callout} from '@shipfox/react-ui/callout';
10
10
  import {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';
11
+ import {Panel, PanelBody} from '@shipfox/react-ui/panel';
11
12
  import {toast} from '@shipfox/react-ui/toast';
12
- import {Header, Text} from '@shipfox/react-ui/typography';
13
+ import {Header} from '@shipfox/react-ui/typography';
13
14
  import {useForm} from '@tanstack/react-form';
14
15
  import {useNavigate} from '@tanstack/react-router';
15
16
  import {useState} from 'react';
@@ -95,93 +96,94 @@ function WorkspaceGeneralForm({workspace}: {workspace: ReturnType<typeof useActi
95
96
  return (
96
97
  <>
97
98
  <div className="flex min-w-0 flex-col gap-section">
98
- <header className="flex flex-col gap-inline">
99
- <Header variant="h1">General</Header>
100
- <Text size="sm" className="text-foreground-neutral-muted">
101
- Update the workspace name and the slug used in its URLs.
102
- </Text>
103
- </header>
99
+ <Header variant="h1">General</Header>
104
100
 
105
- {formError ? (
106
- <Callout role="alert" type="error">
107
- {formError}
108
- </Callout>
109
- ) : null}
101
+ <Panel>
102
+ <PanelBody className="gap-group p-panel">
103
+ {formError ? (
104
+ <Callout role="alert" type="error">
105
+ {formError}
106
+ </Callout>
107
+ ) : null}
110
108
 
111
- <form
112
- className="flex max-w-[560px] flex-col gap-group"
113
- noValidate
114
- onSubmit={(event) => {
115
- event.preventDefault();
116
- event.stopPropagation();
117
- void form.handleSubmit();
118
- }}
119
- >
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
139
- label="Workspace name"
140
- id="workspace-settings-name"
141
- error={fieldError(field)}
109
+ <form
110
+ className="flex w-full max-w-[560px] flex-col gap-group"
111
+ noValidate
112
+ onSubmit={(event) => {
113
+ event.preventDefault();
114
+ event.stopPropagation();
115
+ void form.handleSubmit();
116
+ }}
117
+ >
118
+ <form.Field
119
+ name="name"
120
+ validators={{
121
+ onBlur: ({value}) =>
122
+ displayNameFieldError(
123
+ value,
124
+ 'Workspace name',
125
+ createWorkspaceBodySchema.shape.name,
126
+ ),
127
+ onSubmit: ({value}) =>
128
+ displayNameFieldError(
129
+ value,
130
+ 'Workspace name',
131
+ createWorkspaceBodySchema.shape.name,
132
+ ),
133
+ }}
142
134
  >
143
- <FormFieldInput
144
- name="name"
145
- type="text"
146
- value={field.state.value}
147
- onChange={(event) => field.handleChange(event.target.value)}
148
- onBlur={field.handleBlur}
149
- />
150
- </FormField>
151
- )}
152
- </form.Field>
135
+ {(field) => (
136
+ <FormField
137
+ label="Workspace name"
138
+ id="workspace-settings-name"
139
+ error={fieldError(field)}
140
+ >
141
+ <FormFieldInput
142
+ name="name"
143
+ type="text"
144
+ value={field.state.value}
145
+ onChange={(event) => field.handleChange(event.target.value)}
146
+ onBlur={field.handleBlur}
147
+ />
148
+ </FormField>
149
+ )}
150
+ </form.Field>
153
151
 
154
- <form.Field
155
- name="slug"
156
- validators={{
157
- onBlur: createWorkspaceBodySchema.shape.slug,
158
- onSubmit: createWorkspaceBodySchema.shape.slug,
159
- }}
160
- >
161
- {(field) => (
162
- <SlugField
163
- id="workspace-settings-slug"
164
- label="Workspace slug"
152
+ <form.Field
165
153
  name="slug"
166
- value={field.state.value}
167
- onChange={(value) => field.handleChange(value)}
168
- onBlur={field.handleBlur}
169
- error={fieldError(field)}
170
- description={<span className="break-all font-code">/w/{field.state.value}</span>}
171
- placeholder="acme"
172
- className="font-code"
173
- currentSlug={workspace.slug}
174
- checkEnabled
175
- isValid={isSlugValid}
176
- checkAvailability={checkWorkspaceSlugAvailability}
177
- />
178
- )}
179
- </form.Field>
154
+ validators={{
155
+ onBlur: createWorkspaceBodySchema.shape.slug,
156
+ onSubmit: createWorkspaceBodySchema.shape.slug,
157
+ }}
158
+ >
159
+ {(field) => (
160
+ <SlugField
161
+ id="workspace-settings-slug"
162
+ label="Workspace slug"
163
+ name="slug"
164
+ value={field.state.value}
165
+ onChange={(value) => field.handleChange(value)}
166
+ onBlur={field.handleBlur}
167
+ error={fieldError(field)}
168
+ description={
169
+ <span className="break-all font-code">/w/{field.state.value}</span>
170
+ }
171
+ placeholder="acme"
172
+ className="font-code"
173
+ currentSlug={workspace.slug}
174
+ checkEnabled
175
+ isValid={isSlugValid}
176
+ checkAvailability={checkWorkspaceSlugAvailability}
177
+ />
178
+ )}
179
+ </form.Field>
180
180
 
181
- <Button type="submit" isLoading={updateWorkspace.isPending} className="self-start">
182
- Save changes
183
- </Button>
184
- </form>
181
+ <Button type="submit" isLoading={updateWorkspace.isPending} className="self-start">
182
+ Save changes
183
+ </Button>
184
+ </form>
185
+ </PanelBody>
186
+ </Panel>
185
187
  </div>
186
188
 
187
189
  <SlugChangeWarning