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.
Files changed (50) hide show
  1. package/dist/index.js +68 -17
  2. package/dist/plugins/AccessControl/index.js +3 -56
  3. package/dist/plugins/AuditLog/index.js +66 -0
  4. package/dist/plugins/AuditLog/types.js +1 -0
  5. package/dist/plugins/ForeignInlineListPlugin/custom/InlineList.vue +3 -9
  6. package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +248 -0
  7. package/dist/spa/index.html +2 -2
  8. package/dist/spa/package-lock.json +1 -189
  9. package/dist/spa/package.json +1 -6
  10. package/dist/spa/spa/src/components/ResourceListTable.vue +35 -1
  11. package/dist/spa/spa/src/components/ShowTableItem.vue +14 -0
  12. package/dist/spa/spa/src/composables/useFrontendApi.ts +14 -0
  13. package/dist/spa/spa/src/utils.ts +2 -0
  14. package/dist/spa/spa/src/views/CreateView.vue +3 -5
  15. package/dist/spa/spa/src/views/EditView.vue +2 -5
  16. package/dist/spa/spa/src/views/HomeView.vue +8 -0
  17. package/dist/spa/spa/src/views/ListView.vue +4 -33
  18. package/dist/spa/src/App.vue +57 -76
  19. package/dist/spa/src/components/AcceptModal.vue +1 -1
  20. package/dist/spa/src/components/CustomDatePicker.vue +3 -3
  21. package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
  22. package/dist/spa/src/components/Dropdown.vue +4 -13
  23. package/dist/spa/src/components/Filters.vue +22 -55
  24. package/dist/spa/src/components/MenuLink.vue +2 -2
  25. package/dist/spa/src/components/ResourceForm.vue +99 -157
  26. package/dist/spa/src/components/ValueRenderer.vue +1 -11
  27. package/dist/spa/src/router/index.ts +2 -3
  28. package/dist/spa/src/stores/core.ts +33 -22
  29. package/dist/spa/src/utils.ts +3 -5
  30. package/dist/spa/src/views/CreateView.vue +7 -15
  31. package/dist/spa/src/views/EditView.vue +8 -36
  32. package/dist/spa/src/views/HomeView.vue +8 -0
  33. package/dist/spa/src/views/ListView.vue +23 -35
  34. package/dist/spa/src/views/LoginView.vue +1 -1
  35. package/dist/spa/src/views/ResourceParent.vue +1 -1
  36. package/dist/spa/src/views/ShowView.vue +9 -20
  37. package/dist/spa/tailwind.config.js +2 -5
  38. package/index.ts +87 -17
  39. package/package.json +1 -1
  40. package/plugins/AccessControl/index.ts +1 -55
  41. package/plugins/AccessControl/types.ts +1 -9
  42. package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +3 -9
  43. package/spa/src/components/ResourceListTable.vue +35 -1
  44. package/spa/src/composables/useFrontendApi.ts +14 -0
  45. package/spa/src/utils.ts +2 -0
  46. package/spa/src/views/CreateView.vue +3 -5
  47. package/spa/src/views/EditView.vue +2 -5
  48. package/spa/src/views/ListView.vue +4 -33
  49. package/types/AdminForthConfig.ts +12 -1
  50. 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
- window.adminforth.alert({
145
- message: response.error,
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
- window.adminforth.alert({
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
- window.adminforth.alert({
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
- async function deleteRecord(row) {
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]?: boolean
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>