adminforth 2.4.0-next.31 → 2.4.0-next.310
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +69 -17
- 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 +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -22
- 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 +202 -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 +172 -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 +59 -174
- package/dist/spa/src/adminforth.ts +42 -18
- 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 +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 +127 -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 +315 -73
- 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 +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/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 +90 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +115 -85
- package/dist/spa/src/components/ResourceListTableVirtual.vue +114 -80
- 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 +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 +13 -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 +163 -23
- package/dist/spa/src/types/Common.ts +91 -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 +291 -11
- package/dist/spa/src/views/CreateView.vue +63 -21
- 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 +146 -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 +106 -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
|
@@ -2,34 +2,34 @@
|
|
|
2
2
|
<!-- table -->
|
|
3
3
|
<div class="relative shadow-listTableShadow dark:shadow-darkListTableShadow overflow-auto "
|
|
4
4
|
:class="{'rounded-default': !noRoundings}"
|
|
5
|
-
:style="
|
|
5
|
+
:style="{ maxHeight: `${containerHeight}px` }"
|
|
6
6
|
@scroll="handleScroll"
|
|
7
7
|
ref="containerRef"
|
|
8
|
-
>
|
|
8
|
+
>
|
|
9
9
|
<!-- skelet loader -->
|
|
10
10
|
<div role="status" v-if="!resource || !resource.columns"
|
|
11
11
|
class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded shadow animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
|
|
12
12
|
|
|
13
13
|
<div role="status" class="max-w-sm animate-pulse">
|
|
14
|
-
<div class="h-2 bg-
|
|
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="
|
|
17
|
+
<table v-else class="w-full text-sm text-left rtl:text-right text-lightListTableText dark:text-darkListTableText rounded-default">
|
|
18
18
|
|
|
19
19
|
<tbody>
|
|
20
20
|
<!-- table header -->
|
|
21
|
-
<tr class="t-header sticky z-
|
|
22
|
-
<td scope="col" class="p-4">
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
</
|
|
21
|
+
<tr class="t-header sticky z-20 top-0 text-xs bg-lightListTableHeading dark:bg-darkListTableHeading dark:text-gray-400">
|
|
22
|
+
<td scope="col" class="p-4 sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading">
|
|
23
|
+
<Checkbox
|
|
24
|
+
:modelValue="allFromThisPageChecked"
|
|
25
|
+
:disabled="!rows || !rows.length"
|
|
26
|
+
@update:modelValue="selectAll"
|
|
27
|
+
>
|
|
28
|
+
<span class="sr-only">{{ $t('checkbox') }}</span>
|
|
29
|
+
</Checkbox>
|
|
30
30
|
</td>
|
|
31
31
|
|
|
32
|
-
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3">
|
|
32
|
+
<td v-for="c in columnsListed" ref="headerRefs" scope="col" class="px-2 md:px-3 lg:px-6 py-3" :class="{'sticky-column bg-lightListTableHeading dark:bg-darkListTableHeading': c.listSticky}">
|
|
33
33
|
|
|
34
34
|
<div @click="(evt) => c.sortable && onSortButtonClick(evt, c.name)"
|
|
35
35
|
class="flex items-center " :class="{'cursor-pointer':c.sortable}">
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
</div>
|
|
52
52
|
<span
|
|
53
53
|
class="bg-red-100 text-red-800 text-xs font-medium me-1 px-1 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
|
|
54
|
-
v-if="sort.findIndex((s) => s.field === c.name) !== -1 && sort?.length > 1">
|
|
55
|
-
{{ sort.findIndex((s) => s.field === c.name) + 1 }}
|
|
54
|
+
v-if="sort.findIndex((s: any) => s.field === c.name) !== -1 && sort?.length > 1">
|
|
55
|
+
{{ sort.findIndex((s: any) => s.field === c.name) + 1 }}
|
|
56
56
|
</span>
|
|
57
57
|
|
|
58
58
|
</div>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
<!-- table header end -->
|
|
69
69
|
<SkeleteLoader
|
|
70
70
|
v-if="!rows"
|
|
71
|
-
:columns="resource?.columns.filter(c => c.showIn
|
|
71
|
+
:columns="resource?.columns.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list).length + 2"
|
|
72
72
|
:rows="rowHeights.length || 20"
|
|
73
73
|
:row-heights="rowHeights"
|
|
74
74
|
:column-widths="columnWidths"
|
|
@@ -99,25 +99,22 @@
|
|
|
99
99
|
ref="rowRefs"
|
|
100
100
|
class="bg-lightListTable dark:bg-darkListTable border-lightListBorder dark:border-gray-700 hover:bg-lightListTableRowHover dark:hover:bg-darkListTableRowHover"
|
|
101
101
|
:class="{'border-b': rowI !== visibleRows.length - 1, 'cursor-pointer': row._clickUrl !== null}"
|
|
102
|
-
@mounted="(el) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
|
|
102
|
+
@mounted="(el: any) => updateRowHeight(`row_${row._primaryKeyValue}`, el.offsetHeight)"
|
|
103
103
|
>
|
|
104
|
-
|
|
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,17 +182,42 @@
|
|
|
185
182
|
:resource="coreStore.resource"
|
|
186
183
|
:adminUser="coreStore.adminUser"
|
|
187
184
|
:record="row"
|
|
185
|
+
:updateRecords="()=>emits('update:records', true)"
|
|
188
186
|
/>
|
|
189
187
|
</template>
|
|
190
188
|
|
|
191
|
-
|
|
192
|
-
<Tooltip
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
<template v-if="resource.options?.actions">
|
|
190
|
+
<Tooltip
|
|
191
|
+
v-for="action in resource.options.actions.filter(a => a.showIn?.list)"
|
|
192
|
+
:key="action.id"
|
|
193
|
+
>
|
|
194
|
+
<CallActionWrapper
|
|
195
|
+
:disabled="rowActionLoadingStates?.[action.id]"
|
|
196
|
+
@callAction="startCustomAction(action.id, row)"
|
|
195
197
|
>
|
|
196
|
-
<component
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
<component
|
|
199
|
+
:is="action.customComponent ? getCustomComponent(action.customComponent) : 'span'"
|
|
200
|
+
:meta="action.customComponent?.meta"
|
|
201
|
+
:row="row"
|
|
202
|
+
:resource="resource"
|
|
203
|
+
:adminUser="adminUser"
|
|
204
|
+
@callAction="(payload? : Object) => startCustomAction(action.id, payload ?? row)"
|
|
205
|
+
>
|
|
206
|
+
<button
|
|
207
|
+
type="button"
|
|
208
|
+
:disabled="rowActionLoadingStates?.[action.id]"
|
|
209
|
+
@click.stop.prevent
|
|
210
|
+
>
|
|
211
|
+
<component
|
|
212
|
+
v-if="action.icon"
|
|
213
|
+
:is="getIcon(action.icon)"
|
|
214
|
+
class="w-5 h-5 mr-2 text-lightPrimary dark:text-darkPrimary"
|
|
215
|
+
/>
|
|
216
|
+
</button>
|
|
217
|
+
</component>
|
|
218
|
+
</CallActionWrapper>
|
|
219
|
+
|
|
220
|
+
<template #tooltip>
|
|
199
221
|
{{ action.name }}
|
|
200
222
|
</template>
|
|
201
223
|
</Tooltip>
|
|
@@ -223,7 +245,7 @@
|
|
|
223
245
|
>
|
|
224
246
|
<!-- Buttons -->
|
|
225
247
|
<button
|
|
226
|
-
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-
|
|
248
|
+
class="af-pagination-prev-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 rounded-s border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
227
249
|
@click="page--; pageInput = page.toString();" :disabled="page <= 1">
|
|
228
250
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
229
251
|
viewBox="0 0 14 10">
|
|
@@ -235,14 +257,14 @@
|
|
|
235
257
|
</span>
|
|
236
258
|
</button>
|
|
237
259
|
<button
|
|
238
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
260
|
+
class="af-pagination-first-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-r-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-darkListTablePaginationTextHover dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
239
261
|
@click="page = 1; pageInput = page.toString();" :disabled="page <= 1">
|
|
240
262
|
<!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
|
|
241
263
|
1
|
|
242
264
|
</button>
|
|
243
265
|
<div
|
|
244
266
|
contenteditable="true"
|
|
245
|
-
class="min-w-10 outline-none inline-block w-auto
|
|
267
|
+
class="af-pagination-input min-w-10 outline-none inline-block w-auto py-1.5 px-3 text-sm text-center text-lightListTablePaginationCurrentPageText border border-lightListTablePaginationBorder dark:border-darkListTablePaginationBorder dark:text-darkListTablePaginationCurrentPageText dark:bg-darkListTablePaginationBackgoround z-10"
|
|
246
268
|
@keydown="onPageKeydown($event)"
|
|
247
269
|
@input="onPageInput($event)"
|
|
248
270
|
@blur="validatePageInput()"
|
|
@@ -251,14 +273,14 @@
|
|
|
251
273
|
</div>
|
|
252
274
|
|
|
253
275
|
<button
|
|
254
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
276
|
+
class="af-pagination-last-page-button flex items-center py-1 px-3 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
255
277
|
@click="page = totalPages; pageInput = page.toString();" :disabled="page >= totalPages">
|
|
256
278
|
{{ totalPages }}
|
|
257
279
|
|
|
258
280
|
<!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
|
|
259
281
|
</button>
|
|
260
282
|
<button
|
|
261
|
-
class="flex items-center py-1 px-3 gap-1 text-sm font-medium text-
|
|
283
|
+
class="af-pagination-next-button flex items-center py-1 px-3 gap-1 text-sm font-medium text-lightListTablePaginationText focus:outline-none bg-lightListTablePaginationBackgoround border-l-0 rounded-e border border-lightListTablePaginationBorder hover:bg-lightListTablePaginationBackgoroundHover hover:text-lightListTablePaginationTextHover focus:z-10 focus:ring-4 focus:ring-lightListTablePaginationFocusRing dark:focus:ring-darkListTablePaginationFocusRing dark:bg-darkListTablePaginationBackgoround dark:text-darkListTablePaginationText dark:border-darkListTablePaginationBorder dark:hover:text-white dark:hover:bg-darkListTablePaginationBackgoroundHover disabled:opacity-50"
|
|
262
284
|
@click="page++; pageInput = page.toString();" :disabled="page >= totalPages">
|
|
263
285
|
<span class="hidden sm:inline">{{ $t('Next') }}</span>
|
|
264
286
|
<svg class="w-3.5 h-3.5 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
@@ -270,9 +292,9 @@
|
|
|
270
292
|
</div>
|
|
271
293
|
|
|
272
294
|
<!-- Help text -->
|
|
273
|
-
<span class="text-sm text-
|
|
274
|
-
<span v-if="((page || 1) - 1) * pageSize + 1 > totalRows">{{ $t('Wrong Page') }} </span>
|
|
275
|
-
<template v-else>
|
|
295
|
+
<span class="ml-4 text-sm text-lightListTablePaginationHelpText dark:text-darkListTablePaginationHelpText">
|
|
296
|
+
<span v-if="((((page || 1) - 1) * pageSize + 1 > totalRows) && totalRows > 0)">{{ $t('Wrong Page') }} </span>
|
|
297
|
+
<template v-else-if="resource && totalRows > 0">
|
|
276
298
|
|
|
277
299
|
<span class="hidden sm:inline">
|
|
278
300
|
<i18n-t keypath="Showing {from} to {to} of {total} Entries" tag="p" >
|
|
@@ -328,8 +350,9 @@ import {
|
|
|
328
350
|
} from '@iconify-prerendered/vue-flowbite';
|
|
329
351
|
import router from '@/router';
|
|
330
352
|
import { Tooltip } from '@/afcl';
|
|
331
|
-
import type { AdminForthResourceCommon } from '@/types/Common';
|
|
353
|
+
import type { AdminForthResourceCommon, AdminForthResourceColumnCommon } from '@/types/Common';
|
|
332
354
|
import adminforth from '@/adminforth';
|
|
355
|
+
import Checkbox from '@/afcl/Checkbox.vue';
|
|
333
356
|
|
|
334
357
|
const coreStore = useCoreStore();
|
|
335
358
|
const { t } = useI18n();
|
|
@@ -361,7 +384,7 @@ const emits = defineEmits([
|
|
|
361
384
|
const checkboxesInternal: Ref<any[]> = ref([]);
|
|
362
385
|
const pageInput = ref('1');
|
|
363
386
|
const page = ref(1);
|
|
364
|
-
const sort = ref([]);
|
|
387
|
+
const sort: Ref<Array<{field: string, direction: string}>> = ref([]);
|
|
365
388
|
|
|
366
389
|
|
|
367
390
|
const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1);
|
|
@@ -370,11 +393,11 @@ const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.tot
|
|
|
370
393
|
watch(() => page.value, (newPage) => {
|
|
371
394
|
emits('update:page', newPage);
|
|
372
395
|
});
|
|
373
|
-
async function onPageKeydown(event) {
|
|
396
|
+
async function onPageKeydown(event: any) {
|
|
374
397
|
// page input should accept only numbers, arrow keys and backspace
|
|
375
398
|
if (['Enter', 'Space'].includes(event.code) ||
|
|
376
399
|
(!['Backspace', 'ArrowRight', 'ArrowLeft'].includes(event.code)
|
|
377
|
-
&& isNaN(String.fromCharCode(event.keyCode)))) {
|
|
400
|
+
&& isNaN(Number(String.fromCharCode(event.keyCode || 0))))) {
|
|
378
401
|
event.preventDefault();
|
|
379
402
|
if (event.code === 'Enter') {
|
|
380
403
|
validatePageInput();
|
|
@@ -395,7 +418,7 @@ watch(() => props.checkboxes, (newCheckboxes) => {
|
|
|
395
418
|
checkboxesInternal.value = newCheckboxes;
|
|
396
419
|
});
|
|
397
420
|
|
|
398
|
-
watch(() => props.sort, (newSort) => {
|
|
421
|
+
watch(() => props.sort, (newSort: any) => {
|
|
399
422
|
sort.value = newSort;
|
|
400
423
|
});
|
|
401
424
|
|
|
@@ -406,17 +429,17 @@ watch(() => props.page, (newPage) => {
|
|
|
406
429
|
page.value = newPage;
|
|
407
430
|
});
|
|
408
431
|
|
|
409
|
-
const rowRefs = useTemplateRef('rowRefs');
|
|
410
|
-
const headerRefs = useTemplateRef('headerRefs');
|
|
411
|
-
const rowHeights = ref([]);
|
|
412
|
-
const columnWidths = ref([]);
|
|
432
|
+
const rowRefs = useTemplateRef<HTMLElement[]>('rowRefs');
|
|
433
|
+
const headerRefs = useTemplateRef<HTMLElement[]>('headerRefs');
|
|
434
|
+
const rowHeights = ref<number[]>([]);
|
|
435
|
+
const columnWidths = ref<number[]>([]);
|
|
413
436
|
watch(() => props.rows, (newRows) => {
|
|
414
437
|
// rows are set to null when new records are loading
|
|
415
|
-
rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el) => el.offsetHeight);
|
|
416
|
-
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el) => el.offsetWidth)];
|
|
438
|
+
rowHeights.value = newRows || !rowRefs.value ? [] : rowRefs.value.map((el: HTMLElement) => el.offsetHeight);
|
|
439
|
+
columnWidths.value = newRows || !headerRefs.value ? [] : [48, ...headerRefs.value.map((el: HTMLElement) => el.offsetWidth)];
|
|
417
440
|
});
|
|
418
441
|
|
|
419
|
-
function addToCheckedValues(id) {
|
|
442
|
+
function addToCheckedValues(id: any) {
|
|
420
443
|
if (checkboxesInternal.value.includes(id)) {
|
|
421
444
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== id);
|
|
422
445
|
} else {
|
|
@@ -425,17 +448,17 @@ function addToCheckedValues(id) {
|
|
|
425
448
|
checkboxesInternal.value = [ ...checkboxesInternal.value ]
|
|
426
449
|
}
|
|
427
450
|
|
|
428
|
-
const columnsListed = computed(() => props.resource?.columns?.filter(c => c.showIn
|
|
451
|
+
const columnsListed = computed(() => props.resource?.columns?.filter((c: AdminForthResourceColumnCommon) => c.showIn?.list));
|
|
429
452
|
|
|
430
|
-
async function selectAll(
|
|
453
|
+
async function selectAll() {
|
|
431
454
|
if (!allFromThisPageChecked.value) {
|
|
432
|
-
props.rows
|
|
455
|
+
props.rows?.forEach((r) => {
|
|
433
456
|
if (!checkboxesInternal.value.includes(r._primaryKeyValue)) {
|
|
434
457
|
checkboxesInternal.value.push(r._primaryKeyValue)
|
|
435
458
|
}
|
|
436
459
|
});
|
|
437
460
|
} else {
|
|
438
|
-
props.rows
|
|
461
|
+
props.rows?.forEach((r) => {
|
|
439
462
|
checkboxesInternal.value = checkboxesInternal.value.filter((item) => item !== r._primaryKeyValue);
|
|
440
463
|
});
|
|
441
464
|
}
|
|
@@ -448,15 +471,15 @@ const allFromThisPageChecked = computed(() => {
|
|
|
448
471
|
if (!props.rows || !props.rows.length) return false;
|
|
449
472
|
return props.rows.every((r) => checkboxesInternal.value.includes(r._primaryKeyValue));
|
|
450
473
|
});
|
|
451
|
-
const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
|
|
452
|
-
const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
|
|
474
|
+
const ascArr = computed(() => sort.value.filter((s: any) => s.direction === 'asc').map((s: any) => s.field));
|
|
475
|
+
const descArr = computed(() => sort.value.filter((s: any) => s.direction === 'desc').map((s: any) => s.field));
|
|
453
476
|
|
|
454
477
|
|
|
455
|
-
function onSortButtonClick(event, field) {
|
|
478
|
+
function onSortButtonClick(event: any, field: any) {
|
|
456
479
|
// if ctrl key is pressed, add to sort otherwise sort by this field
|
|
457
480
|
// in any case if field is already in sort, toggle direction
|
|
458
481
|
|
|
459
|
-
const sortIndex = sort.value.findIndex((s) => s.field === field);
|
|
482
|
+
const sortIndex = sort.value.findIndex((s: any) => s.field === field);
|
|
460
483
|
if (sortIndex === -1) {
|
|
461
484
|
// field is not in sort, add it
|
|
462
485
|
if (event.ctrlKey) {
|
|
@@ -477,11 +500,11 @@ function onSortButtonClick(event, field) {
|
|
|
477
500
|
|
|
478
501
|
const clickTarget = ref(null);
|
|
479
502
|
|
|
480
|
-
async function onClick(e,row) {
|
|
503
|
+
async function onClick(e: any,row: any) {
|
|
481
504
|
if(clickTarget.value === e.target) return;
|
|
482
505
|
clickTarget.value = e.target;
|
|
483
506
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
484
|
-
if (window.getSelection()
|
|
507
|
+
if (window.getSelection()?.toString()) return;
|
|
485
508
|
else {
|
|
486
509
|
if (row._clickUrl === null) {
|
|
487
510
|
// user asked to nothing on click
|
|
@@ -496,7 +519,7 @@ async function onClick(e,row) {
|
|
|
496
519
|
router.resolve({
|
|
497
520
|
name: 'resource-show',
|
|
498
521
|
params: {
|
|
499
|
-
resourceId: props.resource
|
|
522
|
+
resourceId: props.resource?.resourceId,
|
|
500
523
|
primaryKey: row._primaryKeyValue,
|
|
501
524
|
},
|
|
502
525
|
}).href,
|
|
@@ -514,7 +537,7 @@ async function onClick(e,row) {
|
|
|
514
537
|
router.push({
|
|
515
538
|
name: 'resource-show',
|
|
516
539
|
params: {
|
|
517
|
-
resourceId: props.resource
|
|
540
|
+
resourceId: props.resource?.resourceId,
|
|
518
541
|
primaryKey: row._primaryKeyValue,
|
|
519
542
|
},
|
|
520
543
|
});
|
|
@@ -523,7 +546,7 @@ async function onClick(e,row) {
|
|
|
523
546
|
}
|
|
524
547
|
}
|
|
525
548
|
|
|
526
|
-
async function deleteRecord(row) {
|
|
549
|
+
async function deleteRecord(row: any) {
|
|
527
550
|
const data = await adminforth.confirm({
|
|
528
551
|
message: t('Are you sure you want to delete this item?'),
|
|
529
552
|
yes: t('Delete'),
|
|
@@ -535,7 +558,7 @@ async function deleteRecord(row) {
|
|
|
535
558
|
path: '/delete_record',
|
|
536
559
|
method: 'POST',
|
|
537
560
|
body: {
|
|
538
|
-
resourceId: props.resource
|
|
561
|
+
resourceId: props.resource?.resourceId,
|
|
539
562
|
primaryKey: row._primaryKeyValue,
|
|
540
563
|
}
|
|
541
564
|
});
|
|
@@ -553,16 +576,16 @@ async function deleteRecord(row) {
|
|
|
553
576
|
}
|
|
554
577
|
}
|
|
555
578
|
|
|
556
|
-
const actionLoadingStates = ref({});
|
|
579
|
+
const actionLoadingStates = ref<Record<string | number, boolean>>({});
|
|
557
580
|
|
|
558
|
-
async function startCustomAction(actionId, row) {
|
|
581
|
+
async function startCustomAction(actionId: string, row: any) {
|
|
559
582
|
actionLoadingStates.value[actionId] = true;
|
|
560
583
|
|
|
561
584
|
const data = await callAdminForthApi({
|
|
562
585
|
path: '/start_custom_action',
|
|
563
586
|
method: 'POST',
|
|
564
587
|
body: {
|
|
565
|
-
resourceId: props.resource
|
|
588
|
+
resourceId: props.resource?.resourceId,
|
|
566
589
|
actionId: actionId,
|
|
567
590
|
recordId: row._primaryKeyValue
|
|
568
591
|
}
|
|
@@ -600,7 +623,7 @@ async function startCustomAction(actionId, row) {
|
|
|
600
623
|
}
|
|
601
624
|
}
|
|
602
625
|
|
|
603
|
-
function onPageInput(event) {
|
|
626
|
+
function onPageInput(event: any) {
|
|
604
627
|
pageInput.value = event.target.innerText;
|
|
605
628
|
}
|
|
606
629
|
|
|
@@ -735,4 +758,15 @@ input[type="checkbox"][disabled] {
|
|
|
735
758
|
input[type="checkbox"]:not([disabled]) {
|
|
736
759
|
@apply cursor-pointer;
|
|
737
760
|
}
|
|
761
|
+
td.sticky-column {
|
|
762
|
+
@apply sticky left-0 z-10;
|
|
763
|
+
&:not(:first-child) {
|
|
764
|
+
@apply left-[56px];
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
tr:not(:first-child):hover {
|
|
768
|
+
td.sticky-column {
|
|
769
|
+
@apply bg-lightListTableRowHover dark:bg-darkListTableRowHover;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
738
772
|
</style>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
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 => {
|