atom-nuxt 1.1.0 → 1.2.2
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/module.cjs +5 -0
- package/dist/module.d.mts +1 -2
- package/dist/module.d.ts +22 -0
- package/dist/module.json +3 -3
- package/dist/runtime/components/AlertDisplay.vue +18 -11
- package/dist/runtime/components/CrudAddressSearchField.vue +35 -22
- package/dist/runtime/components/CrudApiSelectorField.vue +24 -11
- package/dist/runtime/components/CrudConfirmDialog.vue +17 -11
- package/dist/runtime/components/CrudErrorDisplay.vue +4 -3
- package/dist/runtime/components/CrudFilter.vue +46 -25
- package/dist/runtime/components/CrudFilterList.vue +6 -0
- package/dist/runtime/components/CrudFormDialog.vue +16 -6
- package/dist/runtime/components/CrudFormLoader.vue +121 -98
- package/dist/runtime/components/CrudListLoader.vue +28 -17
- package/dist/runtime/components/CrudPaginatedLoader.vue +93 -57
- package/dist/runtime/components/CrudUploadField.vue +28 -6
- package/dist/runtime/components/CrudUploadFieldSelection.vue +27 -19
- package/dist/types.d.mts +2 -4
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
- package/dist/runtime/components/AlertDisplay.vue.d.ts +0 -2
- package/dist/runtime/components/CrudAddressSearchField.vue.d.ts +0 -8
- package/dist/runtime/components/CrudApiSelectorField.vue.d.ts +0 -30
- package/dist/runtime/components/CrudConfirmDialog.vue.d.ts +0 -18
- package/dist/runtime/components/CrudErrorDisplay.vue.d.ts +0 -6
- package/dist/runtime/components/CrudFilter.vue.d.ts +0 -8
- package/dist/runtime/components/CrudFilterList.vue.d.ts +0 -5
- package/dist/runtime/components/CrudFormDialog.vue.d.ts +0 -33
- package/dist/runtime/components/CrudFormLoader.vue.d.ts +0 -41
- package/dist/runtime/components/CrudListLoader.vue.d.ts +0 -37
- package/dist/runtime/components/CrudPaginatedLoader.vue.d.ts +0 -75
- package/dist/runtime/components/CrudUploadField.vue.d.ts +0 -23
- package/dist/runtime/components/CrudUploadFieldSelection.vue.d.ts +0 -18
package/dist/module.cjs
ADDED
package/dist/module.d.mts
CHANGED
|
@@ -19,5 +19,4 @@ interface AtomCrudModuleOptions {
|
|
|
19
19
|
}
|
|
20
20
|
declare const _default: _nuxt_schema.NuxtModule<AtomCrudModuleOptions, AtomCrudModuleOptions, false>;
|
|
21
21
|
|
|
22
|
-
export { _default as default };
|
|
23
|
-
export type { AtomCrudModuleOptions, azureAdB2COptions };
|
|
22
|
+
export { type AtomCrudModuleOptions, type azureAdB2COptions, _default as default };
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
3
|
+
interface azureAdB2COptions {
|
|
4
|
+
clientId: string;
|
|
5
|
+
authority: string;
|
|
6
|
+
knownAuthorities?: Array<string>;
|
|
7
|
+
redirectUri: string;
|
|
8
|
+
postLogoutRedirectUri?: string;
|
|
9
|
+
navigateToLoginRequestUrl: boolean;
|
|
10
|
+
scopes?: string[];
|
|
11
|
+
}
|
|
12
|
+
interface AtomCrudModuleOptions {
|
|
13
|
+
apiBaseUrl?: string;
|
|
14
|
+
storageBaseUrl?: string;
|
|
15
|
+
additionalHeaders?: Record<string, string>;
|
|
16
|
+
azureAdB2C?: azureAdB2COptions;
|
|
17
|
+
googleMapsApiKey?: string;
|
|
18
|
+
debug?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare const _default: _nuxt_schema.NuxtModule<AtomCrudModuleOptions, AtomCrudModuleOptions, false>;
|
|
21
|
+
|
|
22
|
+
export { type AtomCrudModuleOptions, type azureAdB2COptions, _default as default };
|
package/dist/module.json
CHANGED
|
@@ -1,43 +1,46 @@
|
|
|
1
|
-
<script setup>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {useAlertService} from '../composables/useAlertService';
|
|
3
|
+
import {computed} from 'vue';
|
|
4
|
+
|
|
5
|
+
const {alertMessage, alertActive} = useAlertService();
|
|
6
|
+
|
|
5
7
|
const message = computed(() => {
|
|
6
8
|
if (!alertMessage.value || !alertActive.value) {
|
|
7
9
|
return null;
|
|
8
10
|
}
|
|
9
|
-
return typeof alertMessage.value ===
|
|
11
|
+
return typeof alertMessage.value === 'string' ? alertMessage.value : alertMessage.value.message;
|
|
10
12
|
});
|
|
11
13
|
const color = computed(() => {
|
|
12
14
|
if (!alertMessage.value || !alertActive.value) {
|
|
13
15
|
return null;
|
|
14
16
|
}
|
|
15
|
-
return typeof alertMessage.value ===
|
|
17
|
+
return typeof alertMessage.value === 'string' ? 'success' : alertMessage.value.color || 'success';
|
|
16
18
|
});
|
|
17
19
|
const location = computed(() => {
|
|
18
20
|
if (!alertMessage.value || !alertActive.value) {
|
|
19
21
|
return "";
|
|
20
22
|
}
|
|
21
|
-
return typeof alertMessage.value ===
|
|
23
|
+
return typeof alertMessage.value === 'string' ? "" : alertMessage.value.location || "";
|
|
22
24
|
});
|
|
23
25
|
const icon = computed(() => {
|
|
24
26
|
if (!alertMessage.value || !alertActive.value) {
|
|
25
27
|
return null;
|
|
26
28
|
}
|
|
27
|
-
return typeof alertMessage.value ===
|
|
29
|
+
return typeof alertMessage.value === 'string' ? null : alertMessage.value.icon || null;
|
|
28
30
|
});
|
|
31
|
+
|
|
29
32
|
const clickable = computed(() => {
|
|
30
|
-
if (!alertMessage.value || !alertActive.value || typeof alertMessage.value ===
|
|
33
|
+
if (!alertMessage.value || !alertActive.value || typeof alertMessage.value === 'string' || !alertMessage.value.onClick) {
|
|
31
34
|
return false;
|
|
32
35
|
}
|
|
33
36
|
return true;
|
|
34
|
-
})
|
|
37
|
+
})
|
|
35
38
|
const onClick = () => {
|
|
36
39
|
if (!clickable.value) {
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
42
|
alertMessage.value.onClick();
|
|
40
|
-
}
|
|
43
|
+
}
|
|
41
44
|
</script>
|
|
42
45
|
|
|
43
46
|
<template>
|
|
@@ -53,3 +56,7 @@ const onClick = () => {
|
|
|
53
56
|
</div>
|
|
54
57
|
</v-snackbar>
|
|
55
58
|
</template>
|
|
59
|
+
|
|
60
|
+
<style scoped>
|
|
61
|
+
|
|
62
|
+
</style>
|
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import {ref, watch} from 'vue';
|
|
3
|
+
import {useRuntimeConfig} from "nuxt/app";
|
|
4
|
+
import {useDebounceFn} from '@vueuse/core'; // Ensure @vueuse/core is installed
|
|
5
|
+
|
|
5
6
|
const atomNuxtConfig = useRuntimeConfig().public.atomNuxt;
|
|
6
7
|
const props = defineProps({
|
|
7
8
|
hint: {
|
|
8
9
|
type: String,
|
|
9
|
-
default:
|
|
10
|
+
default: 'Search by postcode, city or country. You must select an option from the list.'
|
|
10
11
|
},
|
|
11
12
|
label: {
|
|
12
13
|
type: String,
|
|
13
|
-
default:
|
|
14
|
+
default: 'Address search'
|
|
14
15
|
},
|
|
15
16
|
placeholder: {
|
|
16
17
|
type: String,
|
|
17
|
-
default:
|
|
18
|
+
default: 'Start typing address'
|
|
18
19
|
},
|
|
19
20
|
countries: {
|
|
20
21
|
type: Array,
|
|
21
|
-
default: () => []
|
|
22
|
-
// Example: ['gb', 'fr', 'us']
|
|
22
|
+
default: () => [] // Example: ['gb', 'fr', 'us']
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
|
|
25
|
+
// Data
|
|
26
|
+
const searchText = ref('');
|
|
26
27
|
const addressSearchResults = ref([]);
|
|
27
28
|
const loading = ref(false);
|
|
28
29
|
const selectedPlaceId = ref(null);
|
|
29
30
|
const model = defineModel();
|
|
30
|
-
const placeModel = defineModel("place")
|
|
31
|
+
const placeModel = defineModel("place")
|
|
32
|
+
|
|
33
|
+
// Function to get autocomplete suggestions using fetch
|
|
31
34
|
const fetchAutocompleteSuggestions = async (query) => {
|
|
32
35
|
if (!query || loading.value) return;
|
|
33
36
|
loading.value = true;
|
|
@@ -38,21 +41,23 @@ const fetchAutocompleteSuggestions = async (query) => {
|
|
|
38
41
|
params: {
|
|
39
42
|
input: query,
|
|
40
43
|
key: atomNuxtConfig.googleMapsApiKey,
|
|
41
|
-
components: props.countries.length
|
|
44
|
+
components: props.countries.length
|
|
45
|
+
? props.countries.map(c => `country:${c}`).join('|')
|
|
46
|
+
: undefined
|
|
42
47
|
},
|
|
43
|
-
method:
|
|
48
|
+
method: 'GET'
|
|
44
49
|
}
|
|
45
50
|
);
|
|
46
|
-
if (result.status ===
|
|
47
|
-
addressSearchResults.value = result.predictions.map(
|
|
51
|
+
if (result.status === 'OK') {
|
|
52
|
+
addressSearchResults.value = result.predictions.map(prediction => ({
|
|
48
53
|
text: prediction.description,
|
|
49
54
|
place_id: prediction.place_id
|
|
50
55
|
}));
|
|
51
56
|
} else {
|
|
52
|
-
console.error(
|
|
57
|
+
console.error('Error fetching autocomplete suggestions:', result.status);
|
|
53
58
|
}
|
|
54
59
|
} catch (error) {
|
|
55
|
-
console.error(
|
|
60
|
+
console.error('Error fetching autocomplete suggestions:', error);
|
|
56
61
|
} finally {
|
|
57
62
|
loading.value = false;
|
|
58
63
|
}
|
|
@@ -68,29 +73,37 @@ const fetchPlaceId = async (placeId) => {
|
|
|
68
73
|
`/api/maps/places`,
|
|
69
74
|
{
|
|
70
75
|
params: {
|
|
71
|
-
placeId,
|
|
72
|
-
key: atomNuxtConfig.googleMapsApiKey
|
|
76
|
+
placeId: placeId,
|
|
77
|
+
key: atomNuxtConfig.googleMapsApiKey,
|
|
73
78
|
},
|
|
74
|
-
method:
|
|
79
|
+
method: 'GET'
|
|
75
80
|
}
|
|
76
81
|
);
|
|
77
|
-
if
|
|
82
|
+
// Check if the API call was successful
|
|
83
|
+
if (result.status === 'OK') {
|
|
78
84
|
placeModel.value = result.result;
|
|
79
85
|
model.value = `${result.result.geometry.location.lat},${result.result.geometry.location.lng}`;
|
|
80
86
|
} else {
|
|
81
|
-
console.error(
|
|
87
|
+
console.error('Error fetching place details:', result.status);
|
|
82
88
|
}
|
|
83
89
|
} catch (error) {
|
|
84
|
-
console.error(
|
|
90
|
+
console.error('Error fetching place details:', error);
|
|
85
91
|
}
|
|
86
92
|
};
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
// Debounce the search text updates to avoid excessive API calls
|
|
87
96
|
const debouncedFetchAutocompleteSuggestions = useDebounceFn(fetchAutocompleteSuggestions, 500);
|
|
97
|
+
|
|
98
|
+
// Watch for changes to searchText and trigger debounced API calls
|
|
88
99
|
watch(searchText, (newQuery) => {
|
|
89
100
|
debouncedFetchAutocompleteSuggestions(newQuery);
|
|
90
101
|
});
|
|
102
|
+
|
|
91
103
|
watch(selectedPlaceId, (newPlaceId) => {
|
|
92
104
|
fetchPlaceId(newPlaceId);
|
|
93
105
|
});
|
|
106
|
+
|
|
94
107
|
</script>
|
|
95
108
|
|
|
96
109
|
<template>
|
|
@@ -1,31 +1,44 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import CrudListLoader from "./CrudListLoader.vue";
|
|
3
|
-
import {
|
|
3
|
+
import {defineModel, ref, computed} from "vue";
|
|
4
|
+
|
|
4
5
|
const props = defineProps({
|
|
5
|
-
path: {
|
|
6
|
-
multiple: {
|
|
7
|
-
endpointDisplayKey: {
|
|
8
|
-
endpointFilterKey: {
|
|
9
|
-
endpointResponseKey: {
|
|
10
|
-
additionalFilters: {
|
|
11
|
-
disableUpdate: {
|
|
12
|
-
createBtnText: {
|
|
13
|
-
updateBtnText: {
|
|
6
|
+
path: {type: String, required: true},
|
|
7
|
+
multiple: {type: Boolean, default: false},
|
|
8
|
+
endpointDisplayKey: {type: String, default: "name"},
|
|
9
|
+
endpointFilterKey: {type: String, default: "keywords"},
|
|
10
|
+
endpointResponseKey: {type: String, default: "id"},
|
|
11
|
+
additionalFilters: {type: Object, default: () => ({})},
|
|
12
|
+
disableUpdate: {type: Boolean, default: false},
|
|
13
|
+
createBtnText: {type: String, default: "Select"},
|
|
14
|
+
updateBtnText: {type: String, default: "Update"}
|
|
14
15
|
});
|
|
16
|
+
|
|
15
17
|
const selectionModel = defineModel();
|
|
16
18
|
const search = ref("");
|
|
17
19
|
const searchDialog = ref(false);
|
|
18
20
|
const searchDialogSelectedValue = ref(null);
|
|
21
|
+
|
|
19
22
|
const hasSelection = computed(() => props.multiple ? selectionModel.value !== null && Array.isArray(selectionModel.value) && selectionModel.value.length > 0 : selectionModel.value !== null);
|
|
23
|
+
|
|
20
24
|
const onAction = () => {
|
|
21
|
-
|
|
25
|
+
// Clone selectionModel into searchDialogSelectedValue (to modify freely inside the dialog)
|
|
26
|
+
searchDialogSelectedValue.value = selectionModel.value
|
|
27
|
+
? Array.isArray(selectionModel.value)
|
|
28
|
+
? [...selectionModel.value]
|
|
29
|
+
: selectionModel.value
|
|
30
|
+
: null;
|
|
22
31
|
searchDialog.value = true;
|
|
23
32
|
};
|
|
33
|
+
|
|
24
34
|
const onDone = () => {
|
|
35
|
+
// Update selectionModel only when clicking "Done"
|
|
25
36
|
selectionModel.value = searchDialogSelectedValue.value;
|
|
26
37
|
searchDialog.value = false;
|
|
27
38
|
};
|
|
39
|
+
|
|
28
40
|
const onCancel = () => {
|
|
41
|
+
// Close the dialog without saving changes
|
|
29
42
|
searchDialog.value = false;
|
|
30
43
|
};
|
|
31
44
|
</script>
|
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { ref, defineProps, defineEmits } from
|
|
2
|
+
import { ref, defineProps, defineEmits } from 'vue';
|
|
3
3
|
defineProps({
|
|
4
4
|
title: {
|
|
5
5
|
type: String,
|
|
6
6
|
default() {
|
|
7
|
-
return "Delete item"
|
|
7
|
+
return "Delete item"
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
10
|
message: {
|
|
11
11
|
type: String,
|
|
12
12
|
default() {
|
|
13
|
-
return "Are you sure you want to delete this item?"
|
|
13
|
+
return "Are you sure you want to delete this item?"
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
cancelText: {
|
|
17
17
|
type: String,
|
|
18
18
|
default() {
|
|
19
|
-
return "Cancel"
|
|
19
|
+
return "Cancel"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
confirmText: {
|
|
23
23
|
type: String,
|
|
24
24
|
default() {
|
|
25
|
-
return "OK"
|
|
25
|
+
return "OK"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
})
|
|
29
|
-
const emit = defineEmits([
|
|
30
|
-
const dialog = ref(false)
|
|
28
|
+
})
|
|
29
|
+
const emit = defineEmits(['confirm']); // Declare the 'confirm' event
|
|
30
|
+
const dialog = ref(false)
|
|
31
31
|
const open = () => {
|
|
32
|
-
dialog.value = true
|
|
33
|
-
}
|
|
32
|
+
dialog.value = true
|
|
33
|
+
}
|
|
34
34
|
const handleConfirm = () => {
|
|
35
|
-
emit(
|
|
35
|
+
emit('confirm'); // Emit the 'confirm' event
|
|
36
36
|
dialog.value = false;
|
|
37
37
|
};
|
|
38
|
+
|
|
38
39
|
</script>
|
|
39
40
|
|
|
40
41
|
<template>
|
|
@@ -78,6 +79,11 @@ const handleConfirm = () => {
|
|
|
78
79
|
</v-btn>
|
|
79
80
|
</v-card-actions>
|
|
80
81
|
|
|
82
|
+
|
|
81
83
|
</v-card>
|
|
82
84
|
</v-dialog>
|
|
83
85
|
</template>
|
|
86
|
+
|
|
87
|
+
<style scoped>
|
|
88
|
+
|
|
89
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {computed} from 'vue';
|
|
3
|
+
import {defineProps} from 'vue';
|
|
4
4
|
const props = defineProps({
|
|
5
5
|
errors: Object,
|
|
6
6
|
errorKey: {
|
|
@@ -8,9 +8,9 @@ const props = defineProps({
|
|
|
8
8
|
default: null
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
+
|
|
11
12
|
const random = computed(() => Math.random());
|
|
12
13
|
</script>
|
|
13
|
-
|
|
14
14
|
<template>
|
|
15
15
|
<v-alert
|
|
16
16
|
v-if="errors != null && errors[errorKey ?? 'global'] != null"
|
|
@@ -26,3 +26,4 @@ const random = computed(() => Math.random());
|
|
|
26
26
|
</div>
|
|
27
27
|
</v-alert>
|
|
28
28
|
</template>
|
|
29
|
+
|
|
@@ -1,103 +1,121 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import CrudListLoader from "./CrudListLoader.vue";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {ref, computed, watch, onMounted, onBeforeUnmount} from "vue";
|
|
4
|
+
import {useDebounceFn} from "@vueuse/core";
|
|
5
5
|
import CrudAddressSearchField from "./CrudAddressSearchField.vue";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import {useCrudConverters} from "../composables/useCrudConverters";
|
|
7
|
+
import {useCrudApi} from "../composables/useCrudApi";
|
|
8
|
+
|
|
8
9
|
const props = defineProps({
|
|
9
10
|
filter: {
|
|
10
11
|
type: Object,
|
|
11
|
-
required: true
|
|
12
|
+
required: true,
|
|
12
13
|
},
|
|
13
14
|
modelValue: null,
|
|
14
|
-
disabled: Boolean
|
|
15
|
+
disabled: Boolean,
|
|
15
16
|
});
|
|
16
17
|
const model = ref();
|
|
17
|
-
const emit = defineEmits([
|
|
18
|
+
const emit = defineEmits(['update:modelValue']);
|
|
19
|
+
// Watch for changes in the prop and update the internal model
|
|
18
20
|
watch(() => props.modelValue, (newValue) => {
|
|
19
|
-
model.value = newValue;
|
|
21
|
+
model.value = newValue; // Sync internal model when the prop changes
|
|
20
22
|
});
|
|
23
|
+
// Debounced emit function
|
|
24
|
+
|
|
21
25
|
const filter = ref(props.filter);
|
|
26
|
+
|
|
27
|
+
// Watch for changes in the filter prop and update the internal filter
|
|
22
28
|
watch(() => props.filter, (newValue) => {
|
|
23
|
-
filter.value = newValue;
|
|
29
|
+
filter.value = newValue; // Sync internal filter when the prop changes
|
|
24
30
|
});
|
|
31
|
+
|
|
25
32
|
const searchDialogSelectedValue = ref(null);
|
|
26
33
|
const search = ref(null);
|
|
27
34
|
const searchDialog = ref(false);
|
|
28
35
|
const datePickerValue = ref(null);
|
|
29
36
|
const datePicker = ref(false);
|
|
37
|
+
|
|
30
38
|
const emitUpdate = () => {
|
|
31
|
-
emit(
|
|
32
|
-
}
|
|
39
|
+
emit('update:modelValue', model.value);
|
|
40
|
+
}
|
|
33
41
|
const debouncedUpdate = useDebounceFn(emitUpdate, 700);
|
|
34
|
-
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
const {inputDateForDb, outputDateFromDb, cloneDeep, localDateForDb} = useCrudConverters();
|
|
45
|
+
|
|
35
46
|
const datePickerDisplayValue = computed(() => {
|
|
36
47
|
if (!selectedValue.value || selectedValue.value.length !== 2) {
|
|
37
48
|
return null;
|
|
38
49
|
}
|
|
39
|
-
const startDate = outputDateFromDb(selectedValue.value[0],
|
|
40
|
-
const endDate = outputDateFromDb(selectedValue.value[1],
|
|
50
|
+
const startDate = outputDateFromDb(selectedValue.value[0], 'dd LLL');
|
|
51
|
+
const endDate = outputDateFromDb(selectedValue.value[1], 'dd LLL');
|
|
41
52
|
return `${startDate} - ${endDate}`;
|
|
42
53
|
});
|
|
54
|
+
|
|
43
55
|
const selectedValue = computed({
|
|
44
56
|
get() {
|
|
45
57
|
if (model.value != null) {
|
|
46
|
-
if (props.filter.optionType ===
|
|
58
|
+
if (props.filter.optionType === 'DateTimeRange') {
|
|
47
59
|
if (!model.value || model.value.length !== 2) {
|
|
48
60
|
return null;
|
|
49
61
|
}
|
|
50
62
|
return model.value;
|
|
51
63
|
}
|
|
52
64
|
return model.value;
|
|
53
|
-
} else if (props.filter.type ===
|
|
65
|
+
} else if (props.filter.type === 'multiOption' || props.filter.type === 'multiSearch') {
|
|
54
66
|
return [];
|
|
55
67
|
} else {
|
|
56
68
|
return null;
|
|
57
69
|
}
|
|
58
70
|
},
|
|
59
71
|
set(val) {
|
|
60
|
-
if (props.filter.optionType ===
|
|
72
|
+
if (props.filter.optionType === 'DateTimeRange' && val && val[0]) {
|
|
61
73
|
let start = val[0];
|
|
62
74
|
let end = val[1];
|
|
63
75
|
model.value = [start, end];
|
|
64
76
|
} else {
|
|
65
77
|
model.value = val;
|
|
66
78
|
}
|
|
67
|
-
if (props.filter.type ===
|
|
79
|
+
if (props.filter.type === 'search' || props.filter.type === 'multiSearch' || props.filter.type === 'option' || props.filter.type === 'multiOption' || props.filter.type === 'multiOptionTree') {
|
|
68
80
|
emitUpdate();
|
|
69
81
|
} else {
|
|
70
82
|
debouncedUpdate();
|
|
71
83
|
}
|
|
72
|
-
}
|
|
84
|
+
},
|
|
73
85
|
});
|
|
86
|
+
|
|
74
87
|
const {
|
|
75
88
|
getItems: searchGetItems,
|
|
76
89
|
items: searchListItems,
|
|
77
90
|
listPending: searchLoading
|
|
78
91
|
} = useCrudApi(props.filter.endpoint);
|
|
92
|
+
|
|
93
|
+
|
|
79
94
|
async function getSearchListValue() {
|
|
80
95
|
if (!selectedValue.value || selectedValue.value.length === 0) {
|
|
81
96
|
return;
|
|
82
97
|
}
|
|
83
|
-
const filters = {
|
|
98
|
+
const filters = {ids: selectedValue.value};
|
|
84
99
|
await searchGetItems(1, selectedValue.value.length, filters);
|
|
85
100
|
}
|
|
101
|
+
|
|
86
102
|
const validateDateRange = () => {
|
|
87
103
|
datePicker.value = false;
|
|
104
|
+
|
|
88
105
|
if (!datePickerValue.value || datePickerValue.value.length < 2) {
|
|
89
106
|
return;
|
|
90
107
|
}
|
|
91
108
|
let startDate = localDateForDb(datePickerValue.value[0]);
|
|
92
109
|
let endDate = localDateForDb(datePickerValue.value[datePickerValue.value.length - 1]);
|
|
93
110
|
selectedValue.value = [startDate, endDate];
|
|
94
|
-
}
|
|
111
|
+
}
|
|
112
|
+
|
|
95
113
|
const closeDatePicker = (clear = false) => {
|
|
96
114
|
if (clear) {
|
|
97
115
|
selectedValue.value = null;
|
|
98
116
|
}
|
|
99
117
|
datePicker.value = false;
|
|
100
|
-
}
|
|
118
|
+
}
|
|
101
119
|
watch(searchDialog, (newValue, oldValue) => {
|
|
102
120
|
if (!newValue && searchDialogSelectedValue.value != null) {
|
|
103
121
|
selectedValue.value = cloneDeep(searchDialogSelectedValue.value);
|
|
@@ -108,19 +126,22 @@ watch(searchDialog, (newValue, oldValue) => {
|
|
|
108
126
|
searchDialogSelectedValue.value = cloneDeep(model.value);
|
|
109
127
|
}
|
|
110
128
|
});
|
|
129
|
+
|
|
111
130
|
onMounted(() => {
|
|
112
131
|
model.value = props.modelValue;
|
|
113
|
-
if (props.filter.type ===
|
|
132
|
+
if (props.filter.type === 'search' || props.filter.type === 'multiSearch') {
|
|
114
133
|
getSearchListValue();
|
|
115
134
|
}
|
|
116
135
|
});
|
|
136
|
+
|
|
117
137
|
const displayForChip = (item) => {
|
|
118
|
-
var searchItem = searchListItems.value.find(
|
|
138
|
+
var searchItem = searchListItems.value.find(inlineItem => inlineItem[props.filter.endpointResponseKey] === item.raw);
|
|
119
139
|
if (!searchItem) {
|
|
120
140
|
return "Loading";
|
|
121
141
|
}
|
|
122
142
|
return searchItem[props.filter.endpointDisplayKey] ?? "Loading";
|
|
123
|
-
}
|
|
143
|
+
}
|
|
144
|
+
|
|
124
145
|
</script>
|
|
125
146
|
|
|
126
147
|
<template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import CrudFilter from "../components/CrudFilter.vue";
|
|
3
|
+
|
|
3
4
|
const model = defineModel();
|
|
4
5
|
const props = defineProps({
|
|
5
6
|
filters: {
|
|
@@ -9,6 +10,7 @@ const props = defineProps({
|
|
|
9
10
|
}
|
|
10
11
|
}
|
|
11
12
|
});
|
|
13
|
+
|
|
12
14
|
</script>
|
|
13
15
|
|
|
14
16
|
<template>
|
|
@@ -18,3 +20,7 @@ const props = defineProps({
|
|
|
18
20
|
</div>
|
|
19
21
|
</div>
|
|
20
22
|
</template>
|
|
23
|
+
|
|
24
|
+
<style scoped>
|
|
25
|
+
|
|
26
|
+
</style>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import CrudErrorDisplay from "../components/CrudErrorDisplay.vue";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import {computed} from "vue";
|
|
4
|
+
import {useDisplay} from "vuetify";
|
|
5
|
+
|
|
5
6
|
const props = defineProps({
|
|
6
7
|
action: {
|
|
7
8
|
type: String,
|
|
@@ -96,17 +97,22 @@ const props = defineProps({
|
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
const item = defineModel();
|
|
99
|
-
const dialogOpen = defineModel(
|
|
100
|
+
const dialogOpen = defineModel('dialog');
|
|
100
101
|
const hasItem = computed(() => item.value && Object.keys(item.value).length > 0);
|
|
102
|
+
|
|
101
103
|
const buttonTitle = computed(() => {
|
|
102
|
-
return props.btnText ? props.btnText : props.action ===
|
|
104
|
+
return props.btnText ? props.btnText : (props.action === 'create' ? props.createTitle : (props.action === 'update' ? props.updateTitle : (props.action === 'delete' ? props.deleteTitle : (props.action === 'view' ? props.viewTitle : ''))));
|
|
103
105
|
});
|
|
106
|
+
|
|
104
107
|
const buttonIcon = computed(() => {
|
|
105
|
-
return props.btnIcon ? props.btnIcon : props.action ===
|
|
108
|
+
return props.btnIcon ? props.btnIcon : (props.action === 'create' ? 'mdi-plus' : (props.action === 'update' ? 'mdi-pencil' : (props.action === 'delete' ? 'mdi-delete' : (props.action === 'view' ? 'mdi-eye' : 'mdi-timer-sand'))));
|
|
106
109
|
});
|
|
110
|
+
|
|
107
111
|
const display = useDisplay();
|
|
108
112
|
const isLarge = computed(() => display.lgAndUp.value);
|
|
109
|
-
|
|
113
|
+
|
|
114
|
+
const readonly = computed(() => props.action === 'view');
|
|
115
|
+
|
|
110
116
|
</script>
|
|
111
117
|
|
|
112
118
|
<template>
|
|
@@ -185,3 +191,7 @@ const readonly = computed(() => props.action === "view");
|
|
|
185
191
|
</v-card>
|
|
186
192
|
</v-dialog>
|
|
187
193
|
</template>
|
|
194
|
+
|
|
195
|
+
<style scoped>
|
|
196
|
+
|
|
197
|
+
</style>
|