@abpjs/tenant-management 0.7.6
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.
- package/LICENSE +165 -0
- package/README.md +549 -0
- package/dist/TenantManagementModal/TenantManagementModal.d.ts +60 -0
- package/dist/TenantManagementModal/index.d.ts +2 -0
- package/dist/components/TenantManagementModal/TenantManagementModal.d.ts +60 -0
- package/dist/components/TenantManagementModal/index.d.ts +2 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useTenantManagement.d.ts +80 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +553 -0
- package/dist/index.mjs +530 -0
- package/dist/models/index.d.ts +47 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/tenant-management.service.d.ts +57 -0
- package/package.json +58 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
// src/services/tenant-management.service.ts
|
|
2
|
+
var TenantManagementService = class {
|
|
3
|
+
constructor(rest) {
|
|
4
|
+
this.rest = rest;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Get all tenants (paginated)
|
|
8
|
+
* @returns Promise with paginated tenant response
|
|
9
|
+
*/
|
|
10
|
+
getAll() {
|
|
11
|
+
return this.rest.request({
|
|
12
|
+
method: "GET",
|
|
13
|
+
url: "/api/multi-tenancy/tenants"
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Get a tenant by ID
|
|
18
|
+
* @param id Tenant ID
|
|
19
|
+
* @returns Promise with tenant item
|
|
20
|
+
*/
|
|
21
|
+
getById(id) {
|
|
22
|
+
return this.rest.request({
|
|
23
|
+
method: "GET",
|
|
24
|
+
url: `/api/multi-tenancy/tenants/${id}`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create a new tenant
|
|
29
|
+
* @param body Tenant creation request
|
|
30
|
+
* @returns Promise with created tenant item
|
|
31
|
+
*/
|
|
32
|
+
create(body) {
|
|
33
|
+
return this.rest.request({
|
|
34
|
+
method: "POST",
|
|
35
|
+
url: "/api/multi-tenancy/tenants",
|
|
36
|
+
body
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Update an existing tenant
|
|
41
|
+
* @param body Tenant update request (includes id)
|
|
42
|
+
* @returns Promise with updated tenant item
|
|
43
|
+
*/
|
|
44
|
+
update(body) {
|
|
45
|
+
const { id, ...data } = body;
|
|
46
|
+
return this.rest.request({
|
|
47
|
+
method: "PUT",
|
|
48
|
+
url: `/api/multi-tenancy/tenants/${id}`,
|
|
49
|
+
body: data
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Delete a tenant
|
|
54
|
+
* @param id Tenant ID
|
|
55
|
+
* @returns Promise that resolves when deletion completes
|
|
56
|
+
*/
|
|
57
|
+
delete(id) {
|
|
58
|
+
return this.rest.request({
|
|
59
|
+
method: "DELETE",
|
|
60
|
+
url: `/api/multi-tenancy/tenants/${id}`
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get default connection string for a tenant
|
|
65
|
+
* @param id Tenant ID
|
|
66
|
+
* @returns Promise with connection string (empty string if using shared database)
|
|
67
|
+
*/
|
|
68
|
+
getDefaultConnectionString(id) {
|
|
69
|
+
return this.rest.request({
|
|
70
|
+
method: "GET",
|
|
71
|
+
url: `/api/multi-tenancy/tenants/${id}/default-connection-string`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Update default connection string for a tenant
|
|
76
|
+
* @param payload Request with tenant ID and connection string
|
|
77
|
+
* @returns Promise that resolves when update completes
|
|
78
|
+
*/
|
|
79
|
+
updateDefaultConnectionString(payload) {
|
|
80
|
+
const { id, defaultConnectionString } = payload;
|
|
81
|
+
return this.rest.request({
|
|
82
|
+
method: "PUT",
|
|
83
|
+
url: `/api/multi-tenancy/tenants/${id}/default-connection-string`,
|
|
84
|
+
params: { defaultConnectionString }
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Delete default connection string for a tenant (use shared database)
|
|
89
|
+
* @param id Tenant ID
|
|
90
|
+
* @returns Promise that resolves when deletion completes
|
|
91
|
+
*/
|
|
92
|
+
deleteDefaultConnectionString(id) {
|
|
93
|
+
return this.rest.request({
|
|
94
|
+
method: "DELETE",
|
|
95
|
+
url: `/api/multi-tenancy/tenants/${id}/default-connection-string`
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/hooks/useTenantManagement.ts
|
|
101
|
+
import { useState, useCallback, useMemo } from "react";
|
|
102
|
+
import { useRestService } from "@abpjs/core";
|
|
103
|
+
function useTenantManagement() {
|
|
104
|
+
const restService = useRestService();
|
|
105
|
+
const service = useMemo(() => new TenantManagementService(restService), [restService]);
|
|
106
|
+
const [tenants, setTenants] = useState([]);
|
|
107
|
+
const [selectedTenant, setSelectedTenant] = useState(null);
|
|
108
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
109
|
+
const [error, setError] = useState(null);
|
|
110
|
+
const [defaultConnectionString, setDefaultConnectionString] = useState("");
|
|
111
|
+
const [useSharedDatabase, setUseSharedDatabase] = useState(true);
|
|
112
|
+
const fetchTenants = useCallback(async () => {
|
|
113
|
+
setIsLoading(true);
|
|
114
|
+
setError(null);
|
|
115
|
+
try {
|
|
116
|
+
const response = await service.getAll();
|
|
117
|
+
setTenants(response.items);
|
|
118
|
+
setIsLoading(false);
|
|
119
|
+
return { success: true };
|
|
120
|
+
} catch (err) {
|
|
121
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to fetch tenants";
|
|
122
|
+
setError(errorMessage);
|
|
123
|
+
setIsLoading(false);
|
|
124
|
+
return { success: false, error: errorMessage };
|
|
125
|
+
}
|
|
126
|
+
}, [service]);
|
|
127
|
+
const fetchTenantById = useCallback(
|
|
128
|
+
async (id) => {
|
|
129
|
+
setIsLoading(true);
|
|
130
|
+
setError(null);
|
|
131
|
+
try {
|
|
132
|
+
const tenant = await service.getById(id);
|
|
133
|
+
setSelectedTenant(tenant);
|
|
134
|
+
setIsLoading(false);
|
|
135
|
+
return { success: true };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to fetch tenant";
|
|
138
|
+
setError(errorMessage);
|
|
139
|
+
setIsLoading(false);
|
|
140
|
+
return { success: false, error: errorMessage };
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
[service]
|
|
144
|
+
);
|
|
145
|
+
const createTenant = useCallback(
|
|
146
|
+
async (data) => {
|
|
147
|
+
setIsLoading(true);
|
|
148
|
+
setError(null);
|
|
149
|
+
try {
|
|
150
|
+
await service.create(data);
|
|
151
|
+
await service.getAll().then((response) => setTenants(response.items));
|
|
152
|
+
setIsLoading(false);
|
|
153
|
+
return { success: true };
|
|
154
|
+
} catch (err) {
|
|
155
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to create tenant";
|
|
156
|
+
setError(errorMessage);
|
|
157
|
+
setIsLoading(false);
|
|
158
|
+
return { success: false, error: errorMessage };
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
[service]
|
|
162
|
+
);
|
|
163
|
+
const updateTenant = useCallback(
|
|
164
|
+
async (data) => {
|
|
165
|
+
setIsLoading(true);
|
|
166
|
+
setError(null);
|
|
167
|
+
try {
|
|
168
|
+
await service.update(data);
|
|
169
|
+
await service.getAll().then((response) => setTenants(response.items));
|
|
170
|
+
setIsLoading(false);
|
|
171
|
+
return { success: true };
|
|
172
|
+
} catch (err) {
|
|
173
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to update tenant";
|
|
174
|
+
setError(errorMessage);
|
|
175
|
+
setIsLoading(false);
|
|
176
|
+
return { success: false, error: errorMessage };
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
[service]
|
|
180
|
+
);
|
|
181
|
+
const deleteTenant = useCallback(
|
|
182
|
+
async (id) => {
|
|
183
|
+
setIsLoading(true);
|
|
184
|
+
setError(null);
|
|
185
|
+
try {
|
|
186
|
+
await service.delete(id);
|
|
187
|
+
await service.getAll().then((response) => setTenants(response.items));
|
|
188
|
+
setIsLoading(false);
|
|
189
|
+
return { success: true };
|
|
190
|
+
} catch (err) {
|
|
191
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to delete tenant";
|
|
192
|
+
setError(errorMessage);
|
|
193
|
+
setIsLoading(false);
|
|
194
|
+
return { success: false, error: errorMessage };
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
[service]
|
|
198
|
+
);
|
|
199
|
+
const fetchConnectionString = useCallback(
|
|
200
|
+
async (id) => {
|
|
201
|
+
setIsLoading(true);
|
|
202
|
+
setError(null);
|
|
203
|
+
try {
|
|
204
|
+
const connectionString = await service.getDefaultConnectionString(id);
|
|
205
|
+
setDefaultConnectionString(connectionString || "");
|
|
206
|
+
setUseSharedDatabase(!connectionString);
|
|
207
|
+
setIsLoading(false);
|
|
208
|
+
return { success: true };
|
|
209
|
+
} catch (err) {
|
|
210
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to fetch connection string";
|
|
211
|
+
setError(errorMessage);
|
|
212
|
+
setIsLoading(false);
|
|
213
|
+
return { success: false, error: errorMessage };
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
[service]
|
|
217
|
+
);
|
|
218
|
+
const updateConnectionString = useCallback(
|
|
219
|
+
async (id, connectionString) => {
|
|
220
|
+
setIsLoading(true);
|
|
221
|
+
setError(null);
|
|
222
|
+
try {
|
|
223
|
+
await service.updateDefaultConnectionString({
|
|
224
|
+
id,
|
|
225
|
+
defaultConnectionString: connectionString
|
|
226
|
+
});
|
|
227
|
+
setDefaultConnectionString(connectionString);
|
|
228
|
+
setUseSharedDatabase(false);
|
|
229
|
+
setIsLoading(false);
|
|
230
|
+
return { success: true };
|
|
231
|
+
} catch (err) {
|
|
232
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to update connection string";
|
|
233
|
+
setError(errorMessage);
|
|
234
|
+
setIsLoading(false);
|
|
235
|
+
return { success: false, error: errorMessage };
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
[service]
|
|
239
|
+
);
|
|
240
|
+
const deleteConnectionString = useCallback(
|
|
241
|
+
async (id) => {
|
|
242
|
+
setIsLoading(true);
|
|
243
|
+
setError(null);
|
|
244
|
+
try {
|
|
245
|
+
await service.deleteDefaultConnectionString(id);
|
|
246
|
+
setDefaultConnectionString("");
|
|
247
|
+
setUseSharedDatabase(true);
|
|
248
|
+
setIsLoading(false);
|
|
249
|
+
return { success: true };
|
|
250
|
+
} catch (err) {
|
|
251
|
+
const errorMessage = err instanceof Error ? err.message : "Failed to delete connection string";
|
|
252
|
+
setError(errorMessage);
|
|
253
|
+
setIsLoading(false);
|
|
254
|
+
return { success: false, error: errorMessage };
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
[service]
|
|
258
|
+
);
|
|
259
|
+
const reset = useCallback(() => {
|
|
260
|
+
setTenants([]);
|
|
261
|
+
setSelectedTenant(null);
|
|
262
|
+
setIsLoading(false);
|
|
263
|
+
setError(null);
|
|
264
|
+
setDefaultConnectionString("");
|
|
265
|
+
setUseSharedDatabase(true);
|
|
266
|
+
}, []);
|
|
267
|
+
return {
|
|
268
|
+
tenants,
|
|
269
|
+
selectedTenant,
|
|
270
|
+
isLoading,
|
|
271
|
+
error,
|
|
272
|
+
defaultConnectionString,
|
|
273
|
+
useSharedDatabase,
|
|
274
|
+
fetchTenants,
|
|
275
|
+
fetchTenantById,
|
|
276
|
+
createTenant,
|
|
277
|
+
updateTenant,
|
|
278
|
+
deleteTenant,
|
|
279
|
+
fetchConnectionString,
|
|
280
|
+
updateConnectionString,
|
|
281
|
+
deleteConnectionString,
|
|
282
|
+
setSelectedTenant,
|
|
283
|
+
setUseSharedDatabase,
|
|
284
|
+
setDefaultConnectionString,
|
|
285
|
+
reset
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/components/TenantManagementModal/TenantManagementModal.tsx
|
|
290
|
+
import { useEffect, useCallback as useCallback2, useState as useState2 } from "react";
|
|
291
|
+
import { useLocalization } from "@abpjs/core";
|
|
292
|
+
import { Modal, Alert, Button, Checkbox, FormField } from "@abpjs/theme-shared";
|
|
293
|
+
import {
|
|
294
|
+
Box,
|
|
295
|
+
Flex,
|
|
296
|
+
Input,
|
|
297
|
+
Spinner,
|
|
298
|
+
VStack
|
|
299
|
+
} from "@chakra-ui/react";
|
|
300
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
301
|
+
function TenantManagementModal({
|
|
302
|
+
visible,
|
|
303
|
+
onVisibleChange,
|
|
304
|
+
tenantId,
|
|
305
|
+
initialView = "tenant",
|
|
306
|
+
onSave
|
|
307
|
+
}) {
|
|
308
|
+
const { t } = useLocalization();
|
|
309
|
+
const {
|
|
310
|
+
selectedTenant,
|
|
311
|
+
isLoading,
|
|
312
|
+
error,
|
|
313
|
+
defaultConnectionString,
|
|
314
|
+
useSharedDatabase,
|
|
315
|
+
fetchTenantById,
|
|
316
|
+
createTenant,
|
|
317
|
+
updateTenant,
|
|
318
|
+
fetchConnectionString,
|
|
319
|
+
updateConnectionString,
|
|
320
|
+
deleteConnectionString,
|
|
321
|
+
reset
|
|
322
|
+
} = useTenantManagement();
|
|
323
|
+
const [currentView, setCurrentView] = useState2(initialView);
|
|
324
|
+
const [tenantName, setTenantName] = useState2("");
|
|
325
|
+
const [tenantNameError, setTenantNameError] = useState2(null);
|
|
326
|
+
const [localConnectionString, setLocalConnectionString] = useState2("");
|
|
327
|
+
const [localUseSharedDatabase, setLocalUseSharedDatabase] = useState2(true);
|
|
328
|
+
const isEditing = !!tenantId;
|
|
329
|
+
const getModalTitle = () => {
|
|
330
|
+
if (currentView === "connectionString") {
|
|
331
|
+
return t("AbpTenantManagement::ConnectionStrings");
|
|
332
|
+
}
|
|
333
|
+
return isEditing ? t("AbpTenantManagement::Edit") : t("AbpTenantManagement::NewTenant");
|
|
334
|
+
};
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
if (visible && tenantId) {
|
|
337
|
+
fetchTenantById(tenantId);
|
|
338
|
+
if (initialView === "connectionString") {
|
|
339
|
+
fetchConnectionString(tenantId);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (visible) {
|
|
343
|
+
setCurrentView(initialView);
|
|
344
|
+
}
|
|
345
|
+
if (!visible) {
|
|
346
|
+
reset();
|
|
347
|
+
setTenantName("");
|
|
348
|
+
setTenantNameError(null);
|
|
349
|
+
setLocalConnectionString("");
|
|
350
|
+
setLocalUseSharedDatabase(true);
|
|
351
|
+
}
|
|
352
|
+
}, [visible, tenantId, initialView, fetchTenantById, fetchConnectionString, reset]);
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
if (selectedTenant) {
|
|
355
|
+
setTenantName(selectedTenant.name);
|
|
356
|
+
}
|
|
357
|
+
}, [selectedTenant]);
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
setLocalConnectionString(defaultConnectionString);
|
|
360
|
+
setLocalUseSharedDatabase(useSharedDatabase);
|
|
361
|
+
}, [defaultConnectionString, useSharedDatabase]);
|
|
362
|
+
const validateTenantName = useCallback2(
|
|
363
|
+
(name) => {
|
|
364
|
+
if (!name || name.trim().length === 0) {
|
|
365
|
+
setTenantNameError(t("AbpValidation::ThisFieldIsRequired"));
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
if (name.length > 256) {
|
|
369
|
+
setTenantNameError(
|
|
370
|
+
t("AbpValidation::ThisFieldMustBeBetween{0}And{1}Characters", "1", "256")
|
|
371
|
+
);
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
setTenantNameError(null);
|
|
375
|
+
return true;
|
|
376
|
+
},
|
|
377
|
+
[t]
|
|
378
|
+
);
|
|
379
|
+
const handleTenantSubmit = useCallback2(async () => {
|
|
380
|
+
if (!validateTenantName(tenantName)) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
let result;
|
|
384
|
+
if (isEditing && tenantId) {
|
|
385
|
+
result = await updateTenant({ id: tenantId, name: tenantName.trim() });
|
|
386
|
+
} else {
|
|
387
|
+
result = await createTenant({ name: tenantName.trim() });
|
|
388
|
+
}
|
|
389
|
+
if (result.success) {
|
|
390
|
+
onSave?.();
|
|
391
|
+
onVisibleChange?.(false);
|
|
392
|
+
}
|
|
393
|
+
}, [
|
|
394
|
+
tenantName,
|
|
395
|
+
isEditing,
|
|
396
|
+
tenantId,
|
|
397
|
+
validateTenantName,
|
|
398
|
+
createTenant,
|
|
399
|
+
updateTenant,
|
|
400
|
+
onSave,
|
|
401
|
+
onVisibleChange
|
|
402
|
+
]);
|
|
403
|
+
const handleConnectionStringSubmit = useCallback2(async () => {
|
|
404
|
+
if (!tenantId) return;
|
|
405
|
+
let result;
|
|
406
|
+
if (localUseSharedDatabase) {
|
|
407
|
+
result = await deleteConnectionString(tenantId);
|
|
408
|
+
} else {
|
|
409
|
+
result = await updateConnectionString(tenantId, localConnectionString);
|
|
410
|
+
}
|
|
411
|
+
if (result.success) {
|
|
412
|
+
onSave?.();
|
|
413
|
+
onVisibleChange?.(false);
|
|
414
|
+
}
|
|
415
|
+
}, [
|
|
416
|
+
tenantId,
|
|
417
|
+
localUseSharedDatabase,
|
|
418
|
+
localConnectionString,
|
|
419
|
+
deleteConnectionString,
|
|
420
|
+
updateConnectionString,
|
|
421
|
+
onSave,
|
|
422
|
+
onVisibleChange
|
|
423
|
+
]);
|
|
424
|
+
const handleClose = useCallback2(() => {
|
|
425
|
+
onVisibleChange?.(false);
|
|
426
|
+
}, [onVisibleChange]);
|
|
427
|
+
const handleTenantNameChange = useCallback2(
|
|
428
|
+
(e) => {
|
|
429
|
+
const value = e.target.value;
|
|
430
|
+
setTenantName(value);
|
|
431
|
+
if (tenantNameError) {
|
|
432
|
+
validateTenantName(value);
|
|
433
|
+
}
|
|
434
|
+
},
|
|
435
|
+
[tenantNameError, validateTenantName]
|
|
436
|
+
);
|
|
437
|
+
const handleUseSharedDatabaseChange = useCallback2(() => {
|
|
438
|
+
setLocalUseSharedDatabase((prev) => {
|
|
439
|
+
const newValue = !prev;
|
|
440
|
+
if (newValue) {
|
|
441
|
+
setLocalConnectionString("");
|
|
442
|
+
}
|
|
443
|
+
return newValue;
|
|
444
|
+
});
|
|
445
|
+
}, []);
|
|
446
|
+
const handleConnectionStringChange = useCallback2((e) => {
|
|
447
|
+
setLocalConnectionString(e.target.value);
|
|
448
|
+
}, []);
|
|
449
|
+
const renderTenantForm = () => /* @__PURE__ */ jsx(VStack, { gap: 4, align: "stretch", children: /* @__PURE__ */ jsx(
|
|
450
|
+
FormField,
|
|
451
|
+
{
|
|
452
|
+
label: t("AbpTenantManagement::TenantName"),
|
|
453
|
+
htmlFor: "tenant-name",
|
|
454
|
+
invalid: !!tenantNameError,
|
|
455
|
+
errorText: tenantNameError || void 0,
|
|
456
|
+
required: true,
|
|
457
|
+
children: /* @__PURE__ */ jsx(
|
|
458
|
+
Input,
|
|
459
|
+
{
|
|
460
|
+
id: "tenant-name",
|
|
461
|
+
value: tenantName,
|
|
462
|
+
onChange: handleTenantNameChange,
|
|
463
|
+
placeholder: t("AbpTenantManagement::TenantName"),
|
|
464
|
+
maxLength: 256
|
|
465
|
+
}
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
) });
|
|
469
|
+
const renderConnectionStringForm = () => /* @__PURE__ */ jsxs(VStack, { gap: 4, align: "stretch", children: [
|
|
470
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
|
|
471
|
+
Checkbox,
|
|
472
|
+
{
|
|
473
|
+
id: "use-shared-database",
|
|
474
|
+
checked: localUseSharedDatabase,
|
|
475
|
+
onChange: handleUseSharedDatabaseChange,
|
|
476
|
+
children: t("AbpTenantManagement::DisplayName:UseSharedDatabase")
|
|
477
|
+
}
|
|
478
|
+
) }),
|
|
479
|
+
!localUseSharedDatabase && /* @__PURE__ */ jsx(
|
|
480
|
+
FormField,
|
|
481
|
+
{
|
|
482
|
+
label: t("AbpTenantManagement::DisplayName:DefaultConnectionString"),
|
|
483
|
+
htmlFor: "connection-string",
|
|
484
|
+
children: /* @__PURE__ */ jsx(
|
|
485
|
+
Input,
|
|
486
|
+
{
|
|
487
|
+
id: "connection-string",
|
|
488
|
+
value: localConnectionString,
|
|
489
|
+
onChange: handleConnectionStringChange,
|
|
490
|
+
placeholder: t("AbpTenantManagement::DisplayName:DefaultConnectionString")
|
|
491
|
+
}
|
|
492
|
+
)
|
|
493
|
+
}
|
|
494
|
+
)
|
|
495
|
+
] });
|
|
496
|
+
return /* @__PURE__ */ jsxs(
|
|
497
|
+
Modal,
|
|
498
|
+
{
|
|
499
|
+
visible,
|
|
500
|
+
onVisibleChange,
|
|
501
|
+
size: "md",
|
|
502
|
+
header: getModalTitle(),
|
|
503
|
+
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
504
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleClose, disabled: isLoading, children: t("AbpTenantManagement::Cancel") }),
|
|
505
|
+
/* @__PURE__ */ jsx(
|
|
506
|
+
Button,
|
|
507
|
+
{
|
|
508
|
+
colorPalette: "blue",
|
|
509
|
+
onClick: currentView === "connectionString" ? handleConnectionStringSubmit : handleTenantSubmit,
|
|
510
|
+
loading: isLoading,
|
|
511
|
+
children: t("AbpTenantManagement::Save")
|
|
512
|
+
}
|
|
513
|
+
)
|
|
514
|
+
] }),
|
|
515
|
+
children: [
|
|
516
|
+
isLoading && !selectedTenant && currentView === "tenant" && /* @__PURE__ */ jsx(Flex, { justify: "center", align: "center", py: 8, children: /* @__PURE__ */ jsx(Spinner, { size: "lg" }) }),
|
|
517
|
+
error && /* @__PURE__ */ jsx(Alert, { status: "error", mb: 4, children: error }),
|
|
518
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
519
|
+
currentView === "tenant" && renderTenantForm(),
|
|
520
|
+
currentView === "connectionString" && renderConnectionStringForm()
|
|
521
|
+
] })
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
export {
|
|
527
|
+
TenantManagementModal,
|
|
528
|
+
TenantManagementService,
|
|
529
|
+
useTenantManagement
|
|
530
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant Management module type definitions
|
|
3
|
+
* Translated from @abp/ng.tenant-management v0.7.6
|
|
4
|
+
*/
|
|
5
|
+
import type { ABP } from '@abpjs/core';
|
|
6
|
+
/**
|
|
7
|
+
* TenantManagement namespace containing all tenant-related types
|
|
8
|
+
*/
|
|
9
|
+
export declare namespace TenantManagement {
|
|
10
|
+
/**
|
|
11
|
+
* State interface for tenant management store
|
|
12
|
+
*/
|
|
13
|
+
interface State {
|
|
14
|
+
result: Response;
|
|
15
|
+
selectedItem: Item;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* API response for tenant list (paginated)
|
|
19
|
+
*/
|
|
20
|
+
type Response = ABP.PagedResponse<Item>;
|
|
21
|
+
/**
|
|
22
|
+
* Single tenant item
|
|
23
|
+
*/
|
|
24
|
+
interface Item {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Request payload for creating a new tenant
|
|
30
|
+
*/
|
|
31
|
+
interface AddRequest {
|
|
32
|
+
name: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Request payload for updating an existing tenant
|
|
36
|
+
*/
|
|
37
|
+
interface UpdateRequest extends AddRequest {
|
|
38
|
+
id: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Request payload for updating tenant's default connection string
|
|
42
|
+
*/
|
|
43
|
+
interface DefaultConnectionStringRequest {
|
|
44
|
+
id: string;
|
|
45
|
+
defaultConnectionString: string;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TenantManagementService } from './tenant-management.service';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { RestService } from '@abpjs/core';
|
|
2
|
+
import { TenantManagement } from '../models';
|
|
3
|
+
/**
|
|
4
|
+
* Service for tenant management API calls
|
|
5
|
+
* Translated from @abp/ng.tenant-management v0.7.6
|
|
6
|
+
*/
|
|
7
|
+
export declare class TenantManagementService {
|
|
8
|
+
private rest;
|
|
9
|
+
constructor(rest: RestService);
|
|
10
|
+
/**
|
|
11
|
+
* Get all tenants (paginated)
|
|
12
|
+
* @returns Promise with paginated tenant response
|
|
13
|
+
*/
|
|
14
|
+
getAll(): Promise<TenantManagement.Response>;
|
|
15
|
+
/**
|
|
16
|
+
* Get a tenant by ID
|
|
17
|
+
* @param id Tenant ID
|
|
18
|
+
* @returns Promise with tenant item
|
|
19
|
+
*/
|
|
20
|
+
getById(id: string): Promise<TenantManagement.Item>;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new tenant
|
|
23
|
+
* @param body Tenant creation request
|
|
24
|
+
* @returns Promise with created tenant item
|
|
25
|
+
*/
|
|
26
|
+
create(body: TenantManagement.AddRequest): Promise<TenantManagement.Item>;
|
|
27
|
+
/**
|
|
28
|
+
* Update an existing tenant
|
|
29
|
+
* @param body Tenant update request (includes id)
|
|
30
|
+
* @returns Promise with updated tenant item
|
|
31
|
+
*/
|
|
32
|
+
update(body: TenantManagement.UpdateRequest): Promise<TenantManagement.Item>;
|
|
33
|
+
/**
|
|
34
|
+
* Delete a tenant
|
|
35
|
+
* @param id Tenant ID
|
|
36
|
+
* @returns Promise that resolves when deletion completes
|
|
37
|
+
*/
|
|
38
|
+
delete(id: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Get default connection string for a tenant
|
|
41
|
+
* @param id Tenant ID
|
|
42
|
+
* @returns Promise with connection string (empty string if using shared database)
|
|
43
|
+
*/
|
|
44
|
+
getDefaultConnectionString(id: string): Promise<string>;
|
|
45
|
+
/**
|
|
46
|
+
* Update default connection string for a tenant
|
|
47
|
+
* @param payload Request with tenant ID and connection string
|
|
48
|
+
* @returns Promise that resolves when update completes
|
|
49
|
+
*/
|
|
50
|
+
updateDefaultConnectionString(payload: TenantManagement.DefaultConnectionStringRequest): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Delete default connection string for a tenant (use shared database)
|
|
53
|
+
* @param id Tenant ID
|
|
54
|
+
* @returns Promise that resolves when deletion completes
|
|
55
|
+
*/
|
|
56
|
+
deleteDefaultConnectionString(id: string): Promise<void>;
|
|
57
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abpjs/tenant-management",
|
|
3
|
+
"version": "0.7.6",
|
|
4
|
+
"description": "ABP Framework tenant-management components for React - translated from @abp/ng.tenant-management",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": "^18.2.0",
|
|
21
|
+
"react-dom": "^18.2.0"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@chakra-ui/react": "^3.2.0",
|
|
25
|
+
"@emotion/react": "^11.11.0",
|
|
26
|
+
"@abpjs/core": "0.7.6",
|
|
27
|
+
"@abpjs/theme-shared": "0.7.6"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@abp/ng.tenant-management": "0.7.6",
|
|
31
|
+
"autoprefixer": "^10.4.16",
|
|
32
|
+
"postcss": "^8.4.32",
|
|
33
|
+
"tailwindcss": "^3.4.0"
|
|
34
|
+
},
|
|
35
|
+
"author": "tekthar.com",
|
|
36
|
+
"license": "LGPL-3.0",
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/abpjs/abp-react"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://docs.abpjs.io/docs/packages/tenant-management/overview",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/abpjs/abp-react/issues"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup src/index.ts --format cjs,esm --clean && tsc -p tsconfig.build.json",
|
|
50
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch",
|
|
51
|
+
"lint": "eslint src",
|
|
52
|
+
"lint:fix": "eslint src --fix",
|
|
53
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
|
54
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
|
|
55
|
+
"type-check": "tsc --noEmit",
|
|
56
|
+
"test": "vitest"
|
|
57
|
+
}
|
|
58
|
+
}
|