adminforth 2.4.0-next.33 → 2.4.0-next.331
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/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/templates/package.json.hbs +1 -1
- package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
- package/commands/createApp/templates/schema.prisma.hbs +0 -1
- package/commands/createApp/utils.js +10 -0
- package/commands/createCustomComponent/configLoader.js +17 -4
- package/commands/createCustomComponent/main.js +13 -7
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -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 +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +70 -18
- 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 +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -21
- 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 +62 -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 +209 -25
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +199 -31
- 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 +1208 -708
- package/dist/spa/package.json +34 -34
- package/dist/spa/src/App.vue +132 -174
- package/dist/spa/src/adminforth.ts +41 -17
- package/dist/spa/src/afcl/AreaChart.vue +0 -1
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +3 -3
- 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 +145 -48
- package/dist/spa/src/afcl/Input.vue +14 -6
- 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 +82 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +313 -75
- 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 +28 -18
- package/dist/spa/src/afcl/VerticalTabs.vue +21 -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/CallActionWrapper.vue +15 -0
- 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 -21
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +195 -132
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +95 -23
- package/dist/spa/src/components/ResourceForm.vue +99 -51
- package/dist/spa/src/components/ResourceListTable.vue +121 -95
- package/dist/spa/src/components/ResourceListTableVirtual.vue +119 -88
- package/dist/spa/src/components/ShowTable.vue +21 -15
- package/dist/spa/src/components/Sidebar.vue +472 -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 +84 -15
- package/dist/spa/src/components/Toast.vue +40 -29
- package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
- package/dist/spa/src/components/ValueRenderer.vue +44 -17
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +5 -3
- package/dist/spa/src/main.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 +15 -1
- package/dist/spa/src/stores/filters.ts +33 -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 +168 -23
- package/dist/spa/src/types/Common.ts +109 -32
- package/dist/spa/src/types/FrontendAPI.ts +32 -23
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
- 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/StorageAdapter.ts +4 -2
- package/dist/spa/src/types/adapters/index.ts +3 -0
- package/dist/spa/src/utils.ts +291 -11
- package/dist/spa/src/views/CreateView.vue +88 -22
- package/dist/spa/src/views/EditView.vue +55 -22
- 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 +83 -53
- 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 +151 -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 +123 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +32 -18
- 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 +2 -3
- package/dist/types/adapters/EmailAdapter.d.ts.map +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/StorageAdapter.d.ts +2 -0
- package/dist/types/adapters/StorageAdapter.d.ts.map +1 -1
- package/dist/types/adapters/index.d.ts +3 -0
- package/dist/types/adapters/index.d.ts.map +1 -1
- package/package.json +4 -2
|
@@ -6,27 +6,26 @@
|
|
|
6
6
|
<!-- skelet loader -->
|
|
7
7
|
<div role="status" v-if="!resource || !resource.columns"
|
|
8
8
|
class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
|
|
9
|
-
|
|
10
9
|
<div role="status" class="max-w-sm animate-pulse">
|
|
11
|
-
<div class="h-2 bg-
|
|
10
|
+
<div class="h-2 bg-lightListSkeletLoader rounded-full dark:bg-darkListSkeletLoader max-w-[360px]"></div>
|
|
12
11
|
</div>
|
|
13
12
|
</div>
|
|
14
|
-
<table v-else class=" w-full text-sm text-left rtl:text-right text-
|
|
13
|
+
<table v-else class=" w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
|
|
15
14
|
|
|
16
15
|
<tbody>
|
|
17
16
|
<!-- table header -->
|
|
18
|
-
<tr class="t-header sticky z-
|
|
19
|
-
<td scope="col" class="p-4">
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<
|
|
26
|
-
</
|
|
17
|
+
<tr class="t-header sticky z-20 top-0 text-xs text-lightListTableHeadingText bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-darkListTableHeadingText">
|
|
18
|
+
<td scope="col" class="list-table-header-cell p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
|
|
19
|
+
<Checkbox
|
|
20
|
+
:modelValue="allFromThisPageChecked"
|
|
21
|
+
:disabled="!rows || !rows.length"
|
|
22
|
+
@update:modelValue="selectAll"
|
|
23
|
+
>
|
|
24
|
+
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
25
|
+
</Checkbox>
|
|
27
26
|
</td>
|
|
28
27
|
|
|
29
|
-
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
|
|
28
|
+
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="list-table-header-cell px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
|
|
30
29
|
|
|
31
30
|
<div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
|
|
32
31
|
class="flex items-center " :class="{'cursor-pointer':c.sortable}">
|
|
@@ -48,8 +47,8 @@
|
|
|
48
47
|
</div>
|
|
49
48
|
<span
|
|
50
49
|
class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
|
|
51
|
-
v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
|
|
52
|
-
{{ sort.findIndex((s) => s.field === c.name) + 1 }}
|
|
50
|
+
v-if="sort.findIndex((s: any) => s.field === c.name) !== -1 && sort?.length > 1">
|
|
51
|
+
{{ sort.findIndex((s: any) => s.field === c.name) + 1 }}
|
|
53
52
|
</span>
|
|
54
53
|
|
|
55
54
|
</div>
|
|
@@ -65,7 +64,7 @@
|
|
|
65
64
|
<!-- table header end -->
|
|
66
65
|
<SkeleteLoader
|
|
67
66
|
v-if="!rows"
|
|
68
|
-
:columns="resource?.columns.filter(c => c.showIn
|
|
67
|
+
:columns="resource?.columns.filter((c: AdminForthResourceColumnInputCommon) => c.showIn?.list).length + 2"
|
|
69
68
|
:rows="rowHeights.length || 3"
|
|
70
69
|
:row-heights="rowHeights"
|
|
71
70
|
:column-widths="columnWidths"
|
|
@@ -87,27 +86,25 @@
|
|
|
87
86
|
<tr @click="onClick($event,row)"
|
|
88
87
|
v-else v-for="(row, rowI) in rows" :key="`row_${row._primaryKeyValue}`"
|
|
89
88
|
ref="rowRefs"
|
|
90
|
-
class="bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
|
|
89
|
+
class="list-table-body-row bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
|
|
91
90
|
|
|
92
91
|
:class="{'border-b': rowI !== rows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
|
|
93
92
|
>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
</td>
|
|
106
|
-
<td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4">
|
|
93
|
+
<td class="w-4 p-4 cursor-default sticky-column bg-lightListTable dark:bg-darkListTable" @click="(e)=>e.stopPropagation()">
|
|
94
|
+
<Checkbox
|
|
95
|
+
:model-value="checkboxesInternal.includes(row._primaryKeyValue)"
|
|
96
|
+
@change="(e: any)=>{addToCheckedValues(row._primaryKeyValue)}"
|
|
97
|
+
@click="(e: any)=>e.stopPropagation()"
|
|
98
|
+
>
|
|
99
|
+
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
100
|
+
</Checkbox>
|
|
101
|
+
</td>
|
|
102
|
+
|
|
103
|
+
<td v-for="c in columnsListed" class="px-2 md:px-3 lg:px-6 py-4" :class="{'sticky-column bg-lightListTable dark:bg-darkListTable': c.listSticky}">
|
|
107
104
|
<!-- if c.name in listComponentsPerColumn, render it. If not, render ValueRenderer -->
|
|
108
105
|
<component
|
|
109
|
-
:is="c?.components?.list ? getCustomComponent(c.components.list) : ValueRenderer"
|
|
110
|
-
:meta="c?.components?.list
|
|
106
|
+
:is="c?.components?.list ? getCustomComponent(typeof c.components.list === 'string' ? { file: c.components.list } : c.components.list) : ValueRenderer"
|
|
107
|
+
:meta="typeof c?.components?.list === 'object' ? c.components.list.meta : undefined"
|
|
111
108
|
:column="c"
|
|
112
109
|
:record="row"
|
|
113
110
|
:adminUser="coreStore.adminUser"
|
|
@@ -118,7 +115,7 @@
|
|
|
118
115
|
<div class="flex text-lightPrimary dark:text-darkPrimary items-center">
|
|
119
116
|
<Tooltip>
|
|
120
117
|
<RouterLink
|
|
121
|
-
v-if="resource.options?.allowedActions
|
|
118
|
+
v-if="resource.options?.allowedActions?.show"
|
|
122
119
|
:to="{
|
|
123
120
|
name: 'resource-show',
|
|
124
121
|
params: {
|
|
@@ -138,7 +135,7 @@
|
|
|
138
135
|
|
|
139
136
|
<Tooltip>
|
|
140
137
|
<RouterLink
|
|
141
|
-
v-if="resource.options?.allowedActions
|
|
138
|
+
v-if="resource.options?.allowedActions?.edit"
|
|
142
139
|
:to="{
|
|
143
140
|
name: 'resource-edit',
|
|
144
141
|
params: {
|
|
@@ -156,7 +153,7 @@
|
|
|
156
153
|
|
|
157
154
|
<Tooltip>
|
|
158
155
|
<button
|
|
159
|
-
v-if="resource.options?.allowedActions
|
|
156
|
+
v-if="resource.options?.allowedActions?.delete"
|
|
160
157
|
@click="deleteRecord(row)"
|
|
161
158
|
>
|
|
162
159
|
<IconTrashBinSolid class="af-delete-icon w-5 h-5 me-2"/>
|
|
@@ -175,22 +172,43 @@
|
|
|
175
172
|
:resource="coreStore.resource"
|
|
176
173
|
:adminUser="coreStore.adminUser"
|
|
177
174
|
:record="row"
|
|
175
|
+
:updateRecords="()=>emits('update:records', true)"
|
|
178
176
|
/>
|
|
179
177
|
</template>
|
|
180
178
|
|
|
181
179
|
<template v-if="resource.options?.actions">
|
|
182
|
-
<Tooltip
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
<component
|
|
187
|
-
|
|
188
|
-
|
|
180
|
+
<Tooltip
|
|
181
|
+
v-for="action in resource.options.actions.filter(a => a.showIn?.list)"
|
|
182
|
+
:key="action.id"
|
|
183
|
+
>
|
|
184
|
+
<component
|
|
185
|
+
:is="action.customComponent ? getCustomComponent(action.customComponent) : CallActionWrapper"
|
|
186
|
+
:meta="action.customComponent?.meta"
|
|
187
|
+
:row="row"
|
|
188
|
+
:resource="resource"
|
|
189
|
+
:adminUser="adminUser"
|
|
190
|
+
@callAction="(payload? : Object) => startCustomAction(action.id, payload ?? row)"
|
|
191
|
+
>
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
:disabled="rowActionLoadingStates?.[action.id]"
|
|
195
|
+
@click.stop.prevent
|
|
196
|
+
>
|
|
197
|
+
<component
|
|
198
|
+
v-if="action.icon"
|
|
199
|
+
:is="getIcon(action.icon)"
|
|
200
|
+
class="w-5 h-5 mr-2 text-lightPrimary dark:text-darkPrimary"
|
|
201
|
+
/>
|
|
202
|
+
</button>
|
|
203
|
+
</component>
|
|
204
|
+
|
|
205
|
+
<template #tooltip>
|
|
189
206
|
{{ action.name }}
|
|
190
207
|
</template>
|
|
191
208
|
</Tooltip>
|
|
192
209
|
</template>
|
|
193
210
|
</div>
|
|
211
|
+
|
|
194
212
|
</td>
|
|
195
213
|
</tr>
|
|
196
214
|
</tbody>
|
|
@@ -199,14 +217,14 @@
|
|
|
199
217
|
<!-- pagination
|
|
200
218
|
totalRows in v-if is used to not hide page input during loading when user puts cursor into it and edit directly (rows gets null there during edit)
|
|
201
219
|
-->
|
|
202
|
-
<div class="flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
|
|
220
|
+
<div class="af-pagination-container flex flex-row items-center mt-4 xs:flex-row xs:justify-between xs:items-center gap-3">
|
|
203
221
|
|
|
204
|
-
<div class="inline-flex "
|
|
222
|
+
<div class="af-pagination-buttons-container inline-flex "
|
|
205
223
|
v-if="(rows || totalRows) && totalRows >= pageSize && totalRows > 0"
|
|
206
224
|
>
|
|
207
225
|
<!-- Buttons -->
|
|
208
226
|
<button
|
|
209
|
-
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-
|
|
227
|
+
class="af-pagination-prev-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 rounded-s border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
210
228
|
@click="page--; pageInput = page.toString();" :disabled="page <= 1">
|
|
211
229
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
212
230
|
viewBox="0 0 14 10">
|
|
@@ -218,30 +236,29 @@
|
|
|
218
236
|
</span>
|
|
219
237
|
</button>
|
|
220
238
|
<button
|
|
221
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
239
|
+
class="af-pagination-first-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
222
240
|
@click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
|
|
223
241
|
<!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
|
|
224
242
|
1
|
|
225
243
|
</button>
|
|
226
|
-
<
|
|
227
|
-
|
|
228
|
-
|
|
244
|
+
<input
|
|
245
|
+
type="text"
|
|
246
|
+
v-model="pageInput"
|
|
247
|
+
:style="{ width: `${Math.max(1, pageInput.length+4)}ch` }"
|
|
248
|
+
class="af-pagination-input min-w-10 outline-none inline-block py-1.5 px-3 text-sm text-center text-lightListTablePaginationCurrentPageText border border-lightListTablePaginationBorder dark:border-darkListTablePaginationBorder dark:text-darkListTablePaginationCurrentPageText dark:bg-darkListTablePaginationBackgoround z-10"
|
|
229
249
|
@keydown="onPageKeydown($event)"
|
|
230
|
-
@input="onPageInput($event)"
|
|
231
250
|
@blur="validatePageInput()"
|
|
232
|
-
|
|
233
|
-
{{ pageInput }}
|
|
234
|
-
</div>
|
|
251
|
+
/>
|
|
235
252
|
|
|
236
253
|
<button
|
|
237
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
254
|
+
class="af-pagination-last-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
238
255
|
@click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
|
|
239
256
|
{{ totalPages }}
|
|
240
257
|
|
|
241
258
|
<!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
|
|
242
259
|
</button>
|
|
243
260
|
<button
|
|
244
|
-
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-
|
|
261
|
+
class="af-pagination-next-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 rounded-e border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
245
262
|
@click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
|
|
246
263
|
<span class="hidden sm:inline">{{ $t('Next') }}</span>
|
|
247
264
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
@@ -253,11 +270,11 @@
|
|
|
253
270
|
</div>
|
|
254
271
|
|
|
255
272
|
<!-- Help text -->
|
|
256
|
-
<span class="text-sm text-
|
|
257
|
-
<span v-if="((page || 1) - 1) * pageSize + 1 > totalRows">{{ $t('Wrong Page') }} </span>
|
|
258
|
-
<template v-else>
|
|
273
|
+
<span class="ml-4 text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
|
|
274
|
+
<span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
|
|
275
|
+
<template v-else-if="resource && totalRows > 0">
|
|
259
276
|
|
|
260
|
-
<span class="hidden sm:inline">
|
|
277
|
+
<span class="af-pagination-info hidden sm:inline">
|
|
261
278
|
<i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
|
|
262
279
|
<template v-slot:from>
|
|
263
280
|
<strong>{{ from }}</strong>
|
|
@@ -307,12 +324,13 @@ import {
|
|
|
307
324
|
import {
|
|
308
325
|
IconEyeSolid,
|
|
309
326
|
IconPenSolid,
|
|
310
|
-
IconTrashBinSolid
|
|
327
|
+
IconTrashBinSolid,
|
|
311
328
|
} from '@iconify-prerendered/vue-flowbite';
|
|
312
329
|
import router from '@/router';
|
|
313
330
|
import { Tooltip } from '@/afcl';
|
|
314
|
-
import type { AdminForthResourceCommon } from '@/types/Common';
|
|
331
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnInputCommon, AdminForthResourceColumnCommon } from '@/types/Common';
|
|
315
332
|
import adminforth from '@/adminforth';
|
|
333
|
+
import Checkbox from '@/afcl/Checkbox.vue';
|
|
316
334
|
|
|
317
335
|
const coreStore = useCoreStore();
|
|
318
336
|
const { t } = useI18n();
|
|
@@ -341,7 +359,7 @@ const emits = defineEmits([
|
|
|
341
359
|
const checkboxesInternal: Ref<any[]> = ref([]);
|
|
342
360
|
const pageInput = ref('1');
|
|
343
361
|
const page = ref(1);
|
|
344
|
-
const sort = ref([]);
|
|
362
|
+
const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
|
|
345
363
|
|
|
346
364
|
|
|
347
365
|
const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
|
|
@@ -350,11 +368,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
|
|
|
350
368
|
watch(() => page.value, (newPage) => {
|
|
351
369
|
emits('update:page', newPage);
|
|
352
370
|
});
|
|
353
|
-
async function onPageKeydown(event) {
|
|
371
|
+
async function onPageKeydown(event: any) {
|
|
354
372
|
// page input should accept only numbers, arrow keys and backspace
|
|
355
373
|
if (['Enter', 'Space'].includes(event.code) ||
|
|
356
374
|
(!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
|
|
357
|
-
&& isNaN(String.fromCharCode(event.keyCode)))) {
|
|
375
|
+
&& isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
|
|
358
376
|
event.preventDefault();
|
|
359
377
|
if (event.code === 'Enter') {
|
|
360
378
|
validatePageInput();
|
|
@@ -375,7 +393,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
|
|
|
375
393
|
checkboxesInternal.value = newCheckboxes;
|
|
376
394
|
});
|
|
377
395
|
|
|
378
|
-
watch(() => props.sort, (newSort) => {
|
|
396
|
+
watch(() => props.sort, (newSort: any) => {
|
|
379
397
|
sort.value = newSort;
|
|
380
398
|
});
|
|
381
399
|
|
|
@@ -386,17 +404,17 @@ watch(() => props.page, (newPage) => {
|
|
|
386
404
|
page.value = newPage;
|
|
387
405
|
});
|
|
388
406
|
|
|
389
|
-
const rowRefs = useTemplateRef('rowRefs');
|
|
390
|
-
const headerRefs = useTemplateRef('headerRefs');
|
|
391
|
-
const rowHeights = ref([]);
|
|
392
|
-
const columnWidths = ref([]);
|
|
407
|
+
const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
|
|
408
|
+
const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
|
|
409
|
+
const rowHeights = ref<number[]>([]);
|
|
410
|
+
const columnWidths = ref<number[]>([]);
|
|
393
411
|
watch(() => props.rows, (newRows) => {
|
|
394
412
|
// rows are set to null when new records are loading
|
|
395
|
-
rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el) => el.offsetHeight);
|
|
396
|
-
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el) => el.offsetWidth)];
|
|
413
|
+
rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
|
|
414
|
+
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
|
|
397
415
|
});
|
|
398
416
|
|
|
399
|
-
function addToCheckedValues(id) {
|
|
417
|
+
function addToCheckedValues(id: string) {
|
|
400
418
|
if (checkboxesInternal.value.includes(id)) {
|
|
401
419
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
|
|
402
420
|
} else {
|
|
@@ -405,17 +423,17 @@ function addToCheckedValues(id) {
|
|
|
405
423
|
checkboxesInternal.value = [ ...checkboxesInternal.value ]
|
|
406
424
|
}
|
|
407
425
|
|
|
408
|
-
const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn
|
|
426
|
+
const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
|
|
409
427
|
|
|
410
|
-
async function selectAll(
|
|
428
|
+
async function selectAll() {
|
|
411
429
|
if (!allFromThisPageChecked.value) {
|
|
412
|
-
props.rows
|
|
430
|
+
props.rows?.forEach((r) => {
|
|
413
431
|
if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
|
|
414
432
|
checkboxesInternal.value.push(r._primaryKeyValue)
|
|
415
433
|
}
|
|
416
434
|
});
|
|
417
435
|
} else {
|
|
418
|
-
props.rows
|
|
436
|
+
props.rows?.forEach((r) => {
|
|
419
437
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
|
|
420
438
|
});
|
|
421
439
|
}
|
|
@@ -428,15 +446,15 @@ const allFromThisPageChecked = computed(() => {
|
|
|
428
446
|
if (!props.rows || !props.rows.length) return false;
|
|
429
447
|
return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
|
|
430
448
|
});
|
|
431
|
-
const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
|
|
432
|
-
const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
|
|
449
|
+
const ascArr = computed(() => sort.value.filter((s:any) => s.direction === 'asc').map((s: any) => s.field));
|
|
450
|
+
const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
|
|
433
451
|
|
|
434
452
|
|
|
435
|
-
function onSortButtonClick(event, field) {
|
|
453
|
+
function onSortButtonClick(event: any, field: string) {
|
|
436
454
|
// if ctrl key is pressed, add to sort otherwise sort by this field
|
|
437
455
|
// in any case if field is already in sort, toggle direction
|
|
438
456
|
|
|
439
|
-
const sortIndex = sort.value.findIndex((s) => s.field === field);
|
|
457
|
+
const sortIndex = sort.value.findIndex((s: any) => s.field === field);
|
|
440
458
|
if (sortIndex === -1) {
|
|
441
459
|
// field is not in sort, add it
|
|
442
460
|
if (event.ctrlKey) {
|
|
@@ -447,9 +465,9 @@ function onSortButtonClick(event, field) {
|
|
|
447
465
|
} else {
|
|
448
466
|
const sortField = sort.value[sortIndex];
|
|
449
467
|
if (sortField.direction === 'asc') {
|
|
450
|
-
sort.value = sort.value.map((s) => s.field === field ? {field, direction: 'desc'} : s);
|
|
468
|
+
sort.value = sort.value.map((s: any) => s.field === field ? {field, direction: 'desc'} : s);
|
|
451
469
|
} else {
|
|
452
|
-
sort.value = sort.value.filter((s) => s.field !== field);
|
|
470
|
+
sort.value = sort.value.filter((s: any) => s.field !== field);
|
|
453
471
|
}
|
|
454
472
|
}
|
|
455
473
|
}
|
|
@@ -457,11 +475,11 @@ function onSortButtonClick(event, field) {
|
|
|
457
475
|
|
|
458
476
|
const clickTarget = ref(null);
|
|
459
477
|
|
|
460
|
-
async function onClick(e,row) {
|
|
478
|
+
async function onClick(e: any, row: any) {
|
|
461
479
|
if(clickTarget.value === e.target) return;
|
|
462
480
|
clickTarget.value = e.target;
|
|
463
481
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
464
|
-
if (window.getSelection()
|
|
482
|
+
if (window.getSelection()?.toString()) return;
|
|
465
483
|
else {
|
|
466
484
|
if (row._clickUrl === null) {
|
|
467
485
|
// user asked to nothing on click
|
|
@@ -476,7 +494,7 @@ async function onClick(e,row) {
|
|
|
476
494
|
router.resolve({
|
|
477
495
|
name: 'resource-show',
|
|
478
496
|
params: {
|
|
479
|
-
resourceId: props.resource
|
|
497
|
+
resourceId: props.resource?.resourceId,
|
|
480
498
|
primaryKey: row._primaryKeyValue,
|
|
481
499
|
},
|
|
482
500
|
}).href,
|
|
@@ -494,7 +512,7 @@ async function onClick(e,row) {
|
|
|
494
512
|
router.push({
|
|
495
513
|
name: 'resource-show',
|
|
496
514
|
params: {
|
|
497
|
-
resourceId: props.resource
|
|
515
|
+
resourceId: props.resource?.resourceId,
|
|
498
516
|
primaryKey: row._primaryKeyValue,
|
|
499
517
|
},
|
|
500
518
|
});
|
|
@@ -503,7 +521,7 @@ async function onClick(e,row) {
|
|
|
503
521
|
}
|
|
504
522
|
}
|
|
505
523
|
|
|
506
|
-
async function deleteRecord(row) {
|
|
524
|
+
async function deleteRecord(row: any) {
|
|
507
525
|
const data = await adminforth.confirm({
|
|
508
526
|
message: t('Are you sure you want to delete this item?'),
|
|
509
527
|
yes: t('Delete'),
|
|
@@ -515,7 +533,7 @@ async function deleteRecord(row) {
|
|
|
515
533
|
path: '/delete_record',
|
|
516
534
|
method: 'POST',
|
|
517
535
|
body: {
|
|
518
|
-
resourceId: props.resource
|
|
536
|
+
resourceId: props.resource?.resourceId,
|
|
519
537
|
primaryKey: row._primaryKeyValue,
|
|
520
538
|
}
|
|
521
539
|
});
|
|
@@ -533,16 +551,16 @@ async function deleteRecord(row) {
|
|
|
533
551
|
}
|
|
534
552
|
}
|
|
535
553
|
|
|
536
|
-
const actionLoadingStates = ref({});
|
|
554
|
+
const actionLoadingStates = ref<Record<string | number, boolean>>({});
|
|
537
555
|
|
|
538
|
-
async function startCustomAction(actionId, row) {
|
|
556
|
+
async function startCustomAction(actionId: string, row: any) {
|
|
539
557
|
actionLoadingStates.value[actionId] = true;
|
|
540
558
|
|
|
541
559
|
const data = await callAdminForthApi({
|
|
542
560
|
path: '/start_custom_action',
|
|
543
561
|
method: 'POST',
|
|
544
562
|
body: {
|
|
545
|
-
resourceId: props.resource
|
|
563
|
+
resourceId: props.resource?.resourceId,
|
|
546
564
|
actionId: actionId,
|
|
547
565
|
recordId: row._primaryKeyValue
|
|
548
566
|
}
|
|
@@ -580,10 +598,6 @@ async function startCustomAction(actionId, row) {
|
|
|
580
598
|
}
|
|
581
599
|
}
|
|
582
600
|
|
|
583
|
-
function onPageInput(event) {
|
|
584
|
-
pageInput.value = event.target.innerText;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
601
|
function validatePageInput() {
|
|
588
602
|
const newPage = parseInt(pageInput.value) || 1;
|
|
589
603
|
const validPage = Math.max(1, Math.min(newPage, totalPages.value));
|
|
@@ -600,4 +614,16 @@ input[type="checkbox"][disabled] {
|
|
|
600
614
|
input[type="checkbox"]:not([disabled]) {
|
|
601
615
|
@apply cursor-pointer;
|
|
602
616
|
}
|
|
617
|
+
td.sticky-column {
|
|
618
|
+
@apply sticky left-0 z-10;
|
|
619
|
+
&:not(:first-child) {
|
|
620
|
+
@apply left-[56px];
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
tr.list-table-body-row:not(:first-child):hover {
|
|
624
|
+
td.sticky-column:not(.list-table-header-cell) {
|
|
625
|
+
@apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
603
629
|
</style>
|