adminforth 1.1.34 → 1.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +30 -22
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +17 -4
- package/dist/spa/package.json +2 -1
- package/dist/spa/spa/src/components/ResourceListTable.vue +1 -10
- package/dist/spa/src/App.vue +125 -45
- package/dist/spa/src/assets/logo.svg +19 -1
- package/dist/spa/src/components/AcceptModal.vue +2 -9
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +16 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +11 -2
- package/dist/spa/src/components/Dropdown.vue +6 -1
- package/dist/spa/src/components/Filters.vue +40 -19
- package/dist/spa/src/components/ResourceForm.vue +26 -24
- package/dist/spa/src/components/ResourceListTable.vue +419 -0
- package/dist/spa/src/components/Toast.vue +66 -0
- package/dist/spa/src/components/ValueRenderer.vue +14 -8
- package/dist/spa/src/composables/useFrontendApi.ts +26 -0
- package/dist/spa/src/composables/useStores.ts +113 -0
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/router/index.ts +30 -20
- package/dist/spa/src/spa_types/core.ts +51 -0
- package/dist/spa/src/stores/core.ts +48 -31
- package/dist/spa/src/stores/filters.ts +22 -0
- package/dist/spa/src/stores/modal.ts +13 -3
- package/dist/spa/src/stores/toast.ts +15 -0
- package/dist/spa/src/stores/user.ts +54 -0
- package/dist/spa/src/utils.ts +62 -7
- package/dist/spa/src/views/CreateView.vue +67 -15
- package/dist/spa/src/views/EditView.vue +45 -11
- package/dist/spa/src/views/ListView.vue +82 -366
- package/dist/spa/src/views/LoginView.vue +16 -4
- package/dist/spa/src/views/ShowView.vue +105 -21
- package/dist/spa/tailwind.config.js +1 -1
- package/dist/spa/vite.config.ts +6 -0
- package/index.ts +38 -28
- package/package.json +1 -1
- package/spa/src/components/ResourceListTable.vue +1 -10
- package/types/AdminForthConfig.ts +0 -3
- package/dist/plugins/AccessControl/index.js +0 -66
- package/dist/plugins/AccessControl/types.js +0 -1
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/types.js +0 -30
- /package/dist/spa/{spa/public → public/assets}/favicon.png +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { FrontendAPIInterface, ConfirmParams, AlertParams, FilterParams,Operator } from '../types/FrontendAPI';
|
|
2
|
+
import { useToastStore } from '../stores/toast';
|
|
3
|
+
import { useModalStore } from '../stores/modal';
|
|
4
|
+
import { useCoreStore } from '@/stores/core';
|
|
5
|
+
import { useFiltersStore } from '@/stores/filters';
|
|
6
|
+
import router from '@/router'
|
|
7
|
+
import type { AdminForthResourceColumn } from '@/types/AdminForthConfig';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
declare global {
|
|
14
|
+
interface Window {
|
|
15
|
+
adminforth: {
|
|
16
|
+
confirm: (params: ConfirmParams) => Promise<void>;
|
|
17
|
+
alert: (params: AlertParams) => void;
|
|
18
|
+
setListFilter: (filter: any) => void;
|
|
19
|
+
updateListFilter: (filter: any) => void;
|
|
20
|
+
clearListFilters: () => void;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class FrontendAPI implements FrontendAPIInterface {
|
|
26
|
+
private validOperators: Operator[] = ['lte', 'gte', 'in','ilike'];
|
|
27
|
+
private toastStore:any
|
|
28
|
+
private modalStore:any
|
|
29
|
+
private filtersStore:any
|
|
30
|
+
private coreStore:any
|
|
31
|
+
init() {
|
|
32
|
+
if (window.adminforth) {
|
|
33
|
+
throw new Error('adminforth already initialized');
|
|
34
|
+
}
|
|
35
|
+
this.toastStore = useToastStore();
|
|
36
|
+
this.modalStore = useModalStore();
|
|
37
|
+
console.log(this.toastStore, this.modalStore,'init of adminforth frontend api')
|
|
38
|
+
window.adminforth = {
|
|
39
|
+
confirm: this.confirm.bind(this),
|
|
40
|
+
alert: this.alert.bind(this),
|
|
41
|
+
setListFilter: this.setListFilter.bind(this),
|
|
42
|
+
updateListFilter: this.updateListFilter.bind(this),
|
|
43
|
+
clearListFilters: this.clearListFilters.bind(this),
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
confirm(params: ConfirmParams): Promise<void> {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
this.modalStore.setModalContent({ content: params.message, acceptText: params.yes, cancelText: params.no })
|
|
50
|
+
this.modalStore.onAcceptFunction = resolve
|
|
51
|
+
this.modalStore.onCancelFunction = reject
|
|
52
|
+
this.modalStore.togleModal()
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
alert(params: AlertParams): void {
|
|
57
|
+
this.toastStore.addToast({
|
|
58
|
+
message: params.message,
|
|
59
|
+
variant: params.variant,
|
|
60
|
+
timeout: params.timeout
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
listFilterValidation(filter: FilterParams): boolean {
|
|
65
|
+
if(router.currentRoute.value.meta.type !== 'list'){
|
|
66
|
+
throw new Error(`Cannot use ${this.setListFilter.name} filter on a list page`)
|
|
67
|
+
} else {
|
|
68
|
+
if(!this.coreStore) this.coreStore = useCoreStore()
|
|
69
|
+
console.log(this.coreStore.resourceColumnsWithFilters,'core store')
|
|
70
|
+
const filterField = this.coreStore.resourceColumnsWithFilters.find((col: AdminForthResourceColumn) => col.name === filter.field)
|
|
71
|
+
if(!filterField){
|
|
72
|
+
throw new Error(`Field ${filter.field} is not available for filtering`)
|
|
73
|
+
}
|
|
74
|
+
if(filterField) {
|
|
75
|
+
if(!this.validOperators.includes(filter.operator)){
|
|
76
|
+
throw new Error(`Operator ${filter.operator} is not valid`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
setListFilter(filter: FilterParams): void {
|
|
84
|
+
if(this.listFilterValidation(filter)){
|
|
85
|
+
this.filtersStore = useFiltersStore()
|
|
86
|
+
if(this.filtersStore.filters.some((f) => {return f.field === filter.field && f.operator === filter.operator})){
|
|
87
|
+
throw new Error(`Filter ${filter.field} with operator ${filter.operator} already exists`)
|
|
88
|
+
} else {
|
|
89
|
+
this.filtersStore.setFilter(filter)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clearListFilters(): void {
|
|
95
|
+
this.filtersStore = useFiltersStore()
|
|
96
|
+
this.filtersStore.clearFilters()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
updateListFilter(filter: FilterParams): void {
|
|
100
|
+
if(this.listFilterValidation(filter)){
|
|
101
|
+
this.filtersStore = useFiltersStore()
|
|
102
|
+
const index = this.filtersStore.filters.findIndex((f: FilterParams) => f.field === filter.field)
|
|
103
|
+
if(index === -1) {
|
|
104
|
+
this.filtersStore.setFilter(filter)
|
|
105
|
+
} else {
|
|
106
|
+
this.filtersStore.setFilters([...this.filtersStore.filters.slice(0, index), filter, ...this.filtersStore.filters.slice(index + 1)])
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
}
|
package/dist/spa/src/main.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
2
|
import ResourceParent from '@/views/ResourceParent.vue'
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import EditView from '@/views/EditView.vue'
|
|
6
|
-
import CreateView from '@/views/CreateView.vue'
|
|
3
|
+
import { useUserStore } from '@/stores/user'
|
|
4
|
+
/* IMPORTANT:ADMINFORTH ROUTES IMPORTS */
|
|
7
5
|
|
|
8
6
|
const router = createRouter({
|
|
9
7
|
history: createWebHistory(import.meta.env.BASE_URL),
|
|
10
8
|
routes: [
|
|
11
|
-
{
|
|
12
|
-
path: '/',
|
|
13
|
-
name: 'home',
|
|
14
|
-
//redirect to login
|
|
15
|
-
/* IMPORTANT:ADMINFORTH REDIRECT */
|
|
16
|
-
|
|
17
|
-
},
|
|
18
9
|
{
|
|
19
10
|
path: '/login',
|
|
20
11
|
name: 'login',
|
|
21
|
-
component: () => import('@/views/LoginView.vue')
|
|
12
|
+
component: () => import('@/views/LoginView.vue'),
|
|
13
|
+
meta: { title: 'login' },
|
|
14
|
+
beforeEnter: async (to, from, next) => {
|
|
15
|
+
const userStore = useUserStore()
|
|
16
|
+
if(localStorage.getItem('isAuthorized') === 'true'){
|
|
17
|
+
next({name: 'home'})
|
|
18
|
+
} else {
|
|
19
|
+
next()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
path: '/resource/:resourceId',
|
|
@@ -27,23 +27,29 @@ const router = createRouter({
|
|
|
27
27
|
children: [
|
|
28
28
|
{
|
|
29
29
|
path: '',
|
|
30
|
-
component: ListView,
|
|
31
|
-
name: 'resource-list'
|
|
30
|
+
component: () => import('@/views/ListView.vue'),
|
|
31
|
+
name: 'resource-list',
|
|
32
|
+
meta: { title: 'list',type: 'list' }
|
|
32
33
|
},
|
|
33
34
|
{
|
|
34
35
|
path: 'show/:primaryKey',
|
|
35
|
-
component: ShowView,
|
|
36
|
-
name: 'resource-show'
|
|
36
|
+
component: () => import('@/views/ShowView.vue'),
|
|
37
|
+
name: 'resource-show',
|
|
38
|
+
meta: { title: 'show', type: 'show'}
|
|
39
|
+
|
|
37
40
|
},
|
|
38
41
|
{
|
|
39
42
|
path: 'edit/:primaryKey',
|
|
40
|
-
component: EditView,
|
|
41
|
-
name: 'resource-edit'
|
|
43
|
+
component: () => import('@/views/EditView.vue'),
|
|
44
|
+
name: 'resource-edit',
|
|
45
|
+
meta: { title: 'edit', type: 'edit'}
|
|
42
46
|
},
|
|
43
47
|
{
|
|
44
48
|
path: 'create',
|
|
45
|
-
component: CreateView,
|
|
46
|
-
name: 'resource-create'
|
|
49
|
+
component: () => import('@/views/CreateView.vue'),
|
|
50
|
+
name: 'resource-create',
|
|
51
|
+
meta: { title: 'create', type: 'create'}
|
|
52
|
+
|
|
47
53
|
},
|
|
48
54
|
]
|
|
49
55
|
},
|
|
@@ -51,4 +57,8 @@ const router = createRouter({
|
|
|
51
57
|
]
|
|
52
58
|
})
|
|
53
59
|
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
54
64
|
export default router
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AdminForthResource, AdminForthResourceColumn } from '../types/AdminForthConfig';
|
|
2
|
+
|
|
3
|
+
export type resourceById = {
|
|
4
|
+
[key: string]: AdminForthResource;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type Menu = {
|
|
8
|
+
label: string,
|
|
9
|
+
icon: string,
|
|
10
|
+
resourceId: string,
|
|
11
|
+
children?: Array<Menu>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Record = {
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type ResourceColumns = {
|
|
19
|
+
[key: string]: Array<AdminForthResourceColumn>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type CoreConfig = {
|
|
23
|
+
brandName: string,
|
|
24
|
+
brandLogo: string,
|
|
25
|
+
title: string,
|
|
26
|
+
datesFormat: string,
|
|
27
|
+
usernameField: string,
|
|
28
|
+
usernameFieldName?: string,
|
|
29
|
+
deleteConfirmation?: boolean,
|
|
30
|
+
auth?: {
|
|
31
|
+
resourceId: string,
|
|
32
|
+
usernameField: string,
|
|
33
|
+
passwordHashField: string,
|
|
34
|
+
loginBackgroundImage: string,
|
|
35
|
+
userFullnameField: string,
|
|
36
|
+
},
|
|
37
|
+
emptyFieldPlaceholder?: {
|
|
38
|
+
show: string,
|
|
39
|
+
list: string,
|
|
40
|
+
|
|
41
|
+
} | string,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
export type AllowedActions = {
|
|
46
|
+
show: boolean,
|
|
47
|
+
create: boolean,
|
|
48
|
+
edit: boolean,
|
|
49
|
+
delete: boolean,
|
|
50
|
+
}
|
|
51
|
+
|
|
@@ -1,39 +1,50 @@
|
|
|
1
1
|
import { ref, computed } from 'vue'
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
3
|
import { callAdminForthApi } from '@/utils';
|
|
4
|
+
import type { AdminForthResource, AdminForthResourceColumn } from '@/types/AdminForthConfig';
|
|
5
|
+
import type { Ref } from 'vue'
|
|
4
6
|
|
|
5
7
|
export const useCoreStore = defineStore('core', () => {
|
|
6
|
-
const resourceById = ref(
|
|
8
|
+
const resourceById: Ref<Object> = ref({});
|
|
7
9
|
const menu = ref([]);
|
|
8
10
|
const config = ref({});
|
|
9
|
-
const record = ref({});
|
|
10
|
-
const
|
|
11
|
+
const record: Ref<any | null> = ref({});
|
|
12
|
+
const resource: Ref<AdminForthResource | null> = ref(null);
|
|
13
|
+
|
|
14
|
+
const resourceColumnsWithFilters = computed(() => {
|
|
15
|
+
if (!resource.value) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
return resource.value.columns.filter((col: AdminForthResourceColumn) => col.showIn?.includes('filter'));
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const resourceOptions = ref(null);
|
|
11
22
|
const resourceColumnsError = ref('');
|
|
12
23
|
const resourceColumnsId = ref(null);
|
|
13
|
-
const
|
|
24
|
+
const adminUser = ref(null);
|
|
14
25
|
|
|
15
26
|
async function fetchMenuAndResource() {
|
|
16
27
|
const resp = await callAdminForthApi({
|
|
17
28
|
path: '/get_base_config',
|
|
18
29
|
method: 'GET',
|
|
19
30
|
});
|
|
31
|
+
if(!resp){
|
|
32
|
+
return
|
|
33
|
+
}
|
|
20
34
|
menu.value = resp.menu;
|
|
21
|
-
resourceById.value = resp.resources.reduce((acc, resource) => {
|
|
35
|
+
resourceById.value = resp.resources.reduce((acc: Object, resource: AdminForthResource) => {
|
|
22
36
|
acc[resource.resourceId] = resource;
|
|
23
37
|
return acc;
|
|
24
38
|
}, {});
|
|
25
39
|
config.value = resp.config;
|
|
26
|
-
|
|
40
|
+
adminUser.value = resp.user;
|
|
27
41
|
console.log('🌍 AdminForth v', resp.version);
|
|
28
|
-
|
|
29
|
-
// find homepage:true in menu recuresively
|
|
30
|
-
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
async function fetchRecord({ resourceId, primaryKey }) {
|
|
34
45
|
record.value = null;
|
|
35
46
|
|
|
36
|
-
if (!
|
|
47
|
+
if (!resource.value) {
|
|
37
48
|
throw new Error('Columns not fetched yet');
|
|
38
49
|
}
|
|
39
50
|
|
|
@@ -45,7 +56,7 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
45
56
|
resourceId: resourceId,
|
|
46
57
|
filters: [
|
|
47
58
|
{
|
|
48
|
-
field:
|
|
59
|
+
field: resource.value.columns.find((col: AdminForthResourceColumn) => col.primaryKey).name,
|
|
49
60
|
operator: 'eq',
|
|
50
61
|
value: primaryKey
|
|
51
62
|
}
|
|
@@ -56,30 +67,39 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
56
67
|
}
|
|
57
68
|
});
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
if (respData.error) {
|
|
71
|
+
window.adminforth.alert({
|
|
72
|
+
message: respData.error,
|
|
73
|
+
variant: 'danger',
|
|
74
|
+
timeout: 'unlimited'
|
|
75
|
+
});
|
|
76
|
+
record.value = {};
|
|
77
|
+
} else {
|
|
78
|
+
record.value = respData.data[0];
|
|
79
|
+
}
|
|
80
|
+
|
|
61
81
|
}
|
|
62
82
|
|
|
63
|
-
async function
|
|
64
|
-
if (resourceColumnsId.value === resourceId &&
|
|
83
|
+
async function fetchResourceFull({ resourceId }: { resourceId: string }) {
|
|
84
|
+
if (resourceColumnsId.value === resourceId && resource.value) {
|
|
65
85
|
// already fetched
|
|
66
86
|
return;
|
|
67
87
|
}
|
|
68
88
|
resourceColumnsId.value = resourceId;
|
|
69
|
-
resourceColumns.value = null;
|
|
70
89
|
resourceColumnsError.value = '';
|
|
71
90
|
const res = await callAdminForthApi({
|
|
72
|
-
path: '/
|
|
91
|
+
path: '/get_resource',
|
|
73
92
|
method: 'POST',
|
|
74
93
|
body: {
|
|
75
94
|
resourceId,
|
|
76
95
|
}
|
|
77
|
-
}
|
|
78
|
-
);
|
|
96
|
+
});
|
|
79
97
|
if (res.error) {
|
|
80
98
|
resourceColumnsError.value = res.error;
|
|
81
99
|
} else {
|
|
82
|
-
|
|
100
|
+
resourceById.value[resourceId] = res.resource;
|
|
101
|
+
resource.value = res.resource;
|
|
102
|
+
resourceOptions.value = res.resource.options;
|
|
83
103
|
}
|
|
84
104
|
}
|
|
85
105
|
|
|
@@ -91,21 +111,16 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
91
111
|
config.value = {...config.value, ...res};
|
|
92
112
|
}
|
|
93
113
|
|
|
94
|
-
|
|
95
|
-
await callAdminForthApi({
|
|
96
|
-
path: '/logout',
|
|
97
|
-
method: 'POST',
|
|
98
|
-
});
|
|
99
|
-
}
|
|
114
|
+
|
|
100
115
|
|
|
101
116
|
const username = computed(() => {
|
|
102
117
|
const usernameField = config.value.usernameField;
|
|
103
|
-
return
|
|
118
|
+
return adminUser.value && adminUser.value[usernameField];
|
|
104
119
|
});
|
|
105
120
|
|
|
106
121
|
const userFullname = computed(() => {
|
|
107
122
|
const userFullnameField = config.value.userFullnameField;
|
|
108
|
-
return
|
|
123
|
+
return adminUser.value && adminUser.value[userFullnameField];
|
|
109
124
|
})
|
|
110
125
|
|
|
111
126
|
|
|
@@ -119,9 +134,11 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
119
134
|
fetchMenuAndResource,
|
|
120
135
|
fetchRecord,
|
|
121
136
|
record,
|
|
122
|
-
|
|
123
|
-
fetchColumns,
|
|
137
|
+
fetchResourceFull,
|
|
124
138
|
resourceColumnsError,
|
|
125
|
-
|
|
139
|
+
resourceOptions,
|
|
140
|
+
resource,
|
|
141
|
+
adminUser,
|
|
142
|
+
resourceColumnsWithFilters
|
|
126
143
|
}
|
|
127
144
|
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ref } from 'vue'
|
|
2
|
+
import { defineStore } from 'pinia'
|
|
3
|
+
import { callAdminForthApi } from '@/utils';
|
|
4
|
+
|
|
5
|
+
export const useFiltersStore = defineStore('filters', () => {
|
|
6
|
+
const filters = ref([]);
|
|
7
|
+
const setFilter = (filter: any) => {
|
|
8
|
+
filters.value = [...filters.value, filter];
|
|
9
|
+
}
|
|
10
|
+
const setFilters = (f: any) => {
|
|
11
|
+
filters.value = [...f];
|
|
12
|
+
}
|
|
13
|
+
const getFilters = () => {
|
|
14
|
+
return filters.value;
|
|
15
|
+
}
|
|
16
|
+
const clearFilters = () => {
|
|
17
|
+
filters.value = [];
|
|
18
|
+
}
|
|
19
|
+
return {setFilter, getFilters,clearFilters, filters,setFilters}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { ref } from 'vue'
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
type ModalContentType = {
|
|
5
|
+
title?: string;
|
|
6
|
+
content?: string;
|
|
7
|
+
acceptText?: string;
|
|
8
|
+
cancelText?: string;
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
|
|
6
12
|
export const useModalStore = defineStore('modal', () => {
|
|
@@ -12,13 +18,17 @@ export const useModalStore = defineStore('modal', () => {
|
|
|
12
18
|
});
|
|
13
19
|
const isOpened = ref(false);
|
|
14
20
|
const onAcceptFunction: any = ref(()=>{});
|
|
21
|
+
const onCancelFunction: any = ref(()=>{});
|
|
15
22
|
function togleModal() {
|
|
16
23
|
isOpened.value = !isOpened.value;
|
|
17
24
|
}
|
|
18
25
|
function setOnAcceptFunction(func: Function) {
|
|
19
26
|
onAcceptFunction.value = func;
|
|
20
27
|
}
|
|
21
|
-
function
|
|
28
|
+
function setOnCancelFunction(func: Function) {
|
|
29
|
+
onCancelFunction.value = func;
|
|
30
|
+
}
|
|
31
|
+
function setModalContent(content: ModalContentType) {
|
|
22
32
|
modalContent.value = content;
|
|
23
33
|
}
|
|
24
34
|
function resetmodalState() {
|
|
@@ -33,6 +43,6 @@ export const useModalStore = defineStore('modal', () => {
|
|
|
33
43
|
|
|
34
44
|
}
|
|
35
45
|
|
|
36
|
-
return {isOpened, setModalContent, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState}
|
|
46
|
+
return {isOpened, setModalContent,onCancelFunction, togleModal,modalContent, setOnAcceptFunction, onAcceptFunction,resetmodalState,setOnCancelFunction}
|
|
37
47
|
|
|
38
48
|
})
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ref, computed } from 'vue'
|
|
2
|
+
import { defineStore } from 'pinia'
|
|
3
|
+
import { callAdminForthApi } from '@/utils';
|
|
4
|
+
import { v1 as uuid } from 'uuid';
|
|
5
|
+
|
|
6
|
+
export const useToastStore = defineStore('toast', () => {
|
|
7
|
+
const toasts = ref([]);
|
|
8
|
+
const addToast = (toast) => {
|
|
9
|
+
toasts.value.push({...toast, id: uuid()});
|
|
10
|
+
};
|
|
11
|
+
const removeToast = (toast) => {
|
|
12
|
+
toasts.value = toasts.value.filter((t) => t.id !== toast.id);
|
|
13
|
+
};
|
|
14
|
+
return { toasts, addToast, removeToast };
|
|
15
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {ref} from 'vue';
|
|
2
|
+
import {defineStore} from 'pinia';
|
|
3
|
+
import { callAdminForthApi } from '@/utils';
|
|
4
|
+
|
|
5
|
+
export const useUserStore = defineStore('user', () => {
|
|
6
|
+
const isAuthorized = ref(false);
|
|
7
|
+
|
|
8
|
+
function authorize() {
|
|
9
|
+
isAuthorized.value = true;
|
|
10
|
+
localStorage.setItem('isAuthorized', 'true');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function unauthorize() {
|
|
14
|
+
isAuthorized.value = false;
|
|
15
|
+
localStorage.setItem('isAuthorized', 'false');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function logout() {
|
|
19
|
+
await callAdminForthApi({
|
|
20
|
+
path: '/logout',
|
|
21
|
+
method: 'POST',
|
|
22
|
+
});
|
|
23
|
+
unauthorize();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// async function checkAuth( skipApiCall = false){
|
|
27
|
+
// console.log('checkAuth', isAuthorized.value, skipApiCall)
|
|
28
|
+
// if(isAuthorized.value) {
|
|
29
|
+
// return true}
|
|
30
|
+
// else {
|
|
31
|
+
// if(skipApiCall) return false;
|
|
32
|
+
// const resp = await callAdminForthApi({
|
|
33
|
+
// path: '/check_auth',
|
|
34
|
+
// method: 'POST',
|
|
35
|
+
// });
|
|
36
|
+
// if (resp.status !== 401) {
|
|
37
|
+
// authorize();
|
|
38
|
+
// return true;
|
|
39
|
+
// }
|
|
40
|
+
// else {
|
|
41
|
+
// unauthorize();
|
|
42
|
+
// return false;}
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
// }
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
isAuthorized,
|
|
49
|
+
authorize,
|
|
50
|
+
unauthorize,
|
|
51
|
+
logout
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
});
|
package/dist/spa/src/utils.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
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
9
|
export async function callApi({path, method, body=undefined} ) {
|
|
6
10
|
const options = {
|
|
@@ -13,14 +17,18 @@ export async function callApi({path, method, body=undefined} ) {
|
|
|
13
17
|
const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
|
|
14
18
|
const r = await fetch(fullPath, options);
|
|
15
19
|
if (r.status == 401) {
|
|
16
|
-
|
|
17
|
-
router.push({name: 'login'});
|
|
20
|
+
useUserStore().unauthorize();
|
|
21
|
+
router.push({ name: 'login' });
|
|
18
22
|
return null;
|
|
19
|
-
}
|
|
23
|
+
}
|
|
20
24
|
return await r.json();
|
|
21
25
|
}
|
|
22
26
|
|
|
23
|
-
export async function callAdminForthApi({ path, method, body=undefined }
|
|
27
|
+
export async function callAdminForthApi({ path, method, body=undefined }: {
|
|
28
|
+
path: string,
|
|
29
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
|
30
|
+
body?: any
|
|
31
|
+
}) {
|
|
24
32
|
try {
|
|
25
33
|
return callApi({path: `/adminapi/v1${path}`, method, body} );
|
|
26
34
|
} catch (e) {
|
|
@@ -29,8 +37,8 @@ export async function callAdminForthApi({ path, method, body=undefined }) {
|
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
export function getCustomComponent(
|
|
33
|
-
const name =
|
|
40
|
+
export function getCustomComponent({ file, meta }: { file: string, meta: any }) {
|
|
41
|
+
const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
|
|
34
42
|
console.log('resolving name', name);
|
|
35
43
|
return resolveComponent(name);
|
|
36
44
|
}
|
|
@@ -43,4 +51,51 @@ export function getIcon(icon: string) {
|
|
|
43
51
|
const [iconSet, iconName] = icon.split(':');
|
|
44
52
|
const compName = 'Icon' + iconName.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
|
|
45
53
|
return resolveComponent(compName);
|
|
46
|
-
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const loadFile = (file: string) => {
|
|
57
|
+
if (file.startsWith('http')) {
|
|
58
|
+
return file;
|
|
59
|
+
}
|
|
60
|
+
let path;
|
|
61
|
+
let baseUrl = '';
|
|
62
|
+
if (file.startsWith('@/')) {
|
|
63
|
+
path = file.replace('@/', '');
|
|
64
|
+
baseUrl = new URL(`./${path}`, import.meta.url).href;
|
|
65
|
+
} else if (file.startsWith('@@/')) {
|
|
66
|
+
path = file.replace('@@/', '');
|
|
67
|
+
baseUrl = new URL(`./custom/${path}`, import.meta.url).href;
|
|
68
|
+
} else {
|
|
69
|
+
baseUrl = new URL(`./${file}`, import.meta.url).href;
|
|
70
|
+
}
|
|
71
|
+
return baseUrl;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function checkEmptyValues(value: any, viewType:'show' | 'list' ) {
|
|
75
|
+
const config: CoreConfig | {} = useCoreStore().config;
|
|
76
|
+
let emptyFieldPlaceholder = '';
|
|
77
|
+
if (config.emptyFieldPlaceholder) {
|
|
78
|
+
if(typeof config.emptyFieldPlaceholder === 'string') {
|
|
79
|
+
emptyFieldPlaceholder = config.emptyFieldPlaceholder;
|
|
80
|
+
} else {
|
|
81
|
+
emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '';
|
|
82
|
+
}
|
|
83
|
+
if (value === null || value === undefined || value === '') {
|
|
84
|
+
return emptyFieldPlaceholder;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function checkAcessByAllowedActions(allowedActions:any, action:any ) {
|
|
91
|
+
if (!allowedActions) {
|
|
92
|
+
console.warn('allowedActions not set');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if(allowedActions[action] === false) {
|
|
96
|
+
console.warn(`Action ${action} is not allowed`);
|
|
97
|
+
router.back();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|