Merged PR 37: refinements

This commit is contained in:
D'Alia, Florinda
2024-12-19 15:09:35 +00:00
9 changed files with 241 additions and 50 deletions

View File

@@ -94,12 +94,30 @@
</TabPanels> </TabPanels>
</Tabs> </Tabs>
<!-- Dialog per selezionare lo scenario -->
<Dialog v-model:visible="showScenarioDialog" header="Seleziona uno Scenario" :closable="false" :modal="true" style="width: 400px;">
<div>
<h5>Seleziona uno Scenario</h5>
<Dropdown
v-model="selectedScenario"
:options="scenario_store.scenariosForRE"
optionLabel="name"
placeholder="Seleziona uno scenario"
class="w-full"
/> </div>
<div class="flex justify-end mt-3">
<Button label="Annulla" severity="secondary" @click="showScenarioDialog = false" class="mr-2" />
<Button label="Esegui" severity="primary" :disabled="!selectedScenario" @click="executeScenario" />
</div>
</Dialog>
</template> </template>
<script setup> <script setup>
import { ApplicationCodeService } from '@/service/ApplicationCodeService'; import { ApplicationCodeService } from '@/service/ApplicationCodeService';
import { LoadingStore } from '@/stores/LoadingStore'; import { LoadingStore } from '@/stores/LoadingStore';
import { ScenarioStore } from '@/stores/ScenarioStore';
import { UserPrefStore } from '@/stores/UserPrefStore.js'; import { UserPrefStore } from '@/stores/UserPrefStore.js';
import axios from 'axios'; import axios from 'axios';
import { MdPreview } from 'md-editor-v3'; import { MdPreview } from 'md-editor-v3';
@@ -132,6 +150,7 @@ const { className } = toRefs(props);
const classDetails = ref(null); const classDetails = ref(null);
const classLoaded = ref(false); const classLoaded = ref(false);
const loadingStore = LoadingStore(); const loadingStore = LoadingStore();
const scenario_store = ScenarioStore();
const nodes = ref(null) const nodes = ref(null)
const edges = ref(null) const edges = ref(null)
const dark = ref(false) const dark = ref(false)
@@ -144,6 +163,9 @@ const userPrefStore = UserPrefStore();
const toast = useToast(); const toast = useToast();
const loading_data = ref(false); const loading_data = ref(false);
const { graph, layout, previousDirection } = useLayout() const { graph, layout, previousDirection } = useLayout()
const showScenarioDialog = ref(false); // Controlla la visibilità del dialog
const selectedScenario = ref(null); // Lo scenario selezionato
const scenarios = ref([]); // Lista degli scenari disponibili
const commonRevRequest = reactive({ const commonRevRequest = reactive({
repositoryEntityId: '', repositoryEntityId: '',
applicationName: '', applicationName: '',
@@ -151,8 +173,9 @@ const commonRevRequest = reactive({
fullClassQualifiedName: '', fullClassQualifiedName: '',
applicationVersion: '', applicationVersion: '',
deleteExistingData: true, // Valore booleano deleteExistingData: true, // Valore booleano
applicationType: 'JAVA', applicationType: '',
commitSha: '' commitSha: '',
scenarioId: ''
}); });
onMounted(() => { onMounted(() => {
@@ -160,9 +183,73 @@ onMounted(() => {
console.log("class details: ", classDetails.value); console.log("class details: ", classDetails.value);
console.log("class name: ", className.value); console.log("class name: ", className.value);
}); });
// Mostra il dialog quando si clicca il pulsante
const openToastRE = () => { const openToastRE = () => {
showScenarioDialog.value = true;
};
// Esegue l'azione con lo scenario selezionato
const executeScenario = () => {
if (!selectedScenario.value) {
toast.add({
severity: 'warn',
summary: 'Attenzione',
detail: 'Seleziona uno scenario prima di continuare',
life: 3000
});
return;
}
// Imposta l'ID dello scenario
commonRevRequest.scenarioId = selectedScenario.value.id;
// Nasconde il dialog e chiama la funzione per l'RE
showScenarioDialog.value = false;
doREClass();
};
function checkExtension() {
// Ottieni la parte dopo il punto
const extension = userPrefStore.getSelFile.split('.').pop();
// Controlla se è "java"
if (extension === 'java' || extension === 'jsp') {
commonRevRequest.applicationType = extension;
} else {
commonRevRequest.applicationType = 'GENERIC';
}
};
// Logica per eseguire l'RE
const doREClass = () => {
commonRevRequest.fullClassQualifiedName = props.className;
checkExtension();
console.log("commonRevRequest.fullClassQualifiedName", commonRevRequest.fullClassQualifiedName);
commonRevRequest.applicationName = userPrefStore.getSelApp.internal_name;
commonRevRequest.applicationProjectName = userPrefStore.selectedProject.internal_name;
ApplicationCodeService.doRevEngForSingleClass(commonRevRequest)
.then(response => {
if (response.data !== "KO") {
startPolling(response.data);
} else {
toast.add({
severity: 'error',
summary: 'Errore',
detail: 'Si è verificato un errore. Riprova più tardi.',
life: 3000
});
}
});
};
/*const openToastRE = () => {
confirm.require({ confirm.require({
message: 'Do you want to proceed for the Reverse Engeeniring for this class?', message: 'Do you want to proceed for the Reverse Engeeniring for this class?',
header: 'RE Confirmation', header: 'RE Confirmation',
@@ -196,8 +283,7 @@ const doREClass = () => {
}) })
} }*/
// Function to start polling // Function to start polling
function startPolling(processId) { function startPolling(processId) {
// Set polling interval (every 5 seconds in this case) // Set polling interval (every 5 seconds in this case)
@@ -257,7 +343,7 @@ axios.get('/java-re-module/getProgressRevSingleClass/'+processId).then(response
} }
function tabUpdate(value) { function tabUpdate(value) {
console.log(value); console.log("tab update: ",value);
if ((value === 'class-description' || value ==='class-code') && classLoaded.value === false) { if ((value === 'class-description' || value ==='class-code') && classLoaded.value === false) {
loadClassDetails() loadClassDetails()
} }
@@ -273,7 +359,7 @@ function updateSelectedMethod(value) {
} }
loadingMethod.value = true; loadingMethod.value = true;
axios.get("/source-module/getMethodDetailedInfo?methodName=" + value.value ).then(resp => { axios.get("/source-module/getMethodDetailedInfo?methodName=" + value.value ).then(resp => {
console.log(resp.data); console.log("updateSelectedMethod",resp.data);
selectedMethodDetails.value = resp.data; selectedMethodDetails.value = resp.data;
loadingMethod.value = false; loadingMethod.value = false;
}) })
@@ -296,9 +382,13 @@ function loadClassDetails() {
function createMethodList() { function createMethodList() {
let methods = []; let methods = [];
console.log("classDetails.value.methods", classDetails.value.methods);
classDetails.value.methods.forEach(method => { classDetails.value.methods.forEach(method => {
methods.push({name: method.split(".").slice(-1)[0], value: method}); methods.push({name: method.split(".").slice(-1)[0], value: method});
}); });
console.log("methods", methods);
methods.sort((a, b) => a.name.localeCompare(b.name));
console.log("methods ordered", methods);
return methods; return methods;
} }
@@ -307,4 +397,6 @@ function createMethodList() {
<style> <style>
</style> </style>

View File

@@ -12,7 +12,6 @@ import { UserPrefStore } from '../stores/UserPrefStore.js';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { JellyfishLoader, RiseLoader } from "vue3-spinner"; import { JellyfishLoader, RiseLoader } from "vue3-spinner";
import AppConfigurator from './AppConfigurator.vue';
import AppProfileMenu from './AppProfileMenu.vue'; import AppProfileMenu from './AppProfileMenu.vue';
const auth = useAuth(); const auth = useAuth();
@@ -110,7 +109,9 @@ watch(() => userPrefStore.getSelApp, appUpdated, { immediate: true });
</div> </div>
<div class="topbar-project"> <div class="topbar-project">
<button @click="redirectProject()" class="p-button p-button-outlined" v-tooltip="'Click to change the project'">{{ userPrefStore.user.selectedProject.fe_name }}</button> <button v-if="userPrefStore.user.selectedProject" @click="redirectProject()" class="p-button p-button-outlined" v-tooltip="'Click to change the project'">{{ userPrefStore.user.selectedProject.fe_name }}</button>
<button v-else @click="redirectProject()" class="p-button p-button-outlined" v-tooltip="'Click to change the project'">Project</button>
<!-- <button @click="redirectProject()" class="p-button p-button-outlined" v-tooltip="'Click to change the project'">{{ userPrefStore.user.selectedProject.fe_name }}</button> -->
<!-- <span v-if="userPrefStore.user.selectedProject"> <!-- <span v-if="userPrefStore.user.selectedProject">
<small>PROJECT:</small> {{ userPrefStore.user.selectedProject.fe_name }} <small>PROJECT:</small> {{ userPrefStore.user.selectedProject.fe_name }}
</span> --> </span> -->

View File

@@ -24,8 +24,8 @@ export const ScenarioService = {
getScenariosForRE(){ getScenariosForRE(){
return axios.get('/getScenariosForRE') return axios.get('/getScenariosForRE')
}, },
updateScenarioExecRating(data) { updateScenarioExecRating(id, rating) {
return axios.post('/updateRating', data) return axios.get('/updateRating?id=' + id + '&rating=' + rating)
} }
} }

View File

@@ -13,6 +13,7 @@ export const ScenarioStore = defineStore('scenario_store', () => {
const filterString = ref('') const filterString = ref('')
const allScenarios = ref([]) const allScenarios = ref([])
const typeFilter = ref({ name: 'All', value: 'all' }) const typeFilter = ref({ name: 'All', value: 'all' })
const scenariosForRE = ref([])
const userPrefStore = UserPrefStore() const userPrefStore = UserPrefStore()
const loadingStore = LoadingStore() const loadingStore = LoadingStore()
@@ -28,6 +29,15 @@ export const ScenarioStore = defineStore('scenario_store', () => {
} }
async function fetchScenariosForRE() {
loadingStore.scenario_loading = true;
await ScenarioService.getScenariosForRE().then(resp => {
scenariosForRE.value = resp.data;
loadingStore.scenario_loading = false;
});
}
async function fetchScenariosCross() { async function fetchScenariosCross() {
loadingStore.scenario_loading = true; loadingStore.scenario_loading = true;
await ScenarioService.getScenariosCross().then(resp => { await ScenarioService.getScenariosCross().then(resp => {
@@ -55,6 +65,7 @@ export const ScenarioStore = defineStore('scenario_store', () => {
return allScenarios.value return allScenarios.value
}) })
const filteredScenarios = computed(() => { const filteredScenarios = computed(() => {
console.log("scenarios", allScenarios.value); console.log("scenarios", allScenarios.value);
var filteredScenario = [] var filteredScenario = []
@@ -87,5 +98,5 @@ export const ScenarioStore = defineStore('scenario_store', () => {
fetchScenarios, fetchScenarios,
fetchApplicationScenarios, fetchApplicationScenarios,
scenarios, scenarios,
filterString , typeFilter, fetchScenariosCross, globalScenarios} filterString , typeFilter, fetchScenariosCross, globalScenarios, fetchScenariosForRE, scenariosForRE}
}) })

View File

@@ -10,6 +10,7 @@ export const UserPrefStore = defineStore('userpref_store', () => {
const userLoaded = ref(false) const userLoaded = ref(false)
const selectedApp = ref(null) const selectedApp = ref(null)
const loadingStore = LoadingStore() const loadingStore = LoadingStore()
const selectedFileRE = ref(null)
async function fetchUserData(){ async function fetchUserData(){
@@ -61,6 +62,10 @@ export const UserPrefStore = defineStore('userpref_store', () => {
} }
}); });
async function setSelectedFile(file){
selectedFileRE.value = file;
}
const getSelProj = computed(() => { const getSelProj = computed(() => {
return selectedProject.value return selectedProject.value
}) })
@@ -69,7 +74,11 @@ export const UserPrefStore = defineStore('userpref_store', () => {
return selectedApp.value return selectedApp.value
}) })
const getSelFile = computed(() => {
return selectedFileRE.value
return { user,fetchUserData,userLoaded,selectedProject,availableApp,getSelApp,setSelectedApp,selectedApp, updateSelectedProject,getSelProj } })
return {getSelFile, user,selectedFileRE,fetchUserData,userLoaded,selectedProject,availableApp,getSelApp,setSelectedApp,selectedApp, updateSelectedProject,getSelProj, setSelectedFile }
}) })

View File

@@ -40,10 +40,11 @@
import FileFlowViewer from '@/components/FileFlowViewer.vue'; import FileFlowViewer from '@/components/FileFlowViewer.vue';
import { ApplicationCodeService } from '@/service/ApplicationCodeService'; import { ApplicationCodeService } from '@/service/ApplicationCodeService';
import { LoadingStore } from '@/stores/LoadingStore.js'; import { LoadingStore } from '@/stores/LoadingStore.js';
import { ScenarioStore } from '@/stores/ScenarioStore.js';
import { UserPrefStore } from '@/stores/UserPrefStore.js';
import Tree from 'primevue/tree'; import Tree from 'primevue/tree';
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { UserPrefStore } from '@/stores/UserPrefStore.js';
const nodes = ref(null) const nodes = ref(null)
const expandedKeys = ref({}); const expandedKeys = ref({});
@@ -51,10 +52,13 @@ const selectedFile = ref({})
const router = useRouter(); const router = useRouter();
const userPrefStore = UserPrefStore(); const userPrefStore = UserPrefStore();
const loadingStore = LoadingStore() const loadingStore = LoadingStore()
const scenario_store = ScenarioStore();
onMounted(() => { onMounted(() => {
console.log("Mounted") console.log("Mounted")
fetchApplicationData(); fetchApplicationData();
scenario_store.fetchScenariosForRE();
}) })
function fetchApplicationData() { function fetchApplicationData() {
@@ -88,6 +92,8 @@ function onNodeSelect(e){
if(e.icon == "pi pi-fw pi-file"){ if(e.icon == "pi pi-fw pi-file"){
selectedFile.value = e.key selectedFile.value = e.key
} }
userPrefStore.setSelectedFile(e.label)
console.log("user pref store file", userPrefStore.getSelFile)
console.log(e) console.log(e)
}; };

View File

@@ -33,8 +33,29 @@
<template #header> <template #header>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="font-bold">Execution Input for ID {{execution_id}}</span> <span class="font-bold">Execution Input for ID {{execution_id}}</span>
<div v-if="rating!=null" class="flex justify-end">
<Rating
:modelValue="rating"
:stars="5"
:readonly="true"
/>
</div>
<div v-else class="flex justify-end">
<Rating
:modelValue="rating"
:stars="5"
@change="updateRating($event)"
/>
</div>
</div> </div>
</template> </template>
<div class="box p-4 border rounded-md shadow-sm" style="background-color: white;"> <div class="box p-4 border rounded-md shadow-sm" style="background-color: white;">
<div v-for="(input, index) in inputs" :key="index" class="input-container"> <div v-for="(input, index) in inputs" :key="index" class="input-container">
<div class="input-wrapper"> <div class="input-wrapper">
@@ -91,8 +112,10 @@ import JsonEditorVue from 'json-editor-vue';
import { MdPreview } from 'md-editor-v3'; import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css'; import 'md-editor-v3/lib/style.css';
import ProgressSpinner from 'primevue/progressspinner'; import ProgressSpinner from 'primevue/progressspinner';
import { useToast } from 'primevue/usetoast';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { ScenarioService } from '../../service/ScenarioService.js';
import { ScenarioExecutionStore } from '../../stores/ScenarioExecutionStore.js'; import { ScenarioExecutionStore } from '../../stores/ScenarioExecutionStore.js';
const router = useRouter(); const router = useRouter();
@@ -107,10 +130,12 @@ const formData = ref({});
const exec_id = ref(null); const exec_id = ref(null);
const exec_scenario = ref({}); const exec_scenario = ref({});
const debug_modal = ref(false); const debug_modal = ref(false);
const rating = ref(null);
const scenario_execution_store = ScenarioExecutionStore(); const scenario_execution_store = ScenarioExecutionStore();
const execution = scenario_execution_store.getSelectedExecScenario; const execution = scenario_execution_store.getSelectedExecScenario;
const execution_id = ref(null) const execution_id = ref(null)
const inputs = ref(null); const inputs = ref(null);
const toast = useToast();
onMounted(() => { onMounted(() => {
@@ -136,12 +161,46 @@ const retrieveScenarioExec = (id) => {
scenario.value = response.data.scenario scenario.value = response.data.scenario
exec_scenario.value = response.data exec_scenario.value = response.data
data_loaded.value = true; data_loaded.value = true;
rating.value = response.data.rating
scenario_output.value = response.data.execSharedMap.scenario_output; scenario_output.value = response.data.execSharedMap.scenario_output;
exec_id.value = response.data.scenarioExecution_id exec_id.value = response.data.scenarioExecution_id
inputs.value = response.data.scenarioExecutionInput.inputs inputs.value = response.data.scenarioExecutionInput.inputs
}); });
}; };
async function updateRating(newRating) {
loading_data.value = true;
ScenarioService.updateScenarioExecRating(execution_id.value,newRating.value).then((response) => {
console.log('response:', response);
if (response.data === "OK") {
rating.value = newRating.value;
console.log('Rating aggiornato con successo:', response.data);
toast.add({
severity: 'success', // Tipo di notifica (successo)
summary: 'Successo', // Titolo della notifica
detail: 'Rating updated with success.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
} else {
console.error('Errore nell\'aggiornamento del rating', response.data);
toast.add({
severity: 'error', // Tipo di notifica (errore)
summary: 'Errore', // Titolo della notifica
detail: 'Error updating rating. Try later.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
}
}).catch((error) => {
console.error('Errore durante la chiamata al backend:', error);
}).finally(() => {
loading_data.value = false;
});
}

View File

@@ -71,8 +71,18 @@
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="font-bold">Workflow Response</span> <span class="font-bold">Workflow Response</span>
</div> </div>
<div>
<Rating
:modelValue="rating"
:stars="5"
@change="updateRating($event)"
/>
</div>
</template> </template>
<template #icons> <template #icons>
<div class="flex justify-end"> <div class="flex justify-end">
<Button severity="secondary" rounded @click="openDebug" v-tooltip.left="'View code'"> <Button severity="secondary" rounded @click="openDebug" v-tooltip.left="'View code'">
<i class="pi pi-code"></i> <i class="pi pi-code"></i>
@@ -111,20 +121,24 @@ import axios from 'axios';
import JsonEditorVue from 'json-editor-vue'; import JsonEditorVue from 'json-editor-vue';
import { MdPreview } from 'md-editor-v3'; import { MdPreview } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css'; import 'md-editor-v3/lib/style.css';
import moment from 'moment';
import InputText from 'primevue/inputtext'; import InputText from 'primevue/inputtext';
import Select from 'primevue/select'; import Select from 'primevue/select';
import Textarea from 'primevue/textarea'; import Textarea from 'primevue/textarea';
import { useToast } from 'primevue/usetoast';
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import moment from 'moment';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { JellyfishLoader } from "vue3-spinner"; import { JellyfishLoader } from "vue3-spinner";
import { ScenarioService } from '../../service/ScenarioService';
const loadingStore = LoadingStore(); const loadingStore = LoadingStore();
const toast = useToast();
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const value = ref(''); const value = ref('');
const rating = ref(0);
const scenario = ref({}); const scenario = ref({});
const scenario_response = ref(null); const scenario_response = ref(null);
const scenario_output = ref(null); const scenario_output = ref(null);
@@ -206,7 +220,6 @@ watch(() => route.params.id, fetchScenario);
} }
}; };
const execScenario = () => { const execScenario = () => {
loading_data.value = true; loading_data.value = true;
data_loaded.value = false; data_loaded.value = false;
@@ -286,6 +299,36 @@ watch(() => route.params.id, fetchScenario);
console.log("Polling stopped."); console.log("Polling stopped.");
} }
async function updateRating(newRating) {
ScenarioService.updateScenarioExecRating(exec_id.value,newRating.value).then((response) => {
console.log('response:', response);
if (response.data === "OK") {
rating.value = newRating.value;
console.log('Rating aggiornato con successo:', response.data);
toast.add({
severity: 'success', // Tipo di notifica (successo)
summary: 'Successo', // Titolo della notifica
detail: 'Rating updated with success.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
} else {
console.error('Errore nell\'aggiornamento del rating', response.data);
toast.add({
severity: 'error', // Tipo di notifica (errore)
summary: 'Errore', // Titolo della notifica
detail: 'Error updating rating. Try later.', // Messaggio dettagliato
life: 3000 // Durata della notifica in millisecondi
});
}
}).catch((error) => {
console.error('Errore durante la chiamata al backend:', error);
})
}
</script> </script>
<style scoped> <style scoped>

View File

@@ -100,19 +100,7 @@
/> />
</template> </template>
</Column> </Column>
<Column field="endDate"
header="End Date" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ moment(slotProps.data.endDate).format('DD-MM-YYYY HH:mm:ss') }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column field="scenario.aiModel.apiProvider" header="Model AI" sortable <Column field="scenario.aiModel.apiProvider" header="Model AI" sortable
style="min-width: 12rem"> style="min-width: 12rem">
<template #body="slotProps"> <template #body="slotProps">
@@ -149,21 +137,10 @@
placeholder="Search by File" /> placeholder="Search by File" />
</template> </template>
</Column> </Column>
<Column filterField="outputTypeFilter" header="Output Type" sortable
style="min-width: 12rem">
<template #body="slotProps">
<div class="flex items-center gap-2">
{{ slotProps.data.scenario?.outputType || 'text' }}
</div>
</template>
<template #filter="{ filterModel, filterCallback }">
<InputText v-model="filterModel.value" type="text" @input="filterCallback()"
placeholder="Search by File" />
</template>
</Column>
<Column field="rating" header="Rating"> <Column field="rating" header="Rating">
<template #body="slotProps"> <template #body="slotProps">
<Rating :modelValue="slotProps.data.rating" :stars="5" @change="updateRating(slotProps.data, $event)" /> <Rating :modelValue="slotProps.data.rating" :stars="5" :readonly="true" />
</template> </template>
</Column> </Column>
<Column field="id" :style="{ position: 'sticky', right: '0', zIndex: '1', background: '#f3f3f3'}"> <Column field="id" :style="{ position: 'sticky', right: '0', zIndex: '1', background: '#f3f3f3'}">
@@ -248,17 +225,10 @@ const filters = ref({
operator: FilterOperator.AND, operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }] constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
}, },
outputTypeFilter: {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.CONTAINS }]
},
'startDate': { 'startDate': {
operator: FilterOperator.AND, operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }] constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
},
'endDate': {
operator: FilterOperator.AND,
constraints: [{ value: null, matchMode: FilterMatchMode.DATE_IS }]
} }
}); });