adminforth 2.17.0-next.5 → 2.17.0-next.50
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/commands/callTsProxy.js +2 -1
- package/commands/createApp/templates/adminuser.ts.hbs +2 -1
- package/commands/createApp/templates/index.ts.hbs +3 -2
- package/commands/createCustomComponent/main.js +0 -3
- package/commands/createCustomComponent/templates/customCrud/afterBreadcrumbs.vue.hbs +4 -2
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +3 -2
- package/commands/createCustomComponent/templates/customCrud/beforeBreadcrumbs.vue.hbs +4 -2
- package/commands/createCustomComponent/templates/customCrud/bottom.vue.hbs +4 -2
- package/commands/createCustomComponent/templates/customCrud/threeDotsDropdownItems.vue.hbs +4 -2
- package/commands/createPlugin/templates/index.ts.hbs +4 -0
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +17 -10
- package/dist/auth.js.map +1 -1
- package/dist/basePlugin.d.ts +1 -0
- package/dist/basePlugin.d.ts.map +1 -1
- package/dist/basePlugin.js +4 -2
- package/dist/basePlugin.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts +1 -0
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +100 -14
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts +2 -0
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -4
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts +8 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +72 -28
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts +2 -0
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +22 -23
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts +2 -0
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +23 -26
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts +2 -0
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +19 -19
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +60 -54
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +45 -63
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +14 -9
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/logger.d.ts +5 -0
- package/dist/modules/logger.d.ts.map +1 -0
- package/dist/modules/logger.js +16 -0
- package/dist/modules/logger.js.map +1 -0
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +21 -23
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/socketBroker.d.ts.map +1 -1
- package/dist/modules/socketBroker.js +6 -5
- package/dist/modules/socketBroker.js.map +1 -1
- package/dist/modules/styles.js +1 -1
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +11 -11
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/package-lock.json +0 -13
- package/dist/spa/package.json +0 -1
- package/dist/spa/src/App.vue +6 -3
- package/dist/spa/src/adminforth.ts +60 -1
- package/dist/spa/src/afcl/DatePicker.vue +0 -1
- package/dist/spa/src/afcl/Dropzone.vue +6 -4
- package/dist/spa/src/afcl/Tooltip.vue +38 -4
- package/dist/spa/src/components/ColumnValueInput.vue +14 -1
- package/dist/spa/src/components/CustomDateRangePicker.vue +0 -2
- package/dist/spa/src/components/CustomRangePicker.vue +9 -6
- package/dist/spa/src/components/Filters.vue +4 -4
- package/dist/spa/src/components/ListActionsThreeDots.vue +235 -0
- package/dist/spa/src/components/ResourceForm.vue +4 -4
- package/dist/spa/src/components/ResourceListTable.vue +30 -16
- package/dist/spa/src/components/ResourceListTableVirtual.vue +34 -18
- package/dist/spa/src/components/Sidebar.vue +4 -2
- package/dist/spa/src/components/ThreeDotsMenu.vue +35 -20
- package/dist/spa/src/composables/useFrontendApi.ts +8 -4
- package/dist/spa/src/renderers/CompactField.vue +3 -2
- package/dist/spa/src/renderers/CompactUUID.vue +3 -2
- package/dist/spa/src/renderers/RichText.vue +15 -0
- package/dist/spa/src/stores/core.ts +3 -2
- package/dist/spa/src/stores/filters.ts +1 -1
- package/dist/spa/src/stores/toast.ts +1 -2
- package/dist/spa/src/types/Back.ts +34 -11
- package/dist/spa/src/types/Common.ts +7 -14
- package/dist/spa/src/types/FrontendAPI.ts +25 -10
- package/dist/spa/src/utils.ts +1 -1
- package/dist/spa/src/views/CreateView.vue +23 -31
- package/dist/spa/src/views/EditView.vue +27 -31
- package/dist/spa/src/views/ListView.vue +20 -10
- package/dist/spa/src/views/SettingsView.vue +3 -2
- package/dist/spa/src/views/ShowView.vue +7 -6
- package/dist/types/Back.d.ts +27 -5
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +6 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +8 -15
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js +2 -0
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +32 -10
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/package.json +4 -1
- package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +0 -28
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Express, Request } from 'express';
|
|
2
2
|
import type { Writable } from 'stream';
|
|
3
3
|
|
|
4
|
-
import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections, AllowedActionsEnum,
|
|
4
|
+
import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections, AllowedActionsEnum, AdminForthResourcePages,
|
|
5
5
|
type AdminForthComponentDeclaration,
|
|
6
6
|
type AdminForthResourceCommon,
|
|
7
7
|
type AdminUser, type AllowedActionsResolved,
|
|
@@ -12,7 +12,6 @@ import { ActionCheckSource, AdminForthFilterOperators, AdminForthSortDirections,
|
|
|
12
12
|
type AdminForthComponentDeclarationFull,
|
|
13
13
|
type AdminForthConfigMenuItem,
|
|
14
14
|
type AnnouncementBadgeResponse,
|
|
15
|
-
AdminForthResourcePages,
|
|
16
15
|
type AdminForthResourceColumnInputCommon,
|
|
17
16
|
} from './Common.js';
|
|
18
17
|
|
|
@@ -129,7 +128,7 @@ export interface IAdminForthSingleFilter {
|
|
|
129
128
|
operator?: AdminForthFilterOperators.EQ | AdminForthFilterOperators.NE
|
|
130
129
|
| AdminForthFilterOperators.GT | AdminForthFilterOperators.LT | AdminForthFilterOperators.GTE
|
|
131
130
|
| AdminForthFilterOperators.LTE | AdminForthFilterOperators.LIKE | AdminForthFilterOperators.ILIKE
|
|
132
|
-
| AdminForthFilterOperators.IN | AdminForthFilterOperators.NIN;
|
|
131
|
+
| AdminForthFilterOperators.IN | AdminForthFilterOperators.NIN | AdminForthFilterOperators.IS_EMPTY | AdminForthFilterOperators.IS_NOT_EMPTY;
|
|
133
132
|
value?: any;
|
|
134
133
|
rightField?: string;
|
|
135
134
|
insecureRawSQL?: string;
|
|
@@ -307,7 +306,7 @@ export interface IAdminForthAuth {
|
|
|
307
306
|
|
|
308
307
|
removeCustomCookie({response, name}: {response: any, name: string}): void;
|
|
309
308
|
|
|
310
|
-
setCustomCookie({response, payload}: {response: any, payload: {name: string, value: string, expiry
|
|
309
|
+
setCustomCookie({response, payload}: {response: any, payload: {name: string, value: string, expiry?: number, expirySeconds: number, httpOnly: boolean}}): void;
|
|
311
310
|
|
|
312
311
|
getCustomCookie({cookies, name}: {cookies: {key: string, value: string}[], name: string}): string | null;
|
|
313
312
|
|
|
@@ -360,16 +359,16 @@ export interface IAdminForth {
|
|
|
360
359
|
tr(msg: string, category: string, lang: string, params: any, pluralizationNumber?: number): Promise<string>;
|
|
361
360
|
|
|
362
361
|
createResourceRecord(
|
|
363
|
-
params: { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra }
|
|
362
|
+
params: { resource: AdminForthResource, record: any, response: IAdminForthHttpResponse, adminUser: AdminUser, extra?: HttpExtra }
|
|
364
363
|
): Promise<{ error?: string, createdRecord?: any, newRecordId?: any }>;
|
|
365
364
|
|
|
366
365
|
updateResourceRecord(
|
|
367
|
-
params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates?: never }
|
|
368
|
-
| { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates: any }
|
|
366
|
+
params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, response: IAdminForthHttpResponse, extra?: HttpExtra, updates?: never }
|
|
367
|
+
| { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, response: IAdminForthHttpResponse, extra?: HttpExtra, updates: any }
|
|
369
368
|
): Promise<{ error?: string }>;
|
|
370
369
|
|
|
371
370
|
deleteResourceRecord(
|
|
372
|
-
params: { resource: AdminForthResource, recordId: string, adminUser: AdminUser, record: any, extra?: HttpExtra }
|
|
371
|
+
params: { resource: AdminForthResource, recordId: string, adminUser: AdminUser, record: any, response: IAdminForthHttpResponse, extra?: HttpExtra }
|
|
373
372
|
): Promise<{ error?: string }>;
|
|
374
373
|
|
|
375
374
|
auth: IAdminForthAuth;
|
|
@@ -469,6 +468,13 @@ export interface IAdminForthPlugin {
|
|
|
469
468
|
instanceUniqueRepresentation(pluginOptions: any) : string;
|
|
470
469
|
|
|
471
470
|
|
|
471
|
+
/**
|
|
472
|
+
* If this method returns true, AdminForth will allow only one instance of plugin per whole app
|
|
473
|
+
* (only for case when we are creating copy of resource and activating plugins)
|
|
474
|
+
* If false, multiple instances of plugin can be installed on different resources.
|
|
475
|
+
*/
|
|
476
|
+
shouldHaveSingleInstancePerWholeApp?(): boolean;
|
|
477
|
+
|
|
472
478
|
/**
|
|
473
479
|
* Optional method which will be called after AdminForth discovers all resources and their columns.
|
|
474
480
|
* Can be used to validate types of columns, check if some columns are missing, etc.
|
|
@@ -539,6 +545,7 @@ export type BeforeDeleteSaveFunction = (params: {
|
|
|
539
545
|
adminUser: AdminUser,
|
|
540
546
|
record: any,
|
|
541
547
|
adminforth: IAdminForth,
|
|
548
|
+
response: IAdminForthHttpResponse,
|
|
542
549
|
extra?: HttpExtra,
|
|
543
550
|
}) => Promise<{ok: boolean, error?: string}>;
|
|
544
551
|
|
|
@@ -551,6 +558,7 @@ export type BeforeEditSaveFunction = (params: {
|
|
|
551
558
|
record: any, // legacy, 'updates' should be used instead
|
|
552
559
|
oldRecord: any,
|
|
553
560
|
adminforth: IAdminForth,
|
|
561
|
+
response: IAdminForthHttpResponse,
|
|
554
562
|
extra?: HttpExtra,
|
|
555
563
|
}) => Promise<{ok: boolean, error?: string | null}>;
|
|
556
564
|
|
|
@@ -561,6 +569,7 @@ export type BeforeCreateSaveFunction = (params: {
|
|
|
561
569
|
adminUser: AdminUser,
|
|
562
570
|
record: any,
|
|
563
571
|
adminforth: IAdminForth,
|
|
572
|
+
response: IAdminForthHttpResponse,
|
|
564
573
|
extra?: HttpExtra,
|
|
565
574
|
}) => Promise<{ok: boolean, error?: string | null, newRecordId?: string}>;
|
|
566
575
|
|
|
@@ -571,6 +580,7 @@ export type AfterCreateSaveFunction = (params: {
|
|
|
571
580
|
record: any,
|
|
572
581
|
adminforth: IAdminForth,
|
|
573
582
|
recordWithVirtualColumns?: any,
|
|
583
|
+
response: IAdminForthHttpResponse,
|
|
574
584
|
extra?: HttpExtra,
|
|
575
585
|
}) => Promise<{ok: boolean, error?: string}>;
|
|
576
586
|
|
|
@@ -584,6 +594,7 @@ export type AfterDeleteSaveFunction = (params: {
|
|
|
584
594
|
adminUser: AdminUser,
|
|
585
595
|
record: any,
|
|
586
596
|
adminforth: IAdminForth,
|
|
597
|
+
response: IAdminForthHttpResponse,
|
|
587
598
|
extra?: HttpExtra,
|
|
588
599
|
}) => Promise<{ok: boolean, error?: string}>;
|
|
589
600
|
|
|
@@ -596,6 +607,7 @@ export type AfterEditSaveFunction = (params: {
|
|
|
596
607
|
record: any, // legacy, 'updates' should be used instead
|
|
597
608
|
oldRecord: any,
|
|
598
609
|
adminforth: IAdminForth,
|
|
610
|
+
response: IAdminForthHttpResponse,
|
|
599
611
|
extra?: HttpExtra,
|
|
600
612
|
}) => Promise<{ok: boolean, error?: string}>;
|
|
601
613
|
|
|
@@ -848,6 +860,7 @@ export interface AdminForthActionInput {
|
|
|
848
860
|
name: string;
|
|
849
861
|
showIn?: {
|
|
850
862
|
list?: boolean,
|
|
863
|
+
listThreeDotsMenu?: boolean,
|
|
851
864
|
showButton?: boolean,
|
|
852
865
|
showThreeDotsMenu?: boolean,
|
|
853
866
|
};
|
|
@@ -861,6 +874,7 @@ export interface AdminForthActionInput {
|
|
|
861
874
|
resource: AdminForthResource;
|
|
862
875
|
recordId: string;
|
|
863
876
|
adminUser: AdminUser;
|
|
877
|
+
response: IAdminForthHttpResponse;
|
|
864
878
|
extra?: HttpExtra;
|
|
865
879
|
tr: Function;
|
|
866
880
|
}) => Promise<{
|
|
@@ -1102,7 +1116,7 @@ export interface AdminForthInputConfig {
|
|
|
1102
1116
|
/**
|
|
1103
1117
|
* Add custom page to the settings page
|
|
1104
1118
|
*/
|
|
1105
|
-
userMenuSettingsPages
|
|
1119
|
+
userMenuSettingsPages?: {
|
|
1106
1120
|
icon?: string,
|
|
1107
1121
|
pageLabel: string,
|
|
1108
1122
|
slug?: string,
|
|
@@ -1282,6 +1296,14 @@ export class Filters {
|
|
|
1282
1296
|
subFilters,
|
|
1283
1297
|
};
|
|
1284
1298
|
}
|
|
1299
|
+
|
|
1300
|
+
static IS_EMPTY(field: string): IAdminForthSingleFilter {
|
|
1301
|
+
return { field, operator: AdminForthFilterOperators.IS_EMPTY, value: null };
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
static IS_NOT_EMPTY(field: string): IAdminForthSingleFilter {
|
|
1305
|
+
return { field, operator: AdminForthFilterOperators.IS_NOT_EMPTY, value: null };
|
|
1306
|
+
}
|
|
1285
1307
|
}
|
|
1286
1308
|
|
|
1287
1309
|
export type FDataSort = (field: string, direction: AdminForthSortDirections) => IAdminForthSort;
|
|
@@ -1358,6 +1380,7 @@ export type AllowedActions = {
|
|
|
1358
1380
|
*/
|
|
1359
1381
|
export interface ResourceOptionsInput extends Omit<NonNullable<AdminForthResourceInputCommon['options']>, 'allowedActions' | 'bulkActions'> {
|
|
1360
1382
|
|
|
1383
|
+
baseActionsAsQuickIcons?: ('show' | 'edit' | 'delete')[],
|
|
1361
1384
|
/**
|
|
1362
1385
|
* Custom bulk actions list. Bulk actions available in list view when user selects multiple records by
|
|
1363
1386
|
* using checkboxes.
|
|
@@ -1526,8 +1549,8 @@ export interface AdminForthBulkAction extends AdminForthBulkActionCommon {
|
|
|
1526
1549
|
* Callback which will be called on backend when user clicks on action button.
|
|
1527
1550
|
* It should return Promise which will be resolved when action is done.
|
|
1528
1551
|
*/
|
|
1529
|
-
action: ({ resource, selectedIds, adminUser, tr }: {
|
|
1530
|
-
resource: AdminForthResource, selectedIds: Array<any>, adminUser: AdminUser, tr: (key: string, category?: string, params?: any) => string
|
|
1552
|
+
action: ({ resource, selectedIds, adminUser, response, tr }: {
|
|
1553
|
+
resource: AdminForthResource, selectedIds: Array<any>, adminUser: AdminUser, response: IAdminForthHttpResponse, tr: (key: string, category?: string, params?: any) => string
|
|
1531
1554
|
}) => Promise<{ ok: boolean, error?: string, successMessage?: string }>,
|
|
1532
1555
|
|
|
1533
1556
|
/**
|
|
@@ -28,6 +28,8 @@ export enum AdminForthFilterOperators {
|
|
|
28
28
|
NIN = 'nin',
|
|
29
29
|
AND = 'and',
|
|
30
30
|
OR = 'or',
|
|
31
|
+
IS_EMPTY = 'isEmpty',
|
|
32
|
+
IS_NOT_EMPTY = 'isNotEmpty',
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
export type FilterParams = {
|
|
@@ -294,7 +296,7 @@ export interface AdminForthComponentDeclarationFull {
|
|
|
294
296
|
[key: string]: any,
|
|
295
297
|
}
|
|
296
298
|
}
|
|
297
|
-
import { type AdminForthActionInput } from './Back.js'
|
|
299
|
+
import { type AdminForthActionInput, type AdminForthResource } from './Back.js'
|
|
298
300
|
export { type AdminForthActionInput } from './Back.js'
|
|
299
301
|
|
|
300
302
|
export type AdminForthComponentDeclaration = AdminForthComponentDeclarationFull | string;
|
|
@@ -445,7 +447,7 @@ export interface AdminForthResourceInputCommon {
|
|
|
445
447
|
* If you wish to open page in new tab, add `target=_blank` get param to returned URL, example:
|
|
446
448
|
*
|
|
447
449
|
* ```ts
|
|
448
|
-
* listTableClickUrl: async (record, adminUser) => {
|
|
450
|
+
* listTableClickUrl: async (record, adminUser, resource) => {
|
|
449
451
|
* return `https://google.com/search?q=${record.name}&target=_blank`;
|
|
450
452
|
* }
|
|
451
453
|
* ```
|
|
@@ -455,7 +457,7 @@ export interface AdminForthResourceInputCommon {
|
|
|
455
457
|
* Example:
|
|
456
458
|
*
|
|
457
459
|
* ```ts
|
|
458
|
-
* listTableClickUrl: async (record, adminUser) => {
|
|
460
|
+
* listTableClickUrl: async (record, adminUser, resource) => {
|
|
459
461
|
* return null;
|
|
460
462
|
* }
|
|
461
463
|
* ```
|
|
@@ -464,7 +466,7 @@ export interface AdminForthResourceInputCommon {
|
|
|
464
466
|
* @param adminUser - user who clicked
|
|
465
467
|
* @returns
|
|
466
468
|
*/
|
|
467
|
-
listTableClickUrl?: (record: any, adminUser: AdminUser) => Promise<string | null>,
|
|
469
|
+
listTableClickUrl?: (record: any, adminUser: AdminUser, resource: AdminForthResource) => Promise<string | null>,
|
|
468
470
|
|
|
469
471
|
/**
|
|
470
472
|
* Whether to refresh existing list rows automatically every N seconds.
|
|
@@ -503,6 +505,7 @@ export interface AdminForthResourceInputCommon {
|
|
|
503
505
|
bottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
504
506
|
threeDotsDropdownItems?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
505
507
|
customActionIcons?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
508
|
+
customActionIconsThreeDotsMenuItems?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
506
509
|
tableBodyStart?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
507
510
|
tableRowReplace?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
508
511
|
},
|
|
@@ -529,11 +532,6 @@ export interface AdminForthResourceInputCommon {
|
|
|
529
532
|
afterBreadcrumbs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
530
533
|
bottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
531
534
|
threeDotsDropdownItems?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
532
|
-
/**
|
|
533
|
-
* Custom Save button component for Edit page.
|
|
534
|
-
* Accepts props: [record, resource, adminUser, meta, saving, validating, isValid, disabled, saveRecord]
|
|
535
|
-
*/
|
|
536
|
-
saveButton?: AdminForthComponentDeclaration,
|
|
537
535
|
},
|
|
538
536
|
|
|
539
537
|
/**
|
|
@@ -546,11 +544,6 @@ export interface AdminForthResourceInputCommon {
|
|
|
546
544
|
afterBreadcrumbs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
547
545
|
bottom?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
548
546
|
threeDotsDropdownItems?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
549
|
-
/**
|
|
550
|
-
* Custom Save button component for Create page.
|
|
551
|
-
* Accepts props: [record, resource, adminUser, meta, saving, validating, isValid, disabled, saveRecord]
|
|
552
|
-
*/
|
|
553
|
-
saveButton?: AdminForthComponentDeclaration,
|
|
554
547
|
},
|
|
555
548
|
}
|
|
556
549
|
},
|
|
@@ -10,9 +10,10 @@ export interface FrontendAPIInterface {
|
|
|
10
10
|
* Example:
|
|
11
11
|
*
|
|
12
12
|
* ```ts
|
|
13
|
-
*
|
|
13
|
+
*import { useAdminforth } from '@/adminforth';
|
|
14
14
|
*
|
|
15
|
-
* const
|
|
15
|
+
* const { confirm } = useAdminforth();
|
|
16
|
+
* const isConfirmed = await confirm({message: 'Are you sure?', yes: 'Yes', no: 'No'})
|
|
16
17
|
* if (isConfirmed) {
|
|
17
18
|
* your code...
|
|
18
19
|
* }
|
|
@@ -31,9 +32,10 @@ export interface FrontendAPIInterface {
|
|
|
31
32
|
* Example:
|
|
32
33
|
*
|
|
33
34
|
* ```ts
|
|
34
|
-
* import
|
|
35
|
+
* import { useAdminforth } from '@/adminforth';
|
|
36
|
+
* const { alert } = useAdminforth();
|
|
35
37
|
*
|
|
36
|
-
*
|
|
38
|
+
* alert({message: 'Hello', variant: 'success'})
|
|
37
39
|
* ```
|
|
38
40
|
*
|
|
39
41
|
* @param params - The parameters of the alert
|
|
@@ -76,13 +78,14 @@ export interface FrontendAPIInterface {
|
|
|
76
78
|
* Example:
|
|
77
79
|
*
|
|
78
80
|
* ```ts
|
|
79
|
-
* import
|
|
81
|
+
* import { useAdminforth } from '@/adminforth';
|
|
80
82
|
*
|
|
83
|
+
* const { list } = useAdminforth();
|
|
81
84
|
* // Regular filter (will show in badge if column.showIn.filter !== false)
|
|
82
|
-
*
|
|
85
|
+
* list.setFilter({field: 'name', operator: 'ilike', value: 'john'})
|
|
83
86
|
*
|
|
84
87
|
* // Hidden filter (won't show in badge if column.showIn.filter === false)
|
|
85
|
-
*
|
|
88
|
+
* list.setFilter({field: 'internal_status', operator: 'eq', value: 'active'})
|
|
86
89
|
* ```
|
|
87
90
|
*
|
|
88
91
|
* Please note that you can set/update filter even for fields which have showIn.filter=false in resource configuration.
|
|
@@ -102,9 +105,9 @@ export interface FrontendAPIInterface {
|
|
|
102
105
|
* Example:
|
|
103
106
|
*
|
|
104
107
|
* ```ts
|
|
105
|
-
* import
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
+
* import { useAdminforth } from '@/adminforth';
|
|
109
|
+
* const { list } = useAdminforth();
|
|
110
|
+
* list.updateFilter({field: 'name', operator: 'ilike', value: 'john'})
|
|
108
111
|
* ```
|
|
109
112
|
*
|
|
110
113
|
* @param filter - The filter to update
|
|
@@ -136,6 +139,18 @@ export interface FrontendAPIInterface {
|
|
|
136
139
|
* Close the user menu dropdown
|
|
137
140
|
*/
|
|
138
141
|
closeUserMenuDropdown(): void;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Run save interceptors for a specific resource or all resources if no resourceId is provided
|
|
145
|
+
*/
|
|
146
|
+
runSaveInterceptors(params: { action: 'create'|'edit'; values: any; resource: any; resourceId: string; }): Promise<{ ok: boolean; error?: string | null; extra?: object; }>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Clear save interceptors for a specific resource or all resources if no resourceId is provided
|
|
150
|
+
*
|
|
151
|
+
* @param resourceId - The resource ID to clear interceptors for
|
|
152
|
+
*/
|
|
153
|
+
clearSaveInterceptors(resourceId?: string): void;
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
export type ConfirmParams = {
|
package/dist/spa/src/utils.ts
CHANGED
|
@@ -468,7 +468,7 @@ export function checkShowIf(c: AdminForthResourceColumnInputCommon, record: Reco
|
|
|
468
468
|
const fieldEntries = Object.entries(predicate).filter(([key]) => !key.startsWith('$'));
|
|
469
469
|
if (fieldEntries.length > 0) {
|
|
470
470
|
const fieldResult = fieldEntries.every(([field, condition]) => {
|
|
471
|
-
const recordValue =
|
|
471
|
+
const recordValue = recordCopy[field];
|
|
472
472
|
|
|
473
473
|
if (condition === undefined) {
|
|
474
474
|
return true;
|
|
@@ -17,25 +17,7 @@
|
|
|
17
17
|
>
|
|
18
18
|
{{ $t('Cancel') }}
|
|
19
19
|
</button>
|
|
20
|
-
|
|
21
|
-
<!-- Custom Save Button injection -->
|
|
22
|
-
<component
|
|
23
|
-
v-if="createSaveButtonInjection"
|
|
24
|
-
:is="getCustomComponent(createSaveButtonInjection)"
|
|
25
|
-
:meta="createSaveButtonInjection.meta"
|
|
26
|
-
:record="record"
|
|
27
|
-
:resource="coreStore.resource"
|
|
28
|
-
:adminUser="coreStore.adminUser"
|
|
29
|
-
:saving="saving"
|
|
30
|
-
:validating="validating"
|
|
31
|
-
:isValid="isValid"
|
|
32
|
-
:disabled="saving || (validating && !isValid)"
|
|
33
|
-
:saveRecord="saveRecord"
|
|
34
|
-
/>
|
|
35
|
-
|
|
36
|
-
<!-- Default Save Button fallback -->
|
|
37
20
|
<button
|
|
38
|
-
v-else
|
|
39
21
|
@click="() => saveRecord()"
|
|
40
22
|
class="af-save-button flex items-center py-1 px-3 text-sm font-medium rounded-default text-lightCreateViewSaveButtonText focus:outline-none bg-lightCreateViewButtonBackground rounded border border-lightCreateViewButtonBorder hover:bg-lightCreateViewButtonBackgroundHover hover:text-lightCreateViewSaveButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightCreateViewButtonFocusRing dark:focus:ring-darkCreateViewButtonFocusRing dark:bg-darkCreateViewButtonBackground dark:text-darkCreateViewSaveButtonText dark:border-darkCreateViewButtonBorder dark:hover:text-darkCreateViewSaveButtonTextHover dark:hover:bg-darkCreateViewButtonBackgroundHover disabled:opacity-50 gap-1"
|
|
41
23
|
:disabled="saving || (validating && !isValid)"
|
|
@@ -99,12 +81,12 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
|
99
81
|
import { useCoreStore } from '@/stores/core';
|
|
100
82
|
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown, checkShowIf } from '@/utils';
|
|
101
83
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
102
|
-
import { onMounted, ref, watch, nextTick } from 'vue';
|
|
84
|
+
import { onMounted, onBeforeMount, ref, watch, nextTick } from 'vue';
|
|
103
85
|
import { useRoute, useRouter } from 'vue-router';
|
|
104
86
|
import { computed } from 'vue';
|
|
105
87
|
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
106
88
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
107
|
-
import
|
|
89
|
+
import { useAdminforth } from '@/adminforth';
|
|
108
90
|
import { useI18n } from 'vue-i18n';
|
|
109
91
|
import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
|
|
110
92
|
import type { AdminForthResourceColumn } from '@/types/Back';
|
|
@@ -121,18 +103,12 @@ const router = useRouter();
|
|
|
121
103
|
const record = ref({});
|
|
122
104
|
|
|
123
105
|
const coreStore = useCoreStore();
|
|
106
|
+
const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
|
|
124
107
|
|
|
125
108
|
const { t } = useI18n();
|
|
126
109
|
|
|
127
110
|
const resourceFormRef = ref<InstanceType<typeof ResourceForm> | null>(null);
|
|
128
111
|
|
|
129
|
-
const createSaveButtonInjection = computed<AdminForthComponentDeclarationFull | null>(() => {
|
|
130
|
-
const raw: any = coreStore.resourceOptions?.pageInjections?.create?.saveButton as any;
|
|
131
|
-
if (!raw) return null;
|
|
132
|
-
const item = Array.isArray(raw) ? raw[0] : raw;
|
|
133
|
-
return item as AdminForthComponentDeclarationFull;
|
|
134
|
-
});
|
|
135
|
-
|
|
136
112
|
const initialValues = ref({});
|
|
137
113
|
|
|
138
114
|
const readonlyColumns = ref([]);
|
|
@@ -142,6 +118,10 @@ async function onUpdateRecord(newRecord: any) {
|
|
|
142
118
|
record.value = newRecord;
|
|
143
119
|
}
|
|
144
120
|
|
|
121
|
+
onBeforeMount(() => {
|
|
122
|
+
clearSaveInterceptors(route.params.resourceId as string);
|
|
123
|
+
});
|
|
124
|
+
|
|
145
125
|
onMounted(async () => {
|
|
146
126
|
loading.value = true;
|
|
147
127
|
await coreStore.fetchResourceFull({
|
|
@@ -182,7 +162,7 @@ onMounted(async () => {
|
|
|
182
162
|
initThreeDotsDropdown();
|
|
183
163
|
});
|
|
184
164
|
|
|
185
|
-
async function saveRecord(
|
|
165
|
+
async function saveRecord() {
|
|
186
166
|
if (!isValid.value) {
|
|
187
167
|
validating.value = true;
|
|
188
168
|
await nextTick();
|
|
@@ -194,6 +174,18 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
|
|
|
194
174
|
const requiredColumns = coreStore.resource?.columns.filter(c => c.required?.create === true) || [];
|
|
195
175
|
const requiredColumnsToSkip = requiredColumns.filter(c => checkShowIf(c, record.value, coreStore.resource?.columns || []) === false);
|
|
196
176
|
saving.value = true;
|
|
177
|
+
const interceptorsResult = await runSaveInterceptors({
|
|
178
|
+
action: 'create',
|
|
179
|
+
values: record.value,
|
|
180
|
+
resource: coreStore.resource,
|
|
181
|
+
resourceId: route.params.resourceId as string,
|
|
182
|
+
});
|
|
183
|
+
if (!interceptorsResult.ok) {
|
|
184
|
+
saving.value = false;
|
|
185
|
+
if (interceptorsResult.error) showErrorTost(interceptorsResult.error);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const interceptorConfirmationResult = (interceptorsResult.extra as Record<string, any>)?.confirmationResult;
|
|
197
189
|
const response = await callAdminForthApi({
|
|
198
190
|
method: 'POST',
|
|
199
191
|
path: `/create_record`,
|
|
@@ -202,7 +194,7 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
|
|
|
202
194
|
record: record.value,
|
|
203
195
|
requiredColumnsToSkip,
|
|
204
196
|
meta: {
|
|
205
|
-
...(
|
|
197
|
+
...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
|
|
206
198
|
},
|
|
207
199
|
},
|
|
208
200
|
});
|
|
@@ -220,7 +212,7 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
|
|
|
220
212
|
primaryKey: response.newRecordId
|
|
221
213
|
}
|
|
222
214
|
});
|
|
223
|
-
|
|
215
|
+
alert({
|
|
224
216
|
message: t('Record created successfully!'),
|
|
225
217
|
variant: 'success'
|
|
226
218
|
});
|
|
@@ -236,7 +228,7 @@ function scrollToInvalidField() {
|
|
|
236
228
|
}
|
|
237
229
|
}
|
|
238
230
|
const errorMessage = t('Failed to save. Please fix errors for the following fields:') + '<ul class="mt-2 list-disc list-inside">' + columnsWithErrors.map(c => `<li><strong>${c.column.label || c.column.name}</strong>: ${c.error}</li>`).join('') + '</ul>';
|
|
239
|
-
|
|
231
|
+
alert({
|
|
240
232
|
messageHtml: errorMessage,
|
|
241
233
|
variant: 'danger'
|
|
242
234
|
});
|
|
@@ -16,25 +16,7 @@
|
|
|
16
16
|
>
|
|
17
17
|
{{ $t('Cancel') }}
|
|
18
18
|
</button>
|
|
19
|
-
|
|
20
|
-
<!-- Custom Save Button injection -->
|
|
21
|
-
<component
|
|
22
|
-
v-if="editSaveButtonInjection"
|
|
23
|
-
:is="getCustomComponent(editSaveButtonInjection)"
|
|
24
|
-
:meta="editSaveButtonInjection.meta"
|
|
25
|
-
:record="editableRecord"
|
|
26
|
-
:resource="coreStore.resource"
|
|
27
|
-
:adminUser="coreStore.adminUser"
|
|
28
|
-
:saving="saving"
|
|
29
|
-
:validating="validating"
|
|
30
|
-
:isValid="isValid"
|
|
31
|
-
:disabled="saving || (validating && !isValid)"
|
|
32
|
-
:saveRecord="saveRecord"
|
|
33
|
-
/>
|
|
34
|
-
|
|
35
|
-
<!-- Default Save Button fallback -->
|
|
36
19
|
<button
|
|
37
|
-
v-else
|
|
38
20
|
@click="() => saveRecord()"
|
|
39
21
|
class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-lightEditViewSaveButtonText focus:outline-none bg-lightEditViewButtonBackground rounded border border-lightEditViewButtonBorder hover:bg-lightEditViewButtonBackgroundHover hover:text-lightEditViewSaveButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightEditViewButtonFocusRing dark:focus:ring-darkEditViewButtonFocusRing dark:bg-darkEditViewButtonBackground dark:text-darkEditViewSaveButtonText dark:border-darkEditViewButtonBorder dark:hover:text-darkEditViewSaveButtonTextHover dark:hover:bg-darkEditViewButtonBackgroundHover disabled:opacity-50 gap-1"
|
|
40
22
|
:disabled="saving || (validating && !isValid)"
|
|
@@ -94,17 +76,18 @@ import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
|
94
76
|
import { useCoreStore } from '@/stores/core';
|
|
95
77
|
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
|
|
96
78
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
97
|
-
import { computed, onMounted, ref, type Ref, nextTick } from 'vue';
|
|
79
|
+
import { computed, onMounted, onBeforeMount, ref, type Ref, nextTick, watch } from 'vue';
|
|
98
80
|
import { useRoute, useRouter } from 'vue-router';
|
|
99
81
|
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
100
82
|
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
101
|
-
import
|
|
83
|
+
import { useAdminforth } from '@/adminforth';
|
|
102
84
|
import { useI18n } from 'vue-i18n';
|
|
103
85
|
import { type AdminForthComponentDeclarationFull } from '@/types/Common.js';
|
|
104
86
|
import type { AdminForthResourceColumn } from '@/types/Back';
|
|
105
87
|
|
|
106
88
|
const { t } = useI18n();
|
|
107
89
|
const coreStore = useCoreStore();
|
|
90
|
+
const { clearSaveInterceptors, runSaveInterceptors, alert } = useAdminforth();
|
|
108
91
|
|
|
109
92
|
const isValid = ref(false);
|
|
110
93
|
const validating = ref(false);
|
|
@@ -118,14 +101,11 @@ const saving = ref(false);
|
|
|
118
101
|
|
|
119
102
|
const record: Ref<Record<string, any>> = ref({});
|
|
120
103
|
|
|
121
|
-
|
|
104
|
+
watch(record, (newVal) => {
|
|
105
|
+
console.log('Record updated:', newVal);
|
|
106
|
+
}, { deep: true });
|
|
122
107
|
|
|
123
|
-
const
|
|
124
|
-
const raw: any = coreStore.resourceOptions?.pageInjections?.edit?.saveButton as any;
|
|
125
|
-
if (!raw) return null;
|
|
126
|
-
const item = Array.isArray(raw) ? raw[0] : raw;
|
|
127
|
-
return item as AdminForthComponentDeclarationFull;
|
|
128
|
-
});
|
|
108
|
+
const resourceFormRef = ref<InstanceType<typeof ResourceForm> | null>(null);
|
|
129
109
|
|
|
130
110
|
async function onUpdateRecord(newRecord: Record<string, any>) {
|
|
131
111
|
record.value = newRecord;
|
|
@@ -148,6 +128,10 @@ const editableRecord = computed(() => {
|
|
|
148
128
|
return newRecord;
|
|
149
129
|
})
|
|
150
130
|
|
|
131
|
+
onBeforeMount(() => {
|
|
132
|
+
clearSaveInterceptors(route.params.resourceId as string);
|
|
133
|
+
});
|
|
134
|
+
|
|
151
135
|
onMounted(async () => {
|
|
152
136
|
loading.value = true;
|
|
153
137
|
|
|
@@ -169,7 +153,7 @@ onMounted(async () => {
|
|
|
169
153
|
loading.value = false;
|
|
170
154
|
});
|
|
171
155
|
|
|
172
|
-
async function saveRecord(
|
|
156
|
+
async function saveRecord() {
|
|
173
157
|
if (!isValid.value) {
|
|
174
158
|
validating.value = true;
|
|
175
159
|
await nextTick();
|
|
@@ -180,6 +164,18 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
|
|
|
180
164
|
}
|
|
181
165
|
|
|
182
166
|
saving.value = true;
|
|
167
|
+
const interceptorsResult = await runSaveInterceptors({
|
|
168
|
+
action: 'edit',
|
|
169
|
+
values: record.value,
|
|
170
|
+
resource: coreStore.resource,
|
|
171
|
+
resourceId: route.params.resourceId as string,
|
|
172
|
+
});
|
|
173
|
+
if (!interceptorsResult.ok) {
|
|
174
|
+
saving.value = false;
|
|
175
|
+
if (interceptorsResult.error) showErrorTost(interceptorsResult.error);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const interceptorConfirmationResult = interceptorsResult.extra?.confirmationResult;
|
|
183
179
|
const updates: Record<string, any> = {};
|
|
184
180
|
for (const key in record.value) {
|
|
185
181
|
let columnIsUpdated = false;
|
|
@@ -212,14 +208,14 @@ async function saveRecord(opts?: { confirmationResult?: any }) {
|
|
|
212
208
|
recordId: route.params.primaryKey,
|
|
213
209
|
record: updates,
|
|
214
210
|
meta: {
|
|
215
|
-
...(
|
|
211
|
+
...(interceptorConfirmationResult ? { confirmationResult: interceptorConfirmationResult } : {}),
|
|
216
212
|
},
|
|
217
213
|
},
|
|
218
214
|
});
|
|
219
215
|
if (resp.error && resp.error !== 'Operation aborted by hook') {
|
|
220
216
|
showErrorTost(resp.error);
|
|
221
217
|
} else {
|
|
222
|
-
|
|
218
|
+
alert({
|
|
223
219
|
message: t('Record updated successfully'),
|
|
224
220
|
variant: 'success',
|
|
225
221
|
timeout: 400000
|
|
@@ -238,7 +234,7 @@ function scrollToInvalidField() {
|
|
|
238
234
|
}
|
|
239
235
|
}
|
|
240
236
|
const errorMessage = t('Failed to save. Please fix errors for the following fields:') + '<ul class="mt-2 list-disc list-inside">' + columnsWithErrors.map(c => `<li><strong>${c.column.label || c.column.name}</strong>: ${c.error}</li>`).join('') + '</ul>';
|
|
241
|
-
|
|
237
|
+
alert({
|
|
242
238
|
messageHtml: errorMessage,
|
|
243
239
|
variant: 'danger'
|
|
244
240
|
});
|