Files
raven-client/ayanova/src/views/home-search.vue
John Cardinal d0afdd9855 HUGE REFACTOR / CLEANUP
if there is a issue it's probably something in here that was changed
2021-09-28 20:19:44 +00:00

358 lines
10 KiB
Vue

<template>
<v-row v-if="formState.ready">
<v-col>
<v-form ref="form">
<v-row justify="start">
<gz-error :error-box-message="formState.errorBoxMessage"></gz-error>
<v-col cols="12" sm="4" lg="4" xl="3">
<v-text-field
v-model="searchPhrase"
clearable
:label="$ay.t('Search')"
ref="searchPhrase"
@change="getDataFromApi()"
hint="text, *xt, te*"
data-cy="phrase"
></v-text-field>
</v-col>
<v-col cols="12" sm="4" lg="4" xl="3">
<v-select
v-model="searchAType"
:items="selectLists.objectTypes"
item-text="name"
item-value="id"
:label="$ay.t('Object')"
></v-select>
</v-col>
<v-col cols="12" sm="4" lg="4" xl="3">
<v-btn color="primary" @click="getDataFromApi()" value="SEARCH">
<v-icon data-cy="btnsearch">$ayiSearch</v-icon>
</v-btn>
</v-col>
<v-col cols="12">
<p v-if="!items.length">{{ $ay.t("NoResults") }}</p>
<v-card v-if="items.length" max-width="900">
<v-list>
<v-subheader v-if="maxResultsReturned">
<span>({{ $ay.t("TooManyResults") }})</span>
</v-subheader>
<template v-for="item in items">
<!-- KEY MUST BE UNIQUE INSIDE v-for OR LIST ITEM GOES SNAKEY -->
<v-subheader :key="'s' + item.index" v-if="item.subheader"
><v-icon large class="mr-2">{{ item.icon }}</v-icon>
<span class="text-h6">{{ item.subheader }}</span>
</v-subheader>
<v-list-item link :key="item.index">
<v-list-item-content
@click="openItem(item)"
:data-cy="'btnopenitem' + item.index"
>
<v-list-item-title v-text="item.name"></v-list-item-title>
<v-list-item-subtitle
v-html="item.info"
></v-list-item-subtitle>
</v-list-item-content>
<v-list-item-action>
<v-btn icon @click="getExcerpt(item)">
<v-icon
color="grey lighten-1"
large
:data-cy="'btnexcerpt' + item.index"
>$ayiInfoCircle</v-icon
>
</v-btn>
</v-list-item-action>
</v-list-item>
</template>
</v-list>
</v-card>
</v-col>
</v-row>
</v-form>
</v-col>
</v-row>
</template>
<script>
const FORM_KEY = "search";
const API_BASE_URL = "search/";
const FORM_CUSTOM_TEMPLATE_KEY = "home-search";
const MAX_RESULTS = 200;
export default {
beforeRouteLeave(to, from, next) {
const vm = this;
//save last search in session cache
window.$gz.form.setFormSettings(FORM_KEY, {
temp: {
ayaType: vm.searchAType,
phrase: vm.searchPhrase,
items: vm.items,
maxResultsReturned: vm.maxResultsReturned
}
});
next();
},
async created() {
const vm = this;
try {
await initForm(vm);
vm.formState.ready = true;
window.$gz.eventBus.$on("menu-click", clickHandler);
generateMenu(vm);
if (vm.$route.params.ayatype) {
vm.searchAType = vm.$route.params.ayatype;
}
//get form settings from session cache, if same type as in route then re-use the last search stuff
//however if different than need to clear it (or not rehydrate it)
let savedSettings = window.$gz.form.getFormSettings(FORM_KEY);
if (savedSettings && savedSettings.temp) {
savedSettings = savedSettings.temp;
if (vm.searchAType == null || vm.searchAType == savedSettings.ayaType) {
//same type or no type so go ahead and rehydrate
vm.searchPhrase = savedSettings.phrase;
vm.searchAType = savedSettings.ayaType;
vm.items = savedSettings.items;
}
}
} catch (err) {
vm.formState.ready = true;
window.$gz.errorHandler.handleFormError(err, vm);
}
},
beforeDestroy() {
window.$gz.eventBus.$off("menu-click", clickHandler);
},
components: {},
data() {
return {
formCustomTemplateKey: FORM_CUSTOM_TEMPLATE_KEY,
selectLists: {
objectTypes: []
},
searchPhrase: null,
searchAType: 0,
items: [],
maxResultsReturned: false,
formState: {
ready: false,
dirty: false,
valid: true,
readOnly: false,
loading: false,
errorBoxMessage: null,
appError: null,
serverError: {}
},
rights: window.$gz.role.defaultRightsObject()
};
},
methods: {
openItem(item) {
window.$gz.eventBus.$emit("openobject", {
type: item.type,
id: item.id
});
},
async getExcerpt(item) {
const vm = this;
//Search/Info/2/1?phrase=we
if (item.info || item.id == 0) {
return;
}
let max = 40;
switch (vm.$vuetify.breakpoint.name) {
case "xs":
max = 30;
break;
case "sm":
max = 67;
break;
case "md":
max = 78;
break;
case "lg":
max = 120;
break;
case "xl":
max = 120;
break;
default:
max = 40;
}
try {
let res = await window.$gz.api.get(
API_BASE_URL +
"Info/" +
item.type +
"/" +
item.id +
"?phrase=" +
vm.searchPhrase +
"&max=" +
max
);
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
let showInfo = res.data;
const searchTerms = vm.searchPhrase.replace(/[*]/gi, "").split(" ");
for (let i = 0; i < searchTerms.length; i++) {
const regEx = new RegExp(searchTerms[i], "ig");
const replaceMask =
"<span class='v-list-item__mask'>" + searchTerms[i] + "</span>";
showInfo = showInfo.replace(regEx, replaceMask);
}
item.info = showInfo;
}
} catch (error) {
window.$gz.errorHandler.handleFormError(error, vm);
}
},
async getDataFromApi() {
const vm = this;
if (!vm.searchPhrase || vm.formState.loading) {
return;
}
vm.formState.loading = true;
window.$gz.form.deleteAllErrorBoxErrors(vm);
try {
const res = await window.$gz.api.upsert(API_BASE_URL, {
phrase: vm.searchPhrase,
nameOnly: false,
typeOnly: !!vm.searchAType ? vm.searchAType : 0,
maxResults: MAX_RESULTS
});
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
vm.maxResultsReturned = res.data.searchResults.length == MAX_RESULTS;
const newResults = [];
let nDex = 0;
let lastType = -1;
for (let i = 0; i < res.data.searchResults.length; i++) {
const item = res.data.searchResults[i];
item.name = await window.$gz.translation.translateStringWithMultipleKeysAsync(
item.name
);
if (item.type != lastType) {
//change of type, set subheader props
const tsub = vm.selectLists.objectTypes.find(
z => z.id == item.type
);
if (tsub != null) {
item.subheader = tsub.name;
} else {
item.subheader = "TYPE " + item.type;
}
item.icon = window.$gz.util.iconForType(item.type);
lastType = item.type;
}
item.info = null;
item.index = ++nDex;
newResults.push(item);
}
vm.items = newResults;
}
} catch (error) {
window.$gz.errorHandler.handleFormError(error, vm);
} finally {
window.$gz.form.setFormState({
vm: vm,
loading: false
});
}
}
}
};
/////////////////////////////
//
//
function clickHandler(menuItem) {
if (!menuItem) {
return;
}
const m = window.$gz.menu.parseMenuItem(menuItem);
if (m.owner == FORM_KEY && !m.disabled) {
switch (m.key) {
default:
window.$gz.eventBus.$emit(
"notify-warning",
FORM_KEY + "::context click: [" + m.key + "]"
);
}
}
}
//////////////////////
//
//
function generateMenu(vm) {
const menuOptions = {
isMain: true,
icon: "$ayiSearch",
title: "Search",
helpUrl: "home-search",
hideSearch: true,
formData: {
ayaType: window.$gz.type.UserOptions
},
menuItems: []
};
window.$gz.eventBus.$emit("menu-change", menuOptions);
}
/////////////////////////////////
//
//
async function initForm(vm) {
await fetchTranslatedText(vm);
await populateSelectionLists(vm);
}
//////////////////////////////////////////////////////////
//
// Ensures UI translated text is available
//
async function fetchTranslatedText(vm) {
await window.$gz.translation.cacheTranslations([
"TooManyResults",
"NoResults",
"Object"
//, "ServiceBank"
]);
}
//////////////////////
//
//
async function populateSelectionLists(vm) {
const res = await window.$gz.api.get("enum-list/list/alltranslated");
if (res.error) {
vm.formState.serverError = res.error;
window.$gz.form.setErrorBoxErrors(vm);
} else {
res.data.sort(window.$gz.util.sortByKey("name"));
vm.selectLists.objectTypes = res.data;
// // //ServiceBank will appear in search results but is not a Core biz object so won't be added to list by server
// // //and needs to be added manually here
// // vm.selectLists.objectTypes.push({ name: vm.$ay.t("ServiceBank"), id: 55 });
window.$gz.form.addNoSelectionItem(vm.selectLists.objectTypes);
}
}
</script>