adminforth 2.4.0-next.8 → 2.4.0-next.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/callTsProxy.js +14 -4
- package/commands/cli.js +12 -4
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +8 -1
- package/commands/createApp/templates/package.json.hbs +1 -1
- package/commands/createApp/utils.js +27 -2
- package/commands/createCustomComponent/configLoader.js +3 -0
- package/commands/createCustomComponent/configUpdater.js +25 -21
- package/commands/createCustomComponent/fileGenerator.js +1 -1
- package/commands/createCustomComponent/main.js +2 -1
- package/commands/createCustomComponent/templates/login/beforeLogin.vue.hbs +18 -0
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +16 -3
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +14 -14
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -9
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +25 -9
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +50 -2
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts +1 -0
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +100 -11
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +450 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +506 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +1 -0
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +9 -0
- package/dist/modules/utils.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/src/App.vue +22 -12
- package/dist/spa/src/afcl/Button.vue +6 -6
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +3 -1
- package/dist/spa/src/afcl/Dialog.vue +6 -6
- package/dist/spa/src/afcl/Dropzone.vue +13 -11
- package/dist/spa/src/afcl/Input.vue +8 -6
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/Link.vue +1 -1
- package/dist/spa/src/afcl/LinkButton.vue +1 -1
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +52 -23
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +13 -13
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +2 -2
- package/dist/spa/src/afcl/VerticalTabs.vue +3 -3
- package/dist/spa/src/afcl/index.ts +2 -2
- package/dist/spa/src/components/AcceptModal.vue +6 -6
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/ColumnValueInput.vue +26 -7
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +2 -1
- package/dist/spa/src/components/CustomDatePicker.vue +95 -9
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +32 -3
- package/dist/spa/src/components/Filters.vue +76 -31
- package/dist/spa/src/components/GroupsTable.vue +7 -7
- package/dist/spa/src/components/ResourceForm.vue +61 -26
- package/dist/spa/src/components/ResourceListTable.vue +38 -40
- package/dist/spa/src/components/ResourceListTableVirtual.vue +31 -33
- package/dist/spa/src/components/ShowTable.vue +14 -9
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +1 -1
- package/dist/spa/src/components/ThreeDotsMenu.vue +5 -5
- package/dist/spa/src/components/Toast.vue +2 -7
- package/dist/spa/src/components/ValueRenderer.vue +4 -4
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/spa_types/core.ts +7 -0
- package/dist/spa/src/stores/core.ts +1 -1
- package/dist/spa/src/types/Back.ts +47 -13
- package/dist/spa/src/types/Common.ts +13 -7
- package/dist/spa/src/types/adapters/CompletionAdapter.ts +25 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +29 -0
- package/dist/spa/src/types/adapters/ImageGenerationAdapter.ts +50 -0
- package/dist/spa/src/types/adapters/OAuth2Adapter.ts +34 -0
- package/dist/spa/src/types/adapters/StorageAdapter.ts +73 -0
- package/dist/spa/src/types/adapters/index.ts +5 -0
- package/dist/spa/src/utils.ts +209 -0
- package/dist/spa/src/views/CreateView.vue +4 -4
- package/dist/spa/src/views/EditView.vue +3 -3
- package/dist/spa/src/views/ListView.vue +6 -10
- package/dist/spa/src/views/LoginView.vue +57 -45
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +3 -3
- package/dist/types/Back.d.ts +41 -10
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +12 -6
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/adapters/CompletionAdapter.d.ts +20 -0
- package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CompletionAdapter.js +2 -0
- package/dist/types/adapters/CompletionAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +21 -0
- package/dist/types/adapters/EmailAdapter.d.ts.map +1 -0
- package/dist/types/adapters/EmailAdapter.js +2 -0
- package/dist/types/adapters/EmailAdapter.js.map +1 -0
- package/dist/types/adapters/ImageGenerationAdapter.d.ts +37 -0
- package/dist/types/adapters/ImageGenerationAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageGenerationAdapter.js +2 -0
- package/dist/types/adapters/ImageGenerationAdapter.js.map +1 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts +32 -0
- package/dist/types/adapters/OAuth2Adapter.d.ts.map +1 -0
- package/dist/types/adapters/OAuth2Adapter.js +2 -0
- package/dist/types/adapters/OAuth2Adapter.js.map +1 -0
- package/dist/types/adapters/StorageAdapter.d.ts +63 -0
- package/dist/types/adapters/StorageAdapter.d.ts.map +1 -0
- package/dist/types/adapters/StorageAdapter.js +2 -0
- package/dist/types/adapters/StorageAdapter.js.map +1 -0
- package/dist/types/adapters/index.d.ts +6 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/index.js +2 -0
- package/dist/types/adapters/index.js.map +1 -0
- package/package.json +2 -2
- package/dist/spa/src/types/Adapters.ts +0 -213
- package/dist/types/Adapters.d.ts +0 -168
- package/dist/types/Adapters.d.ts.map +0 -1
- package/dist/types/Adapters.js +0 -2
- package/dist/types/Adapters.js.map +0 -1
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
<div>
|
|
3
3
|
<div class="grid w-40 gap-4 mb-2">
|
|
4
4
|
<div>
|
|
5
|
-
<label v-if="label" for="start-time" class="block mb-2 text-sm font-medium text-
|
|
5
|
+
<label v-if="label" for="start-time" class="block mb-2 text-sm font-medium text-lightDatePickerButtonText dark:text-darkDatePickerButtonText">{{ label }}</label>
|
|
6
6
|
|
|
7
7
|
<div class="relative" :class="{hidden: column.type === 'time'}">
|
|
8
8
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
9
|
-
<IconCalendar class="w-4 h-4 text-
|
|
9
|
+
<IconCalendar class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon"/>
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
12
|
<input ref="datepickerStartEl" type="text"
|
|
13
|
-
class="
|
|
13
|
+
class="af-pick-date-button bg-lightDatePickerButtonBackground border border-lightDatePickerButtonBorder text-lightDatePickerButtonText text-sm rounded-lg block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkInputPlaceholderText dark:text-darkDatePickerButtonText focus:ring-lightInputFocusRing focus:border-lightInputFocusBorder dark:focus:ring-darkInputFocusRing dark:focus:border-darkInputFocusBorder"
|
|
14
14
|
:placeholder="$t('Select date')" :disabled="readonly" />
|
|
15
15
|
|
|
16
16
|
</div>
|
|
@@ -22,18 +22,17 @@
|
|
|
22
22
|
<div>
|
|
23
23
|
<div class="relative">
|
|
24
24
|
<div class="absolute inset-y-0 end-0 top-0 flex items-center pe-3.5 pointer-events-none">
|
|
25
|
-
<IconTime class="w-4 h-4 text-
|
|
25
|
+
<IconTime class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon bg-lightDatePickerButtonBackground dark:bg-darkDatePickerButtonBackground"/>
|
|
26
26
|
</div>
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white focus:ring-lightPrimary focus:border-lightPrimary dark:focus:ring-darkPrimary dark:focus:border-darkPrimary"
|
|
27
|
+
<input v-model="startTime" type="time" id="start-time" step="1"
|
|
28
|
+
class="af-pick-time-button bg-lightDatePickerButtonBackground border border-lightDatePickerButtonBorder text-lightDatePickerButtonText text-sm rounded-lg block w-full p-2.5 dark:bg-darkInputBackground dark:border-darkInputBorder dark:placeholder-darkInputPlaceholderText dark:text-darkDatePickerButtonText focus:ring-lightInputFocusRing focus:border-lightInputFocusBorder dark:focus:ring-darkInputFocusRing dark:focus:border-darkInputFocusBorder"
|
|
30
29
|
value="00:00" :disabled="readonly" required/>
|
|
31
30
|
</div>
|
|
32
31
|
</div>
|
|
33
32
|
</div>
|
|
34
33
|
|
|
35
34
|
<button type="button"
|
|
36
|
-
class="text-
|
|
35
|
+
class="text-lightDatePickerExpandText dark:text-darkDatePickerExpandText text-base font-medium hover:underline p-0 inline-flex items-center mb-2"
|
|
37
36
|
:class="{hidden: column.type !== 'datetime'}"
|
|
38
37
|
@click="toggleTimeInputs">{{ showTimeInputs ? $t('Hide time') : $t('Show time') }}
|
|
39
38
|
<svg class="w-8 h-8 ms-0.5" :class="{'rotate-180': showTimeInputs}" aria-hidden="true"
|
|
@@ -197,4 +196,91 @@ function focus() {
|
|
|
197
196
|
defineExpose({
|
|
198
197
|
focus,
|
|
199
198
|
});
|
|
200
|
-
</script>
|
|
199
|
+
</script>
|
|
200
|
+
|
|
201
|
+
<style lang="css" scoped>
|
|
202
|
+
|
|
203
|
+
:global(.datepicker-controls button svg),
|
|
204
|
+
:global(.datepicker-controls button) {
|
|
205
|
+
@apply text-lightDatePickerCalendarText;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
:global( .datepicker-dropdown ),
|
|
209
|
+
:global( .datepicker-picker ) {
|
|
210
|
+
@apply !bg-lightDatePickerCalendarBackground
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
:global(.datepicker-controls button:hover) {
|
|
214
|
+
@apply bg-lightDatePickerCalendarArrowButtonBackgroundHover;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
:global(.datepicker-controls button) {
|
|
218
|
+
@apply ring-lightDatePickerCalendarArrowButtonFocusRing bg-lightDatePickerCalendarArrowButtonBackground;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
:global(.datepicker-controls button:focus) {
|
|
222
|
+
@apply ring-lightDatePickerCalendarArrowButtonFocusRing;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
:global(.days-of-week span) {
|
|
226
|
+
@apply text-lightDatePickerCalendarDaysOfWeekText;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
:global(.datepicker-grid span) {
|
|
230
|
+
@apply text-lightDatePickerCalendarDateButtonText;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
:global(.datepicker-grid span:hover:not(.selected)) {
|
|
234
|
+
@apply bg-lightDatePickerCalendarDateButtonBackgroundHover;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
:global(.datepicker-grid .selected ) {
|
|
238
|
+
@apply text-lightDatePickerCalendarDateActiveButtonText bg-lightDatePickerCalendarDateActiveButtonBackground;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
:global(.dark .datepicker-controls button svg),
|
|
249
|
+
:global(.dark .datepicker-controls button) {
|
|
250
|
+
@apply text-darkDatePickerCalendarMainText;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
:global(.dark .datepicker-dropdown),
|
|
254
|
+
:global(.dark .datepicker-picker) {
|
|
255
|
+
@apply !bg-darkDatePickerCalendarBackground;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
:global(.dark .datepicker-controls button:hover) {
|
|
259
|
+
@apply bg-darkDatePickerCalendarArrowButtonBackgroundHover;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
:global(.dark .datepicker-controls button) {
|
|
263
|
+
@apply ring-darkDatePickerCalendarArrowButtonFocusRing bg-darkDatePickerCalendarArrowButtonBackground;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
:global(.dark .datepicker-controls button) {
|
|
267
|
+
@apply ring-darkDatePickerCalendarArrowButtonFocusRing;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
:global(.dark .days-of-week span) {
|
|
271
|
+
@apply text-darkDatePickerCalendarDaysOfWeekText;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
:global(.dark .datepicker-grid span) {
|
|
275
|
+
@apply text-darkDatePickerCalendarDateButtonText;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
:global(.dark .datepicker-grid span:hover:not(.selected)) {
|
|
279
|
+
@apply bg-darkDatePickerCalendarDateButtonBackgroundHover;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
:global(.dark .datepicker-grid .selected) {
|
|
283
|
+
@apply text-darkDatePickerCalendarDateActiveButtonText
|
|
284
|
+
bg-darkDatePickerCalendarDateActiveButtonBackground;
|
|
285
|
+
}
|
|
286
|
+
</style>
|
|
@@ -3,21 +3,21 @@
|
|
|
3
3
|
<div class="mx-auto grid grid-cols-2 gap-4 mb-2" :class="{hidden: column.type === 'time'}">
|
|
4
4
|
<div class="relative">
|
|
5
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-
|
|
6
|
+
<IconCalendar class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon"/>
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
9
|
<input ref="datepickerStartEl" type="text"
|
|
10
|
-
class="bg-
|
|
10
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
11
11
|
:placeholder="$t('From')">
|
|
12
12
|
</div>
|
|
13
13
|
|
|
14
14
|
<div class="relative">
|
|
15
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-
|
|
16
|
+
<IconCalendar class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon"/>
|
|
17
17
|
</div>
|
|
18
18
|
|
|
19
19
|
<input ref="datepickerEndEl" type="text"
|
|
20
|
-
class="bg-
|
|
20
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
21
21
|
:placeholder="$t('To')">
|
|
22
22
|
</div>
|
|
23
23
|
</div>
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
<div>
|
|
28
28
|
<div class="relative">
|
|
29
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-
|
|
30
|
+
<IconTime class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon bg-lightDatePickerButtonBackground dark:bg-darkDatePickerButtonBackground"/>
|
|
31
31
|
</div>
|
|
32
32
|
|
|
33
33
|
<input v-model="startTime" type="time" id="start-time"
|
|
34
|
-
class="bg-
|
|
34
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
35
35
|
value="00:00" required/>
|
|
36
36
|
</div>
|
|
37
37
|
</div>
|
|
@@ -39,11 +39,11 @@
|
|
|
39
39
|
<div>
|
|
40
40
|
<div class="relative">
|
|
41
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-
|
|
42
|
+
<IconTime class="w-4 h-4 text-lightDatePickerIcon dark:text-darkDatePickerIcon bg-lightDatePickerButtonBackground dark:bg-darkDatePickerButtonBackground"/>
|
|
43
43
|
</div>
|
|
44
44
|
|
|
45
45
|
<input v-model="endTime" type="time" id="end-time"
|
|
46
|
-
class="bg-
|
|
46
|
+
class="bg-lightDatePickerButtonBackground border leading-none border-lightDatePickerButtonBorder text-lightDatePickerButtonText placeholder-lightDatePickerPlaceHolder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-darkDatePickerButtonBackground dark:border-darkDatePickerButtonBorder dark:placeholder-darkDatePickerPlaceHolder dark:text-darkDatePickerButtonText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
47
47
|
value="00:00" required/>
|
|
48
48
|
</div>
|
|
49
49
|
</div>
|
|
@@ -172,6 +172,7 @@ function updateFromProps() {
|
|
|
172
172
|
if (!props.valueEnd) {
|
|
173
173
|
datepickerEndEl.value.value = '';
|
|
174
174
|
endTime.value = '';
|
|
175
|
+
endDate.value = '';
|
|
175
176
|
} else if (props.column.type === 'time') {
|
|
176
177
|
endTime.value = props.valueEnd;
|
|
177
178
|
} else {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
:min="minFormatted"
|
|
5
5
|
:max="maxFormatted"
|
|
6
6
|
type="number" aria-describedby="helper-text-explanation"
|
|
7
|
-
class="bg-
|
|
7
|
+
class="bg-lightRangePickerInputBackground border border-lightRangePickerInputBorder text-lightRangePickerInputText placeholder-lightRangePickerInputPlaceholder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkRangePickerInputBackground dark:border-darkRangePickerInputBorder dark:placeholder-darkRangePickerInputPlaceholder dark:text-darkRangePickerInputText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
8
8
|
:placeholder="$t('From')"
|
|
9
9
|
v-model="start"
|
|
10
10
|
>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
:min="minFormatted"
|
|
14
14
|
:max="maxFormatted"
|
|
15
15
|
type="number" aria-describedby="helper-text-explanation"
|
|
16
|
-
class="bg-
|
|
16
|
+
class="bg-lightRangePickerInputBackground border border-lightRangePickerInputBorder text-lightRangePickerInputText placeholder-lightRangePickerInputPlaceholder text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-20 p-2.5 dark:bg-darkRangePickerInputBackground dark:border-darkRangePickerInputBorder dark:placeholder-darkRangePickerInputPlaceholder dark:text-darkRangePickerInputText dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
|
17
17
|
:placeholder="$t('To')"
|
|
18
18
|
v-model="end"
|
|
19
19
|
>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<button
|
|
22
22
|
v-if="isChanged"
|
|
23
23
|
type="button"
|
|
24
|
-
class="flex items-center p-0.5 ml-auto px-3 text-sm font-medium text-
|
|
24
|
+
class="flex items-center p-0.5 ml-auto px-3 text-sm font-medium text-lightRangePickerButtonText focus:outline-none bg-lightRangePickerButtonBackground rounded border border-lightRangePickerButtonBorder hover:bg-lightRangePickerButtonBackgroundHover hover:text-lightRangePickerButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightRangePickerFocusRing dark:focus:ring-darkRangePickerFocusRing dark:bg-darkRangePickerButtonBackground dark:text-darkRangePickerButtonText dark:border-darkRangePickerButtonBorder dark:hover:text-darkRangePickerButtonTextHover dark:hover:bg-darkRangePickerButtonBackgroundHover disabled:opacity-50 disabled:cursor-not-allowed"
|
|
25
25
|
@click="clear">Clear
|
|
26
26
|
</button>
|
|
27
27
|
|
|
@@ -153,4 +153,33 @@ function setSliderValues(start, end) {
|
|
|
153
153
|
@apply bg-lightPrimaryOpacity;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
+
|
|
157
|
+
.dark .custom-slider {
|
|
158
|
+
&:deep(.vue-slider-rail) {
|
|
159
|
+
background-color: rgb(55 65 81); // gray-700
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
&:deep(.vue-slider-dot-handle) {
|
|
163
|
+
@apply bg-darkPrimary;
|
|
164
|
+
border: none;
|
|
165
|
+
box-shadow: none;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
&:deep(.vue-slider-dot-handle:hover) {
|
|
169
|
+
@apply bg-darkPrimary;
|
|
170
|
+
filter: brightness(1.1);
|
|
171
|
+
border: none;
|
|
172
|
+
box-shadow: none;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
&:deep(.vue-slider-process) {
|
|
176
|
+
@apply bg-darkPrimaryOpacity;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
&:deep(.vue-slider-process:hover) {
|
|
180
|
+
filter: brightness(1.1);
|
|
181
|
+
@apply bg-darkPrimaryOpacity;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
156
185
|
</style>
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
<!-- drawer component -->
|
|
3
3
|
<div id="drawer-navigation"
|
|
4
4
|
|
|
5
|
-
class="fixed
|
|
5
|
+
class="af-filters-sidebar fixed right-0 z-50 p-4 overflow-y-auto transition-transform translate-x-full bg-lightFiltersBackgroung w-80 dark:bg-darkFiltersBackgroung shadow-xl dark:shadow-gray-900"
|
|
6
6
|
|
|
7
7
|
:class="show ? 'top-0 transform-none' : ''"
|
|
8
8
|
tabindex="-1" aria-labelledby="drawer-navigation-label"
|
|
9
9
|
:style="{ height: `calc(100dvh ` }"
|
|
10
10
|
>
|
|
11
|
-
<h5 id="drawer-navigation-label" class="text-base font-semibold text-
|
|
11
|
+
<h5 id="drawer-navigation-label" class="text-base font-semibold text-lightFiltersHeaderText uppercase dark:text-darkFiltersHeaderText">
|
|
12
12
|
{{ $t('Filters') }}
|
|
13
13
|
|
|
14
|
-
<button type="button" @click="$emit('hide')" class="text-
|
|
14
|
+
<button type="button" @click="$emit('hide')" class="text-lightFiltersCloseIcon bg-transparent hover:bg-lightFiltersCloseIconHoverBackground hover:text-lightFiltersCloseIconHover rounded-lg text-sm p-1.5 absolute end-2.5 inline-flex items-center dark:text-darkFiltersCloseIcon dark:hover:bg-darkFiltersCloseIconHoverBackground dark:hover:text-darkFiltersCloseIconHover" >
|
|
15
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
16
|
<span class="sr-only">{{ $t('Close menu') }}</span>
|
|
17
17
|
</button>
|
|
@@ -43,9 +43,23 @@
|
|
|
43
43
|
:multiple="c.filterOptions.multiselect"
|
|
44
44
|
class="w-full"
|
|
45
45
|
:options="columnOptions[c.name] || []"
|
|
46
|
+
:searchDisabled="!c.foreignResource.searchableFields"
|
|
47
|
+
@scroll-near-end="loadMoreOptions(c.name)"
|
|
48
|
+
@search="(searchTerm) => {
|
|
49
|
+
if (c.foreignResource.searchableFields && onSearchInput[c.name]) {
|
|
50
|
+
onSearchInput[c.name](searchTerm);
|
|
51
|
+
}
|
|
52
|
+
}"
|
|
46
53
|
@update:modelValue="onFilterInput[c.name]({ column: c, operator: c.filterOptions.multiselect ? 'in' : 'eq', value: c.filterOptions.multiselect ? ($event.length ? $event : undefined) : $event || undefined })"
|
|
47
54
|
:modelValue="filtersStore.filters.find(f => f.field === c.name && f.operator === (c.filterOptions.multiselect ? 'in' : 'eq'))?.value || (c.filterOptions.multiselect ? [] : '')"
|
|
48
|
-
|
|
55
|
+
>
|
|
56
|
+
<template #extra-item v-if="columnLoadingState[c.name]?.loading">
|
|
57
|
+
<div class="text-center text-gray-400 dark:text-gray-300 py-2 flex items-center justify-center gap-2">
|
|
58
|
+
<Spinner class="w-4 h-4" />
|
|
59
|
+
{{ $t('Loading...') }}
|
|
60
|
+
</div>
|
|
61
|
+
</template>
|
|
62
|
+
</Select>
|
|
49
63
|
<Select
|
|
50
64
|
:multiple="c.filterOptions.multiselect"
|
|
51
65
|
class="w-full"
|
|
@@ -124,7 +138,7 @@
|
|
|
124
138
|
<button
|
|
125
139
|
:disabled="!filtersStore.filters.length"
|
|
126
140
|
type="button"
|
|
127
|
-
class="flex items-center py-1 px-3 text-sm font-medium text-
|
|
141
|
+
class="flex items-center py-1 px-3 text-sm font-medium text-lightFiltersClearAllButtonText focus:outline-none bg-lightFiltersClearAllButtonBackground rounded border border-lightFiltersClearAllButtonBorder hover:bg-lightFiltersClearAllButtonBackgroundHover hover:text-lightFiltersClearAllButtonTextHover focus:z-10 focus:ring-4 focus:ring-lightFiltersClearAllButtonFocus dark:focus:ring-darkFiltersClearAllButtonFocus dark:bg-darkFiltersClearAllButtonBackground dark:text-darkFiltersClearAllButtonText dark:border-darkFiltersClearAllButtonBorder dark:hover:text-darkFiltersClearAllButtonTextHover dark:hover:bg-darkFiltersClearAllButtonBackgroundHover disabled:opacity-50 disabled:cursor-not-allowed"
|
|
128
142
|
@click="clear">{{ $t('Clear all') }}</button>
|
|
129
143
|
|
|
130
144
|
</div>
|
|
@@ -136,17 +150,17 @@
|
|
|
136
150
|
</template>
|
|
137
151
|
|
|
138
152
|
<script setup>
|
|
139
|
-
import { watch, computed } from 'vue';
|
|
153
|
+
import { watch, computed, ref, reactive } from 'vue';
|
|
140
154
|
import { useI18n } from 'vue-i18n';
|
|
141
155
|
import CustomDateRangePicker from '@/components/CustomDateRangePicker.vue';
|
|
142
|
-
import { callAdminForthApi } from '@/utils';
|
|
156
|
+
import { callAdminForthApi, loadMoreForeignOptions, searchForeignOptions, createSearchInputHandlers } from '@/utils';
|
|
143
157
|
import { useRouter } from 'vue-router';
|
|
144
|
-
import { computedAsync } from '@vueuse/core'
|
|
145
158
|
import CustomRangePicker from "@/components/CustomRangePicker.vue";
|
|
146
159
|
import { useFiltersStore } from '@/stores/filters';
|
|
147
160
|
import { getCustomComponent } from '@/utils';
|
|
148
161
|
import Input from '@/afcl/Input.vue';
|
|
149
162
|
import Select from '@/afcl/Select.vue';
|
|
163
|
+
import Spinner from '@/afcl/Spinner.vue';
|
|
150
164
|
import debounce from 'debounce';
|
|
151
165
|
|
|
152
166
|
const filtersStore = useFiltersStore();
|
|
@@ -165,31 +179,54 @@ const columnsWithFilter = computed(
|
|
|
165
179
|
() => props.columns?.filter(column => column.showIn.filter) || []
|
|
166
180
|
);
|
|
167
181
|
|
|
168
|
-
const columnOptions =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
ret[column.name] = list.items;
|
|
182
|
+
const columnOptions = ref({});
|
|
183
|
+
const columnLoadingState = reactive({});
|
|
184
|
+
const columnOffsets = reactive({});
|
|
185
|
+
const columnEmptyResultsCount = reactive({});
|
|
186
|
+
|
|
187
|
+
watch(() => props.columns, async (newColumns) => {
|
|
188
|
+
if (!newColumns) return;
|
|
189
|
+
|
|
190
|
+
for (const column of newColumns) {
|
|
191
|
+
if (column.foreignResource) {
|
|
192
|
+
if (!columnOptions.value[column.name]) {
|
|
193
|
+
columnOptions.value[column.name] = [];
|
|
194
|
+
columnLoadingState[column.name] = { loading: false, hasMore: true };
|
|
195
|
+
columnOffsets[column.name] = 0;
|
|
196
|
+
columnEmptyResultsCount[column.name] = 0;
|
|
197
|
+
|
|
198
|
+
await loadMoreOptions(column.name);
|
|
187
199
|
}
|
|
188
|
-
}
|
|
189
|
-
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}, { immediate: true });
|
|
203
|
+
|
|
204
|
+
// Function to load more options for a specific column
|
|
205
|
+
async function loadMoreOptions(columnName, searchTerm = '') {
|
|
206
|
+
return loadMoreForeignOptions({
|
|
207
|
+
columnName,
|
|
208
|
+
searchTerm,
|
|
209
|
+
columns: props.columns,
|
|
210
|
+
resourceId: router.currentRoute.value.params.resourceId,
|
|
211
|
+
columnOptions,
|
|
212
|
+
columnLoadingState,
|
|
213
|
+
columnOffsets,
|
|
214
|
+
columnEmptyResultsCount
|
|
215
|
+
});
|
|
216
|
+
}
|
|
190
217
|
|
|
191
|
-
|
|
192
|
-
|
|
218
|
+
async function searchOptions(columnName, searchTerm) {
|
|
219
|
+
return searchForeignOptions({
|
|
220
|
+
columnName,
|
|
221
|
+
searchTerm,
|
|
222
|
+
columns: props.columns,
|
|
223
|
+
resourceId: router.currentRoute.value.params.resourceId,
|
|
224
|
+
columnOptions,
|
|
225
|
+
columnLoadingState,
|
|
226
|
+
columnOffsets,
|
|
227
|
+
columnEmptyResultsCount
|
|
228
|
+
});
|
|
229
|
+
}
|
|
193
230
|
|
|
194
231
|
|
|
195
232
|
// sync 'body' class 'overflow-hidden' with show prop show
|
|
@@ -221,6 +258,14 @@ const onFilterInput = computed(() => {
|
|
|
221
258
|
}, {});
|
|
222
259
|
});
|
|
223
260
|
|
|
261
|
+
const onSearchInput = computed(() => {
|
|
262
|
+
return createSearchInputHandlers(
|
|
263
|
+
props.columns,
|
|
264
|
+
searchOptions,
|
|
265
|
+
(column) => column.filterOptions?.debounceTimeMs || 300
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
224
269
|
function setFilterItem({ column, operator, value }) {
|
|
225
270
|
|
|
226
271
|
const index = filtersStore.filters.findIndex(f => f.field === column.name && f.operator === operator);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="rounded-lg shadow-resourseFormShadow dark:shadow-darkResourseFormShadow dark:shadow-2xl">
|
|
3
|
-
<div v-if="group.groupName && !group.noTitle" class="text-md font-semibold px-6 py-3 flex flex-1 items-center dark:border-
|
|
3
|
+
<div v-if="group.groupName && !group.noTitle" class="text-md font-semibold px-6 py-3 flex flex-1 items-center dark:border-darkFormBorder text-lightListTableHeadingText bg-lightFormHeading dark:bg-darkFormHeading dark:text-darkListTableHeadingText rounded-t-lg">
|
|
4
4
|
{{ group.groupName }}
|
|
5
5
|
</div>
|
|
6
|
-
<table class="w-full text-sm text-left rtl:text-right text-
|
|
7
|
-
<thead v-if="!allColumnsHaveCustomComponent" class="text-xs text-
|
|
6
|
+
<table class="w-full text-sm text-left rtl:text-right text-lightFormFieldTextColor dark:text-darkFormFieldTextColor">
|
|
7
|
+
<thead v-if="!allColumnsHaveCustomComponent" class="text-xs text-lightListTableHeadingText uppercase dark:text-darkListTableHeadingText bg-lightFormHeading dark:bg-darkFormHeading block md:table-row-group ">
|
|
8
8
|
<tr>
|
|
9
9
|
<th scope="col" :class="{'rounded-tl-lg': !group.groupName}" class="px-6 py-3 hidden md:w-52 md:table-cell">
|
|
10
10
|
{{ $t('Field') }}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
v-for="(column, i) in group.columns"
|
|
20
20
|
:key="column.name"
|
|
21
21
|
v-if="currentValues !== null"
|
|
22
|
-
class="bg-
|
|
22
|
+
class="bg-lightForm dark:bg-darkForm dark:border-darkFormBorder block md:table-row"
|
|
23
23
|
:class="{ 'border-b': i !== group.columns.length - 1}"
|
|
24
24
|
>
|
|
25
25
|
<td class="px-6 py-4 flex items-center block md:table-cell pb-0 md:pb-4"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<Tooltip v-if="column.required[mode]">
|
|
30
30
|
|
|
31
31
|
<IconExclamationCircleSolid v-if="column.required[mode]" class="w-4 h-4"
|
|
32
|
-
:class="(columnError(column) && validating) ? 'text-
|
|
32
|
+
:class="(columnError(column) && validating) ? 'text-lightInputErrorColor dark:text-darkInputErrorColor' : 'text-lightRequiredIconColor dark:text-darkRequiredIconColor'"
|
|
33
33
|
/>
|
|
34
34
|
|
|
35
35
|
<template #tooltip>
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
@update:emptiness="customComponentsEmptiness[$event.name] = $event.value"
|
|
56
56
|
:readonly="readonlyColumns?.includes(column.name)"
|
|
57
57
|
/>
|
|
58
|
-
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-
|
|
59
|
-
<div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-
|
|
58
|
+
<div v-if="columnError(column) && validating" class="mt-1 text-xs text-lightInputErrorColor dark:text-darkInputErrorColor">{{ columnError(column) }}</div>
|
|
59
|
+
<div v-if="column.editingNote && column.editingNote[mode]" class="mt-1 text-xs text-lightFormFieldTextColor dark:text-darkFormFieldTextColor">{{ column.editingNote[mode] }}</div>
|
|
60
60
|
</td>
|
|
61
61
|
</tr>
|
|
62
62
|
</tbody>
|
|
@@ -63,9 +63,9 @@
|
|
|
63
63
|
|
|
64
64
|
<script setup lang="ts">
|
|
65
65
|
|
|
66
|
-
import { applyRegexValidation, callAdminForthApi} from '@/utils';
|
|
66
|
+
import { applyRegexValidation, callAdminForthApi, loadMoreForeignOptions, searchForeignOptions, createSearchInputHandlers} from '@/utils';
|
|
67
67
|
import { computedAsync } from '@vueuse/core';
|
|
68
|
-
import { computed, onMounted, ref, watch } from 'vue';
|
|
68
|
+
import { computed, onMounted, reactive, ref, watch, provide } from 'vue';
|
|
69
69
|
import { useRouter, useRoute } from 'vue-router';
|
|
70
70
|
import { useCoreStore } from "@/stores/core";
|
|
71
71
|
import GroupsTable from '@/components/GroupsTable.vue';
|
|
@@ -95,6 +95,11 @@ const currentValues = ref(null);
|
|
|
95
95
|
const customComponentsInValidity = ref({});
|
|
96
96
|
const customComponentsEmptiness = ref({});
|
|
97
97
|
|
|
98
|
+
const columnOptions = ref<Record<string, any[]>>({});
|
|
99
|
+
const columnLoadingState = reactive<Record<string, { loading: boolean; hasMore: boolean }>>({});
|
|
100
|
+
const columnOffsets = reactive<Record<string, number>>({});
|
|
101
|
+
const columnEmptyResultsCount = reactive<Record<string, number>>({});
|
|
102
|
+
|
|
98
103
|
const columnError = (column) => {
|
|
99
104
|
const val = computed(() => {
|
|
100
105
|
if (!currentValues.value) {
|
|
@@ -186,7 +191,7 @@ const validateValue = (type, value, column) => {
|
|
|
186
191
|
};
|
|
187
192
|
|
|
188
193
|
|
|
189
|
-
const setCurrentValue = (key, value, index=null) => {
|
|
194
|
+
const setCurrentValue = (key, value, index = null) => {
|
|
190
195
|
const col = props.resource.columns.find((column) => column.name === key);
|
|
191
196
|
// if field is an array, we need to update the array or individual element
|
|
192
197
|
if (col.type === 'json' && col.isArray?.enabled) {
|
|
@@ -239,6 +244,23 @@ const setCurrentValue = (key, value, index=null) => {
|
|
|
239
244
|
emit('update:record', up);
|
|
240
245
|
};
|
|
241
246
|
|
|
247
|
+
watch(() => props.resource.columns, async (newColumns) => {
|
|
248
|
+
if (!newColumns) return;
|
|
249
|
+
|
|
250
|
+
for (const column of newColumns) {
|
|
251
|
+
if (column.foreignResource) {
|
|
252
|
+
if (!columnOptions.value[column.name]) {
|
|
253
|
+
columnOptions.value[column.name] = [];
|
|
254
|
+
columnLoadingState[column.name] = { loading: false, hasMore: true };
|
|
255
|
+
columnOffsets[column.name] = 0;
|
|
256
|
+
columnEmptyResultsCount[column.name] = 0;
|
|
257
|
+
|
|
258
|
+
await loadMoreOptions(column.name);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}, { immediate: true });
|
|
263
|
+
|
|
242
264
|
onMounted(() => {
|
|
243
265
|
currentValues.value = Object.assign({}, props.record);
|
|
244
266
|
// json values should transform to string
|
|
@@ -266,29 +288,31 @@ onMounted(() => {
|
|
|
266
288
|
emit('update:isValid', isValid.value);
|
|
267
289
|
});
|
|
268
290
|
|
|
269
|
-
|
|
270
|
-
return (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
291
|
+
async function loadMoreOptions(columnName: string, searchTerm = '') {
|
|
292
|
+
return loadMoreForeignOptions({
|
|
293
|
+
columnName,
|
|
294
|
+
searchTerm,
|
|
295
|
+
columns: props.resource.columns,
|
|
296
|
+
resourceId: router.currentRoute.value.params.resourceId as string,
|
|
297
|
+
columnOptions,
|
|
298
|
+
columnLoadingState,
|
|
299
|
+
columnOffsets,
|
|
300
|
+
columnEmptyResultsCount
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async function searchOptions(columnName: string, searchTerm: string) {
|
|
305
|
+
return searchForeignOptions({
|
|
306
|
+
columnName,
|
|
307
|
+
searchTerm,
|
|
308
|
+
columns: props.resource.columns,
|
|
309
|
+
resourceId: router.currentRoute.value.params.resourceId as string,
|
|
310
|
+
columnOptions,
|
|
311
|
+
columnLoadingState,
|
|
312
|
+
columnOffsets,
|
|
313
|
+
columnEmptyResultsCount
|
|
314
|
+
});
|
|
315
|
+
}
|
|
292
316
|
|
|
293
317
|
|
|
294
318
|
const editableColumns = computed(() => {
|
|
@@ -330,6 +354,17 @@ const getOtherColumns = () => {
|
|
|
330
354
|
|
|
331
355
|
const otherColumns = getOtherColumns();
|
|
332
356
|
|
|
357
|
+
const onSearchInput = computed(() => {
|
|
358
|
+
return createSearchInputHandlers(
|
|
359
|
+
props.resource.columns,
|
|
360
|
+
searchOptions
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
provide('columnLoadingState', columnLoadingState);
|
|
365
|
+
provide('onSearchInput', onSearchInput);
|
|
366
|
+
provide('loadMoreOptions', loadMoreOptions);
|
|
367
|
+
|
|
333
368
|
watch(() => isValid.value, (value) => {
|
|
334
369
|
emit('update:isValid', value);
|
|
335
370
|
});
|