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
@@ -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="h-full 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">
@@ -51,8 +51,8 @@
51
51
  </div>
52
52
  <span
53
53
  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"
54
- v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
55
- {{ sort.findIndex((s) => s.field === c.name) + 1 }}
54
+ v-if="sort.findIndex((s: any) => s.field === c.name) !== -1 && sort?.length > 1">
55
+ {{ sort.findIndex((s: any) => s.field === c.name) + 1 }}
56
56
  </span>
57
57
 
58
58
  </div>
@@ -68,13 +68,13 @@
68
68
  <!-- table header end -->
69
69
  <SkeleteLoader
70
70
  v-if="!rows"
71
- :columns="resource?.columns.filter(c => c.showIn.list).length + 2"
71
+ :columns="resource?.columns.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list).length + 2"
72
72
  :rows="rowHeights.length || 20"
73
73
  :row-heights="rowHeights"
74
74
  :column-widths="columnWidths"
75
75
  />
76
76
 
77
- <tr v-else-if="rows.length === 0" class="bg-lightListTable dark:bg-darkListTable dark:border-darkListTableBorder">
77
+ <tr v-else-if="rows.length === 0" class="h-full bg-lightListTable dark:bg-darkListTable dark:border-darkListTableBorder">
78
78
  <td :colspan="resource?.columns.length + 2">
79
79
 
80
80
  <div id="toast-simple"
@@ -99,25 +99,22 @@
99
99
  ref="rowRefs"
100
100
  class="bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
101
101
  :class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
102
- @mounted="(el) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
102
+ @mounted="(el: any) => 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: any)=>{addToCheckedValues(row._primaryKeyValue)}"
108
+ @click="(e: any)=>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 -->
118
115
  <component
119
- :is="c?.components?.list ? getCustomComponent(c.components.list) : ValueRenderer"
120
- :meta="c?.components?.list?.meta"
116
+ :is="c?.components?.list ? getCustomComponent(typeof c.components.list === 'string' ? { file: c.components.list } : c.components.list) : ValueRenderer"
117
+ :meta="typeof c?.components?.list === 'object' ? c.components.list.meta : undefined"
121
118
  :column="c"
122
119
  :record="row"
123
120
  :adminUser="coreStore.adminUser"
@@ -128,7 +125,7 @@
128
125
  <div class="flex text-lightPrimary dark:text-darkPrimary items-center">
129
126
  <Tooltip>
130
127
  <RouterLink
131
- v-if="resource.options?.allowedActions.show"
128
+ v-if="resource.options?.allowedActions?.show"
132
129
  :to="{
133
130
  name: 'resource-show',
134
131
  params: {
@@ -148,7 +145,7 @@
148
145
 
149
146
  <Tooltip>
150
147
  <RouterLink
151
- v-if="resource.options?.allowedActions.edit"
148
+ v-if="resource.options?.allowedActions?.edit"
152
149
  :to="{
153
150
  name: 'resource-edit',
154
151
  params: {
@@ -166,7 +163,7 @@
166
163
 
167
164
  <Tooltip>
168
165
  <button
169
- v-if="resource.options?.allowedActions.delete"
166
+ v-if="resource.options?.allowedActions?.delete"
170
167
  @click="deleteRecord(row)"
171
168
  >
172
169
  <IconTrashBinSolid class="w-5 h-5 me-2"/>
@@ -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" >
@@ -328,8 +325,9 @@ import {
328
325
  } from '@iconify-prerendered/vue-flowbite';
329
326
  import router from '@/router';
330
327
  import { Tooltip } from '@/afcl';
331
- import type { AdminForthResourceCommon } from '@/types/Common';
328
+ import type { AdminForthResourceCommon, AdminForthResourceColumnCommon } 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();
@@ -361,7 +359,7 @@ const emits = defineEmits([
361
359
  const checkboxesInternal: Ref<any[]> = ref([]);
362
360
  const pageInput = ref('1');
363
361
  const page = ref(1);
364
- const sort = ref([]);
362
+ const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
365
363
 
366
364
 
367
365
  const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
@@ -370,11 +368,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
370
368
  watch(() => page.value, (newPage) => {
371
369
  emits('update:page', newPage);
372
370
  });
373
- async function onPageKeydown(event) {
371
+ async function onPageKeydown(event: any) {
374
372
  // page input should accept only numbers, arrow keys and backspace
375
373
  if (['Enter', 'Space'].includes(event.code) ||
376
374
  (!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
377
- && isNaN(String.fromCharCode(event.keyCode)))) {
375
+ && isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
378
376
  event.preventDefault();
379
377
  if (event.code === 'Enter') {
380
378
  validatePageInput();
@@ -395,7 +393,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
395
393
  checkboxesInternal.value = newCheckboxes;
396
394
  });
397
395
 
398
- watch(() => props.sort, (newSort) => {
396
+ watch(() => props.sort, (newSort: any) => {
399
397
  sort.value = newSort;
400
398
  });
401
399
 
@@ -406,17 +404,17 @@ watch(() => props.page, (newPage) => {
406
404
  page.value = newPage;
407
405
  });
408
406
 
409
- const rowRefs = useTemplateRef('rowRefs');
410
- const headerRefs = useTemplateRef('headerRefs');
411
- const rowHeights = ref([]);
412
- const columnWidths = ref([]);
407
+ const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
408
+ const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
409
+ const rowHeights = ref<number[]>([]);
410
+ const columnWidths = ref<number[]>([]);
413
411
  watch(() => props.rows, (newRows) => {
414
412
  // rows are set to null when new records are loading
415
- rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el) => el.offsetHeight);
416
- columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el) => el.offsetWidth)];
413
+ rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
414
+ columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
417
415
  });
418
416
 
419
- function addToCheckedValues(id) {
417
+ function addToCheckedValues(id: any) {
420
418
  if (checkboxesInternal.value.includes(id)) {
421
419
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
422
420
  } else {
@@ -425,17 +423,17 @@ function addToCheckedValues(id) {
425
423
  checkboxesInternal.value = [ ...checkboxesInternal.value ]
426
424
  }
427
425
 
428
- const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn.list));
426
+ const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
429
427
 
430
- async function selectAll(value) {
428
+ async function selectAll() {
431
429
  if (!allFromThisPageChecked.value) {
432
- props.rows.forEach((r) => {
430
+ props.rows?.forEach((r) => {
433
431
  if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
434
432
  checkboxesInternal.value.push(r._primaryKeyValue)
435
433
  }
436
434
  });
437
435
  } else {
438
- props.rows.forEach((r) => {
436
+ props.rows?.forEach((r) => {
439
437
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
440
438
  });
441
439
  }
@@ -448,15 +446,15 @@ const allFromThisPageChecked = computed(() => {
448
446
  if (!props.rows || !props.rows.length) return false;
449
447
  return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
450
448
  });
451
- const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
452
- const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
449
+ const ascArr = computed(() => sort.value.filter((s: any) => s.direction === 'asc').map((s: any) => s.field));
450
+ const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
453
451
 
454
452
 
455
- function onSortButtonClick(event, field) {
453
+ function onSortButtonClick(event: any, field: any) {
456
454
  // if ctrl key is pressed, add to sort otherwise sort by this field
457
455
  // in any case if field is already in sort, toggle direction
458
456
 
459
- const sortIndex = sort.value.findIndex((s) => s.field === field);
457
+ const sortIndex = sort.value.findIndex((s: any) => s.field === field);
460
458
  if (sortIndex === -1) {
461
459
  // field is not in sort, add it
462
460
  if (event.ctrlKey) {
@@ -477,11 +475,11 @@ function onSortButtonClick(event, field) {
477
475
 
478
476
  const clickTarget = ref(null);
479
477
 
480
- async function onClick(e,row) {
478
+ async function onClick(e: any,row: any) {
481
479
  if(clickTarget.value === e.target) return;
482
480
  clickTarget.value = e.target;
483
481
  await new Promise((resolve) => setTimeout(resolve, 100));
484
- if (window.getSelection().toString()) return;
482
+ if (window.getSelection()?.toString()) return;
485
483
  else {
486
484
  if (row._clickUrl === null) {
487
485
  // user asked to nothing on click
@@ -496,7 +494,7 @@ async function onClick(e,row) {
496
494
  router.resolve({
497
495
  name: 'resource-show',
498
496
  params: {
499
- resourceId: props.resource.resourceId,
497
+ resourceId: props.resource?.resourceId,
500
498
  primaryKey: row._primaryKeyValue,
501
499
  },
502
500
  }).href,
@@ -514,7 +512,7 @@ async function onClick(e,row) {
514
512
  router.push({
515
513
  name: 'resource-show',
516
514
  params: {
517
- resourceId: props.resource.resourceId,
515
+ resourceId: props.resource?.resourceId,
518
516
  primaryKey: row._primaryKeyValue,
519
517
  },
520
518
  });
@@ -523,7 +521,7 @@ async function onClick(e,row) {
523
521
  }
524
522
  }
525
523
 
526
- async function deleteRecord(row) {
524
+ async function deleteRecord(row: any) {
527
525
  const data = await adminforth.confirm({
528
526
  message: t('Are you sure you want to delete this item?'),
529
527
  yes: t('Delete'),
@@ -535,7 +533,7 @@ async function deleteRecord(row) {
535
533
  path: '/delete_record',
536
534
  method: 'POST',
537
535
  body: {
538
- resourceId: props.resource.resourceId,
536
+ resourceId: props.resource?.resourceId,
539
537
  primaryKey: row._primaryKeyValue,
540
538
  }
541
539
  });
@@ -553,16 +551,16 @@ async function deleteRecord(row) {
553
551
  }
554
552
  }
555
553
 
556
- const actionLoadingStates = ref({});
554
+ const actionLoadingStates = ref<Record<string | number, boolean>>({});
557
555
 
558
- async function startCustomAction(actionId, row) {
556
+ async function startCustomAction(actionId: string, row: any) {
559
557
  actionLoadingStates.value[actionId] = true;
560
558
 
561
559
  const data = await callAdminForthApi({
562
560
  path: '/start_custom_action',
563
561
  method: 'POST',
564
562
  body: {
565
- resourceId: props.resource.resourceId,
563
+ resourceId: props.resource?.resourceId,
566
564
  actionId: actionId,
567
565
  recordId: row._primaryKeyValue
568
566
  }
@@ -600,7 +598,7 @@ async function startCustomAction(actionId, row) {
600
598
  }
601
599
  }
602
600
 
603
- function onPageInput(event) {
601
+ function onPageInput(event: any) {
604
602
  pageInput.value = event.target.innerText;
605
603
  }
606
604
 
@@ -1,10 +1,12 @@
1
1
  <template>
2
- <div class="overflow-x-auto rounded-default shadow-resourseFormShadow dark:shadow-darkResourseFormShadow">
3
- <div v-if="groupName && !noTitle" class="text-md font-semibold px-6 py-3 flex flex-1 items-center dark:border-gray-600 text-gray-700 bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400 rounded-t-lg">
2
+ <div class="overflow-x-auto shadow-resourseFormShadow dark:shadow-darkResourseFormShadow"
3
+ :class="{'rounded-default' : isRounded}"
4
+ >
5
+ <div v-if="groupName && !noTitle" class="text-md font-semibold px-6 py-3 flex flex-1 items-center text-lightShowTableHeadingText bg-lightShowTableHeadingBackground dark:bg-darkShowTableHeadingBackground dark:text-darkShowTableHeadingText rounded-t-lg">
4
6
  {{ groupName }}
5
7
  </div>
6
- <table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 table-fixed">
7
- <thead v-if="!allColumnsHaveCustomComponent" class="text-gray-700 dark:text-gray-400 bg-lightFormHeading dark:bg-gray-700 block md:table-row-group">
8
+ <table class="w-full text-sm text-left rtl:text-right text-lightShowTableBodyText dark:text-darkShowTableBodyText table-fixed">
9
+ <thead v-if="!allColumnsHaveCustomComponent" class="text-lightShowTableUnderHeadingText dark:text-darkShowTableUnderHeadingText bg-lightShowTableUnderHeadingBackground dark:bg-darkShowTableUnderHeadingBackground dark:border-darkFormBorder block md:table-row-group">
8
10
  <tr>
9
11
  <th scope="col" class="px-6 py-3 text-xs uppercase hidden md:w-52 md:table-cell">
10
12
  {{ $t('Field') }}
@@ -18,8 +20,8 @@
18
20
  <tr
19
21
  v-for="column in columns"
20
22
  :key="column.name"
21
- class="bg-lightForm border-t border-gray-100
22
- dark:bg-gray-800 dark:border-gray-700 block md:table-row"
23
+ class="bg-lightShowTablesBodyBackground border-t border-lightShowTableBodyBorder
24
+ dark:bg-darkShowTablesBodyBackground dark:border-darkShowTableBodyBorder block md:table-row"
23
25
  >
24
26
  <component
25
27
  v-if="column.components?.showRow"
@@ -60,10 +62,11 @@
60
62
  import { getCustomComponent } from '@/utils';
61
63
  import { useCoreStore } from '@/stores/core';
62
64
  import { computed } from 'vue';
63
- const props = defineProps<{
65
+ import type { AdminForthResourceCommon } from '@/types/Common';
66
+ const props = withDefaults(defineProps<{
64
67
  columns: Array<{
65
68
  name: string;
66
- label: string;
69
+ label?: string;
67
70
  components?: {
68
71
  show?: {
69
72
  file: string;
@@ -75,13 +78,15 @@
75
78
  };
76
79
  };
77
80
  }>;
78
- source: string;
79
81
  groupName?: string | null;
80
82
  noTitle?: boolean;
81
- resource: Record<string, any>;
83
+ resource: AdminForthResourceCommon | null;
82
84
  record: Record<string, any>;
83
- }>();
84
-
85
+ isRounded?: boolean;
86
+ }>(), {
87
+ isRounded: true
88
+ });
89
+
85
90
  const coreStore = useCoreStore();
86
91
  const allColumnsHaveCustomComponent = computed(() => {
87
92
  return props.columns.every(column => {