adminforth 2.4.0-next.21 → 2.4.0-next.211

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 (192) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/cli.js +10 -3
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +10 -2
  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/commands/createCustomComponent/main.js +1 -0
  9. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  10. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  11. package/commands/createPlugin/templates/package.json.hbs +1 -1
  12. package/dist/auth.d.ts +9 -1
  13. package/dist/auth.d.ts.map +1 -1
  14. package/dist/auth.js +15 -2
  15. package/dist/auth.js.map +1 -1
  16. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  17. package/dist/dataConnectors/baseConnector.js +46 -15
  18. package/dist/dataConnectors/baseConnector.js.map +1 -1
  19. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  20. package/dist/dataConnectors/clickhouse.js +15 -0
  21. package/dist/dataConnectors/clickhouse.js.map +1 -1
  22. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  23. package/dist/dataConnectors/mongo.js +44 -15
  24. package/dist/dataConnectors/mongo.js.map +1 -1
  25. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  26. package/dist/dataConnectors/mysql.js +11 -0
  27. package/dist/dataConnectors/mysql.js.map +1 -1
  28. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  29. package/dist/dataConnectors/postgres.js +11 -0
  30. package/dist/dataConnectors/postgres.js.map +1 -1
  31. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  32. package/dist/dataConnectors/sqlite.js +11 -0
  33. package/dist/dataConnectors/sqlite.js.map +1 -1
  34. package/dist/index.d.ts +2 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +20 -9
  37. package/dist/index.js.map +1 -1
  38. package/dist/modules/codeInjector.d.ts +2 -0
  39. package/dist/modules/codeInjector.d.ts.map +1 -1
  40. package/dist/modules/codeInjector.js +52 -8
  41. package/dist/modules/codeInjector.js.map +1 -1
  42. package/dist/modules/configValidator.d.ts.map +1 -1
  43. package/dist/modules/configValidator.js +74 -7
  44. package/dist/modules/configValidator.js.map +1 -1
  45. package/dist/modules/restApi.d.ts.map +1 -1
  46. package/dist/modules/restApi.js +154 -26
  47. package/dist/modules/restApi.js.map +1 -1
  48. package/dist/modules/styles.d.ts +503 -13
  49. package/dist/modules/styles.d.ts.map +1 -1
  50. package/dist/modules/styles.js +559 -31
  51. package/dist/modules/styles.js.map +1 -1
  52. package/dist/modules/utils.d.ts +2 -0
  53. package/dist/modules/utils.d.ts.map +1 -1
  54. package/dist/modules/utils.js +16 -0
  55. package/dist/modules/utils.js.map +1 -1
  56. package/dist/servers/express.d.ts.map +1 -1
  57. package/dist/servers/express.js +14 -0
  58. package/dist/servers/express.js.map +1 -1
  59. package/dist/spa/index.html +1 -1
  60. package/dist/spa/package-lock.json +5 -4
  61. package/dist/spa/package.json +1 -1
  62. package/dist/spa/src/App.vue +54 -169
  63. package/dist/spa/src/adminforth.ts +42 -18
  64. package/dist/spa/src/afcl/BarChart.vue +2 -2
  65. package/dist/spa/src/afcl/Button.vue +6 -6
  66. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  67. package/dist/spa/src/afcl/Card.vue +25 -0
  68. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  69. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  70. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  71. package/dist/spa/src/afcl/Dialog.vue +44 -27
  72. package/dist/spa/src/afcl/Dropzone.vue +12 -12
  73. package/dist/spa/src/afcl/Input.vue +6 -6
  74. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  75. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  76. package/dist/spa/src/afcl/PieChart.vue +5 -5
  77. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  78. package/dist/spa/src/afcl/Select.vue +68 -34
  79. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  80. package/dist/spa/src/afcl/Table.vue +213 -74
  81. package/dist/spa/src/afcl/Textarea.vue +31 -0
  82. package/dist/spa/src/afcl/Toggle.vue +32 -0
  83. package/dist/spa/src/afcl/Tooltip.vue +1 -2
  84. package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
  85. package/dist/spa/src/afcl/index.ts +6 -3
  86. package/dist/spa/src/components/AcceptModal.vue +7 -7
  87. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  88. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  89. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  90. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  91. package/dist/spa/src/components/CustomRangePicker.vue +37 -8
  92. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  93. package/dist/spa/src/components/Filters.vue +85 -39
  94. package/dist/spa/src/components/GroupsTable.vue +9 -8
  95. package/dist/spa/src/components/MenuLink.vue +90 -23
  96. package/dist/spa/src/components/ResourceForm.vue +94 -51
  97. package/dist/spa/src/components/ResourceListTable.vue +78 -80
  98. package/dist/spa/src/components/ResourceListTableVirtual.vue +71 -73
  99. package/dist/spa/src/components/ShowTable.vue +17 -12
  100. package/dist/spa/src/components/Sidebar.vue +448 -0
  101. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  102. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  103. package/dist/spa/src/components/ThreeDotsMenu.vue +73 -14
  104. package/dist/spa/src/components/Toast.vue +27 -9
  105. package/dist/spa/src/components/UserMenuSettingsButton.vue +70 -0
  106. package/dist/spa/src/components/ValueRenderer.vue +43 -16
  107. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  108. package/dist/spa/src/i18n.ts +1 -1
  109. package/dist/spa/src/renderers/CompactField.vue +1 -1
  110. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  111. package/dist/spa/src/router/index.ts +8 -0
  112. package/dist/spa/src/shims-vue.d.ts +5 -0
  113. package/dist/spa/src/spa_types/core.ts +13 -1
  114. package/dist/spa/src/stores/core.ts +1 -1
  115. package/dist/spa/src/stores/filters.ts +29 -2
  116. package/dist/spa/src/stores/modal.ts +6 -1
  117. package/dist/spa/src/stores/toast.ts +22 -3
  118. package/dist/spa/src/types/Back.ts +137 -22
  119. package/dist/spa/src/types/Common.ts +67 -32
  120. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  121. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  122. package/dist/spa/src/types/adapters/CompletionAdapter.ts +25 -0
  123. package/dist/spa/src/types/adapters/EmailAdapter.ts +27 -0
  124. package/dist/spa/src/types/adapters/ImageGenerationAdapter.ts +50 -0
  125. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  126. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  127. package/dist/spa/src/types/adapters/OAuth2Adapter.ts +34 -0
  128. package/dist/spa/src/types/adapters/StorageAdapter.ts +73 -0
  129. package/dist/spa/src/types/adapters/index.ts +8 -0
  130. package/dist/spa/src/utils.ts +219 -8
  131. package/dist/spa/src/views/CreateView.vue +18 -19
  132. package/dist/spa/src/views/EditView.vue +25 -19
  133. package/dist/spa/src/views/ListView.vue +139 -86
  134. package/dist/spa/src/views/LoginView.vue +31 -37
  135. package/dist/spa/src/views/ResourceParent.vue +2 -2
  136. package/dist/spa/src/views/SettingsView.vue +121 -0
  137. package/dist/spa/src/views/ShowView.vue +59 -39
  138. package/dist/spa/src/websocket.ts +6 -1
  139. package/dist/spa/tsconfig.app.json +1 -1
  140. package/dist/spa/vite.config.ts +45 -2
  141. package/dist/types/Back.d.ts +115 -14
  142. package/dist/types/Back.d.ts.map +1 -1
  143. package/dist/types/Back.js +15 -0
  144. package/dist/types/Back.js.map +1 -1
  145. package/dist/types/Common.d.ts +59 -29
  146. package/dist/types/Common.d.ts.map +1 -1
  147. package/dist/types/Common.js.map +1 -1
  148. package/dist/types/FrontendAPI.d.ts +31 -3
  149. package/dist/types/FrontendAPI.d.ts.map +1 -1
  150. package/dist/types/FrontendAPI.js.map +1 -1
  151. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  152. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  153. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  154. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  155. package/dist/types/adapters/CompletionAdapter.d.ts +20 -0
  156. package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -0
  157. package/dist/types/adapters/CompletionAdapter.js +2 -0
  158. package/dist/types/adapters/CompletionAdapter.js.map +1 -0
  159. package/dist/types/adapters/EmailAdapter.d.ts +20 -0
  160. package/dist/types/adapters/EmailAdapter.d.ts.map +1 -0
  161. package/dist/types/adapters/EmailAdapter.js +2 -0
  162. package/dist/types/adapters/EmailAdapter.js.map +1 -0
  163. package/dist/types/adapters/ImageGenerationAdapter.d.ts +37 -0
  164. package/dist/types/adapters/ImageGenerationAdapter.d.ts.map +1 -0
  165. package/dist/types/adapters/ImageGenerationAdapter.js +2 -0
  166. package/dist/types/adapters/ImageGenerationAdapter.js.map +1 -0
  167. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  168. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  169. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  170. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  171. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  172. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  173. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  174. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  175. package/dist/types/adapters/OAuth2Adapter.d.ts +32 -0
  176. package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -0
  177. package/dist/types/adapters/OAuth2Adapter.js +2 -0
  178. package/dist/types/adapters/OAuth2Adapter.js.map +1 -0
  179. package/dist/types/adapters/StorageAdapter.d.ts +63 -0
  180. package/dist/types/adapters/StorageAdapter.d.ts.map +1 -0
  181. package/dist/types/adapters/StorageAdapter.js +2 -0
  182. package/dist/types/adapters/StorageAdapter.js.map +1 -0
  183. package/dist/types/adapters/index.d.ts +9 -0
  184. package/dist/types/adapters/index.d.ts.map +1 -0
  185. package/dist/types/adapters/index.js +2 -0
  186. package/dist/types/adapters/index.js.map +1 -0
  187. package/package.json +3 -2
  188. package/dist/spa/src/types/Adapters.ts +0 -213
  189. package/dist/types/Adapters.d.ts +0 -168
  190. package/dist/types/Adapters.d.ts.map +0 -1
  191. package/dist/types/Adapters.js +0 -2
  192. package/dist/types/Adapters.js.map +0 -1
@@ -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">
@@ -48,8 +47,8 @@
48
47
  </div>
49
48
  <span
50
49
  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"
51
- v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
52
- {{ sort.findIndex((s) => s.field === c.name) + 1 }}
50
+ v-if="sort.findIndex((s: any) => s.field === c.name) !== -1 && sort?.length > 1">
51
+ {{ sort.findIndex((s: any) => s.field === c.name) + 1 }}
53
52
  </span>
54
53
 
55
54
  </div>
@@ -65,7 +64,7 @@
65
64
  <!-- table header end -->
66
65
  <SkeleteLoader
67
66
  v-if="!rows"
68
- :columns="resource?.columns.filter(c => c.showIn.list).length + 2"
67
+ :columns="resource?.columns.filter((c: AdminForthResourceColumnInputCommon) => c.showIn?.list).length + 2"
69
68
  :rows="rowHeights.length || 3"
70
69
  :row-heights="rowHeights"
71
70
  :column-widths="columnWidths"
@@ -91,23 +90,21 @@
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: any)=>{addToCheckedValues(row._primaryKeyValue)}"
97
+ @click="(e: any)=>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
109
- :is="c?.components?.list ? getCustomComponent(c.components.list) : ValueRenderer"
110
- :meta="c?.components?.list?.meta"
106
+ :is="c?.components?.list ? getCustomComponent(typeof c.components.list === 'string' ? { file: c.components.list } : c.components.list) : ValueRenderer"
107
+ :meta="typeof c?.components?.list === 'object' ? c.components.list.meta : undefined"
111
108
  :column="c"
112
109
  :record="row"
113
110
  :adminUser="coreStore.adminUser"
@@ -118,7 +115,7 @@
118
115
  <div class="flex text-lightPrimary dark:text-darkPrimary items-center">
119
116
  <Tooltip>
120
117
  <RouterLink
121
- v-if="resource.options?.allowedActions.show"
118
+ v-if="resource.options?.allowedActions?.show"
122
119
  :to="{
123
120
  name: 'resource-show',
124
121
  params: {
@@ -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>
@@ -138,7 +135,7 @@
138
135
 
139
136
  <Tooltip>
140
137
  <RouterLink
141
- v-if="resource.options?.allowedActions.edit"
138
+ v-if="resource.options?.allowedActions?.edit"
142
139
  :to="{
143
140
  name: 'resource-edit',
144
141
  params: {
@@ -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') }}
@@ -156,10 +153,10 @@
156
153
 
157
154
  <Tooltip>
158
155
  <button
159
- v-if="resource.options?.allowedActions.delete"
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
- import type { AdminForthResourceCommon } from '@/types/Common';
311
+ import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon, AdminForthResourceColumnCommon } 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();
@@ -341,7 +339,7 @@ const emits = defineEmits([
341
339
  const checkboxesInternal: Ref<any[]> = ref([]);
342
340
  const pageInput = ref('1');
343
341
  const page = ref(1);
344
- const sort = ref([]);
342
+ const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
345
343
 
346
344
 
347
345
  const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
@@ -350,11 +348,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
350
348
  watch(() => page.value, (newPage) => {
351
349
  emits('update:page', newPage);
352
350
  });
353
- async function onPageKeydown(event) {
351
+ async function onPageKeydown(event: any) {
354
352
  // page input should accept only numbers, arrow keys and backspace
355
353
  if (['Enter', 'Space'].includes(event.code) ||
356
354
  (!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
357
- && isNaN(String.fromCharCode(event.keyCode)))) {
355
+ && isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
358
356
  event.preventDefault();
359
357
  if (event.code === 'Enter') {
360
358
  validatePageInput();
@@ -375,7 +373,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
375
373
  checkboxesInternal.value = newCheckboxes;
376
374
  });
377
375
 
378
- watch(() => props.sort, (newSort) => {
376
+ watch(() => props.sort, (newSort: any) => {
379
377
  sort.value = newSort;
380
378
  });
381
379
 
@@ -386,17 +384,17 @@ watch(() => props.page, (newPage) => {
386
384
  page.value = newPage;
387
385
  });
388
386
 
389
- const rowRefs = useTemplateRef('rowRefs');
390
- const headerRefs = useTemplateRef('headerRefs');
391
- const rowHeights = ref([]);
392
- const columnWidths = ref([]);
387
+ const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
388
+ const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
389
+ const rowHeights = ref<number[]>([]);
390
+ const columnWidths = ref<number[]>([]);
393
391
  watch(() => props.rows, (newRows) => {
394
392
  // rows are set to null when new records are loading
395
- rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el) => el.offsetHeight);
396
- columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el) => el.offsetWidth)];
393
+ rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
394
+ columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
397
395
  });
398
396
 
399
- function addToCheckedValues(id) {
397
+ function addToCheckedValues(id: string) {
400
398
  if (checkboxesInternal.value.includes(id)) {
401
399
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
402
400
  } else {
@@ -405,17 +403,17 @@ function addToCheckedValues(id) {
405
403
  checkboxesInternal.value = [ ...checkboxesInternal.value ]
406
404
  }
407
405
 
408
- const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn.list));
406
+ const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
409
407
 
410
- async function selectAll(value) {
408
+ async function selectAll() {
411
409
  if (!allFromThisPageChecked.value) {
412
- props.rows.forEach((r) => {
410
+ props.rows?.forEach((r) => {
413
411
  if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
414
412
  checkboxesInternal.value.push(r._primaryKeyValue)
415
413
  }
416
414
  });
417
415
  } else {
418
- props.rows.forEach((r) => {
416
+ props.rows?.forEach((r) => {
419
417
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
420
418
  });
421
419
  }
@@ -428,15 +426,15 @@ const allFromThisPageChecked = computed(() => {
428
426
  if (!props.rows || !props.rows.length) return false;
429
427
  return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
430
428
  });
431
- const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
432
- const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
429
+ const ascArr = computed(() => sort.value.filter((s:any) => s.direction === 'asc').map((s: any) => s.field));
430
+ const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
433
431
 
434
432
 
435
- function onSortButtonClick(event, field) {
433
+ function onSortButtonClick(event: any, field: string) {
436
434
  // if ctrl key is pressed, add to sort otherwise sort by this field
437
435
  // in any case if field is already in sort, toggle direction
438
436
 
439
- const sortIndex = sort.value.findIndex((s) => s.field === field);
437
+ const sortIndex = sort.value.findIndex((s: any) => s.field === field);
440
438
  if (sortIndex === -1) {
441
439
  // field is not in sort, add it
442
440
  if (event.ctrlKey) {
@@ -447,9 +445,9 @@ function onSortButtonClick(event, field) {
447
445
  } else {
448
446
  const sortField = sort.value[sortIndex];
449
447
  if (sortField.direction === 'asc') {
450
- sort.value = sort.value.map((s) => s.field === field ? {field, direction: 'desc'} : s);
448
+ sort.value = sort.value.map((s: any) => s.field === field ? {field, direction: 'desc'} : s);
451
449
  } else {
452
- sort.value = sort.value.filter((s) => s.field !== field);
450
+ sort.value = sort.value.filter((s: any) => s.field !== field);
453
451
  }
454
452
  }
455
453
  }
@@ -457,11 +455,11 @@ function onSortButtonClick(event, field) {
457
455
 
458
456
  const clickTarget = ref(null);
459
457
 
460
- async function onClick(e,row) {
458
+ async function onClick(e: any, row: any) {
461
459
  if(clickTarget.value === e.target) return;
462
460
  clickTarget.value = e.target;
463
461
  await new Promise((resolve) => setTimeout(resolve, 100));
464
- if (window.getSelection().toString()) return;
462
+ if (window.getSelection()?.toString()) return;
465
463
  else {
466
464
  if (row._clickUrl === null) {
467
465
  // user asked to nothing on click
@@ -476,7 +474,7 @@ async function onClick(e,row) {
476
474
  router.resolve({
477
475
  name: 'resource-show',
478
476
  params: {
479
- resourceId: props.resource.resourceId,
477
+ resourceId: props.resource?.resourceId,
480
478
  primaryKey: row._primaryKeyValue,
481
479
  },
482
480
  }).href,
@@ -494,7 +492,7 @@ async function onClick(e,row) {
494
492
  router.push({
495
493
  name: 'resource-show',
496
494
  params: {
497
- resourceId: props.resource.resourceId,
495
+ resourceId: props.resource?.resourceId,
498
496
  primaryKey: row._primaryKeyValue,
499
497
  },
500
498
  });
@@ -503,7 +501,7 @@ async function onClick(e,row) {
503
501
  }
504
502
  }
505
503
 
506
- async function deleteRecord(row) {
504
+ async function deleteRecord(row: any) {
507
505
  const data = await adminforth.confirm({
508
506
  message: t('Are you sure you want to delete this item?'),
509
507
  yes: t('Delete'),
@@ -515,7 +513,7 @@ async function deleteRecord(row) {
515
513
  path: '/delete_record',
516
514
  method: 'POST',
517
515
  body: {
518
- resourceId: props.resource.resourceId,
516
+ resourceId: props.resource?.resourceId,
519
517
  primaryKey: row._primaryKeyValue,
520
518
  }
521
519
  });
@@ -533,16 +531,16 @@ async function deleteRecord(row) {
533
531
  }
534
532
  }
535
533
 
536
- const actionLoadingStates = ref({});
534
+ const actionLoadingStates = ref<Record<string | number, boolean>>({});
537
535
 
538
- async function startCustomAction(actionId, row) {
536
+ async function startCustomAction(actionId: string, row: any) {
539
537
  actionLoadingStates.value[actionId] = true;
540
538
 
541
539
  const data = await callAdminForthApi({
542
540
  path: '/start_custom_action',
543
541
  method: 'POST',
544
542
  body: {
545
- resourceId: props.resource.resourceId,
543
+ resourceId: props.resource?.resourceId,
546
544
  actionId: actionId,
547
545
  recordId: row._primaryKeyValue
548
546
  }
@@ -580,7 +578,7 @@ async function startCustomAction(actionId, row) {
580
578
  }
581
579
  }
582
580
 
583
- function onPageInput(event) {
581
+ function onPageInput(event: any) {
584
582
  pageInput.value = event.target.innerText;
585
583
  }
586
584