@buoy-gg/impersonate 1.0.3-beta.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 (95) hide show
  1. package/LICENSE +58 -0
  2. package/lib/commonjs/impersonate/components/DataNukeSettings.js +715 -0
  3. package/lib/commonjs/impersonate/components/ImpersonateBanner.js +217 -0
  4. package/lib/commonjs/impersonate/components/ImpersonateHistoryList.js +173 -0
  5. package/lib/commonjs/impersonate/components/ImpersonateModal.js +304 -0
  6. package/lib/commonjs/impersonate/components/ImpersonateStatusBar.js +130 -0
  7. package/lib/commonjs/impersonate/components/UserAvatar.js +146 -0
  8. package/lib/commonjs/impersonate/components/UserCard.js +200 -0
  9. package/lib/commonjs/impersonate/components/UserSearchView.js +227 -0
  10. package/lib/commonjs/impersonate/components/index.js +85 -0
  11. package/lib/commonjs/impersonate/hooks/index.js +64 -0
  12. package/lib/commonjs/impersonate/hooks/useAutoClearAsyncStorage.js +144 -0
  13. package/lib/commonjs/impersonate/hooks/useAutoClearReactQuery.js +155 -0
  14. package/lib/commonjs/impersonate/hooks/useAutoClearRedux.js +188 -0
  15. package/lib/commonjs/impersonate/hooks/useImpersonate.js +215 -0
  16. package/lib/commonjs/impersonate/hooks/useImpersonateHistory.js +56 -0
  17. package/lib/commonjs/impersonate/index.js +49 -0
  18. package/lib/commonjs/impersonate/types/index.js +16 -0
  19. package/lib/commonjs/impersonate/types/types.js +1 -0
  20. package/lib/commonjs/impersonate/utils/impersonateListener.js +280 -0
  21. package/lib/commonjs/impersonate/utils/impersonateStore.js +607 -0
  22. package/lib/commonjs/impersonate/utils/index.js +49 -0
  23. package/lib/commonjs/index.js +118 -0
  24. package/lib/commonjs/package.json +1 -0
  25. package/lib/commonjs/preset.js +214 -0
  26. package/lib/module/impersonate/components/DataNukeSettings.js +710 -0
  27. package/lib/module/impersonate/components/ImpersonateBanner.js +211 -0
  28. package/lib/module/impersonate/components/ImpersonateHistoryList.js +168 -0
  29. package/lib/module/impersonate/components/ImpersonateModal.js +300 -0
  30. package/lib/module/impersonate/components/ImpersonateStatusBar.js +125 -0
  31. package/lib/module/impersonate/components/UserAvatar.js +140 -0
  32. package/lib/module/impersonate/components/UserCard.js +195 -0
  33. package/lib/module/impersonate/components/UserSearchView.js +222 -0
  34. package/lib/module/impersonate/components/index.js +11 -0
  35. package/lib/module/impersonate/hooks/index.js +7 -0
  36. package/lib/module/impersonate/hooks/useAutoClearAsyncStorage.js +140 -0
  37. package/lib/module/impersonate/hooks/useAutoClearReactQuery.js +151 -0
  38. package/lib/module/impersonate/hooks/useAutoClearRedux.js +183 -0
  39. package/lib/module/impersonate/hooks/useImpersonate.js +212 -0
  40. package/lib/module/impersonate/hooks/useImpersonateHistory.js +52 -0
  41. package/lib/module/impersonate/index.js +13 -0
  42. package/lib/module/impersonate/types/index.js +3 -0
  43. package/lib/module/impersonate/types/types.js +1 -0
  44. package/lib/module/impersonate/utils/impersonateListener.js +271 -0
  45. package/lib/module/impersonate/utils/impersonateStore.js +604 -0
  46. package/lib/module/impersonate/utils/index.js +4 -0
  47. package/lib/module/index.js +103 -0
  48. package/lib/module/preset.js +209 -0
  49. package/lib/typescript/impersonate/components/DataNukeSettings.d.ts +37 -0
  50. package/lib/typescript/impersonate/components/DataNukeSettings.d.ts.map +1 -0
  51. package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts +40 -0
  52. package/lib/typescript/impersonate/components/ImpersonateBanner.d.ts.map +1 -0
  53. package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts +24 -0
  54. package/lib/typescript/impersonate/components/ImpersonateHistoryList.d.ts.map +1 -0
  55. package/lib/typescript/impersonate/components/ImpersonateModal.d.ts +10 -0
  56. package/lib/typescript/impersonate/components/ImpersonateModal.d.ts.map +1 -0
  57. package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts +15 -0
  58. package/lib/typescript/impersonate/components/ImpersonateStatusBar.d.ts.map +1 -0
  59. package/lib/typescript/impersonate/components/UserAvatar.d.ts +32 -0
  60. package/lib/typescript/impersonate/components/UserAvatar.d.ts.map +1 -0
  61. package/lib/typescript/impersonate/components/UserCard.d.ts +28 -0
  62. package/lib/typescript/impersonate/components/UserCard.d.ts.map +1 -0
  63. package/lib/typescript/impersonate/components/UserSearchView.d.ts +31 -0
  64. package/lib/typescript/impersonate/components/UserSearchView.d.ts.map +1 -0
  65. package/lib/typescript/impersonate/components/index.d.ts +16 -0
  66. package/lib/typescript/impersonate/components/index.d.ts.map +1 -0
  67. package/lib/typescript/impersonate/hooks/index.d.ts +11 -0
  68. package/lib/typescript/impersonate/hooks/index.d.ts.map +1 -0
  69. package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts +48 -0
  70. package/lib/typescript/impersonate/hooks/useAutoClearAsyncStorage.d.ts.map +1 -0
  71. package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts +48 -0
  72. package/lib/typescript/impersonate/hooks/useAutoClearReactQuery.d.ts.map +1 -0
  73. package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts +78 -0
  74. package/lib/typescript/impersonate/hooks/useAutoClearRedux.d.ts.map +1 -0
  75. package/lib/typescript/impersonate/hooks/useImpersonate.d.ts +76 -0
  76. package/lib/typescript/impersonate/hooks/useImpersonate.d.ts.map +1 -0
  77. package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts +43 -0
  78. package/lib/typescript/impersonate/hooks/useImpersonateHistory.d.ts.map +1 -0
  79. package/lib/typescript/impersonate/index.d.ts +5 -0
  80. package/lib/typescript/impersonate/index.d.ts.map +1 -0
  81. package/lib/typescript/impersonate/types/index.d.ts +2 -0
  82. package/lib/typescript/impersonate/types/index.d.ts.map +1 -0
  83. package/lib/typescript/impersonate/types/types.d.ts +177 -0
  84. package/lib/typescript/impersonate/types/types.d.ts.map +1 -0
  85. package/lib/typescript/impersonate/utils/impersonateListener.d.ts +115 -0
  86. package/lib/typescript/impersonate/utils/impersonateListener.d.ts.map +1 -0
  87. package/lib/typescript/impersonate/utils/impersonateStore.d.ts +151 -0
  88. package/lib/typescript/impersonate/utils/impersonateStore.d.ts.map +1 -0
  89. package/lib/typescript/impersonate/utils/index.d.ts +3 -0
  90. package/lib/typescript/impersonate/utils/index.d.ts.map +1 -0
  91. package/lib/typescript/index.d.ts +80 -0
  92. package/lib/typescript/index.d.ts.map +1 -0
  93. package/lib/typescript/preset.d.ts +71 -0
  94. package/lib/typescript/preset.d.ts.map +1 -0
  95. package/package.json +78 -0
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * ImpersonateStatusBar Component
5
+ *
6
+ * Shows the current impersonation status with user avatar, info and stop button.
7
+ * Displays at the top of the modal when impersonation is active.
8
+ */
9
+
10
+ import React from "react";
11
+ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
12
+ import { buoyColors, X } from "@buoy-gg/shared-ui";
13
+ import { UserAvatar } from "./UserAvatar";
14
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ export function ImpersonateStatusBar({
16
+ user,
17
+ onStopImpersonation
18
+ }) {
19
+ const displayName = user.displayName || user.email || user.id;
20
+ return /*#__PURE__*/_jsxs(View, {
21
+ style: styles.container,
22
+ children: [/*#__PURE__*/_jsxs(View, {
23
+ style: styles.userSection,
24
+ children: [/*#__PURE__*/_jsx(UserAvatar, {
25
+ userId: user.id,
26
+ name: displayName,
27
+ size: "small",
28
+ showActiveIndicator: false
29
+ }), /*#__PURE__*/_jsx(View, {
30
+ style: styles.userInfo,
31
+ children: /*#__PURE__*/_jsx(Text, {
32
+ style: styles.userName,
33
+ numberOfLines: 1,
34
+ children: displayName
35
+ })
36
+ })]
37
+ }), /*#__PURE__*/_jsxs(View, {
38
+ style: styles.statusPill,
39
+ children: [/*#__PURE__*/_jsx(View, {
40
+ style: styles.pulseDot
41
+ }), /*#__PURE__*/_jsx(Text, {
42
+ style: styles.statusText,
43
+ children: "Active"
44
+ })]
45
+ }), /*#__PURE__*/_jsxs(TouchableOpacity, {
46
+ style: styles.stopButton,
47
+ onPress: onStopImpersonation,
48
+ activeOpacity: 0.7,
49
+ children: [/*#__PURE__*/_jsx(X, {
50
+ size: 12,
51
+ color: buoyColors.error
52
+ }), /*#__PURE__*/_jsx(Text, {
53
+ style: styles.stopButtonText,
54
+ children: "Stop"
55
+ })]
56
+ })]
57
+ });
58
+ }
59
+ const styles = StyleSheet.create({
60
+ container: {
61
+ flexDirection: "row",
62
+ alignItems: "center",
63
+ justifyContent: "space-between",
64
+ paddingVertical: 10,
65
+ paddingHorizontal: 16,
66
+ backgroundColor: buoyColors.success + "10",
67
+ borderBottomWidth: 1,
68
+ borderBottomColor: buoyColors.success + "25",
69
+ gap: 12
70
+ },
71
+ userSection: {
72
+ flexDirection: "row",
73
+ alignItems: "center",
74
+ flex: 1,
75
+ gap: 10,
76
+ minWidth: 0
77
+ },
78
+ userInfo: {
79
+ flex: 1,
80
+ minWidth: 0
81
+ },
82
+ userName: {
83
+ fontSize: 14,
84
+ fontWeight: "600",
85
+ color: buoyColors.text
86
+ },
87
+ statusPill: {
88
+ flexDirection: "row",
89
+ alignItems: "center",
90
+ gap: 6,
91
+ backgroundColor: buoyColors.success + "20",
92
+ paddingHorizontal: 10,
93
+ paddingVertical: 5,
94
+ borderRadius: 12
95
+ },
96
+ pulseDot: {
97
+ width: 6,
98
+ height: 6,
99
+ borderRadius: 3,
100
+ backgroundColor: buoyColors.success
101
+ },
102
+ statusText: {
103
+ fontSize: 11,
104
+ fontWeight: "600",
105
+ color: buoyColors.success,
106
+ textTransform: "uppercase",
107
+ letterSpacing: 0.5
108
+ },
109
+ stopButton: {
110
+ flexDirection: "row",
111
+ alignItems: "center",
112
+ gap: 4,
113
+ paddingHorizontal: 12,
114
+ paddingVertical: 6,
115
+ backgroundColor: buoyColors.error + "15",
116
+ borderRadius: 6,
117
+ borderWidth: 1,
118
+ borderColor: buoyColors.error + "30"
119
+ },
120
+ stopButtonText: {
121
+ fontSize: 12,
122
+ fontWeight: "600",
123
+ color: buoyColors.error
124
+ }
125
+ });
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * UserAvatar Component
5
+ *
6
+ * Displays a user avatar with initials and deterministic color.
7
+ * Supports size variants and active indicator.
8
+ */
9
+
10
+ import React, { useMemo } from "react";
11
+ import { View, Text, StyleSheet } from "react-native";
12
+ import { buoyColors } from "@buoy-gg/shared-ui";
13
+
14
+ // Avatar background colors - deterministic based on user ID
15
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ const AVATAR_COLORS = ["#6366F1",
17
+ // Indigo
18
+ "#8B5CF6",
19
+ // Purple
20
+ "#EC4899",
21
+ // Pink
22
+ "#F43F5E",
23
+ // Rose
24
+ "#F97316",
25
+ // Orange
26
+ "#EAB308",
27
+ // Yellow
28
+ "#22C55E",
29
+ // Green
30
+ "#14B8A6",
31
+ // Teal
32
+ "#06B6D4",
33
+ // Cyan
34
+ "#3B82F6" // Blue
35
+ ];
36
+ const SIZES = {
37
+ small: {
38
+ container: 24,
39
+ font: 10,
40
+ indicator: 8
41
+ },
42
+ medium: {
43
+ container: 36,
44
+ font: 14,
45
+ indicator: 10
46
+ },
47
+ large: {
48
+ container: 44,
49
+ font: 16,
50
+ indicator: 12
51
+ }
52
+ };
53
+ /**
54
+ * Get initials from a name or email
55
+ */
56
+ function getInitials(name) {
57
+ if (!name) return "?";
58
+
59
+ // If it's an email, use first letter before @
60
+ if (name.includes("@")) {
61
+ return name.charAt(0).toUpperCase();
62
+ }
63
+
64
+ // Split by spaces and get first letter of first two words
65
+ const parts = name.trim().split(/\s+/);
66
+ if (parts.length === 1) {
67
+ return parts[0].charAt(0).toUpperCase();
68
+ }
69
+ return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
70
+ }
71
+
72
+ /**
73
+ * Get deterministic color based on user ID
74
+ */
75
+ function getAvatarColor(userId) {
76
+ let hash = 0;
77
+ for (let i = 0; i < userId.length; i++) {
78
+ hash = userId.charCodeAt(i) + ((hash << 5) - hash);
79
+ }
80
+ return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
81
+ }
82
+ export function UserAvatar({
83
+ userId,
84
+ name,
85
+ size = "medium",
86
+ showActiveIndicator = false,
87
+ backgroundColor
88
+ }) {
89
+ const sizeConfig = SIZES[size];
90
+ const avatarColor = useMemo(() => backgroundColor ?? getAvatarColor(userId), [userId, backgroundColor]);
91
+ const initials = useMemo(() => getInitials(name), [name]);
92
+ return /*#__PURE__*/_jsxs(View, {
93
+ style: styles.wrapper,
94
+ children: [/*#__PURE__*/_jsx(View, {
95
+ style: [styles.container, {
96
+ width: sizeConfig.container,
97
+ height: sizeConfig.container,
98
+ borderRadius: sizeConfig.container / 2,
99
+ backgroundColor: avatarColor
100
+ }],
101
+ children: /*#__PURE__*/_jsx(Text, {
102
+ style: [styles.initials, {
103
+ fontSize: sizeConfig.font
104
+ }],
105
+ children: initials
106
+ })
107
+ }), showActiveIndicator && /*#__PURE__*/_jsx(View, {
108
+ style: [styles.indicator, {
109
+ width: sizeConfig.indicator,
110
+ height: sizeConfig.indicator,
111
+ borderRadius: sizeConfig.indicator / 2,
112
+ right: -1,
113
+ bottom: -1
114
+ }]
115
+ })]
116
+ });
117
+ }
118
+ const styles = StyleSheet.create({
119
+ wrapper: {
120
+ position: "relative"
121
+ },
122
+ container: {
123
+ alignItems: "center",
124
+ justifyContent: "center"
125
+ },
126
+ initials: {
127
+ color: "#FFFFFF",
128
+ fontWeight: "600",
129
+ letterSpacing: 0.5
130
+ },
131
+ indicator: {
132
+ position: "absolute",
133
+ backgroundColor: buoyColors.success,
134
+ borderWidth: 2,
135
+ borderColor: buoyColors.card
136
+ }
137
+ });
138
+
139
+ // Export helper functions for reuse
140
+ export { getInitials, getAvatarColor, AVATAR_COLORS };
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * UserCard Component
5
+ *
6
+ * Polished user card for search results and history.
7
+ * Features avatar, clean typography hierarchy, and contextual actions.
8
+ */
9
+
10
+ import React from "react";
11
+ import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
12
+ import { buoyColors, Play, X, PowerToggleButton } from "@buoy-gg/shared-ui";
13
+ import { UserAvatar } from "./UserAvatar";
14
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
+ export function UserCard({
16
+ user,
17
+ isActive = false,
18
+ onPress,
19
+ onStop,
20
+ onRemove,
21
+ showRemoveButton = false,
22
+ lastUsedTime,
23
+ variant = "default"
24
+ }) {
25
+ const displayName = user.displayName || user.email || user.id;
26
+ const showEmail = user.email && user.displayName;
27
+ const role = typeof user.metadata?.role === "string" ? user.metadata.role : null;
28
+ return /*#__PURE__*/_jsxs(TouchableOpacity, {
29
+ style: [styles.container, isActive && styles.activeContainer, variant === "compact" && styles.compactContainer],
30
+ onPress: onPress,
31
+ disabled: isActive,
32
+ activeOpacity: 0.7,
33
+ children: [/*#__PURE__*/_jsx(View, {
34
+ style: styles.avatarSection,
35
+ children: /*#__PURE__*/_jsx(UserAvatar, {
36
+ userId: user.id,
37
+ name: displayName,
38
+ size: variant === "compact" ? "small" : "medium",
39
+ showActiveIndicator: isActive
40
+ })
41
+ }), /*#__PURE__*/_jsxs(View, {
42
+ style: styles.content,
43
+ children: [/*#__PURE__*/_jsxs(View, {
44
+ style: styles.nameRow,
45
+ children: [/*#__PURE__*/_jsx(Text, {
46
+ style: [styles.name, isActive && styles.activeName],
47
+ numberOfLines: 1,
48
+ children: displayName
49
+ }), role && /*#__PURE__*/_jsx(View, {
50
+ style: styles.roleBadge,
51
+ children: /*#__PURE__*/_jsx(Text, {
52
+ style: styles.roleBadgeText,
53
+ children: role.toUpperCase()
54
+ })
55
+ })]
56
+ }), showEmail && /*#__PURE__*/_jsx(Text, {
57
+ style: styles.email,
58
+ numberOfLines: 1,
59
+ children: user.email
60
+ }), /*#__PURE__*/_jsx(View, {
61
+ style: styles.metadataRow,
62
+ children: /*#__PURE__*/_jsxs(Text, {
63
+ style: styles.userId,
64
+ children: ["ID: ", user.id]
65
+ })
66
+ })]
67
+ }), /*#__PURE__*/_jsxs(View, {
68
+ style: styles.actions,
69
+ children: [isActive ? /*#__PURE__*/_jsx(PowerToggleButton, {
70
+ isEnabled: true,
71
+ onToggle: onStop || (() => {}),
72
+ accessibilityLabel: "Stop impersonation"
73
+ }) : /*#__PURE__*/_jsx(TouchableOpacity, {
74
+ style: styles.actionButton,
75
+ onPress: onPress,
76
+ activeOpacity: 0.7,
77
+ children: /*#__PURE__*/_jsx(Play, {
78
+ size: 14,
79
+ color: "#FFFFFF"
80
+ })
81
+ }), showRemoveButton && onRemove && !isActive && /*#__PURE__*/_jsx(TouchableOpacity, {
82
+ style: styles.removeButton,
83
+ onPress: onRemove,
84
+ activeOpacity: 0.7,
85
+ children: /*#__PURE__*/_jsx(X, {
86
+ size: 12,
87
+ color: buoyColors.textMuted
88
+ })
89
+ })]
90
+ }), lastUsedTime && /*#__PURE__*/_jsx(Text, {
91
+ style: styles.lastUsed,
92
+ children: lastUsedTime
93
+ })]
94
+ });
95
+ }
96
+ const styles = StyleSheet.create({
97
+ container: {
98
+ flexDirection: "row",
99
+ alignItems: "center",
100
+ backgroundColor: buoyColors.card,
101
+ borderRadius: 10,
102
+ borderWidth: 1,
103
+ borderColor: buoyColors.border,
104
+ padding: 12,
105
+ gap: 12
106
+ },
107
+ activeContainer: {
108
+ backgroundColor: buoyColors.success + "10",
109
+ borderColor: buoyColors.success + "40"
110
+ },
111
+ compactContainer: {
112
+ padding: 10,
113
+ gap: 10
114
+ },
115
+ avatarSection: {
116
+ flexShrink: 0
117
+ },
118
+ content: {
119
+ flex: 1,
120
+ minWidth: 0
121
+ },
122
+ nameRow: {
123
+ flexDirection: "row",
124
+ alignItems: "center",
125
+ gap: 8,
126
+ marginBottom: 2
127
+ },
128
+ name: {
129
+ fontSize: 15,
130
+ fontWeight: "600",
131
+ color: buoyColors.text,
132
+ flexShrink: 1
133
+ },
134
+ activeName: {
135
+ color: buoyColors.success
136
+ },
137
+ roleBadge: {
138
+ backgroundColor: buoyColors.hover,
139
+ paddingHorizontal: 6,
140
+ paddingVertical: 2,
141
+ borderRadius: 4
142
+ },
143
+ roleBadgeText: {
144
+ fontSize: 9,
145
+ fontWeight: "700",
146
+ color: buoyColors.textMuted,
147
+ letterSpacing: 0.5
148
+ },
149
+ email: {
150
+ fontSize: 13,
151
+ color: buoyColors.textSecondary,
152
+ marginBottom: 4
153
+ },
154
+ metadataRow: {
155
+ flexDirection: "row",
156
+ alignItems: "center",
157
+ gap: 4
158
+ },
159
+ userId: {
160
+ fontSize: 11,
161
+ color: buoyColors.textMuted,
162
+ fontFamily: "monospace"
163
+ },
164
+ lastUsed: {
165
+ position: "absolute",
166
+ bottom: 8,
167
+ right: 10,
168
+ fontSize: 10,
169
+ color: buoyColors.textMuted
170
+ },
171
+ actions: {
172
+ flexDirection: "row",
173
+ alignItems: "center",
174
+ gap: 8,
175
+ flexShrink: 0
176
+ },
177
+ actionButton: {
178
+ width: 36,
179
+ height: 36,
180
+ borderRadius: 8,
181
+ backgroundColor: buoyColors.primary,
182
+ alignItems: "center",
183
+ justifyContent: "center"
184
+ },
185
+ removeButton: {
186
+ width: 28,
187
+ height: 28,
188
+ borderRadius: 6,
189
+ backgroundColor: buoyColors.hover,
190
+ borderWidth: 1,
191
+ borderColor: buoyColors.border,
192
+ alignItems: "center",
193
+ justifyContent: "center"
194
+ }
195
+ });
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * UserSearchView Component
5
+ *
6
+ * Displays search results for user impersonation.
7
+ * Search input is handled by the parent modal's header.
8
+ * Uses polished UserCard component for results.
9
+ */
10
+
11
+ import React, { useCallback } from "react";
12
+ import { View, Text, StyleSheet, FlatList, ActivityIndicator } from "react-native";
13
+ import { SectionHeader, EmptyState, buoyColors, Search, AlertTriangle, Users } from "@buoy-gg/shared-ui";
14
+ import { UserCard } from "./UserCard";
15
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ export function UserSearchView({
17
+ currentUser,
18
+ searchQuery,
19
+ searchResults,
20
+ isSearching,
21
+ searchError,
22
+ onSelectUser,
23
+ onStopImpersonation,
24
+ searchAvailable = true
25
+ }) {
26
+ const renderUserItem = useCallback(({
27
+ item
28
+ }) => {
29
+ const isCurrentUser = currentUser?.id === item.id;
30
+ return /*#__PURE__*/_jsx(UserCard, {
31
+ user: item,
32
+ isActive: isCurrentUser,
33
+ onPress: () => !isCurrentUser && onSelectUser(item),
34
+ onStop: isCurrentUser ? onStopImpersonation : undefined
35
+ });
36
+ }, [currentUser, onSelectUser, onStopImpersonation]);
37
+ return /*#__PURE__*/_jsxs(View, {
38
+ style: styles.container,
39
+ children: [!searchAvailable && /*#__PURE__*/_jsx(View, {
40
+ style: styles.section,
41
+ children: /*#__PURE__*/_jsxs(View, {
42
+ style: styles.warningCard,
43
+ children: [/*#__PURE__*/_jsx(AlertTriangle, {
44
+ size: 18,
45
+ color: buoyColors.warning
46
+ }), /*#__PURE__*/_jsxs(View, {
47
+ style: styles.warningContent,
48
+ children: [/*#__PURE__*/_jsx(Text, {
49
+ style: styles.warningTitle,
50
+ children: "Search Not Available"
51
+ }), /*#__PURE__*/_jsx(Text, {
52
+ style: styles.warningText,
53
+ children: "Configure the onSearchUsers callback to enable user search."
54
+ })]
55
+ })]
56
+ })
57
+ }), isSearching && /*#__PURE__*/_jsxs(View, {
58
+ style: styles.loadingContainer,
59
+ children: [/*#__PURE__*/_jsx(ActivityIndicator, {
60
+ size: "small",
61
+ color: buoyColors.primary
62
+ }), /*#__PURE__*/_jsx(Text, {
63
+ style: styles.loadingText,
64
+ children: "Searching users..."
65
+ })]
66
+ }), searchError && /*#__PURE__*/_jsx(View, {
67
+ style: styles.section,
68
+ children: /*#__PURE__*/_jsxs(View, {
69
+ style: styles.errorCard,
70
+ children: [/*#__PURE__*/_jsx(AlertTriangle, {
71
+ size: 16,
72
+ color: buoyColors.error
73
+ }), /*#__PURE__*/_jsx(Text, {
74
+ style: styles.errorText,
75
+ children: searchError
76
+ })]
77
+ })
78
+ }), searchResults.length > 0 && /*#__PURE__*/_jsxs(View, {
79
+ style: styles.resultsSection,
80
+ children: [/*#__PURE__*/_jsxs(SectionHeader, {
81
+ children: [/*#__PURE__*/_jsx(SectionHeader.Title, {
82
+ children: "Results"
83
+ }), /*#__PURE__*/_jsx(SectionHeader.Badge, {
84
+ count: searchResults.length
85
+ })]
86
+ }), /*#__PURE__*/_jsx(FlatList, {
87
+ data: searchResults,
88
+ keyExtractor: item => item.id,
89
+ renderItem: renderUserItem,
90
+ ItemSeparatorComponent: () => /*#__PURE__*/_jsx(View, {
91
+ style: styles.separator
92
+ }),
93
+ contentContainerStyle: styles.listContent,
94
+ showsVerticalScrollIndicator: false
95
+ })]
96
+ }), searchQuery && !isSearching && searchResults.length === 0 && !searchError && /*#__PURE__*/_jsxs(View, {
97
+ style: styles.emptyStateContainer,
98
+ children: [/*#__PURE__*/_jsx(View, {
99
+ style: styles.emptyStateIcon,
100
+ children: /*#__PURE__*/_jsx(Users, {
101
+ size: 28,
102
+ color: buoyColors.textMuted
103
+ })
104
+ }), /*#__PURE__*/_jsx(Text, {
105
+ style: styles.emptyStateTitle,
106
+ children: "No users found"
107
+ }), /*#__PURE__*/_jsxs(Text, {
108
+ style: styles.emptyStateDescription,
109
+ children: ["No results for \"", searchQuery, "\""]
110
+ }), /*#__PURE__*/_jsx(Text, {
111
+ style: styles.emptyStateHint,
112
+ children: "Try a different search term"
113
+ })]
114
+ }), !searchQuery && searchResults.length === 0 && !isSearching && searchAvailable && /*#__PURE__*/_jsx(EmptyState, {
115
+ title: "Search for users",
116
+ description: "Tap the search icon in the header to find users by email, name, or ID",
117
+ icon: Search,
118
+ variant: "minimal"
119
+ })]
120
+ });
121
+ }
122
+ const styles = StyleSheet.create({
123
+ container: {
124
+ flex: 1
125
+ },
126
+ section: {
127
+ padding: 16,
128
+ paddingBottom: 8
129
+ },
130
+ loadingContainer: {
131
+ flexDirection: "row",
132
+ alignItems: "center",
133
+ justifyContent: "center",
134
+ gap: 10,
135
+ paddingVertical: 20
136
+ },
137
+ loadingText: {
138
+ fontSize: 13,
139
+ color: buoyColors.textSecondary
140
+ },
141
+ resultsSection: {
142
+ flex: 1,
143
+ paddingHorizontal: 16
144
+ },
145
+ listContent: {
146
+ paddingBottom: 16
147
+ },
148
+ separator: {
149
+ height: 8
150
+ },
151
+ warningCard: {
152
+ flexDirection: "row",
153
+ alignItems: "flex-start",
154
+ gap: 12,
155
+ backgroundColor: buoyColors.warning + "12",
156
+ borderRadius: 10,
157
+ borderWidth: 1,
158
+ borderColor: buoyColors.warning + "25",
159
+ padding: 14
160
+ },
161
+ warningContent: {
162
+ flex: 1
163
+ },
164
+ warningTitle: {
165
+ fontSize: 14,
166
+ fontWeight: "600",
167
+ color: buoyColors.warning,
168
+ marginBottom: 4
169
+ },
170
+ warningText: {
171
+ fontSize: 13,
172
+ color: buoyColors.textSecondary,
173
+ lineHeight: 18
174
+ },
175
+ errorCard: {
176
+ flexDirection: "row",
177
+ alignItems: "center",
178
+ gap: 10,
179
+ backgroundColor: buoyColors.error + "12",
180
+ borderRadius: 10,
181
+ borderWidth: 1,
182
+ borderColor: buoyColors.error + "25",
183
+ padding: 12
184
+ },
185
+ errorText: {
186
+ flex: 1,
187
+ fontSize: 13,
188
+ color: buoyColors.error
189
+ },
190
+ emptyStateContainer: {
191
+ flex: 1,
192
+ alignItems: "center",
193
+ justifyContent: "center",
194
+ paddingHorizontal: 32,
195
+ paddingVertical: 48
196
+ },
197
+ emptyStateIcon: {
198
+ width: 56,
199
+ height: 56,
200
+ borderRadius: 28,
201
+ backgroundColor: buoyColors.hover,
202
+ alignItems: "center",
203
+ justifyContent: "center",
204
+ marginBottom: 16
205
+ },
206
+ emptyStateTitle: {
207
+ fontSize: 16,
208
+ fontWeight: "600",
209
+ color: buoyColors.text,
210
+ marginBottom: 6
211
+ },
212
+ emptyStateDescription: {
213
+ fontSize: 14,
214
+ color: buoyColors.textSecondary,
215
+ textAlign: "center",
216
+ marginBottom: 8
217
+ },
218
+ emptyStateHint: {
219
+ fontSize: 12,
220
+ color: buoyColors.textMuted
221
+ }
222
+ });
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+
3
+ export { UserSearchView } from "./UserSearchView";
4
+ export { DataNukeSettings as DataNukeSettingsComponent } from "./DataNukeSettings";
5
+ export { ImpersonateHistoryList } from "./ImpersonateHistoryList";
6
+ export { ImpersonateModal } from "./ImpersonateModal";
7
+ export { ImpersonateBanner, ImpersonateBannerMinimal } from "./ImpersonateBanner";
8
+ export { ImpersonateStatusBar } from "./ImpersonateStatusBar";
9
+ export { UserAvatar } from "./UserAvatar";
10
+ export { getInitials, getAvatarColor, AVATAR_COLORS } from "./UserAvatar";
11
+ export { UserCard } from "./UserCard";
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ export { useImpersonate } from "./useImpersonate";
4
+ export { useImpersonateHistory } from "./useImpersonateHistory";
5
+ export { useAutoClearReactQuery, useReactQueryAvailability } from "./useAutoClearReactQuery";
6
+ export { useAutoClearRedux, useReduxAvailability, REDUX_RESET_ACTION_TYPE } from "./useAutoClearRedux";
7
+ export { useAutoClearAsyncStorage, useAsyncStorageAvailability } from "./useAutoClearAsyncStorage";