adminforth 1.2.96 → 1.2.97
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/dataConnectors/baseConnector.ts +26 -14
- package/dataConnectors/clickhouse.ts +57 -37
- package/dataConnectors/mongo.ts +45 -14
- package/dataConnectors/postgres.ts +53 -36
- package/dataConnectors/sqlite.ts +37 -35
- package/dist/dataConnectors/baseConnector.js +16 -7
- package/dist/dataConnectors/clickhouse.js +58 -48
- package/dist/dataConnectors/mongo.js +29 -12
- package/dist/dataConnectors/postgres.js +62 -52
- package/dist/dataConnectors/sqlite.js +52 -45
- package/dist/index.js +5 -1
- package/dist/modules/operationalResource.js +88 -0
- package/dist/modules/restApi.js +22 -23
- package/dist/spa/index.html +4 -4
- package/dist/spa/package-lock.json +421 -6
- package/dist/spa/package.json +10 -2
- package/dist/spa/src/App.vue +236 -94
- package/dist/spa/src/assets/base.css +2 -0
- package/dist/spa/src/assets/logo.svg +19 -1
- package/dist/spa/src/components/AcceptModal.vue +3 -10
- package/dist/spa/src/components/Breadcrumbs.vue +3 -2
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +25 -10
- package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
- package/dist/spa/src/components/Dropdown.vue +19 -5
- package/dist/spa/src/components/Filters.vue +103 -38
- package/dist/spa/src/components/MenuLink.vue +3 -3
- package/dist/spa/src/components/ResourceForm.vue +205 -116
- package/dist/spa/{spa/src → src}/components/ResourceListTable.vue +6 -3
- package/dist/spa/{spa/src → src}/components/Toast.vue +9 -3
- package/dist/spa/src/components/ValueRenderer.vue +30 -8
- package/dist/spa/src/index.scss +2 -1
- package/dist/spa/src/main.ts +2 -2
- package/dist/spa/src/router/index.ts +29 -19
- package/dist/spa/src/stores/core.ts +65 -59
- package/dist/spa/src/stores/modal.ts +13 -3
- package/dist/spa/src/types/AdminForthConfig.ts +1477 -0
- package/dist/spa/src/types/FrontendAPI.ts +121 -0
- package/dist/spa/src/utils.ts +69 -10
- package/dist/spa/src/views/CreateView.vue +69 -16
- package/dist/spa/src/views/EditView.vue +68 -14
- package/dist/spa/src/views/ListView.vue +100 -371
- package/dist/spa/src/views/LoginView.vue +67 -29
- package/dist/spa/src/views/ResourceParent.vue +1 -2
- package/dist/spa/src/views/ShowView.vue +116 -25
- package/dist/spa/tailwind.config.js +8 -3
- package/dist/spa/vite.config.ts +13 -0
- package/dist/types/AdminForthConfig.js +37 -0
- package/index.ts +12 -3
- package/modules/operationalResource.ts +97 -0
- package/modules/restApi.ts +27 -26
- package/package.json +1 -1
- package/types/AdminForthConfig.ts +91 -8
- package/dist/spa/.vscode/extensions.json +0 -6
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/.eslintrc.cjs +0 -14
- package/dist/spa/spa/.vscode/extensions.json +0 -6
- package/dist/spa/spa/README.md +0 -39
- package/dist/spa/spa/env.d.ts +0 -1
- package/dist/spa/spa/index.html +0 -23
- package/dist/spa/spa/package-lock.json +0 -4573
- package/dist/spa/spa/package.json +0 -49
- package/dist/spa/spa/postcss.config.js +0 -6
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/src/App.vue +0 -314
- package/dist/spa/spa/src/assets/base.css +0 -0
- package/dist/spa/spa/src/assets/logo.svg +0 -19
- package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
- package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
- package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
- package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
- package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
- package/dist/spa/spa/src/components/Dropdown.vue +0 -168
- package/dist/spa/spa/src/components/Filters.vue +0 -222
- package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
- package/dist/spa/spa/src/components/MenuLink.vue +0 -24
- package/dist/spa/spa/src/components/ResourceForm.vue +0 -289
- package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
- package/dist/spa/spa/src/components/ValueRenderer.vue +0 -66
- package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
- package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
- package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
- package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
- package/dist/spa/spa/src/index.scss +0 -27
- package/dist/spa/spa/src/main.ts +0 -18
- package/dist/spa/spa/src/router/index.ts +0 -63
- package/dist/spa/spa/src/stores/core.ts +0 -144
- package/dist/spa/spa/src/stores/modal.ts +0 -48
- package/dist/spa/spa/src/utils.ts +0 -103
- package/dist/spa/spa/src/views/CreateView.vue +0 -156
- package/dist/spa/spa/src/views/EditView.vue +0 -157
- package/dist/spa/spa/src/views/HomeView.vue +0 -8
- package/dist/spa/spa/src/views/ListView.vue +0 -258
- package/dist/spa/spa/src/views/LoginView.vue +0 -143
- package/dist/spa/spa/src/views/ResourceParent.vue +0 -17
- package/dist/spa/spa/src/views/ShowView.vue +0 -184
- package/dist/spa/spa/tailwind.config.js +0 -17
- package/dist/spa/spa/tsconfig.app.json +0 -14
- package/dist/spa/spa/tsconfig.json +0 -11
- package/dist/spa/spa/tsconfig.node.json +0 -19
- package/dist/spa/spa/vite.config.ts +0 -56
- package/dist/spa/src/views/HomeView.vue +0 -8
- package/dist/types.js +0 -30
- /package/dist/spa/{spa/public → public}/assets/favicon.png +0 -0
- /package/dist/spa/{spa/src → src}/components/CustomRangePicker.vue +0 -0
- /package/dist/spa/{spa/src → src}/components/SkeleteLoader.vue +0 -0
- /package/dist/spa/{spa/src → src}/composables/useFrontendApi.ts +0 -0
- /package/dist/spa/{spa/src → src}/composables/useStores.ts +0 -0
- /package/dist/spa/{spa/src → src}/spa_types/core.ts +0 -0
- /package/dist/spa/{spa/src → src}/stores/filters.ts +0 -0
- /package/dist/spa/{spa/src → src}/stores/toast.ts +0 -0
- /package/dist/spa/{spa/src → src}/stores/user.ts +0 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
|
|
2
|
+
export interface FrontendAPIInterface {
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Show a confirmation dialog
|
|
6
|
+
*
|
|
7
|
+
* The dialog will be displayed to the user
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* const isConfirmed = await window.adminforth.confirm({message: 'Are you sure?', yes: 'Yes', no: 'No'})
|
|
13
|
+
* if (isConfirmed) {
|
|
14
|
+
* your code...
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @param params - The parameters of the dialog
|
|
19
|
+
* @returns A promise that resolves when the user confirms the dialog
|
|
20
|
+
*/
|
|
21
|
+
confirm(params:ConfirmParams ): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Show an alert
|
|
24
|
+
*
|
|
25
|
+
* The alert will be displayed to the user
|
|
26
|
+
*
|
|
27
|
+
* Example:
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* window.adminforth.alert({message: 'Hello', variant: 'success'})
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @param params - The parameters of the alert
|
|
34
|
+
*/
|
|
35
|
+
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
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type ConfirmParams = {
|
|
70
|
+
/**
|
|
71
|
+
* The message to display in the dialog
|
|
72
|
+
*/
|
|
73
|
+
message?: string;
|
|
74
|
+
/**
|
|
75
|
+
* The text to display in the "accept" button
|
|
76
|
+
*/
|
|
77
|
+
yes?: string;
|
|
78
|
+
/**
|
|
79
|
+
* The text to display in the "cancel" button
|
|
80
|
+
*/
|
|
81
|
+
no?: string;
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type AlertParams = {
|
|
86
|
+
/**
|
|
87
|
+
* The message to display in the alert
|
|
88
|
+
*/
|
|
89
|
+
message?: string;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The message to display in the alert as HTML (can be used instead of message)
|
|
93
|
+
*/
|
|
94
|
+
messageHtml?: string;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The variant of the alert
|
|
98
|
+
*/
|
|
99
|
+
variant?: AlertVariant | keyof typeof AlertVariant;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The timeout of the alert in seconds or 'unlimited' to keep the alert open until the user closes it.
|
|
103
|
+
* Default is 10 seconds;
|
|
104
|
+
*/
|
|
105
|
+
timeout?: number | 'unlimited';
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
export enum AlertVariant {
|
|
112
|
+
danger = 'danger',
|
|
113
|
+
success = 'success',
|
|
114
|
+
warning = 'warning',
|
|
115
|
+
info = 'info'
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
package/dist/spa/src/utils.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { onMounted, ref, resolveComponent } from 'vue';
|
|
2
|
+
import type { CoreConfig } from './spa_types/core';
|
|
2
3
|
|
|
3
4
|
import router from "./router";
|
|
5
|
+
import { useRouter } from 'vue-router';
|
|
6
|
+
import { useCoreStore } from './stores/core';
|
|
7
|
+
import { useUserStore } from './stores/user';
|
|
4
8
|
|
|
5
|
-
export async function callApi({path, method, body=undefined}
|
|
9
|
+
export async function callApi({path, method, body=undefined}: {
|
|
10
|
+
path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
|
|
11
|
+
body?: any
|
|
12
|
+
}): Promise<any> {
|
|
6
13
|
const options = {
|
|
7
14
|
method,
|
|
8
15
|
headers: {
|
|
@@ -12,15 +19,19 @@ export async function callApi({path, method, body=undefined} ) {
|
|
|
12
19
|
};
|
|
13
20
|
const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
|
|
14
21
|
const r = await fetch(fullPath, options);
|
|
15
|
-
if (r.status == 401) {
|
|
16
|
-
|
|
17
|
-
router.push({name: 'login'});
|
|
22
|
+
if (r.status == 401 ) {
|
|
23
|
+
useUserStore().unauthorize();
|
|
24
|
+
router.push({ name: 'login' });
|
|
18
25
|
return null;
|
|
19
|
-
}
|
|
26
|
+
}
|
|
20
27
|
return await r.json();
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
export async function callAdminForthApi({ path, method, body=undefined }
|
|
30
|
+
export async function callAdminForthApi({ path, method, body=undefined }: {
|
|
31
|
+
path: string,
|
|
32
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
|
33
|
+
body?: any
|
|
34
|
+
}) {
|
|
24
35
|
try {
|
|
25
36
|
return callApi({path: `/adminapi/v1${path}`, method, body} );
|
|
26
37
|
} catch (e) {
|
|
@@ -29,9 +40,10 @@ export async function callAdminForthApi({ path, method, body=undefined }) {
|
|
|
29
40
|
}
|
|
30
41
|
}
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
export function getCustomComponent({ file, meta }: { file: string, meta: any }) {
|
|
44
|
+
const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
|
|
45
|
+
return resolveComponent(name);
|
|
46
|
+
}
|
|
35
47
|
|
|
36
48
|
export function getIcon(icon: string) {
|
|
37
49
|
// icon format is "feather:icon-name". We need to get IconName in pascal case
|
|
@@ -41,4 +53,51 @@ export function getIcon(icon: string) {
|
|
|
41
53
|
const [iconSet, iconName] = icon.split(':');
|
|
42
54
|
const compName = 'Icon' + iconName.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
|
|
43
55
|
return resolveComponent(compName);
|
|
44
|
-
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const loadFile = (file: string) => {
|
|
59
|
+
if (file.startsWith('http')) {
|
|
60
|
+
return file;
|
|
61
|
+
}
|
|
62
|
+
let path;
|
|
63
|
+
let baseUrl = '';
|
|
64
|
+
if (file.startsWith('@/')) {
|
|
65
|
+
path = file.replace('@/', '');
|
|
66
|
+
baseUrl = new URL(`./${path}`, import.meta.url).href;
|
|
67
|
+
} else if (file.startsWith('@@/')) {
|
|
68
|
+
path = file.replace('@@/', '');
|
|
69
|
+
baseUrl = new URL(`./custom/${path}`, import.meta.url).href;
|
|
70
|
+
} else {
|
|
71
|
+
baseUrl = new URL(`./${file}`, import.meta.url).href;
|
|
72
|
+
}
|
|
73
|
+
return baseUrl;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function checkEmptyValues(value: any, viewType: 'show' | 'list' ) {
|
|
77
|
+
const config: CoreConfig | {} = useCoreStore().config;
|
|
78
|
+
let emptyFieldPlaceholder = '';
|
|
79
|
+
if (config.emptyFieldPlaceholder) {
|
|
80
|
+
if(typeof config.emptyFieldPlaceholder === 'string') {
|
|
81
|
+
emptyFieldPlaceholder = config.emptyFieldPlaceholder;
|
|
82
|
+
} else {
|
|
83
|
+
emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '';
|
|
84
|
+
}
|
|
85
|
+
if (value === null || value === undefined || value === '') {
|
|
86
|
+
return emptyFieldPlaceholder;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function checkAcessByAllowedActions(allowedActions:any, action:any ) {
|
|
93
|
+
if (!allowedActions) {
|
|
94
|
+
console.warn('allowedActions not set');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if(allowedActions[action] === false) {
|
|
98
|
+
console.warn(`Action ${action} is not allowed`);
|
|
99
|
+
router.back();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative">
|
|
3
3
|
|
|
4
|
+
<component
|
|
5
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.beforeBreadcrumbs || []"
|
|
6
|
+
:is="getCustomComponent(c)"
|
|
7
|
+
:meta="c.meta"
|
|
8
|
+
:record="coreStore.record"
|
|
9
|
+
:resource="coreStore.resource"
|
|
10
|
+
:adminUser="coreStore.adminUser"
|
|
11
|
+
/>
|
|
12
|
+
|
|
4
13
|
<BreadcrumbsWithButtons>
|
|
5
14
|
<!-- save and cancle -->
|
|
6
15
|
<button @click="$router.back()"
|
|
7
|
-
class="flex items-center py-1 px-3 me-2
|
|
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-blue-700 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"
|
|
8
17
|
>
|
|
9
18
|
Cancel
|
|
10
19
|
</button>
|
|
11
20
|
|
|
12
21
|
<button @click="saveRecord"
|
|
13
|
-
class="flex items-center py-1 px-3
|
|
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"
|
|
14
23
|
:disabled="saving || (validating && !isValid)"
|
|
15
24
|
>
|
|
16
25
|
<svg v-if="saving" x
|
|
@@ -22,32 +31,55 @@
|
|
|
22
31
|
|
|
23
32
|
</BreadcrumbsWithButtons>
|
|
24
33
|
|
|
34
|
+
<component
|
|
35
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.afterBreadcrumbs || []"
|
|
36
|
+
:is="getCustomComponent(c)"
|
|
37
|
+
:meta="c.meta"
|
|
38
|
+
:record="coreStore.record"
|
|
39
|
+
:resource="coreStore.resource"
|
|
40
|
+
:adminUser="coreStore.adminUser"
|
|
41
|
+
/>
|
|
42
|
+
|
|
25
43
|
<SingleSkeletLoader v-if="loading"></SingleSkeletLoader>
|
|
44
|
+
|
|
26
45
|
|
|
27
46
|
<ResourceForm
|
|
28
47
|
v-else
|
|
29
|
-
:record="
|
|
30
|
-
:
|
|
48
|
+
:record="record"
|
|
49
|
+
:resource="coreStore.resource"
|
|
31
50
|
@update:record="onUpdateRecord"
|
|
32
51
|
@update:isValid="isValid = $event"
|
|
33
52
|
:validating="validating"
|
|
53
|
+
:source="'create'"
|
|
34
54
|
>
|
|
35
55
|
</ResourceForm>
|
|
36
56
|
|
|
57
|
+
<component
|
|
58
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.create?.bottom || []"
|
|
59
|
+
:is="getCustomComponent(c)"
|
|
60
|
+
:meta="c.meta"
|
|
61
|
+
:record="coreStore.record"
|
|
62
|
+
:resource="coreStore.resource"
|
|
63
|
+
:adminUser="coreStore.adminUser"
|
|
64
|
+
/>
|
|
65
|
+
|
|
37
66
|
</div>
|
|
38
67
|
</template>
|
|
39
68
|
|
|
40
69
|
|
|
41
70
|
<script setup>
|
|
42
71
|
|
|
43
|
-
import { ref, computed, onMounted } from 'vue';
|
|
44
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
45
|
-
import { useCoreStore } from '@/stores/core';
|
|
46
|
-
import { callAdminForthApi } from '@/utils';
|
|
47
|
-
import ResourceForm from '@/components/ResourceForm.vue';
|
|
48
72
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
49
|
-
import
|
|
73
|
+
import ResourceForm from '@/components/ResourceForm.vue';
|
|
50
74
|
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
75
|
+
import { useCoreStore } from '@/stores/core';
|
|
76
|
+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
|
|
77
|
+
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
78
|
+
import { onMounted, ref, watch } from 'vue';
|
|
79
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
80
|
+
import { computed } from 'vue';
|
|
81
|
+
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
82
|
+
|
|
51
83
|
|
|
52
84
|
const isValid = ref(false);
|
|
53
85
|
const validating = ref(false);
|
|
@@ -62,6 +94,15 @@ const record = ref({});
|
|
|
62
94
|
|
|
63
95
|
const coreStore = useCoreStore();
|
|
64
96
|
|
|
97
|
+
const initalValues = computed(() => {
|
|
98
|
+
if (!route.query.values) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
return JSON.parse(decodeURIComponent(route.query.values));
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
65
106
|
async function onUpdateRecord(newRecord) {
|
|
66
107
|
console.log('newRecord', newRecord);
|
|
67
108
|
record.value = newRecord;
|
|
@@ -69,13 +110,16 @@ async function onUpdateRecord(newRecord) {
|
|
|
69
110
|
|
|
70
111
|
onMounted(async () => {
|
|
71
112
|
loading.value = true;
|
|
72
|
-
|
|
113
|
+
record.value = initalValues.value;
|
|
114
|
+
await coreStore.fetchResourceFull({
|
|
73
115
|
resourceId: route.params.resourceId
|
|
74
116
|
});
|
|
75
117
|
loading.value = false;
|
|
118
|
+
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'create');
|
|
76
119
|
});
|
|
77
120
|
|
|
78
121
|
async function saveRecord() {
|
|
122
|
+
console.log('saveRecord isValid', isValid.value);
|
|
79
123
|
if (!isValid.value) {
|
|
80
124
|
validating.value = true;
|
|
81
125
|
return;
|
|
@@ -91,12 +135,21 @@ async function saveRecord() {
|
|
|
91
135
|
record: record.value,
|
|
92
136
|
},
|
|
93
137
|
});
|
|
138
|
+
if (response.error) {
|
|
139
|
+
showErrorTost(response.error);
|
|
140
|
+
}
|
|
94
141
|
saving.value = false;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
142
|
+
if (route.query.returnTo) {
|
|
143
|
+
router.push(route.query.returnTo);
|
|
144
|
+
} else {
|
|
145
|
+
router.push({
|
|
146
|
+
name: 'resource-show',
|
|
147
|
+
params: {
|
|
148
|
+
resourceId: route.params.resourceId,
|
|
149
|
+
primaryKey: response.newRecordId
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
100
153
|
}
|
|
101
154
|
|
|
102
155
|
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative">
|
|
3
|
+
<component
|
|
4
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.beforeBreadcrumbs || []"
|
|
5
|
+
:is="getCustomComponent(c)"
|
|
6
|
+
:meta="c.meta"
|
|
7
|
+
:record="editableRecord"
|
|
8
|
+
:resource="coreStore.resource"
|
|
9
|
+
:adminUser="coreStore.adminUser"
|
|
10
|
+
/>
|
|
3
11
|
|
|
4
12
|
<BreadcrumbsWithButtons>
|
|
5
13
|
<!-- save and cancle -->
|
|
6
14
|
<button @click="$router.back()"
|
|
7
|
-
class="flex items-center py-1 px-3 me-2
|
|
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-blue-700 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"
|
|
8
16
|
>
|
|
9
17
|
Cancel
|
|
10
18
|
</button>
|
|
11
19
|
|
|
12
20
|
<button @click="saveRecord"
|
|
13
|
-
class="flex items-center py-1 px-3
|
|
21
|
+
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"
|
|
14
22
|
:disabled="saving || (validating && !isValid)"
|
|
15
23
|
>
|
|
16
24
|
<IconFloppyDiskSolid class="w-4 h-4" />
|
|
@@ -19,31 +27,53 @@
|
|
|
19
27
|
|
|
20
28
|
</BreadcrumbsWithButtons>
|
|
21
29
|
|
|
30
|
+
<component
|
|
31
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.afterBreadcrumbs || []"
|
|
32
|
+
:is="getCustomComponent(c)"
|
|
33
|
+
:meta="c.meta"
|
|
34
|
+
:record="coreStore.record"
|
|
35
|
+
:resource="coreStore.resource"
|
|
36
|
+
:adminUser="coreStore.adminUser"
|
|
37
|
+
/>
|
|
38
|
+
|
|
22
39
|
<SingleSkeletLoader v-if="loading"></SingleSkeletLoader>
|
|
23
40
|
|
|
24
41
|
<ResourceForm
|
|
25
42
|
v-else
|
|
26
|
-
:record="
|
|
27
|
-
:
|
|
43
|
+
:record="editableRecord"
|
|
44
|
+
:resource="coreStore.resource"
|
|
45
|
+
:adminUser="coreStore.adminUser"
|
|
28
46
|
@update:record="onUpdateRecord"
|
|
29
47
|
@update:isValid="isValid = $event"
|
|
30
48
|
:validating="validating"
|
|
49
|
+
:source="'edit'"
|
|
31
50
|
>
|
|
32
51
|
</ResourceForm>
|
|
52
|
+
|
|
53
|
+
<component
|
|
54
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.edit?.bottom || []"
|
|
55
|
+
:is="getCustomComponent(c)"
|
|
56
|
+
:meta="c.meta"
|
|
57
|
+
:record="coreStore.record"
|
|
58
|
+
:resource="coreStore.resource"
|
|
59
|
+
:adminUser="coreStore.adminUser"
|
|
60
|
+
/>
|
|
61
|
+
|
|
33
62
|
</div>
|
|
34
63
|
</template>
|
|
35
64
|
|
|
36
65
|
|
|
37
66
|
<script setup>
|
|
38
67
|
|
|
39
|
-
import { ref, computed, onMounted } from 'vue';
|
|
40
|
-
import { useRoute, useRouter } from 'vue-router';
|
|
41
|
-
import { useCoreStore } from '@/stores/core';
|
|
42
|
-
import ResourceForm from '@/components/ResourceForm.vue';
|
|
43
68
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
44
|
-
import
|
|
69
|
+
import ResourceForm from '@/components/ResourceForm.vue';
|
|
45
70
|
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
46
|
-
import {
|
|
71
|
+
import { useCoreStore } from '@/stores/core';
|
|
72
|
+
import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from '@/utils';
|
|
73
|
+
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
74
|
+
import { computed, onMounted, ref } from 'vue';
|
|
75
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
76
|
+
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
47
77
|
|
|
48
78
|
const coreStore = useCoreStore();
|
|
49
79
|
|
|
@@ -63,18 +93,31 @@ async function onUpdateRecord(newRecord) {
|
|
|
63
93
|
record.value = newRecord;
|
|
64
94
|
}
|
|
65
95
|
|
|
96
|
+
const editableRecord = computed(() => {
|
|
97
|
+
const newRecord = { ...coreStore.record };
|
|
98
|
+
if (!coreStore.resource) {
|
|
99
|
+
return {};
|
|
100
|
+
}
|
|
101
|
+
coreStore.resource.columns.forEach(column => {
|
|
102
|
+
if (column.foreignResource) {
|
|
103
|
+
newRecord[column.name] = newRecord[column.name]?.pk
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
return newRecord;
|
|
107
|
+
})
|
|
66
108
|
|
|
67
109
|
onMounted(async () => {
|
|
110
|
+
|
|
68
111
|
loading.value = true;
|
|
69
112
|
|
|
70
|
-
await coreStore.
|
|
113
|
+
await coreStore.fetchResourceFull({
|
|
71
114
|
resourceId: route.params.resourceId
|
|
72
115
|
});
|
|
73
116
|
await coreStore.fetchRecord({
|
|
74
117
|
resourceId: route.params.resourceId,
|
|
75
118
|
primaryKey: route.params.primaryKey,
|
|
76
119
|
});
|
|
77
|
-
|
|
120
|
+
checkAcessByAllowedActions(coreStore.resourceOptions.allowedActions,'edit');
|
|
78
121
|
loading.value = false;
|
|
79
122
|
});
|
|
80
123
|
|
|
@@ -87,15 +130,26 @@ async function saveRecord() {
|
|
|
87
130
|
}
|
|
88
131
|
|
|
89
132
|
saving.value = true;
|
|
90
|
-
|
|
133
|
+
|
|
134
|
+
const updates = {};
|
|
135
|
+
for (const key in record.value) {
|
|
136
|
+
if (record.value[key] !== coreStore.record[key]) {
|
|
137
|
+
updates[key] = record.value[key];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const resp = await callAdminForthApi({
|
|
91
142
|
method: 'POST',
|
|
92
143
|
path: `/update_record`,
|
|
93
144
|
body: {
|
|
94
145
|
resourceId: route.params.resourceId,
|
|
95
146
|
recordId: route.params.primaryKey,
|
|
96
|
-
record:
|
|
147
|
+
record: updates,
|
|
97
148
|
},
|
|
98
149
|
});
|
|
150
|
+
if (resp.error) {
|
|
151
|
+
showErrorTost(resp.error);
|
|
152
|
+
}
|
|
99
153
|
saving.value = false;
|
|
100
154
|
router.push({ name: 'resource-show', params: { resourceId: route.params.resourceId, primaryKey: coreStore.record[coreStore.primaryKey] } });
|
|
101
155
|
}
|