@nimbleflux/fluxbase-sdk-react 2026.3.6 → 2026.3.7-rc.2

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.
@@ -0,0 +1,311 @@
1
+ /**
2
+ * React hooks for multi-tenant management
3
+ *
4
+ * @module use-tenant
5
+ */
6
+
7
+ import { useState, useEffect, useCallback } from "react";
8
+ import { useFluxbaseClient } from "./context";
9
+ import type {
10
+ Tenant,
11
+ TenantWithRole,
12
+ CreateTenantOptions,
13
+ UpdateTenantOptions,
14
+ } from "@nimbleflux/fluxbase-sdk";
15
+
16
+ export interface UseTenantsOptions {
17
+ /**
18
+ * Whether to automatically fetch tenants on mount
19
+ * @default true
20
+ */
21
+ autoFetch?: boolean;
22
+ }
23
+
24
+ export interface UseTenantsReturn {
25
+ /**
26
+ * Array of tenants the user has access to
27
+ */
28
+ tenants: TenantWithRole[];
29
+
30
+ /**
31
+ * Whether tenants are being fetched
32
+ */
33
+ isLoading: boolean;
34
+
35
+ /**
36
+ * Any error that occurred
37
+ */
38
+ error: Error | null;
39
+
40
+ /**
41
+ * Refetch tenants
42
+ */
43
+ refetch: () => Promise<void>;
44
+
45
+ /**
46
+ * Create a new tenant (instance admin only)
47
+ */
48
+ createTenant: (options: CreateTenantOptions) => Promise<Tenant>;
49
+
50
+ /**
51
+ * Update a tenant (tenant admin only)
52
+ */
53
+ updateTenant: (id: string, options: UpdateTenantOptions) => Promise<Tenant>;
54
+
55
+ /**
56
+ * Delete a tenant (instance admin only)
57
+ */
58
+ deleteTenant: (id: string) => Promise<void>;
59
+
60
+ /**
61
+ * Set the current tenant context
62
+ */
63
+ setCurrentTenant: (tenantId: string | undefined) => void;
64
+
65
+ /**
66
+ * Get the current tenant ID
67
+ */
68
+ currentTenantId: string | undefined;
69
+ }
70
+
71
+ /**
72
+ * Hook for managing tenants
73
+ *
74
+ * Provides tenant list and management functions for multi-tenant applications.
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * function TenantManager() {
79
+ * const { tenants, isLoading, setCurrentTenant, currentTenantId } = useTenants()
80
+ *
81
+ * if (isLoading) return <div>Loading...</div>
82
+ *
83
+ * return (
84
+ * <select
85
+ * value={currentTenantId || ''}
86
+ * onChange={(e) => setCurrentTenant(e.target.value || undefined)}
87
+ * >
88
+ * {tenants.map(t => (
89
+ * <option key={t.id} value={t.id}>
90
+ * {t.name} ({t.my_role})
91
+ * </option>
92
+ * ))}
93
+ * </select>
94
+ * )
95
+ * }
96
+ * ```
97
+ */
98
+ export function useTenants(options: UseTenantsOptions = {}): UseTenantsReturn {
99
+ const { autoFetch = true } = options;
100
+ const client = useFluxbaseClient();
101
+
102
+ const [tenants, setTenants] = useState<TenantWithRole[]>([]);
103
+ const [isLoading, setIsLoading] = useState(autoFetch);
104
+ const [error, setError] = useState<Error | null>(null);
105
+ const [currentTenantId, setCurrentTenantId] = useState<string | undefined>(
106
+ client.getTenantId(),
107
+ );
108
+
109
+ const fetchTenants = useCallback(async () => {
110
+ try {
111
+ setIsLoading(true);
112
+ setError(null);
113
+ const { data, error: fetchError } = await client.tenant.listMine();
114
+ if (fetchError) {
115
+ setError(fetchError);
116
+ } else {
117
+ setTenants(data || []);
118
+ }
119
+ } catch (err) {
120
+ setError(err as Error);
121
+ } finally {
122
+ setIsLoading(false);
123
+ }
124
+ }, [client]);
125
+
126
+ const createTenant = useCallback(
127
+ async (opts: CreateTenantOptions): Promise<Tenant> => {
128
+ const { data, error: createError } = await client.tenant.create(opts);
129
+ if (createError) throw createError;
130
+ await fetchTenants();
131
+ return data!;
132
+ },
133
+ [client, fetchTenants],
134
+ );
135
+
136
+ const updateTenant = useCallback(
137
+ async (id: string, opts: UpdateTenantOptions): Promise<Tenant> => {
138
+ const { data, error: updateError } = await client.tenant.update(id, opts);
139
+ if (updateError) throw updateError;
140
+ await fetchTenants();
141
+ return data!;
142
+ },
143
+ [client, fetchTenants],
144
+ );
145
+
146
+ const deleteTenant = useCallback(
147
+ async (id: string): Promise<void> => {
148
+ const { error: deleteError } = await client.tenant.delete(id);
149
+ if (deleteError) throw deleteError;
150
+ await fetchTenants();
151
+ },
152
+ [client, fetchTenants],
153
+ );
154
+
155
+ const setCurrentTenant = useCallback(
156
+ (tenantId: string | undefined) => {
157
+ client.setTenant(tenantId);
158
+ setCurrentTenantId(tenantId);
159
+ },
160
+ [client],
161
+ );
162
+
163
+ useEffect(() => {
164
+ if (autoFetch) {
165
+ fetchTenants();
166
+ }
167
+ }, [autoFetch, fetchTenants]);
168
+
169
+ return {
170
+ tenants,
171
+ isLoading,
172
+ error,
173
+ refetch: fetchTenants,
174
+ createTenant,
175
+ updateTenant,
176
+ deleteTenant,
177
+ setCurrentTenant,
178
+ currentTenantId,
179
+ };
180
+ }
181
+
182
+ export interface UseTenantOptions {
183
+ /**
184
+ * Tenant ID to fetch
185
+ */
186
+ tenantId: string;
187
+
188
+ /**
189
+ * Whether to automatically fetch tenant on mount
190
+ * @default true
191
+ */
192
+ autoFetch?: boolean;
193
+ }
194
+
195
+ export interface UseTenantReturn {
196
+ /**
197
+ * Tenant data
198
+ */
199
+ tenant: Tenant | null;
200
+
201
+ /**
202
+ * Whether tenant is being fetched
203
+ */
204
+ isLoading: boolean;
205
+
206
+ /**
207
+ * Any error that occurred
208
+ */
209
+ error: Error | null;
210
+
211
+ /**
212
+ * Refetch tenant
213
+ */
214
+ refetch: () => Promise<void>;
215
+
216
+ /**
217
+ * Update the tenant
218
+ */
219
+ update: (options: UpdateTenantOptions) => Promise<Tenant>;
220
+
221
+ /**
222
+ * Delete the tenant
223
+ */
224
+ remove: () => Promise<void>;
225
+ }
226
+
227
+ /**
228
+ * Hook for managing a single tenant
229
+ *
230
+ * @example
231
+ * ```tsx
232
+ * function TenantDetails({ tenantId }: { tenantId: string }) {
233
+ * const { tenant, isLoading, update } = useTenant({ tenantId })
234
+ *
235
+ * if (isLoading) return <div>Loading...</div>
236
+ * if (!tenant) return <div>Tenant not found</div>
237
+ *
238
+ * return (
239
+ * <div>
240
+ * <h1>{tenant.name}</h1>
241
+ * <button onClick={() => update({ name: 'New Name' })}>
242
+ * Rename
243
+ * </button>
244
+ * </div>
245
+ * )
246
+ * }
247
+ * ```
248
+ */
249
+ export function useTenant(options: UseTenantOptions): UseTenantReturn {
250
+ const { tenantId, autoFetch = true } = options;
251
+ const client = useFluxbaseClient();
252
+
253
+ const [tenant, setTenant] = useState<Tenant | null>(null);
254
+ const [isLoading, setIsLoading] = useState(autoFetch);
255
+ const [error, setError] = useState<Error | null>(null);
256
+
257
+ const fetchTenant = useCallback(async () => {
258
+ if (!tenantId) return;
259
+
260
+ try {
261
+ setIsLoading(true);
262
+ setError(null);
263
+ const { data, error: fetchError } = await client.tenant.get(tenantId);
264
+ if (fetchError) {
265
+ setError(fetchError);
266
+ setTenant(null);
267
+ } else {
268
+ setTenant(data);
269
+ }
270
+ } catch (err) {
271
+ setError(err as Error);
272
+ setTenant(null);
273
+ } finally {
274
+ setIsLoading(false);
275
+ }
276
+ }, [client, tenantId]);
277
+
278
+ const update = useCallback(
279
+ async (opts: UpdateTenantOptions): Promise<Tenant> => {
280
+ const { data, error: updateError } = await client.tenant.update(
281
+ tenantId,
282
+ opts,
283
+ );
284
+ if (updateError) throw updateError;
285
+ setTenant(data);
286
+ return data!;
287
+ },
288
+ [client, tenantId],
289
+ );
290
+
291
+ const remove = useCallback(async (): Promise<void> => {
292
+ const { error: deleteError } = await client.tenant.delete(tenantId);
293
+ if (deleteError) throw deleteError;
294
+ setTenant(null);
295
+ }, [client, tenantId]);
296
+
297
+ useEffect(() => {
298
+ if (autoFetch && tenantId) {
299
+ fetchTenant();
300
+ }
301
+ }, [autoFetch, fetchTenant, tenantId]);
302
+
303
+ return {
304
+ tenant,
305
+ isLoading,
306
+ error,
307
+ refetch: fetchTenant,
308
+ update,
309
+ remove,
310
+ };
311
+ }
package/src/use-users.ts CHANGED
@@ -1,66 +1,66 @@
1
- import { useState, useEffect, useCallback } from 'react'
2
- import { useFluxbaseClient } from './context'
3
- import type { EnrichedUser, ListUsersOptions } from '@fluxbase/sdk'
1
+ import { useState, useEffect, useCallback } from "react";
2
+ import { useFluxbaseClient } from "./context";
3
+ import type { EnrichedUser, ListUsersOptions } from "@nimbleflux/fluxbase-sdk";
4
4
 
5
5
  export interface UseUsersOptions extends ListUsersOptions {
6
6
  /**
7
7
  * Whether to automatically fetch users on mount
8
8
  * @default true
9
9
  */
10
- autoFetch?: boolean
10
+ autoFetch?: boolean;
11
11
 
12
12
  /**
13
13
  * Refetch interval in milliseconds (0 to disable)
14
14
  * @default 0
15
15
  */
16
- refetchInterval?: number
16
+ refetchInterval?: number;
17
17
  }
18
18
 
19
19
  export interface UseUsersReturn {
20
20
  /**
21
21
  * Array of users
22
22
  */
23
- users: EnrichedUser[]
23
+ users: EnrichedUser[];
24
24
 
25
25
  /**
26
26
  * Total number of users (for pagination)
27
27
  */
28
- total: number
28
+ total: number;
29
29
 
30
30
  /**
31
31
  * Whether users are being fetched
32
32
  */
33
- isLoading: boolean
33
+ isLoading: boolean;
34
34
 
35
35
  /**
36
36
  * Any error that occurred
37
37
  */
38
- error: Error | null
38
+ error: Error | null;
39
39
 
40
40
  /**
41
41
  * Refetch users
42
42
  */
43
- refetch: () => Promise<void>
43
+ refetch: () => Promise<void>;
44
44
 
45
45
  /**
46
46
  * Invite a new user
47
47
  */
48
- inviteUser: (email: string, role: 'user' | 'admin') => Promise<void>
48
+ inviteUser: (email: string, role: "user" | "admin") => Promise<void>;
49
49
 
50
50
  /**
51
51
  * Update user role
52
52
  */
53
- updateUserRole: (userId: string, role: 'user' | 'admin') => Promise<void>
53
+ updateUserRole: (userId: string, role: "user" | "admin") => Promise<void>;
54
54
 
55
55
  /**
56
56
  * Delete a user
57
57
  */
58
- deleteUser: (userId: string) => Promise<void>
58
+ deleteUser: (userId: string) => Promise<void>;
59
59
 
60
60
  /**
61
61
  * Reset user password
62
62
  */
63
- resetPassword: (userId: string) => Promise<{ message: string }>
63
+ resetPassword: (userId: string) => Promise<{ message: string }>;
64
64
  }
65
65
 
66
66
  /**
@@ -94,95 +94,96 @@ export interface UseUsersReturn {
94
94
  * ```
95
95
  */
96
96
  export function useUsers(options: UseUsersOptions = {}): UseUsersReturn {
97
- const { autoFetch = true, refetchInterval = 0, ...listOptions } = options
98
- const client = useFluxbaseClient()
97
+ const { autoFetch = true, refetchInterval = 0, ...listOptions } = options;
98
+ const client = useFluxbaseClient();
99
99
 
100
- const [users, setUsers] = useState<EnrichedUser[]>([])
101
- const [total, setTotal] = useState(0)
102
- const [isLoading, setIsLoading] = useState(autoFetch)
103
- const [error, setError] = useState<Error | null>(null)
100
+ const [users, setUsers] = useState<EnrichedUser[]>([]);
101
+ const [total, setTotal] = useState(0);
102
+ const [isLoading, setIsLoading] = useState(autoFetch);
103
+ const [error, setError] = useState<Error | null>(null);
104
104
 
105
105
  /**
106
106
  * Fetch users from API
107
107
  */
108
108
  const fetchUsers = useCallback(async () => {
109
109
  try {
110
- setIsLoading(true)
111
- setError(null)
112
- const { data, error: apiError } = await client.admin.listUsers(listOptions)
110
+ setIsLoading(true);
111
+ setError(null);
112
+ const { data, error: apiError } =
113
+ await client.admin.listUsers(listOptions);
113
114
  if (apiError) {
114
- throw apiError
115
+ throw apiError;
115
116
  }
116
- setUsers(data!.users)
117
- setTotal(data!.total)
117
+ setUsers(data!.users);
118
+ setTotal(data!.total);
118
119
  } catch (err) {
119
- setError(err as Error)
120
+ setError(err as Error);
120
121
  } finally {
121
- setIsLoading(false)
122
+ setIsLoading(false);
122
123
  }
123
- }, [client, JSON.stringify(listOptions)])
124
+ }, [client, JSON.stringify(listOptions)]);
124
125
 
125
126
  /**
126
127
  * Invite a new user
127
128
  */
128
129
  const inviteUser = useCallback(
129
- async (email: string, role: 'user' | 'admin'): Promise<void> => {
130
- await client.admin.inviteUser({ email, role })
131
- await fetchUsers() // Refresh list
130
+ async (email: string, role: "user" | "admin"): Promise<void> => {
131
+ await client.admin.inviteUser({ email, role });
132
+ await fetchUsers(); // Refresh list
132
133
  },
133
- [client, fetchUsers]
134
- )
134
+ [client, fetchUsers],
135
+ );
135
136
 
136
137
  /**
137
138
  * Update user role
138
139
  */
139
140
  const updateUserRole = useCallback(
140
- async (userId: string, role: 'user' | 'admin'): Promise<void> => {
141
- await client.admin.updateUserRole(userId, role)
142
- await fetchUsers() // Refresh list
141
+ async (userId: string, role: "user" | "admin"): Promise<void> => {
142
+ await client.admin.updateUserRole(userId, role);
143
+ await fetchUsers(); // Refresh list
143
144
  },
144
- [client, fetchUsers]
145
- )
145
+ [client, fetchUsers],
146
+ );
146
147
 
147
148
  /**
148
149
  * Delete a user
149
150
  */
150
151
  const deleteUser = useCallback(
151
152
  async (userId: string): Promise<void> => {
152
- await client.admin.deleteUser(userId)
153
- await fetchUsers() // Refresh list
153
+ await client.admin.deleteUser(userId);
154
+ await fetchUsers(); // Refresh list
154
155
  },
155
- [client, fetchUsers]
156
- )
156
+ [client, fetchUsers],
157
+ );
157
158
 
158
159
  /**
159
160
  * Reset user password
160
161
  */
161
162
  const resetPassword = useCallback(
162
163
  async (userId: string): Promise<{ message: string }> => {
163
- const { data, error } = await client.admin.resetUserPassword(userId)
164
+ const { data, error } = await client.admin.resetUserPassword(userId);
164
165
  if (error) {
165
- throw error
166
+ throw error;
166
167
  }
167
- return data!
168
+ return data!;
168
169
  },
169
- [client]
170
- )
170
+ [client],
171
+ );
171
172
 
172
173
  // Auto-fetch on mount
173
174
  useEffect(() => {
174
175
  if (autoFetch) {
175
- fetchUsers()
176
+ fetchUsers();
176
177
  }
177
- }, [autoFetch, fetchUsers])
178
+ }, [autoFetch, fetchUsers]);
178
179
 
179
180
  // Set up refetch interval
180
181
  useEffect(() => {
181
182
  if (refetchInterval > 0) {
182
- const interval = setInterval(fetchUsers, refetchInterval)
183
- return () => clearInterval(interval)
183
+ const interval = setInterval(fetchUsers, refetchInterval);
184
+ return () => clearInterval(interval);
184
185
  }
185
- }, [refetchInterval, fetchUsers])
186
+ }, [refetchInterval, fetchUsers]);
186
187
 
187
188
  return {
188
189
  users,
@@ -193,6 +194,6 @@ export function useUsers(options: UseUsersOptions = {}): UseUsersReturn {
193
194
  inviteUser,
194
195
  updateUserRole,
195
196
  deleteUser,
196
- resetPassword
197
- }
197
+ resetPassword,
198
+ };
198
199
  }
package/tsconfig.json CHANGED
@@ -15,6 +15,7 @@
15
15
  "isolatedModules": true,
16
16
  "esModuleInterop": true,
17
17
  "forceConsistentCasingInFileNames": true,
18
+ "ignoreDeprecations": "6.0",
18
19
  "strict": true,
19
20
  "skipLibCheck": true,
20
21
  "noEmit": true,
package/tsup.config.ts CHANGED
@@ -7,5 +7,5 @@ export default defineConfig({
7
7
  splitting: false,
8
8
  sourcemap: true,
9
9
  clean: true,
10
- external: ['react', '@tanstack/react-query', '@fluxbase/sdk'],
10
+ external: ['react', '@tanstack/react-query', '@nimbleflux/fluxbase-sdk'],
11
11
  })