adminforth 1.2.41 → 1.2.43
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/dataConnectors/mongo.ts +1 -2
- package/dist/dataConnectors/mongo.js +1 -2
- package/dist/index.js +5 -0
- package/dist/spa/index.html +4 -4
- package/dist/spa/package-lock.json +206 -5
- package/dist/spa/package.json +8 -2
- package/dist/spa/public/assets/favicon.png +0 -0
- package/dist/spa/src/App.vue +171 -72
- package/dist/spa/src/assets/logo.svg +19 -1
- package/dist/spa/src/components/AcceptModal.vue +3 -10
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +19 -6
- package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
- package/dist/spa/src/components/CustomRangePicker.vue +148 -0
- package/dist/spa/src/components/Dropdown.vue +19 -5
- package/dist/spa/src/components/Filters.vue +91 -37
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +163 -103
- package/dist/spa/src/components/ResourceListTable.vue +419 -0
- package/dist/spa/src/components/Toast.vue +66 -0
- package/dist/spa/src/components/ValueRenderer.vue +22 -6
- package/dist/spa/src/composables/useFrontendApi.ts +26 -0
- package/dist/spa/src/composables/useStores.ts +113 -0
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/router/index.ts +30 -19
- package/dist/spa/src/spa_types/core.ts +51 -0
- package/dist/spa/src/stores/core.ts +65 -59
- package/dist/spa/src/stores/filters.ts +22 -0
- package/dist/spa/src/stores/modal.ts +13 -3
- package/dist/spa/src/stores/toast.ts +15 -0
- package/dist/spa/src/stores/user.ts +54 -0
- package/dist/spa/src/utils.ts +65 -8
- package/dist/spa/src/views/CreateView.vue +76 -16
- package/dist/spa/src/views/EditView.vue +76 -14
- package/dist/spa/src/views/ListView.vue +88 -360
- package/dist/spa/src/views/LoginView.vue +17 -5
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +114 -19
- package/dist/spa/tailwind.config.js +5 -2
- package/dist/spa/vite.config.ts +6 -0
- package/index.ts +5 -0
- package/package.json +1 -1
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/src/views/HomeView.vue +0 -8
- package/dist/spa/src/views/HomeView.vue +0 -8
- package/dist/types.js +0 -30
|
@@ -2,20 +2,27 @@
|
|
|
2
2
|
<div class="relative">
|
|
3
3
|
<Teleport to="body">
|
|
4
4
|
<Filters
|
|
5
|
-
:columns="coreStore.
|
|
6
|
-
v-model:filters="filters"
|
|
5
|
+
:columns="coreStore.resource?.columns"
|
|
7
6
|
:columnsMinMax="columnsMinMax" :show="filtersShow"
|
|
8
7
|
@hide="filtersShow = false"
|
|
9
8
|
/>
|
|
10
9
|
</Teleport>
|
|
11
10
|
|
|
11
|
+
<component
|
|
12
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.beforeBreadcrumbs || []"
|
|
13
|
+
:is="getCustomComponent(c)"
|
|
14
|
+
:meta="c.meta"
|
|
15
|
+
:resource="coreStore.resource"
|
|
16
|
+
:adminUser="coreStore.adminUser"
|
|
17
|
+
/>
|
|
18
|
+
|
|
12
19
|
<BreadcrumbsWithButtons>
|
|
13
20
|
<button
|
|
14
21
|
@click="()=>{checkboxes = []}"
|
|
15
22
|
v-if="checkboxes.length"
|
|
16
23
|
data-tooltip-target="tooltip-remove-all"
|
|
17
24
|
data-tooltip-placement="bottom"
|
|
18
|
-
class="flex gap-1 items-center py-1 px-3 me-2
|
|
25
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
19
26
|
>
|
|
20
27
|
<IconBanOutline class="w-5 h-5 "/>
|
|
21
28
|
|
|
@@ -27,9 +34,11 @@
|
|
|
27
34
|
</button>
|
|
28
35
|
|
|
29
36
|
<button
|
|
30
|
-
v-if="checkboxes.length"
|
|
37
|
+
v-if="checkboxes.length"
|
|
38
|
+
v-for="(action,i) in coreStore.resource?.options?.bulkActions"
|
|
39
|
+
:key="action.id"
|
|
31
40
|
@click="startBulkAction(action.id)"
|
|
32
|
-
class="flex gap-1 items-center py-1 px-3
|
|
41
|
+
class="flex gap-1 items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-default border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
33
42
|
:class="{'bg-red-100 text-red-800 border-red-400 dark:bg-red-700 dark:text-red-400 dark:border-red-400':action.state==='danger', 'bg-green-100 text-green-800 border-green-400 dark:bg-green-700 dark:text-green-400 dark:border-green-400':action.state==='success',
|
|
34
43
|
'bg-blue-100 text-blue-800 border-blue-400 dark:bg-blue-700 dark:text-blue-400 dark:border-blue-400':action.state==='active',
|
|
35
44
|
}"
|
|
@@ -42,437 +51,158 @@
|
|
|
42
51
|
{{ `${action.label} (${checkboxes.length})` }}
|
|
43
52
|
</button>
|
|
44
53
|
|
|
45
|
-
<RouterLink v-if="allowedActions
|
|
54
|
+
<RouterLink v-if="coreStore.resource?.options?.allowedActions?.create"
|
|
46
55
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
47
|
-
class="flex items-center py-1 px-3
|
|
56
|
+
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
48
57
|
>
|
|
49
58
|
<IconPlusOutline class="w-4 h-4 me-2"/>
|
|
50
59
|
Create
|
|
51
60
|
</RouterLink>
|
|
52
61
|
|
|
53
62
|
<button
|
|
54
|
-
class="flex gap-1 items-center py-1 px-3 me-2
|
|
63
|
+
class="flex gap-1 items-center py-1 px-3 me-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 rounded-default"
|
|
55
64
|
@click="()=>{filtersShow = !filtersShow}"
|
|
65
|
+
v-if="coreStore.resource?.options?.allowedActions?.filter"
|
|
56
66
|
>
|
|
57
67
|
<IconFilterOutline class="w-4 h-4 me-2"/>
|
|
58
68
|
Filter
|
|
59
69
|
<span
|
|
60
70
|
class="bg-red-100 text-red-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-gray-700 dark:text-red-400 border border-red-400"
|
|
61
|
-
v-if="filters.length">
|
|
62
|
-
{{ filters.length }}
|
|
71
|
+
v-if="filtersStore.filters.length">
|
|
72
|
+
{{ filtersStore.filters.length }}
|
|
63
73
|
</span>
|
|
64
74
|
</button>
|
|
65
75
|
</BreadcrumbsWithButtons>
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
<component
|
|
78
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.afterBreadcrumbs || []"
|
|
79
|
+
:is="getCustomComponent(c)"
|
|
80
|
+
:meta="c.meta"
|
|
81
|
+
:resource="coreStore.resource"
|
|
82
|
+
:adminUser="coreStore.adminUser"
|
|
83
|
+
/>
|
|
84
|
+
|
|
85
|
+
<ResourceListTable
|
|
86
|
+
:resource="coreStore.resource"
|
|
87
|
+
:rows="rows"
|
|
88
|
+
@update:page="page = $event"
|
|
89
|
+
@update:sort="sort = $event"
|
|
90
|
+
@update:checkboxes="checkboxes = $event"
|
|
91
|
+
@update:records="getList"
|
|
92
|
+
:pageSize="pageSize"
|
|
93
|
+
:totalRows="totalRows"
|
|
94
|
+
:checkboxes="checkboxes"
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
<component
|
|
98
|
+
v-for="c in coreStore?.resourceOptions?.pageInjections?.list?.bottom || []"
|
|
99
|
+
:is="getCustomComponent(c)"
|
|
100
|
+
:meta="c.meta"
|
|
101
|
+
:resource="coreStore.resource"
|
|
102
|
+
:adminUser="coreStore.adminUser"
|
|
103
|
+
/>
|
|
81
104
|
|
|
82
|
-
<div class="flex items-center justify-between h-16 pt-4" v-for="i in new Array(10)">
|
|
83
|
-
<div>
|
|
84
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
|
|
85
|
-
<div class="w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
|
|
86
|
-
</div>
|
|
87
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12"></div>
|
|
88
|
-
</div>
|
|
89
|
-
<span class="sr-only">Loading...</span>
|
|
90
|
-
</div>
|
|
91
|
-
|
|
92
|
-
<table v-else class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
93
|
-
<thead class="text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
94
|
-
<tr>
|
|
95
|
-
<th scope="col" class="p-4">
|
|
96
|
-
<div v-if="rows && rows.length" class="flex items-center">
|
|
97
|
-
<input id="checkbox-all-search" type="checkbox" :checked="allFromThisPageChecked" @change="selectAll()"
|
|
98
|
-
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">
|
|
99
|
-
<label for="checkbox-all-search" class="sr-only">checkbox</label>
|
|
100
|
-
</div>
|
|
101
|
-
</th>
|
|
102
|
-
|
|
103
|
-
<th v-for="c in columnsListed" scope="col" class="px-6 py-3">
|
|
104
|
-
<div @click="() => c.sortable && onSortButtonClick(c.name)" class="flex items-center " :class="{'cursor-pointer':c.sortable}">
|
|
105
|
-
{{ c.label }}
|
|
106
|
-
|
|
107
|
-
<div v-if="c.sortable"
|
|
108
|
-
:style="{ 'color':ascArr.includes(c.name)?'green':descArr.includes(c.name)?'red':'currentColor'}">
|
|
109
|
-
<svg v-if="ascArr.includes(c.name) || descArr.includes(c.name)" class="w-3 h-3 ms-1.5"
|
|
110
|
-
:class="{'rotate-180':descArr.includes(c.name)}" viewBox="0 0 24 24">
|
|
111
|
-
<path
|
|
112
|
-
d="M8.574 11.024h6.852a2.075 2.075 0 0 0 1.847-1.086 1.9 1.9 0 0 0-.11-1.986L13.736 2.9a2.122 2.122 0 0 0-3.472 0L6.837 7.952a1.9 1.9 0 0 0-.11 1.986 2.074 2.074 0 0 0 1.847"/>
|
|
113
|
-
</svg>
|
|
114
|
-
<svg v-else class="w-3 h-3 ms-1.5 opacity-30" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
|
115
|
-
fill='currentColor'
|
|
116
|
-
viewBox="0 0 24 24">
|
|
117
|
-
<path
|
|
118
|
-
d="M8.574 11.024h6.852a2.075 2.075 0 0 0 1.847-1.086 1.9 1.9 0 0 0-.11-1.986L13.736 2.9a2.122 2.122 0 0 0-3.472 0L6.837 7.952a1.9 1.9 0 0 0-.11 1.986 2.074 2.074 0 0 0 1.847 1.086Zm6.852 1.952H8.574a2.072 2.072 0 0 0-1.847 1.087 1.9 1.9 0 0 0 .11 1.985l3.426 5.05a2.123 2.123 0 0 0 3.472 0l3.427-5.05a1.9 1.9 0 0 0 .11-1.985 2.074 2.074 0 0 0-1.846-1.087Z"/>
|
|
119
|
-
</svg>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</th>
|
|
123
|
-
|
|
124
|
-
<th scope="col" class="px-6 py-3">
|
|
125
|
-
Actions
|
|
126
|
-
</th>
|
|
127
|
-
</tr>
|
|
128
|
-
</thead>
|
|
129
|
-
<tbody>
|
|
130
|
-
<tr v-if="!rows" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
131
|
-
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
132
|
-
|
|
133
|
-
<div role="status"
|
|
134
|
-
class="max-w p-4 space-y-4 divide-y divide-gray-200 rounded animate-pulse dark:divide-gray-700 md:p-6 dark:border-gray-700">
|
|
135
|
-
<div class="flex items-center justify-between">
|
|
136
|
-
<div>
|
|
137
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
|
|
138
|
-
<div class="w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
|
|
139
|
-
</div>
|
|
140
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12"></div>
|
|
141
|
-
</div>
|
|
142
|
-
<div class="flex items-center justify-between pt-4">
|
|
143
|
-
<div>
|
|
144
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
|
|
145
|
-
<div class="w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
|
|
146
|
-
</div>
|
|
147
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12"></div>
|
|
148
|
-
</div>
|
|
149
|
-
<div class="flex items-center justify-between pt-4">
|
|
150
|
-
<div>
|
|
151
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
|
|
152
|
-
<div class="w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
|
|
153
|
-
</div>
|
|
154
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12"></div>
|
|
155
|
-
</div>
|
|
156
|
-
<div class="flex items-center justify-between pt-4">
|
|
157
|
-
<div>
|
|
158
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
|
|
159
|
-
<div class="w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
|
|
160
|
-
</div>
|
|
161
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12"></div>
|
|
162
|
-
</div>
|
|
163
|
-
<div class="flex items-center justify-between pt-4">
|
|
164
|
-
<div>
|
|
165
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-600 w-24 mb-2.5"></div>
|
|
166
|
-
<div class="w-32 h-2 bg-gray-200 rounded-full dark:bg-gray-700"></div>
|
|
167
|
-
</div>
|
|
168
|
-
<div class="h-2.5 bg-gray-300 rounded-full dark:bg-gray-700 w-12"></div>
|
|
169
|
-
</div>
|
|
170
|
-
<span class="sr-only">Loading...</span>
|
|
171
|
-
</div>
|
|
172
|
-
</td>
|
|
173
|
-
</tr>
|
|
174
|
-
<tr v-else-if="rows.length === 0" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
175
|
-
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
176
|
-
|
|
177
|
-
<div id="toast-simple"
|
|
178
|
-
class=" mx-auto my-5 flex items-center w-full max-w-xs p-4 space-x-4 rtl:space-x-reverse text-gray-500 bg-white divide-x rtl:divide-x-reverse divide-gray-200 dark:text-gray-400 dark:divide-gray-700 space-x dark:bg-gray-800"
|
|
179
|
-
role="alert">
|
|
180
|
-
<IconInboxOutline class="w-6 h-6 text-gray-500 dark:text-gray-400"/>
|
|
181
|
-
<div class="ps-4 text-sm font-normal">No items here yet</div>
|
|
182
|
-
</div>
|
|
183
|
-
|
|
184
|
-
</td>
|
|
185
|
-
</tr>
|
|
186
|
-
|
|
187
|
-
<tr v-else v-for="(row, rowI) in rows" :key="row.id"
|
|
188
|
-
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
|
|
189
|
-
<td class="w-4 p-4">
|
|
190
|
-
<div class="flex items center">
|
|
191
|
-
<input
|
|
192
|
-
id="checkbox-table-search-1"
|
|
193
|
-
type="checkbox"
|
|
194
|
-
:checked="checkboxes.includes(row.id)"
|
|
195
|
-
@change="(e)=>{addToCheckedValues(row.id)}"
|
|
196
|
-
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">
|
|
197
|
-
<label for="checkbox-table-search-1" class="sr-only">checkbox</label>
|
|
198
|
-
</div>
|
|
199
|
-
</td>
|
|
200
|
-
<td v-for="c in columnsListed" class="px-6 py-4">
|
|
201
|
-
<ValueRenderer :column="c" :row="row"/>
|
|
202
|
-
</td>
|
|
203
|
-
<td class="flex items-center px-6 py-4">
|
|
204
|
-
|
|
205
|
-
<RouterLink
|
|
206
|
-
:to="{ name: 'resource-show', params: { resourceId: $route.params.resourceId, primaryKey: row._primaryKeyValue } }"
|
|
207
|
-
class="font-medium text-blue-600 dark:text-blue-500 hover:underline"
|
|
208
|
-
:data-tooltip-target="`tooltip-show-${rowI}`"
|
|
209
|
-
>
|
|
210
|
-
<IconEyeSolid class="w-5 h-5 me-2"/>
|
|
211
|
-
</RouterLink>
|
|
212
|
-
|
|
213
|
-
<div :id="`tooltip-show-${rowI}`"
|
|
214
|
-
role="tooltip"
|
|
215
|
-
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
|
216
|
-
Show item
|
|
217
|
-
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
218
|
-
</div>
|
|
219
|
-
|
|
220
|
-
<RouterLink v-if="allowedActions.edit"
|
|
221
|
-
:to="{ name: 'resource-edit', params: { resourceId: $route.params.resourceId, primaryKey: row._primaryKeyValue } }"
|
|
222
|
-
class="font-medium text-blue-600 dark:text-blue-500 hover:underline ms-3"
|
|
223
|
-
:data-tooltip-target="`tooltip-edit-${rowI}`"
|
|
224
|
-
>
|
|
225
|
-
<IconPenSolid class="w-5 h-5 me-2"/>
|
|
226
|
-
</RouterLink>
|
|
227
|
-
|
|
228
|
-
<div :id="`tooltip-edit-${rowI}`"
|
|
229
|
-
role="tooltip"
|
|
230
|
-
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
|
231
|
-
Edit
|
|
232
|
-
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
<button v-if="allowedActions.delete"
|
|
236
|
-
class="font-medium text-red-600 dark:text-red-500 hover:underline ms-3"
|
|
237
|
-
:data-tooltip-target="`tooltip-delete-${rowI}`"
|
|
238
|
-
@click="showDeleteModal(row)"
|
|
239
|
-
>
|
|
240
|
-
<IconTrashBinSolid class="w-5 h-5 me-2"/>
|
|
241
|
-
</button>
|
|
242
|
-
|
|
243
|
-
<div :id="`tooltip-delete-${rowI}`"
|
|
244
|
-
role="tooltip"
|
|
245
|
-
class="absolute z-10 invisible inline-block px-3 py-2 text-sm font-medium text-white transition-opacity duration-300 bg-gray-900 rounded-lg shadow-sm opacity-0 tooltip dark:bg-gray-700">
|
|
246
|
-
Delete
|
|
247
|
-
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
248
|
-
</div>
|
|
249
|
-
</td>
|
|
250
|
-
</tr>
|
|
251
|
-
</tbody>
|
|
252
|
-
</table>
|
|
253
|
-
</div>
|
|
254
|
-
<!-- pagination -->
|
|
255
|
-
<div class="flex flex-col items-center mt-4 xs:flex-row xs:justify-between xs:items-center"
|
|
256
|
-
v-if="rows && totalRows >= pageSize && totalRows > 0"
|
|
257
|
-
>
|
|
258
|
-
<!-- Help text -->
|
|
259
|
-
<span class="text-sm text-gray-700 dark:text-gray-400">
|
|
260
|
-
Showing <span class="font-semibold text-gray-900 dark:text-white">
|
|
261
|
-
{{ (page - 1) * pageSize + 1 }}
|
|
262
|
-
</span> to <span class="font-semibold text-gray-900 dark:text-white">
|
|
263
|
-
{{ Math.min(page * pageSize, totalRows) }}
|
|
264
|
-
</span> of <span class="font-semibold text-gray-900 dark:text-white">{{
|
|
265
|
-
totalRows
|
|
266
|
-
}}</span> Entries
|
|
267
|
-
</span>
|
|
268
|
-
<div class="inline-flex mt-2 xs:mt-0">
|
|
269
|
-
<!-- Buttons -->
|
|
270
|
-
<button
|
|
271
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-r-0 rounded-s border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
272
|
-
@click="page--" :disabled="page <= 1">
|
|
273
|
-
<svg class="w-3.5 h-3.5 me-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
274
|
-
viewBox="0 0 14 10">
|
|
275
|
-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
276
|
-
d="M13 5H1m0 0 4 4M1 5l4-4"/>
|
|
277
|
-
</svg>
|
|
278
|
-
Prev
|
|
279
|
-
</button>
|
|
280
|
-
<button
|
|
281
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-r-0 border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
282
|
-
@click="page = 1" :disabled="page <= 1">
|
|
283
|
-
<!-- <IconChevronDoubleLeftOutline class="w-4 h-4" /> -->
|
|
284
|
-
1
|
|
285
|
-
</button>
|
|
286
|
-
<input type="text"
|
|
287
|
-
class="w-10 py-1.5 px-3 text-sm text-center text-gray-700 border border-gray-300 dark:border-gray-700 dark:text-gray-400 dark:bg-gray-800 z-10"
|
|
288
|
-
v-model="page"/>
|
|
289
|
-
|
|
290
|
-
<button
|
|
291
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-l-0 border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
292
|
-
@click="page = totalPages" :disabled="page >= totalPages">
|
|
293
|
-
{{ totalPages }}
|
|
294
|
-
<!-- <IconChevronDoubleRightOutline class="w-4 h-4" /> -->
|
|
295
|
-
</button>
|
|
296
|
-
<button
|
|
297
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-gray-900 focus:outline-none bg-white border-l-0 rounded-e border border-gray-300 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
298
|
-
|
|
299
|
-
@click="page++" :disabled="page >= totalPages">
|
|
300
|
-
Next
|
|
301
|
-
<svg class="w-3.5 h-3.5 ms-2 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
|
|
302
|
-
viewBox="0 0 14 10">
|
|
303
|
-
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
304
|
-
d="M1 5h12m0 0L9 1m4 4L9 9"/>
|
|
305
|
-
</svg>
|
|
306
|
-
</button>
|
|
307
|
-
</div>
|
|
308
|
-
</div>
|
|
309
105
|
</div>
|
|
310
106
|
</template>
|
|
311
107
|
|
|
312
108
|
<script setup>
|
|
313
|
-
import {ref, onMounted, watch, computed} from 'vue';
|
|
314
|
-
import {callAdminForthApi, getIcon} from '@/utils';
|
|
315
|
-
import {useRoute} from 'vue-router';
|
|
316
|
-
import {useCoreStore} from '@/stores/core';
|
|
317
|
-
import {useModalStore} from '@/stores/modal';
|
|
318
109
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
319
|
-
import
|
|
110
|
+
import ResourceListTable from '@/components/ResourceListTable.vue';
|
|
111
|
+
import { useCoreStore } from '@/stores/core';
|
|
112
|
+
import { useModalStore } from '@/stores/modal';
|
|
113
|
+
import { useFiltersStore } from '@/stores/filters';
|
|
114
|
+
import { callAdminForthApi, getIcon } from '@/utils';
|
|
115
|
+
import { initFlowbite } from 'flowbite';
|
|
116
|
+
import { computed, onMounted, ref, watch } from 'vue';
|
|
117
|
+
import { useRoute } from 'vue-router';
|
|
118
|
+
import { showErrorTost } from '@/composables/useFrontendApi'
|
|
320
119
|
|
|
321
120
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
121
|
+
import { getCustomComponent } from '@/utils';
|
|
322
122
|
|
|
323
|
-
import {
|
|
324
|
-
IconChevronDoubleLeftOutline,
|
|
325
|
-
IconChevronDoubleRightOutline,
|
|
326
|
-
IconInboxFullSolid,
|
|
327
|
-
IconInboxOutline,
|
|
328
|
-
IconPlusOutline
|
|
329
|
-
} from '@iconify-prerendered/vue-flowbite';
|
|
330
123
|
|
|
331
124
|
import {
|
|
125
|
+
IconBanOutline,
|
|
332
126
|
IconEyeSolid,
|
|
333
|
-
|
|
127
|
+
IconFilterOutline,
|
|
334
128
|
IconPenSolid,
|
|
335
|
-
|
|
129
|
+
IconTrashBinSolid,
|
|
130
|
+
IconInboxOutline,
|
|
131
|
+
IconPlusOutline
|
|
336
132
|
} from '@iconify-prerendered/vue-flowbite';
|
|
337
133
|
|
|
338
134
|
import Filters from '@/components/Filters.vue';
|
|
339
135
|
|
|
340
136
|
const filtersShow = ref(false);
|
|
341
137
|
|
|
342
|
-
|
|
343
138
|
const coreStore = useCoreStore();
|
|
344
139
|
const modalStore = useModalStore();
|
|
140
|
+
const filtersStore = useFiltersStore();
|
|
345
141
|
|
|
346
142
|
const route = useRoute();
|
|
347
|
-
const checkboxes = ref([]);
|
|
348
143
|
|
|
349
144
|
const page = ref(1);
|
|
350
|
-
const filters =
|
|
145
|
+
const filters = filtersStore.filters;
|
|
351
146
|
const columnsMinMax = ref({});
|
|
352
147
|
const sort = ref([]);
|
|
353
|
-
const fetchStatus = ref({pending: false, error: null, success: false});
|
|
354
148
|
|
|
355
149
|
const rows = ref(null);
|
|
356
150
|
const totalRows = ref(0);
|
|
357
|
-
const
|
|
358
|
-
const allowedActions = ref({});
|
|
359
|
-
|
|
151
|
+
const checkboxes = ref([]);
|
|
360
152
|
|
|
361
153
|
const DEFAULT_PAGE_SIZE = 10;
|
|
362
154
|
|
|
363
|
-
const columnsListed = computed(() => coreStore.resourceColumns?.filter(c => c.showIn.includes('list')));
|
|
364
155
|
|
|
365
|
-
|
|
366
|
-
console.log('select all');
|
|
367
|
-
rows.value.forEach((r) => {
|
|
368
|
-
if (!checkboxes.value.includes(r.id)) {
|
|
369
|
-
checkboxes.value.push(r.id)
|
|
370
|
-
} else {
|
|
371
|
-
checkboxes.value = checkboxes.value.filter((item) => item !== r.id)
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
// checkboxes.value = rows.value.map((v) => v.id);
|
|
375
|
-
}
|
|
156
|
+
const pageSize = computed(() => coreStore.resource?.options?.listPageSize || DEFAULT_PAGE_SIZE);
|
|
376
157
|
|
|
377
|
-
const pageSize = computed(() => coreStore.resourceById[route.params.resourceId]?.pageSize || DEFAULT_PAGE_SIZE);
|
|
378
|
-
const totalPages = computed(() => Math.ceil(totalRows.value / pageSize.value));
|
|
379
|
-
const allFromThisPageChecked = computed(() => {
|
|
380
|
-
if (!rows.value) return false;
|
|
381
|
-
return rows.value.every((r) => checkboxes.value.includes(r.id));
|
|
382
|
-
});
|
|
383
|
-
const ascArr = computed(() => sort.value.filter((s) => s.direction === 'asc').map((s) => s.field));
|
|
384
|
-
const descArr = computed(() => sort.value.filter((s) => s.direction === 'desc').map((s) => s.field));
|
|
385
158
|
|
|
386
159
|
watch([page], async () => {
|
|
387
|
-
await
|
|
160
|
+
await getList();
|
|
388
161
|
});
|
|
389
162
|
|
|
390
|
-
watch(
|
|
163
|
+
watch(()=>filtersStore.filters, async () => {
|
|
391
164
|
page.value = 1;
|
|
165
|
+
checkboxes.value = [];
|
|
166
|
+
|
|
392
167
|
await getList();
|
|
393
168
|
}, {deep: true});
|
|
394
169
|
|
|
395
170
|
watch([sort], async () => {
|
|
396
|
-
await
|
|
171
|
+
await getList();
|
|
397
172
|
}, {deep: true});
|
|
398
173
|
|
|
399
|
-
function onSortButtonClick(field) {
|
|
400
|
-
if (fetchStatus.value.pending) return;
|
|
401
|
-
const sortIndex = sort.value.findIndex((s) => s.field === field);
|
|
402
|
-
console.log('sortIndex', sortIndex);
|
|
403
|
-
if (sortIndex === -1) {
|
|
404
|
-
sort.value = [{field, direction: 'asc'}, ...sort.value];
|
|
405
|
-
} else {
|
|
406
|
-
const sortField = sort.value[sortIndex];
|
|
407
|
-
if (sortField.direction === 'asc') {
|
|
408
|
-
sort.value[sortIndex].direction = 'desc';
|
|
409
|
-
} else {
|
|
410
|
-
sort.value.splice(sortIndex, 1);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
174
|
|
|
415
175
|
async function getList() {
|
|
416
176
|
rows.value = null;
|
|
417
|
-
fetchStatus.value.pending = true;
|
|
418
177
|
const data = await callAdminForthApi({
|
|
419
178
|
path: '/get_resource_data',
|
|
420
179
|
method: 'POST',
|
|
421
180
|
body: {
|
|
181
|
+
source: 'list',
|
|
422
182
|
resourceId: route.params.resourceId,
|
|
423
183
|
limit: pageSize.value,
|
|
424
184
|
offset: (page.value - 1) * pageSize.value,
|
|
425
|
-
filters: filters
|
|
185
|
+
filters: filtersStore.filters,
|
|
426
186
|
sort: sort.value,
|
|
427
187
|
}
|
|
428
188
|
});
|
|
429
|
-
|
|
189
|
+
if (data.error) {
|
|
190
|
+
showErrorTost(data.error);
|
|
191
|
+
rows.value = [];
|
|
192
|
+
totalRows.value = 0;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
430
195
|
rows.value = data.data?.map(row => {
|
|
431
|
-
row._primaryKeyValue = row[coreStore.
|
|
196
|
+
row._primaryKeyValue = row[coreStore.resource.columns.find(c => c.primaryKey).name];
|
|
432
197
|
return row;
|
|
433
198
|
});
|
|
434
199
|
totalRows.value = data.total;
|
|
435
|
-
allCheckedActions.value = data.options?.bulkActions || [];
|
|
436
|
-
allowedActions.value = data.options?.allowedActions;
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
setTimeout(() => {
|
|
440
|
-
initFlowbite();
|
|
441
|
-
console.log('initFlowbite');
|
|
442
|
-
});
|
|
443
200
|
}
|
|
444
201
|
|
|
445
202
|
|
|
446
|
-
function showDeleteModal(row) {
|
|
447
|
-
if (!coreStore.config?.deleteConfirmation) {
|
|
448
|
-
return deleteRecord(row);
|
|
449
|
-
}
|
|
450
|
-
modalStore.setModalContent({
|
|
451
|
-
content: 'Are you sure you want to delete this item?',
|
|
452
|
-
acceptText: 'Delete',
|
|
453
|
-
cancelText: 'Cancel',
|
|
454
|
-
});
|
|
455
|
-
modalStore.setOnAcceptFunction(() => {
|
|
456
|
-
return deleteRecord(row)
|
|
457
|
-
})
|
|
458
|
-
console.log('row', row);
|
|
459
|
-
modalStore.togleModal();
|
|
460
203
|
|
|
461
|
-
}
|
|
462
204
|
|
|
463
|
-
|
|
464
|
-
await callAdminForthApi({
|
|
465
|
-
path: '/delete_record',
|
|
466
|
-
method: 'POST',
|
|
467
|
-
body: {
|
|
468
|
-
resourceId: route.params.resourceId,
|
|
469
|
-
primaryKey: row._primaryKeyValue,
|
|
470
|
-
recordId: row.id
|
|
471
|
-
}
|
|
472
|
-
});
|
|
473
|
-
await getList();
|
|
474
|
-
modalStore.resetmodalState()
|
|
475
|
-
}
|
|
205
|
+
|
|
476
206
|
|
|
477
207
|
async function startBulkAction(actionId) {
|
|
478
208
|
const data = await callAdminForthApi({
|
|
@@ -491,16 +221,11 @@ async function startBulkAction(actionId) {
|
|
|
491
221
|
await getList();
|
|
492
222
|
}
|
|
493
223
|
|
|
494
|
-
|
|
495
|
-
if (checkboxes.value.includes(id)) {
|
|
496
|
-
checkboxes.value = checkboxes.value.filter((item) => item !== id);
|
|
497
|
-
} else {
|
|
498
|
-
checkboxes.value.push(id);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
224
|
+
|
|
501
225
|
|
|
502
226
|
async function init() {
|
|
503
|
-
|
|
227
|
+
|
|
228
|
+
await coreStore.fetchResourceFull({
|
|
504
229
|
resourceId: route.params.resourceId
|
|
505
230
|
});
|
|
506
231
|
|
|
@@ -515,12 +240,15 @@ async function init() {
|
|
|
515
240
|
}
|
|
516
241
|
|
|
517
242
|
onMounted(async () => {
|
|
243
|
+
initFlowbite();
|
|
244
|
+
|
|
518
245
|
await init();
|
|
246
|
+
|
|
519
247
|
});
|
|
520
248
|
|
|
521
249
|
// on route param change
|
|
522
250
|
watch(() => route.params.resourceId, async () => {
|
|
523
|
-
|
|
251
|
+
filtersStore.setFilters([]);
|
|
524
252
|
checkboxes.value = [];
|
|
525
253
|
sort.value = [];
|
|
526
254
|
await init();
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800"
|
|
3
3
|
:style="coreStore.config?.loginBackgroundImage ? {
|
|
4
|
-
'background-image': 'url(' + coreStore.config?.loginBackgroundImage + ')',
|
|
4
|
+
'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
|
|
5
5
|
'background-size': 'cover',
|
|
6
6
|
'background-position': 'center',
|
|
7
7
|
'background-blend-mode': 'darken'
|
|
8
8
|
}: {}"
|
|
9
9
|
>
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
<!-- Main modal -->
|
|
11
12
|
<div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
12
13
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
13
14
|
<!-- Modal content -->
|
|
14
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
15
|
+
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black" >
|
|
15
16
|
<!-- Modal header -->
|
|
16
17
|
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
17
18
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
@@ -78,16 +79,21 @@
|
|
|
78
79
|
|
|
79
80
|
<script setup>
|
|
80
81
|
|
|
81
|
-
import { onMounted, ref } from 'vue';
|
|
82
|
+
import { onMounted, ref, watchEffect } from 'vue';
|
|
82
83
|
import { useCoreStore } from '@/stores/core';
|
|
84
|
+
import { useUserStore } from '@/stores/user';
|
|
83
85
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
84
|
-
import { callAdminForthApi } from '@/utils';
|
|
86
|
+
import { callAdminForthApi, loadFile } from '@/utils';
|
|
85
87
|
import { useRouter } from 'vue-router';
|
|
88
|
+
import { initFlowbite } from 'flowbite'
|
|
89
|
+
|
|
86
90
|
|
|
87
91
|
const router = useRouter();
|
|
88
92
|
const inProgress = ref(false);
|
|
89
93
|
|
|
90
94
|
const coreStore = useCoreStore();
|
|
95
|
+
const user = useUserStore();
|
|
96
|
+
|
|
91
97
|
|
|
92
98
|
const showPw = ref(false);
|
|
93
99
|
|
|
@@ -112,8 +118,14 @@ async function login() {
|
|
|
112
118
|
error.value = resp.error;
|
|
113
119
|
} else {
|
|
114
120
|
error.value = null;
|
|
121
|
+
user.authorize()
|
|
115
122
|
router.push('/');
|
|
123
|
+
await router.isReady();
|
|
116
124
|
await coreStore.fetchMenuAndResource();
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
initFlowbite();
|
|
127
|
+
});
|
|
128
|
+
|
|
117
129
|
}
|
|
118
130
|
|
|
119
131
|
}
|