adminforth 1.0.54 → 1.0.56
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/dist/modules/codeInjector.js +21 -32
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +1 -189
- package/dist/spa/package.json +1 -6
- package/dist/spa/spa/src/App.vue +1 -1
- package/dist/spa/spa/src/components/Filters.vue +2 -2
- package/dist/spa/spa/src/components/ResourceForm.vue +3 -3
- package/dist/spa/spa/src/views/CreateView.vue +2 -2
- package/dist/spa/spa/src/views/EditView.vue +2 -2
- package/dist/spa/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/spa/src/views/ListView.vue +4 -4
- package/dist/spa/spa/src/views/ShowView.vue +2 -2
- package/dist/spa/src/App.vue +57 -76
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/src/components/Dropdown.vue +4 -13
- package/dist/spa/src/components/Filters.vue +22 -55
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +99 -157
- package/dist/spa/src/components/ValueRenderer.vue +1 -11
- package/dist/spa/src/router/index.ts +2 -3
- package/dist/spa/src/stores/core.ts +33 -22
- package/dist/spa/src/utils.ts +3 -5
- package/dist/spa/src/views/CreateView.vue +7 -15
- package/dist/spa/src/views/EditView.vue +8 -36
- package/dist/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/src/views/ListView.vue +23 -35
- package/dist/spa/src/views/LoginView.vue +1 -1
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +9 -20
- package/dist/spa/tailwind.config.js +2 -5
- package/modules/codeInjector.ts +25 -36
- package/package.json +1 -1
- package/spa/src/App.vue +1 -1
- package/spa/src/components/Filters.vue +2 -2
- package/spa/src/components/ResourceForm.vue +3 -3
- package/spa/src/views/CreateView.vue +2 -2
- package/spa/src/views/EditView.vue +2 -2
- package/spa/src/views/ListView.vue +4 -4
- package/spa/src/views/ShowView.vue +2 -2
- package/dist/spa/src/components/CustomRangePicker.vue +0 -148
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
</BreadcrumbsWithButtons>
|
|
66
66
|
|
|
67
67
|
<!-- table -->
|
|
68
|
-
<div class="relative overflow-x-auto shadow-
|
|
68
|
+
<div class="relative overflow-x-auto shadow-md sm:rounded-lg dark:shadow-2xl">
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
<!-- skelet loader -->
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
</div>
|
|
91
91
|
|
|
92
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-
|
|
93
|
+
<thead class="text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
94
94
|
<tr>
|
|
95
95
|
<th scope="col" class="p-4">
|
|
96
96
|
<div v-if="rows && rows.length" class="flex items-center">
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
</tr>
|
|
128
128
|
</thead>
|
|
129
129
|
<tbody>
|
|
130
|
-
<tr v-if="!rows" class="bg-
|
|
130
|
+
<tr v-if="!rows" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
131
131
|
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
132
132
|
|
|
133
133
|
<div role="status"
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
</div>
|
|
172
172
|
</td>
|
|
173
173
|
</tr>
|
|
174
|
-
<tr v-else-if="rows.length === 0" class="bg-
|
|
174
|
+
<tr v-else-if="rows.length === 0" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
175
175
|
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
176
176
|
|
|
177
177
|
<div id="toast-simple"
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
</tr>
|
|
186
186
|
|
|
187
187
|
<tr v-else v-for="(row, rowI) in rows" :key="row.id"
|
|
188
|
-
class="bg-
|
|
188
|
+
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
|
|
189
189
|
<td class="w-4 p-4">
|
|
190
190
|
<div class="flex items center">
|
|
191
191
|
<input
|
|
@@ -198,12 +198,7 @@
|
|
|
198
198
|
</div>
|
|
199
199
|
</td>
|
|
200
200
|
<td v-for="c in columnsListed" class="px-6 py-4">
|
|
201
|
-
|
|
202
|
-
<component
|
|
203
|
-
:is="listComponentsPerColumn[c.name] || ValueRenderer"
|
|
204
|
-
:column="c"
|
|
205
|
-
:row="row"
|
|
206
|
-
/>
|
|
201
|
+
<ValueRenderer :column="c" :row="row"/>
|
|
207
202
|
</td>
|
|
208
203
|
<td class="flex items-center px-6 py-4">
|
|
209
204
|
|
|
@@ -315,28 +310,29 @@
|
|
|
315
310
|
</template>
|
|
316
311
|
|
|
317
312
|
<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
318
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
319
|
-
import {
|
|
320
|
-
import { useModalStore } from '@/stores/modal';
|
|
321
|
-
import { callAdminForthApi, getIcon } from '@/utils';
|
|
322
|
-
import { initFlowbite } from 'flowbite';
|
|
323
|
-
import { computed, onMounted, ref, watch } from 'vue';
|
|
324
|
-
import { useRoute } from 'vue-router';
|
|
319
|
+
import {initFlowbite} from 'flowbite'
|
|
325
320
|
|
|
326
321
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
327
|
-
import { getCustomComponent } from '@/utils';
|
|
328
322
|
|
|
329
323
|
import {
|
|
330
|
-
|
|
331
|
-
|
|
324
|
+
IconChevronDoubleLeftOutline,
|
|
325
|
+
IconChevronDoubleRightOutline,
|
|
326
|
+
IconInboxFullSolid,
|
|
327
|
+
IconInboxOutline,
|
|
328
|
+
IconPlusOutline
|
|
332
329
|
} from '@iconify-prerendered/vue-flowbite';
|
|
333
330
|
|
|
334
331
|
import {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
IconTrashBinSolid
|
|
332
|
+
IconEyeSolid,
|
|
333
|
+
IconTrashBinSolid,
|
|
334
|
+
IconPenSolid,
|
|
335
|
+
IconFilterOutline, IconBanOutline
|
|
340
336
|
} from '@iconify-prerendered/vue-flowbite';
|
|
341
337
|
|
|
342
338
|
import Filters from '@/components/Filters.vue';
|
|
@@ -361,6 +357,7 @@ const totalRows = ref(0);
|
|
|
361
357
|
const allCheckedActions = ref([]);
|
|
362
358
|
const allowedActions = ref({});
|
|
363
359
|
|
|
360
|
+
|
|
364
361
|
const DEFAULT_PAGE_SIZE = 10;
|
|
365
362
|
|
|
366
363
|
const columnsListed = computed(() => coreStore.resourceColumns?.filter(c => c.showIn.includes('list')));
|
|
@@ -379,7 +376,6 @@ async function selectAll(value) {
|
|
|
379
376
|
|
|
380
377
|
const pageSize = computed(() => coreStore.resourceById[route.params.resourceId]?.pageSize || DEFAULT_PAGE_SIZE);
|
|
381
378
|
const totalPages = computed(() => Math.ceil(totalRows.value / pageSize.value));
|
|
382
|
-
let listComponentsPerColumn = {};
|
|
383
379
|
const allFromThisPageChecked = computed(() => {
|
|
384
380
|
if (!rows.value) return false;
|
|
385
381
|
return rows.value.every((r) => checkboxes.value.includes(r.id));
|
|
@@ -423,7 +419,6 @@ async function getList() {
|
|
|
423
419
|
path: '/get_resource_data',
|
|
424
420
|
method: 'POST',
|
|
425
421
|
body: {
|
|
426
|
-
source: 'list',
|
|
427
422
|
resourceId: route.params.resourceId,
|
|
428
423
|
limit: pageSize.value,
|
|
429
424
|
offset: (page.value - 1) * pageSize.value,
|
|
@@ -431,14 +426,7 @@ async function getList() {
|
|
|
431
426
|
sort: sort.value,
|
|
432
427
|
}
|
|
433
428
|
});
|
|
434
|
-
|
|
435
|
-
if (column.component?.list) {
|
|
436
|
-
acc[column.name] = getCustomComponent(column.component.list);
|
|
437
|
-
}
|
|
438
|
-
return acc;
|
|
439
|
-
}, {});
|
|
440
|
-
|
|
441
|
-
fetchStatus.value.pending = false;
|
|
429
|
+
fetchStatus.value.pending = false;
|
|
442
430
|
rows.value = data.data?.map(row => {
|
|
443
431
|
row._primaryKeyValue = row[coreStore.resourceColumns.find(c => c.primaryKey).name];
|
|
444
432
|
return row;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<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
12
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
13
13
|
<!-- Modal content -->
|
|
14
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700
|
|
14
|
+
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
15
15
|
<!-- Modal header -->
|
|
16
16
|
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
17
17
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
</div>
|
|
28
28
|
<div
|
|
29
29
|
v-else
|
|
30
|
-
class="relative overflow-x-auto shadow-
|
|
30
|
+
class="relative overflow-x-auto shadow-md sm:rounded-lg"
|
|
31
31
|
>
|
|
32
32
|
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
33
|
-
<thead class="text-xs text-gray-700 uppercase bg-
|
|
33
|
+
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
34
34
|
<tr>
|
|
35
35
|
<th scope="col" class="px-6 py-3">
|
|
36
36
|
Field
|
|
@@ -42,17 +42,13 @@
|
|
|
42
42
|
</thead>
|
|
43
43
|
<tbody>
|
|
44
44
|
<tr v-for="column in coreStore.resourceColumns?.filter(c => c.showIn.includes('show'))" :key="column.name"
|
|
45
|
-
class="bg-
|
|
45
|
+
class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700"
|
|
46
46
|
>
|
|
47
47
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
48
48
|
{{ column.label }}
|
|
49
49
|
</td>
|
|
50
50
|
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
51
|
-
<
|
|
52
|
-
:is="showComponentsPerColumn[column.name] || ValueRenderer"
|
|
53
|
-
:column="column"
|
|
54
|
-
:row="coreStore.record"
|
|
55
|
-
/>
|
|
51
|
+
<ValueRenderer :column="column" :row="coreStore.record" />
|
|
56
52
|
</td>
|
|
57
53
|
</tr>
|
|
58
54
|
|
|
@@ -67,19 +63,18 @@
|
|
|
67
63
|
|
|
68
64
|
<script setup>
|
|
69
65
|
|
|
66
|
+
import { ref, computed, onMounted } from 'vue';
|
|
67
|
+
import { useRoute } from 'vue-router';
|
|
68
|
+
import { callAdminForthApi } from '@/utils';
|
|
70
69
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
71
|
-
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
72
|
-
import { useCoreStore } from '@/stores/core';
|
|
73
|
-
import { getCustomComponent } from '@/utils';
|
|
74
70
|
import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
|
|
75
|
-
import {
|
|
76
|
-
import
|
|
71
|
+
import { useCoreStore } from '@/stores/core';
|
|
72
|
+
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
77
73
|
|
|
78
74
|
|
|
79
75
|
const item = ref(null);
|
|
80
76
|
const route = useRoute();
|
|
81
77
|
const loading = ref(false);
|
|
82
|
-
let showComponentsPerColumn = {};
|
|
83
78
|
|
|
84
79
|
const coreStore = useCoreStore();
|
|
85
80
|
|
|
@@ -92,12 +87,6 @@ onMounted(async () => {
|
|
|
92
87
|
resourceId: route.params.resourceId,
|
|
93
88
|
primaryKey: route.params.primaryKey,
|
|
94
89
|
});
|
|
95
|
-
showComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
96
|
-
if (column.component?.show) {
|
|
97
|
-
acc[column.name] = getCustomComponent(column.component.show);
|
|
98
|
-
}
|
|
99
|
-
return acc;
|
|
100
|
-
}, {});
|
|
101
90
|
loading.value = false;
|
|
102
91
|
});
|
|
103
92
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/** @type {import('tailwindcss').Config} */
|
|
2
2
|
export default {
|
|
3
|
-
content: ["./src/**/*.{vue, js}",
|
|
3
|
+
content: ["./src/**/*.{vue, js}", "./node_modules/flowbite/**/*.js"],
|
|
4
4
|
theme: {
|
|
5
|
-
extend: {
|
|
6
|
-
/* IMPORTANT:ADMINFORTH TAILWIND STYLES */
|
|
7
|
-
}
|
|
5
|
+
extend: {},
|
|
8
6
|
},
|
|
9
|
-
|
|
10
7
|
darkMode: 'class',
|
|
11
8
|
plugins: [
|
|
12
9
|
require('flowbite/plugin'),
|
package/modules/codeInjector.ts
CHANGED
|
@@ -175,23 +175,27 @@ class CodeInjector {
|
|
|
175
175
|
|
|
176
176
|
// for each custom component generate import statement
|
|
177
177
|
const customComponentsDir = this.adminforth.config.customization?.customComponentsDir;
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (data.isFile()) {
|
|
189
|
-
const componentName = getComponentNameFromPath(filePath);
|
|
190
|
-
customComponentsImports += `import ${componentName} from '@/custom/${filePath}';\n`;
|
|
191
|
-
}
|
|
192
|
-
})
|
|
178
|
+
|
|
179
|
+
const customResourceComponents = [];
|
|
180
|
+
this.adminforth.config.resources.forEach((resource) => {
|
|
181
|
+
resource.columns.forEach((field) => {
|
|
182
|
+
if (field.component) {
|
|
183
|
+
Object.values(field.component).forEach((filePath) => {
|
|
184
|
+
if (!customResourceComponents.includes(filePath)) {
|
|
185
|
+
customResourceComponents.push(filePath);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
193
188
|
}
|
|
194
|
-
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
let customComponentsImports = '';
|
|
193
|
+
customResourceComponents.forEach((filePath) => {
|
|
194
|
+
const componentName = getComponentNameFromPath(filePath);
|
|
195
|
+
customComponentsImports += `import ${componentName} from '${filePath}';\n`;
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
|
|
195
199
|
|
|
196
200
|
// Generate Vue.component statements for each icon
|
|
197
201
|
const iconComponents = uniqueIcons.map((icon) => {
|
|
@@ -204,21 +208,10 @@ class CodeInjector {
|
|
|
204
208
|
|
|
205
209
|
// Generate Vue.component statements for each custom component
|
|
206
210
|
let customComponentsComponents = '';
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (err) {
|
|
212
|
-
console.log('Custom components importing error: ', err);
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
if (data.isFile()) {
|
|
216
|
-
const componentName = getComponentNameFromPath(filePath);
|
|
217
|
-
customComponentsComponents += `app.component('${componentName}', ${componentName});\n`;
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
}
|
|
211
|
+
customResourceComponents.forEach((filePath) => {
|
|
212
|
+
const componentName = getComponentNameFromPath(filePath);
|
|
213
|
+
customComponentsComponents += `app.component('${componentName}', ${componentName});\n`;
|
|
214
|
+
})
|
|
222
215
|
|
|
223
216
|
|
|
224
217
|
let imports = iconImports + '\n';
|
|
@@ -251,7 +244,7 @@ class CodeInjector {
|
|
|
251
244
|
|
|
252
245
|
|
|
253
246
|
|
|
254
|
-
/* generate custom
|
|
247
|
+
/* generate custom routes */
|
|
255
248
|
const homepageMenuItem = this.adminforth.config.menu.find((mi)=>mi.homepage);
|
|
256
249
|
let childrenHomePageMenuItem = this.adminforth.config.menu.find((mi)=>mi.children && mi.children?.find((mi)=>mi.homepage));
|
|
257
250
|
let childrenHomepage = childrenHomePageMenuItem?.children?.find((mi)=>mi.homepage)
|
|
@@ -260,10 +253,6 @@ class CodeInjector {
|
|
|
260
253
|
homePagePath=this.adminforth.config.menu.filter((mi)=>mi.path)[0]?.path || `/resource/${this.adminforth.config.menu.filter((mi)=>mi.children)[0]?.resourceId}` ;
|
|
261
254
|
}
|
|
262
255
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
256
|
routes += `{
|
|
268
257
|
path: '/',
|
|
269
258
|
name: 'home',
|
package/package.json
CHANGED
package/spa/src/App.vue
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
<aside
|
|
58
58
|
v-if="loggedIn"
|
|
59
59
|
|
|
60
|
-
id="logo-sidebar" class="fixed border-none top-0 left-0 z-
|
|
60
|
+
id="logo-sidebar" class="fixed border-none top-0 left-0 z-40 w-64 h-screen transition-transform -translate-x-full bg-nav-menu-bg border-r border- sm:translate-x-0 dark:bg-gray-800 dark:border-gray-700" aria-label="Sidebar">
|
|
61
61
|
<div class="h-full px-3 pb-4 overflow-y-auto bg-nav-menu-bg dark:bg-gray-800">
|
|
62
62
|
<div class="flex ms-2 md:me-24 m-4 ">
|
|
63
63
|
<img src="https://flowbite.com/docs/images/logo.svg" class="h-8 me-3" alt="FlowBite Logo" />
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<!-- drawer component -->
|
|
3
3
|
<div id="drawer-navigation"
|
|
4
4
|
|
|
5
|
-
class="fixed top-14 right-0 z-
|
|
5
|
+
class="fixed top-14 right-0 z-50 p-4 overflow-y-auto transition-transform translate-x-full bg-white w-80 dark:bg-gray-800 shadow-xl dark:shadow-black"
|
|
6
6
|
|
|
7
7
|
:class="show ? 'top-0 transform-none' : ''"
|
|
8
8
|
tabindex="-1" aria-labelledby="drawer-navigation-label"
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
</div>
|
|
93
93
|
</div>
|
|
94
94
|
|
|
95
|
-
<div v-if="show" drawer-backdrop="" class="bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-
|
|
95
|
+
<div v-if="show" drawer-backdrop="" class="bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-30"
|
|
96
96
|
@click="$emit('hide')">
|
|
97
97
|
</div>
|
|
98
98
|
</template>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div>
|
|
2
|
+
<div class="rounded-default">
|
|
3
3
|
|
|
4
4
|
<div
|
|
5
|
-
class="relative shadow-resourse-form-shadow sm:rounded-lg dark:shadow-2xl dark:shadow-black"
|
|
5
|
+
class="relative shadow-resourse-form-shadow sm:rounded-lg dark:shadow-2xl dark:shadow-black rounded-default"
|
|
6
6
|
>
|
|
7
7
|
<form autocomplete="off" @submit.prevent>
|
|
8
|
-
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
8
|
+
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
|
|
9
9
|
<thead class="text-xs text-gray-700 uppercase bg-form-view-heading dark:bg-gray-700 dark:text-gray-400">
|
|
10
10
|
<tr>
|
|
11
11
|
<th scope="col" class="px-6 py-3">
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<BreadcrumbsWithButtons>
|
|
5
5
|
<!-- save and cancle -->
|
|
6
6
|
<button @click="$router.back()"
|
|
7
|
-
class="flex items-center py-1 px-3 me-2 mb-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"
|
|
7
|
+
class="flex items-center py-1 px-3 me-2 mb-2 text-sm font-medium rounded-default 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"
|
|
8
8
|
>
|
|
9
9
|
Cancel
|
|
10
10
|
</button>
|
|
11
11
|
|
|
12
12
|
<button @click="saveRecord"
|
|
13
|
-
class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
13
|
+
class="flex items-center py-1 px-3 mb-2 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700 disabled:opacity-50"
|
|
14
14
|
:disabled="saving || (validating && !isValid)"
|
|
15
15
|
>
|
|
16
16
|
<svg v-if="saving" x
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<BreadcrumbsWithButtons>
|
|
5
5
|
<!-- save and cancle -->
|
|
6
6
|
<button @click="$router.back()"
|
|
7
|
-
class="flex items-center py-1 px-3 me-2 mb-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"
|
|
7
|
+
class="flex items-center py-1 px-3 me-2 mb-2 text-sm font-medium text-gray-900 rounded-default 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"
|
|
8
8
|
>
|
|
9
9
|
Cancel
|
|
10
10
|
</button>
|
|
11
11
|
|
|
12
12
|
<button @click="saveRecord"
|
|
13
|
-
class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
13
|
+
class="flex items-center py-1 px-3 mb-2 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
14
14
|
:disabled="saving || (validating && !isValid)"
|
|
15
15
|
>
|
|
16
16
|
<IconFloppyDiskSolid class="w-4 h-4" />
|
|
@@ -44,14 +44,14 @@
|
|
|
44
44
|
|
|
45
45
|
<RouterLink v-if="allowedActions.create"
|
|
46
46
|
:to="{ name: 'resource-create', params: { resourceId: $route.params.resourceId } }"
|
|
47
|
-
class="flex items-center py-1 px-3 mb-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"
|
|
47
|
+
class="flex items-center py-1 px-3 mb-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"
|
|
48
48
|
>
|
|
49
49
|
<IconPlusOutline class="w-4 h-4 me-2"/>
|
|
50
50
|
Create
|
|
51
51
|
</RouterLink>
|
|
52
52
|
|
|
53
53
|
<button
|
|
54
|
-
class="flex gap-1 items-center py-1 px-3 me-2 mb-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"
|
|
54
|
+
class="flex gap-1 items-center py-1 px-3 me-2 mb-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
55
|
@click="()=>{filtersShow = !filtersShow}"
|
|
56
56
|
>
|
|
57
57
|
<IconFilterOutline class="w-4 h-4 me-2"/>
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
</BreadcrumbsWithButtons>
|
|
66
66
|
|
|
67
67
|
<!-- table -->
|
|
68
|
-
<div class="relative overflow-x-auto shadow-list-table-shadow dark:shadow-black
|
|
68
|
+
<div class="relative overflow-x-auto shadow-list-table-shadow dark:shadow-black rounded-default">
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
<!-- skelet loader -->
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
<span class="sr-only">Loading...</span>
|
|
90
90
|
</div>
|
|
91
91
|
|
|
92
|
-
<table v-else class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
92
|
+
<table v-else class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
|
|
93
93
|
<thead class="text-xs text-view-table-heading-text bg-list-view-table-heading dark:bg-gray-700 dark:text-gray-400">
|
|
94
94
|
<tr>
|
|
95
95
|
<th scope="col" class="p-4">
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</RouterLink>
|
|
10
10
|
|
|
11
11
|
<button @click="deleteRecord"
|
|
12
|
-
class="flex items-center py-1 px-3 mb-2 text-sm font-medium text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
12
|
+
class="flex items-center py-1 px-3 mb-2 text-sm font-medium rounded-default text-red-600 focus:outline-none bg-white rounded border border-gray-300 hover:bg-gray-100 hover:text-red-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-red-500 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
|
13
13
|
>
|
|
14
14
|
<IconTrashBinSolid class="w-4 h-4" />
|
|
15
15
|
Delete
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
v-else
|
|
30
30
|
class="relative overflow-x-auto shadow-resourse-form-shadow "
|
|
31
31
|
>
|
|
32
|
-
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
32
|
+
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 rounded-default">
|
|
33
33
|
<thead class="text-xs text-gray-700 uppercase bg-form-view-heading dark:bg-gray-700 dark:text-gray-400">
|
|
34
34
|
<tr>
|
|
35
35
|
<th scope="col" class="px-6 py-3">
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="flex flex-wrap gap-2">
|
|
3
|
-
<input
|
|
4
|
-
type="number" aria-describedby="helper-text-explanation"
|
|
5
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
6
|
-
placeholder="From"
|
|
7
|
-
v-model="start"
|
|
8
|
-
>
|
|
9
|
-
|
|
10
|
-
<input
|
|
11
|
-
type="number" aria-describedby="helper-text-explanation"
|
|
12
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
13
|
-
placeholder="To"
|
|
14
|
-
v-model="end"
|
|
15
|
-
>
|
|
16
|
-
|
|
17
|
-
<button
|
|
18
|
-
v-if="isChanged"
|
|
19
|
-
type="button"
|
|
20
|
-
class="flex items-center p-0.5 ml-auto 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 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
21
|
-
@click="clear">Clear
|
|
22
|
-
</button>
|
|
23
|
-
|
|
24
|
-
<div class="w-full">
|
|
25
|
-
<vue-slider
|
|
26
|
-
class="custom-slider"
|
|
27
|
-
:dot-size="20"
|
|
28
|
-
height="7.99px"
|
|
29
|
-
:min="minFormatted"
|
|
30
|
-
:max="maxFormatted"
|
|
31
|
-
v-model="sliderValue"
|
|
32
|
-
@update:model-value="updateFromSlider($event)"
|
|
33
|
-
/>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
</template>
|
|
37
|
-
<script setup lang="ts">
|
|
38
|
-
import VueSlider from 'vue-slider-component';
|
|
39
|
-
import 'vue-slider-component/theme/antd.css'
|
|
40
|
-
import {computed, onMounted, ref, watch} from "vue";
|
|
41
|
-
import debounce from 'debounce'
|
|
42
|
-
|
|
43
|
-
const props = defineProps({
|
|
44
|
-
valueStart: {
|
|
45
|
-
default: '',
|
|
46
|
-
},
|
|
47
|
-
valueEnd: {
|
|
48
|
-
default: '',
|
|
49
|
-
},
|
|
50
|
-
min: {
|
|
51
|
-
default: 0,
|
|
52
|
-
},
|
|
53
|
-
max: {
|
|
54
|
-
default: 10,
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
|
|
59
|
-
|
|
60
|
-
const minFormatted = computed(() => Math.floor(props.min));
|
|
61
|
-
const maxFormatted = computed(() => Math.ceil(props.max));
|
|
62
|
-
|
|
63
|
-
const isChanged = computed(() => {
|
|
64
|
-
return start.value && start.value !== minFormatted.value || end.value && end.value !== maxFormatted.value;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const start = ref(props.valueStart);
|
|
68
|
-
const end = ref(props.valueEnd);
|
|
69
|
-
|
|
70
|
-
const sliderValue = ref([minFormatted.value, maxFormatted.value]);
|
|
71
|
-
|
|
72
|
-
const updateFromSlider =
|
|
73
|
-
debounce((value: [number, number]) => {
|
|
74
|
-
start.value = value[0] === minFormatted.value ? '': value[0];
|
|
75
|
-
end.value = value[1] === maxFormatted.value ? '': value[1];
|
|
76
|
-
}, 500);
|
|
77
|
-
|
|
78
|
-
onMounted(() => {
|
|
79
|
-
updateStartFromProps();
|
|
80
|
-
updateEndFromProps();
|
|
81
|
-
|
|
82
|
-
watch(() => props.valueStart, (value) => {
|
|
83
|
-
updateStartFromProps();
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
watch(() => props.valueEnd, (value) => {
|
|
87
|
-
updateEndFromProps();
|
|
88
|
-
});
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
function updateStartFromProps() {
|
|
92
|
-
start.value = props.valueStart;
|
|
93
|
-
setSliderValues(start.value, end.value)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function updateEndFromProps() {
|
|
97
|
-
end.value = props.valueEnd;
|
|
98
|
-
setSliderValues(start.value, end.value)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
watch(start, () => {
|
|
102
|
-
console.log('⚡ emit', start.value)
|
|
103
|
-
emit('update:valueStart', start.value)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
watch(end, () => {
|
|
107
|
-
console.log('⚡ emit', end.value)
|
|
108
|
-
emit('update:valueEnd', end.value);
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
const clear = () => {
|
|
112
|
-
start.value = ''
|
|
113
|
-
end.value = ''
|
|
114
|
-
setSliderValues('', '')
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function setSliderValues(start, end) {
|
|
118
|
-
sliderValue.value = [start || minFormatted.value, end || maxFormatted.value];
|
|
119
|
-
}
|
|
120
|
-
</script>
|
|
121
|
-
|
|
122
|
-
<style lang="scss" scoped>
|
|
123
|
-
.custom-slider {
|
|
124
|
-
&:deep(.vue-slider-rail) {
|
|
125
|
-
background-color: rgb(229 231 235);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
&:deep(.vue-slider-dot-handle) {
|
|
129
|
-
background-color: #1c64f2;
|
|
130
|
-
border: none;
|
|
131
|
-
box-shadow: none;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
&:deep(.vue-slider-dot-handle:hover) {
|
|
135
|
-
background-color: #1c64f2;
|
|
136
|
-
border: none;
|
|
137
|
-
box-shadow: none;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
&:deep(.vue-slider-process) {
|
|
141
|
-
background-color: rgb(225 239 254);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
&:deep(.vue-slider-process:hover) {
|
|
145
|
-
background-color: rgb(225 239 254);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
</style>
|