better-auth-ui-svelte 0.6.2 → 0.7.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.
@@ -24,7 +24,8 @@
24
24
  pathname,
25
25
  view: viewProp,
26
26
  hideNav,
27
- organizationCustomActions
27
+ organizationCustomActions,
28
+ fetchOrganizations
28
29
  }: Props = $props();
29
30
 
30
31
  const { localization: contextLocalization, Link, adminBasePath } = getAuthUIConfig();
@@ -76,7 +77,7 @@
76
77
  {:else if view === 'USERS'}
77
78
  <UsersAdminTable syncWithUrl={true} />
78
79
  {:else if view === 'ORGANIZATIONS'}
79
- <OrganizationsAdminTable syncWithUrl={true} customActions={organizationCustomActions} />
80
+ <OrganizationsAdminTable syncWithUrl={true} customActions={organizationCustomActions} {fetchOrganizations} />
80
81
  {/if}
81
82
  </div>
82
83
  </div>
@@ -17,7 +17,7 @@
17
17
  SortingState
18
18
  } from '@tanstack/table-core';
19
19
  import { getCoreRowModel, getPaginationRowModel, getSortedRowModel } from '@tanstack/table-core';
20
- import type { Organization, AdminTableAction } from '../../types/admin.js';
20
+ import type { Organization, AdminTableAction, FetchOrganizationsResponse } from '../../types/admin.js';
21
21
  import { createRawSnippet } from 'svelte';
22
22
  import {
23
23
  getAuthClient,
@@ -46,13 +46,24 @@
46
46
  initialPageSize = 10,
47
47
  syncWithUrl = false,
48
48
  onViewMembers,
49
- customActions = []
49
+ customActions = [],
50
+ fetchOrganizations: fetchOrganizationsProp
50
51
  }: {
51
52
  initialPageSize?: number;
52
53
  syncWithUrl?: boolean;
53
54
  onViewMembers?: (organizationId: string) => void | Promise<void>;
54
55
  /** Custom actions to add to the dropdown menu */
55
56
  customActions?: AdminTableAction<Organization>[];
57
+ /**
58
+ * Custom function to fetch organizations. Use this to provide admin-level
59
+ * access to all organizations (e.g., via a custom API route that queries
60
+ * the database directly), since Better Auth's `organization.list()` only
61
+ * returns organizations the current user belongs to.
62
+ */
63
+ fetchOrganizations?: (params: {
64
+ limit: number;
65
+ offset: number;
66
+ }) => Promise<FetchOrganizationsResponse>;
56
67
  } = $props();
57
68
 
58
69
  // Get Better Auth client and config
@@ -95,34 +106,51 @@
95
106
  }
96
107
  });
97
108
 
98
- // Fetch organizations from Better Auth
109
+ // Fetch organizations
99
110
  async function fetchOrganizations() {
100
111
  isLoading = true;
101
112
  try {
102
- const { data: response, error } = await authClient.organization.list({});
103
-
104
- if (error) {
105
- toast.error(error.message ?? 'Failed to fetch organizations');
106
- return;
107
- }
113
+ if (fetchOrganizationsProp) {
114
+ // Use custom fetch function (recommended for admin access to all orgs)
115
+ const response = await fetchOrganizationsProp({
116
+ limit: pagination.pageSize,
117
+ offset: pagination.pageIndex * pagination.pageSize
118
+ });
108
119
 
109
- if (response) {
110
- // Better Auth organization.list doesn't support pagination params yet
111
- // so we'll do client-side pagination for now
112
- const allOrgs = (response as any[]).map((org) => ({
120
+ data = response.organizations.map((org) => ({
113
121
  id: org.id,
114
122
  name: org.name,
115
123
  slug: org.slug,
116
124
  logo: org.logo,
117
- createdAt: org.createdAt ? new Date(org.createdAt) : null,
125
+ createdAt: org.createdAt ? new Date(org.createdAt as string) : null,
118
126
  metadata: org.metadata
119
127
  }));
128
+ pageCount = Math.ceil(response.total / pagination.pageSize);
129
+ } else {
130
+ // Fallback: Better Auth organization.list only returns orgs the user belongs to
131
+ const { data: response, error } = await authClient.organization.list({});
120
132
 
121
- // Client-side pagination
122
- const start = pagination.pageIndex * pagination.pageSize;
123
- const end = start + pagination.pageSize;
124
- data = allOrgs.slice(start, end);
125
- pageCount = Math.ceil(allOrgs.length / pagination.pageSize);
133
+ if (error) {
134
+ toast.error(error.message ?? 'Failed to fetch organizations');
135
+ return;
136
+ }
137
+
138
+ if (response) {
139
+ const allOrgs = (response as any[]).map((org) => ({
140
+ id: org.id,
141
+ name: org.name,
142
+ slug: org.slug,
143
+ logo: org.logo,
144
+ createdAt: org.createdAt ? new Date(org.createdAt) : null,
145
+ metadata: org.metadata
146
+ }));
147
+
148
+ // Client-side pagination (organization.list doesn't support pagination)
149
+ const start = pagination.pageIndex * pagination.pageSize;
150
+ const end = start + pagination.pageSize;
151
+ data = allOrgs.slice(start, end);
152
+ pageCount = Math.ceil(allOrgs.length / pagination.pageSize);
153
+ }
126
154
  }
127
155
  } catch (err) {
128
156
  toast.error('Failed to fetch organizations');
@@ -1,10 +1,20 @@
1
- import type { Organization, AdminTableAction } from '../../types/admin.js';
1
+ import type { Organization, AdminTableAction, FetchOrganizationsResponse } from '../../types/admin.js';
2
2
  type $$ComponentProps = {
3
3
  initialPageSize?: number;
4
4
  syncWithUrl?: boolean;
5
5
  onViewMembers?: (organizationId: string) => void | Promise<void>;
6
6
  /** Custom actions to add to the dropdown menu */
7
7
  customActions?: AdminTableAction<Organization>[];
8
+ /**
9
+ * Custom function to fetch organizations. Use this to provide admin-level
10
+ * access to all organizations (e.g., via a custom API route that queries
11
+ * the database directly), since Better Auth's `organization.list()` only
12
+ * returns organizations the current user belongs to.
13
+ */
14
+ fetchOrganizations?: (params: {
15
+ limit: number;
16
+ offset: number;
17
+ }) => Promise<FetchOrganizationsResponse>;
8
18
  };
9
19
  declare const OrganizationsAdminTable: import("svelte").Component<$$ComponentProps, {}, "">;
10
20
  type OrganizationsAdminTable = ReturnType<typeof OrganizationsAdminTable>;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Organization } from 'better-auth/plugins/organization';
3
+ import { untrack } from 'svelte';
3
4
  import { createForm } from '@tanstack/svelte-form';
4
5
  import { getAuthUIConfig } from '../../context/auth-ui-config.svelte.js';
5
6
  import { cn, getLocalizedError } from '../../utils/utils.js';
@@ -92,9 +93,12 @@
92
93
  const isSubmitting = $derived(form.state.isSubmitting);
93
94
 
94
95
  // Update form values when organization changes
96
+ // Use untrack to prevent infinite loop from form state updates
95
97
  $effect(() => {
96
98
  if (organization?.name !== undefined) {
97
- form.setFieldValue('name', organization.name || '');
99
+ untrack(() => {
100
+ form.setFieldValue('name', organization.name || '');
101
+ });
98
102
  }
99
103
  });
100
104
  </script>
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { untrack } from 'svelte';
2
3
  import { createForm } from '@tanstack/svelte-form';
3
4
  import * as z from 'zod';
4
5
  import { getAuthUIConfig } from '../../../context/auth-ui-config.svelte.js';
@@ -113,9 +114,12 @@
113
114
  }));
114
115
 
115
116
  // Update form values when session data changes
117
+ // Use untrack to prevent infinite loop from form state updates
116
118
  $effect(() => {
117
119
  if (sessionData?.user.email !== undefined) {
118
- form.setFieldValue('email', sessionData.user.email || '');
120
+ untrack(() => {
121
+ form.setFieldValue('email', sessionData.user.email || '');
122
+ });
119
123
  }
120
124
  });
121
125
  </script>
package/dist/index.d.ts CHANGED
@@ -98,7 +98,7 @@ export * from './utils/get-paths.js';
98
98
  export { getAuthUIConfig, getAuthClient, getLocalization } from './context/auth-ui-config.svelte.js';
99
99
  export { authLocalization } from './localization/auth-localization.js';
100
100
  export type { AuthUIConfig, User, Session, AuthLocalization } from './types/index.js';
101
- export type { UserWithRole, Organization, OrganizationMember, OrganizationWithMembers, OrganizationInvitation, AdminTableAction, UsersAdminTableProps, OrganizationsAdminTableProps, OrganizationMembersDetailProps, BanUserDialogProps, UpdateRoleDialogProps, ImpersonateUserDialogProps, AdminViewProps } from './types/admin.js';
101
+ export type { UserWithRole, Organization, OrganizationMember, OrganizationWithMembers, OrganizationInvitation, AdminTableAction, UsersAdminTableProps, OrganizationsAdminTableProps, FetchOrganizationsResponse, OrganizationMembersDetailProps, BanUserDialogProps, UpdateRoleDialogProps, ImpersonateUserDialogProps, AdminViewProps } from './types/admin.js';
102
102
  import AuthFormComponent from './components/auth/auth-form.svelte';
103
103
  import UserButtonComponent from './components/user-button.svelte';
104
104
  import UserViewComponent from './components/user-view.svelte';
@@ -137,3 +137,4 @@ export type UserInvitationsCardProps = ComponentProps<typeof UserInvitationsCard
137
137
  export type SettingsCardClassNames = ComponentProps<typeof SettingsCardComponent>;
138
138
  export * from './types/auth-hooks.js';
139
139
  export * from './types/auth-mutators.js';
140
+ export { fromStore } from './utils/store-to-rune.svelte.js';
package/dist/index.js CHANGED
@@ -136,3 +136,4 @@ import UserInvitationsCardComponent from './components/organization/user-invitat
136
136
  import SettingsCardComponent from './components/settings/shared/settings-card.svelte';
137
137
  export * from './types/auth-hooks.js';
138
138
  export * from './types/auth-mutators.js';
139
+ export { fromStore } from './utils/store-to-rune.svelte.js';
@@ -107,6 +107,13 @@ export interface UsersAdminTableProps {
107
107
  onResetPassword?: (userId: string) => void | Promise<void>;
108
108
  onRevokeSessions?: (userId: string) => void | Promise<void>;
109
109
  }
110
+ /**
111
+ * Response type for the fetchOrganizations callback
112
+ */
113
+ export interface FetchOrganizationsResponse {
114
+ organizations: Organization[];
115
+ total: number;
116
+ }
110
117
  /**
111
118
  * Props for organizations admin table
112
119
  */
@@ -122,6 +129,16 @@ export interface OrganizationsAdminTableProps {
122
129
  onDeleteOrganization?: (organizationId: string) => void | Promise<void>;
123
130
  /** Custom actions to add to the dropdown menu */
124
131
  customActions?: AdminTableAction<Organization>[];
132
+ /**
133
+ * Custom function to fetch organizations. Use this to provide admin-level
134
+ * access to all organizations (e.g., via a custom API route that queries
135
+ * the database directly), since Better Auth's `organization.list()` only
136
+ * returns organizations the current user belongs to.
137
+ */
138
+ fetchOrganizations?: (params: {
139
+ limit: number;
140
+ offset: number;
141
+ }) => Promise<FetchOrganizationsResponse>;
125
142
  }
126
143
  /**
127
144
  * Props for organization members detail dialog
@@ -184,4 +201,14 @@ export interface AdminViewProps {
184
201
  hideNav?: boolean;
185
202
  /** Custom actions for organizations table */
186
203
  organizationCustomActions?: AdminTableAction<Organization>[];
204
+ /**
205
+ * Custom function to fetch organizations. Use this to provide admin-level
206
+ * access to all organizations (e.g., via a custom API route that queries
207
+ * the database directly), since Better Auth's `organization.list()` only
208
+ * returns organizations the current user belongs to.
209
+ */
210
+ fetchOrganizations?: (params: {
211
+ limit: number;
212
+ offset: number;
213
+ }) => Promise<FetchOrganizationsResponse>;
187
214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-auth-ui-svelte",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Pre-built authentication UI components for Better Auth in Svelte 5",
5
5
  "files": [
6
6
  "dist",