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