@shipfox/client-workspace-settings 12.0.2 → 14.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +41 -0
- package/dist/components/general/form-errors.d.ts +11 -0
- package/dist/components/general/form-errors.d.ts.map +1 -0
- package/dist/components/general/form-errors.js +16 -0
- package/dist/components/general/form-errors.js.map +1 -0
- package/dist/components/members/workspace-members-section.js +2 -2
- package/dist/components/members/workspace-members-section.js.map +1 -1
- package/dist/components/workspace-settings-shell.d.ts +3 -3
- package/dist/components/workspace-settings-shell.d.ts.map +1 -1
- package/dist/components/workspace-settings-shell.js +5 -2
- package/dist/components/workspace-settings-shell.js.map +1 -1
- package/dist/feature.d.ts +20 -4
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +15 -3
- package/dist/feature.js.map +1 -1
- package/dist/pages/general-settings-page.d.ts +2 -0
- package/dist/pages/general-settings-page.d.ts.map +1 -0
- package/dist/pages/general-settings-page.js +191 -0
- package/dist/pages/general-settings-page.js.map +1 -0
- package/dist/routes/general.d.ts +6 -0
- package/dist/routes/general.d.ts.map +1 -0
- package/dist/routes/general.js +7 -0
- package/dist/routes/general.js.map +1 -0
- package/dist/routes/index.d.ts +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -2
- package/dist/routes/index.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/components/general/form-errors.ts +17 -0
- package/src/components/members/workspace-members-section.tsx +2 -2
- package/src/components/workspace-settings-shell.tsx +5 -3
- package/src/feature.ts +15 -3
- package/src/pages/general-settings-page.test.tsx +91 -0
- package/src/pages/general-settings-page.tsx +198 -0
- package/src/routes/general.tsx +4 -0
- package/src/routes/index.tsx +5 -2
- package/test/pages.tsx +11 -5
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
$ shipfox-swc
|
|
2
|
-
Successfully compiled:
|
|
2
|
+
Successfully compiled: 19 files with swc (536.32ms)
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @shipfox/client-workspace-settings
|
|
2
2
|
|
|
3
|
+
## 14.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [baa7594]
|
|
8
|
+
- Updated dependencies [f8a98cb]
|
|
9
|
+
- Updated dependencies [1267eb3]
|
|
10
|
+
- Updated dependencies [b2d4550]
|
|
11
|
+
- @shipfox/react-ui@0.5.0
|
|
12
|
+
- @shipfox/client-ui@14.0.0
|
|
13
|
+
- @shipfox/client-auth@14.0.0
|
|
14
|
+
- @shipfox/client-shell@14.0.0
|
|
15
|
+
|
|
16
|
+
## 13.0.0
|
|
17
|
+
|
|
18
|
+
### Major Changes
|
|
19
|
+
|
|
20
|
+
- e405e92: Move client routes to slug-based `/w/$workspaceSlug` and `/p/$projectSlug` URLs, enforce the new composition contract, and support bounded project-slug resolution.
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- 3c73365: Add project settings, shared slug rename warnings, and live slug availability checks.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- f78740d: Remove Unicode dash punctuation from package prose and source comments.
|
|
29
|
+
- Updated dependencies [e405e92]
|
|
30
|
+
- Updated dependencies [f78740d]
|
|
31
|
+
- Updated dependencies [94aba88]
|
|
32
|
+
- Updated dependencies [9969937]
|
|
33
|
+
- Updated dependencies [3c73365]
|
|
34
|
+
- Updated dependencies [9ebc5b4]
|
|
35
|
+
- Updated dependencies [54c820e]
|
|
36
|
+
- Updated dependencies [6adc228]
|
|
37
|
+
- Updated dependencies [e1efaee]
|
|
38
|
+
- @shipfox/client-auth@13.0.0
|
|
39
|
+
- @shipfox/client-shell@13.0.0
|
|
40
|
+
- @shipfox/client-ui@13.0.0
|
|
41
|
+
- @shipfox/react-ui@0.4.0
|
|
42
|
+
- @shipfox/api-workspaces-dto@12.0.0
|
|
43
|
+
|
|
3
44
|
## 12.0.2
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type WorkspaceGeneralField = 'name' | 'slug';
|
|
2
|
+
export type WorkspaceGeneralFormError = {
|
|
3
|
+
kind: 'field';
|
|
4
|
+
field: WorkspaceGeneralField;
|
|
5
|
+
message: string;
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'form';
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function workspaceGeneralErrorToFormError(error: unknown): WorkspaceGeneralFormError;
|
|
11
|
+
//# sourceMappingURL=form-errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form-errors.d.ts","sourceRoot":"","sources":["../../../src/components/general/form-errors.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,qBAAqB,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpD,MAAM,MAAM,yBAAyB,GACjC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,qBAAqB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAC9D;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,CAAC;AAEpC,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,OAAO,GAAG,yBAAyB,CAQ1F"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ApiError } from '@shipfox/client-api';
|
|
2
|
+
export function workspaceGeneralErrorToFormError(error) {
|
|
3
|
+
if (error instanceof ApiError && error.code === 'slug-conflict') {
|
|
4
|
+
return {
|
|
5
|
+
kind: 'field',
|
|
6
|
+
field: 'slug',
|
|
7
|
+
message: 'That workspace slug is already taken.'
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
kind: 'form',
|
|
12
|
+
message: error instanceof Error ? error.message : 'Could not update workspace settings.'
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=form-errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/general/form-errors.ts"],"sourcesContent":["import {ApiError} from '@shipfox/client-api';\n\nexport type WorkspaceGeneralField = 'name' | 'slug';\n\nexport type WorkspaceGeneralFormError =\n | {kind: 'field'; field: WorkspaceGeneralField; message: string}\n | {kind: 'form'; message: string};\n\nexport function workspaceGeneralErrorToFormError(error: unknown): WorkspaceGeneralFormError {\n if (error instanceof ApiError && error.code === 'slug-conflict') {\n return {kind: 'field', field: 'slug', message: 'That workspace slug is already taken.'};\n }\n return {\n kind: 'form',\n message: error instanceof Error ? error.message : 'Could not update workspace settings.',\n };\n}\n"],"names":["ApiError","workspaceGeneralErrorToFormError","error","code","kind","field","message","Error"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,sBAAsB;AAQ7C,OAAO,SAASC,iCAAiCC,KAAc;IAC7D,IAAIA,iBAAiBF,YAAYE,MAAMC,IAAI,KAAK,iBAAiB;QAC/D,OAAO;YAACC,MAAM;YAASC,OAAO;YAAQC,SAAS;QAAuC;IACxF;IACA,OAAO;QACLF,MAAM;QACNE,SAASJ,iBAAiBK,QAAQL,MAAMI,OAAO,GAAG;IACpD;AACF"}
|
|
@@ -126,7 +126,7 @@ function MemberRow({ member, members, currentUserId, workspaceId, workspaceName
|
|
|
126
126
|
children: [
|
|
127
127
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
128
128
|
className: "font-medium",
|
|
129
|
-
children: member.name ?? '
|
|
129
|
+
children: member.name ?? 'N/A'
|
|
130
130
|
}),
|
|
131
131
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
132
132
|
children: /*#__PURE__*/ _jsx(Code, {
|
|
@@ -303,7 +303,7 @@ function InvitationRow({ invitation, workspaceId }) {
|
|
|
303
303
|
})
|
|
304
304
|
}),
|
|
305
305
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
306
|
-
children: invitation.invitedByDisplay ?? '
|
|
306
|
+
children: invitation.invitedByDisplay ?? 'N/A'
|
|
307
307
|
}),
|
|
308
308
|
/*#__PURE__*/ _jsx(TableCell, {
|
|
309
309
|
children: /*#__PURE__*/ _jsxs("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/members/workspace-members-section.tsx"],"sourcesContent":["import {createInvitationBodySchema} from '@shipfox/api-workspaces-dto';\nimport {useAuthState} from '@shipfox/client-auth';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n ModalTrigger,\n} from '@shipfox/react-ui/modal';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {formatDate} from '@shipfox/react-ui/utils';\nimport {useForm} from '@tanstack/react-form';\nimport {useState} from 'react';\nimport {\n getInvitationExpiry,\n getMemberRemovalRestriction,\n memberCount,\n type PendingInvitation,\n type WorkspaceMember,\n} from '#core/membership.js';\nimport {useCreateInvitation} from '#hooks/api/create-invitation.js';\nimport {useListInvitations} from '#hooks/api/list-invitations.js';\nimport {useListMembers} from '#hooks/api/list-members.js';\nimport {useRemoveMember} from '#hooks/api/remove-member.js';\nimport {useRevokeInvitation} from '#hooks/api/revoke-invitation.js';\nimport {invitationErrorToFormError, memberRemovalErrorMessage} from './form-errors.js';\n\nexport function WorkspaceMembersSettingsSection({\n workspaceId,\n workspaceName,\n}: {\n workspaceId: string;\n workspaceName: string;\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-32\">\n <MembersSection workspaceId={workspaceId} workspaceName={workspaceName} />\n <PendingInvitationsSection workspaceId={workspaceId} workspaceName={workspaceName} />\n </div>\n );\n}\n\nfunction MembersSection({\n workspaceId,\n workspaceName,\n}: {\n workspaceId: string;\n workspaceName: string;\n}) {\n const auth = useAuthState();\n const query = useListMembers(workspaceId);\n const members = query.data ?? [];\n\n return (\n <section className=\"flex flex-col gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Members</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {query.isPending\n ? 'Loading members…'\n : `${memberCount(members)} ${memberCount(members) === 1 ? 'member' : 'members'}`}\n </Text>\n </div>\n\n {query.isPending ? <TableSkeleton rows={3} cols={3} /> : null}\n\n {query.isError && query.data === undefined ? (\n <QueryLoadError query={query} subject=\"members\" />\n ) : null}\n\n {members.length > 0 ? (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Email</TableHead>\n <TableHead>Joined</TableHead>\n <TableHead className=\"w-80 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {members.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n members={members}\n currentUserId={auth.user?.id}\n workspaceId={workspaceId}\n workspaceName={workspaceName}\n />\n ))}\n </TableBody>\n </Table>\n ) : null}\n </section>\n );\n}\n\nfunction MemberRow({\n member,\n members,\n currentUserId,\n workspaceId,\n workspaceName,\n}: {\n member: WorkspaceMember;\n members: readonly WorkspaceMember[];\n currentUserId: string | undefined;\n workspaceId: string;\n workspaceName: string;\n}) {\n const [open, setOpen] = useState(false);\n const remove = useRemoveMember(workspaceId);\n const restriction = getMemberRemovalRestriction({member, members, currentUserId});\n\n async function handleRemove() {\n try {\n await remove.mutateAsync({userId: member.userId});\n toast.success(`Removed ${member.email} from ${workspaceName}.`);\n setOpen(false);\n } catch (error) {\n toast.error(memberRemovalErrorMessage(error));\n }\n }\n\n return (\n <TableRow className={remove.isPending ? 'opacity-60' : undefined}>\n <TableCell className=\"font-medium\">{member.name ?? '—'}</TableCell>\n <TableCell>\n <Code variant=\"paragraph\">{member.email}</Code>\n </TableCell>\n <TableCell>{formatDate(member.joinedAt)}</TableCell>\n <TableCell className=\"text-right\">\n <Modal open={open} onOpenChange={setOpen}>\n <ModalTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"transparentMuted\"\n aria-label=\"Remove member\"\n disabled={restriction !== undefined}\n isLoading={remove.isPending}\n className={\n remove.isPending\n ? 'opacity-100 transition-opacity'\n : 'opacity-0 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100'\n }\n >\n <Icon name=\"userUnfollowLine\" className=\"size-16\" />\n </Button>\n </ModalTrigger>\n <ModalContent>\n <ModalTitle className=\"sr-only\">Remove member</ModalTitle>\n <ModalHeader>\n <Text size=\"lg\">\n Remove {member.name ?? member.email} from {workspaceName}?\n </Text>\n </ModalHeader>\n <ModalBody>\n <Text size=\"sm\">\n They will lose access immediately. They can be re-invited later.\n </Text>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button variant=\"danger\" onClick={handleRemove} isLoading={remove.isPending}>\n Remove\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n </TableCell>\n </TableRow>\n );\n}\n\nfunction PendingInvitationsSection({\n workspaceId,\n workspaceName,\n}: {\n workspaceId: string;\n workspaceName: string;\n}) {\n const query = useListInvitations(workspaceId);\n const invitations = query.data ?? [];\n const [inviteOpen, setInviteOpen] = useState(false);\n\n return (\n <section className=\"flex flex-col gap-16\">\n <div className=\"flex items-center justify-between gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Pending invitations</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {query.isPending\n ? 'Loading invitations…'\n : `${invitations.length} ${invitations.length === 1 ? 'invitation' : 'invitations'}`}\n </Text>\n </div>\n <InviteMemberModal\n open={inviteOpen}\n onOpenChange={setInviteOpen}\n workspaceId={workspaceId}\n workspaceName={workspaceName}\n />\n </div>\n\n {query.isPending ? <TableSkeleton rows={2} cols={3} /> : null}\n\n {query.isError && query.data === undefined ? (\n <QueryLoadError query={query} subject=\"invitations\" />\n ) : null}\n\n {query.data !== undefined && invitations.length === 0 ? <EmptyInvitations /> : null}\n\n {invitations.length > 0 ? (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Email</TableHead>\n <TableHead>Invited by</TableHead>\n <TableHead>Expires</TableHead>\n <TableHead className=\"w-80 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {invitations.map((invitation) => (\n <InvitationRow\n key={invitation.id}\n invitation={invitation}\n workspaceId={workspaceId}\n />\n ))}\n </TableBody>\n </Table>\n ) : null}\n </section>\n );\n}\n\nfunction InvitationRow({\n invitation,\n workspaceId,\n}: {\n invitation: PendingInvitation;\n workspaceId: string;\n}) {\n const [open, setOpen] = useState(false);\n const revoke = useRevokeInvitation(workspaceId);\n const expiry = getInvitationExpiry(invitation);\n\n async function handleRevoke() {\n try {\n await revoke.mutateAsync({invitationId: invitation.id});\n toast.success(`Invitation to ${invitation.email} revoked.`);\n setOpen(false);\n } catch {\n toast.error('Could not revoke invitation.');\n }\n }\n\n return (\n <TableRow className={revoke.isPending ? 'opacity-60' : undefined}>\n <TableCell>\n <Code variant=\"paragraph\">{invitation.email}</Code>\n </TableCell>\n <TableCell>{invitation.invitedByDisplay ?? '—'}</TableCell>\n <TableCell>\n <div className=\"flex items-center gap-8\">\n <Text size=\"sm\">{formatDate(invitation.expiresAt)}</Text>\n {expiry === 'expires-soon' ? <Badge variant=\"warning\">Soon</Badge> : null}\n {expiry === 'expired' ? <Badge variant=\"error\">Expired</Badge> : null}\n </div>\n </TableCell>\n <TableCell className=\"text-right\">\n <Modal open={open} onOpenChange={setOpen}>\n <ModalTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"transparentMuted\"\n aria-label=\"Revoke invitation\"\n isLoading={revoke.isPending}\n className={\n revoke.isPending\n ? 'opacity-100 transition-opacity'\n : 'opacity-0 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100'\n }\n >\n <Icon name=\"closeLine\" className=\"size-16\" />\n </Button>\n </ModalTrigger>\n <ModalContent>\n <ModalTitle className=\"sr-only\">Revoke invitation</ModalTitle>\n <ModalHeader>\n <Text size=\"lg\">Revoke invitation to {invitation.email}?</Text>\n </ModalHeader>\n <ModalBody>\n <Text size=\"sm\">They will no longer be able to use the link from their email.</Text>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button variant=\"danger\" onClick={handleRevoke} isLoading={revoke.isPending}>\n Revoke\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n </TableCell>\n </TableRow>\n );\n}\n\nfunction InviteMemberModal({\n open,\n onOpenChange,\n workspaceId,\n workspaceName,\n}: {\n open: boolean;\n onOpenChange: (next: boolean) => void;\n workspaceId: string;\n workspaceName: string;\n}) {\n const create = useCreateInvitation(workspaceId);\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {email: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const result = await create.mutateAsync({email: value.email});\n toast.success(`Invitation sent to ${result.email}.`);\n // Go through handleOpenChange so form.reset() fires and reopening the\n // modal does not show stale form state.\n handleOpenChange(false);\n } catch (error) {\n const mapped = invitationErrorToFormError(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 function handleOpenChange(nextOpen: boolean) {\n if (!nextOpen) {\n form.reset();\n setFormError(undefined);\n create.reset();\n }\n onOpenChange(nextOpen);\n }\n\n return (\n <Modal open={open} onOpenChange={handleOpenChange}>\n <ModalTrigger asChild>\n <Button size=\"sm\" variant=\"secondary\">\n Invite member\n </Button>\n </ModalTrigger>\n <ModalContent>\n <ModalTitle className=\"sr-only\">Invite a member</ModalTitle>\n <ModalHeader>\n <Text size=\"lg\">Invite a member</Text>\n </ModalHeader>\n <ModalBody className=\"gap-16\">\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form\n id=\"invite-member-form\"\n className=\"flex flex-col gap-16\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Field\n name=\"email\"\n validators={{\n onBlur: createInvitationBodySchema.shape.email,\n onSubmit: createInvitationBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField\n label=\"Email\"\n id=\"invite-email\"\n error={fieldError(field)}\n description={`They'll receive an email with a link to join ${workspaceName}.`}\n >\n <FormFieldInput\n autoComplete=\"email\"\n autoFocus\n name=\"email\"\n type=\"email\"\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 </form>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => handleOpenChange(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" form=\"invite-member-form\" isLoading={create.isPending}>\n Send invitation\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction EmptyInvitations() {\n return (\n <EmptyState\n icon=\"mailLine\"\n title=\"No pending invitations.\"\n description=\"Invite someone above to grow your workspace.\"\n />\n );\n}\n\nfunction TableSkeleton({rows, cols}: {rows: number; cols: number}) {\n return (\n <div className=\"flex flex-col gap-12\">\n {Array.from({length: rows}).map((_, rowIdx) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: stable placeholder rows\n <div key={rowIdx} className=\"grid grid-cols-3 gap-16\">\n {Array.from({length: cols}).map((__, colIdx) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: stable placeholder cells\n <Skeleton key={colIdx} className=\"h-20\" />\n ))}\n </div>\n ))}\n </div>\n );\n}\n"],"names":["createInvitationBodySchema","useAuthState","QueryLoadError","Badge","Button","Callout","EmptyState","FormField","FormFieldInput","fieldError","Icon","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","ModalTrigger","Skeleton","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","formatDate","useForm","useState","getInvitationExpiry","getMemberRemovalRestriction","memberCount","useCreateInvitation","useListInvitations","useListMembers","useRemoveMember","useRevokeInvitation","invitationErrorToFormError","memberRemovalErrorMessage","WorkspaceMembersSettingsSection","workspaceId","workspaceName","div","className","MembersSection","PendingInvitationsSection","auth","query","members","data","section","variant","size","isPending","TableSkeleton","rows","cols","isError","undefined","subject","length","map","member","MemberRow","currentUserId","user","id","open","setOpen","remove","restriction","handleRemove","mutateAsync","userId","success","email","error","name","joinedAt","onOpenChange","asChild","aria-label","disabled","isLoading","onClick","invitations","inviteOpen","setInviteOpen","InviteMemberModal","EmptyInvitations","invitation","InvitationRow","revoke","expiry","handleRevoke","invitationId","invitedByDisplay","expiresAt","create","formError","setFormError","form","defaultValues","onSubmit","value","result","handleOpenChange","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","nextOpen","reset","role","type","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","validators","onBlur","shape","label","description","autoComplete","autoFocus","state","onChange","handleChange","target","handleBlur","icon","title","Array","from","_","rowIdx","__","colIdx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/members/workspace-members-section.tsx"],"sourcesContent":["import {createInvitationBodySchema} from '@shipfox/api-workspaces-dto';\nimport {useAuthState} from '@shipfox/client-auth';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Badge} from '@shipfox/react-ui/badge';\nimport {Button} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {FormField, FormFieldInput, fieldError} from '@shipfox/react-ui/form-field';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n ModalTrigger,\n} from '@shipfox/react-ui/modal';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from '@shipfox/react-ui/table';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Code, Header, Text} from '@shipfox/react-ui/typography';\nimport {formatDate} from '@shipfox/react-ui/utils';\nimport {useForm} from '@tanstack/react-form';\nimport {useState} from 'react';\nimport {\n getInvitationExpiry,\n getMemberRemovalRestriction,\n memberCount,\n type PendingInvitation,\n type WorkspaceMember,\n} from '#core/membership.js';\nimport {useCreateInvitation} from '#hooks/api/create-invitation.js';\nimport {useListInvitations} from '#hooks/api/list-invitations.js';\nimport {useListMembers} from '#hooks/api/list-members.js';\nimport {useRemoveMember} from '#hooks/api/remove-member.js';\nimport {useRevokeInvitation} from '#hooks/api/revoke-invitation.js';\nimport {invitationErrorToFormError, memberRemovalErrorMessage} from './form-errors.js';\n\nexport function WorkspaceMembersSettingsSection({\n workspaceId,\n workspaceName,\n}: {\n workspaceId: string;\n workspaceName: string;\n}) {\n return (\n <div className=\"flex min-w-0 flex-col gap-32\">\n <MembersSection workspaceId={workspaceId} workspaceName={workspaceName} />\n <PendingInvitationsSection workspaceId={workspaceId} workspaceName={workspaceName} />\n </div>\n );\n}\n\nfunction MembersSection({\n workspaceId,\n workspaceName,\n}: {\n workspaceId: string;\n workspaceName: string;\n}) {\n const auth = useAuthState();\n const query = useListMembers(workspaceId);\n const members = query.data ?? [];\n\n return (\n <section className=\"flex flex-col gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Members</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {query.isPending\n ? 'Loading members…'\n : `${memberCount(members)} ${memberCount(members) === 1 ? 'member' : 'members'}`}\n </Text>\n </div>\n\n {query.isPending ? <TableSkeleton rows={3} cols={3} /> : null}\n\n {query.isError && query.data === undefined ? (\n <QueryLoadError query={query} subject=\"members\" />\n ) : null}\n\n {members.length > 0 ? (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Name</TableHead>\n <TableHead>Email</TableHead>\n <TableHead>Joined</TableHead>\n <TableHead className=\"w-80 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {members.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n members={members}\n currentUserId={auth.user?.id}\n workspaceId={workspaceId}\n workspaceName={workspaceName}\n />\n ))}\n </TableBody>\n </Table>\n ) : null}\n </section>\n );\n}\n\nfunction MemberRow({\n member,\n members,\n currentUserId,\n workspaceId,\n workspaceName,\n}: {\n member: WorkspaceMember;\n members: readonly WorkspaceMember[];\n currentUserId: string | undefined;\n workspaceId: string;\n workspaceName: string;\n}) {\n const [open, setOpen] = useState(false);\n const remove = useRemoveMember(workspaceId);\n const restriction = getMemberRemovalRestriction({member, members, currentUserId});\n\n async function handleRemove() {\n try {\n await remove.mutateAsync({userId: member.userId});\n toast.success(`Removed ${member.email} from ${workspaceName}.`);\n setOpen(false);\n } catch (error) {\n toast.error(memberRemovalErrorMessage(error));\n }\n }\n\n return (\n <TableRow className={remove.isPending ? 'opacity-60' : undefined}>\n <TableCell className=\"font-medium\">{member.name ?? 'N/A'}</TableCell>\n <TableCell>\n <Code variant=\"paragraph\">{member.email}</Code>\n </TableCell>\n <TableCell>{formatDate(member.joinedAt)}</TableCell>\n <TableCell className=\"text-right\">\n <Modal open={open} onOpenChange={setOpen}>\n <ModalTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"transparentMuted\"\n aria-label=\"Remove member\"\n disabled={restriction !== undefined}\n isLoading={remove.isPending}\n className={\n remove.isPending\n ? 'opacity-100 transition-opacity'\n : 'opacity-0 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100'\n }\n >\n <Icon name=\"userUnfollowLine\" className=\"size-16\" />\n </Button>\n </ModalTrigger>\n <ModalContent>\n <ModalTitle className=\"sr-only\">Remove member</ModalTitle>\n <ModalHeader>\n <Text size=\"lg\">\n Remove {member.name ?? member.email} from {workspaceName}?\n </Text>\n </ModalHeader>\n <ModalBody>\n <Text size=\"sm\">\n They will lose access immediately. They can be re-invited later.\n </Text>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button variant=\"danger\" onClick={handleRemove} isLoading={remove.isPending}>\n Remove\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n </TableCell>\n </TableRow>\n );\n}\n\nfunction PendingInvitationsSection({\n workspaceId,\n workspaceName,\n}: {\n workspaceId: string;\n workspaceName: string;\n}) {\n const query = useListInvitations(workspaceId);\n const invitations = query.data ?? [];\n const [inviteOpen, setInviteOpen] = useState(false);\n\n return (\n <section className=\"flex flex-col gap-16\">\n <div className=\"flex items-center justify-between gap-16\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Pending invitations</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {query.isPending\n ? 'Loading invitations…'\n : `${invitations.length} ${invitations.length === 1 ? 'invitation' : 'invitations'}`}\n </Text>\n </div>\n <InviteMemberModal\n open={inviteOpen}\n onOpenChange={setInviteOpen}\n workspaceId={workspaceId}\n workspaceName={workspaceName}\n />\n </div>\n\n {query.isPending ? <TableSkeleton rows={2} cols={3} /> : null}\n\n {query.isError && query.data === undefined ? (\n <QueryLoadError query={query} subject=\"invitations\" />\n ) : null}\n\n {query.data !== undefined && invitations.length === 0 ? <EmptyInvitations /> : null}\n\n {invitations.length > 0 ? (\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>Email</TableHead>\n <TableHead>Invited by</TableHead>\n <TableHead>Expires</TableHead>\n <TableHead className=\"w-80 text-right\">Actions</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {invitations.map((invitation) => (\n <InvitationRow\n key={invitation.id}\n invitation={invitation}\n workspaceId={workspaceId}\n />\n ))}\n </TableBody>\n </Table>\n ) : null}\n </section>\n );\n}\n\nfunction InvitationRow({\n invitation,\n workspaceId,\n}: {\n invitation: PendingInvitation;\n workspaceId: string;\n}) {\n const [open, setOpen] = useState(false);\n const revoke = useRevokeInvitation(workspaceId);\n const expiry = getInvitationExpiry(invitation);\n\n async function handleRevoke() {\n try {\n await revoke.mutateAsync({invitationId: invitation.id});\n toast.success(`Invitation to ${invitation.email} revoked.`);\n setOpen(false);\n } catch {\n toast.error('Could not revoke invitation.');\n }\n }\n\n return (\n <TableRow className={revoke.isPending ? 'opacity-60' : undefined}>\n <TableCell>\n <Code variant=\"paragraph\">{invitation.email}</Code>\n </TableCell>\n <TableCell>{invitation.invitedByDisplay ?? 'N/A'}</TableCell>\n <TableCell>\n <div className=\"flex items-center gap-8\">\n <Text size=\"sm\">{formatDate(invitation.expiresAt)}</Text>\n {expiry === 'expires-soon' ? <Badge variant=\"warning\">Soon</Badge> : null}\n {expiry === 'expired' ? <Badge variant=\"error\">Expired</Badge> : null}\n </div>\n </TableCell>\n <TableCell className=\"text-right\">\n <Modal open={open} onOpenChange={setOpen}>\n <ModalTrigger asChild>\n <Button\n size=\"sm\"\n variant=\"transparentMuted\"\n aria-label=\"Revoke invitation\"\n isLoading={revoke.isPending}\n className={\n revoke.isPending\n ? 'opacity-100 transition-opacity'\n : 'opacity-0 transition-opacity group-hover/row:opacity-100 focus-visible:opacity-100'\n }\n >\n <Icon name=\"closeLine\" className=\"size-16\" />\n </Button>\n </ModalTrigger>\n <ModalContent>\n <ModalTitle className=\"sr-only\">Revoke invitation</ModalTitle>\n <ModalHeader>\n <Text size=\"lg\">Revoke invitation to {invitation.email}?</Text>\n </ModalHeader>\n <ModalBody>\n <Text size=\"sm\">They will no longer be able to use the link from their email.</Text>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button variant=\"danger\" onClick={handleRevoke} isLoading={revoke.isPending}>\n Revoke\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n </TableCell>\n </TableRow>\n );\n}\n\nfunction InviteMemberModal({\n open,\n onOpenChange,\n workspaceId,\n workspaceName,\n}: {\n open: boolean;\n onOpenChange: (next: boolean) => void;\n workspaceId: string;\n workspaceName: string;\n}) {\n const create = useCreateInvitation(workspaceId);\n const [formError, setFormError] = useState<string | undefined>();\n\n const form = useForm({\n defaultValues: {email: ''},\n onSubmit: async ({value}) => {\n setFormError(undefined);\n try {\n const result = await create.mutateAsync({email: value.email});\n toast.success(`Invitation sent to ${result.email}.`);\n // Go through handleOpenChange so form.reset() fires and reopening the\n // modal does not show stale form state.\n handleOpenChange(false);\n } catch (error) {\n const mapped = invitationErrorToFormError(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 function handleOpenChange(nextOpen: boolean) {\n if (!nextOpen) {\n form.reset();\n setFormError(undefined);\n create.reset();\n }\n onOpenChange(nextOpen);\n }\n\n return (\n <Modal open={open} onOpenChange={handleOpenChange}>\n <ModalTrigger asChild>\n <Button size=\"sm\" variant=\"secondary\">\n Invite member\n </Button>\n </ModalTrigger>\n <ModalContent>\n <ModalTitle className=\"sr-only\">Invite a member</ModalTitle>\n <ModalHeader>\n <Text size=\"lg\">Invite a member</Text>\n </ModalHeader>\n <ModalBody className=\"gap-16\">\n {formError ? (\n <Callout role=\"alert\" type=\"error\">\n {formError}\n </Callout>\n ) : null}\n <form\n id=\"invite-member-form\"\n className=\"flex flex-col gap-16\"\n noValidate\n onSubmit={(event) => {\n event.preventDefault();\n event.stopPropagation();\n void form.handleSubmit();\n }}\n >\n <form.Field\n name=\"email\"\n validators={{\n onBlur: createInvitationBodySchema.shape.email,\n onSubmit: createInvitationBodySchema.shape.email,\n }}\n >\n {(field) => (\n <FormField\n label=\"Email\"\n id=\"invite-email\"\n error={fieldError(field)}\n description={`They'll receive an email with a link to join ${workspaceName}.`}\n >\n <FormFieldInput\n autoComplete=\"email\"\n autoFocus\n name=\"email\"\n type=\"email\"\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 </form>\n </ModalBody>\n <ModalFooter>\n <Button variant=\"secondary\" onClick={() => handleOpenChange(false)}>\n Cancel\n </Button>\n <Button type=\"submit\" form=\"invite-member-form\" isLoading={create.isPending}>\n Send invitation\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction EmptyInvitations() {\n return (\n <EmptyState\n icon=\"mailLine\"\n title=\"No pending invitations.\"\n description=\"Invite someone above to grow your workspace.\"\n />\n );\n}\n\nfunction TableSkeleton({rows, cols}: {rows: number; cols: number}) {\n return (\n <div className=\"flex flex-col gap-12\">\n {Array.from({length: rows}).map((_, rowIdx) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: stable placeholder rows\n <div key={rowIdx} className=\"grid grid-cols-3 gap-16\">\n {Array.from({length: cols}).map((__, colIdx) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: stable placeholder cells\n <Skeleton key={colIdx} className=\"h-20\" />\n ))}\n </div>\n ))}\n </div>\n );\n}\n"],"names":["createInvitationBodySchema","useAuthState","QueryLoadError","Badge","Button","Callout","EmptyState","FormField","FormFieldInput","fieldError","Icon","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","ModalTrigger","Skeleton","Table","TableBody","TableCell","TableHead","TableHeader","TableRow","toast","Code","Header","Text","formatDate","useForm","useState","getInvitationExpiry","getMemberRemovalRestriction","memberCount","useCreateInvitation","useListInvitations","useListMembers","useRemoveMember","useRevokeInvitation","invitationErrorToFormError","memberRemovalErrorMessage","WorkspaceMembersSettingsSection","workspaceId","workspaceName","div","className","MembersSection","PendingInvitationsSection","auth","query","members","data","section","variant","size","isPending","TableSkeleton","rows","cols","isError","undefined","subject","length","map","member","MemberRow","currentUserId","user","id","open","setOpen","remove","restriction","handleRemove","mutateAsync","userId","success","email","error","name","joinedAt","onOpenChange","asChild","aria-label","disabled","isLoading","onClick","invitations","inviteOpen","setInviteOpen","InviteMemberModal","EmptyInvitations","invitation","InvitationRow","revoke","expiry","handleRevoke","invitationId","invitedByDisplay","expiresAt","create","formError","setFormError","form","defaultValues","onSubmit","value","result","handleOpenChange","mapped","kind","setFieldMeta","field","prev","errorMap","onServer","message","nextOpen","reset","role","type","noValidate","event","preventDefault","stopPropagation","handleSubmit","Field","validators","onBlur","shape","label","description","autoComplete","autoFocus","state","onChange","handleChange","target","handleBlur","icon","title","Array","from","_","rowIdx","__","colIdx"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMaybeActiveWorkspace } from '@shipfox/client-shell/runtime';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
interface WorkspaceSettingsShellProps {
|
|
4
|
-
children: (workspace: ReturnType<typeof
|
|
4
|
+
children: (workspace: NonNullable<ReturnType<typeof useMaybeActiveWorkspace>>) => ReactNode;
|
|
5
5
|
}
|
|
6
|
-
export declare function WorkspaceSettingsShell({ children }: WorkspaceSettingsShellProps): ReactNode;
|
|
6
|
+
export declare function WorkspaceSettingsShell({ children }: WorkspaceSettingsShellProps): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null | undefined;
|
|
7
7
|
export {};
|
|
8
8
|
//# sourceMappingURL=workspace-settings-shell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-settings-shell.d.ts","sourceRoot":"","sources":["../../src/components/workspace-settings-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"workspace-settings-shell.d.ts","sourceRoot":"","sources":["../../src/components/workspace-settings-shell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,uBAAuB,EAAC,MAAM,+BAA+B,CAAC;AAEtE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAErC,UAAU,2BAA2B;IACnC,QAAQ,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,KAAK,SAAS,CAAC;CAC7F;AAED,wBAAgB,sBAAsB,CAAC,EAAC,QAAQ,EAAC,EAAE,2BAA2B,+TAK7E"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useMaybeActiveWorkspace } from '@shipfox/client-shell/runtime';
|
|
3
|
+
import { FullPageLoader } from '@shipfox/react-ui/loader';
|
|
2
4
|
export function WorkspaceSettingsShell({ children }) {
|
|
3
|
-
const workspace =
|
|
5
|
+
const workspace = useMaybeActiveWorkspace();
|
|
6
|
+
if (!workspace) return /*#__PURE__*/ _jsx(FullPageLoader, {});
|
|
4
7
|
return children(workspace);
|
|
5
8
|
}
|
|
6
9
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/workspace-settings-shell.tsx"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../src/components/workspace-settings-shell.tsx"],"sourcesContent":["import {useMaybeActiveWorkspace} from '@shipfox/client-shell/runtime';\nimport {FullPageLoader} from '@shipfox/react-ui/loader';\nimport type {ReactNode} from 'react';\n\ninterface WorkspaceSettingsShellProps {\n children: (workspace: NonNullable<ReturnType<typeof useMaybeActiveWorkspace>>) => ReactNode;\n}\n\nexport function WorkspaceSettingsShell({children}: WorkspaceSettingsShellProps) {\n const workspace = useMaybeActiveWorkspace();\n if (!workspace) return <FullPageLoader />;\n\n return children(workspace);\n}\n"],"names":["useMaybeActiveWorkspace","FullPageLoader","WorkspaceSettingsShell","children","workspace"],"mappings":";AAAA,SAAQA,uBAAuB,QAAO,gCAAgC;AACtE,SAAQC,cAAc,QAAO,2BAA2B;AAOxD,OAAO,SAASC,uBAAuB,EAACC,QAAQ,EAA8B;IAC5E,MAAMC,YAAYJ;IAClB,IAAI,CAACI,WAAW,qBAAO,KAACH;IAExB,OAAOE,SAASC;AAClB"}
|
package/dist/feature.d.ts
CHANGED
|
@@ -2,10 +2,16 @@ export declare const workspaceSettingsNavigation: readonly [{
|
|
|
2
2
|
readonly id: "nav.settings";
|
|
3
3
|
readonly scope: "workspace";
|
|
4
4
|
readonly label: "Settings";
|
|
5
|
-
readonly to: "/
|
|
5
|
+
readonly to: "/w/$workspaceSlug/settings";
|
|
6
6
|
readonly order: 200;
|
|
7
7
|
}];
|
|
8
8
|
export declare const workspaceSettingsSections: readonly [{
|
|
9
|
+
readonly id: "settings.general";
|
|
10
|
+
readonly pathSegment: "general";
|
|
11
|
+
readonly label: "General";
|
|
12
|
+
readonly icon: "settings3Line";
|
|
13
|
+
readonly order: 50;
|
|
14
|
+
}, {
|
|
9
15
|
readonly id: "settings.members";
|
|
10
16
|
readonly pathSegment: "members";
|
|
11
17
|
readonly label: "Members";
|
|
@@ -15,22 +21,32 @@ export declare const workspaceSettingsSections: readonly [{
|
|
|
15
21
|
export declare const workspaceSettingsFeature: {
|
|
16
22
|
readonly id: "shipfox.workspace-settings";
|
|
17
23
|
readonly routes: readonly [{
|
|
18
|
-
readonly path: "/
|
|
24
|
+
readonly path: "/w/$workspaceSlug/settings";
|
|
19
25
|
readonly parent: "workspaceSettings";
|
|
20
26
|
readonly impl: "@shipfox/client-workspace-settings/routes/index";
|
|
21
27
|
}, {
|
|
22
|
-
readonly path: "/
|
|
28
|
+
readonly path: "/w/$workspaceSlug/settings/members";
|
|
23
29
|
readonly parent: "workspaceSettings";
|
|
24
30
|
readonly impl: "@shipfox/client-workspace-settings/routes/members";
|
|
31
|
+
}, {
|
|
32
|
+
readonly path: "/w/$workspaceSlug/settings/general";
|
|
33
|
+
readonly parent: "workspaceSettings";
|
|
34
|
+
readonly impl: "@shipfox/client-workspace-settings/routes/general";
|
|
25
35
|
}];
|
|
26
36
|
readonly navigation: readonly [{
|
|
27
37
|
readonly id: "nav.settings";
|
|
28
38
|
readonly scope: "workspace";
|
|
29
39
|
readonly label: "Settings";
|
|
30
|
-
readonly to: "/
|
|
40
|
+
readonly to: "/w/$workspaceSlug/settings";
|
|
31
41
|
readonly order: 200;
|
|
32
42
|
}];
|
|
33
43
|
readonly settingsSections: readonly [{
|
|
44
|
+
readonly id: "settings.general";
|
|
45
|
+
readonly pathSegment: "general";
|
|
46
|
+
readonly label: "General";
|
|
47
|
+
readonly icon: "settings3Line";
|
|
48
|
+
readonly order: 50;
|
|
49
|
+
}, {
|
|
34
50
|
readonly id: "settings.members";
|
|
35
51
|
readonly pathSegment: "members";
|
|
36
52
|
readonly label: "Members";
|
package/dist/feature.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,2BAA2B;;;;;;EAQG,CAAC;AAE5C,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../src/feature.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,2BAA2B;;;;;;EAQG,CAAC;AAE5C,eAAO,MAAM,yBAAyB;;;;;;;;;;;;EAec,CAAC;AAErD,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqBnC,CAAC"}
|
package/dist/feature.js
CHANGED
|
@@ -4,11 +4,18 @@ export const workspaceSettingsNavigation = [
|
|
|
4
4
|
id: 'nav.settings',
|
|
5
5
|
scope: 'workspace',
|
|
6
6
|
label: 'Settings',
|
|
7
|
-
to: '/
|
|
7
|
+
to: '/w/$workspaceSlug/settings',
|
|
8
8
|
order: 200
|
|
9
9
|
}
|
|
10
10
|
];
|
|
11
11
|
export const workspaceSettingsSections = [
|
|
12
|
+
{
|
|
13
|
+
id: 'settings.general',
|
|
14
|
+
pathSegment: 'general',
|
|
15
|
+
label: 'General',
|
|
16
|
+
icon: 'settings3Line',
|
|
17
|
+
order: 50
|
|
18
|
+
},
|
|
12
19
|
{
|
|
13
20
|
id: 'settings.members',
|
|
14
21
|
pathSegment: 'members',
|
|
@@ -21,14 +28,19 @@ export const workspaceSettingsFeature = defineClientFeature({
|
|
|
21
28
|
id: 'shipfox.workspace-settings',
|
|
22
29
|
routes: [
|
|
23
30
|
{
|
|
24
|
-
path: '/
|
|
31
|
+
path: '/w/$workspaceSlug/settings',
|
|
25
32
|
parent: 'workspaceSettings',
|
|
26
33
|
impl: '@shipfox/client-workspace-settings/routes/index'
|
|
27
34
|
},
|
|
28
35
|
{
|
|
29
|
-
path: '/
|
|
36
|
+
path: '/w/$workspaceSlug/settings/members',
|
|
30
37
|
parent: 'workspaceSettings',
|
|
31
38
|
impl: '@shipfox/client-workspace-settings/routes/members'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: '/w/$workspaceSlug/settings/general',
|
|
42
|
+
parent: 'workspaceSettings',
|
|
43
|
+
impl: '@shipfox/client-workspace-settings/routes/general'
|
|
32
44
|
}
|
|
33
45
|
],
|
|
34
46
|
navigation: workspaceSettingsNavigation,
|
package/dist/feature.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/feature.ts"],"sourcesContent":["import {\n defineClientFeature,\n type NavTabEntry,\n type SettingsSectionEntry,\n} from '@shipfox/client-shell';\n\nexport const workspaceSettingsNavigation = [\n {\n id: 'nav.settings',\n scope: 'workspace',\n label: 'Settings',\n to: '/
|
|
1
|
+
{"version":3,"sources":["../src/feature.ts"],"sourcesContent":["import {\n defineClientFeature,\n type NavTabEntry,\n type SettingsSectionEntry,\n} from '@shipfox/client-shell';\n\nexport const workspaceSettingsNavigation = [\n {\n id: 'nav.settings',\n scope: 'workspace',\n label: 'Settings',\n to: '/w/$workspaceSlug/settings',\n order: 200,\n },\n] as const satisfies readonly NavTabEntry[];\n\nexport const workspaceSettingsSections = [\n {\n id: 'settings.general',\n pathSegment: 'general',\n label: 'General',\n icon: 'settings3Line',\n order: 50,\n },\n {\n id: 'settings.members',\n pathSegment: 'members',\n label: 'Members',\n icon: 'userLine',\n order: 100,\n },\n] as const satisfies readonly SettingsSectionEntry[];\n\nexport const workspaceSettingsFeature = defineClientFeature({\n id: 'shipfox.workspace-settings',\n routes: [\n {\n path: '/w/$workspaceSlug/settings',\n parent: 'workspaceSettings',\n impl: '@shipfox/client-workspace-settings/routes/index',\n },\n {\n path: '/w/$workspaceSlug/settings/members',\n parent: 'workspaceSettings',\n impl: '@shipfox/client-workspace-settings/routes/members',\n },\n {\n path: '/w/$workspaceSlug/settings/general',\n parent: 'workspaceSettings',\n impl: '@shipfox/client-workspace-settings/routes/general',\n },\n ],\n navigation: workspaceSettingsNavigation,\n settingsSections: workspaceSettingsSections,\n});\n"],"names":["defineClientFeature","workspaceSettingsNavigation","id","scope","label","to","order","workspaceSettingsSections","pathSegment","icon","workspaceSettingsFeature","routes","path","parent","impl","navigation","settingsSections"],"mappings":"AAAA,SACEA,mBAAmB,QAGd,wBAAwB;AAE/B,OAAO,MAAMC,8BAA8B;IACzC;QACEC,IAAI;QACJC,OAAO;QACPC,OAAO;QACPC,IAAI;QACJC,OAAO;IACT;CACD,CAA2C;AAE5C,OAAO,MAAMC,4BAA4B;IACvC;QACEL,IAAI;QACJM,aAAa;QACbJ,OAAO;QACPK,MAAM;QACNH,OAAO;IACT;IACA;QACEJ,IAAI;QACJM,aAAa;QACbJ,OAAO;QACPK,MAAM;QACNH,OAAO;IACT;CACD,CAAoD;AAErD,OAAO,MAAMI,2BAA2BV,oBAAoB;IAC1DE,IAAI;IACJS,QAAQ;QACN;YACEC,MAAM;YACNC,QAAQ;YACRC,MAAM;QACR;QACA;YACEF,MAAM;YACNC,QAAQ;YACRC,MAAM;QACR;QACA;YACEF,MAAM;YACNC,QAAQ;YACRC,MAAM;QACR;KACD;IACDC,YAAYd;IACZe,kBAAkBT;AACpB,GAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"general-settings-page.d.ts","sourceRoot":"","sources":["../../src/pages/general-settings-page.tsx"],"names":[],"mappings":"AA2BA,wBAAgB,mBAAmB,gCAWlC"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { createWorkspaceBodySchema } from '@shipfox/api-workspaces-dto';
|
|
3
|
+
import { checkWorkspaceSlugAvailability, useUpdateWorkspaceMutation } from '@shipfox/client-auth';
|
|
4
|
+
import { displayNameFieldError, SlugChangeWarning, SlugField } from '@shipfox/client-ui';
|
|
5
|
+
import { Button } from '@shipfox/react-ui/button';
|
|
6
|
+
import { Callout } from '@shipfox/react-ui/callout';
|
|
7
|
+
import { FormField, FormFieldInput, fieldError } from '@shipfox/react-ui/form-field';
|
|
8
|
+
import { toast } from '@shipfox/react-ui/toast';
|
|
9
|
+
import { Header, Text } from '@shipfox/react-ui/typography';
|
|
10
|
+
import { useForm } from '@tanstack/react-form';
|
|
11
|
+
import { useNavigate } from '@tanstack/react-router';
|
|
12
|
+
import { useState } from 'react';
|
|
13
|
+
import { workspaceGeneralErrorToFormError } from '#components/general/form-errors.js';
|
|
14
|
+
import { WorkspaceSettingsShell } from '#components/workspace-settings-shell.js';
|
|
15
|
+
function isSlugValid(value) {
|
|
16
|
+
return createWorkspaceBodySchema.shape.slug.safeParse(value).success;
|
|
17
|
+
}
|
|
18
|
+
export function GeneralSettingsPage() {
|
|
19
|
+
return /*#__PURE__*/ _jsx(WorkspaceSettingsShell, {
|
|
20
|
+
children: (workspace)=>/*#__PURE__*/ _jsx(WorkspaceGeneralForm, {
|
|
21
|
+
workspace: workspace
|
|
22
|
+
}, `${workspace.id}:${workspace.name}:${workspace.slug}`)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function WorkspaceGeneralForm({ workspace }) {
|
|
26
|
+
const updateWorkspace = useUpdateWorkspaceMutation();
|
|
27
|
+
const navigate = useNavigate();
|
|
28
|
+
const [warningOpen, setWarningOpen] = useState(false);
|
|
29
|
+
const [pendingValues, setPendingValues] = useState();
|
|
30
|
+
const [formError, setFormError] = useState();
|
|
31
|
+
const form = useForm({
|
|
32
|
+
defaultValues: {
|
|
33
|
+
name: workspace.name,
|
|
34
|
+
slug: workspace.slug
|
|
35
|
+
},
|
|
36
|
+
onSubmit: async ({ value })=>{
|
|
37
|
+
if (value.slug !== workspace.slug) {
|
|
38
|
+
setPendingValues(value);
|
|
39
|
+
setWarningOpen(true);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await save(value);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
async function save(values) {
|
|
46
|
+
setFormError(undefined);
|
|
47
|
+
const nameChanged = values.name !== workspace.name;
|
|
48
|
+
const slugChanged = values.slug !== workspace.slug;
|
|
49
|
+
if (!nameChanged && !slugChanged) return;
|
|
50
|
+
const command = {
|
|
51
|
+
workspaceId: workspace.id,
|
|
52
|
+
...nameChanged ? {
|
|
53
|
+
name: values.name
|
|
54
|
+
} : {},
|
|
55
|
+
...slugChanged ? {
|
|
56
|
+
slug: values.slug
|
|
57
|
+
} : {}
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
const updated = await updateWorkspace.mutateAsync(command);
|
|
61
|
+
setPendingValues(undefined);
|
|
62
|
+
setWarningOpen(false);
|
|
63
|
+
toast.success('Workspace settings saved.');
|
|
64
|
+
await navigate({
|
|
65
|
+
to: '/w/$workspaceSlug/settings/general',
|
|
66
|
+
params: {
|
|
67
|
+
workspaceSlug: updated.slug
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
} catch (error) {
|
|
71
|
+
setPendingValues(undefined);
|
|
72
|
+
setWarningOpen(false);
|
|
73
|
+
const mapped = workspaceGeneralErrorToFormError(error);
|
|
74
|
+
if (mapped.kind === 'field') {
|
|
75
|
+
form.setFieldMeta(mapped.field, (previous)=>({
|
|
76
|
+
...previous,
|
|
77
|
+
errorMap: {
|
|
78
|
+
...previous.errorMap,
|
|
79
|
+
onServer: mapped.message
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
} else {
|
|
83
|
+
setFormError(mapped.message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
88
|
+
children: [
|
|
89
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
90
|
+
className: "flex min-w-0 flex-col gap-24",
|
|
91
|
+
children: [
|
|
92
|
+
/*#__PURE__*/ _jsxs("header", {
|
|
93
|
+
className: "flex flex-col gap-6",
|
|
94
|
+
children: [
|
|
95
|
+
/*#__PURE__*/ _jsx(Header, {
|
|
96
|
+
variant: "h1",
|
|
97
|
+
children: "General"
|
|
98
|
+
}),
|
|
99
|
+
/*#__PURE__*/ _jsx(Text, {
|
|
100
|
+
size: "sm",
|
|
101
|
+
className: "text-foreground-neutral-muted",
|
|
102
|
+
children: "Update the workspace name and the slug used in its URLs."
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
}),
|
|
106
|
+
formError ? /*#__PURE__*/ _jsx(Callout, {
|
|
107
|
+
role: "alert",
|
|
108
|
+
type: "error",
|
|
109
|
+
children: formError
|
|
110
|
+
}) : null,
|
|
111
|
+
/*#__PURE__*/ _jsxs("form", {
|
|
112
|
+
className: "flex max-w-[560px] flex-col gap-16",
|
|
113
|
+
noValidate: true,
|
|
114
|
+
onSubmit: (event)=>{
|
|
115
|
+
event.preventDefault();
|
|
116
|
+
event.stopPropagation();
|
|
117
|
+
void form.handleSubmit();
|
|
118
|
+
},
|
|
119
|
+
children: [
|
|
120
|
+
/*#__PURE__*/ _jsx(form.Field, {
|
|
121
|
+
name: "name",
|
|
122
|
+
validators: {
|
|
123
|
+
onBlur: ({ value })=>displayNameFieldError(value, 'Workspace name', createWorkspaceBodySchema.shape.name),
|
|
124
|
+
onSubmit: ({ value })=>displayNameFieldError(value, 'Workspace name', createWorkspaceBodySchema.shape.name)
|
|
125
|
+
},
|
|
126
|
+
children: (field)=>/*#__PURE__*/ _jsx(FormField, {
|
|
127
|
+
label: "Workspace name",
|
|
128
|
+
id: "workspace-settings-name",
|
|
129
|
+
error: fieldError(field),
|
|
130
|
+
children: /*#__PURE__*/ _jsx(FormFieldInput, {
|
|
131
|
+
name: "name",
|
|
132
|
+
type: "text",
|
|
133
|
+
value: field.state.value,
|
|
134
|
+
onChange: (event)=>field.handleChange(event.target.value),
|
|
135
|
+
onBlur: field.handleBlur
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
}),
|
|
139
|
+
/*#__PURE__*/ _jsx(form.Field, {
|
|
140
|
+
name: "slug",
|
|
141
|
+
validators: {
|
|
142
|
+
onBlur: createWorkspaceBodySchema.shape.slug,
|
|
143
|
+
onSubmit: createWorkspaceBodySchema.shape.slug
|
|
144
|
+
},
|
|
145
|
+
children: (field)=>/*#__PURE__*/ _jsx(SlugField, {
|
|
146
|
+
id: "workspace-settings-slug",
|
|
147
|
+
label: "Workspace slug",
|
|
148
|
+
name: "slug",
|
|
149
|
+
value: field.state.value,
|
|
150
|
+
onChange: (value)=>field.handleChange(value),
|
|
151
|
+
onBlur: field.handleBlur,
|
|
152
|
+
error: fieldError(field),
|
|
153
|
+
description: /*#__PURE__*/ _jsxs("span", {
|
|
154
|
+
className: "break-all font-code",
|
|
155
|
+
children: [
|
|
156
|
+
"/w/",
|
|
157
|
+
field.state.value
|
|
158
|
+
]
|
|
159
|
+
}),
|
|
160
|
+
placeholder: "acme",
|
|
161
|
+
className: "font-code",
|
|
162
|
+
currentSlug: workspace.slug,
|
|
163
|
+
checkEnabled: true,
|
|
164
|
+
isValid: isSlugValid,
|
|
165
|
+
checkAvailability: checkWorkspaceSlugAvailability
|
|
166
|
+
})
|
|
167
|
+
}),
|
|
168
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
169
|
+
type: "submit",
|
|
170
|
+
isLoading: updateWorkspace.isPending,
|
|
171
|
+
className: "self-start",
|
|
172
|
+
children: "Save changes"
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
})
|
|
176
|
+
]
|
|
177
|
+
}),
|
|
178
|
+
/*#__PURE__*/ _jsx(SlugChangeWarning, {
|
|
179
|
+
open: warningOpen,
|
|
180
|
+
onOpenChange: setWarningOpen,
|
|
181
|
+
entityLabel: "workspace",
|
|
182
|
+
isLoading: updateWorkspace.isPending,
|
|
183
|
+
onConfirm: ()=>{
|
|
184
|
+
if (pendingValues) void save(pendingValues);
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
]
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
//# sourceMappingURL=general-settings-page.js.map
|