adminforth 1.1.20 → 1.1.21
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 +68 -17
- package/dist/plugins/AccessControl/index.js +3 -56
- package/dist/plugins/AuditLog/index.js +66 -0
- package/dist/plugins/AuditLog/types.js +1 -0
- package/dist/plugins/ForeignInlineListPlugin/custom/InlineList.vue +3 -9
- package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +248 -0
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +1 -189
- package/dist/spa/package.json +1 -6
- package/dist/spa/spa/src/components/ResourceListTable.vue +35 -1
- package/dist/spa/spa/src/components/ShowTableItem.vue +14 -0
- package/dist/spa/spa/src/composables/useFrontendApi.ts +14 -0
- package/dist/spa/spa/src/utils.ts +2 -0
- package/dist/spa/spa/src/views/CreateView.vue +3 -5
- package/dist/spa/spa/src/views/EditView.vue +2 -5
- package/dist/spa/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/spa/src/views/ListView.vue +4 -33
- package/dist/spa/src/App.vue +57 -76
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/src/components/Dropdown.vue +4 -13
- package/dist/spa/src/components/Filters.vue +22 -55
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +99 -157
- package/dist/spa/src/components/ValueRenderer.vue +1 -11
- package/dist/spa/src/router/index.ts +2 -3
- package/dist/spa/src/stores/core.ts +33 -22
- package/dist/spa/src/utils.ts +3 -5
- package/dist/spa/src/views/CreateView.vue +7 -15
- package/dist/spa/src/views/EditView.vue +8 -36
- package/dist/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/src/views/ListView.vue +23 -35
- package/dist/spa/src/views/LoginView.vue +1 -1
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +9 -20
- package/dist/spa/tailwind.config.js +2 -5
- package/index.ts +87 -17
- package/package.json +1 -1
- package/plugins/AccessControl/index.ts +1 -55
- package/plugins/AccessControl/types.ts +1 -9
- package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +3 -9
- package/spa/src/components/ResourceListTable.vue +35 -1
- package/spa/src/composables/useFrontendApi.ts +14 -0
- package/spa/src/utils.ts +2 -0
- package/spa/src/views/CreateView.vue +3 -5
- package/spa/src/views/EditView.vue +2 -5
- package/spa/src/views/ListView.vue +4 -33
- package/types/AdminForthConfig.ts +12 -1
- package/dist/spa/src/components/CustomRangePicker.vue +0 -148
|
@@ -78,6 +78,7 @@ import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
|
78
78
|
import { onMounted, ref, watch } from 'vue';
|
|
79
79
|
import { useRoute, useRouter } from 'vue-router';
|
|
80
80
|
import { computed } from 'vue';
|
|
81
|
+
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
const isValid = ref(false);
|
|
@@ -141,11 +142,8 @@ async function saveRecord() {
|
|
|
141
142
|
},
|
|
142
143
|
});
|
|
143
144
|
if (response.error) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
variant: 'danger',
|
|
147
|
-
timeout: 'unlimited',
|
|
148
|
-
})
|
|
145
|
+
showErrorTost(response.error);
|
|
146
|
+
|
|
149
147
|
}
|
|
150
148
|
saving.value = false;
|
|
151
149
|
if (route.query.returnTo) {
|
|
@@ -73,6 +73,7 @@ import { callAdminForthApi, getCustomComponent,checkAcessByAllowedActions } from
|
|
|
73
73
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
74
74
|
import { computed, onMounted, ref } from 'vue';
|
|
75
75
|
import { useRoute, useRouter } from 'vue-router';
|
|
76
|
+
import { showErrorTost } from '@/composables/useFrontendApi';
|
|
76
77
|
|
|
77
78
|
const coreStore = useCoreStore();
|
|
78
79
|
|
|
@@ -155,11 +156,7 @@ async function saveRecord() {
|
|
|
155
156
|
},
|
|
156
157
|
});
|
|
157
158
|
if (resp.error) {
|
|
158
|
-
|
|
159
|
-
message: resp.error,
|
|
160
|
-
variant: 'danger',
|
|
161
|
-
timeout: 'unlimited',
|
|
162
|
-
})
|
|
159
|
+
showErrorTost(resp.error);
|
|
163
160
|
}
|
|
164
161
|
saving.value = false;
|
|
165
162
|
router.push({ name: 'resource-show', params: { resourceId: route.params.resourceId, primaryKey: coreStore.record[coreStore.primaryKey] } });
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
@update:page="page = $event"
|
|
88
88
|
@update:sort="sort = $event"
|
|
89
89
|
@update:checkboxes="checkboxes = $event"
|
|
90
|
+
@update:records="getList"
|
|
90
91
|
:pageSize="pageSize"
|
|
91
92
|
:totalRows="totalRows"
|
|
92
93
|
:checkboxes="checkboxes"
|
|
@@ -113,6 +114,7 @@ import { callAdminForthApi, getIcon } from '@/utils';
|
|
|
113
114
|
import { initFlowbite } from 'flowbite';
|
|
114
115
|
import { computed, onMounted, ref, watch } from 'vue';
|
|
115
116
|
import { useRoute } from 'vue-router';
|
|
117
|
+
import { showErrorTost } from '@/composables/useFrontendApi'
|
|
116
118
|
|
|
117
119
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
118
120
|
import { getCustomComponent } from '@/utils';
|
|
@@ -184,11 +186,7 @@ async function getList() {
|
|
|
184
186
|
}
|
|
185
187
|
});
|
|
186
188
|
if (data.error) {
|
|
187
|
-
|
|
188
|
-
message: data.error,
|
|
189
|
-
variant: 'danger',
|
|
190
|
-
timeout: 'unlimited',
|
|
191
|
-
});
|
|
189
|
+
showErrorTost(data.error);
|
|
192
190
|
rows.value = [];
|
|
193
191
|
totalRows.value = 0;
|
|
194
192
|
return;
|
|
@@ -201,36 +199,9 @@ async function getList() {
|
|
|
201
199
|
}
|
|
202
200
|
|
|
203
201
|
|
|
204
|
-
function showDeleteModal(row) {
|
|
205
|
-
if (!coreStore.config?.deleteConfirmation) {
|
|
206
|
-
return deleteRecord(row);
|
|
207
|
-
}
|
|
208
|
-
modalStore.setModalContent({
|
|
209
|
-
content: 'Are you sure you want to delete this item?',
|
|
210
|
-
acceptText: 'Delete',
|
|
211
|
-
cancelText: 'Cancel',
|
|
212
|
-
});
|
|
213
|
-
modalStore.setOnAcceptFunction(() => {
|
|
214
|
-
return deleteRecord(row)
|
|
215
|
-
})
|
|
216
|
-
console.log('row', row);
|
|
217
|
-
modalStore.togleModal();
|
|
218
202
|
|
|
219
|
-
}
|
|
220
203
|
|
|
221
|
-
|
|
222
|
-
await callAdminForthApi({
|
|
223
|
-
path: '/delete_record',
|
|
224
|
-
method: 'POST',
|
|
225
|
-
body: {
|
|
226
|
-
resourceId: route.params.resourceId,
|
|
227
|
-
primaryKey: row._primaryKeyValue,
|
|
228
|
-
recordId: row.id
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
await getList();
|
|
232
|
-
modalStore.resetmodalState()
|
|
233
|
-
}
|
|
204
|
+
|
|
234
205
|
|
|
235
206
|
async function startBulkAction(actionId) {
|
|
236
207
|
const data = await callAdminForthApi({
|
|
@@ -833,9 +833,20 @@ export enum AllowedActionsEnum {
|
|
|
833
833
|
filter = 'filter',
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Defines whether user has access to an action, can statically be Boolean
|
|
838
|
+
* or function which returns Boolean or string with error message
|
|
839
|
+
*
|
|
840
|
+
*/
|
|
841
|
+
export type AllowedActionValue = boolean | ((adminUser: AdminUser, resource: AdminForthResource, meta: any) => Promise<boolean | string>);
|
|
836
842
|
|
|
843
|
+
/**
|
|
844
|
+
* Object which describes allowed actions for user.
|
|
845
|
+
*/
|
|
837
846
|
export type AllowedActions = {
|
|
838
|
-
[key in AllowedActionsEnum]?:
|
|
847
|
+
[key in AllowedActionsEnum]?: AllowedActionValue
|
|
848
|
+
} & {
|
|
849
|
+
all?: AllowedActionValue;
|
|
839
850
|
}
|
|
840
851
|
|
|
841
852
|
export type ValidationObject = {
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="flex flex-wrap gap-2">
|
|
3
|
-
<input
|
|
4
|
-
type="number" aria-describedby="helper-text-explanation"
|
|
5
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
6
|
-
placeholder="From"
|
|
7
|
-
v-model="start"
|
|
8
|
-
>
|
|
9
|
-
|
|
10
|
-
<input
|
|
11
|
-
type="number" aria-describedby="helper-text-explanation"
|
|
12
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
13
|
-
placeholder="To"
|
|
14
|
-
v-model="end"
|
|
15
|
-
>
|
|
16
|
-
|
|
17
|
-
<button
|
|
18
|
-
v-if="isChanged"
|
|
19
|
-
type="button"
|
|
20
|
-
class="flex items-center p-0.5 ml-auto 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-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 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
21
|
-
@click="clear">Clear
|
|
22
|
-
</button>
|
|
23
|
-
|
|
24
|
-
<div class="w-full">
|
|
25
|
-
<vue-slider
|
|
26
|
-
class="custom-slider"
|
|
27
|
-
:dot-size="20"
|
|
28
|
-
height="7.99px"
|
|
29
|
-
:min="minFormatted"
|
|
30
|
-
:max="maxFormatted"
|
|
31
|
-
v-model="sliderValue"
|
|
32
|
-
@update:model-value="updateFromSlider($event)"
|
|
33
|
-
/>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</template>
|
|
37
|
-
<script setup lang="ts">
|
|
38
|
-
import VueSlider from 'vue-slider-component';
|
|
39
|
-
import 'vue-slider-component/theme/antd.css'
|
|
40
|
-
import {computed, onMounted, ref, watch} from "vue";
|
|
41
|
-
import debounce from 'debounce'
|
|
42
|
-
|
|
43
|
-
const props = defineProps({
|
|
44
|
-
valueStart: {
|
|
45
|
-
default: '',
|
|
46
|
-
},
|
|
47
|
-
valueEnd: {
|
|
48
|
-
default: '',
|
|
49
|
-
},
|
|
50
|
-
min: {
|
|
51
|
-
default: 0,
|
|
52
|
-
},
|
|
53
|
-
max: {
|
|
54
|
-
default: 10,
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
|
|
59
|
-
|
|
60
|
-
const minFormatted = computed(() => Math.floor(props.min));
|
|
61
|
-
const maxFormatted = computed(() => Math.ceil(props.max));
|
|
62
|
-
|
|
63
|
-
const isChanged = computed(() => {
|
|
64
|
-
return start.value && start.value !== minFormatted.value || end.value && end.value !== maxFormatted.value;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const start = ref(props.valueStart);
|
|
68
|
-
const end = ref(props.valueEnd);
|
|
69
|
-
|
|
70
|
-
const sliderValue = ref([minFormatted.value, maxFormatted.value]);
|
|
71
|
-
|
|
72
|
-
const updateFromSlider =
|
|
73
|
-
debounce((value: [number, number]) => {
|
|
74
|
-
start.value = value[0] === minFormatted.value ? '': value[0];
|
|
75
|
-
end.value = value[1] === maxFormatted.value ? '': value[1];
|
|
76
|
-
}, 500);
|
|
77
|
-
|
|
78
|
-
onMounted(() => {
|
|
79
|
-
updateStartFromProps();
|
|
80
|
-
updateEndFromProps();
|
|
81
|
-
|
|
82
|
-
watch(() => props.valueStart, (value) => {
|
|
83
|
-
updateStartFromProps();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
watch(() => props.valueEnd, (value) => {
|
|
87
|
-
updateEndFromProps();
|
|
88
|
-
});
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
function updateStartFromProps() {
|
|
92
|
-
start.value = props.valueStart;
|
|
93
|
-
setSliderValues(start.value, end.value)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function updateEndFromProps() {
|
|
97
|
-
end.value = props.valueEnd;
|
|
98
|
-
setSliderValues(start.value, end.value)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
watch(start, () => {
|
|
102
|
-
console.log('⚡ emit', start.value)
|
|
103
|
-
emit('update:valueStart', start.value)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
watch(end, () => {
|
|
107
|
-
console.log('⚡ emit', end.value)
|
|
108
|
-
emit('update:valueEnd', end.value);
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
const clear = () => {
|
|
112
|
-
start.value = ''
|
|
113
|
-
end.value = ''
|
|
114
|
-
setSliderValues('', '')
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function setSliderValues(start, end) {
|
|
118
|
-
sliderValue.value = [start || minFormatted.value, end || maxFormatted.value];
|
|
119
|
-
}
|
|
120
|
-
</script>
|
|
121
|
-
|
|
122
|
-
<style lang="scss" scoped>
|
|
123
|
-
.custom-slider {
|
|
124
|
-
&:deep(.vue-slider-rail) {
|
|
125
|
-
background-color: rgb(229 231 235);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
&:deep(.vue-slider-dot-handle) {
|
|
129
|
-
background-color: #1c64f2;
|
|
130
|
-
border: none;
|
|
131
|
-
box-shadow: none;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
&:deep(.vue-slider-dot-handle:hover) {
|
|
135
|
-
background-color: #1c64f2;
|
|
136
|
-
border: none;
|
|
137
|
-
box-shadow: none;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
&:deep(.vue-slider-process) {
|
|
141
|
-
background-color: rgb(225 239 254);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
&:deep(.vue-slider-process:hover) {
|
|
145
|
-
background-color: rgb(225 239 254);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
</style>
|