adminforth 1.1.31 → 1.1.33

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.
Files changed (105) hide show
  1. package/dist/index.js +9 -0
  2. package/dist/modules/codeInjector.js +19 -7
  3. package/dist/modules/styles.js +2 -2
  4. package/dist/spa/index.html +4 -4
  5. package/dist/spa/package-lock.json +206 -5
  6. package/dist/spa/package.json +8 -2
  7. package/dist/spa/src/App.vue +171 -72
  8. package/dist/spa/src/assets/logo.svg +19 -1
  9. package/dist/spa/src/components/AcceptModal.vue +3 -10
  10. package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
  11. package/dist/spa/src/components/CustomDatePicker.vue +19 -6
  12. package/dist/spa/src/components/CustomDateRangePicker.vue +14 -5
  13. package/dist/spa/src/components/Dropdown.vue +19 -5
  14. package/dist/spa/src/components/Filters.vue +91 -37
  15. package/dist/spa/src/components/MenuLink.vue +2 -2
  16. package/dist/spa/src/components/ResourceForm.vue +163 -103
  17. package/dist/spa/src/components/ValueRenderer.vue +22 -6
  18. package/dist/spa/src/main.ts +1 -1
  19. package/dist/spa/src/router/index.ts +30 -19
  20. package/dist/spa/src/stores/core.ts +65 -59
  21. package/dist/spa/src/stores/modal.ts +13 -3
  22. package/dist/spa/src/utils.ts +65 -8
  23. package/dist/spa/src/views/CreateView.vue +76 -16
  24. package/dist/spa/src/views/EditView.vue +76 -14
  25. package/dist/spa/src/views/ListView.vue +88 -360
  26. package/dist/spa/src/views/LoginView.vue +17 -5
  27. package/dist/spa/src/views/ResourceParent.vue +1 -1
  28. package/dist/spa/src/views/ShowView.vue +114 -19
  29. package/dist/spa/tailwind.config.js +5 -2
  30. package/dist/spa/vite.config.ts +6 -0
  31. package/index.ts +11 -0
  32. package/modules/codeInjector.ts +19 -1
  33. package/modules/styles.ts +2 -2
  34. package/package.json +1 -1
  35. package/spa/index.html +1 -1
  36. package/spa/src/App.vue +3 -2
  37. package/spa/src/components/ResourceForm.vue +1 -1
  38. package/types/AdminForthConfig.ts +15 -0
  39. package/dist/plugins/AccessControl/index.js +0 -13
  40. package/dist/plugins/AccessControl/types.js +0 -1
  41. package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +0 -248
  42. package/dist/spa/public/favicon.ico +0 -0
  43. package/dist/spa/spa/.eslintrc.cjs +0 -14
  44. package/dist/spa/spa/.vscode/extensions.json +0 -6
  45. package/dist/spa/spa/README.md +0 -39
  46. package/dist/spa/spa/env.d.ts +0 -1
  47. package/dist/spa/spa/index.html +0 -23
  48. package/dist/spa/spa/package-lock.json +0 -4359
  49. package/dist/spa/spa/package.json +0 -47
  50. package/dist/spa/spa/postcss.config.js +0 -6
  51. package/dist/spa/spa/public/favicon.ico +0 -0
  52. package/dist/spa/spa/src/App.vue +0 -270
  53. package/dist/spa/spa/src/assets/base.css +0 -0
  54. package/dist/spa/spa/src/assets/logo.svg +0 -19
  55. package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
  56. package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
  57. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  58. package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
  59. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
  60. package/dist/spa/spa/src/components/Dropdown.vue +0 -168
  61. package/dist/spa/spa/src/components/Filters.vue +0 -211
  62. package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
  63. package/dist/spa/spa/src/components/MenuLink.vue +0 -24
  64. package/dist/spa/spa/src/components/ResourceForm.vue +0 -265
  65. package/dist/spa/spa/src/components/ShowTableItem.vue +0 -14
  66. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
  67. package/dist/spa/spa/src/components/ValueRenderer.vue +0 -60
  68. package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
  69. package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
  70. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
  71. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
  72. package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
  73. package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
  74. package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
  75. package/dist/spa/spa/src/index.scss +0 -26
  76. package/dist/spa/spa/src/main.ts +0 -18
  77. package/dist/spa/spa/src/router/index.ts +0 -64
  78. package/dist/spa/spa/src/stores/core.ts +0 -144
  79. package/dist/spa/spa/src/stores/modal.ts +0 -48
  80. package/dist/spa/spa/src/utils.ts +0 -101
  81. package/dist/spa/spa/src/views/CreateView.vue +0 -163
  82. package/dist/spa/spa/src/views/EditView.vue +0 -165
  83. package/dist/spa/spa/src/views/HomeView.vue +0 -8
  84. package/dist/spa/spa/src/views/ListView.vue +0 -257
  85. package/dist/spa/spa/src/views/LoginView.vue +0 -134
  86. package/dist/spa/spa/src/views/ResourceParent.vue +0 -18
  87. package/dist/spa/spa/src/views/ShowView.vue +0 -188
  88. package/dist/spa/spa/tailwind.config.js +0 -15
  89. package/dist/spa/spa/tsconfig.app.json +0 -14
  90. package/dist/spa/spa/tsconfig.json +0 -11
  91. package/dist/spa/spa/tsconfig.node.json +0 -19
  92. package/dist/spa/spa/vite.config.ts +0 -49
  93. package/dist/spa/src/views/HomeView.vue +0 -8
  94. package/dist/types.js +0 -30
  95. /package/dist/spa/{spa/public → public/assets}/favicon.png +0 -0
  96. /package/dist/spa/{spa/src → src}/components/CustomRangePicker.vue +0 -0
  97. /package/dist/spa/{spa/src → src}/components/ResourceListTable.vue +0 -0
  98. /package/dist/spa/{spa/src → src}/components/Toast.vue +0 -0
  99. /package/dist/spa/{spa/src → src}/composables/useFrontendApi.ts +0 -0
  100. /package/dist/spa/{spa/src → src}/composables/useStores.ts +0 -0
  101. /package/dist/spa/{spa/src → src}/spa_types/core.ts +0 -0
  102. /package/dist/spa/{spa/src → src}/stores/filters.ts +0 -0
  103. /package/dist/spa/{spa/src → src}/stores/toast.ts +0 -0
  104. /package/dist/spa/{spa/src → src}/stores/user.ts +0 -0
  105. /package/spa/public/{favicon.png → assets/favicon.png} +0 -0
@@ -1,218 +0,0 @@
1
- <template>
2
- <div>
3
- <div class="mx-auto grid grid-cols-2 gap-4 mb-2">
4
- <div class="relative">
5
- <div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
6
- <IconCalendar class="w-4 h-4 text-gray-500 dark:text-gray-400"/>
7
- </div>
8
-
9
- <input ref="datepickerStartEl" type="text"
10
- class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full 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"
11
- placeholder="From">
12
- </div>
13
-
14
- <div class="relative">
15
- <div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
16
- <IconCalendar class="w-4 h-4 text-gray-500 dark:text-gray-400"/>
17
- </div>
18
-
19
- <input ref="datepickerEndEl" type="text"
20
- class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full 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"
21
- placeholder="To">
22
- </div>
23
- </div>
24
-
25
- <div>
26
- <div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: !showTimeInputs}">
27
- <div>
28
- <div class="relative">
29
- <div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
30
- <IconTime class="w-4 h-4 text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-700"/>
31
- </div>
32
-
33
- <input v-model="startTime" type="time" id="start-time"
34
- class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full 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"
35
- value="00:00" required/>
36
- </div>
37
- </div>
38
-
39
- <div>
40
- <div class="relative">
41
- <div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
42
- <IconTime class="w-4 h-4 text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-700"/>
43
- </div>
44
-
45
- <input v-model="endTime" type="time" id="end-time"
46
- class="bg-gray-50 border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full 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"
47
- value="00:00" required/>
48
- </div>
49
- </div>
50
- </div>
51
-
52
- <button type="button"
53
- class="text-blue-700 dark:text-blue-500 text-base font-medium hover:underline p-0 inline-flex items-center mb-2"
54
- @click="toggleTimeInputs">{{ showTimeInputs ? 'Hide time' : 'Show time' }}
55
- <svg class="w-8 h-8 ms-0.5 relative top-px" :class="{'rotate-180': showTimeInputs}" aria-hidden="true"
56
- xmlns="http://www.w3.org/2000/svg" width="24" height="24"
57
- fill="none" viewBox="0 0 24 24">
58
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
59
- d="m8 10 4 4 4-4"/>
60
- </svg>
61
- </button>
62
- </div>
63
- </div>
64
- </template>
65
- <script setup>
66
- import {ref, computed, onMounted, watch, onBeforeUnmount} from 'vue';
67
- import dayjs from 'dayjs';
68
- import utc from 'dayjs/plugin/utc';
69
-
70
- import {useCoreStore} from '@/stores/core';
71
-
72
- import Datepicker from "flowbite-datepicker/Datepicker";
73
- import IconCalendar from "@/components/icons/IconCalendar.vue";
74
- import IconTime from "@/components/icons/IconTime.vue";
75
-
76
- const coreStore = useCoreStore();
77
- dayjs.extend(utc)
78
-
79
- const props = defineProps({
80
- valueStart: {
81
- default: undefined
82
- },
83
- valueEnd: {
84
- default: undefined
85
- },
86
- column: {
87
- type: Object,
88
- },
89
- });
90
-
91
- const emit = defineEmits(['update:valueStart', 'update:valueEnd']);
92
-
93
- const datepickerStartEl = ref();
94
- const datepickerEndEl = ref();
95
-
96
- const showTimeInputs = ref(false);
97
-
98
- const startDate = ref('');
99
- const endDate = ref('');
100
-
101
- const startTime = ref('');
102
- const endTime = ref('');
103
-
104
- const datepickerStartObject = ref('')
105
- const datepickerEndObject = ref('')
106
-
107
- const start = computed(() => {
108
- if (!startDate.value) {
109
- return;
110
- }
111
-
112
- let date = dayjs(startDate.value);
113
-
114
- if (startTime.value) {
115
- date = addTimeToDate(formatTime(startTime.value), date)
116
- }
117
-
118
- return date.utc().toISOString();
119
- })
120
-
121
- const end = computed(() => {
122
- if (!endDate.value) {
123
- return;
124
- }
125
-
126
- let date = dayjs(endDate.value);
127
-
128
- if (endTime.value) {
129
- date = addTimeToDate(formatTime(endTime.value), date)
130
- } else {
131
- date = addTimeToDate('23:59:59', date)
132
- }
133
-
134
- return date.utc().toISOString();
135
- })
136
-
137
- function updateFromProps() {
138
- if (!props.valueStart) {
139
- datepickerStartEl.value.value = '';
140
- startTime.value = '';
141
- }
142
- if (!props.valueEnd) {
143
- datepickerEndEl.value.value = '';
144
- endTime.value = '';
145
- }
146
- }
147
-
148
- onMounted(() => {
149
- updateFromProps();
150
-
151
- watch(() => [props.valueStart, props.valueEnd], (value) => {
152
- updateFromProps();
153
- });
154
- })
155
-
156
- watch(start, () => {
157
- //console.log('⚡ emit', start.value)
158
- emit('update:valueStart', start.value)
159
- })
160
-
161
- watch(end, () => {
162
- //console.log('⚡ emit', end.value)
163
- emit('update:valueEnd', end.value)
164
- })
165
-
166
- function initDatepickers() {
167
-
168
- datepickerStartObject.value = new Datepicker(datepickerStartEl.value, {format: 'dd M yyyy'});
169
-
170
- datepickerEndObject.value = new Datepicker(datepickerEndEl.value, {format: 'dd M yyyy'});
171
- addChangeDateListener();
172
- }
173
-
174
- function addChangeDateListener() {
175
- datepickerStartEl.value.addEventListener('changeDate', setStartDate)
176
- datepickerEndEl.value.addEventListener('changeDate', setEndDate)
177
- }
178
-
179
- function removeChangeDateListener() {
180
- datepickerStartEl.value.removeEventListener('changeDate', setStartDate);
181
- datepickerEndEl.value.removeEventListener('changeDate', setEndDate);
182
- }
183
-
184
- function destroyDatepickerElement() {
185
- datepickerStartObject.value.destroy();
186
- datepickerEndObject.value.destroy();
187
- }
188
-
189
- function setStartDate(event) {
190
- startDate.value = event.detail.date
191
- }
192
-
193
- function setEndDate(event) {
194
- endDate.value = event.detail.date
195
- }
196
-
197
- function formatTime(time) {
198
- return time.split(':').map(Number).length === 2 ? time + ':00' : time;
199
- }
200
-
201
- function addTimeToDate(time, date) {
202
- const [hours, minutes, seconds] = time.split(':').map(Number)
203
- return date.hour(hours).minute(minutes).second(seconds)
204
- }
205
-
206
- const toggleTimeInputs = () => {
207
- showTimeInputs.value = !showTimeInputs.value
208
- }
209
-
210
- onMounted(() => {
211
- initDatepickers();
212
- });
213
-
214
- onBeforeUnmount(() => {
215
- removeChangeDateListener();
216
- destroyDatepickerElement();
217
- })
218
- </script>
@@ -1,168 +0,0 @@
1
- <template>
2
- <div class="relative inline-block w-full" id="dropd">
3
- <div class="relative">
4
- <input
5
- type="text"
6
- v-model="search"
7
- @focus="showDropdown = true"
8
- class="block w-full pl-3 pr-10 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 sm:text-sm transition duration-150 ease-in-out 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"
9
- :placeholder="selectedItems.length ? '' : placeholder || 'Select...'"
10
- />
11
- <div class="absolute inset-y-0 left-2 flex items-center pr-2 flex-wrap">
12
- {{ }}
13
- <div v-for="item in selectedItems" :key="item?.name" class="bg-blue-100 text-blue-800 text-xs font-medium me-2 px-2.5 py-0.5 rounded dark:bg-blue-900 dark:text-blue-300">
14
- <span>{{ item.label }}</span>
15
- <button
16
- type="button"
17
- @click="toogleItem(item)"
18
- class="z-index-100 flex-shrink-0 ml-1 h-4 w-4 -mr-1 rounded-full inline-flex items-center justify-center text-gray-400 hover:text-gray-500 focus:outline-none focus:text-gray-500 focus:bg-gray-100"
19
- >
20
- <span class="sr-only">Remove item</span>
21
- <svg class="h-2 w-2" stroke="currentColor" fill="none" viewBox="0 0 8 8">
22
- <path
23
- stroke-linecap="round"
24
- stroke-linejoin="round"
25
- stroke-width="1.5"
26
- d="M1 1l6 6m0-6L1 7"
27
- />
28
- </svg>
29
- </button>
30
- </div>
31
- </div>
32
- <div class="absolute inset-y-0 right-2 flex items-center pointer-events-none">
33
- <!-- triangle icon -->
34
- <IconCaretDownSolid v-if="!showDropdown" class="h-5 w-5 text-gray-400" />
35
- <IconCaretUpSolid v-else class="h-5 w-5 text-gray-400" />
36
- </div>
37
- </div>
38
- <div v-if="showDropdown" class="absolute z-10 mt-1 w-full bg-white shadow-lg dark:shadow-black rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm">
39
- <div
40
- v-for="item in filteredItems"
41
- :key="item.value"
42
- class="px-4 py-2 cursor-pointer hover:bg-gray-100"
43
- :class="{ 'bg-blue-100': selectedItems.includes(item) }"
44
- @click="toogleItem(item)"
45
- >
46
- <label :for="item.value">{{ item.label }}</label>
47
- </div>
48
- </div>
49
- </div>
50
- </template>
51
-
52
- <script setup>
53
- import { ref, computed, onMounted, onUnmounted, watch } from 'vue';
54
- import { IconCaretDownSolid, IconCaretUpSolid } from '@iconify-prerendered/vue-flowbite';
55
-
56
-
57
- const props = defineProps({
58
- options: Array,
59
- modelValue: {
60
- default: undefined,
61
- },
62
- allowCustom: {
63
- type: Boolean,
64
- default: false,
65
- },
66
- single: {
67
- type: Boolean,
68
- default: false,
69
- },
70
- placeholder: {
71
- type: String,
72
- default: '',
73
-
74
- },
75
- });
76
-
77
- const emit = defineEmits(['update:modelValue']);
78
-
79
- const search = ref('');
80
- const showDropdown = ref(false);
81
-
82
- const selectedItems = ref([]);
83
-
84
- function updateFromProps() {
85
- if (props.modelValue !== undefined) {
86
- if (props.single) {
87
- const el = props.options.find(item => item.value === props.modelValue);
88
- if (el) {
89
- selectedItems.value = [el];
90
- } else {
91
- selectedItems.value = [];
92
- }
93
- } else {
94
- selectedItems.value = props.options.filter(item => props.modelValue.includes(item.value));
95
- }
96
- }
97
- }
98
-
99
- onMounted(() => {
100
- updateFromProps();
101
-
102
- watch(() => props.modelValue, (value) => {
103
- updateFromProps();
104
- });
105
-
106
- watch(() => props.options, () => {
107
- updateFromProps();
108
- });
109
-
110
- addClickListener();
111
-
112
- });
113
-
114
- const filteredItems = computed(() => {
115
- return props.options.filter(item =>
116
- item.label.toLowerCase().includes(search.value.toLowerCase())
117
- );
118
- });
119
-
120
- const handleClickOutside = (event) => {
121
- if (!event.target.closest('#dropd')) {
122
- showDropdown.value = false;
123
- }
124
- };
125
-
126
- const addClickListener = () => {
127
- document.addEventListener('click', handleClickOutside);
128
- };
129
-
130
- const removeClickListener = () => {
131
- document.removeEventListener('click', handleClickOutside);
132
- };
133
-
134
- const toogleItem = (item) => {
135
- if (selectedItems.value.includes(item)) {
136
- selectedItems.value = selectedItems.value.filter(i => i !== item);
137
- } else {
138
- if (props.single) {
139
- selectedItems.value = [item];
140
- } else {
141
- selectedItems.value = [...selectedItems.value, item];
142
- }
143
- }
144
- if (props.single) {
145
- showDropdown.value = false;
146
- }
147
-
148
-
149
- const list = selectedItems.value.map(item => item.value);
150
- const updValue = list.length ? list : undefined;
151
- let emitValue;
152
- if (props.single) {
153
- emitValue = updValue ? updValue[0] : undefined;
154
- } else {
155
- emitValue = updValue;
156
- }
157
- console.log('⚡ emit', emitValue)
158
- emit('update:modelValue', emitValue);
159
-
160
- };
161
-
162
-
163
- onUnmounted(() => {
164
- removeClickListener();
165
- });
166
-
167
-
168
- </script>
@@ -1,211 +0,0 @@
1
- <template>
2
- <!-- drawer component -->
3
- <div id="drawer-navigation"
4
-
5
- class="fixed 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
-
7
- :class="show ? 'top-0 transform-none' : ''"
8
- tabindex="-1" aria-labelledby="drawer-navigation-label"
9
- :style="{ height: `calc(100vh ` }"
10
- >
11
- <h5 id="drawer-navigation-label" class="text-base font-semibold text-gray-500 uppercase dark:text-gray-400">
12
- Filters
13
-
14
- <button type="button" @click="$emit('hide')" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute end-2.5 inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" >
15
- <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
16
- <span class="sr-only">Close menu</span>
17
- </button>
18
- </h5>
19
-
20
- <div class="py-4 ">
21
- <ul class="space-y-3 font-medium">
22
- <li v-for="c in columnsWithFilter" :key="c">
23
- <p class="dark:text-gray-400">{{ c.label }}</p>
24
-
25
- <Dropdown
26
- v-if="c.foreignResource"
27
- :options="columnOptions[c.name] || []"
28
- @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event })"
29
- :modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
30
- />
31
- <Dropdown
32
- v-else-if="c.type === 'boolean'"
33
- :options="[{ label: 'Yes', value: true }, { label: 'No', value: false }, { label: 'Unset', value: null }]"
34
- @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event })"
35
- :modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
36
- />
37
-
38
- <Dropdown
39
- v-else-if="c.enum"
40
- :options="c.enum"
41
- :allowCustom="c.allowCustom"
42
- @update:modelValue="setFilterItem({ column: c, operator: 'in', value: $event })"
43
- :modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === 'in')?.value || []"
44
- />
45
-
46
- <input
47
- v-else-if="[ 'string', 'text' ].includes(c.type)"
48
- type="text" class="w-full py-1 px-2 border border-gray-300 rounded-md 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"
49
- placeholder="Search"
50
- @input="setFilterItem({ column: c, operator: 'ilike', value: $event.target.value || undefined })"
51
- :value="getFilterItem({ column: c, operator: 'ilike' })"
52
- >
53
-
54
- <CustomDateRangePicker
55
- v-else-if="['datetime'].includes(c.type)"
56
- :column="c"
57
- :valueStart="filtersStore.filters.find(f => f.field === c.name && f.operator === 'gte')?.value || undefined"
58
- @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
59
- :valueEnd="filtersStore.filters.find(f => f.field === c.name && f.operator === 'lte')?.value || undefined"
60
- @update:valueEnd="setFilterItem({ column: c, operator: 'lte', value: $event || undefined })"
61
- />
62
-
63
- <input
64
- v-else-if="[ 'date', 'time' ].includes(c.type)"
65
- type="text" class="w-full py-1 px-2 border border-gray-300 rounded-md"
66
- placeholder="Search datetime"
67
- @input="setFilterItem({ column: c, operator: 'ilike', value: $event.target.value || undefined })"
68
- :value="getFilterItem({ column: c, operator: 'ilike' })"
69
- >
70
-
71
- <CustomRangePicker
72
- v-else-if="['integer', 'decimal', 'float'].includes(c.type) && c.allowMinMaxQuery"
73
- :min="c.min"
74
- :max="c.max"
75
- :valueStart="getFilterItem({ column: c, operator: 'gte' })"
76
- @update:valueStart="setFilterItem({ column: c, operator: 'gte', value: $event || undefined })"
77
- :valueEnd="getFilterItem({ column: c, operator: 'lte' })"
78
- @update:valueEnd="setFilterItem({ column: c, operator: 'lte', value: $event || undefined })"
79
- />
80
-
81
- <div v-else-if="['integer', 'decimal', 'float'].includes(c.type)" class="flex gap-2">
82
- <input
83
- type="number" aria-describedby="helper-text-explanation"
84
- 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"
85
- placeholder="From"
86
- @input="setFilterItem({ column: c, operator: 'gte', value: $event.target.value || undefined })"
87
- :value="getFilterItem({ column: c, operator: 'gte' })"
88
- >
89
- <input
90
- type="number" aria-describedby="helper-text-explanation"
91
- 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"
92
- placeholder="To"
93
- @input="setFilterItem({ column: c, operator: 'lte', value: $event.target.value || undefined})"
94
- :value="getFilterItem({ column: c, operator: 'lte' })"
95
- >
96
- </div>
97
-
98
- </li>
99
- </ul>
100
- </div>
101
-
102
- <div class="flex justify-end gap-2">
103
- <button
104
- :disabled="!filtersStore.filters.length"
105
- type="button"
106
- 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 disabled:opacity-50 disabled:cursor-not-allowed"
107
- @click="clear">Clear all</button>
108
-
109
- </div>
110
- </div>
111
-
112
- <div v-if="show" class="bg-gray-900/50 dark:bg-gray-900/80 fixed inset-0 z-30"
113
- @click="$emit('hide')">
114
- </div>
115
- </template>
116
-
117
- <script setup>
118
- import { watch, computed, ref, onMounted } from 'vue'
119
- import Dropdown from '@/components/Dropdown.vue';
120
- import CustomDateRangePicker from '@/components/CustomDateRangePicker.vue';
121
- import { callAdminForthApi } from '@/utils';
122
- import { useRouter } from 'vue-router';
123
- import { computedAsync } from '@vueuse/core'
124
- import CustomRangePicker from "@/components/CustomRangePicker.vue";
125
- import { useFiltersStore } from '@/stores/filters';
126
-
127
- const filtersStore = useFiltersStore();
128
-
129
-
130
- // props: columns
131
- // add support for v-model:filers
132
- const props = defineProps(['columns', 'filters', 'show', 'columnsMinMax']);
133
- const emits = defineEmits(['update:filters', 'hide']);
134
-
135
- const router = useRouter();
136
-
137
-
138
- const columnsWithFilter = computed(
139
- () => props.columns?.filter(column => column.showIn.includes('filter')) || []
140
- );
141
-
142
- const columnOptions = computedAsync(async () => {
143
- const ret = {};
144
- if (!props.columns) {
145
- return ret;
146
- }
147
- await Promise.all(
148
- Object.values(props.columns).map(async (column) => {
149
- if (column.foreignResource) {
150
- const list = await callAdminForthApi({
151
- method: 'POST',
152
- path: `/get_resource_foreign_data`,
153
- body: {
154
- resourceId: router.currentRoute.value.params.resourceId,
155
- column: column.name,
156
- limit: 1000,
157
- offset: 0,
158
- },
159
- });
160
- ret[column.name] = list.items;
161
- }
162
- })
163
- );
164
-
165
- return ret;
166
- }, {});
167
-
168
-
169
- // sync 'body' class 'overflow-hidden' with show prop show
170
- watch(() => props.show, (show) => {
171
- if (show) {
172
- document.body.classList.add('overflow-hidden');
173
- } else {
174
- document.body.classList.remove('overflow-hidden');
175
- }
176
- });
177
-
178
- // filters is a array of objects
179
- // {
180
- // field: 'name',
181
- // value: 'John',
182
- // operator: 'like'
183
- // }
184
-
185
- function setFilterItem({ column, operator, value }) {
186
-
187
- const index = filtersStore.filters.findIndex(f => f.field === column.name && f.operator === operator);
188
- if (value === undefined) {
189
- if (index !== -1) {
190
- filtersStore.filters.splice(index, 1);
191
- }
192
- emits('update:filters', [...filtersStore.filters]);
193
- return;
194
- } else {
195
- if (index === -1) {
196
- filtersStore.setFilter({ field: column.name, value, operator });
197
- } else {
198
- filtersStore.setFilters([...filtersStore.filters.slice(0, index), { field: column.name, value, operator }, ...filtersStore.filters.slice(index + 1)])
199
- }
200
- }
201
- emits('update:filters', [...filtersStore.filters]);
202
- }
203
-
204
- function getFilterItem({ column, operator }) {
205
- return filtersStore.filters.find(f => f.field === column.name && f.operator === operator)?.value || '';
206
- }
207
-
208
- async function clear() {
209
- filtersStore.clearFilters();
210
- }
211
- </script>
@@ -1,17 +0,0 @@
1
- <script setup lang="ts">
2
- defineProps<{
3
- msg: string
4
- }>()
5
- </script>
6
-
7
- <template>
8
- <div class="greetings">
9
- <h1 class="green">{{ msg }}</h1>
10
- <h3>
11
- You’ve successfully created a project with
12
- </h3>
13
- </div>
14
- </template>
15
-
16
- <style scoped>
17
- </style>
@@ -1,24 +0,0 @@
1
- <template>
2
- <RouterLink
3
- :to="{name: item.resourceId ? 'resource-list' : item.path, params: item.resourceId ? { resourceId: item.resourceId }: {}}"
4
- class="flex items-center py-2 text-nav-menu-text rounded-default hover:bg-nav-menu-bg-hover active:bg-nav-menu-bg-active" role="menuitem"
5
- :class="{
6
- 'px-4': isChild,
7
- 'px-2': !isChild,
8
- 'bg-nav-menu-bg-active dark:bg-gray-700': item.resourceId ?
9
- ($route.params.resourceId === item.resourceId && $route.name === 'resource-list') :
10
- ($route.name === item.path)
11
- }"
12
- >
13
- <component v-if="item.icon" :is="getIcon(item.icon)" class="w-5 h-5 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white" ></component>
14
- <span class="ms-3">{{ item.label }}</span>
15
- </RouterLink>
16
- </template>
17
-
18
- <script setup lang="ts">
19
- import { getIcon } from '@/utils';
20
-
21
- const props = defineProps(['item', 'isChild']);
22
-
23
-
24
- </script>