adminforth 2.4.0-next.31 → 2.4.0-next.310

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 (176) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/createApp/templates/api.ts.hbs +10 -0
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +12 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
  7. package/commands/createApp/templates/schema.prisma.hbs +0 -1
  8. package/commands/createApp/utils.js +10 -0
  9. package/commands/createCustomComponent/configLoader.js +17 -4
  10. package/commands/createCustomComponent/main.js +13 -7
  11. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  12. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
  13. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  14. package/commands/createPlugin/templates/package.json.hbs +1 -1
  15. package/commands/generateModels.js +30 -22
  16. package/dist/auth.d.ts +9 -1
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +21 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/dataConnectors/baseConnector.d.ts +1 -1
  21. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  22. package/dist/dataConnectors/baseConnector.js +69 -17
  23. package/dist/dataConnectors/baseConnector.js.map +1 -1
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -0
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  28. package/dist/dataConnectors/mongo.js +50 -15
  29. package/dist/dataConnectors/mongo.js.map +1 -1
  30. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  31. package/dist/dataConnectors/mysql.js +11 -0
  32. package/dist/dataConnectors/mysql.js.map +1 -1
  33. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  34. package/dist/dataConnectors/postgres.js +43 -14
  35. package/dist/dataConnectors/postgres.js.map +1 -1
  36. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  37. package/dist/dataConnectors/sqlite.js +11 -0
  38. package/dist/dataConnectors/sqlite.js.map +1 -1
  39. package/dist/index.d.ts +12 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +45 -22
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/codeInjector.d.ts +2 -0
  44. package/dist/modules/codeInjector.d.ts.map +1 -1
  45. package/dist/modules/codeInjector.js +62 -6
  46. package/dist/modules/codeInjector.js.map +1 -1
  47. package/dist/modules/configValidator.d.ts +6 -0
  48. package/dist/modules/configValidator.d.ts.map +1 -1
  49. package/dist/modules/configValidator.js +202 -25
  50. package/dist/modules/configValidator.js.map +1 -1
  51. package/dist/modules/restApi.d.ts +1 -1
  52. package/dist/modules/restApi.d.ts.map +1 -1
  53. package/dist/modules/restApi.js +172 -31
  54. package/dist/modules/restApi.js.map +1 -1
  55. package/dist/modules/styles.d.ts +499 -13
  56. package/dist/modules/styles.d.ts.map +1 -1
  57. package/dist/modules/styles.js +555 -31
  58. package/dist/modules/styles.js.map +1 -1
  59. package/dist/modules/utils.d.ts +7 -15
  60. package/dist/modules/utils.d.ts.map +1 -1
  61. package/dist/modules/utils.js +45 -68
  62. package/dist/modules/utils.js.map +1 -1
  63. package/dist/servers/express.d.ts +5 -0
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +40 -1
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/index.html +1 -1
  68. package/dist/spa/package-lock.json +1208 -708
  69. package/dist/spa/package.json +34 -34
  70. package/dist/spa/src/App.vue +59 -174
  71. package/dist/spa/src/adminforth.ts +42 -18
  72. package/dist/spa/src/afcl/AreaChart.vue +0 -1
  73. package/dist/spa/src/afcl/BarChart.vue +2 -2
  74. package/dist/spa/src/afcl/Button.vue +6 -6
  75. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  76. package/dist/spa/src/afcl/Card.vue +25 -0
  77. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  78. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  79. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  80. package/dist/spa/src/afcl/Dialog.vue +47 -27
  81. package/dist/spa/src/afcl/Dropzone.vue +127 -48
  82. package/dist/spa/src/afcl/Input.vue +14 -6
  83. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  84. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  85. package/dist/spa/src/afcl/PieChart.vue +5 -5
  86. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  87. package/dist/spa/src/afcl/Select.vue +82 -34
  88. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  89. package/dist/spa/src/afcl/Table.vue +315 -73
  90. package/dist/spa/src/afcl/Textarea.vue +31 -0
  91. package/dist/spa/src/afcl/Toggle.vue +32 -0
  92. package/dist/spa/src/afcl/Tooltip.vue +28 -18
  93. package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
  94. package/dist/spa/src/afcl/index.ts +6 -3
  95. package/dist/spa/src/components/AcceptModal.vue +48 -14
  96. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  97. package/dist/spa/src/components/CallActionWrapper.vue +15 -0
  98. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  99. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  100. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  101. package/dist/spa/src/components/CustomRangePicker.vue +37 -21
  102. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  103. package/dist/spa/src/components/Filters.vue +195 -132
  104. package/dist/spa/src/components/GroupsTable.vue +9 -8
  105. package/dist/spa/src/components/MenuLink.vue +90 -23
  106. package/dist/spa/src/components/ResourceForm.vue +94 -51
  107. package/dist/spa/src/components/ResourceListTable.vue +115 -85
  108. package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
  109. package/dist/spa/src/components/ShowTable.vue +21 -15
  110. package/dist/spa/src/components/Sidebar.vue +470 -0
  111. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  112. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  113. package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
  114. package/dist/spa/src/components/Toast.vue +40 -29
  115. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  116. package/dist/spa/src/components/ValueRenderer.vue +44 -17
  117. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  118. package/dist/spa/src/i18n.ts +5 -3
  119. package/dist/spa/src/main.ts +1 -1
  120. package/dist/spa/src/renderers/CompactField.vue +1 -1
  121. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  122. package/dist/spa/src/router/index.ts +8 -0
  123. package/dist/spa/src/shims-vue.d.ts +5 -0
  124. package/dist/spa/src/spa_types/core.ts +13 -1
  125. package/dist/spa/src/stores/core.ts +13 -1
  126. package/dist/spa/src/stores/filters.ts +33 -2
  127. package/dist/spa/src/stores/modal.ts +6 -1
  128. package/dist/spa/src/stores/toast.ts +22 -3
  129. package/dist/spa/src/types/Back.ts +163 -23
  130. package/dist/spa/src/types/Common.ts +91 -32
  131. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  132. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  133. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
  134. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  135. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  136. package/dist/spa/src/types/adapters/index.ts +8 -0
  137. package/dist/spa/src/utils.ts +291 -11
  138. package/dist/spa/src/views/CreateView.vue +63 -21
  139. package/dist/spa/src/views/EditView.vue +55 -22
  140. package/dist/spa/src/views/ListView.vue +144 -87
  141. package/dist/spa/src/views/LoginView.vue +26 -35
  142. package/dist/spa/src/views/ResourceParent.vue +2 -2
  143. package/dist/spa/src/views/SettingsView.vue +121 -0
  144. package/dist/spa/src/views/ShowView.vue +83 -53
  145. package/dist/spa/src/websocket.ts +6 -1
  146. package/dist/spa/tsconfig.app.json +1 -1
  147. package/dist/spa/vite.config.ts +45 -2
  148. package/dist/types/Back.d.ts +146 -14
  149. package/dist/types/Back.d.ts.map +1 -1
  150. package/dist/types/Back.js +15 -0
  151. package/dist/types/Back.js.map +1 -1
  152. package/dist/types/Common.d.ts +106 -29
  153. package/dist/types/Common.d.ts.map +1 -1
  154. package/dist/types/Common.js.map +1 -1
  155. package/dist/types/FrontendAPI.d.ts +31 -3
  156. package/dist/types/FrontendAPI.d.ts.map +1 -1
  157. package/dist/types/FrontendAPI.js.map +1 -1
  158. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  159. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  160. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  161. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  162. package/dist/types/adapters/EmailAdapter.d.ts +1 -1
  163. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  164. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  165. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  166. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  167. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  168. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  169. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  170. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  171. package/dist/types/adapters/index.d.ts +9 -0
  172. package/dist/types/adapters/index.d.ts.map +1 -0
  173. package/dist/types/adapters/index.js +2 -0
  174. package/dist/types/adapters/index.js.map +1 -0
  175. package/package.json +4 -2
  176. package/dist/spa/src/types/adapters/index.js +0 -5
@@ -6,27 +6,26 @@
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">
19
- <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>
17
+ <tr class="t-header sticky z-20 top-0 text-xs text-lightListTableHeadingText bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-darkListTableHeadingText">
18
+ <td scope="col" class="p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
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
- <td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
28
+ <td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
30
29
 
31
30
  <div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
32
31
  class="flex items-center " :class="{'cursor-pointer':c.sortable}">
@@ -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>
106
- <td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
93
+ <td class="w-4 p-4 cursor-default sticky-column bg-lightListTable dark:bg-darkListTable" @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
+
103
+ <td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4" :class="{'sticky-column bg-lightListTable dark:bg-darkListTable': c.listSticky}">
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: {
@@ -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: {
@@ -156,7 +153,7 @@
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
159
  <IconTrashBinSolid class="af-delete-icon w-5 h-5 me-2"/>
@@ -175,22 +172,43 @@
175
172
  :resource="coreStore.resource"
176
173
  :adminUser="coreStore.adminUser"
177
174
  :record="row"
175
+ :updateRecords="()=>emits('update:records', true)"
178
176
  />
179
177
  </template>
180
178
 
181
179
  <template v-if="resource.options?.actions">
182
- <Tooltip v-for="action in resource.options.actions.filter(a => a.showIn?.list)" :key="action.id">
183
- <button
184
- @click="startCustomAction(action.id, row)"
185
- >
186
- <component v-if="action.icon" :is="getIcon(action.icon)" class="w-5 h-5 mr-2 text-lightPrimary dark:text-darkPrimary"></component>
187
- </button>
188
- <template v-slot:tooltip>
180
+ <Tooltip
181
+ v-for="action in resource.options.actions.filter(a => a.showIn?.list)"
182
+ :key="action.id"
183
+ >
184
+ <component
185
+ :is="action.customComponent ? getCustomComponent(action.customComponent) : CallActionWrapper"
186
+ :meta="action.customComponent?.meta"
187
+ :row="row"
188
+ :resource="resource"
189
+ :adminUser="adminUser"
190
+ @callAction="(payload? : Object) => startCustomAction(action.id, payload ?? row)"
191
+ >
192
+ <button
193
+ type="button"
194
+ :disabled="rowActionLoadingStates?.[action.id]"
195
+ @click.stop.prevent
196
+ >
197
+ <component
198
+ v-if="action.icon"
199
+ :is="getIcon(action.icon)"
200
+ class="w-5 h-5 mr-2 text-lightPrimary dark:text-darkPrimary"
201
+ />
202
+ </button>
203
+ </component>
204
+
205
+ <template #tooltip>
189
206
  {{ action.name }}
190
207
  </template>
191
208
  </Tooltip>
192
209
  </template>
193
210
  </div>
211
+
194
212
  </td>
195
213
  </tr>
196
214
  </tbody>
@@ -199,14 +217,14 @@
199
217
  <!-- pagination
200
218
  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
219
  -->
202
- <div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
220
+ <div class="af-pagination-container flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
203
221
 
204
- <div class="inline-flex "
222
+ <div class="af-pagination-buttons-container inline-flex "
205
223
  v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
206
224
  >
207
225
  <!-- Buttons -->
208
226
  <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"
227
+ 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
228
  @click="page--; pageInput = page.toString();" :disabled="page <= 1">
211
229
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
212
230
  viewBox="0 0 14 10">
@@ -218,14 +236,14 @@
218
236
  </span>
219
237
  </button>
220
238
  <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"
239
+ 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
240
  @click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
223
241
  <!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
224
242
  1
225
243
  </button>
226
244
  <div
227
245
  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"
246
+ 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
247
  @keydown="onPageKeydown($event)"
230
248
  @input="onPageInput($event)"
231
249
  @blur="validatePageInput()"
@@ -234,14 +252,14 @@
234
252
  </div>
235
253
 
236
254
  <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"
255
+ 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
256
  @click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
239
257
  {{ totalPages }}
240
258
 
241
259
  <!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
242
260
  </button>
243
261
  <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"
262
+ 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
263
  @click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
246
264
  <span class="hidden sm:inline">{{ $t('Next') }}</span>
247
265
  <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 +271,11 @@
253
271
  </div>
254
272
 
255
273
  <!-- 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>
274
+ <span class="ml-4 text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
275
+ <span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
276
+ <template v-else-if="resource && totalRows > 0">
259
277
 
260
- <span class="hidden sm:inline">
278
+ <span class="af-pagination-info hidden sm:inline">
261
279
  <i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
262
280
  <template v-slot:from>
263
281
  <strong>{{ from }}</strong>
@@ -307,12 +325,13 @@ import {
307
325
  import {
308
326
  IconEyeSolid,
309
327
  IconPenSolid,
310
- IconTrashBinSolid
328
+ IconTrashBinSolid,
311
329
  } from '@iconify-prerendered/vue-flowbite';
312
330
  import router from '@/router';
313
331
  import { Tooltip } from '@/afcl';
314
- import type { AdminForthResourceCommon } from '@/types/Common';
332
+ import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon, AdminForthResourceColumnCommon } from '@/types/Common';
315
333
  import adminforth from '@/adminforth';
334
+ import Checkbox from '@/afcl/Checkbox.vue';
316
335
 
317
336
  const coreStore = useCoreStore();
318
337
  const { t } = useI18n();
@@ -341,7 +360,7 @@ const emits = defineEmits([
341
360
  const checkboxesInternal: Ref<any[]> = ref([]);
342
361
  const pageInput = ref('1');
343
362
  const page = ref(1);
344
- const sort = ref([]);
363
+ const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
345
364
 
346
365
 
347
366
  const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
@@ -350,11 +369,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
350
369
  watch(() => page.value, (newPage) => {
351
370
  emits('update:page', newPage);
352
371
  });
353
- async function onPageKeydown(event) {
372
+ async function onPageKeydown(event: any) {
354
373
  // page input should accept only numbers, arrow keys and backspace
355
374
  if (['Enter', 'Space'].includes(event.code) ||
356
375
  (!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
357
- && isNaN(String.fromCharCode(event.keyCode)))) {
376
+ && isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
358
377
  event.preventDefault();
359
378
  if (event.code === 'Enter') {
360
379
  validatePageInput();
@@ -375,7 +394,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
375
394
  checkboxesInternal.value = newCheckboxes;
376
395
  });
377
396
 
378
- watch(() => props.sort, (newSort) => {
397
+ watch(() => props.sort, (newSort: any) => {
379
398
  sort.value = newSort;
380
399
  });
381
400
 
@@ -386,17 +405,17 @@ watch(() => props.page, (newPage) => {
386
405
  page.value = newPage;
387
406
  });
388
407
 
389
- const rowRefs = useTemplateRef('rowRefs');
390
- const headerRefs = useTemplateRef('headerRefs');
391
- const rowHeights = ref([]);
392
- const columnWidths = ref([]);
408
+ const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
409
+ const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
410
+ const rowHeights = ref<number[]>([]);
411
+ const columnWidths = ref<number[]>([]);
393
412
  watch(() => props.rows, (newRows) => {
394
413
  // 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)];
414
+ rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
415
+ columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
397
416
  });
398
417
 
399
- function addToCheckedValues(id) {
418
+ function addToCheckedValues(id: string) {
400
419
  if (checkboxesInternal.value.includes(id)) {
401
420
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
402
421
  } else {
@@ -405,17 +424,17 @@ function addToCheckedValues(id) {
405
424
  checkboxesInternal.value = [ ...checkboxesInternal.value ]
406
425
  }
407
426
 
408
- const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn.list));
427
+ const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
409
428
 
410
- async function selectAll(value) {
429
+ async function selectAll() {
411
430
  if (!allFromThisPageChecked.value) {
412
- props.rows.forEach((r) => {
431
+ props.rows?.forEach((r) => {
413
432
  if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
414
433
  checkboxesInternal.value.push(r._primaryKeyValue)
415
434
  }
416
435
  });
417
436
  } else {
418
- props.rows.forEach((r) => {
437
+ props.rows?.forEach((r) => {
419
438
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
420
439
  });
421
440
  }
@@ -428,15 +447,15 @@ const allFromThisPageChecked = computed(() => {
428
447
  if (!props.rows || !props.rows.length) return false;
429
448
  return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
430
449
  });
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));
450
+ const ascArr = computed(() => sort.value.filter((s:any) => s.direction === 'asc').map((s: any) => s.field));
451
+ const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
433
452
 
434
453
 
435
- function onSortButtonClick(event, field) {
454
+ function onSortButtonClick(event: any, field: string) {
436
455
  // if ctrl key is pressed, add to sort otherwise sort by this field
437
456
  // in any case if field is already in sort, toggle direction
438
457
 
439
- const sortIndex = sort.value.findIndex((s) => s.field === field);
458
+ const sortIndex = sort.value.findIndex((s: any) => s.field === field);
440
459
  if (sortIndex === -1) {
441
460
  // field is not in sort, add it
442
461
  if (event.ctrlKey) {
@@ -447,9 +466,9 @@ function onSortButtonClick(event, field) {
447
466
  } else {
448
467
  const sortField = sort.value[sortIndex];
449
468
  if (sortField.direction === 'asc') {
450
- sort.value = sort.value.map((s) => s.field === field ? {field, direction: 'desc'} : s);
469
+ sort.value = sort.value.map((s: any) => s.field === field ? {field, direction: 'desc'} : s);
451
470
  } else {
452
- sort.value = sort.value.filter((s) => s.field !== field);
471
+ sort.value = sort.value.filter((s: any) => s.field !== field);
453
472
  }
454
473
  }
455
474
  }
@@ -457,11 +476,11 @@ function onSortButtonClick(event, field) {
457
476
 
458
477
  const clickTarget = ref(null);
459
478
 
460
- async function onClick(e,row) {
479
+ async function onClick(e: any, row: any) {
461
480
  if(clickTarget.value === e.target) return;
462
481
  clickTarget.value = e.target;
463
482
  await new Promise((resolve) => setTimeout(resolve, 100));
464
- if (window.getSelection().toString()) return;
483
+ if (window.getSelection()?.toString()) return;
465
484
  else {
466
485
  if (row._clickUrl === null) {
467
486
  // user asked to nothing on click
@@ -476,7 +495,7 @@ async function onClick(e,row) {
476
495
  router.resolve({
477
496
  name: 'resource-show',
478
497
  params: {
479
- resourceId: props.resource.resourceId,
498
+ resourceId: props.resource?.resourceId,
480
499
  primaryKey: row._primaryKeyValue,
481
500
  },
482
501
  }).href,
@@ -494,7 +513,7 @@ async function onClick(e,row) {
494
513
  router.push({
495
514
  name: 'resource-show',
496
515
  params: {
497
- resourceId: props.resource.resourceId,
516
+ resourceId: props.resource?.resourceId,
498
517
  primaryKey: row._primaryKeyValue,
499
518
  },
500
519
  });
@@ -503,7 +522,7 @@ async function onClick(e,row) {
503
522
  }
504
523
  }
505
524
 
506
- async function deleteRecord(row) {
525
+ async function deleteRecord(row: any) {
507
526
  const data = await adminforth.confirm({
508
527
  message: t('Are you sure you want to delete this item?'),
509
528
  yes: t('Delete'),
@@ -515,7 +534,7 @@ async function deleteRecord(row) {
515
534
  path: '/delete_record',
516
535
  method: 'POST',
517
536
  body: {
518
- resourceId: props.resource.resourceId,
537
+ resourceId: props.resource?.resourceId,
519
538
  primaryKey: row._primaryKeyValue,
520
539
  }
521
540
  });
@@ -533,16 +552,16 @@ async function deleteRecord(row) {
533
552
  }
534
553
  }
535
554
 
536
- const actionLoadingStates = ref({});
555
+ const actionLoadingStates = ref<Record<string | number, boolean>>({});
537
556
 
538
- async function startCustomAction(actionId, row) {
557
+ async function startCustomAction(actionId: string, row: any) {
539
558
  actionLoadingStates.value[actionId] = true;
540
559
 
541
560
  const data = await callAdminForthApi({
542
561
  path: '/start_custom_action',
543
562
  method: 'POST',
544
563
  body: {
545
- resourceId: props.resource.resourceId,
564
+ resourceId: props.resource?.resourceId,
546
565
  actionId: actionId,
547
566
  recordId: row._primaryKeyValue
548
567
  }
@@ -580,7 +599,7 @@ async function startCustomAction(actionId, row) {
580
599
  }
581
600
  }
582
601
 
583
- function onPageInput(event) {
602
+ function onPageInput(event: any) {
584
603
  pageInput.value = event.target.innerText;
585
604
  }
586
605
 
@@ -600,4 +619,15 @@ input[type="checkbox"][disabled] {
600
619
  input[type="checkbox"]:not([disabled]) {
601
620
  @apply cursor-pointer;
602
621
  }
622
+ td.sticky-column {
623
+ @apply sticky left-0 z-10;
624
+ &:not(:first-child) {
625
+ @apply left-[56px];
626
+ }
627
+ }
628
+ tr:not(:first-child):hover {
629
+ td.sticky-column {
630
+ @apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
631
+ }
632
+ }
603
633
  </style>