@workos-inc/widgets 0.0.0-pre.1 → 0.0.0-pre.2
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/dist/cjs/lib/api/role.d.ts.map +1 -1
- package/dist/cjs/lib/api/role.js +35 -14
- package/dist/cjs/lib/api/role.js.map +1 -1
- package/dist/cjs/lib/api/user.d.ts.map +1 -1
- package/dist/cjs/lib/api/user.js +105 -67
- package/dist/cjs/lib/api/user.js.map +1 -1
- package/dist/cjs/lib/constants.d.ts +2 -1
- package/dist/cjs/lib/constants.d.ts.map +1 -1
- package/dist/cjs/lib/constants.js +3 -2
- package/dist/cjs/lib/constants.js.map +1 -1
- package/dist/cjs/lib/delete-user-dialog.d.ts.map +1 -1
- package/dist/cjs/lib/delete-user-dialog.js +1 -1
- package/dist/cjs/lib/delete-user-dialog.js.map +1 -1
- package/dist/cjs/lib/edit-user-details-dialog.d.ts.map +1 -1
- package/dist/cjs/lib/edit-user-details-dialog.js +3 -5
- package/dist/cjs/lib/edit-user-details-dialog.js.map +1 -1
- package/dist/cjs/lib/elements.d.ts +21 -12
- package/dist/cjs/lib/elements.d.ts.map +1 -1
- package/dist/cjs/lib/elements.js +118 -30
- package/dist/cjs/lib/elements.js.map +1 -1
- package/dist/cjs/lib/error-boundary.d.ts +58 -0
- package/dist/cjs/lib/error-boundary.d.ts.map +1 -0
- package/dist/cjs/lib/error-boundary.js +113 -0
- package/dist/cjs/lib/error-boundary.js.map +1 -0
- package/dist/cjs/lib/errors.d.ts +34 -0
- package/dist/cjs/lib/errors.d.ts.map +1 -0
- package/dist/cjs/lib/errors.js +40 -0
- package/dist/cjs/lib/errors.js.map +1 -0
- package/dist/cjs/lib/invite-user-dialog.d.ts.map +1 -1
- package/dist/cjs/lib/invite-user-dialog.js +2 -2
- package/dist/cjs/lib/invite-user-dialog.js.map +1 -1
- package/dist/cjs/lib/resend-invite-dialog.d.ts.map +1 -1
- package/dist/cjs/lib/resend-invite-dialog.js +2 -2
- package/dist/cjs/lib/resend-invite-dialog.js.map +1 -1
- package/dist/cjs/lib/revoke-invite-dialog.d.ts.map +1 -1
- package/dist/cjs/lib/revoke-invite-dialog.js +1 -1
- package/dist/cjs/lib/revoke-invite-dialog.js.map +1 -1
- package/dist/cjs/lib/use-layout-effect.d.ts +4 -0
- package/dist/cjs/lib/use-layout-effect.d.ts.map +1 -0
- package/dist/cjs/lib/use-layout-effect.js +8 -0
- package/dist/cjs/lib/use-layout-effect.js.map +1 -0
- package/dist/cjs/lib/user-actions-dropdown.d.ts.map +1 -1
- package/dist/cjs/lib/user-actions-dropdown.js +2 -9
- package/dist/cjs/lib/user-actions-dropdown.js.map +1 -1
- package/dist/cjs/lib/users-filter.d.ts +1 -0
- package/dist/cjs/lib/users-filter.d.ts.map +1 -1
- package/dist/cjs/lib/users-filter.js +2 -9
- package/dist/cjs/lib/users-filter.js.map +1 -1
- package/dist/cjs/lib/users-management-context.d.ts +2 -2
- package/dist/cjs/lib/users-management-context.d.ts.map +1 -1
- package/dist/cjs/lib/users-management.d.ts +9 -6
- package/dist/cjs/lib/users-management.d.ts.map +1 -1
- package/dist/cjs/lib/users-management.js +77 -20
- package/dist/cjs/lib/users-management.js.map +1 -1
- package/dist/cjs/lib/users-search.d.ts.map +1 -1
- package/dist/cjs/lib/users-search.js +3 -9
- package/dist/cjs/lib/users-search.js.map +1 -1
- package/dist/cjs/lib/utils.d.ts +2 -0
- package/dist/cjs/lib/utils.d.ts.map +1 -1
- package/dist/cjs/lib/utils.js +18 -0
- package/dist/cjs/lib/utils.js.map +1 -1
- package/dist/cjs/users-management.client.d.ts +1 -1
- package/dist/cjs/users-management.client.d.ts.map +1 -1
- package/dist/cjs/users-management.client.js +15 -5
- package/dist/cjs/users-management.client.js.map +1 -1
- package/dist/cjs/workos-widgets.client.d.ts.map +1 -1
- package/dist/cjs/workos-widgets.client.js +2 -1
- package/dist/cjs/workos-widgets.client.js.map +1 -1
- package/dist/esm/lib/api/role.d.ts.map +1 -1
- package/dist/esm/lib/api/role.js +35 -14
- package/dist/esm/lib/api/role.js.map +1 -1
- package/dist/esm/lib/api/user.d.ts.map +1 -1
- package/dist/esm/lib/api/user.js +105 -67
- package/dist/esm/lib/api/user.js.map +1 -1
- package/dist/esm/lib/constants.d.ts +2 -1
- package/dist/esm/lib/constants.d.ts.map +1 -1
- package/dist/esm/lib/constants.js +2 -1
- package/dist/esm/lib/constants.js.map +1 -1
- package/dist/esm/lib/delete-user-dialog.d.ts.map +1 -1
- package/dist/esm/lib/delete-user-dialog.js +2 -2
- package/dist/esm/lib/delete-user-dialog.js.map +1 -1
- package/dist/esm/lib/edit-user-details-dialog.d.ts.map +1 -1
- package/dist/esm/lib/edit-user-details-dialog.js +4 -6
- package/dist/esm/lib/edit-user-details-dialog.js.map +1 -1
- package/dist/esm/lib/elements.d.ts +21 -12
- package/dist/esm/lib/elements.d.ts.map +1 -1
- package/dist/esm/lib/elements.js +95 -30
- package/dist/esm/lib/elements.js.map +1 -1
- package/dist/esm/lib/error-boundary.d.ts +58 -0
- package/dist/esm/lib/error-boundary.d.ts.map +1 -0
- package/dist/esm/lib/error-boundary.js +86 -0
- package/dist/esm/lib/error-boundary.js.map +1 -0
- package/dist/esm/lib/errors.d.ts +34 -0
- package/dist/esm/lib/errors.d.ts.map +1 -0
- package/dist/esm/lib/errors.js +34 -0
- package/dist/esm/lib/errors.js.map +1 -0
- package/dist/esm/lib/invite-user-dialog.d.ts.map +1 -1
- package/dist/esm/lib/invite-user-dialog.js +3 -3
- package/dist/esm/lib/invite-user-dialog.js.map +1 -1
- package/dist/esm/lib/resend-invite-dialog.d.ts.map +1 -1
- package/dist/esm/lib/resend-invite-dialog.js +3 -3
- package/dist/esm/lib/resend-invite-dialog.js.map +1 -1
- package/dist/esm/lib/revoke-invite-dialog.d.ts.map +1 -1
- package/dist/esm/lib/revoke-invite-dialog.js +2 -2
- package/dist/esm/lib/revoke-invite-dialog.js.map +1 -1
- package/dist/esm/lib/use-layout-effect.d.ts +4 -0
- package/dist/esm/lib/use-layout-effect.d.ts.map +1 -0
- package/dist/esm/lib/use-layout-effect.js +5 -0
- package/dist/esm/lib/use-layout-effect.js.map +1 -0
- package/dist/esm/lib/user-actions-dropdown.d.ts.map +1 -1
- package/dist/esm/lib/user-actions-dropdown.js +3 -10
- package/dist/esm/lib/user-actions-dropdown.js.map +1 -1
- package/dist/esm/lib/users-filter.d.ts +1 -0
- package/dist/esm/lib/users-filter.d.ts.map +1 -1
- package/dist/esm/lib/users-filter.js +4 -11
- package/dist/esm/lib/users-filter.js.map +1 -1
- package/dist/esm/lib/users-management-context.d.ts +2 -2
- package/dist/esm/lib/users-management-context.d.ts.map +1 -1
- package/dist/esm/lib/users-management.d.ts +9 -6
- package/dist/esm/lib/users-management.d.ts.map +1 -1
- package/dist/esm/lib/users-management.js +74 -22
- package/dist/esm/lib/users-management.js.map +1 -1
- package/dist/esm/lib/users-search.d.ts.map +1 -1
- package/dist/esm/lib/users-search.js +5 -11
- package/dist/esm/lib/users-search.js.map +1 -1
- package/dist/esm/lib/utils.d.ts +2 -0
- package/dist/esm/lib/utils.d.ts.map +1 -1
- package/dist/esm/lib/utils.js +13 -0
- package/dist/esm/lib/utils.js.map +1 -1
- package/dist/esm/users-management.client.d.ts +1 -1
- package/dist/esm/users-management.client.d.ts.map +1 -1
- package/dist/esm/users-management.client.js +16 -6
- package/dist/esm/users-management.client.js.map +1 -1
- package/dist/esm/workos-widgets.client.d.ts.map +1 -1
- package/dist/esm/workos-widgets.client.js +2 -1
- package/dist/esm/workos-widgets.client.js.map +1 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +8 -3
- package/src/base.css +111 -0
- package/src/lib/api/role.ts +39 -16
- package/src/lib/api/user.ts +119 -75
- package/src/lib/constants.ts +2 -1
- package/src/lib/delete-user-dialog.tsx +7 -3
- package/src/lib/edit-user-details-dialog.tsx +15 -10
- package/src/lib/elements.tsx +242 -61
- package/src/lib/error-boundary.tsx +166 -0
- package/src/lib/errors.ts +49 -0
- package/src/lib/invite-user-dialog.tsx +21 -12
- package/src/lib/resend-invite-dialog.tsx +11 -5
- package/src/lib/revoke-invite-dialog.tsx +7 -3
- package/src/lib/use-layout-effect.ts +6 -0
- package/src/lib/user-actions-dropdown.tsx +8 -16
- package/src/lib/users-filter.tsx +13 -73
- package/src/lib/users-management-context.tsx +1 -1
- package/src/lib/users-management.tsx +345 -184
- package/src/lib/users-search.tsx +5 -63
- package/src/lib/utils.ts +21 -0
- package/src/users-management.client.tsx +39 -16
- package/src/users-management.css +4 -0
- package/src/workos-widgets.client.tsx +2 -1
- package/dist/cjs/lib/pagination.d.ts +0 -8
- package/dist/cjs/lib/pagination.d.ts.map +0 -1
- package/dist/cjs/lib/pagination.js +0 -67
- package/dist/cjs/lib/pagination.js.map +0 -1
- package/dist/esm/lib/pagination.d.ts +0 -8
- package/dist/esm/lib/pagination.d.ts.map +0 -1
- package/dist/esm/lib/pagination.js +0 -40
- package/dist/esm/lib/pagination.js.map +0 -1
- package/src/lib/pagination.tsx +0 -69
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
Box,
|
|
6
6
|
Flex,
|
|
7
7
|
Grid,
|
|
8
|
+
Heading,
|
|
9
|
+
Select,
|
|
8
10
|
Skeleton,
|
|
9
11
|
Table,
|
|
10
12
|
Text,
|
|
@@ -19,9 +21,12 @@ import {
|
|
|
19
21
|
IconButton,
|
|
20
22
|
PrimaryButton,
|
|
21
23
|
SecondaryButton,
|
|
24
|
+
SelectContent,
|
|
25
|
+
SelectItem,
|
|
26
|
+
SelectTrigger,
|
|
27
|
+
TextField,
|
|
22
28
|
} from "./elements";
|
|
23
29
|
import { InviteUserDialog } from "./invite-user-dialog";
|
|
24
|
-
import { Pagination } from "./pagination";
|
|
25
30
|
import { SearchProvider, useSearchContext } from "./search-provider";
|
|
26
31
|
import { useIsHydrated } from "./use-is-hydrated";
|
|
27
32
|
import { UserActionsDropdown } from "./user-actions-dropdown";
|
|
@@ -30,65 +35,81 @@ import { UsersSearch } from "./users-search";
|
|
|
30
35
|
import { getBestName, getComparativeReadableDate } from "./utils";
|
|
31
36
|
import { USER_ROW_LIMIT } from "./constants";
|
|
32
37
|
import { useUsersManagementContext } from "./users-management-context";
|
|
38
|
+
import clsx from "clsx";
|
|
39
|
+
import { ApiError, FetchError, NoAuthTokenError } from "./errors";
|
|
33
40
|
|
|
34
41
|
interface UsersManagementProps {
|
|
35
|
-
rolesData: Role[]
|
|
36
|
-
userData: Paginated<User[]
|
|
37
|
-
|
|
38
|
-
// Render the rows with an alternate background color
|
|
39
|
-
alternate?: boolean;
|
|
42
|
+
rolesData: Role[];
|
|
43
|
+
userData: Paginated<User[]>;
|
|
44
|
+
disableRolesFilter?: boolean;
|
|
40
45
|
// When the users list is loading new users
|
|
41
|
-
isPending
|
|
42
|
-
// Show the skeleton UI
|
|
43
|
-
isInitialLoading?: boolean;
|
|
46
|
+
isPending: boolean | undefined;
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
export const UsersManagement = ({
|
|
47
50
|
userData,
|
|
48
51
|
rolesData,
|
|
49
|
-
alternate,
|
|
50
52
|
isPending,
|
|
51
|
-
|
|
53
|
+
disableRolesFilter,
|
|
52
54
|
}: UsersManagementProps) => {
|
|
53
55
|
const users = userData?.data;
|
|
54
56
|
const usersCount = users?.length ?? 0;
|
|
55
57
|
const isHydrated = useIsHydrated();
|
|
58
|
+
const pagination = userData?.pagination;
|
|
59
|
+
const { dispatch } = useUsersManagementContext();
|
|
60
|
+
|
|
61
|
+
// we only want to show the loading indicator for some buttons if the request
|
|
62
|
+
// is still pending after 500ms. If the request is fast enough the indicator
|
|
63
|
+
// is a bit jarring.
|
|
64
|
+
const [deferredLoading, setDeferredLoading] = React.useState(false);
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
if (isPending) {
|
|
67
|
+
const timeoutId = window.setTimeout(() => {
|
|
68
|
+
setDeferredLoading(true);
|
|
69
|
+
}, 500);
|
|
70
|
+
return () => {
|
|
71
|
+
window.clearTimeout(timeoutId);
|
|
72
|
+
};
|
|
73
|
+
} else {
|
|
74
|
+
setDeferredLoading(false);
|
|
75
|
+
}
|
|
76
|
+
}, [isPending]);
|
|
77
|
+
|
|
56
78
|
return (
|
|
57
79
|
<SearchProvider>
|
|
58
|
-
<
|
|
80
|
+
<UsersManagementRoot>
|
|
59
81
|
<Grid columns="1fr auto" gap="2">
|
|
60
82
|
<Flex gap="2" align="center">
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
<UsersSearch />
|
|
64
|
-
</Box>
|
|
65
|
-
</Skeleton>
|
|
66
|
-
<Skeleton loading={isInitialLoading}>
|
|
67
|
-
<Box flexGrow="0" flexShrink="0">
|
|
68
|
-
<UsersFilter roles={rolesData} />
|
|
69
|
-
</Box>
|
|
70
|
-
</Skeleton>
|
|
71
|
-
</Flex>
|
|
72
|
-
|
|
73
|
-
<Skeleton loading={isInitialLoading}>
|
|
74
|
-
<Box flexGrow="0" flexShrink="0" style={{ placeSelf: "flex-end" }}>
|
|
75
|
-
<InviteUserDialog>
|
|
76
|
-
<PrimaryButton>Invite user</PrimaryButton>
|
|
77
|
-
</InviteUserDialog>
|
|
83
|
+
<Box flexBasis="380px" flexGrow="0" flexShrink="1">
|
|
84
|
+
<UsersSearch />
|
|
78
85
|
</Box>
|
|
79
|
-
|
|
86
|
+
<Box flexGrow="0" flexShrink="0">
|
|
87
|
+
<Select.Root value="all" onValueChange={() => void 0}>
|
|
88
|
+
<SelectTrigger>All</SelectTrigger>
|
|
89
|
+
<SelectContent>
|
|
90
|
+
<SelectItem value="all">All</SelectItem>
|
|
91
|
+
</SelectContent>
|
|
92
|
+
</Select.Root>
|
|
93
|
+
<UsersFilter roles={rolesData} disabled={disableRolesFilter} />
|
|
94
|
+
</Box>
|
|
95
|
+
</Flex>
|
|
96
|
+
<Box flexGrow="0" flexShrink="0" style={{ placeSelf: "flex-end" }}>
|
|
97
|
+
<InviteUserDialog>
|
|
98
|
+
<PrimaryButton>Invite user</PrimaryButton>
|
|
99
|
+
</InviteUserDialog>
|
|
100
|
+
</Box>
|
|
80
101
|
</Grid>
|
|
81
102
|
<Table.Root variant="ghost" size="1">
|
|
82
103
|
<Table.Header>
|
|
83
104
|
<Table.Row>
|
|
84
105
|
<Table.ColumnHeaderCell width="260px">
|
|
85
|
-
|
|
106
|
+
User
|
|
86
107
|
</Table.ColumnHeaderCell>
|
|
87
108
|
<Table.ColumnHeaderCell width="100px">
|
|
88
|
-
|
|
109
|
+
Role
|
|
89
110
|
</Table.ColumnHeaderCell>
|
|
90
111
|
<Table.ColumnHeaderCell width="140px">
|
|
91
|
-
|
|
112
|
+
Last active
|
|
92
113
|
</Table.ColumnHeaderCell>
|
|
93
114
|
<Table.ColumnHeaderCell width="28px" />
|
|
94
115
|
</Table.Row>
|
|
@@ -100,118 +121,108 @@ export const UsersManagement = ({
|
|
|
100
121
|
opacity: isPending && usersCount > 0 ? 0.5 : 1,
|
|
101
122
|
}}
|
|
102
123
|
>
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
overflow="hidden"
|
|
127
|
-
height="var(--space-7)"
|
|
128
|
-
>
|
|
129
|
-
<Avatar
|
|
130
|
-
size="2"
|
|
131
|
-
fallback={<FallbackUserIcon />}
|
|
132
|
-
src={user.profilePictureUrl ?? undefined}
|
|
133
|
-
dim={dimText}
|
|
134
|
-
/>
|
|
124
|
+
{users.length > 0 ? (
|
|
125
|
+
users.map((user) => {
|
|
126
|
+
// TODO only support one role for now
|
|
127
|
+
const userRole = user.roles[0]?.name;
|
|
128
|
+
const userDisplayName = getBestName(user);
|
|
129
|
+
const dimText =
|
|
130
|
+
user.status === "InviteRevoked" ||
|
|
131
|
+
user.status === "InviteExpired";
|
|
132
|
+
return (
|
|
133
|
+
<Table.Row key={user.id} align="center">
|
|
134
|
+
<Table.RowHeaderCell>
|
|
135
|
+
<Flex
|
|
136
|
+
align="center"
|
|
137
|
+
gap="3"
|
|
138
|
+
overflow="hidden"
|
|
139
|
+
height="var(--space-7)"
|
|
140
|
+
>
|
|
141
|
+
<Avatar
|
|
142
|
+
size="2"
|
|
143
|
+
fallback={<FallbackUserIcon />}
|
|
144
|
+
src={user.profilePictureUrl ?? undefined}
|
|
145
|
+
dim={dimText}
|
|
146
|
+
/>
|
|
135
147
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
{userDisplayName ? (
|
|
149
|
+
<Flex
|
|
150
|
+
direction="column"
|
|
151
|
+
align="start"
|
|
152
|
+
height="var(--space-7)"
|
|
153
|
+
justify="center"
|
|
154
|
+
overflow="hidden"
|
|
155
|
+
>
|
|
156
|
+
<Flex gap="2" align="center" minWidth="0">
|
|
157
|
+
<TableCellText dim={dimText}>
|
|
158
|
+
{userDisplayName}
|
|
159
|
+
</TableCellText>
|
|
160
|
+
<UserBadge user={user} />
|
|
161
|
+
</Flex>
|
|
162
|
+
<TableCellText
|
|
163
|
+
level="secondary"
|
|
164
|
+
title={user.email}
|
|
165
|
+
dim={dimText}
|
|
166
|
+
>
|
|
167
|
+
{user.email}
|
|
168
|
+
</TableCellText>
|
|
169
|
+
</Flex>
|
|
170
|
+
) : (
|
|
144
171
|
<Flex gap="2" align="center" minWidth="0">
|
|
145
|
-
<TableCellText dim={dimText}>
|
|
146
|
-
{
|
|
172
|
+
<TableCellText dim={dimText} title={user.email}>
|
|
173
|
+
{user.email}
|
|
147
174
|
</TableCellText>
|
|
148
175
|
<UserBadge user={user} />
|
|
149
176
|
</Flex>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
177
|
+
)}
|
|
178
|
+
</Flex>
|
|
179
|
+
</Table.RowHeaderCell>
|
|
180
|
+
<Table.Cell>
|
|
181
|
+
<TableCellText dim={dimText}>
|
|
182
|
+
{userRole || (
|
|
183
|
+
<>
|
|
184
|
+
<VisuallyHidden>No roles assigned</VisuallyHidden>
|
|
185
|
+
<span aria-hidden style={{ userSelect: "none" }}>
|
|
186
|
+
–
|
|
187
|
+
</span>
|
|
188
|
+
</>
|
|
189
|
+
)}
|
|
190
|
+
</TableCellText>
|
|
191
|
+
</Table.Cell>
|
|
192
|
+
<Table.Cell>
|
|
193
|
+
<LastActive
|
|
194
|
+
user={user}
|
|
195
|
+
isHydrated={isHydrated}
|
|
196
|
+
dim={dimText}
|
|
197
|
+
/>
|
|
198
|
+
</Table.Cell>
|
|
199
|
+
<Table.Cell justify="end">
|
|
200
|
+
<UserActionsDropdown user={user}>
|
|
201
|
+
<IconButton title="User actions">
|
|
202
|
+
<VisuallyHidden>User actions</VisuallyHidden>
|
|
203
|
+
<svg
|
|
204
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
205
|
+
fill="none"
|
|
206
|
+
viewBox="0 0 24 24"
|
|
207
|
+
width="16"
|
|
208
|
+
height="16"
|
|
209
|
+
strokeWidth={1.5}
|
|
210
|
+
stroke="currentColor"
|
|
211
|
+
aria-hidden
|
|
154
212
|
>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
<Table.Cell>
|
|
169
|
-
<TableCellText dim={dimText}>
|
|
170
|
-
{userRole || (
|
|
171
|
-
<>
|
|
172
|
-
<VisuallyHidden>No roles assigned</VisuallyHidden>
|
|
173
|
-
<span aria-hidden style={{ userSelect: "none" }}>
|
|
174
|
-
–
|
|
175
|
-
</span>
|
|
176
|
-
</>
|
|
177
|
-
)}
|
|
178
|
-
</TableCellText>
|
|
179
|
-
</Table.Cell>
|
|
180
|
-
<Table.Cell>
|
|
181
|
-
<LastActive
|
|
182
|
-
user={user}
|
|
183
|
-
isHydrated={isHydrated}
|
|
184
|
-
dim={dimText}
|
|
185
|
-
/>
|
|
186
|
-
</Table.Cell>
|
|
187
|
-
<Table.Cell justify="end">
|
|
188
|
-
<UserActionsDropdown user={user}>
|
|
189
|
-
<IconButton title="User actions">
|
|
190
|
-
<VisuallyHidden>User actions</VisuallyHidden>
|
|
191
|
-
<svg
|
|
192
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
193
|
-
fill="none"
|
|
194
|
-
viewBox="0 0 24 24"
|
|
195
|
-
width="16"
|
|
196
|
-
height="16"
|
|
197
|
-
strokeWidth={1.5}
|
|
198
|
-
stroke="currentColor"
|
|
199
|
-
aria-hidden
|
|
200
|
-
>
|
|
201
|
-
<path
|
|
202
|
-
strokeLinecap="round"
|
|
203
|
-
strokeLinejoin="round"
|
|
204
|
-
d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
|
|
205
|
-
/>
|
|
206
|
-
</svg>
|
|
207
|
-
</IconButton>
|
|
208
|
-
</UserActionsDropdown>
|
|
209
|
-
</Table.Cell>
|
|
210
|
-
</Table.Row>
|
|
211
|
-
);
|
|
212
|
-
})}
|
|
213
|
-
|
|
214
|
-
{users?.length === 0 && (
|
|
213
|
+
<path
|
|
214
|
+
strokeLinecap="round"
|
|
215
|
+
strokeLinejoin="round"
|
|
216
|
+
d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
|
|
217
|
+
/>
|
|
218
|
+
</svg>
|
|
219
|
+
</IconButton>
|
|
220
|
+
</UserActionsDropdown>
|
|
221
|
+
</Table.Cell>
|
|
222
|
+
</Table.Row>
|
|
223
|
+
);
|
|
224
|
+
})
|
|
225
|
+
) : (
|
|
215
226
|
<Table.Row align="center">
|
|
216
227
|
<Table.Cell colSpan={4}>
|
|
217
228
|
<UsersManagementEmptyState />
|
|
@@ -221,12 +232,205 @@ export const UsersManagement = ({
|
|
|
221
232
|
</Table.Body>
|
|
222
233
|
</Table.Root>
|
|
223
234
|
|
|
224
|
-
<
|
|
225
|
-
|
|
235
|
+
<Flex gap="2" justify="end">
|
|
236
|
+
<SecondaryButton
|
|
237
|
+
size="1"
|
|
238
|
+
disabled={!pagination?.after || isPending || undefined}
|
|
239
|
+
loading={deferredLoading}
|
|
240
|
+
onClick={() => {
|
|
241
|
+
if (pagination?.after) {
|
|
242
|
+
dispatch({
|
|
243
|
+
type: "SET_PAGINATION",
|
|
244
|
+
pagination: { after: pagination.after },
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}}
|
|
248
|
+
>
|
|
249
|
+
Previous
|
|
250
|
+
</SecondaryButton>
|
|
251
|
+
<SecondaryButton
|
|
252
|
+
size="1"
|
|
253
|
+
disabled={!pagination?.before || isPending || undefined}
|
|
254
|
+
loading={deferredLoading}
|
|
255
|
+
onClick={() => {
|
|
256
|
+
if (pagination?.before) {
|
|
257
|
+
dispatch({
|
|
258
|
+
type: "SET_PAGINATION",
|
|
259
|
+
pagination: { before: pagination.before },
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}}
|
|
263
|
+
>
|
|
264
|
+
Next
|
|
265
|
+
</SecondaryButton>
|
|
266
|
+
</Flex>
|
|
267
|
+
</UsersManagementRoot>
|
|
226
268
|
</SearchProvider>
|
|
227
269
|
);
|
|
228
270
|
};
|
|
229
271
|
|
|
272
|
+
export function UsersManagementLoading() {
|
|
273
|
+
return (
|
|
274
|
+
<UsersManagementRoot>
|
|
275
|
+
<Grid columns="1fr auto" gap="2">
|
|
276
|
+
<Flex gap="2" align="center">
|
|
277
|
+
<Skeleton loading>
|
|
278
|
+
<Box flexBasis="380px" flexGrow="0" flexShrink="1">
|
|
279
|
+
<TextField />
|
|
280
|
+
</Box>
|
|
281
|
+
</Skeleton>
|
|
282
|
+
<Skeleton loading>
|
|
283
|
+
<Box flexGrow="0" flexShrink="0">
|
|
284
|
+
<Select.Root value="all" onValueChange={() => void 0}>
|
|
285
|
+
<SelectTrigger>All</SelectTrigger>
|
|
286
|
+
<SelectContent>
|
|
287
|
+
<SelectItem value="all">All</SelectItem>
|
|
288
|
+
</SelectContent>
|
|
289
|
+
</Select.Root>
|
|
290
|
+
</Box>
|
|
291
|
+
</Skeleton>
|
|
292
|
+
</Flex>
|
|
293
|
+
<Skeleton loading>
|
|
294
|
+
<Box flexGrow="0" flexShrink="0" style={{ placeSelf: "flex-end" }}>
|
|
295
|
+
<PrimaryButton>Invite user</PrimaryButton>
|
|
296
|
+
</Box>
|
|
297
|
+
</Skeleton>
|
|
298
|
+
</Grid>
|
|
299
|
+
<Table.Root variant="ghost" size="1">
|
|
300
|
+
<Table.Header>
|
|
301
|
+
<Table.Row>
|
|
302
|
+
<Table.ColumnHeaderCell width="260px">
|
|
303
|
+
<Skeleton loading>User</Skeleton>
|
|
304
|
+
</Table.ColumnHeaderCell>
|
|
305
|
+
<Table.ColumnHeaderCell width="100px">
|
|
306
|
+
<Skeleton>Role</Skeleton>
|
|
307
|
+
</Table.ColumnHeaderCell>
|
|
308
|
+
<Table.ColumnHeaderCell width="140px">
|
|
309
|
+
<Skeleton>Last active</Skeleton>
|
|
310
|
+
</Table.ColumnHeaderCell>
|
|
311
|
+
<Table.ColumnHeaderCell width="28px" />
|
|
312
|
+
</Table.Row>
|
|
313
|
+
</Table.Header>
|
|
314
|
+
|
|
315
|
+
<Table.Body>
|
|
316
|
+
{Array.from({ length: USER_ROW_LIMIT }, (_, index) => (
|
|
317
|
+
<Table.Row key={index} align="center">
|
|
318
|
+
<Table.RowHeaderCell>
|
|
319
|
+
<Flex align="center" gap="3">
|
|
320
|
+
<Skeleton>
|
|
321
|
+
<Avatar size="2" fallback="F" />
|
|
322
|
+
</Skeleton>
|
|
323
|
+
|
|
324
|
+
<Flex
|
|
325
|
+
direction="column"
|
|
326
|
+
height="var(--space-7)"
|
|
327
|
+
justify="center"
|
|
328
|
+
>
|
|
329
|
+
<Skeleton width="180px" height="var(--space-4)" />
|
|
330
|
+
<Skeleton width="90px" height="var(--space-3)" mt="1" />
|
|
331
|
+
</Flex>
|
|
332
|
+
</Flex>
|
|
333
|
+
</Table.RowHeaderCell>
|
|
334
|
+
<Table.Cell>
|
|
335
|
+
<Flex wrap="wrap" gap="1">
|
|
336
|
+
<Skeleton width="75px" height="var(--space-4)" />
|
|
337
|
+
</Flex>
|
|
338
|
+
</Table.Cell>
|
|
339
|
+
<Table.Cell>
|
|
340
|
+
<Skeleton width="120px" height="var(--space-4)" />
|
|
341
|
+
</Table.Cell>
|
|
342
|
+
<Table.Cell justify="end" />
|
|
343
|
+
</Table.Row>
|
|
344
|
+
))}
|
|
345
|
+
</Table.Body>
|
|
346
|
+
</Table.Root>
|
|
347
|
+
|
|
348
|
+
<Flex gap="2" justify="end">
|
|
349
|
+
<Skeleton loading>
|
|
350
|
+
<SecondaryButton size="1">Previous</SecondaryButton>
|
|
351
|
+
</Skeleton>
|
|
352
|
+
<Skeleton loading>
|
|
353
|
+
<SecondaryButton size="1">Next</SecondaryButton>
|
|
354
|
+
</Skeleton>
|
|
355
|
+
</Flex>
|
|
356
|
+
</UsersManagementRoot>
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function UsersManagementError({ error }: { error: unknown }) {
|
|
361
|
+
React.useEffect(() => {
|
|
362
|
+
console.error(error);
|
|
363
|
+
}, [error]);
|
|
364
|
+
|
|
365
|
+
const render = (heading: string, message: React.ReactNode) => (
|
|
366
|
+
<UsersManagementRoot justify="center" align="center" minHeight="676px">
|
|
367
|
+
<Flex
|
|
368
|
+
p="8"
|
|
369
|
+
justify="center"
|
|
370
|
+
align="center"
|
|
371
|
+
direction="column"
|
|
372
|
+
maxWidth="520px"
|
|
373
|
+
>
|
|
374
|
+
<Heading size="5" align="center" mb="1" wrap="balance" asChild>
|
|
375
|
+
<h3>{heading}</h3>
|
|
376
|
+
</Heading>
|
|
377
|
+
<Text align="center" wrap="balance">
|
|
378
|
+
{message}
|
|
379
|
+
</Text>
|
|
380
|
+
</Flex>
|
|
381
|
+
</UsersManagementRoot>
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
if (error instanceof FetchError) {
|
|
385
|
+
return render(
|
|
386
|
+
"Error fetching data",
|
|
387
|
+
"An error occurred. You may need to configure CORS in the WorkOS Dashboard. " +
|
|
388
|
+
"Contact your organization admin for support.",
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (error instanceof NoAuthTokenError) {
|
|
393
|
+
return render(
|
|
394
|
+
"Error fetching data",
|
|
395
|
+
"Authorization error. You likely forgot to provide an authorization " +
|
|
396
|
+
"token to the Users Management Widget.",
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (error instanceof ApiError && error.status === 404) {
|
|
401
|
+
// The widgets API treats all authorization errors as 404s. If there is a
|
|
402
|
+
// legitimate 404, it's a bug on our end but there's currently no way to
|
|
403
|
+
// distinguish between the two.
|
|
404
|
+
return render(
|
|
405
|
+
"Error fetching data",
|
|
406
|
+
"Authorization error. Contact your organization admin for support.",
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return render(
|
|
411
|
+
"Error fetching data",
|
|
412
|
+
"Unknown error. Contact your organization admin for support.",
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function UsersManagementRoot({
|
|
417
|
+
className,
|
|
418
|
+
children,
|
|
419
|
+
...props
|
|
420
|
+
}: React.ComponentProps<typeof Flex>) {
|
|
421
|
+
return (
|
|
422
|
+
<Flex
|
|
423
|
+
className={clsx("woswidgets-widget", className)}
|
|
424
|
+
data-woswidgets-widget-id="users-management"
|
|
425
|
+
direction="column"
|
|
426
|
+
gap="3"
|
|
427
|
+
{...props}
|
|
428
|
+
>
|
|
429
|
+
{children}
|
|
430
|
+
</Flex>
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
230
434
|
function UserBadge({ user }: { user: User }) {
|
|
231
435
|
// TODO: This is not yet available in the data. Update here after API is updated.
|
|
232
436
|
if (user.isLoggedInUser) {
|
|
@@ -343,49 +547,6 @@ function LastActiveImpl({
|
|
|
343
547
|
);
|
|
344
548
|
}
|
|
345
549
|
|
|
346
|
-
const SkeletonRows = ({
|
|
347
|
-
length,
|
|
348
|
-
alternate = false,
|
|
349
|
-
}: {
|
|
350
|
-
length: number;
|
|
351
|
-
alternate?: boolean;
|
|
352
|
-
}) => {
|
|
353
|
-
return Array.from({ length }, (_, index) => {
|
|
354
|
-
return (
|
|
355
|
-
<Table.Row
|
|
356
|
-
key={index}
|
|
357
|
-
align="center"
|
|
358
|
-
style={{
|
|
359
|
-
background:
|
|
360
|
-
alternate && index % 2 === 1 ? "var(--gray-a1)" : undefined,
|
|
361
|
-
}}
|
|
362
|
-
>
|
|
363
|
-
<Table.RowHeaderCell>
|
|
364
|
-
<Flex align="center" gap="3">
|
|
365
|
-
<Skeleton>
|
|
366
|
-
<Avatar size="2" fallback="F" />
|
|
367
|
-
</Skeleton>
|
|
368
|
-
|
|
369
|
-
<Flex direction="column" height="var(--space-7)" justify="center">
|
|
370
|
-
<Skeleton width="180px" height="var(--space-4)" />
|
|
371
|
-
<Skeleton width="90px" height="var(--space-3)" mt="1" />
|
|
372
|
-
</Flex>
|
|
373
|
-
</Flex>
|
|
374
|
-
</Table.RowHeaderCell>
|
|
375
|
-
<Table.Cell>
|
|
376
|
-
<Flex wrap="wrap" gap="1">
|
|
377
|
-
<Skeleton width="75px" height="var(--space-4)" />
|
|
378
|
-
</Flex>
|
|
379
|
-
</Table.Cell>
|
|
380
|
-
<Table.Cell>
|
|
381
|
-
<Skeleton width="120px" height="var(--space-4)" />
|
|
382
|
-
</Table.Cell>
|
|
383
|
-
<Table.Cell justify="end" />
|
|
384
|
-
</Table.Row>
|
|
385
|
-
);
|
|
386
|
-
});
|
|
387
|
-
};
|
|
388
|
-
|
|
389
550
|
const TableCellText = React.forwardRef<HTMLSpanElement, TableCellTextProps>(
|
|
390
551
|
function TableCellText(
|
|
391
552
|
{ children, dim, level = "primary", ...props },
|