better-auth-ui 3.2.5

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 (226) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -0
  3. package/dist/auth-hooks-IOEvlYYv.d.cts +6966 -0
  4. package/dist/auth-hooks-IOEvlYYv.d.ts +6966 -0
  5. package/dist/auth-mutators-DdqOmQ32.d.cts +29 -0
  6. package/dist/auth-mutators-DdqOmQ32.d.ts +29 -0
  7. package/dist/auth-ui-provider-BsH3xJDw.d.ts +697 -0
  8. package/dist/auth-ui-provider-DhZfncd3.d.cts +697 -0
  9. package/dist/chunk-BDFQSFBU.js +750 -0
  10. package/dist/chunk-CRAHKL2C.cjs +801 -0
  11. package/dist/chunk-MJPOA6PK.js +801 -0
  12. package/dist/chunk-SV64DXGW.cjs +750 -0
  13. package/dist/index.cjs +12618 -0
  14. package/dist/index.d.cts +771 -0
  15. package/dist/index.d.ts +771 -0
  16. package/dist/index.js +12618 -0
  17. package/dist/instantdb.cjs +189 -0
  18. package/dist/instantdb.d.cts +36 -0
  19. package/dist/instantdb.d.ts +36 -0
  20. package/dist/instantdb.js +189 -0
  21. package/dist/metafile-cjs.json +1 -0
  22. package/dist/metafile-esm.json +1 -0
  23. package/dist/server.cjs +194 -0
  24. package/dist/server.d.cts +35 -0
  25. package/dist/server.d.ts +35 -0
  26. package/dist/server.js +194 -0
  27. package/dist/style.css +1 -0
  28. package/dist/tanstack.cjs +153 -0
  29. package/dist/tanstack.d.cts +18 -0
  30. package/dist/tanstack.d.ts +18 -0
  31. package/dist/tanstack.js +153 -0
  32. package/dist/triplit.cjs +201 -0
  33. package/dist/triplit.d.cts +31 -0
  34. package/dist/triplit.d.ts +31 -0
  35. package/dist/triplit.js +201 -0
  36. package/dist/utils-C5R37WDe.d.cts +3 -0
  37. package/dist/utils-C5R37WDe.d.ts +3 -0
  38. package/dist/view-paths-CHSJf5dv.d.cts +645 -0
  39. package/dist/view-paths-CHSJf5dv.d.ts +645 -0
  40. package/package.json +156 -0
  41. package/src/components/account/account-view.tsx +220 -0
  42. package/src/components/auth/auth-callback.tsx +36 -0
  43. package/src/components/auth/auth-form.tsx +277 -0
  44. package/src/components/auth/auth-view.tsx +389 -0
  45. package/src/components/auth/email-otp-button.tsx +53 -0
  46. package/src/components/auth/forms/email-otp-form.tsx +288 -0
  47. package/src/components/auth/forms/forgot-password-form.tsx +168 -0
  48. package/src/components/auth/forms/magic-link-form.tsx +191 -0
  49. package/src/components/auth/forms/recover-account-form.tsx +138 -0
  50. package/src/components/auth/forms/reset-password-form.tsx +215 -0
  51. package/src/components/auth/forms/sign-in-form.tsx +289 -0
  52. package/src/components/auth/forms/sign-up-form.tsx +788 -0
  53. package/src/components/auth/forms/two-factor-form.tsx +372 -0
  54. package/src/components/auth/magic-link-button.tsx +54 -0
  55. package/src/components/auth/one-tap.tsx +48 -0
  56. package/src/components/auth/otp-input-group.tsx +65 -0
  57. package/src/components/auth/passkey-button.tsx +85 -0
  58. package/src/components/auth/provider-button.tsx +141 -0
  59. package/src/components/auth/sign-out.tsx +25 -0
  60. package/src/components/auth-loading.tsx +21 -0
  61. package/src/components/captcha/captcha.tsx +79 -0
  62. package/src/components/captcha/recaptcha-badge.tsx +61 -0
  63. package/src/components/captcha/recaptcha-v2.tsx +58 -0
  64. package/src/components/captcha/recaptcha-v3.tsx +73 -0
  65. package/src/components/email/email-template.tsx +216 -0
  66. package/src/components/form-error.tsx +27 -0
  67. package/src/components/organization/accept-invitation-card.tsx +362 -0
  68. package/src/components/organization/create-organization-dialog.tsx +395 -0
  69. package/src/components/organization/delete-organization-card.tsx +101 -0
  70. package/src/components/organization/delete-organization-dialog.tsx +209 -0
  71. package/src/components/organization/invitation-cell.tsx +156 -0
  72. package/src/components/organization/invite-member-dialog.tsx +258 -0
  73. package/src/components/organization/leave-organization-dialog.tsx +150 -0
  74. package/src/components/organization/member-cell.tsx +187 -0
  75. package/src/components/organization/organization-cell-view.tsx +122 -0
  76. package/src/components/organization/organization-cell.tsx +154 -0
  77. package/src/components/organization/organization-invitations-card.tsx +94 -0
  78. package/src/components/organization/organization-logo-card.tsx +308 -0
  79. package/src/components/organization/organization-logo.tsx +120 -0
  80. package/src/components/organization/organization-members-card.tsx +155 -0
  81. package/src/components/organization/organization-name-card.tsx +204 -0
  82. package/src/components/organization/organization-settings-cards.tsx +67 -0
  83. package/src/components/organization/organization-slug-card.tsx +223 -0
  84. package/src/components/organization/organization-switcher.tsx +512 -0
  85. package/src/components/organization/organization-view.tsx +228 -0
  86. package/src/components/organization/organizations-card.tsx +72 -0
  87. package/src/components/organization/personal-account-view.tsx +115 -0
  88. package/src/components/organization/remove-member-dialog.tsx +144 -0
  89. package/src/components/organization/update-member-role-dialog.tsx +213 -0
  90. package/src/components/organization/user-invitations-card.tsx +238 -0
  91. package/src/components/password-input.tsx +56 -0
  92. package/src/components/provider-icons.tsx +385 -0
  93. package/src/components/redirect-to-sign-in.tsx +16 -0
  94. package/src/components/redirect-to-sign-up.tsx +16 -0
  95. package/src/components/settings/account/account-cell.tsx +158 -0
  96. package/src/components/settings/account/accounts-card.tsx +75 -0
  97. package/src/components/settings/account/delete-account-card.tsx +65 -0
  98. package/src/components/settings/account/delete-account-dialog.tsx +231 -0
  99. package/src/components/settings/account/update-avatar-card.tsx +198 -0
  100. package/src/components/settings/account/update-field-card.tsx +282 -0
  101. package/src/components/settings/account/update-name-card.tsx +39 -0
  102. package/src/components/settings/account/update-username-card.tsx +42 -0
  103. package/src/components/settings/account-settings-cards.tsx +123 -0
  104. package/src/components/settings/api-key/api-key-cell.tsx +108 -0
  105. package/src/components/settings/api-key/api-key-delete-dialog.tsx +162 -0
  106. package/src/components/settings/api-key/api-key-display-dialog.tsx +110 -0
  107. package/src/components/settings/api-key/api-keys-card.tsx +98 -0
  108. package/src/components/settings/api-key/create-api-key-dialog.tsx +376 -0
  109. package/src/components/settings/passkey/passkey-cell.tsx +113 -0
  110. package/src/components/settings/passkey/passkeys-card.tsx +111 -0
  111. package/src/components/settings/providers/provider-cell.tsx +152 -0
  112. package/src/components/settings/providers/providers-card.tsx +108 -0
  113. package/src/components/settings/security/change-email-card.tsx +200 -0
  114. package/src/components/settings/security/change-password-card.tsx +326 -0
  115. package/src/components/settings/security/session-cell.tsx +120 -0
  116. package/src/components/settings/security/sessions-card.tsx +58 -0
  117. package/src/components/settings/security-settings-cards.tsx +111 -0
  118. package/src/components/settings/shared/session-freshness-dialog.tsx +97 -0
  119. package/src/components/settings/shared/settings-action-button.tsx +51 -0
  120. package/src/components/settings/shared/settings-card-footer.tsx +94 -0
  121. package/src/components/settings/shared/settings-card-header.tsx +67 -0
  122. package/src/components/settings/shared/settings-card.tsx +106 -0
  123. package/src/components/settings/skeletons/input-field-skeleton.tsx +18 -0
  124. package/src/components/settings/skeletons/settings-cell-skeleton.tsx +37 -0
  125. package/src/components/settings/two-factor/backup-codes-dialog.tsx +113 -0
  126. package/src/components/settings/two-factor/two-factor-card.tsx +63 -0
  127. package/src/components/settings/two-factor/two-factor-password-dialog.tsx +226 -0
  128. package/src/components/signed-in.tsx +20 -0
  129. package/src/components/signed-out.tsx +20 -0
  130. package/src/components/ui/alert.tsx +66 -0
  131. package/src/components/ui/avatar.tsx +53 -0
  132. package/src/components/ui/button.tsx +59 -0
  133. package/src/components/ui/card.tsx +92 -0
  134. package/src/components/ui/checkbox.tsx +32 -0
  135. package/src/components/ui/dialog.tsx +143 -0
  136. package/src/components/ui/drawer.tsx +135 -0
  137. package/src/components/ui/dropdown-menu.tsx +257 -0
  138. package/src/components/ui/form.tsx +167 -0
  139. package/src/components/ui/input-otp.tsx +77 -0
  140. package/src/components/ui/input.tsx +21 -0
  141. package/src/components/ui/label.tsx +24 -0
  142. package/src/components/ui/select.tsx +185 -0
  143. package/src/components/ui/separator.tsx +28 -0
  144. package/src/components/ui/skeleton.tsx +13 -0
  145. package/src/components/ui/tabs.tsx +66 -0
  146. package/src/components/ui/textarea.tsx +18 -0
  147. package/src/components/user-avatar.tsx +147 -0
  148. package/src/components/user-button.tsx +409 -0
  149. package/src/components/user-view.tsx +138 -0
  150. package/src/hooks/use-auth-data.ts +184 -0
  151. package/src/hooks/use-authenticate.ts +62 -0
  152. package/src/hooks/use-captcha.tsx +138 -0
  153. package/src/hooks/use-current-organization.ts +59 -0
  154. package/src/hooks/use-hydrated.ts +13 -0
  155. package/src/hooks/use-lang.ts +32 -0
  156. package/src/hooks/use-success-transition.ts +51 -0
  157. package/src/hooks/use-theme.ts +39 -0
  158. package/src/index.ts +65 -0
  159. package/src/instantdb.ts +1 -0
  160. package/src/lib/auth-data-cache.ts +90 -0
  161. package/src/lib/auth-ui-provider.tsx +658 -0
  162. package/src/lib/gravatar-utils.ts +58 -0
  163. package/src/lib/image-utils.ts +55 -0
  164. package/src/lib/instantdb/model-names.ts +24 -0
  165. package/src/lib/instantdb/use-instant-options.ts +98 -0
  166. package/src/lib/instantdb/use-list-accounts.ts +38 -0
  167. package/src/lib/instantdb/use-list-sessions.ts +53 -0
  168. package/src/lib/instantdb/use-session.ts +55 -0
  169. package/src/lib/organization-refetcher.tsx +56 -0
  170. package/src/lib/social-providers.ts +144 -0
  171. package/src/lib/tanstack/auth-ui-provider-tanstack.tsx +49 -0
  172. package/src/lib/tanstack/use-tanstack-options.ts +112 -0
  173. package/src/lib/triplit/model-names.ts +24 -0
  174. package/src/lib/triplit/use-conditional-query.ts +82 -0
  175. package/src/lib/triplit/use-list-accounts.ts +31 -0
  176. package/src/lib/triplit/use-list-sessions.ts +33 -0
  177. package/src/lib/triplit/use-session.ts +42 -0
  178. package/src/lib/triplit/use-triplit-hooks.ts +68 -0
  179. package/src/lib/triplit/use-triplit-token.ts +44 -0
  180. package/src/lib/utils.ts +105 -0
  181. package/src/lib/view-paths.ts +55 -0
  182. package/src/localization/admin-error-codes.ts +20 -0
  183. package/src/localization/anonymous-error-codes.ts +6 -0
  184. package/src/localization/api-key-error-codes.ts +32 -0
  185. package/src/localization/auth-localization.ts +740 -0
  186. package/src/localization/base-error-codes.ts +27 -0
  187. package/src/localization/captcha-error-codes.ts +17 -0
  188. package/src/localization/email-otp-error-codes.ts +7 -0
  189. package/src/localization/generic-oauth-error-codes.ts +3 -0
  190. package/src/localization/haveibeenpwned-error-codes.ts +4 -0
  191. package/src/localization/multi-session-error-codes.ts +3 -0
  192. package/src/localization/organization-error-codes.ts +57 -0
  193. package/src/localization/passkey-error-codes.ts +10 -0
  194. package/src/localization/phone-number-error-codes.ts +10 -0
  195. package/src/localization/stripe-localization.ts +12 -0
  196. package/src/localization/two-factor-error-codes.ts +12 -0
  197. package/src/localization/username-error-codes.ts +9 -0
  198. package/src/server.ts +4 -0
  199. package/src/style.css +1 -0
  200. package/src/tanstack.ts +1 -0
  201. package/src/triplit.ts +1 -0
  202. package/src/types/account-options.ts +35 -0
  203. package/src/types/additional-fields.ts +21 -0
  204. package/src/types/any-auth-client.ts +6 -0
  205. package/src/types/api-key.ts +9 -0
  206. package/src/types/auth-client.ts +37 -0
  207. package/src/types/auth-hooks.ts +61 -0
  208. package/src/types/auth-mutators.ts +17 -0
  209. package/src/types/avatar-options.ts +29 -0
  210. package/src/types/captcha-options.ts +32 -0
  211. package/src/types/captcha-provider.ts +6 -0
  212. package/src/types/credentials-options.ts +32 -0
  213. package/src/types/delete-user-options.ts +7 -0
  214. package/src/types/fetch-error.ts +6 -0
  215. package/src/types/generic-oauth-options.ts +16 -0
  216. package/src/types/gravatar-options.ts +21 -0
  217. package/src/types/image.ts +7 -0
  218. package/src/types/invitation.ts +10 -0
  219. package/src/types/link.ts +7 -0
  220. package/src/types/organization-options.ts +106 -0
  221. package/src/types/password-validation.ts +16 -0
  222. package/src/types/profile.ts +15 -0
  223. package/src/types/refetch.ts +1 -0
  224. package/src/types/render-toast.ts +9 -0
  225. package/src/types/sign-up-options.ts +7 -0
  226. package/src/types/social-options.ts +16 -0
@@ -0,0 +1,512 @@
1
+ "use client"
2
+
3
+ import type { Organization } from "better-auth/plugins/organization"
4
+ import {
5
+ ChevronsUpDown,
6
+ LogInIcon,
7
+ PlusCircleIcon,
8
+ SettingsIcon
9
+ } from "lucide-react"
10
+ import {
11
+ type ComponentProps,
12
+ type ReactNode,
13
+ useCallback,
14
+ useContext,
15
+ useEffect,
16
+ useMemo,
17
+ useState
18
+ } from "react"
19
+
20
+ import { useCurrentOrganization } from "../../hooks/use-current-organization"
21
+ import { AuthUIContext } from "../../lib/auth-ui-provider"
22
+ import { cn, getLocalizedError } from "../../lib/utils"
23
+ import type { AuthLocalization } from "../../localization/auth-localization"
24
+ import type { User } from "../../types/auth-client"
25
+ import { Button } from "../ui/button"
26
+ import {
27
+ DropdownMenu,
28
+ DropdownMenuContent,
29
+ DropdownMenuItem,
30
+ DropdownMenuSeparator,
31
+ DropdownMenuTrigger
32
+ } from "../ui/dropdown-menu"
33
+ import { UserAvatar, type UserAvatarClassNames } from "../user-avatar"
34
+ import type { UserViewClassNames } from "../user-view"
35
+ import { CreateOrganizationDialog } from "./create-organization-dialog"
36
+ import {
37
+ OrganizationCellView,
38
+ type OrganizationViewClassNames
39
+ } from "./organization-cell-view"
40
+ import { OrganizationLogo } from "./organization-logo"
41
+ import { PersonalAccountView } from "./personal-account-view"
42
+
43
+ export interface OrganizationSwitcherClassNames {
44
+ base?: string
45
+ skeleton?: string
46
+ trigger?: {
47
+ base?: string
48
+ avatar?: UserAvatarClassNames
49
+ user?: UserViewClassNames
50
+ organization?: OrganizationViewClassNames
51
+ skeleton?: string
52
+ }
53
+ content?: {
54
+ base?: string
55
+ user?: UserViewClassNames
56
+ organization?: OrganizationViewClassNames
57
+ avatar?: UserAvatarClassNames
58
+ menuItem?: string
59
+ separator?: string
60
+ }
61
+ }
62
+
63
+ export interface OrganizationSwitcherProps
64
+ extends Omit<ComponentProps<typeof Button>, "trigger"> {
65
+ classNames?: OrganizationSwitcherClassNames
66
+ align?: "center" | "start" | "end"
67
+ alignOffset?: number
68
+ side?: "top" | "right" | "bottom" | "left"
69
+ sideOffset?: number
70
+ trigger?: ReactNode
71
+ localization?: AuthLocalization
72
+ slug?: string
73
+ onSetActive?: (organization: Organization | null) => void
74
+ /**
75
+ * Hide the personal organization option from the switcher.
76
+ * When true, users can only switch between organizations and cannot access their personal account.
77
+ * If no organization is active, the first available organization will be automatically selected.
78
+ * @default false
79
+ */
80
+ hidePersonal?: boolean
81
+ }
82
+
83
+ /**
84
+ * Displays an interactive user button with dropdown menu functionality
85
+ *
86
+ * Renders a user interface element that can be displayed as either an icon or full button:
87
+ * - Shows a user avatar or placeholder when in icon mode
88
+ * - Displays user name and email with dropdown indicator in full mode
89
+ * - Provides dropdown menu with authentication options (sign in/out, settings, etc.)
90
+ * - Supports multi-session functionality for switching between accounts
91
+ * - Can be customized with additional links and styling options
92
+ */
93
+ export function OrganizationSwitcher({
94
+ className,
95
+ classNames,
96
+ align,
97
+ alignOffset,
98
+ side,
99
+ sideOffset,
100
+ trigger,
101
+ localization: localizationProp,
102
+ slug: slugProp,
103
+ size,
104
+ onSetActive,
105
+ hidePersonal,
106
+ ...props
107
+ }: OrganizationSwitcherProps) {
108
+ const {
109
+ authClient,
110
+ basePath,
111
+ hooks: { useSession, useListOrganizations },
112
+ localization: contextLocalization,
113
+ account: accountOptions,
114
+ organization: organizationOptions,
115
+ redirectTo,
116
+ navigate,
117
+ toast,
118
+ viewPaths,
119
+ Link
120
+ } = useContext(AuthUIContext)
121
+
122
+ const {
123
+ pathMode,
124
+ slug: contextSlug,
125
+ personalPath
126
+ } = organizationOptions || {}
127
+
128
+ const slug = slugProp || contextSlug
129
+
130
+ const localization = useMemo(
131
+ () => ({ ...contextLocalization, ...localizationProp }),
132
+ [contextLocalization, localizationProp]
133
+ )
134
+
135
+ const [activeOrganizationPending, setActiveOrganizationPending] =
136
+ useState(false)
137
+ const [isCreateOrgDialogOpen, setIsCreateOrgDialogOpen] = useState(false)
138
+ const [dropdownOpen, setDropdownOpen] = useState(false)
139
+
140
+ const { data: sessionData, isPending: sessionPending } = useSession()
141
+ const user = sessionData?.user
142
+
143
+ const { data: organizations, isPending: organizationsPending } =
144
+ useListOrganizations()
145
+
146
+ const {
147
+ data: activeOrganization,
148
+ isPending: organizationPending,
149
+ isRefetching: organizationRefetching,
150
+ refetch: organizationRefetch
151
+ } = useCurrentOrganization({ slug })
152
+
153
+ const isPending =
154
+ organizationsPending ||
155
+ sessionPending ||
156
+ activeOrganizationPending ||
157
+ organizationPending
158
+
159
+ // biome-ignore lint/correctness/useExhaustiveDependencies: ignore
160
+ useEffect(() => {
161
+ if (organizationRefetching) return
162
+
163
+ setActiveOrganizationPending(false)
164
+ }, [activeOrganization, organizationRefetching])
165
+
166
+ const switchOrganization = useCallback(
167
+ async (organization: Organization | null) => {
168
+ // Prevent switching to personal account when hidePersonal is true
169
+ if (hidePersonal && organization === null) {
170
+ return
171
+ }
172
+
173
+ if (pathMode === "slug") {
174
+ if (organization) {
175
+ navigate(
176
+ `${organizationOptions?.basePath}/${organization.slug}`
177
+ )
178
+ } else {
179
+ navigate(personalPath ?? redirectTo)
180
+ }
181
+
182
+ return
183
+ }
184
+
185
+ setActiveOrganizationPending(true)
186
+
187
+ try {
188
+ onSetActive?.(organization)
189
+
190
+ await authClient.organization.setActive({
191
+ organizationId: organization?.id || null,
192
+ fetchOptions: {
193
+ throw: true
194
+ }
195
+ })
196
+
197
+ organizationRefetch?.()
198
+ } catch (error) {
199
+ toast({
200
+ variant: "error",
201
+ message: getLocalizedError({ error, localization })
202
+ })
203
+
204
+ setActiveOrganizationPending(false)
205
+ }
206
+ },
207
+ [
208
+ authClient,
209
+ toast,
210
+ localization,
211
+ onSetActive,
212
+ hidePersonal,
213
+ pathMode,
214
+ personalPath,
215
+ organizationOptions?.basePath,
216
+ redirectTo,
217
+ navigate,
218
+ organizationRefetch
219
+ ]
220
+ )
221
+
222
+ // Auto-select first organization when hidePersonal is true
223
+ useEffect(() => {
224
+ if (
225
+ hidePersonal &&
226
+ !activeOrganization &&
227
+ !activeOrganizationPending &&
228
+ organizations &&
229
+ organizations.length > 0 &&
230
+ !sessionPending &&
231
+ !organizationPending &&
232
+ !slug
233
+ ) {
234
+ switchOrganization(organizations[0])
235
+ }
236
+ }, [
237
+ hidePersonal,
238
+ activeOrganization,
239
+ activeOrganizationPending,
240
+ organizations,
241
+ sessionPending,
242
+ organizationPending,
243
+ switchOrganization,
244
+ slug
245
+ ])
246
+
247
+ return (
248
+ <>
249
+ <DropdownMenu open={dropdownOpen} onOpenChange={setDropdownOpen}>
250
+ <DropdownMenuTrigger asChild>
251
+ {trigger ||
252
+ (size === "icon" ? (
253
+ <Button
254
+ size="icon"
255
+ className={cn(
256
+ "size-fit rounded-full",
257
+ className,
258
+ classNames?.trigger?.base
259
+ )}
260
+ variant="ghost"
261
+ type="button"
262
+ {...props}
263
+ >
264
+ {isPending ||
265
+ activeOrganization ||
266
+ !sessionData ||
267
+ (user as User)?.isAnonymous ||
268
+ hidePersonal ? (
269
+ <OrganizationLogo
270
+ key={activeOrganization?.logo}
271
+ className={cn(
272
+ className,
273
+ classNames?.base
274
+ )}
275
+ classNames={classNames?.trigger?.avatar}
276
+ isPending={isPending}
277
+ organization={activeOrganization}
278
+ aria-label={localization.ORGANIZATION}
279
+ localization={localization}
280
+ />
281
+ ) : (
282
+ <UserAvatar
283
+ key={user?.image}
284
+ className={cn(
285
+ className,
286
+ classNames?.base
287
+ )}
288
+ classNames={classNames?.trigger?.avatar}
289
+ user={user}
290
+ aria-label={localization.ACCOUNT}
291
+ localization={localization}
292
+ />
293
+ )}
294
+ </Button>
295
+ ) : (
296
+ <Button
297
+ className={cn(
298
+ "!p-2 h-fit",
299
+ className,
300
+ classNames?.trigger?.base
301
+ )}
302
+ size={size}
303
+ {...props}
304
+ >
305
+ {isPending ||
306
+ activeOrganization ||
307
+ !sessionData ||
308
+ (user as User)?.isAnonymous ||
309
+ hidePersonal ? (
310
+ <OrganizationCellView
311
+ classNames={
312
+ classNames?.trigger?.organization
313
+ }
314
+ isPending={isPending}
315
+ localization={localization}
316
+ organization={activeOrganization}
317
+ size={size}
318
+ />
319
+ ) : (
320
+ <PersonalAccountView
321
+ classNames={classNames?.trigger?.user}
322
+ localization={localization}
323
+ size={size}
324
+ user={user}
325
+ />
326
+ )}
327
+
328
+ <ChevronsUpDown className="ml-auto" />
329
+ </Button>
330
+ ))}
331
+ </DropdownMenuTrigger>
332
+
333
+ <DropdownMenuContent
334
+ className={cn(
335
+ "w-[--radix-dropdown-menu-trigger-width] min-w-56 max-w-64",
336
+ classNames?.content?.base
337
+ )}
338
+ align={align}
339
+ alignOffset={alignOffset}
340
+ side={side}
341
+ sideOffset={sideOffset}
342
+ onCloseAutoFocus={(e) => e.preventDefault()}
343
+ >
344
+ <div
345
+ className={cn(
346
+ "flex items-center justify-between gap-2 p-2",
347
+ classNames?.content?.menuItem
348
+ )}
349
+ >
350
+ {(user && !(user as User).isAnonymous) || isPending ? (
351
+ <>
352
+ {activeOrganizationPending ||
353
+ activeOrganization ||
354
+ hidePersonal ? (
355
+ <OrganizationCellView
356
+ classNames={
357
+ classNames?.content?.organization
358
+ }
359
+ isPending={
360
+ isPending ||
361
+ activeOrganizationPending
362
+ }
363
+ organization={activeOrganization}
364
+ localization={localization}
365
+ />
366
+ ) : (
367
+ <PersonalAccountView
368
+ classNames={classNames?.content?.user}
369
+ isPending={isPending}
370
+ localization={localization}
371
+ user={user}
372
+ />
373
+ )}
374
+
375
+ {!isPending && (
376
+ <Link
377
+ href={
378
+ activeOrganization
379
+ ? pathMode === "slug"
380
+ ? `${organizationOptions?.basePath}/${activeOrganization.slug}/${organizationOptions?.viewPaths.SETTINGS}`
381
+ : `${organizationOptions?.basePath}/${organizationOptions?.viewPaths.SETTINGS}`
382
+ : `${accountOptions?.basePath}/${accountOptions?.viewPaths.SETTINGS}`
383
+ }
384
+ >
385
+ <Button
386
+ size="icon"
387
+ variant="outline"
388
+ className="!size-8 ml-auto"
389
+ onClick={() =>
390
+ setDropdownOpen(false)
391
+ }
392
+ >
393
+ <SettingsIcon className="size-4" />
394
+ </Button>
395
+ </Link>
396
+ )}
397
+ </>
398
+ ) : (
399
+ <div className="-my-1 text-muted-foreground text-xs">
400
+ {localization.ORGANIZATION}
401
+ </div>
402
+ )}
403
+ </div>
404
+
405
+ <DropdownMenuSeparator
406
+ className={classNames?.content?.separator}
407
+ />
408
+
409
+ {activeOrganization &&
410
+ !hidePersonal &&
411
+ (pathMode === "slug" ? (
412
+ <Link href={personalPath ?? redirectTo}>
413
+ <DropdownMenuItem>
414
+ <PersonalAccountView
415
+ classNames={classNames?.content?.user}
416
+ isPending={isPending}
417
+ localization={localization}
418
+ user={user}
419
+ />
420
+ </DropdownMenuItem>
421
+ </Link>
422
+ ) : (
423
+ <DropdownMenuItem
424
+ onClick={() => switchOrganization(null)}
425
+ >
426
+ <PersonalAccountView
427
+ classNames={classNames?.content?.user}
428
+ isPending={isPending}
429
+ localization={localization}
430
+ user={user}
431
+ />
432
+ </DropdownMenuItem>
433
+ ))}
434
+
435
+ {organizations?.map(
436
+ (organization) =>
437
+ organization.id !== activeOrganization?.id &&
438
+ (pathMode === "slug" ? (
439
+ <Link
440
+ key={organization.id}
441
+ href={`${organizationOptions?.basePath}/${organization.slug}`}
442
+ >
443
+ <DropdownMenuItem>
444
+ <OrganizationCellView
445
+ classNames={
446
+ classNames?.content
447
+ ?.organization
448
+ }
449
+ isPending={isPending}
450
+ localization={localization}
451
+ organization={organization}
452
+ />
453
+ </DropdownMenuItem>
454
+ </Link>
455
+ ) : (
456
+ <DropdownMenuItem
457
+ key={organization.id}
458
+ onClick={() =>
459
+ switchOrganization(organization)
460
+ }
461
+ >
462
+ <OrganizationCellView
463
+ classNames={
464
+ classNames?.content?.organization
465
+ }
466
+ isPending={isPending}
467
+ localization={localization}
468
+ organization={organization}
469
+ />
470
+ </DropdownMenuItem>
471
+ ))
472
+ )}
473
+
474
+ {organizations &&
475
+ organizations.length > 0 &&
476
+ (!hidePersonal || organizations.length > 1) && (
477
+ <DropdownMenuSeparator
478
+ className={classNames?.content?.separator}
479
+ />
480
+ )}
481
+
482
+ {!isPending &&
483
+ sessionData &&
484
+ !(user as User).isAnonymous ? (
485
+ <DropdownMenuItem
486
+ className={cn(classNames?.content?.menuItem)}
487
+ onClick={() => setIsCreateOrgDialogOpen(true)}
488
+ >
489
+ <PlusCircleIcon />
490
+ {localization.CREATE_ORGANIZATION}
491
+ </DropdownMenuItem>
492
+ ) : (
493
+ <Link href={`${basePath}/${viewPaths.SIGN_IN}`}>
494
+ <DropdownMenuItem
495
+ className={cn(classNames?.content?.menuItem)}
496
+ >
497
+ <LogInIcon />
498
+ {localization.SIGN_IN}
499
+ </DropdownMenuItem>
500
+ </Link>
501
+ )}
502
+ </DropdownMenuContent>
503
+ </DropdownMenu>
504
+
505
+ <CreateOrganizationDialog
506
+ open={isCreateOrgDialogOpen}
507
+ onOpenChange={setIsCreateOrgDialogOpen}
508
+ localization={localization}
509
+ />
510
+ </>
511
+ )
512
+ }