adminforth 1.1.14 → 1.1.16

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 CHANGED
@@ -158,6 +158,7 @@ class AdminForth {
158
158
  res.columns = [];
159
159
  }
160
160
  res.columns.forEach((col) => {
161
+ var _b, _c, _d, _e;
161
162
  col.label = col.label || guessLabelFromName(col.name);
162
163
  //define default sortable
163
164
  if (!Object.keys(col).includes('sortable')) {
@@ -192,6 +193,20 @@ class AdminForth {
192
193
  errors.push(`Resource "${res.resourceId}" column "${col.name}" has invalid showIn value "${wrongShowIn}", allowed values are ${Object.keys(AdminForthResourcePages).join(', ')}`);
193
194
  }
194
195
  col.showIn = col.showIn || Object.values(AdminForthResourcePages);
196
+ if (col.foreignResource) {
197
+ const befHook = (_c = (_b = col.foreignResource.hooks) === null || _b === void 0 ? void 0 : _b.dropdownList) === null || _c === void 0 ? void 0 : _c.beforeDatasourceRequest;
198
+ if (befHook) {
199
+ if (!Array.isArray(befHook)) {
200
+ col.foreignResource.hooks.dropdownList.beforeDatasourceRequest = [befHook];
201
+ }
202
+ }
203
+ const aftHook = (_e = (_d = col.foreignResource.hooks) === null || _d === void 0 ? void 0 : _d.dropdownList) === null || _e === void 0 ? void 0 : _e.afterDatasourceResponse;
204
+ if (aftHook) {
205
+ if (!Array.isArray(aftHook)) {
206
+ col.foreignResource.hooks.dropdownList.afterDatasourceResponse = [aftHook];
207
+ }
208
+ }
209
+ }
195
210
  });
196
211
  if (!res.options) {
197
212
  res.options = { bulkActions: [], allowedActions: {} };
@@ -0,0 +1,248 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <!-- todo exclude foreign column-->
4
+ <Filters
5
+ v-if="listResource"
6
+ :columns="listResource.columns.filter((c) => c.name !== listResourceRefColumn.name)"
7
+ v-model:filters="filters"
8
+ :columnsMinMax="columnsMinMax"
9
+ :show="filtersShow"
10
+ @hide="filtersShow = false"
11
+ />
12
+ </Teleport>
13
+
14
+ <td colspan="2">
15
+ <div class="flex items-center gap-1">
16
+ <h4 v-if="listResource"
17
+ class="px-6 py-4"
18
+ >{{ listResource.label }} inline records</h4>
19
+
20
+ <button
21
+ @click="()=>{checkboxes = []}"
22
+ v-if="checkboxes.length"
23
+ data-tooltip-target="tooltip-remove-all"
24
+ data-tooltip-placement="bottom"
25
+ class="flex gap-1 items-center py-1 px-3 me-2 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"
26
+ >
27
+ <IconBanOutline class="w-5 h-5 "/>
28
+ <div id="tooltip-remove-all" role="tooltip"
29
+ class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
30
+ Remove selection
31
+ <div class="tooltip-arrow" data-popper-arrow></div>
32
+ </div>
33
+ </button>
34
+
35
+ <button
36
+ v-if="checkboxes.length"
37
+ v-for="(action,i) in listResource?.options?.bulkActions"
38
+ :key="action.id"
39
+ @click="startBulkAction(action.id)"
40
+ class="flex gap-1 items-center py-1 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"
41
+ :class="{'bg-red-100 text-red-800 border-red-400 dark:bg-red-700 dark:text-red-400 dark:border-red-400':action.state==='danger', 'bg-green-100 text-green-800 border-green-400 dark:bg-green-700 dark:text-green-400 dark:border-green-400':action.state==='success',
42
+ 'bg-blue-100 text-blue-800 border-blue-400 dark:bg-blue-700 dark:text-blue-400 dark:border-blue-400':action.state==='active',
43
+ }"
44
+ >
45
+ <component
46
+ v-if="action.icon"
47
+ :is="getIcon(action.icon)"
48
+ class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"></component>
49
+
50
+ {{ `${action.label} (${checkboxes.length})` }}
51
+ </button>
52
+
53
+ <RouterLink v-if="listResource?.options?.allowedActions?.create"
54
+ :to="{
55
+ name: 'resource-create',
56
+ params: { resourceId: listResource.resourceId },
57
+ query: {
58
+ values: encodeURIComponent(JSON.stringify({[listResourceRefColumn.name]: props.record[selfPrimaryKeyColumn.name]})),
59
+ returnTo: $route.fullPath,
60
+ },
61
+ }"
62
+ class="flex items-center py-1 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 rounded-default"
63
+ >
64
+ <IconPlusOutline class="w-4 h-4 me-2"/>
65
+ Create
66
+ </RouterLink>
67
+
68
+ <button
69
+ class="flex gap-1 items-center py-1 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 rounded-default"
70
+ @click="()=>{filtersShow = !filtersShow}"
71
+ >
72
+ <IconFilterOutline class="w-4 h-4 me-2"/>
73
+ Filter
74
+ <span
75
+ class="bg-red-100 text-red-800 text-xs font-medium px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
76
+ v-if="filters.length">
77
+ {{ filters.length }}
78
+ </span>
79
+ </button>
80
+
81
+ </div>
82
+
83
+ <ResourceListTable
84
+ :resource="listResource"
85
+ :rows="rows"
86
+ @update:page="page = $event"
87
+ @update:sort="sort = $event"
88
+ @update:checkboxes="checkboxes = $event"
89
+ :pageSize="pageSize"
90
+ :totalRows="totalRows"
91
+ :checkboxes="checkboxes"
92
+ />
93
+
94
+ </td>
95
+ </template>
96
+
97
+ <script setup>
98
+ import { callAdminForthApi } from '@/utils';
99
+ import { ref, onMounted, watch, computed } from 'vue';
100
+ import ResourceListTable from '@/components/ResourceListTable.vue';
101
+ import Filters from '@/components/Filters.vue';
102
+ import {
103
+ IconBanOutline,
104
+ IconFilterOutline,
105
+ IconPlusOutline,
106
+ } from '@iconify-prerendered/vue-flowbite';
107
+
108
+ import { getIcon } from '@/utils';
109
+
110
+ const props = defineProps(['column', 'record', 'meta', 'resource', 'adminUser']);
111
+
112
+ const listResource = ref(null);
113
+ const loading = ref(true);
114
+
115
+ const page = ref(1);
116
+ const sort = ref([]);
117
+ const checkboxes = ref([]);
118
+ const pageSize = computed(() => listResource.value?.options?.listPageSize || 10);
119
+
120
+ const rows = ref(null);
121
+ const totalRows = ref(0);
122
+
123
+ const filters = ref([]);
124
+ const filtersShow = ref(false);
125
+ const columnsMinMax = ref(null);
126
+
127
+ const listResourceRefColumn = computed(() => {
128
+ if (!listResource.value) {
129
+ return null;
130
+ }
131
+ return listResource.value.columns.find(c => c.foreignResource?.resourceId === props.resource.resourceId);
132
+ });
133
+
134
+ const selfPrimaryKeyColumn = computed(() => {
135
+ return props.resource.columns.find(c => c.primaryKey);
136
+ });
137
+
138
+ const endFilters = computed(() => {
139
+ if (!listResource.value) {
140
+ return [];
141
+ }
142
+ // get name of the column that is foreign key
143
+ const refColumn = listResourceRefColumn.value;
144
+
145
+ const primaryKeyColumn = selfPrimaryKeyColumn.value;
146
+
147
+ if (!refColumn) {
148
+ window.adminforth.alert({
149
+ message: `Column with foreignResource.resourceId which is equal to '${props.resource.resourceId}' not found in resource which is specified as foreighResourceId '${listResource.value.resourceId}'`,
150
+ variant: 'danger',
151
+ });
152
+ return [];
153
+ }
154
+ return [
155
+ ...filters.value,
156
+ {
157
+ field: refColumn.name,
158
+ operator: 'eq',
159
+ value: props.record[primaryKeyColumn.name],
160
+ },
161
+ ];
162
+ });
163
+
164
+ watch([page], async () => {
165
+ await getList();
166
+ });
167
+
168
+ watch([sort], async () => {
169
+ await getList();
170
+ }, {deep: true});
171
+
172
+ watch([filters], async () => {
173
+ page.value = 1;
174
+ checkboxes.value = [];
175
+ await getList();
176
+ }, {deep: true});
177
+
178
+
179
+ async function startBulkAction(actionId) {
180
+ const data = await callAdminForthApi({
181
+ path: '/start_bulk_action',
182
+ method: 'POST',
183
+ body: {
184
+ resourceId: listResource.value.resourceId,
185
+ actionId: actionId,
186
+ recordIds: checkboxes.value
187
+ }
188
+ });
189
+ if (data?.status === 'success') {
190
+ checkboxes.value = [];
191
+ }
192
+ await getList();
193
+ }
194
+
195
+ async function getList() {
196
+ rows.value = null;
197
+ console.log('getList', listResource.value)
198
+ const data = await callAdminForthApi({
199
+ path: '/get_resource_data',
200
+ method: 'POST',
201
+ body: {
202
+ source: 'list',
203
+ resourceId: listResource.value.resourceId,
204
+ limit: pageSize.value,
205
+ offset: (page.value - 1) * pageSize.value,
206
+ filters: endFilters.value,
207
+ sort: sort.value,
208
+ }
209
+ });
210
+
211
+ if (data.error) {
212
+ window.adminforth.alert({
213
+ message: data.error,
214
+ variant: 'danger',
215
+ timeout: 'unlimited',
216
+ });
217
+ rows.value = [];
218
+ totalRows.value = 0;
219
+ return;
220
+ }
221
+
222
+ rows.value = data.data?.map(row => {
223
+ row._primaryKeyValue = row[listResource.value.columns.find(c => c.primaryKey).name];
224
+ return row;
225
+ });
226
+ totalRows.value = data.total;
227
+ }
228
+
229
+ onMounted( async () => {
230
+ loading.value = true;
231
+ const foreighResourceId = props.meta.foreignResourceId;
232
+ listResource.value = (await callAdminForthApi({
233
+ path: `/plugin/${props.meta.pluginInstanceId}/get_resource`,
234
+ method: 'POST',
235
+ body: {},
236
+ })).resource;
237
+ columnsMinMax.value = await callAdminForthApi({
238
+ path: '/get_min_max_for_columns',
239
+ method: 'POST',
240
+ body: {
241
+ resourceId: foreighResourceId,
242
+ }
243
+ });
244
+ loading.value = false;
245
+ await getList();
246
+ });
247
+
248
+ </script>
@@ -0,0 +1,248 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <!-- todo exclude foreign column-->
4
+ <Filters
5
+ v-if="listResource"
6
+ :columns="listResource.columns.filter((c) => c.name !== listResourceRefColumn.name)"
7
+ v-model:filters="filters"
8
+ :columnsMinMax="columnsMinMax"
9
+ :show="filtersShow"
10
+ @hide="filtersShow = false"
11
+ />
12
+ </Teleport>
13
+
14
+ <td colspan="2">
15
+ <div class="flex items-center gap-1">
16
+ <h4 v-if="listResource"
17
+ class="px-6 py-4"
18
+ >{{ listResource.label }} inline records</h4>
19
+
20
+ <button
21
+ @click="()=>{checkboxes = []}"
22
+ v-if="checkboxes.length"
23
+ data-tooltip-target="tooltip-remove-all"
24
+ data-tooltip-placement="bottom"
25
+ class="flex gap-1 items-center py-1 px-3 me-2 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"
26
+ >
27
+ <IconBanOutline class="w-5 h-5 "/>
28
+ <div id="tooltip-remove-all" role="tooltip"
29
+ class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
30
+ Remove selection
31
+ <div class="tooltip-arrow" data-popper-arrow></div>
32
+ </div>
33
+ </button>
34
+
35
+ <button
36
+ v-if="checkboxes.length"
37
+ v-for="(action,i) in listResource?.options?.bulkActions"
38
+ :key="action.id"
39
+ @click="startBulkAction(action.id)"
40
+ class="flex gap-1 items-center py-1 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"
41
+ :class="{'bg-red-100 text-red-800 border-red-400 dark:bg-red-700 dark:text-red-400 dark:border-red-400':action.state==='danger', 'bg-green-100 text-green-800 border-green-400 dark:bg-green-700 dark:text-green-400 dark:border-green-400':action.state==='success',
42
+ 'bg-blue-100 text-blue-800 border-blue-400 dark:bg-blue-700 dark:text-blue-400 dark:border-blue-400':action.state==='active',
43
+ }"
44
+ >
45
+ <component
46
+ v-if="action.icon"
47
+ :is="getIcon(action.icon)"
48
+ class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"></component>
49
+
50
+ {{ `${action.label} (${checkboxes.length})` }}
51
+ </button>
52
+
53
+ <RouterLink v-if="listResource?.options?.allowedActions?.create"
54
+ :to="{
55
+ name: 'resource-create',
56
+ params: { resourceId: listResource.resourceId },
57
+ query: {
58
+ values: encodeURIComponent(JSON.stringify({[listResourceRefColumn.name]: props.record[selfPrimaryKeyColumn.name]})),
59
+ returnTo: $route.fullPath,
60
+ },
61
+ }"
62
+ class="flex items-center py-1 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 rounded-default"
63
+ >
64
+ <IconPlusOutline class="w-4 h-4 me-2"/>
65
+ Create
66
+ </RouterLink>
67
+
68
+ <button
69
+ class="flex gap-1 items-center py-1 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 rounded-default"
70
+ @click="()=>{filtersShow = !filtersShow}"
71
+ >
72
+ <IconFilterOutline class="w-4 h-4 me-2"/>
73
+ Filter
74
+ <span
75
+ class="bg-red-100 text-red-800 text-xs font-medium px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
76
+ v-if="filters.length">
77
+ {{ filters.length }}
78
+ </span>
79
+ </button>
80
+
81
+ </div>
82
+
83
+ <ResourceListTable
84
+ :resource="listResource"
85
+ :rows="rows"
86
+ @update:page="page = $event"
87
+ @update:sort="sort = $event"
88
+ @update:checkboxes="checkboxes = $event"
89
+ :pageSize="pageSize"
90
+ :totalRows="totalRows"
91
+ :checkboxes="checkboxes"
92
+ />
93
+
94
+ </td>
95
+ </template>
96
+
97
+ <script setup>
98
+ import { callAdminForthApi } from '@/utils';
99
+ import { ref, onMounted, watch, computed } from 'vue';
100
+ import ResourceListTable from '@/components/ResourceListTable.vue';
101
+ import Filters from '@/components/Filters.vue';
102
+ import {
103
+ IconBanOutline,
104
+ IconFilterOutline,
105
+ IconPlusOutline,
106
+ } from '@iconify-prerendered/vue-flowbite';
107
+
108
+ import { getIcon } from '@/utils';
109
+
110
+ const props = defineProps(['column', 'record', 'meta', 'resource', 'adminUser']);
111
+
112
+ const listResource = ref(null);
113
+ const loading = ref(true);
114
+
115
+ const page = ref(1);
116
+ const sort = ref([]);
117
+ const checkboxes = ref([]);
118
+ const pageSize = computed(() => listResource.value?.options?.listPageSize || 10);
119
+
120
+ const rows = ref(null);
121
+ const totalRows = ref(0);
122
+
123
+ const filters = ref([]);
124
+ const filtersShow = ref(false);
125
+ const columnsMinMax = ref(null);
126
+
127
+ const listResourceRefColumn = computed(() => {
128
+ if (!listResource.value) {
129
+ return null;
130
+ }
131
+ return listResource.value.columns.find(c => c.foreignResource?.resourceId === props.resource.resourceId);
132
+ });
133
+
134
+ const selfPrimaryKeyColumn = computed(() => {
135
+ return props.resource.columns.find(c => c.primaryKey);
136
+ });
137
+
138
+ const endFilters = computed(() => {
139
+ if (!listResource.value) {
140
+ return [];
141
+ }
142
+ // get name of the column that is foreign key
143
+ const refColumn = listResourceRefColumn.value;
144
+
145
+ const primaryKeyColumn = selfPrimaryKeyColumn.value;
146
+
147
+ if (!refColumn) {
148
+ window.adminforth.alert({
149
+ message: `Column with foreignResource.resourceId which is equal to '${props.resource.resourceId}' not found in resource which is specified as foreighResourceId '${listResource.value.resourceId}'`,
150
+ variant: 'danger',
151
+ });
152
+ return [];
153
+ }
154
+ return [
155
+ ...filters.value,
156
+ {
157
+ field: refColumn.name,
158
+ operator: 'eq',
159
+ value: props.record[primaryKeyColumn.name],
160
+ },
161
+ ];
162
+ });
163
+
164
+ watch([page], async () => {
165
+ await getList();
166
+ });
167
+
168
+ watch([sort], async () => {
169
+ await getList();
170
+ }, {deep: true});
171
+
172
+ watch([filters], async () => {
173
+ page.value = 1;
174
+ checkboxes.value = [];
175
+ await getList();
176
+ }, {deep: true});
177
+
178
+
179
+ async function startBulkAction(actionId) {
180
+ const data = await callAdminForthApi({
181
+ path: '/start_bulk_action',
182
+ method: 'POST',
183
+ body: {
184
+ resourceId: listResource.value.resourceId,
185
+ actionId: actionId,
186
+ recordIds: checkboxes.value
187
+ }
188
+ });
189
+ if (data?.status === 'success') {
190
+ checkboxes.value = [];
191
+ }
192
+ await getList();
193
+ }
194
+
195
+ async function getList() {
196
+ rows.value = null;
197
+ console.log('getList', listResource.value)
198
+ const data = await callAdminForthApi({
199
+ path: '/get_resource_data',
200
+ method: 'POST',
201
+ body: {
202
+ source: 'list',
203
+ resourceId: listResource.value.resourceId,
204
+ limit: pageSize.value,
205
+ offset: (page.value - 1) * pageSize.value,
206
+ filters: endFilters.value,
207
+ sort: sort.value,
208
+ }
209
+ });
210
+
211
+ if (data.error) {
212
+ window.adminforth.alert({
213
+ message: data.error,
214
+ variant: 'danger',
215
+ timeout: 'unlimited',
216
+ });
217
+ rows.value = [];
218
+ totalRows.value = 0;
219
+ return;
220
+ }
221
+
222
+ rows.value = data.data?.map(row => {
223
+ row._primaryKeyValue = row[listResource.value.columns.find(c => c.primaryKey).name];
224
+ return row;
225
+ });
226
+ totalRows.value = data.total;
227
+ }
228
+
229
+ onMounted( async () => {
230
+ loading.value = true;
231
+ const foreighResourceId = props.meta.foreignResourceId;
232
+ listResource.value = (await callAdminForthApi({
233
+ path: `/plugin/${props.meta.pluginInstanceId}/get_resource`,
234
+ method: 'POST',
235
+ body: {},
236
+ })).resource;
237
+ columnsMinMax.value = await callAdminForthApi({
238
+ path: '/get_min_max_for_columns',
239
+ method: 'POST',
240
+ body: {
241
+ resourceId: foreighResourceId,
242
+ }
243
+ });
244
+ loading.value = false;
245
+ await getList();
246
+ });
247
+
248
+ </script>
@@ -13,8 +13,8 @@ const router = createRouter({
13
13
  meta: { title: 'login' },
14
14
  beforeEnter: async (to, from, next) => {
15
15
  const userStore = useUserStore()
16
- if (await userStore.checkAuth(true)) {
17
- next({ name: 'home' })
16
+ if(localStorage.getItem('isAuthorized') === 'true'){
17
+ next({name: 'home'})
18
18
  } else {
19
19
  next()
20
20
  }
@@ -7,10 +7,12 @@ export const useUserStore = defineStore('user', () => {
7
7
 
8
8
  function authorize() {
9
9
  isAuthorized.value = true;
10
+ localStorage.setItem('isAuthorized', 'true');
10
11
  }
11
12
 
12
13
  function unauthorize() {
13
14
  isAuthorized.value = false;
15
+ localStorage.setItem('isAuthorized', 'false');
14
16
  }
15
17
 
16
18
  async function logout() {
@@ -18,32 +20,34 @@ export const useUserStore = defineStore('user', () => {
18
20
  path: '/logout',
19
21
  method: 'POST',
20
22
  });
23
+ unauthorize();
21
24
  }
22
25
 
23
- async function checkAuth( skipApiCall = false){
24
- if(isAuthorized.value) return true;
25
- else {
26
- if(skipApiCall) return false;
27
- const resp = await callAdminForthApi({
28
- path: '/check_auth',
29
- method: 'POST',
30
- });
31
- if (resp.status !== 401) {
32
- authorize();
33
- return true;
34
- }
35
- else {
36
- unauthorize();
37
- return false;}
38
- }
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
+ // }
39
44
 
40
- }
45
+ // }
41
46
 
42
47
  return {
43
48
  isAuthorized,
44
49
  authorize,
45
50
  unauthorize,
46
- checkAuth,
47
51
  logout
48
52
  }
49
53
 
@@ -20,7 +20,7 @@ export async function callApi({path, method, body=undefined} ) {
20
20
  useUserStore().unauthorize();
21
21
  router.push({ name: 'login' });
22
22
  return null;
23
- }
23
+ }
24
24
  return await r.json();
25
25
  }
26
26
 
@@ -81,6 +81,7 @@
81
81
 
82
82
  import { onMounted, ref, watchEffect } from 'vue';
83
83
  import { useCoreStore } from '@/stores/core';
84
+ import { useUserStore } from '@/stores/user';
84
85
  import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
85
86
  import { callAdminForthApi, loadFile } from '@/utils';
86
87
  import { useRouter } from 'vue-router';
@@ -91,6 +92,8 @@ const router = useRouter();
91
92
  const inProgress = ref(false);
92
93
 
93
94
  const coreStore = useCoreStore();
95
+ const user = useUserStore();
96
+
94
97
 
95
98
  const showPw = ref(false);
96
99
 
@@ -115,6 +118,7 @@ async function login() {
115
118
  error.value = resp.error;
116
119
  } else {
117
120
  error.value = null;
121
+ user.authorize()
118
122
  router.push('/');
119
123
  await router.isReady();
120
124
  await coreStore.fetchMenuAndResource();
package/index.ts CHANGED
@@ -229,6 +229,21 @@ class AdminForth implements AdminForthClass {
229
229
  errors.push(`Resource "${res.resourceId}" column "${col.name}" has invalid showIn value "${wrongShowIn}", allowed values are ${Object.keys(AdminForthResourcePages).join(', ')}`);
230
230
  }
231
231
  col.showIn = col.showIn || Object.values(AdminForthResourcePages);
232
+
233
+ if (col.foreignResource) {
234
+ const befHook = col.foreignResource.hooks?.dropdownList?.beforeDatasourceRequest;
235
+ if (befHook) {
236
+ if (!Array.isArray(befHook)) {
237
+ col.foreignResource.hooks.dropdownList.beforeDatasourceRequest = [befHook];
238
+ }
239
+ }
240
+ const aftHook = col.foreignResource.hooks?.dropdownList?.afterDatasourceResponse;
241
+ if (aftHook) {
242
+ if (!Array.isArray(aftHook)) {
243
+ col.foreignResource.hooks.dropdownList.afterDatasourceResponse = [aftHook];
244
+ }
245
+ }
246
+ }
232
247
  })
233
248
 
234
249
  if (!res.options) {
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "test": "echo \"Error: no test specified\" && exit 1",
9
9
  "build": "tsc",
10
- "rollout": "tsc && cp -r spa dist/spa && npm version patch && npm publish && cd documentation && npm run build && npm run deploy",
10
+ "prepareDist": "cp -r spa dist/spa && find plugins -type f ! -name '*.ts' -exec cp --parents {} dist \\;",
11
+ "rollout": "tsc && npm run prepareDist && npm version patch && npm publish && cd documentation && npm run build && npm run deploy",
11
12
  "docs": "typedoc"
12
13
  },
13
14
  "author": "devforth.io",
@@ -13,8 +13,8 @@ const router = createRouter({
13
13
  meta: { title: 'login' },
14
14
  beforeEnter: async (to, from, next) => {
15
15
  const userStore = useUserStore()
16
- if (await userStore.checkAuth(true)) {
17
- next({ name: 'home' })
16
+ if(localStorage.getItem('isAuthorized') === 'true'){
17
+ next({name: 'home'})
18
18
  } else {
19
19
  next()
20
20
  }
@@ -7,10 +7,12 @@ export const useUserStore = defineStore('user', () => {
7
7
 
8
8
  function authorize() {
9
9
  isAuthorized.value = true;
10
+ localStorage.setItem('isAuthorized', 'true');
10
11
  }
11
12
 
12
13
  function unauthorize() {
13
14
  isAuthorized.value = false;
15
+ localStorage.setItem('isAuthorized', 'false');
14
16
  }
15
17
 
16
18
  async function logout() {
@@ -18,32 +20,34 @@ export const useUserStore = defineStore('user', () => {
18
20
  path: '/logout',
19
21
  method: 'POST',
20
22
  });
23
+ unauthorize();
21
24
  }
22
25
 
23
- async function checkAuth( skipApiCall = false){
24
- if(isAuthorized.value) return true;
25
- else {
26
- if(skipApiCall) return false;
27
- const resp = await callAdminForthApi({
28
- path: '/check_auth',
29
- method: 'POST',
30
- });
31
- if (resp.status !== 401) {
32
- authorize();
33
- return true;
34
- }
35
- else {
36
- unauthorize();
37
- return false;}
38
- }
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
+ // }
39
44
 
40
- }
45
+ // }
41
46
 
42
47
  return {
43
48
  isAuthorized,
44
49
  authorize,
45
50
  unauthorize,
46
- checkAuth,
47
51
  logout
48
52
  }
49
53
 
package/spa/src/utils.ts CHANGED
@@ -20,7 +20,7 @@ export async function callApi({path, method, body=undefined} ) {
20
20
  useUserStore().unauthorize();
21
21
  router.push({ name: 'login' });
22
22
  return null;
23
- }
23
+ }
24
24
  return await r.json();
25
25
  }
26
26
 
@@ -81,6 +81,7 @@
81
81
 
82
82
  import { onMounted, ref, watchEffect } from 'vue';
83
83
  import { useCoreStore } from '@/stores/core';
84
+ import { useUserStore } from '@/stores/user';
84
85
  import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
85
86
  import { callAdminForthApi, loadFile } from '@/utils';
86
87
  import { useRouter } from 'vue-router';
@@ -91,6 +92,8 @@ const router = useRouter();
91
92
  const inProgress = ref(false);
92
93
 
93
94
  const coreStore = useCoreStore();
95
+ const user = useUserStore();
96
+
94
97
 
95
98
  const showPw = ref(false);
96
99
 
@@ -115,6 +118,7 @@ async function login() {
115
118
  error.value = resp.error;
116
119
  } else {
117
120
  error.value = null;
121
+ user.authorize()
118
122
  router.push('/');
119
123
  await router.isReady();
120
124
  await coreStore.fetchMenuAndResource();