adminforth 1.3.54-next.9 → 1.3.55-next.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/auth.d.ts +31 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +42 -56
- package/dist/auth.js.map +1 -0
- package/dist/basePlugin.d.ts +18 -0
- package/dist/basePlugin.d.ts.map +1 -0
- package/dist/basePlugin.js +1 -0
- package/dist/basePlugin.js.map +1 -0
- package/dist/dataConnectors/baseConnector.d.ts +94 -0
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -0
- package/dist/dataConnectors/baseConnector.js +108 -122
- package/dist/dataConnectors/baseConnector.js.map +1 -0
- package/dist/dataConnectors/clickhouse.d.ts +92 -0
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -0
- package/dist/dataConnectors/clickhouse.js +132 -149
- package/dist/dataConnectors/clickhouse.js.map +1 -0
- package/dist/dataConnectors/mongo.d.ts +93 -0
- package/dist/dataConnectors/mongo.d.ts.map +1 -0
- package/dist/dataConnectors/mongo.js +75 -101
- package/dist/dataConnectors/mongo.js.map +1 -0
- package/dist/dataConnectors/postgres.d.ts +71 -0
- package/dist/dataConnectors/postgres.d.ts.map +1 -0
- package/dist/dataConnectors/postgres.js +124 -143
- package/dist/dataConnectors/postgres.js.map +1 -0
- package/dist/dataConnectors/sqlite.d.ts +67 -0
- package/dist/dataConnectors/sqlite.d.ts.map +1 -0
- package/dist/dataConnectors/sqlite.js +113 -130
- package/dist/dataConnectors/sqlite.js.map +1 -0
- package/dist/index.d.ts +92 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +197 -217
- package/dist/index.js.map +1 -0
- package/dist/modules/codeInjector.d.ts +35 -0
- package/dist/modules/codeInjector.d.ts.map +1 -0
- package/dist/modules/codeInjector.js +480 -486
- package/dist/modules/codeInjector.js.map +1 -0
- package/dist/modules/configValidator.d.ts +12 -0
- package/dist/modules/configValidator.d.ts.map +1 -0
- package/dist/modules/configValidator.js +31 -22
- package/dist/modules/configValidator.js.map +1 -0
- package/dist/modules/operationalResource.d.ts +17 -0
- package/dist/modules/operationalResource.d.ts.map +1 -0
- package/dist/modules/operationalResource.js +50 -70
- package/dist/modules/operationalResource.js.map +1 -0
- package/dist/modules/restApi.d.ts +10 -0
- package/dist/modules/restApi.d.ts.map +1 -0
- package/dist/modules/restApi.js +104 -116
- package/dist/modules/restApi.js.map +1 -0
- package/dist/modules/styleGenerator.d.ts +9 -0
- package/dist/modules/styleGenerator.d.ts.map +1 -0
- package/dist/modules/styleGenerator.js +1 -0
- package/dist/modules/styleGenerator.js.map +1 -0
- package/dist/modules/styles.d.ts +96 -0
- package/dist/modules/styles.d.ts.map +1 -0
- package/dist/modules/styles.js +1 -0
- package/dist/modules/styles.js.map +1 -0
- package/dist/modules/utils.d.ts +39 -0
- package/dist/modules/utils.d.ts.map +1 -0
- package/dist/modules/utils.js +1 -0
- package/dist/modules/utils.js.map +1 -0
- package/dist/plugins/audit-log/types.d.ts +35 -0
- package/dist/plugins/audit-log/types.d.ts.map +1 -0
- package/dist/plugins/audit-log/types.js +2 -0
- package/dist/plugins/audit-log/types.js.map +1 -0
- package/dist/plugins/chat-gpt/types.d.ts +82 -0
- package/dist/plugins/chat-gpt/types.d.ts.map +1 -0
- package/dist/plugins/chat-gpt/types.js +2 -0
- package/dist/plugins/chat-gpt/types.js.map +1 -0
- package/dist/plugins/email-password-reset/types.d.ts +28 -0
- package/dist/plugins/email-password-reset/types.d.ts.map +1 -0
- package/dist/plugins/email-password-reset/types.js +2 -0
- package/dist/plugins/email-password-reset/types.js.map +1 -0
- package/dist/plugins/foreign-inline-list/types.d.ts +19 -0
- package/dist/plugins/foreign-inline-list/types.d.ts.map +1 -0
- package/dist/plugins/foreign-inline-list/types.js +2 -0
- package/dist/plugins/foreign-inline-list/types.js.map +1 -0
- package/dist/plugins/import-export/types.d.ts +3 -0
- package/dist/plugins/import-export/types.d.ts.map +1 -0
- package/dist/plugins/import-export/types.js +2 -0
- package/dist/plugins/import-export/types.js.map +1 -0
- package/dist/plugins/rich-editor/custom/async-queue.d.ts +8 -0
- package/dist/plugins/rich-editor/custom/async-queue.d.ts.map +1 -0
- package/dist/plugins/rich-editor/custom/async-queue.js +29 -0
- package/dist/plugins/rich-editor/custom/async-queue.js.map +1 -0
- package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts +8 -0
- package/dist/plugins/rich-editor/dist/custom/async-queue.d.ts.map +1 -0
- package/dist/plugins/rich-editor/dist/custom/async-queue.js +29 -0
- package/dist/plugins/rich-editor/dist/custom/async-queue.js.map +1 -0
- package/dist/plugins/rich-editor/types.d.ts +153 -0
- package/dist/plugins/rich-editor/types.d.ts.map +1 -0
- package/dist/plugins/rich-editor/types.js +16 -0
- package/dist/plugins/rich-editor/types.js.map +1 -0
- package/dist/plugins/two-factors-auth/types.d.ts +18 -0
- package/dist/plugins/two-factors-auth/types.d.ts.map +1 -0
- package/dist/plugins/two-factors-auth/types.js +2 -0
- package/dist/plugins/two-factors-auth/types.js.map +1 -0
- package/dist/plugins/upload/types.d.ts +132 -0
- package/dist/plugins/upload/types.d.ts.map +1 -0
- package/dist/plugins/upload/types.js +2 -0
- package/dist/plugins/upload/types.js.map +1 -0
- package/dist/servers/express.d.ts +18 -0
- package/dist/servers/express.d.ts.map +1 -0
- package/dist/servers/express.js +30 -42
- package/dist/servers/express.js.map +1 -0
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +87 -1
- package/dist/spa/package.json +4 -1
- package/dist/spa/src/App.vue +154 -50
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/Breadcrumbs.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +1 -1
- package/dist/spa/src/components/CustomDateRangePicker.vue +1 -1
- package/dist/spa/src/components/CustomRangePicker.vue +9 -5
- package/dist/spa/src/components/Dropdown.vue +4 -4
- package/dist/spa/src/components/Filters.vue +2 -2
- package/dist/spa/src/components/MenuLink.vue +3 -0
- package/dist/spa/src/components/ResourceForm.vue +67 -36
- package/dist/spa/src/components/ResourceListTable.vue +216 -144
- package/dist/spa/src/components/SkeleteLoader.vue +4 -4
- package/dist/spa/src/components/Toast.vue +3 -2
- package/dist/spa/src/components/ValueRenderer.vue +81 -6
- package/dist/spa/src/composables/useFrontendApi.ts +1 -1
- package/dist/spa/src/composables/useStores.ts +18 -14
- package/dist/spa/src/index.scss +4 -0
- package/{spa → dist/spa}/src/renderers/CompactUUID.vue +4 -4
- package/{spa → dist/spa}/src/renderers/CountryFlag.vue +2 -2
- package/dist/spa/src/router/index.ts +4 -8
- package/dist/spa/src/spa_types/core.ts +2 -0
- package/dist/spa/src/stores/core.ts +6 -2
- package/dist/spa/src/stores/filters.ts +15 -10
- package/dist/spa/src/stores/toast.ts +22 -6
- package/dist/spa/src/types/AdminForthConfig.ts +340 -55
- package/dist/spa/src/types/FrontendAPI.ts +52 -30
- package/dist/spa/src/utils.ts +59 -2
- package/dist/spa/src/views/CreateView.vue +15 -4
- package/dist/spa/src/views/EditView.vue +20 -7
- package/dist/spa/src/views/ListView.vue +132 -38
- package/dist/spa/src/views/LoginView.vue +50 -18
- package/dist/spa/src/views/ShowView.vue +25 -15
- package/dist/types/AdminForthConfig.d.ts +1619 -0
- package/dist/types/AdminForthConfig.d.ts.map +1 -0
- package/dist/types/AdminForthConfig.js +1 -0
- package/dist/types/AdminForthConfig.js.map +1 -0
- package/{types/FrontendAPI.ts → dist/types/FrontendAPI.d.ts} +27 -52
- package/dist/types/FrontendAPI.d.ts.map +1 -0
- package/dist/types/FrontendAPI.js +1 -0
- package/dist/types/FrontendAPI.js.map +1 -0
- package/package.json +16 -6
- package/auth.ts +0 -140
- package/basePlugin.ts +0 -70
- package/dataConnectors/baseConnector.ts +0 -216
- package/dataConnectors/clickhouse.ts +0 -338
- package/dataConnectors/mongo.ts +0 -202
- package/dataConnectors/postgres.ts +0 -306
- package/dataConnectors/sqlite.ts +0 -254
- package/index.ts +0 -428
- package/modules/codeInjector.ts +0 -736
- package/modules/configValidator.ts +0 -571
- package/modules/operationalResource.ts +0 -98
- package/modules/restApi.ts +0 -718
- package/modules/styleGenerator.ts +0 -55
- package/modules/styles.ts +0 -126
- package/modules/utils.ts +0 -472
- package/servers/express.ts +0 -259
- package/spa/.eslintrc.cjs +0 -14
- package/spa/README.md +0 -39
- package/spa/env.d.ts +0 -1
- package/spa/index.html +0 -23
- package/spa/package-lock.json +0 -4602
- package/spa/package.json +0 -51
- package/spa/postcss.config.js +0 -6
- package/spa/public/assets/favicon.png +0 -0
- package/spa/src/App.vue +0 -418
- package/spa/src/assets/base.css +0 -2
- package/spa/src/assets/logo.svg +0 -19
- package/spa/src/components/AcceptModal.vue +0 -45
- package/spa/src/components/Breadcrumbs.vue +0 -41
- package/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
- package/spa/src/components/CustomDatePicker.vue +0 -176
- package/spa/src/components/CustomDateRangePicker.vue +0 -218
- package/spa/src/components/CustomRangePicker.vue +0 -156
- package/spa/src/components/Dropdown.vue +0 -168
- package/spa/src/components/Filters.vue +0 -222
- package/spa/src/components/HelloWorld.vue +0 -17
- package/spa/src/components/MenuLink.vue +0 -27
- package/spa/src/components/ResourceForm.vue +0 -290
- package/spa/src/components/ResourceListTable.vue +0 -466
- package/spa/src/components/SingleSkeletLoader.vue +0 -13
- package/spa/src/components/SkeleteLoader.vue +0 -23
- package/spa/src/components/Toast.vue +0 -78
- package/spa/src/components/ValueRenderer.vue +0 -114
- package/spa/src/components/icons/IconCalendar.vue +0 -5
- package/spa/src/components/icons/IconCommunity.vue +0 -7
- package/spa/src/components/icons/IconDocumentation.vue +0 -7
- package/spa/src/components/icons/IconEcosystem.vue +0 -7
- package/spa/src/components/icons/IconSupport.vue +0 -7
- package/spa/src/components/icons/IconTime.vue +0 -5
- package/spa/src/components/icons/IconTooling.vue +0 -19
- package/spa/src/composables/useFrontendApi.ts +0 -26
- package/spa/src/composables/useStores.ts +0 -131
- package/spa/src/index.scss +0 -31
- package/spa/src/main.ts +0 -18
- package/spa/src/router/index.ts +0 -59
- package/spa/src/spa_types/core.ts +0 -53
- package/spa/src/stores/core.ts +0 -148
- package/spa/src/stores/filters.ts +0 -27
- package/spa/src/stores/modal.ts +0 -48
- package/spa/src/stores/toast.ts +0 -31
- package/spa/src/stores/user.ts +0 -72
- package/spa/src/utils.ts +0 -160
- package/spa/src/views/CreateView.vue +0 -167
- package/spa/src/views/EditView.vue +0 -170
- package/spa/src/views/ListView.vue +0 -352
- package/spa/src/views/LoginView.vue +0 -192
- package/spa/src/views/ResourceParent.vue +0 -17
- package/spa/src/views/ShowView.vue +0 -186
- package/spa/tailwind.config.js +0 -17
- package/spa/tsconfig.app.json +0 -14
- package/spa/tsconfig.json +0 -11
- package/spa/tsconfig.node.json +0 -19
- package/spa/vite.config.ts +0 -56
- package/tsconfig.json +0 -112
- package/types/AdminForthConfig.ts +0 -1762
- /package/{spa → dist/spa}/src/components/ThreeDotsMenu.vue +0 -0
|
@@ -19,6 +19,7 @@ export interface FrontendAPIInterface {
|
|
|
19
19
|
* @returns A promise that resolves when the user confirms the dialog
|
|
20
20
|
*/
|
|
21
21
|
confirm(params:ConfirmParams ): Promise<void>;
|
|
22
|
+
|
|
22
23
|
/**
|
|
23
24
|
* Show an alert
|
|
24
25
|
*
|
|
@@ -33,37 +34,58 @@ export interface FrontendAPIInterface {
|
|
|
33
34
|
* @param params - The parameters of the alert
|
|
34
35
|
*/
|
|
35
36
|
alert(params:AlertParams): void;
|
|
36
|
-
/**
|
|
37
|
-
* Add a filter to the list of filters.
|
|
38
|
-
* Works only when user located on the list page.
|
|
39
|
-
* Can be used to set filter from charts or other components in pageInjections.
|
|
40
|
-
*
|
|
41
|
-
* Example:
|
|
42
|
-
*
|
|
43
|
-
* ```ts
|
|
44
|
-
* window.adminforth.updateListFilter({field: 'name', operator: 'ilike', value: 'john'})
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* @param filter - The filter to add
|
|
48
|
-
*/
|
|
49
|
-
setListFilter(filter: any): void;
|
|
50
|
-
/**
|
|
51
|
-
* Update a filter in the list of filters
|
|
52
|
-
*
|
|
53
|
-
* Example:
|
|
54
|
-
*
|
|
55
|
-
* ```ts
|
|
56
|
-
* window.adminforth.updateListFilter({field: 'name', operator: 'ilike', value: 'john'})
|
|
57
|
-
* ```
|
|
58
|
-
*
|
|
59
|
-
* @param filter - The filter to update
|
|
60
|
-
*/
|
|
61
|
-
updateListFilter(filter: any): void;
|
|
62
|
-
/**
|
|
63
|
-
* Clear all filters from the list
|
|
64
|
-
*/
|
|
65
|
-
clearListFilters(): void;
|
|
66
37
|
|
|
38
|
+
|
|
39
|
+
list: {
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Refresh the list
|
|
43
|
+
*/
|
|
44
|
+
refresh(): void;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Close the three dots dropdown
|
|
48
|
+
*/
|
|
49
|
+
closeThreeDotsDropdown(): void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Close the user menu dropdown
|
|
53
|
+
*/
|
|
54
|
+
closeUserMenuDropdown(): void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Set a filter in the list
|
|
58
|
+
* Works only when user located on the list page.
|
|
59
|
+
* Can be used to set filter from charts or other components in pageInjections.
|
|
60
|
+
*
|
|
61
|
+
* Example:
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* window.adminforth.list.setFilter({field: 'name', operator: 'ilike', value: 'john'})
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @param filter - The filter to set
|
|
68
|
+
*/
|
|
69
|
+
setFilter(filter: any): void;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Update a filter in the list
|
|
73
|
+
*
|
|
74
|
+
* Example:
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* window.adminforth.list.updateFilter({field: 'name', operator: 'ilike', value: 'john'})
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @param filter - The filter to update
|
|
81
|
+
*/
|
|
82
|
+
updateFilter(filter: any): void;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Clear all filters from the list
|
|
86
|
+
*/
|
|
87
|
+
clearFilters(): void;
|
|
88
|
+
}
|
|
67
89
|
}
|
|
68
90
|
|
|
69
91
|
export type ConfirmParams = {
|
package/dist/spa/src/utils.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { onMounted, ref, resolveComponent } from 'vue';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CoreConfig } from './spa_types/core';
|
|
3
|
+
import type { ValidationObject } from './types/AdminForthConfig';
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
import router from "./router";
|
|
5
|
-
import { useRouter } from 'vue-router';
|
|
6
7
|
import { useCoreStore } from './stores/core';
|
|
7
8
|
import { useUserStore } from './stores/user';
|
|
9
|
+
import { Dropdown } from 'flowbite';
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
export async function callApi({path, method, body=undefined}: {
|
|
10
13
|
path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
|
@@ -100,4 +103,58 @@ export function checkAcessByAllowedActions(allowedActions:any, action:any ) {
|
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
export function initThreeDotsDropdown() {
|
|
107
|
+
const threeDotsDropdown: HTMLElement | null = document.querySelector('#listThreeDotsDropdown');
|
|
108
|
+
if (threeDotsDropdown) {
|
|
109
|
+
// this resource has three dots dropdown
|
|
110
|
+
const dd = new Dropdown(
|
|
111
|
+
threeDotsDropdown,
|
|
112
|
+
document.querySelector('[data-dropdown-toggle="listThreeDotsDropdown"]') as HTMLElement,
|
|
113
|
+
);
|
|
114
|
+
window.adminforth.list.closeThreeDotsDropdown = () => {
|
|
115
|
+
dd.hide();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function applyRegexValidation(value: any, validation: ValidationObject[] | undefined) {
|
|
121
|
+
|
|
122
|
+
if ( validation?.length ) {
|
|
123
|
+
const validationArray = validation;
|
|
124
|
+
for (let i = 0; i < validationArray.length; i++) {
|
|
125
|
+
if (validationArray[i].regExp) {
|
|
126
|
+
let flags = '';
|
|
127
|
+
if (validationArray[i].caseSensitive) {
|
|
128
|
+
flags += 'i';
|
|
129
|
+
}
|
|
130
|
+
if (validationArray[i].multiline) {
|
|
131
|
+
flags += 'm';
|
|
132
|
+
}
|
|
133
|
+
if (validationArray[i].global) {
|
|
134
|
+
flags += 'g';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const regExp = new RegExp(validationArray[i].regExp, flags);
|
|
138
|
+
if (value === undefined || value === null) {
|
|
139
|
+
value = '';
|
|
140
|
+
}
|
|
141
|
+
let valueS = `${value}`;
|
|
142
|
+
|
|
143
|
+
if (!regExp.test(valueS)) {
|
|
144
|
+
return validationArray[i].message;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function currentQuery() {
|
|
152
|
+
return router.currentRoute.value.query;
|
|
153
|
+
}
|
|
103
154
|
|
|
155
|
+
export function setQuery(query: any) {
|
|
156
|
+
const currentQuery = { ...router.currentRoute.value.query, ...query };
|
|
157
|
+
router.replace({
|
|
158
|
+
query: currentQuery,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
@@ -13,12 +13,13 @@
|
|
|
13
13
|
<BreadcrumbsWithButtons>
|
|
14
14
|
<!-- save and cancle -->
|
|
15
15
|
<button @click="$router.back()"
|
|
16
|
-
class="flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-
|
|
16
|
+
class="flex items-center py-1 px-3 me-2 text-sm font-medium rounded-default text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
17
17
|
>
|
|
18
18
|
Cancel
|
|
19
19
|
</button>
|
|
20
20
|
|
|
21
|
-
<button
|
|
21
|
+
<button
|
|
22
|
+
@click="saveRecord"
|
|
22
23
|
class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
23
24
|
:disabled="saving || (validating && !isValid)"
|
|
24
25
|
>
|
|
@@ -29,6 +30,10 @@
|
|
|
29
30
|
Save
|
|
30
31
|
</button>
|
|
31
32
|
|
|
33
|
+
<ThreeDotsMenu
|
|
34
|
+
:threeDotsDropdownItems="coreStore.resourceOptions?.pageInjections?.create?.threeDotsDropdownItems"
|
|
35
|
+
></ThreeDotsMenu>
|
|
36
|
+
|
|
32
37
|
</BreadcrumbsWithButtons>
|
|
33
38
|
|
|
34
39
|
<component
|
|
@@ -73,18 +78,19 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
|
73
78
|
import ResourceForm from '@/components/ResourceForm.vue';
|
|
74
79
|
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
75
80
|
import { useCoreStore } from '@/stores/core';
|
|
76
|
-
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
|
|
81
|
+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
|
|
77
82
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
78
83
|
import { onMounted, ref, watch } from 'vue';
|
|
79
84
|
import { useRoute, useRouter } from 'vue-router';
|
|
80
85
|
import { computed } from 'vue';
|
|
81
86
|
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
87
|
+
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
82
88
|
|
|
83
89
|
|
|
84
90
|
const isValid = ref(false);
|
|
85
91
|
const validating = ref(false);
|
|
86
92
|
|
|
87
|
-
const loading = ref(
|
|
93
|
+
const loading = ref(true);
|
|
88
94
|
const saving = ref(false);
|
|
89
95
|
|
|
90
96
|
const route = useRoute();
|
|
@@ -116,6 +122,7 @@ onMounted(async () => {
|
|
|
116
122
|
});
|
|
117
123
|
loading.value = false;
|
|
118
124
|
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');
|
|
125
|
+
initThreeDotsDropdown();
|
|
119
126
|
});
|
|
120
127
|
|
|
121
128
|
async function saveRecord() {
|
|
@@ -149,6 +156,10 @@ async function saveRecord() {
|
|
|
149
156
|
primaryKey: response.newRecordId
|
|
150
157
|
}
|
|
151
158
|
});
|
|
159
|
+
window.adminforth.alert({
|
|
160
|
+
message: 'Record created successfully',
|
|
161
|
+
variant: 'success'
|
|
162
|
+
});
|
|
152
163
|
}
|
|
153
164
|
}
|
|
154
165
|
|
|
@@ -12,19 +12,24 @@
|
|
|
12
12
|
<BreadcrumbsWithButtons>
|
|
13
13
|
<!-- save and cancle -->
|
|
14
14
|
<button @click="$router.back()"
|
|
15
|
-
class="flex items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 rounded-default focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-
|
|
15
|
+
class="flex items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 rounded-default focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
16
16
|
>
|
|
17
17
|
Cancel
|
|
18
18
|
</button>
|
|
19
19
|
|
|
20
|
-
<button
|
|
21
|
-
|
|
20
|
+
<button
|
|
21
|
+
@click="saveRecord"
|
|
22
|
+
class="flex items-center py-1 px-3 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
22
23
|
:disabled="saving || (validating && !isValid)"
|
|
23
24
|
>
|
|
24
25
|
<IconFloppyDiskSolid class="w-4 h-4" />
|
|
25
26
|
Save
|
|
26
27
|
</button>
|
|
27
28
|
|
|
29
|
+
<ThreeDotsMenu
|
|
30
|
+
:threeDotsDropdownItems="coreStore.resourceOptions?.pageInjections?.edit?.threeDotsDropdownItems"
|
|
31
|
+
></ThreeDotsMenu>
|
|
32
|
+
|
|
28
33
|
</BreadcrumbsWithButtons>
|
|
29
34
|
|
|
30
35
|
<component
|
|
@@ -69,11 +74,13 @@ import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
|
69
74
|
import ResourceForm from '@/components/ResourceForm.vue';
|
|
70
75
|
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
71
76
|
import { useCoreStore } from '@/stores/core';
|
|
72
|
-
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
|
|
77
|
+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions, initThreeDotsDropdown } from '@/utils';
|
|
73
78
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
74
79
|
import { computed, onMounted, ref } from 'vue';
|
|
75
80
|
import { useRoute, useRouter } from 'vue-router';
|
|
76
81
|
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
82
|
+
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
83
|
+
|
|
77
84
|
|
|
78
85
|
const coreStore = useCoreStore();
|
|
79
86
|
|
|
@@ -83,7 +90,7 @@ const validating = ref(false);
|
|
|
83
90
|
const route = useRoute();
|
|
84
91
|
const router = useRouter();
|
|
85
92
|
|
|
86
|
-
const loading = ref(
|
|
93
|
+
const loading = ref(true);
|
|
87
94
|
|
|
88
95
|
const saving = ref(false);
|
|
89
96
|
|
|
@@ -107,12 +114,13 @@ const editableRecord = computed(() => {
|
|
|
107
114
|
})
|
|
108
115
|
|
|
109
116
|
onMounted(async () => {
|
|
110
|
-
|
|
111
117
|
loading.value = true;
|
|
112
118
|
|
|
113
119
|
await coreStore.fetchResourceFull({
|
|
114
120
|
resourceId: route.params.resourceId
|
|
115
121
|
});
|
|
122
|
+
initThreeDotsDropdown();
|
|
123
|
+
|
|
116
124
|
await coreStore.fetchRecord({
|
|
117
125
|
resourceId: route.params.resourceId,
|
|
118
126
|
primaryKey: route.params.primaryKey,
|
|
@@ -130,7 +138,6 @@ async function saveRecord() {
|
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
saving.value = true;
|
|
133
|
-
|
|
134
141
|
const updates = {};
|
|
135
142
|
for (const key in record.value) {
|
|
136
143
|
if (record.value[key] !== coreStore.record[key]) {
|
|
@@ -149,6 +156,12 @@ async function saveRecord() {
|
|
|
149
156
|
});
|
|
150
157
|
if (resp.error) {
|
|
151
158
|
showErrorTost(resp.error);
|
|
159
|
+
} else {
|
|
160
|
+
window.adminforth.alert({
|
|
161
|
+
message: 'Record updated successfully',
|
|
162
|
+
variant: 'success',
|
|
163
|
+
timeout: 400000
|
|
164
|
+
});
|
|
152
165
|
}
|
|
153
166
|
saving.value = false;
|
|
154
167
|
router.push({ name: 'resource-show', params: { resourceId: route.params.resourceId, primaryKey: coreStore.record[coreStore.primaryKey] } });
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
@click="()=>{checkboxes = []}"
|
|
22
22
|
v-if="checkboxes.length"
|
|
23
23
|
data-tooltip-target="tooltip-remove-all"
|
|
24
|
-
|
|
25
|
-
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-darkListTable dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
24
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-darkListTable dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
26
25
|
>
|
|
27
26
|
<IconBanOutline class="w-5 h-5 "/>
|
|
28
27
|
|
|
@@ -38,9 +37,9 @@
|
|
|
38
37
|
v-for="(action,i) in coreStore.resource?.options?.bulkActions"
|
|
39
38
|
:key="action.id"
|
|
40
39
|
@click="startBulkAction(action.id)"
|
|
41
|
-
class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default border border-gray-300 hover:bg-gray-100 hover:text-
|
|
40
|
+
class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
42
41
|
:class="{'bg-red-100 text-red-800 border-red-400 dark:bg-red-700 dark:text-red-400 dark:border-red-400':action.state==='danger', 'bg-green-100 text-green-800 border-green-400 dark:bg-green-700 dark:text-green-400 dark:border-green-400':action.state==='success',
|
|
43
|
-
'bg-
|
|
42
|
+
'bg-lightPrimaryOpacity text-lightPrimary border-blue-400 dark:bg-blue-700 dark:text-blue-400 dark:border-blue-400':action.state==='active',
|
|
44
43
|
}"
|
|
45
44
|
>
|
|
46
45
|
<component
|
|
@@ -53,14 +52,14 @@
|
|
|
53
52
|
|
|
54
53
|
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
55
54
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
56
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-
|
|
55
|
+
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
57
56
|
>
|
|
58
57
|
<IconPlusOutline class="w-4 h-4 me-2"/>
|
|
59
58
|
Create
|
|
60
59
|
</RouterLink>
|
|
61
60
|
|
|
62
61
|
<button
|
|
63
|
-
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-
|
|
62
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
64
63
|
@click="()=>{filtersShow = !filtersShow}"
|
|
65
64
|
v-if="coreStore.resource?.options?.allowedActions?.filter"
|
|
66
65
|
>
|
|
@@ -72,6 +71,10 @@
|
|
|
72
71
|
{{ filtersStore.filters.length }}
|
|
73
72
|
</span>
|
|
74
73
|
</button>
|
|
74
|
+
|
|
75
|
+
<ThreeDotsMenu
|
|
76
|
+
:threeDotsDropdownItems="coreStore.resourceOptions?.pageInjections?.list?.threeDotsDropdownItems"
|
|
77
|
+
></ThreeDotsMenu>
|
|
75
78
|
</BreadcrumbsWithButtons>
|
|
76
79
|
|
|
77
80
|
<component
|
|
@@ -85,6 +88,7 @@
|
|
|
85
88
|
<ResourceListTable
|
|
86
89
|
:resource="coreStore.resource"
|
|
87
90
|
:rows="rows"
|
|
91
|
+
:page="page"
|
|
88
92
|
@update:page="page = $event"
|
|
89
93
|
@update:sort="sort = $event"
|
|
90
94
|
@update:checkboxes="checkboxes = $event"
|
|
@@ -110,16 +114,14 @@
|
|
|
110
114
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
111
115
|
import ResourceListTable from '@/components/ResourceListTable.vue';
|
|
112
116
|
import { useCoreStore } from '@/stores/core';
|
|
113
|
-
import { useModalStore } from '@/stores/modal';
|
|
114
117
|
import { useFiltersStore } from '@/stores/filters';
|
|
115
|
-
import { callAdminForthApi, getIcon } from '@/utils';
|
|
116
|
-
import { initFlowbite } from 'flowbite';
|
|
118
|
+
import { callAdminForthApi, currentQuery, getIcon, setQuery } from '@/utils';
|
|
117
119
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
118
120
|
import { useRoute } from 'vue-router';
|
|
119
121
|
import { showErrorTost } from '@/composables/useFrontendApi'
|
|
120
|
-
|
|
121
|
-
import
|
|
122
|
-
import
|
|
122
|
+
import { getCustomComponent, initThreeDotsDropdown } from '@/utils';
|
|
123
|
+
import { initFlowbite } from 'flowbite';
|
|
124
|
+
import ThreeDotsMenu from '@/components/ThreeDotsMenu.vue';
|
|
123
125
|
|
|
124
126
|
|
|
125
127
|
import {
|
|
@@ -141,6 +143,11 @@ const page = ref(1);
|
|
|
141
143
|
const columnsMinMax = ref({});
|
|
142
144
|
const sort = ref([]);
|
|
143
145
|
|
|
146
|
+
watch(() => sort, async (to, from) => {
|
|
147
|
+
// in store sort might be needed for plugins
|
|
148
|
+
filtersStore.setSort(sort.value);
|
|
149
|
+
}, {deep: true});
|
|
150
|
+
|
|
144
151
|
const rows = ref(null);
|
|
145
152
|
const totalRows = ref(0);
|
|
146
153
|
const checkboxes = ref([]);
|
|
@@ -151,22 +158,6 @@ const DEFAULT_PAGE_SIZE = 10;
|
|
|
151
158
|
const pageSize = computed(() => coreStore.resource?.options?.listPageSize || DEFAULT_PAGE_SIZE);
|
|
152
159
|
|
|
153
160
|
|
|
154
|
-
watch([page], async () => {
|
|
155
|
-
await getList();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
watch(()=>filtersStore.filters, async () => {
|
|
159
|
-
page.value = 1;
|
|
160
|
-
checkboxes.value = [];
|
|
161
|
-
|
|
162
|
-
await getList();
|
|
163
|
-
}, {deep: true});
|
|
164
|
-
|
|
165
|
-
watch([sort], async () => {
|
|
166
|
-
await getList();
|
|
167
|
-
}, {deep: true});
|
|
168
|
-
|
|
169
|
-
|
|
170
161
|
async function getList() {
|
|
171
162
|
rows.value = null;
|
|
172
163
|
const data = await callAdminForthApi({
|
|
@@ -176,7 +167,7 @@ async function getList() {
|
|
|
176
167
|
source: 'list',
|
|
177
168
|
resourceId: route.params.resourceId,
|
|
178
169
|
limit: pageSize.value,
|
|
179
|
-
offset: (page.value - 1) * pageSize.value,
|
|
170
|
+
offset: ((page.value || 1) - 1) * pageSize.value,
|
|
180
171
|
filters: filtersStore.filters,
|
|
181
172
|
sort: sort.value,
|
|
182
173
|
}
|
|
@@ -192,9 +183,22 @@ async function getList() {
|
|
|
192
183
|
return row;
|
|
193
184
|
});
|
|
194
185
|
totalRows.value = data.total;
|
|
186
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
187
|
+
initFlowbite(); // for tooltips in table
|
|
188
|
+
|
|
195
189
|
}
|
|
196
190
|
|
|
197
191
|
async function startBulkAction(actionId) {
|
|
192
|
+
const action = coreStore.resource.options.bulkActions.find(a => a.id === actionId);
|
|
193
|
+
if (action.confirm) {
|
|
194
|
+
const confirmed = await window.adminforth.confirm({
|
|
195
|
+
message: action.confirm,
|
|
196
|
+
});
|
|
197
|
+
if (!confirmed) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
198
202
|
const data = await callAdminForthApi({
|
|
199
203
|
path: '/start_bulk_action',
|
|
200
204
|
method: 'POST',
|
|
@@ -208,6 +212,14 @@ async function startBulkAction(actionId) {
|
|
|
208
212
|
if (data?.ok) {
|
|
209
213
|
checkboxes.value = [];
|
|
210
214
|
await getList();
|
|
215
|
+
|
|
216
|
+
if (data.successMessage) {
|
|
217
|
+
window.adminforth.alert({
|
|
218
|
+
message: data.successMessage,
|
|
219
|
+
variant: 'success'
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
211
223
|
}
|
|
212
224
|
if (data?.error) {
|
|
213
225
|
showErrorTost(data.error);
|
|
@@ -215,6 +227,18 @@ async function startBulkAction(actionId) {
|
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
|
|
230
|
+
class SortQuerySerializer {
|
|
231
|
+
static serialize(sort) {
|
|
232
|
+
return sort.map(s => `${s.field}__${s.direction}`).join(',');
|
|
233
|
+
}
|
|
234
|
+
static deserialize(str) {
|
|
235
|
+
return str.split(',').map(s => {
|
|
236
|
+
const [field, direction] = s.split('__');
|
|
237
|
+
return { field, direction };
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
218
242
|
|
|
219
243
|
async function init() {
|
|
220
244
|
|
|
@@ -222,7 +246,28 @@ async function init() {
|
|
|
222
246
|
resourceId: route.params.resourceId
|
|
223
247
|
});
|
|
224
248
|
|
|
225
|
-
|
|
249
|
+
initFlowbite();
|
|
250
|
+
|
|
251
|
+
// !!! clear filters should be in same tick with sort assignment so that watch can catch it as one change
|
|
252
|
+
|
|
253
|
+
// try to init filters from query params
|
|
254
|
+
const filters = Object.keys(route.query).filter(k => k.startsWith('filter__')).map(k => {
|
|
255
|
+
const [_, field, operator] = k.split('__');
|
|
256
|
+
return {
|
|
257
|
+
field,
|
|
258
|
+
operator,
|
|
259
|
+
value: JSON.parse(decodeURIComponent(route.query[k]))
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
if (filters.length) {
|
|
263
|
+
filtersStore.setFilters(filters);
|
|
264
|
+
} else {
|
|
265
|
+
filtersStore.clearFilters();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (route.query.sort) {
|
|
269
|
+
sort.value = SortQuerySerializer.deserialize(route.query.sort);
|
|
270
|
+
} else if (coreStore.resource.options?.defaultSort) {
|
|
226
271
|
sort.value = [{
|
|
227
272
|
field: coreStore.resource.options.defaultSort.columnName,
|
|
228
273
|
direction: coreStore.resource.options.defaultSort.direction
|
|
@@ -230,8 +275,12 @@ async function init() {
|
|
|
230
275
|
} else {
|
|
231
276
|
sort.value = [];
|
|
232
277
|
}
|
|
278
|
+
// page init should be also in same tick
|
|
279
|
+
if (route.query.page) {
|
|
280
|
+
page.value = parseInt(route.query.page);
|
|
281
|
+
}
|
|
233
282
|
|
|
234
|
-
await getList();
|
|
283
|
+
// await getList(); - Not needed here, watch will trigger it
|
|
235
284
|
columnsMinMax.value = await callAdminForthApi({
|
|
236
285
|
path: '/get_min_max_for_columns',
|
|
237
286
|
method: 'POST',
|
|
@@ -241,18 +290,63 @@ async function init() {
|
|
|
241
290
|
});
|
|
242
291
|
}
|
|
243
292
|
|
|
293
|
+
watch([page, sort, () => filtersStore.filters], async () => {
|
|
294
|
+
console.log('🔄️ page/sort/filter change fired, page:', page.value);
|
|
295
|
+
|
|
296
|
+
await getList();
|
|
297
|
+
}, { deep: true });
|
|
298
|
+
|
|
299
|
+
window.adminforth.list.refresh = async () => {
|
|
300
|
+
await getList();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let initInProcess = false;
|
|
304
|
+
|
|
305
|
+
watch(() => filtersStore.filters, async (to, from) => {
|
|
306
|
+
if (initInProcess) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
console.log('🔄️ filters changed', JSON.stringify(to))
|
|
310
|
+
page.value = 1;
|
|
311
|
+
checkboxes.value = [];
|
|
312
|
+
// update query param for each filter as filter_<column_name>=value
|
|
313
|
+
const query = {};
|
|
314
|
+
const currentQ = currentQuery();
|
|
315
|
+
filtersStore.filters.forEach(f => {
|
|
316
|
+
if (f.value) {
|
|
317
|
+
query[`filter__${f.field}__${f.operator}`] = encodeURIComponent(JSON.stringify(f.value));
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// set every key in currentQ which starts with filter_ to undefined if it is not in query
|
|
321
|
+
Object.keys(currentQ).forEach(k => {
|
|
322
|
+
if (k.startsWith('filter_') && !query[k]) {
|
|
323
|
+
query[k] = undefined;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
setQuery(query);
|
|
327
|
+
}, {deep: true});
|
|
328
|
+
|
|
244
329
|
onMounted(async () => {
|
|
245
|
-
|
|
246
|
-
|
|
330
|
+
initInProcess = true;
|
|
247
331
|
await init();
|
|
332
|
+
initThreeDotsDropdown();
|
|
333
|
+
initInProcess = false;
|
|
334
|
+
});
|
|
248
335
|
|
|
336
|
+
watch([page], async () => {
|
|
337
|
+
setQuery({ page: page.value });
|
|
249
338
|
});
|
|
250
339
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
watch([sort], async () => {
|
|
344
|
+
if (!sort.value.length) {
|
|
345
|
+
setQuery({ sort: undefined });
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
setQuery({ sort: SortQuerySerializer.serialize(sort.value) });
|
|
256
349
|
});
|
|
257
350
|
|
|
351
|
+
|
|
258
352
|
</script>
|