adminforth 1.2.42 → 1.2.44
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 +5 -0
- package/dist/spa/index.html +4 -4
- package/dist/spa/package-lock.json +206 -5
- package/dist/spa/package.json +8 -2
- package/dist/spa/public/assets/favicon.png +0 -0
- package/dist/spa/src/App.vue +171 -72
- package/dist/spa/src/assets/logo.svg +19 -1
- package/dist/spa/src/components/AcceptModal.vue +3 -10
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +19 -6
- package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
- package/dist/spa/src/components/CustomRangePicker.vue +148 -0
- package/dist/spa/src/components/Dropdown.vue +19 -5
- package/dist/spa/src/components/Filters.vue +91 -37
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +163 -103
- 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 +22 -6
- 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 -19
- package/dist/spa/src/spa_types/core.ts +51 -0
- package/dist/spa/src/stores/core.ts +65 -59
- 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 +65 -8
- package/dist/spa/src/views/CreateView.vue +76 -16
- package/dist/spa/src/views/EditView.vue +76 -14
- package/dist/spa/src/views/ListView.vue +88 -360
- package/dist/spa/src/views/LoginView.vue +17 -5
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +114 -19
- package/dist/spa/tailwind.config.js +5 -2
- package/dist/spa/vite.config.ts +6 -0
- package/index.ts +5 -0
- package/package.json +1 -1
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/src/views/HomeView.vue +0 -8
- package/dist/spa/src/views/HomeView.vue +0 -8
- package/dist/types.js +0 -30
|
@@ -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,23 +1,24 @@
|
|
|
1
1
|
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
|
-
import HomeView from '../views/HomeView.vue'
|
|
3
2
|
import ResourceParent from '@/views/ResourceParent.vue'
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
import EditView from '@/views/EditView.vue'
|
|
7
|
-
import CreateView from '@/views/CreateView.vue'
|
|
3
|
+
import { useUserStore } from '@/stores/user'
|
|
4
|
+
/* IMPORTANT:ADMINFORTH ROUTES IMPORTS */
|
|
8
5
|
|
|
9
6
|
const router = createRouter({
|
|
10
7
|
history: createWebHistory(import.meta.env.BASE_URL),
|
|
11
8
|
routes: [
|
|
12
|
-
{
|
|
13
|
-
path: '/',
|
|
14
|
-
name: 'home',
|
|
15
|
-
component: HomeView
|
|
16
|
-
},
|
|
17
9
|
{
|
|
18
10
|
path: '/login',
|
|
19
11
|
name: 'login',
|
|
20
|
-
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
|
+
}
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
24
|
path: '/resource/:resourceId',
|
|
@@ -26,23 +27,29 @@ const router = createRouter({
|
|
|
26
27
|
children: [
|
|
27
28
|
{
|
|
28
29
|
path: '',
|
|
29
|
-
component: ListView,
|
|
30
|
-
name: 'resource-list'
|
|
30
|
+
component: () => import('@/views/ListView.vue'),
|
|
31
|
+
name: 'resource-list',
|
|
32
|
+
meta: { title: 'list',type: 'list' }
|
|
31
33
|
},
|
|
32
34
|
{
|
|
33
35
|
path: 'show/:primaryKey',
|
|
34
|
-
component: ShowView,
|
|
35
|
-
name: 'resource-show'
|
|
36
|
+
component: () => import('@/views/ShowView.vue'),
|
|
37
|
+
name: 'resource-show',
|
|
38
|
+
meta: { title: 'show', type: 'show'}
|
|
39
|
+
|
|
36
40
|
},
|
|
37
41
|
{
|
|
38
42
|
path: 'edit/:primaryKey',
|
|
39
|
-
component: EditView,
|
|
40
|
-
name: 'resource-edit'
|
|
43
|
+
component: () => import('@/views/EditView.vue'),
|
|
44
|
+
name: 'resource-edit',
|
|
45
|
+
meta: { title: 'edit', type: 'edit'}
|
|
41
46
|
},
|
|
42
47
|
{
|
|
43
48
|
path: 'create',
|
|
44
|
-
component: CreateView,
|
|
45
|
-
name: 'resource-create'
|
|
49
|
+
component: () => import('@/views/CreateView.vue'),
|
|
50
|
+
name: 'resource-create',
|
|
51
|
+
meta: { title: 'create', type: 'create'}
|
|
52
|
+
|
|
46
53
|
},
|
|
47
54
|
]
|
|
48
55
|
},
|
|
@@ -50,4 +57,8 @@ const router = createRouter({
|
|
|
50
57
|
]
|
|
51
58
|
})
|
|
52
59
|
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
53
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,96 +1,105 @@
|
|
|
1
1
|
import { ref, computed } from 'vue'
|
|
2
2
|
import { defineStore } from 'pinia'
|
|
3
3
|
import { callAdminForthApi } from '@/utils';
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
async function findHomepage(menu) {
|
|
7
|
-
for (const item of menu) {
|
|
8
|
-
if (item.homepage) {
|
|
9
|
-
return item;
|
|
10
|
-
}
|
|
11
|
-
if (item.children) {
|
|
12
|
-
const res = findHomepage(item.children);
|
|
13
|
-
if (res) {
|
|
14
|
-
return res;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
4
|
+
import type { AdminForthResource, AdminForthResourceColumn } from '@/types/AdminForthConfig';
|
|
5
|
+
import type { Ref } from 'vue'
|
|
20
6
|
|
|
21
7
|
export const useCoreStore = defineStore('core', () => {
|
|
22
|
-
const resourceById = ref(
|
|
8
|
+
const resourceById: Ref<Object> = ref({});
|
|
23
9
|
const menu = ref([]);
|
|
24
10
|
const config = ref({});
|
|
25
|
-
const record = ref({});
|
|
26
|
-
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);
|
|
27
22
|
const resourceColumnsError = ref('');
|
|
28
23
|
const resourceColumnsId = ref(null);
|
|
29
|
-
const
|
|
24
|
+
const adminUser = ref(null);
|
|
30
25
|
|
|
31
26
|
async function fetchMenuAndResource() {
|
|
32
27
|
const resp = await callAdminForthApi({
|
|
33
28
|
path: '/get_base_config',
|
|
34
29
|
method: 'GET',
|
|
35
30
|
});
|
|
31
|
+
if(!resp){
|
|
32
|
+
return
|
|
33
|
+
}
|
|
36
34
|
menu.value = resp.menu;
|
|
37
|
-
resourceById.value = resp.resources.reduce((acc, resource) => {
|
|
35
|
+
resourceById.value = resp.resources.reduce((acc: Object, resource: AdminForthResource) => {
|
|
38
36
|
acc[resource.resourceId] = resource;
|
|
39
37
|
return acc;
|
|
40
38
|
}, {});
|
|
41
39
|
config.value = resp.config;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// find homepage:true in menu recuresively
|
|
45
|
-
if (import.meta.env.DEV){
|
|
46
|
-
return
|
|
47
|
-
} else {
|
|
48
|
-
const homepage = await findHomepage(menu.value);
|
|
49
|
-
if (homepage) {
|
|
50
|
-
if (homepage.resourceId) {
|
|
51
|
-
// redirect to homepage
|
|
52
|
-
router.push({ name: 'resource-list', params: { resourceId: homepage.resourceId } });
|
|
53
|
-
} else {
|
|
54
|
-
// redirect to path
|
|
55
|
-
router.push(homepage.path);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
40
|
+
adminUser.value = resp.user;
|
|
41
|
+
console.log('🌍 AdminForth v', resp.version);
|
|
59
42
|
}
|
|
60
43
|
|
|
61
44
|
async function fetchRecord({ resourceId, primaryKey }) {
|
|
62
45
|
record.value = null;
|
|
46
|
+
|
|
47
|
+
if (!resource.value) {
|
|
48
|
+
throw new Error('Columns not fetched yet');
|
|
49
|
+
}
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
path: '/
|
|
51
|
+
const respData = await callAdminForthApi({
|
|
52
|
+
path: '/get_resource_data',
|
|
66
53
|
method: 'POST',
|
|
67
54
|
body: {
|
|
55
|
+
source: 'show',
|
|
68
56
|
resourceId: resourceId,
|
|
69
|
-
|
|
57
|
+
filters: [
|
|
58
|
+
{
|
|
59
|
+
field: resource.value.columns.find((col: AdminForthResourceColumn) => col.primaryKey).name,
|
|
60
|
+
operator: 'eq',
|
|
61
|
+
value: primaryKey
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
sort: [],
|
|
65
|
+
limit: 1,
|
|
66
|
+
offset: 0
|
|
70
67
|
}
|
|
71
68
|
});
|
|
69
|
+
|
|
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
|
+
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
async function
|
|
75
|
-
if (resourceColumnsId.value === resourceId &&
|
|
83
|
+
async function fetchResourceFull({ resourceId }: { resourceId: string }) {
|
|
84
|
+
if (resourceColumnsId.value === resourceId && resource.value) {
|
|
76
85
|
// already fetched
|
|
77
86
|
return;
|
|
78
87
|
}
|
|
79
88
|
resourceColumnsId.value = resourceId;
|
|
80
|
-
resourceColumns.value = null;
|
|
81
89
|
resourceColumnsError.value = '';
|
|
82
90
|
const res = await callAdminForthApi({
|
|
83
|
-
path: '/
|
|
91
|
+
path: '/get_resource',
|
|
84
92
|
method: 'POST',
|
|
85
93
|
body: {
|
|
86
94
|
resourceId,
|
|
87
95
|
}
|
|
88
|
-
}
|
|
89
|
-
);
|
|
96
|
+
});
|
|
90
97
|
if (res.error) {
|
|
91
98
|
resourceColumnsError.value = res.error;
|
|
92
99
|
} else {
|
|
93
|
-
|
|
100
|
+
resourceById.value[resourceId] = res.resource;
|
|
101
|
+
resource.value = res.resource;
|
|
102
|
+
resourceOptions.value = res.resource.options;
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
|
|
@@ -102,21 +111,16 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
102
111
|
config.value = {...config.value, ...res};
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
|
|
106
|
-
await callAdminForthApi({
|
|
107
|
-
path: '/logout',
|
|
108
|
-
method: 'POST',
|
|
109
|
-
});
|
|
110
|
-
}
|
|
114
|
+
|
|
111
115
|
|
|
112
116
|
const username = computed(() => {
|
|
113
117
|
const usernameField = config.value.usernameField;
|
|
114
|
-
return
|
|
118
|
+
return adminUser.value && adminUser.value[usernameField];
|
|
115
119
|
});
|
|
116
120
|
|
|
117
121
|
const userFullname = computed(() => {
|
|
118
122
|
const userFullnameField = config.value.userFullnameField;
|
|
119
|
-
return
|
|
123
|
+
return adminUser.value && adminUser.value[userFullnameField];
|
|
120
124
|
})
|
|
121
125
|
|
|
122
126
|
|
|
@@ -130,9 +134,11 @@ export const useCoreStore = defineStore('core', () => {
|
|
|
130
134
|
fetchMenuAndResource,
|
|
131
135
|
fetchRecord,
|
|
132
136
|
record,
|
|
133
|
-
|
|
134
|
-
fetchColumns,
|
|
137
|
+
fetchResourceFull,
|
|
135
138
|
resourceColumnsError,
|
|
136
|
-
|
|
139
|
+
resourceOptions,
|
|
140
|
+
resource,
|
|
141
|
+
adminUser,
|
|
142
|
+
resourceColumnsWithFilters
|
|
137
143
|
}
|
|
138
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,9 +37,11 @@ export async function callAdminForthApi({ path, method, body=undefined }) {
|
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
export function getCustomComponent({ file, meta }: { file: string, meta: any }) {
|
|
41
|
+
const name = file.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
|
|
42
|
+
console.log('resolving name', name);
|
|
43
|
+
return resolveComponent(name);
|
|
44
|
+
}
|
|
35
45
|
|
|
36
46
|
export function getIcon(icon: string) {
|
|
37
47
|
// icon format is "feather:icon-name". We need to get IconName in pascal case
|
|
@@ -41,4 +51,51 @@ export function getIcon(icon: string) {
|
|
|
41
51
|
const [iconSet, iconName] = icon.split(':');
|
|
42
52
|
const compName = 'Icon' + iconName.split('-').map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');
|
|
43
53
|
return resolveComponent(compName);
|
|
44
|
-
}
|
|
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
|
+
|