@abpjs/saas 0.7.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.
package/dist/index.js ADDED
@@ -0,0 +1,1187 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ EditionsComponent: () => EditionsComponent,
24
+ SAAS_ROUTES: () => SAAS_ROUTES,
25
+ SaasService: () => SaasService,
26
+ TenantsComponent: () => TenantsComponent,
27
+ useEditions: () => useEditions,
28
+ useTenants: () => useTenants
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/constants/routes.ts
33
+ var SAAS_ROUTES = {
34
+ routes: [
35
+ {
36
+ name: "Saas",
37
+ path: "saas",
38
+ layout: "application",
39
+ order: 50,
40
+ children: [
41
+ {
42
+ name: "Tenants",
43
+ path: "tenants",
44
+ order: 1,
45
+ requiredPolicy: "Saas.Tenants"
46
+ },
47
+ {
48
+ name: "Editions",
49
+ path: "editions",
50
+ order: 2,
51
+ requiredPolicy: "Saas.Editions"
52
+ }
53
+ ]
54
+ }
55
+ ]
56
+ };
57
+
58
+ // src/services/saas.service.ts
59
+ var SaasService = class {
60
+ constructor(restService) {
61
+ this.restService = restService;
62
+ }
63
+ // ==================== Tenant Operations ====================
64
+ /**
65
+ * Get paginated list of tenants
66
+ * @param params Query parameters for filtering and pagination
67
+ * @returns Promise with paginated tenant response
68
+ */
69
+ async getTenants(params = {}) {
70
+ return this.restService.request({
71
+ method: "GET",
72
+ url: "/api/saas/tenants",
73
+ params
74
+ });
75
+ }
76
+ /**
77
+ * Get a tenant by ID
78
+ * @param id Tenant ID
79
+ * @returns Promise with tenant data
80
+ */
81
+ async getTenantById(id) {
82
+ return this.restService.request({
83
+ method: "GET",
84
+ url: `/api/saas/tenants/${id}`
85
+ });
86
+ }
87
+ /**
88
+ * Create a new tenant
89
+ * @param body Tenant creation request
90
+ * @returns Promise with created tenant data
91
+ */
92
+ async createTenant(body) {
93
+ return this.restService.request({
94
+ method: "POST",
95
+ url: "/api/saas/tenants",
96
+ body
97
+ });
98
+ }
99
+ /**
100
+ * Update an existing tenant
101
+ * @param body Tenant update request (must include id)
102
+ * @returns Promise with updated tenant data
103
+ */
104
+ async updateTenant(body) {
105
+ const { id, ...rest } = body;
106
+ return this.restService.request({
107
+ method: "PUT",
108
+ url: `/api/saas/tenants/${id}`,
109
+ body: rest
110
+ });
111
+ }
112
+ /**
113
+ * Delete a tenant by ID
114
+ * @param id Tenant ID to delete
115
+ * @returns Promise that resolves when deletion is complete
116
+ */
117
+ async deleteTenant(id) {
118
+ return this.restService.request({
119
+ method: "DELETE",
120
+ url: `/api/saas/tenants/${id}`
121
+ });
122
+ }
123
+ // ==================== Connection String Operations ====================
124
+ /**
125
+ * Get the default connection string for a tenant
126
+ * @param id Tenant ID
127
+ * @returns Promise with connection string (empty string if using shared database)
128
+ */
129
+ async getDefaultConnectionString(id) {
130
+ return this.restService.request({
131
+ method: "GET",
132
+ url: `/api/saas/tenants/${id}/default-connection-string`,
133
+ responseType: "text"
134
+ });
135
+ }
136
+ /**
137
+ * Update the default connection string for a tenant
138
+ * @param payload Object containing tenant ID and connection string
139
+ * @returns Promise that resolves when update is complete
140
+ */
141
+ async updateDefaultConnectionString(payload) {
142
+ return this.restService.request({
143
+ method: "PUT",
144
+ url: `/api/saas/tenants/${payload.id}/default-connection-string`,
145
+ params: { defaultConnectionString: payload.defaultConnectionString }
146
+ });
147
+ }
148
+ /**
149
+ * Delete the default connection string for a tenant (revert to shared database)
150
+ * @param id Tenant ID
151
+ * @returns Promise that resolves when deletion is complete
152
+ */
153
+ async deleteDefaultConnectionString(id) {
154
+ return this.restService.request({
155
+ method: "DELETE",
156
+ url: `/api/saas/tenants/${id}/default-connection-string`
157
+ });
158
+ }
159
+ // ==================== Edition Operations ====================
160
+ /**
161
+ * Get paginated list of editions
162
+ * @param params Query parameters for filtering and pagination
163
+ * @returns Promise with paginated editions response
164
+ */
165
+ async getEditions(params = {}) {
166
+ return this.restService.request({
167
+ method: "GET",
168
+ url: "/api/saas/editions",
169
+ params
170
+ });
171
+ }
172
+ /**
173
+ * Get an edition by ID
174
+ * @param id Edition ID
175
+ * @returns Promise with edition data
176
+ */
177
+ async getEditionById(id) {
178
+ return this.restService.request({
179
+ method: "GET",
180
+ url: `/api/saas/editions/${id}`
181
+ });
182
+ }
183
+ /**
184
+ * Create a new edition
185
+ * @param body Edition creation request
186
+ * @returns Promise with created edition data
187
+ */
188
+ async createEdition(body) {
189
+ return this.restService.request({
190
+ method: "POST",
191
+ url: "/api/saas/editions",
192
+ body
193
+ });
194
+ }
195
+ /**
196
+ * Update an existing edition
197
+ * @param body Edition update request (must include id)
198
+ * @returns Promise with updated edition data
199
+ */
200
+ async updateEdition(body) {
201
+ const { id, ...rest } = body;
202
+ return this.restService.request({
203
+ method: "PUT",
204
+ url: `/api/saas/editions/${id}`,
205
+ body: rest
206
+ });
207
+ }
208
+ /**
209
+ * Delete an edition by ID
210
+ * @param id Edition ID to delete
211
+ * @returns Promise that resolves when deletion is complete
212
+ */
213
+ async deleteEdition(id) {
214
+ return this.restService.request({
215
+ method: "DELETE",
216
+ url: `/api/saas/editions/${id}`
217
+ });
218
+ }
219
+ // ==================== Statistics Operations ====================
220
+ /**
221
+ * Get usage statistics for editions
222
+ * @returns Promise with usage statistics data
223
+ */
224
+ async getUsageStatistics() {
225
+ return this.restService.request({
226
+ method: "GET",
227
+ url: "/api/saas/editions/statistics/usage-statistic"
228
+ });
229
+ }
230
+ };
231
+
232
+ // src/hooks/useTenants.ts
233
+ var import_react = require("react");
234
+ var import_core = require("@abpjs/core");
235
+ function useTenants() {
236
+ const restService = (0, import_core.useRestService)();
237
+ const service = (0, import_react.useMemo)(() => new SaasService(restService), [restService]);
238
+ const [tenants, setTenants] = (0, import_react.useState)([]);
239
+ const [totalCount, setTotalCount] = (0, import_react.useState)(0);
240
+ const [selectedTenant, setSelectedTenant] = (0, import_react.useState)(null);
241
+ const [isLoading, setIsLoading] = (0, import_react.useState)(false);
242
+ const [error, setError] = (0, import_react.useState)(null);
243
+ const [sortKey, setSortKey] = (0, import_react.useState)("name");
244
+ const [sortOrder, setSortOrder] = (0, import_react.useState)("");
245
+ const [defaultConnectionString, setDefaultConnectionString] = (0, import_react.useState)("");
246
+ const [useSharedDatabase, setUseSharedDatabase] = (0, import_react.useState)(true);
247
+ const fetchTenants = (0, import_react.useCallback)(
248
+ async (params) => {
249
+ setIsLoading(true);
250
+ setError(null);
251
+ try {
252
+ const response = await service.getTenants(params);
253
+ setTenants(response.items || []);
254
+ setTotalCount(response.totalCount || 0);
255
+ setIsLoading(false);
256
+ return { success: true, data: response };
257
+ } catch (err) {
258
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch tenants";
259
+ setError(errorMessage);
260
+ setIsLoading(false);
261
+ return { success: false, error: errorMessage };
262
+ }
263
+ },
264
+ [service]
265
+ );
266
+ const getTenantById = (0, import_react.useCallback)(
267
+ async (id) => {
268
+ setIsLoading(true);
269
+ setError(null);
270
+ try {
271
+ const tenant = await service.getTenantById(id);
272
+ setSelectedTenant(tenant);
273
+ setIsLoading(false);
274
+ return { success: true, data: tenant };
275
+ } catch (err) {
276
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch tenant";
277
+ setError(errorMessage);
278
+ setIsLoading(false);
279
+ return { success: false, error: errorMessage };
280
+ }
281
+ },
282
+ [service]
283
+ );
284
+ const createTenant = (0, import_react.useCallback)(
285
+ async (tenant) => {
286
+ setIsLoading(true);
287
+ setError(null);
288
+ try {
289
+ const created = await service.createTenant(tenant);
290
+ setIsLoading(false);
291
+ return { success: true, data: created };
292
+ } catch (err) {
293
+ const errorMessage = err instanceof Error ? err.message : "Failed to create tenant";
294
+ setError(errorMessage);
295
+ setIsLoading(false);
296
+ return { success: false, error: errorMessage };
297
+ }
298
+ },
299
+ [service]
300
+ );
301
+ const updateTenant = (0, import_react.useCallback)(
302
+ async (tenant) => {
303
+ setIsLoading(true);
304
+ setError(null);
305
+ try {
306
+ const updated = await service.updateTenant(tenant);
307
+ setIsLoading(false);
308
+ return { success: true, data: updated };
309
+ } catch (err) {
310
+ const errorMessage = err instanceof Error ? err.message : "Failed to update tenant";
311
+ setError(errorMessage);
312
+ setIsLoading(false);
313
+ return { success: false, error: errorMessage };
314
+ }
315
+ },
316
+ [service]
317
+ );
318
+ const deleteTenant = (0, import_react.useCallback)(
319
+ async (id) => {
320
+ setIsLoading(true);
321
+ setError(null);
322
+ try {
323
+ await service.deleteTenant(id);
324
+ setIsLoading(false);
325
+ return { success: true };
326
+ } catch (err) {
327
+ const errorMessage = err instanceof Error ? err.message : "Failed to delete tenant";
328
+ setError(errorMessage);
329
+ setIsLoading(false);
330
+ return { success: false, error: errorMessage };
331
+ }
332
+ },
333
+ [service]
334
+ );
335
+ const getDefaultConnectionString = (0, import_react.useCallback)(
336
+ async (id) => {
337
+ setError(null);
338
+ try {
339
+ const connString = await service.getDefaultConnectionString(id);
340
+ setDefaultConnectionString(connString || "");
341
+ setUseSharedDatabase(!connString);
342
+ return { success: true, data: connString };
343
+ } catch (err) {
344
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch connection string";
345
+ setError(errorMessage);
346
+ return { success: false, error: errorMessage };
347
+ }
348
+ },
349
+ [service]
350
+ );
351
+ const updateDefaultConnectionString = (0, import_react.useCallback)(
352
+ async (payload) => {
353
+ setIsLoading(true);
354
+ setError(null);
355
+ try {
356
+ await service.updateDefaultConnectionString(payload);
357
+ setDefaultConnectionString(payload.defaultConnectionString);
358
+ setUseSharedDatabase(false);
359
+ setIsLoading(false);
360
+ return { success: true };
361
+ } catch (err) {
362
+ const errorMessage = err instanceof Error ? err.message : "Failed to update connection string";
363
+ setError(errorMessage);
364
+ setIsLoading(false);
365
+ return { success: false, error: errorMessage };
366
+ }
367
+ },
368
+ [service]
369
+ );
370
+ const deleteDefaultConnectionString = (0, import_react.useCallback)(
371
+ async (id) => {
372
+ setIsLoading(true);
373
+ setError(null);
374
+ try {
375
+ await service.deleteDefaultConnectionString(id);
376
+ setDefaultConnectionString("");
377
+ setUseSharedDatabase(true);
378
+ setIsLoading(false);
379
+ return { success: true };
380
+ } catch (err) {
381
+ const errorMessage = err instanceof Error ? err.message : "Failed to delete connection string";
382
+ setError(errorMessage);
383
+ setIsLoading(false);
384
+ return { success: false, error: errorMessage };
385
+ }
386
+ },
387
+ [service]
388
+ );
389
+ const reset = (0, import_react.useCallback)(() => {
390
+ setTenants([]);
391
+ setTotalCount(0);
392
+ setSelectedTenant(null);
393
+ setIsLoading(false);
394
+ setError(null);
395
+ setSortKey("name");
396
+ setSortOrder("");
397
+ setDefaultConnectionString("");
398
+ setUseSharedDatabase(true);
399
+ }, []);
400
+ return {
401
+ tenants,
402
+ totalCount,
403
+ selectedTenant,
404
+ isLoading,
405
+ error,
406
+ sortKey,
407
+ sortOrder,
408
+ defaultConnectionString,
409
+ useSharedDatabase,
410
+ fetchTenants,
411
+ getTenantById,
412
+ createTenant,
413
+ updateTenant,
414
+ deleteTenant,
415
+ getDefaultConnectionString,
416
+ updateDefaultConnectionString,
417
+ deleteDefaultConnectionString,
418
+ setSelectedTenant,
419
+ setSortKey,
420
+ setSortOrder,
421
+ reset
422
+ };
423
+ }
424
+
425
+ // src/hooks/useEditions.ts
426
+ var import_react2 = require("react");
427
+ var import_core2 = require("@abpjs/core");
428
+ function useEditions() {
429
+ const restService = (0, import_core2.useRestService)();
430
+ const service = (0, import_react2.useMemo)(() => new SaasService(restService), [restService]);
431
+ const [editions, setEditions] = (0, import_react2.useState)([]);
432
+ const [totalCount, setTotalCount] = (0, import_react2.useState)(0);
433
+ const [selectedEdition, setSelectedEdition] = (0, import_react2.useState)(null);
434
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
435
+ const [error, setError] = (0, import_react2.useState)(null);
436
+ const [sortKey, setSortKey] = (0, import_react2.useState)("displayName");
437
+ const [sortOrder, setSortOrder] = (0, import_react2.useState)("");
438
+ const [usageStatistics, setUsageStatistics] = (0, import_react2.useState)({});
439
+ const fetchEditions = (0, import_react2.useCallback)(
440
+ async (params) => {
441
+ setIsLoading(true);
442
+ setError(null);
443
+ try {
444
+ const response = await service.getEditions(params);
445
+ setEditions(response.items || []);
446
+ setTotalCount(response.totalCount || 0);
447
+ setIsLoading(false);
448
+ return { success: true, data: response };
449
+ } catch (err) {
450
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch editions";
451
+ setError(errorMessage);
452
+ setIsLoading(false);
453
+ return { success: false, error: errorMessage };
454
+ }
455
+ },
456
+ [service]
457
+ );
458
+ const getEditionById = (0, import_react2.useCallback)(
459
+ async (id) => {
460
+ setIsLoading(true);
461
+ setError(null);
462
+ try {
463
+ const edition = await service.getEditionById(id);
464
+ setSelectedEdition(edition);
465
+ setIsLoading(false);
466
+ return { success: true, data: edition };
467
+ } catch (err) {
468
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch edition";
469
+ setError(errorMessage);
470
+ setIsLoading(false);
471
+ return { success: false, error: errorMessage };
472
+ }
473
+ },
474
+ [service]
475
+ );
476
+ const createEdition = (0, import_react2.useCallback)(
477
+ async (edition) => {
478
+ setIsLoading(true);
479
+ setError(null);
480
+ try {
481
+ const created = await service.createEdition(edition);
482
+ setIsLoading(false);
483
+ return { success: true, data: created };
484
+ } catch (err) {
485
+ const errorMessage = err instanceof Error ? err.message : "Failed to create edition";
486
+ setError(errorMessage);
487
+ setIsLoading(false);
488
+ return { success: false, error: errorMessage };
489
+ }
490
+ },
491
+ [service]
492
+ );
493
+ const updateEdition = (0, import_react2.useCallback)(
494
+ async (edition) => {
495
+ setIsLoading(true);
496
+ setError(null);
497
+ try {
498
+ const updated = await service.updateEdition(edition);
499
+ setIsLoading(false);
500
+ return { success: true, data: updated };
501
+ } catch (err) {
502
+ const errorMessage = err instanceof Error ? err.message : "Failed to update edition";
503
+ setError(errorMessage);
504
+ setIsLoading(false);
505
+ return { success: false, error: errorMessage };
506
+ }
507
+ },
508
+ [service]
509
+ );
510
+ const deleteEdition = (0, import_react2.useCallback)(
511
+ async (id) => {
512
+ setIsLoading(true);
513
+ setError(null);
514
+ try {
515
+ await service.deleteEdition(id);
516
+ setIsLoading(false);
517
+ return { success: true };
518
+ } catch (err) {
519
+ const errorMessage = err instanceof Error ? err.message : "Failed to delete edition";
520
+ setError(errorMessage);
521
+ setIsLoading(false);
522
+ return { success: false, error: errorMessage };
523
+ }
524
+ },
525
+ [service]
526
+ );
527
+ const fetchUsageStatistics = (0, import_react2.useCallback)(
528
+ async () => {
529
+ setError(null);
530
+ try {
531
+ const response = await service.getUsageStatistics();
532
+ setUsageStatistics(response.data || {});
533
+ return { success: true, data: response.data };
534
+ } catch (err) {
535
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch usage statistics";
536
+ setError(errorMessage);
537
+ return { success: false, error: errorMessage };
538
+ }
539
+ },
540
+ [service]
541
+ );
542
+ const reset = (0, import_react2.useCallback)(() => {
543
+ setEditions([]);
544
+ setTotalCount(0);
545
+ setSelectedEdition(null);
546
+ setIsLoading(false);
547
+ setError(null);
548
+ setSortKey("displayName");
549
+ setSortOrder("");
550
+ setUsageStatistics({});
551
+ }, []);
552
+ return {
553
+ editions,
554
+ totalCount,
555
+ selectedEdition,
556
+ isLoading,
557
+ error,
558
+ sortKey,
559
+ sortOrder,
560
+ usageStatistics,
561
+ fetchEditions,
562
+ getEditionById,
563
+ createEdition,
564
+ updateEdition,
565
+ deleteEdition,
566
+ fetchUsageStatistics,
567
+ setSelectedEdition,
568
+ setSortKey,
569
+ setSortOrder,
570
+ reset
571
+ };
572
+ }
573
+
574
+ // src/components/Tenants/TenantsComponent.tsx
575
+ var import_react3 = require("react");
576
+ var import_core3 = require("@abpjs/core");
577
+ var import_theme_shared = require("@abpjs/theme-shared");
578
+ var import_react4 = require("@chakra-ui/react");
579
+ var import_react5 = require("@chakra-ui/react");
580
+ var import_jsx_runtime = require("react/jsx-runtime");
581
+ function TenantsComponent({
582
+ onTenantCreated,
583
+ onTenantUpdated,
584
+ onTenantDeleted
585
+ }) {
586
+ const { t } = (0, import_core3.useLocalization)();
587
+ const { warn } = (0, import_theme_shared.useConfirmation)();
588
+ const {
589
+ tenants,
590
+ totalCount,
591
+ selectedTenant,
592
+ isLoading,
593
+ error,
594
+ fetchTenants,
595
+ getTenantById,
596
+ createTenant,
597
+ updateTenant,
598
+ deleteTenant,
599
+ getDefaultConnectionString,
600
+ updateDefaultConnectionString,
601
+ deleteDefaultConnectionString,
602
+ setSelectedTenant
603
+ } = useTenants();
604
+ const { editions, fetchEditions } = useEditions();
605
+ const [modalType, setModalType] = (0, import_react3.useState)(null);
606
+ const [modalVisible, setModalVisible] = (0, import_react3.useState)(false);
607
+ const [modalBusy, setModalBusy] = (0, import_react3.useState)(false);
608
+ const [page, setPage] = (0, import_react3.useState)(0);
609
+ const pageSize = 10;
610
+ const [filter, setFilter] = (0, import_react3.useState)("");
611
+ const [tenantName, setTenantName] = (0, import_react3.useState)("");
612
+ const [tenantEditionId, setTenantEditionId] = (0, import_react3.useState)("");
613
+ const [connStringUseShared, setConnStringUseShared] = (0, import_react3.useState)(true);
614
+ const [connString, setConnString] = (0, import_react3.useState)("");
615
+ (0, import_react3.useEffect)(() => {
616
+ fetchTenants({
617
+ skipCount: page * pageSize,
618
+ maxResultCount: pageSize,
619
+ filter: filter || void 0,
620
+ getEditionNames: true
621
+ });
622
+ }, [page, pageSize, fetchTenants]);
623
+ (0, import_react3.useEffect)(() => {
624
+ fetchEditions();
625
+ }, [fetchEditions]);
626
+ const handleSearch = (0, import_react3.useCallback)(() => {
627
+ setPage(0);
628
+ fetchTenants({
629
+ skipCount: 0,
630
+ maxResultCount: pageSize,
631
+ filter: filter || void 0,
632
+ getEditionNames: true
633
+ });
634
+ }, [filter, pageSize, fetchTenants]);
635
+ const handleAddTenant = (0, import_react3.useCallback)(() => {
636
+ setSelectedTenant(null);
637
+ setTenantName("");
638
+ setTenantEditionId("");
639
+ setModalType("tenant");
640
+ setModalVisible(true);
641
+ }, [setSelectedTenant]);
642
+ const handleEditTenant = (0, import_react3.useCallback)(
643
+ async (id) => {
644
+ const result = await getTenantById(id);
645
+ if (result.success && result.data) {
646
+ setTenantName(result.data.name);
647
+ setTenantEditionId(result.data.editionId || "");
648
+ setModalType("tenant");
649
+ setModalVisible(true);
650
+ }
651
+ },
652
+ [getTenantById]
653
+ );
654
+ const handleEditConnectionString = (0, import_react3.useCallback)(
655
+ async (id) => {
656
+ const tenantResult = await getTenantById(id);
657
+ if (tenantResult.success) {
658
+ const connResult = await getDefaultConnectionString(id);
659
+ setConnStringUseShared(!connResult.data);
660
+ setConnString(connResult.data || "");
661
+ setModalType("connectionString");
662
+ setModalVisible(true);
663
+ }
664
+ },
665
+ [getTenantById, getDefaultConnectionString]
666
+ );
667
+ const handleDeleteTenant = (0, import_react3.useCallback)(
668
+ async (tenant) => {
669
+ const status = await warn(
670
+ t("Saas::TenantDeletionConfirmationMessage").replace("{0}", tenant.name),
671
+ t("Saas::AreYouSure")
672
+ );
673
+ if (status === import_theme_shared.Toaster.Status.confirm) {
674
+ const result = await deleteTenant(tenant.id);
675
+ if (result.success) {
676
+ onTenantDeleted?.(tenant.id);
677
+ fetchTenants({
678
+ skipCount: page * pageSize,
679
+ maxResultCount: pageSize,
680
+ filter: filter || void 0,
681
+ getEditionNames: true
682
+ });
683
+ }
684
+ }
685
+ },
686
+ [warn, t, deleteTenant, onTenantDeleted, fetchTenants, page, pageSize, filter]
687
+ );
688
+ const handleSaveTenant = (0, import_react3.useCallback)(async () => {
689
+ if (!tenantName.trim()) return;
690
+ setModalBusy(true);
691
+ try {
692
+ if (selectedTenant?.id) {
693
+ const result = await updateTenant({
694
+ id: selectedTenant.id,
695
+ name: tenantName,
696
+ editionId: tenantEditionId || void 0,
697
+ concurrencyStamp: selectedTenant.concurrencyStamp
698
+ });
699
+ if (result.success && result.data) {
700
+ onTenantUpdated?.(result.data);
701
+ setModalVisible(false);
702
+ fetchTenants({
703
+ skipCount: page * pageSize,
704
+ maxResultCount: pageSize,
705
+ filter: filter || void 0,
706
+ getEditionNames: true
707
+ });
708
+ }
709
+ } else {
710
+ const result = await createTenant({
711
+ name: tenantName,
712
+ editionId: tenantEditionId || void 0
713
+ });
714
+ if (result.success && result.data) {
715
+ onTenantCreated?.(result.data);
716
+ setModalVisible(false);
717
+ fetchTenants({
718
+ skipCount: page * pageSize,
719
+ maxResultCount: pageSize,
720
+ filter: filter || void 0,
721
+ getEditionNames: true
722
+ });
723
+ }
724
+ }
725
+ } finally {
726
+ setModalBusy(false);
727
+ }
728
+ }, [
729
+ tenantName,
730
+ tenantEditionId,
731
+ selectedTenant,
732
+ updateTenant,
733
+ createTenant,
734
+ onTenantUpdated,
735
+ onTenantCreated,
736
+ fetchTenants,
737
+ page,
738
+ pageSize,
739
+ filter
740
+ ]);
741
+ const handleSaveConnectionString = (0, import_react3.useCallback)(async () => {
742
+ if (!selectedTenant?.id) return;
743
+ setModalBusy(true);
744
+ try {
745
+ if (connStringUseShared || !connString.trim()) {
746
+ await deleteDefaultConnectionString(selectedTenant.id);
747
+ } else {
748
+ await updateDefaultConnectionString({
749
+ id: selectedTenant.id,
750
+ defaultConnectionString: connString
751
+ });
752
+ }
753
+ setModalVisible(false);
754
+ } finally {
755
+ setModalBusy(false);
756
+ }
757
+ }, [
758
+ selectedTenant,
759
+ connStringUseShared,
760
+ connString,
761
+ deleteDefaultConnectionString,
762
+ updateDefaultConnectionString
763
+ ]);
764
+ const handleSave = (0, import_react3.useCallback)(() => {
765
+ if (modalType === "tenant") {
766
+ handleSaveTenant();
767
+ } else if (modalType === "connectionString") {
768
+ handleSaveConnectionString();
769
+ }
770
+ }, [modalType, handleSaveTenant, handleSaveConnectionString]);
771
+ const handleCloseModal = (0, import_react3.useCallback)(() => {
772
+ setModalVisible(false);
773
+ setModalType(null);
774
+ setSelectedTenant(null);
775
+ }, [setSelectedTenant]);
776
+ const totalPages = Math.ceil(totalCount / pageSize);
777
+ const getModalTitle = () => {
778
+ if (modalType === "connectionString") {
779
+ return t("Saas::ConnectionStrings");
780
+ }
781
+ return selectedTenant?.id ? t("Saas::Edit") : t("Saas::NewTenant");
782
+ };
783
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Box, { id: "saas-tenants-wrapper", className: "card", children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { justifyContent: "space-between", alignItems: "center", mb: 4, children: [
785
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Text, { fontSize: "xl", fontWeight: "bold", children: t("Saas::Tenants") }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.Button, { colorPalette: "blue", onClick: handleAddTenant, children: t("Saas::NewTenant") })
787
+ ] }),
788
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { gap: 2, mb: 4, children: [
789
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
790
+ import_react4.Input,
791
+ {
792
+ value: filter,
793
+ onChange: (e) => setFilter(e.target.value),
794
+ placeholder: t("AbpUi::PagerSearch"),
795
+ onKeyDown: (e) => e.key === "Enter" && handleSearch()
796
+ }
797
+ ),
798
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.Button, { onClick: handleSearch, children: t("AbpUi::Search") })
799
+ ] }),
800
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Box, { mb: 4, p: 3, bg: "red.100", borderRadius: "md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Text, { color: "red.800", children: error }) }),
801
+ isLoading && tenants.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Flex, { justifyContent: "center", p: 8, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Spinner, { size: "lg" }) }),
802
+ !isLoading && tenants.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Text, { textAlign: "center", p: 8, color: "gray.500", children: t("Saas::NoTenantsFound") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Box, { borderWidth: "1px", borderRadius: "md", overflow: "hidden", children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Table.Root, { children: [
804
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.Header, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Table.Row, { children: [
805
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.ColumnHeader, { children: t("Saas::Actions") }),
806
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.ColumnHeader, { children: t("Saas::TenantName") }),
807
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.ColumnHeader, { children: t("Saas::EditionName") })
808
+ ] }) }),
809
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.Body, { children: tenants.map((tenant) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Table.Row, { children: [
810
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.Cell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { gap: 1, children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
812
+ import_theme_shared.Button,
813
+ {
814
+ size: "sm",
815
+ colorPalette: "blue",
816
+ variant: "outline",
817
+ onClick: () => handleEditTenant(tenant.id),
818
+ children: t("Saas::Edit")
819
+ }
820
+ ),
821
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
822
+ import_theme_shared.Button,
823
+ {
824
+ size: "sm",
825
+ variant: "outline",
826
+ onClick: () => handleEditConnectionString(tenant.id),
827
+ children: t("Saas::ConnectionStrings")
828
+ }
829
+ ),
830
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
831
+ import_theme_shared.Button,
832
+ {
833
+ size: "sm",
834
+ colorPalette: "red",
835
+ variant: "outline",
836
+ onClick: () => handleDeleteTenant(tenant),
837
+ children: t("Saas::Delete")
838
+ }
839
+ )
840
+ ] }) }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.Cell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Text, { fontWeight: "medium", children: tenant.name }) }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Table.Cell, { children: tenant.editionName ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Badge, { colorPalette: "blue", children: tenant.editionName }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Text, { color: "gray.500", children: "-" }) })
843
+ ] }, tenant.id)) })
844
+ ] }),
845
+ totalCount > pageSize && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { justifyContent: "space-between", alignItems: "center", p: 4, borderTopWidth: "1px", children: [
846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react4.Text, { fontSize: "sm", children: `${page * pageSize + 1} - ${Math.min((page + 1) * pageSize, totalCount)} / ${totalCount}` }),
847
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { gap: 2, children: [
848
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
849
+ import_theme_shared.Button,
850
+ {
851
+ size: "sm",
852
+ disabled: page === 0,
853
+ onClick: () => setPage((p) => Math.max(0, p - 1)),
854
+ children: t("AbpUi::Previous")
855
+ }
856
+ ),
857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
858
+ import_theme_shared.Button,
859
+ {
860
+ size: "sm",
861
+ disabled: page >= totalPages - 1,
862
+ onClick: () => setPage((p) => p + 1),
863
+ children: t("AbpUi::Next")
864
+ }
865
+ )
866
+ ] })
867
+ ] })
868
+ ] }),
869
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
870
+ import_theme_shared.Modal,
871
+ {
872
+ visible: modalVisible,
873
+ onVisibleChange: setModalVisible,
874
+ header: getModalTitle(),
875
+ footer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { gap: 2, children: [
876
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.Button, { variant: "outline", onClick: handleCloseModal, children: t("Saas::Cancel") }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.Button, { colorPalette: "blue", onClick: handleSave, loading: modalBusy, children: t("AbpIdentity::Save") })
878
+ ] }),
879
+ children: [
880
+ modalType === "tenant" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.VStack, { gap: 4, align: "stretch", children: [
881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.FormField, { label: t("Saas::TenantName"), required: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
882
+ import_react4.Input,
883
+ {
884
+ value: tenantName,
885
+ onChange: (e) => setTenantName(e.target.value),
886
+ placeholder: t("Saas::TenantName"),
887
+ autoFocus: true
888
+ }
889
+ ) }),
890
+ editions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.FormField, { label: t("Saas::Edition"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react5.NativeSelectRoot, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
891
+ import_react5.NativeSelectField,
892
+ {
893
+ value: tenantEditionId,
894
+ onChange: (e) => setTenantEditionId(e.target.value),
895
+ children: [
896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "", children: t("Saas::SelectEdition") }),
897
+ editions.map((edition) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: edition.id, children: edition.displayName }, edition.id))
898
+ ]
899
+ }
900
+ ) }) })
901
+ ] }),
902
+ modalType === "connectionString" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.VStack, { gap: 4, align: "stretch", children: [
903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react4.Flex, { alignItems: "center", gap: 2, children: [
904
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
905
+ "input",
906
+ {
907
+ type: "checkbox",
908
+ id: "useSharedDatabase",
909
+ checked: connStringUseShared,
910
+ onChange: (e) => setConnStringUseShared(e.target.checked)
911
+ }
912
+ ),
913
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: "useSharedDatabase", children: t("Saas::DisplayName:UseSharedDatabase") })
914
+ ] }),
915
+ !connStringUseShared && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_theme_shared.FormField, { label: t("Saas::DisplayName:DefaultConnectionString"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
916
+ import_react4.Input,
917
+ {
918
+ value: connString,
919
+ onChange: (e) => setConnString(e.target.value),
920
+ placeholder: t("Saas::DisplayName:DefaultConnectionString")
921
+ }
922
+ ) })
923
+ ] })
924
+ ]
925
+ }
926
+ )
927
+ ] });
928
+ }
929
+
930
+ // src/components/Editions/EditionsComponent.tsx
931
+ var import_react6 = require("react");
932
+ var import_core4 = require("@abpjs/core");
933
+ var import_theme_shared2 = require("@abpjs/theme-shared");
934
+ var import_react7 = require("@chakra-ui/react");
935
+ var import_jsx_runtime2 = require("react/jsx-runtime");
936
+ function EditionsComponent({
937
+ onEditionCreated,
938
+ onEditionUpdated,
939
+ onEditionDeleted,
940
+ onManageFeatures
941
+ }) {
942
+ const { t } = (0, import_core4.useLocalization)();
943
+ const { warn } = (0, import_theme_shared2.useConfirmation)();
944
+ const {
945
+ editions,
946
+ totalCount,
947
+ selectedEdition,
948
+ isLoading,
949
+ error,
950
+ fetchEditions,
951
+ getEditionById,
952
+ createEdition,
953
+ updateEdition,
954
+ deleteEdition,
955
+ setSelectedEdition
956
+ } = useEditions();
957
+ const [modalVisible, setModalVisible] = (0, import_react6.useState)(false);
958
+ const [modalBusy, setModalBusy] = (0, import_react6.useState)(false);
959
+ const [page, setPage] = (0, import_react6.useState)(0);
960
+ const pageSize = 10;
961
+ const [filter, setFilter] = (0, import_react6.useState)("");
962
+ const [displayName, setDisplayName] = (0, import_react6.useState)("");
963
+ (0, import_react6.useEffect)(() => {
964
+ fetchEditions({
965
+ skipCount: page * pageSize,
966
+ maxResultCount: pageSize,
967
+ filter: filter || void 0
968
+ });
969
+ }, [page, pageSize, fetchEditions]);
970
+ const handleSearch = (0, import_react6.useCallback)(() => {
971
+ setPage(0);
972
+ fetchEditions({
973
+ skipCount: 0,
974
+ maxResultCount: pageSize,
975
+ filter: filter || void 0
976
+ });
977
+ }, [filter, pageSize, fetchEditions]);
978
+ const handleAddEdition = (0, import_react6.useCallback)(() => {
979
+ setSelectedEdition(null);
980
+ setDisplayName("");
981
+ setModalVisible(true);
982
+ }, [setSelectedEdition]);
983
+ const handleEditEdition = (0, import_react6.useCallback)(
984
+ async (id) => {
985
+ const result = await getEditionById(id);
986
+ if (result.success && result.data) {
987
+ setDisplayName(result.data.displayName);
988
+ setModalVisible(true);
989
+ }
990
+ },
991
+ [getEditionById]
992
+ );
993
+ const handleDeleteEdition = (0, import_react6.useCallback)(
994
+ async (edition) => {
995
+ const status = await warn(
996
+ t("Saas::EditionDeletionConfirmationMessage").replace("{0}", edition.displayName),
997
+ t("Saas::AreYouSure")
998
+ );
999
+ if (status === import_theme_shared2.Toaster.Status.confirm) {
1000
+ const result = await deleteEdition(edition.id);
1001
+ if (result.success) {
1002
+ onEditionDeleted?.(edition.id);
1003
+ fetchEditions({
1004
+ skipCount: page * pageSize,
1005
+ maxResultCount: pageSize,
1006
+ filter: filter || void 0
1007
+ });
1008
+ }
1009
+ }
1010
+ },
1011
+ [warn, t, deleteEdition, onEditionDeleted, fetchEditions, page, pageSize, filter]
1012
+ );
1013
+ const handleManageFeatures = (0, import_react6.useCallback)(
1014
+ (editionId) => {
1015
+ onManageFeatures?.(editionId);
1016
+ },
1017
+ [onManageFeatures]
1018
+ );
1019
+ const handleSave = (0, import_react6.useCallback)(async () => {
1020
+ if (!displayName.trim()) return;
1021
+ setModalBusy(true);
1022
+ try {
1023
+ if (selectedEdition?.id) {
1024
+ const result = await updateEdition({
1025
+ id: selectedEdition.id,
1026
+ displayName,
1027
+ concurrencyStamp: selectedEdition.concurrencyStamp
1028
+ });
1029
+ if (result.success && result.data) {
1030
+ onEditionUpdated?.(result.data);
1031
+ setModalVisible(false);
1032
+ fetchEditions({
1033
+ skipCount: page * pageSize,
1034
+ maxResultCount: pageSize,
1035
+ filter: filter || void 0
1036
+ });
1037
+ }
1038
+ } else {
1039
+ const result = await createEdition({
1040
+ displayName
1041
+ });
1042
+ if (result.success && result.data) {
1043
+ onEditionCreated?.(result.data);
1044
+ setModalVisible(false);
1045
+ fetchEditions({
1046
+ skipCount: page * pageSize,
1047
+ maxResultCount: pageSize,
1048
+ filter: filter || void 0
1049
+ });
1050
+ }
1051
+ }
1052
+ } finally {
1053
+ setModalBusy(false);
1054
+ }
1055
+ }, [
1056
+ displayName,
1057
+ selectedEdition,
1058
+ updateEdition,
1059
+ createEdition,
1060
+ onEditionUpdated,
1061
+ onEditionCreated,
1062
+ fetchEditions,
1063
+ page,
1064
+ pageSize,
1065
+ filter
1066
+ ]);
1067
+ const handleCloseModal = (0, import_react6.useCallback)(() => {
1068
+ setModalVisible(false);
1069
+ setSelectedEdition(null);
1070
+ }, [setSelectedEdition]);
1071
+ const totalPages = Math.ceil(totalCount / pageSize);
1072
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Box, { id: "saas-editions-wrapper", className: "card", children: [
1073
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Flex, { justifyContent: "space-between", alignItems: "center", mb: 4, children: [
1074
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Text, { fontSize: "xl", fontWeight: "bold", children: t("Saas::Editions") }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_theme_shared2.Button, { colorPalette: "blue", onClick: handleAddEdition, children: t("Saas::NewEdition") })
1076
+ ] }),
1077
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Flex, { gap: 2, mb: 4, children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1079
+ import_react7.Input,
1080
+ {
1081
+ value: filter,
1082
+ onChange: (e) => setFilter(e.target.value),
1083
+ placeholder: t("AbpUi::PagerSearch"),
1084
+ onKeyDown: (e) => e.key === "Enter" && handleSearch()
1085
+ }
1086
+ ),
1087
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_theme_shared2.Button, { onClick: handleSearch, children: t("AbpUi::Search") })
1088
+ ] }),
1089
+ error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Box, { mb: 4, p: 3, bg: "red.100", borderRadius: "md", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Text, { color: "red.800", children: error }) }),
1090
+ isLoading && editions.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Flex, { justifyContent: "center", p: 8, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Spinner, { size: "lg" }) }),
1091
+ !isLoading && editions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Text, { textAlign: "center", p: 8, color: "gray.500", children: t("Saas::NoEditionsFound") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Box, { borderWidth: "1px", borderRadius: "md", overflow: "hidden", children: [
1092
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Table.Root, { children: [
1093
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Table.Header, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Table.Row, { children: [
1094
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Table.ColumnHeader, { children: t("Saas::Actions") }),
1095
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Table.ColumnHeader, { children: t("Saas::EditionName") })
1096
+ ] }) }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Table.Body, { children: editions.map((edition) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Table.Row, { children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Table.Cell, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Flex, { gap: 1, children: [
1099
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1100
+ import_theme_shared2.Button,
1101
+ {
1102
+ size: "sm",
1103
+ colorPalette: "blue",
1104
+ variant: "outline",
1105
+ onClick: () => handleEditEdition(edition.id),
1106
+ children: t("Saas::Edit")
1107
+ }
1108
+ ),
1109
+ onManageFeatures && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1110
+ import_theme_shared2.Button,
1111
+ {
1112
+ size: "sm",
1113
+ variant: "outline",
1114
+ onClick: () => handleManageFeatures(edition.id),
1115
+ children: t("Saas::Features")
1116
+ }
1117
+ ),
1118
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1119
+ import_theme_shared2.Button,
1120
+ {
1121
+ size: "sm",
1122
+ colorPalette: "red",
1123
+ variant: "outline",
1124
+ onClick: () => handleDeleteEdition(edition),
1125
+ children: t("Saas::Delete")
1126
+ }
1127
+ )
1128
+ ] }) }),
1129
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Table.Cell, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Text, { fontWeight: "medium", children: edition.displayName }) })
1130
+ ] }, edition.id)) })
1131
+ ] }),
1132
+ totalCount > pageSize && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Flex, { justifyContent: "space-between", alignItems: "center", p: 4, borderTopWidth: "1px", children: [
1133
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.Text, { fontSize: "sm", children: `${page * pageSize + 1} - ${Math.min((page + 1) * pageSize, totalCount)} / ${totalCount}` }),
1134
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Flex, { gap: 2, children: [
1135
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1136
+ import_theme_shared2.Button,
1137
+ {
1138
+ size: "sm",
1139
+ disabled: page === 0,
1140
+ onClick: () => setPage((p) => Math.max(0, p - 1)),
1141
+ children: t("AbpUi::Previous")
1142
+ }
1143
+ ),
1144
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1145
+ import_theme_shared2.Button,
1146
+ {
1147
+ size: "sm",
1148
+ disabled: page >= totalPages - 1,
1149
+ onClick: () => setPage((p) => p + 1),
1150
+ children: t("AbpUi::Next")
1151
+ }
1152
+ )
1153
+ ] })
1154
+ ] })
1155
+ ] }),
1156
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1157
+ import_theme_shared2.Modal,
1158
+ {
1159
+ visible: modalVisible,
1160
+ onVisibleChange: setModalVisible,
1161
+ header: selectedEdition?.id ? t("Saas::Edit") : t("Saas::NewEdition"),
1162
+ footer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react7.Flex, { gap: 2, children: [
1163
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_theme_shared2.Button, { variant: "outline", onClick: handleCloseModal, children: t("Saas::Cancel") }),
1164
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_theme_shared2.Button, { colorPalette: "blue", onClick: handleSave, loading: modalBusy, children: t("AbpIdentity::Save") })
1165
+ ] }),
1166
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react7.VStack, { gap: 4, align: "stretch", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_theme_shared2.FormField, { label: t("Saas::EditionName"), required: true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1167
+ import_react7.Input,
1168
+ {
1169
+ value: displayName,
1170
+ onChange: (e) => setDisplayName(e.target.value),
1171
+ placeholder: t("Saas::EditionName"),
1172
+ autoFocus: true
1173
+ }
1174
+ ) }) })
1175
+ }
1176
+ )
1177
+ ] });
1178
+ }
1179
+ // Annotate the CommonJS export names for ESM import in node:
1180
+ 0 && (module.exports = {
1181
+ EditionsComponent,
1182
+ SAAS_ROUTES,
1183
+ SaasService,
1184
+ TenantsComponent,
1185
+ useEditions,
1186
+ useTenants
1187
+ });