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
@@ -2,34 +2,34 @@
2
2
  <!-- table -->
3
3
  <div class="relative shadow-listTableShadow dark:shadow-darkListTableShadow overflow-auto "
4
4
  :class="{'rounded-default': !noRoundings}"
5
- :style="`height: ${containerHeight}px; will-change: transform;`"
5
+ :style="{ maxHeight: `${containerHeight}px` }"
6
6
  @scroll="handleScroll"
7
7
  ref="containerRef"
8
- >
8
+ >
9
9
  <!-- skelet loader -->
10
10
  <div role="status" v-if="!resource || !resource.columns"
11
11
  class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
12
12
 
13
13
  <div role="status" class="max-w-sm animate-pulse">
14
- <div class="h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[360px]"></div>
14
+ <div class="h-2 bg-lightListSkeletLoader rounded-full dark:bg-darkListSkeletLoader max-w-[360px]"></div>
15
15
  </div>
16
16
  </div>
17
- <table v-else class=" w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
17
+ <table v-else class="w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
18
18
 
19
19
  <tbody>
20
20
  <!-- table header -->
21
- <tr class="t-header sticky z-10 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
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>
21
+ <tr class="t-header sticky z-20 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
22
+ <td scope="col" class="p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
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
- <td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
32
+ <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}">
33
33
 
34
34
  <div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
35
35
  class="flex items-center " :class="{'cursor-pointer':c.sortable}">
@@ -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,7 +68,7 @@
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"
@@ -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 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading" @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
- <td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
113
+ <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}">
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"/>
@@ -185,17 +182,42 @@
185
182
  :resource="coreStore.resource"
186
183
  :adminUser="coreStore.adminUser"
187
184
  :record="row"
185
+ :updateRecords="()=>emits('update:records', true)"
188
186
  />
189
187
  </template>
190
188
 
191
- <template v-if="resource.options?.actions">
192
- <Tooltip v-for="action in resource.options.actions.filter(a => a.showIn?.list)" :key="action.id">
193
- <button
194
- @click="startCustomAction(action.id, row)"
189
+ <template v-if="resource.options?.actions">
190
+ <Tooltip
191
+ v-for="action in resource.options.actions.filter(a => a.showIn?.list)"
192
+ :key="action.id"
193
+ >
194
+ <CallActionWrapper
195
+ :disabled="rowActionLoadingStates?.[action.id]"
196
+ @callAction="startCustomAction(action.id, row)"
195
197
  >
196
- <component v-if="action.icon" :is="getIcon(action.icon)" class="w-5 h-5 mr-2 text-lightPrimary dark:text-darkPrimary"></component>
197
- </button>
198
- <template v-slot:tooltip>
198
+ <component
199
+ :is="action.customComponent ? getCustomComponent(action.customComponent) : 'span'"
200
+ :meta="action.customComponent?.meta"
201
+ :row="row"
202
+ :resource="resource"
203
+ :adminUser="adminUser"
204
+ @callAction="(payload? : Object) => startCustomAction(action.id, payload ?? row)"
205
+ >
206
+ <button
207
+ type="button"
208
+ :disabled="rowActionLoadingStates?.[action.id]"
209
+ @click.stop.prevent
210
+ >
211
+ <component
212
+ v-if="action.icon"
213
+ :is="getIcon(action.icon)"
214
+ class="w-5 h-5 mr-2 text-lightPrimary dark:text-darkPrimary"
215
+ />
216
+ </button>
217
+ </component>
218
+ </CallActionWrapper>
219
+
220
+ <template #tooltip>
199
221
  {{ action.name }}
200
222
  </template>
201
223
  </Tooltip>
@@ -223,7 +245,7 @@
223
245
  >
224
246
  <!-- Buttons -->
225
247
  <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"
248
+ 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
249
  @click="page--; pageInput = page.toString();" :disabled="page <= 1">
228
250
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
229
251
  viewBox="0 0 14 10">
@@ -235,14 +257,14 @@
235
257
  </span>
236
258
  </button>
237
259
  <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"
260
+ 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
261
  @click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
240
262
  <!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
241
263
  1
242
264
  </button>
243
265
  <div
244
266
  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"
267
+ 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
268
  @keydown="onPageKeydown($event)"
247
269
  @input="onPageInput($event)"
248
270
  @blur="validatePageInput()"
@@ -251,14 +273,14 @@
251
273
  </div>
252
274
 
253
275
  <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"
276
+ 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
277
  @click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
256
278
  {{ totalPages }}
257
279
 
258
280
  <!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
259
281
  </button>
260
282
  <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"
283
+ 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
284
  @click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
263
285
  <span class="hidden sm:inline">{{ $t('Next') }}</span>
264
286
  <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 +292,9 @@
270
292
  </div>
271
293
 
272
294
  <!-- 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>
295
+ <span class="ml-4 text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
296
+ <span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
297
+ <template v-else-if="resource && totalRows > 0">
276
298
 
277
299
  <span class="hidden sm:inline">
278
300
  <i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
@@ -328,8 +350,9 @@ import {
328
350
  } from '@iconify-prerendered/vue-flowbite';
329
351
  import router from '@/router';
330
352
  import { Tooltip } from '@/afcl';
331
- import type { AdminForthResourceCommon } from '@/types/Common';
353
+ import type { AdminForthResourceCommon, AdminForthResourceColumnCommon } from '@/types/Common';
332
354
  import adminforth from '@/adminforth';
355
+ import Checkbox from '@/afcl/Checkbox.vue';
333
356
 
334
357
  const coreStore = useCoreStore();
335
358
  const { t } = useI18n();
@@ -361,7 +384,7 @@ const emits = defineEmits([
361
384
  const checkboxesInternal: Ref<any[]> = ref([]);
362
385
  const pageInput = ref('1');
363
386
  const page = ref(1);
364
- const sort = ref([]);
387
+ const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
365
388
 
366
389
 
367
390
  const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
@@ -370,11 +393,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
370
393
  watch(() => page.value, (newPage) => {
371
394
  emits('update:page', newPage);
372
395
  });
373
- async function onPageKeydown(event) {
396
+ async function onPageKeydown(event: any) {
374
397
  // page input should accept only numbers, arrow keys and backspace
375
398
  if (['Enter', 'Space'].includes(event.code) ||
376
399
  (!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
377
- && isNaN(String.fromCharCode(event.keyCode)))) {
400
+ && isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
378
401
  event.preventDefault();
379
402
  if (event.code === 'Enter') {
380
403
  validatePageInput();
@@ -395,7 +418,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
395
418
  checkboxesInternal.value = newCheckboxes;
396
419
  });
397
420
 
398
- watch(() => props.sort, (newSort) => {
421
+ watch(() => props.sort, (newSort: any) => {
399
422
  sort.value = newSort;
400
423
  });
401
424
 
@@ -406,17 +429,17 @@ watch(() => props.page, (newPage) => {
406
429
  page.value = newPage;
407
430
  });
408
431
 
409
- const rowRefs = useTemplateRef('rowRefs');
410
- const headerRefs = useTemplateRef('headerRefs');
411
- const rowHeights = ref([]);
412
- const columnWidths = ref([]);
432
+ const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
433
+ const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
434
+ const rowHeights = ref<number[]>([]);
435
+ const columnWidths = ref<number[]>([]);
413
436
  watch(() => props.rows, (newRows) => {
414
437
  // 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)];
438
+ rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
439
+ columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
417
440
  });
418
441
 
419
- function addToCheckedValues(id) {
442
+ function addToCheckedValues(id: any) {
420
443
  if (checkboxesInternal.value.includes(id)) {
421
444
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
422
445
  } else {
@@ -425,17 +448,17 @@ function addToCheckedValues(id) {
425
448
  checkboxesInternal.value = [ ...checkboxesInternal.value ]
426
449
  }
427
450
 
428
- const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn.list));
451
+ const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
429
452
 
430
- async function selectAll(value) {
453
+ async function selectAll() {
431
454
  if (!allFromThisPageChecked.value) {
432
- props.rows.forEach((r) => {
455
+ props.rows?.forEach((r) => {
433
456
  if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
434
457
  checkboxesInternal.value.push(r._primaryKeyValue)
435
458
  }
436
459
  });
437
460
  } else {
438
- props.rows.forEach((r) => {
461
+ props.rows?.forEach((r) => {
439
462
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
440
463
  });
441
464
  }
@@ -448,15 +471,15 @@ const allFromThisPageChecked = computed(() => {
448
471
  if (!props.rows || !props.rows.length) return false;
449
472
  return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
450
473
  });
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));
474
+ const ascArr = computed(() => sort.value.filter((s: any) => s.direction === 'asc').map((s: any) => s.field));
475
+ const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
453
476
 
454
477
 
455
- function onSortButtonClick(event, field) {
478
+ function onSortButtonClick(event: any, field: any) {
456
479
  // if ctrl key is pressed, add to sort otherwise sort by this field
457
480
  // in any case if field is already in sort, toggle direction
458
481
 
459
- const sortIndex = sort.value.findIndex((s) => s.field === field);
482
+ const sortIndex = sort.value.findIndex((s: any) => s.field === field);
460
483
  if (sortIndex === -1) {
461
484
  // field is not in sort, add it
462
485
  if (event.ctrlKey) {
@@ -477,11 +500,11 @@ function onSortButtonClick(event, field) {
477
500
 
478
501
  const clickTarget = ref(null);
479
502
 
480
- async function onClick(e,row) {
503
+ async function onClick(e: any,row: any) {
481
504
  if(clickTarget.value === e.target) return;
482
505
  clickTarget.value = e.target;
483
506
  await new Promise((resolve) => setTimeout(resolve, 100));
484
- if (window.getSelection().toString()) return;
507
+ if (window.getSelection()?.toString()) return;
485
508
  else {
486
509
  if (row._clickUrl === null) {
487
510
  // user asked to nothing on click
@@ -496,7 +519,7 @@ async function onClick(e,row) {
496
519
  router.resolve({
497
520
  name: 'resource-show',
498
521
  params: {
499
- resourceId: props.resource.resourceId,
522
+ resourceId: props.resource?.resourceId,
500
523
  primaryKey: row._primaryKeyValue,
501
524
  },
502
525
  }).href,
@@ -514,7 +537,7 @@ async function onClick(e,row) {
514
537
  router.push({
515
538
  name: 'resource-show',
516
539
  params: {
517
- resourceId: props.resource.resourceId,
540
+ resourceId: props.resource?.resourceId,
518
541
  primaryKey: row._primaryKeyValue,
519
542
  },
520
543
  });
@@ -523,7 +546,7 @@ async function onClick(e,row) {
523
546
  }
524
547
  }
525
548
 
526
- async function deleteRecord(row) {
549
+ async function deleteRecord(row: any) {
527
550
  const data = await adminforth.confirm({
528
551
  message: t('Are you sure you want to delete this item?'),
529
552
  yes: t('Delete'),
@@ -535,7 +558,7 @@ async function deleteRecord(row) {
535
558
  path: '/delete_record',
536
559
  method: 'POST',
537
560
  body: {
538
- resourceId: props.resource.resourceId,
561
+ resourceId: props.resource?.resourceId,
539
562
  primaryKey: row._primaryKeyValue,
540
563
  }
541
564
  });
@@ -553,16 +576,16 @@ async function deleteRecord(row) {
553
576
  }
554
577
  }
555
578
 
556
- const actionLoadingStates = ref({});
579
+ const actionLoadingStates = ref<Record<string | number, boolean>>({});
557
580
 
558
- async function startCustomAction(actionId, row) {
581
+ async function startCustomAction(actionId: string, row: any) {
559
582
  actionLoadingStates.value[actionId] = true;
560
583
 
561
584
  const data = await callAdminForthApi({
562
585
  path: '/start_custom_action',
563
586
  method: 'POST',
564
587
  body: {
565
- resourceId: props.resource.resourceId,
588
+ resourceId: props.resource?.resourceId,
566
589
  actionId: actionId,
567
590
  recordId: row._primaryKeyValue
568
591
  }
@@ -600,7 +623,7 @@ async function startCustomAction(actionId, row) {
600
623
  }
601
624
  }
602
625
 
603
- function onPageInput(event) {
626
+ function onPageInput(event: any) {
604
627
  pageInput.value = event.target.innerText;
605
628
  }
606
629
 
@@ -735,4 +758,15 @@ input[type="checkbox"][disabled] {
735
758
  input[type="checkbox"]:not([disabled]) {
736
759
  @apply cursor-pointer;
737
760
  }
761
+ td.sticky-column {
762
+ @apply sticky left-0 z-10;
763
+ &:not(:first-child) {
764
+ @apply left-[56px];
765
+ }
766
+ }
767
+ tr:not(:first-child):hover {
768
+ td.sticky-column {
769
+ @apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
770
+ }
771
+ }
738
772
  </style>
@@ -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,18 +20,18 @@
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
- v-if="column.components?.showRow"
27
+ v-if="column.components?.showRow && checkShowIf(column, record)"
26
28
  :is="getCustomComponent(column.components.showRow)"
27
29
  :meta="column.components.showRow.meta"
28
30
  :column="column"
29
31
  :resource="coreStore.resource"
30
32
  :record="coreStore.record"
31
33
  />
32
- <template v-else>
34
+ <template v-else-if="checkShowIf(column, record)">
33
35
  <td class="px-6 py-4 relative block md:table-cell font-bold md:font-normal pb-0 md:pb-4">
34
36
  {{ column.label }}
35
37
  </td>
@@ -57,13 +59,15 @@
57
59
 
58
60
  <script setup lang="ts">
59
61
  import ValueRenderer from '@/components/ValueRenderer.vue';
60
- import { getCustomComponent } from '@/utils';
62
+ import { getCustomComponent, checkShowIf } from '@/utils';
61
63
  import { useCoreStore } from '@/stores/core';
62
64
  import { computed } from 'vue';
63
- const props = defineProps<{
65
+ import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon } from '@/types/Common';
66
+ const props = withDefaults(defineProps<{
64
67
  columns: Array<{
65
68
  name: string;
66
- label: string;
69
+ label?: string;
70
+ showIf?: AdminForthResourceColumnInputCommon['showIf'];
67
71
  components?: {
68
72
  show?: {
69
73
  file: string;
@@ -75,13 +79,15 @@
75
79
  };
76
80
  };
77
81
  }>;
78
- source: string;
79
82
  groupName?: string | null;
80
83
  noTitle?: boolean;
81
- resource: Record<string, any>;
84
+ resource: AdminForthResourceCommon | null;
82
85
  record: Record<string, any>;
83
- }>();
84
-
86
+ isRounded?: boolean;
87
+ }>(), {
88
+ isRounded: true
89
+ });
90
+
85
91
  const coreStore = useCoreStore();
86
92
  const allColumnsHaveCustomComponent = computed(() => {
87
93
  return props.columns.every(column => {