adminforth 1.2.35 → 1.2.37

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.
@@ -248,6 +248,7 @@ export default class ConfigValidator {
248
248
  allowed: (_b) => __awaiter(this, [_b], void 0, function* ({ resource, adminUser, allowedActions }) { return allowedActions.delete; }),
249
249
  action: (_c) => __awaiter(this, [_c], void 0, function* ({ selectedIds, adminUser }) {
250
250
  const connector = this.adminforth.connectors[res.dataSource];
251
+ // for now if at least one error, stop and return error
251
252
  let error = null;
252
253
  yield Promise.all(selectedIds.map((recordId) => __awaiter(this, void 0, void 0, function* () {
253
254
  const record = yield connector.getRecordByPrimaryKey(res, recordId);
@@ -257,6 +258,9 @@ export default class ConfigValidator {
257
258
  error = resp.error;
258
259
  }
259
260
  })));
261
+ if (error) {
262
+ return;
263
+ }
260
264
  yield connector.deleteRecord({ resource: res, recordId });
261
265
  // call afterDelete hook
262
266
  yield Promise.all(res.hooks.delete.afterSave.map((hook) => __awaiter(this, void 0, void 0, function* () {
@@ -622,13 +622,10 @@ export default class AdminForthRestAPI {
622
622
  if (!execAllowed) {
623
623
  return { error: `Action '${actionId}' is not allowed` };
624
624
  }
625
- yield action.action({ selectedIds: recordIds, adminUser, resource });
626
- return {
627
- actionId,
625
+ const response = yield action.action({ selectedIds: recordIds, adminUser, resource });
626
+ return Object.assign({ actionId,
628
627
  recordIds,
629
- resourceId,
630
- status: 'success'
631
- };
628
+ resourceId }, response);
632
629
  })
633
630
  });
634
631
  // setup endpoints for all plugins
@@ -20,7 +20,7 @@ export const styles = () => ({
20
20
  lightSidebarHeading: "alias:lightSidebarText opacity:0.3", // sidebar heading
21
21
  lightList: "#FFFFFF", // list view background
22
22
  lightListTable: "#FFFFFF", // list view table background
23
- lightListTableHeading: "rgb(249 250 251)", // list view table heading
23
+ lightListTableHeading: "alias:lightListTableHeadingText opacity:0.04", // list view table heading
24
24
  lightListTableHeadingText: "#333333", // list view table heading text
25
25
  lightListTableText: "#333333", // list view table text
26
26
  lightListTableRowHover: "rgb(249 250 251)", // list view row hover
@@ -36,7 +36,7 @@ export const styles = () => ({
36
36
  // lightListButtonIcon: "#333333", // list view button icon
37
37
  lightForm: "#FFFFFF", // show view background
38
38
  lightFormBorder: "#F5F5F5", // show view rows border
39
- lightFormHeading: "rgb(249 250 251)", // show view heading
39
+ lightFormHeading: "alias:lightListTableHeading", // show view heading
40
40
  lightButtons: "#FFFFFF", // button background
41
41
  lightButtonsBorder: "#DDDDDD", // button border
42
42
  lightButtonsText: "#111827", // button text
@@ -75,7 +75,7 @@ export const styles = () => ({
75
75
  darkListBorder: "#444444",
76
76
  darkForm: "#111111",
77
77
  darkFormBorder: "#222222",
78
- darkFormHeading: "#334155"
78
+ darkFormHeading: "alias:darkListTableHeading"
79
79
  },
80
80
  boxShadow: {
81
81
  customLight: "0 4px 8px rgba(0, 0, 0, 0.1)", // Lighter shadow
@@ -84,7 +84,7 @@
84
84
  :aria-controls="`dropdown-example${i}`"
85
85
  :data-collapse-toggle="`dropdown-example${i}`"
86
86
  >
87
- <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
87
+ <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
88
88
 
89
89
  <span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}</span>
90
90
  <svg :class="{'rotate-180': opened.includes(i) }" class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
@@ -262,13 +262,11 @@ async function loadMenu() {
262
262
  }
263
263
 
264
264
  function handleCustomLayout() {
265
- console.log(route)
266
265
  if (route.meta?.customLayout) {
267
266
  defaultLayout.value = false;
268
267
  } else {
269
268
  defaultLayout.value = true;
270
269
  }
271
- console.log(defaultLayout.value,route);
272
270
  }
273
271
 
274
272
 
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <div class="flex flex-wrap gap-2">
3
3
  <input
4
+ :min="minFormatted"
5
+ :max="maxFormatted"
4
6
  type="number" aria-describedby="helper-text-explanation"
5
7
  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
8
  placeholder="From"
@@ -8,6 +10,8 @@
8
10
  >
9
11
 
10
12
  <input
13
+ :min="minFormatted"
14
+ :max="maxFormatted"
11
15
  type="number" aria-describedby="helper-text-explanation"
12
16
  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
17
  placeholder="To"
@@ -21,7 +25,7 @@
21
25
  @click="clear">Clear
22
26
  </button>
23
27
 
24
- <div class="w-full px-2.5">
28
+ <div v-if="min && max" class="w-full px-2.5">
25
29
  <vue-slider
26
30
  class="custom-slider"
27
31
  :dot-size="20"
@@ -47,12 +51,8 @@ const props = defineProps({
47
51
  valueEnd: {
48
52
  default: '',
49
53
  },
50
- min: {
51
- default: 0,
52
- },
53
- max: {
54
- default: 10,
55
- },
54
+ min: {},
55
+ max: {},
56
56
  });
57
57
 
58
58
  const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
@@ -108,6 +108,10 @@ watch(end, () => {
108
108
  emit('update:valueEnd', end.value);
109
109
  })
110
110
 
111
+ watch([minFormatted,maxFormatted], () => {
112
+ setSliderValues(minFormatted.value, maxFormatted.value)
113
+ })
114
+
111
115
  const clear = () => {
112
116
  start.value = ''
113
117
  end.value = ''
@@ -70,8 +70,8 @@
70
70
 
71
71
  <CustomRangePicker
72
72
  v-else-if="['integer', 'decimal', 'float'].includes(c.type) && c.allowMinMaxQuery"
73
- :min="c.min"
74
- :max="c.max"
73
+ :min="getFilterMinValue(c.name)"
74
+ :max="getFilterMaxValue(c.name)"
75
75
  :valueStart="getFilterItem({ column: c, operator: 'gte' })"
76
76
  @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
77
77
  :valueEnd="getFilterItem({ column: c, operator: 'lte' })"
@@ -207,4 +207,16 @@ async function clear() {
207
207
  filtersStore.clearFilters();
208
208
  emits('update:filters', [...filtersStore.filters]);
209
209
  }
210
+
211
+ function getFilterMinValue(columnName) {
212
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
213
+ return props.columnsMinMax[columnName]?.min
214
+ }
215
+ }
216
+
217
+ function getFilterMaxValue(columnName) {
218
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
219
+ return props.columnsMinMax[columnName]?.max
220
+ }
221
+ }
210
222
  </script>
@@ -5,7 +5,7 @@
5
5
  :class="{
6
6
  'px-4': isChild,
7
7
  'px-2': !isChild,
8
- 'bg-sidebarActive dark:bg-gray-700': item.resourceId ?
8
+ 'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': item.resourceId ?
9
9
  ($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
10
10
  ($route.name === item.path)
11
11
  }"
@@ -5,7 +5,7 @@
5
5
  >
6
6
  <form autocomplete="off" @submit.prevent>
7
7
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
8
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
9
9
  <tr>
10
10
  <th scope="col" class="px-6 py-3">
11
11
  Field
@@ -41,7 +41,7 @@
41
41
  </div>
42
42
  <span
43
43
  class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
44
- v-if="sort.findIndex((s) => s.field === c.name) !== -1">
44
+ v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
45
45
  {{ sort.findIndex((s) => s.field === c.name) + 1 }}
46
46
  </span>
47
47
 
@@ -228,7 +228,7 @@ import { callAdminForthApi, getIcon } from '@/utils';
228
228
  import ValueRenderer from '@/components/ValueRenderer.vue';
229
229
  import { getCustomComponent } from '@/utils';
230
230
  import { useCoreStore } from '@/stores/core';
231
- import { showSuccesTost } from '@/composables/useFrontendApi';
231
+ import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
232
232
  import SkeleteLoader from '@/components/SkeleteLoader.vue';
233
233
 
234
234
  import {
@@ -321,9 +321,6 @@ async function selectAll(value) {
321
321
 
322
322
  const totalPages = computed(() => Math.ceil(props.totalRows / props.pageSize));
323
323
 
324
-
325
-
326
-
327
324
  const allFromThisPageChecked = computed(() => {
328
325
  if (!props.rows) return false;
329
326
  return props.rows.every((r) => checkboxesInternal.value.includes(r.id));
@@ -361,14 +358,6 @@ async function onClick(e,row) {
361
358
  },
362
359
  })}
363
360
  }
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
361
 
373
362
  async function deleteRecord(row) {
374
363
  const data = await window.adminforth.confirm({
@@ -379,26 +368,24 @@ async function deleteRecord(row) {
379
368
  if (data) {
380
369
  try {
381
370
  const res = await callAdminForthApi({
382
- path: '/delete_record',
383
- method: 'POST',
384
- body: {
385
- resourceId: props.resource.resourceId,
386
- primaryKey: row._primaryKeyValue,
387
- }});
371
+ path: '/delete_record',
372
+ method: 'POST',
373
+ body: {
374
+ resourceId: props.resource.resourceId,
375
+ primaryKey: row._primaryKeyValue,
376
+ }
377
+ });
388
378
  if (!res.error){
389
379
  emits('update:records', true)
390
380
  showSuccesTost('Record deleted successfully')
391
381
  } else {
392
- console.error(res.error)
382
+ showErrorTost(res.error)
393
383
  }
394
384
 
395
385
  } catch (e) {
386
+ showErrorTost(`Something went wrong, please try again later`);
396
387
  console.error(e);
397
- };
398
- }
399
-
400
-
388
+ };
389
+ }
401
390
  }
402
-
403
-
404
391
  </script>
@@ -23,4 +23,5 @@
23
23
  // ::-webkit-scrollbar-thumb:hover {
24
24
  // @apply bg-gray-700
25
25
  // }
26
- // }
26
+ // }
27
+
@@ -200,11 +200,6 @@ async function getList() {
200
200
  totalRows.value = data.total;
201
201
  }
202
202
 
203
-
204
-
205
-
206
-
207
-
208
203
  async function startBulkAction(actionId) {
209
204
  const data = await callAdminForthApi({
210
205
  path: '/start_bulk_action',
@@ -216,10 +211,13 @@ async function startBulkAction(actionId) {
216
211
 
217
212
  }
218
213
  });
219
- if (data?.status === 'success') {
214
+ if (data?.ok) {
220
215
  checkboxes.value = [];
216
+ await getList();
217
+ }
218
+ if (data?.error) {
219
+ showErrorTost(data.error);
221
220
  }
222
- await getList();
223
221
  }
224
222
 
225
223
 
@@ -47,7 +47,7 @@
47
47
  class="relative overflow-x-auto shadow-resourseFormShadow "
48
48
  >
49
49
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
50
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
50
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
51
51
  <tr>
52
52
  <th scope="col" class="px-6 py-3">
53
53
  Field
@@ -269,6 +269,8 @@ export default class ConfigValidator implements IConfigValidator {
269
269
  allowed: async ({ resource, adminUser, allowedActions }) => { return allowedActions.delete },
270
270
  action: async ({ selectedIds, adminUser }) => {
271
271
  const connector = this.adminforth.connectors[res.dataSource];
272
+
273
+ // for now if at least one error, stop and return error
272
274
  let error = null;
273
275
 
274
276
  await Promise.all(
@@ -285,6 +287,10 @@ export default class ConfigValidator implements IConfigValidator {
285
287
  }
286
288
  )
287
289
  )
290
+
291
+ if (error) {
292
+ return;
293
+ }
288
294
 
289
295
  await connector.deleteRecord({ resource: res, recordId });
290
296
  // call afterDelete hook
@@ -712,13 +712,13 @@ export default class AdminForthRestAPI {
712
712
  if (!execAllowed) {
713
713
  return { error: `Action '${actionId}' is not allowed` };
714
714
  }
715
- await action.action({selectedIds: recordIds, adminUser, resource});
715
+ const response = await action.action({selectedIds: recordIds, adminUser, resource});
716
716
 
717
717
  return {
718
718
  actionId,
719
719
  recordIds,
720
720
  resourceId,
721
- status: 'success'
721
+ ...response
722
722
  }
723
723
  }
724
724
  })
package/modules/styles.ts CHANGED
@@ -25,7 +25,7 @@ export const styles = () => ({
25
25
 
26
26
  lightList: "#FFFFFF", // list view background
27
27
  lightListTable: "#FFFFFF", // list view table background
28
- lightListTableHeading: "rgb(249 250 251)", // list view table heading
28
+ lightListTableHeading: "alias:lightListTableHeadingText opacity:0.04", // list view table heading
29
29
  lightListTableHeadingText: "#333333", // list view table heading text
30
30
  lightListTableText: "#333333", // list view table text
31
31
  lightListTableRowHover: "rgb(249 250 251)", // list view row hover
@@ -42,7 +42,7 @@ export const styles = () => ({
42
42
 
43
43
  lightForm: "#FFFFFF", // show view background
44
44
  lightFormBorder: "#F5F5F5", // show view rows border
45
- lightFormHeading: "rgb(249 250 251)", // show view heading
45
+ lightFormHeading: "alias:lightListTableHeading", // show view heading
46
46
 
47
47
  lightButtons: "#FFFFFF", // button background
48
48
  lightButtonsBorder: "#DDDDDD", // button border
@@ -90,7 +90,7 @@ export const styles = () => ({
90
90
 
91
91
  darkForm: "#111111",
92
92
  darkFormBorder: "#222222",
93
- darkFormHeading: "#334155"
93
+ darkFormHeading: "alias:darkListTableHeading"
94
94
 
95
95
  },
96
96
  boxShadow: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.35",
3
+ "version": "1.2.37",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/spa/src/App.vue CHANGED
@@ -84,7 +84,7 @@
84
84
  :aria-controls="`dropdown-example${i}`"
85
85
  :data-collapse-toggle="`dropdown-example${i}`"
86
86
  >
87
- <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
87
+ <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-lightSidebarIcons group-hover:text-lightSidebarIconsHover transition duration-75 dark:group-hover:text-darkSidebarIconsHover dark:text-darkSidebarIcons" ></component>
88
88
 
89
89
  <span class="flex-1 ms-3 text-left rtl:text-right whitespace-nowrap">{{ item.label }}</span>
90
90
  <svg :class="{'rotate-180': opened.includes(i) }" class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 10 6">
@@ -262,13 +262,11 @@ async function loadMenu() {
262
262
  }
263
263
 
264
264
  function handleCustomLayout() {
265
- console.log(route)
266
265
  if (route.meta?.customLayout) {
267
266
  defaultLayout.value = false;
268
267
  } else {
269
268
  defaultLayout.value = true;
270
269
  }
271
- console.log(defaultLayout.value,route);
272
270
  }
273
271
 
274
272
 
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <div class="flex flex-wrap gap-2">
3
3
  <input
4
+ :min="minFormatted"
5
+ :max="maxFormatted"
4
6
  type="number" aria-describedby="helper-text-explanation"
5
7
  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
8
  placeholder="From"
@@ -8,6 +10,8 @@
8
10
  >
9
11
 
10
12
  <input
13
+ :min="minFormatted"
14
+ :max="maxFormatted"
11
15
  type="number" aria-describedby="helper-text-explanation"
12
16
  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
17
  placeholder="To"
@@ -21,7 +25,7 @@
21
25
  @click="clear">Clear
22
26
  </button>
23
27
 
24
- <div class="w-full px-2.5">
28
+ <div v-if="min && max" class="w-full px-2.5">
25
29
  <vue-slider
26
30
  class="custom-slider"
27
31
  :dot-size="20"
@@ -47,12 +51,8 @@ const props = defineProps({
47
51
  valueEnd: {
48
52
  default: '',
49
53
  },
50
- min: {
51
- default: 0,
52
- },
53
- max: {
54
- default: 10,
55
- },
54
+ min: {},
55
+ max: {},
56
56
  });
57
57
 
58
58
  const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
@@ -108,6 +108,10 @@ watch(end, () => {
108
108
  emit('update:valueEnd', end.value);
109
109
  })
110
110
 
111
+ watch([minFormatted,maxFormatted], () => {
112
+ setSliderValues(minFormatted.value, maxFormatted.value)
113
+ })
114
+
111
115
  const clear = () => {
112
116
  start.value = ''
113
117
  end.value = ''
@@ -70,8 +70,8 @@
70
70
 
71
71
  <CustomRangePicker
72
72
  v-else-if="['integer', 'decimal', 'float'].includes(c.type) && c.allowMinMaxQuery"
73
- :min="c.min"
74
- :max="c.max"
73
+ :min="getFilterMinValue(c.name)"
74
+ :max="getFilterMaxValue(c.name)"
75
75
  :valueStart="getFilterItem({ column: c, operator: 'gte' })"
76
76
  @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
77
77
  :valueEnd="getFilterItem({ column: c, operator: 'lte' })"
@@ -207,4 +207,16 @@ async function clear() {
207
207
  filtersStore.clearFilters();
208
208
  emits('update:filters', [...filtersStore.filters]);
209
209
  }
210
+
211
+ function getFilterMinValue(columnName) {
212
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
213
+ return props.columnsMinMax[columnName]?.min
214
+ }
215
+ }
216
+
217
+ function getFilterMaxValue(columnName) {
218
+ if(props.columnsMinMax && props.columnsMinMax[columnName]) {
219
+ return props.columnsMinMax[columnName]?.max
220
+ }
221
+ }
210
222
  </script>
@@ -5,7 +5,7 @@
5
5
  :class="{
6
6
  'px-4': isChild,
7
7
  'px-2': !isChild,
8
- 'bg-sidebarActive dark:bg-gray-700': item.resourceId ?
8
+ 'bg-lightSidebarItemActive dark:bg-darkSidebarItemActive': item.resourceId ?
9
9
  ($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
10
10
  ($route.name === item.path)
11
11
  }"
@@ -5,7 +5,7 @@
5
5
  >
6
6
  <form autocomplete="off" @submit.prevent>
7
7
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
8
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
8
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
9
9
  <tr>
10
10
  <th scope="col" class="px-6 py-3">
11
11
  Field
@@ -41,7 +41,7 @@
41
41
  </div>
42
42
  <span
43
43
  class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
44
- v-if="sort.findIndex((s) => s.field === c.name) !== -1">
44
+ v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
45
45
  {{ sort.findIndex((s) => s.field === c.name) + 1 }}
46
46
  </span>
47
47
 
@@ -228,7 +228,7 @@ import { callAdminForthApi, getIcon } from '@/utils';
228
228
  import ValueRenderer from '@/components/ValueRenderer.vue';
229
229
  import { getCustomComponent } from '@/utils';
230
230
  import { useCoreStore } from '@/stores/core';
231
- import { showSuccesTost } from '@/composables/useFrontendApi';
231
+ import { showSuccesTost, showErrorTost } from '@/composables/useFrontendApi';
232
232
  import SkeleteLoader from '@/components/SkeleteLoader.vue';
233
233
 
234
234
  import {
@@ -321,9 +321,6 @@ async function selectAll(value) {
321
321
 
322
322
  const totalPages = computed(() => Math.ceil(props.totalRows / props.pageSize));
323
323
 
324
-
325
-
326
-
327
324
  const allFromThisPageChecked = computed(() => {
328
325
  if (!props.rows) return false;
329
326
  return props.rows.every((r) => checkboxesInternal.value.includes(r.id));
@@ -361,14 +358,6 @@ async function onClick(e,row) {
361
358
  },
362
359
  })}
363
360
  }
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
361
 
373
362
  async function deleteRecord(row) {
374
363
  const data = await window.adminforth.confirm({
@@ -379,26 +368,24 @@ async function deleteRecord(row) {
379
368
  if (data) {
380
369
  try {
381
370
  const res = await callAdminForthApi({
382
- path: '/delete_record',
383
- method: 'POST',
384
- body: {
385
- resourceId: props.resource.resourceId,
386
- primaryKey: row._primaryKeyValue,
387
- }});
371
+ path: '/delete_record',
372
+ method: 'POST',
373
+ body: {
374
+ resourceId: props.resource.resourceId,
375
+ primaryKey: row._primaryKeyValue,
376
+ }
377
+ });
388
378
  if (!res.error){
389
379
  emits('update:records', true)
390
380
  showSuccesTost('Record deleted successfully')
391
381
  } else {
392
- console.error(res.error)
382
+ showErrorTost(res.error)
393
383
  }
394
384
 
395
385
  } catch (e) {
386
+ showErrorTost(`Something went wrong, please try again later`);
396
387
  console.error(e);
397
- };
398
- }
399
-
400
-
388
+ };
389
+ }
401
390
  }
402
-
403
-
404
391
  </script>
@@ -23,4 +23,5 @@
23
23
  // ::-webkit-scrollbar-thumb:hover {
24
24
  // @apply bg-gray-700
25
25
  // }
26
- // }
26
+ // }
27
+
@@ -200,11 +200,6 @@ async function getList() {
200
200
  totalRows.value = data.total;
201
201
  }
202
202
 
203
-
204
-
205
-
206
-
207
-
208
203
  async function startBulkAction(actionId) {
209
204
  const data = await callAdminForthApi({
210
205
  path: '/start_bulk_action',
@@ -216,10 +211,13 @@ async function startBulkAction(actionId) {
216
211
 
217
212
  }
218
213
  });
219
- if (data?.status === 'success') {
214
+ if (data?.ok) {
220
215
  checkboxes.value = [];
216
+ await getList();
217
+ }
218
+ if (data?.error) {
219
+ showErrorTost(data.error);
221
220
  }
222
- await getList();
223
221
  }
224
222
 
225
223
 
@@ -47,7 +47,7 @@
47
47
  class="relative overflow-x-auto shadow-resourseFormShadow "
48
48
  >
49
49
  <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
50
- <thead class="text-xs text-gray-700 uppercase bg-lightormHeading dark:bg-gray-700 dark:text-gray-400">
50
+ <thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400">
51
51
  <tr>
52
52
  <th scope="col" class="px-6 py-3">
53
53
  Field