adminforth 2.4.0-next.26 → 2.4.0-next.261

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 (168) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/cli.js +10 -3
  3. package/commands/createApp/templates/api.ts.hbs +10 -0
  4. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  5. package/commands/createApp/templates/index.ts.hbs +12 -1
  6. package/commands/createApp/utils.js +25 -8
  7. package/commands/createCustomComponent/configLoader.js +17 -4
  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/commands/generateModels.js +30 -22
  13. package/dist/auth.d.ts +9 -1
  14. package/dist/auth.d.ts.map +1 -1
  15. package/dist/auth.js +21 -2
  16. package/dist/auth.js.map +1 -1
  17. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  18. package/dist/dataConnectors/baseConnector.js +46 -15
  19. package/dist/dataConnectors/baseConnector.js.map +1 -1
  20. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  21. package/dist/dataConnectors/clickhouse.js +15 -0
  22. package/dist/dataConnectors/clickhouse.js.map +1 -1
  23. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  24. package/dist/dataConnectors/mongo.js +50 -15
  25. package/dist/dataConnectors/mongo.js.map +1 -1
  26. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  27. package/dist/dataConnectors/mysql.js +11 -0
  28. package/dist/dataConnectors/mysql.js.map +1 -1
  29. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  30. package/dist/dataConnectors/postgres.js +43 -14
  31. package/dist/dataConnectors/postgres.js.map +1 -1
  32. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  33. package/dist/dataConnectors/sqlite.js +11 -0
  34. package/dist/dataConnectors/sqlite.js.map +1 -1
  35. package/dist/index.d.ts +2 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +23 -9
  38. package/dist/index.js.map +1 -1
  39. package/dist/modules/codeInjector.d.ts +2 -0
  40. package/dist/modules/codeInjector.d.ts.map +1 -1
  41. package/dist/modules/codeInjector.js +50 -6
  42. package/dist/modules/codeInjector.js.map +1 -1
  43. package/dist/modules/configValidator.d.ts +6 -0
  44. package/dist/modules/configValidator.d.ts.map +1 -1
  45. package/dist/modules/configValidator.js +184 -19
  46. package/dist/modules/configValidator.js.map +1 -1
  47. package/dist/modules/restApi.d.ts.map +1 -1
  48. package/dist/modules/restApi.js +164 -26
  49. package/dist/modules/restApi.js.map +1 -1
  50. package/dist/modules/styles.d.ts +499 -13
  51. package/dist/modules/styles.d.ts.map +1 -1
  52. package/dist/modules/styles.js +555 -31
  53. package/dist/modules/styles.js.map +1 -1
  54. package/dist/modules/utils.d.ts +7 -15
  55. package/dist/modules/utils.d.ts.map +1 -1
  56. package/dist/modules/utils.js +45 -68
  57. package/dist/modules/utils.js.map +1 -1
  58. package/dist/servers/express.d.ts +5 -0
  59. package/dist/servers/express.d.ts.map +1 -1
  60. package/dist/servers/express.js +40 -1
  61. package/dist/servers/express.js.map +1 -1
  62. package/dist/spa/index.html +1 -1
  63. package/dist/spa/package-lock.json +5 -4
  64. package/dist/spa/package.json +1 -1
  65. package/dist/spa/src/App.vue +58 -173
  66. package/dist/spa/src/adminforth.ts +42 -18
  67. package/dist/spa/src/afcl/BarChart.vue +2 -2
  68. package/dist/spa/src/afcl/Button.vue +6 -6
  69. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  70. package/dist/spa/src/afcl/Card.vue +25 -0
  71. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  72. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  73. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  74. package/dist/spa/src/afcl/Dialog.vue +47 -27
  75. package/dist/spa/src/afcl/Dropzone.vue +12 -12
  76. package/dist/spa/src/afcl/Input.vue +5 -5
  77. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  78. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  79. package/dist/spa/src/afcl/PieChart.vue +5 -5
  80. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  81. package/dist/spa/src/afcl/Select.vue +68 -34
  82. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  83. package/dist/spa/src/afcl/Table.vue +213 -74
  84. package/dist/spa/src/afcl/Textarea.vue +31 -0
  85. package/dist/spa/src/afcl/Toggle.vue +32 -0
  86. package/dist/spa/src/afcl/Tooltip.vue +26 -18
  87. package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
  88. package/dist/spa/src/afcl/index.ts +6 -3
  89. package/dist/spa/src/components/AcceptModal.vue +48 -14
  90. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  91. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  92. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  93. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  94. package/dist/spa/src/components/CustomRangePicker.vue +37 -8
  95. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  96. package/dist/spa/src/components/Filters.vue +85 -39
  97. package/dist/spa/src/components/GroupsTable.vue +9 -8
  98. package/dist/spa/src/components/MenuLink.vue +90 -23
  99. package/dist/spa/src/components/ResourceForm.vue +94 -51
  100. package/dist/spa/src/components/ResourceListTable.vue +90 -80
  101. package/dist/spa/src/components/ResourceListTableVirtual.vue +86 -76
  102. package/dist/spa/src/components/ShowTable.vue +21 -15
  103. package/dist/spa/src/components/Sidebar.vue +470 -0
  104. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  105. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  106. package/dist/spa/src/components/ThreeDotsMenu.vue +73 -14
  107. package/dist/spa/src/components/Toast.vue +27 -9
  108. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  109. package/dist/spa/src/components/ValueRenderer.vue +43 -16
  110. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  111. package/dist/spa/src/i18n.ts +1 -1
  112. package/dist/spa/src/renderers/CompactField.vue +1 -1
  113. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  114. package/dist/spa/src/router/index.ts +8 -0
  115. package/dist/spa/src/shims-vue.d.ts +5 -0
  116. package/dist/spa/src/spa_types/core.ts +13 -1
  117. package/dist/spa/src/stores/core.ts +13 -1
  118. package/dist/spa/src/stores/filters.ts +29 -2
  119. package/dist/spa/src/stores/modal.ts +6 -1
  120. package/dist/spa/src/stores/toast.ts +22 -3
  121. package/dist/spa/src/types/Back.ts +158 -22
  122. package/dist/spa/src/types/Common.ts +81 -32
  123. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  124. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  125. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
  126. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  127. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  128. package/dist/spa/src/types/adapters/index.ts +8 -0
  129. package/dist/spa/src/utils.ts +279 -9
  130. package/dist/spa/src/views/CreateView.vue +18 -19
  131. package/dist/spa/src/views/EditView.vue +25 -19
  132. package/dist/spa/src/views/ListView.vue +144 -87
  133. package/dist/spa/src/views/LoginView.vue +26 -35
  134. package/dist/spa/src/views/ResourceParent.vue +2 -2
  135. package/dist/spa/src/views/SettingsView.vue +121 -0
  136. package/dist/spa/src/views/ShowView.vue +59 -39
  137. package/dist/spa/src/websocket.ts +6 -1
  138. package/dist/spa/tsconfig.app.json +1 -1
  139. package/dist/spa/vite.config.ts +45 -2
  140. package/dist/types/Back.d.ts +134 -14
  141. package/dist/types/Back.d.ts.map +1 -1
  142. package/dist/types/Back.js +15 -0
  143. package/dist/types/Back.js.map +1 -1
  144. package/dist/types/Common.d.ts +96 -29
  145. package/dist/types/Common.d.ts.map +1 -1
  146. package/dist/types/Common.js.map +1 -1
  147. package/dist/types/FrontendAPI.d.ts +31 -3
  148. package/dist/types/FrontendAPI.d.ts.map +1 -1
  149. package/dist/types/FrontendAPI.js.map +1 -1
  150. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  151. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  152. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  153. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  154. package/dist/types/adapters/EmailAdapter.d.ts +1 -1
  155. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  156. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  157. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  158. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  159. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  160. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  161. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  162. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  163. package/dist/types/adapters/index.d.ts +9 -0
  164. package/dist/types/adapters/index.d.ts.map +1 -0
  165. package/dist/types/adapters/index.js +2 -0
  166. package/dist/types/adapters/index.js.map +1 -0
  167. package/package.json +4 -2
  168. package/dist/spa/src/types/adapters/index.js +0 -5
@@ -5,31 +5,31 @@
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
- <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>
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,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 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,6 +182,7 @@
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
 
@@ -216,14 +214,14 @@
216
214
  <!-- pagination
217
215
  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
216
  -->
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
- >
217
+ <div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
222
218
 
223
- <div class="inline-flex ">
219
+ <div class="inline-flex "
220
+ v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
221
+ >
224
222
  <!-- Buttons -->
225
223
  <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"
224
+ 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
225
  @click="page--; pageInput = page.toString();" :disabled="page <= 1">
228
226
  <svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
229
227
  viewBox="0 0 14 10">
@@ -235,14 +233,14 @@
235
233
  </span>
236
234
  </button>
237
235
  <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"
236
+ 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
237
  @click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
240
238
  <!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
241
239
  1
242
240
  </button>
243
241
  <div
244
242
  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"
243
+ 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
244
  @keydown="onPageKeydown($event)"
247
245
  @input="onPageInput($event)"
248
246
  @blur="validatePageInput()"
@@ -251,14 +249,14 @@
251
249
  </div>
252
250
 
253
251
  <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"
252
+ 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
253
  @click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
256
254
  {{ totalPages }}
257
255
 
258
256
  <!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
259
257
  </button>
260
258
  <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"
259
+ 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
260
  @click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
263
261
  <span class="hidden sm:inline">{{ $t('Next') }}</span>
264
262
  <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 +268,9 @@
270
268
  </div>
271
269
 
272
270
  <!-- 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>
271
+ <span class="text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
272
+ <span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
273
+ <template v-else-if="resource && totalRows > 0">
276
274
 
277
275
  <span class="hidden sm:inline">
278
276
  <i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
@@ -328,8 +326,9 @@ import {
328
326
  } from '@iconify-prerendered/vue-flowbite';
329
327
  import router from '@/router';
330
328
  import { Tooltip } from '@/afcl';
331
- import type { AdminForthResourceCommon } from '@/types/Common';
329
+ import type { AdminForthResourceCommon, AdminForthResourceColumnCommon } from '@/types/Common';
332
330
  import adminforth from '@/adminforth';
331
+ import Checkbox from '@/afcl/Checkbox.vue';
333
332
 
334
333
  const coreStore = useCoreStore();
335
334
  const { t } = useI18n();
@@ -361,7 +360,7 @@ const emits = defineEmits([
361
360
  const checkboxesInternal: Ref<any[]> = ref([]);
362
361
  const pageInput = ref('1');
363
362
  const page = ref(1);
364
- const sort = ref([]);
363
+ const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
365
364
 
366
365
 
367
366
  const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
@@ -370,11 +369,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
370
369
  watch(() => page.value, (newPage) => {
371
370
  emits('update:page', newPage);
372
371
  });
373
- async function onPageKeydown(event) {
372
+ async function onPageKeydown(event: any) {
374
373
  // page input should accept only numbers, arrow keys and backspace
375
374
  if (['Enter', 'Space'].includes(event.code) ||
376
375
  (!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
377
- && isNaN(String.fromCharCode(event.keyCode)))) {
376
+ && isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
378
377
  event.preventDefault();
379
378
  if (event.code === 'Enter') {
380
379
  validatePageInput();
@@ -395,7 +394,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
395
394
  checkboxesInternal.value = newCheckboxes;
396
395
  });
397
396
 
398
- watch(() => props.sort, (newSort) => {
397
+ watch(() => props.sort, (newSort: any) => {
399
398
  sort.value = newSort;
400
399
  });
401
400
 
@@ -406,17 +405,17 @@ watch(() => props.page, (newPage) => {
406
405
  page.value = newPage;
407
406
  });
408
407
 
409
- const rowRefs = useTemplateRef('rowRefs');
410
- const headerRefs = useTemplateRef('headerRefs');
411
- const rowHeights = ref([]);
412
- 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[]>([]);
413
412
  watch(() => props.rows, (newRows) => {
414
413
  // 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)];
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)];
417
416
  });
418
417
 
419
- function addToCheckedValues(id) {
418
+ function addToCheckedValues(id: any) {
420
419
  if (checkboxesInternal.value.includes(id)) {
421
420
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
422
421
  } else {
@@ -425,17 +424,17 @@ function addToCheckedValues(id) {
425
424
  checkboxesInternal.value = [ ...checkboxesInternal.value ]
426
425
  }
427
426
 
428
- const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn.list));
427
+ const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
429
428
 
430
- async function selectAll(value) {
429
+ async function selectAll() {
431
430
  if (!allFromThisPageChecked.value) {
432
- props.rows.forEach((r) => {
431
+ props.rows?.forEach((r) => {
433
432
  if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
434
433
  checkboxesInternal.value.push(r._primaryKeyValue)
435
434
  }
436
435
  });
437
436
  } else {
438
- props.rows.forEach((r) => {
437
+ props.rows?.forEach((r) => {
439
438
  checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
440
439
  });
441
440
  }
@@ -448,15 +447,15 @@ const allFromThisPageChecked = computed(() => {
448
447
  if (!props.rows || !props.rows.length) return false;
449
448
  return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
450
449
  });
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));
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));
453
452
 
454
453
 
455
- function onSortButtonClick(event, field) {
454
+ function onSortButtonClick(event: any, field: any) {
456
455
  // if ctrl key is pressed, add to sort otherwise sort by this field
457
456
  // in any case if field is already in sort, toggle direction
458
457
 
459
- const sortIndex = sort.value.findIndex((s) => s.field === field);
458
+ const sortIndex = sort.value.findIndex((s: any) => s.field === field);
460
459
  if (sortIndex === -1) {
461
460
  // field is not in sort, add it
462
461
  if (event.ctrlKey) {
@@ -477,11 +476,11 @@ function onSortButtonClick(event, field) {
477
476
 
478
477
  const clickTarget = ref(null);
479
478
 
480
- async function onClick(e,row) {
479
+ async function onClick(e: any,row: any) {
481
480
  if(clickTarget.value === e.target) return;
482
481
  clickTarget.value = e.target;
483
482
  await new Promise((resolve) => setTimeout(resolve, 100));
484
- if (window.getSelection().toString()) return;
483
+ if (window.getSelection()?.toString()) return;
485
484
  else {
486
485
  if (row._clickUrl === null) {
487
486
  // user asked to nothing on click
@@ -496,7 +495,7 @@ async function onClick(e,row) {
496
495
  router.resolve({
497
496
  name: 'resource-show',
498
497
  params: {
499
- resourceId: props.resource.resourceId,
498
+ resourceId: props.resource?.resourceId,
500
499
  primaryKey: row._primaryKeyValue,
501
500
  },
502
501
  }).href,
@@ -514,7 +513,7 @@ async function onClick(e,row) {
514
513
  router.push({
515
514
  name: 'resource-show',
516
515
  params: {
517
- resourceId: props.resource.resourceId,
516
+ resourceId: props.resource?.resourceId,
518
517
  primaryKey: row._primaryKeyValue,
519
518
  },
520
519
  });
@@ -523,7 +522,7 @@ async function onClick(e,row) {
523
522
  }
524
523
  }
525
524
 
526
- async function deleteRecord(row) {
525
+ async function deleteRecord(row: any) {
527
526
  const data = await adminforth.confirm({
528
527
  message: t('Are you sure you want to delete this item?'),
529
528
  yes: t('Delete'),
@@ -535,7 +534,7 @@ async function deleteRecord(row) {
535
534
  path: '/delete_record',
536
535
  method: 'POST',
537
536
  body: {
538
- resourceId: props.resource.resourceId,
537
+ resourceId: props.resource?.resourceId,
539
538
  primaryKey: row._primaryKeyValue,
540
539
  }
541
540
  });
@@ -553,16 +552,16 @@ async function deleteRecord(row) {
553
552
  }
554
553
  }
555
554
 
556
- const actionLoadingStates = ref({});
555
+ const actionLoadingStates = ref<Record<string | number, boolean>>({});
557
556
 
558
- async function startCustomAction(actionId, row) {
557
+ async function startCustomAction(actionId: string, row: any) {
559
558
  actionLoadingStates.value[actionId] = true;
560
559
 
561
560
  const data = await callAdminForthApi({
562
561
  path: '/start_custom_action',
563
562
  method: 'POST',
564
563
  body: {
565
- resourceId: props.resource.resourceId,
564
+ resourceId: props.resource?.resourceId,
566
565
  actionId: actionId,
567
566
  recordId: row._primaryKeyValue
568
567
  }
@@ -600,7 +599,7 @@ async function startCustomAction(actionId, row) {
600
599
  }
601
600
  }
602
601
 
603
- function onPageInput(event) {
602
+ function onPageInput(event: any) {
604
603
  pageInput.value = event.target.innerText;
605
604
  }
606
605
 
@@ -735,4 +734,15 @@ input[type="checkbox"][disabled] {
735
734
  input[type="checkbox"]:not([disabled]) {
736
735
  @apply cursor-pointer;
737
736
  }
737
+ td.sticky-column {
738
+ @apply sticky left-0 z-10;
739
+ &:not(:first-child) {
740
+ @apply left-[56px];
741
+ }
742
+ }
743
+ tr:not(:first-child):hover {
744
+ td.sticky-column {
745
+ @apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
746
+ }
747
+ }
738
748
  </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 => {