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.
- package/commands/callTsProxy.js +14 -4
- package/commands/cli.js +10 -3
- package/commands/createApp/templates/api.ts.hbs +10 -0
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +12 -1
- package/commands/createApp/utils.js +25 -8
- package/commands/createCustomComponent/configLoader.js +17 -4
- package/commands/createCustomComponent/main.js +1 -0
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/commands/createPlugin/templates/package.json.hbs +1 -1
- package/commands/generateModels.js +30 -22
- package/dist/auth.d.ts +9 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +21 -2
- package/dist/auth.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +46 -15
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +50 -15
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +43 -14
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -9
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +50 -6
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts +6 -0
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +184 -19
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +164 -26
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +499 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +555 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +7 -15
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +45 -68
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts +5 -0
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +40 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/package-lock.json +5 -4
- package/dist/spa/package.json +1 -1
- package/dist/spa/src/App.vue +58 -173
- package/dist/spa/src/adminforth.ts +42 -18
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +6 -6
- package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
- package/dist/spa/src/afcl/Card.vue +25 -0
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +4 -1
- package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
- package/dist/spa/src/afcl/Dialog.vue +47 -27
- package/dist/spa/src/afcl/Dropzone.vue +12 -12
- package/dist/spa/src/afcl/Input.vue +5 -5
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/LinkButton.vue +3 -3
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +68 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +213 -74
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +26 -18
- package/dist/spa/src/afcl/VerticalTabs.vue +16 -7
- package/dist/spa/src/afcl/index.ts +6 -3
- package/dist/spa/src/components/AcceptModal.vue +48 -14
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/ColumnValueInput.vue +38 -18
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +37 -8
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +85 -39
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +90 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +90 -80
- package/dist/spa/src/components/ResourceListTableVirtual.vue +86 -76
- package/dist/spa/src/components/ShowTable.vue +21 -15
- package/dist/spa/src/components/Sidebar.vue +470 -0
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +3 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +73 -14
- package/dist/spa/src/components/Toast.vue +27 -9
- package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
- package/dist/spa/src/components/ValueRenderer.vue +43 -16
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +1 -1
- package/dist/spa/src/renderers/CompactField.vue +1 -1
- package/dist/spa/src/renderers/CompactUUID.vue +1 -1
- package/dist/spa/src/router/index.ts +8 -0
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +13 -1
- package/dist/spa/src/stores/core.ts +13 -1
- package/dist/spa/src/stores/filters.ts +29 -2
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/stores/toast.ts +22 -3
- package/dist/spa/src/types/Back.ts +158 -22
- package/dist/spa/src/types/Common.ts +81 -32
- package/dist/spa/src/types/FrontendAPI.ts +31 -5
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -2
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
- package/dist/spa/src/types/adapters/index.ts +8 -0
- package/dist/spa/src/utils.ts +279 -9
- package/dist/spa/src/views/CreateView.vue +18 -19
- package/dist/spa/src/views/EditView.vue +25 -19
- package/dist/spa/src/views/ListView.vue +144 -87
- package/dist/spa/src/views/LoginView.vue +26 -35
- package/dist/spa/src/views/ResourceParent.vue +2 -2
- package/dist/spa/src/views/SettingsView.vue +121 -0
- package/dist/spa/src/views/ShowView.vue +59 -39
- package/dist/spa/src/websocket.ts +6 -1
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/spa/vite.config.ts +45 -2
- package/dist/types/Back.d.ts +134 -14
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +15 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +96 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +31 -3
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
- package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CaptchaAdapter.js +5 -0
- package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +1 -1
- package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.js +2 -0
- package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.js +2 -0
- package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
- package/dist/types/adapters/index.d.ts +9 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/index.js +2 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/package.json +4 -2
- 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-
|
|
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-
|
|
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
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
</
|
|
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
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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-
|
|
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
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
427
|
+
const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
|
|
429
428
|
|
|
430
|
-
async function selectAll(
|
|
429
|
+
async function selectAll() {
|
|
431
430
|
if (!allFromThisPageChecked.value) {
|
|
432
|
-
props.rows
|
|
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
|
|
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()
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
3
|
-
|
|
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-
|
|
7
|
-
<thead v-if="!allColumnsHaveCustomComponent" class="text-
|
|
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-
|
|
22
|
-
dark:bg-
|
|
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
|
-
|
|
65
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon } from '@/types/Common';
|
|
66
|
+
const props = withDefaults(defineProps<{
|
|
64
67
|
columns: Array<{
|
|
65
68
|
name: string;
|
|
66
|
-
label
|
|
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:
|
|
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 => {
|