@workos-inc/widgets 1.1.5 → 1.2.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/dist/cjs/lib/organization-switcher.d.ts +10 -1
- package/dist/cjs/lib/organization-switcher.d.ts.map +1 -1
- package/dist/cjs/lib/organization-switcher.js +31 -3
- package/dist/cjs/lib/organization-switcher.js.map +1 -1
- package/dist/cjs/workos-widgets.client.d.ts +6 -0
- package/dist/cjs/workos-widgets.client.d.ts.map +1 -1
- package/dist/cjs/workos-widgets.client.js +23 -3
- package/dist/cjs/workos-widgets.client.js.map +1 -1
- package/dist/esm/lib/organization-switcher.d.ts +10 -1
- package/dist/esm/lib/organization-switcher.d.ts.map +1 -1
- package/dist/esm/lib/organization-switcher.js +31 -3
- package/dist/esm/lib/organization-switcher.js.map +1 -1
- package/dist/esm/workos-widgets.client.d.ts +6 -0
- package/dist/esm/workos-widgets.client.d.ts.map +1 -1
- package/dist/esm/workos-widgets.client.js +25 -5
- package/dist/esm/workos-widgets.client.js.map +1 -1
- package/package.json +8 -9
- package/src/api/api-provider.tsx +0 -158
- package/src/api/constants.ts +0 -1
- package/src/api/endpoint.ts +0 -3097
- package/src/api/errors.ts +0 -48
- package/src/api/index.ts +0 -2
- package/src/api/utils.ts +0 -42
- package/src/api/widgets-api-client.ts +0 -87
- package/src/card-list.tsx +0 -26
- package/src/index.ts +0 -9
- package/src/lib/add-mfa-dialog.tsx +0 -379
- package/src/lib/api/config.ts +0 -9
- package/src/lib/api/user.ts +0 -98
- package/src/lib/change-password-dialog.tsx +0 -290
- package/src/lib/constants.ts +0 -3
- package/src/lib/copy-button.tsx +0 -53
- package/src/lib/delete-user-dialog.tsx +0 -110
- package/src/lib/edit-user-profile-dialog.tsx +0 -181
- package/src/lib/edit-user-role-dialog.tsx +0 -178
- package/src/lib/elements.tsx +0 -428
- package/src/lib/elevated-access.tsx +0 -261
- package/src/lib/error-boundary.tsx +0 -166
- package/src/lib/errors.ts +0 -49
- package/src/lib/generic-error.tsx +0 -70
- package/src/lib/icon-panel.tsx +0 -26
- package/src/lib/icons.tsx +0 -21
- package/src/lib/invite-user-dialog.tsx +0 -327
- package/src/lib/logout-all-sessions-dialog.tsx +0 -82
- package/src/lib/logout-dialog.tsx +0 -85
- package/src/lib/marker.tsx +0 -39
- package/src/lib/oauth-icons.tsx +0 -138
- package/src/lib/organization-switcher.tsx +0 -156
- package/src/lib/otp-input.tsx +0 -276
- package/src/lib/resend-invite-dialog.tsx +0 -145
- package/src/lib/reset-mfa-dialog.tsx +0 -104
- package/src/lib/revoke-invite-dialog.tsx +0 -111
- package/src/lib/save-button.tsx +0 -113
- package/src/lib/search-provider.tsx +0 -51
- package/src/lib/set-password-dialog.tsx +0 -204
- package/src/lib/use-dialog-close.tsx +0 -19
- package/src/lib/use-is-hydrated.ts +0 -13
- package/src/lib/use-layout-effect.ts +0 -6
- package/src/lib/use-security-settings.tsx +0 -49
- package/src/lib/user-actions-dropdown.tsx +0 -157
- package/src/lib/user-profile.tsx +0 -227
- package/src/lib/user-security.tsx +0 -187
- package/src/lib/user-sessions.tsx +0 -204
- package/src/lib/users-filter.tsx +0 -62
- package/src/lib/users-management-context.tsx +0 -74
- package/src/lib/users-management-state.ts +0 -165
- package/src/lib/users-management.tsx +0 -594
- package/src/lib/users-search.tsx +0 -73
- package/src/lib/utils.ts +0 -131
- package/src/lib/widgets-context.ts +0 -29
- package/src/organization-switcher.client.tsx +0 -81
- package/src/user-profile.client.tsx +0 -55
- package/src/user-security.client.tsx +0 -55
- package/src/user-sessions.client.tsx +0 -100
- package/src/users-management.client.tsx +0 -73
- package/src/workos-widgets.client.tsx +0 -75
- /package/{src → dist/css}/base.css +0 -0
- /package/{src → dist/css}/lib/card-list.css +0 -0
- /package/{src → dist/css}/lib/marker.css +0 -0
- /package/{src → dist/css}/lib/save-button.css +0 -0
- /package/{src → dist/css}/styles.css +0 -0
- /package/{src → dist/css}/users-management.css +0 -0
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { DropdownMenu } from "@radix-ui/themes";
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
import { Member } from "../api";
|
|
6
|
-
import { DeleteUserDialog } from "./delete-user-dialog";
|
|
7
|
-
import { EditUserRoleDialog } from "./edit-user-role-dialog";
|
|
8
|
-
import {
|
|
9
|
-
DestructiveMenuItem,
|
|
10
|
-
DropdownMenuContent,
|
|
11
|
-
PrimaryMenuItem,
|
|
12
|
-
} from "./elements";
|
|
13
|
-
import { ResendInviteDialog } from "./resend-invite-dialog";
|
|
14
|
-
import { RevokeInviteDialog } from "./revoke-invite-dialog";
|
|
15
|
-
import { useRoles } from "../api";
|
|
16
|
-
|
|
17
|
-
interface UserActionsDropdownProps {
|
|
18
|
-
user: Member;
|
|
19
|
-
children: React.ReactNode;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type UserActionDialog =
|
|
23
|
-
| "revoke-membership"
|
|
24
|
-
| "revoke-invite"
|
|
25
|
-
| "resend-invite"
|
|
26
|
-
| "edit-role";
|
|
27
|
-
|
|
28
|
-
export const UserActionsDropdown = ({
|
|
29
|
-
user,
|
|
30
|
-
children,
|
|
31
|
-
}: UserActionsDropdownProps) => {
|
|
32
|
-
const rolesQuery = useRoles({
|
|
33
|
-
query: {
|
|
34
|
-
initialData: [],
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
const [openDialog, setOpenDialog] = React.useState<UserActionDialog | null>(
|
|
38
|
-
null,
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Assigns a key for each dialog based on its open state to ensure its
|
|
43
|
-
* internal state is cleared when it is closed.
|
|
44
|
-
*/
|
|
45
|
-
function getDialogKey(dialog: UserActionDialog) {
|
|
46
|
-
return `${dialog}-${openDialog === dialog}-${user.id}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const { actions, items } = React.useMemo(() => {
|
|
50
|
-
const actions = new Set(user.actions);
|
|
51
|
-
const items: React.ReactElement[] = [];
|
|
52
|
-
if (actions.has("edit-role")) {
|
|
53
|
-
items.push(
|
|
54
|
-
<PrimaryMenuItem
|
|
55
|
-
key="edit-role"
|
|
56
|
-
onSelect={() => setOpenDialog("edit-role")}
|
|
57
|
-
disabled={
|
|
58
|
-
rolesQuery.isLoading ||
|
|
59
|
-
(rolesQuery.isSuccess && rolesQuery.data.length <= 1)
|
|
60
|
-
}
|
|
61
|
-
title={
|
|
62
|
-
rolesQuery.isSuccess && rolesQuery.data.length <= 1
|
|
63
|
-
? "You cannot update the role for this user as there is only one role available."
|
|
64
|
-
: undefined
|
|
65
|
-
}
|
|
66
|
-
>
|
|
67
|
-
Edit role
|
|
68
|
-
</PrimaryMenuItem>,
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
if (actions.has("resend-invite")) {
|
|
72
|
-
items.push(
|
|
73
|
-
<PrimaryMenuItem
|
|
74
|
-
key="resend-invite"
|
|
75
|
-
onSelect={() => setOpenDialog("resend-invite")}
|
|
76
|
-
>
|
|
77
|
-
Resend invitation
|
|
78
|
-
</PrimaryMenuItem>,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
if (actions.has("revoke-invite")) {
|
|
82
|
-
items.push(
|
|
83
|
-
<DestructiveMenuItem
|
|
84
|
-
key="revoke-invite"
|
|
85
|
-
onSelect={() => setOpenDialog("revoke-invite")}
|
|
86
|
-
>
|
|
87
|
-
Revoke invitation
|
|
88
|
-
</DestructiveMenuItem>,
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
if (actions.has("revoke-membership")) {
|
|
92
|
-
items.push(
|
|
93
|
-
<DestructiveMenuItem
|
|
94
|
-
key="revoke-membership"
|
|
95
|
-
onSelect={() => setOpenDialog("revoke-membership")}
|
|
96
|
-
>
|
|
97
|
-
Remove user
|
|
98
|
-
</DestructiveMenuItem>,
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
actions,
|
|
103
|
-
items,
|
|
104
|
-
};
|
|
105
|
-
}, [rolesQuery, user.actions]);
|
|
106
|
-
|
|
107
|
-
if (user.isLoggedInUser || items.length === 0) {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<>
|
|
113
|
-
<DropdownMenu.Root>
|
|
114
|
-
<DropdownMenu.Trigger>{children}</DropdownMenu.Trigger>
|
|
115
|
-
<DropdownMenuContent size="2" align="end">
|
|
116
|
-
{items}
|
|
117
|
-
</DropdownMenuContent>
|
|
118
|
-
</DropdownMenu.Root>
|
|
119
|
-
|
|
120
|
-
{actions.has("edit-role") && (
|
|
121
|
-
<EditUserRoleDialog
|
|
122
|
-
key={getDialogKey("edit-role")}
|
|
123
|
-
user={user}
|
|
124
|
-
open={openDialog === "edit-role"}
|
|
125
|
-
onOpenChange={(open) => !open && setOpenDialog(null)}
|
|
126
|
-
/>
|
|
127
|
-
)}
|
|
128
|
-
|
|
129
|
-
{actions.has("revoke-membership") && (
|
|
130
|
-
<DeleteUserDialog
|
|
131
|
-
key={getDialogKey("revoke-membership")}
|
|
132
|
-
user={user}
|
|
133
|
-
open={openDialog === "revoke-membership"}
|
|
134
|
-
onOpenChange={(open) => !open && setOpenDialog(null)}
|
|
135
|
-
/>
|
|
136
|
-
)}
|
|
137
|
-
|
|
138
|
-
{actions.has("revoke-invite") && (
|
|
139
|
-
<RevokeInviteDialog
|
|
140
|
-
key={getDialogKey("revoke-invite")}
|
|
141
|
-
user={user}
|
|
142
|
-
open={openDialog === "revoke-invite"}
|
|
143
|
-
onOpenChange={(open) => !open && setOpenDialog(null)}
|
|
144
|
-
/>
|
|
145
|
-
)}
|
|
146
|
-
|
|
147
|
-
{actions.has("resend-invite") && (
|
|
148
|
-
<ResendInviteDialog
|
|
149
|
-
key={getDialogKey("resend-invite")}
|
|
150
|
-
user={user}
|
|
151
|
-
open={openDialog === "resend-invite"}
|
|
152
|
-
onOpenChange={(open) => !open && setOpenDialog(null)}
|
|
153
|
-
/>
|
|
154
|
-
)}
|
|
155
|
-
</>
|
|
156
|
-
);
|
|
157
|
-
};
|
package/src/lib/user-profile.tsx
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Box,
|
|
5
|
-
Card,
|
|
6
|
-
DataList,
|
|
7
|
-
Flex,
|
|
8
|
-
Inset,
|
|
9
|
-
Separator,
|
|
10
|
-
Strong,
|
|
11
|
-
Text,
|
|
12
|
-
} from "@radix-ui/themes";
|
|
13
|
-
import clsx from "clsx";
|
|
14
|
-
import { Avatar, SecondaryButton, Skeleton } from "./elements";
|
|
15
|
-
import { Me } from "../api";
|
|
16
|
-
import { EditUserProfileDialog } from "./edit-user-profile-dialog";
|
|
17
|
-
import { getBestName } from "./utils";
|
|
18
|
-
import { getOAuthIcon, getOAuthName } from "./oauth-icons";
|
|
19
|
-
import { GenericError } from "./generic-error";
|
|
20
|
-
|
|
21
|
-
interface UserProfileProps {
|
|
22
|
-
userData: Me;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const UserProfile = ({ userData: user }: UserProfileProps) => {
|
|
26
|
-
const oauthAccounts = Object.entries(user.oauthProfiles || {});
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<Card
|
|
30
|
-
className={clsx("woswidgets-widget")}
|
|
31
|
-
data-woswidgets-widget-id="user-profile"
|
|
32
|
-
size="2"
|
|
33
|
-
>
|
|
34
|
-
<DataList.Root>
|
|
35
|
-
{user.profilePictureUrl && (
|
|
36
|
-
<>
|
|
37
|
-
<DataList.Item align="center">
|
|
38
|
-
<DataList.Label minWidth="220px" highContrast>
|
|
39
|
-
<Strong>Profile picture</Strong>
|
|
40
|
-
</DataList.Label>
|
|
41
|
-
<DataList.Value>
|
|
42
|
-
<Avatar
|
|
43
|
-
size="2"
|
|
44
|
-
fallback={<FallbackUserIcon />}
|
|
45
|
-
src={user.profilePictureUrl}
|
|
46
|
-
/>
|
|
47
|
-
</DataList.Value>
|
|
48
|
-
</DataList.Item>
|
|
49
|
-
|
|
50
|
-
<ListSeparator />
|
|
51
|
-
</>
|
|
52
|
-
)}
|
|
53
|
-
|
|
54
|
-
<DataList.Item align="center">
|
|
55
|
-
<DataList.Label highContrast>
|
|
56
|
-
<Strong>Full name</Strong>
|
|
57
|
-
</DataList.Label>
|
|
58
|
-
<DataList.Value>
|
|
59
|
-
<Flex
|
|
60
|
-
align="center"
|
|
61
|
-
justify="between"
|
|
62
|
-
width="100%"
|
|
63
|
-
minHeight="var(--space-6)"
|
|
64
|
-
>
|
|
65
|
-
<Text size="2">
|
|
66
|
-
{getBestName(user) || (
|
|
67
|
-
<Text
|
|
68
|
-
color="gray"
|
|
69
|
-
style={{ color: "var(--gray-10)", cursor: "default" }}
|
|
70
|
-
>
|
|
71
|
-
Not set
|
|
72
|
-
</Text>
|
|
73
|
-
)}
|
|
74
|
-
</Text>
|
|
75
|
-
|
|
76
|
-
<EditUserProfileDialog user={user}>
|
|
77
|
-
<SecondaryButton>Edit</SecondaryButton>
|
|
78
|
-
</EditUserProfileDialog>
|
|
79
|
-
</Flex>
|
|
80
|
-
</DataList.Value>
|
|
81
|
-
</DataList.Item>
|
|
82
|
-
|
|
83
|
-
<ListSeparator />
|
|
84
|
-
|
|
85
|
-
<DataList.Item align="center">
|
|
86
|
-
<DataList.Label highContrast>
|
|
87
|
-
<Strong>Email address</Strong>
|
|
88
|
-
</DataList.Label>
|
|
89
|
-
<DataList.Value>
|
|
90
|
-
<Flex align="center" minHeight="var(--space-6)">
|
|
91
|
-
<Text size="2">{user.email}</Text>
|
|
92
|
-
</Flex>
|
|
93
|
-
</DataList.Value>
|
|
94
|
-
</DataList.Item>
|
|
95
|
-
|
|
96
|
-
{oauthAccounts.length > 0 && (
|
|
97
|
-
<>
|
|
98
|
-
<ListSeparator />
|
|
99
|
-
|
|
100
|
-
<DataList.Item>
|
|
101
|
-
<DataList.Label highContrast>
|
|
102
|
-
<Strong>Connected accounts</Strong>
|
|
103
|
-
</DataList.Label>
|
|
104
|
-
<DataList.Value>
|
|
105
|
-
<Flex direction="column" gap="2">
|
|
106
|
-
{oauthAccounts.map(([account, data]) => (
|
|
107
|
-
<OAuthAccount
|
|
108
|
-
key={account}
|
|
109
|
-
account={account}
|
|
110
|
-
email={data?.email}
|
|
111
|
-
/>
|
|
112
|
-
))}
|
|
113
|
-
</Flex>
|
|
114
|
-
</DataList.Value>
|
|
115
|
-
</DataList.Item>
|
|
116
|
-
</>
|
|
117
|
-
)}
|
|
118
|
-
</DataList.Root>
|
|
119
|
-
</Card>
|
|
120
|
-
);
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
export function UserProfileLoading() {
|
|
124
|
-
return (
|
|
125
|
-
<Card size="2">
|
|
126
|
-
<DataList.Root>
|
|
127
|
-
<DataList.Item align="center">
|
|
128
|
-
<DataList.Label minWidth="220px" highContrast>
|
|
129
|
-
<Strong>
|
|
130
|
-
<Skeleton>Full name</Skeleton>
|
|
131
|
-
</Strong>
|
|
132
|
-
</DataList.Label>
|
|
133
|
-
<DataList.Value>
|
|
134
|
-
<Flex
|
|
135
|
-
align="center"
|
|
136
|
-
justify="between"
|
|
137
|
-
width="100%"
|
|
138
|
-
minHeight="var(--space-6)"
|
|
139
|
-
>
|
|
140
|
-
<Text size="2">
|
|
141
|
-
<Skeleton>Full name</Skeleton>
|
|
142
|
-
</Text>
|
|
143
|
-
</Flex>
|
|
144
|
-
</DataList.Value>
|
|
145
|
-
</DataList.Item>
|
|
146
|
-
|
|
147
|
-
<ListSeparator />
|
|
148
|
-
|
|
149
|
-
<DataList.Item align="center">
|
|
150
|
-
<DataList.Label highContrast>
|
|
151
|
-
<Strong>
|
|
152
|
-
<Skeleton>Email address</Skeleton>
|
|
153
|
-
</Strong>
|
|
154
|
-
</DataList.Label>
|
|
155
|
-
<DataList.Value>
|
|
156
|
-
<Flex align="center" minHeight="var(--space-6)">
|
|
157
|
-
<Text size="2">
|
|
158
|
-
<Skeleton>Email address</Skeleton>
|
|
159
|
-
</Text>
|
|
160
|
-
</Flex>
|
|
161
|
-
</DataList.Value>
|
|
162
|
-
</DataList.Item>
|
|
163
|
-
</DataList.Root>
|
|
164
|
-
</Card>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function UserProfileError({ error }: { error: unknown }) {
|
|
169
|
-
return (
|
|
170
|
-
<Card size="2">
|
|
171
|
-
<GenericError error={error} />
|
|
172
|
-
</Card>
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const ListSeparator = () => (
|
|
177
|
-
<Box asChild gridColumn="span 2">
|
|
178
|
-
<Inset side="x">
|
|
179
|
-
<Separator size="4" />
|
|
180
|
-
</Inset>
|
|
181
|
-
</Box>
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
const OAuthAccount = ({
|
|
185
|
-
account,
|
|
186
|
-
email,
|
|
187
|
-
}: {
|
|
188
|
-
account: string;
|
|
189
|
-
email?: string | null;
|
|
190
|
-
}) => {
|
|
191
|
-
const Icon = getOAuthIcon(account);
|
|
192
|
-
const name = getOAuthName(account);
|
|
193
|
-
|
|
194
|
-
return (
|
|
195
|
-
<Flex align="center" gap="1">
|
|
196
|
-
<Icon />
|
|
197
|
-
|
|
198
|
-
<Text size="2" ml="1">
|
|
199
|
-
{name}
|
|
200
|
-
</Text>
|
|
201
|
-
|
|
202
|
-
{email && (
|
|
203
|
-
<Box display={{ initial: "none", sm: "contents" }}>
|
|
204
|
-
<Text size="2" color="gray">
|
|
205
|
-
∙
|
|
206
|
-
</Text>
|
|
207
|
-
<Text size="2" color="gray">
|
|
208
|
-
{email}
|
|
209
|
-
</Text>
|
|
210
|
-
</Box>
|
|
211
|
-
)}
|
|
212
|
-
</Flex>
|
|
213
|
-
);
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
const FallbackUserIcon = () => (
|
|
217
|
-
<svg
|
|
218
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
219
|
-
width="20"
|
|
220
|
-
height="20"
|
|
221
|
-
fill="currentColor"
|
|
222
|
-
viewBox="0 0 256 256"
|
|
223
|
-
>
|
|
224
|
-
<title>User icon</title>
|
|
225
|
-
<path d="M229.19,213c-15.81-27.32-40.63-46.49-69.47-54.62a70,70,0,1,0-63.44,0C67.44,166.5,42.62,185.67,26.81,213a6,6,0,1,0,10.38,6C56.4,185.81,90.34,166,128,166s71.6,19.81,90.81,53a6,6,0,1,0,10.38-6ZM70,96a58,58,0,1,1,58,58A58.07,58.07,0,0,1,70,96Z" />
|
|
226
|
-
</svg>
|
|
227
|
-
);
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Card, Flex, Grid, Text } from "@radix-ui/themes";
|
|
4
|
-
import clsx from "clsx";
|
|
5
|
-
import { SecondaryButton, Skeleton } from "./elements";
|
|
6
|
-
import { IconPanel } from "./icon-panel";
|
|
7
|
-
import { ButtonIcon, LockClosedIcon } from "@radix-ui/react-icons";
|
|
8
|
-
import { SetPasswordDialog } from "./set-password-dialog";
|
|
9
|
-
import { ChangePasswordDialog } from "./change-password-dialog";
|
|
10
|
-
import { AddMfaDialog } from "./add-mfa-dialog";
|
|
11
|
-
import type {
|
|
12
|
-
AuthenticationInformationResponseData as AuthenticationSettings,
|
|
13
|
-
AuthenticationInformationResponseDataVerificationMethodsPassword as PasswordVerificationMethod,
|
|
14
|
-
AuthenticationInformationResponseDataVerificationMethodsMfa as MfaVerificationMethod,
|
|
15
|
-
} from "../api";
|
|
16
|
-
import { ResetMfaDialog } from "./reset-mfa-dialog";
|
|
17
|
-
import * as CardList from "../card-list";
|
|
18
|
-
import { GenericError } from "./generic-error";
|
|
19
|
-
import { getComparativeReadableDate } from "./utils";
|
|
20
|
-
|
|
21
|
-
interface UserSecurityProps {
|
|
22
|
-
settings: AuthenticationSettings;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const UserSecurity = ({ settings }: UserSecurityProps) => {
|
|
26
|
-
const passwordSettings = settings.verificationMethods.Password;
|
|
27
|
-
const mfaSettings = settings.verificationMethods.Mfa;
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<CardList.Root
|
|
31
|
-
className={clsx("woswidgets-widget")}
|
|
32
|
-
data-woswidgets-widget-id="user-security"
|
|
33
|
-
>
|
|
34
|
-
{passwordSettings && (
|
|
35
|
-
<CardList.Item>
|
|
36
|
-
<PasswordSettings settings={passwordSettings} />
|
|
37
|
-
</CardList.Item>
|
|
38
|
-
)}
|
|
39
|
-
|
|
40
|
-
{mfaSettings && (
|
|
41
|
-
<CardList.Item>
|
|
42
|
-
<MfaSettings
|
|
43
|
-
settings={mfaSettings}
|
|
44
|
-
isPasswordSet={!!passwordSettings?.isSetUp}
|
|
45
|
-
/>
|
|
46
|
-
</CardList.Item>
|
|
47
|
-
)}
|
|
48
|
-
</CardList.Root>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export function UserSecurityError({ error }: { error: unknown }) {
|
|
53
|
-
return (
|
|
54
|
-
<Card size="2">
|
|
55
|
-
<GenericError error={error} />
|
|
56
|
-
</Card>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function UserSecurityLoading() {
|
|
61
|
-
return (
|
|
62
|
-
<Card size="2">
|
|
63
|
-
<Grid columns="auto 1fr auto" align="center" gap="4">
|
|
64
|
-
<Skeleton>
|
|
65
|
-
<IconPanel />
|
|
66
|
-
</Skeleton>
|
|
67
|
-
|
|
68
|
-
<Flex direction="column">
|
|
69
|
-
<Text size="2" highContrast weight="bold" as="p" mb="-2px">
|
|
70
|
-
<Skeleton>Password</Skeleton>
|
|
71
|
-
</Text>
|
|
72
|
-
<Text as="p" size="2" color="gray">
|
|
73
|
-
<Skeleton>Set a password to access your account</Skeleton>
|
|
74
|
-
</Text>
|
|
75
|
-
</Flex>
|
|
76
|
-
|
|
77
|
-
<ChangePasswordDialog>
|
|
78
|
-
<Skeleton>
|
|
79
|
-
<SecondaryButton>Change</SecondaryButton>
|
|
80
|
-
</Skeleton>
|
|
81
|
-
</ChangePasswordDialog>
|
|
82
|
-
</Grid>
|
|
83
|
-
</Card>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function PasswordSettings({
|
|
88
|
-
settings,
|
|
89
|
-
}: {
|
|
90
|
-
settings: NonNullable<PasswordVerificationMethod>;
|
|
91
|
-
}) {
|
|
92
|
-
return (
|
|
93
|
-
<Grid columns="auto 1fr auto" align="center" gap="4">
|
|
94
|
-
<IconPanel>
|
|
95
|
-
<LockClosedIcon />
|
|
96
|
-
</IconPanel>
|
|
97
|
-
|
|
98
|
-
<Flex direction="column">
|
|
99
|
-
<Text size="2" highContrast weight="bold" as="p" mb="-2px">
|
|
100
|
-
Password
|
|
101
|
-
</Text>
|
|
102
|
-
|
|
103
|
-
{settings.isSetUp ? (
|
|
104
|
-
settings.lastUsed && (
|
|
105
|
-
<Text size="2" color="gray">
|
|
106
|
-
Last used{" "}
|
|
107
|
-
{getComparativeReadableDate(
|
|
108
|
-
new Date(),
|
|
109
|
-
new Date(settings.lastUsed),
|
|
110
|
-
)}
|
|
111
|
-
</Text>
|
|
112
|
-
)
|
|
113
|
-
) : (
|
|
114
|
-
<Text as="p" size="2" color="gray">
|
|
115
|
-
Set a password to access your account
|
|
116
|
-
</Text>
|
|
117
|
-
)}
|
|
118
|
-
</Flex>
|
|
119
|
-
|
|
120
|
-
{settings.isSetUp ? (
|
|
121
|
-
<ChangePasswordDialog>
|
|
122
|
-
<SecondaryButton>Change</SecondaryButton>
|
|
123
|
-
</ChangePasswordDialog>
|
|
124
|
-
) : (
|
|
125
|
-
<SetPasswordDialog>
|
|
126
|
-
<SecondaryButton>Set a password</SecondaryButton>
|
|
127
|
-
</SetPasswordDialog>
|
|
128
|
-
)}
|
|
129
|
-
</Grid>
|
|
130
|
-
);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function MfaSettings({
|
|
134
|
-
settings,
|
|
135
|
-
isPasswordSet,
|
|
136
|
-
}: {
|
|
137
|
-
settings: NonNullable<MfaVerificationMethod>;
|
|
138
|
-
isPasswordSet: boolean;
|
|
139
|
-
}) {
|
|
140
|
-
return (
|
|
141
|
-
<Grid columns="auto 1fr auto" align="center" gap="4">
|
|
142
|
-
<IconPanel>
|
|
143
|
-
<ButtonIcon />
|
|
144
|
-
</IconPanel>
|
|
145
|
-
|
|
146
|
-
<Flex direction="column">
|
|
147
|
-
<Text size="2" highContrast weight="bold" as="p" mb="-2px">
|
|
148
|
-
Multi-factor authentication
|
|
149
|
-
</Text>
|
|
150
|
-
|
|
151
|
-
{settings.isSetUp ? (
|
|
152
|
-
<Text size="2">
|
|
153
|
-
Authenticator app
|
|
154
|
-
{settings.lastUsed && (
|
|
155
|
-
<>
|
|
156
|
-
<Text size="2" color="gray" mx="1">
|
|
157
|
-
∙
|
|
158
|
-
</Text>
|
|
159
|
-
<Text size="2" color="gray">
|
|
160
|
-
Last used{" "}
|
|
161
|
-
{getComparativeReadableDate(
|
|
162
|
-
new Date(),
|
|
163
|
-
new Date(settings.lastUsed),
|
|
164
|
-
)}
|
|
165
|
-
</Text>
|
|
166
|
-
</>
|
|
167
|
-
)}
|
|
168
|
-
</Text>
|
|
169
|
-
) : (
|
|
170
|
-
<Text as="p" size="2" color="gray">
|
|
171
|
-
Secure your account with an extra verification step
|
|
172
|
-
</Text>
|
|
173
|
-
)}
|
|
174
|
-
</Flex>
|
|
175
|
-
|
|
176
|
-
{settings.isSetUp ? (
|
|
177
|
-
<ResetMfaDialog isPasswordSet={isPasswordSet}>
|
|
178
|
-
<SecondaryButton>Disable</SecondaryButton>
|
|
179
|
-
</ResetMfaDialog>
|
|
180
|
-
) : (
|
|
181
|
-
<AddMfaDialog>
|
|
182
|
-
<SecondaryButton>Set up authenticator app</SecondaryButton>
|
|
183
|
-
</AddMfaDialog>
|
|
184
|
-
)}
|
|
185
|
-
</Grid>
|
|
186
|
-
);
|
|
187
|
-
}
|