@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.
Files changed (82) hide show
  1. package/dist/cjs/lib/organization-switcher.d.ts +10 -1
  2. package/dist/cjs/lib/organization-switcher.d.ts.map +1 -1
  3. package/dist/cjs/lib/organization-switcher.js +31 -3
  4. package/dist/cjs/lib/organization-switcher.js.map +1 -1
  5. package/dist/cjs/workos-widgets.client.d.ts +6 -0
  6. package/dist/cjs/workos-widgets.client.d.ts.map +1 -1
  7. package/dist/cjs/workos-widgets.client.js +23 -3
  8. package/dist/cjs/workos-widgets.client.js.map +1 -1
  9. package/dist/esm/lib/organization-switcher.d.ts +10 -1
  10. package/dist/esm/lib/organization-switcher.d.ts.map +1 -1
  11. package/dist/esm/lib/organization-switcher.js +31 -3
  12. package/dist/esm/lib/organization-switcher.js.map +1 -1
  13. package/dist/esm/workos-widgets.client.d.ts +6 -0
  14. package/dist/esm/workos-widgets.client.d.ts.map +1 -1
  15. package/dist/esm/workos-widgets.client.js +25 -5
  16. package/dist/esm/workos-widgets.client.js.map +1 -1
  17. package/package.json +8 -9
  18. package/src/api/api-provider.tsx +0 -158
  19. package/src/api/constants.ts +0 -1
  20. package/src/api/endpoint.ts +0 -3097
  21. package/src/api/errors.ts +0 -48
  22. package/src/api/index.ts +0 -2
  23. package/src/api/utils.ts +0 -42
  24. package/src/api/widgets-api-client.ts +0 -87
  25. package/src/card-list.tsx +0 -26
  26. package/src/index.ts +0 -9
  27. package/src/lib/add-mfa-dialog.tsx +0 -379
  28. package/src/lib/api/config.ts +0 -9
  29. package/src/lib/api/user.ts +0 -98
  30. package/src/lib/change-password-dialog.tsx +0 -290
  31. package/src/lib/constants.ts +0 -3
  32. package/src/lib/copy-button.tsx +0 -53
  33. package/src/lib/delete-user-dialog.tsx +0 -110
  34. package/src/lib/edit-user-profile-dialog.tsx +0 -181
  35. package/src/lib/edit-user-role-dialog.tsx +0 -178
  36. package/src/lib/elements.tsx +0 -428
  37. package/src/lib/elevated-access.tsx +0 -261
  38. package/src/lib/error-boundary.tsx +0 -166
  39. package/src/lib/errors.ts +0 -49
  40. package/src/lib/generic-error.tsx +0 -70
  41. package/src/lib/icon-panel.tsx +0 -26
  42. package/src/lib/icons.tsx +0 -21
  43. package/src/lib/invite-user-dialog.tsx +0 -327
  44. package/src/lib/logout-all-sessions-dialog.tsx +0 -82
  45. package/src/lib/logout-dialog.tsx +0 -85
  46. package/src/lib/marker.tsx +0 -39
  47. package/src/lib/oauth-icons.tsx +0 -138
  48. package/src/lib/organization-switcher.tsx +0 -156
  49. package/src/lib/otp-input.tsx +0 -276
  50. package/src/lib/resend-invite-dialog.tsx +0 -145
  51. package/src/lib/reset-mfa-dialog.tsx +0 -104
  52. package/src/lib/revoke-invite-dialog.tsx +0 -111
  53. package/src/lib/save-button.tsx +0 -113
  54. package/src/lib/search-provider.tsx +0 -51
  55. package/src/lib/set-password-dialog.tsx +0 -204
  56. package/src/lib/use-dialog-close.tsx +0 -19
  57. package/src/lib/use-is-hydrated.ts +0 -13
  58. package/src/lib/use-layout-effect.ts +0 -6
  59. package/src/lib/use-security-settings.tsx +0 -49
  60. package/src/lib/user-actions-dropdown.tsx +0 -157
  61. package/src/lib/user-profile.tsx +0 -227
  62. package/src/lib/user-security.tsx +0 -187
  63. package/src/lib/user-sessions.tsx +0 -204
  64. package/src/lib/users-filter.tsx +0 -62
  65. package/src/lib/users-management-context.tsx +0 -74
  66. package/src/lib/users-management-state.ts +0 -165
  67. package/src/lib/users-management.tsx +0 -594
  68. package/src/lib/users-search.tsx +0 -73
  69. package/src/lib/utils.ts +0 -131
  70. package/src/lib/widgets-context.ts +0 -29
  71. package/src/organization-switcher.client.tsx +0 -81
  72. package/src/user-profile.client.tsx +0 -55
  73. package/src/user-security.client.tsx +0 -55
  74. package/src/user-sessions.client.tsx +0 -100
  75. package/src/users-management.client.tsx +0 -73
  76. package/src/workos-widgets.client.tsx +0 -75
  77. /package/{src → dist/css}/base.css +0 -0
  78. /package/{src → dist/css}/lib/card-list.css +0 -0
  79. /package/{src → dist/css}/lib/marker.css +0 -0
  80. /package/{src → dist/css}/lib/save-button.css +0 -0
  81. /package/{src → dist/css}/styles.css +0 -0
  82. /package/{src → dist/css}/users-management.css +0 -0
@@ -1,204 +0,0 @@
1
- "use client";
2
-
3
- import { Box, Button, Card, Flex, Text } from "@radix-ui/themes";
4
- import clsx from "clsx";
5
- import { Badge, SecondaryButton, Skeleton } from "./elements";
6
- import {
7
- getComparativeReadableDate,
8
- getUserLocation,
9
- parseUserAgent,
10
- } from "./utils";
11
- import { LogoutDialog } from "./logout-dialog";
12
- import { LogoutAllSessionsDialog } from "./logout-all-sessions-dialog";
13
- import { ActiveSession } from "../api";
14
- import * as CardList from "../card-list";
15
- import { IconPanel } from "./icon-panel";
16
- import { useState } from "react";
17
- import { GenericError } from "./generic-error";
18
- import { LaptopIcon, MobileIcon } from "@radix-ui/react-icons";
19
-
20
- interface UserSessionsProps {
21
- sessionsData: ActiveSession[];
22
- currentSessionId: string;
23
- }
24
-
25
- export const UserSessions = ({
26
- sessionsData: sessions,
27
- currentSessionId,
28
- }: UserSessionsProps) => {
29
- const currentSession = sessions.find(
30
- (session) => session.id === currentSessionId,
31
- );
32
- const otherSessions = sessions.filter(
33
- (session) => session.id !== currentSessionId,
34
- );
35
- const [openLogoutDialog, setOpenLogoutDialog] = useState(false);
36
- const [sessionToSignOut, setSessionToSignOut] =
37
- useState<ActiveSession | null>(null);
38
- const [openLogoutAllSessionsDialog, setOpenLogoutAllSessionsDialog] =
39
- useState(false);
40
-
41
- return (
42
- <Flex
43
- direction="column"
44
- gap="4"
45
- className={clsx("woswidgets-widget")}
46
- data-woswidgets-widget-id="user-sessions"
47
- >
48
- <CardList.Root>
49
- {currentSession && (
50
- <CardList.Item>
51
- <UserSession session={currentSession} isCurrentSession />
52
- </CardList.Item>
53
- )}
54
-
55
- {otherSessions.map((session) => (
56
- <CardList.Item key={session.id}>
57
- <UserSession
58
- session={session}
59
- onSignOut={() => {
60
- setSessionToSignOut(session);
61
- setOpenLogoutDialog(true);
62
- }}
63
- />
64
- </CardList.Item>
65
- ))}
66
- </CardList.Root>
67
-
68
- {sessions.length > 1 && (
69
- <Card size="2">
70
- <Flex gap="2" justify="between" align="center">
71
- <Box>
72
- <Text size="2" highContrast weight="bold" as="p">
73
- Sign out of all other devices
74
- </Text>
75
- <Text size="2" color="gray" as="p">
76
- Remove access from all devices except this one
77
- </Text>
78
- </Box>
79
-
80
- <SecondaryButton
81
- onClick={() => {
82
- setOpenLogoutAllSessionsDialog(true);
83
- }}
84
- >
85
- Sign out
86
- </SecondaryButton>
87
- </Flex>
88
- </Card>
89
- )}
90
-
91
- {sessionToSignOut && (
92
- <LogoutDialog
93
- session={sessionToSignOut}
94
- open={openLogoutDialog}
95
- onOpenChange={setOpenLogoutDialog}
96
- />
97
- )}
98
-
99
- <LogoutAllSessionsDialog
100
- currentSessionId={currentSessionId}
101
- open={openLogoutAllSessionsDialog}
102
- onOpenChange={setOpenLogoutAllSessionsDialog}
103
- />
104
- </Flex>
105
- );
106
- };
107
-
108
- export function UserSessionsLoading() {
109
- return (
110
- <Card size="2">
111
- <Flex gap="4" align="center">
112
- <Skeleton>
113
- <IconPanel />
114
- </Skeleton>
115
-
116
- <Flex direction="column">
117
- <Text size="2" highContrast weight="bold" as="p" mb="-2px">
118
- <Skeleton>The location</Skeleton>
119
- </Text>
120
-
121
- <Text size="2" color="gray" as="p">
122
- <Skeleton>The location</Skeleton>
123
- </Text>
124
- </Flex>
125
- </Flex>
126
- </Card>
127
- );
128
- }
129
-
130
- export function UserSessionsError({ error }: { error: unknown }) {
131
- return (
132
- <Card size="2">
133
- <GenericError error={error} />
134
- </Card>
135
- );
136
- }
137
-
138
- interface UserSessionProps {
139
- session: ActiveSession;
140
- isCurrentSession?: boolean;
141
- onSignOut?: (session: ActiveSession) => void;
142
- }
143
-
144
- const UserSession = ({
145
- session,
146
- isCurrentSession = false,
147
- onSignOut,
148
- }: UserSessionProps) => {
149
- const userAgent = parseUserAgent(session.userAgent);
150
- const userLocation = getUserLocation(
151
- session.currentLocation,
152
- session.ipAddress,
153
- );
154
-
155
- return (
156
- <Flex gap="4" align="center">
157
- <IconPanel>
158
- {userAgent.isMobile ? <MobileIcon /> : <LaptopIcon />}
159
- </IconPanel>
160
-
161
- <Flex direction="column">
162
- <Text size="2" highContrast weight="bold" as="p" mb="-2px">
163
- {userAgent.pretty}
164
- </Text>
165
-
166
- <Flex gap="1" align="center">
167
- <Text size="2" color="gray" as="p">
168
- {userLocation}
169
- </Text>
170
-
171
- {isCurrentSession && <Badge ml="2">This device</Badge>}
172
-
173
- {!isCurrentSession && session.lastActivityAt && (
174
- <>
175
- <Text size="2" color="gray">
176
-
177
- </Text>
178
- <Text size="2" color="gray">
179
- Last seen{" "}
180
- {getComparativeReadableDate(
181
- new Date(),
182
- new Date(session.lastActivityAt),
183
- )}
184
- </Text>
185
- </>
186
- )}
187
- </Flex>
188
- </Flex>
189
-
190
- {!isCurrentSession && (
191
- <Flex ml="auto" mr="2">
192
- <Button
193
- variant="ghost"
194
- onClick={() => {
195
- onSignOut?.(session);
196
- }}
197
- >
198
- Sign out
199
- </Button>
200
- </Flex>
201
- )}
202
- </Flex>
203
- );
204
- };
@@ -1,62 +0,0 @@
1
- "use client";
2
-
3
- import { Select } from "@radix-ui/themes";
4
- import * as React from "react";
5
- import { SelectContent, SelectItem, SelectTrigger } from "./elements";
6
- import { useUsersManagementContext } from "./users-management-context";
7
- import { MemberRole } from "../api";
8
-
9
- const ALL_ROLES_NAME = "All";
10
- const ALL_ROLES_VALUE = "null";
11
-
12
- type UsersFilterProps = React.ComponentPropsWithoutRef<typeof Select.Root> & {
13
- roles: MemberRole[] | undefined;
14
- disabled?: boolean;
15
- };
16
-
17
- export const UsersFilter: React.FC<UsersFilterProps> = ({
18
- roles,
19
- disabled,
20
- }) => {
21
- const {
22
- dispatch,
23
- state: { role },
24
- } = useUsersManagementContext();
25
-
26
- const setFilterParams = (value: string | null) => {
27
- dispatch({ type: "FILTER_BY_ROLE", role: value });
28
- };
29
- const isValidRole = (value: unknown): value is string =>
30
- roles ? roles.findIndex((role) => role.slug === value) !== -1 : false;
31
- const filteredRole = isValidRole(role) ? role : ALL_ROLES_VALUE;
32
- const roleName =
33
- roles?.find((role) => role.slug === filteredRole)?.name || ALL_ROLES_NAME;
34
-
35
- const onValueChange = (value: string) => {
36
- if (value === ALL_ROLES_VALUE) {
37
- setFilterParams(null);
38
- } else {
39
- if (isValidRole(value)) {
40
- setFilterParams(value);
41
- }
42
- }
43
- };
44
-
45
- return (
46
- <Select.Root
47
- value={roles ? filteredRole : "null"}
48
- onValueChange={onValueChange}
49
- disabled={disabled}
50
- >
51
- <SelectTrigger>{roles ? roleName : ALL_ROLES_NAME}</SelectTrigger>
52
- <SelectContent>
53
- <SelectItem value={ALL_ROLES_VALUE}>{ALL_ROLES_NAME}</SelectItem>
54
- {roles?.map((role) => (
55
- <React.Fragment key={role.slug}>
56
- <SelectItem value={role.slug}>{role.name}</SelectItem>
57
- </React.Fragment>
58
- ))}
59
- </SelectContent>
60
- </Select.Root>
61
- );
62
- };
@@ -1,74 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import {
5
- UsersManagementAction,
6
- UsersManagementState,
7
- useUsersManagementState,
8
- } from "./users-management-state";
9
- import { ListMetadata } from "../api";
10
-
11
- export interface UsersManagementContextType {
12
- state: UsersManagementState;
13
- dispatch: React.Dispatch<UsersManagementAction>;
14
- }
15
-
16
- const UsersManagementContext = React.createContext<
17
- UsersManagementContextType | undefined
18
- >(undefined);
19
- UsersManagementContext.displayName = "UsersManagementContext";
20
-
21
- const initialState: UsersManagementState = {
22
- pagination: null,
23
- role: null,
24
- searchQuery: null,
25
- };
26
-
27
- export const UsersManagementContextProvider: React.FC<{
28
- children?: React.ReactNode;
29
- }> = ({ children }) => {
30
- const [state, dispatch] = useUsersManagementState(initialState);
31
-
32
- const context = React.useMemo<UsersManagementContextType>(
33
- () => ({
34
- state,
35
- dispatch,
36
- }),
37
- [state, dispatch],
38
- );
39
-
40
- return (
41
- <UsersManagementContext.Provider value={context}>
42
- {children}
43
- </UsersManagementContext.Provider>
44
- );
45
- };
46
-
47
- const NOOP = () => void 0;
48
- const EMPTY_PAGINATION: ListMetadata = {};
49
-
50
- /**
51
- * The context may be provided if it is instantiated in the tree above the user.
52
- */
53
- export function useUsersManagementContext(
54
- initialContext?: UsersManagementContextType | null,
55
- ): UsersManagementContextType {
56
- const context = React.useContext(UsersManagementContext);
57
-
58
- if (context) {
59
- return context;
60
- }
61
-
62
- if (initialContext) {
63
- return initialContext;
64
- }
65
-
66
- return {
67
- dispatch: NOOP,
68
- state: {
69
- pagination: EMPTY_PAGINATION,
70
- role: null,
71
- searchQuery: null,
72
- },
73
- };
74
- }
@@ -1,165 +0,0 @@
1
- import * as React from "react";
2
- import { canUseDOM } from "./utils";
3
- import { ListMetadata } from "../api";
4
-
5
- export function useUsersManagementState(initialState: UsersManagementState) {
6
- const [[state, effects], dispatch] = React.useReducer(reducer, [
7
- initialState,
8
- [],
9
- ]);
10
-
11
- React.useEffect(() => {
12
- if (window !== window.top) {
13
- // do not use query params if widget is rendered in an iframe
14
- return;
15
- }
16
- const params = new URLSearchParams(window.location.search);
17
- dispatch({ type: "INIT", params });
18
- }, []);
19
-
20
- React.useEffect(() => {
21
- for (const effect of effects) {
22
- effect();
23
- }
24
- }, [effects]);
25
-
26
- return [state, dispatch] as const;
27
- }
28
-
29
- function reducer(
30
- current: StateWithEffects<UsersManagementState>,
31
- action: UsersManagementAction,
32
- ): StateWithEffects<UsersManagementState> {
33
- const effects: EffectFn[] = [];
34
- const exec = (fn: () => void) => {
35
- const effect = () => {
36
- if (!effect.disposed) {
37
- effect.disposed = true;
38
- fn();
39
- }
40
- };
41
- effect.disposed = false;
42
- effects.push(effect);
43
- };
44
-
45
- const [currentState] = current;
46
- switch (action.type) {
47
- case "INIT": {
48
- const { params } = action;
49
- let role, searchQuery;
50
- if ((role = params.get("ak_role"))) {
51
- return [{ ...currentState, searchQuery: null, role }, effects];
52
- }
53
- if ((searchQuery = params.get("ak_q"))) {
54
- return [{ ...currentState, searchQuery, role: null }, effects];
55
- }
56
- return current;
57
- }
58
- case "SET_PAGINATION": {
59
- const { pagination } = action;
60
- if (
61
- (pagination.after &&
62
- pagination.after === currentState.pagination?.after) ||
63
- (pagination.before &&
64
- pagination.before === currentState.pagination?.before)
65
- ) {
66
- return current;
67
- }
68
-
69
- return [{ ...currentState, pagination }, effects];
70
- }
71
- case "FILTER_BY_ROLE": {
72
- if (action.role === currentState.role) {
73
- return current;
74
- }
75
- const { role } = action;
76
- exec(() =>
77
- updateQueryParams(
78
- { ak_role: role, ak_q: null },
79
- { replaceState: true },
80
- ),
81
- );
82
- return [
83
- { ...currentState, pagination: null, role, searchQuery: null },
84
- effects,
85
- ];
86
- }
87
- case "FILTER_BY_SEARCH": {
88
- if (action.searchQuery === currentState.searchQuery) {
89
- return current;
90
- }
91
- const { searchQuery } = action;
92
- exec(() =>
93
- updateQueryParams(
94
- { ak_role: null, ak_q: searchQuery },
95
- { replaceState: true },
96
- ),
97
- );
98
- return [
99
- { ...currentState, pagination: null, role: null, searchQuery },
100
- effects,
101
- ];
102
- //
103
- break;
104
- }
105
- default:
106
- return current;
107
- }
108
- }
109
-
110
- export interface UsersManagementState {
111
- searchQuery: string | null;
112
- role: string | null;
113
- pagination: ListMetadata | null;
114
- }
115
-
116
- export type UsersManagementAction =
117
- | { type: "INIT"; params: URLSearchParams }
118
- | { type: "FILTER_BY_SEARCH"; searchQuery: string | null }
119
- | { type: "FILTER_BY_ROLE"; role: string | null }
120
- | { type: "SET_PAGINATION"; pagination: ListMetadata };
121
-
122
- type EffectFn = {
123
- (): void;
124
- disposed: boolean;
125
- };
126
-
127
- type StateWithEffects<State> = [State, EffectFn[]];
128
-
129
- function updateQueryParams(
130
- newParams: { [key: string]: { toString(): string } | null },
131
- args: { replaceState?: boolean } = {},
132
- ) {
133
- if (!canUseDOM) {
134
- throw new Error("Cannot update query params on the server");
135
- }
136
-
137
- if (window !== window.top) {
138
- // do not update query params if widget is rendered in an iframe
139
- return;
140
- }
141
-
142
- const initialSearch = window.location.search;
143
- const params = new URLSearchParams(initialSearch);
144
- Object.entries(newParams).forEach(([key, value]) => {
145
- if (value != null && value !== "") {
146
- params.set(key, String(value));
147
- } else {
148
- params.delete(key);
149
- }
150
- });
151
-
152
- if (initialSearch === `?${params.toString()}`) {
153
- return;
154
- }
155
-
156
- const newUrl = params.toString()
157
- ? `${window.location.pathname}?${params}`
158
- : window.location.pathname;
159
-
160
- if (args.replaceState) {
161
- window.history.replaceState({}, "", newUrl);
162
- } else {
163
- window.history.pushState({}, "", newUrl);
164
- }
165
- }