@workos-inc/widgets 1.1.4 → 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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/lib/organization-switcher.d.ts +10 -1
  3. package/dist/cjs/lib/organization-switcher.d.ts.map +1 -1
  4. package/dist/cjs/lib/organization-switcher.js +31 -3
  5. package/dist/cjs/lib/organization-switcher.js.map +1 -1
  6. package/dist/cjs/workos-widgets.client.d.ts +6 -0
  7. package/dist/cjs/workos-widgets.client.d.ts.map +1 -1
  8. package/dist/cjs/workos-widgets.client.js +23 -3
  9. package/dist/cjs/workos-widgets.client.js.map +1 -1
  10. package/dist/esm/lib/organization-switcher.d.ts +10 -1
  11. package/dist/esm/lib/organization-switcher.d.ts.map +1 -1
  12. package/dist/esm/lib/organization-switcher.js +31 -3
  13. package/dist/esm/lib/organization-switcher.js.map +1 -1
  14. package/dist/esm/workos-widgets.client.d.ts +6 -0
  15. package/dist/esm/workos-widgets.client.d.ts.map +1 -1
  16. package/dist/esm/workos-widgets.client.js +25 -5
  17. package/dist/esm/workos-widgets.client.js.map +1 -1
  18. package/package.json +40 -47
  19. package/src/api/api-provider.tsx +0 -158
  20. package/src/api/constants.ts +0 -1
  21. package/src/api/endpoint.ts +0 -3097
  22. package/src/api/errors.ts +0 -48
  23. package/src/api/index.ts +0 -2
  24. package/src/api/utils.ts +0 -42
  25. package/src/api/widgets-api-client.ts +0 -87
  26. package/src/card-list.tsx +0 -26
  27. package/src/index.ts +0 -9
  28. package/src/lib/add-mfa-dialog.tsx +0 -379
  29. package/src/lib/api/config.ts +0 -9
  30. package/src/lib/api/user.ts +0 -98
  31. package/src/lib/change-password-dialog.tsx +0 -290
  32. package/src/lib/constants.ts +0 -3
  33. package/src/lib/copy-button.tsx +0 -53
  34. package/src/lib/delete-user-dialog.tsx +0 -110
  35. package/src/lib/edit-user-profile-dialog.tsx +0 -181
  36. package/src/lib/edit-user-role-dialog.tsx +0 -178
  37. package/src/lib/elements.tsx +0 -428
  38. package/src/lib/elevated-access.tsx +0 -261
  39. package/src/lib/error-boundary.tsx +0 -166
  40. package/src/lib/errors.ts +0 -49
  41. package/src/lib/generic-error.tsx +0 -70
  42. package/src/lib/icon-panel.tsx +0 -26
  43. package/src/lib/icons.tsx +0 -21
  44. package/src/lib/invite-user-dialog.tsx +0 -327
  45. package/src/lib/logout-all-sessions-dialog.tsx +0 -82
  46. package/src/lib/logout-dialog.tsx +0 -85
  47. package/src/lib/marker.tsx +0 -39
  48. package/src/lib/oauth-icons.tsx +0 -138
  49. package/src/lib/organization-switcher.tsx +0 -156
  50. package/src/lib/otp-input.tsx +0 -276
  51. package/src/lib/resend-invite-dialog.tsx +0 -145
  52. package/src/lib/reset-mfa-dialog.tsx +0 -104
  53. package/src/lib/revoke-invite-dialog.tsx +0 -111
  54. package/src/lib/save-button.tsx +0 -113
  55. package/src/lib/search-provider.tsx +0 -51
  56. package/src/lib/set-password-dialog.tsx +0 -204
  57. package/src/lib/use-dialog-close.tsx +0 -19
  58. package/src/lib/use-is-hydrated.ts +0 -13
  59. package/src/lib/use-layout-effect.ts +0 -6
  60. package/src/lib/use-security-settings.tsx +0 -49
  61. package/src/lib/user-actions-dropdown.tsx +0 -157
  62. package/src/lib/user-profile.tsx +0 -227
  63. package/src/lib/user-security.tsx +0 -187
  64. package/src/lib/user-sessions.tsx +0 -204
  65. package/src/lib/users-filter.tsx +0 -62
  66. package/src/lib/users-management-context.tsx +0 -74
  67. package/src/lib/users-management-state.ts +0 -165
  68. package/src/lib/users-management.tsx +0 -594
  69. package/src/lib/users-search.tsx +0 -73
  70. package/src/lib/utils.ts +0 -131
  71. package/src/lib/widgets-context.ts +0 -29
  72. package/src/organization-switcher.client.tsx +0 -81
  73. package/src/user-profile.client.tsx +0 -55
  74. package/src/user-security.client.tsx +0 -55
  75. package/src/user-sessions.client.tsx +0 -100
  76. package/src/users-management.client.tsx +0 -73
  77. package/src/workos-widgets.client.tsx +0 -75
  78. /package/{src → dist/css}/base.css +0 -0
  79. /package/{src → dist/css}/lib/card-list.css +0 -0
  80. /package/{src → dist/css}/lib/marker.css +0 -0
  81. /package/{src → dist/css}/lib/save-button.css +0 -0
  82. /package/{src → dist/css}/styles.css +0 -0
  83. /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
- }