adminforth 2.4.0-next.12 → 2.4.0-next.121

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 (145) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/cli.js +12 -4
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +8 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/utils.js +27 -2
  7. package/commands/createCustomComponent/configLoader.js +3 -0
  8. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  9. package/dist/dataConnectors/baseConnector.js +16 -3
  10. package/dist/dataConnectors/baseConnector.js.map +1 -1
  11. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  12. package/dist/dataConnectors/clickhouse.js +4 -0
  13. package/dist/dataConnectors/clickhouse.js.map +1 -1
  14. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  15. package/dist/dataConnectors/mongo.js +14 -14
  16. package/dist/dataConnectors/mongo.js.map +1 -1
  17. package/dist/index.d.ts +2 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +20 -9
  20. package/dist/index.js.map +1 -1
  21. package/dist/modules/codeInjector.d.ts.map +1 -1
  22. package/dist/modules/codeInjector.js +22 -5
  23. package/dist/modules/codeInjector.js.map +1 -1
  24. package/dist/modules/configValidator.d.ts.map +1 -1
  25. package/dist/modules/configValidator.js +48 -1
  26. package/dist/modules/configValidator.js.map +1 -1
  27. package/dist/modules/restApi.d.ts.map +1 -1
  28. package/dist/modules/restApi.js +145 -25
  29. package/dist/modules/restApi.js.map +1 -1
  30. package/dist/modules/styles.d.ts +451 -13
  31. package/dist/modules/styles.d.ts.map +1 -1
  32. package/dist/modules/styles.js +507 -31
  33. package/dist/modules/styles.js.map +1 -1
  34. package/dist/modules/utils.d.ts +1 -0
  35. package/dist/modules/utils.d.ts.map +1 -1
  36. package/dist/modules/utils.js +9 -0
  37. package/dist/modules/utils.js.map +1 -1
  38. package/dist/spa/index.html +1 -1
  39. package/dist/spa/src/App.vue +25 -15
  40. package/dist/spa/src/adminforth.ts +10 -0
  41. package/dist/spa/src/afcl/Button.vue +6 -6
  42. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  43. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  44. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  45. package/dist/spa/src/afcl/Dialog.vue +43 -26
  46. package/dist/spa/src/afcl/Dropzone.vue +10 -10
  47. package/dist/spa/src/afcl/Input.vue +9 -7
  48. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  49. package/dist/spa/src/afcl/Link.vue +1 -1
  50. package/dist/spa/src/afcl/LinkButton.vue +1 -1
  51. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  52. package/dist/spa/src/afcl/Select.vue +60 -27
  53. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  54. package/dist/spa/src/afcl/Table.vue +13 -13
  55. package/dist/spa/src/afcl/Textarea.vue +31 -0
  56. package/dist/spa/src/afcl/Toggle.vue +32 -0
  57. package/dist/spa/src/afcl/Tooltip.vue +2 -3
  58. package/dist/spa/src/afcl/VerticalTabs.vue +3 -3
  59. package/dist/spa/src/afcl/index.ts +4 -3
  60. package/dist/spa/src/components/AcceptModal.vue +7 -7
  61. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  62. package/dist/spa/src/components/ColumnValueInput.vue +37 -18
  63. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  64. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  65. package/dist/spa/src/components/CustomRangePicker.vue +37 -8
  66. package/dist/spa/src/components/Filters.vue +83 -37
  67. package/dist/spa/src/components/GroupsTable.vue +9 -8
  68. package/dist/spa/src/components/MenuLink.vue +3 -3
  69. package/dist/spa/src/components/ResourceForm.vue +94 -51
  70. package/dist/spa/src/components/ResourceListTable.vue +38 -40
  71. package/dist/spa/src/components/ResourceListTableVirtual.vue +31 -33
  72. package/dist/spa/src/components/ShowTable.vue +17 -12
  73. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  74. package/dist/spa/src/components/SkeleteLoader.vue +1 -1
  75. package/dist/spa/src/components/ThreeDotsMenu.vue +49 -7
  76. package/dist/spa/src/components/Toast.vue +2 -7
  77. package/dist/spa/src/components/ValueRenderer.vue +4 -4
  78. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  79. package/dist/spa/src/i18n.ts +1 -1
  80. package/dist/spa/src/shims-vue.d.ts +5 -0
  81. package/dist/spa/src/spa_types/core.ts +7 -0
  82. package/dist/spa/src/stores/core.ts +1 -1
  83. package/dist/spa/src/types/Back.ts +82 -21
  84. package/dist/spa/src/types/Common.ts +25 -10
  85. package/dist/spa/src/types/FrontendAPI.ts +8 -0
  86. package/dist/spa/src/types/adapters/CompletionAdapter.ts +25 -0
  87. package/dist/spa/src/types/adapters/EmailAdapter.ts +27 -0
  88. package/dist/spa/src/types/adapters/ImageGenerationAdapter.ts +50 -0
  89. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  90. package/dist/spa/src/types/adapters/OAuth2Adapter.ts +34 -0
  91. package/dist/spa/src/types/adapters/StorageAdapter.ts +73 -0
  92. package/dist/spa/src/types/adapters/index.ts +6 -0
  93. package/dist/spa/src/utils.ts +217 -7
  94. package/dist/spa/src/views/CreateView.vue +21 -18
  95. package/dist/spa/src/views/EditView.vue +29 -19
  96. package/dist/spa/src/views/ListView.vue +60 -51
  97. package/dist/spa/src/views/LoginView.vue +50 -47
  98. package/dist/spa/src/views/ResourceParent.vue +1 -1
  99. package/dist/spa/src/views/ShowView.vue +34 -13
  100. package/dist/spa/src/websocket.ts +6 -1
  101. package/dist/spa/tsconfig.app.json +1 -1
  102. package/dist/spa/vite.config.ts +44 -1
  103. package/dist/types/Back.d.ts +61 -14
  104. package/dist/types/Back.d.ts.map +1 -1
  105. package/dist/types/Back.js.map +1 -1
  106. package/dist/types/Common.d.ts +23 -8
  107. package/dist/types/Common.d.ts.map +1 -1
  108. package/dist/types/Common.js.map +1 -1
  109. package/dist/types/FrontendAPI.d.ts +7 -0
  110. package/dist/types/FrontendAPI.d.ts.map +1 -1
  111. package/dist/types/FrontendAPI.js.map +1 -1
  112. package/dist/types/adapters/CompletionAdapter.d.ts +20 -0
  113. package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -0
  114. package/dist/types/adapters/CompletionAdapter.js +2 -0
  115. package/dist/types/adapters/CompletionAdapter.js.map +1 -0
  116. package/dist/types/adapters/EmailAdapter.d.ts +20 -0
  117. package/dist/types/adapters/EmailAdapter.d.ts.map +1 -0
  118. package/dist/types/adapters/EmailAdapter.js +2 -0
  119. package/dist/types/adapters/EmailAdapter.js.map +1 -0
  120. package/dist/types/adapters/ImageGenerationAdapter.d.ts +37 -0
  121. package/dist/types/adapters/ImageGenerationAdapter.d.ts.map +1 -0
  122. package/dist/types/adapters/ImageGenerationAdapter.js +2 -0
  123. package/dist/types/adapters/ImageGenerationAdapter.js.map +1 -0
  124. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  125. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  126. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  127. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  128. package/dist/types/adapters/OAuth2Adapter.d.ts +32 -0
  129. package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -0
  130. package/dist/types/adapters/OAuth2Adapter.js +2 -0
  131. package/dist/types/adapters/OAuth2Adapter.js.map +1 -0
  132. package/dist/types/adapters/StorageAdapter.d.ts +63 -0
  133. package/dist/types/adapters/StorageAdapter.d.ts.map +1 -0
  134. package/dist/types/adapters/StorageAdapter.js +2 -0
  135. package/dist/types/adapters/StorageAdapter.js.map +1 -0
  136. package/dist/types/adapters/index.d.ts +7 -0
  137. package/dist/types/adapters/index.d.ts.map +1 -0
  138. package/dist/types/adapters/index.js +2 -0
  139. package/dist/types/adapters/index.js.map +1 -0
  140. package/package.json +2 -2
  141. package/dist/spa/src/types/Adapters.ts +0 -213
  142. package/dist/types/Adapters.d.ts +0 -168
  143. package/dist/types/Adapters.d.ts.map +0 -1
  144. package/dist/types/Adapters.js +0 -2
  145. package/dist/types/Adapters.js.map +0 -1
@@ -63,14 +63,14 @@
63
63
 
64
64
  <script setup lang="ts">
65
65
 
66
- import { applyRegexValidation, callAdminForthApi} from '@/utils';
66
+ import { applyRegexValidation, callAdminForthApi, loadMoreForeignOptions, searchForeignOptions, createSearchInputHandlers} from '@/utils';
67
67
  import { computedAsync } from '@vueuse/core';
68
- import { computed, onMounted, ref, watch } from 'vue';
68
+ import { computed, onMounted, reactive, ref, watch, provide, type Ref } from 'vue';
69
69
  import { useRouter, useRoute } from 'vue-router';
70
70
  import { useCoreStore } from "@/stores/core";
71
71
  import GroupsTable from '@/components/GroupsTable.vue';
72
72
  import { useI18n } from 'vue-i18n';
73
- import { type AdminForthResourceCommon } from '@/types/Common';
73
+ import { type AdminForthResourceColumnCommon, type AdminForthResourceCommon } from '@/types/Common';
74
74
 
75
75
  const { t } = useI18n();
76
76
 
@@ -91,11 +91,16 @@ const mode = computed(() => route.name === 'resource-create' ? 'create' : 'edit'
91
91
 
92
92
  const emit = defineEmits(['update:record', 'update:isValid']);
93
93
 
94
- const currentValues = ref(null);
95
- const customComponentsInValidity = ref({});
96
- const customComponentsEmptiness = ref({});
94
+ const currentValues = ref<any | any[] | null>(null);
95
+ const customComponentsInValidity: Ref<Record<string, AdminForthResourceColumnCommon>> = ref({});
96
+ const customComponentsEmptiness: Ref<Record<string, AdminForthResourceColumnCommon>> = ref({});
97
97
 
98
- const columnError = (column) => {
98
+ const columnOptions = ref<Record<string, any[]>>({});
99
+ const columnLoadingState = reactive<Record<string, { loading: boolean; hasMore: boolean }>>({});
100
+ const columnOffsets = reactive<Record<string, number>>({});
101
+ const columnEmptyResultsCount = reactive<Record<string, number>>({});
102
+
103
+ const columnError = (column: AdminForthResourceColumnCommon) => {
99
104
  const val = computed(() => {
100
105
  if (!currentValues.value) {
101
106
  return null;
@@ -104,7 +109,7 @@ const columnError = (column) => {
104
109
  return customComponentsInValidity.value?.[column.name];
105
110
  }
106
111
 
107
- if ( column.required[mode.value] ) {
112
+ if ( column.required?.[mode.value] ) {
108
113
  const naturalEmptiness = currentValues.value[column.name] === undefined ||
109
114
  currentValues.value[column.name] === null ||
110
115
  currentValues.value[column.name] === '' ||
@@ -131,17 +136,23 @@ const columnError = (column) => {
131
136
  }
132
137
  } else if (column.isArray?.enabled) {
133
138
  if (!column.isArray.allowDuplicateItems) {
134
- if (currentValues.value[column.name].filter((value, index, self) => self.indexOf(value) !== index).length > 0) {
139
+ if (currentValues.value[column.name].filter((value: any, index: any, self: any) => self.indexOf(value) !== index).length > 0) {
135
140
  return t('Array cannot contain duplicate items');
136
141
  }
137
142
  }
138
143
 
139
- return currentValues.value[column.name] && currentValues.value[column.name].reduce((error, item) => {
140
- return error || validateValue(column.isArray.itemType, item, column) ||
141
- (item === null || !item.toString() ? t('Array cannot contain empty items') : null);
144
+ return currentValues.value[column.name] && currentValues.value[column.name].reduce((error: any, item: any) => {
145
+ if (column.isArray) {
146
+ return error || validateValue(column.isArray.itemType, item, column) ||
147
+ (item === null || !item.toString() ? t('Array cannot contain empty items') : null);
148
+ } else {
149
+ return error;
150
+ }
142
151
  }, null);
143
152
  } else {
144
- return validateValue(column.type, currentValues.value[column.name], column);
153
+ if (column.type) {
154
+ return validateValue(column.type, currentValues.value[column.name], column);
155
+ }
145
156
  }
146
157
 
147
158
  return null;
@@ -149,7 +160,7 @@ const columnError = (column) => {
149
160
  return val.value;
150
161
  };
151
162
 
152
- const validateValue = (type, value, column) => {
163
+ const validateValue = (type: string, value: any, column: AdminForthResourceColumnCommon) => {
153
164
  if (type === 'string' || type === 'text') {
154
165
  if (column.maxLength && value?.length > column.maxLength) {
155
166
  return t('This field must be shorter than {maxLength} characters', { maxLength: column.maxLength });
@@ -157,7 +168,7 @@ const validateValue = (type, value, column) => {
157
168
 
158
169
  if (column.minLength && value?.length < column.minLength) {
159
170
  // if column.required[mode.value] is false, then we check if the field is empty
160
- let needToCheckEmpty = column.required[mode.value] || value?.length > 0;
171
+ let needToCheckEmpty = column.required?.[mode.value] || value?.length > 0;
161
172
  if (!needToCheckEmpty) {
162
173
  return null;
163
174
  }
@@ -186,10 +197,10 @@ const validateValue = (type, value, column) => {
186
197
  };
187
198
 
188
199
 
189
- const setCurrentValue = (key, value, index=null) => {
200
+ const setCurrentValue = (key: any, value: any, index = null) => {
190
201
  const col = props.resource.columns.find((column) => column.name === key);
191
202
  // if field is an array, we need to update the array or individual element
192
- if (col.type === 'json' && col.isArray?.enabled) {
203
+ if (((col?.type && col.type === 'json') && (col?.type && col.isArray?.enabled))) {
193
204
  if (index === null) {
194
205
  currentValues.value[key] = value;
195
206
  } else if (index === currentValues.value[key].length) {
@@ -204,12 +215,12 @@ const setCurrentValue = (key, value, index=null) => {
204
215
  } else {
205
216
  currentValues.value[key][index] = value;
206
217
  }
207
- if (['text', 'richtext', 'string'].includes(col.isArray.itemType) && col.enforceLowerCase) {
218
+ if (col?.isArray && ['text', 'richtext', 'string'].includes(col.isArray.itemType) && col.enforceLowerCase) {
208
219
  currentValues.value[key][index] = currentValues.value[key][index].toLowerCase();
209
220
  }
210
221
  }
211
222
  } else {
212
- if (['integer', 'float', 'decimal'].includes(col.type)) {
223
+ if (col?.type && ['integer', 'float', 'decimal'].includes(col.type)) {
213
224
  if (value || value === 0) {
214
225
  currentValues.value[key] = +value;
215
226
  } else {
@@ -218,7 +229,7 @@ const setCurrentValue = (key, value, index=null) => {
218
229
  } else {
219
230
  currentValues.value[key] = value;
220
231
  }
221
- if (['text', 'richtext', 'string'].includes(col.type) && col.enforceLowerCase) {
232
+ if (col?.type && ['text', 'richtext', 'string'].includes(col?.type) && col.enforceLowerCase) {
222
233
  currentValues.value[key] = currentValues.value[key].toLowerCase();
223
234
  }
224
235
  }
@@ -239,11 +250,28 @@ const setCurrentValue = (key, value, index=null) => {
239
250
  emit('update:record', up);
240
251
  };
241
252
 
253
+ watch(() => props.resource.columns, async (newColumns) => {
254
+ if (!newColumns) return;
255
+
256
+ for (const column of newColumns) {
257
+ if (column.foreignResource) {
258
+ if (!columnOptions.value[column.name]) {
259
+ columnOptions.value[column.name] = [];
260
+ columnLoadingState[column.name] = { loading: false, hasMore: true };
261
+ columnOffsets[column.name] = 0;
262
+ columnEmptyResultsCount[column.name] = 0;
263
+
264
+ await loadMoreOptions(column.name);
265
+ }
266
+ }
267
+ }
268
+ }, { immediate: true });
269
+
242
270
  onMounted(() => {
243
271
  currentValues.value = Object.assign({}, props.record);
244
272
  // json values should transform to string
245
273
  props.resource.columns.forEach((column) => {
246
- if (column.type === 'json') {
274
+ if (column.type === 'json' && currentValues.value) {
247
275
  if (column.isArray?.enabled) {
248
276
  // if value is null or undefined, we should set it to empty array
249
277
  if (!currentValues.value[column.name]) {
@@ -266,33 +294,35 @@ onMounted(() => {
266
294
  emit('update:isValid', isValid.value);
267
295
  });
268
296
 
269
- const columnOptions = computedAsync(async () => {
270
- return (await Promise.all(
271
- Object.values(props.resource.columns).map(async (column) => {
272
- if (column.foreignResource) {
273
- const list = await callAdminForthApi({
274
- method: 'POST',
275
- path: `/get_resource_foreign_data`,
276
- body: {
277
- resourceId: router.currentRoute.value.params.resourceId,
278
- column: column.name,
279
- limit: 1000,
280
- offset: 0,
281
- },
282
- });
283
-
284
- if (!column.required[props.source] && !column.isArray?.enabled) list.items.push({ value: null, label: column.foreignResource.unsetLabel });
285
-
286
- return { [column.name]: list.items };
287
- }
288
- })
289
- )).reduce((acc, val) => Object.assign(acc, val), {})
290
-
291
- }, {});
297
+ async function loadMoreOptions(columnName: string, searchTerm = '') {
298
+ return loadMoreForeignOptions({
299
+ columnName,
300
+ searchTerm,
301
+ columns: props.resource.columns,
302
+ resourceId: router.currentRoute.value.params.resourceId as string,
303
+ columnOptions,
304
+ columnLoadingState,
305
+ columnOffsets,
306
+ columnEmptyResultsCount
307
+ });
308
+ }
309
+
310
+ async function searchOptions(columnName: string, searchTerm: string) {
311
+ return searchForeignOptions({
312
+ columnName,
313
+ searchTerm,
314
+ columns: props.resource.columns,
315
+ resourceId: router.currentRoute.value.params.resourceId as string,
316
+ columnOptions,
317
+ columnLoadingState,
318
+ columnOffsets,
319
+ columnEmptyResultsCount
320
+ });
321
+ }
292
322
 
293
323
 
294
324
  const editableColumns = computed(() => {
295
- return props.resource?.columns?.filter(column => column.showIn[mode.value]);
325
+ return props.resource?.columns?.filter(column => column.showIn?.[mode.value]);
296
326
  });
297
327
 
298
328
  const isValid = computed(() => {
@@ -302,12 +332,14 @@ const isValid = computed(() => {
302
332
 
303
333
  const groups = computed(() => {
304
334
  let fieldGroupType;
305
- if (mode.value === 'edit' && coreStore.resource.options?.editFieldGroups !== undefined) {
306
- fieldGroupType = coreStore.resource.options.editFieldGroups;
307
- } else if (mode.value === 'create' && coreStore.resource.options?.createFieldGroups !== undefined) {
308
- fieldGroupType = coreStore.resource.options.createFieldGroups;
309
- } else {
310
- fieldGroupType = coreStore.resource.options?.fieldGroups;
335
+ if(coreStore.resource){
336
+ if (mode.value === 'edit' && coreStore.resource.options?.editFieldGroups !== undefined) {
337
+ fieldGroupType = coreStore.resource.options.editFieldGroups;
338
+ } else if (mode.value === 'create' && coreStore.resource.options?.createFieldGroups !== undefined) {
339
+ fieldGroupType = coreStore.resource.options.createFieldGroups;
340
+ } else {
341
+ fieldGroupType = coreStore.resource.options?.fieldGroups;
342
+ }
311
343
  }
312
344
  return fieldGroupType ?? [];
313
345
  });
@@ -330,6 +362,17 @@ const getOtherColumns = () => {
330
362
 
331
363
  const otherColumns = getOtherColumns();
332
364
 
365
+ const onSearchInput = computed(() => {
366
+ return createSearchInputHandlers(
367
+ props.resource.columns,
368
+ searchOptions
369
+ );
370
+ });
371
+
372
+ provide('columnLoadingState', columnLoadingState);
373
+ provide('onSearchInput', onSearchInput);
374
+ provide('loadMoreOptions', loadMoreOptions);
375
+
333
376
  watch(() => isValid.value, (value) => {
334
377
  emit('update:isValid', value);
335
378
  });
@@ -6,24 +6,23 @@
6
6
  <!-- skelet loader -->
7
7
  <div role="status" v-if="!resource || !resource.columns"
8
8
  class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
9
-
10
9
  <div role="status" class="max-w-sm animate-pulse">
11
- <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px]"></div>
10
+ <div class="h-2 bg-lightListSkeletLoader rounded-full dark:bg-darkListSkeletLoader max-w-[360px]"></div>
12
11
  </div>
13
12
  </div>
14
- <table v-else class=" w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
13
+ <table v-else class=" w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
15
14
 
16
15
  <tbody>
17
16
  <!-- table header -->
18
- <tr class="t-header sticky z-10 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
17
+ <tr class="t-header sticky z-10 top-0 text-xs text-lightListTableHeadingText bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-darkListTableHeadingText">
19
18
  <td scope="col" class="p-4">
20
- <div class="flex items-center">
21
- <input id="checkbox-all-search" type="checkbox" :checked="allFromThisPageChecked" @change="selectAll()"
22
- :disabled="!rows || !rows.length"
23
- class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded
24
- focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
25
- <label for="checkbox-all-search" class="sr-only">{{ $t('checkbox') }}</label>
26
- </div>
19
+ <Checkbox
20
+ :modelValue="allFromThisPageChecked"
21
+ :disabled="!rows || !rows.length"
22
+ @update:modelValue="selectAll"
23
+ >
24
+ <span class="sr-only">{{ $t('checkbox') }}</span>
25
+ </Checkbox>
27
26
  </td>
28
27
 
29
28
  <td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
@@ -91,18 +90,16 @@
91
90
 
92
91
  :class="{'border-b': rowI !== rows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
93
92
  >
94
- <td class="w-4 p-4 cursor-default" @click="(e)=>{e.stopPropagation()}">
95
- <div class="flex items center ">
96
- <input
97
- @click="(e)=>{e.stopPropagation()}"
98
- id="checkbox-table-search-1"
99
- type="checkbox"
100
- :checked="checkboxesInternal.includes(row._primaryKeyValue)"
101
- @change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
102
- class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 cursor-pointer">
103
- <label for="checkbox-table-search-1" class="sr-only">{{ $t('checkbox') }}</label>
104
- </div>
105
- </td>
93
+ <td class="w-4 p-4 cursor-default" @click="(e)=>e.stopPropagation()">
94
+ <Checkbox
95
+ :model-value="checkboxesInternal.includes(row._primaryKeyValue)"
96
+ @change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
97
+ @click="(e)=>e.stopPropagation()"
98
+ >
99
+ <span class="sr-only">{{ $t('checkbox') }}</span>
100
+ </Checkbox>
101
+ </td>
102
+
106
103
  <td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
107
104
  <!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
108
105
  <component
@@ -128,7 +125,7 @@
128
125
  }"
129
126
 
130
127
  >
131
- <IconEyeSolid class="w-5 h-5 me-2"/>
128
+ <IconEyeSolid class="af-show-icon w-5 h-5 me-2"/>
132
129
  </RouterLink>
133
130
 
134
131
  <template v-slot:tooltip>
@@ -147,7 +144,7 @@
147
144
  }
148
145
  }"
149
146
  >
150
- <IconPenSolid class="w-5 h-5 me-2"/>
147
+ <IconPenSolid class="af-edit-icon w-5 h-5 me-2"/>
151
148
  </RouterLink>
152
149
  <template v-slot:tooltip>
153
150
  {{ $t('Edit item') }}
@@ -159,7 +156,7 @@
159
156
  v-if="resource.options?.allowedActions.delete"
160
157
  @click="deleteRecord(row)"
161
158
  >
162
- <IconTrashBinSolid class="w-5 h-5 me-2"/>
159
+ <IconTrashBinSolid class="af-delete-icon w-5 h-5 me-2"/>
163
160
  </button>
164
161
 
165
162
  <template v-slot:tooltip>
@@ -199,14 +196,14 @@
199
196
  <!-- pagination
200
197
  totalRows in v-if is used to not hide page input during loading when user puts cursor into it and edit directly (rows gets null there during edit)
201
198
  -->
202
- <div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3"
203
- v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
204
- >
199
+ <div class="af-pagination-container flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
205
200
 
206
- <div class="inline-flex ">
201
+ <div class="af-pagination-buttons-container inline-flex "
202
+ v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
203
+ >
207
204
  <!-- Buttons -->
208
205
  <button
209
- class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-gray-900 focus:outline-none bg-white border-r-0 rounded-s border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
206
+ class="af-pagination-prev-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 rounded-s border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
210
207
  @click="page--; pageInput = page.toString();" :disabled="page <= 1">
211
208
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
212
209
  viewBox="0 0 14 10">
@@ -218,14 +215,14 @@
218
215
  </span>
219
216
  </button>
220
217
  <button
221
- class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-r-0 border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
218
+ class="af-pagination-first-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
222
219
  @click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
223
220
  <!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
224
221
  1
225
222
  </button>
226
223
  <div
227
224
  contenteditable="true"
228
- class="min-w-10 outline-none inline-block w-auto min-w-10 py-1.5 px-3 text-sm text-center text-gray-700 border border-gray-300 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800 z-10"
225
+ class="af-pagination-input min-w-10 outline-none inline-block w-auto py-1.5 px-3 text-sm text-center text-lightListTablePaginationCurrentPageText border border-lightListTablePaginationBorder dark:border-darkListTablePaginationBorder dark:text-darkListTablePaginationCurrentPageText dark:bg-darkListTablePaginationBackgoround z-10"
229
226
  @keydown="onPageKeydown($event)"
230
227
  @input="onPageInput($event)"
231
228
  @blur="validatePageInput()"
@@ -234,14 +231,14 @@
234
231
  </div>
235
232
 
236
233
  <button
237
- class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-l-0 border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
234
+ class="af-pagination-last-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
238
235
  @click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
239
236
  {{ totalPages }}
240
237
 
241
238
  <!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
242
239
  </button>
243
240
  <button
244
- class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-gray-900 focus:outline-none bg-white border-l-0 rounded-e border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
241
+ class="af-pagination-next-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 rounded-e border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
245
242
  @click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
246
243
  <span class="hidden sm:inline">{{ $t('Next') }}</span>
247
244
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
@@ -253,11 +250,11 @@
253
250
  </div>
254
251
 
255
252
  <!-- Help text -->
256
- <span class="text-sm text-gray-700 dark:text-gray-400">
257
- <span v-if="((page || 1) - 1) * pageSize + 1 > totalRows">{{ $t('Wrong Page') }} </span>
258
- <template v-else>
253
+ <span class="text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
254
+ <span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
255
+ <template v-else-if="resource && totalRows > 0">
259
256
 
260
- <span class="hidden sm:inline">
257
+ <span class="af-pagination-info hidden sm:inline">
261
258
  <i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
262
259
  <template v-slot:from>
263
260
  <strong>{{ from }}</strong>
@@ -307,12 +304,13 @@ import {
307
304
  import {
308
305
  IconEyeSolid,
309
306
  IconPenSolid,
310
- IconTrashBinSolid
307
+ IconTrashBinSolid,
311
308
  } from '@iconify-prerendered/vue-flowbite';
312
309
  import router from '@/router';
313
310
  import { Tooltip } from '@/afcl';
314
311
  import type { AdminForthResourceCommon } from '@/types/Common';
315
312
  import adminforth from '@/adminforth';
313
+ import Checkbox from '@/afcl/Checkbox.vue';
316
314
 
317
315
  const coreStore = useCoreStore();
318
316
  const { t } = useI18n();
@@ -5,28 +5,28 @@
5
5
  :style="`height: ${containerHeight}px; will-change: transform;`"
6
6
  @scroll="handleScroll"
7
7
  ref="containerRef"
8
- >
8
+ >
9
9
  <!-- skelet loader -->
10
10
  <div role="status" v-if="!resource || !resource.columns"
11
11
  class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
12
12
 
13
13
  <div role="status" class="max-w-sm animate-pulse">
14
- <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px]"></div>
14
+ <div class="h-2 bg-lightListSkeletLoader rounded-full dark:bg-darkListSkeletLoader max-w-[360px]"></div>
15
15
  </div>
16
16
  </div>
17
- <table v-else class=" w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
17
+ <table v-else class=" w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
18
18
 
19
19
  <tbody>
20
20
  <!-- table header -->
21
21
  <tr class="t-header sticky z-10 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
22
22
  <td scope="col" class="p-4">
23
- <div class="flex items-center">
24
- <input id="checkbox-all-search" type="checkbox" :checked="allFromThisPageChecked" @change="selectAll()"
25
- :disabled="!rows || !rows.length"
26
- class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded
27
- focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
28
- <label for="checkbox-all-search" class="sr-only">{{ $t('checkbox') }}</label>
29
- </div>
23
+ <Checkbox
24
+ :modelValue="allFromThisPageChecked"
25
+ :disabled="!rows || !rows.length"
26
+ @update:modelValue="selectAll"
27
+ >
28
+ <span class="sr-only">{{ $t('checkbox') }}</span>
29
+ </Checkbox>
30
30
  </td>
31
31
 
32
32
  <td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
@@ -101,17 +101,14 @@
101
101
  :class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
102
102
  @mounted="(el) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
103
103
  >
104
- <td class="w-4 p-4 cursor-default" @click="(e)=>{e.stopPropagation()}">
105
- <div class="flex items center ">
106
- <input
107
- @click="(e)=>{e.stopPropagation()}"
108
- id="checkbox-table-search-1"
109
- type="checkbox"
110
- :checked="checkboxesInternal.includes(row._primaryKeyValue)"
111
- @change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
112
- class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 cursor-pointer">
113
- <label for="checkbox-table-search-1" class="sr-only">{{ $t('checkbox') }}</label>
114
- </div>
104
+ <td class="w-4 p-4 cursor-default" @click="(e)=>e.stopPropagation()">
105
+ <Checkbox
106
+ :model-value="checkboxesInternal.includes(row._primaryKeyValue)"
107
+ @change="(e)=>{addToCheckedValues(row._primaryKeyValue)}"
108
+ @click="(e)=>e.stopPropagation()"
109
+ >
110
+ <span class="sr-only">{{ $t('checkbox') }}</span>
111
+ </Checkbox>
115
112
  </td>
116
113
  <td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
117
114
  <!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
@@ -216,14 +213,14 @@
216
213
  <!-- pagination
217
214
  totalRows in v-if is used to not hide page input during loading when user puts cursor into it and edit directly (rows gets null there during edit)
218
215
  -->
219
- <div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3"
220
- v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
221
- >
216
+ <div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
222
217
 
223
- <div class="inline-flex ">
218
+ <div class="inline-flex "
219
+ v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
220
+ >
224
221
  <!-- Buttons -->
225
222
  <button
226
- class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-gray-900 focus:outline-none bg-white border-r-0 rounded-s border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
223
+ class="af-pagination-prev-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 rounded-s border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
227
224
  @click="page--; pageInput = page.toString();" :disabled="page <= 1">
228
225
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
229
226
  viewBox="0 0 14 10">
@@ -235,14 +232,14 @@
235
232
  </span>
236
233
  </button>
237
234
  <button
238
- class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-r-0 border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
235
+ class="af-pagination-first-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
239
236
  @click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
240
237
  <!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
241
238
  1
242
239
  </button>
243
240
  <div
244
241
  contenteditable="true"
245
- class="min-w-10 outline-none inline-block w-auto min-w-10 py-1.5 px-3 text-sm text-center text-gray-700 border border-gray-300 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800 z-10"
242
+ class="af-pagination-input min-w-10 outline-none inline-block w-auto py-1.5 px-3 text-sm text-center text-lightListTablePaginationCurrentPageText border border-lightListTablePaginationBorder dark:border-darkListTablePaginationBorder dark:text-darkListTablePaginationCurrentPageText dark:bg-darkListTablePaginationBackgoround z-10"
246
243
  @keydown="onPageKeydown($event)"
247
244
  @input="onPageInput($event)"
248
245
  @blur="validatePageInput()"
@@ -251,14 +248,14 @@
251
248
  </div>
252
249
 
253
250
  <button
254
- class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-l-0 border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
251
+ class="af-pagination-last-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
255
252
  @click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
256
253
  {{ totalPages }}
257
254
 
258
255
  <!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
259
256
  </button>
260
257
  <button
261
- class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-gray-900 focus:outline-none bg-white border-l-0 rounded-e border border-gray-300 hover:bg-gray-100 hover:text-lightPrimary 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"
258
+ class="af-pagination-next-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 rounded-e border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
262
259
  @click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
263
260
  <span class="hidden sm:inline">{{ $t('Next') }}</span>
264
261
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
@@ -270,9 +267,9 @@
270
267
  </div>
271
268
 
272
269
  <!-- Help text -->
273
- <span class="text-sm text-gray-700 dark:text-gray-400">
274
- <span v-if="((page || 1) - 1) * pageSize + 1 > totalRows">{{ $t('Wrong Page') }} </span>
275
- <template v-else>
270
+ <span class="text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
271
+ <span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
272
+ <template v-else-if="resource && totalRows > 0">
276
273
 
277
274
  <span class="hidden sm:inline">
278
275
  <i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
@@ -330,6 +327,7 @@ import router from '@/router';
330
327
  import { Tooltip } from '@/afcl';
331
328
  import type { AdminForthResourceCommon } from '@/types/Common';
332
329
  import adminforth from '@/adminforth';
330
+ import Checkbox from '@/afcl/Checkbox.vue';
333
331
 
334
332
  const coreStore = useCoreStore();
335
333
  const { t } = useI18n();